用Qt(C++)实现如苹果般的亮屏效果

用Qt(C++)实现如苹果般的亮屏效果

苹果的亮屏效果可能有很多人没注意到,和其他大部分手机或电脑不同的是,苹果的亮屏特效不是简单的亮度变化,而是一个渐亮的过程。详细来说就是,图片中较亮的部分先显示出来,而后渐变的显示较暗的地方,最后整个图片完全显示。

那么,Qt该如何实现类似效果?

先看最终效果:

图中是一束灯光,点亮时光束中间较亮的部位显现出来,再带动其他部位显现,这个效果暂且成为“渐亮”。

用到

想要实现此效果,首先需要了解到的Qt函数:

//该函数用于设置图片中(x,y)点的rgb值
void QImage::setPixel(int x, int y, uint index_or_rgb);

思路

可以很明显的看出,点亮和关屏都不是一瞬间完成的,所以,我们首先需要一个计时器来控制该过程。而后我们需要确定该算法的索引,也就是说,假设这个过程需要100帧,那么我们肯定需要一个变量用于记录这个过程执行的进度。再根据进度,计算出当前帧的所有像素点应显示的内容,从而得到这一帧的图片。

如果是普通的亮度计算的话,很简单,只需要每一个像素的RGB值分别和亮度系数相乘即可。亮度系数如果在0-1区间内的话,那么对应的就是从完全黑的图片显示出图片本身的一个过程。

例如:

下边这个RGB颜色值为深绿色:

将RGB三个数字乘以1.5,就会变成下边这个颜色:

这就是亮度调节的基本原理。

所以,要实现渐亮,就要先显现出亮度大于阈值的像素,随着时间推移,阈值越来越大,显示的像素越来越多。

那么,如何获取某像素的亮度值呢?

当RGB三个值均为255时,亮度最大,显白色,当三个值均为0时,亮度最小,显黑色。那么我们可以获取RGB三值的平均数,将其与255相除,规定其为亮度值,也就是说:亮度值=(red+green+blue)/3/255,化简后得:亮度值=(red+green+blue)/765

到此,基本的原理就搞清楚了,但是这里仍然还有一个问题,等我们先实现后再说明。

实现

跟着思路,写下如下代码:

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QRgb>
#include <QImage>
#include <QTimer>
#define PASS 0.3;
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    p.load(":/b.jpg");
    double size=0.4; //图片缩放系数
    p=p.scaled(p.width()*size,p.height()*size);
    ui->label->setPixmap(QPixmap::fromImage(p));
    speed=0;
    timer=new QTimer;
    timer->setInterval(1);
    i=0;
    connect(timer,&QTimer::timeout,[=](){
        i+=speed;
        MainWindow::update(i);
        if(i>100 || i<0){
            i=0;
            timer->stop();
        }
    });
    on_pushButton_clicked();
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::update(int position) //参数为0-100,若从0开始,则逐步变亮,若从100开始,则逐步变暗
{
    QImage t(p);
    double bright=double(position)/100; //亮度系数
    bright=(bright>1)?1:bright;
    bright=(bright<0)?0:bright;

    double flag=-bright+1; //渐亮需要的阈值
    setWindowTitle("亮度:" + QString::number(bright)+
                   "阈值:"+ QString::number(flag));
    for(int x=0;x<p.width();x++){
        for (int y=0;y<p.height();y++) {
            QColor c(p.pixel(x,y));
            double b=double(c.red()+c.green()+c.blue())/765; //计算当前像素的亮度
            if(b>flag){ //判断亮度与阈值的大小关系
                //若亮度大于阈值,将该像素乘以亮度系数
                c.setRgb((int(c.red()*bright)>255)?255:int(c.red()*bright),
                         (int(c.green()*bright)>255)?255:int(c.green()*bright),
                         (int(c.blue()*bright)>255)?255:int(c.blue()*bright));
            }else {
                //若亮度小于阈值,则显示黑色像素,这部分会导致出现问题,后边会替换。
                c.setRgb(0,0,0);
            }
            t.setPixel(x,y,c.rgb());//将像素写入临时变量
        }
    }
    ui->label->setPixmap(QPixmap::fromImage(t));//将计算好的一帧显示在label上
}

void MainWindow::on_pushButton_clicked()
{
    //灭
    i=100;
    speed=-3;
    timer->start();
}

void MainWindow::on_pushButton_2_clicked()
{
    //亮
    i=0;
    speed=3;
    timer->start();
}

如下效果:

可见效果虽然大体实现了,但是还是很差劲的,渐亮的边缘过于生硬,这就是我上边提到的问题,怎么解决呢?

问题

先分析出现这个问题的原因,代码中将阈值以外的像素直接置为黑色了,其实应当有一个过度的过程,可阈值相差不多的边缘,可以渐变直到和阈值相差较大,最终变为黑色。

所以,将像素置为黑色部分的代码替换为以下代码:

else {
	//c.setRgb(0,0,0);
    double a=1.0-(flag-b)*10; //获取加权亮度系数,亮度越大,系数越大,亮度越小,系数越小,最小到0
    a=(a<0)?0:a;
    c.setRgb((int(c.red()*bright*a)>255)?255:int(c.red()*bright*a),
             (int(c.green()*bright*a)>255)?255:int(c.green()*bright*a),
             (int(c.blue()*bright*a)>255)?255:int(c.blue()*bright*a));
}

修改后的效果:

很明显,柔和了许多。

到此为止基本上效果已经实现了,当然,肯定没有苹果那么丝滑,只是形似。