Пример #1
0
        public void BlackAndWhiteReductionToOneColor()
        {
            Octree octree = new Octree();

            octree.AddColor(Color.FromArgb(0, 0, 0));
            octree.AddColor(Color.FromArgb(255, 255, 255));
            octree.ReduceColorsCount(1);
            Color reductedColor = octree.GetColor(Color.FromArgb(0, 0, 0));

            Assert.AreEqual(reductedColor, Color.FromArgb(127, 127, 127));
        }
Пример #2
0
        public void AddNewAndGetNewColor()
        {
            Octree octree = new Octree();

            octree.AddColor(Color.FromArgb(0, 0, 0));
            Assert.AreEqual(octree.GetColor(Color.FromArgb(0, 0, 0)), Color.FromArgb(0, 0, 0));
        }
Пример #3
0
        private static unsafe System.Drawing.Image ReduceColors(Bitmap bitmap, int maxColors, int numBits, Color transparentColor)
        {
            byte *numPtr;

            if ((numBits < 3) || (numBits > 8))
            {
                throw new ArgumentOutOfRangeException("numBits");
            }
            if (maxColors < 0x10)
            {
                throw new ArgumentOutOfRangeException("maxColors");
            }
            int    width  = bitmap.Width;
            int    height = bitmap.Height;
            Octree octree = new Octree(maxColors, numBits, transparentColor);

            for (int i = 0; i < width; i++)
            {
                for (int k = 0; k < height; k++)
                {
                    octree.AddColor(bitmap.GetPixel(i, k));
                }
            }
            ColorIndexTable colorIndexTable = octree.GetColorIndexTable();
            Bitmap          bitmap2         = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
            ColorPalette    palette         = bitmap2.Palette;
            Rectangle       rect            = new Rectangle(0, 0, width, height);
            BitmapData      bitmapdata      = bitmap2.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
            IntPtr          ptr             = bitmapdata.Scan0;

            if (bitmapdata.Stride > 0)
            {
                numPtr = (byte *)ptr.ToPointer();
            }
            else
            {
                numPtr = (byte *)(ptr.ToPointer() + (bitmapdata.Stride * (height - 1)));
            }
            int num5 = Math.Abs(bitmapdata.Stride);

            for (int j = 0; j < height; j++)
            {
                for (int m = 0; m < width; m++)
                {
                    byte *numPtr2 = (numPtr + (j * num5)) + m;
                    Color pixel   = bitmap.GetPixel(m, j);
                    byte  num8    = (byte)colorIndexTable[pixel];
                    numPtr2[0] = num8;
                }
            }
            colorIndexTable.CopyToColorPalette(palette);
            bitmap2.Palette = palette;
            bitmap2.UnlockBits(bitmapdata);
            return(bitmap2);
        }
 private void NewOctree(Bitmap bitmap, int colorsLimit)
 {
     octree = new Octree();
     for (int i = 0; i < bitmap.Width; i++)
     {
         for (int j = 0; j < bitmap.Height; j++)
         {
             octree.AddColor(bitmap.GetPixel(i, j));
             if (ReduceColorsAlongCreatingOctree)
             {
                 octree.ReduceColorsCount(colorsLimit);
             }
         }
     }
 }
