Beispiel #1
0
        /// <summary>
        /// Find the first intercept between two glyph bounding boxes
        /// </summary>
        /// <param name="charSet">The character set to test</param>
        /// <returns>The overlapping characters</returns>
        private static char[] FirstIntercept(Dictionary <char, QFontGlyph> charSet)
        {
            char[] keys = Helper.ToArray(charSet.Keys);

            for (int i = 0; i < keys.Length; i++)
            {
                for (int j = i + 1; j < keys.Length; j++)
                {
                    if (charSet[keys[i]].Page == charSet[keys[j]].Page && RectangleIntersect(charSet[keys[i]].Rect, charSet[keys[j]].Rect))
                    {
                        return(new[] { keys[i], keys[j] });
                    }
                }
            }
            return(null);
        }
Beispiel #2
0
        public static QFontData LoadQFontDataFromFile(string filePath, float downSampleFactor, QFontConfiguration loaderConfig)
        {
            var          lines  = new List <String>();
            StreamReader reader = new StreamReader(filePath);
            string       line;

            while ((line = reader.ReadLine()) != null)
            {
                lines.Add(line);
            }
            reader.Close();

            var data      = new QFontData();
            int pageCount = 0;

            char[] charSet;
            data.Deserialize(lines, out pageCount, out charSet);

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

            var bitmapPages = new List <QBitmap>();

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

            foreach (var glyph in data.CharSetMapping.Values)
            {
                RetargetGlyphRectangleOutwards(bitmapPages[glyph.page].bitmapData, glyph, false, loaderConfig.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.
                QFontGlyph[] shrunkGlyphs;
                QBitmap[]    shrunkBitmapsPerGlyph;
                CreateBitmapPerGlyph(Helper.ToArray(data.CharSetMapping.Values), 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();
                }

                QFontGlyph[] shrunkRepackedGlyphs;
                bitmapPages         = GenerateBitmapSheetsAndRepack(shrunkGlyphs, shrunkBitmapData, newWidth, newHeight, out shrunkRepackedGlyphs, 4);
                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, loaderConfig.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 <QFontGlyph>();

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

            if (loaderConfig.ShadowConfig != null)
            {
                data.dropShadowFont = BuildDropShadow(bitmapPages, glyphList.ToArray(), loaderConfig.ShadowConfig, Helper.ToArray(charSet), loaderConfig.KerningConfig.alphaEmptyPixelTolerance);
            }

            data.KerningPairs = KerningCalculator.CalculateKerning(Helper.ToArray(charSet), glyphList.ToArray(), bitmapPages, loaderConfig.KerningConfig);

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

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

            return(data);
        }