示例#1
0
        static void MakeSpriteFont(FontDescription options, Stream stream)
        {
            float lineSpacing;

            Glyph[] glyphs = ImportFont(options, out lineSpacing);

            // Optimize.
            foreach (Glyph glyph in glyphs)
            {
                GlyphCropper.Crop(glyph);
            }

            Bitmap bitmap = GlyphPacker.ArrangeGlyphs(glyphs);

            // Adjust line and character spacing.
            lineSpacing += options.LineSpacing;

            foreach (Glyph glyph in glyphs)
            {
                glyph.XAdvance += options.Spacing;
            }

            // Automatically detect whether this is a monochromatic or color font?
            if (options.Format == FontTextureFormat.Auto)
            {
                bool isMono = BitmapUtils.IsRgbEntirely(Color.White, bitmap);

                options.Format = isMono ? FontTextureFormat.CompressedMono :
                                 FontTextureFormat.Rgba32;
            }

            // Convert to premultiplied alpha format.
            if (!options.NoPremultiply)
            {
                BitmapUtils.PremultiplyAlpha(bitmap);
            }

            SpriteFontWriter.WriteSpriteFont(options, stream, glyphs, lineSpacing, bitmap);
        }
示例#2
0
        static void WriteBitmap(BinaryWriter writer, FontDescription options, Bitmap bitmap)
        {
            writer.Write(bitmap.Width);
            writer.Write(bitmap.Height);

            switch (options.Format)
            {
            case FontTextureFormat.Rgba32:
                WriteRgba32(writer, bitmap);
                break;

            case FontTextureFormat.Bgra4444:
                WriteBgra4444(writer, bitmap);
                break;

            case FontTextureFormat.CompressedMono:
                WriteCompressedMono(writer, bitmap, options);
                break;

            default:
                throw new NotSupportedException();
            }
        }
示例#3
0
        // Writes a block compressed monochromatic font texture.
        static void WriteCompressedMono(BinaryWriter writer, Bitmap bitmap, FontDescription options)
        {
            if ((bitmap.Width & 3) != 0 ||
                (bitmap.Height & 3) != 0)
            {
                throw new ArgumentException("Block compression requires texture size to be a multiple of 4.");
            }

            writer.Write(DXGI_FORMAT_BC2_UNORM);

            writer.Write(bitmap.Width * 4);
            writer.Write(bitmap.Height / 4);

            using (var bitmapData = new BitmapUtils.PixelAccessor(bitmap, ImageLockMode.ReadOnly))
            {
                for (int y = 0; y < bitmap.Height; y += 4)
                {
                    for (int x = 0; x < bitmap.Width; x += 4)
                    {
                        CompressBlock(writer, bitmapData, x, y, options);
                    }
                }
            }
        }
示例#4
0
        public void Import(FontDescription options)
        {
            // Load the source bitmap.
            Bitmap bitmap;

            try
            {
                bitmap = new Bitmap(options.FontName);
            }
            catch
            {
                throw new FontException(string.Format("Unable to load '{0}'.", options.FontName));
            }

            // Convert to our desired pixel format.
            bitmap = BitmapUtils.ChangePixelFormat(bitmap, PixelFormat.Format32bppArgb);

            // What characters are included in this font?
            var characters = Utilities.ToArray(CharacterRegion.Flatten(options.CharacterRegions));
            int characterIndex = 0;
            char currentCharacter = '\0';

            // Split the source image into a list of individual glyphs.
            var glyphList = new List<Glyph>();

            Glyphs = glyphList;
            LineSpacing = 0;

            foreach (Rectangle rectangle in FindGlyphs(bitmap))
            {
                if (characterIndex < characters.Length)
                    currentCharacter = characters[characterIndex++];
                else
                    currentCharacter++;

                glyphList.Add(new Glyph(currentCharacter, bitmap, rectangle));

                LineSpacing = Math.Max(LineSpacing, rectangle.Height);
            }

            // If the bitmap doesn't already have an alpha channel, create one now.
            if (BitmapUtils.IsAlphaEntirely(255, bitmap))
            {
                BitmapUtils.ConvertGreyToAlpha(bitmap);
            }
        }
示例#5
0
 public static void WriteSpriteFont(FontDescription options, string outputFilename, Glyph[] glyphs, float lineSpacing, Bitmap bitmap)
 {
     using (var stream = new NativeFileStream(outputFilename, NativeFileMode.Create, NativeFileAccess.Write))
     {
         WriteSpriteFont(options, stream, glyphs, lineSpacing, bitmap);
     }
 }
