2
0

GR32.Paint.Controller.pas 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604
  1. unit GR32.Paint.Controller;
  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_System,
  38. GR32_Image,
  39. GR32.Paint.Host.API,
  40. GR32.Paint.Tool.API,
  41. GR32.Paint.Controller.API;
  42. //------------------------------------------------------------------------------
  43. //
  44. // TCustomBitmap32PaintController
  45. //
  46. //------------------------------------------------------------------------------
  47. // Sample paint controller base class.
  48. //------------------------------------------------------------------------------
  49. type
  50. TCustomBitmap32PaintController = class(TInterfacedObject, IBitmap32PaintController)
  51. strict private
  52. FPaintHost: IBitmap32PaintHost;
  53. // Cached feature capabilities
  54. FFeatureCursor: IBitmap32PaintFeatureCursor;
  55. FFeatureVectorCursor: IBitmap32PaintFeatureVectorCursor;
  56. strict protected
  57. property PaintHost: IBitmap32PaintHost read FPaintHost;
  58. strict private
  59. // Tool
  60. FPaintTool: IBitmap32PaintTool; // The selected tool
  61. FActivePaintTool: IBitmap32PaintTool; // The tool that is currently handling mouse messages. Nil if none.
  62. FActivePaintToolContext: IBitmap32PaintToolContext;
  63. strict protected
  64. procedure SetActivePaintTool(const Value: IBitmap32PaintTool);
  65. property PaintTool: IBitmap32PaintTool read FPaintTool;
  66. property ActivePaintTool: IBitmap32PaintTool read FActivePaintTool;
  67. property ActivePaintToolContext: IBitmap32PaintToolContext read FActivePaintToolContext;
  68. strict protected
  69. // Cursor
  70. procedure ShowToolCursor(AShow, ATransientChange: Boolean);
  71. procedure UpdateToolCursor;
  72. strict protected
  73. // IBitmap32PaintController
  74. function BeginOperation(const Context: IBitmap32PaintToolContext): boolean;
  75. function ContinueOperation(const Context: IBitmap32PaintToolContext): boolean;
  76. procedure EndOperation(Complete: boolean);
  77. procedure MouseDown(const Context: IBitmap32PaintToolContext; Button: TMouseButton);
  78. procedure MouseMove(const Context: IBitmap32PaintToolContext);
  79. procedure MouseUp(const Context: IBitmap32PaintToolContext; Button: TMouseButton);
  80. procedure MouseEnter;
  81. procedure MouseExit;
  82. function CreateToolContext: IBitmap32PaintToolContext;
  83. function GetPaintTool: IBitmap32PaintTool;
  84. procedure SetPaintTool(const Value: IBitmap32PaintTool);
  85. function GetActivePaintTool: IBitmap32PaintTool;
  86. function GetActivePaintToolContext: IBitmap32PaintToolContext;
  87. public
  88. constructor Create(const APaintHost: IBitmap32PaintHost);
  89. destructor Destroy; override;
  90. end;
  91. //------------------------------------------------------------------------------
  92. //
  93. // TBitmap32PaintController
  94. //
  95. //------------------------------------------------------------------------------
  96. // An example paint controller optimized for use with TImage32 and TImgView32.
  97. //------------------------------------------------------------------------------
  98. type
  99. TBitmap32PaintController = class(TCustomBitmap32PaintController, IBitmap32PaintController)
  100. strict private
  101. FImage: TCustomImage32;
  102. strict private
  103. // Update optimization
  104. FUpdateTimer: TStopwatch;
  105. strict protected
  106. function GetHasCapture: boolean;
  107. procedure SetHasCapture(const Value: boolean);
  108. strict protected
  109. // IBitmap32PaintController
  110. function BeginOperation(const Context: IBitmap32PaintToolContext): boolean;
  111. function ContinueOperation(const Context: IBitmap32PaintToolContext): boolean;
  112. procedure EndOperation(Complete: boolean);
  113. property HasCapture: boolean read GetHasCapture write SetHasCapture;
  114. public
  115. constructor Create(AImage: TCustomImage32; const APaintHost: IBitmap32PaintHost = nil);
  116. end;
  117. //------------------------------------------------------------------------------
  118. //
  119. // Global settings
  120. //
  121. //------------------------------------------------------------------------------
  122. var
  123. // Max time between repaints during MouseMove/ContinueOperation
  124. Bitmap32PaintControllerMaxUpdateInterval: Cardinal = 50; // mS (zero for continuous update)
  125. //------------------------------------------------------------------------------
  126. //------------------------------------------------------------------------------
  127. //------------------------------------------------------------------------------
  128. implementation
  129. uses
  130. {$if defined(MSWINDOWS)}
  131. Windows,
  132. {$ifend}
  133. SysUtils,
  134. GR32.Paint.Host;
  135. type
  136. TImage32Cracker = class(TCustomImage32);
  137. //------------------------------------------------------------------------------
  138. //
  139. // TCustomBitmap32PaintController
  140. //
  141. //------------------------------------------------------------------------------
  142. constructor TCustomBitmap32PaintController.Create(const APaintHost: IBitmap32PaintHost);
  143. begin
  144. inherited Create;
  145. FPaintHost := APaintHost;
  146. // Cache feature capabilities so we don't have to resolve these continously
  147. if (not Supports(FPaintHost, IBitmap32PaintFeatureCursor, FFeatureCursor)) then
  148. FFeatureCursor := nil;
  149. if (not Supports(FPaintHost, IBitmap32PaintFeatureVectorCursor, FFeatureVectorCursor)) then
  150. FFeatureVectorCursor := nil;
  151. end;
  152. destructor TCustomBitmap32PaintController.Destroy;
  153. begin
  154. SetPaintTool(nil);
  155. FPaintTool := nil; // In case the above failed
  156. FActivePaintTool := nil;
  157. FActivePaintToolContext := nil;
  158. inherited;
  159. end;
  160. //------------------------------------------------------------------------------
  161. function TCustomBitmap32PaintController.CreateToolContext: IBitmap32PaintToolContext;
  162. var
  163. Tool: IBitmap32PaintTool;
  164. begin
  165. if (FActivePaintTool <> nil) then
  166. Tool := FActivePaintTool
  167. else
  168. Tool := FPaintTool;
  169. if (Tool <> nil) then
  170. Result := FPaintHost.CreateToolContext(Tool)
  171. else
  172. Result := nil;
  173. end;
  174. //------------------------------------------------------------------------------
  175. function TCustomBitmap32PaintController.GetActivePaintTool: IBitmap32PaintTool;
  176. begin
  177. Result := FActivePaintTool;
  178. end;
  179. function TCustomBitmap32PaintController.GetActivePaintToolContext: IBitmap32PaintToolContext;
  180. begin
  181. Result := FActivePaintToolContext;
  182. end;
  183. procedure TCustomBitmap32PaintController.SetActivePaintTool(const Value: IBitmap32PaintTool);
  184. begin
  185. FActivePaintToolContext := nil;
  186. FActivePaintTool := Value;
  187. end;
  188. //------------------------------------------------------------------------------
  189. function TCustomBitmap32PaintController.GetPaintTool: IBitmap32PaintTool;
  190. begin
  191. Result := FPaintTool;
  192. end;
  193. procedure TCustomBitmap32PaintController.SetPaintTool(const Value: IBitmap32PaintTool);
  194. var
  195. Continue: boolean;
  196. begin
  197. if (Value = FPaintTool) then
  198. exit;
  199. if (FActivePaintTool <> nil) then
  200. EndOperation(False);
  201. // Only activate new tool if we managed to deactivate old tool - or if there was no old tool
  202. if (FActivePaintTool = nil) then
  203. begin
  204. // Hide old cursor
  205. ShowToolCursor(False, False);
  206. if (FPaintTool <> nil) then
  207. begin
  208. FPaintTool.Deactivate;
  209. FPaintTool := nil;
  210. end;
  211. if (Value <> nil) then
  212. begin
  213. Continue := True;
  214. Value.Activate(Continue);
  215. if (Continue) then
  216. begin
  217. FPaintTool := Value;
  218. // Display new cursor
  219. UpdateToolCursor;
  220. end;
  221. end;
  222. end;
  223. end;
  224. //------------------------------------------------------------------------------
  225. procedure TCustomBitmap32PaintController.ShowToolCursor(AShow, ATransientChange: boolean);
  226. begin
  227. if (FFeatureCursor <> nil) then
  228. FFeatureCursor.ShowToolCursor(AShow, ATransientChange);
  229. end;
  230. procedure TCustomBitmap32PaintController.UpdateToolCursor;
  231. var
  232. NewCursor: TCursor;
  233. begin
  234. if (FFeatureCursor = nil) then
  235. exit;
  236. // Note: Calling FPaintTool.GetCursor will automatically create
  237. // a complex cursor, if the tool supplies one.
  238. if (FPaintTool = nil) or (not FPaintTool.GetCursor(NewCursor)) then
  239. NewCursor := crDefault;
  240. FFeatureCursor.SetToolCursor(NewCursor);
  241. ShowToolCursor(True, False);
  242. end;
  243. //------------------------------------------------------------------------------
  244. function TCustomBitmap32PaintController.BeginOperation(const Context: IBitmap32PaintToolContext): boolean;
  245. var
  246. ToolState: TBitmap32PaintToolState;
  247. Continue: boolean;
  248. begin
  249. Assert(FPaintTool <> nil);
  250. Continue := True;
  251. Result := False;
  252. FPaintTool.BeginTool(Continue);
  253. try
  254. if (not Continue) then
  255. begin
  256. ToolState := tsAbort;
  257. Exit;
  258. end;
  259. ToolState := tsContinue;
  260. try
  261. FPaintTool.BeginAction(Context, ToolState);
  262. except
  263. ToolState := tsAbort;
  264. FPaintTool.EndAction(Context, ToolState);
  265. raise;
  266. end;
  267. case ToolState of
  268. tsComplete:
  269. ;
  270. tsAbort:
  271. ;
  272. tsContinue:
  273. begin
  274. SetActivePaintTool(FPaintTool);
  275. FActivePaintToolContext := Context;
  276. Result := True;
  277. end;
  278. end;
  279. finally
  280. if (not Result) then
  281. begin
  282. FPaintTool.EndTool;
  283. SetActivePaintTool(nil);
  284. end;
  285. end;
  286. end;
  287. //------------------------------------------------------------------------------
  288. function TCustomBitmap32PaintController.ContinueOperation(const Context: IBitmap32PaintToolContext): boolean;
  289. var
  290. ToolState: TBitmap32PaintToolState;
  291. begin
  292. if (FActivePaintTool = nil) then
  293. raise Exception.Create('Operation is not in progress');
  294. Result := False;
  295. ToolState := tsContinue;
  296. try
  297. try
  298. FActivePaintTool.ContinueAction(FActivePaintToolContext, ToolState);
  299. // Note: EndOperation may be called from Tool.ContinueAction so we must
  300. // not assume that the tool is still active when ContinueAction returns.
  301. if (FActivePaintTool = nil) then
  302. exit;
  303. except
  304. ToolState := tsAbort;
  305. raise;
  306. end;
  307. case ToolState of
  308. tsComplete:
  309. ;
  310. tsAbort:
  311. ;
  312. tsContinue:
  313. Result := True;
  314. end;
  315. finally
  316. if (not Result) and (FActivePaintTool <> nil) then
  317. begin
  318. FActivePaintTool.EndTool;
  319. SetActivePaintTool(nil);
  320. end;
  321. end;
  322. end;
  323. //------------------------------------------------------------------------------
  324. procedure TCustomBitmap32PaintController.EndOperation(Complete: boolean);
  325. var
  326. ToolState: TBitmap32PaintToolState;
  327. begin
  328. if (FActivePaintTool = nil) then
  329. exit; // TODO : Is this an error condition?
  330. if (Complete) then
  331. ToolState := tsComplete
  332. else
  333. ToolState := tsAbort;
  334. try
  335. try
  336. FActivePaintTool.EndAction(FActivePaintToolContext, ToolState);
  337. if (FActivePaintTool = nil) then
  338. exit;
  339. except
  340. ToolState := tsAbort;
  341. raise;
  342. end;
  343. case ToolState of
  344. tsComplete:
  345. ;
  346. tsAbort:
  347. ;
  348. end;
  349. finally
  350. if (ToolState <> tsContinue) and (FActivePaintTool <> nil) then
  351. begin
  352. FActivePaintTool.EndTool;
  353. SetActivePaintTool(nil);
  354. end;
  355. end;
  356. end;
  357. //------------------------------------------------------------------------------
  358. procedure TCustomBitmap32PaintController.MouseEnter;
  359. begin
  360. // Enable vector cursor
  361. ShowToolCursor(True, True);
  362. end;
  363. procedure TCustomBitmap32PaintController.MouseExit;
  364. begin
  365. // Disable vector cursor
  366. ShowToolCursor(False, True);
  367. end;
  368. //------------------------------------------------------------------------------
  369. procedure TCustomBitmap32PaintController.MouseDown(const Context: IBitmap32PaintToolContext; Button: TMouseButton);
  370. begin
  371. if (Context.PaintTool <> nil) then
  372. Context.PaintTool.MouseDown(Context, Button);
  373. end;
  374. procedure TCustomBitmap32PaintController.MouseMove(const Context: IBitmap32PaintToolContext);
  375. begin
  376. if (Context.PaintTool <> nil) then
  377. Context.PaintTool.MouseMove(Context);
  378. // Update cursor layer with most recent position
  379. if (FFeatureVectorCursor <> nil) then
  380. FFeatureVectorCursor.MoveToolVectorCursor(Context.MouseParams.ViewPortPos);
  381. end;
  382. procedure TCustomBitmap32PaintController.MouseUp(const Context: IBitmap32PaintToolContext; Button: TMouseButton);
  383. begin
  384. if (Context.PaintTool <> nil) then
  385. Context.PaintTool.MouseUp(Context, Button);
  386. end;
  387. //------------------------------------------------------------------------------
  388. //
  389. // TBitmap32PaintController
  390. //
  391. //------------------------------------------------------------------------------
  392. constructor TBitmap32PaintController.Create(AImage: TCustomImage32; const APaintHost: IBitmap32PaintHost);
  393. var
  394. PaintHost: IBitmap32PaintHost;
  395. begin
  396. if (APaintHost = nil) then
  397. // Embed
  398. PaintHost := TBitmap32PaintHost.Create(AImage)
  399. else
  400. // Inject
  401. PaintHost := APaintHost;
  402. inherited Create(PaintHost);
  403. FImage := AImage;
  404. end;
  405. //------------------------------------------------------------------------------
  406. function TBitmap32PaintController.BeginOperation(const Context: IBitmap32PaintToolContext): boolean;
  407. begin
  408. Result := inherited;
  409. if (Result) then
  410. begin
  411. // Reacquire capture in case tool did something stupid that caused us to lose it (e.g. Move Select tool)
  412. if (not (betfMouseCapture in PaintTool.ToolFeatures)) and (not HasCapture) then
  413. HasCapture := True;
  414. end;
  415. end;
  416. //------------------------------------------------------------------------------
  417. function TBitmap32PaintController.ContinueOperation(const Context: IBitmap32PaintToolContext): boolean;
  418. begin
  419. Result := inherited;
  420. if (Result) then
  421. begin
  422. // Reacquire capture in case tool did something stupid that caused us to lose it (e.g. Move Select tool)
  423. if (not (betfMouseCapture in PaintTool.ToolFeatures)) and (not HasCapture) then
  424. HasCapture := True;
  425. end;
  426. // Repaint ASAP to avoid lag caused by continous mouse messages during the operation.
  427. // The WM_PAINT messages are only generated once the message queue is otherwise empty or
  428. // UpdateWindow is called.
  429. if (not TImage32Cracker(FImage).CacheValid) or (not TImage32Cracker(FImage).BufferValid) then
  430. begin
  431. // Buffer has been invalidated. Limit how long we wait for an update.
  432. if (Bitmap32PaintControllerMaxUpdateInterval = 0) then
  433. // No wait; Update immediately
  434. FImage.Update
  435. else
  436. if (FUpdateTimer.IsRunning) then
  437. begin
  438. // Already waiting; Have we waited long enough?
  439. if (FUpdateTimer.ElapsedMilliseconds > Bitmap32PaintControllerMaxUpdateInterval) then
  440. begin
  441. FImage.Update;
  442. FUpdateTimer.Stop;
  443. end;
  444. end else
  445. // Not already waiting; Start timer
  446. FUpdateTimer := TStopwatch.StartNew;
  447. end else
  448. // Nothing to update; Don't wait.
  449. FUpdateTimer.Stop;
  450. end;
  451. //------------------------------------------------------------------------------
  452. procedure TBitmap32PaintController.EndOperation(Complete: boolean);
  453. begin
  454. inherited;
  455. // Ensure mouse capture is released (this takes care of right-button which TImage32 doesn't handle properly)
  456. // TODO : I'm not sure that this is necessary anymore but there's no harm in it
  457. if (ActivePaintTool = nil) and (HasCapture) then
  458. HasCapture := False;
  459. end;
  460. //------------------------------------------------------------------------------
  461. function TBitmap32PaintController.GetHasCapture: boolean;
  462. begin
  463. {$if defined(MSWINDOWS)}
  464. Result := (GetCapture = FImage.Handle);
  465. {$else}
  466. Result := (GetCaptureControl = FImage);
  467. {$ifend}
  468. end;
  469. procedure TBitmap32PaintController.SetHasCapture(const Value: boolean);
  470. begin
  471. {$if defined(MSWINDOWS)}
  472. if (Value) then
  473. SetCapture(FImage.Handle)
  474. else
  475. ReleaseCapture;
  476. {$else}
  477. if (Value) then
  478. SetCaptureControl(FImage)
  479. else
  480. SetCaptureControl(nil);
  481. {$ifend}
  482. end;
  483. //------------------------------------------------------------------------------
  484. end.