Exemplo n.º 1
0
        public BitmapFont(Font font, FontBuilderConfiguration config)
        {
            if (config == null)
                config = new FontBuilderConfiguration();

            fontData = BuildFont(font, config, null);
        }
Exemplo n.º 2
0
        public BitmapFont(string fileName, float size, FontStyle style, FontBuilderConfiguration config)
        {
            PrivateFontCollection pfc = new PrivateFontCollection();
            pfc.AddFontFile(fileName);
            var fontFamily = pfc.Families[0];

            if (!fontFamily.IsStyleAvailable(style))
                throw new ArgumentException("Font file: " + fileName + " does not support style: " + style);

            if (config == null)
                config = new FontBuilderConfiguration();

            float fontScale = 1f;

            using (var font = new Font(fontFamily, size * fontScale * config.SuperSampleLevels, style))
            {
                fontData = BuildFont(font, config, null);
            }
        }
Exemplo n.º 3
0
 internal BitmapFont(FontData fontData)
 {
     this.fontData = fontData;
 }
Exemplo n.º 4
0
        public static void CreateTextureFontFiles(string fileName, float size, FontStyle style, FontBuilderConfiguration config, string newFontName)
        {
            PrivateFontCollection pfc = new PrivateFontCollection();
            pfc.AddFontFile(fileName);
            var fontFamily = pfc.Families[0];

            if (!fontFamily.IsStyleAvailable(style))
                throw new ArgumentException("Font file: " + fileName + " does not support style: " + style);

            FontData fontData = null;
            if (config == null)
                config = new FontBuilderConfiguration();

            using (var font = new Font(fontFamily, size * config.SuperSampleLevels, style))
            {
                fontData = BuildFont(font, config, newFontName);
            }

            Builder.SaveQFontDataToFile(fontData, newFontName);
        }
Exemplo n.º 5
0
        private float MeasureTextNodeLength(TextNode node, FontData fontData, FontRenderOptions options)
        {
            bool monospaced = fontData.IsMonospacingActive(options);
            float monospaceWidth = fontData.GetMonoSpaceWidth(options);

            if (node.Type == TextNodeType.Space)
            {
                if (monospaced)
                    return monospaceWidth;

                return (float)Math.Ceiling(fontData.meanGlyphWidth * options.WordSpacing);
            }

            float length = 0f;
            if (node.Type == TextNodeType.Word)
            {
                for (int i = 0; i < node.Text.Length; i++)
                {
                    char c = node.Text[i];
                    if (fontData.CharSetMapping.ContainsKey(c))
                    {
                        if (monospaced)
                            length += monospaceWidth;
                        else
                            length += (float)Math.Ceiling(fontData.CharSetMapping[c].rect.Width + fontData.meanGlyphWidth * options.CharacterSpacing + fontData.GetKerningPairCorrection(i, node.Text, node));
                    }
                }
            }
            return length;
        }
Exemplo n.º 6
0
 public void MeasureNodes(FontData fontData, FontRenderOptions options)
 {
     foreach (TextNode node in this)
     {
         if (node.Length == 0f)
             node.Length = MeasureTextNodeLength(node, fontData, options);
     }
 }