示例#6
0
        // We want to compress our font textures, because, like, smaller is better, 
        // right? But a standard DXT compressor doesn't do a great job with fonts that 
        // are in premultiplied alpha format. Our font data is greyscale, so all of the 
        // RGBA channels have the same value. If one channel is compressed differently 
        // to another, this causes an ugly variation in brightness of the rendered text. 
        // Also, fonts are mostly either black or white, with grey values only used for 
        // antialiasing along their edges. It is very important that the black and white 
        // areas be accurately represented, while the precise value of grey is less 
        // important.
        //
        // Trouble is, your average DXT compressor knows nothing about these 
        // requirements. It will optimize to minimize a generic error metric such as 
        // RMS, but this will often sacrifice crisp black and white in exchange for 
        // needless accuracy of the antialiasing pixels, or encode RGB differently to 
        // alpha. UGLY!
        //
        // Fortunately, encoding monochrome fonts turns out to be trivial. Using DXT3, 
        // we can fix the end colors as black and white, which gives guaranteed exact 
        // encoding of the font inside and outside, plus two fractional values for edge 
        // antialiasing. Also, these RGB values (0, 1/3, 2/3, 1) map exactly to four of 
        // the possible 16 alpha values available in DXT3, so we can ensure the RGB and 
        // alpha channels always exactly match.

        static void CompressBlock(BinaryWriter writer, BitmapUtils.PixelAccessor bitmapData, int blockX, int blockY, FontDescription options)
        {
            long alphaBits = 0;
            int rgbBits = 0;

            int pixelCount = 0;

            for (int y = 0; y < 4; y++)
            {
                for (int x = 0; x < 4; x++)
                {
                    long alpha;
                    int rgb;

                    int value = bitmapData[blockX + x, blockY + y].A;

                    if (options.NoPremultiply)
                    {
                        // If we are not premultiplied, RGB is always white and we have 4 bit alpha.
                        alpha = value >> 4;
                        rgb = 0;
                    }
                    else
                    {
                        // For premultiplied encoding, quantize the source value to 2 bit precision.
                        if (value < 256 / 6)
                        {
                            alpha = 0;
                            rgb = 1;
                        }
                        else if (value < 256 / 2)
                        {
                            alpha = 5;
                            rgb = 3;
                        }
                        else if (value < 256 * 5 / 6)
                        {
                            alpha = 10;
                            rgb = 2;
                        }
                        else
                        {
                            alpha = 15;
                            rgb = 0;
                        }
                    }

                    // Add this pixel to the alpha and RGB bit masks.
                    alphaBits |= alpha << (pixelCount * 4);
                    rgbBits |= rgb << (pixelCount * 2);

                    pixelCount++;
                }
            }

            // Output the alpha bit mask.
            writer.Write(alphaBits);

            // Output the two endpoint colors (black and white in 5.6.5 format).
            writer.Write((ushort)0xFFFF);
            writer.Write((ushort)0);

            // Output the RGB bit mask.
            writer.Write(rgbBits);
        }
示例#7
0
        // Writes a block compressed monochromatic font texture.
        static void WriteCompressedMono(BinaryWriter writer, Bitmap bitmap, FontDescription options)
        {
            if ((bitmap.Width & 3) != 0 ||
                (bitmap.Height & 3) != 0)
            {
                throw new ArgumentException("Block compression requires texture size to be a multiple of 4.");
            }

            writer.Write(DXGI_FORMAT_BC2_UNORM);

            writer.Write(bitmap.Width * 4);
            writer.Write(bitmap.Height / 4);

            using (var bitmapData = new BitmapUtils.PixelAccessor(bitmap, ImageLockMode.ReadOnly))
            {
                for (int y = 0; y < bitmap.Height; y += 4)
                {
                    for (int x = 0; x < bitmap.Width; x += 4)
                    {
                        CompressBlock(writer, bitmapData, x, y, options);
                    }
                }
            }
        }
示例#8
0
        static void WriteBitmap(BinaryWriter writer, FontDescription options, Bitmap bitmap)
        {
            writer.Write(bitmap.Width);
            writer.Write(bitmap.Height);

            switch (options.Format)
            {
                case FontTextureFormat.Rgba32:
                    WriteRgba32(writer, bitmap);
                    break;

                case FontTextureFormat.Bgra4444:
                    WriteBgra4444(writer, bitmap);
                    break;

                case FontTextureFormat.CompressedMono:
                    WriteCompressedMono(writer, bitmap, options);
                    break;

                default:
                    throw new NotSupportedException();
            }
        }
