/// <summary> /// Gets a <see cref="FontBitmap"/>'s intance. /// </summary> /// <param name="font"></param> /// <param name="charSet"></param> /// <param name="drawBoundary"></param> /// <returns></returns> public static FontBitmap GetFontBitmap(this Font font, string charSet, bool drawBoundary = false) { var fontBitmap = new FontBitmap(); fontBitmap.GlyphFont = font; // 以下几步,不能调换先后顺序。 // Don't change the order in which these functions invoked. int singleCharWidth, singleCharHeight; PrepareInitialGlyphDict(fontBitmap, charSet, out singleCharWidth, out singleCharHeight); int width, height; PrepareFinalBitmapSize(fontBitmap, out width, out height); PrintBitmap(fontBitmap, singleCharWidth, singleCharHeight, width, height); if (drawBoundary) { using (var graphics = Graphics.FromImage(fontBitmap.GlyphBitmap)) { bool odd = false; foreach (GlyphInfo item in fontBitmap.GlyphInfoDictionary.Values) { graphics.DrawRectangle( odd ? Pens.Green : Pens.Blue, item.ToRectangle(1, 1, -2, -1)); odd = !odd; } graphics.DrawRectangle(Pens.Red, 0, 0, fontBitmap.GlyphBitmap.Width - 1, fontBitmap.GlyphBitmap.Height - 1); } } return(fontBitmap); }
/// <summary> /// prepare final bitmap's size. /// </summary> /// <param name="fontBitmap"></param> /// <param name="width">final bitmap's width.</param> /// <param name="height">final bitmap's height.</param> private static void PrepareFinalBitmapSize(FontBitmap fontBitmap, out int width, out int height) { int sideLength; int maxGlyphHeight = 0; { int totalWidth = 0; foreach (GlyphInfo item in fontBitmap.GlyphInfoDictionary.Values) { totalWidth += item.width + glyphInterval; if (maxGlyphHeight < item.height) { maxGlyphHeight = item.height; } } int area = totalWidth * maxGlyphHeight; sideLength = (int)Math.Ceiling(Math.Sqrt(area)); fontBitmap.GlyphHeight = maxGlyphHeight; } { int maxWidth = 0, maxHeight = 0; int currentX = leftMargin, currentY = 0; foreach (var item in fontBitmap.GlyphInfoDictionary) { if (currentX + item.Value.width < sideLength) { currentX += item.Value.width + glyphInterval; } else// new row will start. { if (maxWidth < currentX) { maxWidth = currentX; } currentX = leftMargin; currentY += maxGlyphHeight; currentX += item.Value.width + glyphInterval; } } // if there is no glyph // or if there is only 1 line of glyph. if (maxWidth < currentX) { maxWidth = currentX; } if (currentX > leftMargin)// last line is not finished. { maxHeight = currentY + maxGlyphHeight; } else// last line is finished and new line is started and new line has no glyph. { maxHeight = currentY; } width = maxWidth; height = maxHeight; } }
private static FontTexture InitializeDefaultFontTexture() { Font font = new Font("Arial", 64.0f, FontStyle.Regular, GraphicsUnit.Pixel);// SystemFonts.DefaultFont; FontBitmap fontBitmap = font.GetFontBitmap(defaultCharSet); FontTexture fontTexture = fontBitmap.GetFontTexture(); return(fontTexture); }
/// <summary> /// Get glyph's size by graphics.MeasureString(). /// Then shrink glyph's size. /// xoffset now means offset in a single glyph's bitmap. /// </summary> /// <param name="fontBitmap"></param> /// <param name="charSet"></param> /// <param name="singleCharWidth"></param> /// <param name="singleCharHeight"></param> private static void PrepareInitialGlyphDict(FontBitmap fontBitmap, string charSet, out int singleCharWidth, out int singleCharHeight) { // Get glyph's size by graphics.MeasureString(). { int maxWidth = 0, maxHeight = 0; float fontSize = fontBitmap.GlyphFont.Size; using (var bitmap = new Bitmap(1, 1, PixelFormat.Format24bppRgb)) { using (Graphics graphics = Graphics.FromImage(bitmap)) { foreach (char c in charSet) { SizeF size = graphics.MeasureString(c.ToString(), fontBitmap.GlyphFont); var info = new GlyphInfo(0, 0, (int)size.Width, (int)size.Height); fontBitmap.GlyphInfoDictionary.Add(c, info); if (maxWidth < (int)size.Width) { maxWidth = (int)size.Width; } if (maxHeight < (int)size.Height) { maxHeight = (int)size.Height; } } } } singleCharWidth = maxWidth; singleCharHeight = maxHeight; } // shrink glyph's size. // xoffset now means offset in a single glyph's bitmap. { using (var bitmap = new Bitmap(singleCharWidth, singleCharHeight)) { using (var graphics = Graphics.FromImage(bitmap)) { Color clearColor = Color.FromArgb(0, 0, 0, 0); foreach (var item in fontBitmap.GlyphInfoDictionary) { if (item.Key == ' ' || item.Key == '\t' || item.Key == '\r' || item.Key == '\n') { continue; } graphics.Clear(clearColor); graphics.DrawString(item.Key.ToString(), fontBitmap.GlyphFont, Brushes.White, 0, 0); BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); RetargetGlyphRectangleInwards(data, item.Value); bitmap.UnlockBits(data); } } } } }
/// <summary> /// prepare final bitmap's size. /// </summary> /// <param name="fontBitmap"></param> /// <param name="width">final bitmap's width.</param> /// <param name="height">final bitmap's height.</param> private static void PrepareFinalBitmapSize(FontBitmap fontBitmap, out int width, out int height) { int sideLength; int maxGlyphHeight = 0; { int totalWidth = 0; foreach (GlyphInfo item in fontBitmap.GlyphInfoDictionary.Values) { totalWidth += item.width + glyphInterval; if (maxGlyphHeight < item.height) { maxGlyphHeight = item.height; } } int area = totalWidth * maxGlyphHeight; sideLength = (int)Math.Ceiling(Math.Sqrt(area)); fontBitmap.GlyphHeight = maxGlyphHeight; } { int maxWidth = 0, maxHeight = 0; int currentX = leftMargin, currentY = 0; foreach (KeyValuePair<char, GlyphInfo> item in fontBitmap.GlyphInfoDictionary) { if (currentX + item.Value.width < sideLength) { currentX += item.Value.width + glyphInterval; } else// new row will start. { if (maxWidth < currentX) { maxWidth = currentX; } currentX = leftMargin; currentY += maxGlyphHeight; currentX += item.Value.width + glyphInterval; } } // if there is no glyph // or if there is only 1 line of glyph. if (maxWidth < currentX) { maxWidth = currentX; } if (currentX > leftMargin)// last line is not finished. { maxHeight = currentY + maxGlyphHeight; } else// last line is finished and new line is started and new line has no glyph. { maxHeight = currentY; } width = maxWidth; height = maxHeight; } }
/// <summary> /// Get glyph's size by graphics.MeasureString(). /// Then shrink glyph's size. /// xoffset now means offset in a single glyph's bitmap. /// </summary> /// <param name="fontBitmap"></param> /// <param name="charSet"></param> /// <param name="singleCharWidth"></param> /// <param name="singleCharHeight"></param> private static void PrepareInitialGlyphDict(FontBitmap fontBitmap, string charSet, out int singleCharWidth, out int singleCharHeight) { // Get glyph's size by graphics.MeasureString(). { int maxWidth = 0, maxHeight = 0; float fontSize = fontBitmap.GlyphFont.Size; using (var bitmap = new Bitmap(1, 1, PixelFormat.Format24bppRgb)) { using (Graphics graphics = Graphics.FromImage(bitmap)) { foreach (char c in charSet) { SizeF size = graphics.MeasureString(c.ToString(), fontBitmap.GlyphFont); var info = new GlyphInfo(0, 0, (int)size.Width, (int)size.Height); fontBitmap.GlyphInfoDictionary.Add(c, info); if (maxWidth < (int)size.Width) { maxWidth = (int)size.Width; } if (maxHeight < (int)size.Height) { maxHeight = (int)size.Height; } } } } singleCharWidth = maxWidth; singleCharHeight = maxHeight; } // shrink glyph's size. // xoffset now means offset in a single glyph's bitmap. { using (var bitmap = new Bitmap(singleCharWidth, singleCharHeight)) { using (var graphics = Graphics.FromImage(bitmap)) { Color clearColor = Color.FromArgb(0, 0, 0, 0); foreach (KeyValuePair<char, GlyphInfo> item in fontBitmap.GlyphInfoDictionary) { if (item.Key == ' ' || item.Key == '\t' || item.Key == '\r' || item.Key == '\n') { continue; } graphics.Clear(clearColor); graphics.DrawString(item.Key.ToString(), fontBitmap.GlyphFont, Brushes.White, 0, 0); BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format24bppRgb); RetargetGlyphRectangleInwards(data, item.Value); bitmap.UnlockBits(data); } } } } }
/// <summary> /// Gets an instance of <see cref="FontTexture"/>. /// </summary> /// <param name="fontBitmap"></param> /// <param name="parameters"></param> /// <param name="mipmapFiltering"></param> /// <returns></returns> public static FontTexture GetFontTexture(this FontBitmap fontBitmap, SamplerParameters parameters = null, MipmapFilter mipmapFiltering = MipmapFilter.LinearMipmapLinear) { var texture = new Texture(fontBitmap.GlyphBitmap, parameters, mipmapFiltering); texture.Initialize(); var result = new FontTexture(); result.GlyphFont = fontBitmap.GlyphFont; result.GlyphHeight = fontBitmap.GlyphHeight; result.TextureSize = fontBitmap.GlyphBitmap.Size; result.GlyphInfoDictionary = fontBitmap.GlyphInfoDictionary; result.TextureObj = texture; return(result); }
/// <summary> /// Print the final bitmap that contains all glyphs. /// And also setup glyph's xoffset, yoffset. /// </summary> /// <param name="fontBitmap"></param> /// <param name="singleCharWidth"></param> /// <param name="singleCharHeight"></param> /// <param name="width"></param> /// <param name="height"></param> private static void PrintBitmap(FontBitmap fontBitmap, int singleCharWidth, int singleCharHeight, int width, int height) { var bitmap = new Bitmap(width, height); using (var graphics = Graphics.FromImage(bitmap)) { using (var glyphBitmap = new Bitmap(singleCharWidth, singleCharHeight)) { using (var glyphGraphics = Graphics.FromImage(glyphBitmap)) { int currentX = leftMargin, currentY = 0; Color clearColor = Color.FromArgb(0, 0, 0, 0); foreach (KeyValuePair <char, GlyphInfo> item in fontBitmap.GlyphInfoDictionary) { glyphGraphics.Clear(clearColor); glyphGraphics.DrawString(item.Key.ToString(), fontBitmap.GlyphFont, Brushes.White, 0, 0); // move to new line if this line is full. if (currentX + item.Value.width > width) { currentX = leftMargin; currentY += singleCharHeight; } // draw the current glyph. graphics.DrawImage(glyphBitmap, new Rectangle(currentX, currentY, item.Value.width, item.Value.height), item.Value.ToRectangle(), GraphicsUnit.Pixel); // move line cursor to next(right) position. item.Value.xoffset = currentX; item.Value.yoffset = currentY; // prepare for next glyph's position. currentX += item.Value.width + glyphInterval; } } } } fontBitmap.GlyphBitmap = bitmap; }
/// <summary> /// Print the final bitmap that contains all glyphs. /// And also setup glyph's xoffset, yoffset. /// </summary> /// <param name="fontBitmap"></param> /// <param name="singleCharWidth"></param> /// <param name="singleCharHeight"></param> /// <param name="width"></param> /// <param name="height"></param> private static void PrintBitmap(FontBitmap fontBitmap, int singleCharWidth, int singleCharHeight, int width, int height) { var bitmap = new Bitmap(width, height); using (var graphics = Graphics.FromImage(bitmap)) { using (var glyphBitmap = new Bitmap(singleCharWidth, singleCharHeight)) { using (var glyphGraphics = Graphics.FromImage(glyphBitmap)) { int currentX = leftMargin, currentY = 0; Color clearColor = Color.FromArgb(0, 0, 0, 0); foreach (KeyValuePair<char, GlyphInfo> item in fontBitmap.GlyphInfoDictionary) { glyphGraphics.Clear(clearColor); glyphGraphics.DrawString(item.Key.ToString(), fontBitmap.GlyphFont, Brushes.White, 0, 0); // move to new line if this line is full. if (currentX + item.Value.width > width) { currentX = leftMargin; currentY += singleCharHeight; } // draw the current glyph. graphics.DrawImage(glyphBitmap, new Rectangle(currentX, currentY, item.Value.width, item.Value.height), item.Value.ToRectangle(), GraphicsUnit.Pixel); // move line cursor to next(right) position. item.Value.xoffset = currentX; item.Value.yoffset = currentY; // prepare for next glyph's position. currentX += item.Value.width + glyphInterval; } } } } fontBitmap.GlyphBitmap = bitmap; }
/// <summary> /// Gets a <see cref="FontBitmap"/>'s intance. /// </summary> /// <param name="font">建议最大字体不超过32像素高度,否则可能无法承载所有Unicode字符。</param> /// <param name="charSet"></param> /// <param name="drawBoundary"></param> /// <returns></returns> public static FontBitmap GetFontBitmap(this Font font, string charSet, bool drawBoundary = false) { var fontBitmap = new FontBitmap(); fontBitmap.GlyphFont = font; //fontBitmap.GlyphInfoDictionary //fontBitmap.GlyphHeight //fontBitmap.GlyphBitmap // 先获取各个glyph的width和height fontBitmap.GlyphInfoDictionary = GetGlyphDict(font, charSet); // 获取所有glyph的面积之和,开方得到最终贴图的宽度textureWidth int textureWidth = GetTextureWidth(fontBitmap.GlyphInfoDictionary); // 以所有glyph中height最大的为标准高度 fontBitmap.GlyphHeight = GetGlyphHeight(fontBitmap.GlyphInfoDictionary, textureWidth); // 摆放glyph,得到x偏移和y偏移量,同时顺便得到最终贴图的高度textureHeight int textureHeight = LayoutGlyphs(fontBitmap.GlyphInfoDictionary, textureWidth, fontBitmap.GlyphHeight); // 根据glyph的摆放位置,生成最终的贴图 fontBitmap.GlyphBitmap = PaintTexture(textureWidth, textureHeight, fontBitmap.GlyphInfoDictionary, font); if (drawBoundary) { using (var graphics = Graphics.FromImage(fontBitmap.GlyphBitmap)) { bool odd = false; foreach (GlyphInfo item in fontBitmap.GlyphInfoDictionary.Values) { graphics.DrawRectangle( odd ? Pens.Green : Pens.Blue, item.ToRectangle(1, 1, -2, -1)); odd = !odd; } graphics.DrawRectangle(Pens.Red, 0, 0, fontBitmap.GlyphBitmap.Width - 1, fontBitmap.GlyphBitmap.Height - 1); } } //fontBitmap.GlyphBitmap.Save("aaaaaaaaaaaaa.png"); return(fontBitmap); }