/// <summary> /// Renders the Unicode character to a buffer at the Font Size, and returns true on success /// </summary> public unsafe bool Render(char unicode, Span <Color> buffer, out int width, out int height) { if (Charset.TryGetValue(unicode, out var ch) && ch.HasGlyph) { if (buffer.Length < ch.Width * ch.Height) { throw new Exception("Buffer provided isn't large enough to store rendered data"); } if (Font.Disposed) throw new Exception("Cannot get Font data as it is disposed"); fixed(Color *ptr = buffer) { // we actually use the bitmap buffer as our temporary buffer, and fill the pixels out backwards after // kinda weird but it works & saves creating more memory var input = (byte *)ptr; StbTrueType.stbtt_MakeGlyphBitmap(Font.fontInfo, input, ch.Width, ch.Height, ch.Width, Scale, Scale, ch.Glyph); for (int i = ch.Width * ch.Height - 1; i >= 0; i--) { ptr[i] = new Color(input[i], input[i], input[i], input[i]); } } width = ch.Width; height = ch.Height; return(true); } width = height = 0; return(false); }
public static unsafe void CompareMetricsWithStb(Font f, DrawableFontAtlas atlas, StbTrueType.stbtt_fontinfo stbFont) { var pc = new StbTrueType.stbtt_pack_context(); StbTrueType.stbtt_PackBegin(pc, (byte *)0, 512, 512, 512, 1, null); var cd = new StbTrueType.stbtt_packedchar[f.LastCharIndex + 1]; StbTrueType.stbrp_rect[] rects; fixed(StbTrueType.stbtt_packedchar *charDataPtr = &cd[0]) { var range = new StbTrueType.stbtt_pack_range { first_unicode_codepoint_in_range = 0, array_of_unicode_codepoints = null, num_chars = (int)f.LastCharIndex + 1, chardata_for_range = charDataPtr, font_size = -atlas.FontSize }; rects = new StbTrueType.stbrp_rect[f.LastCharIndex + 1]; fixed(StbTrueType.stbrp_rect *rectPtr = &rects[0]) { int n = StbTrueType.stbtt_PackFontRangesGatherRects(pc, stbFont, &range, 1, rectPtr); StbTrueType.stbtt_PackFontRangesPackRects(pc, rectPtr, n); } } foreach ((char charIndex, DrawableGlyph atlasGlyph) in atlas.Glyphs) { FontGlyph glyph = atlasGlyph.FontGlyph; var advance = 0; var bearing = 0; StbTrueType.stbtt_GetCodepointHMetrics(stbFont, charIndex, &advance, &bearing); Assert.True(advance == glyph.AdvanceWidth); Assert.True(bearing == glyph.LeftSideBearing || glyph.LeftSideBearing == 0); // stb has junk data beyond valid var minX = 0; var maxX = 0; var minY = 0; var maxY = 0; StbTrueType.stbtt_GetCodepointBitmapBoxSubpixel(stbFont, charIndex, atlas.RenderScale, atlas.RenderScale, 0, 0, &minX, &minY, &maxX, &maxY); Rectangle bbox = glyph.GetBBox(atlas.RenderScale); Assert.Equal(minX, bbox.X); Assert.Equal(minY, bbox.Y); Assert.Equal(maxX, bbox.Width); Assert.Equal(maxY, bbox.Height); Rectangle drawBox = new Rectangle(0, 0, bbox.Width - bbox.X, bbox.Height - bbox.Y); drawBox.Size += Vector2.One; // Add padding from stb StbTrueType.stbrp_rect rect = rects[charIndex]; Assert.Equal(rect.w, drawBox.Width); Assert.Equal(rect.h, drawBox.Height); } }
public int LoadFont(StbTrueType.stbtt_fontinfo font, byte *data, int dataSize) { var stbError = 0; font.userdata = this; stbError = StbTrueType.stbtt_InitFont(font, data, 0); return(stbError); }
public FontSize(Font font, int size, string charset) { if (font.Disposed) { throw new Exception("Cannot get Font data as it is disposed"); } Font = font; Size = size; Scale = font.GetScale(size); Ascent = font.Ascent * Scale; Descent = font.Descent * Scale; LineGap = font.LineGap * Scale; Height = Ascent - Descent; LineHeight = Height + LineGap; for (int i = 0; i < charset.Length; i++) { // get font info var unicode = charset[i]; var glyph = font.GetGlyph(unicode); if (glyph > 0) { unsafe { int advance, offsetX, x0, y0, x1, y1; StbTrueType.stbtt_GetGlyphHMetrics(font.fontInfo, glyph, &advance, &offsetX); StbTrueType.stbtt_GetGlyphBitmapBox(font.fontInfo, glyph, Scale, Scale, &x0, &y0, &x1, &y1); int w = (x1 - x0); int h = (y1 - y0); // define character var ch = new Character { Unicode = unicode, Glyph = glyph, Width = w, Height = h, Advance = advance * Scale, OffsetX = offsetX * Scale, OffsetY = y0 }; ch.HasGlyph = (w > 0 && h > 0 && StbTrueType.stbtt_IsGlyphEmpty(font.fontInfo, ch.Glyph) == 0); Charset[unicode] = ch; } } } }
/// <summary> /// Gets the Kerning Value between two Unicode characters at the Font Size, or 0 if there is no Kerning /// </summary> public float GetKerning(char unicode0, char unicode1) { if (Charset.TryGetValue(unicode0, out var char0) && Charset.TryGetValue(unicode1, out var char1)) { if (Font.Disposed) { throw new Exception("Cannot get Font data as it is disposed"); } return(StbTrueType.stbtt_GetGlyphKernAdvance(Font.fontInfo, char0.Glyph, char1.Glyph) * Scale); } return(0f); }
public void Begin(int width, int height) { bitmapWidth = width; bitmapHeight = height; _bitmap = new byte[width * height]; _context = new StbTrueType.stbtt_pack_context(); fixed(byte *pixelsPtr = _bitmap) { StbTrueType.stbtt_PackBegin(_context, pixelsPtr, width, height, width, 1, null); } _glyphs = new Dictionary <int, GlyphInfo>(); }
public static StbTrueType.stbtt_fontinfo LoadFontStb(byte[] bytes) { var font = new StbTrueType.stbtt_fontinfo(); unsafe { fixed(byte *fontDataPtr = &bytes[0]) { StbTrueType.stbtt_InitFont(font, fontDataPtr, 0); } } return(font); }
static unsafe string GetName(StbTrueType.stbtt_fontinfo fontInfo, int nameID) { int length = 0; sbyte *ptr = StbTrueType.stbtt_GetFontNameString(fontInfo, &length, StbTrueType.STBTT_PLATFORM_ID_MICROSOFT, StbTrueType.STBTT_MS_EID_UNICODE_BMP, StbTrueType.STBTT_MS_LANG_ENGLISH, nameID); if (length > 0) { return(new string(ptr, 0, length, Encoding.BigEndianUnicode)); } return("Unknown"); }
public static unsafe DrawableFontAtlas RenderFontStbPacked(byte[] ttf, float fontSize, Vector2 atlasSize, int numChars, Font f, out StbTrueType.stbtt_fontinfo fontInfo) { var atlasObj = new DrawableFontAtlas(f, fontSize); fontSize = atlasObj.FontSize; fontInfo = new StbTrueType.stbtt_fontinfo(); fixed(byte *ttPtr = &ttf[0]) { StbTrueType.stbtt_InitFont(fontInfo, ttPtr, 0); float scaleFactor = StbTrueType.stbtt_ScaleForMappingEmToPixels(fontInfo, fontSize); int ascent, descent, lineGap; StbTrueType.stbtt_GetFontVMetrics(fontInfo, &ascent, &descent, &lineGap); atlasSize *= 3; // Needs to be big as the packing sucks, and glyphs getting cut out messes with the tests. var pixels = new byte[(int)atlasSize.X * (int)atlasSize.Y]; var pc = new StbTrueType.stbtt_pack_context(); fixed(byte *pixelsPtr = pixels) { StbTrueType.stbtt_PackBegin(pc, pixelsPtr, (int)atlasSize.X, (int)atlasSize.Y, (int)atlasSize.X, 1, null); } var cd = new StbTrueType.stbtt_packedchar[numChars]; fixed(StbTrueType.stbtt_packedchar *charPtr = cd) { StbTrueType.stbtt_PackFontRange(pc, ttPtr, 0, -fontSize, 0, numChars, charPtr); } StbTrueType.stbtt_PackEnd(pc); for (var i = 0; i < cd.Length; ++i) { var atlasGlyph = DrawableGlyph.CreateForTest(cd[i].xadvance, cd[i].xoff, cd[i].x1 - cd[i].x0, cd[i].y1 - cd[i].y0); atlasGlyph.GlyphUV = new Rectangle(cd[i].x0, cd[i].y0, atlasGlyph.Width, atlasGlyph.Height); atlasObj.Glyphs[(char)i] = atlasGlyph; } } return(atlasObj); }
/// <summary> /// Loads a Font from the byte array. The Font will use this buffer until it is disposed. /// </summary> public unsafe Font(byte[] buffer) { fontBuffer = buffer; fontHandle = GCHandle.Alloc(fontBuffer, GCHandleType.Pinned); fontInfo = new StbTrueType.stbtt_fontinfo(); StbTrueType.stbtt_InitFont(fontInfo, (byte *)(fontHandle.AddrOfPinnedObject().ToPointer()), 0); FamilyName = GetName(fontInfo, 1); StyleName = GetName(fontInfo, 2); // properties int ascent, descent, linegap; StbTrueType.stbtt_GetFontVMetrics(fontInfo, &ascent, &descent, &linegap); Ascent = ascent; Descent = descent; LineGap = linegap; Height = Ascent - Descent; LineHeight = Height + LineGap;
public static float __tt_getPixelHeightScale(this StbTrueType.stbtt_fontinfo font, float size) { return((float)(StbTrueType.stbtt_ScaleForPixelHeight(font, (float)(size)))); }
public static void __tt_getFontVMetrics(this StbTrueType.stbtt_fontinfo font, int *ascent, int *descent, int *lineGap) { StbTrueType.stbtt_GetFontVMetrics(font, ascent, descent, lineGap); }
public static int __tt_getGlyphKernAdvance(this StbTrueType.stbtt_fontinfo font, int glyph1, int glyph2) { return((int)(StbTrueType.stbtt_GetGlyphKernAdvance(font, (int)(glyph1), (int)(glyph2)))); }
public static void __tt_renderGlyphBitmap(this StbTrueType.stbtt_fontinfo font, byte *output, int outWidth, int outHeight, int outStride, float scaleX, float scaleY, int glyph) { StbTrueType.stbtt_MakeGlyphBitmap(font, output, (int)(outWidth), (int)(outHeight), (int)(outStride), (float)(scaleX), (float)(scaleY), (int)(glyph)); }
public static int __tt_getGlyphIndex(this StbTrueType.stbtt_fontinfo font, int codepoint) { return((int)(StbTrueType.stbtt_FindGlyphIndex(font, (int)(codepoint)))); }
public void Add(byte[] ttf, float fontPixelHeight, IEnumerable <CharacterRange> characterRanges) { if (ttf == null || ttf.Length == 0) { throw new ArgumentNullException(nameof(ttf)); } if (fontPixelHeight <= 0) { throw new ArgumentOutOfRangeException(nameof(fontPixelHeight)); } if (characterRanges == null) { throw new ArgumentNullException(nameof(characterRanges)); } if (!characterRanges.Any()) { throw new ArgumentException("characterRanges must have a least one value."); } var fontInfo = StbTrueType.CreateFont(ttf, 0); if (fontInfo == null) { throw new Exception("Failed to init font."); } var scaleFactor = StbTrueType.stbtt_ScaleForPixelHeight(fontInfo, fontPixelHeight); int ascent, descent, lineGap; StbTrueType.stbtt_GetFontVMetrics(fontInfo, &ascent, &descent, &lineGap); foreach (var range in characterRanges) { if (range.Start > range.End) { continue; } var cd = new StbTrueType.stbtt_packedchar[range.End - range.Start + 1]; fixed(StbTrueType.stbtt_packedchar *chardataPtr = cd) { StbTrueType.stbtt_PackFontRange(_context, fontInfo.data, 0, fontPixelHeight, range.Start, range.End - range.Start + 1, chardataPtr); } for (var i = 0; i < cd.Length; ++i) { var yOff = cd[i].yoff; yOff += ascent * scaleFactor; var glyphInfo = new GlyphInfo { X = cd[i].x0, Y = cd[i].y0, Width = cd[i].x1 - cd[i].x0, Height = cd[i].y1 - cd[i].y0, XOffset = (int)cd[i].xoff, YOffset = (int)Math.Round(yOff), XAdvance = (int)Math.Round(cd[i].xadvance) }; _glyphs[i + range.Start] = glyphInfo; } } }
public static TtfFontBakerResult Bake(byte[] ttf, float fontPixelHeight, int bitmapWidth, int bitmapHeight, IEnumerable <CharacterRange> characterRanges) { if (ttf == null || ttf.Length == 0) { throw new ArgumentNullException(nameof(ttf)); } if (fontPixelHeight <= 0) { throw new ArgumentOutOfRangeException(nameof(fontPixelHeight)); } if (bitmapWidth <= 0) { throw new ArgumentOutOfRangeException(nameof(bitmapWidth)); } if (bitmapHeight <= 0) { throw new ArgumentOutOfRangeException(nameof(bitmapHeight)); } if (characterRanges == null) { throw new ArgumentNullException(nameof(characterRanges)); } if (!characterRanges.Any()) { throw new ArgumentException("characterRanges must have a least one value."); } byte[] pixels; var glyphs = new Dictionary <int, GlyphInfo>(); fixed(byte *ttfPtr = ttf) { StbTrueType.stbtt_fontinfo fontInfo = new StbTrueType.stbtt_fontinfo(); if (StbTrueType.stbtt_InitFont(fontInfo, ttfPtr, 0) == 0) { throw new Exception("Failed to init font."); } float scaleFactor = StbTrueType.stbtt_ScaleForPixelHeight(fontInfo, fontPixelHeight); int ascent, descent, lineGap; StbTrueType.stbtt_GetFontVMetrics(fontInfo, &ascent, &descent, &lineGap); pixels = new byte[bitmapWidth * bitmapHeight]; StbTrueType.stbtt_pack_context pc = new StbTrueType.stbtt_pack_context(); fixed(byte *pixelsPtr = pixels) { StbTrueType.stbtt_PackBegin(pc, pixelsPtr, bitmapWidth, bitmapHeight, bitmapWidth, 1, null); } foreach (var range in characterRanges) { if (range.Start > range.End) { continue; } var cd = new StbTrueType.stbtt_packedchar[range.End - range.Start + 1]; fixed(StbTrueType.stbtt_packedchar *chardataPtr = cd) { StbTrueType.stbtt_PackFontRange(pc, ttfPtr, 0, fontPixelHeight, range.Start, range.End - range.Start + 1, chardataPtr); } for (var i = 0; i < cd.Length; ++i) { var yOff = cd[i].yoff; yOff += ascent * scaleFactor; var glyphInfo = new GlyphInfo { X = cd[i].x0, Y = cd[i].y0, Width = cd[i].x1 - cd[i].x0, Height = cd[i].y1 - cd[i].y0, XOffset = (int)cd[i].xoff, YOffset = (int)Math.Round(yOff), XAdvance = (int)Math.Round(cd[i].xadvance) }; glyphs[(char)(i + range.Start)] = glyphInfo; } } } return(new TtfFontBakerResult(glyphs, fontPixelHeight, pixels, bitmapWidth, bitmapHeight)); }
public static int __tt_buildGlyphBitmap(this StbTrueType.stbtt_fontinfo font, int glyph, float size, float scale, int *advance, int *lsb, int *x0, int *y0, int *x1, int *y1) { StbTrueType.stbtt_GetGlyphHMetrics(font, (int)(glyph), advance, lsb); StbTrueType.stbtt_GetGlyphBitmapBox(font, (int)(glyph), (float)(scale), (float)(scale), x0, y0, x1, y1); return((int)(1)); }