bool GetFace(uint codepoint, out uint index, out IntPtr face) { //use our font! index = FT.FT_Get_Char_Index(facePtr, codepoint); if (index != 0) { face = facePtr; return(true); } //try fallback font if (context.Fallback == IntPtr.Zero) { face = IntPtr.Zero; return(false); } FT.FT_Set_Char_Size(context.Fallback, FTMath.To26Dot6(0), size26d6, 0, 96 ); index = FT.FT_Get_Char_Index(context.Fallback, codepoint); if (index != 0) { face = context.Fallback; return(true); } //none of the fonts have this mysterious character face = IntPtr.Zero; return(false); }
public void DrawString(SpriteBatch spriteBatch, string text, int x, int y, Color4 color) { if (text == "") //Skip empty strings { return; } var iter = new CodepointIterator(text); float penX = x, penY = y; while (iter.Iterate()) { uint c = iter.Codepoint; if (c == (uint)'\n') { penY += lineHeight; penX = x; continue; } var glyph = GetGlyph(c); if (glyph.Render) { spriteBatch.Draw( glyph.Texture, glyph.Rectangle, new Rectangle( (int)penX + glyph.XOffset, (int)penY + (LineHeight - glyph.YOffset), glyph.Rectangle.Width, glyph.Rectangle.Height ), color ); penX += glyph.HorizontalAdvance; penY += glyph.AdvanceY; } else { penX += glyph.AdvanceX; penY += glyph.AdvanceY; } if (glyph.Kerning && iter.Index < iter.Count - 1) { var g2 = GetGlyph(iter.PeekNext()); if (g2.Face == glyph.Face) { FT.FT_Vector vec; FT.FT_Get_Kerning(glyph.Face, glyph.CharIndex, g2.CharIndex, 2, out vec); var krn = FTMath.From26Dot6(vec.x); penX += krn; } } } }
public Point MeasureString(string text) { if (text == "") //Skip empty strings { return(new Point(0, 0)); } var iter = new CodepointIterator(text); float penX = 0, penY = 0; while (iter.Iterate()) { uint c = iter.Codepoint; if (c == (uint)'\n') { penY += lineHeight; penX = 0; continue; } var glyph = GetGlyph(c); if (glyph.Render) { penX += glyph.HorizontalAdvance; penY += glyph.AdvanceY; } else { penX += glyph.AdvanceX; penY += glyph.AdvanceY; } if (glyph.Kerning && iter.Index < iter.Count - 1) { var g2 = GetGlyph(iter.PeekNext()); if (g2.Face == glyph.Face) { FT.FT_Vector vec; FT.FT_Get_Kerning(glyph.Face, glyph.CharIndex, g2.CharIndex, 2, out vec); var krn = FTMath.From26Dot6(vec.x); penX += krn; } } } return(new Point((int)penX, (int)penY)); }
public Font(FontContext ctx, string filename, float size) { context = ctx; size26d6 = FTMath.To26Dot6(size); if (!File.Exists(filename)) { throw new FileNotFoundException("Font not found " + filename, filename); } var err = FT.FT_New_Face(context.Library, filename, 0, out facePtr); if (err != 0) { throw new Exception("Freetype Error"); } FT.FT_Set_Char_Size(facePtr, FTMath.To26Dot6(0), size26d6, 0, 96 ); //get metrics var faceRec = Marshal.PtrToStructure <FT.FaceRec> (facePtr); var szRec = Marshal.PtrToStructure <FT.SizeRec> (faceRec.size); lineHeight = (int)FTMath.From26Dot6(szRec.metrics.height); pages.Add(new Texture( PAGE_SIZE, PAGE_SIZE )); //Rasterize standard ASCII for (int i = 32; i < 127; i++) { AddCharacter((uint)i); } }
unsafe void AddCharacter(uint codepoint) { if (codepoint == (uint)'\t') { var spaceGlyph = GetGlyph((uint)' '); glyphs.Add(codepoint, new GlyphInfo(spaceGlyph.AdvanceX * 4, spaceGlyph.AdvanceY, spaceGlyph.CharIndex, spaceGlyph.Face, spaceGlyph.Kerning)); return; } IntPtr face; uint index; if (!GetFace(codepoint, out index, out face)) { if (codepoint == (uint)'?') { throw new Exception("Font does not have required ASCII characters"); } var qmGlyph = GetGlyph((uint)'?'); glyphs.Add(codepoint, qmGlyph); return; } FT.FT_Load_Glyph(face, index, FT.FT_LOAD_DEFAULT | FT.FT_LOAD_TARGET_NORMAL); var faceRec = Marshal.PtrToStructure <FT.FaceRec> (face); //not exactly the right spot, but this is the only place we access the members of the face bool kerning = (((long)faceRec.face_flags) & FT.FT_FACE_FLAG_KERNING) == FT.FT_FACE_FLAG_KERNING; FT.FT_Render_Glyph(faceRec.glyph, FT.FT_RENDER_MODE_NORMAL); var glyphRec = Marshal.PtrToStructure <FT.GlyphSlotRec> (faceRec.glyph); if (glyphRec.bitmap.width == 0 || glyphRec.bitmap.rows == 0) { glyphs.Add(codepoint, new GlyphInfo( (int)Math.Ceiling(FTMath.From26Dot6(glyphRec.advance.x)), (int)Math.Ceiling(FTMath.From26Dot6(glyphRec.advance.y)), index, face, kerning ) ); } else { var colors = new ByteColor[glyphRec.bitmap.width * glyphRec.bitmap.rows]; if (glyphRec.bitmap.pixel_mode == 2) { byte *data = (byte *)glyphRec.bitmap.buffer; for (int i = 0; i < glyphRec.bitmap.width * glyphRec.bitmap.rows; i++) { //TODO: 4 bytes used for 1 byte of alpha data? investigate compression with GL_RED and shader. colors [i] = new ByteColor(255, 255, 255, data [i]); } } else { throw new NotImplementedException(); } if (currentX + glyphRec.bitmap.width > PAGE_SIZE) { currentX = 0; currentY += lineMax; lineMax = 0; } if (currentY + glyphRec.bitmap.rows > PAGE_SIZE) { currentX = 0; currentY = 0; lineMax = 0; pages.Add(new Texture(PAGE_SIZE, PAGE_SIZE)); //TODO: new font page! should probably log this } lineMax = (int)Math.Max(lineMax, glyphRec.bitmap.rows); var rect = new Rectangle(currentX, currentY, glyphRec.bitmap.width, glyphRec.bitmap.rows); var tex = pages [pages.Count - 1]; tex.SetData(colors, rect); currentX += glyphRec.bitmap.width; glyphs.Add(codepoint, new GlyphInfo( tex, rect, (int)Math.Ceiling(FTMath.From26Dot6(glyphRec.advance.x)), (int)Math.Ceiling(FTMath.From26Dot6(glyphRec.advance.y)), (int)Math.Ceiling(FTMath.From26Dot6(glyphRec.metrics.horiAdvance)), glyphRec.bitmap_left, glyphRec.bitmap_top, index, face, kerning ) ); } }