c# - 투명한 - wpf opacity mask



From and To Clipboard는 이미지 투명도를 잃습니다. (1)

기본적으로 Windows 클립 보드는 투명도를 지원하지 않지만 대부분의 응용 프로그램이 사용할 수있는 유형을 찾기 위해 여러 유형의 클립 보드에 내용을 함께 넣을 수 있습니다. 안타깝게도 가장 일반적인 유형 (Windows 자체가 사용하는 것)은 실제로 더럽고 신뢰할 수없는 형식입니다. 나는 그것에 대해 큰 호언 장담 설명을 here 에 썼다.

나는 당신이 다음 부분을 위해 필요한 배경 정보를 포함하고 있기 때문에 나의 대답을 계속하기 전에 그것을 읽었다 고 가정 할 것이다.

이제는 투명도 지원을 사용하여 클립 보드에 이미지를 가장 깨끗하게 놓는 방법은 PNG 스트림이지만 모든 응용 프로그램에서 붙여 넣을 수 있다고 보장 할 수는 없습니다. Gimp는 PNG 붙여 넣기를 지원하며, 더 최근의 MS Office 프로그램을 지원하지만, 예를 들어 Google 크롬은 연결되지 않은 DIB 유형 만 받아들입니다. 반면에, Gimp는 투명성을 갖는 것으로 DIB를 받아들이지 않을 것입니다. 왜냐하면 제작자가 실제로 형식 사양을 따르고 형식이 신뢰할 수 없다는 것을 깨닫게 되었기 때문입니다.

DIB 혼란 때문에 슬프게도 PNG, DIB 및 일반 이미지를 포함하여 최대한 많이 지원되는 유형을 단순히 거기에 넣는 것이 가장 좋습니다.

PNG와 DIB는 모두 같은 방식으로 클립 보드에 저장됩니다. DataObjectMemoryStream 을 넣은 다음 클립 보드에 실제로 복사 할 때 "복사"명령을 제공합니다.

대부분은 간단하지만 DIB는 다소 복잡합니다. 다음 부분에는 내 도구 세트에 대한 몇 가지 참조가 포함되어 있습니다. GetImageData 는이 답변 에서 찾을 수 있으며 BuildImage 는 here 에서 찾을 수 있으며 ArrayUtils 는 아래에 나와 있습니다.

/// <summary>
/// Copies the given image to the clipboard as PNG, DIB and standard Bitmap format.
/// </summary>
/// <param name="image">Image to put on the clipboard.</param>
/// <param name="imageNoTr">Optional specifically nontransparent version of the image to put on the clipboard.</param>
/// <param name="data">Clipboard data object to put the image into. Might already contain other stuff. Leave null to create a new one.</param>
public static void SetClipboardImage(Bitmap image, Bitmap imageNoTr, DataObject data)
{
    Clipboard.Clear();
    if (data == null)
        data = new DataObject();
    if (imageNoTr == null)
        imageNoTr = image;
    using (MemoryStream pngMemStream = new MemoryStream())
    using (MemoryStream dibMemStream = new MemoryStream())
    {
        // As standard bitmap, without transparency support
        data.SetData(DataFormats.Bitmap, true, imageNoTr);
        // As PNG. Gimp will prefer this over the other two.
        image.Save(pngMemStream, ImageFormat.Png);
        data.SetData("PNG", false, pngMemStream);
        // As DIB. This is (wrongly) accepted as ARGB by many applications.
        Byte[] dibData = ConvertToDib(image);
        dibMemStream.Write(dibData, 0, dibData.Length);
        data.SetData(DataFormats.Dib, false, dibMemStream);
        // The 'copy=true' argument means the MemoryStreams can be safely disposed after the operation.
        Clipboard.SetDataObject(data, true);
    }
}

