/// <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(ColorBgra* pixel)
 {
     byte colorIndex = 0;
     uint colorHash = pixel->Bgra;
     // Check if the color is in the lookup table
     if (this._colorMap.ContainsKey(colorHash)) {
         colorIndex = this._colorMap[colorHash];
     } else {
         // Not found - loop through the palette and find the nearest match.
         // Firstly check the alpha value - if 0, lookup the transparent color
         if (0 == pixel->A) {
             // Transparent. Lookup the first color with an alpha value of 0
             for (int index = 0; index < this._colors.Length; index++) {
                 if (0 == this._colors[index].A) {
                     colorIndex = (byte) index;
                     break;
                 }
             }
         } else {
             // Not transparent...
             int leastDistance = int.MaxValue;
             int red = pixel->R;
             int green = pixel->G;
             int blue = pixel->B;
             // Loop through the entire palette, looking for the closest color match
             for (int index = 0; index < this._colors.Length; index++) {
                 Color paletteColor = this._colors[index];
                 int redDistance = paletteColor.R - red;
                 int greenDistance = paletteColor.G - green;
                 int blueDistance = paletteColor.B - blue;
                 int distance = (redDistance * redDistance) + (greenDistance * greenDistance) +
                                (blueDistance * blueDistance);
                 if (distance < leastDistance) {
                     colorIndex = (byte) index;
                     leastDistance = distance;
                     // And if it's an exact match, exit the loop
                     if (0 == distance) {
                         break;
                     }
                 }
             }
         }
         // Now I have the color, pop it into the hashtable for next time
         this._colorMap.Add(colorHash, colorIndex);
     }
     return colorIndex;
 }
Example #2
0
 /// <summary>
 /// Constructs a new ColorBgra instance with the given 32-bit value.
 /// </summary>
 public static ColorBgra FromUInt32(UInt32 bgra)
 {
     ColorBgra color = new ColorBgra();
     color.Bgra = bgra;
     return color;
 }
Example #3
0
 /// <summary>
 /// Creates a new ColorBgra instance with the given color and alpha values.
 /// </summary>
 public static ColorBgra FromRgba(byte r, byte g, byte b, byte a)
 {
     ColorBgra color = new ColorBgra();
     color.R = r;
     color.G = g;
     color.B = b;
     color.A = a;
     return color;
 }
Example #4
0
 /// <summary>
 /// Creates a new ColorBgra instance with the given color and alpha values.
 /// </summary>
 public static ColorBgra FromBgra(byte b, byte g, byte r, byte a)
 {
     ColorBgra color = new ColorBgra();
     color.Bgra = BgraToUInt32(b, g, r, a);
     return color;
 }
