< 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 Crap Score Cyclomatic complexity Line coverage
.ctor()100%11100%
Quantize(...)100%11100%
Quantize(...)100%66100%
GetIndex(...)100%11100%
Volume(...)100%11100%
Bottom(...)80%5596.96%
Top(...)80%5596.96%
Clear()100%11100%
Build3DHistogram(...)100%22100%
Get3DMoments()100%88100%
Variance(...)100%11100%
Maximize(...)100%88100%
Cut(...)100%2525100%
Mark(...)100%88100%
BuildCube(...)87.5%16.021695.83%
GenerateResult(...)83.33%6.25680.95%

File(s)

https://raw.githubusercontent.com/JeremyAnsel/JeremyAnsel.ColorQuant/c10d236cc66f271c33ae73d6471e0c94607a8d9c/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>
 361        private readonly long[] vwt = new long[WuAlphaColorQuantizer.TableLength];
 62
 63        /// <summary>
 64        /// Moment of <c>r*P(c)</c>.
 65        /// </summary>
 366        private readonly long[] vmr = new long[WuAlphaColorQuantizer.TableLength];
 67
 68        /// <summary>
 69        /// Moment of <c>g*P(c)</c>.
 70        /// </summary>
 371        private readonly long[] vmg = new long[WuAlphaColorQuantizer.TableLength];
 72
 73        /// <summary>
 74        /// Moment of <c>b*P(c)</c>.
 75        /// </summary>
 376        private readonly long[] vmb = new long[WuAlphaColorQuantizer.TableLength];
 77
 78        /// <summary>
 79        /// Moment of <c>a*P(c)</c>.
 80        /// </summary>
 381        private readonly long[] vma = new long[WuAlphaColorQuantizer.TableLength];
 82
 83        /// <summary>
 84        /// Moment of <c>c^2*P(c)</c>.
 85        /// </summary>
 386        private readonly double[] m2 = new double[WuAlphaColorQuantizer.TableLength];
 87
 88        /// <summary>
 89        /// Color space tag.
 90        /// </summary>
 391        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        {
 27100            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        {
 36111            if (image == null)
 112            {
 6113                throw new ArgumentNullException(nameof(image));
 114            }
 115
 30116            if (colorCount < 1 || colorCount > 256)
 117            {
 6118                throw new ArgumentOutOfRangeException(nameof(colorCount));
 119            }
 120
 24121            this.Clear();
 122
 24123            this.Build3DHistogram(image);
 24124            this.Get3DMoments();
 125
 126            Box[] cube;
 24127            this.BuildCube(out cube, ref colorCount);
 128
 24129            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        {
 314579340142            return (r << ((WuAlphaColorQuantizer.IndexBits * 2) + WuAlphaColorQuantizer.IndexAlphaBits))
 314579340143                + (r << (WuAlphaColorQuantizer.IndexBits + WuAlphaColorQuantizer.IndexAlphaBits + 1))
 314579340144                + (g << (WuAlphaColorQuantizer.IndexBits + WuAlphaColorQuantizer.IndexAlphaBits))
 314579340145                + (r << (WuAlphaColorQuantizer.IndexBits * 2))
 314579340146                + (r << (WuAlphaColorQuantizer.IndexBits + 1))
 314579340147                + (g << WuAlphaColorQuantizer.IndexBits)
 314579340148                + ((r + g + b) << WuAlphaColorQuantizer.IndexAlphaBits)
 314579340149                + 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        {
 35535160            return moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, cube.A1)]
 35535161                - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
 35535162                - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A1)]
 35535163                + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
 35535164                - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A1)]
 35535165                + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
 35535166                + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
 35535167                - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
 35535168                - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A1)]
 35535169                + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
 35535170                + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
 35535171                - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
 35535172                + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
 35535173                - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
 35535174                - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
 35535175                + 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:
 11940191                    return -moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A1)]
 11940192                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
 11940193                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
 11940194                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
 11940195                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
 11940196                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
 11940197                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
 11940198                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
 199
 200                // Green
 201                case 2:
 11940202                    return -moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A1)]
 11940203                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
 11940204                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
 11940205                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
 11940206                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
 11940207                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
 11940208                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
 11940209                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
 210
 211                // Blue
 212                case 1:
 11940213                    return -moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A1)]
 11940214                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
 11940215                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
 11940216                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
 11940217                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
 11940218                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
 11940219                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
 11940220                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
 221
 222                // Alpha
 223                case 0:
 11940224                    return -moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
 11940225                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
 11940226                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
 11940227                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
 11940228                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
 11940229                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
 11940230                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
 11940231                        + 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:
 346140252                    return moment[WuAlphaColorQuantizer.GetIndex(position, cube.G1, cube.B1, cube.A1)]
 346140253                        - moment[WuAlphaColorQuantizer.GetIndex(position, cube.G1, cube.B1, cube.A0)]
 346140254                        - moment[WuAlphaColorQuantizer.GetIndex(position, cube.G1, cube.B0, cube.A1)]
 346140255                        + moment[WuAlphaColorQuantizer.GetIndex(position, cube.G1, cube.B0, cube.A0)]
 346140256                        - moment[WuAlphaColorQuantizer.GetIndex(position, cube.G0, cube.B1, cube.A1)]
 346140257                        + moment[WuAlphaColorQuantizer.GetIndex(position, cube.G0, cube.B1, cube.A0)]
 346140258                        + moment[WuAlphaColorQuantizer.GetIndex(position, cube.G0, cube.B0, cube.A1)]
 346140259                        - moment[WuAlphaColorQuantizer.GetIndex(position, cube.G0, cube.B0, cube.A0)];
 260
 261                // Green
 262                case 2:
 469980263                    return moment[WuAlphaColorQuantizer.GetIndex(cube.R1, position, cube.B1, cube.A1)]
 469980264                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, position, cube.B1, cube.A0)]
 469980265                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, position, cube.B0, cube.A1)]
 469980266                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, position, cube.B0, cube.A0)]
 469980267                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, position, cube.B1, cube.A1)]
 469980268                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, position, cube.B1, cube.A0)]
 469980269                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, position, cube.B0, cube.A1)]
 469980270                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, position, cube.B0, cube.A0)];
 271
 272                // Blue
 273                case 1:
 487260274                    return moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, position, cube.A1)]
 487260275                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, position, cube.A0)]
 487260276                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, position, cube.A1)]
 487260277                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, position, cube.A0)]
 487260278                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, position, cube.A1)]
 487260279                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, position, cube.A0)]
 487260280                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, position, cube.A1)]
 487260281                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, position, cube.A0)];
 282
 283                // Alpha
 284                case 0:
 144060285                    return moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, position)]
 144060286                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, position)]
 144060287                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, position)]
 144060288                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, position)]
 144060289                        - moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, position)]
 144060290                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, position)]
 144060291                        + moment[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, position)]
 144060292                        - 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        {
 24304            Array.Clear(this.vwt, 0, WuAlphaColorQuantizer.TableLength);
 24305            Array.Clear(this.vmr, 0, WuAlphaColorQuantizer.TableLength);
 24306            Array.Clear(this.vmg, 0, WuAlphaColorQuantizer.TableLength);
 24307            Array.Clear(this.vmb, 0, WuAlphaColorQuantizer.TableLength);
 24308            Array.Clear(this.vma, 0, WuAlphaColorQuantizer.TableLength);
 24309            Array.Clear(this.m2, 0, WuAlphaColorQuantizer.TableLength);
 310
 24311            Array.Clear(this.tag, 0, WuAlphaColorQuantizer.TableLength);
 24312        }
 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        {
 9276320            for (int i = 0; i < image.Length; i += 4)
 321            {
 4614322                int a = image[i + 3];
 4614323                int r = image[i + 2];
 4614324                int g = image[i + 1];
 4614325                int b = image[i];
 326
 4614327                int inr = r >> (8 - WuAlphaColorQuantizer.IndexBits);
 4614328                int ing = g >> (8 - WuAlphaColorQuantizer.IndexBits);
 4614329                int inb = b >> (8 - WuAlphaColorQuantizer.IndexBits);
 4614330                int ina = a >> (8 - WuAlphaColorQuantizer.IndexAlphaBits);
 331
 4614332                int ind = WuAlphaColorQuantizer.GetIndex(inr + 1, ing + 1, inb + 1, ina + 1);
 333
 4614334                this.vwt[ind]++;
 4614335                this.vmr[ind] += r;
 4614336                this.vmg[ind] += g;
 4614337                this.vmb[ind] += b;
 4614338                this.vma[ind] += a;
 4614339                this.m2[ind] += (r * r) + (g * g) + (b * b) + (a * a);
 340            }
 24341        }
 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        {
 24349            long[] volume = new long[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 24350            long[] volumeR = new long[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 24351            long[] volumeG = new long[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 24352            long[] volumeB = new long[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 24353            long[] volumeA = new long[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 24354            double[] volume2 = new double[WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount];
 355
 24356            long[] area = new long[WuAlphaColorQuantizer.IndexAlphaCount];
 24357            long[] areaR = new long[WuAlphaColorQuantizer.IndexAlphaCount];
 24358            long[] areaG = new long[WuAlphaColorQuantizer.IndexAlphaCount];
 24359            long[] areaB = new long[WuAlphaColorQuantizer.IndexAlphaCount];
 24360            long[] areaA = new long[WuAlphaColorQuantizer.IndexAlphaCount];
 24361            double[] area2 = new double[WuAlphaColorQuantizer.IndexAlphaCount];
 362
 3120363            for (int r = 1; r < WuAlphaColorQuantizer.IndexCount; r++)
 364            {
 1536365                Array.Clear(volume, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 1536366                Array.Clear(volumeR, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 1536367                Array.Clear(volumeG, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 1536368                Array.Clear(volumeB, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 1536369                Array.Clear(volumeA, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 1536370                Array.Clear(volume2, 0, WuAlphaColorQuantizer.IndexCount * WuAlphaColorQuantizer.IndexAlphaCount);
 371
 199680372                for (int g = 1; g < WuAlphaColorQuantizer.IndexCount; g++)
 373                {
 98304374                    Array.Clear(area, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 98304375                    Array.Clear(areaR, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 98304376                    Array.Clear(areaG, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 98304377                    Array.Clear(areaB, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 98304378                    Array.Clear(areaA, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 98304379                    Array.Clear(area2, 0, WuAlphaColorQuantizer.IndexAlphaCount);
 380
 12779520381                    for (int b = 1; b < WuAlphaColorQuantizer.IndexCount; b++)
 382                    {
 6291456383                        long line = 0;
 6291456384                        long lineR = 0;
 6291456385                        long lineG = 0;
 6291456386                        long lineB = 0;
 6291456387                        long lineA = 0;
 6291456388                        double line2 = 0;
 389
 213909504390                        for (int a = 1; a < WuAlphaColorQuantizer.IndexAlphaCount; a++)
 391                        {
 100663296392                            int ind1 = WuAlphaColorQuantizer.GetIndex(r, g, b, a);
 393
 100663296394                            line += this.vwt[ind1];
 100663296395                            lineR += this.vmr[ind1];
 100663296396                            lineG += this.vmg[ind1];
 100663296397                            lineB += this.vmb[ind1];
 100663296398                            lineA += this.vma[ind1];
 100663296399                            line2 += this.m2[ind1];
 400
 100663296401                            area[a] += line;
 100663296402                            areaR[a] += lineR;
 100663296403                            areaG[a] += lineG;
 100663296404                            areaB[a] += lineB;
 100663296405                            areaA[a] += lineA;
 100663296406                            area2[a] += line2;
 407
 100663296408                            int inv = (b * WuAlphaColorQuantizer.IndexAlphaCount) + a;
 409
 100663296410                            volume[inv] += area[a];
 100663296411                            volumeR[inv] += areaR[a];
 100663296412                            volumeG[inv] += areaG[a];
 100663296413                            volumeB[inv] += areaB[a];
 100663296414                            volumeA[inv] += areaA[a];
 100663296415                            volume2[inv] += area2[a];
 416
 100663296417                            int ind2 = ind1 - WuAlphaColorQuantizer.GetIndex(1, 0, 0, 0);
 418
 100663296419                            this.vwt[ind1] = this.vwt[ind2] + volume[inv];
 100663296420                            this.vmr[ind1] = this.vmr[ind2] + volumeR[inv];
 100663296421                            this.vmg[ind1] = this.vmg[ind2] + volumeG[inv];
 100663296422                            this.vmb[ind1] = this.vmb[ind2] + volumeB[inv];
 100663296423                            this.vma[ind1] = this.vma[ind2] + volumeA[inv];
 100663296424                            this.m2[ind1] = this.m2[ind2] + volume2[inv];
 425                        }
 426                    }
 427                }
 428            }
 24429        }
 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        {
 3129438            double dr = WuAlphaColorQuantizer.Volume(cube, this.vmr);
 3129439            double dg = WuAlphaColorQuantizer.Volume(cube, this.vmg);
 3129440            double db = WuAlphaColorQuantizer.Volume(cube, this.vmb);
 3129441            double da = WuAlphaColorQuantizer.Volume(cube, this.vma);
 442
 3129443            double xx =
 3129444                this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, cube.A1)]
 3129445                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B1, cube.A0)]
 3129446                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A1)]
 3129447                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G1, cube.B0, cube.A0)]
 3129448                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A1)]
 3129449                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B1, cube.A0)]
 3129450                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A1)]
 3129451                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R1, cube.G0, cube.B0, cube.A0)]
 3129452                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A1)]
 3129453                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B1, cube.A0)]
 3129454                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A1)]
 3129455                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G1, cube.B0, cube.A0)]
 3129456                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A1)]
 3129457                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B1, cube.A0)]
 3129458                - this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A1)]
 3129459                + this.m2[WuAlphaColorQuantizer.GetIndex(cube.R0, cube.G0, cube.B0, cube.A0)];
 460
 3129461            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        {
 9552484            long baseR = WuAlphaColorQuantizer.Bottom(cube, direction, this.vmr);
 9552485            long baseG = WuAlphaColorQuantizer.Bottom(cube, direction, this.vmg);
 9552486            long baseB = WuAlphaColorQuantizer.Bottom(cube, direction, this.vmb);
 9552487            long baseA = WuAlphaColorQuantizer.Bottom(cube, direction, this.vma);
 9552488            long baseW = WuAlphaColorQuantizer.Bottom(cube, direction, this.vwt);
 489
 9552490            double max = 0.0;
 9552491            cut = -1;
 492
 598080493            for (int i = first; i < last; i++)
 494            {
 289488495                double halfR = baseR + WuAlphaColorQuantizer.Top(cube, direction, i, this.vmr);
 289488496                double halfG = baseG + WuAlphaColorQuantizer.Top(cube, direction, i, this.vmg);
 289488497                double halfB = baseB + WuAlphaColorQuantizer.Top(cube, direction, i, this.vmb);
 289488498                double halfA = baseA + WuAlphaColorQuantizer.Top(cube, direction, i, this.vma);
 289488499                double halfW = baseW + WuAlphaColorQuantizer.Top(cube, direction, i, this.vwt);
 500
 289488501                if (halfW == 0)
 502                {
 503                    continue;
 504                }
 505
 221991506                double temp = ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW;
 507
 221991508                halfR = wholeR - halfR;
 221991509                halfG = wholeG - halfG;
 221991510                halfB = wholeB - halfB;
 221991511                halfA = wholeA - halfA;
 221991512                halfW = wholeW - halfW;
 513
 221991514                if (halfW == 0)
 515                {
 516                    continue;
 517                }
 518
 25509519                temp += ((halfR * halfR) + (halfG * halfG) + (halfB * halfB) + (halfA * halfA)) / halfW;
 520
 25509521                if (temp > max)
 522                {
 5058523                    max = temp;
 5058524                    cut = i;
 525                }
 526            }
 527
 9552528            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        {
 2388539            double wholeR = WuAlphaColorQuantizer.Volume(set1, this.vmr);
 2388540            double wholeG = WuAlphaColorQuantizer.Volume(set1, this.vmg);
 2388541            double wholeB = WuAlphaColorQuantizer.Volume(set1, this.vmb);
 2388542            double wholeA = WuAlphaColorQuantizer.Volume(set1, this.vma);
 2388543            double wholeW = WuAlphaColorQuantizer.Volume(set1, this.vwt);
 544
 545            int cutr;
 546            int cutg;
 547            int cutb;
 548            int cuta;
 549
 2388550            double maxr = this.Maximize(set1, 3, set1.R0 + 1, set1.R1, out cutr, wholeR, wholeG, wholeB, wholeA, wholeW)
 2388551            double maxg = this.Maximize(set1, 2, set1.G0 + 1, set1.G1, out cutg, wholeR, wholeG, wholeB, wholeA, wholeW)
 2388552            double maxb = this.Maximize(set1, 1, set1.B0 + 1, set1.B1, out cutb, wholeR, wholeG, wholeB, wholeA, wholeW)
 2388553            double maxa = this.Maximize(set1, 0, set1.A0 + 1, set1.A1, out cuta, wholeR, wholeG, wholeB, wholeA, wholeW)
 554
 555            int dir;
 556
 2388557            if ((maxr >= maxg) && (maxr >= maxb) && (maxr >= maxa))
 558            {
 1251559                dir = 3;
 560
 1251561                if (cutr < 0)
 562                {
 822563                    return false;
 564                }
 565            }
 1137566            else if ((maxg >= maxr) && (maxg >= maxb) && (maxg >= maxa))
 567            {
 291568                dir = 2;
 569            }
 846570            else if ((maxb >= maxr) && (maxb >= maxg) && (maxb >= maxa))
 571            {
 393572                dir = 1;
 573            }
 574            else
 575            {
 453576                dir = 0;
 577            }
 578
 1566579            set2.R1 = set1.R1;
 1566580            set2.G1 = set1.G1;
 1566581            set2.B1 = set1.B1;
 1566582            set2.A1 = set1.A1;
 583
 584            switch (dir)
 585            {
 586                // Red
 587                case 3:
 429588                    set2.R0 = set1.R1 = cutr;
 429589                    set2.G0 = set1.G0;
 429590                    set2.B0 = set1.B0;
 429591                    set2.A0 = set1.A0;
 429592                    break;
 593
 594                // Green
 595                case 2:
 291596                    set2.G0 = set1.G1 = cutg;
 291597                    set2.R0 = set1.R0;
 291598                    set2.B0 = set1.B0;
 291599                    set2.A0 = set1.A0;
 291600                    break;
 601
 602                // Blue
 603                case 1:
 393604                    set2.B0 = set1.B1 = cutb;
 393605                    set2.R0 = set1.R0;
 393606                    set2.G0 = set1.G0;
 393607                    set2.A0 = set1.A0;
 393608                    break;
 609
 610                // Alpha
 611                case 0:
 453612                    set2.A0 = set1.A1 = cuta;
 453613                    set2.R0 = set1.R0;
 453614                    set2.G0 = set1.G0;
 453615                    set2.B0 = set1.B0;
 616                    break;
 617            }
 618
 1566619            set1.Volume = (set1.R1 - set1.R0) * (set1.G1 - set1.G0) * (set1.B1 - set1.B0) * (set1.A1 - set1.A0);
 1566620            set2.Volume = (set2.R1 - set2.R0) * (set2.G1 - set2.G0) * (set2.B1 - set2.B0) * (set2.A1 - set2.A0);
 621
 1566622            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        {
 84588632            for (int r = cube.R0 + 1; r <= cube.R1; r++)
 633            {
 2563584634                for (int g = cube.G0 + 1; g <= cube.G1; g++)
 635                {
 43376640636                    for (int b = cube.B0 + 1; b <= cube.B1; b++)
 637                    {
 242221056638                        for (int a = cube.A0 + 1; a <= cube.A1; a++)
 639                        {
 100663296640                            this.tag[WuAlphaColorQuantizer.GetIndex(r, g, b, a)] = label;
 641                        }
 642                    }
 643                }
 644            }
 1590645        }
 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        {
 24654            cube = new Box[colorCount];
 24655            double[] vv = new double[colorCount];
 656
 12336657            for (int i = 0; i < colorCount; i++)
 658            {
 6144659                cube[i] = new Box();
 660            }
 661
 24662            cube[0].R0 = cube[0].G0 = cube[0].B0 = cube[0].A0 = 0;
 24663            cube[0].R1 = cube[0].G1 = cube[0].B1 = WuAlphaColorQuantizer.IndexCount - 1;
 24664            cube[0].A1 = WuAlphaColorQuantizer.IndexAlphaCount - 1;
 665
 24666            int next = 0;
 667
 4776668            for (int i = 1; i < colorCount; i++)
 669            {
 2388670                if (this.Cut(cube[next], cube[i]))
 671                {
 1566672                    vv[next] = cube[next].Volume > 1 ? this.Variance(cube[next]) : 0.0;
 1566673                    vv[i] = cube[i].Volume > 1 ? this.Variance(cube[i]) : 0.0;
 674                }
 675                else
 676                {
 822677                    vv[next] = 0.0;
 822678                    i--;
 679                }
 680
 2388681                next = 0;
 682
 2388683                double temp = vv[0];
 347928684                for (int k = 1; k <= i; k++)
 685                {
 171576686                    if (vv[k] > temp)
 687                    {
 2259688                        temp = vv[k];
 2259689                        next = k;
 690                    }
 691                }
 692
 2388693                if (temp <= 0.0)
 694                {
 24695                    colorCount = i + 1;
 24696                    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        {
 24710            var quantizedImage = new ColorQuantizerResult(image.Length / 4, colorCount);
 711
 3228712            for (int k = 0; k < colorCount; k++)
 713            {
 1590714                this.Mark(cube[k], (byte)k);
 715
 1590716                double weight = WuAlphaColorQuantizer.Volume(cube[k], this.vwt);
 717
 1590718                if (weight != 0)
 719                {
 1590720                    quantizedImage.Palette[(k * 4) + 3] = (byte)(WuAlphaColorQuantizer.Volume(cube[k], this.vma) / weigh
 1590721                    quantizedImage.Palette[(k * 4) + 2] = (byte)(WuAlphaColorQuantizer.Volume(cube[k], this.vmr) / weigh
 1590722                    quantizedImage.Palette[(k * 4) + 1] = (byte)(WuAlphaColorQuantizer.Volume(cube[k], this.vmg) / weigh
 1590723                    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
 9276734            for (int i = 0; i < image.Length / 4; i++)
 735            {
 4614736                int a = image[(i * 4) + 3] >> (8 - WuAlphaColorQuantizer.IndexAlphaBits);
 4614737                int r = image[(i * 4) + 2] >> (8 - WuAlphaColorQuantizer.IndexBits);
 4614738                int g = image[(i * 4) + 1] >> (8 - WuAlphaColorQuantizer.IndexBits);
 4614739                int b = image[i * 4] >> (8 - WuAlphaColorQuantizer.IndexBits);
 740
 4614741                int ind = WuAlphaColorQuantizer.GetIndex(r + 1, g + 1, b + 1, a + 1);
 742
 4614743                quantizedImage.Bytes[i] = this.tag[ind];
 744            }
 745
 24746            return quantizedImage;
 747        }
 748    }
 749}
 750