Recent

Author Topic: How to copy bitmaps from/to the clipboard in Windows?  (Read 20971 times)

mishob

  • New member
  • *
  • Posts: 7
How to copy bitmaps from/to the clipboard in Windows?
« on: November 25, 2008, 08:26:37 pm »
Hello,

I have searched the forum and I know that the topic has been discussed several times but I could not find an apparent working solution for these issues.

I want to get an image that has been copied into the clipboard from an external graphics editor (Paint, GIMP for Windows, Photoshop), process it and then put it back to the clipboard so that it can be pasted to the original application or another one. Applications like Photoshop do not understand LCL’s internal clipboard formats, so I have to use CF_BITMAP, CF_DIB or a similar standard (for Windows) clipboard format.

I am using 32-bit Windows XP and Lazarus 0.9.26. It seems that at present LCL does not know how to handle predefined clipboard formats like CF_BITMAP. As far as I traced, LCL tries to get a MIME type for the corresponding clipboard format ID, and CF_BITMAP does not have one. Its format name is an empty string.

For getting a non-LCL bitmap from the clipboard, the following workaround does the job correctly in most cases (but not after Ctrl+PrtScr, for example):

Code: [Select]

  if Clipboard.FindPictureFormatID = Windows.CF_BITMAP then
  begin
    OpenClipboard(Handle); // Handle is my form's handle
    Image.Picture.Bitmap.Handle := HBITMAP(GetClipboardData(Windows.CF_BITMAP));
    CloseClipboard;
  end;


For the opposite (putting a bitmap into the clipboard so that it can be pasted into another Windows application), I have been unable to find a working solution. I have tried the following:

Code: [Select]

Clipboard.Assign(Image3.Picture.Bitmap); // Supposedly the correct way?
Clipboard.Assign(Image3.Picture);
Image3.Picture.SaveToClipboardFormat(PredefinedClipboardFormat(pcfBitmap));
Image3.Picture.Bitmap.SaveToClipboardFormat(PredefinedClipboardFormat(pcfBitmap));
Image3.Picture.Bitmap.SaveToClipboardFormat(CF_BITMAP);


After any of the above lines and choosing Edit | Paste in MS Paint, MS paint says ‘Error getting the Clipboard Data!’.

Code: [Select]

Image3.Picture.Bitmap.SaveToClipboardFormat(CF_DIB);


After this and Edit | Paste in MS Paint, I get an ‘Error getting the Clipboard Data!’ followed by ‘There is not enough memory or resources to complete operation. Close some programs, and then try again.’

Code: [Select]

Image3.Picture.Bitmap.SaveToClipboardFormat(PredefinedClipboardFormat(pcfPicture));


After this, Edit | Paste just stays disabled in MS Paint.

Apparently, LCL cannot put the bitmap into the clipboard in a format that is understandable for a non-LCL Windows application. I probably have to use Windows API for another workaround but I am unsure how exactly to proceed. The following does not work:

Code: [Select]

OpenClipboard(Handle);
EmptyClipboard;
SetClipboardData(Windows.CF_BITMAP, Image3.Picture.Bitmap.Handle);
CloseClipboard;    


After the above, MS Paint goes ‘Error getting the Clipboard Data!’ again.

Has anyone successfully used the clipboard to transfer bitmaps between an LCL application and another application in Windows?

Can you hint what is wrong with my second workaround and, hopefully, how to make it work?


Thanks in advance!

Phil

  • Hero Member
  • *****
  • Posts: 2750
Re: How to copy bitmaps from/to the clipboard in Windows?
« Reply #1 on: November 25, 2008, 09:07:52 pm »
I'm wondering if you're mixing your Win API calls with the LCL stuff.

Try using just the LCL stuff.

Try RegisterClipboardFormat with your mime type. Returned value should be useable with Clipboard.HasFormat and TPicture.LoadFromClipboardFormat.

Look at clipbrd.pp and graphics.pp.

Thanks.