示例#9
0
        public void Import(FontDescription options)
        {
            //ImportBitmapFonts(options);
            //return;
            var factory = new DirectWrite.Factory();
            DirectWrite.Font font = null;

            using (var fontCollection = factory.GetSystemFontCollection(false))
            {
                int index;
                if(!fontCollection.FindFamilyName(options.FontName, out index))
                {
                    // Lets try to import System.Drawing for old system bitmap fonts (like MS Sans Serif)
                    throw new FontException(string.Format("Can't find font '{0}'.", options.FontName));
                }

                using(var fontFamily = fontCollection.GetFontFamily(index))
                {
                    var weight = FontWeight.Regular;
                    var style = DirectWrite.FontStyle.Normal;
                    switch(options.Style)
                    {
                        case FontStyle.Bold:
                            weight = FontWeight.Bold;
                            break;
                        case FontStyle.Italic:
                            weight = FontWeight.Regular;
                            style = DirectWrite.FontStyle.Italic;
                            break;
                        case FontStyle.Regular:
                            weight = FontWeight.Regular;
                            break;
                    }

                    font = fontFamily.GetFirstMatchingFont(weight, DirectWrite.FontStretch.Normal, style);
                }
            }

            var fontFace = new FontFace(font);
            var fontMetrics = fontFace.Metrics;

            // Create a bunch of GDI+ objects.
            var fontSize = PointsToPixels(options.Size);
            
            // Which characters do we want to include?
            var characters = CharacterRegion.Flatten(options.CharacterRegions);

            var glyphList = new List<Glyph>();

            // Store the font height.
            LineSpacing = (float)Math.Round((float)(fontMetrics.LineGap + fontMetrics.Ascent + fontMetrics.Descent) / fontMetrics.DesignUnitsPerEm * fontSize);

            var baseLine = (float)Math.Round((float)(fontMetrics.LineGap + fontMetrics.Ascent) / fontMetrics.DesignUnitsPerEm * fontSize);

            // If font size <= 13, use aliased fonts instead
            bool activateAntiAliasDetection = options.Size > 13;

            // Rasterize each character in turn.
            foreach (char character in characters)
            {
                var glyph = ImportGlyph(factory, fontFace, character, fontMetrics, fontSize, activateAntiAliasDetection);
                glyph.YOffset += baseLine;

                glyphList.Add(glyph);
            }

            Glyphs = glyphList;

        }
示例#10
0
        static void MakeSpriteFont(FontDescription options, Stream stream)
        {
            float lineSpacing;

            Glyph[] glyphs = ImportFont(options, out lineSpacing);

            // Optimize.
            foreach (Glyph glyph in glyphs)
            {
                // Output cleartype texture
                if (options.AntiAlias != FontAntiAliasMode.ClearType)
                {
                    BitmapUtils.ConvertGreyToAlpha(glyph.Bitmap);
                }
                GlyphCropper.Crop(glyph);
            }

            Bitmap bitmap = GlyphPacker.ArrangeGlyphs(glyphs);

            // Adjust line and character spacing.
            lineSpacing += options.LineSpacing;

            foreach (Glyph glyph in glyphs)
            {
                glyph.XAdvance += options.Spacing;
            }

            // Automatically detect whether this is a monochromatic or color font?
            if (options.Format == FontTextureFormat.Auto)
            {
                bool isMono = BitmapUtils.IsRgbEntirely(Color.White, bitmap);

                options.Format = isMono ? FontTextureFormat.CompressedMono :
                                                 FontTextureFormat.Rgba32;
            }

            // Convert to premultiplied alpha format.
            if (!options.NoPremultiply)
            {
                if (options.AntiAlias == FontAntiAliasMode.ClearType)
                {
                    BitmapUtils.PremultiplyAlphaClearType(bitmap);
                }
                else
                {
                    BitmapUtils.PremultiplyAlpha(bitmap);
                }
            }


            SpriteFontWriter.WriteSpriteFont(options, stream, glyphs, lineSpacing, bitmap);
        }
示例#11
0
 /// <summary>
 /// Compiles the specified font description into a <see cref="SpriteFontData" /> object.
 /// </summary>
 /// <param name="fontDescription">The font description.</param>
 /// <param name="stream">The stream to output the compiled SpriteFontData.</param>
 /// <returns>A SpriteFontData object.</returns>
 public static void Compile(FontDescription fontDescription, Stream stream)
 {
     MakeSpriteFont(fontDescription, stream);
 }
