Quantize using an Octree
Inheritance: Quantizer
Example #1
0
        void RenderLoop() {
            renderer = MakeRenderer();
            using( MemoryStream ms = new MemoryStream() ) {
                // loop terminates with the rest of the program (this is a background thread)
                while( true ) {
                    // wait (block) until a map is available for drawing
                    RenderTask task = inQueue.WaitDequeue();
                    try {
                        // render the map
                        IsoCatResult result = renderer.Draw( task.Map );
                        task.Map = null;

                        // crop image (if needed)
                        Image image;
                        if( p.Uncropped ) {
                            image = result.Bitmap;
                        } else {
                            image = result.Bitmap.Clone( result.CropRectangle, result.Bitmap.PixelFormat );
                            result.Bitmap.Dispose();
                        }

                        // encode image
                        if( p.ExportFormat.Equals( ImageFormat.Jpeg ) ) {
                            EncoderParameters encoderParams = new EncoderParameters();
                            encoderParams.Param[0] = new EncoderParameter( Encoder.Quality, p.JpegQuality );
                            image.Save( ms, p.ImageEncoder, encoderParams );
                        } else if( p.ExportFormat.Equals( ImageFormat.Gif ) ) {
                            OctreeQuantizer q = new OctreeQuantizer( 255, 8 );
                            image = q.Quantize( image );
                            image.Save( ms, p.ExportFormat );
                        } else {
                            image.Save( ms, p.ExportFormat );
                        }
                        image.Dispose();

                        // store result as a byte[]
                        task.Result = ms.ToArray();

                    } catch( Exception ex ) {
                        task.Exception = ex;
                    }

                    // send stack to the results queue
                    outQueue.Enqueue( task );
                    ms.SetLength( 0 );
                }
            }
        }
        public static bool ResizeImage( string sourceFile, string targetFile, int maxWidth, int maxHeight, bool preserverAspectRatio, int quality )
        {
            System.Drawing.Image sourceImage;

            System.IO.Stream sourceStream = Achilles.Acme.Storage.IO.File.OpenRead( sourceFile );
            System.IO.Stream targetStream = Achilles.Acme.Storage.IO.File.OpenWrite( targetFile );

            try
            {
                sourceImage = System.Drawing.Image.FromStream( sourceStream );
            }
            catch ( OutOfMemoryException )
            {
                // This is not a valid image. Do nothing.
                return false;
            }

            // If 0 is passed in any of the max sizes it means that that size must be ignored,
            // so the original image size is used.
            maxWidth = maxWidth == 0 ? sourceImage.Width : maxWidth;
            maxHeight = maxHeight == 0 ? sourceImage.Height : maxHeight;

            if ( sourceImage.Width <= maxWidth && sourceImage.Height <= maxHeight )
            {
                sourceImage.Dispose();

                if ( sourceFile != targetFile )
                    Achilles.Acme.Storage.IO.File.Copy( sourceFile, targetFile );

                return true;
            }

            Size oSize;
            if ( preserverAspectRatio )
            {
                // Gets the best size for aspect ratio resampling
                oSize = GetAspectRatioSize( maxWidth, maxHeight, sourceImage.Width, sourceImage.Height );
            }
            else
                oSize = new Size( maxWidth, maxHeight );

            System.Drawing.Image oResampled;

            if (sourceImage.PixelFormat == PixelFormat.Indexed || sourceImage.PixelFormat == PixelFormat.Format1bppIndexed || sourceImage.PixelFormat == PixelFormat.Format4bppIndexed || sourceImage.PixelFormat == PixelFormat.Format8bppIndexed || sourceImage.PixelFormat.ToString() == "8207")
                oResampled = new Bitmap( oSize.Width, oSize.Height, PixelFormat.Format24bppRgb );
            else
                oResampled = new Bitmap( oSize.Width, oSize.Height, sourceImage.PixelFormat );

            // Creates a Graphics for the oResampled image
            Graphics oGraphics = Graphics.FromImage( oResampled );

            // The Rectangle that holds the Resampled image size
            Rectangle oRectangle;

            // High quality resizing
            if ( quality > 80 )
            {
                oGraphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;

                // If HighQualityBicubic is used, bigger Rectangle is required to remove the white border
                oRectangle = new Rectangle( -1, -1, oSize.Width +1, oSize.Height +1 );
            }
            else
                oRectangle = new Rectangle( 0, 0, oSize.Width, oSize.Height );

            // Place a white background (for transparent images).
            oGraphics.FillRectangle( new SolidBrush( Color.White ), oRectangle );

            // Draws over the oResampled image the resampled Image
            oGraphics.DrawImage( sourceImage, oRectangle );

            sourceImage.Dispose();

            String extension = System.IO.Path.GetExtension( targetFile ).ToLower();

            if ( extension == ".jpg" || extension == ".jpeg" )
            {
                ImageCodecInfo oCodec = GetJpgCodec();

                if ( oCodec != null )
                {
                    EncoderParameters aCodecParams = new EncoderParameters( 1 );
                    aCodecParams.Param[0] = new EncoderParameter( Encoder.Quality, quality );
                    oResampled.Save( targetStream, oCodec, aCodecParams );
                }
                else
                {
                    // TJT: Fixme - stream save requires oCodec?
                    //oResampled.Save( targetStream, oCodec );
                }
            }
            else
            {
                switch ( extension )
                {
                    case ".gif":
                        try
                        {
                            // Use a proper palette
                            OctreeQuantizer quantizer = new OctreeQuantizer( 255, 8 );
                            using ( Bitmap quantized = quantizer.Quantize( oResampled ) )
                            {
                                quantized.Save( targetStream, System.Drawing.Imaging.ImageFormat.Gif );
                            }
                        }
                        catch ( System.Security.SecurityException )
                        {
                            // The calls to Marshal might fail in Medium trust, save the image using the default palette
                            oResampled.Save( targetStream, System.Drawing.Imaging.ImageFormat.Png );
                        }
                        break;

                    case ".png":
                        oResampled.Save( targetStream, System.Drawing.Imaging.ImageFormat.Png );
                        break;

                    case ".bmp":
                        oResampled.Save( targetStream, System.Drawing.Imaging.ImageFormat.Bmp );
                        break;
                }
            }
            oGraphics.Dispose();
            oResampled.Dispose();

            targetStream.Close();
            sourceStream.Close();

            return true;
        }
        /// <summary>
        /// Turn a list of images into a packed sprite image and matching css file
        /// </summary>
        /// <param name="pOptions"></param>
        /// <param name="oResults">result messages</param>
        /// <returns>true/false for success</returns>
        public static bool CombineImages(PackOptions pOptions, out List<string> oResults)
        {
            oResults = new List<string>();

            long inputFileTotalSize = 0;
            long outputFileSize = 0;
            long areaTotalImages = 0;
            long areaTotalFrames = 0;
            long areaImage = 0;
            long areaFrame = 0;
            int widestImage = 0;
            int tallestImage = 0;

            var cssBuffer = new StringBuilder();
            var testPage = new StringBuilder();

            if (!string.IsNullOrWhiteSpace(pOptions.CssHeaderText))
                cssBuffer.AppendLine(pOptions.CssHeaderText);

            pOptions.ImageFilePaths.Sort(); //doing this as a debugging test

            var frameArray = new List<ImageFrame>();
            foreach (string imageFilePath in pOptions.ImageFilePaths)
            {
                if (File.Exists(imageFilePath))
                {
                    inputFileTotalSize += new FileInfo(imageFilePath).Length;

                    using (var image = Image.FromFile(imageFilePath))
                    {
                        var frame = new ImageFrame(imageFilePath, image.Width, image.Height);
                        frameArray.Add(frame);
                        #region Debugging and Error Checking
                        if (image.Width > widestImage)
                            widestImage = image.Width;
                        if (image.Height > tallestImage)
                            tallestImage = image.Height;

                        areaImage = (image.Width * image.Height);
                        areaFrame = (frame.Width * frame.Height);

                        areaTotalImages += areaImage;
                        areaTotalFrames += areaFrame;

                        if (image.Width != frame.Width
                            || image.Height != frame.Height
                            || areaImage != frame.Area)
                        {
                            oResults.Add(string.Format("Error: Image>Frame conversion size is incorrect: {0} ", imageFilePath));
                            oResults.Add(string.Format("H/W/A: Image:{0}/{1}/{2}, Frame:{3}/{4}/{5}"
                                , image.Height, image.Width, areaImage
                                , frame.Height, frame.Width, frame.Area
                                ));
                        }
                        #endregion
                    }
                }
                else
                {
                    oResults.Add(string.Format("Could not find file: {0} ", imageFilePath));
                    if (pOptions.TreatInputFileNotFoundAsError)
                        return false;
                }
            }

            long minArea = tallestImage * widestImage;
            if (areaTotalFrames > minArea)
                minArea = areaTotalFrames;
            var pref = ImageLayoutHelper.PackPreference.ByWidth;
            if (widestImage > tallestImage)
                pref = ImageLayoutHelper.PackPreference.ByHeight;

            int optimalTotalWidth = (int)Math.Ceiling(Math.Sqrt(minArea));
            if (optimalTotalWidth < widestImage)
                optimalTotalWidth = widestImage;
            optimalTotalWidth = GetNextPowerOf2(optimalTotalWidth, false);

            oResults.Add(string.Format("Tallest Image: {0:0,0} ", tallestImage));
            oResults.Add(string.Format("Widest Image: {0:0,0} ", widestImage));
            oResults.Add(string.Format("Total Image Area: {0:0,0} ", areaTotalFrames));
            oResults.Add(string.Format("Smallest Possible Area: {0:0,0} ", minArea));
            oResults.Add(string.Format("Optimal Width: {0:0,0} ", optimalTotalWidth));
            oResults.Add(string.Format("PackPreference: {0} ", pref));

            ImageFrame layoutFrame = ImageLayoutHelper.LayImageFrames(frameArray, optimalTotalWidth);
            List<ImageFrame> framesList = layoutFrame.GetFlatList();

            using (var bitmap = new Bitmap(layoutFrame.Width, layoutFrame.Height))
            {
                using (var g = Graphics.FromImage(bitmap))
                {
                    g.InterpolationMode = InterpolationMode.HighQualityBicubic; //http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.interpolationmode.aspx
                    g.SmoothingMode = SmoothingMode.None;                       //http://msdn.microsoft.com/en-us/library/z714w2y9.aspx
                    g.PixelOffsetMode = PixelOffsetMode.None;                   //http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.pixeloffsetmode.aspx
                    g.CompositingQuality = CompositingQuality.HighQuality;      //http://msdn.microsoft.com/en-us/library/system.drawing.drawing2d.compositingquality.aspx

                    if (pOptions.GenerateTestHtmlPage)
                        testPage.AppendFormat("<h1>{0} Images</h1>", framesList.Count);
                    var testPageAltClass = "a";

                    foreach (var f in framesList)
                    {
                        using (var image = Image.FromFile(f.Id))
                        {
                            g.DrawImage(image, f.OffsetX, f.OffsetY, f.Width, f.Height);
                            pOptions.CssPlaceholderValues[pOptions.CSS_PLACEHOLDER_CSS_CLASS_NAME_BASE] = GetOutputCssClassNameFromFilename(
                                    Path.GetFileName(f.Id).Substring(0, Path.GetFileName(f.Id).LastIndexOf("."))
                                    , pOptions.CssClassNameInvalidCharReplacement
                                    , pOptions.ValidSpriteClassNameCharacters);
                            pOptions.CssPlaceholderValues[pOptions.CSS_PLACEHOLDER_WIDTH] = f.Width.ToString();
                            pOptions.CssPlaceholderValues[pOptions.CSS_PLACEHOLDER_HEIGHT] = f.Height.ToString();
                            pOptions.CssPlaceholderValues[pOptions.CSS_PLACEHOLDER_IMAGE_FILE_NAME] = Path.GetFileName(pOptions.ImageOutputFilePath);
                            pOptions.CssPlaceholderValues[pOptions.CSS_PLACEHOLDER_OFFSET_X] = (-f.OffsetX).ToString();
                            pOptions.CssPlaceholderValues[pOptions.CSS_PLACEHOLDER_OFFSET_Y] = (-f.OffsetY).ToString();

                            var currentStyle = new StringBuilder(pOptions.CssFormatStringForSpriteDefinition);
                            foreach (KeyValuePair<string, string> entry in pOptions.CssPlaceholderValues)
                                currentStyle.Replace(entry.Key, entry.Value);

                            cssBuffer.AppendLine(currentStyle.ToString());
                            if (pOptions.GenerateTestHtmlPage)
                            {
                                testPageAltClass = testPageAltClass == "a" ? "" : "a";
                                testPage.AppendFormat("<tr class='{4}'><td>{0}{1}</td><td>{2}</td><td>{3}</td><td><img src='{5}' class='{0}{1}' title='{0}{1}' /></td></tr>"
                                    , pOptions.CssPlaceholderValues[pOptions.CSS_PLACEHOLDER_CSS_CLASS_NAME_PREFIX] + pOptions.CssPlaceholderValues[pOptions.CSS_PLACEHOLDER_CSS_CLASS_NAME_BASE]
                                    , pOptions.CssPlaceholderValues[pOptions.CSS_PLACEHOLDER_CSS_CLASS_NAME_SUFFIX]
                                    , f.Height
                                    , f.Width
                                    , testPageAltClass
                                    , pOptions.TestHtmlClearGifUrl
                                );
                            }
                        }
                    }
                }

                string imageOutputFilePath = pOptions.ImageOutputFilePath;

                File.WriteAllText(pOptions.CssOutputFilePath, cssBuffer.ToString());
                if (pOptions.GenerateTestHtmlPage)
                {
                    var css = cssBuffer.ToString();
                    if (!string.IsNullOrWhiteSpace(pOptions.TestHtmlImageDeployUrlBase))
                        css = css.Replace(pOptions.CssPlaceholderValues[pOptions.CSS_PLACEHOLDER_IMAGE_DEPLOY_URL_BASE], pOptions.TestHtmlImageDeployUrlBase);

                    var htmlForRows = testPage.ToString();
                    var html = string.Format("<html><head><title>CSS Sprite Preview for: {0}</title>{3}<style>body {1}background-color:#eeeeff;{2} img {1}margin:1px;border:solid 1px red;{2} TH {1}background-color:#bbbbbb;{2} TD {1}background-color:#cccccc;{2} TR.a TD {1}background-color:#DDDDDD;{2}  .clickable {1}cursor:pointer;cursor:hand;{2} {4}</style></head><body><table class='sortable' cellspacing=0 cellpadding=5><tr><td class=clickable>Css Class</td><td class=clickable>Height</td><td class=clickable>Width</td><td>Preview</td></tr>",
                        Path.GetFileName(pOptions.CssOutputFilePath), "{", "}", pOptions.TestHtmlHeadIncludes, css)
                        + htmlForRows
                        + "</table></body></html>";

                    var testFilePath = pOptions.CssOutputFilePath + pOptions.TestHtmlFilenameSuffix;
                    if (!string.IsNullOrWhiteSpace(pOptions.TestHtmlPath))
                        testFilePath = Path.Combine(pOptions.TestHtmlPath, Path.GetFileName(pOptions.CssOutputFilePath) + pOptions.TestHtmlFilenameSuffix);

                    File.WriteAllText(testFilePath, html);
                    oResults.Add("Successfully generated the CSS test html file to: " + testFilePath);
                }

                oResults.Add("Successfully generated the corresponding CSS file to: " + pOptions.CssOutputFilePath);

                //option for reducing the output bit depth using OctreeQuantizer
                if (pOptions.LimitOutputImageTo8Bits)
                {
                    var octreeQuantizer = new OctreeQuantizer(OctreeQuantizer.BitDepth.Bits8);
                    using (var quantizedBitmap = octreeQuantizer.Quantize(bitmap))
                    {
                        quantizedBitmap.Save(imageOutputFilePath, pOptions.OutputImageFormat);
                        oResults.Add(string.Format("Limited {0} to {1} bits using OctreeQuantizer", imageOutputFilePath, 8));
                    }
                }
                else
                {
                    bitmap.Save(imageOutputFilePath, pOptions.OutputImageFormat);
                }

                //if (pOptions.MakeTransparentUsingColorKey)
                //{
                //    Color c = bitmap.GetPixel(0, 0); or use passed in coordinate or color
                //    bitmap.MakeTransparent(c);
                //    var bitmapT = Transparency.MakeTransparent(bitmap, Color.Transparent);
                //}

                oResults.Add(string.Format("New sprite: {0} ", imageOutputFilePath));
                outputFileSize = new FileInfo(imageOutputFilePath).Length;
                oResults.Add(string.Format("Successfully converted {0} files of cumulative size {1} bytes into one file of size {2} bytes."
                    , pOptions.ImageFilePaths.Count, inputFileTotalSize, outputFileSize));

            }
            return true;
        }
