From 5889113ce386306ff312be84b8a9567e7ad8e045 Mon Sep 17 00:00:00 2001 From: Cameron White Date: Mon, 23 Dec 2024 18:07:58 -0500 Subject: [PATCH] Cleanup and performance improvements for ColorBgra - Remove unused code - Simplify functions which use premultiplied alpha, e.g. removing unnecessary special cases for a total alpha of 0. This shows some performance improvements in the effects benchmark: - Bulge effect: 194.6ms -> 125.5ms - Fragment effect: 178.7ms -> 164.9ms --- Pinta.Core/Classes/ColorBgra.cs | 215 ++++---------------------------- 1 file changed, 27 insertions(+), 188 deletions(-) diff --git a/Pinta.Core/Classes/ColorBgra.cs b/Pinta.Core/Classes/ColorBgra.cs index cce963512e..ca2793c602 100644 --- a/Pinta.Core/Classes/ColorBgra.cs +++ b/Pinta.Core/Classes/ColorBgra.cs @@ -114,18 +114,6 @@ public unsafe byte this[int channel] { /// A value in the range 0 to 255 inclusive. public readonly byte GetIntensityByte () => (byte) ((7471 * B + 38470 * G + 19595 * R) >> 16); - /// - /// Returns the maximum value out of the B, G, and R values. Alpha is ignored. - /// - /// - public readonly byte GetMaxColorChannelValue () => Math.Max (B, Math.Max (G, R)); - - /// - /// Returns the average of the B, G, and R values. Alpha is ignored. - /// - /// - public readonly byte GetAverageColorChannelValue () => (byte) ((B + G + R) / 3); - /// /// Compares two ColorBgra instance to determine if they are equal. /// @@ -209,6 +197,7 @@ public static ColorBgra FromUInt32 (uint bgra) /// /// Smoothly blends between two colors. + /// NOTE: this assumes unpremultipled alpha! /// public static ColorBgra Blend (ColorBgra ca, ColorBgra cb, byte cbAlpha) { @@ -234,43 +223,33 @@ public static ColorBgra Blend (ColorBgra ca, ColorBgra cb, byte cbAlpha) } /// - /// Linearly interpolates between two color values. + /// Linearly interpolates between two color values with premultiplied alpha. /// /// The color value that represents 0 on the lerp number line. /// The color value that represents 1 on the lerp number line. /// A value in the range [0, 1]. - /// - /// This method does a simple lerp on each color value and on the alpha channel. It does - /// not properly take into account the alpha channel's effect on color blending. - /// public static ColorBgra Lerp (ColorBgra from, ColorBgra to, float frac) => FromBgra ( - b: Utility.ClampToByte (Lerp (from.B, to.B, frac)), - g: Utility.ClampToByte (Lerp (from.G, to.G, frac)), - r: Utility.ClampToByte (Lerp (from.R, to.R, frac)), - a: Utility.ClampToByte (Lerp (from.A, to.A, frac)) + b: Utility.ClampToByte (Mathematics.Lerp (from.B, to.B, frac)), + g: Utility.ClampToByte (Mathematics.Lerp (from.G, to.G, frac)), + r: Utility.ClampToByte (Mathematics.Lerp (from.R, to.R, frac)), + a: Utility.ClampToByte (Mathematics.Lerp (from.A, to.A, frac)) ); - public static float Lerp (float from, float to, float frac) => from + frac * (to - from); - public static double Lerp (double from, double to, double frac) => from + frac * (to - from); /// - /// Linearly interpolates between two color values. + /// Linearly interpolates between two color values with premultiplied alpha. /// /// The color value that represents 0 on the lerp number line. /// The color value that represents 1 on the lerp number line. /// A value in the range [0, 1]. - /// - /// This method does a simple lerp on each color value and on the alpha channel. It does - /// not properly take into account the alpha channel's effect on color blending. - /// public static ColorBgra Lerp (ColorBgra from, ColorBgra to, double frac) => FromBgra ( - b: Utility.ClampToByte (Lerp (from.B, to.B, frac)), - g: Utility.ClampToByte (Lerp (from.G, to.G, frac)), - r: Utility.ClampToByte (Lerp (from.R, to.R, frac)), - a: Utility.ClampToByte (Lerp (from.A, to.A, frac)) + b: Utility.ClampToByte (Mathematics.Lerp (from.B, to.B, frac)), + g: Utility.ClampToByte (Mathematics.Lerp (from.G, to.G, frac)), + r: Utility.ClampToByte (Mathematics.Lerp (from.R, to.R, frac)), + a: Utility.ClampToByte (Mathematics.Lerp (from.A, to.A, frac)) ); /// - /// Blends four colors together based on the given weight values. + /// Blends four premultiplied colors together based on the given weight values. /// /// The blended color. /// @@ -285,141 +264,10 @@ public static ColorBgra BlendColors4W16IP (in ColorBgra c1, uint w1, in ColorBgr #endif const uint ww = 32768; - uint af = (c1.A * w1) + (c2.A * w2) + (c3.A * w3) + (c4.A * w4); - uint a = (af + ww) >> 16; - - uint b; - uint g; - uint r; - - if (a == 0) { - b = 0; - g = 0; - r = 0; - } else { - b = (uint) ((((long) c1.A * c1.B * w1) + ((long) c2.A * c2.B * w2) + ((long) c3.A * c3.B * w3) + ((long) c4.A * c4.B * w4)) / af); - g = (uint) ((((long) c1.A * c1.G * w1) + ((long) c2.A * c2.G * w2) + ((long) c3.A * c3.G * w3) + ((long) c4.A * c4.G * w4)) / af); - r = (uint) ((((long) c1.A * c1.R * w1) + ((long) c2.A * c2.R * w2) + ((long) c3.A * c3.R * w3) + ((long) c4.A * c4.R * w4)) / af); - } - - return FromBgra ((byte) b, (byte) g, (byte) r, (byte) a); - } - - /// - /// Blends the colors based on the given weight values. - /// - /// The array of color values. - /// The array of weight values. - /// - /// The weights should be fixed point numbers. - /// The total summation of the weight values will be treated as "1.0". - /// Each color will be blended in proportionally to its weight value respective to - /// the total summation of the weight values. - /// - /// - /// "WAIP" stands for "weights, arbitrary integer precision" - public static ColorBgra BlendColorsWAIP (ColorBgra[] c, uint[] w) - { - if (c.Length != w.Length) { - throw new ArgumentException ("c.Length != w.Length"); - } - - if (c.Length == 0) { - return FromUInt32 (0); - } - - long wsum = 0; - long asum = 0; - - for (int i = 0; i < w.Length; ++i) { - wsum += w[i]; - asum += c[i].A * w[i]; - } - - uint a = (uint) ((asum + (wsum >> 1)) / wsum); - - long b; - long g; - long r; - - if (a == 0) { - b = 0; - g = 0; - r = 0; - } else { - b = 0; - g = 0; - r = 0; - - for (int i = 0; i < c.Length; ++i) { - b += (long) c[i].A * c[i].B * w[i]; - g += (long) c[i].A * c[i].G * w[i]; - r += (long) c[i].A * c[i].R * w[i]; - } - - b /= asum; - g /= asum; - r /= asum; - } - - return FromUInt32 ((uint) b + ((uint) g << 8) + ((uint) r << 16) + (a << 24)); - } - - /// - /// Blends the colors based on the given weight values. - /// - /// The array of color values. - /// The array of weight values. - /// - /// Each color will be blended in proportionally to its weight value respective to - /// the total summation of the weight values. - /// - /// - /// "WFP" stands for "weights, floating-point" - public static ColorBgra BlendColorsWFP (ColorBgra[] c, double[] w) - { - if (c.Length != w.Length) { - throw new ArgumentException ("c.Length != w.Length"); - } - - if (c.Length == 0) { - return FromUInt32 (0); - } - - double wsum = 0; - double asum = 0; - - for (int i = 0; i < w.Length; ++i) { - wsum += w[i]; - asum += c[i].A * w[i]; - } - - double a = asum / wsum; - double aMultWsum = a * wsum; - - double b; - double g; - double r; - - if (asum == 0) { - b = 0; - g = 0; - r = 0; - } else { - b = 0; - g = 0; - r = 0; - - for (int i = 0; i < c.Length; ++i) { - b += (double) c[i].A * c[i].B * w[i]; - g += (double) c[i].A * c[i].G * w[i]; - r += (double) c[i].A * c[i].R * w[i]; - } - - b /= aMultWsum; - g /= aMultWsum; - r /= aMultWsum; - } + uint r = (c1.R * w1 + c2.R * w2 + c3.R * w3 + c4.R * w4 + ww) >> 16; + uint g = (c1.G * w1 + c2.G * w2 + c3.G * w3 + c4.G * w4 + ww) >> 16; + uint b = (c1.B * w1 + c2.B * w2 + c3.B * w3 + c4.B * w4 + ww) >> 16; + uint a = (c1.A * w1 + c2.A * w2 + c3.A * w3 + c4.A * w4 + ww) >> 16; return FromBgra ((byte) b, (byte) g, (byte) r, (byte) a); } @@ -435,29 +283,20 @@ public static ColorBgra Blend (ReadOnlySpan colors) return Transparent; ulong a_sum = 0; - for (var i = 0; i < count; ++i) + ulong b_sum = 0; + ulong g_sum = 0; + ulong r_sum = 0; + for (int i = 0; i < count; ++i) { a_sum += colors[i].A; + b_sum += colors[i].B; + g_sum += colors[i].G; + r_sum += colors[i].R; + } - byte b = 0; - byte g = 0; - byte r = 0; byte a = (byte) (a_sum / (ulong) count); - - if (a_sum != 0) { - ulong b_sum = 0; - ulong g_sum = 0; - ulong r_sum = 0; - - for (var i = 0; i < count; ++i) { - b_sum += colors[i].B; - g_sum += colors[i].G; - r_sum += colors[i].R; - } - - b = (byte) (b_sum / (ulong) count); - g = (byte) (g_sum / (ulong) count); - r = (byte) (r_sum / (ulong) count); - } + byte b = (byte) (b_sum / (ulong) count); + byte g = (byte) (g_sum / (ulong) count); + byte r = (byte) (r_sum / (ulong) count); return FromBgra (b, g, r, a); }