Подтвердить что ты не робот

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

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

screen cap 1

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

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

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

enter image description here

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

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

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

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

  • 58% времени потрачено на QPainter::drawPoint
  • 27% времени тратится на QPainter::setPen

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

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

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

4b9b3361

Ответ 1

Решение без OpenGl:

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

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

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

Ответ 2

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

Ответ 3

Один из способов сделать это - использовать класс, наследующий от 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 - некоторая структура данных, которую вы создали, удерживая треугольные вершины и связанные с ними сопоставления в изображении. Графическая карта будет обрабатывать интерполяцию пикселей для вас.

Ответ 4

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

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

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

Ответ 5

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

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