Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: Pritcher от Январь 21, 2016, 21:50



Название: [РЕШЕНО]Быстрое рисование линий инструментом "карандаш"
Отправлено: Pritcher от Январь 21, 2016, 21:50
Приветствую!

Пытаюсь реализовать рисование линий на виджете, как "карандаш" в Paint, не устраивает скорость отрисовки. Рисование на виджете размером 1000x1000 еще более-менее приемлемое, а вот когда окно становится размером 1920х1080, то линия "не успевает" рисоваться так же быстро, как перемещается курсор.

.h
Код
C++ (Qt)
#pragma once
 
#include <QtWidgets/QWidget>
 
#include <QPen>
 
class MyScene : public QWidget
{
public:
MyScene( QWidget * parent = nullptr );
 
void paintEvent( QPaintEvent * event ) override;
 
protected:
void mousePressEvent( QMouseEvent * event ) override;
void mouseMoveEvent( QMouseEvent * event ) override;
void mouseReleaseEvent( QMouseEvent * event ) override;
 
private:
bool _leftPressed;
QPen _pen;
 
QPointF _startPoint;
QPointF _endPoint;
 
QImage * _image;
};
 

.cpp
Код
C++ (Qt)
#include <QPaintEvent>
 
#include <QPainter>
#include <QImage>
#include <QTimer>
 
const int SZ = 1000;
 
MyScene::MyScene( QWidget * parent ):
QWidget( parent )
{
_pen.setWidth( 1 );
_pen.setColor( Qt::red );
 
_leftPressed = false;
 
_image = new QImage( SZ, SZ, QImage::Format_ARGB32_Premultiplied );
_image->fill( Qt::white );
 
resize( SZ, SZ );
 
QTimer * timer = new QTimer( this );
timer->setInterval( 16 );
 
connect( timer, SIGNAL( timeout() ), this, SLOT( update() ) );
 
timer->start();
}
 
void MyScene::paintEvent( QPaintEvent * )
{
QPainter painter( this );
painter.drawImage( 0, 0, *_image );
}
 
void MyScene::mousePressEvent(QMouseEvent * event)
{
if ( event->button() == Qt::LeftButton ) {
_leftPressed = true;
_startPoint = event->pos();
}
}
 
void MyScene::mouseMoveEvent(QMouseEvent * event)
{
if ( _leftPressed ) {
_endPoint = event->pos();
QPainter painter( _image );
painter.drawLine( _startPoint, _endPoint );
_startPoint = _endPoint;
}
}
 
void MyScene::mouseReleaseEvent(QMouseEvent * event)
{
_leftPressed = false;
}
 
 

Пробовал наследовать MyScene от QOpenGLWidget, использовать в качестве буфера QPixmap вместо QImage, менял частоту срабатывания таймера, делал апдейт непосредственно после отрисовки линии по нужной области — результат один и тот же. В OpenGL не умею, есть ли способы добиться мгновенной отрисовки без задержек не используя OpenGL?

Ну а ответ отрицательный, не могли бы привести пример кода для отрисовки линии средствами OpenGL?

Заранее спасибо.


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Old от Январь 21, 2016, 22:28
А для чего эта "жесть" с обнавлением по таймеру?
Ну и вызывайте update из mouseMoveEvent, мосле рисования на backbuffer.


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Bepec от Январь 21, 2016, 22:30
Попробуйте update вызывать с координатами измененного участка.
На данный момент у вас каждый тик отрисовывается вся картинка. При передаче координат измененного участка должен будет перерисовываться только изменённый участок.
Так же вам необходимо будет переписать функцию paintEvent. Чтобы она рисовала не весь рисунок, а только передаваемую в евенте часть.

После этого скорость отрисовки у вас должна возрасти процентов на 90 где то.

Ну потому что вы каждый тик рисуете 1920*1080, а после внесения рекомендованных мною изменений будет отрисовываться где то 20*20. :)

to Old:
Там выскакивает проблема слишком частого обновления. MouseMove генерируется на каждый пиксель,а update необходим не чаще чем раз в 10-15 мс :P

PS нужны именно 2 правки - передача участка который нужен обновить и отрисовка только переданного участка. По отдельности эти правки не принесут никакой выгоды :)


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Old от Январь 21, 2016, 22:38
to Old:
Там выскакивает проблема слишком частого обновления. MouseMove генерируется на каждый пиксель,а update необходим не чаще чем раз в 10-15 мс :P
Я же вам уже много раз писал, прежде чем писать бред - проверяйте. С чего mouseMove  генерироваться на каждый пиксель?


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Bepec от Январь 21, 2016, 23:06
Я бред не пишу, вы голословно меня обижаете.
Действие: Зажатие и движение слева направо мышью.
Код:
Код:
void MainWindow::mouseMoveEvent(QMouseEvent * event)
{
    qDebug() << event;
}

void MainWindow::mousePressEvent(QMouseEvent * event)
{
    qDebug() << event;
}