-Phil

mishob

  • New member
  • *
  • Posts: 7
RE: Re: How to copy bitmaps from/to the clipboard in Windows
« Reply #2 on: November 25, 2008, 10:19:03 pm »
Thanks for your reply, Phil,

I don’t want to use my own MIME types; they would also be alien to Photoshop, MS Paint, and the like (the artists I am writing a tool for use Photoshop). It seems that using pure LCL without Windows API would not let me transfer bitmap data to/from many Windows applications.

GIMP for Windows understands some MIME types like 'image/png' or 'image/bmp' that work nicely with TPicture.LoadFromClipboardFormat. This does not seem to be the case with MS Paint, Photoshop, and others though. They expose their bitmaps through the clipboard using format IDs like CF_BITMAP, CF_DIB, and CF_DIBV5. These predefined, Windows-specific clipboard formats are unusable with LoadFromClipboardFormat (superficially – because it is not prepared to deal with clipboard format IDs with empty-string format names; but I suspect the issue is deeper, maybe something related with the LCL being platform-independent and not supporting every peculiarity of every platform).

I may be missing something but as far as I can tell, I just need to connect LCL to the native API code in a better way. The get-bitmap-from-clipboard workaround using GetClipboardData with TBitmap.Handle seems to work well enough. Apparently, the reverse should also work: using TBitmap.Handle with SetClipboardData to return an image to the clipboard.

Maybe I should prepare the handle somehow? It seems that in MFC, there is a method called CBitmap::GetSafeHandle() that is used together with SetClipboardData() but I am unaware of what it does. Maybe someone that has worked on the implementation of TBitmap.Handle will know more?

mishob

  • New member
  • *
  • Posts: 7
A working solution
« Reply #3 on: November 27, 2008, 01:38:09 pm »
I have reached some kind of a working solution and I decided to post it here, for what it’s worth.

If one looks closely in win32winapi.inc, it turns out that the LCL’s clipboard format pcfDelphiBitmap is ‘short-circuited’ to CF_BITMAP when copying from the clipboard, so we can do this entirely via LCL:

Code: [Select]

CF := Clipboard.FindPictureFormatID;
if CF = Windows.CF_BITMAP then // Handle CF_BITMAP separately
  Image.Picture.LoadFromClipboardFormat(PredefinedClipboardFormat(pcfDelphiBitmap))
else
  Image.Picture.LoadFromClipboardFormat(CF);


Unfortunately, the same does not work for sending the bitmap back to the clipboard. TWin32WidgetSet.ClipboardRegisterFormat converts pcfBitmap to Windows.CF_BITMAP and Clipboard.Assign(Image.Picture.Bitmap) puts some data on the clipboard and marks them as CF_BITMAP but the format of the data itself is apparently incorrect (no application can retrieve them and even Windows does not recognize the data for its implicit conversions to CF_DIB and CF_DIBV5).

One way to circumvent the issue is to copy the bitmap into one created using Windows API:

Code: [Select]

procedure TForm1.btnCopyResultClick(Sender: TObject);
var
  MemDC: HDC;
  MemBitmap, OldBitmap: HBITMAP;
  W, H: Integer;
begin
  W := Image3.Picture.Bitmap.Width;
  H := Image3.Picture.Bitmap.Height;

  MemDC := Windows.CreateCompatibleDC(Canvas.Handle);
  MemBitmap := Windows.CreateCompatibleBitmap(Canvas.Handle, W, H);
  OldBitmap := Windows.SelectObject(MemDC, MemBitmap);
  Windows.BitBlt(MemDC, 0, 0, W, H, Image3.Picture.Bitmap.Canvas.Handle, 0, 0, SRCCOPY);

  Windows.OpenClipboard(Handle);
  Windows.EmptyClipboard;
  Windows.SetClipboardData(CF_BITMAP, MemBitmap);
  Windows.CloseClipboard;

  Windows.SelectObject(MemDC, OldBitmap);
  Windows.DeleteDC(MemDC);
  Windows.DeleteObject(MemBitmap);
