脏矩形

发布于 2024-07-05 12:41:12 字数 81 浏览 8 评论 0原文

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

Where may one find references on implementing an algorithm for calculating a "dirty rectangle" for minimizing frame buffer updates? A display model that permits arbitrary edits and computes the minimal set of "bit blit" operations required to update the display.

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

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

发布评论

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

评论(6

苍景流年 2024-07-12 12:41:12

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

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

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

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

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

代码片段。 完整代码 在线访问

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++;
    }

    ...
}

Vexi is a reference implementation of this. The class is org.vexi.util.DirtyList (Apache License), and is used as part of production systems i.e. thoroughly tested, and is well commented.

A caveat, the currently class description is a bit inaccurate, "A general-purpose data structure for holding a list of rectangular regions that need to be repainted, with intelligent coalescing." Actually it does not currently do the coalescing. Therefore you can consider this a basic DirtyList implementation in that it only intersects dirty() requests to make sure there are no overlapping dirty regions.

The one nuance to this implementation is that, instead of using Rect or another similar region object, the regions are stored in an array of ints i.e. in blocks of 4 ints in a 1-dimensional array. This is done for run time efficiency although in retrospect I'm not sure whether there's much merit to this. (Yes, I implemented it.) It should be simple enough to substitute Rect for the array blocks in use.

The purpose of the class is to be fast. With Vexi, dirty may be called thousands of times per frame, so intersections of the dirty regions with the dirty request has to be as quick as possible. No more than 4 number comparisons are used to determine the relative position of two regions.

It is not entirely optimal due to the missing coalescing. Whilst it does ensure no overlaps between dirty/painted regions, you might end up with regions that line up and could be merged into a larger region - and therefore reducing the number of paint calls.

Code snippet. Full code 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++;
    }

    ...
}
最偏执的依靠 2024-07-12 12:41:12

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

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

对于每个脏区域添加:

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

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

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

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

To build the smallest rectangle that contains all the areas that need to be repainted:

  • Start with a blank area (perhaps a rectangle set to 0,0,0,0 - something you can detect as 'no update required')

For each dirty area added:

  • Normalize the new area (i.e. ensure that left is less than right, top less than bottom)
  • If the dirty rectangle is currently empty, set it to the supplied area
  • Otherwise, set the left and top co-ordinates of the dirty rectangle to the smallest of {dirty,new}, and the right and bottom co-ordinates to the largest of {dirty,new}.

Windows, at least, maintains an update region of the changes that it's been informed of, and any repainting that needs to be done due to the window being obscured and revealed. A region is an object that is made up of many possibly discontinuous rectangles, polygons and ellipses. You tell Windows about a part of the screen that needs to be repainted by calling InvalidateRect - there is also an InvalidateRgn function for more complicated areas. If you choose to do some painting before the next WM_PAINT message arrives, and you want to exclude that from the dirty area, there are ValidateRect and ValidateRgn functions.

When you start painting with BeginPaint, you supply a PAINTSTRUCT that Windows fills with information about what needs to be painted. One of the members is the smallest rectangle that contains the invalid region. You can get the region itself using GetUpdateRgn (you must call this before BeginPaint, because BeginPaint marks the whole window as valid) if you want to minimize drawing when there are multiple small invalid areas.

I would assume that, as minimizing drawing was important on the Mac and on X when those environments were originally written, there are equivalent mechanisms for maintaining an update region.

疯到世界奔溃 2024-07-12 12:41:12

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

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

现在,如果您在 Java 中完成所有这些操作,则可以直接使用 getBound2D()方法

It sounds like what you need is a bounding box for each shape that you're rendering to the screen. Remember that a bounding box of a polygon can be defined as a "lower left" (the minimum point) and an "upper right" (the maximum point). That is, the x-component of the minimum point is defined as the minimum of all the x-components of each point in a polygon. Use the same methodology for the y-component (in the case of 2D) and the maximal point of the bounding box.

If it's sufficient to have a bounding box (aka "dirty rectangle") per polygon, you're done. If you need an overall composite bounding box, the same algorithm applies, except you can just populate a single box with minimal and maximal points.

Now, if you're doing all this in Java, you can get your bounding box for an Area (which you can construct from any Shape) directly by using the getBound2D() method.

逆光下的微笑 2024-07-12 12:41:12

您使用什么语言? 在 Python 中,Pygame 可以为你做到这一点。 使用 RenderUpdates Group 和一些具有图像和矩形属性的 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,我会这样做:

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

What language are you using? In Python, Pygame can do this for you. Use the RenderUpdates Group and some Sprite objects with image and rect attributes.

For example:

#!/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))

If you're not using Python+Pygame, here's what I would do:

  • Make a Sprite class that's update(),
    move() etc. method sets a "dirty"
    flag.
  • Keep a rect for each sprite
  • If your API supports updating a list of rects, use that on the list of rects whose sprites are dirty. In SDL, this is SDL_UpdateRects.
  • If your API doesn't support updating a list of rects (I've never had the chance to use anything besides SDL so I wouldn't know), test to see if it's quicker to call the blit function multiple times or once with a big rect. I doubt that any API would be faster using one big rect, but again, I haven't used anything besides SDL.
醉酒的小男人 2024-07-12 12:41:12

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

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

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

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

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

  4. 找到所有差异后,我会遍历所有有差异的子矩形,如果它们彼此相邻,则将它们形成更大的矩形,并使用最左边、最顶部、最右边以及这些子矩形最底部的差异,以形成我使用的实际差异矩形。

这似乎对我来说效果很好。 如果您尚未实施自己的解决方案,请告诉我,如果您愿意,我会通过电子邮件将我的代码发送给您。 另外,到目前为止,我是 StackOverflow 的新用户,所以如果您喜欢我的回答,请投票。 :)

I just recently wrote a Delphi class to calculate the difference rectangles of two images and was quite suprised by how fast it ran - fast enough to run in a short timer and after mouse/keyboard messages for recording screen activity.

The step by step gist of how it works is by:

  1. Sub-dividing the image into logical 12x12 by rectangles.

  2. Looping through each pixel and if there's a difference then I tell the sub-rectangle which the pixel belongs to that there's a difference in one of it's pixels and where.

  3. Each sub-rectangle remembers the co-ordinates of it's own left-most, top-most, right-most and bottom-most difference.

  4. Once all the differences have been found, I loop through all the sub-rectangles that have differences and form bigger rectangles out of them if they are next to each other and use the left-most, top-most, right-most and bottom-most differences of those sub-rectangles to make actual difference rectangles I use.

This seems to work quite well for me. If you haven't already implemented your own solution, let me know and I'll email you my code if you like. Also as of now, I'm a new user of StackOverflow so if you appreciate my answer then please vote it up. :)

半枫 2024-07-12 12:41:12

查看 R-tree四叉树 数据结构。

Look into R-tree and quadtree data structures.

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