/// <summary>
 /// Adds a pixel to the color history.
 /// </summary>
 /// <param name="pixel">
 /// The pixel to add.
 /// </param>
 public void AddPixel(Color32 pixel)
 {
     this.Alpha += pixel.A;
     this.Red += pixel.R;
     this.Green += pixel.G;
     this.Blue += pixel.B;
     this.Sum++;
 }
Example #2
0
 /// <summary>
 /// Quantizes the image contained within the <see cref="ImageBuffer"/> returning the result.
 /// </summary>
 /// <param name="imageBuffer">
 /// The <see cref="ImageBuffer"/> for storing and manipulating pixel information..
 /// </param>
 /// <param name="colorCount">
 /// The maximum number of colors apply to the image.
 /// </param>
 /// <param name="lookups">
 /// The array of <see cref="Color32"/> containing indexed versions of the images colors.
 /// </param>
 /// <param name="alphaThreshold">
 /// All colors with an alpha value less than this will be considered fully transparent.
 /// </param>
 /// <returns>
 /// The quantized <see cref="Bitmap"/>.
 /// </returns>
 internal override Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Color32[] lookups, int alphaThreshold)
 {
     Bitmap result = new Bitmap(imageBuffer.Image.Width, imageBuffer.Image.Height, PixelFormat.Format8bppIndexed);
     result.SetResolution(imageBuffer.Image.HorizontalResolution, imageBuffer.Image.VerticalResolution);
     ImageBuffer resultBuffer = new ImageBuffer(result);
     PaletteColorHistory[] paletteHistogram = new PaletteColorHistory[colorCount + 1];
     resultBuffer.UpdatePixelIndexes(IndexedPixels(imageBuffer, lookups, alphaThreshold, paletteHistogram));
     result.Palette = BuildPalette(result.Palette, paletteHistogram);
     return result;
 }
        /// <summary>
        /// Initializes a new instance of the <see cref="PaletteLookup"/> class.
        /// </summary>
        /// <param name="palette">
        /// The palette.
        /// </param>
        public PaletteLookup(Color32[] palette)
        {
            this.Palette = new LookupNode[palette.Length];
            for (int paletteIndex = 0; paletteIndex < palette.Length; paletteIndex++)
            {
                this.Palette[paletteIndex] = new LookupNode
                {
                    Color32 = palette[paletteIndex],
                    PaletteIndex = (byte)paletteIndex
                };
            }

            this.BuildLookup(palette);
        }
        /// <summary>
        /// Gets palette index for the given pixel.
        /// </summary>
        /// <param name="pixel">
        /// The pixel to return the index for.
        /// </param>
        /// <returns>
        /// The <see cref="byte"/> representing the index.
        /// </returns>
        public byte GetPaletteIndex(Color32 pixel)
        {
            int pixelKey = pixel.Argb & this.paletteMask;
            LookupNode[] bucket;
            if (!this.lookupNodes.TryGetValue(pixelKey, out bucket))
            {
                bucket = this.Palette;
            }

            if (bucket.Length == 1)
            {
                return bucket[0].PaletteIndex;
            }

            int bestDistance = int.MaxValue;
            byte bestMatch = 0;
            foreach (LookupNode lookup in bucket)
            {
                Color32 lookupPixel = lookup.Color32;

                int deltaAlpha = pixel.A - lookupPixel.A;
                int distance = deltaAlpha * deltaAlpha;

                int deltaRed = pixel.R - lookupPixel.R;
                distance += deltaRed * deltaRed;

                int deltaGreen = pixel.G - lookupPixel.G;
                distance += deltaGreen * deltaGreen;

                int deltaBlue = pixel.B - lookupPixel.B;
                distance += deltaBlue * deltaBlue;

                if (distance >= bestDistance)
                {
                    continue;
                }

                bestDistance = distance;
                bestMatch = lookup.PaletteIndex;
            }

            if ((bucket == this.Palette) && (pixelKey != 0))
            {
                this.lookupNodes[pixelKey] = new[] { bucket[bestMatch] };
            }

            return bestMatch;
        }
            public static void then_should_add_each_property_value_together()
            {

                // Arrange
                var colorMoment = new ColorMoment { Alpha = 1, Blue = 2, Green = 3, Moment = 4, Red = 5, Weight = 6 };
                var color32 = new Color32(6, 5, 4, 3);

                // Act
                colorMoment.Add(color32);

                // Assert
                Assert.That(colorMoment.Alpha, Is.EqualTo(7));
                Assert.That(colorMoment.Red, Is.EqualTo(10));
                Assert.That(colorMoment.Green, Is.EqualTo(7));
                Assert.That(colorMoment.Blue, Is.EqualTo(5));
                Assert.That(colorMoment.Moment, Is.EqualTo(90f));
                Assert.That(colorMoment.Weight, Is.EqualTo(7));
            }