/// <summary>
/// Converts the image to Device Independent Bitmap format of type BITFIELDS.
/// This is (wrongly) accepted by many applications as containing transparency,
/// so I'm abusing it for that.
/// </summary>
/// <param name="image">Image to convert to DIB</param>
/// <returns>The image converted to DIB, in bytes.</returns>
public static Byte[] ConvertToDib(Image image)
{
    Byte[] bm32bData;
    Int32 width = image.Width;
    Int32 height = image.Height;
    // Ensure image is 32bppARGB by painting it on a new 32bppARGB image.
    using (Bitmap bm32b = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppArgb))
    {
        using (Graphics gr = Graphics.FromImage(bm32b))
            gr.DrawImage(image, new Rectangle(0, 0, bm32b.Width, bm32b.Height));
        // Bitmap format has its lines reversed.
        bm32b.RotateFlip(RotateFlipType.Rotate180FlipX);
        Int32 stride;
        bm32bData = ImageUtils.GetImageData(bm32b, out stride);
    }
    // BITMAPINFOHEADER struct for DIB.
    Int32 hdrSize = 0x28;
    Byte[] fullImage = new Byte[hdrSize + 12 + bm32bData.Length];
    //Int32 biSize;
    ArrayUtils.WriteIntToByteArray(fullImage, 0x00, 4, true, (UInt32)hdrSize);
    //Int32 biWidth;
    ArrayUtils.WriteIntToByteArray(fullImage, 0x04, 4, true, (UInt32)width);
    //Int32 biHeight;
    ArrayUtils.WriteIntToByteArray(fullImage, 0x08, 4, true, (UInt32)height);
    //Int16 biPlanes;
    ArrayUtils.WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
    //Int16 biBitCount;
    ArrayUtils.WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
    //BITMAPCOMPRESSION biCompression = BITMAPCOMPRESSION.BITFIELDS;
    ArrayUtils.WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
    //Int32 biSizeImage;
    ArrayUtils.WriteIntToByteArray(fullImage, 0x14, 4, true, (UInt32)bm32bData.Length);
    // These are all 0. Since .net clears new arrays, don't bother writing them.
    //Int32 biXPelsPerMeter = 0;
    //Int32 biYPelsPerMeter = 0;
    //Int32 biClrUsed = 0;
    //Int32 biClrImportant = 0;

    // The aforementioned "BITFIELDS": colour masks applied to the Int32 pixel value to get the R, G and B values.
    ArrayUtils.WriteIntToByteArray(fullImage, hdrSize + 0, 4, true, 0x00FF0000);
    ArrayUtils.WriteIntToByteArray(fullImage, hdrSize + 4, 4, true, 0x0000FF00);
    ArrayUtils.WriteIntToByteArray(fullImage, hdrSize + 8, 4, true, 0x000000FF);
    Array.Copy(bm32bData, 0, fullImage, hdrSize + 12, bm32bData.Length);
    return fullImage;
}

지금, 클립 보드에서 이미지를 얻는 방법에 관해서는, 닷넷 3.5와 그 후의 DIB를 실제로 사용하는 것의 차이점이 분명히 있음을 발견했습니다. 그 차이를 감안할 때 DIB 형식의 신뢰성이 얼마나 떨어지는지를 고려하여 실제로 모든 유형을 수동으로 확인해야합니다. 바람직하게는 완전히 신뢰할 수있는 PNG 형식으로 시작하는 것이 좋습니다.

이 코드를 사용하여 클립 보드에서 DataObject 를 가져올 수 있습니다.

DataObject retrievedData = Clipboard.GetDataObject() as DataObject;

여기에 사용 된 CloneImage 함수는 기본적으로 내 GetImageDataBuildImage 도구 세트의 조합으로, 엉망이 될 수있는 백업 리소스없이 새 이미지가 생성되도록합니다. 이미지 객체는 Stream 을 기반으로 할 때 충돌을 일으키는 것으로 알려져 있습니다. 압축되고 최적화 된 버전 이이 복제가 왜 중요한지에 대한 주제에 대해 읽을 가치가있는 질문 에 게시 되었습니다.

