Пример #1
0
        public void Deserialize(string input, out int pageCount, out char[] charSet)
        {
            CharSetMapping = new Dictionary<char, FontGlyph>();
            var charSetList = new List<char>();
            pageCount = 0;

            XmlTextReader r = new XmlTextReader(new StringReader(input));

            while (r.Read())
            {
                if (r.NodeType == XmlNodeType.Element && r.Name == "FontData")
                {
                    pageCount = int.Parse(r.GetAttribute("Pages"));
                    int glyphCount = int.Parse(r.GetAttribute("CharSetLen"));

                    while (r.Read() && r.NodeType == XmlNodeType.Element && r.Name == "Glyph")
                    {
                        if (CharSetMapping.Count < glyphCount)
                        {
                            char c = r.GetAttribute("char")[0];
                            var vals = r.GetAttribute("rect").Split(' ');
                            var glyph = new FontGlyph(int.Parse(r.GetAttribute("page")),
                                new Rectangle(int.Parse(vals[0]), int.Parse(vals[1]), int.Parse(vals[2]), int.Parse(vals[3])),
                                int.Parse(r.GetAttribute("yoffset")), c);

                            CharSetMapping.Add(c, glyph);
                            charSetList.Add(c);
                        }
                    }
                }
            }
            charSet = charSetList.ToArray();
        }
Пример #2
0
 public static void CreateBitmapPerGlyph(FontGlyph[] sourceGlyphs, HelperBitmap[] sourceBitmaps, out FontGlyph[] destGlyphs, out HelperBitmap[] destBitmaps)
 {
     destBitmaps = new HelperBitmap[sourceGlyphs.Length];
     destGlyphs = new FontGlyph[sourceGlyphs.Length];
     for (int i = 0; i < sourceGlyphs.Length; i++)
     {
         var sg = sourceGlyphs[i];
         destGlyphs[i] = new FontGlyph(i, new Rectangle(0, 0, sg.rect.Width, sg.rect.Height), sg.yOffset, sg.character);
         destBitmaps[i] = new HelperBitmap(new Bitmap(sg.rect.Width, sg.rect.Height, PixelFormat.Format32bppArgb));
         HelperBitmap.Blit(sourceBitmaps[sg.page].bitmapData, destBitmaps[i].bitmapData, sg.rect, 0, 0);
     }
 }
Пример #3
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;
        }
Пример #4
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;
        }
Пример #5
0
        //The initial bitmap is simply a long thin strip of all glyphs in a row
        private Bitmap CreateInitialBitmap(Font font, SizeF maxSize, int initialMargin, out FontGlyph[] glyphs, TextGenerationRenderHint renderHint)
        {
            glyphs = new FontGlyph[charSet.Length];

            int spacing = (int)Math.Ceiling(maxSize.Width) + 2 * initialMargin;
            Bitmap bmp = new Bitmap(spacing * charSet.Length, (int)Math.Ceiling(maxSize.Height) + 2 * initialMargin, PixelFormat.Format24bppRgb);
            var graph = System.Drawing.Graphics.FromImage(bmp);

            switch (renderHint)
            {
                case TextGenerationRenderHint.SizeDependent:
                    graph.TextRenderingHint = font.Size <= 12.0f ? TextRenderingHint.ClearTypeGridFit : TextRenderingHint.AntiAlias;
                    break;
                case TextGenerationRenderHint.AntiAlias:
                    graph.TextRenderingHint = TextRenderingHint.AntiAlias;
                    break;
                case TextGenerationRenderHint.AntiAliasGridFit:
                    graph.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
                    break;
                case TextGenerationRenderHint.ClearTypeGridFit:
                    graph.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
                    break;
                case TextGenerationRenderHint.SystemDefault:
                    graph.TextRenderingHint = TextRenderingHint.SystemDefault;
                    break;
            }

            int xOffset = initialMargin;
            for (int i = 0; i < charSet.Length; i++)
            {
                graph.DrawString("" + charSet[i], font, Brushes.White, xOffset, initialMargin);
                var charSize = graph.MeasureString("" + charSet[i], font);
                glyphs[i] = new FontGlyph(0, new Rectangle(xOffset - initialMargin, 0, (int)charSize.Width + initialMargin * 2, (int)charSize.Height + initialMargin * 2), 0, charSet[i]);
                xOffset += (int)charSize.Width + initialMargin * 2;
            }

            graph.Flush();
            graph.Dispose();

            return bmp;
        }
Пример #6
0
        private static void ScaleSheetsAndGlyphs(List<HelperBitmap> pages, FontGlyph[] glyphs, float scale)
        {
            foreach (var page in pages)
                page.DownScale32((int)(page.bitmap.Width * scale), (int)(page.bitmap.Height * scale));

            foreach (var glyph in glyphs)
            {
                glyph.rect = new Rectangle((int)(glyph.rect.X * scale), (int)(glyph.rect.Y * scale), (int)(glyph.rect.Width * scale), (int)(glyph.rect.Height * scale));
                glyph.yOffset = (int)(glyph.yOffset * scale);
            }
        }
