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:PicturesMona_Lisa.jpg');
FImage.Alignment := taCenter;
FImage.Layout := tlCenter;
FImage.AutoSize := True;
FImage.Parent := Self;
end;