UWP透明png颜色叠加

3
我在我的UWP c#项目中有一张透明的png图片,前景是白色。现在我想把这个png图片中的白色改成另一种颜色(比如蓝色)。
例如(请注意,彩色图像没有透明背景。这是由于我使用的糟糕的图像处理软件,并且为了演示白色颜色的变化。最终结果应该是透明的背景)。

bubble1 bubble2

我记得在Unity中可以实现这个功能,现在我想在UWP应用程序中实现。我考虑使用Lumia ImagingSDK或者Composition API,但是不知道如何使用它们来实现。


1
当然,可以更改位图的色调并进行图像处理,但您为什么需要这样做呢?为什么不预先生成所需的所有色调?这个线程中可能有一个可能的答案:https://dev59.com/eG3Xa4cB1Zd3GeqPc0re,其中涉及到色调等其他因素 - 但我会认真考虑需要并权衡其与预加载所需色调的速度。 - Pedro G. Dias
“为什么不预先生成所需的所有色调?”这句话是什么意思?我想要相同的图像,但颜色不同。考虑到包装尺寸和可定制性,我认为我的改变颜色的方法可能适合这种情况。所以,只是为了确保我理解正确:您的方法是将ARGB转换为HSV/HSL颜色空间,然后在那里手动进行颜色操作吗?我希望有一些已经在某些微软API中实现的函数。 - user3079834
打开Photoshop并生成您所需的所有变体-然后可以预加载它们到您的应用程序中,以比编程更快的方式改变色调。但是,是的,这是一个考虑因素,您可能希望减少内存使用或提高性能。我不知道您正在构建的应用程序,在这种情况下,更改色调是一种快速而整洁的方法来达到目的 :) - Pedro G. Dias
1个回答

5
一种实现此功能的方法是使用Composition效果系统。
先决条件:
1.至少针对10586版本进行目标定位(在此之前,Composition API是实验性的)。
2.虽然不是必需的,但具备基本的视觉层理解会更好。我写了一篇博客文章作为该主题的介绍here
3.添加Win2D nuget包。
另外,您可以查看我写的一个gisthere,这是一种在XAML应用程序中使用Composition API快速启动的方法。它还演示了使用效果。不仅如此,它还涵盖了使用Composition API加载图像(使用我编写的软件包)。
入门指南:
你需要做的与代码片段非常相似,但不是定义一个InvertEffect,而是同时定义一个CompositeEffect和一个ColorSourceEffect。这将使用图像作为“掩码”,并将图像中的白色替换为一种颜色。你可以像这样定义效果:
IGraphicsEffect graphicsEffect = new CompositeEffect
{
    Mode = Microsoft.Graphics.Canvas.CanvasComposite.DestinationIn,
    Sources =
    {
        new ColorSourceEffect
        {
            Name = "colorSource",
            Color = Color.FromArgb(255, 255, 255, 255)
        },
        new CompositionEffectSourceParameter("mask")
    }
};

下一步是创建一个效果工厂:
var effectFactory = compositor.CreateEffectFactory(graphicsEffect, new string[] { "colorSource.Color" });

第二个参数虽然不是必需的,但在这种情况下可能是您想要的。设置此参数允许您在编译效果后更改属性,从而使您可以手动设置它并创建每个新的效果画笔或对效果画笔上的属性进行动画处理。我们只需手动设置它。使用您的新效果工厂创建一个新的效果画笔。请注意,此工厂可以使用上面使用的定义创建许多新的效果画笔:

var effectBrush = effectFactory.CreateBrush();

但是,首先您需要将图像应用为蒙版。您可以使用我编写的名为 CompositionImageLoader 的库将图像加载到表面上。您也可以在 nuget 上下载它。创建带有图像的表面后,创建一个 CompositionSurfaceBrush 并将其应用于效果。

var imageLoader = ImageLoaderFactory.CreateImageLoader(compositor);

var surface = imageLoader.LoadImageFromUri(new Uri("ms-appx:///Assets/Images/HAvng.png"));
var brush = compositor.CreateSurfaceBrush(surface);

effectBrush.SetSourceParameter("mask", brush);

请注意,您应该将ImageLoader保存在某个地方,因为反复创建它将会很昂贵。剩下的事情就是将效果笔刷应用于可视对象并设置颜色:
visual.Brush = effectBrush;

effectBrush.Properties.InsertColor("colorSource.Color", Colors.Red);

然后你就完成了!请注意,如果您想在此之后更改颜色,只需使用新的颜色调用与上述相同的InsertColor方法即可。

最终产品

在我的测试代码中,该方法如下:

var compositor = ElementCompositionPreview.GetElementVisual(this).Compositor;
var visual = compositor.CreateSpriteVisual();

visual.Size = new Vector2(83, 86);
visual.Offset = new Vector3(50, 50, 0);

_imageLoader = ImageLoaderFactory.CreateImageLoader(compositor);

var surface = _imageLoader.LoadImageFromUri(new Uri("ms-appx:///Assets/Images/HAvng.png"));
var brush = compositor.CreateSurfaceBrush(surface);

IGraphicsEffect graphicsEffect = new CompositeEffect
{
    Mode = Microsoft.Graphics.Canvas.CanvasComposite.DestinationIn,
    Sources =
    {
        new ColorSourceEffect
        {
            Name = "colorSource",
            Color = Color.FromArgb(255, 255, 255, 255)
        },
        new CompositionEffectSourceParameter("mask")
    }
};

_effectFactory = compositor.CreateEffectFactory(graphicsEffect, new string[] { "colorSource.Color" });
var effectBrush = _effectFactory.CreateBrush();

effectBrush.SetSourceParameter("mask", brush);

visual.Brush = effectBrush;

effectBrush.Properties.InsertColor("colorSource.Color", Colors.Red);

ElementCompositionPreview.SetElementChildVisual(this, visual);

请注意,在此示例中,可视化对象附加到了this,也就是我的MainPage页面上。你可以将其附加到任何XAML元素上。如果你想看一个自定义控件的示例,它可以在你调整控件大小时创建并调整大小你的可视化对象,你可以在这里找到它。
如果你想了解更多有关Composition相关的信息,请访问我们的GitHub页面!我们将很乐意帮助你解决关于该API的任何问题。

这个很好用,谢谢。我之前使用的是一张图片而不是“this”。代码创建了一个漂亮的红色,之前是透明白色,就像我想要的那样。我设置了visual.Offset = new Vector3(0, 0, 0);现在有两个问题:1.如何将新元素放在旧(原始)“Image”之前?当前的代码中,新图片将出现在旧图像后面。2.如何替换旧(原始)“Image”? - user3079834
@robmikh,Composition示例应用程序中是否已添加了此类型的效果?我们可以查看一些更新的参考代码来了解如何使用Composition进行颜色替换吗?这段代码有点过时,特别是使用ImageLoaderFactory。正如OP所说,将其直接应用于图像控件会很好。 - Maximus
@Maximus:通过使用 brush.SetSourceParameter("mask", AnImageControl.GetAlphaMask()); - Gábor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接