示例#12
0
        public void Import(FontDescription options)
        {
            // Create a bunch of GDI+ objects.
            using (Font font = CreateFont(options))
            using (Brush brush = new SolidBrush(Color.White))
            using (StringFormat stringFormat = new StringFormat(StringFormatFlags.NoFontFallback))
            using (Bitmap bitmap = new Bitmap(MaxGlyphSize, MaxGlyphSize, PixelFormat.Format32bppArgb))
            using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(bitmap))
            {
                graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
                graphics.InterpolationMode = InterpolationMode.HighQualityBicubic;
                graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;

                // Which characters do we want to include?
                var characters = CharacterRegion.Flatten(options.CharacterRegions);

                var glyphList = new List<Glyph>();

                // Rasterize each character in turn.
                foreach (char character in characters)
                {
                    Glyph glyph = ImportGlyph(character, font, brush, stringFormat, bitmap, graphics);

                    glyphList.Add(glyph);
                }

                Glyphs = glyphList;

                // Store the font height.
                LineSpacing = font.GetHeight();
            }
        }
示例#13
0
        // Attempts to instantiate the requested GDI+ font object.
        static Font CreateFont(FontDescription options)
        {
            Font font = new Font(options.FontName, PointsToPixels(options.Size), (System.Drawing.FontStyle)options.Style, GraphicsUnit.Pixel);

            try
            {
                // The font constructor automatically substitutes fonts if it can't find the one requested.
                // But we prefer the caller to know if anything is wrong with their data. A simple string compare
                // isn't sufficient because some fonts (eg. MS Mincho) change names depending on the locale.

                // Early out: in most cases the name will match the current or invariant culture.
                if (options.FontName.Equals(font.FontFamily.GetName(CultureInfo.CurrentCulture.LCID), StringComparison.OrdinalIgnoreCase) ||
                    options.FontName.Equals(font.FontFamily.GetName(CultureInfo.InvariantCulture.LCID), StringComparison.OrdinalIgnoreCase))
                {
                    return font;
                }

                // Check the font name in every culture.
                foreach (CultureInfo culture in CultureInfo.GetCultures(CultureTypes.SpecificCultures))
                {
                    if (options.FontName.Equals(font.FontFamily.GetName(culture.LCID), StringComparison.OrdinalIgnoreCase))
                    {
                        return font;
                    }
                }

                // A font substitution must have occurred.
                throw new Exception(string.Format("Can't find font '{0}'.", options.FontName));
            }
            catch
            {
                font.Dispose();
                throw;
            }
        }
示例#14
0
        public void Import(FontDescription options)
        {
            var factory = new DirectWrite.Factory();

            DirectWrite.Font font = null;

            using (var fontCollection = factory.GetSystemFontCollection(false))
            {
                int index;
                if (!fontCollection.FindFamilyName(options.FontName, out index))
                {
                    // Lets try to import System.Drawing for old system bitmap fonts (like MS Sans Serif)
                    throw new FontException(string.Format("Can't find font '{0}'.", options.FontName));
                }

                using (var fontFamily = fontCollection.GetFontFamily(index))
                {
                    var weight = FontWeight.Regular;
                    var style  = DirectWrite.FontStyle.Normal;
                    switch (options.Style)
                    {
                    case FontStyle.Bold:
                        weight = FontWeight.Bold;
                        break;

                    case FontStyle.Italic:
                        weight = FontWeight.Regular;
                        style  = DirectWrite.FontStyle.Italic;
                        break;

                    case FontStyle.Regular:
                        weight = FontWeight.Regular;
                        break;
                    }

                    font = fontFamily.GetFirstMatchingFont(weight, DirectWrite.FontStretch.Normal, style);
                }
            }

            var fontFace    = new FontFace(font);
            var fontMetrics = fontFace.Metrics;

            // Create a bunch of GDI+ objects.
            var fontSize = PointsToPixels(options.Size);

            // Which characters do we want to include?
            var characters = CharacterRegion.Flatten(options.CharacterRegions);

            var glyphList = new List <Glyph>();

            // Store the font height.
            LineSpacing = (float)(fontMetrics.LineGap + fontMetrics.Ascent + fontMetrics.Descent) / fontMetrics.DesignUnitsPerEm * fontSize;

            var baseLine = (float)(fontMetrics.LineGap + fontMetrics.Ascent) / fontMetrics.DesignUnitsPerEm * fontSize;

            // Rasterize each character in turn.
            foreach (char character in characters)
            {
                var glyph = ImportGlyph(factory, fontFace, character, fontMetrics, fontSize, options.AntiAlias);
                glyph.YOffset += baseLine;

                glyphList.Add(glyph);
            }

            Glyphs = glyphList;

            factory.Dispose();
        }