void MainWindow::mouseReleaseEvent(QMouseEvent * event)
{
    qDebug() << event;
}
Вывод:
Код:
QMouseEvent(MouseButtonPress, LeftButton, localPos=95,219, screenPos=855,589)
QMouseEvent(MouseMove, buttons=LeftButton, localPos=96,219, screenPos=856,589)
QMouseEvent(MouseMove, buttons=LeftButton, localPos=97,219, screenPos=857,589)
QMouseEvent(MouseMove, buttons=LeftButton, localPos=98,219, screenPos=858,589)
QMouseEvent(MouseMove, buttons=LeftButton, localPos=99,219, screenPos=859,589)
QMouseEvent(MouseMove, buttons=LeftButton, localPos=100,219, screenPos=860,589)
QMouseEvent(MouseMove, buttons=LeftButton, localPos=101,219, screenPos=861,589)
QMouseEvent(MouseButtonRelease, LeftButton, localPos=101,219, screenPos=861,589)


PS с того, что это механика QWidget. При каждом изменении положения указателя генерируется событие MouseMove. По умолчанию mouseMove генерируется только при зажатой кнопке мыши, но можно включить tracking и тогда оно будет генерироваться без кнопки.


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Old от Январь 21, 2016, 23:14
Вы попробуйте мышью подвигать более размашисто, сразу увидите разрывы.


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Bepec от Январь 21, 2016, 23:23
Да, но это исключение. Более я думаю что это внутренняя оптимизация, т.к. цикл событий будет перегружен если выдавать при таких масштабных движениях столько событий.
 
Тем более что в данном вопросе это без разницы.
Проведя линию в 1000 пикселей за 0,3с., вы вызовите 40-60  циклов перерисовки. Как думаете это поможет ТСсу?



Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Old от Январь 21, 2016, 23:26
Да, но это исключение. Более я думаю что это внутренняя оптимизация, т.к. цикл событий будет перегружен если выдавать при таких масштабных движениях столько событий.
 
Тем более что в данном вопросе это без разницы.
Проведя линию в 1000 пикселей за 0,3с., вы вызовите 40-60  циклов перерисовки. Как думаете это поможет ТСсу?


Не все вызовы update заставляют окно перерисовываться. Попробуйте вместо однократного вызова update, вызывать его в цикле 10 раз. Окно все равно перерисуется единожды.


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Bepec от Январь 22, 2016, 00:25
С этим не спорю, а вот извинений по поводу бреда жду.

Хотелось бы уточнить что имеет в виду ТС под отставанием рисования. К примеру задержка рисования при среднем скорости мыши - есть почти везде. Мб именно её он считает за "тормоза"?


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: kambala от Январь 22, 2016, 00:31
Верес, если достаточно резко дергать мышкой, то mouseMoveEvent вызовется не для каждого пикселя на пути курсора — отсюда и «отставание рисования»


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Bepec от Январь 22, 2016, 00:50
В проекте ТСса эта проблема решена тем, что у него берётся первая и последняя точка и рисуется линия. Так что этот вариант отпадает. Первая и последняя точка есть всегда, промежуточные вот да, пропадают.

Не, я имею в виду то отставание когда ведёшь со средней скоростью линию. Пейнт тот же самый открываешь, берёшь карандашь и ведёшь линию не рывком, а плавно но быстро, см 2 в секунду. И идёт отставание рисование где то на курсор. Ну собственно это и есть задержка рисования.

Но это уже надо ТСса спросить. Мб у него комп слабый или ещё какие тормоза он имеет в виду.

PS тем более что координаты пусть и пропадают, но промежуточные передаются быстро.



Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Racheengel от Январь 22, 2016, 01:49
А если в paintEvent обновлять не все изображение, а только кусок, который включает в себя последнюю проведенную линию...?


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Old от Январь 22, 2016, 05:44
С этим не спорю, а вот извинений по поводу бреда жду.
А какие извинения вы ждете, если пишите бред? Разве вы первый раз утверждали то, что не соответствует действительности?

