private void CopyBitmapToDestinationArea( Graphics.Color[] data, int dataRowSize, Vector2 destinationTopleft, FTBitmap ftBitmap ) { byte[] buffer = ftBitmap.BufferData; int dataOffset, bufferOffset, pitch = Util.Math.Abs(ftBitmap.Pitch); for (int row = 0; row < ftBitmap.Rows; row++) { dataOffset = (int)((destinationTopleft.Y + row) * dataRowSize + destinationTopleft.X); bufferOffset = row * pitch; for (int column = 0; column < ftBitmap.Width; column++, dataOffset++) { byte px = buffer[bufferOffset + column]; data[dataOffset] = new Graphics.Color(px, px, px, px); } } }
public GlyphBitmap(FTBitmap bitmap) { Pitch = bitmap.Pitch; Rows = bitmap.Rows; Width = bitmap.Width; PixelMode = bitmap.PixelMode; BufferData = Pitch == 0 ? new byte[0] : bitmap.BufferData; }
private static Bitmap CreateBitmap(FTBitmap ftbmp, uint charCode) { if (ftbmp.Rows > 0 && ftbmp.Width > 0) { return(FontBitmapConverter.Convert(ftbmp, Color.White, charCode)); } return(null); }
/// <summary> /// Renders a glyph with a given height, measured as a distance between two text lines in the device pixels.</param> /// </summary> public Glyph Render(char @char, int height) { if (lastHeight != height) { lastHeight = height; var pixelSize = (uint)Math.Abs( CalcPixelSize(face, fontHeightResolver?.Invoke(height) ?? height).Round() ); face.SetPixelSizes(pixelSize, pixelSize); } var glyphIndex = face.GetCharIndex(@char); if (glyphIndex == 0) { return(null); } face.LoadGlyph(glyphIndex, LoadFlags.Default, lcdSupported ? LoadTarget.Lcd : LoadTarget.Normal); face.Glyph.RenderGlyph(lcdSupported ? RenderMode.Lcd : RenderMode.Normal); FTBitmap bitmap = face.Glyph.Bitmap; var verticalOffset = height - face.Glyph.BitmapTop + face.Size.Metrics.Descender.Round(); var bearingX = (float)face.Glyph.Metrics.HorizontalBearingX; bool rgbIntensity = bitmap.PixelMode == PixelMode.Lcd || bitmap.PixelMode == PixelMode.VerticalLcd; var glyph = new Glyph { Pixels = bitmap.BufferData, RgbIntensity = rgbIntensity, Pitch = bitmap.Pitch, Width = rgbIntensity ? bitmap.Width / 3 : bitmap.Width, Height = bitmap.Rows, VerticalOffset = verticalOffset, ACWidths = new Vector2( bearingX, (float)face.Glyph.Metrics.HorizontalAdvance - (float)face.Glyph.Metrics.Width - bearingX ), }; // Iterate through kerning pairs foreach (var nextChar in KerningCharacters) { var nextGlyphIndex = face.GetCharIndex(nextChar); var kerning = (float)face.GetKerning(glyphIndex, nextGlyphIndex, KerningMode.Default).X; if (kerning != 0) { if (glyph.KerningPairs == null) { glyph.KerningPairs = new List <KerningPair>(); } glyph.KerningPairs.Add(new KerningPair { Char = nextChar, Kerning = kerning }); } } return(glyph); }
public FontTex CreateTexture(char c) { FontTex ft; if (Cache.TryGetValue(c, out ft)) { return(ft); } uint glyphIndex = FontFace.GetCharIndex(c); FontFace.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); FontFace.Glyph.RenderGlyph(RenderMode.Light); FTBitmap ftbmp = FontFace.Glyph.Bitmap; int fontW = (int)((float)FontFace.Glyph.Metrics.HorizontalAdvance); int fontH = (int)((float)FontFace.Glyph.Metrics.VerticalAdvance); if (ftbmp.Width > 0 && ftbmp.Rows > 0) { ft = FontTex.Create(ftbmp); ft.PosX = (int)((float)FontFace.Glyph.Metrics.HorizontalBearingX); if (ft.PosX < 0) { ft.PosX = 0; } ; float top = (float)FontFace.Size.Metrics.Ascender; float bottom = (float)(FontFace.Glyph.Metrics.Height - FontFace.Glyph.Metrics.HorizontalBearingY); int y = (int)(top - (float)FontFace.Glyph.Metrics.HorizontalBearingY); ft.PosY = y; ft.FontW = Math.Max(fontW, ft.ImgW); ft.FontH = (int)(top + bottom); } else { ft = FontTex.CreateSpace((int)FontFace.Glyph.Advance.X, (int)FontFace.Glyph.Advance.Y); ft.FontW = fontW; ft.FontH = fontH; } Cache.Add(c, ft); //ft.dump_b(); //Console.WriteLine(); return(ft); }
private static Bitmap FromMono(FTBitmap ftbmp, Color color) { var data = new byte[ftbmp.Rows * ftbmp.Width]; var bitmap = new Bitmap(ftbmp.Width, ftbmp.Rows, PixelFormat.Format32bppArgb); var rect = new Rectangle(0, 0, bitmap.Width, bitmap.Height); var data2 = bitmap.LockBits(rect, ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb); var bytes2 = new byte[Math.Abs(data2.Stride) * data2.Height]; for (var y = 0; y < ftbmp.Rows; y++) { for (var x = 0; x < ftbmp.Pitch; x++) { var v = ftbmp.BufferData[y * ftbmp.Pitch + x]; var num = x * 8; var row = y * ftbmp.Width + x * 8; var bits = 8; if ((ftbmp.Width - num) < 8) { bits = ftbmp.Width - num; } for (var i = 0; i < bits; i++) { var bit = v & (1 << (7 - i)); data[row + i] = (byte)bit; } } } for (var y = 0; y < bitmap.Height; y++) { for (var x = 0; x < bitmap.Width; x++) { var v = data[(y * bitmap.Width) + x]; if (v != 0) { bytes2[x * 4 + y * data2.Stride + 0] = color.B; bytes2[x * 4 + y * data2.Stride + 1] = color.G; bytes2[x * 4 + y * data2.Stride + 2] = color.R; bytes2[x * 4 + y * data2.Stride + 3] = color.A; } } } Marshal.Copy(bytes2, 0, data2.Scan0, bytes2.Length); bitmap.UnlockBits(data2); return(bitmap); }
private static Bitmap FromGray(FTBitmap ftbmp, Color color) { Bitmap bmp = new Bitmap(ftbmp.Width, ftbmp.Rows, PixelFormat.Format32bppArgb); for (var y = 0; y < bmp.Height; y++) { for (var x = 0; x < bmp.Width; x++) { var d = ftbmp.BufferData[y * bmp.Width + x]; if (d != 0) { bmp.SetPixel(x, y, Color.FromArgb(d, color.R, color.G, color.B)); } } } return(bmp); }
private static Pixmap ToPixmap(FTBitmap bitmap) { var bufferData = bitmap.BufferData; var size = new Size(bitmap.Width, bitmap.Rows); var pixmap = new Pixmap(PixelFormat.Rgba, size); for (int j = 0; j < size.Height; j++) { for (int i = 0; i < size.Width; i++) { int k = (i + bitmap.Width * j) * 4; pixmap.PixelData[k] = 255; pixmap.PixelData[k + 1] = 255; pixmap.PixelData[k + 2] = 255; pixmap.PixelData[k + 3] = bufferData[i + bitmap.Width * j]; } } return(pixmap); }
private void RenderIntoColorData(Color[] data, FTBitmap bitmap, Vector2 pos) { unsafe { byte *colors = (byte *)bitmap.Buffer; for (int y = 0; y < Math.Min(bitmap.Rows, TEXTURE_SIZE); y++) { for (int x = 0; x < Math.Min(bitmap.Width, TEXTURE_SIZE); x++) { if ((int)(((y + pos.Y) * TEXTURE_SIZE) + (x + pos.X)) < data.Length) { data[(int)(((y + pos.Y) * TEXTURE_SIZE) + (x + pos.X))] = Color.White * (colors[(y * bitmap.Pitch) + x] / 255f); } } } } }
/// <summary> /// Renders a <see cref="FTBitmap"/> to a <see cref="Texture2D"/>. /// </summary> /// <remarks> /// Size of the texture must be equal to or larger than size of the bitmap. /// The format of the bitmap must be <see cref="PixelMode.Gray"/>, and the format of the texture must be a 32-bit format /// (<see cref="SurfaceFormat.Bgr32"/>, <see cref="SurfaceFormat.Bgra32"/>, or <see cref="SurfaceFormat.Color"/>). /// </remarks> /// <param name="bitmap">The <see cref="FTBitmap"/> containing character image.</param> /// <param name="texture">The <see cref="Texture2D"/> to render to.</param> public static void RenderToTexture([NotNull] this FTBitmap bitmap, [NotNull] Texture2D texture) { Debug.Assert(texture.Width >= bitmap.Width); Debug.Assert(texture.Height >= bitmap.Rows); Debug.Assert(bitmap.PixelMode == PixelMode.Gray); var textureFormat = texture.Format; Debug.Assert(textureFormat == SurfaceFormat.Bgr32 | textureFormat == SurfaceFormat.Bgra32 || textureFormat == SurfaceFormat.Color); // Are pixel bytes in A-R-G-B order? var argb = textureFormat == SurfaceFormat.Color; var textureData = new uint[texture.Width * texture.Height]; var bitmapData = bitmap.BufferData; for (var j = 0; j < bitmap.Rows; j++) { var bitmapLineStart = j * bitmap.Pitch; var textureLineStart = j * texture.Width; for (var i = 0; i < bitmap.Width; i++) { var bitmapPixelIndex = bitmapLineStart + i; var texturePixelIndex = textureLineStart + i; var alpha = (uint)bitmapData[bitmapPixelIndex]; var color = alpha; if (argb) { textureData[texturePixelIndex] = color << 16 | color << 8 | color | alpha << 24; } else { textureData[texturePixelIndex] = color << 24 | color << 16 | color << 8 | alpha; } } } texture.SetData(textureData); }
Character GetCharacter(char c) { if (chars.ContainsKey(c)) { return(chars[c]); } //rendering character face.LoadChar(c, LoadFlags.Render, LoadTarget.Light); FTBitmap bitmap = face.Glyph.Bitmap; //calculating position if (x + bitmap.Width > mapSize) { y += ymax; x = 0; } if (y + bitmap.Rows > mapSize) { throw new Exception("out of memory string"); } if (ymax < bitmap.Rows) { ymax = bitmap.Rows; } charmap.AddSubImage(bitmap.Buffer, x, y, bitmap.Width, bitmap.Rows); Character ch = new Character(new Vector2(x / (float)mapSize, y / (float)mapSize), new Vector2(bitmap.Width, bitmap.Rows), new Vector2(face.Glyph.BitmapLeft, face.Glyph.BitmapTop), face.Glyph.Advance.X.Value); chars.Add(c, ch); x += bitmap.Width + 1; bitmap.Dispose(); return(ch); }
public unsafe GlyphTexture(GL gl, FTBitmap bitmap) : base() { TextureType = TextureType.Other; Size = new GlmSharp.uvec2((uint)bitmap.Width, (uint)bitmap.Rows); gl.PixelStore(GLEnum.UnpackAlignment, 1); gl.BindTexture(TextureTarget.Texture2D, ID); gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)GLEnum.ClampToEdge); gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)GLEnum.Linear); gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)GLEnum.ClampToEdge); gl.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)GLEnum.Linear); fixed(void *i = &bitmap.BufferData[0]) gl.TexImage2D(TextureTarget.Texture2D, 0, (int)InternalFormat.Red, (uint)bitmap.Width, (uint)bitmap.Rows, 0, PixelFormat.Red, PixelType.UnsignedByte, i); gl.PixelStore(GLEnum.UnpackAlignment, 4); }
/// <summary> /// Renders the given glyph. /// </summary> /// <param name="c">The character code (Unicode) of the character to render.</param> /// <param name="bitmapLeft"> /// The x-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the left border of the image /// to the leftmost pixel of the glyph within the rendered image. /// </param> /// <param name="bitmapTop"> /// The y-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the character's origin /// (base line) of the image to the topmost pixel of the glyph within the rendered image. /// </param> /// <returns> /// An image data structure containing an image of the given character. /// </returns> public IImageData RenderGlyph(uint c, out int bitmapLeft, out int bitmapTop) { _face.LoadChar(c, LoadFlags.Default, LoadTarget.Normal); _face.Glyph.RenderGlyph(RenderMode.Normal); FTBitmap bmp = _face.Glyph.Bitmap; byte[] pixelData = new byte[0]; // empty?? if (bmp.Width != 0 && bmp.Rows != 0) { pixelData = new byte[bmp.BufferData.Length]; Array.Copy(bmp.BufferData, pixelData, bmp.BufferData.Length); } ImageData ret = new ImageData(pixelData, bmp.Width, bmp.Rows, new ImagePixelFormat(ColorFormat.Intensity)); bitmapLeft = _face.Glyph.BitmapLeft; bitmapTop = _face.Glyph.BitmapTop; return(ret); }
private void CopyBitmap(FTBitmap bitmap, Color4[] pixels, int x, int y) { if (bitmap.PixelMode != PixelMode.Gray) { throw new System.Exception("Invalid pixel mode: " + bitmap.PixelMode); } if (bitmap.Pitch != bitmap.Width) { throw new System.Exception("Bitmap pitch doesn't match its width"); } var data = bitmap.BufferData; var t = 0; for (int i = 0; i < bitmap.Rows; i++) { int w = (y + i) * TextureSize + x; for (int j = 0; j < bitmap.Width; j++) { pixels[w++] = new Color4(255, 255, 255, data[t++]); } } }
public static Bitmap Convert(this FTBitmap ftbmp, Color color, uint charCode) { switch (ftbmp.PixelMode) { case PixelMode.Mono: return(FromMono(ftbmp, color)); case PixelMode.Gray4: throw new NotImplementedException(); case PixelMode.Gray: return(FromGray(ftbmp, color)); case PixelMode.Lcd: throw new NotImplementedException(); default: #if NET45 return(ftbmp.ToGdipBitmap()); #else throw new NotImplementedException(); #endif } }
private int renderCharacter(Face face, char character, int posX, int posY, int atlas, FontInfo info, Graphics graphics) { uint index = face.GetCharIndex(character); face.LoadGlyph(index, LoadFlags.Default, LoadTarget.Normal); face.Glyph.RenderGlyph(RenderMode.Normal); GlyphMetrics metrics = face.Glyph.Metrics; int width = metrics.Width.ToInt32() + metrics.HorizontalBearingX.ToInt32(); int xAdvance = metrics.HorizontalAdvance.ToInt32(); int yoffset = metrics.VerticalAdvance.ToInt32() - metrics.HorizontalBearingY.ToInt32(); int charHeight = metrics.Height.ToInt32(); if (face.Glyph.Bitmap.Width > 0) { FTBitmap ftbmp = face.Glyph.Bitmap; Bitmap copy = ftbmp.ToGdipBitmap(Color.White); graphics.DrawImageUnscaled(copy, posX + metrics.HorizontalBearingX.ToInt32(), posY); } info.addCharacter(new CharacterInfo(character, posX, posY, width, charHeight, xAdvance, yoffset, atlas)); return(width); }
/// <summary> /// Renders the given glyph. /// </summary> /// <param name="c">The character code (Unicode) of the character to render.</param> /// <param name="bitmapLeft"> /// The x-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the left border of the image /// to the leftmost pixel of the glyph within the rendered image. /// </param> /// <param name="bitmapTop"> /// The y-Bearing of the glyph on the bitmap (in pixels). The number of pixels from the character's origin /// (base line) of the image to the topmost pixel of the glyph within the rendered image. /// </param> /// <returns> /// An image data structure containing an image of the given character. /// </returns> public ImageData RenderGlyph(uint c, out int bitmapLeft, out int bitmapTop) { _face.LoadChar(c, LoadFlags.Default, LoadTarget.Normal); _face.Glyph.RenderGlyph(RenderMode.Normal); FTBitmap bmp = _face.Glyph.Bitmap; ImageData ret = new ImageData { Height = bmp.Rows, Width = bmp.Width, Stride = bmp.Width, PixelFormat = ImagePixelFormat.Intensity, }; if (!ret.IsEmpty) { ret.PixelData = new byte[bmp.BufferData.Length]; Array.Copy(bmp.BufferData, ret.PixelData, bmp.BufferData.Length); } bitmapLeft = _face.Glyph.BitmapLeft; bitmapTop = _face.Glyph.BitmapTop; return(ret); }
public Bitmap test_draw(string teststrings) { //teststrings = teststrings.Replace("\n" , ""); //teststrings = teststrings.Replace("\r", ""); Library library = new Library(); Face face = library.NewFace(fontName, 0); Bitmap bmp = new Bitmap((int)Math.Ceiling((double)image_width), (int)Math.Ceiling((double)image_height)); Graphics g = Graphics.FromImage(bmp); g.Clear(GlobalSettings.cBgColor); //g.Clear(Color.Black); int x = 0, y = 0; for (int i = 0; i < teststrings.ToCharArray().Length; i++) { string currentChar0 = teststrings.ToCharArray()[i].ToString(); uint glyphIndex = face.GetCharIndex(uchar2code(currentChar0)); if (this.fontHeight <= 14) { face.SetPixelSizes((uint)0, (uint)this.fontHeight); face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); face.Glyph.RenderGlyph(RenderMode.Normal); } else { face.SetCharSize(0, this.fontHeight, 0, 72); face.LoadGlyph(glyphIndex, LoadFlags.ForceAutohint, LoadTarget.Lcd); face.Glyph.RenderGlyph(RenderMode.Lcd); } //获取字符对齐 float left, right, top, bottom, FHT; int FHD, kx, ky; left = (float)face.Glyph.Metrics.HorizontalBearingX; right = (float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width; top = (float)face.Glyph.Metrics.HorizontalBearingY; bottom = (float)face.Glyph.Metrics.HorizontalBearingY + (float)face.Glyph.Metrics.Height; FHT = this.fontHeight; FHD = (int)Math.Ceiling(FHT); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - face.Glyph.BitmapTop)); //选择渲染模式(1倍 or 2倍) if (this.grender_mode == "freetype_nearestneighbor") { face.SetCharSize(0, this.fontHeight * 2, 0, 72); face.LoadGlyph(glyphIndex, LoadFlags.ForceAutohint, LoadTarget.Lcd); face.Glyph.RenderGlyph(RenderMode.Lcd); left = (float)face.Glyph.Metrics.HorizontalBearingX; right = (float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width; top = (float)face.Glyph.Metrics.HorizontalBearingY; bottom = (float)face.Glyph.Metrics.HorizontalBearingY + (float)face.Glyph.Metrics.Height; FHT = this.fontHeight; FHD = (int)Math.Ceiling(FHT); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - face.Glyph.BitmapTop)); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - (float)face.Glyph.Metrics.HorizontalBearingY)); FTBitmap ftbmp = face.Glyph.Bitmap; if (ftbmp.Width == 0) { x += this.tile_width; if (x + this.tile_width > this.image_width) { x = 0; y += this.tile_height; } continue; } Bitmap tmpBmp = ftbmp.ToGdipBitmap(this.penColor); tmpBmp = kPasteImage(tmpBmp, tile_width * 2, tile_height * 2, (int)face.Glyph.BitmapLeft, (int)Math.Round(((float)this.fontHeight * 2 - face.Glyph.BitmapTop))); Bitmap cBmp = kResizeImage(tmpBmp, tmpBmp.Width / 2, tmpBmp.Height / 2, System.Drawing.Drawing2D.InterpolationMode.NearestNeighbor); Bitmap nBmp = gray2alpha(cBmp); cBmp.Dispose(); g.DrawImageUnscaled(nBmp, x + GlobalSettings.relativePositionX, y + GlobalSettings.relativePositionY); nBmp.Dispose(); } else if (this.grender_mode == "freetype_HighQualityBicubic") { face.SetCharSize(0, this.fontHeight * 2, 0, 72); face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); face.Glyph.RenderGlyph(RenderMode.Normal); left = (float)face.Glyph.Metrics.HorizontalBearingX; right = (float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width; top = (float)face.Glyph.Metrics.HorizontalBearingY; bottom = (float)face.Glyph.Metrics.HorizontalBearingY + (float)face.Glyph.Metrics.Height; FHT = this.fontHeight; FHD = (int)Math.Ceiling(FHT); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - face.Glyph.BitmapTop)); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - (float)face.Glyph.Metrics.HorizontalBearingY)); FTBitmap ftbmp = face.Glyph.Bitmap; if (ftbmp.Width == 0) { x += this.tile_width; if (x + this.tile_width > this.image_width) { x = 0; y += this.tile_height; } continue; } Bitmap tmpBmp = ftbmp.ToGdipBitmap(this.penColor); tmpBmp = kPasteImage(tmpBmp, tile_width * 2, tile_height * 2, (int)face.Glyph.BitmapLeft, (int)Math.Round(((float)this.fontHeight * 2 - face.Glyph.BitmapTop))); Bitmap cBmp = kResizeImage(tmpBmp, tmpBmp.Width / 2, tmpBmp.Height / 2, System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic); Bitmap nBmp = gray2alpha(cBmp); cBmp.Dispose(); g.DrawImageUnscaled(nBmp, x + GlobalSettings.relativePositionX, y + GlobalSettings.relativePositionY); nBmp.Dispose(); } else if (this.grender_mode == "freetype_drawtwice") { face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); face.Glyph.RenderGlyph(RenderMode.Normal); left = (float)face.Glyph.Metrics.HorizontalBearingX; right = (float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width; top = (float)face.Glyph.Metrics.HorizontalBearingY; bottom = (float)face.Glyph.Metrics.HorizontalBearingY + (float)face.Glyph.Metrics.Height; FHT = this.fontHeight; FHD = (int)Math.Ceiling(FHT); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - face.Glyph.BitmapTop)); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - (float)face.Glyph.Metrics.HorizontalBearingY)); FTBitmap ftbmp = face.Glyph.Bitmap; if (ftbmp.Width == 0) { x += this.tile_width; if (x + this.tile_width > this.image_width) { x = 0; y += this.tile_height; } continue; } //这是一个临时描边的功能,还没有添加到UI if (GlobalSettings.bUseOutlineEffect == true) { Bitmap cBmp = ftbmp.ToGdipBitmap(this.penColor); face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); face.Glyph.Outline.Embolden(2); face.Glyph.RenderGlyph(RenderMode.Normal); left = (float)face.Glyph.Metrics.HorizontalBearingX; right = (float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width; top = (float)face.Glyph.Metrics.HorizontalBearingY; bottom = (float)face.Glyph.Metrics.HorizontalBearingY + (float)face.Glyph.Metrics.Height; FHT = this.fontHeight; FHD = (int)Math.Ceiling(FHT); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - face.Glyph.BitmapTop)); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - (float)face.Glyph.Metrics.HorizontalBearingY)); FTBitmap ftbmp1 = face.Glyph.Bitmap; Bitmap sBmp = ftbmp1.ToGdipBitmap(GlobalSettings.cShadowColor); Bitmap nBmp = gray2alpha(cBmp); //Bitmap s2Bmp = gray2alpha(sBmp); g.DrawImageUnscaled(sBmp, kx + GlobalSettings.relativePositionX - 1, ky + GlobalSettings.relativePositionY - 1);//draw twice g.DrawImageUnscaled(nBmp, kx + GlobalSettings.relativePositionX, ky + GlobalSettings.relativePositionY); cBmp.Dispose(); nBmp.Dispose(); sBmp.Dispose(); } else { Bitmap cBmp = ftbmp.ToGdipBitmap(this.penColor); face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); face.Glyph.RenderGlyph(RenderMode.Normal); left = (float)face.Glyph.Metrics.HorizontalBearingX; right = (float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width; top = (float)face.Glyph.Metrics.HorizontalBearingY; bottom = (float)face.Glyph.Metrics.HorizontalBearingY + (float)face.Glyph.Metrics.Height; FHT = this.fontHeight; FHD = (int)Math.Ceiling(FHT); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - face.Glyph.BitmapTop)); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - (float)face.Glyph.Metrics.HorizontalBearingY)); FTBitmap ftbmp1 = face.Glyph.Bitmap; Bitmap sBmp = ftbmp1.ToGdipBitmap(this.penColor); Bitmap nBmp = gray2alpha(cBmp); sBmp = gray2alpha(sBmp); g.DrawImageUnscaled(sBmp, kx + GlobalSettings.relativePositionX - 1, ky + GlobalSettings.relativePositionY - 1);//draw twice g.DrawImageUnscaled(nBmp, kx + GlobalSettings.relativePositionX, ky + GlobalSettings.relativePositionY); cBmp.Dispose(); nBmp.Dispose(); sBmp.Dispose(); } } else if (this.grender_mode == "freetype_nosmoothing") { face.SetPixelSizes((uint)0, (uint)this.fontHeight); face.LoadGlyph(glyphIndex, LoadFlags.Monochrome, LoadTarget.Mono); face.Glyph.RenderGlyph(RenderMode.Mono); left = (float)face.Glyph.Metrics.HorizontalBearingX; right = (float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width; top = (float)face.Glyph.Metrics.HorizontalBearingY; bottom = (float)face.Glyph.Metrics.HorizontalBearingY + (float)face.Glyph.Metrics.Height; FHT = this.fontHeight; FHD = (int)Math.Ceiling(FHT); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - face.Glyph.BitmapTop)); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - (float)face.Glyph.Metrics.HorizontalBearingY)); FTBitmap ftbmp = face.Glyph.Bitmap; if (ftbmp.Width == 0) { x += this.tile_width; if (x + this.tile_width > this.image_width) { x = 0; y += this.tile_height; } continue; } Bitmap cBmp = ftbmp.ToGdipBitmap(this.penColor); g.DrawImageUnscaled(cBmp, kx + GlobalSettings.relativePositionX, ky + GlobalSettings.relativePositionY); cBmp.Dispose(); } else { face.SetCharSize(0, this.fontHeight, 0, 72); if (GlobalSettings.iFontBold > (float)0) { //临时加粗face.Glyph.Outline.Embolden(0.4);值要小于1,不然很丑 face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); face.Glyph.Outline.Embolden(0.4); face.Glyph.RenderGlyph(RenderMode.Normal); left = (float)face.Glyph.Metrics.HorizontalBearingX; right = (float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width; top = (float)face.Glyph.Metrics.HorizontalBearingY; bottom = (float)face.Glyph.Metrics.HorizontalBearingY + (float)face.Glyph.Metrics.Height; FHT = this.fontHeight; FHD = (int)Math.Ceiling(FHT); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - face.Glyph.BitmapTop)); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - (float)face.Glyph.Metrics.HorizontalBearingY)); } FTBitmap ftbmp = face.Glyph.Bitmap; if (ftbmp.Width == 0) { x += this.tile_width; if (x + this.tile_width > this.image_width) { x = 0; y += this.tile_height; } continue; } if (GlobalSettings.bUseOutlineEffect == true) { Bitmap cBmp = ftbmp.ToGdipBitmap(this.penColor); face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); face.Glyph.Outline.Embolden(2); face.Glyph.RenderGlyph(RenderMode.Normal); left = (float)face.Glyph.Metrics.HorizontalBearingX; right = (float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width; top = (float)face.Glyph.Metrics.HorizontalBearingY; bottom = (float)face.Glyph.Metrics.HorizontalBearingY + (float)face.Glyph.Metrics.Height; FHT = this.fontHeight; FHD = (int)Math.Ceiling(FHT); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - face.Glyph.BitmapTop)); kx = x + face.Glyph.BitmapLeft; ky = (int)Math.Round((float)y + (FHD - (float)face.Glyph.Metrics.HorizontalBearingY)); FTBitmap ftbmp1 = face.Glyph.Bitmap; Bitmap sBmp = ftbmp1.ToGdipBitmap(GlobalSettings.cShadowColor); Bitmap nBmp = gray2alpha(cBmp); //Bitmap s2Bmp = gray2alpha(sBmp); g.DrawImageUnscaled(sBmp, kx + GlobalSettings.relativePositionX - 1, ky + GlobalSettings.relativePositionY - 1);//draw twice g.DrawImageUnscaled(nBmp, kx + GlobalSettings.relativePositionX, ky + GlobalSettings.relativePositionY); cBmp.Dispose(); nBmp.Dispose(); sBmp.Dispose(); } else { Bitmap cBmp = ftbmp.ToGdipBitmap(this.penColor); Bitmap nBmp = gray2alpha(cBmp); cBmp.Dispose(); g.DrawImageUnscaled(nBmp, kx + GlobalSettings.relativePositionX, ky + GlobalSettings.relativePositionY); nBmp.Dispose(); } } x += this.tile_width; if (x + this.tile_width > this.image_width) { x = 0; y += this.tile_height; } } g.Dispose(); library.Dispose(); return(bmp); }
/// <summary> /// Copies the contents of the <see cref="FTBitmap"/> to a GDI+ <see cref="Bitmap"/>. /// </summary> /// <returns>A GDI+ <see cref="Bitmap"/> containing this bitmap's data.</returns> public static Bitmap ToGdipBitmap(this FTBitmap ftBitmap) { return(ToGdipBitmap(ftBitmap, Color.Black)); }
public Table.XYWH[] GetXYWHTable(string fTextStrings) { Library library = new Library(); Face face = library.NewFace(fontName, 0); face.SetCharSize(0, this.fontHeight, 0, 72); List <Table.XYWH> tmp = new List <Table.XYWH>(); StringReader fReader = new StringReader(fTextStrings); int img_nums; int chars_per_page = (image_width / tile_width) * (image_height / tile_height); if (fTextStrings.Length < (chars_per_page)) { img_nums = 1; } else { img_nums = (fTextStrings.Length / chars_per_page); if ((fTextStrings.Length % chars_per_page) > 0) { img_nums = (fTextStrings.Length / chars_per_page) + 1; } } for (int i = 0; i < img_nums; i++) { int pos = 0; string currentString; if (i != img_nums - 1) { char[] buffer = new char[chars_per_page]; fReader.Read(buffer, pos, chars_per_page); currentString = new string(buffer); pos += currentString.Length; } else { currentString = fReader.ReadToEnd(); } currentString = currentString.Replace("\n", ""); currentString = currentString.Replace("\r", ""); int x = 0, y = 0; for (int n = 0; n < currentString.ToCharArray().Length; n++) { Table.XYWH currentXYWH = new Table.XYWH(); currentXYWH.x_pos = (uint)x; currentXYWH.y_pos = (uint)y; currentXYWH.page_num = (uint)i + 1; string currentChar0 = currentString.ToCharArray()[n].ToString(); uint char_code = uchar2code(currentChar0); uint glyphIndex = face.GetCharIndex(uchar2code(currentChar0)); currentXYWH.charid = char_code; //set charid face.LoadChar((uint)glyphIndex, LoadFlags.Render, LoadTarget.Lcd); face.LoadGlyph((uint)glyphIndex, LoadFlags.Render, LoadTarget.Lcd); face.Glyph.RenderGlyph(RenderMode.Lcd); FTBitmap ftbmp = face.Glyph.Bitmap; if (ftbmp.Width == 0) { currentXYWH.c_width = (uint)tile_width; } else { float advance = (float)face.Glyph.Metrics.HorizontalAdvance; if (advance >= (float)tile_width) { currentXYWH.c_width = (uint)tile_width; } else { currentXYWH.c_width = (uint)face.Glyph.BitmapLeft + (uint)((float)face.Glyph.Metrics.HorizontalBearingX + (float)GlobalSettings.relativePositionX + (float)face.Glyph.Metrics.Width); } } //currentXYWH.c_width = (uint)((float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width);//set c_width if (char_code == (uint)32) { currentXYWH.c_width = (uint)tile_width / 2 - 1; } else if ((char_code <= (uint)0x3000) && (char_code >= (uint)0x7e)) { currentXYWH.c_width = (uint)tile_width; } else if (char_code >= (uint)8000) { currentXYWH.c_width = (uint)tile_width; } currentXYWH.c_height = (uint)tile_height; tmp.Add(currentXYWH); x += this.tile_width; if (x + this.tile_width > this.image_width) { x = 0; y += this.tile_height; } ftbmp.Dispose(); } } return(tmp.ToArray()); }
public NISFont DrawT3BFontBitmap(char[] strings) { NISFont fnt_data = new NISFont(); Bitmap bmp = new Bitmap(Config.texture_width, Config.texture_height); Graphics g = Graphics.FromImage(bmp); g.Clear(Color.FromArgb(0x00000000)); int x = 0, y = 0; int tile_w = Config.fontWidth; int tile_h = Config.fontHeight; int relativePositionX = 1; int relativePositionY = -2; int font_height = Config.fontSize; Library library = new Library(); string facename = Config.ttfName; Face face = library.NewFace(facename, 0); float left, right, top, bottom, FHT; int FHD, kx, ky; foreach (char currentChar0 in strings) { uint charid = uchar2code(currentChar0.ToString()); face.SetCharSize(0, font_height, 0, 72); if (charid < 0x7f) { font_height = Config.fontSize - 2; face.SetCharSize(0, font_height, 0, 72); } else { font_height = Config.fontSize; } face.SetPixelSizes((uint)0, (uint)font_height); uint glyphIndex = face.GetCharIndex(charid); //Console.WriteLine(glyphIndex); face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Lcd); face.Glyph.Outline.Embolden(0.5); face.Glyph.RenderGlyph(RenderMode.Normal); FTBitmap ftbmp = face.Glyph.Bitmap; FTBitmap ftbmp2 = face.Glyph.Bitmap; left = (float)face.Glyph.Metrics.HorizontalBearingX; right = (float)face.Glyph.Metrics.HorizontalBearingX + (float)face.Glyph.Metrics.Width; top = (float)face.Glyph.Metrics.HorizontalBearingY; bottom = (float)face.Glyph.Metrics.HorizontalBearingY + (float)face.Glyph.Metrics.Height; FHT = font_height; FHD = (int)Math.Ceiling(FHT); kx = x + (int)Math.Round(left); ky = (int)Math.Round((float)y + (float)Math.Ceiling(FHT) - (float)top); if (ftbmp.Width == 0 || glyphIndex < 0x20) { Face face1 = library.NewFace(Config.baseName, 0); face1.SetCharSize(0, font_height, 0, 72); face1.SetPixelSizes((uint)0, (uint)font_height); glyphIndex = face1.GetCharIndex(charid); face1.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Lcd); face1.Glyph.Outline.Embolden(Fixed26Dot6.FromDouble(0.4)); face1.Glyph.RenderGlyph(RenderMode.Normal); left = (float)face1.Glyph.Metrics.HorizontalBearingX; right = (float)face1.Glyph.Metrics.HorizontalBearingX + (float)face1.Glyph.Metrics.Width; top = (float)face1.Glyph.Metrics.HorizontalBearingY; bottom = (float)face1.Glyph.Metrics.HorizontalBearingY + (float)face1.Glyph.Metrics.Height; FHT = font_height; FHD = (int)Math.Ceiling(FHT); kx = x + (int)Math.Round(left); ky = (int)Math.Round((float)y + (float)Math.Ceiling(FHT) - (float)top); ftbmp = face1.Glyph.Bitmap; ftbmp2 = face1.Glyph.Bitmap; } fnt_data.charvalues.Add(new XYWH((int)uchar2code(currentChar0.ToString()), x, y, tile_w, tile_h, 0)); if (ftbmp2.Width == 0) { x += tile_w; if (x + tile_w > Config.texture_width) { x = 0; y += tile_h; } continue; } Bitmap cBmp = ftbmp2.ToGdipBitmap(Color.White); g.DrawImageUnscaled(cBmp, kx + relativePositionX, ky + relativePositionY); g.DrawImageUnscaled(cBmp, kx + relativePositionX, ky + relativePositionY); cBmp.Dispose(); x += tile_w; if (x + tile_w > Config.texture_width) { x = 0; y += tile_h; } } fnt_data.bitmap = bmp; return(fnt_data); }
private unsafe void CopyFTBitmapToAtlas_LcdRGB(uint *targetPtr, int offsetX, int offsetY, int targetWidth, FTBitmap bmp, int width, int height) { var bmpPtr = (byte *)bmp.Buffer; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++, targetPtr++, bmpPtr += 3) { uint value = *(uint *)(bmpPtr) >> 8; * targetPtr = 0xFF000000 | value; } targetPtr += targetWidth - width; } }
private unsafe void CopyFTBitmapToAtlas_BGRA(uint *targetPtr, int offsetX, int offsetY, int targetWidth, FTBitmap bmp, int width, int height) { var bmpPtr = (uint *)bmp.Buffer; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++, targetPtr++, bmpPtr++) { uint value = *(uint *)(bmpPtr); //A | R | G | B *targetPtr = (value << 24) | (value >> 24) | (value << 8) & 0xFF0000 | (value >> 8) & 0xFF; } targetPtr += targetWidth - width; } }
private unsafe void CopyFTBitmapToAtlas_Gray(uint *targetPtr, int offsetX, int offsetY, int targetWidth, FTBitmap bmp, int width, int height) { var bmpPtr = (byte *)bmp.Buffer; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++, targetPtr++, bmpPtr++) { byte value = *bmpPtr; * targetPtr = (uint)(value << 24) | 0xFFFFFFu; } targetPtr += targetWidth - width; } }
private unsafe void CopyFTBitmapToAtlas_Gray4(uint *targetPtr, int offsetX, int offsetY, int targetWidth, FTBitmap bmp, int width, int height, int padding) { //TODO: implement var bmpPtr = (byte *)bmp.Buffer; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++, targetPtr++, bmpPtr++) { byte value = *bmpPtr; * targetPtr = (uint)(value << 24) | 0xFFFFFFu; //value > 0 ? 255<< 24: 0; } targetPtr += targetWidth - width; bmpPtr += padding; } }
private unsafe void CopyFTBitmapToAtlas_Mono(uint *targetPtr, int offsetX, int offsetY, int targetWidth, FTBitmap bmp, int width, int height) { var bmpPtr = (byte *)bmp.Buffer; int subIndex = 0; for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++, targetPtr++, subIndex++, bmpPtr++) { if ((((*bmpPtr) >> subIndex) & 0x1) != 0) { *targetPtr = 0xFFFFFFFF; } if (subIndex == 8) { subIndex = 0; } } targetPtr += targetWidth - width; } }
internal static Bitmap RenderString(Library library, Face face, string text, Color foreColor, Color backColor) { var measuredChars = new List <DebugChar>(); var renderedChars = new List <DebugChar>(); float penX = 0, penY = 0; float stringWidth = 0; // the measured width of the string float stringHeight = 0; // the measured height of the string float overrun = 0; float underrun = 0; float kern = 0; int spacingError = 0; bool trackingUnderrun = true; int rightEdge = 0; // tracking rendered right side for debugging // Bottom and top are both positive for simplicity. // Drawing in .Net has 0,0 at the top left corner, with positive X to the right // and positive Y downward. // Glyph metrics have an origin typically on the left side and at baseline // of the visual data, but can draw parts of the glyph in any quadrant, and // even move the origin (via kerning). float top = 0, bottom = 0; // Measure the size of the string before rendering it. We need to do this so // we can create the proper size of bitmap (canvas) to draw the characters on. for (int i = 0; i < text.Length; i++) { #region Load character char c = text[i]; // Look up the glyph index for this character. uint glyphIndex = face.GetCharIndex(c); // Load the glyph into the font's glyph slot. There is usually only one slot in the font. face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); // Refer to the diagram entitled "Glyph Metrics" at http://www.freetype.org/freetype2/docs/tutorial/step2.html. // There is also a glyph diagram included in this example (glyph-dims.svg). // The metrics below are for the glyph loaded in the slot. float gAdvanceX = (float)face.Glyph.Advance.X; // same as the advance in metrics float gBearingX = (float)face.Glyph.Metrics.HorizontalBearingX; //float gWidth = face.Glyph.Metrics.Width.ToSingle(); float gWidth = face.Glyph.Metrics.Width; var rc = new DebugChar(c, gAdvanceX, gBearingX, gWidth); #endregion #region Underrun // Negative bearing would cause clipping of the first character // at the left boundary, if not accounted for. // A positive bearing would cause empty space. underrun += -(gBearingX); if (stringWidth == 0) { stringWidth += underrun; } if (trackingUnderrun) { rc.Underrun = underrun; } if (trackingUnderrun && underrun <= 0) { underrun = 0; trackingUnderrun = false; } #endregion #region Overrun // Accumulate overrun, which coould cause clipping at the right side of characters near // the end of the string (typically affects fonts with slanted characters) if (gBearingX + gWidth > 0 || gAdvanceX > 0) { overrun -= Math.Max(gBearingX + gWidth, gAdvanceX); if (overrun <= 0) { overrun = 0; } } overrun += (float)(gBearingX == 0 && gWidth == 0 ? 0 : gBearingX + gWidth - gAdvanceX); // On the last character, apply whatever overrun we have to the overall width. // Positive overrun prevents clipping, negative overrun prevents extra space. if (i == text.Length - 1) { stringWidth += overrun; } rc.Overrun = overrun; // accumulating (per above) #endregion #region Top/Bottom // If this character goes higher or lower than any previous character, adjust // the overall height of the bitmap. float glyphTop = (float)face.Glyph.Metrics.HorizontalBearingY; float glyphBottom = (float)(face.Glyph.Metrics.Height - face.Glyph.Metrics.HorizontalBearingY); if (glyphTop > top) { top = glyphTop; } if (glyphBottom > bottom) { bottom = glyphBottom; } #endregion // Accumulate the distance between the origin of each character (simple width). stringWidth += gAdvanceX; rc.RightEdge = stringWidth; measuredChars.Add(rc); #region Kerning (for NEXT character) // Calculate kern for the NEXT character (if any) // The kern value adjusts the origin of the next character (positive or negative). if (face.HasKerning && i < text.Length - 1) { char cNext = text[i + 1]; kern = (float)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X; // sanity check for some fonts that have kern way out of whack if (kern > gAdvanceX * 5 || kern < -(gAdvanceX * 5)) { kern = 0; } rc.Kern = kern; stringWidth += kern; } #endregion } stringHeight = top + bottom; // If any dimension is 0, we can't create a bitmap if (stringWidth == 0 || stringHeight == 0) { return(null); } // Create a new bitmap that fits the string. Bitmap bmp = new Bitmap((int)Math.Ceiling(stringWidth), (int)Math.Ceiling(stringHeight)); trackingUnderrun = true; underrun = 0; overrun = 0; stringWidth = 0; using (var g = Graphics.FromImage(bmp)) { #region Set up graphics // HighQuality and GammaCorrected both specify gamma correction be applied (2.2 in sRGB) // https://msdn.microsoft.com/en-us/library/windows/desktop/ms534094(v=vs.85).aspx g.CompositingQuality = CompositingQuality.HighQuality; // HighQuality and AntiAlias both specify antialiasing g.SmoothingMode = SmoothingMode.HighQuality; // If a background color is specified, blend over it. g.CompositingMode = CompositingMode.SourceOver; g.Clear(backColor); #endregion // Draw the string into the bitmap. // A lot of this is a repeat of the measuring steps, but this time we have // an actual bitmap to work with (both canvas and bitmaps in the glyph slot). for (int i = 0; i < text.Length; i++) { #region Load character char c = text[i]; // Same as when we were measuring, except RenderGlyph() causes the glyph data // to be converted to a bitmap. uint glyphIndex = face.GetCharIndex(c); face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); face.Glyph.RenderGlyph(RenderMode.Normal); FTBitmap ftbmp = face.Glyph.Bitmap; float gAdvanceX = (float)face.Glyph.Advance.X; float gBearingX = (float)face.Glyph.Metrics.HorizontalBearingX; float gWidth = (float)face.Glyph.Metrics.Width; var rc = new DebugChar(c, gAdvanceX, gBearingX, gWidth); #endregion #region Underrun // Underrun underrun += -(gBearingX); if (penX == 0) { penX += underrun; } if (trackingUnderrun) { rc.Underrun = underrun; } if (trackingUnderrun && underrun <= 0) { underrun = 0; trackingUnderrun = false; } #endregion #region Draw glyph // Whitespace characters sometimes have a bitmap of zero size, but a non-zero advance. // We can't draw a 0-size bitmap, but the pen position will still get advanced (below). if ((ftbmp.Width > 0 && ftbmp.Rows > 0)) { // Get a bitmap that .Net can draw (GDI+ in this case). Bitmap cBmp = ftbmp.ToGdipBitmap(foreColor); rc.Width = cBmp.Width; rc.BearingX = face.Glyph.BitmapLeft; int x = (int)Math.Round(penX + face.Glyph.BitmapLeft); int y = (int)Math.Round(penY + top - (float)face.Glyph.Metrics.HorizontalBearingY); //Not using g.DrawImage because some characters come out blurry/clipped. (Is this still true?) g.DrawImageUnscaled(cBmp, x, y); rc.Overrun = face.Glyph.BitmapLeft + cBmp.Width - gAdvanceX; // Check if we are aligned properly on the right edge (for debugging) rightEdge = Math.Max(rightEdge, x + cBmp.Width); spacingError = bmp.Width - rightEdge; } else { rightEdge = (int)(penX + gAdvanceX); spacingError = bmp.Width - rightEdge; } #endregion #region Overrun if (gBearingX + gWidth > 0 || gAdvanceX > 0) { overrun -= Math.Max(gBearingX + gWidth, gAdvanceX); if (overrun <= 0) { overrun = 0; } } overrun += (float)(gBearingX == 0 && gWidth == 0 ? 0 : gBearingX + gWidth - gAdvanceX); if (i == text.Length - 1) { penX += overrun; } rc.Overrun = overrun; #endregion // Advance pen positions for drawing the next character. penX += (float)face.Glyph.Advance.X; // same as Metrics.HorizontalAdvance? penY += (float)face.Glyph.Advance.Y; rc.RightEdge = penX; spacingError = bmp.Width - (int)Math.Round(rc.RightEdge); renderedChars.Add(rc); #region Kerning (for NEXT character) // Adjust for kerning between this character and the next. if (face.HasKerning && i < text.Length - 1) { char cNext = text[i + 1]; kern = (float)face.GetKerning(glyphIndex, face.GetCharIndex(cNext), KerningMode.Default).X; if (kern > gAdvanceX * 5 || kern < -(gAdvanceX * 5)) { kern = 0; } rc.Kern = kern; penX += (float)kern; } #endregion } } bool printedHeader = false; if (spacingError != 0) { for (int i = 0; i < renderedChars.Count; i++) { //if (measuredChars[i].RightEdge != renderedChars[i].RightEdge) //{ if (!printedHeader) { DebugChar.PrintHeader(); } printedHeader = true; Debug.Print(measuredChars[i].ToString()); Debug.Print(renderedChars[i].ToString()); //} } string msg = string.Format("Right edge: {0,3} ({1}) {2}", spacingError, spacingError == 0 ? "perfect" : spacingError > 0 ? "space " : "clipped", face.FamilyName); System.Diagnostics.Debug.Print(msg); //throw new ApplicationException(msg); } return(bmp); }
private void Load() { //SizeMetrics fontSizeMetrics = Face.Size.Metrics; //Size glyphSlotSize = new Size(fontSizeMetrics.NominalWidth, fontSizeMetrics.Height.ToSingle()); /* * FontFaceRenderMap renderMap = new FontFaceRenderMap( * face, * glyphSlotSize, * fontSizeMetrics.NominalWidth, * fontSizeMetrics.NominalHeight, * fontSizeMetrics.Height.ToSingle(), * fontSizeMetrics.Ascender.ToSingle(), * fontSizeMetrics.Descender.ToSingle() * ); */ // adjust signs to respect Raccoon y-origin direction LineHeight = ConvertPxToEm(Face.Size.Metrics.Height.ToSingle(), Size); Ascender = -ConvertPxToEm(Face.Size.Metrics.Ascender.ToSingle(), Size); Descender = Math.Abs(ConvertPxToEm(Face.Size.Metrics.Descender.ToSingle(), Size)); UnderlinePosition = Math.Abs(Face.UnderlinePosition / (float)Face.UnitsPerEM); UnderlineThickness = Face.UnderlineThickness / (float)Face.UnitsPerEM; // prepare texture int sideSize = (int)(Util.Math.Ceiling(System.Math.Sqrt(Face.GlyphCount)) * GlyphSlotSize.Width), textureSideSize = Util.Math.CeilingPowerOfTwo(sideSize); if (Texture == null) { Texture = new Graphics.Texture(textureSideSize, textureSideSize); } else if (Texture.Width != textureSideSize || Texture.Height != textureSideSize) { Texture.Dispose(); Texture = new Graphics.Texture(textureSideSize, textureSideSize); } // prepare texture data copying glyphs bitmaps Graphics.Color[] textureData = new Graphics.Color[textureSideSize * textureSideSize]; Vector2 glyphPosition = Vector2.Zero; uint charCode = Face.GetFirstChar(out uint glyphIndex); while (glyphIndex != 0) { Face.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); Face.Glyph.RenderGlyph(RenderMode.Normal); FTBitmap ftBitmap = Face.Glyph.Bitmap; if (ftBitmap != null && ftBitmap.Width > 0 && ftBitmap.Rows > 0) { if (ftBitmap.PixelMode != PixelMode.Gray) { throw new System.NotImplementedException("Supported PixelMode formats are: Gray"); } // tests if glyph bitmap actually fits on current row #if TIGHT_PACK_FACE_RENDER_MAP if (glyphPosition.X + ftBitmap.Width >= textureSideSize) { glyphPosition.Y += GlyphSlotSize.Height; glyphPosition.X = 0; } #else if (glyphPosition.X + GlyphSlotSize.Width >= textureSideSize) { glyphPosition.Y += GlyphSlotSize.Height; glyphPosition.X = 0; } #endif CopyBitmapToDestinationArea(textureData, textureSideSize, glyphPosition, ftBitmap); RegisterGlyph( charCode, new Rectangle( glyphPosition, new Size(ftBitmap.Width, ftBitmap.Rows) ), -ConvertPxToEm(Face.Glyph.Metrics.HorizontalBearingX.ToDouble(), Size), -ConvertPxToEm(Face.Glyph.Metrics.HorizontalBearingY.ToDouble(), Size), ConvertPxToEm(Face.Glyph.Metrics.Width.ToDouble(), Size), ConvertPxToEm(Face.Glyph.Metrics.Height.ToDouble(), Size), ConvertPxToEm(Face.Glyph.Advance.X.ToDouble(), Size), ConvertPxToEm(Face.Glyph.Advance.Y.ToDouble(), Size) ); // advance to next glyph area #if TIGHT_PACK_FACE_RENDER_MAP glyphPosition.X += ftBitmap.Width; #else glyphPosition.X += GlyphSlotSize.Width; #endif } else { RegisterGlyph( charCode, Rectangle.Empty, -ConvertPxToEm(Face.Glyph.Metrics.HorizontalBearingX.ToDouble(), Size), -ConvertPxToEm(Face.Glyph.Metrics.HorizontalBearingY.ToDouble(), Size), ConvertPxToEm(Face.Glyph.Metrics.Width.ToDouble(), Size), ConvertPxToEm(Face.Glyph.Metrics.Height.ToDouble(), Size), ConvertPxToEm(Face.Glyph.Advance.X.ToDouble(), Size), ConvertPxToEm(Face.Glyph.Advance.Y.ToDouble(), Size) ); } charCode = Face.GetNextChar(charCode, out glyphIndex); } // render glyphs Texture.SetData(textureData); }
public Texture2D RenderString(Face font, string text, Color4 foreColor, Color4 backColor, bool wrapText) { LineMetrics[] metrics = formatText(font, text, wrapText); float maxWidth = 0f, maxHeight = 0f, totalHeight = 0f; for (int lineIndex = 0; lineIndex < metrics.Length; lineIndex++) { LineMetrics lineMetric = metrics[lineIndex]; maxWidth = (lineMetric.Width > maxWidth) ? lineMetric.Width : maxWidth; maxHeight = (lineMetric.Height > maxHeight) ? lineMetric.Height : maxHeight; //Check is to ensure we don't have additional extra space accumulating beneath the texture for large Heights (commas being an example) totalHeight += (lineIndex < metrics.Length - 1) ? lineMetric.BaseHeight + LineSpacing : lineMetric.Height; metrics[lineIndex] = lineMetric; } //If any dimension is 0, we can't create a bitmap if (maxWidth <= 0 || totalHeight <= 0) { return(null); } //Create a new bitmap that fits the string. Bitmap bmp = new Bitmap((int)Math.Ceiling(maxWidth), (int)Math.Ceiling(totalHeight)); using (var g = Graphics.FromImage(bmp)) { #region Rendering Code g.CompositingQuality = CompositingQuality.HighQuality; g.SmoothingMode = SmoothingMode.HighQuality; g.CompositingMode = CompositingMode.SourceOver; g.Clear((Color)backColor); //Draw the string into the bitmap. float lineOffset = 0f; for (int lineIndex = 0; lineIndex < metrics.Length; lineIndex++) { LineMetrics lineMetrics = metrics[lineIndex]; //float xOffset = (maxWidth - lineMetrics.Width) / 2f; //Centered float xOffset = 0f; //float xOffset = maxWidth - lineMetrics.Width - 20f; float penX = 0f, penY = 0f; for (int i = 0; i < lineMetrics.Characters.Count; i++) { var cm = lineMetrics.Characters[i]; char c = cm.Character; uint glyphIndex = font.GetCharIndex(c); font.LoadGlyph(glyphIndex, LoadFlags.Default, LoadTarget.Normal); font.Glyph.RenderGlyph(RenderMode.Normal); FTBitmap ftbmp = font.Glyph.Bitmap; //Underrun if (penX == 0) //First character { penX += -(cm.BearingX); } //We can't draw a 0-size bitmap, but the pen position will still get advanced. if (ftbmp.Width > 0 && ftbmp.Rows > 0) { using (Bitmap cBmp = ftbmp.ToGdipBitmap((Color)foreColor)) { int x = (int)Math.Round(penX + cm.BearingX + xOffset); int y = (int)Math.Round(penY + lineMetrics.Top - cm.BearingY + lineOffset); g.DrawImageUnscaled(cBmp, x, y); } } //Advance pen position for the next character penX += cm.AdvanceX + cm.Kern; } lineOffset += lineMetrics.BaseHeight + LineSpacing; } #endregion } Texture2D texture = Texture2D.CreateFromBitmap(bmp); bmp.Dispose(); return(texture); }
/// <summary> /// Copies the contents of the <see cref="FTBitmap"/> to a GDI+ <see cref="Bitmap"/>. /// </summary> /// <param name="color">The color of the text.</param> /// <returns>A GDI+ <see cref="Bitmap"/> containing this bitmap's data with a transparent background.</returns> public static Bitmap ToGdipBitmap(this FTBitmap b, Color color) { if (b.IsDisposed) { throw new ObjectDisposedException("FTBitmap", "Cannot access a disposed object."); } if (b.Width == 0 || b.Rows == 0) { throw new InvalidOperationException("Invalid image size - one or both dimensions are 0."); } //TODO deal with negative pitch switch (b.PixelMode) { case PixelMode.Mono: { Bitmap bmp = new Bitmap(b.Width, b.Rows, PixelFormat.Format1bppIndexed); var locked = bmp.LockBits(new Rectangle(0, 0, b.Width, b.Rows), ImageLockMode.ReadWrite, PixelFormat.Format1bppIndexed); for (int i = 0; i < b.Rows; i++) { Copy(b.Buffer, i * b.Pitch, locked.Scan0, i * locked.Stride, locked.Stride); } bmp.UnlockBits(locked); ColorPalette palette = bmp.Palette; palette.Entries[0] = Color.FromArgb(0, color); palette.Entries[1] = Color.FromArgb(255, color); bmp.Palette = palette; return(bmp); } case PixelMode.Gray4: { Bitmap bmp = new Bitmap(b.Width, b.Rows, PixelFormat.Format4bppIndexed); var locked = bmp.LockBits(new Rectangle(0, 0, b.Width, b.Rows), ImageLockMode.ReadWrite, PixelFormat.Format4bppIndexed); for (int i = 0; i < b.Rows; i++) { Copy(b.Buffer, i * b.Pitch, locked.Scan0, i * locked.Stride, locked.Stride); } bmp.UnlockBits(locked); ColorPalette palette = bmp.Palette; for (int i = 0; i < palette.Entries.Length; i++) { float a = (i * 17) / 255f; palette.Entries[i] = Color.FromArgb(i * 17, (int)(color.R * a), (int)(color.G * a), (int)(color.B * a)); } bmp.Palette = palette; return(bmp); } case PixelMode.Gray: { Bitmap bmp = new Bitmap(b.Width, b.Rows, PixelFormat.Format8bppIndexed); var locked = bmp.LockBits(new Rectangle(0, 0, b.Width, b.Rows), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); for (int i = 0; i < b.Rows; i++) { Copy(b.Buffer, i * b.Pitch, locked.Scan0, i * locked.Stride, locked.Stride); } bmp.UnlockBits(locked); ColorPalette palette = bmp.Palette; for (int i = 0; i < palette.Entries.Length; i++) { float a = i / 255f; palette.Entries[i] = Color.FromArgb(i, (int)(color.R * a), (int)(color.G * a), (int)(color.B * a)); } //HACK There's a bug in Mono's libgdiplus requiring the "PaletteHasAlpha" flag to be set for transparency to work properly //See https://github.com/Robmaister/SharpFont/issues/62 if (!hasCheckedForMono) { hasCheckedForMono = true; isRunningOnMono = Type.GetType("Mono.Runtime") != null; if (isRunningOnMono) { monoPaletteFlagsField = typeof(ColorPalette).GetField("flags", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic); } } if (isRunningOnMono) { monoPaletteFlagsField.SetValue(palette, palette.Flags | 1); } bmp.Palette = palette; return(bmp); } case PixelMode.Lcd: { //TODO apply color int bmpWidth = b.Width / 3; Bitmap bmp = new Bitmap(bmpWidth, b.Rows, PixelFormat.Format24bppRgb); var locked = bmp.LockBits(new Rectangle(0, 0, bmpWidth, b.Rows), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); for (int i = 0; i < b.Rows; i++) { Copy(b.Buffer, i * b.Pitch, locked.Scan0, i * locked.Stride, locked.Stride); } bmp.UnlockBits(locked); return(bmp); } /*case PixelMode.VerticalLcd: * { * int bmpHeight = b.Rows / 3; * Bitmap bmp = new Bitmap(b.Width, bmpHeight, PixelFormat.Format24bppRgb); * var locked = bmp.LockBits(new Rectangle(0, 0, b.Width, bmpHeight), ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb); * for (int i = 0; i < bmpHeight; i++) * PInvokeHelper.Copy(Buffer, i * b.Pitch, locked.Scan0, i * locked.Stride, b.Width); * bmp.UnlockBits(locked); * * return bmp; * }*/ default: throw new InvalidOperationException("System.Drawing.Bitmap does not support this pixel mode."); } }