Пример #5
0
        /// <summary>
        /// Does the quantize.
        /// </summary>
        /// <param name="bitmapSource">The bitmap source.</param>
        /// <param name="pixelFormat">The pixel format.</param>
        /// <param name="useDither">if set to <c>true</c> [use dither].</param>
        /// <returns>The quantized image with the recalculated color palette.</returns>
        private static Bitmap DoQuantize(Bitmap bitmapSource, PixelFormat pixelFormat, bool useDither)
        {
            // We use these values a lot
            int width = bitmapSource.Width;
            int height = bitmapSource.Height;
            Rectangle sourceRect = Rectangle.FromLTRB(0, 0, width, height);

            Bitmap bitmapOptimized = null;

            try
            {
                // Create a bitmap with the same dimensions and the desired format
                bitmapOptimized = new Bitmap(width, height, pixelFormat);

                // Lock the bits of the source image for reading.
                // we will need to write if we do the dither.
                BitmapData bitmapDataSource = bitmapSource.LockBits(
                    sourceRect,
                    ImageLockMode.ReadWrite,
                    PixelFormat.Format32bppArgb);

                try
                {
                    // Perform the first pass, which generates the octree data
                    // Create an Octree
                    Octree octree = new Octree(pixelFormat);

                    // Stride might be negative, indicating inverted row order.
                    // Allocate a managed buffer for the pixel data, and copy it from the unmanaged pointer.
                    int strideSource = Math.Abs(bitmapDataSource.Stride);
                    byte[] sourceDataBuffer = new byte[strideSource * height];
                    Marshal.Copy(bitmapDataSource.Scan0, sourceDataBuffer, 0, sourceDataBuffer.Length);

                    // We could skip every other row and/or every other column when sampling the colors
                    // of the source image, rather than hitting every other pixel. It doesn't seem to
                    // degrade the resulting image too much. But it doesn't really help the performance
                    // too much because the majority of the time seems to be spent in other places.

                    // For every row
                    int rowStartSource = 0;
                    for (int ndxRow = 0; ndxRow < height; ndxRow += 1)
                    {
                        // For each column
                        for (int ndxCol = 0; ndxCol < width; ndxCol += 1)
                        {
                            // Add the color (4 bytes per pixel - ARGB)
                            Pixel pixel = GetSourcePixel(sourceDataBuffer, rowStartSource, ndxCol);
                            octree.AddColor(pixel);
                        }

                        rowStartSource += strideSource;
                    }

                    // Get the optimized colors
                    Color[] colors = octree.GetPaletteColors();

                    // Set the palette from the octree
                    ColorPalette palette = bitmapOptimized.Palette;
                    for (var ndx = 0; ndx < palette.Entries.Length; ++ndx)
                    {
                        // Use the colors we calculated
                        // for the rest, just set to transparent
                        palette.Entries[ndx] = (ndx < colors.Length)
                            ? colors[ndx]
                            : Color.Transparent;
                    }

                    bitmapOptimized.Palette = palette;

                    // Lock the bits of the optimized bitmap for writing.
                    // we will also need to read if we are doing 1bpp or 4bpp
                    BitmapData bitmapDataOutput = bitmapOptimized.LockBits(sourceRect, ImageLockMode.ReadWrite, pixelFormat);
                    try
                    {
                        // Create a managed array for the destination bytes given the desired color depth
                        // and marshal the unmanaged data to the managed array
                        int strideOutput = Math.Abs(bitmapDataOutput.Stride);
                        byte[] bitmapOutputBuffer = new byte[strideOutput * height];

                        // For each source pixel, compute the appropriate color index
                        rowStartSource = 0;
                        int rowStartOutput = 0;

                        for (int ndxRow = 0; ndxRow < height; ++ndxRow)
                        {
                            // For each column
                            for (int ndxCol = 0; ndxCol < width; ++ndxCol)
                            {
                                // Get the source color
                                Pixel pixel = GetSourcePixel(sourceDataBuffer, rowStartSource, ndxCol);

                                // Get the closest palette index
                                int paletteIndex = octree.GetPaletteIndex(pixel);

                                // If we want to dither and this isn't the transparent pixel
                                if (useDither && pixel.Alpha != 0)
                                {
                                    // Calculate the error
                                    Color paletteColor = colors[paletteIndex];
                                    int deltaRed = pixel.Red - paletteColor.R;
                                    int deltaGreen = pixel.Green - paletteColor.G;
                                    int deltaBlue = pixel.Blue - paletteColor.B;

                                    // Propagate the dither error.
                                    // we'll use a standard Floyd-Steinberg matrix (1/16):
                                    // | 0 0 0 |
                                    // | 0 x 7 |
                                    // | 3 5 1 |

                                    // Make sure we're not on the right-hand edge
                                    if (ndxCol + 1 < width)
                                    {
                                        DitherSourcePixel(sourceDataBuffer, rowStartSource, ndxCol + 1, deltaRed, deltaGreen, deltaBlue, 7);
                                    }

                                    // Make sure we're not already on the bottom row
                                    if (ndxRow + 1 < height)
                                    {
                                        int nextRow = rowStartSource + strideSource;

                                        // Make sure we're not on the left-hand column
                                        if (ndxCol > 0)
                                        {
                                            // Down one row, but back one pixel
                                            DitherSourcePixel(sourceDataBuffer, nextRow, ndxCol - 1, deltaRed, deltaGreen, deltaBlue, 3);
                                        }

                                        // pixel directly below us
                                        DitherSourcePixel(sourceDataBuffer, nextRow, ndxCol, deltaRed, deltaGreen, deltaBlue, 5);

                                        // Make sure we're not on the right-hand column
                                        if (ndxCol + 1 < width)
                                        {
                                            // Down one row, but right one pixel
                                            DitherSourcePixel(sourceDataBuffer, nextRow, ndxCol + 1, deltaRed, deltaGreen, deltaBlue, 1);
                                        }
                                    }
                                }

                                // Set the bitmap index based on the format
                                switch (pixelFormat)
                                {
                                    case PixelFormat.Format8bppIndexed:
                                        // Each byte is a palette index
                                        bitmapOutputBuffer[rowStartOutput + ndxCol] = (byte)paletteIndex;
                                        break;

                                    case PixelFormat.Format4bppIndexed:
                                        // Each byte contains two pixels
                                        bitmapOutputBuffer[rowStartOutput + (ndxCol >> 1)] |= ((ndxCol & 1) == 1)
                                            ? (byte)(paletteIndex & 0x0f) // lower nibble
                                            : (byte)(paletteIndex << 4);  // upper nibble
                                        break;

                                    case PixelFormat.Format1bppIndexed:
                                        // Each byte contains eight pixels
                                        if (paletteIndex != 0)
                                        {
                                            bitmapOutputBuffer[rowStartOutput + (ndxCol >> 3)] |= (byte)(0x80 >> (ndxCol & 0x07));
                                        }

                                        break;
                                }
                            }

                            rowStartSource += strideSource;
                            rowStartOutput += strideOutput;
                        }

                        // Now copy the calculated pixel bytes from the managed array to the unmanaged bitmap
                        Marshal.Copy(bitmapOutputBuffer, 0, bitmapDataOutput.Scan0, bitmapOutputBuffer.Length);
                    }
                    finally
                    {
                        bitmapOptimized.UnlockBits(bitmapDataOutput);
                        bitmapDataOutput = null;
                    }
                }
                finally
                {
                    bitmapSource.UnlockBits(bitmapDataSource);
                    bitmapDataSource = null;
                }
            }
            catch (Exception)
            {
                // If any exception is thrown, dispose of the bitmap object
                // we've been working on before we rethrow and bail
                if (bitmapOptimized != null)
                {
                    bitmapOptimized.Dispose();
                }

                throw;
            }

            // Caller is responsible for disposing of this bitmap!
            return bitmapOptimized;
        }