Exemplo n.º 7
0
        public static FontData LoadQFontDataFromFile(string filePath, float downSampleFactor, FontKerningConfiguration kerningConfig)
        {
            string xmldoc = File.ReadAllText(filePath);
            var data = new FontData();
            int pageCount = 0;
            char[] charSet;
            data.Deserialize(xmldoc, out pageCount, out charSet);

            string namePrefix = filePath.Replace(".bmf", "").Replace(" ", "");

            var bitmapPages = new List<HelperBitmap>();

            if (pageCount == 1)
            {
                bitmapPages.Add(new HelperBitmap(namePrefix + ".png"));
            }
            else
            {
                for (int i = 0; i < pageCount; i++)
                    bitmapPages.Add(new HelperBitmap(namePrefix + "_" + i + ".png"));
            }

            foreach (var glyph in data.CharSetMapping.Values)
                RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].bitmapData, glyph, false, kerningConfig.alphaEmptyPixelTolerance);

            var intercept = FirstIntercept(data.CharSetMapping);
            if (intercept != null)
            {
                throw new Exception("Failed to load font from file. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. If you are texturing your font without locking pixel opacity, then consider using a larger glyph margin. This can be done by setting QFontBuilderConfiguration myQfontBuilderConfig.GlyphMargin, and passing it into CreateTextureFontFiles.");
            }

            if (downSampleFactor > 1.0f)
            {
                foreach (var page in bitmapPages)
                    page.DownScale32((int)(page.bitmap.Width * downSampleFactor), (int)(page.bitmap.Height * downSampleFactor));

                foreach (var glyph in data.CharSetMapping.Values)
                {
                    glyph.rect = new Rectangle((int)(glyph.rect.X * downSampleFactor),
                                                (int)(glyph.rect.Y * downSampleFactor),
                                                (int)(glyph.rect.Width * downSampleFactor),
                                                (int)(glyph.rect.Height * downSampleFactor));
                    glyph.yOffset = (int)(glyph.yOffset * downSampleFactor);
                }
            }
            else if (downSampleFactor < 1.0f)
            {
                // If we were simply to shrink the entire texture, then at some point we will make glyphs overlap, breaking the font.
                // For this reason it is necessary to copy every glyph to a separate bitmap, and then shrink each bitmap individually.
                FontGlyph[] shrunkGlyphs;
                HelperBitmap[] shrunkBitmapsPerGlyph;
                CreateBitmapPerGlyph(data.CharSetMapping.Values.ToArray(), bitmapPages.ToArray(), out shrunkGlyphs, out shrunkBitmapsPerGlyph);

                //shrink each bitmap
                for (int i = 0; i < shrunkGlyphs.Length; i++)
                {
                    var bmp = shrunkBitmapsPerGlyph[i];
                    bmp.DownScale32(Math.Max((int)(bmp.bitmap.Width * downSampleFactor), 1), Math.Max((int)(bmp.bitmap.Height * downSampleFactor), 1));
                    shrunkGlyphs[i].rect = new Rectangle(0, 0, bmp.bitmap.Width, bmp.bitmap.Height);
                    shrunkGlyphs[i].yOffset = (int)(shrunkGlyphs[i].yOffset * downSampleFactor);
                }

                var shrunkBitmapData = new BitmapData[shrunkBitmapsPerGlyph.Length];
                for (int i = 0; i < shrunkBitmapsPerGlyph.Length; i++)
                {
                    shrunkBitmapData[i] = shrunkBitmapsPerGlyph[i].bitmapData;
                }

                //use roughly the same number of pages as before..
                int newWidth = (int)(bitmapPages[0].bitmap.Width * (0.1f + downSampleFactor));
                int newHeight = (int)(bitmapPages[0].bitmap.Height * (0.1f + downSampleFactor));

                //free old bitmap pages since we are about to chuck them away
                for (int i = 0; i < pageCount; i++)
                    bitmapPages[i].Free();

                FontGlyph[] shrunkRepackedGlyphs;
                bitmapPages = GenerateBitmapSheetsAndRepack(shrunkGlyphs, shrunkBitmapData, newWidth, newHeight, out shrunkRepackedGlyphs, 4, false);
                data.CharSetMapping = CreateCharGlyphMapping(shrunkRepackedGlyphs);

                foreach (var bmp in shrunkBitmapsPerGlyph)
                    bmp.Free();

                pageCount = bitmapPages.Count;
            }

            data.Pages = new TexturePage[pageCount];
            for (int i = 0; i < pageCount; i++)
                data.Pages[i] = new TexturePage(bitmapPages[i].bitmapData);

            if (downSampleFactor != 1.0f)
            {
                foreach (var glyph in data.CharSetMapping.Values)
                    RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].bitmapData, glyph, false, kerningConfig.alphaEmptyPixelTolerance);

                intercept = FirstIntercept(data.CharSetMapping);
                if (intercept != null)
                {
                    throw new Exception("Failed to load font from file. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. This occurred only after resizing your texture font, implying that there is a bug in QFont. ");
                }
            }

            var glyphList = new List<FontGlyph>();

            foreach (var c in charSet)
                glyphList.Add(data.CharSetMapping[c]);

            data.KerningPairs = KerningCalculator.CalculateKerning(charSet.ToArray(), glyphList.ToArray(), bitmapPages, kerningConfig);

            data.CalculateMeanWidth();
            data.CalculateMaxHeight();

            for (int i = 0; i < pageCount; i++)
                bitmapPages[i].Free();

            return data;
        }