Example #6
0
        /// <summary>
        /// Override this to process the pixel in the second pass of the algorithm
        /// </summary>
        /// <param name="pixel">
        /// The pixel to quantize
        /// </param>
        /// <returns>
        /// The quantized value
        /// </returns>
        protected override byte QuantizePixel(Color32* pixel)
        {
            // The color at [maxColors] is set to transparent
            byte paletteIndex = (byte)this.maxColors;

            // Get the palette index if this non-transparent
            if (pixel->A > 0)
            {
                paletteIndex = (byte)this.octree.GetPaletteIndex(pixel);
            }

            return paletteIndex;
        }
Example #7
0
 /// <summary>
 /// Adds a pixel to the current instance.
 /// </summary>
 /// <param name="pixel">
 /// The pixel to add.
 /// </param>
 public void Add(Color32 pixel)
 {
     byte alpha = pixel.A;
     byte red = pixel.R;
     byte green = pixel.G;
     byte blue = pixel.B;
     this.Alpha += alpha;
     this.Red += red;
     this.Green += green;
     this.Blue += blue;
     this.Weight++;
     this.Moment += (alpha * alpha) + (red * red) + (green * green) + (blue * blue);
 }
Example #8
0
 /// <summary>
 /// Returns the hash code for the given instance.
 /// </summary>
 /// <param name="obj">
 /// The instance of <see cref="Color32"/> to return the hash code for.
 /// </param>
 /// <returns>
 /// A 32-bit signed integer that is the hash code for this instance.
 /// </returns>
 private int GetHashCode(Color32 obj)
 {
     unchecked
     {
         int hashCode = obj.B.GetHashCode();
         hashCode = (hashCode * 397) ^ obj.G.GetHashCode();
         hashCode = (hashCode * 397) ^ obj.R.GetHashCode();
         hashCode = (hashCode * 397) ^ obj.A.GetHashCode();
         return hashCode;
     }
 }
        /// <summary>
        /// The build lookup.
        /// </summary>
        /// <param name="palette">
        /// The palette.
        /// </param>
        private void BuildLookup(Color32[] palette)
        {
            int mask = GetMask(palette);
            Dictionary<int, List<LookupNode>> tempLookup = new Dictionary<int, List<LookupNode>>();
            foreach (LookupNode lookup in this.Palette)
            {
                int pixelKey = lookup.Color32.Argb & mask;

                List<LookupNode> bucket;
                if (!tempLookup.TryGetValue(pixelKey, out bucket))
                {
                    bucket = new List<LookupNode>();
                    tempLookup[pixelKey] = bucket;
                }

                bucket.Add(lookup);
            }

            this.lookupNodes = new Dictionary<int, LookupNode[]>(tempLookup.Count);
            foreach (int key in tempLookup.Keys)
            {
                this.lookupNodes[key] = tempLookup[key].ToArray();
            }

            this.paletteMask = mask;
        }
Example #10
0
 /// <summary>
 /// Increment the pixel count and add to the color information
 /// </summary>
 /// <param name="pixel">
 /// The pixel to add.
 /// </param>
 public void Increment(Color32* pixel)
 {
     this.pixelCount++;
     this.red += pixel->R;
     this.green += pixel->G;
     this.blue += pixel->B;
 }
Example #11
0
                /// <summary>
                /// Add a color into the tree
                /// </summary>
                /// <param name="pixel">
                /// The color
                /// </param>
                /// <param name="colorBits">
                /// The number of significant color bits
                /// </param>
                /// <param name="level">
                /// The level in the tree
                /// </param>
                /// <param name="octree">
                /// The tree to which this node belongs
                /// </param>
                public void AddColor(Color32* pixel, int colorBits, int level, Octree octree)
                {
                    // Update the color information if this is a leaf
                    if (this.leaf)
                    {
                        this.Increment(pixel);

                        // Setup the previous node
                        octree.TrackPrevious(this);
                    }
                    else
                    {
                        // Go to the next level down in the tree
                        int shift = 7 - level;
                        int index = ((pixel->R & Mask[level]) >> (shift - 2)) |
                                    ((pixel->G & Mask[level]) >> (shift - 1)) |
                                    ((pixel->B & Mask[level]) >> shift);

                        OctreeNode child = this.children[index];

                        if (null == child)
                        {
                            // Create a new child node & store in the array
                            child = new OctreeNode(level + 1, colorBits, octree);
                            this.children[index] = child;
                        }

                        // Add the color to the child node
                        child.AddColor(pixel, colorBits, level + 1, octree);
                    }
                }