Example #5
0
 public static ColorBgra Clamped(double b, double g, double r, double a)
 {
     ColorBgra color = new ColorBgra();
     if (r > 255) {
         color.R = 255;
     } else if (r < 0) {
         color.R = 0;
     } else {
         color.R = (byte) r;
     }
     if (g > 255) {
         color.G = 255;
     } else if (g < 0) {
         color.G = 0;
     } else {
         color.G = (byte) g;
     }
     if (b > 255) {
         color.B = 255;
     } else if (b < 0) {
         color.B = 0;
     } else {
         color.B = (byte) b;
     }
     if (a > 255) {
         color.A = 255;
     } else if (a < 0) {
         color.A = 0;
     } else {
         color.A = (byte) a;
     }
     return color;
 }
 /// <summary>
 /// Increment the pixel count and add to the color information
 /// </summary>
 public void Increment(ColorBgra* pixel)
 {
     ++this._pixelCount;
     this._red += pixel->R;
     this._green += pixel->G;
     this._blue += pixel->B;
 }
 /// <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(ColorBgra* 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);
     }
 }
 /// <summary>
 /// Return the palette index for the passed color
 /// </summary>
 public int GetPaletteIndex(ColorBgra* pixel, int level)
 {
     int paletteIndex = this._paletteIndex;
     if (!this._leaf) {
         int shift = 7 - level;
         int index = ((pixel->R & mask[level]) >> (shift - 2)) |
                     ((pixel->G & mask[level]) >> (shift - 1)) | ((pixel->B & mask[level]) >> (shift));
         if (null != this._children[index]) {
             paletteIndex = this._children[index].GetPaletteIndex(pixel, level + 1);
         } else {
             paletteIndex = -1;
         }
     }
     return paletteIndex;
 }
 /// <summary>
 /// Add a given color value to the octree
 /// </summary>
 /// <param name="pixel"></param>
 public void AddColor(ColorBgra* pixel)
 {
     // Check if this request is for the same color as the last
     if (this._previousColor == pixel->Bgra) {
         // If so, check if I have a previous node setup. This will only ocurr if the first color in the image
         // happens to be black, with an alpha component of zero.
         if (null == this._previousNode) {
             this._previousColor = pixel->Bgra;
             this._root.AddColor(pixel, this._maxColorBits, 0, this);
         } else {
             // Just update the previous node
             this._previousNode.Increment(pixel);
         }
     } else {
         this._previousColor = pixel->Bgra;
         this._root.AddColor(pixel, this._maxColorBits, 0, this);
     }
 }
Example #10
0
 /// <summary>
 /// Get the palette index for the passed color
 /// </summary>
 /// <param name="pixel"></param>
 /// <returns></returns>
 public int GetPaletteIndex(ColorBgra* pixel)
 {
     int ret = -1;
     ret = this._root.GetPaletteIndex(pixel, 0);
     if (ret < 0) {
         if (this.paletteTable == null) {
             this.paletteTable = new PaletteTable(this._palette);
         }
         ret = this.paletteTable.FindClosestPaletteIndex(pixel->ToColor());
     }
     return ret;
 }
Example #11
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(ColorBgra* pixel)
 {
     byte paletteIndex = (byte) this._maxColors; // The color at [_maxColors] is set to transparent
     // Get the palette index if this non-transparent
     if (pixel->A > 0) {
         paletteIndex = (byte) this._octree.GetPaletteIndex(pixel);
     }
     return paletteIndex;
 }
Example #12
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(ColorBgra* pixel)
 {
     this._octree.AddColor(pixel);
 }