end;


This is quite ugly but it works (that is, Photoshop, MS Paint and other applications are able to fetch the bitmap from the clipboard).

Phil

  • Hero Member
  • *****
  • Posts: 2750
Re: A working solution
« Reply #4 on: November 27, 2008, 02:55:23 pm »
Quote from: "mishob"
Unfortunately, the same does not work for sending the bitmap back to the clipboard. TWin32WidgetSet.ClipboardRegisterFormat converts pcfBitmap to Windows.CF_BITMAP and Clipboard.Assign(Image.Picture.Bitmap) puts some data on the clipboard and marks them as CF_BITMAP but the format of the data itself is apparently incorrect (no application can retrieve them and even Windows does not recognize the data for its implicit conversions to CF_DIB and CF_DIBV5).


Could you please log all of this as a bug report on Mantis? That way the results of your investigation won't be lost!

Thanks.

-Phil

mishob

  • New member
  • *
  • Posts: 7
Re: A working solution
« Reply #5 on: November 28, 2008, 03:07:38 pm »
Quote from: "Phil"

Could you please log all of this as a bug report on Mantis? That way the results of your investigation won't be lost!


Done!
http://bugs.freepascal.org/view.php?id=12729

tReby

  • Newbie
  • Posts: 4
Re: A working solution
« Reply #6 on: January 23, 2009, 06:38:52 am »
What lib do i need to add in Uses???
can seem to run this code... :D


I have reached some kind of a working solution and I decided to post it here, for what it’s worth.

If one looks closely in win32winapi.inc, it turns out that the LCL’s clipboard format pcfDelphiBitmap is ‘short-circuited’ to CF_BITMAP when copying from the clipboard, so we can do this entirely via LCL:

Code: [Select]
CF := Clipboard.FindPictureFormatID;
if CF = Windows.CF_BITMAP then // Handle CF_BITMAP separately
  Image.Picture.LoadFromClipboardFormat(PredefinedClipboardFormat(pcfDelphiBitmap))
else
  Image.Picture.LoadFromClipboardFormat(CF);

Unfortunately, the same does not work for sending the bitmap back to the clipboard. TWin32WidgetSet.ClipboardRegisterFormat converts pcfBitmap to Windows.CF_BITMAP and Clipboard.Assign(Image.Picture.Bitmap) puts some data on the clipboard and marks them as CF_BITMAP but the format of the data itself is apparently incorrect (no application can retrieve them and even Windows does not recognize the data for its implicit conversions to CF_DIB and CF_DIBV5).

One way to circumvent the issue is to copy the bitmap into one created using Windows API:

Code: [Select]
procedure TForm1.btnCopyResultClick(Sender: TObject);
var
  MemDC: HDC;
  MemBitmap, OldBitmap: HBITMAP;
  W, H: Integer;
begin
  W := Image3.Picture.Bitmap.Width;
  H := Image3.Picture.Bitmap.Height;

  MemDC := Windows.CreateCompatibleDC(Canvas.Handle);
  MemBitmap := Windows.CreateCompatibleBitmap(Canvas.Handle, W, H);
  OldBitmap := Windows.SelectObject(MemDC, MemBitmap);
  Windows.BitBlt(MemDC, 0, 0, W, H, Image3.Picture.Bitmap.Canvas.Handle, 0, 0, SRCCOPY);

  Windows.OpenClipboard(Handle);
  Windows.EmptyClipboard;
  Windows.SetClipboardData(CF_BITMAP, MemBitmap);
  Windows.CloseClipboard;

  Windows.SelectObject(MemDC, OldBitmap);
  Windows.DeleteDC(MemDC);
  Windows.DeleteObject(MemBitmap);
end;

This is quite ugly but it works (that is, Photoshop, MS Paint and other applications are able to fetch the bitmap from the clipboard).

 

TinyPortal © 2005-2018