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

修改後的效果:

很明顯,柔和了許多。

到此為止基本上效果已經實現了,當然,肯定沒有蘋果那麼絲滑,只是形似。