Example #1
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 = 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 = Quantizer.ClampToByte(srcPixel.B - ((errorThisRowB[col] * weight) / 8));
                        target.G = Quantizer.ClampToByte(srcPixel.G - ((errorThisRowG[col] * weight) / 8));
                        target.R = Quantizer.ClampToByte(srcPixel.R - ((errorThisRowR[col] * weight) / 8));
                        target.A = srcPixel.A;

                        byte pixelValue = 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 #2
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)
 {
 }
Example #3
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 #4
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 #5
0
        /// <summary>
        /// Linearly interpolates between two color values.
        /// </summary>
        /// <param name="from">The color value that represents 0 on the lerp number line.</param>
        /// <param name="to">The color value that represents 1 on the lerp number line.</param>
        /// <param name="frac">A value in the range [0, 1].</param>
        public static ColorBgra Lerp(ColorBgra from, ColorBgra to, double frac)
        {
            ColorBgra ret = new ColorBgra();

                ret.B = (byte)Quantizer.ClampToByte(Quantizer.Lerp(from.B, to.B, frac));
                ret.G = (byte)Quantizer.ClampToByte(Quantizer.Lerp(from.G, to.G, frac));
                ret.R = (byte)Quantizer.ClampToByte(Quantizer.Lerp(from.R, to.R, frac));
                ret.A = (byte)Quantizer.ClampToByte(Quantizer.Lerp(from.A, to.A, frac));

                return ret;
        }
Example #6
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 #7
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;
        }
                /// <summary>
                /// Return the palette index for the passed color
                /// </summary>
                public int GetPaletteIndex(ColorBgra* pixel, int level)
                {
                    int paletteIndex = _paletteIndex;

                    if (!_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 != _children[index])
                        {
                            paletteIndex = _children[index].GetPaletteIndex(pixel, level + 1);
                        }
                        else
                        {
                            paletteIndex = -1;
                        }
                    }

                    return paletteIndex;
                }
 /// <summary>
 /// Increment the pixel count and add to the color information
 /// </summary>
 public void Increment(ColorBgra* pixel)
 {
     ++_pixelCount;
     _red += pixel->R;
     _green += pixel->G;
     _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 (_leaf)
                    {
                        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 = _children[index];

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

                        // Add the color to the child node
                        child.AddColor(pixel, colorBits, level + 1, octree);
                    }
                }
            /// <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 = _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;
            }
 /// <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 (_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 == _previousNode)
         {
             _previousColor = pixel->Bgra;
             _root.AddColor(pixel, _maxColorBits, 0, this);
         }
         else
         {
             // Just update the previous node
             _previousNode.Increment(pixel);
         }
     }
     else
     {
         _previousColor = pixel->Bgra;
         _root.AddColor(pixel, _maxColorBits, 0, this);
     }
 }
        /// <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)_maxColors;    // The color at [_maxColors] is set to transparent

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

            return paletteIndex;
        }
 /// <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)
 {
     _octree.AddColor(pixel);
 }