Ejemplo n.º 1
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, CommandLineOptions 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);
        }
Ejemplo n.º 2
0
        static void MakeSpriteFont(CommandLineOptions options)
        {
            // Import.
            Console.WriteLine("Importing {0}", options.SourceFont);

            float lineSpacing;

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

            Console.WriteLine("Captured {0} glyphs", glyphs.Length);

            // Optimize.
            Console.WriteLine("Cropping glyph borders");

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

            Console.WriteLine("Packing glyphs into sprite sheet");

            Bitmap bitmap;

            if (options.FastPack)
            {
                bitmap = GlyphPacker.ArrangeGlyphsFast(glyphs);
            }
            else
            {
                bitmap = GlyphPacker.ArrangeGlyphs(glyphs);
            }

            // Emit texture size warning based on known Feature Level limits.
            if (bitmap.Width > 16384 || bitmap.Height > 16384)
            {
                Console.WriteLine("WARNING: Resulting texture is too large for all known Feature Levels (9.1 - 12.1)");
            }
            else if (bitmap.Width > 8192 || bitmap.Height > 8192)
            {
                if (options.FeatureLevel < FeatureLevel.FL11_0)
                {
                    Console.WriteLine("WARNING: Resulting texture requires a Feature Level 11.0 or later device.");
                }
            }
            else if (bitmap.Width > 4096 || bitmap.Height > 4096)
            {
                if (options.FeatureLevel < FeatureLevel.FL10_0)
                {
                    Console.WriteLine("WARNING: Resulting texture requires a Feature Level 10.0 or later device.");
                }
            }
            else if (bitmap.Width > 2048 || bitmap.Height > 2048)
            {
                if (options.FeatureLevel < FeatureLevel.FL9_3)
                {
                    Console.WriteLine("WARNING: Resulting texture requires a Feature Level 9.3 or later device.");
                }
            }

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

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

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

                options.TextureFormat = isMono ? TextureFormat.CompressedMono :
                                        TextureFormat.Rgba32;
            }

            // Convert to premultiplied alpha format.
            if (!options.NoPremultiply)
            {
                Console.WriteLine("Premultiplying alpha");

                BitmapUtils.PremultiplyAlpha(bitmap);
            }

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

                bitmap.Save(options.DebugOutputSpriteSheet);
            }

            Console.WriteLine("Writing {0} ({1} format)", options.OutputFile, options.TextureFormat);

            SpriteFontWriter.WriteSpriteFont(options, glyphs, lineSpacing, bitmap);
        }