📄 gr32_layers.pas
字号:
unit GR32_Layers;
(* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Graphics32
*
* The Initial Developer of the Original Code is
* Alex A. Denisov
*
* Portions created by the Initial Developer are Copyright (C) 2000-2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Andre Beckedorf <Andre@metaException.de>
* Michael Hansen <dyster_tid@hotmail.com>
* Dieter K鰄ler <dieter.koehler@philo.de>
*
* ***** END LICENSE BLOCK ***** *)
interface
{$INCLUDE GR32.inc}
uses
{$IFDEF CLX}
Qt, Types, QControls, QGraphics, QForms,
{$IFDEF LINUX}Libc, {$ENDIF}
{$IFDEF MSWINDOWS}Windows, {$ENDIF}
{$ELSE}
Windows, Controls, Graphics, Forms,
{$ENDIF}
Classes, SysUtils, Math, GR32;
const
{ Layer Options Bits }
LOB_VISIBLE = $80000000; // 31-st bit
LOB_GDI_OVERLAY = $40000000; // 30-th bit
LOB_MOUSE_EVENTS = $20000000; // 29-th bit
LOB_NO_UPDATE = $10000000; // 28-th bit
LOB_NO_CAPTURE = $08000000; // 27-th bit
LOB_INVALID = $04000000; // 26-th bit
LOB_FORCE_UPDATE = $02000000; // 25-th bit
LOB_RESERVED_24 = $01000000; // 24-th bit
LOB_RESERVED_MASK = $FF000000;
type
TCustomLayer = class;
TPositionedLayer = class;
TLayerClass = class of TCustomLayer;
{ TCoordXForm - transformations from bitmap image to buffer origin }
TCoordXForm = record
ScaleX: TFixed; // bitmap image to buf
ScaleY: TFixed;
ShiftX: Integer;
ShiftY: Integer;
RevScaleX: TFixed;
RevScaleY: TFixed;
end;
PCoordXForm = ^TCoordXForm;
TLayerCollection = class;
TLayerUpdateEvent = procedure(Sender: TObject; Layer: TCustomLayer) of object;
TAreaUpdateEvent = TAreaChangedEvent;
TLayerListNotification = (lnLayerAdded, lnLayerInserted, lnLayerDeleted, lnCleared);
TLayerListNotifyEvent = procedure(Sender: TLayerCollection; Action: TLayerListNotification;
Layer: TCustomLayer; Index: Integer) of object;
TGetScaleEvent = procedure(Sender: TObject; var ScaleX, ScaleY: Single) of object;
TGetShiftEvent = procedure(Sender: TObject; var ShiftX, ShiftY: Single) of object;
TLayerCollection = class(TPersistent)
private
{$IFDEF DEPRECATEDMODE}
FCoordXForm: PCoordXForm;
{$ENDIF}
FItems: TList;
FMouseEvents: Boolean;
FMouseListener: TCustomLayer;
FUpdateCount: Integer;
FOwner: TPersistent;
FOnChanging: TNotifyEvent;
FOnChange: TNotifyEvent;
FOnGDIUpdate: TNotifyEvent;
FOnListNotify: TLayerListNotifyEvent;
FOnLayerUpdated: TLayerUpdateEvent;
FOnAreaUpdated: TAreaUpdateEvent;
FOnGetViewportScale: TGetScaleEvent;
FOnGetViewportShift: TGetShiftEvent;
function GetCount: Integer;
procedure InsertItem(Item: TCustomLayer);
procedure RemoveItem(Item: TCustomLayer);
procedure SetMouseEvents(Value: Boolean);
procedure SetMouseListener(Value: TCustomLayer);
protected
procedure BeginUpdate;
procedure Changed;
procedure Changing;
procedure EndUpdate;
function FindLayerAtPos(X, Y: Integer; OptionsMask: Cardinal): TCustomLayer;
function GetItem(Index: Integer): TCustomLayer;
function GetOwner: TPersistent; override;
procedure GDIUpdate;
procedure DoUpdateLayer(Layer: TCustomLayer);
procedure DoUpdateArea(const Rect: TRect);
procedure Notify(Action: TLayerListNotification; Layer: TCustomLayer; Index: Integer);
procedure SetItem(Index: Integer; Value: TCustomLayer);
function MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): TCustomLayer;
function MouseMove(Shift: TShiftState; X, Y: Integer): TCustomLayer;
function MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer): TCustomLayer;
property OnChanging: TNotifyEvent read FOnChanging write FOnChanging;
property OnChange: TNotifyEvent read FOnChange write FOnChange;
property OnListNotify: TLayerListNotifyEvent read FOnListNotify write FOnListNotify;
property OnGDIUpdate: TNotifyEvent read FOnGDIUpdate write FOnGDIUpdate;
property OnLayerUpdated: TLayerUpdateEvent read FOnLayerUpdated write FOnLayerUpdated;
property OnAreaUpdated: TAreaUpdateEvent read FOnAreaUpdated write FOnAreaUpdated;
property OnGetViewportScale: TGetScaleEvent read FOnGetViewportScale write FOnGetViewportScale;
property OnGetViewportShift: TGetShiftEvent read FOnGetViewportShift write FOnGetViewportShift;
public
constructor Create(AOwner: TPersistent);
destructor Destroy; override;
function Add(ItemClass: TLayerClass): TCustomLayer;
procedure Assign(Source: TPersistent); override;
procedure Clear;
procedure Delete(Index: Integer);
function Insert(Index: Integer; ItemClass: TLayerClass): TCustomLayer;
function LocalToViewport(const APoint: TFloatPoint; AScaled: Boolean): TFloatPoint;
function ViewportToLocal(const APoint: TFloatPoint; AScaled: Boolean): TFloatPoint;
procedure GetViewportScale(var ScaleX, ScaleY: Single); virtual;
procedure GetViewportShift(var ShiftX, ShiftY: Single); virtual;
property Count: Integer read GetCount;
{$IFDEF DEPRECATEDMODE}
property CoordXForm: PCoordXForm read FCoordXForm write FCoordXForm;
{$ENDIF}
property Owner: TPersistent read FOwner;
property Items[Index: Integer]: TCustomLayer read GetItem write SetItem; default;
property MouseListener: TCustomLayer read FMouseListener write SetMouseListener;
property MouseEvents: Boolean read FMouseEvents write SetMouseEvents;
end;
TLayerState = (lsMouseLeft, lsMouseRight, lsMouseMiddle);
TLayerStates = set of TLayerState;
TPaintLayerEvent = procedure(Sender: TObject; Buffer: TBitmap32) of object;
THitTestEvent = procedure(Sender: TObject; X, Y: Integer; var Passed: Boolean) of object;
TCustomLayer = class(TNotifiablePersistent)
private
FCursor: TCursor;
FFreeNotifies: TList;
FLayerCollection: TLayerCollection;
FLayerStates: TLayerStates;
FLayerOptions: Cardinal;
FOnHitTest: THitTestEvent;
FOnMouseDown: TMouseEvent;
FOnMouseMove: TMouseMoveEvent;
FOnMouseUp: TMouseEvent;
FOnPaint: TPaintLayerEvent;
FTag: Integer;
FOnDestroy: TNotifyEvent;
function GetIndex: Integer;
function GetMouseEvents: Boolean;
function GetVisible: Boolean;
procedure SetMouseEvents(Value: Boolean);
procedure SetVisible(Value: Boolean);
function GetInvalid: Boolean;
procedure SetInvalid(Value: Boolean);
function GetForceUpdate: Boolean;
procedure SetForceUpdate(Value: Boolean);
protected
procedure AddNotification(ALayer: TCustomLayer);
procedure Changing;
function DoHitTest(X, Y: Integer): Boolean; virtual;
procedure DoPaint(Buffer: TBitmap32);
function GetOwner: TPersistent; override;
procedure MouseDown(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual;
procedure MouseMove(Shift: TShiftState; X, Y: Integer); virtual;
procedure MouseUp(Button: TMouseButton; Shift: TShiftState; X, Y: Integer); virtual;
procedure Notification(ALayer: TCustomLayer); virtual;
procedure Paint(Buffer: TBitmap32); virtual;
procedure PaintGDI(Canvas: TCanvas); virtual;
procedure RemoveNotification(ALayer: TCustomLayer);
procedure SetIndex(Value: Integer); virtual;
procedure SetCursor(Value: TCursor); virtual;
procedure SetLayerCollection(Value: TLayerCollection); virtual;
procedure SetLayerOptions(Value: Cardinal); virtual;
property Invalid: Boolean read GetInvalid write SetInvalid;
property ForceUpdate: Boolean read GetForceUpdate write SetForceUpdate;
public
constructor Create(ALayerCollection: TLayerCollection); virtual;
destructor Destroy; override;
procedure BeforeDestruction; override;
procedure BringToFront;
procedure Changed; overload; override;
procedure Changed(const Rect: TRect); reintroduce; overload;
procedure Update; overload;
procedure Update(const Rect: TRect); overload;
function HitTest(X, Y: Integer): Boolean;
procedure SendToBack;
procedure SetAsMouseListener;
property Cursor: TCursor read FCursor write SetCursor;
property Index: Integer read GetIndex write SetIndex;
property LayerCollection: TLayerCollection read FLayerCollection write SetLayerCollection;
property LayerOptions: Cardinal read FLayerOptions write SetLayerOptions;
property LayerStates: TLayerStates read FLayerStates;
property MouseEvents: Boolean read GetMouseEvents write SetMouseEvents;
property Tag: Integer read FTag write FTag;
property Visible: Boolean read GetVisible write SetVisible;
property OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
property OnHitTest: THitTestEvent read FOnHitTest write FOnHitTest;
property OnPaint: TPaintLayerEvent read FOnPaint write FOnPaint;
property OnMouseDown: TMouseEvent read FOnMouseDown write FOnMouseDown;
property OnMouseMove: TMouseMoveEvent read FOnMouseMove write FOnMouseMove;
property OnMouseUp: TMouseEvent read FOnMouseUp write FOnMouseUp;
end;
TPositionedLayer = class(TCustomLayer)
private
FLocation: TFloatRect;
FScaled: Boolean;
procedure SetLocation(const Value: TFloatRect);
procedure SetScaled(Value: Boolean);
protected
function DoHitTest(X, Y: Integer): Boolean; override;
procedure DoSetLocation(const NewLocation: TFloatRect); virtual;
public
constructor Create(ALayerCollection: TLayerCollection); override;
function GetAdjustedRect(const R: TFloatRect): TFloatRect; virtual;
function GetAdjustedLocation: TFloatRect;
property Location: TFloatRect read FLocation write SetLocation;
property Scaled: Boolean read FScaled write SetScaled;
end;
TBitmapLayer = class(TPositionedLayer)
private
FBitmap: TBitmap32;
FAlphaHit: Boolean;
FCropped: Boolean;
procedure BitmapAreaChanged(Sender: TObject; const Area: TRect; const Info: Cardinal);
procedure SetBitmap(Value: TBitmap32);
procedure SetCropped(Value: Boolean);
protected
function DoHitTest(X, Y: Integer): Boolean; override;
procedure Paint(Buffer: TBitmap32); override;
public
constructor Create(ALayerCollection: TLayerCollection); override;
destructor Destroy; override;
property AlphaHit: Boolean read FAlphaHit write FAlphaHit;
property Bitmap: TBitmap32 read FBitmap write SetBitmap;
property Cropped: Boolean read FCropped write SetCropped;
end;
TDragState = (dsNone, dsMove, dsSizeL, dsSizeT, dsSizeR, dsSizeB,
dsSizeTL, dsSizeTR, dsSizeBL, dsSizeBR);
TRBHandles = set of (rhCenter, rhSides, rhCorners, rhFrame,
rhNotLeftSide, rhNotRightSide, rhNotTopSide, rhNotBottomSide,
rhNotTLCorner, rhNotTRCorner, rhNotBLCorner, rhNotBRCorner);
TRBResizingEvent = procedure(
Sender: TObject;
const OldLocation: TFloatRect;
var NewLocation: TFloatRect;
DragState: TDragState;
Shift: TShiftState) of object;
TRubberbandLayer = class(TPositionedLayer)
private
FChildLayer: TPositionedLayer;
FFrameStipplePattern: TArrayOfColor32;
FFrameStippleStep: Single;
FFrameStippleCounter: Single;
FHandleFrame: TColor32;
FHandleFill: TColor32;
FHandles: TRBHandles;
FHandleSize: Integer;
FMinWidth: Single;
FMaxHeight: Single;
FMinHeight: Single;
FMaxWidth: Single;
FOnUserChange: TNotifyEvent;
FOnResizing: TRBResizingEvent;
procedure SetFrameStippleStep(const Value: Single);
procedure SetFrameStippleCounter(const Value: Single);
procedure SetChildLayer(Value: TPositionedLayer);
procedure SetHandleFill(Value: TColor32);
procedure SetHandleFrame(Value: TColor32);
procedure SetHandles(Value: TRBHandles);
procedure SetHandleSize(Value: Integer);
protected
IsDragging: Boolean;
DragState: TDragState;
OldLocation: TFloatRect;
MouseShift: TFloatPoint;
function DoHitTest(X, Y: Integer): Boolean; override;
procedure DoResizing(var OldLocation, NewLocation: TFloatRect; DragState: TDragState; Shift: TShiftState); virtual;
procedure DoSetLocation(const NewLocation: TFloatRect); override;
function GetDragState(X, Y: Integer): TDragState; virtual;
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 Notification(ALayer: TCustomLayer); override;
procedure Paint(Buffer: TBitmap32); override;
procedure SetLayerOptions(Value: Cardinal); override;
procedure UpdateChildLayer;
public
constructor Create(ALayerCollection: TLayerCollection); override;
procedure SetFrameStipple(const Value: Array of TColor32);
property ChildLayer: TPositionedLayer read FChildLayer write SetChildLayer;
property Handles: TRBHandles read FHandles write SetHandles;
property HandleSize: Integer read FHandleSize write SetHandleSize;
property HandleFill: TColor32 read FHandleFill write SetHandleFill;
property HandleFrame: TColor32 read FHandleFrame write SetHandleFrame;
property FrameStippleStep: Single read FFrameStippleStep write SetFrameStippleStep;
property FrameStippleCounter: Single read FFrameStippleCounter write SetFrameStippleCounter;
property MaxHeight: Single read FMaxHeight write FMaxHeight;
property MaxWidth: Single read FMaxWidth write FMaxWidth;
property MinHeight: Single read FMinHeight write FMinHeight;
property MinWidth: Single read FMinWidth write FMinWidth;
property OnUserChange: TNotifyEvent read FOnUserChange write FOnUserChange;
property OnResizing: TRBResizingEvent read FOnResizing write FOnResizing;
end;
implementation
uses
TypInfo, GR32_Image, GR32_LowLevel, GR32_Transforms, GR32_Resamplers,
GR32_RepaintOpt;
{ mouse state mapping }
const
CStateMap: array [TMouseButton] of TLayerState =
(lsMouseLeft, lsMouseRight, lsMouseMiddle);
type
TImage32Access = class(TCustomImage32);
{ TLayerCollection }
function TLayerCollection.Add(ItemClass: TLayerClass): TCustomLayer;
begin
Result := ItemClass.Create(Self);
Result.Index := FItems.Count - 1;
Notify(lnLayerAdded, Result, Result.Index);
end;
procedure TLayerCollection.Assign(Source: TPersistent);
var
I: Integer;
Item: TCustomLayer;
begin
if Source is TLayerCollection then
begin
BeginUpdate;
try
while FItems.Count > 0 do TCustomLayer(FItems.Last).Free;
for I := 0 to TLayerCollection(Source).Count - 1 do
begin
Item := TLayerCollection(Source).Items[I];
Add(TLayerClass(Item.ClassType)).Assign(Item);
end;
finally
EndUpdate;
end;
Exit;
end;
inherited Assign(Source);
end;
procedure TLayerCollection.BeginUpdate;
begin
if FUpdateCount = 0 then Changing;
Inc(FUpdateCount);
end;
procedure TLayerCollection.Changed;
begin
if Assigned(FOnChange) then FOnChange(Self);
end;
procedure TLayerCollection.Changing;
begin
if Assigned(FOnChanging) then FOnChanging(Self);
end;
procedure TLayerCollection.Clear;
begin
BeginUpdate;
try
while FItems.Count > 0 do TCustomLayer(FItems.Last).Free;
Notify(lnCleared, nil, 0);
finally
EndUpdate;
end;
end;
constructor TLayerCollection.Create(AOwner: TPersistent);
begin
FOwner := AOwner;
FItems := TList.Create;
FMouseEvents := True;
end;
procedure TLayerCollection.Delete(Index: Integer);
begin
TCustomLayer(FItems[Index]).Free;
end;
destructor TLayerCollection.Destroy;
begin
FUpdateCount := 1; // disable update notification
if Assigned(FItems) then Clear;
FItems.Free;
inherited;
end;
procedure TLayerCollection.EndUpdate;
begin
Dec(FUpdateCount);
if FUpdateCount = 0 then Changed;
Assert(FUpdateCount >= 0, 'Unpaired EndUpdate');
end;
function TLayerCollection.FindLayerAtPos(X, Y: Integer; OptionsMask: Cardinal): TCustomLayer;
var
I: Integer;
begin
for I := Count - 1 downto 0 do
begin
Result := Items[I];
if (Result.LayerOptions and OptionsMask) = 0 then Continue; // skip to the next one
if Result.HitTest(X, Y) then Exit;
end;
Result := nil;
end;
procedure TLayerCollection.GDIUpdate;
begin
if (FUpdateCount = 0) and Assigned(FOnGDIUpdate) then FOnGDIUpdate(Self);
end;
function TLayerCollection.GetCount: Integer;
begin
Result := FItems.Count;
end;
function TLayerCollection.GetItem(Index: Integer): TCustomLayer;
begin
Result := FItems[Index];
end;
function TLayerCollection.GetOwner: TPersistent;
begin
Result := FOwner;
end;
function TLayerCollection.Insert(Index: Integer; ItemClass: TLayerClass): TCustomLayer;
begin
BeginUpdate;
try
Result := Add(ItemClass);
Result.Index := Index;
Notify(lnLayerInserted, Result, Index);
finally
EndUpdate;
end;
end;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -