public static GlyphService FromGlyph(char character, GlyphSlot glyph, Renderer render_context) { glyph.RenderGlyph(RenderMode.Normal); Bitmap m = null; using (var g = glyph.GetGlyph()) { g.ToBitmap(RenderMode.Normal, new FTVector26Dot6(0, 0), false); var bt = g.ToBitmapGlyph(); if (bt.Bitmap.Width > 0) { m = bt.Bitmap.ToGdipBitmap(); } } // Later on write a Sprite.FromBitmap for Glyph Bitmaps.. var sprite = m == null ? null : Sprite.FromBitmap(m, render_context.ShaderProgram, 0, 0, 0, true); sprite?.AcquireUse(); return(new GlyphService(character, CalcNext(glyph), CalcOffset(glyph), sprite)); }
public static Matrix4 CalcOffset(GlyphSlot glyph) => Matrix4.CreateTranslation(glyph.Metrics.HorizontalBearingX.ToSingle() / Sprite.TileSize, (glyph.Metrics.HorizontalBearingY - glyph.Metrics.Height).ToSingle() / Sprite.TileSize, 0) * Matrix4.CreateScale(0.68f);
private void CompileCharacterToTexture(Face 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); uint index = sharpFace.GetCharIndex((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()); //} try { sharpFace.LoadGlyph(index, LoadFlags.Default, LoadTarget.Normal); } catch { 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()); //} GlyphSlot glyphrec = sharpFace.Glyph; 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.NewList((list_base + c), ListMode.Compile); GL.Translate(font_size >> 1, 0, 0); extent_x[c] = font_size >> 1; GL.EndList(); 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.BindTexture(TextureTarget.Texture2D, textures[c]); GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, 0x812F); //const Int32 GL_CLAMP_TO_EDGE = 0x812F; GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, 0x812F); //const Int32 GL_CLAMP_TO_EDGE = 0x812F; GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, 0x2601); //const Int32 GL_LINEAR = 0x2601; GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, 0x2601); //const Int32 GL_LINEAR = 0x2601; //Create the texture GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, width, height, 0, PixelFormat.LuminanceAlpha, PixelType.UnsignedByte, expanded); expanded = null; bmp = null; //Create a display list and bind a texture to it GL.NewList((list_base + c), ListMode.Compile); GL.BindTexture(TextureTarget.Texture2D, textures[c]); //Account for freetype spacing rules GL.Translate(glyphrec.BitmapLeft, 0, 0); GL.PushMatrix(); GL.Translate(0, glyphrec.BitmapTop - glyphrec.Bitmap.Rows, 0); float x = (float)glyphrec.Bitmap.Width / (float)width; float y = (float)glyphrec.Bitmap.Rows / (float)height; offset.left = glyphrec.BitmapLeft; offset.top = glyphrec.BitmapTop; offset.height = glyphrec.Bitmap.Rows; offset.width = glyphrec.Bitmap.Width; offset.advance = glyphrec.Advance; offset.lsb_delta = glyphrec.DeltaLsb; offset.rsb_delta = glyphrec.DeltaRsb; offset.linearHoriAdvance = glyphrec.LinearHorizontalAdvance; offset.linearVertAdvance = glyphrec.LinearVerticalAdvance; offsets[c] = offset; //Draw the quad GL.Begin(BeginMode.Quads); GL.TexCoord2(0, 0); GL.Vertex2(0, glyphrec.Bitmap.Rows); GL.TexCoord2(0, y); GL.Vertex2(0, 0); GL.TexCoord2(x, y); GL.Vertex2(glyphrec.Bitmap.Width, 0); GL.TexCoord2(x, 0); GL.Vertex2(glyphrec.Bitmap.Width, glyphrec.Bitmap.Rows); GL.End(); GL.PopMatrix(); //Advance for the next character GL.Translate(glyphrec.Bitmap.Width, 0, 0); extent_x[c] = glyphrec.BitmapLeft + glyphrec.Bitmap.Width; GL.EndList(); sChars += "f:" + c.ToString() + "[w:" + glyphrec.Bitmap.Width.ToString() + "][h:" + glyphrec.Bitmap.Rows.ToString() + "]" + sError; }
public static Matrix4 CalcNext(GlyphSlot glyph) => Matrix4.CreateTranslation((glyph.Advance.X.ToSingle() / Sprite.TileSize) * 0.68f, glyph.Advance.Y.ToSingle() / Sprite.TileSize, 0);
public FreeTypeFont(uint pixelheight) { // initialize library Library lib = new Library(); //Face face = new Face(lib, "FreeSans.ottf"); Assembly assembly = Assembly.GetExecutingAssembly(); //string[] names = assembly.GetManifestResourceNames(); Stream resource_stream = assembly.GetManifestResourceStream("OpenTK_example_5.Resource.FreeSans.ttf"); MemoryStream ms = new MemoryStream(); resource_stream.CopyTo(ms); Face face = new Face(lib, ms.ToArray(), 0); face.SetPixelSizes(0, pixelheight); // set 1 byte pixel alignment GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1); // set texture unit GL.ActiveTexture(TextureUnit.Texture0); // Load first 128 characters of ASCII set for (uint c = 0; c < 128; c++) { try { // load glyph //face.LoadGlyph(c, LoadFlags.Render, LoadTarget.Normal); face.LoadChar(c, LoadFlags.Render, LoadTarget.Normal); GlyphSlot glyph = face.Glyph; FTBitmap bitmap = glyph.Bitmap; // create glyph texture int texObj = GL.GenTexture(); GL.BindTexture(TextureTarget.Texture2D, texObj); GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.R8, bitmap.Width, bitmap.Rows, 0, PixelFormat.Red, PixelType.UnsignedByte, bitmap.Buffer); // set texture parameters GL.TextureParameter(texObj, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear); GL.TextureParameter(texObj, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear); GL.TextureParameter(texObj, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge); GL.TextureParameter(texObj, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge); // add character Character ch = new Character(); ch.TextureID = texObj; ch.Size = new Vector2(bitmap.Width, bitmap.Rows); ch.Bearing = new Vector2(glyph.BitmapLeft, glyph.BitmapTop); ch.Advance = (int)glyph.Advance.X.Value; _characters.Add(c, ch); } catch (Exception ex) { Console.WriteLine(ex); } } // bind default texture GL.BindTexture(TextureTarget.Texture2D, 0); // set default (4 byte) pixel alignment GL.PixelStore(PixelStoreParameter.UnpackAlignment, 4); float[] vquad = { // x y u v 0.0f, -1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f }; // Create [Vertex Buffer Object](https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Buffer_Object) _vbo = GL.GenBuffer(); GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo); GL.BufferData(BufferTarget.ArrayBuffer, 4 * 6 * 4, vquad, BufferUsageHint.StaticDraw); // [Vertex Array Object](https://www.khronos.org/opengl/wiki/Vertex_Specification#Vertex_Array_Object) _vao = GL.GenVertexArray(); GL.BindVertexArray(_vao); GL.EnableVertexAttribArray(0); GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 4 * 4, 0); GL.EnableVertexAttribArray(1); GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 4 * 4, 2 * 4); GL.BindBuffer(BufferTarget.ArrayBuffer, 0); GL.BindVertexArray(0); }