Mombu the Programming Forum sponsored links

Go Back   Mombu the Programming Forum > Programming > Transparent Image Copy to Clipboard Adds Blue BG
User Name
Password
REGISTER NOW! Mark Forums Read

sponsored links


Reply
 
1 20th March 17:40
bill henning
External User
 
Posts: 1
Default Transparent Image Copy to Clipboard Adds Blue BG


Hi there... I am running in the .NET 1.0 framework have an object that has
an Image property. Upon a certain event, I want to copy the Image to the
Clipboard. This is easy enough:
Clipboard.SetDataObject(image)

However, if the Image has a transparent background, when I go to paste the
Image into a drawing app like Paint or Photoshop, the transparent area of
the Image has been replaced by a blue color. So it seems that the Image
isn't telling the clipboard what parts need to be transparent. This doesn't
make sense since it can draw the Image transparent areas fine.

Can anyone explain what is wrong here and if this is some sort of .NET
framework limitation, provide either C# or VB.NET code to work around it?

Thanks in advance!

Bill
  Reply With Quote


  sponsored links


2 20th March 17:40
michael phillips, jr.
External User
 
Posts: 1
Default Transparent Image Copy to Clipboard Adds Blue BG


The CF_DIBV5 clipboard format preserves the alpha channel
by design.

This clipboard format requires that you create
a packed DIB from a DIBSECTION created with the BITMAPV5HEADER.

You can use P-INVOKE with CreateDIBSection for your bitmap
and then transfer the bits with LockBits, BitBlt or DrawImage.

You create a global memory object to represent the packed DIB.
This packed DIB will include the BITMAPV5HEADER and the bitmap's
bits.

Place the global memory handle on the clipboard.

Clipboard.SetDataObject(image) places a BI_RGB bitmap
on the clipboard. Per the do***entation, the high byte in each
DWORD is not used.

There is no way for an aplication to natively know that the bitmap
that is retrieved from the clipboard with a CF_DIB or
CF_BITMAP clipboard format has an alpha channel.

An application could test but a CF_DIBV5 clipboard format
advertises that an alpha channel is present!
  Reply With Quote
3 20th March 17:41
bill henning
External User
 
Posts: 1
Default Transparent Image Copy to Clipboard Adds Blue BG


Thanks for the reply Michael. I'm not an expert with the GDI APIs so can


internal void CopyAlphaBitmapToClipboard(Bitmap image) {
BITMAPV4HEADER bih = new BITMAPV4HEADER();
bih.bV5Size =
System.Runtime.InteropServices.Marshal.SizeOf(type of(BITMAPV4HEADER));
bih.bV5Width = image.Width;
bih.bV5Height = image.Height;
bih.bV5Planes = 1;
bih.bV5BitCount = 32;
bih.bV5Compression = 0;
bih.bV5RedMask = 0x00FF0000;
bih.bV5GreenMask = 0x0000FF00;
bih.bV5BlueMask = 0x000000FF;
bih.bV5AlphaMask = 0xFF000000;
BITMAPINFO bi = new BITMAPINFO();
bi.bmiHeader = bih;

Graphics g = Graphics.FromImage(image);
IntPtr imageHdc = g.GetHdc();

IntPtr hdc = NativeMethods.CreateCompatibleDC(imageHdc);

// Create the DIB section with an alpha channel
IntPtr ppvBits = IntPtr.Zero;
const int DIB_RGB_COLORS = 0;
IntPtr hBitmap = NativeMethods.CreateDIBSection(hdc, bi, DIB_RGB_COLORS,
out ppvBits, IntPtr.Zero, 0);

const int SRCCOPY = 0x00CC0020;
NativeMethods.BitBlt(hdc, 0, 0, image.Width, image.Height, imageHdc, 0, 0,
SRCCOPY); // SRCCOPY
NativeMethods.DeleteDC(hdc);

g.ReleaseHdc(imageHdc);
g.Dispose();

// TODO: Do more stuff here
}

Is that correct so far or do you see any bugs with bad initializations or
memory leaks? I used the V4 header since I think it has the alpha stuff I
need and was less to define.

What I think I have in the procedure above is a handle to a bitmap (hBitmap)
that is still open. Somehow I need to send that to a SetClipboardData API
call. But I think I need a global memory handle as you said. What do I
need to add to get that working?

Thanks so much for your help! Bill
"Michael Phillips, Jr." <mphillips53@nospam.jun0.c0m> wrote in message news:uDNOSv0AGHA.2040@TK2MSFTNGP14.phx.gbl...
  Reply With Quote
4 20th March 22:21
michael phillips, jr.
External User
 
Posts: 1
Default Transparent Image Copy to Clipboard Adds Blue BG


