【问题标题】:UWP Transparent png color overlayUWP透明png彩色叠加
【发布时间】:2016-03-21 16:26:12
【问题描述】:

我的 UWP c# 项目中有一个图像,它是一个带有白色前景的透明 png。我现在想将此 png 图像中的白色更改为另一种颜色(如蓝色)。

示例(请注意,彩色图像没有透明背景。这是由于我使用的图像处理软件不好,并演示了白色的变化。最终结果中的背景应该是透明的)。

我记得,这在统一中是可能的,现在我想在 uwp-app 中执行此操作。我考虑过使用Lumia ImagingSDK 或者Composition API,但不知道,用这两种方法都很热。

【问题讨论】:

  • 当然可以更改色调并在位图上进行图像处理,但是您需要这样做吗?为什么不预先生成您需要的所有色调?一个可能的答案是在这个线程中:stackoverflow.com/questions/14364716/… 它与色调一起使用 - 但我会认真考虑需要并权衡它与预加载所需色调的速度。
  • Why not pre-generate all the hues that you need? 是什么意思我想要相同的图像但颜色不同。而且我认为考虑到包装尺寸和成本,我改变颜色的方法可能适合这个。所以我说得对:您的方法是从 ARGB 转换为 HSV/HSL 颜色空间并在那里手动进行颜色操作?我希望,一些 MS API 中已经实现了一些功能。
  • 打开 Photoshop 并生成您需要的所有变体 - 然后您可以将它们预加载到您的应用程序中,并拥有比以编程方式更改色调更快的应用程序。但是,是的,这是一个考虑因素,您可能想要更少的内存使用或更高的性能。我不知道你正在构建的应用程序,在这种情况下,改变色调是一种快速而简洁的方式:)

标签: c# win-universal-app imaging


【解决方案1】:

一种方法是使用合成效果系统。

先决条件

  1. 至少以 build 10586 为目标(在此之前,Composition API 是实验性的)。
  2. 虽然没有严格要求,但对可视层有基本的了解不会有什么坏处。我写了一篇博文,介绍了这个话题here
  3. 添加 Win2D nuget 包。

此外,您可以查看我写的 here 的要点,这是在 XAML 应用程序中使用组合 API 快速启动和运行的方法。它也使用效果进行演示。不仅如此,它还包括使用 Composition API(使用我编写的包)加载图像。

入门

您需要做一些与要点非常相似的事情,但不是定义InvertEffect,而是同时定义CompositeEffectColorSourceEffect。这将做的是拍摄图像并将其用作“蒙版”,然后用颜色替换图像中的白色。你可以这样定义效果:

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 元素。如果您想查看可以在 XAML 标记中定义的自定义控件示例,该控件会在您调整控件大小时创建并调整您的视觉对象,您可以找到 here

要查看更多与作曲相关的内容,请访问我们的GitHub page!如果您对 API 有任何疑问,我们很乐意为您提供帮助。

【讨论】:

  • 这很好用,谢谢。我使用的是图像,而不是 this。该代码创建了一种漂亮的红色,之前它是透明的白色,就像我想要的那样。我设置了visual.Offset = new Vector3(0, 0, 0);,现在有两个问题:1.如何将新元素放在旧(原始)Image之前?使用当前代码,新图片将出现在旧图片的后面。 2.如何替换旧的(原)Image
  • @robmikh,这种效果是否已添加到合成示例应用程序中??是否有一些更新的参考代码可以让我们了解如何使用 Composition 进行颜色替换?这段代码有些过时,尤其是在使用 ImageLoaderFactory 时。就像 OP 说的那样,直接应用于 Image 控件会很好。
  • @Maximus:使用brush.SetSourceParameter("mask", AnImageControl.GetAlphaMask());
猜你喜欢
  • 2011-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-05-21
相关资源
最近更新 更多