📄 gr32_image.pas
字号:
BitBlt(Canvas.Handle, Left, Top, Right - Left, Bottom - Top, FBuffer.Handle, Left, Top, SRCCOPY)
else
with GetViewportRect do
BitBlt(Canvas.Handle, Left, Top, Right - Left, Bottom - Top, FBuffer.Handle, Left, Top, SRCCOPY);
{$ENDIF}
finally
FBuffer.Unlock;
end;
DoPaintGDIOverlay;
if FRepaintOptimizer.Enabled then
FRepaintOptimizer.EndPaint;
ResetInvalidRects;
FForceFullRepaint := False;
end;
procedure TCustomPaintBox32.ResetInvalidRects;
begin
FInvalidRects.Clear;
end;
procedure TCustomPaintBox32.Resize;
begin
ResizeBuffer;
BufferValid := False;
inherited;
end;
procedure TCustomPaintBox32.ResizeBuffer;
var
NewWidth, NewHeight, W, H: Integer;
OldWidth, OldHeight: Integer;
begin
// get the viewport parameters
with GetViewportRect do
begin
NewWidth := Right - Left;
NewHeight := Bottom - Top;
end;
if NewWidth < 0 then NewWidth := 0;
if NewHeight < 0 then NewHeight := 0;
W := FBuffer.Width;
if NewWidth > W then
W := NewWidth + FBufferOversize
else if NewWidth < W - FBufferOversize then
W := NewWidth;
if W < 1 then W := 1;
H := FBuffer.Height;
if NewHeight > H then
H := NewHeight + FBufferOversize
else if NewHeight < H - FBufferOversize then
H := NewHeight;
if H < 1 then H := 1;
if (W <> FBuffer.Width) or (H <> FBuffer.Height) then
begin
FBuffer.Lock;
OldWidth := Buffer.Width;
OldHeight := Buffer.Height;
FBuffer.SetSize(W, H);
FBuffer.Unlock;
DoBufferResized(OldWidth, OldHeight);
ForceFullInvalidate;
end;
end;
procedure TCustomPaintBox32.SetBounds(ALeft, ATop, AWidth, AHeight: Integer);
begin
inherited;
if csDesigning in ComponentState then ResizeBuffer;
FBufferValid := False;
end;
procedure TCustomPaintBox32.SetBufferOversize(Value: Integer);
begin
if Value < 0 then Value := 0;
FBufferOversize := Value;
end;
{$IFDEF CLX}
function TCustomPaintBox32.WidgetFlags: Integer;
begin
Result := Inherited WidgetFlags or Integer(WidgetFlags_WRepaintNoErase) or
Integer(WidgetFlags_WResizeNoErase);
end;
{$ELSE}
procedure TCustomPaintBox32.WMEraseBkgnd(var Message: TWmEraseBkgnd);
begin
Message.Result := 1;
end;
procedure TCustomPaintBox32.WMGetDlgCode(var Msg: TWmGetDlgCode);
begin
with Msg do if pboWantArrowKeys in Options then
Result:= Result or DLGC_WANTARROWS
else
Result:= Result and not DLGC_WANTARROWS;
end;
procedure TCustomPaintBox32.WMPaint(var Message: TMessage);
begin
if CustomRepaint then
begin
if InvalidRectsAvailable then
// BeginPaint deeper might set invalid clipping, so we call Paint here
// to force repaint of our invalid rects...
Paint
else
// no invalid rects available? Invalidate the whole client area
InvalidateRect(Handle, nil, False);
end;
inherited;
end;
{$ENDIF}
procedure TCustomPaintBox32.DirectAreaUpdateHandler(Sender: TObject;
const Area: TRect; const Info: Cardinal);
begin
FInvalidRects.Add(Area);
if not(csCustomPaint in ControlState) then Repaint;
end;
procedure TCustomPaintBox32.SetRepaintMode(const Value: TRepaintMode);
begin
if Assigned(FRepaintOptimizer) then
begin
// setup event handler on change of area
if (Value = rmOptimizer) and not(Self is TCustomImage32) then
FBuffer.OnAreaChanged := FRepaintOptimizer.AreaUpdateHandler
else if Value = rmDirect then
FBuffer.OnAreaChanged := DirectAreaUpdateHandler
else
FBuffer.OnAreaChanged := nil;
FRepaintOptimizer.Enabled := Value = rmOptimizer;
FRepaintMode := Value;
Invalidate;
end;
end;
{ TPaintBox32 }
procedure TPaintBox32.DoPaintBuffer;
begin
if Assigned(FOnPaintBuffer) then FOnPaintBuffer(Self);
inherited;
end;
{ TCustomImage32 }
procedure TCustomImage32.BeginUpdate;
begin
// disable OnChange & OnChanging generation
Inc(FUpdateCount);
end;
procedure TCustomImage32.BitmapResized;
{$IFNDEF CLX}
var
W, H: Integer;
{$ENDIF}
begin
{$IFNDEF CLX}
if AutoSize then
begin
W := Bitmap.Width;
H := Bitmap.Height;
if ScaleMode = smScale then
begin
W := Round(W * Scale);
H := Round(H * Scale);
end;
if AutoSize and (W > 0) and (H > 0) then SetBounds(Left, Top, W, H);
end;
{$ENDIF}
if (FUpdateCount <> 0) and Assigned(FOnBitmapResize) then FOnBitmapResize(Self);
InvalidateCache;
ForceFullInvalidate;
end;
procedure TCustomImage32.BitmapChanged(const Area: TRect);
begin
Changed;
end;
function TCustomImage32.BitmapToControl(const APoint: TPoint): TPoint;
begin
// convert coordinates from bitmap's ref. frame to control's ref. frame
UpdateCache;
with CachedXForm, APoint do
begin
Result.X := X * ScaleX div $10000 + ShiftX;
Result.Y := Y * ScaleY div $10000 + ShiftY;
end;
end;
{$IFNDEF CLX}
function TCustomImage32.CanAutoSize(var NewWidth, NewHeight: Integer): Boolean;
var
W, H: Integer;
begin
InvalidateCache;
Result := True;
W := Bitmap.Width;
H := Bitmap.Height;
if ScaleMode = smScale then
begin
W := Round(W * Scale);
H := Round(H * Scale);
end;
if not (csDesigning in ComponentState) or (W > 0) and (H > 0) then
begin
if Align in [alNone, alLeft, alRight] then NewWidth := W;
if Align in [alNone, alTop, alBottom] then NewHeight := H;
end;
end;
{$ENDIF}
procedure TCustomImage32.Changed;
begin
if FUpdateCount = 0 then
begin
Invalidate;
if Assigned(FOnChange) then FOnChange(Self);
end;
end;
procedure TCustomImage32.BitmapResizeHandler(Sender: TObject);
begin
{$IFDEF CLX}
// workaround to stop CLX from calling BitmapResized and to prevent
// AV when accessing Layers. Layers is already freed at that time
if not(csDestroying in ComponentState) then
{$ENDIF}
BitmapResized;
end;
procedure TCustomImage32.BitmapChangeHandler(Sender: TObject);
begin
FRepaintOptimizer.Reset;
BitmapChanged(Bitmap.Boundsrect);
end;
procedure TCustomImage32.BitmapAreaChangeHandler(Sender: TObject; const Area: TRect; const Info: Cardinal);
var
T, R: TRect;
Width, Tx, Ty, I, J: Integer;
begin
if Sender = FBitmap then
begin
T := Area;
Width := Trunc(FBitmap.Resampler.Width) + 1;
InflateArea(T, Width, Width);
T.TopLeft := BitmapToControl(T.TopLeft);
T.BottomRight := BitmapToControl(T.BottomRight);
if FBitmapAlign <> baTile then
FRepaintOptimizer.AreaUpdateHandler(Self, T, AREAINFO_RECT)
else
begin
with CachedBitmapRect do
begin
Tx := Buffer.Width div Right;
Ty := Buffer.Height div Bottom;
for J := 0 to Ty do
for I := 0 to Tx do
begin
R := T;
OffsetRect(R, Right * I, Bottom * J);
FRepaintOptimizer.AreaUpdateHandler(Self, R, AREAINFO_RECT);
end;
end;
end;
end;
BitmapChanged(Area);
end;
procedure TCustomImage32.BitmapDirectAreaChangeHandler(Sender: TObject; const Area: TRect; const Info: Cardinal);
var
T, R: TRect;
Width, Tx, Ty, I, J: Integer;
begin
if Sender = FBitmap then
begin
T := Area;
Width := Trunc(FBitmap.Resampler.Width) + 1;
InflateArea(T, Width, Width);
T.TopLeft := BitmapToControl(T.TopLeft);
T.BottomRight := BitmapToControl(T.BottomRight);
if FBitmapAlign <> baTile then
InvalidRects.Add(T)
else
begin
with CachedBitmapRect do
begin
Tx := Buffer.Width div Right;
Ty := Buffer.Height div Bottom;
for J := 0 to Ty do
for I := 0 to Tx do
begin
R := T;
OffsetRect(R, Right * I, Bottom * J);
InvalidRects.Add(R);
end;
end;
end;
end;
if FUpdateCount = 0 then
begin
if not(csCustomPaint in ControlState) then Repaint;
if Assigned(FOnChange) then FOnChange(Self);
end;
end;
procedure TCustomImage32.LayerCollectionChangeHandler(Sender: TObject);
begin
Changed;
end;
procedure TCustomImage32.LayerCollectionGDIUpdateHandler(Sender: TObject);
begin
Paint;
end;
procedure TCustomImage32.LayerCollectionGetViewportScaleHandler(Sender: TObject; var ScaleX, ScaleY: Single);
begin
UpdateCache;
ScaleX := CachedXForm.ScaleX / FixedOne;
ScaleY := CachedXForm.ScaleY / FixedOne;
end;
procedure TCustomImage32.LayerCollectionGetViewportShiftHandler(Sender: TObject; var ShiftX, ShiftY: Single);
begin
UpdateCache;
ShiftX := CachedXForm.ShiftX;
ShiftY := CachedXForm.ShiftY;
end;
function TCustomImage32.ControlToBitmap(const APoint: TPoint): TPoint;
begin
// convert point coords from control's ref. frame to bitmap's ref. frame
// the coordinates are not clipped to bitmap image boundary
UpdateCache;
with CachedXForm, APoint do
begin
Result.X := (X - ShiftX) * RevScaleX div $10000;
Result.Y := (Y - ShiftY) * RevScaleY div $10000;
end;
end;
constructor TCustomImage32.Create(AOwner: TComponent);
begin
inherited;
ControlStyle := [csAcceptsControls, csCaptureMouse, csClickEvents,
csDoubleClicks, csReplicatable, csOpaque];
FBitmap := TBitmap32.Create;
FBitmap.OnResize := BitmapResizeHandler;
FLayers := TLayerCollection.Create(Self);
with TLayerCollectionAccess(FLayers) do
begin
{$IFDEF DEPRECATEDMODE}
CoordXForm := @CachedXForm;
{$ENDIF}
OnChange := LayerCollectionChangeHandler;
OnGDIUpdate := LayerCollectionGDIUpdateHandler;
OnGetViewportScale := LayerCollectionGetViewportScaleHandler;
OnGetViewportShift := LayerCollectionGetViewportShiftHandler;
end;
FRepaintOptimizer.RegisterLayerCollection(FLayers);
RepaintMode := rmFull;
FPaintStages := TPaintStages.Create;
FScaleX := 1.0;
FScaleY := 1.0;
InitDefaultStages;
end;
destructor TCustomImage32.Destroy;
begin
BeginUpdate;
FPaintStages.Free;
FRepaintOptimizer.UnregisterLayerCollection(FLayers);
FLayers.Free;
FBitmap.Free;
inherited;
end;
procedure TCustomImage32.DoInitStages;
begin
if Assigned(FOnInitStages) then FOnInitStages(Self);
end;
procedure TCustomImage32.DoPaintBuffer;
var
PaintStageHandlerCount: Integer;
I, J: Integer;
DT, RT: Boolean;
begin
if FRepaintOptimizer.Enabled then
FRepaintOptimizer.BeginPaintBuffer;
UpdateCache;
SetLength(FPaintStageHandlers, FPaintStages.Count);
SetLength(FPaintStageNum, FPaintStages.Count);
PaintStageHandlerCount := 0;
DT := csDesigning in ComponentState;
RT := not DT;
// compile list of paintstage handler methods
for I := 0 to FPaintStages.Count - 1 do
begin
with FPaintStages[I]^ do
if (DsgnTime and DT) or (RunTime and RT) then
begin
FPaintStageNum[PaintStageHandlerCount] := I;
case Stage of
PST_CUSTOM: FPaintStageHandlers[PaintStageHandlerCount] := ExecCustom;
PST_CLEAR_BUFFER: FPaintStageHandlers[PaintStageHandlerCount] := ExecClearBuffer;
PST_CLEAR_BACKGND: FPaintStageHandlers[PaintStageHandlerCount] := ExecClearBackgnd;
PST_DRAW_BITMAP: FPaintStageHandlers[PaintStageHandlerCount] := ExecDrawBitmap;
PST_DRAW_LAYERS: FPaintStageHandlers[PaintStageHandlerCount] := ExecDrawLayers;
PST_CONTROL_FRAME: FPaintStageHandlers[PaintStageHandlerCount] := ExecControlFrame;
PST_BITMAP_FRAME: FPaintStageHandlers[PaintStageHandlerCount] := ExecBitmapFrame;
else
Dec(PaintStageHandlerCount); // this should not happen .
end;
Inc(PaintStageHandlerCount);
end;
end;
Buffer.BeginUpdate;
if FInvalidRects.Count = 0 then
begin
Buffer.ClipRect := GetViewportRect;
for I := 0 to PaintStageHandlerCount - 1 do
FPaintStageHandlers[I](Buffer, FPaintStageNum[I]);
end
else
begin
for J := 0 to FInvalidRects.Count - 1 do
begin
Buffer.ClipRect := FInvalidRects[J]^;
for I := 0 to PaintStageHandlerCount - 1 do
FPaintStageHandlers[I](Buffer, FPaintStageNum[I]);
end;
Buffer.ClipRect := GetViewportRect;
end;
Buffer.EndUpdate;
if FRepaintOptimizer.Enabled then
FRepaintOptimizer.EndPaintBuffer;
// avoid calling inherited, we have a totally different behaviour here...
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -