我想大家應該都用過 ACDSee 或者 Windows XP,它們都支持圖像的放大和漫游,雖然網上也有相關的資料,但是都不是很全面,今天我給大家介紹一種方法,由於篇幅的關系,主要講如何實現,盡量做到言簡意赅,好了,我們現在就開始。
說明:
1.本文使用 Object pascal 進行描述,請讀者自行轉換為相關的代碼,作者盡量做到與代碼無關
2.涉及到圖像操作,盡量使用 Windows API 和位操作
理論篇:
關鍵詞:
繪圖區-即窗口顯示圖像的區域,亦可為全屏幕(在全屏幕下繪圖的效果比一般窗口下好)
中心點-即要繪圖區顯示的中心點在原始圖像的坐標(聲明:這個概念特別重要)
先說說圖像的放大,要放大一張圖片,我們一般的做法是直接放大圖像,但本文介紹的方法僅放大我們能夠看到的部分,放大分兩種情況,一種是放大後比繪圖區還要小,這種情況沒什麼好說,當然是顯示全部的圖像;第二種是放大後的圖像比繪圖區大,這才是我們今天要討論的重點話題,這種情況下我們先要確定圖像放大後的大小,然後根據“中心點”計算在原始圖像的位置和大小,最後把截取的圖像放大到繪圖區。
再說說圖像的漫游,當顯示的圖像超過繪圖區時,我們需要對圖像進行漫游,以便看到全部的圖像。原理是:當鼠標在繪圖區進行單擊時,這時開始漫游,先記錄鼠標的單擊位置,然後檢測鼠標的移動,根據鼠標和上次的位移計算出“中心點”(需要將屏幕坐標轉換為原始圖像坐標),根據在上面放大的原理到原始圖像中取出要顯示的部分,放大顯示到繪圖區。
算法實現篇:
1.圖像放大
變量定義:
PZoom:放大率(整數:100時為100%,根據需要可以將 100 該為 10000 或者更大些,但不推薦使用浮點數)
a,b:中心點
w,h:要截取原始圖像的寬和高
x,y:要截取的位置(左上角)
sw,sh:原始圖像的寬和高
p1,p2:放大比例
aw,ah:放大後圖像的大小
pw,ph:繪圖區大小
vx,vy:在繪圖區顯示的位置(左上角)
vw,vh:在繪圖區顯示的大小
ptx,pty:臨時變量
已知的變量:PZoom,(a,b),(sw,sh),(p1,p2),(aw,ah),(pw,ph)
要計算的變量:(x,y),(w,h),(vx,vy),(vw,vh)
開始計算:
aw=Round(PZoom*sw/100);
ah=Round(PZoom*sh/100);
p1=aw/pw
p2=ah/ph
// 注:Round 用於取整,如其他語言的Int(),Fix()等
if p1>1 then w=Round(sw/p1) else w=sw
if p2>1 then h=Round(sh/p2) else h=sh
// 注:shr 為右移運算符,可以使用“>>1”、“div 2”、“2”或“Round(w/2)”代替
x=a-w shr 1
y=b-h shr 1
// 注:div 為整除運算符
ptx=(w*PZoom) div 100
pty=(h*PZoom) div 100
// 以下計算在繪圖區顯示的圖像大小和位置
變量
Pencent:double; // 縮放比
wx:double; // 寬縮放比
hx:double; // 高縮放比
// 獲得縮放比
wx:=pw/ptx
hx:=ph/pty
if wx>hx then Pencent:=hx
else Pencent:=wx;
// 獲得圖片最後的大小
vw:=Round(Pencent*ptx);
vh:=Round(Pencent*pty);
// 計算出圖片的位置
vx:=(pw-vw) div 2;
vy:=(ph-vh) div 2;
// ------------------------------------
好了,兩個重要的任務完成(x,y),(w,h),(vx,vy),(vw,vh)已經全部計算得出,下面的工作就是顯示了,我們選擇 Windows API 進行操作
// 以下顯示圖像 -----------------------
變量
sDC 為原始圖片的設備句柄(DC)
tDC 為臨時設備句柄
dDC 最終設備句柄
BitBlt(tDC,0,0,w,h,sDC,0,0,SRCCOPY);
SetStretchBltMode(dDC,STRETCH_DELETESCANS);
StretchBlt(dDC,0,0,vw,vh,tDC,0,0,w,h,SRCCOPY);
最後繪制到顯示的區域即可:
例如:BitBlt(GetDC(0),vx,vy,vx+vw,xy+vh,dDC,0,0,SRCCOPY);
// ------------------------------------
2.圖像漫游
先定義三個全局變量:
FBeginDragPoint :TPoint; // 記錄鼠標開始拖動的位置
FBeginDragSBPoint :TPoint; // 記錄“中心點”位置
FBeginDrag :boolean; // 是否已經開始“拖動”
a,b :integer; // “中心點”位置
在鼠標左鍵點擊時,記錄鼠標的位置和“中心點”的位置,同時設置 FBeginDrag 為真
當鼠標右鍵彈起時,設置 FBeginDrag 為假
鼠標移動時,判斷 FBeginDrag ,如果為假不進行處理,如果為真進行下面處理:
假設 X,Y 為鼠標當前的位置
a=FBeginDragPoint.X-((X-FBeginDragPoint.X)*100) div PZoom
b=FBeginDragPoint.Y-((Y-FBeginDragPoint.Y)*100) div PZoom
最後使用上面介紹的圖像放大顯示出圖像
技巧篇:
1.如果圖像較大,使用 Delphi 的 位圖對象會出現內存溢出錯誤,這時可以進行如下設置:
bitImage:=TBitmap.Create;
bitImage.PixelFormat:=pf24bit;
bitImage.ReleaseHandle;
2.如果要讓圖像自動適應窗口的大小,參考以下代碼:
var
p1,p2 :double;
begin
p1:=pw/sw;
p2:=ph/sw;
if p1>p2 then PZoom:=Round(p2*100)
else PZoom:=Round(p1*100);
if PZoom=0 then PZoom:=100;
end;
3.要想讓圖像的效果好,最好在全屏幕方式下進行
該方法為“Crazy Marker”所用,如果讀者有興趣可以看看效果