How do I copy a transparent TBitmap to the clipboard?
How do I copy a transparent TBitmap to the clipboard?
(Need a tested snippet as a fix or replacement for
unit CCR.FMXClipboard.Win
to copy a FMX TBitmap to the clipboard in XE4.)
There is a blog topic:
https://delphihaven.wordpress.com/2013/07/22/fmx-tclipboard-and-tmacpreferencesinifile-xe4/
which says: "On Windows again, when a source bitmap includes transparency, the outputted DIB has this transparency removed, however at the same time a PNG representation is added that maintains the original alpha channel."
My problem: the transparent png does not seem to work, I mean it cannot be pasted into Photoshop.
//using transparent background:
Viewport.Color := claNull;
procedure TForm1.CopyBitmap;
var
bmp: TBitmap;
r: TRect;
begin
r := Rect(0, 0, Viewport.Width, Viewport.Height);
bmp := TBitmap.Create(r.Width, r.Height);
Viewport.Context.CopyToBitmap(bmp, r);
//bmp.SaveToFile(filename); //works!
Clipboard.Assign(bmp); //DIB ok, not what I want
//Clipboard.Assign(cfPNG, ABitmap); //nope
bmp.Free;
end;
I can paste the DIB (without transparency) into Microsoft Paint, but this is no longer good enough. The new goal is to copy and paste a transparent png image (TBitmap) directly from my application to Photoshop Elements.
(Need a tested snippet as a fix or replacement for
unit CCR.FMXClipboard.Win
to copy a FMX TBitmap to the clipboard in XE4.)
There is a blog topic:
https://delphihaven.wordpress.com/2013/07/22/fmx-tclipboard-and-tmacpreferencesinifile-xe4/
which says: "On Windows again, when a source bitmap includes transparency, the outputted DIB has this transparency removed, however at the same time a PNG representation is added that maintains the original alpha channel."
My problem: the transparent png does not seem to work, I mean it cannot be pasted into Photoshop.
//using transparent background:
Viewport.Color := claNull;
procedure TForm1.CopyBitmap;
var
bmp: TBitmap;
r: TRect;
begin
r := Rect(0, 0, Viewport.Width, Viewport.Height);
bmp := TBitmap.Create(r.Width, r.Height);
Viewport.Context.CopyToBitmap(bmp, r);
//bmp.SaveToFile(filename); //works!
Clipboard.Assign(bmp); //DIB ok, not what I want
//Clipboard.Assign(cfPNG, ABitmap); //nope
bmp.Free;
end;
I can paste the DIB (without transparency) into Microsoft Paint, but this is no longer good enough. The new goal is to copy and paste a transparent png image (TBitmap) directly from my application to Photoshop Elements.
Untested, but an idea: you could try saving it to a stream - it streams in PNG format (it's a whole, valid PNG file in the stream.) Then copy the stream to the clipboard marking the data as PNG?
ReplyDeleteI tried this (to copy the png only), no error, but nothing can be pasted then in Paint or Photoshop. (Not quite sure how to mark it as png.) I think I need a tested snippet, have already googled for one, but nope :)
ReplyDeleteChris Rolliston is the author of the unit, btw.
ReplyDeletethe unit under test looks like so:
procedure TWinClipboard.DoAssignBitmap(const ABitmap: TBitmap);
begin
DoAssignBitmap1(ABitmap);
end;
//Trimmed down method
procedure TWinClipboard.DoAssignBitmap1(const ABitmap: TBitmap);
var
Stream: TMemoryStream;
begin
Stream := TMemoryStream.Create;
try
ABitmap.SaveToStream(Stream);
DoAssignBuffer(cfPNG, Stream.Memory^, Stream.Size);
finally
Stream.Free;
end;
end;
procedure TWinClipboard.DoAssignBuffer(const AFormat: TClipboardFormat; const ABuffer; ASize: Integer);
var
DataHandle: HGLOBAL;
Ptr: PByte;
begin
DataHandle := GlobalAlloc(GMEM_MOVEABLE or GMEM_DDESHARE, ASize);
if DataHandle = 0 then RaiseLastOSError;
Ptr := GlobalLock(DataHandle);
try
if Ptr = nil then
begin
GlobalFree(DataHandle);
RaiseLastOSError;
end;
Move(ABuffer, Ptr^, ASize);
finally
GlobalUnlock(DataHandle);
end;
SetClipboardData(AFormat, DataHandle);
end;
> "Not quite sure how to mark it as png."
ReplyDeleteSetClipboardData is where you set the format - try something like:
CF_PNG := RegisterClipboardFormat('PNG');
and then use that for the format parameter.
OK, progress, I managed to copy and paste a transparent png into Paint.net, but not yet into Photoshop Elements.
ReplyDeleteInteresting. It probably depends on the formats Photoshop is looking for. Can you copy a PNG in a way that already works for Photoshop, then use Clipboard Viewer to see what formats are on the clipboard? (I can't remember if that program shows the codes or not - can't access it right now - but if not there's bound to be a freeware utility somewhere that will show you the same info.)
ReplyDeleteSearched for "photoshop clipboard formats" and found
ReplyDeletehttp://stackoverflow.com/questions/12258519/how-to-save-pngimage-from-clipboard
I will use this as an entry point for further reading...
I confess I've never used Photoshop. If you can find a reliable way for Photoshop to accept transparent bitmaps via the clipboard I'll add it to the published CCR.FMXClipboard.Win code soon enough...
ReplyDeleteDid some reading, and some testing. TBitmap is a nice class, SafeToFile is the way to go. No change needed in CCR.FMXClipboard.Win.pas, You are simply not supposed to import a transparent Bitmap into Photoshop through the clipboard. Normally, Clipboard.Assign(cfPNG, ABitmap) is the alternative to SaveToFile for transparent images. (Just be aware of this overload before you complain.)
ReplyDelete:)
//Pseudocode used for testing
procedure CopyBitmapToClipboard(ABitmap: TBitmap);
begin
if HardCopyFlag then
ABitmap.SaveToFile(filename)
else if PngCopyFlag then
Clipboard.Assign(cfPNG, ABitmap)
else
Clipboard.Assign(ABitmap);
end;
//Tried to paste the copied image into
a) MS Paint, from Windows 8
b) PSE, Photoshop Elements Editor version 8.0
c) Paint.net, version 4.0.3
d) Gimp, version 2.8.14
1. CCR.FMXClipboard.pas / Clipboard.Assign(cfPNG, ABitmap)
Format: PNG (49288)
a) MS Paint no (expected)
b) PSE no (surprise)
c) Paint.net yes
d) Gimp yes
2. CCR.FMXClipboard.pas / Clipboard.Assign(ABitmap)
Format: CF_DIBV5
Width: 1024 px
Height: -768 px
Bit depth: 32 bpp
Compression: BI_BITFIELDS
a) MS Paint yes (w/o transparency)
b) PSE no (bummer)
c) Paint.net no (surprise)
d) Gimp yes (transparency preserved)
3. Image copied from MS Paint (not transparent)
Format: CF_DIBV5
Width: 1024 px
Height: 768 px
Bit depth: 24 bpp
Compression: BI_RGB
a) MS Paint yes (expected)
b) PSE (yes, if image is RGB)
c) Paint.net yes
d) Gimp yes
Findings:
* Clipboard.Assign(cfPNG, ABitmap) works great for transfer of transparent TBitmap to Paint.net and Gimp, but does not work with Photoshop at all.
* Keep new feature in App: Pass transparent png to Photoshop by redefining the meaning of "copy to clipboard" such that it uses SaveToFile with known location, followed by drag and drop from Explorer.
* Clipboard.Assign(ABitmap) works with MS Paint if there is no transparency.
* Perhaps CCR.FMXClipboard.Win should copy 24 bit DIBV5 for best support of TBitmap without Alpha?
Gustav Schubert Nice work! And very strange that PS won't paste those formats.
ReplyDeleteGustav Schubert
ReplyDeleteWhen using Clipboard.Assign(ABitmap) on a bitmap with transparency, the code puts both a PNG and a top-down DIBV5 onto the clipboard. IMO when reading from the clipboard Paint.NET should be doing what my code does (where should <> is!), which is to look for a PNG first, and a DIB only secondly as a fallback. What version of Paint.NET are you using out of interest?
Also, and without expecting this to work, but what happens if you copy a PNG image file in Windows Explorer - does Photoshop's Paste function work then?
Chris Rolliston
ReplyDelete1. When I copy the png image (saved from Delphi with SaveToFile)
from Explorer using right mouse button context menu, I have this on the clipboard:
Shell IDList Array (49328)
DataObjectAttributes (49507)
DataObjectAttributesRequiringElevation (49509)
Standard clipboard format (CF_HDROP)
FileName (49158)
FileContents (49331)
FileNameW (49159)
FileGroupDescriptorW (49333)
UIDisplayed (49356)
DropDescription (49353)
DropEffectFolderList (49348)
Shell Object Offsets (49329)
Preferred DropEffect (49338)
AsyncFlag (49355)
I can paste the image into paint.net v4.0.3,
but cannot paste into Photoshop Elements 8.0.
2. When using Clipboard.Assign(ABitmap) on a bitmap with transparency, transparency is lost when pasted into paint.net.
I see this on the clipboard:
PNG (49287)
Standard clipboard format (CF_DIBV5)
Standard clipboard format (CF_BITMAP)
Standard clipboard format (CF_DIB)
-----------------------------------------------------------------
Clipboard data of CF_DIBV5 format:
-----------------------------------------------------------------
Width: 1024 px
Height: -748 px
Bit depth: 32 bpp
Compression format: Not compressed with color masks (BI_BITFIELDS)
So paint.net may not behave as expected!
(3. Added "if HasTransparency then Break;"
to the outer loop where it tests for HasTransparency
in TWinClipboard.DoAssignBitmap. Just a cosmetic thing,
so that I can leave the finger on F7 when debugging.)
Gustav Schubert
ReplyDeleteThanks. I'll have a look at re-working the code to use the OLE clipboard API instead of the flat-function version, which will allow adding FileGroupDescriptorW and FileContents to the formats uploaded. Can you confirm your Paint.NET version though?
Chris Rolliston My Paint.NET version is 4.0.3, which I downloaded days ago.
ReplyDeleteGustav Schubert
ReplyDeleteSorry, I now see you had actually reported your Paint.NET version originally.
I've got a revised version of my code nearly ready; while I doubt it will help with Photoshop, the issue with Paint.NET you mention will be fixed. Main new feature will be delayed rendering support on desktop platforms, once I can figure out why Windows is prone to requesting a DIB immediately (which rather defeats the object of delayed rendering support)...
I googled "delayed rendering support"
ReplyDeletehttp://msdn.microsoft.com/en-us/library/cc241081.aspx
Gustav Schubert
ReplyDeleteYes, though nothing to do with RDP specificially. Instead of providing the data upfront, you provide a way for the system to request it when someone actually asks for it. Explicit delay rendering will be enabled with new Assign overloads, though internally data will always be delay rendered from the system point of view - i.e., use one of the existing assignment methods and the data will be held in memory by TClipboard until the system requests it.