GR32_Backends_LCL_Gtk.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. unit GR32_Backends_LCL_Gtk;
  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 Backend Extension for Graphics32
  23. *
  24. * The Initial Developer of the Original Code is
  25. * Felipe Monteiro de Carvalho <[email protected]>
  26. *
  27. * Portions created by the Initial Developer are Copyright (C) 2007-2012
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. *
  32. * ***** END LICENSE BLOCK ***** *)
  33. interface
  34. {$I GR32.inc}
  35. {$DEFINE VerboseGR32GTK}
  36. uses
  37. LCLIntf, LCLType, types, Controls, SysUtils, Classes,
  38. {$IFDEF LCLGtk2}
  39. gdk2, gdk2pixbuf, glib2, gtk2Def,
  40. {$ELSE}
  41. gdk, gdkpixbuf, glib, gtkdef,
  42. {$ENDIF}
  43. Graphics, GraphType, FPImage, IntfGraphics,
  44. GR32, GR32_Backends, GR32_Containers, GR32_Image;
  45. type
  46. { TLCLBackend }
  47. TLCLBackend = class(TCustomBackend, IPaintSupport, ITextSupport,
  48. IFontSupport, ICanvasSupport, IDeviceContextSupport,
  49. IInteroperabilitySupport)
  50. private
  51. FFont: TFont;
  52. FOnFontChange: TNotifyEvent;
  53. FOnCanvasChange: TNotifyEvent;
  54. FWidth, FHeight: Cardinal;
  55. FRawImage: TRawImage;
  56. FBitmap: TBitmap;
  57. FPixmapDirty: boolean;
  58. FCanvasDirty: boolean;
  59. private
  60. procedure CanvasChangedHandler(Sender: TObject);
  61. procedure CanvasChanged;
  62. procedure CopyPixmapToCanvas;
  63. procedure CopyCanvasToPixmap;
  64. procedure NeedBits; {$IFDEF USEINLINING} inline; {$ENDIF}
  65. procedure NeedCanvas; {$IFDEF USEINLINING} inline; {$ENDIF}
  66. protected
  67. {$IFDEF BITS_GETTER}
  68. function GetBits: PColor32Array; override;
  69. {$ELSE BITS_GETTER}
  70. {$MESSAGE FATAL LCL GTK backend requires that BITS_GETTER is defined}
  71. {$ENDIF BITS_GETTER}
  72. procedure InitializeSurface(NewWidth, NewHeight: Integer; ClearBuffer: Boolean); override;
  73. procedure FinalizeSurface; override;
  74. protected
  75. // IPaintSupport
  76. procedure DoPaint(ABuffer: TBitmap32; AInvalidRects: TRectList; ACanvas: TCanvas; APaintBox: TCustomPaintBox32);
  77. procedure ImageNeeded;
  78. procedure CheckPixmap;
  79. protected
  80. // IDeviceContextSupport
  81. function GetHandle: HDC;
  82. procedure Draw(const DstRect, SrcRect: TRect; hSrc: HDC); overload;
  83. procedure DrawTo(hDst: HDC; DstX, DstY: Integer); overload;
  84. procedure DrawTo(hDst: HDC; const DstRect, SrcRect: TRect); overload;
  85. protected
  86. // ITextSupport
  87. procedure Textout(X, Y: Integer; const Text: string); overload;
  88. procedure Textout(X, Y: Integer; const ClipRect: TRect; const Text: string); overload;
  89. procedure Textout(var DstRect: TRect; const Flags: Cardinal; const Text: string); overload;
  90. function TextExtent(const Text: string): TSize;
  91. protected
  92. // IFontSupport
  93. function GetOnFontChange: TNotifyEvent;
  94. procedure SetOnFontChange(Handler: TNotifyEvent);
  95. function GetFont: TFont;
  96. procedure SetFont(const AFont: TFont);
  97. procedure UpdateFont;
  98. property Font: TFont read GetFont;
  99. protected
  100. // IInteroperabilitySupport
  101. function CopyFrom(ImageBitmap: TFPImageBitmap): Boolean; overload;
  102. function CopyFrom(Graphic: TGraphic): Boolean; overload;
  103. protected
  104. // ICanvasSupport
  105. function GetCanvasChange: TNotifyEvent;
  106. procedure SetCanvasChange(Handler: TNotifyEvent);
  107. function GetCanvas: TCanvas;
  108. function CanvasAllocated: Boolean;
  109. procedure DeleteCanvas;
  110. protected
  111. property Canvas: TCanvas read GetCanvas;
  112. public
  113. constructor Create; override;
  114. destructor Destroy; override;
  115. procedure Changed; override;
  116. function Empty: Boolean; override;
  117. end;
  118. implementation
  119. uses
  120. GR32_LowLevel;
  121. { TLCLBackend }
  122. constructor TLCLBackend.Create;
  123. begin
  124. inherited;
  125. end;
  126. destructor TLCLBackend.Destroy;
  127. begin
  128. FFont.Free;
  129. FBitmap.Free;
  130. inherited;
  131. end;
  132. procedure TLCLBackend.NeedBits;
  133. begin
  134. // If canvas was modified, copy it back to the pixmap
  135. if (FCanvasDirty) then
  136. CopyCanvasToPixmap;
  137. end;
  138. procedure TLCLBackend.NeedCanvas;
  139. begin
  140. if (FBitmap = nil) then
  141. begin
  142. FBitmap := TBitmap.Create;
  143. FBitmap.Canvas.Brush.Style := bsClear; // Otherwise text is drawn opaque
  144. FBitmap.Canvas.OnChange := CanvasChangedHandler;
  145. FBitmap.LoadFromRawImage(FRawImage, False);
  146. FPixmapDirty := False;
  147. FCanvasDirty := False;
  148. end;
  149. // If pixmap was modified ensure that canvas is up to date
  150. if (FPixmapDirty) then
  151. CopyPixmapToCanvas;
  152. end;
  153. procedure TLCLBackend.CopyCanvasToPixmap;
  154. var
  155. PixBuf: PGdkPixbuf;
  156. P: TPoint;
  157. SourceBits: pointer;
  158. begin
  159. // Allocate a new pixbuf, 8 bits per channel with alpha.
  160. PixBuf := gdk_pixbuf_new(GDK_COLORSPACE_RGB, True, 8, FWidth, FHeight);
  161. try
  162. P := TGtkDeviceContext(Canvas.Handle).Offset;
  163. // Fill the pixbuf with the canvas pixel data
  164. if (gdk_pixbuf_get_from_drawable(PixBuf,
  165. TGtkDeviceContext(Canvas.Handle).Drawable, nil,
  166. P.X,P.Y, 0,0, FWidth, FHeight) = nil) then
  167. raise Exception.Create('[TLCLBackend.CopyCanvasToPixmap] gdk_pixbuf_get_from_drawable failed');
  168. // Note: we cant directly assign data pointer to FBits here,
  169. // because the pointer will be soon disposed (see 'finally' below).
  170. // Instead, we should do copy the pixels (pointer content) to FBits to keep it accesible later.
  171. // Get a pointer to the pixbuf pixel data
  172. SourceBits := gdk_pixbuf_get_pixels(PixBuf);
  173. // Copy data (pointer content) from pixbuf to pixmap
  174. MoveLongword(SourceBits^, FBits^, FWidth*FHeight);
  175. finally
  176. g_object_unref(PixBuf);
  177. end;
  178. FPixmapDirty := False;
  179. FCanvasDirty := False;
  180. end;
  181. procedure TLCLBackend.CopyPixmapToCanvas;
  182. var
  183. P: TPoint;
  184. begin
  185. P := TGtkDeviceContext(FBitmap.Canvas.Handle).Offset;
  186. // Draw the pixbuf data onto the canvas.
  187. gdk_draw_rgb_32_image(TGtkDeviceContext(FBitmap.Canvas.Handle).Drawable,
  188. TGtkDeviceContext(FBitmap.Canvas.Handle).GC, P.X, P.Y, FWidth, FHeight,
  189. GDK_RGB_DITHER_NONE, pguchar(FBits), FWidth * SizeOf(TColor32));
  190. FPixmapDirty := False;
  191. FCanvasDirty := False;
  192. end;
  193. {$IFDEF BITS_GETTER}
  194. function TLCLBackend.GetBits: PColor32Array;
  195. begin
  196. NeedBits;
  197. Result := FBits;
  198. // Since caller now has direct access to the pixmap, assume it will be modified
  199. FPixmapDirty := True;
  200. end;
  201. {$ENDIF}
  202. procedure TLCLBackend.CanvasChangedHandler(Sender: TObject);
  203. begin
  204. CanvasChanged;
  205. end;
  206. procedure TLCLBackend.CanvasChanged;
  207. begin
  208. FCanvasDirty := True;
  209. if Assigned(FOnCanvasChange) then
  210. FOnCanvasChange(Self);
  211. end;
  212. procedure TLCLBackend.InitializeSurface(NewWidth, NewHeight: Integer; ClearBuffer: Boolean);
  213. begin
  214. { We allocate our own memory for the image }
  215. FRawImage.Init;
  216. {$ifdef RGBA_FORMAT}
  217. FRawImage.Description.Init_BPP32_R8G8B8A8_BIO_TTB(NewWidth, NewHeight);
  218. {$else RGBA_FORMAT}
  219. FRawImage.Description.Init_BPP32_B8G8R8A8_BIO_TTB(NewWidth, NewHeight);
  220. {$endif RGBA_FORMAT}
  221. FRawImage.CreateData(ClearBuffer);
  222. FBits := PColor32Array(FRawImage.Data);
  223. if (FBits = nil) then
  224. raise Exception.Create('[TLCLBackend.InitializeSurface] FBits = nil');
  225. FWidth := NewWidth;
  226. FHeight := NewHeight;
  227. FPixmapDirty := True;
  228. end;
  229. procedure TLCLBackend.FinalizeSurface;
  230. begin
  231. if (FBits <> nil) then
  232. begin
  233. FRawImage.FreeData;
  234. FBits := nil;
  235. FreeAndNil(FBitmap);
  236. end;
  237. end;
  238. procedure TLCLBackend.Changed;
  239. begin
  240. inherited;
  241. end;
  242. function TLCLBackend.Empty: Boolean;
  243. begin
  244. Result := (FBits = nil);
  245. end;
  246. { IPaintSupport }
  247. procedure TLCLBackend.ImageNeeded;
  248. begin
  249. // empty by purpose
  250. end;
  251. procedure TLCLBackend.CheckPixmap;
  252. begin
  253. // empty by purpose
  254. end;
  255. procedure TLCLBackend.DoPaint(ABuffer: TBitmap32; AInvalidRects: TRectList;
  256. ACanvas: TCanvas; APaintBox: TCustomPaintBox32);
  257. var
  258. P: TPoint;
  259. begin
  260. P := TGtkDeviceContext(ACanvas.Handle).Offset;
  261. gdk_draw_rgb_32_image(TGtkDeviceContext(ACanvas.Handle).Drawable,
  262. TGtkDeviceContext(ACanvas.Handle).GC, P.X, P.Y,
  263. ABuffer.Width, ABuffer.Height,
  264. GDK_RGB_DITHER_NONE, pguchar(ABuffer.Bits), ABuffer.Width * SizeOf(TColor32)
  265. );
  266. end;
  267. { IDeviceContextSupport }
  268. function TLCLBackend.GetHandle: HDC;
  269. begin
  270. Result := Canvas.Handle;
  271. end;
  272. procedure TLCLBackend.Draw(const DstRect, SrcRect: TRect; hSrc: HDC);
  273. begin
  274. NeedCanvas;
  275. StretchMaskBlt(FBitmap.Canvas.Handle, DstRect.Left, DstRect.Top,
  276. DstRect.Right - DstRect.Left, DstRect.Bottom - DstRect.Top,
  277. hSrc, SrcRect.Left, SrcRect.Top, SrcRect.Right - SrcRect.Left,
  278. SrcRect.Bottom - SrcRect.Top, 0, 0, 0, FBitmap.Canvas.CopyMode);
  279. end;
  280. procedure TLCLBackend.DrawTo(hDst: HDC; DstX, DstY: Integer);
  281. var
  282. P: TPoint;
  283. begin
  284. NeedBits;
  285. P := TGtkDeviceContext(hDst).Offset;
  286. Inc(DstX, P.X);
  287. Inc(DstY, P.Y);
  288. gdk_draw_rgb_32_image(TGtkDeviceContext(hDst).Drawable,
  289. TGtkDeviceContext(hDst).GC, DstX, DstY, FWidth, FHeight,
  290. GDK_RGB_DITHER_NONE, PGuChar(FBits), FWidth * SizeOf(TColor32)
  291. );
  292. end;
  293. procedure TLCLBackend.DrawTo(hDst: HDC; const DstRect, SrcRect: TRect);
  294. var
  295. P: TPoint;
  296. DR: TRect;
  297. begin
  298. NeedBits;
  299. P := TGtkDeviceContext(hDst).Offset;
  300. DR := DstRect;
  301. Inc(DR.Left , P.X);
  302. Inc(DR.Right, P.X);
  303. gdk_draw_rgb_32_image(TGtkDeviceContext(hDst).Drawable,
  304. TGtkDeviceContext(hDst).GC, DR.Left, DR.Top, SrcRect.Right - SrcRect.Left,
  305. SrcRect.Bottom - SrcRect.Top, GDK_RGB_DITHER_NONE, PGuChar(FBits),
  306. FWidth * SizeOf(TColor32)
  307. );
  308. end;
  309. { ITextSupport }
  310. procedure TLCLBackend.Textout(X, Y: Integer; const Text: string);
  311. begin
  312. if Empty then
  313. Exit;
  314. UpdateFont;
  315. if not FOwner.MeasuringMode then
  316. Canvas.TextOut(X, Y, Text);
  317. end;
  318. procedure TLCLBackend.Textout(X, Y: Integer; const ClipRect: TRect; const Text: string);
  319. begin
  320. if Empty then
  321. Exit;
  322. UpdateFont;
  323. LCLIntf.ExtTextOut(Canvas.Handle, X, Y, ETO_CLIPPED, @ClipRect, PChar(Text), Length(Text), nil);
  324. end;
  325. procedure TLCLBackend.Textout(var DstRect: TRect; const Flags: Cardinal; const Text: string);
  326. begin
  327. if Empty then
  328. Exit;
  329. UpdateFont;
  330. LCLIntf.DrawText(Canvas.Handle, PChar(Text), Length(Text), DstRect, Flags);
  331. end;
  332. function TLCLBackend.TextExtent(const Text: string): TSize;
  333. begin
  334. Result.cx := 0;
  335. Result.cy := 0;
  336. UpdateFont;
  337. Result := Canvas.TextExtent(Text);
  338. end;
  339. { IFontSupport }
  340. function TLCLBackend.GetOnFontChange: TNotifyEvent;
  341. begin
  342. Result := Font.OnChange;
  343. end;
  344. procedure TLCLBackend.SetOnFontChange(Handler: TNotifyEvent);
  345. begin
  346. Font.OnChange := Handler;
  347. end;
  348. function TLCLBackend.GetFont: TFont;
  349. begin
  350. if (FFont = nil) then
  351. FFont := TFont.Create;
  352. Result := FFont;
  353. end;
  354. procedure TLCLBackend.SetFont(const AFont: TFont);
  355. begin
  356. Font.Assign(AFont);
  357. end;
  358. procedure TLCLBackend.UpdateFont;
  359. begin
  360. Font.OnChange := FOnFontChange;
  361. Canvas.Font := FFont;
  362. end;
  363. { IInteroperabilitySupport }
  364. type
  365. TGraphicAccess = class(TGraphic);
  366. function TLCLBackend.CopyFrom(ImageBitmap: TFPImageBitmap): Boolean;
  367. var
  368. Src: TLazIntfImage;
  369. X, Y: Integer;
  370. SrcLine: PCardinalArray;
  371. DestLine: PByte;
  372. begin
  373. NeedBits; // Ensure that pixmap is up to date
  374. Src := ImageBitmap.CreateIntfImage;
  375. try
  376. if ImageBitmap.Transparent then
  377. begin
  378. for Y := 0 to Src.Height - 1 do
  379. begin
  380. SrcLine := Src.GetDataLineStart(Y);
  381. DestLine := FRawImage.GetLineStart(Y);
  382. for X := 0 to Src.Width - 1 do
  383. begin
  384. DestLine^ := BlueComponent(SrcLine^[X]);
  385. Inc(DestLine);
  386. DestLine^ := GreenComponent(SrcLine^[X]);
  387. Inc(DestLine);
  388. DestLine^ := RedComponent(SrcLine^[X]);
  389. Inc(DestLine);
  390. DestLine^ := AlphaComponent(SrcLine^[X]);
  391. Inc(DestLine);
  392. end;
  393. end;
  394. end
  395. else
  396. begin
  397. for Y := 0 to Src.Height - 1 do
  398. begin
  399. SrcLine := Src.GetDataLineStart(Y);
  400. DestLine := FRawImage.GetLineStart(Y);
  401. for X := 0 to Src.Width - 1 do
  402. begin
  403. DestLine^ := BlueComponent(SrcLine^[X]);
  404. Inc(DestLine);
  405. DestLine^ := GreenComponent(SrcLine^[X]);
  406. Inc(DestLine);
  407. DestLine^ := RedComponent(SrcLine^[X]);
  408. Inc(DestLine);
  409. DestLine^ := $FF;
  410. Inc(DestLine);
  411. end;
  412. end;
  413. end;
  414. finally
  415. Src.Free;
  416. end;
  417. Result := True;
  418. end;
  419. function TLCLBackend.CopyFrom(Graphic: TGraphic): Boolean;
  420. begin
  421. Result := (Graphic is TFPImageBitmap) and CopyFrom(TFPImageBitmap(Graphic));
  422. if not Result then
  423. begin
  424. NeedCanvas;
  425. TGraphicAccess(Graphic).Draw(FBitmap.Canvas, MakeRect(0, 0, FBitmap.Canvas.Width, FBitmap.Canvas.Height));
  426. Result := True;
  427. end;
  428. end;
  429. { ICanvasSupport }
  430. function TLCLBackend.GetCanvasChange: TNotifyEvent;
  431. begin
  432. Result := FOnCanvasChange;
  433. end;
  434. procedure TLCLBackend.SetCanvasChange(Handler: TNotifyEvent);
  435. begin
  436. FOnCanvasChange := Handler;
  437. end;
  438. function TLCLBackend.GetCanvas: TCanvas;
  439. begin
  440. NeedCanvas;
  441. Result := FBitmap.Canvas;
  442. end;
  443. function TLCLBackend.CanvasAllocated: Boolean;
  444. begin
  445. result := GetCanvas() <> nil;
  446. end;
  447. procedure TLCLBackend.DeleteCanvas;
  448. begin
  449. end;
  450. end.