GR32.Paint.Tool.Brush.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474
  1. unit GR32.Paint.Tool.Brush;
  2. (* ***** BEGIN LICENSE BLOCK *****
  3. * Version: MPL 1.1 or LGPL 2.1 with linking exception
  4. *
  5. * The contents of this file are subject to the Mozilla Public License Version
  6. * 1.1 (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. * http://www.mozilla.org/MPL/
  9. *
  10. * Software distributed under the License is distributed on an "AS IS" basis,
  11. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12. * for the specific language governing rights and limitations under the
  13. * License.
  14. *
  15. * Alternatively, the contents of this file may be used under the terms of the
  16. * Free Pascal modified version of the GNU Lesser General Public License
  17. * Version 2.1 (the "FPC modified LGPL License"), in which case the provisions
  18. * of this license are applicable instead of those above.
  19. * Please see the file LICENSE.txt for additional information concerning this
  20. * license.
  21. *
  22. * The Original Code is Paint tools for Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Anders Melander, [email protected]
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2008-2025
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * ***** END LICENSE BLOCK ***** *)
  31. interface
  32. {$INCLUDE GR32.inc}
  33. uses
  34. Classes,
  35. Controls,
  36. GR32,
  37. GR32.Paint.Host.API,
  38. GR32.Paint.Tool,
  39. GR32.Paint.Tool.API,
  40. GR32.Paint.Brush;
  41. //------------------------------------------------------------------------------
  42. //
  43. // TBitmap32PaintToolBrush
  44. //
  45. //------------------------------------------------------------------------------
  46. type
  47. IBitmap32PaintToolBrush = interface
  48. ['{9AF10CB0-6D29-48A6-8DE6-F19CDEEBAFB2}']
  49. function CreateBrush(Color: TColor32): TCustomPaintBrush;
  50. end;
  51. TBitmap32PaintToolBrush = class abstract(TCustomBitmap32PaintTool, IBitmap32PaintToolBrush)
  52. strict private
  53. FBrushLineState: TPaintBrushLineState;
  54. FLastPos: TFloatPoint;
  55. FBrush: TCustomPaintBrush;
  56. FBrushLine: TPaintBrushLine;
  57. FHasVectorCursor: boolean;
  58. FCursorSize: Single;
  59. FBrushSize: Single;
  60. strict protected
  61. // IBitmap32PaintToolBrush
  62. function CreateBrush(Color: TColor32): TCustomPaintBrush; virtual; abstract;
  63. strict protected
  64. function GetCaption: string; override;
  65. procedure BeginAction(const Context: IBitmap32PaintToolContext; var ToolState: TBitmap32PaintToolState); override;
  66. procedure ContinueAction(const Context: IBitmap32PaintToolContext; var ToolState: TBitmap32PaintToolState); override;
  67. procedure EndAction(const Context: IBitmap32PaintToolContext; var ToolState: TBitmap32PaintToolState); override;
  68. function GetCursor(out Cursor: TCursor): boolean; override;
  69. strict protected
  70. procedure GetVectorCursor;
  71. function CreateVectorCursor: boolean; virtual;
  72. function GetStep: integer; virtual;
  73. property Brush: TCustomPaintBrush read FBrush;
  74. property Step: integer read GetStep;
  75. public
  76. constructor Create(const APaintHost: IBitmap32PaintHost); override;
  77. destructor Destroy; override;
  78. property BrushSize: Single read FBrushSize write FBrushSize;
  79. end;
  80. TBitmap32PaintToolBrushClass = class of TBitmap32PaintToolBrush;
  81. resourcestring
  82. sBitmap32PaintToolBrushCaption = 'Brush';
  83. //------------------------------------------------------------------------------
  84. //
  85. // TBitmap32PaintToolCircularBrush
  86. //
  87. //------------------------------------------------------------------------------
  88. type
  89. TBitmapBrushClass = class of TBitmapPaintBrush;
  90. TBitmap32PaintToolCircularBrush = class(TBitmap32PaintToolBrush)
  91. strict private
  92. FBlendFunc: TPixelCombineEvent;
  93. FAntiAlias: boolean;
  94. FHardness: Single;
  95. FStep: integer;
  96. strict protected
  97. procedure BlendWrapper(F: TColor32; var B: TColor32; M: Cardinal);
  98. function GetStep: integer; override;
  99. function CreateBrush(Color: TColor32): TCustomPaintBrush; override;
  100. function GetBrushClass: TBitmapBrushClass; virtual;
  101. public
  102. constructor Create(const APaintHost: IBitmap32PaintHost); override;
  103. property AntiAlias: boolean read FAntiAlias write FAntiAlias;
  104. // [0..100]
  105. property Hardness: Single read FHardness write FHardness;
  106. // [1..]
  107. property Step: integer read FStep write FStep;
  108. end;
  109. //------------------------------------------------------------------------------
  110. //
  111. // TBitmap32PaintToolSmudgeBrush
  112. //
  113. //------------------------------------------------------------------------------
  114. type
  115. TBitmap32PaintToolSmudgeBrush = class(TBitmap32PaintToolCircularBrush)
  116. strict private
  117. FPressure: integer;
  118. strict protected
  119. function GetCaption: string; override;
  120. function GetStep: integer; override;
  121. function GetBrushClass: TBitmapBrushClass; override;
  122. function GetCursor(out Cursor: TCursor): boolean; override;
  123. function CreateBrush(Color: TColor32): TCustomPaintBrush; override;
  124. public
  125. constructor Create(const APaintHost: IBitmap32PaintHost); override;
  126. // [0..100]
  127. property Pressure: integer read FPressure write FPressure;
  128. end;
  129. resourcestring
  130. sBitmap32PaintToolSmudgeCaption = 'Smudge';
  131. //------------------------------------------------------------------------------
  132. //------------------------------------------------------------------------------
  133. //------------------------------------------------------------------------------
  134. implementation
  135. uses
  136. Math,
  137. Types,
  138. SysUtils,
  139. GR32_VectorUtils,
  140. GR32_Polygons,
  141. GR32.Blur;
  142. //------------------------------------------------------------------------------
  143. //
  144. // TBitmap32PaintToolBrush
  145. //
  146. //------------------------------------------------------------------------------
  147. constructor TBitmap32PaintToolBrush.Create(const APaintHost: IBitmap32PaintHost);
  148. begin
  149. inherited;
  150. FBrushSize := 15;
  151. end;
  152. destructor TBitmap32PaintToolBrush.Destroy;
  153. begin
  154. inherited;
  155. end;
  156. //------------------------------------------------------------------------------
  157. function TBitmap32PaintToolBrush.CreateVectorCursor: boolean;
  158. var
  159. Radius: Single;
  160. Steps: integer;
  161. Stipple: TArrayOfColor32;
  162. Polygon: TArrayOfFixedPoint;
  163. FeatureVectorCursor: IBitmap32PaintFeatureVectorCursor;
  164. begin
  165. Result := False;
  166. if (not Supports(PaintHost, IBitmap32PaintFeatureVectorCursor, FeatureVectorCursor)) then
  167. exit;
  168. FCursorSize := BrushSize;
  169. Radius := FCursorSize / 2;
  170. if (Radius > 1) then
  171. begin
  172. Steps := Round(Radius * Abs(PaintHost.Magnification)) + 4;
  173. if Odd(Steps) then
  174. Inc(Steps);
  175. Polygon := FloatPointToFixedPoint(Circle(FloatPoint(0, 0), Radius, Steps));
  176. SetLength(Stipple, 6);
  177. Stipple[0] := $80FFFFFF;
  178. Stipple[1] := $80FFFFFF;
  179. Stipple[2] := $00000000;
  180. Stipple[3] := $80000000;
  181. Stipple[4] := $80000000;
  182. Stipple[5] := $00000000;
  183. Result := FeatureVectorCursor.SetToolVectorCursor(Polygon, GR32.Point(0, 0), 0, Stipple);
  184. end;
  185. end;
  186. //------------------------------------------------------------------------------
  187. function TBitmap32PaintToolBrush.GetCaption: string;
  188. begin
  189. Result := sBitmap32PaintToolBrushCaption;
  190. end;
  191. //------------------------------------------------------------------------------
  192. function TBitmap32PaintToolBrush.GetCursor(out Cursor: TCursor): boolean;
  193. begin
  194. Cursor := crCross;
  195. Result := True;
  196. GetVectorCursor;
  197. end;
  198. //------------------------------------------------------------------------------
  199. function TBitmap32PaintToolBrush.GetStep: integer;
  200. begin
  201. Result := 1;
  202. end;
  203. //------------------------------------------------------------------------------
  204. procedure TBitmap32PaintToolBrush.GetVectorCursor;
  205. begin
  206. FHasVectorCursor := CreateVectorCursor;
  207. end;
  208. //------------------------------------------------------------------------------
  209. procedure TBitmap32PaintToolBrush.BeginAction(const Context: IBitmap32PaintToolContext; var ToolState: TBitmap32PaintToolState);
  210. begin
  211. inherited;
  212. // Note: TRemapTransformation requires the width/height of the map to be greater than one!
  213. if ([ssLeft, ssRight] * Context.MouseParams.ShiftState <> []) and (Context.Buffer.Width > 1) and (Context.Buffer.Height > 1) then
  214. begin
  215. FLastPos := Context.MouseParams.BitmapPosFloat;
  216. FBrush := CreateBrush(ActiveColor(Context.MouseParams.ShiftState));
  217. FBrush.BeginBrush(Context.Buffer);
  218. ASSERT(FBrushLine = nil);
  219. FBrushLine := TPaintBrushLine.Create(Context.Buffer, FBrush, Step);
  220. FBrushLine.MoveTo(FLastPos.X, FLastPos.Y);
  221. FBrushLineState.Valid := False;
  222. PaintHost.Changed(Caption);
  223. end else
  224. ToolState := tsAbort;
  225. end;
  226. //------------------------------------------------------------------------------
  227. procedure TBitmap32PaintToolBrush.ContinueAction(const Context: IBitmap32PaintToolContext; var ToolState: TBitmap32PaintToolState);
  228. begin
  229. if ([ssLeft, ssRight] * Context.MouseParams.ShiftState <> []) then
  230. begin
  231. // Erase old line (via update of background)
  232. if (FBrushLineState.Valid) then
  233. begin
  234. Context.Buffer.BeginMeasuring(nil);
  235. try
  236. // Replay stroke without actually modifying anything
  237. FBrushLine.RestoreState(FBrushLineState);
  238. FBrushLine.LineTo(FLastPos.X, FLastPos.Y);
  239. finally
  240. Context.Buffer.EndMeasuring;
  241. end;
  242. end;
  243. // Save brush state so we can replay the stroke we're about to make when
  244. // the old line should be erased (via background repaint).
  245. FBrushLine.SaveState(FBrushLineState);
  246. FLastPos := Context.MouseParams.BitmapPosFloat;
  247. FBrushLine.LineTo(FLastPos.X, FLastPos.Y);
  248. PaintHost.Changed(Caption);
  249. end;
  250. end;
  251. //------------------------------------------------------------------------------
  252. procedure TBitmap32PaintToolBrush.EndAction(const Context: IBitmap32PaintToolContext; var ToolState: TBitmap32PaintToolState);
  253. begin
  254. (*
  255. ** [] Replace
  256. ** [ssShift] Transparent (blend)
  257. *)
  258. if (FLastPos <> Context.MouseParams.BitmapPosFloat) then
  259. begin
  260. FBrushLine.LineTo(Context.MouseParams.BitmapPosFloat.X, Context.MouseParams.BitmapPosFloat.Y);
  261. PaintHost.Changed(Caption);
  262. end;
  263. FreeAndNil(FBrushLine);
  264. FBrush.EndBrush;
  265. FreeAndNil(FBrush);
  266. end;
  267. //------------------------------------------------------------------------------
  268. //
  269. // TBitmap32PaintToolCircularBrush
  270. //
  271. //------------------------------------------------------------------------------
  272. constructor TBitmap32PaintToolCircularBrush.Create(const APaintHost: IBitmap32PaintHost);
  273. begin
  274. inherited;
  275. FAntiAlias := True;
  276. FHardness := 75;
  277. FStep := 1;
  278. end;
  279. //------------------------------------------------------------------------------
  280. function TBitmap32PaintToolCircularBrush.CreateBrush(Color: TColor32): TCustomPaintBrush;
  281. var
  282. Feather, Radius: Single;
  283. BrushCircle: TArrayOfFloatPoint;
  284. // Blender: IBitmap32Blender;
  285. begin
  286. // +2 to make room for antialiasing
  287. Result := GetBrushClass.Create(Ceil(BrushSize)+2, Ceil(BrushSize)+2);
  288. TBitmapPaintBrush(Result).Bitmap.Clear(0);
  289. if (AntiAlias) then
  290. Feather := BrushSize - BrushSize / 100 * Hardness
  291. else
  292. Feather := 0;
  293. Radius := (BrushSize - Feather) / 2;
  294. BrushCircle := Circle(Result.Width/2-0.5, Result.Height/2-0.5, Radius);
  295. PolygonFS(TBitmapPaintBrush(Result).Bitmap, BrushCircle, Color);
  296. if (Feather > 0) then
  297. Blur32(TBitmapPaintBrush(Result).Bitmap, Feather);
  298. (* Maybe later...
  299. DrawCircle(TBitmapPaintBrush(Result).Bitmap, Result.Width/2-0.5, Result.Height/2-0.5, Radius, Color, Color, 0, Feather, AntiAlias, True);
  300. *)
  301. (*
  302. Blender := BitmapEditorBlendService.BlenderByID(SettingValues['Blend']);
  303. if (Blender <> nil) then
  304. begin
  305. Blender.GetBlendFunc(FBlendFunc);
  306. if (Assigned(FBlendFunc)) then
  307. TBitmapPaintBrush(Result).BlendFunc := BlendWrapper
  308. else
  309. TBitmapPaintBrush(Result).BlendFunc := nil;
  310. end else
  311. *)
  312. TBitmapPaintBrush(Result).BlendFunc := nil;
  313. end;
  314. //------------------------------------------------------------------------------
  315. procedure TBitmap32PaintToolCircularBrush.BlendWrapper(F: TColor32; var B: TColor32; M: Cardinal);
  316. begin
  317. if (Assigned(FBlendFunc)) then
  318. FBlendFunc(F, B, M or $0000FF00); // Set source master alpha to 255
  319. end;
  320. //------------------------------------------------------------------------------
  321. function TBitmap32PaintToolCircularBrush.GetBrushClass: TBitmapBrushClass;
  322. begin
  323. Result := TBitmapPaintBrush;
  324. end;
  325. //------------------------------------------------------------------------------
  326. function TBitmap32PaintToolCircularBrush.GetStep: integer;
  327. begin
  328. Result := FStep;
  329. end;
  330. //------------------------------------------------------------------------------
  331. //
  332. // TBitmap32PaintToolSmudgeBrush
  333. //
  334. //------------------------------------------------------------------------------
  335. constructor TBitmap32PaintToolSmudgeBrush.Create(const APaintHost: IBitmap32PaintHost);
  336. begin
  337. inherited;
  338. FPressure := 50;
  339. end;
  340. //------------------------------------------------------------------------------
  341. function TBitmap32PaintToolSmudgeBrush.CreateBrush(
  342. Color: TColor32): TCustomPaintBrush;
  343. begin
  344. Result := inherited CreateBrush(Color or $FF000000);
  345. TSmudgePaintBrush(Result).Pressure := FPressure;
  346. end;
  347. //------------------------------------------------------------------------------
  348. function TBitmap32PaintToolSmudgeBrush.GetBrushClass: TBitmapBrushClass;
  349. begin
  350. Result := TSmudgePaintBrush;
  351. end;
  352. //------------------------------------------------------------------------------
  353. function TBitmap32PaintToolSmudgeBrush.GetCaption: string;
  354. begin
  355. Result := sBitmap32PaintToolSmudgeCaption;
  356. end;
  357. //------------------------------------------------------------------------------
  358. function TBitmap32PaintToolSmudgeBrush.GetCursor(out Cursor: TCursor): boolean;
  359. begin
  360. Cursor := crCross;
  361. Result := True;
  362. GetVectorCursor;
  363. end;
  364. //------------------------------------------------------------------------------
  365. function TBitmap32PaintToolSmudgeBrush.GetStep: integer;
  366. begin
  367. Result := 1;
  368. end;
  369. //------------------------------------------------------------------------------
  370. end.