private FT_GlyphSlotRec GetCharBitmap(uint c) { uint index = FT.FT_Get_Char_Index(faceptr, c); int ret = FT.FT_Load_Glyph(faceptr, index, FT.FT_LOAD_DEFAULT); FT_GlyphSlotRec glyph_rec = (FT_GlyphSlotRec)Marshal.PtrToStructure(face.glyph, typeof(FT_GlyphSlotRec)); int retb = FT.FT_Render_Glyph(ref glyph_rec, FT_Render_Mode.FT_RENDER_MODE_NORMAL); return(glyph_rec); }
public override int GetCharWidth(PDFFont pdfFont, TTFontData data, char charCode) { InitLibrary(); int aint = (int)charCode; if (data.Widths.IndexOfKey(charCode) >= 0) { return(data.Widths[charCode]); } LogFontFt cfont = data.LogFont; cfont.OpenFont(); int awidth = 0; Monitor.Enter(flag); try { if (data.Widths.IndexOfKey(charCode) >= 0) { awidth = data.Widths[charCode]; } else { if (0 == FT.FT_Load_Char(cfont.iface, (uint)charCode, (int)FT.FT_LOAD_NO_SCALE)) { FT_FaceRec aface = (FT_FaceRec)Marshal.PtrToStructure(cfont.iface, typeof(FT_FaceRec)); FT_GlyphSlotRec aglyph = (FT_GlyphSlotRec)Marshal.PtrToStructure(aface.glyph, typeof(FT_GlyphSlotRec)); ushort width1 = (ushort)(aglyph.linearHoriAdvance >> 16); ushort width2 = (ushort)(aglyph.linearHoriAdvance & 0x0000FFFF); double dwidth = width1 + width2 / (double)65535; awidth = System.Convert.ToInt32(Math.Round(cfont.widthmult * dwidth)); } data.Widths[charCode] = awidth; data.Glyphs[charCode] = System.Convert.ToInt32(FT.FT_Get_Char_Index(cfont.iface, charCode)); if (data.FirstLoaded > aint) { data.FirstLoaded = aint; } if (data.LastLoaded < aint) { data.LastLoaded = aint; } } } finally { Monitor.Exit(flag); } return(awidth); }
static extern int FT_Render_Glyph (ref FT_GlyphSlotRec slot, FT_Render_Mode render_mode);
/// <summary> /// Create a bitmap font from the specified dynamic font. /// </summary> static public bool CreateFont(Font ttf, int size, int faceIndex, bool kerning, string characters, int padding, out BMFont font, out Texture2D tex) { font = null; tex = null; if (ttf == null || !isPresent) { return(false); } IntPtr lib = IntPtr.Zero; IntPtr face = IntPtr.Zero; if (FT_Init_FreeType(out lib) != 0) { Debug.LogError("Failed to initialize FreeType"); return(false); } string fileName = Application.dataPath.Substring(0, Application.dataPath.Length - "Assets".Length) + UnityEditor.AssetDatabase.GetAssetPath(ttf); if (!File.Exists(fileName)) { Debug.LogError("Unable to use the chosen font."); } else if (FT_New_Face(lib, fileName, faceIndex, out face) != 0) { Debug.LogError("Unable to use the chosen font (FT_New_Face)."); } else { font = new BMFont(); font.charSize = size; Color32 white = Color.white; List <int> entries = new List <int>(); List <Texture2D> textures = new List <Texture2D>(); FT_FaceRec faceRec = (FT_FaceRec)Marshal.PtrToStructure(face, typeof(FT_FaceRec)); FT_Set_Pixel_Sizes(face, 0, (uint)size); // Calculate the baseline value that would let the printed font be centered vertically //int ascender = (faceRec.met.ascender >> 6); //int descender = (faceRec.descender >> 6); //int baseline = ((ascender - descender) >> 1); //if ((baseline & 1) == 1) --baseline; //Debug.Log(ascender + " " + descender + " " + baseline); // Space character is not renderable FT_Load_Glyph(face, FT_Get_Char_Index(face, 32), FT_LOAD_DEFAULT); FT_GlyphSlotRec space = (FT_GlyphSlotRec)Marshal.PtrToStructure(faceRec.glyph, typeof(FT_GlyphSlotRec)); // Space is not visible and doesn't have a texture BMGlyph spaceGlyph = font.GetGlyph(32, true); spaceGlyph.offsetX = 0; spaceGlyph.offsetY = 0; spaceGlyph.advance = (space.metrics.horiAdvance >> 6); spaceGlyph.channel = 15; spaceGlyph.x = 0; spaceGlyph.y = 0; spaceGlyph.width = 0; spaceGlyph.height = 0; // Save kerning information if (kerning) { for (int b = 0; b < characters.Length; ++b) { uint ch2 = characters[b]; if (ch2 == 32) { continue; } FT_Vector vec; if (FT_Get_Kerning(face, ch2, 32, 0, out vec) != 0) { continue; } int offset = (vec.x >> 6); if (offset != 0) { spaceGlyph.SetKerning((int)ch2, offset); } } } // Run through all requested characters foreach (char ch in characters) { uint charIndex = FT_Get_Char_Index(face, (uint)ch); FT_Load_Glyph(face, charIndex, FT_LOAD_DEFAULT); FT_GlyphSlotRec glyph = (FT_GlyphSlotRec)Marshal.PtrToStructure(faceRec.glyph, typeof(FT_GlyphSlotRec)); FT_Render_Glyph(ref glyph, FT_Render_Mode.FT_RENDER_MODE_NORMAL); if (glyph.bitmap.width > 0 && glyph.bitmap.rows > 0) { byte[] buffer = new byte[glyph.bitmap.width * glyph.bitmap.rows]; Marshal.Copy(glyph.bitmap.buffer, buffer, 0, buffer.Length); Texture2D texture = new Texture2D(glyph.bitmap.width, glyph.bitmap.rows, UnityEngine.TextureFormat.ARGB32, false); Color32[] colors = new Color32[buffer.Length]; for (int i = 0, y = 0; y < glyph.bitmap.rows; ++y) { for (int x = 0; x < glyph.bitmap.width; ++x) { white.a = buffer[i++]; colors[x + glyph.bitmap.width * (glyph.bitmap.rows - y - 1)] = white; } } // Save the texture texture.SetPixels32(colors); texture.Apply(); textures.Add(texture); entries.Add(ch); // Record the metrics BMGlyph bmg = font.GetGlyph(ch, true); bmg.offsetX = (glyph.metrics.horiBearingX >> 6); bmg.offsetY = -(glyph.metrics.horiBearingY >> 6); bmg.advance = (glyph.metrics.horiAdvance >> 6); bmg.channel = 15; // Save kerning information if (kerning) { for (int b = 0; b < characters.Length; ++b) { uint ch2 = characters[b]; if (ch2 == ch) { continue; } FT_Vector vec; if (FT_Get_Kerning(face, ch2, ch, 0, out vec) != 0) { continue; } int offset = (vec.x >> 6); if (offset != 0) { bmg.SetKerning((int)ch2, offset); } } } } } // Create a packed texture with all the characters tex = new Texture2D(32, 32, TextureFormat.ARGB32, false); Rect[] rects = tex.PackTextures(textures.ToArray(), padding); // Make the RGB channel pure white Color32[] cols = tex.GetPixels32(); for (int i = 0, imax = cols.Length; i < imax; ++i) { Color32 c = cols[i]; c.r = 255; c.g = 255; c.b = 255; cols[i] = c; } tex.SetPixels32(cols); tex.Apply(); font.texWidth = tex.width; font.texHeight = tex.height; int min = int.MaxValue; int max = int.MinValue; // Other glyphs are visible and need to be added for (int i = 0, imax = entries.Count; i < imax; ++i) { // Destroy the texture now that it's a part of an atlas UnityEngine.Object.DestroyImmediate(textures[i]); textures[i] = null; Rect rect = rects[i]; // Set the texture coordinates BMGlyph glyph = font.GetGlyph(entries[i], true); glyph.x = Mathf.RoundToInt(rect.x * font.texWidth); glyph.y = Mathf.RoundToInt(rect.y * font.texHeight); glyph.width = Mathf.RoundToInt(rect.width * font.texWidth); glyph.height = Mathf.RoundToInt(rect.height * font.texHeight); // Flip the Y since the UV coordinate system is different glyph.y = font.texHeight - glyph.y - glyph.height; max = Mathf.Max(max, -glyph.offsetY); min = Mathf.Min(min, -glyph.offsetY - glyph.height); } int baseline = size + min; baseline += ((max - min - size) >> 1); // Offset all glyphs so that they are not using the baseline for (int i = 0, imax = entries.Count; i < imax; ++i) { BMGlyph glyph = font.GetGlyph(entries[i], true); glyph.offsetY += baseline; } } if (face != IntPtr.Zero) { FT_Done_Face(face); } #if !UNITY_3_5 if (lib != IntPtr.Zero) { FT_Done_FreeType(lib); } #endif return(tex != null); }
static extern int FT_Render_Glyph(ref FT_GlyphSlotRec slot, FT_Render_Mode render_mode);
private void CompileCharacterToTexture(FT_FaceRec face, int c) { FTGlyphOffset offset = new FTGlyphOffset(); //We first convert the number index to a character index uint index = FT.FT_Get_Char_Index(faceptr, (uint)c); string sError = ""; if (index == 0) { sError = "No Glyph"; } //Here we load the actual glyph for the character int ret = FT.FT_Load_Glyph(faceptr, index, FT.FT_LOAD_DEFAULT); if (ret != 0) { Console.Write("Load_Glyph failed for character " + c.ToString()); } FT_GlyphSlotRec glyphrec = (FT_GlyphSlotRec)Marshal.PtrToStructure(face.glyph, typeof(FT_GlyphSlotRec)); ret = FT.FT_Render_Glyph(ref glyphrec, FT_Render_Mode.FT_RENDER_MODE_NORMAL); if (ret != 0) { Console.Write("Render failed for character " + c.ToString()); } int size = (glyphrec.bitmap.width * glyphrec.bitmap.rows); if (size <= 0) { //Console.Write("Blank Character: " + c.ToString()); //space is a special `blank` character extent_x[c] = 0; if (c == 32) { Gl.glNewList((list_base + c), Gl.GL_COMPILE); Gl.glTranslatef(font_size >> 1, 0, 0); extent_x[c] = font_size >> 1; Gl.glEndList(); offset.left = 0; offset.top = 0; offset.height = 0; offset.width = extent_x[c]; offsets[c] = offset; } return; } byte[] bmp = new byte[size]; Marshal.Copy(glyphrec.bitmap.buffer, bmp, 0, bmp.Length); //Next we expand the bitmap into an opengl texture int width = next_po2(glyphrec.bitmap.width); int height = next_po2(glyphrec.bitmap.rows); byte[] expanded = new byte[2 * width * height]; for (int j = 0; j < height; j++) { for (int i = 0; i < width; i++) { //Luminance expanded[2 * (i + j * width)] = (byte)255; //expanded[4 * (i + j * width) + 1] = (byte)255; //expanded[4 * (i + j * width) + 2] = (byte)255; // Alpha expanded[2 * (i + j * width) + 1] = (i >= glyphrec.bitmap.width || j >= glyphrec.bitmap.rows) ? (byte)0 : (byte)(bmp[i + glyphrec.bitmap.width * j]); } } //Set up some texture parameters for opengl Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[c]); Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_S, Gl.GL_CLAMP_TO_EDGE); Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_WRAP_T, Gl.GL_CLAMP_TO_EDGE); Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR); Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR); //Create the texture Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, Gl.GL_RGBA, width, height, 0, Gl.GL_LUMINANCE_ALPHA, Gl.GL_UNSIGNED_BYTE, expanded); expanded = null; bmp = null; //Create a display list and bind a texture to it Gl.glNewList((list_base + c), Gl.GL_COMPILE); Gl.glBindTexture(Gl.GL_TEXTURE_2D, textures[c]); //Account for freetype spacing rules Gl.glTranslatef(glyphrec.bitmap_left, 0, 0); Gl.glPushMatrix(); Gl.glTranslatef(0, glyphrec.bitmap_top - glyphrec.bitmap.rows, 0); float x = (float)glyphrec.bitmap.width / (float)width; float y = (float)glyphrec.bitmap.rows / (float)height; offset.left = glyphrec.bitmap_left; offset.top = glyphrec.bitmap_top; offset.height = glyphrec.bitmap.rows; offset.width = glyphrec.bitmap.width; offset.advance = glyphrec.advance; offset.lsb_delta = glyphrec.lsb_delta; offset.rsb_delta = glyphrec.rsb_delta; offset.linearHoriAdvance = glyphrec.linearHoriAdvance; offset.linearVertAdvance = glyphrec.linearVertAdvance; offsets[c] = offset; //Draw the quad Gl.glBegin(Gl.GL_QUADS); Gl.glTexCoord2d(0, 0); Gl.glVertex2f(0, glyphrec.bitmap.rows); Gl.glTexCoord2d(0, y); Gl.glVertex2f(0, 0); Gl.glTexCoord2d(x, y); Gl.glVertex2f(glyphrec.bitmap.width, 0); Gl.glTexCoord2d(x, 0); Gl.glVertex2f(glyphrec.bitmap.width, glyphrec.bitmap.rows); Gl.glEnd(); Gl.glPopMatrix(); //Advance for the next character Gl.glTranslatef(glyphrec.bitmap.width, 0, 0); extent_x[c] = glyphrec.bitmap_left + glyphrec.bitmap.width; Gl.glEndList(); sChars += "f:" + c.ToString() + "[w:" + glyphrec.bitmap.width.ToString() + "][h:" + glyphrec.bitmap.rows.ToString() + "]" + sError; }
public CharacterInfo RequestCharacterInfo(int charactorCode) { var glyphIndex = FT.FT_Get_Char_Index(face.Face, (uint)charactorCode); if (glyphIndex == 0) { return(null); } FT_Error error; error = FT.FT_Load_Glyph(face.Face, glyphIndex, FT.FT_LOAD_DEFAULT); if (error != FT_Error.FT_Err_Ok) { throw new FreeTypeException(error); } FT_Render_Mode renderMode = antialias ? FT_Render_Mode.FT_RENDER_MODE_NORMAL : FT_Render_Mode.FT_RENDER_MODE_MONO; IntPtr pGlyphSlot; unsafe { pGlyphSlot = (IntPtr)face.GlyphSlot; } error = FT.FT_Render_Glyph(pGlyphSlot, renderMode); if (error != FT_Error.FT_Err_Ok) { throw new FreeTypeException(error); } FT_GlyphSlotRec glyphSlot = (FT_GlyphSlotRec)Marshal.PtrToStructure(pGlyphSlot, typeof(FT_GlyphSlotRec)); FT_Bitmap bitmap = glyphSlot.bitmap; if (bitmap.buffer == IntPtr.Zero) { return(new CharacterInfo(this, charactorCode, new Vector2((int)glyphSlot.advance.x >> 6, (int)glyphSlot.advance.y >> 6))); } Texture texture = Font.atlas.Allocate(bitmap.width, bitmap.rows); switch ((FT_Pixel_Mode)bitmap.pixel_mode) { case FT_Pixel_Mode.FT_PIXEL_MODE_GRAY: { uint length = bitmap.width * bitmap.rows; byte[] buffer = new Byte[length]; Marshal.Copy(bitmap.buffer, buffer, 0, (int)length); Color[] pixels = new Color[length]; int count = 0; for (int y = (int)bitmap.rows - 1; y >= 0; y--) { for (int x = 0; x < bitmap.width; x++) { int index = y * (int)bitmap.width + x; pixels[count] = new Color { r = 1, g = 1, b = 1, a = (float)buffer[index] / 255.0f }; count++; } } texture.SetPixels(pixels); } break; case FT_Pixel_Mode.FT_PIXEL_MODE_MONO: { uint length = bitmap.width * bitmap.rows; byte[] buffer = new Byte[bitmap.pitch * bitmap.rows]; Marshal.Copy(bitmap.buffer, buffer, 0, (int)buffer.Length); Color[] pixels = new Color[length]; int count = 0; for (int y = (int)bitmap.rows - 1; y >= 0; y--) { for (int x = 0; x < bitmap.width; x++) { int index = y * bitmap.pitch + (int)(x / 8); byte group = buffer[index]; float pixel = (group & (1 << (7 - x % 8))) > 0 ? 255.0f : 0; pixels[count] = new Color { r = 1, g = 1, b = 1, a = pixel / 255.0f }; count++; } } texture.SetPixels(pixels); } break; default: throw new NotImplementedException(string.Format("Pixel mode ({0})", bitmap.pixel_mode.ToString())); } return(new CharacterInfo( this, charactorCode, new Vector2((float)glyphSlot.advance.x / (float)(1 << 6), (float)glyphSlot.advance.y / (float)(1 << 6)), texture, new Vector2(glyphSlot.bitmap_left, glyphSlot.bitmap_top) )); }