I suggest the following:

public const uint BI_BITFIELDS = 3;
public const uint LCS_WINDOWS_COLOR_SPACE = 2;
public const uint LCS_GM_IMAGES = 4;
public const uint CF_DIBV5 = 17;
public const uint GMEM_MOVEABLE = 0x00000002;
public const uint GMEM_ZEROINIT = 0x00000040;
public const uint GMEM_DDESHARE = 0x00002000;
public const uint GHND = GMEM_MOVEABLE | GMEM_ZEROINIT;

[StructLayout(LayoutKind.Sequential)]
public struct CIEXYZ
{
public uint ciexyzX; //FXPT2DOT30
public uint ciexyzY; //FXPT2DOT30
public uint ciexyzZ; //FXPT2DOT30
}

[StructLayout(LayoutKind.Sequential)]
public struct CIEXYZTRIPLE
{
public CIEXYZ ciexyzRed;
public CIEXYZ ciexyzGreen;
public CIEXYZ ciexyzBlue;
}

[StructLayout(LayoutKind.Sequential)]
public struct BITFIELDS
{
public uint BlueMask;
public uint GreenMask;
public uint RedMask;
}

[StructLayout(LayoutKind.********)]
public struct BITMAPV5HEADER
{
[FieldOffset(0)] public uint bV5Size;
[FieldOffset(4)] public int bV5Width;
[FieldOffset(8)] public int bV5Height;
[FieldOffset(12)]public ushort bV5Planes;
[FieldOffset(14)]public ushort bV5BitCount;
[FieldOffset(16)]public uint bV5Compression;
[FieldOffset(20)]public uint bV5SizeImage;
[FieldOffset(24)]public int bV5XPelsPerMeter;
[FieldOffset(28)]public int bV5YPelsPerMeter;
[FieldOffset(32)]public uint bV5ClrUsed;
[FieldOffset(36)]public uint bV5ClrImportant;
[FieldOffset(40)]public uint bV5RedMask;
[FieldOffset(44)]public uint bV5GreenMask;
[FieldOffset(48)]public uint bV5BlueMask;
[FieldOffset(52)]public uint bV5AlphaMask;
[FieldOffset(56)]public uint bV5CSType;
[FieldOffset(60)]public CIEXYZTRIPLE bV5Endpoints;
[FieldOffset(96)]public uint bV5GammaRed;
[FieldOffset(100)]public uint bV5GammaGreen;
[FieldOffset(104)]public uint bV5GammaBlue;
[FieldOffset(108)]public uint bV5Intent;
[FieldOffset(112)]public uint bV5ProfileData;
[FieldOffset(116)]public uint bV5ProfileSize;
[FieldOffset(120)]public uint bV5Reserved;
}

[DllImport("user32.dll")]
static extern bool OpenClipboard(IntPtr hWndNewOwner);

[DllImport("user32.dll")]
static extern bool EmptyClipboard();

[DllImport("user32.dll")]
static extern bool CloseClipboard();

[DllImport("user32.dll")]
static extern IntPtr SetClipboardData(uint uFormat, IntPtr hMem);

[DllImport("kernel32.dll")]
static extern IntPtr GlobalAlloc(uint uFlags, uint dwBytes);

[DllImport("kernel32.dll")]
static extern IntPtr GlobalLock(IntPtr hMem);

[DllImport("kernel32.dll")]
static extern bool GlobalUnlock(IntPtr hMem);

