用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));
}
修改後的效果:
很明顯,柔和了許多。
到此為止基本上效果已經實現了,當然,肯定沒有蘋果那麼絲滑,只是形似。