如何正确缩放宽高比 [英] How To Zoom with keeping aspect ratio correctly
问题描述
使用鼠标左键滚动图像,鼠标右键单击
选择缩放矩形并双击恢复完全缩放。
我目前累了,到目前为止,发现它不像我加载图像或显示图像的方式,但它的颜色如何。屏幕图像始终填充控件的客户区,无论窗体或源图像的形状如何,因此无法保留宽高比。我不知道如何改变这个或保持长宽比。因此给我一个干净的漂亮的图片。
我发布了我的ZImage单元的整个代码虽然我认为问题是在Zimage.paint或Zimage.mouseup但是想知道如果你需要在其中的一个内部看到一个功能,它将有助于它全部发布。
unit ZImage;
接口
使用
Windows,消息,SysUtils,jpeg,类,图形,控件,表单,对话框,
ExtCtrls;
type
TZImage = class(TGraphicControl)
private
FBitmap:Tbitmap;
PicRect:TRect;
ShowRect:TRect;
FShowBorder:boolean;
FBorderWidth:integer;
FForceRepaint:boolean;
FMouse:(mNone,mDrag,mZoom);
FProportional:boolean;
FDblClkEnable:boolean;
FLeft:integer;
FRight:integer;
FTop:integer;
FBottom:integer;
startx,starty,
oldx,oldy:integer;
程序SetShowBorder(s:boolean);
程序SetBitmap(b:TBitmap);
程序SetBorderWidth(w:integer);
procedure SetProportional(b:boolean);
protected
procedure Paint;覆盖
procedure MouseDown(Button:TMouseButton; Shift:TShiftState;
X,Y:Integer);覆盖
procedure MouseMove(Shift:TShiftState; X,Y:Integer);覆盖
procedure MouseUp(Button:TMouseButton; Shift:TShiftState;
X,Y:Integer);覆盖
public
构造函数Create(AOwner:TComponent);覆盖
析构函数覆盖
程序DblClick;覆盖
发布
程序缩放(Endleft,EndRight,EndTop,EndBottom:integer);
属性ValueLeft:整数读FLeft写FLeft;
属性ValueRight:整数读取FRight写入FRight;
属性ValueTop:整数读取FTop写入FTop;
属性ValueBottom:整数读取FBottom写入FBottom;
属性ShowBorder:boolean
读取FShowBorder
写入SetShowBorder default true;
属性KeepAspect:boolean
read FProportional
write SetProportional default true;
属性位图:TBitmap
读取FBitmap
写入Setbitmap;
属性BorderWidth:integer
读取FBorderWidth
写入SetBorderWidth默认值7;
属性ForceRepaint:boolean
读取FForceRepaint
写入FForceRepaint默认值为true;
属性DblClkEnable:boolean
读取FDblClkEnable
写入FDblClkEnable default False;
property Align;
属性宽;
属性高;
属性顶部;
属性左;
属性可见;
属性提示;
属性ShowHint;
结束
程序注册;
实现
//这是基本的创建选项。
构造函数TZImage.Create(AOwner:TComponent);
开始
继承;
FShowBorder:= True;
FBorderWidth:= 7;
FMouse:= mNone;
FForceRepaint:= true; // is true
FDblClkEnable:= False;
FProportional:= true; //是true
宽度:= 100;身高:= 100;
FBitmap:= Tbitmap.Create;
FBitmap.Width:= width;
FBitmap.height:=高度;
ControlStyle:= ControlStyle + [csOpaque];
autosize:= false;
// Scaled:= false;
结束
// basic destroy释放FBitmap
析构函数TZImage.Destroy;
begin
FBitmap.Free;
继承;
结束
//这是一个自定义缩放我用来给自动缩放效果
程序TZimage.zoom(Endleft,EndRight,EndTop,EndBottom:integer); $($)
同时((Endbottom picrect.bottom)或(Endtop<<<<< picrect.top))或((endleft) (endright<> picrect.right))
begin
如果picrect.left> endleft然后
picrect.left:= picrect.left -1;
如果picrect.left< endleft然后//开始
picrect.left:= picrect.left +1;
如果picrect.right>然后//开始
picrect.right:= picrect.right -1;
if picrect.right<直接然后
picrect.right:= picrect.right +1;
如果picrect.top> endtop然后
picrect.top:= picrect.top -1;
如果picrect.top< endtop然后//开始
picrect.top:= picrect.top +1;
如果picrect.bottom> endbottom然后//开始
picrect.bottom:= picrect.bottom -1;
如果picrect.bottom< endbottom然后
picrect.bottom:= picrect.bottom +1;
self.refresh;
结束
end;
//这是我知道的自定义绘画,如果我把
//Canvas.Draw(0,0,FBitmap);因为它显示
//完美,但缩放选项已经过去了,
//我需要缩放。
程序TZImage.Paint;
var buf:TBitmap;
coef,asps,aspp:Double;
sz,a:integer;
begin
buf:= TBitmap.Create;
buf.Width:= Width;
buf.Height:=身高;
如果不是FShowBorder
then ShowRect:= ClientRect
else ShowRect:= Rect(ClientRect.Left,ClientRect.Top,
ClientRect.Right-FBorderWidth,
ClientRect。底部FBorderWidth);
ShowRect:= ClientRect;
与PicRect做开始
如果Right = 0然后右:= FBitmap.Width;
如果Bottom = 0 then Bottom:= FBitmap.Height;
结束
buf.Canvas.CopyMode:= cmSrcCopy;
buf.Canvas.CopyRect(ShowRect,FBitmap.Canvas,PicRect);
Canvas.CopyMode:= cmSrcCopy;
Canvas.Draw(0,0,buf);
buf.Free;
结束
程序TZImage.MouseDown(Button:TMouseButton; Shift:TShiftState;
X,Y:Integer);
begin
//如果mbLeft<>按钮然后退出;
如果不是PtInRect(ShowRect,Point(X,Y))和
不是PtInRect(Rect(ShowRect.Right,ShowRect.Bottom,
宽度,高度),Point(X,Y) )然后退出;
如果PtInRect(Rect(ShowRect.Right,ShowRect.Bottom,
宽度,高度),点(X,Y))然后开始
DblClick;
退出;
结束
//这里点击只在图片区域
startx:= x; oldx:= x;
starty:= y; oldy:= y;
如果mbRight = Button然后开始
MouseCapture:= True;
FMouse:= mZoom;
Canvas.Pen.Mode:= pmNot;
end else begin
FMouse:= mDrag;
Screen.Cursor:= crHandPoint;
结束
结束
函数Min(a,b:integer):integer;
begin
如果一个< b then Result:= a else Result:= b;
结束
函数Max(a,b:integer):integer;
begin
如果一个< b then结果:= b else结果:= a;
结束
程序TZImage.MouseMove(Shift:TShiftState; X,Y:Integer);
var d,s:integer;
coef:Double;
begin
如果FMouse = mNone然后退出;
如果FMouse = mZoom然后开始
Canvas.DrawFocusRect(Rect(Min(startx,oldx),Min(starty,oldy),Max(startx,oldx),Max(starty,oldy)));
oldx:= x; oldy:= y;
Canvas.DrawFocusRect(Rect(Min(startx,oldx),Min(starty,oldy),Max(startx,oldx),Max(starty,oldy)));
结束
如果FMouse = mDrag然后开始
//水平运动
coef:=(PicRect.Right-PicRect.Left)/(ShowRect.Right-ShowRect.Left);
d:= Round(coef *(x-oldx));
s:= PicRect.Right-PicRect.Left;
如果d> 0然后开始
如果PicRect.Left> = d然后开始
PicRect.Left:= PicRect.Left-d;
PicRect.Right:= PicRect.Right-d;
end else begin
PicRect.Left:= 0;
PicRect.Right:= PicRect.Left + s;
结束
结束
如果d< 0然后开始
如果PicRect.Right< FBitmap.Width + d然后开始
PicRect.Left:= PicRect.Left-d;
PicRect.Right:= PicRect.Right-d;
end else begin
PicRect.Right:= FBitmap.Width;
PicRect.Left:= PicRect.Right-s;
结束
结束
//垂直移动
coef:=(PicRect.Bottom-PicRect.Top)/(ShowRect.Bottom-ShowRect.Top);
d:= Round(coef *(y-oldy));
s:= PicRect.Bottom-PicRect.Top;
如果d> 0然后开始
如果PicRect.Top> = d然后开始
PicRect.Top:=PicRect.Topdd;
PicRect.Bottom:= PicRect.Bottom-d;
end else begin
PicRect.Top:=0;
PicRect.Bottom:= PicRect.Top + s;
结束
结束
{下面的片段中有一个错误。感谢所有,谁报告这个bug给我}
如果d< 0然后开始
如果PicRect.Bottom< FBitmap.Height + d然后开始
PicRect.Top:=PicRect.Top- d;
PicRect.Bottom:= PicRect.Bottom-d;
end else begin
PicRect.Bottom:= FBitmap.Height;
PicRect.Top:=PicRect.Bottom-s;
结束
结束
oldx:= x; oldy:= y;
如果FForceRepaint then Repaint
else Invalidate;
结束
结束
程序TZImage.MouseUp(Button:TMouseButton; Shift:TShiftState;
X,Y:Integer);
var coef:Double;
t:integer;
left,right,top,bottom:integer;
begin
如果FMouse = mNone然后退出;
如果x> ShowRect.Right然后x:= ShowRect.Right;
如果y> ShowRect.Bottom则y:= ShowRect.Bottom;
如果FMouse = mZoom然后开始//计算新的PicRect
t:= startx;
startx:= Min(startx,x);
x:= Max(t,x);
t:= starty;
starty:= Min(starty,y);
y:= Max(t,y);
FMouse:= mNone;
MouseCapture:= False;
//如果要通过相反的方向拖动来启用以下命令}
{如果Startx> x然后开始
DblClick;
退出;
end;}
如果Abs(x-startx)< 5 then Exit;
// showmessage('picrect Left ='+ inttostr(picrect.Left)+'right ='+ inttostr(picrect.Right)+'top ='+ inttostr(picrect.Top)+'bottom ='+ inttostr(picrect.Bottom));
// startx并且开始y是从所选区域启动x / y
// x,y是所选区域的结束x / y
if(x - startx< y - starty)然后
begin
while(x - startx< y - starty)do
begin
x:= x + 100;
startx:= startx - 100;
结束
end
else if(x - startx> y - starty)then
begin
while(x - startx> y - starty)do
begin
y:= y + 100;
starty:= starty - 100;
结束
结束
// picrect是整个区域的大小
//PicRect.top和left为0,0
//在v.1.2中添加了IF,以避免零分割
if(PicRect.Right = PicRect.Left)
then
coef:= 100000
else
coef:= ShowRect.Right /(PicRect.Right-PicRect。剩下); //如果新屏幕coef = 1
left:= Round(PicRect.Left + startx / coef);
右:=左+回((x-startx)/ coef);
if(PicRect.Bottom = PicRect.Top)
then
coef:= 100000
else
coef:= ShowRect.Bottom /(PicRect。 Bottom-PicRect.Top);
顶部:= Round(PicRect.Top + starty / coef);
Bottom:= Top + Round((y-starty)/ coef);
// showmessage(inttostr(left)+''+ inttostr(Right)+''+ inttostr(top)+''+ inttostr(bottom));
缩放(左,右,上,下);
ValueLeft:= left;
ValueRight:=对;
ValueTop:= top;
ValueBottom:= bottom;
结束
如果FMouse = mDrag然后开始
FMouse:= mNone;
Canvas.Pen.Mode:= pmCopy;
Screen.Cursor:= crDefault;
结束
无效;
结束
程序TZImage.DblClick;
begin
zoom(0,FBitMap.Width,0,FBitMap.Height);
ValueLeft:= 0;
ValueRight:= FBitMap.Width;
ValueTop:= 0;
ValueBottom:= FBitMap.Height;
//PicRect:=Rect(0,0,FBitmap.Width,FBitmap.Height);
无效;
结束
程序TZImage.SetBitmap(b:TBitmap);
begin
FBitmap.Assign(b);
PicRect:= Rect(0,0,b.Width,b.Height);
无效;
结束
程序TZImage.SetBorderWidth(w:integer);
begin
FBorderWidth:= w;
无效;
结束
程序TZImage.SetShowBorder(s:boolean);
begin
FShowBorder:= s;
无效;
结束
程序TZImage.SetProportional(b:boolean);
begin
FProportional:= b;
无效;
结束
程序注册;
begin
RegisterComponents('Custom',[TZImage]);
结束
结束。
使用此代码,您可以注册组件ZImage,并查看它如何运行..如果需要
问题很清楚,但我认为回答问题的方法是如何重写完整的代码才能为您理解。因为我更好的编码,然后解释,我做了。
我想你正在搜索如下的东西:
unit ZImage2;
接口
使用
Windows,消息,类,控件,图形,StdCtrls,ExtCtrls,Math;
const
DefAnimDuration = 500;
type
TZImage = class(TGraphicControl)
private
FAlignment:TAlignment;
FAnimDuration:红衣主教;
FAnimRect:TRect;
FAnimStartTick:红衣主教;
FAnimTimer:TTimer;
FBuffer:TBitmap;
FCropRect:TRect;
FImgRect:TRect;
FLayout:TTextLayout;
FPicture:TPicture;
FPrevCropRect:TRect;
FProportional:Boolean;
FProportionalCrop:Boolean;
FScale:Single;
FSelColor:TColor;
FSelecting:Boolean;
FSelPoint:TPoint;
FSelRect:TRect;
程序Animate(Sender:TObject);
函数HasGraphic:Boolean;
程序PictureChanged(发件人:TObject);
程序RealignImage;
程序SetAlignment(Value:TAlignment);
procedure SetLayout(Value:TTextLayout);
程序SetPicture(Value:TPicture);
procedure SetProportional(Value:Boolean);
程序UpdateBuffer;
protected
function CanAutoSize(var NewWidth,NewHeight:Integer):Boolean;覆盖
procedure ChangeScale(M:Integer; D:Integer);覆盖
程序DblClick;覆盖
procedure MouseDown(Button:TMouseButton; Shift:TShiftState; X,
Y:Integer);覆盖
procedure MouseMove(Shift:TShiftState; X,Y:Integer);覆盖
procedure MouseUp(Button:TMouseButton; Shift:TShiftState; X,
Y:Integer);覆盖
程序油漆;覆盖
procedure调整大小;覆盖
public
构造函数Create(AOwner:TComponent);覆盖
析构函数覆盖
程序重置;
函数ScreenToGraphic(R:TRect):TRect;
程序缩放(const ACropRect:TRect);
procedure ZoomSelection(const ASelRect:TRect);
发布
属性对齐:TAlignment读取FAlignment写入SetAlignment
默认taLeftJustify;
属性AnimDuration:Cardinal read FAnimDuration write FAnimDuration
default DefAnimDuration;
属性布局:TTextLayout读FLayout写SetLayout默认tlTop;
属性图片:图片读取图片写入SetPicture;
属性比例:布尔读取FProportional write SetProportional
default False;
属性ProportionalCrop:Boolean读取FProportionalCrop
写入FProportionalCrop默认值为True;
属性SelColor:TColor读取FSelColor写入FSelColor默认clWhite;
发布
属性对齐;
属性锚点;
属性AutoSize;
属性颜色;
结束
实现
函数FitRect(const Boundary:TRect; Width,Height:Integer;
CanGrow:Boolean; HorzAlign:TAlignment; VertAlign:TTextLayout):TRect;
var
W:整数;
H:整数;
比例:单
偏移量:TPoint;
begin
宽度:=最大(1,宽度);
高度:= Max(1,Height);
W:= Boundary.Right - Boundary.Left;
H:= Boundary.Bottom - Boundary.Top;
如果CanGrow然后
比例:=最小(宽/高,高/高)
else
比例:=最小(1,最小(宽/高,高/高) );
结果:= Rect(0,0,Round(Width * Scale),Round(Height * Scale));
case HorzAlign
taLeftJustify:
Offset.X:= 0;
taCenter:
Offset.X:=(W - Result.Right)div 2;
taRightJustify:
Offset.X:= W - Result.Right;
结束
case VertAlign
tlTop:
Offset.Y:= 0;
tlCenter:
Offset.Y:=(H - Result.Bottom)div 2;
tlBottom:
Offset.Y:= H - Result.Bottom;
结束
OffsetRect(Result,Boundary.Left + Offset.X,Boundary.Top + Offset.Y);
结束
函数NormalizeRect(const Point1,Point2:TPoint):TRect;
begin
Result.Left:= Min(Point1.X,Point2.X);
Result.Top:= Min(Point1.Y,Point2.Y);
Result.Right:= Max(Point1.X,Point2.X);
Result.Bottom:= Max(Point1.Y,Point2.Y);
结束
{TZImage}
程序TZImage.Animate(Sender:TObject);
var
完成:单个;
begin
完成:=(GetTickCount - FAnimStartTick)/ FAnimDuration;
if Done> = 1.0 then
begin
FAnimTimer.Enabled:= False;
FAnimRect:= FCropRect;
end
else
with FPrevCropRect do
FAnimRect:= Rect(
Left + Round(Done *(FCropRect.Left - Left)),
Top +圆(完成*(FCropRect.Top - 顶部)),
右+回合(完成*(FCropRect.Right - 右)),
底部+回合(完成*(FCropRect.Bottom - 底部) ));
UpdateBuffer;
RealignImage;
无效;
结束
函数TZImage.CanAutoSize(var NewWidth,NewHeight:Integer):Boolean;
begin
结果:= True;
如果没有(csDesigning在ComponentState)或HasGraphic然后
开始
如果对齐[alNone,alLeft,alRight]然后
NewWidth:= Round(FScale * FPicture.Width);
如果对齐[alNone,alTop,alBottom]然后
NewHeight:= Round(FScale * FPicture.Height);
结束
结束
procedure TZImage.ChangeScale(M,D:Integer);
var
SaveAnchors:TAnchors
begin
SaveAnchors:= Anchors;
Anchors:= [akLeft,akTop];
FScale:= FScale * M / D;
继承ChangeScale(M,D);
Anchors:= SaveAnchors;
结束
构造函数TZImage.Create(AOwner:TComponent);
begin
继承Create(AOwner);
ControlStyle:= [csCaptureMouse,csClickEvents,csOpaque,csDoubleClicks];
FAnimTimer:= TTimer.Create(Self);
FAnimTimer.Interval:= 15;
FAnimTimer.OnTimer:= Animate;
FAnimDuration:= DefAnimDuration;
FBuffer:= TBitmap.Create;
FPicture:= TPicture.Create;
FPicture.OnChange:= PictureChanged;
FProportionalCrop:= True;
FScale:= 1.0;
FSelColor:= clWhite;
结束
程序TZImage.DblClick;
begin
如果不是HasGraphic然后
重置
else
缩放(Rect(0,0,FPicture.Width,FPicture.Height));
继承DblClick;
结束
析构函数TZImage.Destroy;
begin
FPicture.Free;
FBuffer.Free;
继承了Destroy;
结束
函数TZImage.HasGraphic:Boolean;
begin
结果:=(Picture.Width> 0)和(Picture.Height> 0);
结束
程序TZImage.MouseDown(Button:TMouseButton; Shift:TShiftState; X,
Y:Integer);
begin
if(Button = mbRight)和HasGraphic和PtInRect(FImgRect,Point(X,Y))然后
begin
FSelPoint.X:= X;
FSelPoint.Y:= Y;
FSelRect:= Rect(X,Y,X,Y);
FS选择:= True;
Canvas.Brush.Color:= FSelColor;
Canvas.DrawFocusRect(FSelRect);
结束
继承了MouseDown(Button,Shift,X,Y);
结束
程序TZImage.MouseMove(Shift:TShiftState; X,Y:Integer);
const
HorzAlign:数组[Boolean]的TAlignment =(taLeftJustify,taRightJustify);
VertAlign:TTextLayout =(tlTop,tlBottom)的数组[Boolean];
begin
如果选择和PtInRect(FImgRect,Point(X,Y))然后
begin
Canvas.DrawFocusRect(FSelRect);
FSelRect:= NormalizeRect(FSelPoint,Point(X,Y));
if(not FProportionalCrop)then
FSelRect:= FitRect(FSelRect,FPicture.Graphic.Width,
FPicture.Graphic.Height,True,HorzAlign [X< FSelPoint.X],
VertAlign [Y< FSelPoint.Y]);
Canvas.DrawFocusRect(FSelRect);
结束
继承MouseMove(Shift,X,Y);
结束
程序TZImage.MouseUp(Button:TMouseButton; Shift:TShiftState; X,
Y:Integer);
开始
如果选择然后
开始
FS选择:= False;
Canvas.DrawFocusRect(FSelRect);
if(Abs(X - FSelPoint.X)> Mouse.DragThreshold)或
(Abs(Y - FSelPoint.Y)> Mouse.DragThreshold)然后
ZoomSelection(FSelRect);
结束
继承MouseUp(Button,Shift,X,Y);
结束
程序TZImage.Paint;
begin
Canvas.Brush.Color:=颜色;
如果HasGraphic然后
开始
Canvas.StretchDraw(FImgRect,FBuffer);
如果选择然后
Canvas.DrawFocusRect(FSelRect);
与FImgRect do
ExcludeClipRect(Canvas.Handle,Left,Top,Right,Bottom);
结束
Canvas.FillRect(Canvas.ClipRect);
结束
程序TZImage.PictureChanged(发件人:TObject);
begin
重置;
结束
程序TZImage.RealignImage;
begin
如果不是HasGraphic然后
FImgRect:= Rect(0,0,0,0)
如果FProportional然后
FImgRect:= ClientRect
else
FImgRect:= FitRect(ClientRect,FBuffer.Width,FBuffer.Height,True,
FAlignment,FLayout);
结束
程序TZImage.Reset;
begin
FCropRect:= Rect(0,0,FPicture.Width,FPicture.Height);
FAnimRect:= FCropRect;
UpdateBuffer;
RealignImage;
无效;
结束
程序TZImage.Resize;
begin
RealignImage;
继承了Resize;
结束
函数TZImage.ScreenToGraphic(R:TRect):TRect;
var
CropWidth:Integer;
CropHeight:整数;
ImgWidth:Integer;
ImgHeight:整数;
begin
CropWidth:= FCropRect.Right - FCropRect.Left;
CropHeight:= FCropRect.Bottom - FCropRect.Top;
ImgWidth:= FImgRect.Right - FImgRect.Left;
ImgHeight:= FImgRect.Bottom - FImgRect.Top;
IntersectRect(R,R,FImgRect);
OffsetRect(R,-FImgRect.Left,-FImgRect.Top);
结果:= Rect(
FCropRect.Left + Round(CropWidth *(R.Left / ImgWidth)),
FCropRect.Top + Round(CropHeight *(R.Top / ImgHeight)) ,
FCropRect.Left + Round(CropWidth *(R.Right / ImgWidth)),
FCropRect.Top + Round(CropHeight *(R.Bottom / ImgHeight)));
结束
程序TZImage.SetAlignment(Value:TAlignment);
begin
如果FAlignment<>值然后
begin
FAlignment:= Value;
RealignImage;
无效;
结束
结束
程序TZImage.SetLayout(值:TTextLayout);
begin
如果FLayout<>值
开始
FLayout:= Value;
RealignImage;
无效;
结束
结束
程序TZImage.SetPicture(Value:TPicture);
begin
FPicture.Assign(Value);
结束
程序TZImage.SetProportional(Value:Boolean);
begin
如果FProportional<>值然后
begin
FProportional:= Value;
RealignImage;
无效;
结束
结束
程序TZImage.UpdateBuffer;
begin
如果HasGraphic然后
开始
FBuffer.Width:= FAnimRect.Right - FAnimRect.Left;
FBuffer.Height:= FAnimRect.Bottom - FAnimRect.Top;
FBuffer.Canvas.Draw(-FAnimRect.Left,-FAnimRect.Top,FPicture.Graphic);
结束
结束
程序TZImage.Zoom(const ACropRect:TRect);
begin
如果HasGraphic然后
开始
FPrevCropRect:= FAnimRect;
FCropRect:= ACropRect;
如果FAnimDuration = 0,那么
begin
FAnimRect:= FCropRect;
UpdateBuffer;
RealignImage;
无效;
end
else
begin
FAnimStartTick:= GetTickCount;
FAnimTimer.Enabled:= True;
结束
结束
结束
程序TZImage.ZoomSelection(const ASelRect:TRect);
begin
缩放(ScreenToGraphic(ASelRect));
结束
结束。
示例代码:
procedure TForm1.FormCreate(Sender:TObject);
begin
FImage:= TZImage.Create(Self);
FImage.SetBounds(10,10,200,300);
FImage.Picture.LoadFromFile('D:\Pictures\Mona_Lisa.jpg');
FImage.Alignment:= taCenter;
FImage.Layout:= tlCenter;
FImage.AutoSize:= True;
FImage.Parent:=自我;
结束
Well this is my goal. Use left mouse button to scroll the image, right mouse button to choose zoom rectangle and doubleclick to restore full zoom.
I have currently tired, so far found its NOT to do with the way i load the images or display the image but something with how it paints. The on-screen image always fills the control's client area regardless of the shape of the form or the source image, so the aspect ratio cannot possibly be preserved. I am not sure how to change this or keep the aspect ratio. Thus giving me a clean nice picture.
I am posting the whole code for my ZImage unit Though i think the problem is either in the Zimage.paint or Zimage.mouseup But figured if you needed to see a function inside one of those it would help to have it all posted.
unit ZImage;
interface
uses
Windows, Messages, SysUtils,jpeg, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls;
type
TZImage = class(TGraphicControl)
private
FBitmap : Tbitmap;
PicRect : TRect;
ShowRect : TRect;
FShowBorder : boolean;
FBorderWidth : integer;
FForceRepaint : boolean;
FMouse : (mNone, mDrag, mZoom);
FProportional : boolean;
FDblClkEnable : boolean;
FLeft :integer;
FRight :integer;
FTop :integer;
FBottom :integer;
startx, starty,
oldx, oldy : integer;
procedure SetShowBorder(s:boolean);
procedure SetBitmap(b:TBitmap);
procedure SetBorderWidth(w:integer);
procedure SetProportional(b:boolean);
protected
procedure Paint; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer); override;
public
constructor Create(AOwner:TComponent); override;
destructor Destroy; override;
procedure DblClick; override;
published
procedure zoom(Endleft,EndRight,EndTop,EndBottom:integer);
property ValueLeft : integer read FLeft write FLeft;
property ValueRight : Integer read FRight write FRight;
Property ValueTop : Integer read FTop write FTop;
Property ValueBottom : Integer read FBottom write FBottom;
property ShowBorder : boolean
read FShowBorder
write SetShowBorder default true;
property KeepAspect : boolean
read FProportional
write SetProportional default true;
property Bitmap : TBitmap
read FBitmap
write Setbitmap;
property BorderWidth : integer
read FBorderWidth
write SetBorderWidth default 7;
property ForceRepaint : boolean
read FForceRepaint
write FForceRepaint default true;
property DblClkEnable : boolean
read FDblClkEnable
write FDblClkEnable default False;
property Align;
property Width;
property Height;
property Top;
property Left;
property Visible;
property Hint;
property ShowHint;
end;
procedure Register;
implementation
//This is the basic create options.
constructor TZImage.Create(AOwner:TComponent);
begin
inherited;
FShowBorder:=True;
FBorderWidth:=7;
FMouse:=mNone;
FForceRepaint:=true; //was true
FDblClkEnable:=False;
FProportional:=true; //was true
Width:=100; Height:=100;
FBitmap:=Tbitmap.Create;
FBitmap.Width:=width;
FBitmap.height:=Height;
ControlStyle:=ControlStyle+[csOpaque];
autosize:= false;
//Scaled:=false;
end;
//basic destroy frees the FBitmap
destructor TZImage.Destroy;
begin
FBitmap.Free;
inherited;
end;
//This was a custom zoom i was using to give the automated zoom effect
procedure TZimage.zoom(Endleft,EndRight,EndTop,EndBottom:integer);
begin
while ((Endbottom <> picrect.bottom) or (Endtop <> picrect.top)) or ((endleft <> picrect.left) or (endright <> picrect.right)) do
begin
if picrect.left > endleft then
picrect.left := picrect.left -1;
if picrect.left < endleft then //starting
picrect.left := picrect.left +1;
if picrect.right > endright then //starting
picrect.right := picrect.right -1;
if picrect.right < endright then
picrect.right := picrect.right +1;
if picrect.top > endtop then
picrect.top := picrect.top -1;
if picrect.top < endtop then //starting
picrect.top := picrect.top +1;
if picrect.bottom > endbottom then //starting
picrect.bottom := picrect.bottom -1;
if picrect.bottom < endbottom then
picrect.bottom := picrect.bottom +1;
self.refresh;
end;
end;
//this is the custom paint I know if i put
//Canvas.Draw(0,0,FBitmap); as the methond it displays
//perfect but the zoom option is gone of course and
//i need the Zoom.
procedure TZImage.Paint;
var buf:TBitmap;
coef,asps,aspp:Double;
sz,a : integer;
begin
buf:=TBitmap.Create;
buf.Width:=Width;
buf.Height:=Height;
if not FShowBorder
then ShowRect:=ClientRect
else ShowRect:=Rect(ClientRect.Left,ClientRect.Top,
ClientRect.Right-FBorderWidth,
ClientRect.Bottom-FBorderWidth);
ShowRect:=ClientRect;
with PicRect do begin
if Right=0 then Right:=FBitmap.Width;
if Bottom=0 then Bottom:=FBitmap.Height;
end;
buf.Canvas.CopyMode:=cmSrcCopy;
buf.Canvas.CopyRect(ShowRect,FBitmap.Canvas,PicRect);
Canvas.CopyMode:=cmSrcCopy;
Canvas.Draw(0,0,buf);
buf.Free;
end;
procedure TZImage.MouseDown(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
begin
// if mbLeft<>Button then Exit;
if not PtInRect(ShowRect,Point(X,Y)) and
not PtInRect(Rect(ShowRect.Right,ShowRect.Bottom,
Width,Height),Point(X,Y)) then Exit;
if PtInRect(Rect(ShowRect.Right,ShowRect.Bottom,
Width,Height),Point(X,Y)) then begin
DblClick;
Exit;
end;
//here click is in the picture area only
startx:=x; oldx:=x;
starty:=y; oldy:=y;
if mbRight=Button then begin
MouseCapture:=True;
FMouse:=mZoom;
Canvas.Pen.Mode:=pmNot;
end else begin
FMouse:=mDrag;
Screen.Cursor:=crHandPoint;
end;
end;
function Min(a,b:integer):integer;
begin
if a<b then Result:=a else Result:=b;
end;
function Max(a,b:integer):integer;
begin
if a<b then Result:=b else Result:=a;
end;
procedure TZImage.MouseMove(Shift: TShiftState; X, Y: Integer);
var d,s:integer;
coef:Double;
begin
if FMouse=mNone then Exit;
if FMouse=mZoom then begin
Canvas.DrawFocusRect(Rect(Min(startx,oldx),Min(starty,oldy),Max(startx,oldx),Max(starty,oldy)));
oldx:=x; oldy:=y;
Canvas.DrawFocusRect(Rect(Min(startx,oldx),Min(starty,oldy),Max(startx,oldx),Max(starty,oldy)));
end;
if FMouse=mDrag then begin
//horizontal movement
coef:=(PicRect.Right-PicRect.Left)/(ShowRect.Right-ShowRect.Left);
d:=Round(coef*(x-oldx));
s:=PicRect.Right-PicRect.Left;
if d>0 then begin
if PicRect.Left>=d then begin
PicRect.Left:=PicRect.Left-d;
PicRect.Right:=PicRect.Right-d;
end else begin
PicRect.Left:=0;
PicRect.Right:=PicRect.Left+s;
end;
end;
if d<0 then begin
if PicRect.Right<FBitmap.Width+d then begin
PicRect.Left:=PicRect.Left-d;
PicRect.Right:=PicRect.Right-d;
end else begin
PicRect.Right:=FBitmap.Width;
PicRect.Left:=PicRect.Right-s;
end;
end;
//vertical movement
coef:=(PicRect.Bottom-PicRect.Top)/(ShowRect.Bottom-ShowRect.Top);
d:=Round(coef*(y-oldy));
s:=PicRect.Bottom-PicRect.Top;
if d>0 then begin
if PicRect.Top>=d then begin
PicRect.Top:=PicRect.Top-d;
PicRect.Bottom:=PicRect.Bottom-d;
end else begin
PicRect.Top:=0;
PicRect.Bottom:=PicRect.Top+s;
end;
end;
{There was a bug in the fragment below. Thanks to all, who reported this bug to me}
if d<0 then begin
if PicRect.Bottom<FBitmap.Height+d then begin
PicRect.Top:=PicRect.Top-d;
PicRect.Bottom:=PicRect.Bottom-d;
end else begin
PicRect.Bottom:=FBitmap.Height;
PicRect.Top:=PicRect.Bottom-s;
end;
end;
oldx:=x; oldy:=y;
if FForceRepaint then Repaint
else Invalidate;
end;
end;
procedure TZImage.MouseUp(Button: TMouseButton; Shift: TShiftState;
X, Y: Integer);
var coef:Double;
t:integer;
left,right,top,bottom : integer;
begin
if FMouse=mNone then Exit;
if x>ShowRect.Right then x:=ShowRect.Right;
if y>ShowRect.Bottom then y:=ShowRect.Bottom;
if FMouse=mZoom then begin //calculate new PicRect
t:=startx;
startx:=Min(startx,x);
x:=Max(t,x);
t:=starty;
starty:=Min(starty,y);
y:=Max(t,y);
FMouse:=mNone;
MouseCapture:=False;
//enable the following if you want to zoom-out by dragging in the opposite direction}
{ if Startx>x then begin
DblClick;
Exit;
end;}
if Abs(x-startx)<5 then Exit;
//showmessage('picrect Left='+inttostr(picrect.Left)+' right='+inttostr(picrect.Right)+' top='+inttostr(picrect.Top)+' bottom='+inttostr(picrect.Bottom));
//startx and start y is teh starting x/y of the selected area
//x and y is the ending x/y of the selected area
if (x - startx < y - starty) then
begin
while (x - startx < y - starty) do
begin
x := x + 100;
startx := startx - 100;
end;
end
else if (x - startx > y - starty) then
begin
while (x - startx > y - starty) do
begin
y := y + 100;
starty := starty - 100;
end;
end;
//picrect is the size of whole area
//PicRect.top and left are 0,0
//IFs were added in v.1.2 to avoid zero-divide
if (PicRect.Right=PicRect.Left)
then
coef := 100000
else
coef:=ShowRect.Right/(PicRect.Right-PicRect.Left); //if new screen coef= 1
left:=Round(PicRect.Left+startx/coef);
Right:=Left+Round((x-startx)/coef);
if (PicRect.Bottom=PicRect.Top)
then
coef := 100000
else
coef:=ShowRect.Bottom/(PicRect.Bottom-PicRect.Top);
Top:=Round(PicRect.Top+starty/coef);
Bottom:=Top+Round((y-starty)/coef);
//showmessage(inttostr(left)+' '+inttostr(Right)+' '+inttostr(top)+' '+inttostr(bottom));
zoom(left,right,top,bottom);
ValueLeft := left;
ValueRight := Right;
ValueTop := top;
ValueBottom := bottom;
end;
if FMouse=mDrag then begin
FMouse:=mNone;
Canvas.Pen.Mode:=pmCopy;
Screen.Cursor:=crDefault;
end;
Invalidate;
end;
procedure TZImage.DblClick;
begin
zoom(0,FBitMap.Width,0,FBitMap.Height);
ValueLeft := 0;
ValueRight := FBitMap.Width;
ValueTop := 0;
ValueBottom := FBitMap.Height;
//PicRect:=Rect(0,0,FBitmap.Width,FBitmap.Height);
Invalidate;
end;
procedure TZImage.SetBitmap(b:TBitmap);
begin
FBitmap.Assign(b);
PicRect:=Rect(0,0,b.Width, b.Height);
Invalidate;
end;
procedure TZImage.SetBorderWidth(w:integer);
begin
FBorderWidth:=w;
Invalidate;
end;
procedure TZImage.SetShowBorder(s:boolean);
begin
FShowBorder:=s;
Invalidate;
end;
procedure TZImage.SetProportional(b:boolean);
begin
FProportional:=b;
Invalidate;
end;
procedure Register;
begin
RegisterComponents('Custom', [TZImage]);
end;
end.
With this code you can register the componet ZImage and see how it runs.. if needed
The question is clear, but I think the problem answering it is how not to rewrite the complete code to be understandable for you. And since I am better at coding then explaining, I did.
I think you are searching for something like the following:
unit ZImage2;
interface
uses
Windows, Messages, Classes, Controls, Graphics, StdCtrls, ExtCtrls, Math;
const
DefAnimDuration = 500;
type
TZImage = class(TGraphicControl)
private
FAlignment: TAlignment;
FAnimDuration: Cardinal;
FAnimRect: TRect;
FAnimStartTick: Cardinal;
FAnimTimer: TTimer;
FBuffer: TBitmap;
FCropRect: TRect;
FImgRect: TRect;
FLayout: TTextLayout;
FPicture: TPicture;
FPrevCropRect: TRect;
FProportional: Boolean;
FProportionalCrop: Boolean;
FScale: Single;
FSelColor: TColor;
FSelecting: Boolean;
FSelPoint: TPoint;
FSelRect: TRect;
procedure Animate(Sender: TObject);
function HasGraphic: Boolean;
procedure PictureChanged(Sender: TObject);
procedure RealignImage;
procedure SetAlignment(Value: TAlignment);
procedure SetLayout(Value: TTextLayout);
procedure SetPicture(Value: TPicture);
procedure SetProportional(Value: Boolean);
procedure UpdateBuffer;
protected
function CanAutoSize(var NewWidth, NewHeight: Integer): Boolean; override;
procedure ChangeScale(M: Integer; D: Integer); override;
procedure DblClick; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer); override;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); override;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer); override;
procedure Paint; override;
procedure Resize; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure Reset;
function ScreenToGraphic(R: TRect): TRect;
procedure Zoom(const ACropRect: TRect);
procedure ZoomSelection(const ASelRect: TRect);
published
property Alignment: TAlignment read FAlignment write SetAlignment
default taLeftJustify;
property AnimDuration: Cardinal read FAnimDuration write FAnimDuration
default DefAnimDuration;
property Layout: TTextLayout read FLayout write SetLayout default tlTop;
property Picture: TPicture read FPicture write SetPicture;
property Proportional: Boolean read FProportional write SetProportional
default False;
property ProportionalCrop: Boolean read FProportionalCrop
write FProportionalCrop default True;
property SelColor: TColor read FSelColor write FSelColor default clWhite;
published
property Align;
property Anchors;
property AutoSize;
property Color;
end;
implementation
function FitRect(const Boundary: TRect; Width, Height: Integer;
CanGrow: Boolean; HorzAlign: TAlignment; VertAlign: TTextLayout): TRect;
var
W: Integer;
H: Integer;
Scale: Single;
Offset: TPoint;
begin
Width := Max(1, Width);
Height := Max(1, Height);
W := Boundary.Right - Boundary.Left;
H := Boundary.Bottom - Boundary.Top;
if CanGrow then
Scale := Min(W / Width, H / Height)
else
Scale := Min(1, Min(W / Width, H / Height));
Result := Rect(0, 0, Round(Width * Scale), Round(Height * Scale));
case HorzAlign of
taLeftJustify:
Offset.X := 0;
taCenter:
Offset.X := (W - Result.Right) div 2;
taRightJustify:
Offset.X := W - Result.Right;
end;
case VertAlign of
tlTop:
Offset.Y := 0;
tlCenter:
Offset.Y := (H - Result.Bottom) div 2;
tlBottom:
Offset.Y := H - Result.Bottom;
end;
OffsetRect(Result, Boundary.Left + Offset.X, Boundary.Top + Offset.Y);
end;
function NormalizeRect(const Point1, Point2: TPoint): TRect;
begin
Result.Left := Min(Point1.X, Point2.X);
Result.Top := Min(Point1.Y, Point2.Y);
Result.Right := Max(Point1.X, Point2.X);
Result.Bottom := Max(Point1.Y, Point2.Y);
end;
{ TZImage }
procedure TZImage.Animate(Sender: TObject);
var
Done: Single;
begin
Done := (GetTickCount - FAnimStartTick) / FAnimDuration;
if Done >= 1.0 then
begin
FAnimTimer.Enabled := False;
FAnimRect := FCropRect;
end
else
with FPrevCropRect do
FAnimRect := Rect(
Left + Round(Done * (FCropRect.Left - Left)),
Top + Round(Done * (FCropRect.Top - Top)),
Right + Round(Done * (FCropRect.Right - Right)),
Bottom + Round(Done * (FCropRect.Bottom - Bottom)));
UpdateBuffer;
RealignImage;
Invalidate;
end;
function TZImage.CanAutoSize(var NewWidth, NewHeight: Integer): Boolean;
begin
Result := True;
if not (csDesigning in ComponentState) or HasGraphic then
begin
if Align in [alNone, alLeft, alRight] then
NewWidth := Round(FScale * FPicture.Width);
if Align in [alNone, alTop, alBottom] then
NewHeight := Round(FScale * FPicture.Height);
end;
end;
procedure TZImage.ChangeScale(M, D: Integer);
var
SaveAnchors: TAnchors;
begin
SaveAnchors := Anchors;
Anchors := [akLeft, akTop];
FScale := FScale * M / D;
inherited ChangeScale(M, D);
Anchors := SaveAnchors;
end;
constructor TZImage.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := [csCaptureMouse, csClickEvents, csOpaque, csDoubleClicks];
FAnimTimer := TTimer.Create(Self);
FAnimTimer.Interval := 15;
FAnimTimer.OnTimer := Animate;
FAnimDuration := DefAnimDuration;
FBuffer := TBitmap.Create;
FPicture := TPicture.Create;
FPicture.OnChange := PictureChanged;
FProportionalCrop := True;
FScale := 1.0;
FSelColor := clWhite;
end;
procedure TZImage.DblClick;
begin
if not HasGraphic then
Reset
else
Zoom(Rect(0, 0, FPicture.Width, FPicture.Height));
inherited DblClick;
end;
destructor TZImage.Destroy;
begin
FPicture.Free;
FBuffer.Free;
inherited Destroy;
end;
function TZImage.HasGraphic: Boolean;
begin
Result := (Picture.Width > 0) and (Picture.Height > 0);
end;
procedure TZImage.MouseDown(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
if (Button = mbRight) and HasGraphic and PtInRect(FImgRect, Point(X, Y)) then
begin
FSelPoint.X := X;
FSelPoint.Y := Y;
FSelRect := Rect(X, Y, X, Y);
FSelecting := True;
Canvas.Brush.Color := FSelColor;
Canvas.DrawFocusRect(FSelRect);
end;
inherited MouseDown(Button, Shift, X, Y);
end;
procedure TZImage.MouseMove(Shift: TShiftState; X, Y: Integer);
const
HorzAlign: array[Boolean] of TAlignment = (taLeftJustify, taRightJustify);
VertAlign: array[Boolean] of TTextLayout = (tlTop, tlBottom);
begin
if FSelecting and PtInRect(FImgRect, Point(X, Y)) then
begin
Canvas.DrawFocusRect(FSelRect);
FSelRect := NormalizeRect(FSelPoint, Point(X, Y));
if (not FProportionalCrop) then
FSelRect := FitRect(FSelRect, FPicture.Graphic.Width,
FPicture.Graphic.Height, True, HorzAlign[X < FSelPoint.X],
VertAlign[Y < FSelPoint.Y]);
Canvas.DrawFocusRect(FSelRect);
end;
inherited MouseMove(Shift, X, Y);
end;
procedure TZImage.MouseUp(Button: TMouseButton; Shift: TShiftState; X,
Y: Integer);
begin
if FSelecting then
begin
FSelecting := False;
Canvas.DrawFocusRect(FSelRect);
if (Abs(X - FSelPoint.X) > Mouse.DragThreshold) or
(Abs(Y - FSelPoint.Y) > Mouse.DragThreshold) then
ZoomSelection(FSelRect);
end;
inherited MouseUp(Button, Shift, X, Y);
end;
procedure TZImage.Paint;
begin
Canvas.Brush.Color := Color;
if HasGraphic then
begin
Canvas.StretchDraw(FImgRect, FBuffer);
if FSelecting then
Canvas.DrawFocusRect(FSelRect);
with FImgRect do
ExcludeClipRect(Canvas.Handle, Left, Top, Right, Bottom);
end;
Canvas.FillRect(Canvas.ClipRect);
end;
procedure TZImage.PictureChanged(Sender: TObject);
begin
Reset;
end;
procedure TZImage.RealignImage;
begin
if not HasGraphic then
FImgRect := Rect(0, 0, 0, 0)
else if FProportional then
FImgRect := ClientRect
else
FImgRect := FitRect(ClientRect, FBuffer.Width, FBuffer.Height, True,
FAlignment, FLayout);
end;
procedure TZImage.Reset;
begin
FCropRect := Rect(0, 0, FPicture.Width, FPicture.Height);
FAnimRect := FCropRect;
UpdateBuffer;
RealignImage;
Invalidate;
end;
procedure TZImage.Resize;
begin
RealignImage;
inherited Resize;
end;
function TZImage.ScreenToGraphic(R: TRect): TRect;
var
CropWidth: Integer;
CropHeight: Integer;
ImgWidth: Integer;
ImgHeight: Integer;
begin
CropWidth := FCropRect.Right - FCropRect.Left;
CropHeight := FCropRect.Bottom - FCropRect.Top;
ImgWidth := FImgRect.Right - FImgRect.Left;
ImgHeight := FImgRect.Bottom - FImgRect.Top;
IntersectRect(R, R, FImgRect);
OffsetRect(R, -FImgRect.Left, -FImgRect.Top);
Result := Rect(
FCropRect.Left + Round(CropWidth * (R.Left / ImgWidth)),
FCropRect.Top + Round(CropHeight * (R.Top / ImgHeight)),
FCropRect.Left + Round(CropWidth * (R.Right / ImgWidth)),
FCropRect.Top + Round(CropHeight * (R.Bottom / ImgHeight)));
end;
procedure TZImage.SetAlignment(Value: TAlignment);
begin
if FAlignment <> Value then
begin
FAlignment := Value;
RealignImage;
Invalidate;
end;
end;
procedure TZImage.SetLayout(Value: TTextLayout);
begin
if FLayout <> Value then
begin
FLayout := Value;
RealignImage;
Invalidate;
end;
end;
procedure TZImage.SetPicture(Value: TPicture);
begin
FPicture.Assign(Value);
end;
procedure TZImage.SetProportional(Value: Boolean);
begin
if FProportional <> Value then
begin
FProportional := Value;
RealignImage;
Invalidate;
end;
end;
procedure TZImage.UpdateBuffer;
begin
if HasGraphic then
begin
FBuffer.Width := FAnimRect.Right - FAnimRect.Left;
FBuffer.Height := FAnimRect.Bottom - FAnimRect.Top;
FBuffer.Canvas.Draw(-FAnimRect.Left, -FAnimRect.Top, FPicture.Graphic);
end;
end;
procedure TZImage.Zoom(const ACropRect: TRect);
begin
if HasGraphic then
begin
FPrevCropRect := FAnimRect;
FCropRect := ACropRect;
if FAnimDuration = 0 then
begin
FAnimRect := FCropRect;
UpdateBuffer;
RealignImage;
Invalidate;
end
else
begin
FAnimStartTick := GetTickCount;
FAnimTimer.Enabled := True;
end;
end;
end;
procedure TZImage.ZoomSelection(const ASelRect: TRect);
begin
Zoom(ScreenToGraphic(ASelRect));
end;
end.
Sample code:
procedure TForm1.FormCreate(Sender: TObject);
begin
FImage := TZImage.Create(Self);
FImage.SetBounds(10, 10, 200, 300);
FImage.Picture.LoadFromFile('D:\Pictures\Mona_Lisa.jpg');
FImage.Alignment := taCenter;
FImage.Layout := tlCenter;
FImage.AutoSize := True;
FImage.Parent := Self;
end;
这篇关于如何正确缩放宽高比的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!