示例#15
0
 /// <summary>
 /// Compiles the specified font description into a <see cref="SpriteFontData" /> object.
 /// </summary>
 /// <param name="fontDescription">The font description.</param>
 /// <param name="stream">The stream to output the compiled SpriteFontData.</param>
 /// <returns>A SpriteFontData object.</returns>
 public static void Compile(FontDescription fontDescription, Stream stream)
 {
     MakeSpriteFont(fontDescription, stream);
 }
示例#16
0
        // We want to compress our font textures, because, like, smaller is better,
        // right? But a standard DXT compressor doesn't do a great job with fonts that
        // are in premultiplied alpha format. Our font data is greyscale, so all of the
        // RGBA channels have the same value. If one channel is compressed differently
        // to another, this causes an ugly variation in brightness of the rendered text.
        // Also, fonts are mostly either black or white, with grey values only used for
        // antialiasing along their edges. It is very important that the black and white
        // areas be accurately represented, while the precise value of grey is less
        // important.
        //
        // Trouble is, your average DXT compressor knows nothing about these
        // requirements. It will optimize to minimize a generic error metric such as
        // RMS, but this will often sacrifice crisp black and white in exchange for
        // needless accuracy of the antialiasing pixels, or encode RGB differently to
        // alpha. UGLY!
        //
        // Fortunately, encoding monochrome fonts turns out to be trivial. Using DXT3,
        // we can fix the end colors as black and white, which gives guaranteed exact
        // encoding of the font inside and outside, plus two fractional values for edge
        // antialiasing. Also, these RGB values (0, 1/3, 2/3, 1) map exactly to four of
        // the possible 16 alpha values available in DXT3, so we can ensure the RGB and
        // alpha channels always exactly match.

        static void CompressBlock(BinaryWriter writer, BitmapUtils.PixelAccessor bitmapData, int blockX, int blockY, FontDescription options)
        {
            long alphaBits = 0;
            int  rgbBits   = 0;

            int pixelCount = 0;

            for (int y = 0; y < 4; y++)
            {
                for (int x = 0; x < 4; x++)
                {
                    long alpha;
                    int  rgb;

                    int value = bitmapData[blockX + x, blockY + y].A;

                    if (options.NoPremultiply)
                    {
                        // If we are not premultiplied, RGB is always white and we have 4 bit alpha.
                        alpha = value >> 4;
                        rgb   = 0;
                    }
                    else
                    {
                        // For premultiplied encoding, quantize the source value to 2 bit precision.
                        if (value < 256 / 6)
                        {
                            alpha = 0;
                            rgb   = 1;
                        }
                        else if (value < 256 / 2)
                        {
                            alpha = 5;
                            rgb   = 3;
                        }
                        else if (value < 256 * 5 / 6)
                        {
                            alpha = 10;
                            rgb   = 2;
                        }
                        else
                        {
                            alpha = 15;
                            rgb   = 0;
                        }
                    }

                    // Add this pixel to the alpha and RGB bit masks.
                    alphaBits |= alpha << (pixelCount * 4);
                    rgbBits   |= rgb << (pixelCount * 2);

                    pixelCount++;
                }
            }

            // Output the alpha bit mask.
            writer.Write(alphaBits);

            // Output the two endpoint colors (black and white in 5.6.5 format).
            writer.Write((ushort)0xFFFF);
            writer.Write((ushort)0);

            // Output the RGB bit mask.
            writer.Write(rgbBits);
        }
示例#17
0
 /// <summary>
 /// Compiles the specified font description into a <see cref="SpriteFontData"/> object.
 /// </summary>
 /// <param name="fontDescription">The font description.</param>
 /// <returns>A SpriteFontData object.</returns>
 public static SpriteFontData Compile(FontDescription fontDescription)
 {
     // We are using a MemoryStream, this is not efficient
     // but this was a quickest way to use existing from MakeSpriteFont from DirectXTk
     var stream = new MemoryStream();
     MakeSpriteFont(fontDescription, stream);
     stream.Position = 0;
     return SpriteFontData.Load(stream);
 }
