Грязные прямоугольники - PullRequest
11 голосов
/ 17 сентября 2008

Где можно найти ссылки на реализацию алгоритма вычисления «грязного прямоугольника» для минимизации обновлений кадрового буфера? Модель дисплея, которая разрешает произвольное редактирование и вычисляет минимальный набор «битовых» операций, необходимых для обновления дисплея.

Ответы [ 6 ]

3 голосов
/ 29 июня 2011

Vexi является эталонной реализацией этого. Класс org.vexi.util.DirtyList (лицензия Apache) и используется как часть производственных систем, то есть тщательно протестирован, и хорошо прокомментирован.

Предостережение, описание класса в настоящее время немного неточно, "Структура данных общего назначения для хранения списка прямоугольных областей, которые необходимо перекрасить, с интеллектуальным объединением." На самом деле это не так В настоящее время занимаюсь объединением. Поэтому вы можете считать это базовой реализацией DirtyList, поскольку она только пересекает запросы dirty (), чтобы убедиться, что нет перекрывающихся грязных областей.

Единственный нюанс этой реализации состоит в том, что вместо использования Rect или другого подобного объекта области регионы сохраняются в массиве целых чисел, то есть в блоках по 4 дюйма в одномерном массиве. Это сделано для эффективности времени выполнения, хотя в ретроспективе я не уверен, есть ли в этом какая-то заслуга. (Да, я реализовал это.) Должно быть достаточно просто заменить Rect для используемых блоков массива.

Цель занятия - быть быстрым . С Vexi, грязный может вызываться тысячи раз за кадр, поэтому пересечение грязных областей с грязным запросом должно быть как можно быстрее. Для определения относительного положения двух регионов используется не более четырех числовых сравнений.

Это не совсем оптимально из-за отсутствия объединения. Хотя это гарантирует отсутствие перекрытий между грязными / закрашенными областями, вы можете получить области, которые выстраиваются в линию и могут быть объединены в большую область - и, следовательно, уменьшить количество вызовов рисования.

Фрагмент кода. Полный код онлайн здесь .

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

    ...
}
3 голосов
/ 21 ноября 2008

Звучит так, будто вам нужна ограничительная рамка для каждой фигуры, которую вы отображаете на экране. Помните, что ограничивающий прямоугольник многоугольника можно определить как «нижний левый угол» (минимальная точка) и «верхний правый угол» (максимальный пункт). То есть, x-компонент минимальной точки определяется как минимум всех x-компонентов каждой точки в многоугольнике. Используйте ту же методологию для компонента y (в случае 2D) и максимальной точки ограничительной рамки.

Если для полигона достаточно иметь ограничивающий прямоугольник (он же «грязный прямоугольник»), все готово. Если вам нужен общий составной ограничивающий прямоугольник, применяется тот же алгоритм, за исключением того, что вы можете просто заполнить один прямоугольник минимальными и максимальными точками.

Теперь, если вы делаете все это на Java, вы можете получить свой ограничивающий прямоугольник для Area (который вы можете построить из любого Shape) напрямую, используя getBound2D() метод .

3 голосов
/ 17 сентября 2008

Чтобы построить наименьший прямоугольник, содержащий все области, которые нужно перекрасить:

  • Начните с пустой области (возможно, прямоугольник установлен на 0,0,0,0 - то, что вы можете определить как «обновление не требуется»)

За каждую грязную область добавлено:

  • Нормализовать новую область (т.е. убедиться, что слева меньше правого, сверху меньше нижнего)
  • Если грязный прямоугольник в настоящее время пуст, установите его в предоставленную область
  • В противном случае установите для левой и верхней координат грязного прямоугольника наименьшее из {грязных, новых}, а для правых и нижних координат - наибольшее из {грязных, новых}.

Windows, по крайней мере, поддерживает область обновления об изменениях, о которых она была проинформирована, и о любых перерисовках, которые необходимо выполнить из-за того, что окно скрыто и открыто. регион - это объект, состоящий из множества, возможно, прерывистых прямоугольников, многоугольников и эллипсов. Вы говорите Windows о части экрана, которую нужно перекрасить, вызывая InvalidateRect - для более сложных областей есть также функция InvalidateRgn. Если вы решили выполнить рисование до того, как будет получено следующее сообщение WM_PAINT, и вы хотите исключить его из грязной области, есть функции ValidateRect и ValidateRgn.

Когда вы начинаете рисовать с BeginPaint, вы предоставляете PAINTSTRUCT, который Windows заполняет информацией о том, что нужно рисовать. Один из членов - это самый маленький прямоугольник, содержащий недопустимую область. Вы можете получить сам регион, используя GetUpdateRgn (вы должны вызывать это до BeginPaint, потому что BeginPaint помечает все окно как действительное), если вы хотите минимизировать рисование, когда есть несколько маленьких недопустимых областей.

Я бы предположил, что, поскольку минимизация рисования была важна на Mac и на X, когда эти среды были изначально написаны, существуют эквивалентные механизмы для поддержки области обновления.

2 голосов
/ 25 марта 2011

Просмотр R-дерева и квадродерева структур данных.

2 голосов
/ 27 ноября 2008

Я только недавно написал класс Delphi для вычисления разностных прямоугольников двух изображений, и был весьма удивлен тем, насколько быстро он работал - достаточно быстро, чтобы работать в течение короткого таймера и после сообщений мыши / клавиатуры для записи активности экрана.

Пошаговая инструкция о том, как это работает:

  1. Подразделяя изображение на логические 12x12 прямоугольниками.

  2. Цикл по каждому пикселю, и если есть разница, я сообщаю под прямоугольнику, к какому пикселю принадлежит, что есть разница в одном из его пикселей и где.

  3. Каждый под-прямоугольник запоминает координаты его самой левой, самой верхней, самой правой и самой нижней разницы.

  4. Как только все различия будут найдены, я перебираю все суб-прямоугольники, которые имеют различия, и формирую из них большие прямоугольники, если они находятся рядом друг с другом, и использую самый левый, самый верхний, Самые правые и самые нижние различия этих под прямоугольников, чтобы получить реальные разницы, которые я использую.

Мне кажется, это работает очень хорошо. Если вы еще не внедрили свое собственное решение, дайте мне знать, и я отправлю вам код, если хотите. Кроме того, на данный момент я новый пользователь StackOverflow, поэтому, если вы оцените мой ответ, пожалуйста, проголосуйте за него. :)

2 голосов
/ 17 сентября 2008

Какой язык вы используете? В Python Pygame может сделать это для вас. Используйте группу RenderUpdates и некоторые объекты 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 update (), Метод move () и т. д. устанавливает «грязный» флаг.
  • Сохраняйте прямоугольник для каждого спрайта
  • Если ваш API поддерживает обновление списка ректов, используйте его в списке ректов, чьи спрайты грязные. В SDL это SDL_UpdateRects.
  • Если ваш API не поддерживает обновление списка rects (у меня никогда не было возможности использовать что-либо, кроме SDL, поэтому я не знаю), проверьте, быстрее ли вызывать функцию blit несколько раз или один раз с большим прямоугольником. Я сомневаюсь, что любой API будет быстрее, если использовать один большой прямоугольник, но опять же, я не использовал ничего, кроме SDL.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...