Exemplo n.º 8
0
        public FontData BuildFontData(string saveName)
        {
            if (config.ForcePowerOfTwo && config.SuperSampleLevels != PowerOfTwo(config.SuperSampleLevels))
            {
                throw new ArgumentOutOfRangeException("SuperSampleLevels must be a power of two when using ForcePowerOfTwo.");
            }

            if (config.SuperSampleLevels <= 0 || config.SuperSampleLevels > 8)
            {
                throw new ArgumentOutOfRangeException("SuperSampleLevels = [" + config.SuperSampleLevels + "] is an unsupported value. Please use values in the range [1,8]");
            }

            int margin = 2; //margin in initial bitmap (don't bother to make configurable - likely to cause confusion
            int pageWidth = config.PageWidth * config.SuperSampleLevels; //texture page width
            int pageHeight = config.PageHeight * config.SuperSampleLevels; //texture page height
            bool usePowerOfTwo = config.ForcePowerOfTwo;
            int glyphMargin = config.GlyphMargin * config.SuperSampleLevels;

            FontGlyph[] initialGlyphs;
            var sizes = GetGlyphSizes(font);
            var maxSize = GetMaxGlyphSize(sizes);
            var initialBmp = CreateInitialBitmap(font, maxSize, margin, out initialGlyphs, config.TextGenerationRenderHint);
            var initialBitmapData = initialBmp.LockBits(new Rectangle(0, 0, initialBmp.Width, initialBmp.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb);

            int minYOffset = int.MaxValue;
            foreach (var glyph in initialGlyphs)
            {
                RetargetGlyphRectangleInwards(initialBitmapData, glyph, true, config.KerningConfig.alphaEmptyPixelTolerance);
                minYOffset = Math.Min(minYOffset, glyph.yOffset);
            }
            minYOffset--; //give one pixel of breathing room?

            foreach (var glyph in initialGlyphs)
                glyph.yOffset -= minYOffset;

            FontGlyph[] glyphs;
            var bitmapPages = GenerateBitmapSheetsAndRepack(initialGlyphs, new BitmapData[1] { initialBitmapData }, pageWidth, pageHeight, out glyphs, glyphMargin, usePowerOfTwo);

            initialBmp.UnlockBits(initialBitmapData);
            initialBmp.Dispose();

            if (config.SuperSampleLevels != 1)
            {
                ScaleSheetsAndGlyphs(bitmapPages, glyphs, 1.0f / config.SuperSampleLevels);
                RetargetAllGlyphs(bitmapPages, glyphs, config.KerningConfig.alphaEmptyPixelTolerance);
            }

            //create list of texture pages
            var pages = new List<TexturePage>();
            foreach (var page in bitmapPages)
                pages.Add(new TexturePage(page.bitmapData));

            var fontData = new FontData();
            fontData.CharSetMapping = CreateCharGlyphMapping(glyphs);
            fontData.Pages = pages.ToArray();
            fontData.CalculateMeanWidth();
            fontData.CalculateMaxHeight();
            fontData.KerningPairs = KerningCalculator.CalculateKerning(charSet.ToCharArray(), glyphs, bitmapPages, config.KerningConfig);
            fontData.naturallyMonospaced = IsMonospaced(sizes);

            if (saveName != null)
            {
                if (bitmapPages.Count == 1)
                    bitmapPages[0].bitmap.Save(saveName + ".png", System.Drawing.Imaging.ImageFormat.Png);
                else
                {
                    for (int i = 0; i < bitmapPages.Count; i++)
                        bitmapPages[i].bitmap.Save(saveName + "_" + i + ".png", System.Drawing.Imaging.ImageFormat.Png);
                }
            }

            foreach (var page in bitmapPages)
                page.Free();

            //validate glyphs
            var intercept = FirstIntercept(fontData.CharSetMapping);
            if (intercept != null)
                throw new Exception("Failed to create glyph set. Glyphs '" + intercept[0] + "' and '" + intercept[1] + "' were overlapping. This is could be due to an error in the font, or a bug in Graphics.MeasureString().");

            return fontData;
        }
Exemplo n.º 9
0
        public static void SaveQFontDataToFile(FontData data, string filePath)
        {
            string xmldoc = data.Serialize();
            StreamWriter writer = new StreamWriter(filePath + ".bmf");
            writer.WriteLine(xmldoc);

            writer.Close();
        }