【问题标题】:Dirty Rectangles脏矩形
【发布时间】:2010-09-09 18:02:08
【问题描述】:

在哪里可以找到关于实现一种用于计算“脏矩形”以最小化帧缓冲区更新的算法的参考资料?允许任意编辑并计算更新显示所需的最小“位 blit”操作集的显示模型。

【问题讨论】:

  • 对语言、平台和用例的澄清将使这个问题能够以更有用的方式得到回答。
  • 边界框标签可能会有所帮助。

标签: graphics framebuffer bounding-box dirtyrectangle


【解决方案1】:

构建包含所有需要重新绘制的区域的最小矩形:

  • 从空白区域开始(可能是设置为 0,0,0,0 的矩形 - 您可以将其检测为“无需更新”)

对于添加的每个脏区:

  • 规范化新区域(即确保左小于右,上小于下)
  • 如果脏矩形当前为空,请将其设置为提供的区域
  • 否则,将脏矩形的左上坐标设置为 {dirty,new} 的最小值,将右下坐标设置为 {dirty,new} 的最大值。

至少,Windows 会维护一个更新区域,其中包含通知的更改,以及由于窗口被遮挡和显示而需要进行的任何重新绘制。 region 是由许多可能不连续的矩形、多边形和椭圆组成的对象。您通过调用 InvalidateRect 告诉 Windows 需要重新绘制屏幕的一部分 - 对于更复杂的区域还有一个 InvalidateRgn 函数。如果您选择在下一个 WM_PAINT 消息到达之前进行一些绘制,并且您希望将其从脏区中排除,则可以使用 ValidateRect 和 ValidateRgn 函数。

当您开始使用 BeginPaint 进行绘画时,您提供了一个 PAINTSTRUCT,Windows 会在其中填充有关需要绘画的内容的信息。其中一个成员是包含无效区域的最小矩形。如果您想在有多个小的无效区域时最小化绘图,您可以使用 GetUpdateRgn 获取区域本身(您必须在 BeginPaint 之前调用它,因为 BeginPaint 将整个窗口标记为有效)。

我认为,由于在最初编写这些环境时在 Mac 和 X 上最小化绘图很重要,因此存在维护更新区域的等效机制。

【讨论】:

  • “最初编写这些环境的时间” - 我认为它一直很重要,而且将永远很重要,因为窗口管理器是您非常常用的半低级代码想尽快跑。
【解决方案2】:

您使用什么语言?在 Python 中,Pygame 可以为您做到这一点。使用 RenderUpdates Group 和一些具有 image 和 rect 属性的 Sprite 对象。

例如:

#!/usr/bin/env python
import pygame

class DirtyRectSprite(pygame.sprite.Sprite):
    """Sprite with image and rect attributes."""
    def __init__(self, some_image, *groups):
        pygame.sprite.Sprite.__init__(self, *groups)
        self.image = pygame.image.load(some_image).convert()
        self.rect = self.image.get_rect()
    def update(self):
        pass #do something here

def main():
    screen = pygame.display.set_mode((640, 480))
    background = pygame.image.load(open("some_bg_image.png")).convert()
    render_group = pygame.sprite.RenderUpdates()
    dirty_rect_sprite = DirtyRectSprite(open("some_image.png"))
    render_group.add(dirty_rect_sprite)

    while True:
        dirty_rect_sprite.update()
        render_group.clear(screen, background)
        pygame.display.update(render_group.draw(screen))

如果你不使用 Python+Pygame,我会这样做:

  • 创建一个更新()的 Sprite 类, move() 等方法设置一个“脏” 标志。
  • 为每个精灵保留一个矩形
  • 如果您的 API 支持更新 rect 列表,请在其 sprite 脏的 rect 列表中使用它。在 SDL 中,这是 SDL_UpdateRects。
  • 如果您的 API 不支持更新 rect 列表(我从来没有机会使用除 SDL 之外的任何东西,所以我不知道),请测试多次调用 blit 函数是否更快或一次有一个大矩形。我怀疑任何 API 使用一个大矩形会更快,但同样,除了 SDL,我没有使用任何其他东西。