/// <summary>
/// Retrieves an image from the given clipboard data object, in the order PNG, DIB, Bitmap, Image object.
/// </summary>
/// <param name="retrievedData">The clipboard data.</param>
/// <returns>The extracted image, or null if no supported image type was found.</returns>
public static Bitmap GetClipboardImage(DataObject retrievedData)
{
    Bitmap clipboardimage = null;
    // Order: try PNG, move on to try 32-bit ARGB DIB, then try the normal Bitmap and Image types.
    if (retrievedData.GetDataPresent("PNG"))
    {
        MemoryStream png_stream = retrievedData.GetData("PNG") as MemoryStream;
        if (png_stream != null)
            using (Bitmap bm = new Bitmap(png_stream))
                clipboardimage = ImageUtils.CloneImage(bm);
    }
    if (clipboardimage == null && retrievedData.GetDataPresent(DataFormats.Dib))
    {
        MemoryStream dib = retrievedData.GetData(DataFormats.Dib) as MemoryStream;
        if (dib != null)
            clipboardimage = ImageFromClipboardDib(dib.ToArray());
    }
    if (clipboardimage == null && retrievedData.GetDataPresent(DataFormats.Bitmap))
        clipboardimage = new Bitmap(retrievedData.GetData(DataFormats.Bitmap) as Image);
    if (clipboardimage == null && retrievedData.GetDataPresent(typeof(Image)))
        clipboardimage = new Bitmap(retrievedData.GetData(typeof(Image)) as Image);
    return clipboardimage;
}

public static Bitmap ImageFromClipboardDib(Byte[] dibBytes)
{
    if (dibBytes == null || dibBytes.Length < 4)
        return null;
    try
    {
        Int32 headerSize = (Int32)ArrayUtils.ReadIntFromByteArray(dibBytes, 0, 4, true);
        // Only supporting 40-byte DIB from clipboard
        if (headerSize != 40)
            return null;
        Byte[] header = new Byte[40];
        Array.Copy(dibBytes, header, 40);
        Int32 imageIndex = headerSize;
        Int32 width = (Int32)ArrayUtils.ReadIntFromByteArray(header, 0x04, 4, true);
        Int32 height = (Int32)ArrayUtils.ReadIntFromByteArray(header, 0x08, 4, true);
        Int16 planes = (Int16)ArrayUtils.ReadIntFromByteArray(header, 0x0C, 2, true);
        Int16 bitCount = (Int16)ArrayUtils.ReadIntFromByteArray(header, 0x0E, 2, true);
        //Compression: 0 = RGB; 3 = BITFIELDS.
        Int32 compression = (Int32)ArrayUtils.ReadIntFromByteArray(header, 0x10, 4, true);
        // Not dealing with non-standard formats.
        if (planes != 1 || (compression != 0 && compression != 3))
            return null;
        PixelFormat fmt;
        switch (bitCount)
        {
            case 32:
                fmt = PixelFormat.Format32bppRgb;
                break;
            case 24:
                fmt = PixelFormat.Format24bppRgb;
                break;
            case 16:
                fmt = PixelFormat.Format16bppRgb555;
                break;
            default:
                return null;
        }
        if (compression == 3)
            imageIndex += 12;
        if (dibBytes.Length < imageIndex)
            return null;
        Byte[] image = new Byte[dibBytes.Length - imageIndex];
        Array.Copy(dibBytes, imageIndex, image, 0, image.Length);
        // Classic stride: fit within blocks of 4 bytes.
        Int32 stride = (((((bitCount * width) + 7) / 8) + 3) / 4) * 4;
        if (compression == 3)
        {
            UInt32 redMask = ArrayUtils.ReadIntFromByteArray(dibBytes, headerSize + 0, 4, true);
            UInt32 greenMask = ArrayUtils.ReadIntFromByteArray(dibBytes, headerSize + 4, 4, true);
            UInt32 blueMask = ArrayUtils.ReadIntFromByteArray(dibBytes, headerSize + 8, 4, true);
            // Fix for the undocumented use of 32bppARGB disguised as BITFIELDS. Despite lacking an alpha bit field,
            // the alpha bytes are still filled in, without any header indication of alpha usage.
            // Pure 32-bit RGB: check if a switch to ARGB can be made by checking for non-zero alpha.
            // Admitted, this may give a mess if the alpha bits simply aren't cleared, but why the hell wouldn't it use 24bpp then?
            if (bitCount == 32 && redMask == 0xFF0000 && greenMask == 0x00FF00 && blueMask == 0x0000FF)
            {
                // Stride is always a multiple of 4; no need to take it into account for 32bpp.
                for (Int32 pix = 3; pix < image.Length; pix += 4)
                {
                    // 0 can mean transparent, but can also mean the alpha isn't filled in, so only check for non-zero alpha,
                    // which would indicate there is actual data in the alpha bytes.
                    if (image[pix] == 0)
                        continue;
                    fmt = PixelFormat.Format32bppPArgb;
                    break;
                }
            }
            else
                // Could be supported with a system that parses the colour masks,
                // but I don't think the clipboard ever uses these anyway.
                return null;
        }
        Bitmap bitmap = ImageUtils.BuildImage(image, width, height, stride, fmt, null, null);
        // This is bmp; reverse image lines.
        bitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
        return bitmap;
    }
    catch
    {
        return null;
    }
}

