winforms 图形,DrawString在透明背景上呈现带有黑色轮廓的文本

a7qyws3x  于 2022-11-16  发布在  其他
关注(0)|答案(1)|浏览(194)

我正在使用Graphics.DrawString()在透明背景的位图上绘制字符串,当字体大小小于23毫米(字体是使用GraphicsUnit.Millimeter创建的)时,我得到了黑色轮廓的文本。
编码:

Bitmap bmp = new Bitmap(2000, 2000);
Color alpha = Color.FromArgb(0, 0, 0, 0);
for (int x = 0; x < bmp.Width; x++)
   for (int y = 0; y < bmp.Height; y++)
      bmp.SetPixel(x, y, alpha);

Graphics g = Graphics.FromImage(bmp);
g.InterpolationMode = InterpolationMode.HighQualityBicubic;
g.SmoothingMode = SmoothingMode.HighQuality;

Font labelFont = new Font("Cascadia Mono SemiBold", 23/*22*/, FontStyle.Regular, GraphicsUnit.Millimeter);
Brush brush = new SolidBrush(Color.White);
g.DrawString("Some text", labelFont, brush, 200, 200);

23毫米-单位字体:

22毫米-单位字体:

我尝试使用TextRenderer,但它绘制的文本没有透明背景。

rslzwgfq

rslzwgfq1#

此处提供的代码存在多个问题:

  • 初始循环会产生反效果,原因有很多:
  • 尝试用透明色填充位图,但这已经是与新创建的位图关联的非颜色(Color.FromArb(0, 0, 0, 0)
  • 使用SetPixel()方法,这是执行此任务的最慢的工具
  • 如果需要,可以使用Graphics.Clear()方法用颜色填充位图,该方法调用本机GDI+函数来执行该任务
  • 在此上下文中设置InterpolationMode没有用,此属性选择用于缩放或旋转图像的算法
  • SmoothingMode属性选择用于消除锯齿的算法线条、曲线和填充区域的边缘。它不适用于字体的呈现,因此对绘制的文本没有影响。它适用于用GraphicsPath呈现的文本,因为文本被转换为曲线
  • 没有一个可释放对象(GraphicsFontBrush)是显式释放的,也没有用using语句声明的(这是非常糟糕的)。位图何时被释放还不清楚,但可能是使用它的代码的职责

若要指定Fonts的呈现模式,则使用TextRenderingHint属性。由于未指定该属性,因此使用Font的系统默认平滑,通常为ClearType。
关于此渲染形式,请参见中的注解:
Drawing a Long String on to a Bitmap results in Drawing Issues
ClearType使用像素内平滑(最初是为LCD屏幕设计的)将文本与背景混合;它对小字体特别有效。它不支持alpha颜色(至少在这里不支持)。
呈现文本的设备上下文(GDI+ MemoryBitmap)不使用或 * 理解 * 这种类型的提示(平滑),因此无法呈现的像素将填充为空颜色,通常显示为黑色
当ClearType提示填充小于一个像素时,带黑色的 * 轮廓 * 可能显示不同的字体大小(不仅仅是问题中报告的度量)
若要修复呈现,请删除混乱,指定合适的TextRenderingHint模式并正确声明所有可处置对象。
我没有包括位图,因为我不知道它是如何使用的。当然,它必须在某个时候被处置(非常重要,它分配非托管资源,垃圾收集器无法帮助您)

var bmp = new Bitmap(2000, 2000);

using (var g = Graphics.FromImage(bmp))
using (var font = new Font("Cascadia Mono SemiBold", 22, FontStyle.Regular, GraphicsUnit.Millimeter))
using (var brush = new SolidBrush(Color.White)) {
    g.CompositingQuality = CompositingQuality.HighQuality;
    g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
    g.DrawString("Some text", font, brush, 200, 200);
}

TextRenderingHint.AntiAliasGridFit在这里是合适的(另请参见链接的注解),字符是使用其反锯齿字形位图和提示(平滑)绘制的
TextRenderingHint.AntiAlias也可用于此上下文中(并且 * 成本 * 略低)
CompositingQuality.HighQuality在这里实际上并没有使用,因为没有在背景上渲染图像(这可能需要gamma校正),所以实际上没有任何合成,但也没有 cost。您可以保留它,以防您决定在某个时候在当前上绘制位图;或者简单地将其移除。
关于 *TextRenderer绘制不带透明背景的文本 *
这是不正确的。TextRendererGDI)当然可以呈现具有透明背景的文本,只是它不支持alpha颜色(在此上下文中)
如前所述,我们使用的是内存中的**GDI+**设备上下文
但是,如果您在不同的设备上下文(例如,控件的表面)中绘制相同的文本,则情况会发生变化。
另请注意,TextRenderer不能用于在打印时呈现文本(原因与上述相同)。
测试此代码,订阅PictureBox的Paint事件,同时添加背景图像(如果没有背景图像,结果不会更改,只是更明显)
Graphics.DrawString()用于使用半透明(ARGB)颜色渲染文本

TextFormatFlags flags = TextFormatFlags.Left | TextFormatFlags.Top;

private void someControl_Paint(object sender, PaintEventArgs e)
{
    using (var font = new Font("Segoe UI", 22, FontStyle.Regular, GraphicsUnit.Millimeter))
    using (var brush = new SolidBrush(Color.White)) {
        TextRenderer.DrawText(e.Graphics, "Some text", font, 
            new Rectangle(new Point(0, 10), pictureBox1.ClientSize), Color.White, Color.Transparent, flags);

        e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
        e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
        e.Graphics.DrawString("Some text", font, brush, 0, 130);
    }

    using (Font font1 = new Font("Segoe UI", 8.0f, FontStyle.Regular, GraphicsUnit.Millimeter))
    using (Font font2 = new Font("Segoe UI", 5.5f, FontStyle.Regular, GraphicsUnit.Millimeter))
    using (var brush = new SolidBrush(Color.FromArgb(100, Color.Black))) {
        e.Graphics.DrawString("← TextRenderer", font1, brush, 610, 70);
        e.Graphics.DrawString("ForeColor: White, BackColor: Transparent", font2, brush, 610, 110);
        e.Graphics.DrawString("← GDI+ Graphics", font1, brush, 610, 190);
        e.Graphics.DrawString("ForeColor: White, Hinting: AntiAliasGridFit", font2, brush, 610, 230);
    }
}

导致(放大):

相关问题