public static FontResource Load(Stream stream, IEnumerable<char> targets, int pixelSize)
        {
            InitStandardWidths();

            int count = targets.Count();
            int maxWidth = GetMaxWidth(pixelSize, count);

            var dict = new FullDictionary<char, CharacterInfo>(CharacterInfo.Default);
            Bitmap finalBitmap;
            using (var bitmap = new Bitmap(maxWidth, maxWidth, PixelFormat.Format24bppRgb))
            {
                int currentX = 0, currentY = 0;
                using (Graphics graphics = Graphics.FromImage(bitmap))
                {
                    var typeface = new FontFace(stream);

                    foreach (char c in targets)
                    {
                        BlitCharacter(pixelSize, maxWidth, dict, ref currentX, ref currentY, graphics, typeface, c);
                    }
                }

                finalBitmap = ShortenBitmap(bitmap, maxWidth, currentY + yInterval + pixelSize + (pixelSize / 10 > 1 ? pixelSize / 10 : 1));
            }

            var fontResource = new FontResource();
            fontResource.FontHeight = pixelSize + yInterval;
            fontResource.CharInfoDict = dict;
            fontResource.InitTexture(finalBitmap);
            finalBitmap.Dispose();
            return fontResource;
        }
Exemple #2
0
        public unsafe void Append(TextAnalyzer analyzer, FontFace font, string text)
        {
            var layout = new TextLayout();
            var format = new TextFormat {
                Font = font,
                Size = 32.0f
            };

            analyzer.AppendText(text, format);
            analyzer.PerformLayout(0, 32, 1000, 1000, layout);

            var memBlock = new MemoryBlock(text.Length * 6 * PosColorTexture.Layout.Stride);
            var mem = (PosColorTexture*)memBlock.Data;
            foreach (var thing in layout.Stuff) {
                var width = thing.Width;
                var height = thing.Height;
                var region = new Vector4(thing.SourceX, thing.SourceY, width, height) / 4096;
                var origin = new Vector2(thing.DestX, thing.DestY);
                *mem++ = new PosColorTexture(origin + new Vector2(0, height), new Vector2(region.X, region.Y + region.W), unchecked((int)0xff000000));
                *mem++ = new PosColorTexture(origin + new Vector2(width, height), new Vector2(region.X + region.Z, region.Y + region.W), unchecked((int)0xff000000));
                *mem++ = new PosColorTexture(origin + new Vector2(width, 0), new Vector2(region.X + region.Z, region.Y), unchecked((int)0xff000000));
                *mem++ = new PosColorTexture(origin, new Vector2(region.X, region.Y), unchecked((int)0xff000000));
                count++;
            }

            vertexBuffer = new DynamicVertexBuffer(memBlock, PosColorTexture.Layout);
        }
Exemple #3
0
        private static unsafe bool RenderGlyph(FontFace typeface, char c, float pixelSize, out Surface surface)
        {
            bool result = false;

            Glyph glyph = typeface.GetGlyph(c, pixelSize);
            if (glyph != null && glyph.RenderWidth > 0 && glyph.RenderHeight > 0)
            {
                surface = new Surface
                {
                    Bits = Marshal.AllocHGlobal(glyph.RenderWidth * glyph.RenderHeight),
                    Width = glyph.RenderWidth,
                    Height = glyph.RenderHeight,
                    Pitch = glyph.RenderWidth
                };

                var stuff = (byte*)surface.Bits;
                for (int i = 0; i < surface.Width * surface.Height; i++)
                    *stuff++ = 0;

                glyph.RenderTo(surface);

                result = true;
            }
            else
            {
                surface = new Surface();
            }

            return result;
        }