public IntPtr CreatePackedDIBV5( Bitmap bm )
{
BitmapData bmData = bm.LockBits( new Rectangle(0,0,bm.Width, bm.Height),
ImageLockMode.ReadOnly, bm.PixelFormat);
uint bufferLen = (uint)(Marshal.SizeOf(typeof(BITMAPV5HEADER)) +
(Marshal.SizeOf(typeof(uint)) * 3) + bmData.Height * bmData.Stride);
IntPtr hMem = GlobalAlloc(GHND | GMEM_DDESHARE, bufferLen);
IntPtr packedDIBV5 = GlobalLock(hMem);
BITMAPV5HEADER bmi = (BITMAPV5HEADER)Marshal.PtrToStructure( packedDIBV5,
typeof(BITMAPV5HEADER));
bmi.bV5Size = (uint)Marshal.SizeOf(typeof(BITMAPV5HEADER));
bmi.bV5Width = bmData.Width;
bmi.bV5Height = bmData.Height;
bmi.bV5BitCount = 32;
bmi.bV5Planes = 1;
bmi.bV5Compression = BI_BITFIELDS;
bmi.bV5XPelsPerMeter = 0;
bmi.bV5YPelsPerMeter = 0;
bmi.bV5ClrUsed = 0;
bmi.bV5ClrImportant = 0;
bmi.bV5BlueMask = 0x000000FF;
bmi.bV5GreenMask = 0x0000FF00;
bmi.bV5RedMask = 0x00FF0000;
bmi.bV5AlphaMask = 0xFF000000;
bmi.bV5CSType = LCS_WINDOWS_COLOR_SPACE;
bmi.bV5GammaBlue = 0;
bmi.bV5GammaGreen = 0;
bmi.bV5GammaRed = 0;
bmi.bV5ProfileData = 0;
bmi.bV5ProfileSize = 0;
bmi.bV5Reserved = 0;
bmi.bV5Intent = LCS_GM_IMAGES;
bmi.bV5SizeImage = (uint)(bmData.Height * bmData.Stride);
bmi.bV5Endpoints.ciexyzBlue.ciexyzX =
bmi.bV5Endpoints.ciexyzBlue.ciexyzY =
bmi.bV5Endpoints.ciexyzBlue.ciexyzZ = 0;
bmi.bV5Endpoints.ciexyzGreen.ciexyzX =
bmi.bV5Endpoints.ciexyzGreen.ciexyzY =
bmi.bV5Endpoints.ciexyzGreen.ciexyzZ = 0;
bmi.bV5Endpoints.ciexyzRed.ciexyzX =
bmi.bV5Endpoints.ciexyzRed.ciexyzY =
bmi.bV5Endpoints.ciexyzRed.ciexyzZ = 0;
Marshal.StructureToPtr(bmi, packedDIBV5, false);

BITFIELDS Masks = (BITFIELDS)Marshal.PtrToStructure(
(IntPtr)(packedDIBV5.ToInt32() + bmi.bV5Size), typeof(BITFIELDS));
Masks.BlueMask = 0x000000FF;
Masks.GreenMask = 0x0000FF00;
Masks.RedMask = 0x00FF0000;
Marshal.StructureToPtr(Masks, (IntPtr)(packedDIBV5.ToInt32() +
bmi.bV5Size), false);

long offsetBits = bmi.bV5Size + Marshal.SizeOf(typeof(uint)) * 3;
IntPtr bits = (IntPtr)(packedDIBV5.ToInt32() + offsetBits);

for ( int y = 0; y < bmData.Height; y++ )
{
IntPtr DstDib = (IntPtr)(bits.ToInt32() + (y* bmData.Stride));
IntPtr SrcDib = (IntPtr)(bmData.Scan0.ToInt32() + ((bmData.Height-1-y)*
bmData.Stride));

for ( int x = 0; x < bmData.Width; x++ )
{
Marshal.WriteInt32(DstDib, Marshal.ReadInt32(SrcDib));
DstDib = (IntPtr)(DstDib.ToInt32() + 4);
SrcDib = (IntPtr)(SrcDib.ToInt32() + 4);
}
}

bm.UnlockBits(bmData);

GlobalUnlock(hMem);

return hMem;
}

An example on how to use this function follows:
IntPtr packedDIBV5 = CreatePackedDIBV5(AlphaBitmap);
OpenClipboard(this.Handle);
EmptyClipboard();
SetClipboardData(CF_DIBV5, packedDIBV5);
CloseClipboard();

I hope this helps!
  Reply With Quote
5 21st March 02:22
bill henning
External User
 
Posts: 1
Default Transparent Image Copy to Clipboard Adds Blue BG


Thanks Michael, I tried it out however when I paste an image that was copied
using this procedure, it seems that the pixels of the icons are offset to
the right a couple pixels and the transparent area is black.

I'm pasting using the .NET classes so is that perhaps the problem since
maybe Clipboard.GetDataObject messes up some of the data? I'm using
DataFormats.Bitmap as the format to retrieve.

Thanks again for your help. The code you posted is exactly what I want to
do to get the data to the clipboard. Now I just need to retrieve it again
into an Image class and have it render the same as when it was copied.

Bill
  Reply With Quote
6 21st March 02:22
michael phillips, jr.
External User
 
Posts: 1
Default Transparent Image Copy to Clipboard Adds Blue BG


The clipboard will synthesize the following formats
in addition to the CF_DIBV5:
CF_DIB and CF_BITMAP

The clipboard may not synthesize the other formats correctly.

A CF_DIBV5 containes a BITMAPV5HEADER + 3 DWORD bitfields +
the image's bits. The image compression is marked as BI_BITFIELDS
and therefore must contain a color table with 3 DWORDS for the mask.

This is redundant since the BITMAPV5HEADER contains the masks.

For 32bpp images, it is normally assumed that the image uses
the default masks with the compression set to BI_RGB.

You can try editing the code to use BI_RGB compression and get rid of the
bitfields. The result will be no offset of the image by 3 DWORDs.