BitConverter 항상 시스템 엔디안에 대한 벙어리 체크가 필요하기 때문에 ArrayUtils 클래스에 내 자신의 ReadIntFromByteArrayWriteIntToByteArray 가 있습니다.

public static void WriteIntToByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian, UInt32 value)
{
    Int32 lastByte = bytes - 1;
    if (data.Length < startIndex + bytes)
        throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to write a " + bytes + "-byte value at offset " + startIndex + ".");
    for (Int32 index = 0; index < bytes; index++)
    {
        Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
        data[offs] = (Byte)(value >> (8 * index) & 0xFF);
    }
}

public static UInt32 ReadIntFromByteArray(Byte[] data, Int32 startIndex, Int32 bytes, Boolean littleEndian)
{
    Int32 lastByte = bytes - 1;
    if (data.Length < startIndex + bytes)
        throw new ArgumentOutOfRangeException("startIndex", "Data array is too small to read a " + bytes + "-byte value at offset " + startIndex + ".");
    UInt32 value = 0;
    for (Int32 index = 0; index < bytes; index++)
    {
        Int32 offs = startIndex + (littleEndian ? index : lastByte - index);
        value += (UInt32)(data[offs] << (8 * index));
    }
    return value;
}

투명 PNG 이미지를 클립 보드에 복사하고 투명도 유지하여 이를 지원하는 특정 프로그램에 붙여 넣으려고했습니다.

나는 이미 많은 해결책을 시도했지만 그 배경은 언제나 어떤면에서 회색으로 끝났습니다.

그래서 크롬을 사용하여 동일한 이미지를 복사하고 프로그램에 붙여 넣으려고했는데 효과가있었습니다. 그것은 투명성을 보존했다. 그래서 나는 크롬을 사용하여 복사 한 클립 보드에서 이미지를 얻으려고 시도했다. 이미지를 다시 설정하고 투명도가 아직 거기에 있음을 예상했다.하지만, 클립 보드에서 이미지를 가져 와서 설정했지만 투명도가 유지되지 않았다. 다시.

var img = Clipboard.GetImage(); // copied using Chrome and transparency is preserved
Clipboard.SetImage(img); // transparency lost

System.Windows.Forms.Clipboard 사용하거나 Image 대신 DataObject 를 가져오고 설정해도 동일한 문제가 발생합니다.





clipboard