GR32_Clipboard.pas 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382
  1. unit GR32_Clipboard;
  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 Clipboard support 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-2022
  28. * the Initial Developer. All Rights Reserved.
  29. *
  30. * Contributor(s):
  31. *
  32. * ***** END LICENSE BLOCK ***** *)
  33. interface
  34. {$WARN SYMBOL_PLATFORM OFF}
  35. uses
  36. GR32;
  37. //------------------------------------------------------------------------------
  38. //
  39. // Clipboard functions
  40. //
  41. //------------------------------------------------------------------------------
  42. function CopyBitmap32ToClipboard(const Source: TCustomBitmap32): boolean;
  43. function PasteBitmap32FromClipboard(const Dest: TCustomBitmap32): boolean;
  44. function CanPasteBitmap32: boolean;
  45. function CanPasteBitmap32Alpha: boolean;
  46. //------------------------------------------------------------------------------
  47. //------------------------------------------------------------------------------
  48. //------------------------------------------------------------------------------
  49. implementation
  50. uses
  51. {$IFDEF FPC}
  52. LCLType,
  53. LCLIntf,
  54. {$ELSE FPC}
  55. Windows,
  56. {$ENDIF FPC}
  57. Classes,
  58. Graphics,
  59. Clipbrd,
  60. SysUtils,
  61. GR32_Resamplers;
  62. {$IFDEF FPC}
  63. const
  64. CF_DIBV5 = 17;
  65. {$ENDIF}
  66. {$IFNDEF FPC}
  67. type
  68. TGlobalMemoryStream = class(TCustomMemoryStream)
  69. private
  70. FHandle: HGlobal;
  71. FPointer: pointer;
  72. protected
  73. public
  74. constructor Create(const AHandle: HGlobal); overload;
  75. destructor Destroy; override;
  76. function Write(const Buffer; Count: Longint): Longint; override;
  77. property Handle: HGlobal read FHandle;
  78. end;
  79. constructor TGlobalMemoryStream.Create(const AHandle: HGlobal);
  80. begin
  81. inherited Create;
  82. FHandle := AHandle;
  83. FPointer := GlobalLock(Handle);
  84. if (FPointer = nil) then
  85. RaiseLastOSError;
  86. SetPointer(FPointer, GlobalSize(Handle));
  87. end;
  88. destructor TGlobalMemoryStream.Destroy;
  89. begin
  90. if (FPointer <> nil) then
  91. GlobalUnlock(Handle);
  92. inherited Destroy;
  93. end;
  94. function TGlobalMemoryStream.Write(const Buffer; Count: Integer): Longint;
  95. var
  96. Pos: Longint;
  97. begin
  98. Result := 0;
  99. if (Position >= 0) and (Count >= 0) then
  100. begin
  101. Pos := Position + Count;
  102. if Pos > 0 then
  103. begin
  104. if Pos > Size then
  105. begin
  106. FHandle := GlobalReAlloc(FHandle, Pos, GMEM_MOVEABLE);
  107. if (FHandle = 0) then
  108. RaiseLastOSError;
  109. FPointer := GlobalLock(FHandle);
  110. if (FPointer = nil) then
  111. RaiseLastOSError;
  112. SetPointer(FPointer, Pos);
  113. end;
  114. System.Move(Buffer, Pointer(Longint(FPointer) + Position)^, Count);
  115. Seek(Pos, soFromBeginning);
  116. Result := Count;
  117. end;
  118. end;
  119. end;
  120. {$ENDIF FPC}
  121. //------------------------------------------------------------------------------
  122. //
  123. // Clipboard functions
  124. //
  125. //------------------------------------------------------------------------------
  126. type
  127. TBitmap32Cracker = class(TCustomBitmap32);
  128. function CopyBitmap32ToClipboard(const Source: TCustomBitmap32): boolean;
  129. var
  130. Stream: TStream;
  131. {$IFNDEF FPC}
  132. Matte: TBitmap32;
  133. Bitmap: TBitmap;
  134. Size: integer;
  135. Handle: HGlobal;
  136. {$ENDIF FPC}
  137. begin
  138. Result := True;
  139. (*
  140. ** We place the following data on the clipboard:
  141. **
  142. ** - CF_BITMAP
  143. ** This is the source bitmap rendered onto a white background.
  144. ** Transparency is not retained.
  145. ** For use by applications that doesn't support Alpha.
  146. **
  147. ** - CF_DIBV5
  148. ** A 32 bit DIB with alpha. This alone can be used to recreate the original
  149. ** 32 bit bitmap, including alpha.
  150. ** This format provides round trip support.
  151. **
  152. ** Since Windows can synthesize between any of CF_DIB, CF_BITMAP and CF_DIBV5
  153. ** theoretically we could just supply the most capable format (CF_DIBV5) and
  154. ** let Windows supply the others. Unfortunately we need to supply both CF_DIBV5
  155. ** and CF_BITMAP/CF_DIB in order to work around various Windows bugs:
  156. **
  157. ** - When the clipboard synthesizes CF_DIBV5 from CF_DIB it uses BI_BITFIELDS.
  158. ** However, if the clipboard synthesizes CF_DIB from CF_DIBV5 with
  159. ** BI_BITFIELDS compression, the clipboard apparently forgets to take the
  160. ** extra 3 mask DWORDs into account which messes up the resulting DIB.
  161. **
  162. ** - When the clipboard synthesizes CF_DIB or CF_BITMAP from CF_DIBV5 it
  163. ** appears to require 68 extra bytes after the bitmap header.
  164. ** Inserting these 68 bytes would fix that but would also make the bitmap
  165. ** stream invalid for everything else.
  166. ** FWIW, 68 = SizeOf(BITMAPV4HEADER)-SizeOf(BITMAPINFOHEADER)...
  167. **
  168. ** As a bonus we get to control the background color of the CF_DIB/CF_BITMAP
  169. ** bitmap instead of the black one Windows would use.
  170. *)
  171. Clipboard.Open;
  172. try
  173. Clipboard.Clear;
  174. if (Source.Empty) then
  175. exit(False);
  176. {$IFNDEF FPC}
  177. // Render the bitmap onto a white background and copy it as CF_BITMAP.
  178. // Note: In some older versions of Windows it appears that the
  179. // clipboard gives priority to the synthesized CF_BITMAP over the
  180. // explicit CF_BITMAP.
  181. Bitmap := TBitmap.Create;
  182. try
  183. Matte := TBitmap32.Create;
  184. try
  185. Matte.SetSize(Source.Width, Source.Height);
  186. Matte.Clear(clWhite32);
  187. BlockTransfer(Matte, 0, 0, Matte.ClipRect, Source, Source.BoundsRect, dmBlend);
  188. Bitmap.Assign(Matte);
  189. finally
  190. Matte.Free;
  191. end;
  192. Clipboard.Assign(Bitmap);
  193. finally
  194. Bitmap.Free;
  195. end;
  196. // Allocate room for BI_BITFIELDS whether we use it or not. It's just 12 bytes.
  197. Size := SizeOf(TBitmapV5Header) + 3 * SizeOf(DWORD) + Source.Width * Source.Height * SizeOf(DWORD);
  198. Handle := GlobalAlloc(GMEM_MOVEABLE, Size);
  199. if (Handle = 0) then
  200. RaiseLastOSError;
  201. try
  202. // Copy the unaltered image as CF_DIBV5
  203. Stream := TGlobalMemoryStream.Create(Handle);
  204. try
  205. TBitmap32Cracker(Source).SaveToDIBStream(Stream);
  206. finally
  207. Stream.Free;
  208. end;
  209. Clipboard.SetAsHandle(CF_DIBV5, Handle);
  210. Handle := 0;
  211. except
  212. if (Handle <> 0) then
  213. GlobalFree(Handle);
  214. raise;
  215. end;
  216. {$ELSE FPC}
  217. Stream := TMemoryStream.Create;
  218. try
  219. Source.SaveToStream(Stream);
  220. Clipboard.AddFormat(PredefinedClipboardFormat(pcfBitmap), Stream);
  221. finally
  222. Stream.Free;
  223. end;
  224. {$ENDIF FPC}
  225. finally
  226. Clipboard.Close;
  227. end;
  228. end;
  229. //------------------------------------------------------------------------------
  230. function PasteBitmap32FromClipboard(const Dest: TCustomBitmap32): boolean;
  231. var
  232. Stream: TStream;
  233. {$IFNDEF FPC}
  234. Handle: HGlobal;
  235. Bitmap: TBitmap;
  236. {$ENDIF FPC}
  237. begin
  238. {$IFNDEF FPC}
  239. Result := False;
  240. if (Clipboard.HasFormat(CF_DIBV5)) then
  241. begin
  242. Dest.BeginUpdate;
  243. try
  244. Win32Check(OpenClipboard(0));
  245. try
  246. Handle := GetClipboardData(CF_DIBV5);
  247. if (Handle = 0) then
  248. RaiseLastOSError;
  249. Stream := TGlobalMemoryStream.Create(Handle);
  250. try
  251. Result := TBitmap32Cracker(Dest).LoadFromDIBStream(Stream, Stream.Size);
  252. finally
  253. Stream.Free;
  254. end;
  255. finally
  256. CloseClipboard;
  257. end;
  258. finally
  259. Dest.EndUpdate;
  260. Dest.Changed;
  261. end;
  262. end;
  263. if (not Result) and (Clipboard.HasFormat(CF_BITMAP)) then
  264. begin
  265. // Fall back to CF_BITMAP format.
  266. // Note: We must do an explicit assign to a bitmap or we risk that the
  267. // clipboard retrives the data in some other compatible format.
  268. // E.g. if the clipboard contains CF_METAFILE and CF_BITMAP and we do a
  269. // TBitmap32.Assign(Clipboard), then we end grabbing the CF_METAFILE data
  270. // leading to a rasterized copy of a metafile capture of a bitmap... Ugh!
  271. Dest.BeginUpdate;
  272. try
  273. Bitmap := TBitmap.Create;
  274. try
  275. Bitmap.Assign(Clipboard);
  276. Dest.Assign(Bitmap);
  277. finally
  278. Bitmap.Free;
  279. end;
  280. finally
  281. Dest.EndUpdate;
  282. Dest.Changed;
  283. end;
  284. Result := True;
  285. end;
  286. {$ELSE FPC}
  287. Stream := TMemoryStream.Create;
  288. try
  289. Clipboard.GetFormat(PredefinedClipboardFormat(pcfBitmap), Stream);
  290. Stream.Position := 0;
  291. Dest.LoadFromStream(Stream);
  292. finally
  293. Stream.Free;
  294. end;
  295. Result := True;
  296. {$ENDIF FPC}
  297. end;
  298. //------------------------------------------------------------------------------
  299. function CanPasteBitmap32: boolean;
  300. begin
  301. {$IFNDEF FPC}
  302. try
  303. Result:= Clipboard.HasFormat(CF_BITMAP) or Clipboard.HasFormat(CF_DIBV5);
  304. except
  305. on E: EClipboardException do
  306. Result := False; // Something else has the clipboard open
  307. end;
  308. {$ELSE FPC}
  309. Result := Clipboard.HasFormat(PredefinedClipboardFormat(pcfBitmap));
  310. {$ENDIF FPC}
  311. end;
  312. //------------------------------------------------------------------------------
  313. function CanPasteBitmap32Alpha: boolean;
  314. begin
  315. {$IFNDEF FPC}
  316. try
  317. Result:= Clipboard.HasFormat(CF_DIBV5);
  318. except
  319. on E: EClipboardException do
  320. Result := False; // Something else has the clipboard open
  321. end;
  322. {$ELSE FPC}
  323. Result := Clipboard.HasFormat(PredefinedClipboardFormat(pcfBitmap));
  324. {$ENDIF FPC}
  325. end;
  326. //------------------------------------------------------------------------------
  327. end.