Пример #6
0
        /// <summary>
        /// Does the quantize.
        /// </summary>
        /// <param name="bitmapSource">The bitmap source.</param>
        /// <param name="pixelFormat">The pixel format.</param>
        /// <param name="useDither">if set to <c>true</c> [use dither].</param>
        /// <returns>The quantized image with the recalculated color palette.</returns>
        private static Bitmap DoQuantize(Bitmap bitmapSource, PixelFormat pixelFormat, bool useDither)
        {
            // We use these values a lot
            int       width      = bitmapSource.Width;
            int       height     = bitmapSource.Height;
            Rectangle sourceRect = Rectangle.FromLTRB(0, 0, width, height);

            Bitmap bitmapOptimized = null;

            try
            {
                // Create a bitmap with the same dimensions and the desired format
                bitmapOptimized = new Bitmap(width, height, pixelFormat);

                // Lock the bits of the source image for reading.
                // we will need to write if we do the dither.
                BitmapData bitmapDataSource = bitmapSource.LockBits(
                    sourceRect,
                    ImageLockMode.ReadWrite,
                    PixelFormat.Format32bppArgb);

                try
                {
                    // Perform the first pass, which generates the octree data
                    // Create an Octree
                    Octree octree = new Octree(pixelFormat);

                    // Stride might be negative, indicating inverted row order.
                    // Allocate a managed buffer for the pixel data, and copy it from the unmanaged pointer.
                    int    strideSource     = Math.Abs(bitmapDataSource.Stride);
                    byte[] sourceDataBuffer = new byte[strideSource * height];
                    Marshal.Copy(bitmapDataSource.Scan0, sourceDataBuffer, 0, sourceDataBuffer.Length);

                    // We could skip every other row and/or every other column when sampling the colors
                    // of the source image, rather than hitting every other pixel. It doesn't seem to
                    // degrade the resulting image too much. But it doesn't really help the performance
                    // too much because the majority of the time seems to be spent in other places.

                    // For every row
                    int rowStartSource = 0;
                    for (int ndxRow = 0; ndxRow < height; ndxRow += 1)
                    {
                        // For each column
                        for (int ndxCol = 0; ndxCol < width; ndxCol += 1)
                        {
                            // Add the color (4 bytes per pixel - ARGB)
                            Pixel pixel = GetSourcePixel(sourceDataBuffer, rowStartSource, ndxCol);
                            octree.AddColor(pixel);
                        }

                        rowStartSource += strideSource;
                    }

                    // Get the optimized colors
                    Color[] colors = octree.GetPaletteColors();

                    // Set the palette from the octree
                    ColorPalette palette = bitmapOptimized.Palette;
                    for (var ndx = 0; ndx < palette.Entries.Length; ++ndx)
                    {
                        // Use the colors we calculated
                        // for the rest, just set to transparent
                        palette.Entries[ndx] = (ndx < colors.Length)
                            ? colors[ndx]
                            : Color.Transparent;
                    }

                    bitmapOptimized.Palette = palette;

                    // Lock the bits of the optimized bitmap for writing.
                    // we will also need to read if we are doing 1bpp or 4bpp
                    BitmapData bitmapDataOutput = bitmapOptimized.LockBits(sourceRect, ImageLockMode.ReadWrite, pixelFormat);
                    try
                    {
                        // Create a managed array for the destination bytes given the desired color depth
                        // and marshal the unmanaged data to the managed array
                        int    strideOutput       = Math.Abs(bitmapDataOutput.Stride);
                        byte[] bitmapOutputBuffer = new byte[strideOutput * height];

                        // For each source pixel, compute the appropriate color index
                        rowStartSource = 0;
                        int rowStartOutput = 0;

                        for (int ndxRow = 0; ndxRow < height; ++ndxRow)
                        {
                            // For each column
                            for (int ndxCol = 0; ndxCol < width; ++ndxCol)
                            {
                                // Get the source color
                                Pixel pixel = GetSourcePixel(sourceDataBuffer, rowStartSource, ndxCol);

                                // Get the closest palette index
                                int paletteIndex = octree.GetPaletteIndex(pixel);

                                // If we want to dither and this isn't the transparent pixel
                                if (useDither && pixel.Alpha != 0)
                                {
                                    // Calculate the error
                                    Color paletteColor = colors[paletteIndex];
                                    int   deltaRed     = pixel.Red - paletteColor.R;
                                    int   deltaGreen   = pixel.Green - paletteColor.G;
                                    int   deltaBlue    = pixel.Blue - paletteColor.B;

                                    // Propagate the dither error.
                                    // we'll use a standard Floyd-Steinberg matrix (1/16):
                                    // | 0 0 0 |
                                    // | 0 x 7 |
                                    // | 3 5 1 |

                                    // Make sure we're not on the right-hand edge
                                    if (ndxCol + 1 < width)
                                    {
                                        DitherSourcePixel(sourceDataBuffer, rowStartSource, ndxCol + 1, deltaRed, deltaGreen, deltaBlue, 7);
                                    }

                                    // Make sure we're not already on the bottom row
                                    if (ndxRow + 1 < height)
                                    {
                                        int nextRow = rowStartSource + strideSource;

                                        // Make sure we're not on the left-hand column
                                        if (ndxCol > 0)
                                        {
                                            // Down one row, but back one pixel
                                            DitherSourcePixel(sourceDataBuffer, nextRow, ndxCol - 1, deltaRed, deltaGreen, deltaBlue, 3);
                                        }

                                        // pixel directly below us
                                        DitherSourcePixel(sourceDataBuffer, nextRow, ndxCol, deltaRed, deltaGreen, deltaBlue, 5);

                                        // Make sure we're not on the right-hand column
                                        if (ndxCol + 1 < width)
                                        {
                                            // Down one row, but right one pixel
                                            DitherSourcePixel(sourceDataBuffer, nextRow, ndxCol + 1, deltaRed, deltaGreen, deltaBlue, 1);
                                        }
                                    }
                                }

                                // Set the bitmap index based on the format
                                switch (pixelFormat)
                                {
                                case PixelFormat.Format8bppIndexed:
                                    // Each byte is a palette index
                                    bitmapOutputBuffer[rowStartOutput + ndxCol] = (byte)paletteIndex;
                                    break;

                                case PixelFormat.Format4bppIndexed:
                                    // Each byte contains two pixels
                                    bitmapOutputBuffer[rowStartOutput + (ndxCol >> 1)] |= ((ndxCol & 1) == 1)
                                            ? (byte)(paletteIndex & 0x0f) // lower nibble
                                            : (byte)(paletteIndex << 4);  // upper nibble
                                    break;

                                case PixelFormat.Format1bppIndexed:
                                    // Each byte contains eight pixels
                                    if (paletteIndex != 0)
                                    {
                                        bitmapOutputBuffer[rowStartOutput + (ndxCol >> 3)] |= (byte)(0x80 >> (ndxCol & 0x07));
                                    }

                                    break;
                                }
                            }

                            rowStartSource += strideSource;
                            rowStartOutput += strideOutput;
                        }

                        // Now copy the calculated pixel bytes from the managed array to the unmanaged bitmap
                        Marshal.Copy(bitmapOutputBuffer, 0, bitmapDataOutput.Scan0, bitmapOutputBuffer.Length);
                    }
                    finally
                    {
                        bitmapOptimized.UnlockBits(bitmapDataOutput);
                        bitmapDataOutput = null;
                    }
                }
                finally
                {
                    bitmapSource.UnlockBits(bitmapDataSource);
                    bitmapDataSource = null;
                }
            }
            catch (Exception)
            {
                // If any exception is thrown, dispose of the bitmap object
                // we've been working on before we rethrow and bail
                if (bitmapOptimized != null)
                {
                    bitmapOptimized.Dispose();
                }

                throw;
            }

            // Caller is responsible for disposing of this bitmap!
            return(bitmapOptimized);
        }