示例#18
0
        public static void WriteSpriteFont(FontDescription options, Stream outputStream, Glyph[] glyphs, float lineSpacing, Bitmap bitmap)
        {
            var writer = new BinaryWriter(outputStream);
            WriteMagic(writer);
            writer.Write(SpriteFontData.Version);
            WriteGlyphs(writer, glyphs);

            writer.Write(lineSpacing);
            writer.Write(options.DefaultCharacter);

            WriteBitmap(writer, options, bitmap);
            writer.Flush();
        }
示例#19
0
        static Glyph[] ImportFont(FontDescription options, out float lineSpacing)
        {
            // Which importer knows how to read this source font?
            IFontImporter importer;

            string fileExtension = Path.GetExtension(options.FontName).ToLowerInvariant();

            var BitmapFileExtensions = new List<string> { ".bmp", ".png", ".gif" };

            if (BitmapFileExtensions.Contains(fileExtension))
            {
                importer = new BitmapImporter();
            }
            else
            {
                importer = new TrueTypeImporter();
            }

            // Import the source font data.
            importer.Import(options);

            lineSpacing = importer.LineSpacing;

            // Get all glyphs
            var glyphs = new List<Glyph>(importer.Glyphs);

            // Validate.
            if (glyphs.Count == 0)
            {
                throw new FontException("Font does not contain any glyphs.");
            }

            // Sort the glyphs
            glyphs.Sort((left, right) => left.Character.CompareTo(right.Character));


            // Check that the default character is part of the glyphs
            if (options.DefaultCharacter != 0)
            {
                bool defaultCharacterFound = false;
                foreach (var glyph in glyphs)
                {
                    if (glyph.Character == options.DefaultCharacter)
                    {
                        defaultCharacterFound = true;
                        break;
                    }
                }
                if (!defaultCharacterFound)
                {
                    throw new FontException("The specified DefaultCharacter is not part of this font.");
                }
            }

            return glyphs.ToArray();
        }
示例#20
0
        public void Run(string[] args)
        {
            // Print the exe header
            PrintHeader();

            // Parse the command line
            if (!ParseCommandLine(args))
            {
                Environment.Exit(1);
            }

            // Loads the description
            var filePath = Path.Combine(Environment.CurrentDirectory, XmlFontFile);

            var fontDescription = FontDescription.Load(XmlFontFile);

            var defaultOutputFile = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath));

            // Compiles to SpriteData
            OutputFile = OutputFile ?? defaultOutputFile;

            string dependencyFile = null;

            if (CompileOnlyIfNewer)
            {
                dependencyFile = Path.Combine(OutputDependencyDirectory, FileDependencyList.GetDependencyFileNameFromSourcePath(Path.GetFileName(filePath)));
            }

            bool forceCompilation = (DebugOutputSpriteSheet != null && !File.Exists(DebugOutputSpriteSheet));

            var result = FontCompiler.CompileAndSave(filePath, OutputFile, dependencyFile);

            if (result.Logger.HasErrors)
            {
                ErrorColor();
                foreach (var logMessage in result.Logger.Messages)
                {
                    Console.WriteLine(logMessage);
                }
                ResetColor();
                Environment.Exit(1);
            }
            else if (result.IsContentGenerated || forceCompilation)
            {
                Console.WriteLine("Writing [{0}] ({1} format)", OutputFile, fontDescription.Format);

                // Save output files.
                if (!string.IsNullOrEmpty(DebugOutputSpriteSheet))
                {
                    Console.WriteLine("Saving debug output spritesheet {0}", DebugOutputSpriteSheet);

                    var spriteFontData = SpriteFontData.Load(OutputFile);

                    if (spriteFontData.Bitmaps.Length > 0 && spriteFontData.Bitmaps[0].Data is SpriteFontData.BitmapData)
                    {
                        var bitmapData = (SpriteFontData.BitmapData)spriteFontData.Bitmaps[0].Data;
                        using (var image = Image.New2D(bitmapData.Width, bitmapData.Height, 1, bitmapData.PixelFormat))
                        {
                            Utilities.Write(image.DataPointer, bitmapData.Data, 0, bitmapData.Data.Length);
                            image.Save(DebugOutputSpriteSheet, ImageFileType.Dds);
                        }
                    }
                }
            }
            else
            {
                Console.WriteLine("No need to write [{0}]. File is up-to-date from XML description", OutputFile);
            }
        }