public TextGenerator(string path, string fontName, uint fontSize = 12) { if (_textPs == null) { LoadProgramShader(); } FontName = fontName; FontSize = fontSize; SharpFont.Library fontLib = new SharpFont.Library(); SharpFont.Face fontFace = new SharpFont.Face(fontLib, path); fontFace.SetPixelSizes(0, FontSize); GL.PixelStore(PixelStoreParameter.UnpackAlignment, 1); for (char c = (char)0x20; c <= (char)0x7E; c++) { fontFace.LoadChar(c, LoadFlags.Render, LoadTarget.Normal); Texture texture = Texture.CreateTexture( fontFace.Glyph.Bitmap.Width, fontFace.Glyph.Bitmap.Rows, PixelInternalFormat.R8, PixelFormat.Red, fontFace.Glyph.Bitmap.Buffer); Character character = new Character( texture.ID, new Vector2(fontFace.Glyph.Bitmap.Width, fontFace.Glyph.Bitmap.Rows), new Vector2(fontFace.Glyph.BitmapLeft, fontFace.Glyph.BitmapTop), (int)fontFace.Glyph.Advance.X); _characters.Add(c, character); } fontFace.Dispose(); fontLib.Dispose(); vao = VAO.Create(); vao.LinkPS(_textPs); vbo = VBO.Create <float>(vertices.Length, 4, "vertex"); indices = new IndicesCollection(); indices.Add(4, vertices.Length); vao.AddVBuff(vbo); vao.AddIndicesCollection(indices); }
private Glyph LoadGlyph(int codePoint, int characterSize, bool bold, float outlineThickness) { Glyph glyph = new Glyph(); if (_face == null) { return(null); } // Set the character size if (!SetCurrentSize(characterSize)) { return(glyph); } // Load the glyph corresponding to the code point var flags = LoadFlags.ForceAutohint; if (outlineThickness != 0) { flags |= LoadFlags.NoBitmap; } _face.LoadChar((uint)codePoint, flags, SharpFont.LoadTarget.Normal); // Retrieve the glyph SharpFont.Glyph glyphDesc = _face.Glyph.GetGlyph(); // Apply bold if necessary -- first technique using outline (highest quality) SharpFont.Fixed26Dot6 weight = new SharpFont.Fixed26Dot6(1); bool outline = glyphDesc.Format == SharpFont.GlyphFormat.Outline; if (outline) { if (bold) { SharpFont.OutlineGlyph outlineGlyph = glyphDesc.ToOutlineGlyph(); outlineGlyph.Outline.Embolden(weight); } if (outlineThickness != 0) { _stroker.Set((int)(outlineThickness * Fixed26Dot6.FromInt32(1).Value), StrokerLineCap.Round, StrokerLineJoin.Round, Fixed16Dot16.FromSingle(0)); // This function returning a new instance of Glyph // Because the pointer may changed upon applying stroke to the glyph glyphDesc = glyphDesc.Stroke(_stroker, false); } } // Convert the glyph to a bitmap (i.e. rasterize it) glyphDesc.ToBitmap(SharpFont.RenderMode.Normal, new FTVector26Dot6(0, 0), true); SharpFont.FTBitmap bitmap = glyphDesc.ToBitmapGlyph().Bitmap; // Apply bold if necessary -- fallback technique using bitmap (lower quality) if (!outline) { if (bold) { bitmap.Embolden(_library, weight, weight); } if (outlineThickness != 0) { Logger.Warning("Failed to outline glyph (no fallback available)"); } } // Compute the glyph's advance offset glyph.Advance = _face.Glyph.Metrics.HorizontalAdvance.ToSingle(); if (bold) { glyph.Advance += weight.ToSingle(); } int width = bitmap.Width; int height = bitmap.Rows; if ((width > 0) && (height > 0)) { // Leave a small padding around characters, so that filtering doesn't // pollute them with pixels from neighbors int padding = 1; // Get the glyphs page corresponding to the character size Page page = _pages[characterSize]; // Find a good position for the new glyph into the texture glyph.TexCoords = FindGlyphRectangle(page, width + 2 * padding, height + 2 * padding); var texRect = glyph.TexCoords; // Make sure the texture data is positioned in the center // of the allocated texture rectangle glyph.TexCoords = new Rectangle(texRect.X + padding, texRect.Y + padding, texRect.Width - 2 * padding, texRect.Height - 2 * padding); // Compute the glyph's bounding box float boundsX = (float)(_face.Glyph.Metrics.HorizontalBearingX); float boundsY = -(float)(_face.Glyph.Metrics.HorizontalBearingY); float boundsWidth = (float)(_face.Glyph.Metrics.Width) + outlineThickness * 2; float boundsHeight = (float)(_face.Glyph.Metrics.Height) + outlineThickness * 2; glyph.Bounds = new RectangleF(boundsX, boundsY, boundsWidth, boundsHeight); // Extract the glyph's pixels from the bitmap byte[] pixelBuffer = new byte[width * height * 4]; for (int i = 0; i < pixelBuffer.Length; i++) { pixelBuffer[i] = 255; } unsafe { byte *pixels = (byte *)bitmap.Buffer.ToPointer(); if (bitmap.PixelMode == SharpFont.PixelMode.Mono) { // Pixels are 1 bit monochrome values for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { // The color channels remain white, just fill the alpha channel int index = (x + y * width) * 4 + 3; pixelBuffer[index] = (byte)((((pixels[x / 8]) & (1 << (7 - (x % 8)))) > 0) ? 255 : 0); } pixels += bitmap.Pitch; } } else { // Pixels are 8 bits gray levels for (int y = 0; y < height; ++y) { for (int x = 0; x < width; ++x) { // The color channels remain white, just fill the alpha channel int index = (x + y * width) * 4 + 3; pixelBuffer[index] = pixels[x]; } pixels += bitmap.Pitch; } } } // Write the pixels to the texture int tx = glyph.TexCoords.Left; int ty = glyph.TexCoords.Top; int tw = glyph.TexCoords.Width; int th = glyph.TexCoords.Height; page.texture.Update(pixelBuffer, tx, ty, tw, th); } // Delete the FT glyph glyphDesc.Dispose(); return(glyph); }