Last active
April 15, 2025 20:36
-
-
Save ksasao/b4f5f7bef56e1cacddee4fd10204fec4 to your computer and use it in GitHub Desktop.
Draw color emoji to System.Drawing.Bitmap
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using SharpDX; | |
using SharpDX.Direct2D1; | |
using SharpDX.DirectWrite; | |
using SharpDX.Mathematics.Interop; | |
using d2 = SharpDX.Direct2D1; | |
using d3d = SharpDX.Direct3D11; | |
using dxgi = SharpDX.DXGI; | |
using wic = SharpDX.WIC; | |
using dw = SharpDX.DirectWrite; | |
using System.IO; | |
// Add System.Drawing and | |
// nuget SharpDX.Direct3D11, SharpDX.Direct2D1 | |
namespace EmojiTest | |
{ | |
public class Direct2DText : IDisposable | |
{ | |
// initialize the D3D device which will allow to render to image any graphics - 3D or 2D | |
d3d.Device defaultDevice; | |
d3d.Device1 d3dDevice; | |
dxgi.Device dxgiDevice; | |
Device d2dDevice; | |
wic.ImagingFactory2 imagingFactory = new wic.ImagingFactory2(); // initialize the WIC factory | |
// initialize the DeviceContext - it will be the D2D render target and will allow all rendering operations | |
DeviceContext d2dContext; | |
dw.Factory dwFactory; | |
// specify a pixel format that is supported by both D2D and WIC | |
PixelFormat d2PixelFormat = new d2.PixelFormat(dxgi.Format.R8G8B8A8_UNorm, d2.AlphaMode.Premultiplied); | |
// if in D2D was specified an R-G-B-A format - use the same for wic | |
Guid wicPixelFormat = wic.PixelFormat.Format32bppPRGBA; | |
TextFormat textFormat; | |
SolidColorBrush textBrush; | |
BitmapProperties1 d2dBitmapProps; | |
float dpi = 96; | |
public Direct2DText() | |
{ | |
defaultDevice = new SharpDX.Direct3D11.Device(SharpDX.Direct3D.DriverType.Hardware, | |
d3d.DeviceCreationFlags.VideoSupport | |
| d3d.DeviceCreationFlags.BgraSupport | |
| d3d.DeviceCreationFlags.None); // take out the Debug flag for better performance | |
d3dDevice = defaultDevice.QueryInterface<d3d.Device1>(); // get a reference to the Direct3D 11.1 device | |
dxgiDevice = d3dDevice.QueryInterface<dxgi.Device>(); // get a reference to DXGI device | |
d2dDevice = new d2.Device(dxgiDevice); // initialize the D2D device | |
imagingFactory = new wic.ImagingFactory2(); // initialize the WIC factory | |
d2dContext = new d2.DeviceContext(d2dDevice, d2.DeviceContextOptions.None); | |
dwFactory = new dw.Factory(); | |
d2dBitmapProps = new BitmapProperties1(d2PixelFormat, dpi, dpi, BitmapOptions.Target | BitmapOptions.CannotDraw); | |
} | |
public void SetFont(string fontName, float fontSize) | |
{ | |
if(textFormat != null) | |
{ | |
textFormat.Dispose(); | |
} | |
textFormat = new TextFormat(dwFactory, fontName, fontSize); | |
} | |
public void SetColor(System.Drawing.Color color) | |
{ | |
if(textBrush != null) | |
{ | |
textBrush.Dispose(); | |
} | |
textBrush = new SolidColorBrush(d2dContext, new RawColor4(color.R/255f,color.G/255f,color.B/255f, color.A/255)); | |
} | |
private int GetTextWidth(string text, int width, int height) | |
{ | |
// measure text width including white spaces | |
TextLayout tl0 = new TextLayout(dwFactory, "A", textFormat, width, height); | |
TextLayout tl1 = new TextLayout(dwFactory, text + "A", textFormat, width, height); | |
int result = (int)(tl1.Metrics.Width - tl0.Metrics.Width); | |
tl0.Dispose(); | |
tl1.Dispose(); | |
return result > width ? width : result; | |
} | |
public System.Drawing.Bitmap TextToBitmap(string text, int width, int height) | |
{ | |
int pixelWidth = GetTextWidth(text, width, height); | |
int pixelHeight = height; | |
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.BeginDraw(); | |
d2dContext.DrawTextLayout(new RawVector2(0, 0), textLayout, textBrush, DrawTextOptions.EnableColorFont); | |
d2dContext.EndDraw(); | |
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, ref 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; | |
} | |
#region IDisposable Support | |
private bool disposedValue = false; | |
protected virtual void Dispose(bool disposing) | |
{ | |
if (!disposedValue) | |
{ | |
if (disposing) | |
{ | |
d2dContext.Dispose(); | |
dwFactory.Dispose(); | |
imagingFactory.Dispose(); | |
d2dDevice.Dispose(); | |
dxgiDevice.Dispose(); | |
d3dDevice.Dispose(); | |
defaultDevice.Dispose(); | |
textBrush.Dispose(); | |
textFormat.Dispose(); | |
} | |
disposedValue = true; | |
} | |
} | |
public void Dispose() | |
{ | |
Dispose(true); | |
} | |
#endregion | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using EmojiTest; | |
using System; | |
using System.Collections.Generic; | |
using System.Drawing; | |
using System.Linq; | |
using System.Text; | |
using System.Threading.Tasks; | |
namespace EmojiToBitmap | |
{ | |
class Program | |
{ | |
static void Main(string[] args) | |
{ | |
int maxWidth = 500; | |
int maxHeight = 100; | |
Direct2DText dt = new Direct2DText(); | |
dt.SetFont("メイリオ", maxHeight / 96f * 72f); | |
dt.SetColor(Color.Black); | |
Bitmap bmp = dt.TextToBitmap("🍣ซูชิ ", maxWidth, maxHeight); | |
bmp.Save("output.png"); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment