| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- // SPDX-License-Identifier: GPL-3.0-only
- unit UToolBrush;
- {$mode objfpc}{$H+}
- interface
- uses
- Classes, SysUtils, UToolBasic, BGRABitmapTypes, BGRABitmap, UTool,
- UBrushType, LCVectorialFill;
- type
- { TToolGenericBrush }
- TToolGenericBrush = class(TToolPen)
- private
- function GetBrushInfo: TLazPaintBrush;
- protected
- brushOrigin: TPointF;
- originDrawn: boolean;
- defaultBrush: TLazPaintBrush;
- function DrawBrushAt(toolDest: TBGRABitmap; x, y: single): TRect; virtual; abstract;
- procedure PrepareBrush(rightBtn: boolean); virtual; abstract;
- procedure ReleaseBrush; virtual; abstract;
- function StartDrawing(toolDest: TBGRABitmap; ptF: TPointF; rightBtn: boolean): TRect; override;
- function ContinueDrawing(toolDest: TBGRABitmap; {%H-}originF, destF: TPointF; {%H-}rightBtn: boolean): TRect; override;
- function GetBrushAlpha(AAlpha: byte): byte;
- function GetLayerOffset: TPoint; override;
- public
- constructor Create(AManager: TToolManager); override;
- function ToolUp: TRect; override;
- function SubPixelAccuracy: boolean; virtual;
- destructor Destroy; override;
- property BrushInfo: TLazPaintBrush read GetBrushInfo;
- end;
- { TToolBrush }
- TToolBrush = class(TToolGenericBrush)
- protected
- coloredBrushImage: TBGRABitmap;
- function DrawBrushAt(toolDest: TBGRABitmap; x, y: single): TRect; override;
- procedure PrepareBrush({%H-}rightBtn: boolean); override;
- procedure ReleaseBrush; override;
- function GetAllowedForeFillTypes: TVectorialFillTypes; override;
- function GetAllowedBackFillTypes: TVectorialFillTypes; override;
- public
- destructor Destroy; override;
- function GetContextualToolbars: TContextualToolbars; override;
- end;
- { TToolClone }
- TToolClone = class(TToolGenericBrush)
- protected
- class var RightClickHintShown: boolean;
- definingSource: boolean;
- class var sourceLayerId: integer;
- class var sourcePosition: TPoint;
- class var sourcePositionRelative: boolean;
- class var sourceFlattened: boolean;
- class var sourceDefined: boolean;
- function PickColorWithShift: boolean; override;
- function DrawBrushAt(toolDest: TBGRABitmap; x, y: single): TRect; override;
- procedure PrepareBrush(rightBtn: boolean); override;
- procedure ReleaseBrush; override;
- function DoToolMove(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF): TRect; override;
- function DoToolShiftClick({%H-}toolDest: TBGRABitmap; {%H-}ptF: TPointF; {%H-}rightBtn: boolean): TRect; override;
- public
- class procedure ForgetHintShown;
- function SubPixelAccuracy: boolean; override;
- constructor Create(AManager: TToolManager); override;
- destructor Destroy; override;
- function GetContextualToolbars: TContextualToolbars; override;
- function Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth, VirtualScreenHeight: integer;
- BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect; override;
- end;
- implementation
- uses Math, UGraph, UResourceStrings, Graphics, LazPaintType;
- { TToolClone }
- function TToolClone.PickColorWithShift: boolean;
- begin
- Result:= false;
- end;
- function TToolClone.DrawBrushAt(toolDest: TBGRABitmap; x, y: single): TRect;
- var source: TBGRABitmap;
- sourceOfs: TPoint;
- sourceIdx: Integer;
- begin
- if definingSource then
- begin
- sourceOfs := Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex];
- sourcePosition := Point(round(x) + sourceOfs.x,round(y) + sourceOfs.y);
- sourceLayerId := Manager.Image.LayerId[Manager.Image.CurrentLayerIndex];
- sourcePositionRelative:= false;
- sourceFlattened := ssShift in ShiftState;
- sourceDefined := true;
- result := OnlyRenderChange;
- end else
- begin
- if not sourceDefined then
- begin
- Manager.ToolPopup(tpmRightClickForSource, 0, true);
- result := EmptyRect;
- exit;
- end;
- if (ssShift in ShiftState) or sourceFlattened then
- begin
- source := Manager.Image.RenderedImage;
- sourceOfs := Point(0,0);
- end else
- begin
- sourceIdx := Manager.Image.GetLayerIndexById(sourceLayerId);
- if sourceIdx = -1 then
- begin
- Manager.ToolPopup(tpmRightClickForSource, 0, true);
- result := EmptyRect;
- exit;
- end;
- source := Manager.Image.LayerBitmap[sourceIdx];
- sourceOfs := Manager.Image.LayerOffset[sourceIdx];
- end;
- if not SubPixelAccuracy then
- begin
- x := round(x);
- y := round(y);
- end;
- if not sourcePositionRelative then
- begin
- sourcePosition.x -= round(x) + sourceOfs.x;
- sourcePosition.y -= round(y) + sourceOfs.y;
- sourcePositionRelative := true;
- end;
- with BrushInfo.BrushImage do
- begin
- x -= (Width-1)/2;
- y -= (Height-1)/2;
- result := rect(floor(x-0.5),floor(y-0.5),ceil(x+0.5)+Width,ceil(y+0.5)+Height);
- end;
- toolDest.ClipRect := result;
- source.ScanOffset := Point(sourcePosition.x, sourcePosition.y);
- toolDest.FillMask(round(x),round(y),BrushInfo.BrushImage,source,dmDrawWithTransparency,Manager.ApplyPressure(255));
- source.ScanOffset := Point(0,0);
- toolDest.NoClip;
- end;
- end;
- procedure TToolClone.PrepareBrush(rightBtn: boolean);
- begin
- definingSource := rightBtn;
- end;
- procedure TToolClone.ReleaseBrush;
- begin
- end;
- function TToolClone.DoToolMove(toolDest: TBGRABitmap; pt: TPoint; ptF: TPointF
- ): TRect;
- begin
- if not RightClickHintShown then
- begin
- Manager.ToolPopup(tpmRightClickForSource);
- RightClickHintShown := true;
- end;
- Result:=inherited DoToolMove(toolDest, pt, ptF);
- end;
- function TToolClone.DoToolShiftClick(toolDest: TBGRABitmap; ptF: TPointF;
- rightBtn: boolean): TRect;
- begin
- Result:= EmptyRect;
- end;
- class procedure TToolClone.ForgetHintShown;
- begin
- RightClickHintShown:= false;
- end;
- function TToolClone.SubPixelAccuracy: boolean;
- begin
- Result:=false;
- end;
- constructor TToolClone.Create(AManager: TToolManager);
- begin
- inherited Create(AManager);
- end;
- destructor TToolClone.Destroy;
- begin
- inherited Destroy;
- end;
- function TToolClone.GetContextualToolbars: TContextualToolbars;
- begin
- Result:= [ctPenWidth,ctBrush];
- end;
- function TToolClone.Render(VirtualScreen: TBGRABitmap; VirtualScreenWidth,
- VirtualScreenHeight: integer;
- BitmapToVirtualScreen: TBitmapToVirtualScreenFunction): TRect;
- var sourcePosF: TPointF;
- begin
- Result:=inherited Render(VirtualScreen, VirtualScreenWidth,
- VirtualScreenHeight, BitmapToVirtualScreen);
- if not sourcePositionRelative and (sourceFlattened or
- (Manager.Image.LayerBitmapById[sourceLayerId] <> nil)) then
- begin
- sourcePosF := BitmapToVirtualScreen(PointF(sourcePosition.X mod Manager.Image.Width,
- sourcePosition.Y mod Manager.Image.Height));
- result := RectUnion(result,NicePoint(VirtualScreen, sourcePosF.X,sourcePosF.Y));
- if sourcePosF.Y > virtualScreenHeight/2 then
- result := RectUnion(result,NiceText(VirtualScreen, round(sourcePosF.X),round(sourcePosF.Y-6), VirtualScreenWidth,VirtualScreenHeight, rsSourcePosition, taCenter, tlBottom))
- else
- result := RectUnion(result,NiceText(VirtualScreen, round(sourcePosF.X),round(sourcePosF.Y+6), VirtualScreenWidth,VirtualScreenHeight, rsSourcePosition, taCenter, tlTop));
- end;
- end;
- { TToolBrush }
- function TToolBrush.DrawBrushAt(toolDest: TBGRABitmap; x, y: single): TRect;
- begin
- if not Assigned(coloredBrushImage) then
- begin
- result := EmptyRect;
- exit;
- end;
- if not SubPixelAccuracy then
- begin
- x := round(x);
- y := round(y);
- end;
- x -= (coloredBrushImage.Width-1)/2;
- y -= (coloredBrushImage.Height-1)/2;
- result := rect(floor(x-0.5),floor(y-0.5),ceil(x+0.5)+coloredBrushImage.Width,ceil(y+0.5)+coloredBrushImage.Height);
- toolDest.ClipRect := result;
- if not SubPixelAccuracy then
- toolDest.PutImage(round(x),round(y),coloredBrushImage,dmDrawWithTransparency,GetBrushAlpha(Manager.ApplyPressure(255)))
- else
- toolDest.PutImageSubpixel(x,y,coloredBrushImage,GetBrushAlpha(Manager.ApplyPressure(255)));
- toolDest.NoClip;
- end;
- procedure TToolBrush.PrepareBrush(rightBtn: boolean);
- var
- penColor: TBGRAPixel;
- begin
- FreeAndNil(coloredBrushImage);
- if rightBtn then penColor := Manager.BackColor else penColor := Manager.ForeColor;
- coloredBrushImage := BrushInfo.MakeColoredBrushImage(BGRA(penColor.red,penColor.green,penColor.blue,GetBrushAlpha(penColor.alpha)));
- end;
- procedure TToolBrush.ReleaseBrush;
- begin
- FreeAndNil(coloredBrushImage);
- end;
- function TToolBrush.GetAllowedForeFillTypes: TVectorialFillTypes;
- begin
- Result:= [vftSolid];
- end;
- function TToolBrush.GetAllowedBackFillTypes: TVectorialFillTypes;
- begin
- Result:= [vftSolid];
- end;
- destructor TToolBrush.Destroy;
- begin
- ReleaseBrush;
- inherited Destroy;
- end;
- function TToolBrush.GetContextualToolbars: TContextualToolbars;
- begin
- Result:= [ctPenFill,ctBackFill,ctPenWidth,ctBrush];
- end;
- { TToolGenericBrush }
- function TToolGenericBrush.GetBrushInfo: TLazPaintBrush;
- begin
- result := manager.BrushInfo;
- if result = nil then
- begin
- if defaultBrush = nil then
- defaultBrush := TLazPaintBrush.Create;
- result := defaultBrush;
- end;
- result.Size := manager.PenWidth;
- end;
- function TToolGenericBrush.StartDrawing(toolDest: TBGRABitmap; ptF: TPointF;
- rightBtn: boolean): TRect;
- begin
- if not SubPixelAccuracy then
- brushOrigin:= PointF(round(ptF.x),round(ptF.y))
- else brushOrigin := ptF;
- originDrawn := false;
- PrepareBrush(rightBtn);
- result := ContinueDrawing(toolDest, brushOrigin, brushOrigin, rightBtn);
- end;
- function TToolGenericBrush.ContinueDrawing(toolDest: TBGRABitmap; originF,
- destF: TPointF; rightBtn: boolean): TRect;
- var v: TPointF;
- count: integer;
- len, minLen: single;
- begin
- result := EmptyRect;
- if not originDrawn then //and ((destF <> brushOrigin) or not Manager.ToolBrushOriented) then
- begin
- result := RectUnion(result, DrawBrushAt(toolDest, brushOrigin.x,brushOrigin.y));
- originDrawn:= true;
- end;
- if destF<>brushOrigin then
- begin
- v := destF-brushOrigin;
- if not SubPixelAccuracy then
- len := max(abs(v.x),abs(v.y))
- else
- len := sqrt(v**v);
- minLen := round(power(BrushInfo.Size/10,0.8));
- if minLen < 1 then minLen := 1;
- if minLen > 5 then minLen := 5;
- minLen *=Manager.BrushSpacing;
- if len >= minLen then
- begin
- v := v*(1/len)*minLen;
- count := trunc(len/minLen);
- while count > 0 do
- begin
- brushOrigin += v;
- result := RectUnion(result, DrawBrushAt(toolDest, brushOrigin.x,brushOrigin.y));
- originDrawn:= true;
- dec(count);
- end;
- end;
- end;
- end;
- function TToolGenericBrush.GetBrushAlpha(AAlpha: byte): byte;
- var exponent: single;
- begin
- exponent := (BrushInfo.Size-1)/10+1;
- if exponent > 2 then exponent := 2;
- result := round(Power(AAlpha/255,exponent)*255)
- end;
- function TToolGenericBrush.GetLayerOffset: TPoint;
- begin
- if IsSelectingTool or not Manager.Image.SelectionMaskEmpty then
- result := Manager.Image.LayerOffset[Manager.Image.CurrentLayerIndex]
- else
- result := Point(0,0);
- end;
- constructor TToolGenericBrush.Create(AManager: TToolManager);
- begin
- inherited Create(AManager);
- end;
- function TToolGenericBrush.ToolUp: TRect;
- var penWasDrawing: boolean;
- begin
- penWasDrawing:= penDrawing;
- Result:=inherited ToolUp;
- if not penDrawing and penWasDrawing then ReleaseBrush;
- end;
- function TToolGenericBrush.SubPixelAccuracy: boolean;
- begin
- result := BrushInfo.Size < 10;
- end;
- destructor TToolGenericBrush.Destroy;
- begin
- FreeAndNil(defaultBrush);
- inherited Destroy;
- end;
- initialization
- RegisterTool(ptBrush,TToolBrush);
- RegisterTool(ptClone,TToolClone);
- TToolClone.sourceLayerId := -1;
- end.
|