public System.Drawing.Bitmap TextToBitmap(string text, out Size2F size, RawColor4 bgcolor, d2.TextAntialiasMode aamode, int maxWidth = 1000, int maxHeight = 1000) { var sz = GetTextSize(text, maxWidth, maxHeight); int pixelWidth = (int)(sz.Width * 2); int pixelHeight = (int)(sz.Height * 2); var d2dRenderTarget = new Bitmap1(d2dContext, new Size2(pixelWidth, pixelHeight), d2dBitmapProps); if (d2dContext.Target != null) { d2dContext.Target.Dispose(); } d2dContext.Target = d2dRenderTarget; // associate bitmap with the d2d context // Draw Text TextLayout textLayout = new TextLayout(dwFactory, text, textFormat, pixelWidth, pixelHeight); //d2dContext.TextRenderingParams = new RenderingParams(dwFactory, 1, 0, 0, PixelGeometry.Flat, renderingMode); d2dContext.TextAntialiasMode = aamode; d2dContext.BeginDraw(); d2dContext.Clear(bgcolor); d2dContext.DrawTextLayout(new RawVector2(0, 0), textLayout, textBrush, DrawTextOptions.EnableColorFont); d2dContext.EndDraw(); size = new Size2F(textLayout.Metrics.Width, textLayout.Metrics.Height); textLayout.Dispose(); // Copy to MemoryStream var stream = new MemoryStream(); var encoder = new wic.PngBitmapEncoder(imagingFactory); encoder.Initialize(stream); var bitmapFrameEncode = new wic.BitmapFrameEncode(encoder); bitmapFrameEncode.Initialize(); bitmapFrameEncode.SetSize(pixelWidth, pixelHeight); bitmapFrameEncode.SetPixelFormat(ref wicPixelFormat); // this is the trick to write D2D1 bitmap to WIC var imageEncoder = new wic.ImageEncoder(imagingFactory, d2dDevice); var imageParam = new wic.ImageParameters(d2PixelFormat, dpi, dpi, 0, 0, pixelWidth, pixelHeight); imageEncoder.WriteFrame(d2dRenderTarget, bitmapFrameEncode, imageParam); bitmapFrameEncode.Commit(); encoder.Commit(); imageEncoder.Dispose(); encoder.Dispose(); bitmapFrameEncode.Dispose(); d2dRenderTarget.Dispose(); // Convert To Bitmap byte[] data = stream.ToArray(); stream.Seek(0, SeekOrigin.Begin); var bmp = new System.Drawing.Bitmap(stream); stream.Dispose(); return(bmp); }
// todo: use this renderer; it's much better for monochrome fonts. for color there's still no better solution i know of. //// https://stackoverflow.com/questions/9080231/how-to-save-geometry-as-image //public static GenerateEmojiBitmapResults GenerateFontMap2(string fontName, int WidthAndHeight, string outputFileName) //{ // GlyphTypeface font; // if (File.Exists(fontName)) // { // font = new GlyphTypeface(new Uri(fontName)); // } // else // { // Typeface face = new Typeface(fontName); // face.TryGetGlyphTypeface(out font); // } // //int ColumnCount = 10; // //int MaxDrawCount = 30; // use int.MaxValue to draw them all // double fontSize = WidthAndHeight; // // the height of each cell has to include over/underhanging glyphs // SizeF cellSize = new SizeF((float)fontSize, (float)(fontSize * font.Height)); // var Glyphs = from glyphIndex in font.CharacterToGlyphMap.Values // select font.GetGlyphOutline(glyphIndex, fontSize, 1d); // int ColumnCount = (int)Math.Ceiling(Math.Sqrt(Glyphs.Count())); // // now create the visual we'll draw them to // DrawingVisual viz = new DrawingVisual(); // //int drawCount = -1; // using (DrawingContext dc = viz.RenderOpen()) // { // foreach (var g in Glyphs) // { // drawCount++; // if (g.IsEmpty()) continue; // don't draw the blank ones // // center horizontally in the cell // double xOffset = (drawCount % ColumnCount) * cellSize.Width + cellSize.Width / 2d - g.Bounds.Width / 2d; // // place the character on the baseline of the cell // double yOffset = (drawCount / ColumnCount) * cellSize.Height + fontSize * font.Baseline; // dc.PushTransform(new TranslateTransform(xOffset, yOffset)); // dc.DrawGeometry(System.Windows.Media.Brushes.Red, null, g); // dc.Pop(); // get rid of the transform // } // } // int RowCount = drawCount / ColumnCount; // if (drawCount % ColumnCount != 0) // RowCount++; // to include partial rows // int bitWidth = (int)Math.Ceiling((double)(cellSize.Width * ColumnCount)); // int bitHeight = (int)Math.Ceiling((double)(cellSize.Height * RowCount)); // RenderTargetBitmap bmp = new RenderTargetBitmap( // bitWidth, bitHeight, // 96, 96, // PixelFormats.Pbgra32); // bmp.Render(viz); // PngBitmapEncoder encoder = new PngBitmapEncoder(); // encoder.Frames.Add(BitmapFrame.Create(bmp)); // using (FileStream file = new FileStream(outputFileName, FileMode.Create)) // encoder.Save(file); //} public static GenerateEmojiBitmapResults GenerateEmojiBitmap(string fontName, int cellWidth, int cellHeight, float additionalScale, int shiftX, int shiftY, IEnumerable <EmojiInfo> codepointsToInclude, System.Drawing.Color[] backgroundPalette, System.Drawing.Color[] textPalette, float?aspectToleranceFromTarget, bool tryToFit, System.Windows.FontStyle fontStyle, System.Windows.FontWeight fontWeight, System.Windows.FontStretch fontStretch, bool strictGlyphChecking, d2.TextAntialiasMode aaMode) { System.Windows.Media.FontFamily fm = new System.Windows.Media.FontFamily(fontName); System.Windows.Media.Typeface tf = new System.Windows.Media.Typeface(fm, fontStyle, fontWeight, fontStretch); PetsciiMapgen.ProgressReporter pr = new PetsciiMapgen.ProgressReporter((ulong)codepointsToInclude.Count() * (ulong)backgroundPalette.Length * (ulong)textPalette.Length); if (!tf.TryGetGlyphTypeface(out System.Windows.Media.GlyphTypeface gtf)) { PetsciiMapgen.Log.WriteLine("!!!!!!!!!! FONT FAMILY HAS NO GLYPH MAP; you will end up with unsupported glyphs in the map."); // throw new Exception(); } // select codepoints to actually use PetsciiMapgen.Utils.ValueRangeInspector rangeX = new PetsciiMapgen.Utils.ValueRangeInspector(); PetsciiMapgen.Utils.ValueRangeInspector rangeY = new PetsciiMapgen.Utils.ValueRangeInspector(); PetsciiMapgen.Utils.ValueRangeInspector allAspects = new PetsciiMapgen.Utils.ValueRangeInspector(); PetsciiMapgen.Utils.ValueRangeInspector selectedAspects = new PetsciiMapgen.Utils.ValueRangeInspector(); int rejectedBecauseNotInTypeface = 0; int rejectedBecauseAspect = 0; int targetWidth = cellWidth; int targetHeight = cellHeight; float targetAspect = (float)targetWidth / targetHeight; EmojiTest.Direct2DText dt = new EmojiTest.Direct2DText(); dt.SetFont(fontName, targetHeight); var emoji = codepointsToInclude.Select(e => { pr.Visit(); GlyphData ret = new GlyphData(); ret.info = e; //ret.str = char.ConvertFromUtf32(cp); var sz = dt.GetTextSize(ret.info.str); ret.width = sz.Width; ret.height = sz.Height; rangeX.Visit(ret.width); rangeY.Visit(ret.height); allAspects.Visit(ret.width / ret.height); ret.scaleNeeded = 1; if (tryToFit) { if (ret.height > 0 && ret.width > 0) { float scaleNeededY = (float)targetHeight / ret.height;// factor to match target float scaleNeededX = (float)targetWidth / ret.width; ret.scaleNeeded = Math.Min(scaleNeededX, scaleNeededY); } else { ret.scaleNeeded = -1; } } return(ret); }) .Where(o => { if (o.info.forceInclude) { return(true); } if (o.info.str == "\r" || o.info.str == "\n") { return(false); } if (gtf != null) { if (!gtf.CharacterToGlyphMap.ContainsKey(o.info.cps[0])) { rejectedBecauseNotInTypeface++; if (strictGlyphChecking) { return(false); } } } float aspect = o.width / o.height; float da = Math.Abs(aspect - targetAspect); if (aspectToleranceFromTarget.HasValue && da > aspectToleranceFromTarget) { rejectedBecauseAspect++; return(false); } selectedAspects.Visit(aspect); return(true); }) .OrderBy(o => o.scaleNeeded).ToArray(); PetsciiMapgen.Log.WriteLine("EMOJI font encountered aspect ratios between {0}", allAspects); PetsciiMapgen.Log.WriteLine("Chars rejected because they don't have glyphs in this typeface: {0:N0}", rejectedBecauseNotInTypeface); PetsciiMapgen.Log.WriteLine("Chars rejected because aspect ratio out of range: {0:N0}", rejectedBecauseAspect); if (aspectToleranceFromTarget.HasValue) { PetsciiMapgen.Log.WriteLine("EMOJI font allowed aspect ratios between {0}", selectedAspects); } int totalCharCount = emoji.Count() * backgroundPalette.Length * textPalette.Length; int scaleChanges = 0; int columns = (int)Math.Ceiling(Math.Sqrt(totalCharCount)); int rows = columns;// we're aiming for square bitmap. int imgWidth = columns * targetWidth; int imgHeight = rows * targetHeight; List <GlyphData> fullEmoji = new List <GlyphData>(totalCharCount); if (emoji.Length < 2) { throw new Exception("NOt enough glyphs to generate anything meaningful."); } foreach (var backgroundColor in backgroundPalette) { foreach (var textColor in textPalette) { var bmp = new System.Drawing.Bitmap(imgWidth, imgHeight); using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(bmp)) { float lastScale = 0; //int iemoji = 0; var pr2 = new PetsciiMapgen.ProgressReporter((ulong)emoji.Count()); //foreach (var e in emoji) for (int iemoji = 0; iemoji < emoji.Length; ++iemoji) { var e = emoji[iemoji]; pr2.Visit(); if (e.scaleNeeded <= 0) { continue; } if (Math.Abs(lastScale - e.scaleNeeded) > 0.001) { scaleChanges++; dt.SetFont(fontName, targetHeight * e.scaleNeeded * additionalScale); } //SharpDX.Size2F sz; var n = new GlyphData(e); fullEmoji.Add(n); RawColor4 bg = new RawColor4(backgroundColor.R / 255.0f, backgroundColor.G / 255.0f, backgroundColor.B / 255.0f, 1); dt.SetColor(textColor); n.bmp = dt.TextToBitmap(n.info.str, out n.bmpSize, bg, aaMode); // offset where to blit from, so it's centered. int ox = (int)((n.bmpSize.Width - targetWidth) / 2); int oy = (int)((n.bmpSize.Height - targetHeight) / 2); n.blitSourcRect = new System.Drawing.Rectangle(ox - shiftX, oy - shiftY, targetWidth, targetHeight); n.bgColor = backgroundColor; n.textColor = textColor; //int y = iemoji / columns; //int x = iemoji % columns; //g.DrawImage(bmpChar, // new System.Drawing.Rectangle(x * targetWidth, y * targetHeight, targetWidth, targetHeight), // new System.Drawing.Rectangle(ox - shiftX, oy - shiftY, targetWidth, targetHeight), System.Drawing.GraphicsUnit.Pixel // ); //bmpChar.Dispose(); //iemoji++; } } } } PetsciiMapgen.Log.WriteLine("Scale changes: {0}", scaleChanges); PetsciiMapgen.Log.WriteLine("Total char count: {0}", fullEmoji.Count); PetsciiMapgen.Log.WriteLine("Image size to hold entire colored charset: {0}, {1}", imgWidth, imgHeight); GenerateEmojiBitmapResults rv = new GenerateEmojiBitmapResults(); //rv.bmp = bmp; rv.columns = columns; rv.rows = rows; rv.AllCells = fullEmoji.ToArray(); return(rv); }