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

使用Graphics.DrawImage放大图像时尺寸偏小及裁剪问题求助

问题:使用Graphics.DrawImage放大图像时尺寸不准确

当我使用Graphics.DrawImage绘制图像并将其放大到比原始尺寸更大的尺寸时,最终图像会略小一些。可以看到绘制在图像后方的绿色背景线条没有被完全覆盖:

图像放大后未完全覆盖绿色背景

绿色线条不属于图像本身,本应被图像完全覆盖。

补充信息:

  1. 我用传入DrawImage的同一个矩形绘制绿色背景,这个矩形的尺寸就是图像应该有的准确尺寸,所以参数值应该没问题。
  2. 绿色矩形是用FillRectangle绘制的,不需要画笔计算,而且我核对过参数值都是正确的,只有图像显示有偏差。
  3. 附上渲染代码:
// 缩放比例集合
public readonly float[] SCALES = { 0.05f, 0.1f, 0.125f, 0.25f, 0.333f, 0.5f, 0.667f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, 7.0f, 8.0f, 10.0f, 12.0f, 15.0f, 20.0f, 30.0f, 36.0f };
private int scaleIndex = 8;

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    float ScaleFactor = SCALES[scaleIndex];
    e.Graphics.InterpolationMode = ScaleFactor < 1 ? InterpolationMode.Bicubic : InterpolationMode.NearestNeighbor;
    
    Image im = Properties.Resources.TSprite0;
    for (int y = 0; y < TilesVertical; y++)
    {
        for (int x = 0; x < TilesHorizontal; x++)
        {
            float sx = im.Width * ScaleFactor;
            float sy = im.Height * ScaleFactor;
            Point p = new Point((int)(-scrollPosition.X + sx * x), (int)(-scrollPosition.Y + sy * y));
            Size s = new Size((int)Math.Floor(sx), (int)Math.Floor(sy));
            
            // 背景绿色矩形应该和图像尺寸一致
            e.Graphics.FillRectangle(Brushes.Lime, new Rectangle(p, s));
            e.Graphics.DrawImage(im, new Rectangle(p, s), 0, 0, 16, 16, GraphicsUnit.Pixel);
        }
    }
    im.Dispose();
}
  1. 另外发现图像像是左侧和顶部被裁剪,对比Photoshop放大效果和GDI+渲染效果:

Photoshop与GDI+放大效果对比


解决方案

嘿,这个问题我之前做GDI+绘图的时候也碰到过,本质是GDI+的像素对齐逻辑加上你代码里的尺寸处理方式导致的,咱们一步步来修复:

1. 别再用Math.Floor截断尺寸了

你现在把缩放后的浮点尺寸用Math.Floor转成整数,这会直接把小数部分砍掉——比如缩放后应该是20.9像素的宽度,直接变成20,自然会比背景矩形小一圈。不如直接用RectangleF来定义目标区域,让GDI+自己处理像素级的对齐,避免手动舍入的误差:

把原来的Point和Size替换成RectangleF,绘制的时候用对应的重载:

// 计算目标区域的浮点矩形,完全保留缩放后的精确尺寸
RectangleF destRect = new RectangleF(
    -scrollPosition.X + sx * x,
    -scrollPosition.Y + sy * y,
    sx,
    sy
);

// 绘制绿色背景用RectangleF版本的FillRectangle
e.Graphics.FillRectangle(Brushes.Lime, destRect);
// DrawImage也用RectangleF作为目标区域
e.Graphics.DrawImage(im, destRect, 0, 0, 16, 16, GraphicsUnit.Pixel);

2. 调整Graphics的像素偏移模式

GDI+默认的像素对齐方式有时候会“偷偷”偏移半个像素,导致图像看起来像是被裁剪。你可以在OnPaint开头加上这一行,强制更精确的像素对齐:

e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;

如果这个模式效果不好,也可以试试PixelOffsetMode.None,根据你的实际显示效果调整就行。

3. 明确设置Graphics的单位为像素

虽然默认是像素,但有时候某些场景下单位会被意外修改,明确设置一下更保险:

e.Graphics.PageUnit = GraphicsUnit.Pixel;

4. 用using自动管理图像资源

你现在手动调用im.Dispose(),虽然没问题,但用using语句会更可靠,能自动处理资源释放,避免忘记Dispose导致的内存泄漏:

using (Image im = Properties.Resources.TSprite0)
{
    // 原来的循环代码放这里面
}

整合后的完整代码

把这些修改整合起来,代码大概是这样:

public readonly float[] SCALES = { 0.05f, 0.1f, 0.125f, 0.25f, 0.333f, 0.5f, 0.667f, 0.75f, 1.0f, 1.25f, 1.5f, 1.75f, 2.0f, 2.5f, 3.0f, 3.5f, 4.0f, 4.5f, 5.0f, 6.0f, 7.0f, 8.0f, 10.0f, 12.0f, 15.0f, 20.0f, 30.0f, 36.0f };
private int scaleIndex = 8;

protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    
    // 配置Graphics的关键参数
    float ScaleFactor = SCALES[scaleIndex];
    e.Graphics.PageUnit = GraphicsUnit.Pixel;
    e.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality;
    e.Graphics.InterpolationMode = ScaleFactor < 1 ? InterpolationMode.Bicubic : InterpolationMode.NearestNeighbor;
    
    using (Image im = Properties.Resources.TSprite0)
    {
        float tileWidth = im.Width * ScaleFactor;
        float tileHeight = im.Height * ScaleFactor;
        
        for (int y = 0; y < TilesVertical; y++)
        {
            for (int x = 0; x < TilesHorizontal; x++)
            {
                RectangleF destRect = new RectangleF(
                    -scrollPosition.X + tileWidth * x,
                    -scrollPosition.Y + tileHeight * y,
                    tileWidth,
                    tileHeight
                );
                
                e.Graphics.FillRectangle(Brushes.Lime, destRect);
                // 这里用im.Width和im.Height代替硬编码的16,以后换图像不用改代码
                e.Graphics.DrawImage(im, destRect, 0, 0, im.Width, im.Height, GraphicsUnit.Pixel);
            }
        }
    }
}

测试一下这个修改后的代码,应该就能解决图像尺寸不准、边缘露出绿色背景的问题了,而且图像的裁剪感也会消失~


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 06:42:19