Exemplo n.º 1
0
        public static BitmapFont FromQFontFile(string filePath, float downSampleFactor, FontKerningConfiguration kerningConfig)
        {
            if (kerningConfig == null)
                kerningConfig = new FontKerningConfiguration();

            float fontScale = 1f;

            BitmapFont qfont = new BitmapFont();
            qfont.fontData = Builder.LoadQFontDataFromFile(filePath, downSampleFactor * fontScale, kerningConfig);

            return qfont;
        }
Exemplo n.º 2
0
 public static BitmapFont FromQFontFile(string filePath, FontKerningConfiguration kerningConfig)
 {
     return FromQFontFile(filePath, 1.0f, kerningConfig);
 }
Exemplo n.º 3
0
        public static Dictionary<String, int> CalculateKerning(char[] charSet, FontGlyph[] glyphs, List<HelperBitmap> bitmapPages, FontKerningConfiguration config)
        {
            var kerningPairs = new Dictionary<String, int>();

            //we start by computing the index of the first and last non-empty pixel in each row of each glyph
            XLimits[][] limits = new XLimits[charSet.Length][];
            int maxHeight = 0;
            for (int n = 0; n < charSet.Length; n++)
            {
                var rect = glyphs[n].rect;
                var page = bitmapPages[glyphs[n].page];

                limits[n] = new XLimits[rect.Height];

                maxHeight = Math.Max(rect.Height, maxHeight);

                int yStart = rect.Y;
                int yEnd = rect.Y + rect.Height;
                int xStart = rect.X;
                int xEnd = rect.X + rect.Width;

                for (int j = yStart; j < yEnd; j++)
                {
                    int last = xStart;

                    bool yetToFindFirst = true;
                    for (int i = xStart; i < xEnd; i++)
                    {
                        if (!HelperBitmap.EmptyAlphaPixel(page.bitmapData, i, j, config.alphaEmptyPixelTolerance))
                        {
                            if (yetToFindFirst)
                            {
                                limits[n][j - yStart].Min = i - xStart;
                                yetToFindFirst = false;
                            }
                            last = i;
                        }
                    }

                    limits[n][j - yStart].Max = last - xStart;

                    if (yetToFindFirst)
                        limits[n][j - yStart].Min = xEnd - 1;
                }
            }

            //we now bring up each row to the max (or min) of it's two adjacent rows, this is to stop glyphs sliding together too closely
            var tmp = new XLimits[maxHeight];

            for (int n = 0; n < charSet.Length; n++)
            {
                //clear tmp
                for (int j = 0; j < limits[n].Length; j++)
                    tmp[j] = limits[n][j];

                for (int j = 0; j < limits[n].Length; j++)
                {
                    if (j != 0)
                    {
                        tmp[j].Min = Math.Min(limits[n][j - 1].Min, tmp[j].Min);
                        tmp[j].Max = Math.Max(limits[n][j - 1].Max, tmp[j].Max);
                    }

                    if (j != limits[n].Length - 1)
                    {
                        tmp[j].Min = Math.Min(limits[n][j + 1].Min, tmp[j].Min);
                        tmp[j].Max = Math.Max(limits[n][j + 1].Max, tmp[j].Max);
                    }
                }

                for (int j = 0; j < limits[n].Length; j++)
                    limits[n][j] = tmp[j];
            }

            for (int i = 0; i < charSet.Length; i++)
                for (int j = 0; j < charSet.Length; j++)
                    kerningPairs.Add("" + charSet[i] + charSet[j], 1 - Kerning(glyphs[i], glyphs[j], limits[i], limits[j], config));

            return kerningPairs;
        }
Exemplo n.º 4
0
        private static int Kerning(FontGlyph g1, FontGlyph g2, XLimits[] lim1, XLimits[] lim2, FontKerningConfiguration config)
        {
            int yOffset1 = g1.yOffset;
            int yOffset2 = g2.yOffset;

            int startY = Math.Max(yOffset1, yOffset2);
            int endY = Math.Min(g1.rect.Height + yOffset1, g2.rect.Height + yOffset2);

            int w1 = g1.rect.Width;

            int worstCase = w1;

            for (int j = startY; j < endY; j++)
                worstCase = Math.Min(worstCase, w1 - lim1[j - yOffset1].Max + lim2[j - yOffset2].Min);

            worstCase = Math.Min(worstCase, g1.rect.Width);
            worstCase = Math.Min(worstCase, g2.rect.Width);

            //modify by character kerning rules
            CharacterKerningRule kerningRule = config.GetOverridingCharacterKerningRuleForPair("" + g1.character + g2.character);
            if (kerningRule == CharacterKerningRule.Zero)
            {
                return 0;
            }
            else if (kerningRule == CharacterKerningRule.NotMoreThanHalf)
            {
                return (int)Math.Min(Math.Min(g1.rect.Width, g2.rect.Width) * 0.5f, worstCase);
            }

            return worstCase;
        }
Exemplo n.º 5
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;
        }