Sto facendo un semplice controllo basato su un TScrollingWinControl
(e un codice copiato da un TScrollBox
) con un controllo TImage
. Ho in qualche modo ottenuto lo zoom per funzionare, ma non necessariamente lo zoom su un punto focalizzato - le barre di scorrimento non cambiano di conseguenza per mantenere il punto centrale a fuoco.Ingrandimento/riduzione di una tImmagine all'interno di un TScrollBox per un particolare focus?
Vorrei essere in grado di dire a questo controllo ZoomTo(const X, Y, ZoomBy: Integer);
di indicare dove ingrandire lo stato attivo. Quindi quando zooma, le coordinate che ho passato rimarranno 'centrate'. Allo stesso tempo, ho anche bisogno di avere un ZoomBy(const ZoomBy: Integer);
che gli dica di tenerlo centrato nella vista corrente.
Ad esempio, ci sarà uno scenario in cui il mouse è puntato su un particolare punto dell'immagine, e quando si tiene premuto il controllo e si scorre il mouse verso l'alto, lo zoom deve essere focalizzato sul puntatore del mouse. D'altra parte, un altro scenario sarebbe lo scorrimento di un controllo per regolare il livello di zoom, nel qual caso è sufficiente mantenere focalizzato il centro della vista corrente (non necessariamente il centro dell'immagine).
Il problema è che la mia matematica si perde a questo punto e non riesco a capire la formula giusta per regolare queste barre di scorrimento. Ho provato diversi metodi di calcolo, niente sembra funzionare correttamente.
Ecco una versione ridotta del mio controllo. Ho rimosso la maggior parte solo per il materiale pertinente, l'unità originale ha oltre 600 righe di codice. La procedura più importante qui di seguito è SetZoom(const Value: Integer);
unit JD.Imaging;
interface
uses
Windows, Classes, SysUtils, Graphics, Jpeg, PngImage, Controls, Forms,
ExtCtrls, Messages;
type
TJDImageBox = class;
TJDImageZoomEvent = procedure(Sender: TObject; const Zoom: Integer) of object;
TJDImageBox = class(TScrollingWinControl)
private
FZoom: Integer; //level of zoom by percentage
FPicture: TImage; //displays image within scroll box
FOnZoom: TJDImageZoomEvent; //called when zoom occurs
FZoomBy: Integer; //amount to zoom by (in pixels)
procedure MouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
procedure SetZoom(const Value: Integer);
procedure SetZoomBy(const Value: Integer);
public
constructor Create(AOwner: TComponent); override;
published
property Zoom: Integer read FZoom write SetZoom;
property ZoomBy: Integer read FZoomBy write SetZoomBy;
property OnZoom: TJDImageZoomEvent read FOnZoom write FOnZoom;
end;
implementation
{ TJDImageBox }
constructor TJDImageBox.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
OnMouseWheel:= MouseWheel;
ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
csSetCaption, csDoubleClicks, csPannable, csGestures];
AutoScroll := True;
TabStop:= True;
VertScrollBar.Tracking:= True;
HorzScrollBar.Tracking:= True;
Width:= 100;
Height:= 100;
FPicture:= TImage.Create(nil);
FPicture.Parent:= Self;
FPicture.AutoSize:= False;
FPicture.Stretch:= True;
FPicture.Proportional:= True;
FPicture.Left:= 0;
FPicture.Top:= 0;
FPicture.Width:= 1;
FPicture.Height:= 1;
FPicture.Visible:= False;
FZoom:= 100;
FZoomBy:= 10;
end;
destructor TJDImageBox.Destroy;
begin
FImage.Free;
FPicture.Free;
inherited;
end;
procedure TJDImageBox.MouseWheel(Sender: TObject; Shift: TShiftState;
WheelDelta: Integer; MousePos: TPoint; var Handled: Boolean);
var
NewScrollPos: Integer;
begin
if ssCtrl in Shift then begin
if WheelDelta > 0 then
NewScrollPos := Zoom + 5
else
NewScrollPos:= Zoom - 5;
if NewScrollPos >= 5 then
Zoom:= NewScrollPos;
end else
if ssShift in Shift then begin
NewScrollPos := HorzScrollBar.Position - WheelDelta;
HorzScrollBar.Position := NewScrollPos;
end else begin
NewScrollPos := VertScrollBar.Position - WheelDelta;
VertScrollBar.Position := NewScrollPos;
end;
Handled := True;
end;
procedure TJDImageBox.SetZoom(const Value: Integer);
var
Perc: Single;
begin
FZoom := Value;
if FZoom < FZoomBy then
FZoom:= FZoomBy;
Perc:= FZoom/100;
//Resize picture to new zoom level
FPicture.Width:= Trunc(FImage.Width * Perc);
FPicture.Height:= Trunc(FImage.Height * Perc);
//Move scroll bars to properly position the center of the view
//This is where I don't know how to calculate the 'center'
//or by how much I need to move the scroll bars.
HorzScrollBar.Position:= HorzScrollBar.Position - (FZoomBy div 2);
VertScrollBar.Position:= VertScrollBar.Position - (FZoomBy div 2);
if assigned(FOnZoom) then
FOnZoom(Self, FZoom);
end;
procedure TJDImageBox.SetZoomBy(const Value: Integer);
begin
if FZoomBy <> Value then begin
FZoomBy := EnsureRange(Value, 1, 100);
Paint;
end;
end;
end.
Non riesco nemmeno ad immaginare quale sarebbe il "punto di zoom in avanti". Vorrei "zoomare" su un rettangolo, non su un punto. Non riesco a indovinare quale sia l'aspetto della tua classe, quindi non posso indovinare di quale matematica hai bisogno, né potrei qualcun altro. –
@WarrenP Supponiamo di visualizzare una foto di più persone, il mouse è puntato al centro del volto di una persona. Quando l'utente tiene premuto il tasto di controllo e fa scorrere la rotella del mouse verso l'alto, si ingrandirà il volto di quella persona, con il puntatore del mouse ancora nella stessa posizione dell'immagine. Questo è il motivo per cui sto facendo lo zoom su un 'Punto' e non su un' Retto'. Sono abbastanza sicuro di aver incluso tutto il codice pertinente sopra per dimostrare come gestisco gli eventi del mouse. –