我大概写了下,比较占用资源,比较乱,不过基本功能是实现了
unit uMain;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComCtrls, ToolWin, ImgList, ExtCtrls, StdCtrls, ExtDlgs;
type
TfrmMain = class(TForm)
ToolBar1: TToolBar;
btnZoomOut: TToolButton;
btnRecover: TToolButton;
ToolButton3: TToolButton;
btnMove: TToolButton;
ImageList1: TImageList;
btnZoomIn: TToolButton;
StatusBar: TStatusBar;
imgMap: TImage;
btnOpenPic: TToolButton;
ToolButton2: TToolButton;
OpenPictureDialog: TOpenPictureDialog;
procedure btnOpenPicClick(Sender: TObject);
procedure imgMapMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure imgMapMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
procedure FormResize(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure imgMapMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
procedure btnMoveClick(Sender: TObject);
procedure btnRecoverClick(Sender: TObject);
procedure btnZoomInClick(Sender: TObject);
procedure btnZoomOutClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
theCursor:Integer;//当前光标状态
{
originMap ->原图,为了以后不再因载入文件而带来的延迟,特加此缓冲
bmp ->当前内存缓冲,主要为了放大缩小用
}
originMap,bmp:TBitmap;
oldX,oldY:Integer;//保存需要搽除的坐标点
oldRect:TRect;//保存绝对放大后上一次所截取的区域
zoomState
![Big Grin :D :D](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f600.png)
ouble;//保存当前放大缩小状态
isLeftDown:Integer;//判断左键是否按下,标示拖动开始
dragOldX,dragOldY:Integer;
mendZoomIn:Integer;//修正最后一次ZoomIn的地图偏差
//矢量图支持
maxWidth,maxHeight:Integer;
isWmf:Integer;
public
{ Public declarations }
procedure DrawPoint(const AX,AY:Integer;needNew:Boolean);
procedure ZoomOut(const AX,AY:Integer);
procedure ZoomIn(const AX,AY:Integer);
procedure MapMoveTo(var endX,endY:Integer);
procedure LogOldRect;
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
procedure TfrmMain.FormCreate(Sender: TObject);
begin
Screen.Cursors[1] :=LoadCursorFromFile('zout.cur');//放大
Screen.Cursors[2] :=LoadCursorFromFile('zin.cur');//缩小
Screen.Cursors[3] :=LoadCursorFromFile('hand.cur');//拖动
Screen.Cursors[4] :=LoadCursorFromFile('handDown.cur');//拖动,按下的手
theCursor :=0;
originMap :=TBitmap.Create;
bmp :=TBitmap.Create;
bmp.LoadFromFile('map.bmp');
originMap.Assign(bmp);
imgMap.Canvas.StretchDraw(imgMap.ClientRect,originMap);
Self.DoubleBuffered :=True;
oldRect :=Rect(0,0,bmp.Width,bmp.Height);//初始化
zoomState :=1;
mendZoomIn:=0;
isWmf :=0;
//由下面的测试得知默认的PenMode是4,即pmCopy
//ShowMessage(IntToStr(Byte(bmp.Canvas.Pen.Mode)));
end;
procedure TfrmMain.btnZoomOutClick(Sender: TObject);
begin
if not (mendZoomIn<0) then//修正状态,不允许继续点击
theCursor :=1
else begin
ShowMessage('修正,自动恢复');
btnRecover.Click;
end;
end;
procedure TfrmMain.btnZoomInClick(Sender: TObject);
begin
if not (mendZoomIn<0) then//修正状态,不允许继续点击
theCursor :=2
else begin
ShowMessage('修正,自动恢复');
btnRecover.Click;
end;
end;
procedure TfrmMain.btnRecoverClick(Sender: TObject);
begin
theCursor :=0;
bmp.Width :=imgMap.ClientWidth;
bmp.Height:=imgMap.ClientHeight;
oldRect :=Rect(0,0,bmp.Width,bmp.Height);
bmp.FreeImage;
bmp.Canvas.Pen.Mode :=pmCopy;
bmp.Canvas.StretchDraw(bmp.Canvas.ClipRect,originMap);
imgMap.Canvas.FillRect(imgMap.ClientRect);
imgMap.Picture.Assign(bmp);
zoomState :=1;
mendZoomIn:=0;
end;
procedure TfrmMain.btnMoveClick(Sender: TObject);
begin
theCursor :=3;
end;
procedure TfrmMain.imgMapMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer);
begin
if Screen.Cursor<>theCursor then Screen.Cursor :=theCursor;
StatusBar.Panels[0].Text :=Format('座标位置(%d,%d)',[X,Y]);
if (Screen.Cursor<3) then
DrawPoint(X,Y,True)
else if (isLeftDown>0)and(zoomState>=1) then//拖动
begin
MapMoveTo(x,y);
dragOldX :=x;
dragOldY :=y;
end;
end;
procedure TfrmMain.FormClose(Sender: TObject; var Action: TCloseAction);
begin
bmp.Destroy;
bmp :=nil;
originMap.Destroy;
originMap :=nil;
end;
//指示当前鼠标所在点
//采用异或的方法来画图,画出一个区域(半径为3的红色圆)
procedure TfrmMain.DrawPoint(const AX,AY:Integer;needNew:Boolean);
var
circle:Integer;//区域标示,本变量根据不同光标确定不同大小的区域
begin
if isWmf=0 then//对矢量图不画点
begin
circle :=0;
case theCursor of
0: circle :=4;
1..4: circle :=10;
end;
if imgMap.Canvas.Pen.Mode<>pmXor then
imgMap.Canvas.Pen.Mode :=pmXor;
//搽除
imgMap.Canvas.Ellipse(oldX-circle,oldY-circle,oldX+circle,oldY+circle);
if needNew then
imgMap.Canvas.Ellipse(AX-circle,AY-circle,AX+circle,AY+circle);
oldX :=AX;
oldY :=AY;
end;
end;
procedure TfrmMain.FormResize(Sender: TObject);
begin
if Assigned(bmp) then
begin
bmp.Width :=imgMap.ClientWidth;
bmp.Height:=imgMap.ClientHeight;
bmp.Canvas.StretchDraw(bmp.Canvas.ClipRect,originMap);
imgMap.Picture.Assign(bmp);
end;
end;
procedure TfrmMain.ZoomOut(const AX,AY:Integer);
const zoutFactor
![Big Grin :D :D](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f600.png)
ouble =1.25;
var
allRect:TRect;
x,y :Integer;
oRect :TRect;//原始区域
begin
//计算绝对坐标
x :=Round((AX +oldRect.Left) *zoutFactor);
y :=Round((AY +oldRect.Top) *zoutFactor);
oldRect.Left :=x -AX;
oldRect.Right:=oldRect.Left +imgMap.Width;
oldRect.Top :=y -AY;
oldRect.Bottom:=oldRect.Top +imgMap.Height;
oRect :=Rect(0,0,bmp.Width,bmp.Height); //记录原大小
//固定放大倍数,一次1.25
bmp.Width :=Round(bmp.Width * zoutFactor);
bmp.Height:=Round(bmp.Height* zoutFactor);
allRect :=Rect(0,0,bmp.Width,bmp.Height);
if isWmf=0 then
begin
//绝对放大
bmp.Canvas.CopyRect(bmp.Canvas.ClipRect,bmp.Canvas,oRect);
end
else begin//矢量图的支持
oRect :=Rect(0,0,originMap.Width,originMap.Height);
bmp.Canvas.CopyRect(bmp.Canvas.ClipRect,originMap.Canvas,oRect);
end;
//复制,实现放大效果
imgMap.Canvas.CopyRect(imgMap.ClientRect,bmp.Canvas,oldRect);
if isWmf=1 then//或许能够释放一些资源
bmp.FreeImage;
//标记当前状态
zoomState :=zoomState *zoutFactor;
end;
procedure TfrmMain.ZoomIn(const AX,AY:Integer);
const zinFactor
![Big Grin :D :D](https://cdn.jsdelivr.net/joypixels/assets/8.0/png/unicode/64/1f600.png)
ouble =0.8;
var
x,y :Integer;
eRect :TRect;//目标区域
begin
if (bmp.Width>=imgMap.Width)and(bmp.Height>imgMap.Height) then
begin
eRect :=Rect(0,0,Round(bmp.Width * zinFactor),Round(bmp.Height* zinFactor));
//绝对缩小
bmp.Canvas.CopyRect(eRect,bmp.Canvas,Rect(0,0,bmp.Width,bmp.Height));
//修正图片大小
bmp.Width :=eRect.Right -eRect.Left;
bmp.Height:=eRect.Bottom -eRect.Top;
//可视区域的计算
//固定缩小倍数,一次0.8
//计算绝对坐标
x :=Round((AX +oldRect.Left) *zinFactor);
y :=Round((AY +oldRect.Top) *zinFactor);
oldRect.Left :=x -AX;
oldRect.Top :=y -AY;
oldRect.Right:=oldRect.Left +imgMap.ClientWidth;
oldRect.Bottom:=oldRect.Top +imgMap.ClientHeight;
//对出错状态进行标记
if (oldRect.Left<0)or(oldRect.Top<0)
or(oldRect.Right>bmp.Canvas.ClipRect.Right)
or(bmp.Canvas.ClipRect.Bottom<oldRect.Bottom)
then
Dec(mendZoomIn);
{
//修正左侧边界
if oldRect.Left<0 then
begin
oldRect.Right:=imgMap.ClientWidth;
oldRect.Left :=0;
end
else begin//修正右侧边界
oldRect.Right:=oldRect.Left +imgMap.ClientWidth;
if bmp.Canvas.ClipRect.Right<oldRect.Right then
begin
oldRect.Right:=bmp.Canvas.ClipRect.Right;
oldRect.Left :=oldRect.Right -imgMap.ClientWidth;
end;
end;
//修正上侧边界
if oldRect.Top<0 then
begin
oldRect.Bottom:=imgMap.ClientHeight;
oldRect.Top :=0;
end
else begin//修正下侧边界
oldRect.Bottom:=oldRect.Top +imgMap.ClientHeight;
if bmp.Canvas.ClipRect.Bottom<oldRect.Bottom then
begin
oldRect.Bottom :=bmp.Canvas.ClipRect.Bottom;
oldRect.Top :=oldRect.Bottom -imgMap.ClientHeight;
end;
end;
}
//复制,实现缩小效果
imgMap.Canvas.FillRect(imgMap.Canvas.ClipRect);
imgMap.Canvas.CopyRect(imgMap.ClientRect,bmp.Canvas,oldRect);
//标记当前状态
zoomState :=zoomState *zinFactor;
LogOldRect;
end;
end;
procedure TfrmMain.imgMapMouseUp(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
if Button=mbLeft then
if theCursor=1 then
ZoomOut(x,y)
else if (theCursor=2)and(zoomState>1) then
ZoomIn(x,y);
isLeftDown :=0;
if Screen.Cursor=4 then Screen.Cursor :=3;
end;
procedure TfrmMain.imgMapMouseDown(Sender: TObject; Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
begin
isLeftDown :=1;
dragOldX :=x;
dragOldY :=y;
if Screen.Cursor=3 then Screen.Cursor :=4;
end;
//根据dragOldX_Y和当前的endX,endY来实现拖动
procedure TfrmMain.MapMoveTo(var endX,endY:Integer);
procedure ModifyTheRect;
begin
//修正左侧边界
if oldRect.Left<0 then
begin
oldRect.Right:=imgMap.ClientWidth;
oldRect.Left :=0;
end
else begin//修正右侧边界
oldRect.Right:=oldRect.Left +imgMap.ClientWidth;
if bmp.Canvas.ClipRect.Right<oldRect.Right then
begin
oldRect.Right:=bmp.Canvas.ClipRect.Right;
oldRect.Left :=oldRect.Right -imgMap.ClientWidth;
end;
end;
//修正上侧边界
if oldRect.Top<0 then
begin
oldRect.Bottom:=imgMap.ClientHeight;
oldRect.Top :=0;
end
else begin//修正下侧边界
oldRect.Bottom:=oldRect.Top +imgMap.ClientHeight;
if bmp.Canvas.ClipRect.Bottom<oldRect.Bottom then
begin
oldRect.Bottom :=bmp.Canvas.ClipRect.Bottom;
oldRect.Top :=oldRect.Bottom -imgMap.ClientHeight;
end;
end;
end;
var
x,y :Integer;
begin
//计算绝对坐标
x :=dragOldX +oldRect.Left;
y :=dragOldY +oldRect.Top;
oldRect.Left :=x -endX;
oldRect.Top :=y -endY;
oldRect.Right:=oldRect.Left +imgMap.ClientWidth;
oldRect.Bottom:=oldRect.Top +imgMap.ClientHeight;
if not (mendZoomIn<0) then
ModifyTheRect;
{
else begin
ShowMessage('越界,自动恢复');
btnRecover.Click;
end;
}
imgMap.Canvas.FillRect(imgMap.ClientRect);
imgMap.Canvas.CopyRect(imgMap.ClientRect,bmp.Canvas,oldRect);
LogOldRect;
end;
procedure TfrmMain.LogOldRect;
begin
StatusBar.Panels[1].Text :=Format('OldRect(%d,%d,%d,%d)',[oldRect.Left,oldRect.Top,oldRect.Right,oldRect.Bottom]);
StatusBar.Panels[2].Text :=Format('BmpClipRect(%d,%d,%d,%d)',[bmp.Canvas.ClipRect.Left,bmp.Canvas.ClipRect.Top,bmp.Canvas.ClipRect.Right,bmp.Canvas.ClipRect.Bottom]);
end;
procedure TfrmMain.btnOpenPicClick(Sender: TObject);
var
wmfMap:TMetafile;
wmfMapCanvas:TMetafileCanvas;
begin
if OpenPictureDialog.Execute then
begin
if UpperCase(ExtractFileExt(OpenPictureDialog.FileName))=UpperCase('.wmf') then
begin
wmfMap :=TMetafile.Create;
wmfMap.LoadFromFile(OpenPictureDialog.FileName);
wmfMapCanvas :=TMetafileCanvas.Create(wmfMap,GetDC(originMap.Handle));
maxWidth :=1024 *4;
maxHeight:=768 *4;
originMap.Width :=maxWidth;
originMap.Height:=maxHeight;
originMap.Canvas.FillRect(originMap.Canvas.ClipRect);
originMap.Canvas.StretchDraw(originMap.Canvas.ClipRect,wmfMap);
btnRecover.Click;
wmfMapCanvas.Destroy;
wmfMap.Destroy;
isWmf :=1;
end;
end;
end;
end.