当前位置 博文首页 > Maicss:用Qt(C++)实现如苹果般的亮屏效果

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

    作者:Maicss 时间:2021-05-02 18:13

    用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));
    }
    

    修改后的效果:

    很明显,柔和了许多。

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

    bk