Exemple #4
0
        static void Main(string[] args)
        {
            using (FileStream file = File.OpenRead("../../../Fonts/OpenSans-Regular.ttf"))
            {
                var typeface = new FontFace(file);
                long totalLength = 0;
                int average = 0;

                for (int c = 0; c <= char.MaxValue; c++)
                {
                    Console.WriteLine("Dump {0}: {1}", c, (char)c);
                    string comparisonFile = Path.Combine(ComparisonPath, (int)c + ".png");
                    Surface surface;
                    if (RenderGlyph(typeface, (char)c, 32, out surface))
                    {
                        SaveSurface(surface, comparisonFile);
                        totalLength += surface.Width;
                        surface.Dispose();
                    }
                }

                Console.WriteLine("Total width: {0}", totalLength);
            }

            Console.Read();
        }
        private static void BlitCharacter(int pixelSize, int maxWidth, FullDictionary<char, CharacterInfo> dict, ref int currentX, ref int currentY, Graphics g, FontFace typeface, char c)
        {
            if (c == ' ' || c == '\t')
            {
                int width = (c == ' ') ? pixelSize / 2 : pixelSize * 4;
                if (currentX + xInterval + width >= maxWidth)
                {
                    currentX = 0;
                    currentY += yInterval + pixelSize;
                    if (currentY + yInterval + pixelSize >= maxWidth)
                    { throw new Exception("Texture Size not big enough for required characters."); }
                }
                Bitmap glyphBitmap = new Bitmap(width + xInterval, pixelSize + yInterval);
                //float yoffset = pixelSize * 3 / 4 - glyph.HorizontalMetrics.Bearing.Y;
                g.DrawImage(glyphBitmap, currentX + xInterval, currentY + yInterval);
                CharacterInfo info = new CharacterInfo(currentX, currentY, width + xInterval, pixelSize + yInterval);
                dict.Add(c, info);
                glyphBitmap.Dispose();
                currentX += width;
            }
            else
            {
                Surface surface; Glyph glyph;
                if (RenderGlyph(typeface, c, pixelSize, out surface, out glyph))
                {
                    if (currentX + xInterval + surface.Width >= maxWidth)
                    {
                        currentX = 0;
                        currentY += yInterval + pixelSize;
                        if (currentY + yInterval + pixelSize >= maxWidth)
                        { throw new Exception("Texture Size not big enough for required characters."); }
                    }
                    Bitmap glyphBitmap = GetGlyphBitmap(surface);
                    const int a = 5;
                    const int b = 8;
                    //float yoffset = pixelSize * a / b - glyph.HorizontalMetrics.Bearing.Y;
                    float skyHeight = yInterval + pixelSize * a / b - glyph.HorizontalMetrics.Bearing.Y;
                    if (skyHeight < 0) { skyHeight = 0; }
                    if (skyHeight < 0)
                    { skyHeight = 0; }
                    else if (skyHeight + glyphBitmap.Height > yInterval + pixelSize)
                    { skyHeight -= glyphBitmap.Height - (yInterval + pixelSize); }
                    g.DrawImage(glyphBitmap, currentX + xInterval, currentY + skyHeight, glyphBitmap.Width, glyphBitmap.Height);
            #if DEBUG
                    g.DrawRectangle(greenPen, currentX, currentY, glyphBitmap.Width + xInterval, yInterval + pixelSize - 1);
                    g.DrawRectangle(redPen, currentX + xInterval, currentY + skyHeight, glyphBitmap.Width, glyphBitmap.Height);
                    g.DrawLine(bluePen, currentX, currentY + yInterval + pixelSize * a / b,
                        currentX + glyphBitmap.Width, currentY + yInterval + pixelSize * a / b);
            #endif
                    CharacterInfo info = new CharacterInfo(currentX, currentY, glyphBitmap.Width + xInterval, yInterval + pixelSize - 1);
                    dict.Add(c, info);
                    glyphBitmap.Dispose();
                    currentX += xInterval + surface.Width;
                }

                surface.Dispose();
            }
        }
