Jump to content
Paint.NET 5.1 is now available! ×

Best way to blend colors in a BitmapEffect?


Go to solution Solved by Rick Brewster,

Recommended Posts

Posted

Granted, the math to just do a linear interpolation between 2 colors is not difficult or cumbersome for the most basic blend, but I'm wondering if I'm missing some built in utility.

 

I do know of the ColorBgra.Blend method, but reading the API docs it's more of a legacy thing and doesn't have support for the new more exotic color depths or anything, so I figured I should change that to something more modern while I'm updating an effect.

  • Solution
Posted

Everything in ColorBgra should be considered legacy and should not be used.

 

Let's assume you're starting with a ColorBgra32:

 

1. Cast to ColorRgba128Float

2. Call ToPremultiplied(), giving you a ColorPrgba128Float. We premultiply because blending needs to operate in premultiplied alpha space, at which point the math gets really simple.

3. Cast to System.Numerics.Vector4

 

For a 50/50 blend, it's just c=(a+b)/2, there are operator overloads that really make it that easy. For a specific opacity, it'd be c=Vector4.Lerp(a, b, opacityOfB), where opacityOfB should be in the range [0, 1] inclusive. Remember we're working with values in the [0, 1] range, not [0, 255]!

 

4. Cast back to ColorPrgba128Float

5. Call ToUnpremultiplied() to get back to olorRgba128Float

6. To get back to ColorBgra32, use ColorBgra32.Round()

 

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Posted
1 hour ago, Rick Brewster said:

Note that my instructions above do not convert to linear gamma. If you want to do that, which does produce better results, I can detail how to properly do that as well. 

Actually, not sure if I need it for this particular case, but might be good to know for later anyway, so if you don't mind.

 

Also re: using premultiplied alpha, that's not getting me the results I'm after: I'm often interested in the colors even in fully transparent pixels, so doing a blend between, say, RGBA 255, 0, 0, 0 and 0, 255, 0, 0 is meaningful to produce values that have 0 alpha but colors between red and green; if I use premultiplied, the color info is lost. Am I missing something?

Posted

For converting colors between color spaces, you can use Environment.ImagingFactory.ConvertColors()

 

// Get the color context we're actually rendering with
// ("color context" is an object that describes a color space or something similar)
IColorContext docColorContext = Environment.Document.ColorContext;

// Linearize the color context
IColorContext docColorContextLinear = docColorContext.CreateLinearizedColorContextOrScRgb(docColorContextLiner);

// Make a list of colors
ColorRgbaFloat[] colors = [ c1, c2, c3, ... ];

// Convert the colors
ColorRgbaFloat[] colorsLinear = Environment.ImagingFactory.ConvertColors(colors, docColorContext, docColorContextLinear).ToArray();

// Do some processing on them?
...

// And later we can convert them back to the original color context
IReadOnlyList<ColorRgbaFloat> colors2 = Environment.ImagingFactory.ConvertColors(colorsLinear, docColorContextLinear, docColorContext);

// And then convert back to ColorBgra32
IReadOnlyList<ColorBgra32> colors2Bgra32 = colors2.Select(c => ColorBgra32.Round(c)).ToArray();

 

If you only need to convert 1 color, it's reasonable to use ManagedColor

// Obtain color contexts just like in the above snippet

// Make 1 color
ManagedColor color = ManagedColor.Create(..., colorContext);

// Convert to linear
ColorRgba128Float colorLinear = color.Get(colorContextLinear);

 

  • Thanks 1

The Paint.NET Blog: https://blog.getpaint.net/

Donations are always appreciated! https://www.getpaint.net/donate.html

forumSig_bmwE60.jpg

Posted
2 hours ago, frio said:

if I use premultiplied, the color info is lost.

 

Yeah, pre-multiplied alpha doesn't preserve RGB when alpha = 0.
But when you blend two colors, they are usually weighted with alpha, so blended result doesn't preserve RGB after all.
Maybe what you want is per component interpolation in this case, which is just Lerp().

Posted (edited)
11 hours ago, _koh_ said:

Maybe what you want is per component interpolation in this case, which is just Lerp().

Yeah, I thought about it overnight and realized I'm using "blending" inaccurately here, since what I'm really interested in is interpolating between 2 colors based on an intensity 0-1 value which is why the Lerp on straight-alpha colors gets me what I'm expecting to see. Though, it's still good to have the right idea for any future effects where I may be dealing with proper blending!

 

Thanks to you and Rick both.

Edited by frio
Posted

Yup, that's what I went with and things are now looking good, once 5.1 releases I'm good to put out this updated version.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...