将带有 Alpha 通道的图像复制到具有自定义背景颜色的剪贴板

本文关键字:自定义 颜色 剪贴板 背景 图像 Alpha 通道 复制 | 更新日期: 2023-09-27 18:35:36

Code :

private void Foo(Canvas canvas)
{
    // The content is a bit larger...
    Size size = new Size(canvas.ActualWidth * 1.1, canvas.ActualHeight * 1.2);
    // Create a render bitmap and push the surface to it
    RenderTargetBitmap renderBitmap =
        new RenderTargetBitmap(
        (int)size.Width,
        (int)size.Height,
        96d,
        96d,
        PixelFormats.Pbgra32
    );
    renderBitmap.Render(canvas);
    // Then copy to clipboard
    Clipboard.SetImage(renderBitmap);
}

我需要什么 :

将具有透明背景的画布渲染为图像,然后将其复制到剪贴板(退出简单?其实不然)

问题:

粘贴时,我得到一个黑色背景的丑陋图像

解决方案 1 :

canvas.Background = new SolidColorBrush(Colors.White);

不。这个厚不行,canvas的背景在接下来的renderBitmap.Render(canvas);不会改变

相反,我必须使用计时器,给 WPF 一些时间来更改背景,然后在该计时器的 tick 事件中呈现它。它有效,但不幸的是,canvas的内容大于它的大小......所以白色背景只能覆盖一部分,还是丑陋的结果。(顺便说一句,有人知道为什么需要一些时间来更改背景吗?我认为应该立即更改)

我做错了什么吗?如何在剪贴板中获得白色背景前透明图像?

更重要的是,我注意到,如果您将其粘贴到 mspaint 中,某些 PNG 图像的背景仍然是白色的.exe MSPAINT 不支持 alpha 通道,但其他一些图像会变成黑色。

有没有类似的东西,比如说,alternative color,如果您粘贴图像的位置不支持 alpha 通道,则用作背景?我们可以定制吗?

现在我渲染了另一个带有白色内容的BitmapSource,如果有办法将其与渲染位图作为背景相结合,问题就解决了,但我不知道如何......

int dWidth = (int)size.Width;
int dHeight = (int)size.Height;
int dStride = dWidth * 4;
byte[] pixels = new byte[dHeight * dStride];
for (int i = 0; i < pixels.Length; i++)
{
    pixels[i] = 0xFF;
}
BitmapSource bg = BitmapSource.Create(
    dWidth,
    dHeight,
    96,
    96,
    PixelFormats.Pbgra32,
    null,
    pixels,
    dStride
);
// Combine bg with renderBitmap

将带有 Alpha 通道的图像复制到具有自定义背景颜色的剪贴板

这是我

的最后一个解决方案,希望它能帮助其他人解决同样的问题

// Create a render bitmap and push the surface to it
RenderTargetBitmap renderBitmap =
    new RenderTargetBitmap(
    (int)size.Width,
    (int)size.Height,
    96d,
    96d,
    PixelFormats.Pbgra32
);
renderBitmap.Render(surface);
// Create a white background render bitmap
int dWidth = (int)size.Width;
int dHeight = (int)size.Height;
int dStride = dWidth * 4;
byte[] pixels = new byte[dHeight * dStride];
for (int i = 0; i < pixels.Length; i++)
{
    pixels[i] = 0xFF;
}
BitmapSource bg = BitmapSource.Create(
    dWidth,
    dHeight,
    96,
    96,
    PixelFormats.Pbgra32,
    null,
    pixels,
    dStride
);
// Adding those two render bitmap to the same drawing visual
DrawingVisual dv = new DrawingVisual();
DrawingContext dc = dv.RenderOpen();
dc.DrawImage(bg, new Rect(size));
dc.DrawImage(renderBitmap, new Rect(size));
dc.Close();
// Render the result
RenderTargetBitmap resultBitmap =
    new RenderTargetBitmap(
    (int)size.Width,
    (int)size.Height,
    96d,
    96d,
    PixelFormats.Pbgra32
);
resultBitmap.Render(dv);
// Copy it to clipboard
try
{
    Clipboard.SetImage(resultBitmap);
} catch { ... }

我找到了一个更小,更好读的解决方案,我在 https://social.msdn.microsoft.com/Forums/vstudio/en-US/a6972b7f-5ccb-422d-b203-134ef9f10084/how-to-capture-entire-usercontrol-image-to-clipboard?forum=wpf 找到了它:

// Create a render bitmap and push the surface to it
RenderTargetBitmap renderBitmap =
    new RenderTargetBitmap(
    (int)size.Width,
    (int)size.Height,
    96d,
    96d,
    PixelFormats.Pbgra32
);
// Render a white background into buffer for clipboard to avoid black background on some elements
Rectangle vRect = new Rectangle()
{
    Width = (int)size.Width,
    Height = (int)size.Height,
    Fill = Brushes.White,
};
vRect.Arrange(new Rect(size));
renderBitmap.Render(vRect);
// renderBitmap is now white, so render your object on it
renderBitmap.Render(surface);
// Copy it to clipboard
try
{
    Clipboard.SetImage(resultBitmap);
} catch { ... }