< Summary

Line coverage
97%
Covered lines: 330
Uncovered lines: 7
Coverable lines: 337
Total lines: 750
Line coverage: 97.9%
Branch coverage
94%
Covered branches: 84
Total branches: 89
Branch coverage: 94.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Cyclomatic complexity Line coverage
.ctor()100%1100%
Quantize(...)100%1100%
Quantize(...)100%6100%
GetIndex(...)100%1100%
Volume(...)100%1100%
Bottom(...)80%596.96%
Top(...)80%596.96%
Clear()100%1100%
Build3DHistogram(...)100%2100%
Get3DMoments()100%8100%
Variance(...)100%1100%
Maximize(...)100%8100%
Cut(...)100%25100%
Mark(...)100%8100%
BuildCube(...)87.5%1695.83%
GenerateResult(...)83.33%680.95%

File(s)

https://raw.githubusercontent.com/JeremyAnsel/JeremyAnsel.ColorQuant/6d66b33d08452017f55aed57c4f1133aa87f71ea/JeremyAnsel.ColorQuant/JeremyAnsel.ColorQuant/WuAlphaColorQuantizer.cs

#LineLine coverage
 1// <copyright file="WuAlphaColorQuantizer.cs" company="Jérémy Ansel">
 2// Copyright (c) 2014-2019 Jérémy Ansel
 3// </copyright>
 4// <license>
 5// Licensed under the MIT license. See LICENSE.txt
 6// </license>
 7
 8namespace JeremyAnsel.ColorQuant
 9{
 10    using System;
 11    using System.Diagnostics.CodeAnalysis;
 12
 13    /// <summary>
 14    /// A Wu's color quantizer with alpha channel.
 15    /// </summary>
 16    /// <remarks>
 17    /// <para>
 18    /// Based on C Implementation of Xiaolin Wu's Color Quantizer (v. 2)
 19    /// (see Graphics Gems volume II, pages 126-133)
 20    /// (<see href="http://www.ece.mcmaster.ca/~xwu/cq.c"/>).
 21    /// </para>
 22    /// <para>
 23    /// Algorithm: Greedy orthogonal bipartition of RGB space for variance
 24    /// minimization aided by inclusion-exclusion tricks.
 25    /// For speed no nearest neighbor search is done. Slightly
 26    /// better performance can be expected by more sophisticated
 27    /// but more expensive versions.
 28    /// </para>
 29    /// </remarks>
 30    [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Wu", Justification = "
 31    public sealed class WuAlphaColorQuantizer : IColorQuantizer
 32    {
 33        /// <summary>
 34        /// The index bits.
 35        /// </summary>
 36        private const int IndexBits = 6;
 37
 38        /// <summary>
 39        /// The index alpha bits.
 40        /// </summary>
 41        private const int IndexAlphaBits = 4;
 42
 43        /// <summary>
 44        /// The index count.
 45        /// </summary>
 46        private const int IndexCount = (1 << WuAlphaColorQuantizer.IndexBits) + 1;
 47
 48        /// <summary>
 49        /// The index alpha count.
 50        /// </summary>
 51        private const int IndexAlphaCount = (1 << WuAlphaColorQuantizer.IndexAlphaBits) + 1;
 52
 53        /// <summary>
 54        /// The table length.
 55        /// </summary>
 56        private const int TableLength = WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexCount * WuAlphaCol
 57
 58        /// <summary>
 59        /// Moment of <c>P(c)</c>.
 60        /// </summary>
 261        private readonly long[] vwt = new long[WuAlphaColorQuantizer.TableLength];
 62
 63        /// <summary>
 64        /// Moment of <c>r*P(c)</c>.
 65        /// </summary>
 266        private readonly long[] vmr = new long[WuAlphaColorQuantizer.TableLength];
 67
 68        /// <summary>
 69        /// Moment of <c>g*P(c)</c>.
 70        /// </summary>
 271        private readonly long[] vmg = new long[WuAlphaColorQuantizer.TableLength];
 72
 73        /// <summary>
 74        /// Moment of <c>b*P(c)</c>.
 75        /// </summary>
 276        private readonly long[] vmb = new long[WuAlphaColorQuantizer.TableLength];
 77
 78        /// <summary>
 79        /// Moment of <c>a*P(c)</c>.
 80        /// </summary>
 281        private readonly long[] vma = new long[WuAlphaColorQuantizer.TableLength];
 82
 83        /// <summary>
 84        /// Moment of <c>c^2*P(c)</c>.
 85        /// </summary>
 286        private readonly double[] m2 = new double[WuAlphaColorQuantizer.TableLength];
 87
 88        /// <summary>
 89        /// Color space tag.
 90        /// </summary>
 291        private readonly byte[] tag = new byte[WuAlphaColorQuantizer.TableLength];
 92
 93        /// <summary>
 94        /// Quantizes an image.
 95        /// </summary>
 96        /// <param name="image">The image (ARGB).</param>
 97        /// <returns>The result.</returns>
 98        public ColorQuantizerResult Quantize(byte[] image)
 99        {
 18100            return this.Quantize(image, 256);
 101        }
 102
 103        /// <summary>
 104        /// Quantizes an image.
 105        /// </summary>
 106        /// <param name="image">The image (ARGB).</param>
 107        /// <param name="colorCount">The color count.</param>
 108        /// <returns>The result.</returns>
 109        public ColorQuantizerResult Quantize(byte[] image, int colorCount)
 110        {
 24111            if (image == null)
 112            {
 4113                throw new ArgumentNullException(nameof(image));
 114            }
 115
 20116            if (colorCount < 1 || colorCount > 256)
 117            {
 4118                throw new ArgumentOutOfRangeException(nameof(colorCount));
 119            }
 120
 16121            this.Clear();
 122
 16123            this.Build3DHistogram(image);
 16124            this.Get3DMoments();
 125
 126            Box[] cube;
 16127            this.BuildCube(out cube, ref colorCount);
 128
 16129            return this.GenerateResult(image, colorCount, cube);
 130        }
 131
 132        /// <summary>
 133        /// Gets an index.
 134        /// </summary>
 135        /// <param name="r">The red value.</param>
 136        /// <param name="g">The green value.</param>
 137        /// <param name="b">The blue value.</param>
 138        /// <param name="a">The alpha value.</param>
 139        /// <returns>The index.</returns>
 140        private static int GetIndex(int r, int g, int b, int a)
 141        {
 209719560142            return (r << ((WuAlphaColorQuantizer.IndexBits * 2) + WuAlphaColorQuantizer.IndexAlphaBits))
 209719560143                + (r << (WuAlphaColorQuantizer.IndexBits + WuAlphaColorQuantizer.IndexAlphaBits + 1))
 209719560144                + (g << (WuAlphaColorQuantizer.IndexBits + WuAlphaColorQuantizer.IndexAlphaBits))
 209719560145                + (r << (WuAlphaColorQuantizer.IndexBits * 2))
 209719560146                + (r << (WuAlphaColorQuantizer.IndexBits + 1))
 209719560147                + (g << WuAlphaColorQuantizer.IndexBits)
 209719560148                + ((r + g + b) << WuAlphaColorQuantizer.IndexAlphaBits)
 209719560149                + r + g + b + a;
 150        }
 151
 152        /// <summary>
 153        /// Computes sum over a box of any given statistic.
 154        /// </summary>
 155        /// <param name="cube">The cube.</param>
 156        /// <param name="moment">The moment.</param>
 157        /// <returns>The result.</returns>
 158        private static double Volume(Box cube, long[] moment)
 159        {
 23690160            return moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, cube.A1)]
 23690161                - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
 23690162                - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A1)]
 23690163                + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
 23690164                - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A1)]
 23690165                + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
 23690166                + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
 23690167                - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
 23690168                - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A1)]
 23690169                + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
 23690170                + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
 23690171                - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
 23690172                + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
 23690173                - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
 23690174                - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
 23690175                + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
 176        }
 177
 178        /// <summary>
 179        /// Computes part of Volume(cube, moment) that doesn't depend on r1, g1, or b1 (depending on direction).
 180        /// </summary>
 181        /// <param name="cube">The cube.</param>
 182        /// <param name="direction">The direction.</param>
 183        /// <param name="moment">The moment.</param>
 184        /// <returns>The result.</returns>
 185        private static long Bottom(Box cube, int direction, long[] moment)
 186        {
 187            switch (direction)
 188            {
 189                // Red
 190                case 3:
 7960191                    return -moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A1)]
 7960192                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
 7960193                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
 7960194                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
 7960195                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
 7960196                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
 7960197                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
 7960198                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
 199
 200                // Green
 201                case 2:
 7960202                    return -moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A1)]
 7960203                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
 7960204                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
 7960205                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
 7960206                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
 7960207                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
 7960208                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
 7960209                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
 210
 211                // Blue
 212                case 1:
 7960213                    return -moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A1)]
 7960214                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
 7960215                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
 7960216                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
 7960217                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
 7960218                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
 7960219                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
 7960220                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
 221
 222                // Alpha
 223                case 0:
 7960224                    return -moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
 7960225                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
 7960226                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
 7960227                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
 7960228                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
 7960229                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
 7960230                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
 7960231                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
 232
 233                default:
 0234                    throw new ArgumentOutOfRangeException(nameof(direction));
 235            }
 236        }
 237
 238        /// <summary>
 239        /// Computes remainder of Volume(cube, moment), substituting position for r1, g1, or b1 (depending on direction)
 240        /// </summary>
 241        /// <param name="cube">The cube.</param>
 242        /// <param name="direction">The direction.</param>
 243        /// <param name="position">The position.</param>
 244        /// <param name="moment">The moment.</param>
 245        /// <returns>The result.</returns>
 246        private static long Top(Box cube, int direction, int position, long[] moment)
 247        {
 248            switch (direction)
 249            {
 250                // Red
 251                case 3:
 230760252                    return moment[WuAlphaColorQuantizer.GetIndex(position, cube.G1, cube.B1, cube.A1)]
 230760253                        - moment[WuAlphaColorQuantizer.GetIndex(position, cube.G1, cube.B1, cube.A0)]
 230760254                        - moment[WuAlphaColorQuantizer.GetIndex(position, cube.G1, cube.B0, cube.A1)]
 230760255                        + moment[WuAlphaColorQuantizer.GetIndex(position, cube.G1, cube.B0, cube.A0)]
 230760256                        - moment[WuAlphaColorQuantizer.GetIndex(position, cube.G0, cube.B1, cube.A1)]
 230760257                        + moment[WuAlphaColorQuantizer.GetIndex(position, cube.G0, cube.B1, cube.A0)]
 230760258                        + moment[WuAlphaColorQuantizer.GetIndex(position, cube.G0, cube.B0, cube.A1)]
 230760259                        - moment[WuAlphaColorQuantizer.GetIndex(position, cube.G0, cube.B0, cube.A0)];
 260
 261                // Green
 262                case 2:
 313320263                    return moment[WuAlphaColorQuantizer.GetIndex(cube.R1, position, cube.B1, cube.A1)]
 313320264                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, position, cube.B1, cube.A0)]
 313320265                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, position, cube.B0, cube.A1)]
 313320266                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, position, cube.B0, cube.A0)]
 313320267                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, position, cube.B1, cube.A1)]
 313320268                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, position, cube.B1, cube.A0)]
 313320269                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, position, cube.B0, cube.A1)]
 313320270                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, position, cube.B0, cube.A0)];
 271
 272                // Blue
 273                case 1:
 324840274                    return moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, position, cube.A1)]
 324840275                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, position, cube.A0)]
 324840276                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, position, cube.A1)]
 324840277                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, position, cube.A0)]
 324840278                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, position, cube.A1)]
 324840279                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, position, cube.A0)]
 324840280                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, position, cube.A1)]
 324840281                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, position, cube.A0)];
 282
 283                // Alpha
 284                case 0:
 96040285                    return moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, position)]
 96040286                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, position)]
 96040287                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, position)]
 96040288                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, position)]
 96040289                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, position)]
 96040290                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, position)]
 96040291                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, position)]
 96040292                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, position)];
 293
 294                default:
 0295                    throw new ArgumentOutOfRangeException(nameof(direction));
 296            }
 297        }
 298
 299        /// <summary>
 300        /// Clears the tables.
 301        /// </summary>
 302        private void Clear()
 303        {
 16304            Array.Clear(this.vwt, 0, WuAlphaColorQuantizer.TableLength);
 16305            Array.Clear(this.vmr, 0, WuAlphaColorQuantizer.TableLength);
 16306            Array.Clear(this.vmg, 0, WuAlphaColorQuantizer.TableLength);
 16307            Array.Clear(this.vmb, 0, WuAlphaColorQuantizer.TableLength);
 16308            Array.Clear(this.vma, 0, WuAlphaColorQuantizer.TableLength);
 16309            Array.Clear(this.m2, 0, WuAlphaColorQuantizer.TableLength);
 310
 16311            Array.Clear(this.tag, 0, WuAlphaColorQuantizer.TableLength);
 16312        }
 313
 314        /// <summary>
 315        /// Builds a 3-D color histogram of <c>counts, r/g/b, c^2</c>.
 316        /// </summary>
 317        /// <param name="image">The image.</param>
 318        private void Build3DHistogram(byte[] image)
 319        {
 6184320            for (int i = 0; i < image.Length; i += 4)
 321            {
 3076322                int a = image[i + 3];
 3076323                int r = image[i + 2];
 3076324                int g = image[i + 1];
 3076325                int b = image[i];
 326
 3076327                int inr = r >> (8 - WuAlphaColorQuantizer.IndexBits);
 3076328                int ing = g >> (8 - WuAlphaColorQuantizer.IndexBits);
 3076329                int inb = b >> (8 - WuAlphaColorQuantizer.IndexBits);
 3076330                int ina = a >> (8 - WuAlphaColorQuantizer.IndexAlphaBits);
 331
 3076332                int ind = WuAlphaColorQuantizer.GetIndex(inr + 1, ing + 1, inb + 1, ina + 1);
 333
 3076334                this.vwt[ind]++;
 3076335                this.vmr[ind] += r;
 3076336                this.vmg[ind] += g;
 3076337                this.vmb[ind] += b;
 3076338                this.vma[ind] += a;
 3076339                this.m2[ind] += (r * r) + (g * g) + (b * b) + (a * a);
 340            }
 16341        }
 342
 343        /// <summary>
 344        /// Converts the histogram into moments so that we can rapidly calculate
 345        /// the sums of the above quantities over any desired box.
 346        /// </summary>
 347        private void Get3DMoments()
 348        {
 16349            long[] volume = new long[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 16350            long[] volumeR = new long[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 16351            long[] volumeG = new long[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 16352            long[] volumeB = new long[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 16353            long[] volumeA = new long[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 16354            double[] volume2 = new double[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 355
 16356            long[] area = new long[WuAlphaColorQuantizer.IndexAlphaCount];
 16357            long[] areaR = new long[WuAlphaColorQuantizer.IndexAlphaCount];
 16358            long[] areaG = new long[WuAlphaColorQuantizer.IndexAlphaCount];
 16359            long[] areaB = new long[WuAlphaColorQuantizer.IndexAlphaCount];
 16360            long[] areaA = new long[WuAlphaColorQuantizer.IndexAlphaCount];
 16361            double[] area2 = new double[WuAlphaColorQuantizer.IndexAlphaCount];
 362
 2080363            for (int r = 1; r < WuAlphaColorQuantizer.IndexCount; r++)
 364            {
 1024365                Array.Clear(volume, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 1024366                Array.Clear(volumeR, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 1024367                Array.Clear(volumeG, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 1024368                Array.Clear(volumeB, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 1024369                Array.Clear(volumeA, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 1024370                Array.Clear(volume2, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 371
 133120372                for (int g = 1; g < WuAlphaColorQuantizer.IndexCount; g++)
 373                {
 65536374                    Array.Clear(area, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 65536375                    Array.Clear(areaR, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 65536376                    Array.Clear(areaG, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 65536377                    Array.Clear(areaB, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 65536378                    Array.Clear(areaA, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 65536379                    Array.Clear(area2, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 380
 8519680381                    for (int b = 1; b < WuAlphaColorQuantizer.IndexCount; b++)
 382                    {
 4194304383                        long line = 0;
 4194304384                        long lineR = 0;
 4194304385                        long lineG = 0;
 4194304386                        long lineB = 0;
 4194304387                        long lineA = 0;
 4194304388                        double line2 = 0;
 389
 142606336390                        for (int a = 1; a < WuAlphaColorQuantizer.IndexAlphaCount; a++)
 391                        {
 67108864392                            int ind1 = WuAlphaColorQuantizer.GetIndex(r, g, b, a);
 393
 67108864394                            line += this.vwt[ind1];
 67108864395                            lineR += this.vmr[ind1];
 67108864396                            lineG += this.vmg[ind1];
 67108864397                            lineB += this.vmb[ind1];
 67108864398                            lineA += this.vma[ind1];
 67108864399                            line2 += this.m2[ind1];
 400
 67108864401                            area[a] += line;
 67108864402                            areaR[a] += lineR;
 67108864403                            areaG[a] += lineG;
 67108864404                            areaB[a] += lineB;
 67108864405                            areaA[a] += lineA;
 67108864406                            area2[a] += line2;
 407
 67108864408                            int inv = (b * WuAlphaColorQuantizer.IndexAlphaCount) + a;
 409
 67108864410                            volume[inv] += area[a];
 67108864411                            volumeR[inv] += areaR[a];
 67108864412                            volumeG[inv] += areaG[a];
 67108864413                            volumeB[inv] += areaB[a];
 67108864414                            volumeA[inv] += areaA[a];
 67108864415                            volume2[inv] += area2[a];
 416
 67108864417                            int ind2 = ind1 - WuAlphaColorQuantizer.GetIndex(1, 0, 0, 0);
 418
 67108864419                            this.vwt[ind1] = this.vwt[ind2] + volume[inv];
 67108864420                            this.vmr[ind1] = this.vmr[ind2] + volumeR[inv];
 67108864421                            this.vmg[ind1] = this.vmg[ind2] + volumeG[inv];
 67108864422                            this.vmb[ind1] = this.vmb[ind2] + volumeB[inv];
 67108864423                            this.vma[ind1] = this.vma[ind2] + volumeA[inv];
 67108864424                            this.m2[ind1] = this.m2[ind2] + volume2[inv];
 425                        }
 426                    }
 427                }
 428            }
 16429        }
 430
 431        /// <summary>
 432        /// Computes the weighted variance of a box.
 433        /// </summary>
 434        /// <param name="cube">The cube.</param>
 435        /// <returns>The result.</returns>
 436        private double Variance(Box cube)
 437        {
 2086438            double dr = WuAlphaColorQuantizer.Volume(cube, this.vmr);
 2086439            double dg = WuAlphaColorQuantizer.Volume(cube, this.vmg);
 2086440            double db = WuAlphaColorQuantizer.Volume(cube, this.vmb);
 2086441            double da = WuAlphaColorQuantizer.Volume(cube, this.vma);
 442
 2086443            double xx =
 2086444                this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, cube.A1)]
 2086445                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
 2086446                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A1)]
 2086447                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
 2086448                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A1)]
 2086449                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
 2086450                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
 2086451                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
 2086452                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A1)]
 2086453                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
 2086454                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
 2086455                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
 2086456                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
 2086457                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
 2086458                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
 2086459                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
 460
 2086461            return xx - (((dr * dr) + (dg * dg) + (db * db) + (da * da)) / WuAlphaColorQuantizer.Volume(cube, this.vwt))
 462        }
 463
 464        /// <summary>
 465        /// We want to minimize the sum of the variances of two sub-boxes.
 466        /// The sum(c^2) terms can be ignored since their sum over both sub-boxes
 467        /// is the same (the sum for the whole box) no matter where we split.
 468        /// The remaining terms have a minus sign in the variance formula,
 469        /// so we drop the minus sign and maximize the sum of the two terms.
 470        /// </summary>
 471        /// <param name="cube">The cube.</param>
 472        /// <param name="direction">The direction.</param>
 473        /// <param name="first">The first position.</param>
 474        /// <param name="last">The last position.</param>
 475        /// <param name="cut">The cutting point.</param>
 476        /// <param name="wholeR">The whole red.</param>
 477        /// <param name="wholeG">The whole green.</param>
 478        /// <param name="wholeB">The whole blue.</param>
 479        /// <param name="wholeA">The whole alpha.</param>
 480        /// <param name="wholeW">The whole weight.</param>
 481        /// <returns>The result.</returns>
 482        private double Maximize(Box cube, int direction, int first, int last, out int cut, double wholeR, double wholeG,
 483        {
 6368484            long baseR = WuAlphaColorQuantizer.Bottom(cube, direction, this.vmr);
 6368485            long baseG = WuAlphaColorQuantizer.Bottom(cube, direction, this.vmg);
 6368486            long baseB = WuAlphaColorQuantizer.Bottom(cube, direction, this.vmb);
 6368487            long baseA = WuAlphaColorQuantizer.Bottom(cube, direction, this.vma);
 6368488            long baseW = WuAlphaColorQuantizer.Bottom(cube, direction, this.vwt);
 489
 6368490            double max = 0.0;
 6368491            cut = -1;
 492
 398720493            for (int i = first; i < last; i++)
 494            {
 192992495                double halfR = baseR + WuAlphaColorQuantizer.Top(cube, direction, i, this.vmr);
 192992496                double halfG = baseG + WuAlphaColorQuantizer.Top(cube, direction, i, this.vmg);
 192992497                double halfB = baseB + WuAlphaColorQuantizer.Top(cube, direction, i, this.vmb);
 192992498                double halfA = baseA + WuAlphaColorQuantizer.Top(cube, direction, i, this.vma);
 192992499                double halfW = baseW + WuAlphaColorQuantizer.Top(cube, direction, i, this.vwt);
 500
 192992501                if (halfW == 0)
 502                {
 503                    continue;
 504                }
 505
 147994506                double temp = ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW;
 507
 147994508                halfR = wholeR - halfR;
 147994509                halfG = wholeG - halfG;
 147994510                halfB = wholeB - halfB;
 147994511                halfA = wholeA - halfA;
 147994512                halfW = wholeW - halfW;
 513
 147994514                if (halfW == 0)
 515                {
 516                    continue;
 517                }
 518
 17006519                temp += ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW;
 520
 17006521                if (temp > max)
 522                {
 3372523                    max = temp;
 3372524                    cut = i;
 525                }
 526            }
 527
 6368528            return max;
 529        }
 530
 531        /// <summary>
 532        /// Cuts a box.
 533        /// </summary>
 534        /// <param name="set1">The first set.</param>
 535        /// <param name="set2">The second set.</param>
 536        /// <returns>Returns a value indicating whether the box has been split.</returns>
 537        private bool Cut(Box set1, Box set2)
 538        {
 1592539            double wholeR = WuAlphaColorQuantizer.Volume(set1, this.vmr);
 1592540            double wholeG = WuAlphaColorQuantizer.Volume(set1, this.vmg);
 1592541            double wholeB = WuAlphaColorQuantizer.Volume(set1, this.vmb);
 1592542            double wholeA = WuAlphaColorQuantizer.Volume(set1, this.vma);
 1592543            double wholeW = WuAlphaColorQuantizer.Volume(set1, this.vwt);
 544
 545            int cutr;
 546            int cutg;
 547            int cutb;
 548            int cuta;
 549
 1592550            double maxr = this.Maximize(set1, 3, set1.R0 + 1, set1.R1, out cutr, wholeR, wholeG, wholeB, wholeA, wholeW)
 1592551            double maxg = this.Maximize(set1, 2, set1.G0 + 1, set1.G1, out cutg, wholeR, wholeG, wholeB, wholeA, wholeW)
 1592552            double maxb = this.Maximize(set1, 1, set1.B0 + 1, set1.B1, out cutb, wholeR, wholeG, wholeB, wholeA, wholeW)
 1592553            double maxa = this.Maximize(set1, 0, set1.A0 + 1, set1.A1, out cuta, wholeR, wholeG, wholeB, wholeA, wholeW)
 554
 555            int dir;
 556
 1592557            if ((maxr >= maxg) && (maxr >= maxb) && (maxr >= maxa))
 558            {
 834559                dir = 3;
 560
 834561                if (cutr < 0)
 562                {
 548563                    return false;
 564                }
 565            }
 758566            else if ((maxg >= maxr) && (maxg >= maxb) && (maxg >= maxa))
 567            {
 194568                dir = 2;
 569            }
 564570            else if ((maxb >= maxr) && (maxb >= maxg) && (maxb >= maxa))
 571            {
 262572                dir = 1;
 573            }
 574            else
 575            {
 302576                dir = 0;
 577            }
 578
 1044579            set2.R1 = set1.R1;
 1044580            set2.G1 = set1.G1;
 1044581            set2.B1 = set1.B1;
 1044582            set2.A1 = set1.A1;
 583
 584            switch (dir)
 585            {
 586                // Red
 587                case 3:
 286588                    set2.R0 = set1.R1 = cutr;
 286589                    set2.G0 = set1.G0;
 286590                    set2.B0 = set1.B0;
 286591                    set2.A0 = set1.A0;
 286592                    break;
 593
 594                // Green
 595                case 2:
 194596                    set2.G0 = set1.G1 = cutg;
 194597                    set2.R0 = set1.R0;
 194598                    set2.B0 = set1.B0;
 194599                    set2.A0 = set1.A0;
 194600                    break;
 601
 602                // Blue
 603                case 1:
 262604                    set2.B0 = set1.B1 = cutb;
 262605                    set2.R0 = set1.R0;
 262606                    set2.G0 = set1.G0;
 262607                    set2.A0 = set1.A0;
 262608                    break;
 609
 610                // Alpha
 611                case 0:
 302612                    set2.A0 = set1.A1 = cuta;
 302613                    set2.R0 = set1.R0;
 302614                    set2.G0 = set1.G0;
 302615                    set2.B0 = set1.B0;
 616                    break;
 617            }
 618
 1044619            set1.Volume = (set1.R1 - set1.R0) * (set1.G1 - set1.G0) * (set1.B1 - set1.B0) * (set1.A1 - set1.A0);
 1044620            set2.Volume = (set2.R1 - set2.R0) * (set2.G1 - set2.G0) * (set2.B1 - set2.B0) * (set2.A1 - set2.A0);
 621
 1044622            return true;
 623        }
 624
 625        /// <summary>
 626        /// Marks a color space tag.
 627        /// </summary>
 628        /// <param name="cube">The cube.</param>
 629        /// <param name="label">A label.</param>
 630        private void Mark(Box cube, byte label)
 631        {
 56392632            for (int r = cube.R0 + 1; r <= cube.R1; r++)
 633            {
 1709056634                for (int g = cube.G0 + 1; g <= cube.G1; g++)
 635                {
 28917760636                    for (int b = cube.B0 + 1; b <= cube.B1; b++)
 637                    {
 161480704638                        for (int a = cube.A0 + 1; a <= cube.A1; a++)
 639                        {
 67108864640                            this.tag[WuAlphaColorQuantizer.GetIndex(r, g, b, a)] = label;
 641                        }
 642                    }
 643                }
 644            }
 1060645        }
 646
 647        /// <summary>
 648        /// Builds the cube.
 649        /// </summary>
 650        /// <param name="cube">The cube.</param>
 651        /// <param name="colorCount">The color count.</param>
 652        private void BuildCube(out Box[] cube, ref int colorCount)
 653        {
 16654            cube = new Box[colorCount];
 16655            double[] vv = new double[colorCount];
 656
 8224657            for (int i = 0; i < colorCount; i++)
 658            {
 4096659                cube[i] = new Box();
 660            }
 661
 16662            cube[0].R0 = cube[0].G0 = cube[0].B0 = cube[0].A0 = 0;
 16663            cube[0].R1 = cube[0].G1 = cube[0].B1 = WuAlphaColorQuantizer.IndexCount - 1;
 16664            cube[0].A1 = WuAlphaColorQuantizer.IndexAlphaCount - 1;
 665
 16666            int next = 0;
 667
 3184668            for (int i = 1; i < colorCount; i++)
 669            {
 1592670                if (this.Cut(cube[next], cube[i]))
 671                {
 1044672                    vv[next] = cube[next].Volume > 1 ? this.Variance(cube[next]) : 0.0;
 1044673                    vv[i] = cube[i].Volume > 1 ? this.Variance(cube[i]) : 0.0;
 674                }
 675                else
 676                {
 548677                    vv[next] = 0.0;
 548678                    i--;
 679                }
 680
 1592681                next = 0;
 682
 1592683                double temp = vv[0];
 231952684                for (int k = 1; k <= i; k++)
 685                {
 114384686                    if (vv[k] > temp)
 687                    {
 1506688                        temp = vv[k];
 1506689                        next = k;
 690                    }
 691                }
 692
 1592693                if (temp <= 0.0)
 694                {
 16695                    colorCount = i + 1;
 16696                    break;
 697                }
 698            }
 0699        }
 700
 701        /// <summary>
 702        /// Generates the quantized result.
 703        /// </summary>
 704        /// <param name="image">The image.</param>
 705        /// <param name="colorCount">The color count.</param>
 706        /// <param name="cube">The cube.</param>
 707        /// <returns>The result.</returns>
 708        private ColorQuantizerResult GenerateResult(byte[] image, int colorCount, Box[] cube)
 709        {
 16710            var quantizedImage = new ColorQuantizerResult(image.Length / 4, colorCount);
 711
 2152712            for (int k = 0; k < colorCount; k++)
 713            {
 1060714                this.Mark(cube[k], (byte)k);
 715
 1060716                double weight = WuAlphaColorQuantizer.Volume(cube[k], this.vwt);
 717
 1060718                if (weight != 0)
 719                {
 1060720                    quantizedImage.Palette[(k * 4) + 3] = (byte)(WuAlphaColorQuantizer.Volume(cube[k], this.vma) / weigh
 1060721                    quantizedImage.Palette[(k * 4) + 2] = (byte)(WuAlphaColorQuantizer.Volume(cube[k], this.vmr) / weigh
 1060722                    quantizedImage.Palette[(k * 4) + 1] = (byte)(WuAlphaColorQuantizer.Volume(cube[k], this.vmg) / weigh
 1060723                    quantizedImage.Palette[k * 4] = (byte)(WuAlphaColorQuantizer.Volume(cube[k], this.vmb) / weight);
 724                }
 725                else
 726                {
 0727                    quantizedImage.Palette[(k * 4) + 3] = 0xff;
 0728                    quantizedImage.Palette[(k * 4) + 2] = 0;
 0729                    quantizedImage.Palette[(k * 4) + 1] = 0;
 0730                    quantizedImage.Palette[k * 4] = 0;
 731                }
 732            }
 733
 6184734            for (int i = 0; i < image.Length / 4; i++)
 735            {
 3076736                int a = image[(i * 4) + 3] >> (8 - WuAlphaColorQuantizer.IndexAlphaBits);
 3076737                int r = image[(i * 4) + 2] >> (8 - WuAlphaColorQuantizer.IndexBits);
 3076738                int g = image[(i * 4) + 1] >> (8 - WuAlphaColorQuantizer.IndexBits);
 3076739                int b = image[i * 4] >> (8 - WuAlphaColorQuantizer.IndexBits);
 740
 3076741                int ind = WuAlphaColorQuantizer.GetIndex(r + 1, g + 1, b + 1, a + 1);
 742
 3076743                quantizedImage.Bytes[i] = this.tag[ind];
 744            }
 745
 16746            return quantizedImage;
 747        }
 748    }
 749}
 750