(.NET/C#/winforms) 在带有图形的表单上绘图时的伪像

发布于 2024-07-17 08:45:39 字数 1763 浏览 8 评论 0原文

我的目标是在窗体上移动使用 Graphics 对象的方法绘制的视觉元素,而不会出现闪烁或伪影(在 .NET 3.5 中)。 我可以通过使用自动双缓冲(将 Form 的 DoubleBuffered 属性设置为 true)或自己实现后台缓冲区来成功实现无闪烁运动。 然而,我正在努力寻找一种方法来使用这两种方法而不显示清晰可见的伪影。

当使用自动双缓冲时,页面撕裂效果很明显。 看起来好像后台缓冲区正在从上到下缓慢地、逐步地​​复制到表单中,在我的 60Hz LCD 上刷新多达 3 次。

当我自己实现双缓冲时(有关详细信息,请参阅代码块),看起来后台缓冲区复制到表单的速度足够快,不会发生页面撕裂。 然而有时会出现另一种类型的工件。 下面的代码允许您使用箭头键在表单上左右移动白色背景上的蓝色矩形,应该将效果再现为一系列水平白色带,有时出现在矩形的左右边缘就像它被移动一样。

public partial class Form1 : Form {
    int x;
    Bitmap buffer;

    public Form1() {
        InitializeComponent();
        buffer = new Bitmap(Width, Height);
    }

    private void Form1_Paint(object sender, PaintEventArgs e) {
        Graphics g = Graphics.FromImage(buffer);
        g.Clear(Color.FromArgb(255, 255, 255));
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        g.FillRectangle(Brushes.Blue, x, 0, 100, 500);
        e.Graphics.DrawImageUnscaled(buffer, 0, 0);
    }

    protected override void OnPaintBackground(PaintEventArgs pevent) {
        //Don't allow the background to paint
    }

    private void Form1_KeyDown(object sender, KeyEventArgs e) {
        const int scrollSpeed = 20;
        if(e.KeyData == Keys.Left) {
            x -= scrollSpeed;
            Refresh();
        }
        else {
            if(e.KeyData == Keys.Right) {
                x += scrollSpeed;
                Refresh();
            }
        }
    }
}

if(其他人能够重现该效果){
    我是不是做错了什么,也许会触发一些
    绘画代码中的竞争条件或者是
    这只是我必须的运行时行为
    住在一起?
}
否则{
    这可能是我的显卡的特殊性还是
    显示驱动程序有缺陷?
}

My objective is to move visual elements drawn using methods of a Graphics object across a Form with no flicker or artifacting (in .NET 3.5). I can successfully achieve flickerless movement by either using automatic double buffering (setting the Form's DoubleBuffered property to true) or implementing a back-buffer myself. I am however struggling to find a way to use either method without clearly visible artifacts showing.

When using automatic double buffering, a page-tearing effect is evident. It looks as though the back-buffer is being slowly, progressively copied to the form, from top to bottom, over as many as three refreshes of my 60Hz LCD.

When I implement double buffering myself (see code block for details) it looks as though the back buffer is copied to the form fast enough for no page-tearing to occur. However another type of artifact sometimes appears. The code below, which allows you to move a blue rectangle on a white background left and right on the form using the arrow keys, should reproduce the effect as a series of horizontal white bands that sometimes appear on the left and right edges of the rectangle just as it is moved.

public partial class Form1 : Form {
    int x;
    Bitmap buffer;

    public Form1() {
        InitializeComponent();
        buffer = new Bitmap(Width, Height);
    }

    private void Form1_Paint(object sender, PaintEventArgs e) {
        Graphics g = Graphics.FromImage(buffer);
        g.Clear(Color.FromArgb(255, 255, 255));
        g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
        g.FillRectangle(Brushes.Blue, x, 0, 100, 500);
        e.Graphics.DrawImageUnscaled(buffer, 0, 0);
    }

    protected override void OnPaintBackground(PaintEventArgs pevent) {
        //Don't allow the background to paint
    }

    private void Form1_KeyDown(object sender, KeyEventArgs e) {
        const int scrollSpeed = 20;
        if(e.KeyData == Keys.Left) {
            x -= scrollSpeed;
            Refresh();
        }
        else {
            if(e.KeyData == Keys.Right) {
                x += scrollSpeed;
                Refresh();
            }
        }
    }
}

if(others able to reproduce the effect) {
    Am I doing something wrong, perhaps triggering some
    kind of race condition in the painting code or is
    this just a behavior of the runtime that I have to
    live with?
}
else {
    Could it be a peculiarity of my display card or a
    bugged display driver?
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

花伊自在美 2024-07-24 08:45:39

重要的是要知道加速 2D 操作的显卡通常会加速 GDI,而不是 GDI+。 我认为使用 GDI 位图并直接使用 GDI 方法(例如通过 p/invoke)绘制它会使事情变得更快。

It may be important to know that graphics cards accelerating 2D operations usually accelerate GDI, not GDI+. I think that using a GDI bitmap and drawing it directly using a GDI methods (e.g. through p/invoke) would make things faster.

天生の放荡 2024-07-24 08:45:39

每当我完成双缓冲时,我都会确保 Paint 事件中没有渲染代码; 该事件中唯一的代码是将缓冲区复制到屏幕。

尝试将渲染代码移至其自己的函数中,然后使该区域(在本例中为表单)无效:

RenderScene()
Invalidate()

看看这是否有帮助。 为了更好地衡量,覆盖 OnResize 也可以调整缓冲区的大小。

Whenever I've done double-buffering, I've made sure to have no rendering code inside of the Paint event; the only code in the event was to copy from the buffer to the screen.

Try moving the rendering code into its own function, and then Invalidate the region (in this case the form):

RenderScene()
Invalidate()

See if that helps. For good measure, override OnResize to resize your buffer as well.

九命猫 2024-07-24 08:45:39

当按下一个键并释放时,它实际上对我来说看起来“很好”(尽管如果你按住键,有一点撕裂会在下一步移动时自行解决)。

我使用的是 Vista,配备 ATI x1300 和 Intel e6400。 也许处理速度或您的显卡有问题?

编辑: 如上所述,我同意其他人的观点,并且说如果您追求快速,GDI+ 不是最佳选择。 DirectX 或使用 Windows API 可能是可行的方法。

It actually looks "fine" to me when pressing a key and releasing (though if you hold the key down, there's a little bit of tearing that resolves itself on the next move).

I'm on Vista with an ATI x1300 and Intel e6400. Perhaps processing speed or your video card are an issue?

edit: The above being said, I'll agree with the others and say GDI+ is not the way to go if you're looking for fast. DirectX or using the Windows API may be the way to go.

肥爪爪 2024-07-24 08:45:39

除了 overslacked 所说的使用 Invalidate 之外,您也不需要抗锯齿来填充未应用变换的矩形。

Besides what overslacked said to use Invalidate, you also dont need antialiasing for filling a rectangle with no transform applied.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文