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

WinForm中Graphics.DrawImage网格与Bitmap像素对齐异常求助

WinForm中Graphics.DrawImage网格偏移问题

在WinForm开发中使用Graphics.DrawImage时遇到以下问题:需要在可调整大小的Panel中显示Bitmap的指定区域(通过源矩形控制每次仅显示Bitmap的一部分),Bitmap像素因缩放被大幅放大后,为原始Bitmap像素绘制的网格在部分缩放级别下出现偏移。推测原因是DrawImage使用近似算法处理非整数的屏幕像素映射(比如单个Bitmap像素应占241.4个屏幕像素,无法精确实现),但无法复现该近似逻辑。需求是网格无需均匀间距,只需匹配DrawImage生成的实际像素尺寸差异。

测试代码

Bitmap CScopeBitmap;
RectangleF VisibleBitmapPortion = new RectangleF(0, 0, 10, 20.75f);

public Form1()
{
    InitializeComponent();
    CScopeBitmap = GenerateRandomBitmap();
    panel1.Size = new Size(2414, 1002);
}

private void panel1_Paint(object sender, PaintEventArgs e)
{
    e.Graphics.InterpolationMode = InterpolationMode.NearestNeighbor;
    e.Graphics.PixelOffsetMode = PixelOffsetMode.Half;
    e.Graphics.SmoothingMode = SmoothingMode.None;

    Rectangle destRect = panel1.ClientRectangle;
    RectangleF srcRect = VisibleBitmapPortion;

    e.Graphics.DrawImage(
        CScopeBitmap,
        destRect,
        srcRect,
        GraphicsUnit.Pixel
    );


    float width = VisibleBitmapPortion.Width;
    float height = VisibleBitmapPortion.Height;

    float scaleX = panel1.ClientRectangle.Width / width;
    float scaleY = panel1.ClientRectangle.Height / height;

    Pen gridPen = Pens.Black;

    for (int x = 0; x < width; x++)
    {
        float posX = (float)(x * scaleX);
        e.Graphics.DrawLine(gridPen, posX, 0, posX, panel1.Height);
    }

    for (int y = 0; y < height; y++)
    {
        float posY = (float)(y * scaleY);
        e.Graphics.DrawLine(gridPen, 0, posY, panel1.Width, posY);
    }
}

public static Bitmap GenerateRandomBitmap()
{
    Size bitmapSize = new Size(3000, 5005);
    int verticalResolution = 1;

    Bitmap bmp = new Bitmap(bitmapSize.Width, bitmapSize.Height, PixelFormat.Format24bppRgb);
    int width = bitmapSize.Width;
    int height = bitmapSize.Height;

    BitmapData bmpData = bmp.LockBits(
        new Rectangle(0, 0, width, height),
        ImageLockMode.WriteOnly,
        bmp.PixelFormat
    );

    int bytesPerPixel = 3; // 24bpp
    int stride = bmpData.Stride;
    int byteCount = stride * bmpData.Height;
    byte[] pixels = new byte[byteCount];

    Random rand = new Random();

    for (int x = 0; x < width; x++)
    {
        int y = 0;
        while (y < height)
        {
            byte r = (byte)rand.Next(256);
            byte g = (byte)rand.Next(256);
            byte b = (byte)rand.Next(256);

            int blockHeight = Math.Min(verticalResolution, height - y);

            for (int dy = 0; dy < blockHeight; dy++)
            {
                int rowStart = (y + dy) * stride;
                int i = rowStart + x * bytesPerPixel;

                pixels[i + 0] = b;
                pixels[i + 1] = g;
                pixels[i + 2] = r;
            }

            y += verticalResolution;
        }
    }

    Marshal.Copy(pixels, 0, bmpData.Scan0, byteCount);
    bmp.UnlockBits(bmpData);

    return bmp;
}

已尝试的解决方案

  • 使用Round、Ceiling、Floor等简单近似算法;
  • 实现累计误差的近似算法:
    int[] ComputeSteps(int count, double step)
    {
        int[] result = new int[count];
        int pos = 0;
        double err = 0.0;
        int baseStep = (int)Math.Floor(step);
        double frac = step - baseStep;
    
        for (int i = 0; i < count; i++)
        {
            int s = baseStep;
            err += frac;
            if (err >= 1.0)
            {
                s++;
                err -= 1.0;
            }
            pos += s;
            result[i] = pos;
        }
        return result;
    }
    
  • 手动创建变换矩阵后调用DrawImage和DrawLine:
    Matrix m = new Matrix();
    m.Translate(destRect.Left, destRect.Top);
    m.Scale(
        destRect.Width / srcRect.Width,
        destRect.Height / srcRect.Height
    );
    m.Translate(-srcRect.Left, -srcRect.Top);
    e.Graphics.Transform = m;
    
    e.Graphics.DrawImage(CScopeBitmap, new Point(0,0));
    
    
    Pen gridPen = new Pen(Color.Black, 1 / (destRect.Width / srcRect.Width));
    
    for (int x = 0; x<VisibleBitmapPortion.Width; x++)
    {
        e.Graphics.DrawLine(gridPen, x, 0, x, panel1.Height);
    }
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 10:44:51