默认情况下,Windows剪贴板不支持透明度,但你可以将内容以多种类型放入剪贴板,以确保大多数应用程序在其中找到一些可用的类型。不幸的是,最常见的类型
DeviceIndependentBitmap
(似乎是Windows本身使用的)非常混乱和不可靠。关于此问题我写了一个很长的
抱怨解释
这里。
我假设在继续回答之前,您已阅读了其中包含的背景信息,因为它包含了下一部分所需的背景信息。
现在,放置带有透明度支持的图像在剪贴板上的最清洁的方法是PNG流,但它不能保证所有应用程序都可以粘贴它。GIMP支持PNG粘贴,显然新的MS Office程序也支持,但是例如Google Chrome不支持,只会接受我提供链接的繁琐的DIB类型。另一方面,GIMP不会将DIB作为具有透明度的格式接受,因为其创建者实际上遵循了格式规范,并意识到该格式是不可靠的(正如我所提供链接的问题清楚地证明的那样)。
由于DIB的混乱,不幸的是,最好的方法只是尽可能将其以许多普遍支持的类型放入其中,包括PNG、DIB和正常的Image。
PNG和DIB都可以通过将它们作为
MemoryStream
放入
DataObject
中,然后在实际放置时给剪贴板“复制”指令来放入剪贴板中。
大部分都很简单,但 DIB 比较复杂。请注意,以下部分包含对我自己工具集的几个引用。可以在
此答案中 找到
GetImageData
,可以在
这里 找到
BuildImage
,
ArrayUtils
的方法如下。
这些工具集都使用 System.Drawing
。你需要自己弄清楚如何在 WPF 中完成相同的事情。
public static void SetClipboardImage(Bitmap image, Bitmap imageNoTr, DataObject data)
}
public static Byte[] ConvertToDib(Image image)
Int32 hdrSize = 0x28;
Byte[] fullImage = new Byte[hdrSize + 12 + bm32bData.Length];
ArrayUtils.WriteIntToByteArray(fullImage, 0x00, 4, true, (UInt32)hdrSize);
ArrayUtils.WriteIntToByteArray(fullImage, 0x04, 4, true, (UInt32)width);
ArrayUtils.WriteIntToByteArray(fullImage, 0x08, 4, true, (UInt32)height);
ArrayUtils.WriteIntToByteArray(fullImage, 0x0C, 2, true, 1);
ArrayUtils.WriteIntToByteArray(fullImage, 0x0E, 2, true, 32);
ArrayUtils.WriteIntToByteArray(fullImage, 0x10, 4, true, 3);
ArrayUtils.WriteIntToByteArray(fullImage, 0x14, 4, true, (UInt32)bm32bData.Length);
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;
}
现在,关于从剪贴板获取图像的问题,我注意到在.NET 3.5和后续版本之间似乎存在行为差异,后续版本似乎实际上使用了DIB。鉴于这种差异以及DIB格式的不可靠性,最好手动检查所有类型,最好从完全可靠的PNG格式开始。
您可以使用以下代码从剪贴板中获取
DataObject
:
DataObject retrievedData = Clipboard.GetDataObject() as DataObject;
这里使用的
CloneImage
函数基本上只是我
GetImageData
和
BuildImage
工具集的组合,确保创建一个没有任何可能引起混乱的后备资源的新图像;当基于一个然后被处理的
Stream
时,图像对象会导致崩溃。在这个主题上阅读值得的一个问题中
这里发布了它的压缩和优化版本。
public static Bitmap GetClipboardImage(DataObject retrievedData)
if (clipboardimage == null && retrievedData.GetDataPresent(DataFormats.Dib, false))
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 (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);
Int32 stride = (((((bitCount * width) + 7) / 8) + 3) / 4) * 4;
if (compression == 3)
}
else
return null;
}
Bitmap bitmap = ImageUtils.BuildImage(image, width, height, stride, fmt, null, null);
bitmap.RotateFlip(RotateFlipType.Rotate180FlipX);
return bitmap;
}
catch
}
因为 BitConverter
总是需要在系统字节序上进行愚蠢的检查,所以我在一个名为 ArrayUtils
的类中编写了自己的 ReadIntFromByteArray
和 WriteIntToByteArray
方法:
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;
}