【问题标题】:Black border around circle after copying bitmap to another bitmap将位图复制到另一个位图后,圆圈周围有黑色边框
【发布时间】:2014-08-15 17:00:04
【问题描述】:

我从 BitmapSource (RenderTargetBitmap) 中提取了位图,其中带有蓝色圆圈。 RenderTargetBitmap 是使用 PixelFormats.Pbgra32 创建的。

PixelFormats Pbgra32 将每个颜色通道预乘以 alpha 值。因此,当我尝试将位图转换为光标时,我得到的不透明图像比应有的要少。

我找到了here 问题的解决方案,它将位图克隆到Format24bppRgb 并手动设置 R、B、G 和 alpha 值。然而,解决方案工作得非常好,但对于克隆的位图,我看到视觉周围有黑色边框。

我可以去掉克隆位图中的黑色边框吗? (我怀疑是 SafeCopy 方法内部的东西)

链接中使用的方法是

private static void SafeCopy(BitmapData srcData, BitmapData dstData, byte alphaLevel)
{
  for (int y = 0; y < srcData.Height; y++)
    for (int x = 0; x < srcData.Width; x++)
    {
       byte b = Marshal.ReadByte(srcData.Scan0, y * srcData.Stride + x * 3);
       byte g = Marshal.ReadByte(srcData.Scan0, y * srcData.Stride + x * 3 + 1);
       byte r = Marshal.ReadByte(srcData.Scan0, y * srcData.Stride + x * 3 + 2);

       Marshal.WriteByte(dstData.Scan0, y * dstData.Stride + x * 4, b);
       Marshal.WriteByte(dstData.Scan0, y * dstData.Stride + x * 4 + 1, g);
       Marshal.WriteByte(dstData.Scan0, y * dstData.Stride + x * 4 + 2, r);
       Marshal.WriteByte(dstData.Scan0, y * dstData.Stride + x * 4 + 3, alphaLevel);
    }
}

private static Cursor CreateCustomCursorInternal(Bitmap bitmap, double opacity)
{
    Bitmap cursorBitmap = null;
    IconInfo iconInfo = new IconInfo();
    Rectangle rectangle = new Rectangle(0, 0, bitmap.Width, bitmap.Height);

    try
    {
        byte alphaLevel = System.Convert.ToByte(byte.MaxValue * opacity);

        // Here, the pre-multiplied alpha channel is specified
        cursorBitmap = new Bitmap(bitmap.Width, bitmap.Height, 
                                   PixelFormat.Format32bppPArgb);

        // Assuming the source bitmap can be locked in a 24 bits per pixel format
        BitmapData bitmapData = bitmap.LockBits(rectangle, ImageLockMode.ReadOnly, 
                                                PixelFormat.Format24bppRgb);
        BitmapData cursorBitmapData = cursorBitmap.LockBits(rectangle, 
                               ImageLockMode.WriteOnly, cursorBitmap.PixelFormat);

        // Use SafeCopy() to set the bitmap contents
        SafeCopy(bitmapData, cursorBitmapData, alphaLevel);

        cursorBitmap.UnlockBits(cursorBitmapData);
        bitmap.UnlockBits(bitmapData);

        .......
}

原始位图

克隆位图

【问题讨论】:

  • 通过转换为 24bpp RGB,您移除了 Alpha 通道,即移除了透明背景。那么您希望背景颜色是什么?
  • 没有cursorBitmap(克隆位图)仍然是Format32bppPArgb。只是我将源位图锁定在 24bpp 以避免预乘位,然后从源位图的 SafeCopy 方法中手动设置 cursorBitmap 上的 R、G、B 和 alpha 值。因此,克隆的位图仍然具有 alpha 值。
  • 当然,但是您(错误地)将相同的 alpha 值应用于所有像素。但是对于完全透明的背景像素,它必须是0。您可以简单地通过将每个像素 R、G 和 B 值乘以 255d/(double)A 来恢复预乘,而不是转换为 RGB。
  • 你想对所有像素应用不透明度,当然。但是透明背景的像素(圆圈外)在原始图像中的 alpha 值(即不透明度)为0。你不会想要改变它。他们应该保持这个价值。但是通过转换为 24bpp RGB,您可以有效地删除所有 alpha 值,这会留下黑色背景像素或背景像素所具有的任何(不可见)颜色。同样,转换为 24bpp 是错误的。它有效地消除了背景的透明度,这不是您想要的。
  • 或者更简单,转换为Format32bppArgb而不是Format24bppRgb,然后应用您的全局alpha值。这可能已经解决了所有问题。

标签: c# wpf bitmap


【解决方案1】:

将 WPF 32 位 PBGRA 位图转换为 WinForms PARGB 位图并同时应用全局不透明度的最简单方法似乎是将所有 A、R、G 和 B 值乘以不透明度因子(介于0 和 1) 如下所示的方法。但是,我原以为也有必要交换字节,但显然不是。

private static void CopyBufferWithOpacity(byte[] sourceBuffer,
    System.Drawing.Imaging.BitmapData targetBuffer, double opacity)
{
    for (int i = 0; i < sourceBuffer.Length; i++)
    {
        sourceBuffer[i] = (byte)Math.Round(opacity * sourceBuffer[i]);
    }

    Marshal.Copy(sourceBuffer, 0, targetBuffer.Scan0, sourceBuffer.Length);
}

给定一个 32 位 PBGRA 位图 pbgraBitmap(例如 RenderTargetBitmap),您将使用如下方法:

var width = pbgraBitmap.PixelWidth;
var height = pbgraBitmap.PixelHeight;
var stride = width * 4;
var buffer = new byte[stride * height];
pbgraBitmap.CopyPixels(buffer, stride, 0);

var targetFormat = System.Drawing.Imaging.PixelFormat.Format32bppPArgb;
var bitmap = new System.Drawing.Bitmap(width, height, targetFormat);
var bitmapData = bitmap.LockBits(
    new System.Drawing.Rectangle(0, 0, width, height),
    System.Drawing.Imaging.ImageLockMode.WriteOnly,
    targetFormat);

CopyBufferWithOpacity(buffer, bitmapData, 0.6);

bitmap.UnlockBits(bitmapData);

【讨论】:

  • +1。谢谢克莱门斯。一个快速的问题 - bgraBitmap 我得到的是 BitmapSource。对于光标我需要位图,所以我现在需要在Format24bppRgbFormat32bppPArgb 创建位图吗?
  • 从您的 cmets 我通过将源位图锁定为 Format32bppArgb 格式解决了我的问题,然后将单个 RGB 值 + 我的 alpha 值复制到目标位图,但 alpha 设置为 0 的像素除外。所以,只是对您在上面的答案中提到的这种方法感到好奇。
  • 您应该创建与bgraBitmap 格式相同的光标位图,即Format32bppRgba。不幸的是,我刚刚意识到没有这种格式。因此,您可能还需要交换像素缓冲区中的字节,然后使用Format32bppArgb
  • 理想情况下不应该是Format32bppBgra,因为我们已转换为 Bgra 格式吗?
  • 好的。另外我在想这两种方法有什么区别 - 1)使用您的公式删除预乘位并使用格式 Bgra32 再次生成 bitmapSource。 2) 创建 FormatConvertedBitmap,源设置为 PBgra32,并将其像素格式设置为 Bgra32。
猜你喜欢
  • 2011-05-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-16
  • 1970-01-01
  • 2021-06-24
  • 2019-03-29
  • 2011-12-28
相关资源
最近更新 更多