Ejemplo n.º 1
0
        /// <summary>
        /// Color quantization overview:
        /// http://algolist.manual.ru/graphics/quant/qoverview.php
        /// MSDN Article on changing palettes on GIFs
        /// http://support.microsoft.com/default.aspx?scid=kb;en-us;Q319061
        /// Median Cut Algorithm (simple, but slow)
        /// http://rsb.info.nih.gov/ij/developer/source/ij/process/MedianCut.java.html (Java)
        /// NeuQuant Algorithm (best quality, but slow)
        /// http://www.acm.org/~dekker/NEUQUANT.HTML (Java)
        /// Octree Quantization (fast)
        /// http://www.microsoft.com/msj/archive/S3F1.aspx (Documentation)
        /// http://www.microsoft.com/downloads/details.aspx?FamilyID=cb9a0bc8-c96b-4c3e-9652-df609352fa89&DisplayLang=en (Download)
        /// 
        /// </summary>
        /// <param name="image"></param>
        /// <param name="fTransparent"></param>
        /// <returns></returns>
        public static Bitmap GenerateGIFWithNewColorTable(
            Bitmap image,
            bool fTransparent, ColorReduction colorReductionAlgorithm)
        {
            // Make a new 8-BPP indexed bitmap that is the same size as the source image.
            int nColors = 256;
            int Width = image.Width;
            int Height = image.Height;
            // Always use PixelFormat8bppIndexed because that is the color
            // table-based interface to the GIF codec.
            Bitmap bitmap = new Bitmap(Width, Height, PixelFormat.Format8bppIndexed);
            // Initialize a new color table with entries that are determined
            IDictionary colorOccurences = new Hashtable();
            IDictionary colorReducedOccurences = new Hashtable();
            IDictionaryEnumerator cenum;
            Color[] cpixels = new Color[Width * Height];
            int ix = 0;
            for (int y = 0; y < Height; y++)
            {
                for (int x = 0; x < Width; x++)
                {
                    Color c = image.GetPixel(x, y);
                    cpixels[ix++] = c;
                    if (colorOccurences.Contains(c))
                    {
                        int v = (int)colorOccurences[c];
                        colorOccurences[c] = v++;
                    }
                    else colorOccurences[c] = 1;
                }
            }
            IDictionary colorIndexMap = new Hashtable();
            ColorPalette pal;
            if (colorOccurences.Count <= 256)
            {
                nColors = colorOccurences.Count;
                pal = GetColorPalette(nColors);
                ix = 0;
                cenum = colorOccurences.GetEnumerator();
                while (cenum.MoveNext())
                {
                    Color c = (Color)cenum.Key;
                    //ix = colorOccurences
                    colorIndexMap.Add(c, ix);
                    pal.Entries[ix++] = c;
                }
            }
            else
            {
                // color reduction
                DateTime sDate = DateTime.Now;
                ColorQuantizer cq;
                switch (colorReductionAlgorithm)
                {
                    default: cq = new OctreeQuantizer(255, cpixels); break;
                    case ColorReduction.NEUQUANT: cq = new NeuQuant(30, cpixels); break;
                }
                cq.init();
                cenum = colorOccurences.GetEnumerator();
                while (cenum.MoveNext())
                {
                    colorIndexMap.Add(cenum.Key, cq.lookup((Color)(cenum.Key)));
                }
                nColors = cq.getColorCount();
                // GIF codec supports 256 colors maximum, monochrome minimum.
                // Create a color palette big enough to hold the colors you want.
                pal = GetColorPalette(nColors);
                for (int x = 0; x < nColors; x++) pal.Entries[x] = cq.getColor(x);
                //Common.logDebug("time for color reduction (" + colorOccurences.Count + " to 256) needed = " + ((TimeSpan)(DateTime.Now - sDate)).TotalMilliseconds + " ms");
            }
            // Set the palette into the new Bitmap object.
            bitmap.Palette = pal;
            // Lock a rectangular portion of the bitmap for writing.
            BitmapData bitmapData;
            Rectangle rect = new Rectangle(0, 0, Width, Height);
            bitmapData = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format8bppIndexed);
            // Write to the temporary buffer that is provided by LockBits.
            // Copy the pixels from the source image in this loop.
            // Because you want an index, convert RGB to the appropriate
            // palette index here.
            IntPtr pixels = bitmapData.Scan0;
            //unsafe
            //{
            //    // Get the pointer to the image bits. This is the unsafe operation.
            //    byte* pBits;
            //    if (bitmapData.Stride > 0) pBits = (byte*)pixels.ToPointer();
            //    else
            //        // If the Stide is negative, Scan0 points to the last
            //        // scanline in the buffer. To normalize the loop, obtain
            //        // a pointer to the front of the buffer that is located
            //        // (Height-1) scanlines previous.
            //        pBits = (byte*)pixels.ToPointer() + bitmapData.Stride * (Height - 1);
            //    uint stride = (uint)Math.Abs(bitmapData.Stride);

            //    for (uint row = 0; row < Height; ++row)
            //    {
            //        for (uint col = 0; col < Width; ++col)
            //        {
            //            // The destination pixel.
            //            // The pointer to the color index byte of the destination; this real pointer causes this
            //            // code to be considered unsafe.
            //            byte* p8bppPixel = pBits + row * stride + col;
            //            Color pixel = cpixels[Width * row + col];
            //            // determine index of that color
            //            object index = colorIndexMap[pixel];
            //            if (index != null)
            //            {
            //                *p8bppPixel = (byte)(int)index;
            //            }
            //            else
            //            {
            //                *p8bppPixel = 0;
            //            }
            //        } /* end loop for col */
            //    } /* end loop for row */
            //} /* end unsafe */
            // To commit the changes, unlock the portion of the bitmap.
            bitmap.UnlockBits(bitmapData);
            return bitmap;
        }
Ejemplo n.º 2
0
 public static void SaveGIFWithNewColorTable(
     Bitmap image,
     string filename,
     bool fTransparent,
     ColorReduction colorReductionAlgorithm
     )
 {
     Bitmap bitmap = GenerateGIFWithNewColorTable(image, fTransparent, colorReductionAlgorithm);
     bitmap.Save(filename, ImageFormat.Gif);
     // Bitmap goes out of scope here and is also marked for
     // garbage collection.
     // Pal is referenced by bitmap and goes away.
     bitmap.Dispose();
 }