0

I think I have a simple problem that seems very hard for me to figure out - how to check if an image I get from Clipboard.GetImage() uses transparency. If it does then I will show it in a PictureBox with the transparent background.

I copy the picture directly from the application - e.g. the browser or one of the Windows image viewers. Pasting the picture to e.g. Word will get the transparent background.

I am using this code:

// Check if the picture in clipboard has any transparency (alpha channel != 255)
Bitmap img = new Bitmap(Clipboard.GetImage());
for (int y = 0; y < img.Height; ++y)
{
    for (int x = 0; x < img.Width; ++x)
    {
        if (img.GetPixel(x, y).A != 255)
        {
            Debug.WriteLine("Picture is transparent - set breakpoint here");
        }
    }
}
...

// Show the picture with transparent background - this works fine
img.MakeTransparent(img.GetPixel(0,0));
myPictureBox.Image = (Image)img;

I am trying with various pictures found on the net and I can copy/paste those pictures with the transparent background so I know they are transparent but no pictures will trigger the Debug.WriteLine and all values equals 255?

Though I recognize this has been asked before then I must be doing something wrong since this simple example does not work? Also they are old so maybe there is a new and better way? I have tried to find other solutions besides these:

.. and more also not from StackOverflow. I have seen both really simple solutions and horrofying complex ones - but still none of them seems to work.

is this because the clipboard object cannot see the transparency or .. ?

Beauvais
  • 2,149
  • 4
  • 28
  • 63
  • How are you copying the picture into the clipboard? Are you checking the `PixelFormat` to see if the original `Image` has an alpha channel? – NetMage Jan 06 '21 at 20:57
  • I have updated my question. The picture does contain the alpha channel, I am sure, as I otherwise will not be able to paste it with the transparent background to e.g. Word or alike. – Beauvais Jan 06 '21 at 21:03
  • Your issue isn't with detecting transparency in the image. Your issue is with getting the right _representation_ of the image to begin with. Images copied to the clipboard are often formatted in multiple different ways, with varying amounts of information loss; `GetImage` only gets you one particular format, and in this case it's a format that does not preserve transparency. [This answer](https://stackoverflow.com/questions/44177115/copying-from-and-to-clipboard-loses-image-transparency) has some insight into getting a different format which preserves transparency. I think it's `Format17`. – Jeff Jan 06 '21 at 22:02
  • That isn't true - Word may understand formats that `GetImage` doesn't and the default for clipboard is DIB abused to hold the alpha channel: see [this code here](https://github.com/skoshy/CopyTransparentImages/blob/304e383b8f3239496999087421545a9b4dc765e5/ConsoleApp2/Program.cs#L58). – NetMage Jan 07 '21 at 01:20

1 Answers1

0

I ended up this solution, based on the comment from @Jeff, CopyTransparentImages. This will get the (real?) image from the clipboard (which will also include the alpha channel) and then I will check if the image contains any transparency afterwards. If it does then I will make the image background color transparent, according to my original question, before I show it in a PictureBox.

// Get the image formats from clipboard and check if it is transparent
Image imgCopy = GetImageFromClipboard();
bool isClipboardImageTransparent = IsImageTransparent(imgCopy);
if (isClipboardImageTransparent)
{
    ...
}

// Get the real image from clipboard (this supports the alpha channel)
private Image GetImageFromClipboard()
{
    if (Clipboard.GetDataObject() == null) return null;
    if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Dib))
    {

        // Sometimes getting the image data fails and results in a "System.NullReferenceException" error - probably because clipboard handling also can be messy and complex
        byte[] dib;
        try
        {
            dib = ((System.IO.MemoryStream)Clipboard.GetData(DataFormats.Dib)).ToArray();
        }
        catch (Exception ex)
        {
            return Clipboard.ContainsImage() ? Clipboard.GetImage() : null;
        }

        var width = BitConverter.ToInt32(dib, 4);
        var height = BitConverter.ToInt32(dib, 8);
        var bpp = BitConverter.ToInt16(dib, 14);
        if (bpp == 32)
        {
            var gch = GCHandle.Alloc(dib, GCHandleType.Pinned);
            Bitmap bmp = null;
            try
            {
                var ptr = new IntPtr((long)gch.AddrOfPinnedObject() + 40);
                bmp = new Bitmap(width, height, width * 4, System.Drawing.Imaging.PixelFormat.Format32bppArgb, ptr);
                return new Bitmap(bmp);
            }
            finally
            {
                gch.Free();
                if (bmp != null) bmp.Dispose();
            }
        }
    }
    return Clipboard.ContainsImage() ? Clipboard.GetImage() : null;
}

// Check if the image contains any transparency
private static bool IsImageTransparent(Image image)
{
    Bitmap img = new Bitmap(image);
    for (int y = 0; y < img.Height; ++y)
    {
        for (int x = 0; x < img.Width; ++x)
        {
            if (img.GetPixel(x, y).A != 255)
            {
                return true;
            }
        }
    }
    return false;
}

At least this is working for me :-)

Beauvais
  • 2,149
  • 4
  • 28
  • 63