Qt & двойная буферизация - есть ли хитрые приемы для захвата пикселей или манипулирования задним буфером? - PullRequest
3 голосов
/ 21 мая 2009

Я переношу приложение в Qt из MFC.

Приложение MFC будет использовать вызовы GDI для построения окна (в основном, графического графика). Это вывело бы обратно в буфер растрового изображения памяти, а затем BitBlt это на экран. Qt, однако, уже выполняет двойную буферизацию.

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

Я бы хотел найти лучший способ сделать это. Есть ли способ сделать что-то вроде grabWindow (), который будет извлекать из заднего буфера виджета, а не с экрана? ... может быть эквивалент BitBlt (..., DST_INVERT)?

Я видел setCompositionMode () в QPainter, но в документах говорится, что он работает только с художниками, работающими на QImage. (В противном случае я мог бы сложить изображение сплошного прямоугольника на свой виджет в режиме причудливой композиции, чтобы получить что-то вроде эффекта инвертирования)

Я мог бы сделать то же самое, что и MFC, рисуя обратно в буфер QImage ... но я читал, что аппаратное ускорение может не работать таким образом. Похоже, было бы бесполезно переопределять двойную буферизацию, уже предоставленную вам в Qt. Я также не уверен, какими могут быть побочные эффекты отключения двойной буферизации виджета (чтобы избежать тройной буферизации).

В какой-то момент у меня был запутанный вызов QPixmap :: grabWidget () с флагами, предотвращающими рекурсию, защищающими его, но он рендерил все дважды и явно хуже, чем просто рисование в QImage. (и об этом специально предупреждают в документах)

Должен ли я сдаться и нарисовать все в QImage, делая это в основном так же, как я это делал в MFC?

EDIT:

Хорошо, художник QPixmap работает примерно с той же скоростью, что и сейчас. Таким образом, использование обратного буфера QPixmap представляется наилучшим способом сделать это.

Решение не было для меня очевидным, но, возможно, если бы я посмотрел больше примеров (например, демо Арии), я бы просто закодировал его так, как ожидалось, и он бы работал просто отлично.

Вот разница. Я видел демонстрационные справочные системы, использующие это:

    QPainter painter(this)

в начале paintEvent (). Таким образом, мне казалось естественным, что для того, чтобы создать двойной буфер для QPixmap и затем нарисовать на экране, вам нужно сделать следующее:

    QPainter painter(&pixmap);
    QPainter painterWidget(this);
    ...  draw using 'painter' ...
    painterWidget.drawPixmap(QPoint(0,0), pixmap);

когда на самом деле вы, очевидно, должны делать это:

    QPainter painter;
    painter.begin(&pixmap);
    ... draw using 'painter' ...
    painter.end();
    painter.begin(this);
    painter.drawPixmap(QPoint(0,0), pixmap);
    painter.end();

Я вижу, что на моем пути были два активных художника одновременно. Я не совсем уверен , почему это быстрее, но интуитивно мне нравится последний лучше. Это один объект QPainter, и он делает только одну вещь за раз. Может быть, кто-то может объяснить, почему первый метод плох? (С точки зрения ошибочных предположений в движке рендеринга Qt)

Ответы [ 3 ]

4 голосов
/ 22 мая 2009

Предполагая, что вы на самом деле не хотите получать пиксельные значения из вашего внеэкранного буфера (а просто просто рисовать что-то поверх него и снова перетаскивать на экран), вы должны использовать QPixmap в качестве буфера, а не QImage. Использование последнего отключает все ускорение рисования, так как Qt использует его программный растровый движок, поэтому используйте QPixmap. Если вы используете графическую систему OpenGL, вы все равно сможете воспользоваться ею.

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

Чтобы отключить резервное хранилище Qt (что, как правило, не очень хорошая идея), используйте Qt :: WA_PaintOnScreen для вашего виджета верхнего уровня.

Немного не связано, но вы можете захотеть посмотреть QRubberBand виджет.

4 голосов
/ 22 мая 2009

Рисуя поверх области графика, вы должны иметь возможность использовать режимы композиции для инвертирования. Нарисуйте белый, используя режим Разница композиции. Следующий пример - это подкласс QLabel, показывающий растровое изображение:

void Widget::paintEvent(QPaintEvent *pe)
{
    // make sure we paint background
    QLabel::paintEvent(pe);

    // paint the overlay
    if (!selectionRect.isNull()) {
        QPainter p(this);
        p.setCompositionMode(QPainter::CompositionMode_Difference);
        p.fillRect(selectionRect,QColor("#FFFFFF"));
    }
}

альтернативный текст http://chaos.troll.no/~hhartz/yesManInverted.png

0 голосов
/ 21 мая 2009

Самый простой, самый простой ответ, который я знаю, состоит в том, чтобы сделать это, как вы делали раньше, в QImage и использовать QImage в качестве источника для вашего виджета на экране.

Другим вариантом может быть добавление прозрачного виджета поверх графика, который рисует только перевернутую часть графика. Однако я не думаю, что это вообще оптимизирует рисунок. Это, вероятно, приведет к отрисовке базового графика, а затем к наложению перевернутой детали.

...