Qt / C ++: рисование эффективно

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

крышка экрана 1

Чтобы нарисовать эти треугольники, я использую QPainter::drawPolygon, Каждый треугольник справа соответствует треугольнику слева, поэтому я знаю, какой цвет я хочу использовать, чтобы нарисовать его.

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

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

введите описание изображения здесь

Проблема в том, что способ, которым я это делаю, слишком медленный. Вот как я это делаю:

  1. Я бегу через все треугольники
  2. Для каждого треугольника я вычисляю координаты каждого пикселя, который будет отображаться.
  3. Для каждого из этих пикселей я вычисляю координаты соответствующего пикселя на картинке (это простая математическая операция) и извлекаю цвет этого пикселя.
  4. я использую QPainter::setPen(QColor) а также QPainter::drawPoint(QPoint) нарисовать пиксель.

Я новичок в программировании на Qt и ничего не знаю о графике, так что это то, что я мог придумать. Проблема в том, что это «недопустимо» слишком медленно ( paintEvent каждого холста занимает около 0,15 с, по сравнению с 0,01 с в случае простых треугольников).

Я запустил профилировщик, чтобы попытаться понять, что происходит, я заметил, что в виджете холста paintEvent,

  1. 58% времени проводится в QPainter::drawPoint
  2. 27% времени проводится в QPainter::setPen

Кажется, что QPainter::drawPoint слишком сложный и медленный: я просто хочу, чтобы он печатал пиксель заданного цвета, вот и все.

Возможно, я нашел решение своей проблемы: сохранить QImage (как переменная-член моего виджета Canvas), которая представляет все, что я хочу, чтобы мой холст отображал, и определяю его полностью в моем paintEvent пиксель за пикселем, а затем нарисовать его сразу в конце моего paintEvent с QPainter::drawImage, У меня есть подсказка, что это будет намного быстрее. Но прежде чем я переписываю свой код заново, я хотел бы знать, действительно ли это то, что я хочу сделать.

Надеюсь, я не утомлял тебя смертью! Большое спасибо заранее за ваши идеи.

18

Решение

Решение не OpenGl:

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

QImage::QImage ( uchar * data, int width, int height, Format format )

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

4

Другие решения

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

7

Один из способов сделать это — использовать класс, унаследованный от QGLWidget, вместо комбо QGraphicsScene / QGraphicsView. К сожалению, кривая обучения для OpenGL начинается немного круто. Тем не менее, это будет очень быстро, потому что это произойдет непосредственно на видеокарте, которая оптимизирована именно для этого типа операций.
Вы загрузите изображение QGLWidget::bindTexture(),
Вы свяжете точки на изображении с вашей треугольной сеткой и отправите их все на свою видеокарту. В устаревшей версии OpenGL (которая на мой взгляд проще в использовании, чем новый API), это выглядело бы примерно так:

glEnable(GL_TEXTURE_2D);

glBegin(GL_TRIANGLES);
for (int ii=0;ii<triangle.size();++ii) {
for (int jj=0;jj<3;++jj) {
glTexCoord2d(triangle[ii].tex[jj][0],triangle[ii].tex[jj][1]);
glVertex2d(triangle[ii].point[jj[0],triangle[ii].point[jj][1]);
}
}
glEnd();

куда triangle это некоторая структура данных, которую вы создали, удерживая вершины треугольника и связанные отображения в изображении. Видеокарта будет обрабатывать пиксельную интерполяцию за вас.

3

Другой вариант, кроме OpenGL, заключается в использовании OpenCL, что может быть проще для вас. Вам просто нужно отобразить в памяти растровое изображение ввода / вывода на видеокарту, написать небольшое ядро ​​на C, которое обрабатывает один треугольник, а затем поставить в очередь выполнение ядра для каждого треугольника. Это будет работать в 100 раз быстрее, чем одно ядро ​​на процессоре.

Здесь есть обертка Qt для API хоста OpenCL:

http://doc.qt.digia.com/opencl-snapshot/index.html

1

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

Это можно распараллелить так, чтобы вы обрабатывали столько треугольников параллельно, сколько имеется ядер ЦП.

0
По вопросам рекламы [email protected]