Пример #7
0
        public void GetColorCounts()
        {
            Octree octree = new Octree();

            octree.AddColor(Color.FromArgb(0, 0, 0));
            octree.AddColor(Color.FromArgb(0, 0, 0));
            octree.AddColor(Color.FromArgb(0, 0, 1));
            octree.AddColor(Color.FromArgb(0, 1, 0));
            octree.AddColor(Color.FromArgb(1, 0, 0));
            octree.AddColor(Color.FromArgb(0, 0, 0));
            octree.AddColor(Color.FromArgb(0, 0, 2));
            octree.AddColor(Color.FromArgb(255, 255, 255));
            octree.AddColor(Color.FromArgb(255, 255, 255));
            octree.AddColor(Color.FromArgb(54, 23, 54));
            octree.AddColor(Color.FromArgb(21, 213, 144));
            octree.AddColor(Color.FromArgb(124, 43, 255));
            Assert.AreEqual(octree.ColorsCount, 9);
        }
 protected override void InitialQuantizePixel(Color32 pixel)
 {
     // Add the color to the octree
     _octree.AddColor(pixel);
 }
 private static unsafe System.Drawing.Image ReduceColors(Bitmap bitmap, int maxColors, int numBits, Color transparentColor)
 {
     byte* numPtr;
     if ((numBits < 3) || (numBits > 8))
     {
         throw new ArgumentOutOfRangeException("numBits");
     }
     if (maxColors < 0x10)
     {
         throw new ArgumentOutOfRangeException("maxColors");
     }
     int width = bitmap.Width;
     int height = bitmap.Height;
     Octree octree = new Octree(maxColors, numBits, transparentColor);
     for (int i = 0; i < width; i++)
     {
         for (int k = 0; k < height; k++)
         {
             octree.AddColor(bitmap.GetPixel(i, k));
         }
     }
     ColorIndexTable colorIndexTable = octree.GetColorIndexTable();
     Bitmap bitmap2 = new Bitmap(width, height, PixelFormat.Format8bppIndexed);
     ColorPalette palette = bitmap2.Palette;
     Rectangle rect = new Rectangle(0, 0, width, height);
     BitmapData bitmapdata = bitmap2.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
     IntPtr ptr = bitmapdata.Scan0;
     if (bitmapdata.Stride > 0)
     {
         numPtr = (byte*) ptr.ToPointer();
     }
     else
     {
         numPtr = (byte*) (ptr.ToPointer() + (bitmapdata.Stride * (height - 1)));
     }
     int num5 = Math.Abs(bitmapdata.Stride);
     for (int j = 0; j < height; j++)
     {
         for (int m = 0; m < width; m++)
         {
             byte* numPtr2 = (numPtr + (j * num5)) + m;
             Color pixel = bitmap.GetPixel(m, j);
             byte num8 = (byte) colorIndexTable[pixel];
             numPtr2[0] = num8;
         }
     }
     colorIndexTable.CopyToColorPalette(palette);
     bitmap2.Palette = palette;
     bitmap2.UnlockBits(bitmapdata);
     return bitmap2;
 }