bright
bright

Reputation: 4811

How to paste a transparent image from the clipboard in a C# winforms app?

Note: This question is about pasting from the clipboard, not copying to the clipboard. There are several posts about copying to the clipboard, but couldn't find one that addresses this question.

How can I paste an image with transparency, for example this one, into a winforms app and retain transparency?

I have tried using System.Windows.Forms.GetImage(), but that produces a bitmap with a black background.

I am copying this image from Google Chrome, which supports several clipboard formats, including DeviceIndependentBitmap and Format17.

Upvotes: 8

Views: 4291

Answers (2)

Andrey059918
Andrey059918

Reputation: 67

(I can't comment answers)

Answer of Hans Passant is good, but not correct.

Screenshot saved with this code.

You need to replace var ptr = new IntPtr((long)gch.AddrOfPinnedObject() + 40); with var ptr = new IntPtr((long)gch.AddrOfPinnedObject() + 52);

Screenshot saved with correct code.

Also you need flip image vertically

Upvotes: 1

Hans Passant
Hans Passant

Reputation: 941455

Chrome copies the image to the clipboard in a 24bpp format. Which turns the transparency into black. You can get a 32bpp format out of the clipboard but that requires handling the DIB format. There's no built-in support for that in System.Drawing, you need a little helper function that make the conversion:

    private Image GetImageFromClipboard() {
        if (Clipboard.GetDataObject() == null) return null;
        if (Clipboard.GetDataObject().GetDataPresent(DataFormats.Dib)) {
            var dib = ((System.IO.MemoryStream)Clipboard.GetData(DataFormats.Dib)).ToArray();
            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;
    }

Sample usage:

    protected override void OnPaint(PaintEventArgs e) {
        using (var bmp = GetImageFromClipboard()) {
            if (bmp != null) e.Graphics.DrawImage(bmp, 0, 0);
        }
    }

Which produced this screen-shot with the form's BackgroundImage property set to a stock bitmap:

enter image description here

Upvotes: 13

Related Questions