Пример #7
0
        private static void RetargetGlyphRectangleOutwards(BitmapData bitmapData, FontGlyph glyph, bool setYOffset, byte alphaTolerance)
        {
            int startX, endX;
            int startY, endY;

            var rect = glyph.rect;

            EmptyDel emptyPix;

            if (bitmapData.PixelFormat == PixelFormat.Format32bppArgb)
                emptyPix = delegate(BitmapData data, int x, int y) { return HelperBitmap.EmptyAlphaPixel(data, x, y, alphaTolerance); };
            else
                emptyPix = delegate(BitmapData data, int x, int y) { return HelperBitmap.EmptyPixel(data, x, y); };

            unsafe
            {
                for (startX = rect.X; startX >= 0; startX--)
                {
                    bool foundPix = false;
                    for (int j = rect.Y; j <= rect.Y + rect.Height; j++)
                    {
                        if (!emptyPix(bitmapData, startX, j))
                        {
                            foundPix = true;
                            break;
                        }
                    }

                    if (!foundPix)
                    {
                        startX++;
                        break;
                    }
                }

                for (endX = rect.X + rect.Width; endX < bitmapData.Width; endX++)
                {
                    bool foundPix = false;
                    for (int j = rect.Y; j <= rect.Y + rect.Height; j++)
                    {
                        if (!emptyPix(bitmapData, endX, j))
                        {
                            foundPix = true;
                            break;
                        }
                    }

                    if (!foundPix)
                    {
                        endX--;
                        break;
                    }
                }

                for (startY = rect.Y; startY >= 0; startY--)
                {
                    bool foundPix = false;
                    for (int i = startX; i <= endX; i++)
                    {
                        if (!emptyPix(bitmapData, i, startY))
                        {
                            foundPix = true;
                            break;
                        }
                    }

                    if (!foundPix)
                    {
                        startY++;
                        break;
                    }
                }

                for (endY = rect.Y + rect.Height; endY < bitmapData.Height; endY++)
                {
                    bool foundPix = false;
                    for (int i = startX; i <= endX; i++)
                    {
                        if (!emptyPix(bitmapData, i, endY))
                        {
                            foundPix = true;
                            break;
                        }
                    }

                    if (!foundPix)
                    {
                        endY--;
                        break;
                    }
                }
            }

            glyph.rect = new Rectangle(startX, startY, endX - startX + 1, endY - startY + 1);

            if (setYOffset)
                glyph.yOffset = glyph.rect.Y;
        }
Пример #8
0
        private static void RetargetGlyphRectangleInwards(BitmapData bitmapData, FontGlyph glyph, bool setYOffset, byte alphaTolerance)
        {
            int startX, endX;
            int startY, endY;

            var rect = glyph.rect;

            EmptyDel emptyPix;

            if (bitmapData.PixelFormat == PixelFormat.Format32bppArgb)
                emptyPix = delegate(BitmapData data, int x, int y) { return HelperBitmap.EmptyAlphaPixel(data, x, y, alphaTolerance); };
            else
                emptyPix = delegate(BitmapData data, int x, int y) { return HelperBitmap.EmptyPixel(data, x, y); };

            unsafe
            {
                for (startX = rect.X; startX < bitmapData.Width; startX++)
                    for (int j = rect.Y; j < rect.Y + rect.Height; j++)
                        if (!emptyPix(bitmapData, startX, j))
                            goto Done1;
            Done1:

                for (endX = rect.X + rect.Width; endX >= 0; endX--)
                    for (int j = rect.Y; j < rect.Y + rect.Height; j++)
                        if (!emptyPix(bitmapData, endX, j))
                            goto Done2;
            Done2:

                for (startY = rect.Y; startY < bitmapData.Height; startY++)
                    for (int i = startX; i < endX; i++)
                        if (!emptyPix(bitmapData, i, startY))
                            goto Done3;

            Done3:

                for (endY = rect.Y + rect.Height; endY >= 0; endY--)
                    for (int i = startX; i < endX; i++)
                        if (!emptyPix(bitmapData, i, endY))
                            goto Done4;
            Done4: ;
            }

            if (endY < startY)
                startY = endY = rect.Y;

            if (endX < startX)
                startX = endX = rect.X;

            glyph.rect = new Rectangle(startX, startY, endX - startX + 1, endY - startY + 1);

            if (setYOffset)
                glyph.yOffset = glyph.rect.Y;
        }
Пример #9
0
 private static void RetargetAllGlyphs(List<HelperBitmap> pages, FontGlyph[] glyphs, byte alphaTolerance)
 {
     foreach (var glyph in glyphs)
         RetargetGlyphRectangleOutwards(pages[glyph.page].bitmapData, glyph, false, alphaTolerance);
 }