The packed dib will look like the following:
BITMAPV5HEADER <---BI_RGB compression
bitmap bits


The MSDN do***entation
  Reply With Quote
7 21st March 07:23
bill henning
External User
 
Posts: 1
Default Transparent Image Copy to Clipboard Adds Blue BG


Getting closer.... removing the 3 bitfield offsets and changing to BI_RGB
fixed the offset problem with the pasted image. So now the image matches up
however the transparent background of the copied image still renders as
black when I paste it. Any ideas?

Bill
  Reply With Quote
8 21st March 07:23
michael phillips, jr.
External User
 
Posts: 1
Default Transparent Image Copy to Clipboard Adds Blue BG


When you paste a CF_DIBV5 image, you get a handle to
a packed DIB.

If the application is GDI based, that application must create
a bitmap with CreateDIBSection and then pre-multiply the alpha
channel against the colors and use the GDI AlphaBlend function
for rendering.

If the application is Gdiplus based, that application must create
a PixelFormat32bppARGB bitmap object and use DrawImage
for rendering.

For a BI_RGB bitmap, the high order byte (i.e., Alpha ) is not
used. If that byte is 0x00 (i.e., transparent ), then it will be rendered
as opaque. The background for the transparent portion of the bitmap
will be rendered as black.

The original Windows Bitmap 3.1 specification did not include a
provision for an alpha channel. Any application that receives
a paste of an alpha channel bitmap must be aware that the alpha
channel exists and render it accordingly.

That application can either ********ly test for the presence of an
alpha channel or can use the BITMAPV5HEADER structure
to ascertain whether or not that bitmap was created with an
alpha channel.

If you look further in this newsgroup, you will see an
algorithm that I posted to test for an alpha channel
bitmap with only a handle (i.e., HBITMAP ) as the
source.
  Reply With Quote
9 21st March 07:23
bill henning
External User
 
Posts: 1
Default Transparent Image Copy to Clipboard Adds Blue BG


Here's something I tried as an experiment and it worked... It captured the
alpha transparency fine. The only problem is that it flipped the image
over. So I added code to flip it back. Not sure why that happened. But do
you see any issues with doing this like memory leaks or anything?

Note: Our DrawingHelper.DrawImage method allows us to do a flip.


internal static Bitmap PasteAlphaBitmapFromClipboard() {
NativeMethods.OpenClipboard(IntPtr.Zero);
IntPtr hMem = NativeMethods.GetClipboardData(CF_DIBV5);
if (hMem == IntPtr.Zero) {
NativeMethods.CloseClipboard();
return null;
}

IntPtr packedDIBV5 = NativeMethods.GlobalLock(hMem);
BITMAPV5HEADER bmi = (BITMAPV5HEADER)Marshal.PtrToStructure(packedDIBV5 ,
typeof(BITMAPV5HEADER));

Bitmap bitmap = new Bitmap(bmi.bV5Width, bmi.bV5Height,
(int)(bmi.bV5SizeImage / bmi.bV5Height), PixelFormat.Format32bppArgb,
new IntPtr(packedDIBV5.ToInt32() + bmi.bV5Size));

Bitmap outputBitmap = new Bitmap(bmi.bV5Width, bmi.bV5Height,
PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(outputBitmap);
DrawingHelper.DrawImage(g, bitmap, 0, 0, bmi.bV5Width, bmi.bV5Height, 1.0f,
RotateFlipType.RotateNoneFlipY);
g.Dispose();

bitmap.Dispose();

NativeMethods.GlobalUnlock(hMem);
NativeMethods.CloseClipboard();

return outputBitmap;
}
  Reply With Quote
10 21st March 07:23
michael phillips, jr.
External User
 
Posts: 1
Default Transparent Image Copy to Clipboard Adds Blue BG


No issues, that is exactly how I do it in my own code.

Instead of:
DrawingHelper.DrawImage(g, bitmap, 0, 0, bmi.bV5Width, bmi.bV5Height, 1.0f,


Try this:
// note the negative stride
Bitmap bitmap = new Bitmap(bmi.bV5Width, bmi.bV5Height, -
(int)(bmi.bV5SizeImage / bmi.bV5Height), PixelFormat.Format32bppArgb,
new IntPtr(packedDIBV5.ToInt32() + bmi.bV5Size + (bmi.bV5Height-1)*
(int)(bmi.bV5SizeImage / bmi.bV5Height)));

Using a negative stride flips the orientation of the bitmap. Scan0 must
point to the
end of the bits.
  Reply With Quote
Reply


Thread Tools
Display Modes




Copyright 2006 SmartyDevil.com - Dies Mies Jeschet Boenedoesef Douvema Enitemaus -
666