这是我的目标。
使用鼠标左键滚动图像,鼠标右键
选择缩放矩形并双击恢复完全缩放。
使用鼠标左键滚动图像,鼠标右键
选择缩放矩形并双击恢复完全缩放。
我现在已经累了,到目前为止,发现它不是用我加载图像或显示图像的方式,而是与它的颜色有关。屏幕图像始终填充控件的客户区,无论窗体或源图像的形状如何,因此无法保留宽高比。我不知道如何改变这个或保持长宽比。所以给我一个干净漂亮的照片。
我正在发布我的ZImage单元的整个代码虽然我认为问题是在Zimage.paint或Zimage.mouseup但是想到如果你需要看到其中一个功能,它将有助于它的所有发布。
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,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,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,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),Y)) then Exit; if PtInRect(Rect(ShowRect.Right,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,Max(starty,oldy))); oldx:=x; oldy:=y; Canvas.DrawFocusRect(Rect(Min(startx,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,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,FBitMap.Height); ValueLeft := 0; ValueRight := FBitMap.Width; ValueTop := 0; ValueBottom := FBitMap.Height; //PicRect:=Rect(0,FBitmap.Width,FBitmap.Height); Invalidate; end; procedure TZImage.SetBitmap(b:TBitmap); begin FBitmap.Assign(b); PicRect:=Rect(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.
解决方法
问题很清楚,但我认为回答问题是如何重写完整的代码是不可理解的。因为我更好的编码然后解释,我做到了。
我想你正在寻找如下的东西:
unit ZImage2; interface uses Windows,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 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,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,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,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,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,Y)) then begin Canvas.DrawFocusRect(FSelRect); FSelRect := NormalizeRect(FSelPoint,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,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,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) else if FProportional then FImgRect := ClientRect else FImgRect := FitRect(ClientRect,FBuffer.Width,FBuffer.Height,FAlignment,FLayout); end; procedure TZImage.Reset; begin FCropRect := Rect(0,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.
示例代码:
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;