使用 TextRenderer 将文本绘制到位图

问题描述 投票:0回答:4

我正在尝试使用

TextRenderer
(因为这有利于使用
Graphics.DrawString
)在
Bitmap
上绘制一些文本,但是它会产生一些非常不良的效果。

示例代码

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);
}

结果

enter image description here

我不确定如何解决这个问题...帮助!

我不想使用

Graphics.DrawString
,因为我想要正确的 GDI(而不是 GDI+)渲染。

注意:我刚刚意识到,我在这个问题上留下了一个漏洞。有些人指出渲染 ClearType 文本在他们的机器上运行良好......

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

c# bitmap gdi+ gdi text-rendering
4个回答
4
投票

在调用 DrawText() 时指定 BackColor:

TextRenderer.DrawText(graphics, "Hello World", this.Font, this.ClientRectangle, Color.Black, this.BackColor);

4
投票

您可以尝试为您的

TextRenderingHint
设置
Image Graphics
:

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

4
投票

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

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

  1. 创建与显示设备上下文兼容的内存位图缓冲区(IntPtr.Zero 句柄)
  2. 用纯色或图像填充缓冲区背景
  3. 将文本渲染到内存位图中
  4. 从内存位图复制到图像设备上下文(BitBlt)

有关详细信息,请参阅此博客:GDI 文本渲染到图像

示例代码,抱歉有点长:

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;
    }
}

0
投票

我知道这个问题几乎十年前就被问过,但显然没有答案可以解决 OP 问题。我自己刚刚找到了解决方案,我想这对陷入这个问题的每个人来说都是一个很大的帮助。

方法如下:诀窍是使用

BufferedGraphics
。通过 BufferedGraphics,您可以使用 TextRenderer 以完全透明的方式绘制字符串。
BufferedGraphics
创建一个 DeviceIndependentBitmap (DIB) 并使用该 DIB 生成一个 HDC,然后将其传递给 Graphics 类。这个 Graphics 实例非常适合 TextRenderer.DrawText。当然,您将需要一些 DllImports,特别是 gdi32.dll 的 GetCurrentObject,将 BufferedGraphics 的 Graphics 指针提供给通过调用 Graphics.GetHdc() 获得的本机设备上下文。通过 BufferedGraphics 图形绘制后,您一定不能调用 BufferedGraphics.Render() 方法,因为它将删除 HDC 指针并销毁当前在其中选择的位图。相反,获取指向本机 HDC 的指针,调用 gdi32 的 GetCurrentObject 并通过从 GetCurrentObject 获取的指针创建 Bitmap。

static Bitmap BitmapFromGraphics(this Graphics g, Rectangle r)
{
using (BufferedGraphics bg = BuffereGraphicsManager.Context.Allocate(g, r))
{
// make your drawing calls here
IntPtr hdc = bg.Graphics.GetHdc();
IntPtr bmp = GetCurrentObject(hdc, 7); // 7 is OBJ_BITMAP
Bitmap result = (Bitmap)Image.FromHBitmap(bmp);
bg.Graphics.ReleaseHdc(hdc)
return result;
}
}
© www.soinside.com 2019 - 2024. All rights reserved.