Example #13
0
 /// <summary>
 /// Execute a second pass through the bitmap
 /// </summary>
 /// <param name="sourceData">The source bitmap, locked into memory</param>
 /// <param name="output">The output bitmap</param>
 /// <param name="width">The width in pixels of the image</param>
 /// <param name="height">The height in pixels of the image</param>
 /// <param name="bounds">The bounding rectangle</param>
 protected virtual void SecondPass(BitmapData sourceData, Bitmap output, int width, int height, Rectangle bounds)
 {
     BitmapData outputData = null;
     Color[] pallete = output.Palette.Entries;
     int weight = this.ditherLevel;
     try {
         // Lock the output bitmap into memory
         outputData = output.LockBits(bounds, ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed);
         // Define the source data pointers. The source row is a byte to
         // keep addition of the stride value easier (as this is in bytes)
         byte* pSourceRow = (byte*) sourceData.Scan0.ToPointer();
         Int32* pSourcePixel = (Int32*) pSourceRow;
         // Now define the destination data pointers
         byte* pDestinationRow = (byte*) outputData.Scan0.ToPointer();
         byte* pDestinationPixel = pDestinationRow;
         int[] errorThisRowR = new int[width + 1];
         int[] errorThisRowG = new int[width + 1];
         int[] errorThisRowB = new int[width + 1];
         for (int row = 0; row < height; row++) {
             int[] errorNextRowR = new int[width + 1];
             int[] errorNextRowG = new int[width + 1];
             int[] errorNextRowB = new int[width + 1];
             int ptrInc;
             if ((row & 1) == 0) {
                 pSourcePixel = (Int32*) pSourceRow;
                 pDestinationPixel = pDestinationRow;
                 ptrInc = +1;
             } else {
                 pSourcePixel = (Int32*) pSourceRow + width - 1;
                 pDestinationPixel = pDestinationRow + width - 1;
                 ptrInc = -1;
             }
             // Loop through each pixel on this scan line
             for (int col = 0; col < width; ++col) {
                 // Quantize the pixel
                 ColorBgra srcPixel = *(ColorBgra*) pSourcePixel;
                 ColorBgra target = new ColorBgra();
                 //target.B = ColorBgra.Clamped(srcPixel.B - ((errorThisRowB[col] * weight) / 8));
                 //target.G = Utility.ClampToByte(srcPixel.G - ((errorThisRowG[col] * weight) / 8));
                 //target.R = Utility.ClampToByte(srcPixel.R - ((errorThisRowR[col] * weight) / 8));
                 //target.A = srcPixel.A;
                 target = ColorBgra.Clamped(srcPixel.B - ((errorThisRowB[col] * weight) / 8),
                                            srcPixel.G - ((errorThisRowG[col] * weight) / 8),
                                            srcPixel.R - ((errorThisRowR[col] * weight) / 8), srcPixel.A);
                 byte pixelValue = this.QuantizePixel(&target);
                 *pDestinationPixel = pixelValue;
                 ColorBgra actual = ColorBgra.FromColor(pallete[pixelValue]);
                 int errorR = actual.R - target.R;
                 int errorG = actual.G - target.G;
                 int errorB = actual.B - target.B;
                 // Floyd-Steinberg Error Diffusion:
                 // a) 7/16 error goes to x+1
                 // b) 5/16 error goes to y+1
                 // c) 3/16 error goes to x-1,y+1
                 // d) 1/16 error goes to x+1,y+1
                 const int a = 7;
                 const int b = 5;
                 const int c = 3;
                 int errorRa = (errorR * a) / 16;
                 int errorRb = (errorR * b) / 16;
                 int errorRc = (errorR * c) / 16;
                 int errorRd = errorR - errorRa - errorRb - errorRc;
                 int errorGa = (errorG * a) / 16;
                 int errorGb = (errorG * b) / 16;
                 int errorGc = (errorG * c) / 16;
                 int errorGd = errorG - errorGa - errorGb - errorGc;
                 int errorBa = (errorB * a) / 16;
                 int errorBb = (errorB * b) / 16;
                 int errorBc = (errorB * c) / 16;
                 int errorBd = errorB - errorBa - errorBb - errorBc;
                 errorThisRowR[col + 1] += errorRa;
                 errorThisRowG[col + 1] += errorGa;
                 errorThisRowB[col + 1] += errorBa;
                 errorNextRowR[width - col] += errorRb;
                 errorNextRowG[width - col] += errorGb;
                 errorNextRowB[width - col] += errorBb;
                 if (col != 0) {
                     errorNextRowR[width - (col - 1)] += errorRc;
                     errorNextRowG[width - (col - 1)] += errorGc;
                     errorNextRowB[width - (col - 1)] += errorBc;
                 }
                 errorNextRowR[width - (col + 1)] += errorRd;
                 errorNextRowG[width - (col + 1)] += errorGd;
                 errorNextRowB[width - (col + 1)] += errorBd;
                 unchecked {
                     pSourcePixel += ptrInc;
                     pDestinationPixel += ptrInc;
                 }
             }
             // Add the stride to the source row
             pSourceRow += sourceData.Stride;
             // And to the destination row
             pDestinationRow += outputData.Stride;
             errorThisRowB = errorNextRowB;
             errorThisRowG = errorNextRowG;
             errorThisRowR = errorNextRowR;
         }
     } finally {
         // Ensure that I unlock the output bits
         output.UnlockBits(outputData);
     }
 }
Example #14
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(ColorBgra* pixel);
Example #15
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(ColorBgra* pixel)
 {
 }