Example #12
0
 /// <summary>
 /// Override this to process the pixel in the second pass of the algorithm
 /// </summary>
 /// <param name="pixel">
 /// The pixel to quantize
 /// </param>
 /// <returns>
 /// The quantized value
 /// </returns>
 protected abstract byte QuantizePixel(Color32* pixel);
Example #13
0
 /// <summary>
 /// Override this to process the pixel in the first pass of the algorithm
 /// </summary>
 /// <param name="pixel">
 /// The pixel to quantize
 /// </param>
 /// <remarks>
 /// This function need only be overridden if your quantize algorithm needs two passes,
 /// such as an Octree quantizer.
 /// </remarks>
 protected virtual void InitialQuantizePixel(Color32* pixel)
 {
 }
Example #14
0
 /// <summary>
 /// Add a given color value to the Octree
 /// </summary>
 /// <param name="pixel">
 /// The <see cref="Color32"/>containing color information to add.
 /// </param>
 public void AddColor(Color32* pixel)
 {
     // Check if this request is for the same color as the last
     if (this.previousColor == pixel->Argb)
     {
         // If so, check if I have a previous node setup. This will only occur if the first color in the image
         // happens to be black, with an alpha component of zero.
         if (null == this.previousNode)
         {
             this.previousColor = pixel->Argb;
             this.root.AddColor(pixel, this.maxColorBits, 0, this);
         }
         else
         {
             // Just update the previous node
             this.previousNode.Increment(pixel);
         }
     }
     else
     {
         this.previousColor = pixel->Argb;
         this.root.AddColor(pixel, this.maxColorBits, 0, this);
     }
 }
Example #15
0
        /// <summary>
        /// Gets an enumerable array of bytes representing each row of the image.
        /// </summary>
        /// <param name="image">
        /// The <see cref="ImageBuffer"/> for storing and manipulating pixel information.
        /// </param>
        /// <param name="lookups">
        /// The array of <see cref="Color32"/> containing indexed versions of the images colors.
        /// </param>
        /// <param name="alphaThreshold">
        /// The alpha threshold.
        /// </param>
        /// <param name="paletteHistogram">
        /// The palette histogram.
        /// </param>
        /// <returns>
        /// The enumerable list of <see cref="byte"/> representing each pixel.
        /// </returns>
        private static IEnumerable<byte[]> IndexedPixels(ImageBuffer image, Color32[] lookups, int alphaThreshold, PaletteColorHistory[] paletteHistogram)
        {
            byte[] lineIndexes = new byte[image.Image.Width];
            PaletteLookup lookup = new PaletteLookup(lookups);

            // Determine the correct fallback color.
            byte fallback = lookups.Length < AlphaMax ? AlphaMin : AlphaMax;
            foreach (Color32[] pixelLine in image.PixelLines)
            {
                int length = pixelLine.Length;
                for (int i = 0; i < length; i++)
                {
                    Color32 pixel = pixelLine[i];
                    byte bestMatch = fallback;
                    if (pixel.A > alphaThreshold)
                    {
                        bestMatch = lookup.GetPaletteIndex(pixel);
                        paletteHistogram[bestMatch].AddPixel(pixel);
                    }

                    lineIndexes[i] = bestMatch;
                }

                yield return lineIndexes;
            }
        }
Example #16
0
 /// <summary>
 /// Get the palette index for the passed color
 /// </summary>
 /// <param name="pixel">
 /// The <see cref="Color32"/> containing the pixel data.
 /// </param>
 /// <returns>
 /// The index of the given structure.
 /// </returns>
 public int GetPaletteIndex(Color32* pixel)
 {
     return this.root.GetPaletteIndex(pixel, 0);
 }
 /// <summary>
 /// Quantizes the image contained within the <see cref="ImageBuffer"/> returning the result.
 /// </summary>
 /// <param name="imageBuffer">
 /// The <see cref="ImageBuffer"/> for storing and manipulating pixel information..
 /// </param>
 /// <param name="colorCount">
 /// The maximum number of colors apply to the image.
 /// </param>
 /// <param name="lookups">
 /// The array of <see cref="Color32"/> containing indexed versions of the images colors.
 /// </param>
 /// <param name="alphaThreshold">
 /// All colors with an alpha value less than this will be considered fully transparent.
 /// </param>
 /// <returns>
 /// The quantized <see cref="Bitmap"/>.
 /// </returns>
 internal abstract Bitmap GetQuantizedImage(ImageBuffer imageBuffer, int colorCount, Color32[] lookups, int alphaThreshold);