Example #4
0
 static void SaveImage( Bitmap image, string targetFileName ) {
     if( exportFormat.Equals( ImageFormat.Jpeg ) ) {
         EncoderParameters encoderParams = new EncoderParameters();
         encoderParams.Param[0] = new EncoderParameter( Encoder.Quality, jpegQuality );
         image.Save( targetFileName, imageEncoder, encoderParams );
     } else if( exportFormat.Equals( ImageFormat.Gif ) ) {
         OctreeQuantizer q = new OctreeQuantizer( 255, 8 );
         image = q.Quantize( image );
         image.Save( targetFileName, exportFormat );
     } else {
         image.Save( targetFileName, exportFormat );
     }
 }
            void Save(Stream stream, ImageFormat format)
            {
                if (format == ImageFormat.Gif)
                {
                    if ((bitmap.PixelFormat & PixelFormat.Indexed) != PixelFormat.Indexed && bitmap.Palette.Entries.Length == 0 && !ImageHelper2.IsAnimated(bitmap))
                    {
                        OctreeQuantizer quantizer = new OctreeQuantizer(255, 8);
                        bitmap = quantizer.Quantize(bitmap);
                    }

                    /*
                    ColorPalette p = bitmap.Palette;
                    bool seenTransparent = false;
                    foreach (Color c in p.Entries)
                    {
                        if (c.A == 0)
                        {
                            Trace.Assert(!seenTransparent);
                            seenTransparent = true;
                        }
                    }
                    */
                }

                bitmap.Save(stream, format);
            }