Exemple #6
0
        public void AppendText(char *text, int count, TextFormat format)
        {
            // look up the cache entry for the given font and size
            CachedFace cachedFace;
            var        font = format.Font;
            var        size = FontFace.ComputePixelSize(format.Size, Dpi);
            var        key  = new CacheKey(font.Id, size);

            if (!cache.TryGetValue(key, out cachedFace))
            {
                cache.Add(key, cachedFace = new CachedFace(font, size));
            }

            // process each character in the string
            var   nextBreak = BreakCategory.None;
            var   previous  = new CodePoint();
            char *end       = text + count;

            while (text != end)
            {
                // handle surrogate pairs properly
                CodePoint codePoint;
                char      c = *text++;
                if (char.IsSurrogate(c) && text != end)
                {
                    codePoint = new CodePoint(c, *text++);
                }
                else
                {
                    codePoint = c;
                }

                // ignore linefeeds directly after a carriage return
                if (c == '\n' && (char)previous == '\r')
                {
                    continue;
                }

                // get the glyph data
                CachedGlyph glyph;
                if (!cachedFace.Glyphs.TryGetValue(codePoint, out glyph) && !char.IsControl(c))
                {
                    var data   = font.GetGlyph(codePoint, size);
                    var width  = data.RenderWidth;
                    var height = data.RenderHeight;
                    if (width > atlas.Width || height > atlas.Height)
                    {
                        throw new InvalidOperationException("Glyph is larger than the size of the provided atlas.");
                    }

                    var rect = new Rect();
                    if (width > 0 && height > 0)
                    {
                        // render the glyph
                        var memSize = width * height;
                        var mem     = memoryBuffer;
                        if (mem == null)
                        {
                            memoryBuffer = mem = new MemoryBuffer(memSize);
                        }

                        mem.Clear(memSize);
                        data.RenderTo(new Surface {
                            Bits   = mem.Pointer,
                            Width  = width,
                            Height = height,
                            Pitch  = width
                        });

                        // save the rasterized glyph in the user's atlas
                        rect = packer.Insert(width, height);
                        if (rect.Height == 0)
                        {
                            // didn't fit in the atlas... start a new sheet
                            currentPage++;
                            packer.Clear(atlas.Width, atlas.Height);
                            rect = packer.Insert(width, height);
                            if (rect.Height == 0)
                            {
                                throw new InvalidOperationException("Failed to insert glyph into fresh page.");
                            }
                        }
                        atlas.Insert(currentPage, rect.X, rect.Y, rect.Width, rect.Height, mem.Pointer);
                    }

                    glyph = new CachedGlyph(rect, data.HorizontalMetrics.Bearing, data.HorizontalMetrics.Advance);
                    cachedFace.Glyphs.Add(codePoint, glyph);
                }

                // check for a kerning offset
                var kerning = font.GetKerning(previous, codePoint, size);
                previous = codePoint;

                // figure out whether this character can serve as a line break point
                // TODO: more robust character class handling
                var breakCategory = BreakCategory.None;
                if (char.IsWhiteSpace(c))
                {
                    if (c == '\r' || c == '\n')
                    {
                        breakCategory = BreakCategory.Mandatory;
                    }
                    else
                    {
                        breakCategory = BreakCategory.Opportunity;
                    }
                }

                // the previous character might make us think that this one should be a break opportunity
                if (nextBreak > breakCategory)
                {
                    breakCategory = nextBreak;
                }
                if (c == '-')
                {
                    nextBreak = BreakCategory.Opportunity;
                }

                // alright, we have all the right glyph data cached and loaded
                // append relevant info to our buffer; we'll do the actual layout later
                buffer.Add(new BufferEntry {
                    GlyphData = glyph,
                    Kerning   = kerning,
                    Break     = breakCategory
                });
            }
        }
Exemple #7
0
 public CachedFace(FontFace font, float size)
 {
     Metrics = font.GetFaceMetrics(size);
     Glyphs  = new Dictionary <CodePoint, CachedGlyph>();
 }