Example #18
0
                /// <summary>
                /// Return the palette index for the passed color
                /// </summary>
                /// <param name="pixel">
                /// The <see cref="Color32"/> representing the pixel.
                /// </param>
                /// <param name="level">
                /// The level.
                /// </param>
                /// <returns>
                /// The <see cref="int"/> representing the index of the pixel in the palette.
                /// </returns>
                public int GetPaletteIndex(Color32* pixel, int level)
                {
                    int index = this.paletteIndex;

                    if (!this.leaf)
                    {
                        int shift = 7 - level;
                        int pixelIndex = ((pixel->R & Mask[level]) >> (shift - 2)) |
                                    ((pixel->G & Mask[level]) >> (shift - 1)) |
                                    ((pixel->B & Mask[level]) >> shift);

                        if (null != this.children[pixelIndex])
                        {
                            index = this.children[pixelIndex].GetPaletteIndex(pixel, level + 1);
                        }
                        else
                        {
                            throw new Exception("Didn't expect this!");
                        }
                    }

                    return index;
                }
        private static Color32[] BuildLookups(Box[] cubes, ColorMoment[, , ,] moments)
        {
            Color32[] lookups = new Color32[cubes.Length];

            for (int cubeIndex = 0; cubeIndex < cubes.Length; cubeIndex++)
            {
                ColorMoment volume = Volume(moments, cubes[cubeIndex]);

                if (volume.Weight <= 0)
                {
                    continue;
                }

                Color32 lookup = new Color32
                {
                    A = (byte)(volume.Alpha / volume.Weight),
                    R = (byte)(volume.Red / volume.Weight),
                    G = (byte)(volume.Green / volume.Weight),
                    B = (byte)(volume.Blue / volume.Weight)
                };

                lookups[cubeIndex] = lookup;
            }

            return lookups;
        }
Example #20
0
 /// <summary>
 /// Process the pixel in the first pass of the algorithm
 /// </summary>
 /// <param name="pixel">
 /// The pixel to quantize
 /// </param>
 /// <remarks>
 /// This function need only be overridden if your quantize algorithm needs two passes,
 /// such as an Octree quantizer.
 /// </remarks>
 protected override void InitialQuantizePixel(Color32* pixel)
 {
     // Add the color to the Octree
     this.octree.AddColor(pixel);
 }
        /// <summary>
        /// Gets the mask value from the palette.
        /// </summary>
        /// <param name="palette">
        /// The palette.
        /// </param>
        /// <returns>
        /// The <see cref="int"/> representing the component value of the mask.
        /// </returns>
        private static int GetMask(Color32[] palette)
        {
            IEnumerable<byte> alphas = palette.Select(p => p.A).ToArray();
            byte maxAlpha = alphas.Max();
            int uniqueAlphas = alphas.Distinct().Count();

            IEnumerable<byte> reds = palette.Select(p => p.R).ToArray();
            byte maxRed = reds.Max();
            int uniqueReds = reds.Distinct().Count();

            IEnumerable<byte> greens = palette.Select(p => p.G).ToArray();
            byte maxGreen = greens.Max();
            int uniqueGreens = greens.Distinct().Count();

            IEnumerable<byte> blues = palette.Select(p => p.B).ToArray();
            byte maxBlue = blues.Max();
            int uniqueBlues = blues.Distinct().Count();

            double totalUniques = uniqueAlphas + uniqueReds + uniqueGreens + uniqueBlues;

            double availableBits = 1.0 + Math.Log(uniqueAlphas * uniqueReds * uniqueGreens * uniqueBlues);

            byte alphaMask = ComputeBitMask(maxAlpha, Convert.ToInt32(Math.Round(uniqueAlphas / totalUniques * availableBits)));
            byte redMask = ComputeBitMask(maxRed, Convert.ToInt32(Math.Round(uniqueReds / totalUniques * availableBits)));
            byte greenMask = ComputeBitMask(maxGreen, Convert.ToInt32(Math.Round(uniqueGreens / totalUniques * availableBits)));
            byte blueMask = ComputeBitMask(maxBlue, Convert.ToInt32(Math.Round(uniqueBlues / totalUniques * availableBits)));

            Color32 maskedPixel = new Color32(alphaMask, redMask, greenMask, blueMask);
            return maskedPixel.Argb;
        }