【讨论】:

    【解决方案3】:

    听起来您需要的是每个要渲染到屏幕的形状的边界框。请记住,多边形的边界框可以定义为“左下”(最小点)和“右上”(最大点)。即最小点的 x 分量定义为多边形中每个点的所有 x 分量中的最小值。对 y 分量(在 2D 的情况下)和边界框的最大点使用相同的方法。

    如果每个多边形有一个边界框(又名“脏矩形”)就足够了,那么你就完成了。如果您需要一个整体的复合边界框,则适用相同的算法,只是您可以只用最小和最大点填充单个框。

    现在,如果您在 Java 中执行所有这些操作,则可以使用 getBound2D() method 直接获取 Area(您可以从任何 Shape 构造)的边界框。

    【讨论】:

      【解决方案4】:

      我最近刚刚编写了一个 Delphi 类来计算两个图像的差异矩形,并且对它的运行速度感到非常惊讶 - 速度足以在短时间内运行,并且在记录屏幕活动的鼠标/键盘消息之后运行。

      其工作原理的分步要点是:

      1. 通过矩形将图像细分为逻辑 12x12。

      2. 循环遍历每个像素,如果存在差异,我会告诉子矩形该像素属于哪个像素,其中一个像素存在差异以及位置差异。

      3. 每个子矩形都会记住它自己最左边、最上面、最右边和最下面的差异坐标。

      4. 一旦找到所有差异,我会遍历所有有差异的子矩形,如果它们彼此相邻,则从它们中形成更大的矩形,并使用最左边的,最上面的,这些子矩形的最右边和最底部的差异来制作我使用的实际差异矩形。

      这对我来说似乎工作得很好。如果您还没有实现自己的解决方案,请告诉我,如果您愿意,我会通过电子邮件将我的代码发送给您。同样到目前为止,我是 StackOverflow 的新用户,所以如果您欣赏我的回答,请投票。 :)

      【讨论】:

      • 回复有点晚,但“循环遍历每个像素”是一个很好的例子,说明不该做什么,实际上它违背了整个目的。显示合成器的概念是向它提供已更新的区域。速度提升来自于它能够消除重叠的矩形,并且还可以合并非矩形(多边形)的区域。这里的想法是尽可能避免使用像素。所以检查每个像素的变化正是人们想要避免的:)
      • 视情况而定。在我的情况下,我没有提前知道更新点的奢侈,我只有两张图像,所以我必须找到差异,没有办法绕过它。当然,如果您已经知道更新的点,那么您可以计算子矩形。请注意,OP 要求的是矩形而不是任意多边形。
      【解决方案5】:

      查看R-treequadtree 数据结构。

      【讨论】:

        【解决方案6】:

        Vexi 是一个参考实现。该类是org.vexi.util.DirtyList(Apache 许可证),用作生产系统的一部分,即经过彻底测试,并得到很好的评论。

        需要注意的是,当前的类描述有点不准确,“用于保存需要重新绘制的矩形区域列表的通用数据结构,具有智能合并功能。” 实际上确实如此目前不进行合并。因此,您可以认为这是一个基本的 DirtyList 实现,因为它只与dirty() 请求相交以确保没有重叠的脏区域。

        这个实现的一个细微差别是,区域不是使用 Rect 或其他类似的区域对象,而是存储在一个整数数组中,即在一维数组中的 4 个整数块中。这样做是为了提高运行时效率,尽管回想起来我不确定这是否有很多优点。 (是的,我实现了。)用 Rect 代替正在使用的数组块应该很简单。

        课程的目的是快速。使用 Vexi,脏可能每帧调用数千次,因此脏区域与脏请求的交集必须尽可能快。不超过 4 个数字比较用于确定两个区域的相对位置。

        由于缺少合并,它并不完全是最优的。虽然它确实可以确保脏/绘制区域之间没有重叠,但您最终可能会得到排列整齐的区域并且可以合并到更大的区域中 - 从而减少绘制调用的数量。

        代码 sn-p。完整代码online here.

        public class DirtyList {
        
            /** The dirty regions (each one is an int[4]). */
            private int[] dirties = new int[10 * 4]; // gets grown dynamically
        
            /** The number of dirty regions */
            private int numdirties = 0;
        
            ...
        
            /** 
             *  Pseudonym for running a new dirty() request against the entire dirties list
             *  (x,y) represents the topleft coordinate and (w,h) the bottomright coordinate 
             */
            public final void dirty(int x, int y, int w, int h) { dirty(x, y, w, h, 0); }
        
            /** 
             *  Add a new rectangle to the dirty list; returns false if the
             *  region fell completely within an existing rectangle or set of
             *  rectangles (i.e. did not expand the dirty area)
             */
            private void dirty(int x, int y, int w, int h, int ind) {
                int _n;
                if (w<x || h<y) {
                    return;
                }
                for (int i=ind; i<numdirties; i++) {
                    _n = 4*i;
                    // invalid dirties are marked with x=-1
                    if (dirties[_n]<0) {
                        continue;
                    }
        
                    int _x = dirties[_n];
                    int _y = dirties[_n+1];
                    int _w = dirties[_n+2];
                    int _h = dirties[_n+3];
        
                    if (x >= _w || y >= _h || w <= _x || h <= _y) {
                        // new region is outside of existing region
                        continue;
                    }
        
                    if (x < _x) {
                        // new region starts to the left of existing region
        
                        if (y < _y) {
                            // new region overlaps at least the top-left corner of existing region
        
                            if (w > _w) {
                                // new region overlaps entire width of existing region
        
                                if (h > _h) {
                                    // new region contains existing region
                                    dirties[_n] = -1;
                                    continue;
                                }// else {
                                // new region contains top of existing region
                                dirties[_n+1] = h;
                                continue;
        
                            } else {
                                // new region overlaps to the left of existing region
        
                                if (h > _h) {
                                    // new region contains left of existing region
                                    dirties[_n] = w;
                                    continue;
                                }// else {
                                // new region overlaps top-left corner of existing region
                                dirty(x, y, w, _y, i+1);
                                dirty(x, _y, _x, h, i+1);
                                return;
        
                            }
                        } else {
                            // new region starts within the vertical range of existing region
        
                            if (w > _w) {
                                // new region horizontally overlaps existing region
        
                                if (h > _h) {
                                    // new region contains bottom of existing region
                                    dirties[_n+3] = y;
                                    continue;
                                }// else {
                                // new region overlaps to the left and right of existing region
                                dirty(x, y, _x, h, i+1);
                                dirty(_w, y, w, h, i+1);
                                return;
        
                            } else {
                                // new region ends within horizontal range of existing region
        
                                if (h > _h) {
                                    // new region overlaps bottom-left corner of existing region
                                    dirty(x, y, _x, h, i+1);
                                    dirty(_x, _h, w, h, i+1);
                                    return;
                                }// else {
                                // existing region contains right part of new region
                                w = _x;
                                continue;
                            }
                        }
                    } else {
                        // new region starts within the horizontal range of existing region
        
                        if (y < _y) {
                            // new region starts above existing region
        
                            if (w > _w) {
                                // new region overlaps at least top-right of existing region
        
                                if (h > _h) {
                                    // new region contains the right of existing region
                                    dirties[_n+2] = x;
                                    continue;
                                }// else {
                                // new region overlaps top-right of existing region
                                dirty(x, y, w, _y, i+1);
                                dirty(_w, _y, w, h, i+1);
                                return;
        
                            } else {
                                // new region is horizontally contained within existing region
        
                                if (h > _h) {
                                    // new region overlaps to the above and below of existing region
                                    dirty(x, y, w, _y, i+1);
                                    dirty(x, _h, w, h, i+1);
                                    return;
                                }// else {
                                // existing region contains bottom part of new region
                                h = _y;
                                continue;
                            }
                        } else {
                            // new region starts within existing region
        
                            if (w > _w) {
                                // new region overlaps at least to the right of existing region
        
                                if (h > _h) {
                                    // new region overlaps bottom-right corner of existing region
                                    dirty(x, _h, w, h, i+1);
                                    dirty(_w, y, w, _h, i+1);
                                    return;
                                }// else {
                                // existing region contains left part of new region
                                x = _w;
                                continue;
                            } else {
                                // new region is horizontally contained within existing region
        
                                if (h > _h) {
                                    // existing region contains top part of new region
                                    y = _h;
                                    continue;
                                }// else {
                                // new region is contained within existing region
                                return;
                            }
                        }
                    }
                }
        
                // region is valid; store it for rendering
                _n = numdirties*4;
                size(_n);
                dirties[_n] = x;
                dirties[_n+1] = y;
                dirties[_n+2] = w;
                dirties[_n+3] = h;
                numdirties++;
            }
        
            ...
        }
        

        【讨论】:

        猜你喜欢
        • 2021-06-03
        • 2011-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-21
        • 1970-01-01
        相关资源
        最近更新 更多