You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用MeasureCharacterRanges在图片绘制多色文本时位置异常问题

问题原因分析

直接从缩放图片创建Graphics时出现文本位置扭曲,核心原因是GDI+与GDI的DPI处理逻辑不兼容:

  • MeasureCharacterRanges属于GDI+ API,计算结果完全依赖当前Graphics对象的DPI设置;
  • TextRenderer.DrawText是GDI API的封装,默认使用系统屏幕DPI进行渲染,而非当前Graphics的DPI。

当你直接从缩放图片创建Graphics时,其DPI通常与屏幕DPI不一致(比如图片DPI为96,高分辨率屏幕DPI为144),导致两者的坐标计算出现偏差,最终文本位置扭曲。而中间Bitmap的Graphics对象,默认会继承Bitmap的DPI(或创建时显式匹配了DPI),让GDI+和GDI的DPI设置对齐,因此渲染正常。

解决方案

方案1:统一Graphics与TextRenderer的DPI设置

显式将Graphics的DPI设置为与TextRenderer一致(通常是屏幕DPI),同时强制TextRenderer使用当前Graphics的DPI:

private void AddText1(string txt, Graphics g, Rectangle rect)
{
    // 获取屏幕DPI并同步到当前Graphics
    using (Graphics screenG = Graphics.FromHwnd(IntPtr.Zero))
    {
        g.DpiX = screenG.DpiX;
        g.DpiY = screenG.DpiY;
    }

    TextFormatFlags flags = TextFormatFlags.HorizontalCenter | TextFormatFlags.VerticalCenter |
                TextFormatFlags.NoPadding | TextFormatFlags.NoClipping;

    using (StringFormat format = new StringFormat())
    {
        format.Alignment = StringAlignment.Center;
        format.LineAlignment = StringAlignment.Center;

        MatchCollection mc = Regex.Matches(txt, @"[^\s]+");
        CharacterRange[] ranges = mc.Cast<Match>().Select(m => new CharacterRange(m.Index, m.Length)).ToArray();
        format.SetMeasurableCharacterRanges(ranges);

        using (Font font = new Font("Times New Roman", 40, FontStyle.Regular, GraphicsUnit.Point))
        {
            Region[] regions = g.MeasureCharacterRanges(txt, font, rect, format);

            for (int i = 0; i < ranges.Length; i++)
            {
                Rectangle WordBounds = Rectangle.Round(regions[i].GetBounds(g));
                string word = txt.Substring(ranges[i].First, ranges[i].Length);
                // 强制TextRenderer遵循当前Graphics的变换和裁剪规则
                TextFormatFlags dpiAwareFlags = flags | TextFormatFlags.PreserveGraphicsClipping | TextFormatFlags.PreserveGraphicsTranslateTransform;
                if (word == "keyword")
                    TextRenderer.DrawText(g, word, font, WordBounds, Color.YellowGreen, dpiAwareFlags);
                else
                    TextRenderer.DrawText(g, word, font, WordBounds, Color.White, dpiAwareFlags);
            }
        }
    }
}

方案2:统一使用GDI+ API渲染

放弃TextRenderer.DrawText,改用GDI+的Graphics.DrawString,避免跨API的DPI差异:

private void AddText1(string txt, Graphics g, Rectangle rect)
{
    using (StringFormat format = new StringFormat())
    {
        format.Alignment = StringAlignment.Center;
        format.LineAlignment = StringAlignment.Center;

        MatchCollection mc = Regex.Matches(txt, @"[^\s]+");
        CharacterRange[] ranges = mc.Cast<Match>().Select(m => new CharacterRange(m.Index, m.Length)).ToArray();
        format.SetMeasurableCharacterRanges(ranges);

        using (Font font = new Font("Times New Roman", 40, FontStyle.Regular, GraphicsUnit.Point))
        {
            Region[] regions = g.MeasureCharacterRanges(txt, font, rect, format);

            for (int i = 0; i < ranges.Length; i++)
            {
                RectangleF WordBounds = regions[i].GetBounds(g);
                string word = txt.Substring(ranges[i].First, ranges[i].Length);
                using (SolidBrush brush = new SolidBrush(word == "keyword" ? Color.YellowGreen : Color.White))
                {
                    g.DrawString(word, font, brush, WordBounds, format);
                }
            }
        }
    }
}

方案3:创建Graphics时显式匹配目标图片DPI

如果目标图片有特定DPI,创建Graphics时直接设置对应参数:

// 示例:从图片创建Graphics时同步DPI
Bitmap targetImage = new Bitmap("scaled-image.png");
using (Graphics g = Graphics.FromImage(targetImage))
{
    g.DpiX = targetImage.HorizontalResolution;
    g.DpiY = targetImage.VerticalResolution;
    AddText1("Hello keyword world", g, new Rectangle(0, 0, targetImage.Width, targetImage.Height));
    targetImage.Save("output.png");
}

内容的提问来源于stack exchange,提问作者frek

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 08:10:38