数码相框项目之显示一张可放大、缩小、拖拽的图片
之前我做過一個(gè)電子相框的項(xiàng)目,涉及到的重難點(diǎn)主要為:在LCD上放大、縮小、移動(dòng)圖片。
首先我們得明白的一點(diǎn)是:無論是放大或縮小,實(shí)際上都是對(duì)原圖進(jìn)行等比例的縮小,然后在LCD上面顯示,只不過縮小的程度不同罷了(關(guān)于如何取出原圖的數(shù)據(jù),然后縮小為我們需要的大小,可以看我另外一篇博客:https://blog.csdn.net/qq_37659294/article/details/104382032)。
經(jīng)過上一篇博客介紹的縮放算法之后,我們已經(jīng)得到一張縮小后、和原圖等比例的圖片并存放在buffer中,但這只是得到一張圖片,還沒有顯示到LCD上,那么我們?nèi)绾螌⑦@張圖片在LCD上動(dòng)態(tài)地放大、縮小、移動(dòng)呢?
?
首先,我們來看一下一個(gè)非常關(guān)鍵的函數(shù),ShowZoomedPictureInLayout函數(shù),這個(gè)函數(shù)是在LCD上顯示一張經(jīng)過縮放、移動(dòng)的圖片,因?yàn)閳D片的大小可能比顯示區(qū)域要大,也有可能小,也有可能被移動(dòng)到相框的邊緣,只能顯示圖片的一部分,所以我們必須確認(rèn)要從圖片的哪里(iStartXofNewPic,iStartYofNewPic)開始取數(shù)據(jù),在顯示區(qū)域的哪里(iStartXofOldPic,iStartYofOldPic)開始顯示,顯示多大(iWidthPictureInPlay,iHeightPictureInPlay)的區(qū)域。
ShowZoomedPictureInLayout函數(shù)的實(shí)現(xiàn)思路為:
①計(jì)算出iStartXofNewPic,iStartYofNewPic,判斷要從圖片的哪個(gè)位置開始取數(shù)據(jù)
②利用 g_iXofZoomedPicShowInCenter - iStartXofNewPic?= iDeltaX = 顯示區(qū)中心點(diǎn)X坐標(biāo)(g_tManualPictureLayout.iTopLeftX + iPictureLayoutWidth / 2) - iStartXofOldPic(LCD上顯示圖片的起始坐標(biāo))等式算出iStartXofOldPic,iStartYofOldPic,判斷要從顯示區(qū)域的哪個(gè)位置開始顯示圖片
③計(jì)算出實(shí)際顯示的寬度iWidthPictureInPlay和高度iHeightPictureInPlay
④根據(jù)上面計(jì)算好的參數(shù),把圖片數(shù)據(jù)刷到LCD的framebuffer中
/**********************************************************************
?* 函數(shù)名稱: ShowZoomedPictureInLayout
?* 功能描述: 在"manual頁面"中顯示經(jīng)過縮放的圖片
?* 輸入?yún)?shù): ptZoomedPicPixelDatas - 內(nèi)含已經(jīng)縮放的圖片的象素?cái)?shù)據(jù)
?* ? ? ? ? ? ?ptVideoMem ? ? ? ? ? ?- 在這個(gè)VideoMem中顯示
?* 輸出參數(shù): 無
?* 返 回 值: 無
?***********************************************************************/
static void ShowZoomedPictureInLayout(PT_PixelDatas ptZoomedPicPixelDatas, PT_VideoMem ptVideoMem)
{
? ? /* iStartXofNewPic和iStartYofNewPic這兩個(gè)變量意思不是新圖片在顯示區(qū)域(或者整個(gè)屏幕區(qū)域)的xy坐標(biāo)
? ? ?* 而是從縮放后的圖片坐標(biāo)(iStartXofNewPic,iStartYofNewPic)開始,顯示這個(gè)圖片
? ? ?* 坐標(biāo)(x,y)之前的一塊區(qū)域,即橫坐標(biāo)小于x,縱坐標(biāo)小于y的所有地方,都不顯示
? ? ?* 因?yàn)榭赡芸s放后的圖片,不能完全顯示到屏幕上,只能顯示一部分
? ? ?* 算出從圖片什么地方開始顯示,并且加上一個(gè)長(zhǎng)和寬,那么在屏上顯示的部分就可以描繪出來了
? ? ?*/
? ? int iStartXofNewPic, iStartYofNewPic;
?? ?/* iStartXofOldPic和iStartYofOldPic這兩個(gè)變量并說不是上一張圖片的顯示位置,而是新圖片在屏幕顯示的起始坐標(biāo)
?? ? * 這六個(gè)變量連起來想就是:
?? ? * 從zoom后的圖片的(iStartXofNewPic,iStartYofNewPic)這個(gè)地方開始
?? ? * 取長(zhǎng)寬為iWidthPictureInPlay, iHeightPictureInPlay這么大的一塊區(qū)域
?? ? * 顯示到顯存的(iStartXofOldPic,iStartYofOldPic)這個(gè)地方去
?? ? */
? ? int iStartXofOldPic, iStartYofOldPic;
? ? int iWidthPictureInPlay, iHeightPictureInPlay;
? ? int iPictureLayoutWidth, iPictureLayoutHeight;
? ? int iDeltaX, iDeltaY;//計(jì)算過程的中間變量
?
?? ?/* iPictureLayoutWidth? 顯示區(qū)域的寬
?? ? * iPictureLayoutHeight 顯示區(qū)域的高
?? ? */
? ? iPictureLayoutWidth ?= g_tManualPictureLayout.iBotRightX - g_tManualPictureLayout.iTopLeftX + 1;
? ? iPictureLayoutHeight = g_tManualPictureLayout.iBotRightY - g_tManualPictureLayout.iTopLeftY + 1;
? ??
? ? /* g_iXofZoomedPicShowInCenter 是 圖片最左邊 到 顯示區(qū)中心 的距離,g_iXofZoomedPicShowInCenter = 縮放后寬的一半 + 移動(dòng)的偏移量 (左移時(shí)為正,右移時(shí)為負(fù)(移動(dòng)太大有可能變成負(fù)數(shù)的))
?? ? * 這個(gè)變量第一次賦值是在ShowPictureInManualPage(第一次顯示圖片(居中顯示),這時(shí)候圖片的大小比LCD的圖片顯示區(qū)域小)這個(gè)函數(shù)里,其值就是顯示圖片的寬的一半,也代表著最左邊到中心的距離
?? ? * 在不移動(dòng)時(shí),g_iXofZoomedPicShowInCenter值跟著放大和縮小相同的倍數(shù)
?? ? * 當(dāng)移動(dòng)的時(shí)候,在ManualPageRun函數(shù)里"移動(dòng)圖片"的處理邏輯中,左移就增加,右移就減小,
?? ? * 同理 g_iYofZoomedPicShowInCenter 是圖片顯示位置最上面到顯示區(qū)域中心的距離
?? ? */
? ? iStartXofNewPic = g_iXofZoomedPicShowInCenter - iPictureLayoutWidth/2;//利用g_iXofZoomedPicShowInCenter算出iStartXofNewPic,便知道了要從圖片的哪個(gè)位置取出數(shù)據(jù)來顯示了,對(duì)應(yīng)上面思路的第①步
? ? if (iStartXofNewPic < 0)//即g_iXofZoomedPicShowInCenter < iPictureLayoutWidth/2(顯示區(qū)寬的一半),說明圖片的最左邊在顯示區(qū)域內(nèi)
? ? {
? ? ? ? iStartXofNewPic = 0;//如果圖片的最左邊在顯示區(qū)域內(nèi),那么就從圖片的最左邊開始取數(shù)據(jù)
? ? }
?? ?/* 這中間還有一種情況,就是
?? ? * 當(dāng) (iStartXofNewPic > 0) && (iStartXofNewPic < ptZoomedPicPixelDatas->iWidth) 時(shí)
?? ? * 表示 圖片最左邊 已經(jīng)移出了顯示區(qū)域(最右邊還在顯示區(qū)域內(nèi)),只能顯示一部分
?? ? * 此時(shí) iStartXofNewPic = g_iXofZoomedPicShowInCenter - iPictureLayoutWidth/2;
?? ? * 表示就從圖片的這個(gè)地方開始顯示
?? ? */
? ? if (iStartXofNewPic > ptZoomedPicPixelDatas->iWidth)//即g_iXofZoomedPicShowInCenter >?ptZoomedPicPixelDatas->iWidth(圖寬) + iPictureLayoutWidth/2(顯示區(qū)寬的一半),說明圖片已經(jīng)完全左移出去了
? ? {
? ? ? ? iStartXofNewPic = ptZoomedPicPixelDatas->iWidth;//整張圖片都已經(jīng)被左移出顯示區(qū)域了,不用顯示了
? ? }
? ? ?/* iDeltaX 是 實(shí)際所顯示圖片的起始位置?到 顯示區(qū)域中心點(diǎn) 的距離,由g_iXofZoomedPicShowInCenter減去iStartXofNewPic得到,只是一個(gè)用來計(jì)算iStartXofOldPic的中間變量
? ? ?* 因?yàn)榭赡軙?huì)移動(dòng)到或者放大到左邊不能從頭開始,就是 0<iStartXofNewPic<ptZoomedPicPixelDatas->iWidth的情況了
? ? ?* 這個(gè)式子算出的是,實(shí)際圖片開始顯示的最左邊 到 顯示區(qū)域中心點(diǎn)的距離
? ? ?* iDeltaX有可能是負(fù)數(shù)(當(dāng)圖片移動(dòng)到中心線右邊時(shí),那么iDeltaX就是個(gè)負(fù)數(shù)了),但因?yàn)槲覀兪抢脭?shù)學(xué)等式來計(jì)算iStartXofOldPic,最后負(fù)負(fù)會(huì)得正
?? ? */
? ? iDeltaX = g_iXofZoomedPicShowInCenter - iStartXofNewPic;
? ? ?/* g_iXofZoomedPicShowInCenter - iStartXofNewPic
? ? ?* = iDeltaX
? ? ?* = 顯示區(qū)中心點(diǎn)X坐標(biāo)(g_tManualPictureLayout.iTopLeftX + iPictureLayoutWidth / 2) - iStartXofOldPic(LCD上顯示圖片的起始坐標(biāo))
? ? ?* 根據(jù)上面的等式我們可算出?iStartXofOldPic
?? ? */
? ? iStartXofOldPic = (g_tManualPictureLayout.iTopLeftX + iPictureLayoutWidth / 2) - iDeltaX;
? ? ?/* 當(dāng) iStartXofNewPic = ptZoomedPicPixelDatas->iWidth 時(shí),說明整張圖片已經(jīng)從左邊劃出去了
?? ? * iDeltaX = g_iXofZoomedPicShowInCenter - ptZoomedPicPixelDatas->iWidth >= iPictureLayoutWidth / 2
?? ? * 所以iStartXofOldPic在這種情況下是可能小于g_tManualPictureLayout.iTopLeftX,所以需要判斷
?? ? */
? ? if (iStartXofOldPic < g_tManualPictureLayout.iTopLeftX)
? ? {
? ? ? ? iStartXofOldPic = g_tManualPictureLayout.iTopLeftX;
? ? }
? ? ?/* 向右邊劃出去了 */
? ? if (iStartXofOldPic > g_tManualPictureLayout.iBotRightX)
? ? {
? ? ? ? iStartXofOldPic = g_tManualPictureLayout.iBotRightX + 1;
? ? }
? ? ?
? ? ?/* ptZoomedPicPixelDatas->iWidth - iStartXofNewPic 是圖片除去了左邊可能不顯示的地方,剩下要顯示的部分
?? ? * g_tManualPictureLayout.iBotRightX - iStartXofOldPic + 1 是從顯示位置開始,到可以顯示圖片區(qū)域的結(jié)束,一共多寬
?? ? * 這兩個(gè)選一個(gè)小的,作為實(shí)際顯示的寬度
?? ? */
? ? if ((ptZoomedPicPixelDatas->iWidth - iStartXofNewPic) > (g_tManualPictureLayout.iBotRightX - iStartXofOldPic + 1))
? ? ? ? iWidthPictureInPlay = (g_tManualPictureLayout.iBotRightX - iStartXofOldPic + 1);
? ? else
? ? ? ? iWidthPictureInPlay = (ptZoomedPicPixelDatas->iWidth - iStartXofNewPic);
? ??
? ? ?/* 上面是關(guān)于X坐標(biāo)的計(jì)算,下面開始Y方向的計(jì)算,原理一樣 */
? ? iStartYofNewPic = g_iYofZoomedPicShowInCenter - iPictureLayoutHeight/2;
? ? if (iStartYofNewPic < 0)
? ? {
? ? ? ? iStartYofNewPic = 0;
? ? }
? ? if (iStartYofNewPic > ptZoomedPicPixelDatas->iHeight)
? ? {
? ? ? ? iStartYofNewPic = ptZoomedPicPixelDatas->iHeight;
? ? }
? ? iDeltaY = g_iYofZoomedPicShowInCenter - iStartYofNewPic;
? ? iStartYofOldPic = (g_tManualPictureLayout.iTopLeftY + iPictureLayoutHeight / 2) - iDeltaY;
? ? if (iStartYofOldPic < g_tManualPictureLayout.iTopLeftY)
? ? {
? ? ? ? iStartYofOldPic = g_tManualPictureLayout.iTopLeftY;
? ? }
? ? if (iStartYofOldPic > g_tManualPictureLayout.iBotRightY)
? ? {
? ? ? ? iStartYofOldPic = g_tManualPictureLayout.iBotRightY + 1;
? ? }
? ??
? ? if ((ptZoomedPicPixelDatas->iHeight - iStartYofNewPic) > (g_tManualPictureLayout.iBotRightY - iStartYofOldPic + 1))
? ? {
? ? ? ? iHeightPictureInPlay = (g_tManualPictureLayout.iBotRightY - iStartYofOldPic + 1);
? ? }
? ? else
? ? {
? ? ? ? iHeightPictureInPlay = (ptZoomedPicPixelDatas->iHeight - iStartYofNewPic);
? ? }
? ?
? ? ?/* 把整個(gè)圖片顯示區(qū)填充為背景色 */? ??
? ? ClearVideoMemRegion(ptVideoMem, &g_tManualPictureLayout, COLOR_BACKGROUND);
? ? ?/* 傳入前面計(jì)算好的參數(shù),顯示縮放、移動(dòng)后的圖片
?? ? * iStartXofNewPic, iStartYofNewPic? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?:從圖片的哪個(gè)位置開始取數(shù)據(jù)
?? ? * iStartXofOldPic, iStartYofOldPic? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? :在LCD的哪個(gè)位置開始顯示圖片
?? ? * iWidthPictureInPlay, iHeightPictureInPlay ? ? ? ? ? ? ? ? ? :正真顯示圖片的寬和高
?? ? * ptZoomedPicPixelDatas, &ptVideoMem->tPixelDatas:源和目的
?? ? */
? ? PicMergeRegion(iStartXofNewPic, iStartYofNewPic, iStartXofOldPic, iStartYofOldPic, iWidthPictureInPlay, iHeightPictureInPlay, ptZoomedPicPixelDatas, &ptVideoMem->tPixelDatas);
}
?
PicMergeRegion函數(shù)定義如下:
/*********************************************************************** 函數(shù)名稱: PicMergeRegion* 功能描述: 把新圖片的某部分, 合并入老圖片的指定區(qū)域* 輸入?yún)?shù): iStartXofNewPic, iStartYofNewPic : 從新圖片的(iStartXofNewPic, iStartYofNewPic)座標(biāo)處開始讀出數(shù)據(jù)用于合并* iStartXofOldPic, iStartYofOldPic : 合并到老圖片的(iStartXofOldPic, iStartYofOldPic)座標(biāo)去* iWidth, iHeight : 合并區(qū)域的大小* ptNewPic : 新圖片* ptOldPic : 老圖片* 輸出參數(shù): 無* 返 回 值: 0 - 成功, 其他值 - 失敗***********************************************************************/ int PicMergeRegion(int iStartXofNewPic, int iStartYofNewPic, int iStartXofOldPic, int iStartYofOldPic, int iWidth, int iHeight, PT_PixelDatas ptNewPic, PT_PixelDatas ptOldPic) {int i;unsigned char *pucSrc;unsigned char *pucDst;int iLineBytesCpy = iWidth * ptNewPic->iBpp / 8;if ((iStartXofNewPic < 0 || iStartXofNewPic >= ptNewPic->iWidth) || \(iStartYofNewPic < 0 || iStartYofNewPic >= ptNewPic->iHeight) || \(iStartXofOldPic < 0 || iStartXofOldPic >= ptOldPic->iWidth) || \(iStartYofOldPic < 0 || iStartYofOldPic >= ptOldPic->iHeight)){return -1;}pucSrc = ptNewPic->aucPixelDatas + iStartYofNewPic * ptNewPic->iLineBytes + iStartXofNewPic * ptNewPic->iBpp / 8;pucDst = ptOldPic->aucPixelDatas + iStartYofOldPic * ptOldPic->iLineBytes + iStartXofOldPic * ptOldPic->iBpp / 8;for (i = 0; i < iHeight; i++){memcpy(pucDst, pucSrc, iLineBytesCpy);pucSrc += ptNewPic->iLineBytes;pucDst += ptOldPic->iLineBytes;}return 0; }?
?
下面是放大的完整處理過程:
static int g_iXofZoomedPicShowInCenter; static int g_iYofZoomedPicShowInCenter; /* 放大/縮小系數(shù) */ #define ZOOM_RATIO (0.9) case 2: /* 放大按鈕 */ {/* 獲得放大后的數(shù)據(jù) */iZoomedWidth = (float)g_tZoomedPicPixelDatas.iWidth / ZOOM_RATIO;iZoomedHeight = (float)g_tZoomedPicPixelDatas.iHeight / ZOOM_RATIO;ptZoomedPicPixelDatas = GetZoomedPicPixelDatas(&g_tOriginPicPixelDatas, iZoomedWidth, iZoomedHeight);/* 重新計(jì)算中心點(diǎn) */g_iXofZoomedPicShowInCenter = (float)g_iXofZoomedPicShowInCenter / ZOOM_RATIO;g_iYofZoomedPicShowInCenter = (float)g_iYofZoomedPicShowInCenter / ZOOM_RATIO;/* 顯示新數(shù)據(jù) */ShowZoomedPictureInLayout(ptZoomedPicPixelDatas, ptDevVideoMem);break; }縮小:
/* 放大/縮小系數(shù) */ #define ZOOM_RATIO (0.9) case 1: /* 縮小按鈕 */ {/* 獲得縮小后的數(shù)據(jù) */iZoomedWidth = (float)g_tZoomedPicPixelDatas.iWidth * ZOOM_RATIO;iZoomedHeight = (float)g_tZoomedPicPixelDatas.iHeight * ZOOM_RATIO;ptZoomedPicPixelDatas = GetZoomedPicPixelDatas(&g_tOriginPicPixelDatas, iZoomedWidth, iZoomedHeight);/* 重新計(jì)算中心點(diǎn) */g_iXofZoomedPicShowInCenter = (float)g_iXofZoomedPicShowInCenter * ZOOM_RATIO;g_iYofZoomedPicShowInCenter = (float)g_iYofZoomedPicShowInCenter * ZOOM_RATIO;/* 顯示新數(shù)據(jù) */ShowZoomedPictureInLayout(ptZoomedPicPixelDatas, ptDevVideoMem);break; }拖拽:
/* 如果觸點(diǎn)滑動(dòng)距離大于規(guī)定值, 則挪動(dòng)圖片 */ if (DistanceBetweenTwoPoint(&tInputEvent, &tPreInputEvent) > SLIP_MIN_DISTANCE) { /* 重新計(jì)算中心點(diǎn) */g_iXofZoomedPicShowInCenter -= (tInputEvent.iX - tPreInputEvent.iX);g_iYofZoomedPicShowInCenter -= (tInputEvent.iY - tPreInputEvent.iY);/* 顯示新數(shù)據(jù) */ShowZoomedPictureInLayout(ptZoomedPicPixelDatas, ptDevVideoMem);/* 記錄上次滑動(dòng)點(diǎn) */tPreInputEvent = tInputEvent; }實(shí)驗(yàn)效果:
一開始顯示是居中顯示
拖拽?
放大?
參考文章:https://blog.csdn.net/qq_22655017/article/details/97627382
?
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
以上是生活随笔為你收集整理的数码相框项目之显示一张可放大、缩小、拖拽的图片的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 华为荣耀X2能改系统吗
- 下一篇: TCP协议-如何保证传输可靠性