C# 使用TextRenderer将文本绘制为位图

C# 使用TextRenderer将文本绘制为位图,c#,bitmap,gdi+,gdi,text-rendering,C#,Bitmap,Gdi+,Gdi,Text Rendering,我正在尝试使用TextRenderer(因为这有利于使用Graphics.DrawString)将一些文本绘制到位图,但是这会产生一些非常不希望的效果 示例代码 using (Bitmap buffer = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb)) { using (Graphics gra

我正在尝试使用
TextRenderer
(因为这有利于使用
Graphics.DrawString
)将一些文本绘制到
位图
,但是这会产生一些非常不希望的效果

示例代码

using (Bitmap buffer = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
{
    using (Graphics graphics = Graphics.FromImage(buffer))
    {
        // Produces the result below
        graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.ClearTypeGridFit;
        // Produces clean text, but I'd really like ClearType!
        graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
        TextRenderer.DrawText(graphics, "Hello World", this.Font, this.ClientRectangle, Color.Black);
    }
    e.Graphics.DrawImageUnscaled(buffer, this.ClientRectangle);
}
结果

我不知道怎么解决这个问题…救命

我不想使用
Graphics.DrawString
,因为我想要正确的GDI(与GDI+渲染相反)

注意:我刚刚意识到,我在这个问题上留下了一个很大的漏洞。一些人指出,在他们的机器上呈现ClearType文本工作得很好


我正在尝试将文本渲染为透明(Color.transparent)位图。如果我用纯色,一切都很好!(但我必须渲染为透明位图)。

在调用DrawText()时指定背景色:


您可以尝试为
图像图形设置
textrendinghint

using (Graphics graphics = Graphics.FromImage(buffer))
{
    graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAliasGridFit;
    TextRenderer.DrawText(graphics, "Hello World", this.Font, this.ClientRectangle, Color.Black);
}

问题是TextRenderer使用GDI渲染来渲染文本,clear type使用特殊的抗锯齿算法来平滑文本,不幸的是,当您尝试在位图设备上绘制时,它不起作用

要使其工作,您必须使用技巧,在内存中绘制,然后复制到位图:

  • 创建与显示设备上下文(IntPtr.Zero handle)兼容的内存位图缓冲区
  • 用纯色或图像填充缓冲区背景
  • 将文本渲染到内存位图中
  • 从内存位图复制到图像设备上下文(BitBlt)
  • 有关详细信息,请参阅此博客:

    示例代码,抱歉有点长:

    public static class Test
    {
        public static Image Render()
        {
            // create the final image to render into
            var image = new Bitmap(190, 30, PixelFormat.Format32bppArgb);
    
            // create memory buffer from desktop handle that supports alpha channel
            IntPtr dib;
            var memoryHdc = CreateMemoryHdc(IntPtr.Zero, image.Width, image.Height, out dib);
            try
            {
                // create memory buffer graphics to use for HTML rendering
                using (var memoryGraphics = Graphics.FromHdc(memoryHdc))
                {
                    // must not be transparent background 
                    memoryGraphics.Clear(Color.White);
    
                    // execute GDI text rendering
                    TextRenderer.DrawText(memoryGraphics, "Test string 1", new Font("Arial", 12), new Point(5, 5), Color.Red, Color.Wheat);
                    TextRenderer.DrawText(memoryGraphics, "Test string 2", new Font("Arial", 12), new Point(100, 5), Color.Red);
                }
    
                // copy from memory buffer to image
                using (var imageGraphics = Graphics.FromImage(image))
                {
                    var imgHdc = imageGraphics.GetHdc();
                    BitBlt(imgHdc, 0, 0, image.Width, image.Height, memoryHdc, 0, 0, 0x00CC0020);
                    imageGraphics.ReleaseHdc(imgHdc);
                }
            }
            finally
            {
                // release memory buffer
                DeleteObject(dib);
                DeleteDC(memoryHdc);
            }
    
            return image;
        }
    
        private static IntPtr CreateMemoryHdc(IntPtr hdc, int width, int height, out IntPtr dib)
        {
            // Create a memory DC so we can work off-screen
            IntPtr memoryHdc = CreateCompatibleDC(hdc);
            SetBkMode(memoryHdc, 1);
    
            // Create a device-independent bitmap and select it into our DC
            var info = new BitMapInfo();
            info.biSize = Marshal.SizeOf(info);
            info.biWidth = width;
            info.biHeight = -height;
            info.biPlanes = 1;
            info.biBitCount = 32;
            info.biCompression = 0; // BI_RGB
            IntPtr ppvBits;
            dib = CreateDIBSection(hdc, ref info, 0, out ppvBits, IntPtr.Zero, 0);
            SelectObject(memoryHdc, dib);
    
            return memoryHdc;
        }
    
        [DllImport("gdi32.dll")]
        public static extern int SetBkMode(IntPtr hdc, int mode);
    
        [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
        private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
    
        [DllImport("gdi32.dll")]
        private static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BitMapInfo pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);
    
        [DllImport("gdi32.dll")]
        public static extern int SelectObject(IntPtr hdc, IntPtr hgdiObj);
    
        [DllImport("gdi32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
    
        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
    
        [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
        public static extern bool DeleteDC(IntPtr hdc);
    
        [StructLayout(LayoutKind.Sequential)]
        internal struct BitMapInfo
        {
            public int biSize;
            public int biWidth;
            public int biHeight;
            public short biPlanes;
            public short biBitCount;
            public int biCompression;
            public int biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public int biClrUsed;
            public int biClrImportant;
            public byte bmiColors_rgbBlue;
            public byte bmiColors_rgbGreen;
            public byte bmiColors_rgbRed;
            public byte bmiColors_rgbReserved;
        }
    }
    

    尝试设置正确的
    图形。插值模式
    图形。平滑模式
    @HamletHakobyan,我至少尝试过,但都没有解决问题。当使用Graphics.DrawString时,这些属性似乎可以工作,但是由于TextRenderer是GDI文本绘制系统,因此这些属性似乎不起作用。这样可以工作,但文本的背景实际上是图像,因此,文本的背景色应该是透明的。不幸的是,由于GDI不支持alpha,所以使用Color.Transparent无法正确混合ClearType文本。对抗锯齿文本有效,但对ClearType无效。我想要的是后者。@series0ne为什么需要
    ClearType
    ?您所说的不适用于ClearType的
    是什么意思?您应该更新代码以显示您的尝试。默认情况下(至少在我的系统上),TextRenderingHint是ClearType。因此,我的代码省略了TextRenderingHint,因为默认值已经是ClearType,因此没有必要显式定义TextRenderingHint。我认为这可能会解决问题…@series0ne只是您不想使用
    TextRenderingHint
    ,我不知道为什么你的
    图形。TextRenderingHint
    有默认值
    ClearType
    ,我测试了打印默认值,它是
    SystemDefault
    TextRenderingHint=TextRenderingHint。ClearTypeGridFit
    也可以工作。嗨,我从BitBlt()调用中得到一个异常。我试图将CallingConvention=CallingConvention.Cdecl添加到导入中,但没有任何区别。消息:调用PInvoke函数“BitmapRecognition!”!BitmapRecognition.Text::BitBlt'已使堆栈不平衡。这可能是因为托管PInvoke签名与非托管目标签名不匹配。检查PInvoke签名的调用约定和参数是否与目标非托管签名匹配。哎呀,BitBlt的签名不正确,请将最后一个参数(dwRop)从long更改为int。抱歉。
    public static class Test
    {
        public static Image Render()
        {
            // create the final image to render into
            var image = new Bitmap(190, 30, PixelFormat.Format32bppArgb);
    
            // create memory buffer from desktop handle that supports alpha channel
            IntPtr dib;
            var memoryHdc = CreateMemoryHdc(IntPtr.Zero, image.Width, image.Height, out dib);
            try
            {
                // create memory buffer graphics to use for HTML rendering
                using (var memoryGraphics = Graphics.FromHdc(memoryHdc))
                {
                    // must not be transparent background 
                    memoryGraphics.Clear(Color.White);
    
                    // execute GDI text rendering
                    TextRenderer.DrawText(memoryGraphics, "Test string 1", new Font("Arial", 12), new Point(5, 5), Color.Red, Color.Wheat);
                    TextRenderer.DrawText(memoryGraphics, "Test string 2", new Font("Arial", 12), new Point(100, 5), Color.Red);
                }
    
                // copy from memory buffer to image
                using (var imageGraphics = Graphics.FromImage(image))
                {
                    var imgHdc = imageGraphics.GetHdc();
                    BitBlt(imgHdc, 0, 0, image.Width, image.Height, memoryHdc, 0, 0, 0x00CC0020);
                    imageGraphics.ReleaseHdc(imgHdc);
                }
            }
            finally
            {
                // release memory buffer
                DeleteObject(dib);
                DeleteDC(memoryHdc);
            }
    
            return image;
        }
    
        private static IntPtr CreateMemoryHdc(IntPtr hdc, int width, int height, out IntPtr dib)
        {
            // Create a memory DC so we can work off-screen
            IntPtr memoryHdc = CreateCompatibleDC(hdc);
            SetBkMode(memoryHdc, 1);
    
            // Create a device-independent bitmap and select it into our DC
            var info = new BitMapInfo();
            info.biSize = Marshal.SizeOf(info);
            info.biWidth = width;
            info.biHeight = -height;
            info.biPlanes = 1;
            info.biBitCount = 32;
            info.biCompression = 0; // BI_RGB
            IntPtr ppvBits;
            dib = CreateDIBSection(hdc, ref info, 0, out ppvBits, IntPtr.Zero, 0);
            SelectObject(memoryHdc, dib);
    
            return memoryHdc;
        }
    
        [DllImport("gdi32.dll")]
        public static extern int SetBkMode(IntPtr hdc, int mode);
    
        [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
        private static extern IntPtr CreateCompatibleDC(IntPtr hdc);
    
        [DllImport("gdi32.dll")]
        private static extern IntPtr CreateDIBSection(IntPtr hdc, [In] ref BitMapInfo pbmi, uint iUsage, out IntPtr ppvBits, IntPtr hSection, uint dwOffset);
    
        [DllImport("gdi32.dll")]
        public static extern int SelectObject(IntPtr hdc, IntPtr hgdiObj);
    
        [DllImport("gdi32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool BitBlt(IntPtr hdc, int nXDest, int nYDest, int nWidth, int nHeight, IntPtr hdcSrc, int nXSrc, int nYSrc, int dwRop);
    
        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);
    
        [DllImport("gdi32.dll", ExactSpelling = true, SetLastError = true)]
        public static extern bool DeleteDC(IntPtr hdc);
    
        [StructLayout(LayoutKind.Sequential)]
        internal struct BitMapInfo
        {
            public int biSize;
            public int biWidth;
            public int biHeight;
            public short biPlanes;
            public short biBitCount;
            public int biCompression;
            public int biSizeImage;
            public int biXPelsPerMeter;
            public int biYPelsPerMeter;
            public int biClrUsed;
            public int biClrImportant;
            public byte bmiColors_rgbBlue;
            public byte bmiColors_rgbGreen;
            public byte bmiColors_rgbRed;
            public byte bmiColors_rgbReserved;
        }
    }