Уже надоело опровергать ваши заявления, которые не понятно для чего вы делаете? Вовсе не обязательно отвечать во всех темах, тем более в которых не разбираетесь.


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Old от Январь 22, 2016, 05:47
А отстование у ТС, скорее всего, из-за "странного" способа обновления окна по таймеру.


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: gil9red от Январь 22, 2016, 06:26
Исходникик программы рисования на с++/Qt (https://github.com/Gr1N/EasyPaint)


Название: Re: Быстрое рисование линий инструментом "карандаш"
Отправлено: Pritcher от Январь 22, 2016, 06:28
Что ж, вчера был уже вечер, когда я пробовал вызывать update по нужной области, видимо, не заметил прироста скорости. Вызов update непосредственно после отрисовки линии по области помог.
Спасибо, Old, Bepec, Racheengel!

.cpp изменился следующим образом:
Код
C++ (Qt)
#include <QPaintEvent>
 
#include <QPainter>
#include <QImage>
#include <QTimer>
 
#include <QtGlobal>
 
const int SZ = 1000;
 
MyScene::MyScene( QWidget * parent ):
QWidget( parent )
{
_pen.setWidth( 1 );
_pen.setColor( Qt::red );
 
_leftPressed = false;
 
_image = new QImage( SZ, SZ, QImage::Format_ARGB32_Premultiplied );
_image->fill( Qt::white );
 
resize( SZ, SZ );
}
 
void MyScene::paintEvent( QPaintEvent * event )
{
QPainter painter( this );
QRect rect( event->rect() );
painter.drawImage( rect, *_image, rect );
}
 
void MyScene::mousePressEvent( QMouseEvent * event )
{
if ( event->button() == Qt::LeftButton ) {
_leftPressed = true;
_startPoint = event->pos();
}
}
 
void MyScene::mouseMoveEvent( QMouseEvent * event )
{
if ( _leftPressed ) {
_endPoint = event->pos();
QPainter painter( _image );
painter.drawLine( _startPoint, _endPoint );
QRect rect( qMin( _startPoint.x(), _endPoint.x() ),
qMin( _startPoint.y(), _endPoint.y() ),
qAbs( _startPoint.x() - _endPoint.x() ) + 1,
qAbs( _startPoint.y() - _endPoint.y() ) + 1 );
update( rect );
_startPoint = _endPoint;
}
}
 
void MyScene::mouseReleaseEvent( QMouseEvent *)
{
_leftPressed = false;
}
 


Название: Re: [РЕШЕНО]Быстрое рисование линий инструментом "карандаш"
Отправлено: kambala от Январь 22, 2016, 12:37
а насколько выгоднее рисовать по QImage вместо непосредственного рисования на виджете?


Название: Re: [РЕШЕНО]Быстрое рисование линий инструментом "карандаш"
Отправлено: Racheengel от Январь 22, 2016, 12:49
а насколько выгоднее рисовать по QImage вместо непосредственного рисования на виджете?

Хотя бы то, что рисовать можно в любой момент времени, а не в paintEvent.


Название: Re: [РЕШЕНО]Быстрое рисование линий инструментом "карандаш"
Отправлено: Igors от Январь 22, 2016, 13:00
а насколько выгоднее рисовать по QImage вместо непосредственного рисования на виджете?
Сейчас рисовать на QImage уже невыгодно т.к. окна кешируются. Но у ТС нет выбора т.к. предыдущие мазюкания должны сохраняться


Название: Re: [РЕШЕНО]Быстрое рисование линий инструментом "карандаш"
Отправлено: kambala от Январь 22, 2016, 13:07
в общем случае-то понятно. но вот применимо к задаче ТС — ведем мышкой и тут же рисуем — есть существенная разница рисовать в картинку или прямо на виджет?

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


Название: Re: [РЕШЕНО]Быстрое рисование линий инструментом "карандаш"
Отправлено: Bepec от Январь 22, 2016, 13:14
Если не тормозит, то и беспокоиться не надо.

PS преждевременная оптимизация это зло :)


Название: Re: [РЕШЕНО]Быстрое рисование линий инструментом "карандаш"
Отправлено: Old от Январь 22, 2016, 13:15
Можно рисовать непосредственно на виджет. Я использую рисование на QImage,  в случаях когда картинку можно использовать для нескольких кадров. Например, сформировали фон и пока в нем ничего не меняется, использовать его кеш, а остальное рисовать поверх.


Название: Re: [РЕШЕНО]Быстрое рисование линий инструментом "карандаш"
Отправлено: Igors от Январь 22, 2016, 13:40
в общем случае-то понятно. но вот применимо к задаче ТС — ведем мышкой и тут же рисуем — есть существенная разница рисовать в картинку или прямо на виджет?
"Прямо" точно быстрее. Насколько.. в OSX+Qt4.7 было намного, на Qt5 не мерял

к чему я это спрашиваю: сейчас пишу небольшую программку, которая рисует сетку, а пользователь водит мышкой и заполняет квадратики в сетке. рисую прямо на виджете, на каждое действие пользователя перерисовывается полностью все, визуально никаких проблем нет (ничего не тормозит). это хорошо или лучше рисовать в QImage? или сетку нарисовать на виджете, а квадратики рисовать в QImage?
Нужна приличная нагрузка (напр неск тысяч линий) чтобы они "заслуживали кеширования" в QImage. В Mac доках не раз мелькает "triple buffer" - поясняется почему

PS преждевременная оптимизация это зло :)
Может когда-то (в ледниковый период) это выражение и имело смысл, но сейчас никакого, "дежурная отмазка" по абсолютно любому поводу


Название: Re: [РЕШЕНО]Быстрое рисование линий инструментом "карандаш"
Отправлено: kambala от Январь 22, 2016, 13:44
понял, всем спасибо!