Пример #10
0
        private static List<HelperBitmap> GenerateBitmapSheetsAndRepack(FontGlyph[] sourceGlyphs, BitmapData[] sourceBitmaps, int destSheetWidth, int destSheetHeight, out FontGlyph[] destGlyphs, int destMargin, bool usePowerOfTwo)
        {
            var pages = new List<HelperBitmap>();
            destGlyphs = new FontGlyph[sourceGlyphs.Length];

            HelperBitmap currentPage = null;

            int maxY = 0;
            foreach (var glph in sourceGlyphs)
                maxY = Math.Max(glph.rect.Height, maxY);

            int finalPageIndex = 0;
            int finalPageRequiredWidth = 0;
            int finalPageRequiredHeight = 0;

            for (int k = 0; k < 2; k++)
            {
                bool pre = k == 0;  //first iteration is simply to determine the required size of the final page, so that we can crop it in advance

                int xPos = 0;
                int yPos = 0;
                int maxYInRow = 0;
                int totalTries = 0;

                for (int i = 0; i < sourceGlyphs.Length; i++)
                {
                    if (!pre && currentPage == null)
                    {
                        if (finalPageIndex == pages.Count)
                        {
                            int width = Math.Min(destSheetWidth, usePowerOfTwo ? PowerOfTwo(finalPageRequiredWidth) : finalPageRequiredWidth);
                            int height = Math.Min(destSheetHeight, usePowerOfTwo ? PowerOfTwo(finalPageRequiredHeight) : finalPageRequiredHeight);

                            currentPage = new HelperBitmap(new Bitmap(width, height, PixelFormat.Format32bppArgb));
                            currentPage.Clear32(255, 255, 255, 0); //clear to white, but totally transparent
                        }
                        else
                        {
                            currentPage = new HelperBitmap(new Bitmap(destSheetWidth, destSheetHeight, PixelFormat.Format32bppArgb));
                            currentPage.Clear32(255, 255, 255, 0); //clear to white, but totally transparent
                        }
                        pages.Add(currentPage);
                    }

                    totalTries++;

                    if (totalTries > 10 * sourceGlyphs.Length)
                        throw new Exception("Failed to fit font into texture pages");

                    var rect = sourceGlyphs[i].rect;

                    if (xPos + rect.Width + 2 * destMargin <= destSheetWidth && yPos + rect.Height + 2 * destMargin <= destSheetHeight)
                    {
                        if (!pre)
                        {
                            //add to page
                            if (sourceBitmaps[sourceGlyphs[i].page].PixelFormat == PixelFormat.Format32bppArgb)
                                HelperBitmap.Blit(sourceBitmaps[sourceGlyphs[i].page], currentPage.bitmapData, rect.X, rect.Y, rect.Width, rect.Height, xPos + destMargin, yPos + destMargin);
                            else
                                HelperBitmap.BlitMask(sourceBitmaps[sourceGlyphs[i].page], currentPage.bitmapData, rect.X, rect.Y, rect.Width, rect.Height, xPos + destMargin, yPos + destMargin);

                            destGlyphs[i] = new FontGlyph(pages.Count - 1, new Rectangle(xPos + destMargin, yPos + destMargin, rect.Width, rect.Height), sourceGlyphs[i].yOffset, sourceGlyphs[i].character);
                        }
                        else
                        {
                            finalPageRequiredWidth = Math.Max(finalPageRequiredWidth, xPos + rect.Width + 2 * destMargin);
                            finalPageRequiredHeight = Math.Max(finalPageRequiredHeight, yPos + rect.Height + 2 * destMargin);
                        }

                        xPos += rect.Width + 2 * destMargin;
                        maxYInRow = Math.Max(maxYInRow, rect.Height);

                        continue;
                    }

                    if (xPos + rect.Width + 2 * destMargin > destSheetWidth)
                    {
                        i--;

                        yPos += maxYInRow + 2 * destMargin;
                        xPos = 0;

                        if (yPos + maxY + 2 * destMargin > destSheetHeight)
                        {
                            yPos = 0;

                            if (!pre)
                            {
                                currentPage = null;
                            }
                            else
                            {
                                finalPageRequiredWidth = 0;
                                finalPageRequiredHeight = 0;
                                finalPageIndex++;
                            }
                        }
                        continue;
                    }
                }
            }

            return pages;
        }
Пример #11
0
        private static Dictionary<char, FontGlyph> CreateCharGlyphMapping(FontGlyph[] glyphs)
        {
            var dict = new Dictionary<char, FontGlyph>();
            for (int i = 0; i < glyphs.Length; i++)
                dict.Add(glyphs[i].character, glyphs[i]);

            return dict;
        }