QPainter Performance
-
I want to implement whiteboard functions, and one of the important functions is to draw lines. During the implementation, I ran into a problem: if I keep drawing (lines containing more than 10,000 points) on the Widget,the Widget is very stuck. Is there a good solution?
Here's part of my code:
void RenderArea::drawMyImage()
{
QPainter painter;
painter.begin(&mImage);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setBrush(brush);
painter.setPen(penLists[9]);QMapIterator<int, QList<QPointF> *> it(m_id2PointList); while(it.hasNext()) { auto item = it.next(); QList<QPointF> *pointList = item.value(); this->bezier = false; if (this->bezier && pointList->length() > 3) { QList<QPointF> points = *pointList; QPainterPath path = getBezierPath(points); painter.drawPath(path); continue; } painter.drawPolyline(pointList->toVector().data(), pointList->length()); } painter.end();
}
bool RenderArea::event(QEvent *event)
{
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
{
const auto touchPoints = static_cast<QTouchEvent >(event)->points();
for (const QTouchEvent::TouchPoint &touchPoint : touchPoints) {
int id = touchPoint.id();
QList<QPointF> p_listPoint = m_id2PointList[id];switch (touchPoint.state()) { case QEventPoint::Released: m_id2PointList.remove(id); if (p_listPoint) { m_completePointLists.push_back(p_listPoint); m_completePenLists.push_back(pen); } continue; case QEventPoint::Pressed: pListPoint = new QList<QPointF>(); p_listPoint = new QList<QPointF>(); m_id2PointList[touchPoint.id()] = p_listPoint; default: { QPointF point = touchPoint.position(); p_listPoint->push_back(point); drawMyImage(); if (p_listPoint->size() >= 2){ QPointF pointA = p_listPoint->at(p_listPoint->size() - 2); QPointF pointB = p_listPoint->at(p_listPoint->size() - 1); QRect rect(qMin(pointA.x(), pointB.x()), qMin(pointA.y(), pointB.y()), qAbs(pointA.x() - pointB.x()), qAbs(pointA.y() - pointB.y())); rect.adjust(-10 , -10 , 10, 10); update(rect); } else { QRect rect(point.x(), point.y(), 0, 0); rect.adjust(-10 , -10 , 10, 10); update(rect); } } break; } } break; } default: return QWidget::event(event); } return true;
}
void RenderArea::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
const QRect rect = event->rect();
painter.drawImage(rect.topLeft(), mImage, rect);
} -
I want to implement whiteboard functions, and one of the important functions is to draw lines. During the implementation, I ran into a problem: if I keep drawing (lines containing more than 10,000 points) on the Widget,the Widget is very stuck. Is there a good solution?
Here's part of my code:
void RenderArea::drawMyImage()
{
QPainter painter;
painter.begin(&mImage);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setBrush(brush);
painter.setPen(penLists[9]);QMapIterator<int, QList<QPointF> *> it(m_id2PointList); while(it.hasNext()) { auto item = it.next(); QList<QPointF> *pointList = item.value(); this->bezier = false; if (this->bezier && pointList->length() > 3) { QList<QPointF> points = *pointList; QPainterPath path = getBezierPath(points); painter.drawPath(path); continue; } painter.drawPolyline(pointList->toVector().data(), pointList->length()); } painter.end();
}
bool RenderArea::event(QEvent *event)
{
switch (event->type()) {
case QEvent::MouseButtonPress:
case QEvent::MouseMove:
case QEvent::MouseButtonRelease:
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
{
const auto touchPoints = static_cast<QTouchEvent >(event)->points();
for (const QTouchEvent::TouchPoint &touchPoint : touchPoints) {
int id = touchPoint.id();
QList<QPointF> p_listPoint = m_id2PointList[id];switch (touchPoint.state()) { case QEventPoint::Released: m_id2PointList.remove(id); if (p_listPoint) { m_completePointLists.push_back(p_listPoint); m_completePenLists.push_back(pen); } continue; case QEventPoint::Pressed: pListPoint = new QList<QPointF>(); p_listPoint = new QList<QPointF>(); m_id2PointList[touchPoint.id()] = p_listPoint; default: { QPointF point = touchPoint.position(); p_listPoint->push_back(point); drawMyImage(); if (p_listPoint->size() >= 2){ QPointF pointA = p_listPoint->at(p_listPoint->size() - 2); QPointF pointB = p_listPoint->at(p_listPoint->size() - 1); QRect rect(qMin(pointA.x(), pointB.x()), qMin(pointA.y(), pointB.y()), qAbs(pointA.x() - pointB.x()), qAbs(pointA.y() - pointB.y())); rect.adjust(-10 , -10 , 10, 10); update(rect); } else { QRect rect(point.x(), point.y(), 0, 0); rect.adjust(-10 , -10 , 10, 10); update(rect); } } break; } } break; } default: return QWidget::event(event); } return true;
}
void RenderArea::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
const QRect rect = event->rect();
painter.drawImage(rect.topLeft(), mImage, rect);
}@inthewind2004
What is the lifetime of m_id2PointList and mImage?If m_id2PointList just gets longer and longer, no need to wonder about performance issues. QPainter is pretty optimized, but it is single-threaded and doesn't use GPU support. And Bezier lines are not the simplest thing to draw.
If you only ever want to add lines, then store the image, and only paint new lines on top of the existing image. That should be pretty fast.
-
Thank you for your advice. It was very helpful.
But, I found the following statement to be ineffective on the image:
painter.setRenderHint(QPainter::Antialiasing, true);
How do I set it? -
Thank you for your advice. It was very helpful.
But, I found the following statement to be ineffective on the image:
painter.setRenderHint(QPainter::Antialiasing, true);
How do I set it?@inthewind2004 said in QPainter Performance:
How do I set it?
I don't understand the question. You're already setting it.
-
Thank you for your advice. It was very helpful.
But, I found the following statement to be ineffective on the image:
painter.setRenderHint(QPainter::Antialiasing, true);
How do I set it?@inthewind2004 said in QPainter Performance:
Thank you for your advice. It was very helpful.
But, I found the following statement to be ineffective on the image:
painter.setRenderHint(QPainter::Antialiasing, true);
How do I set it?try:
painter.setRenderHint(QPainter::SmoothPixmapTransform); -
@inthewind2004 said in QPainter Performance:
Thank you for your advice. It was very helpful.
But, I found the following statement to be ineffective on the image:
painter.setRenderHint(QPainter::Antialiasing, true);
How do I set it?try:
painter.setRenderHint(QPainter::SmoothPixmapTransform);@mpergand Thanks.
I try to use the sentence above, but the effect is not very good.
Antialiasing has no effect on QImage?
But I didn't see similar conclusions in the help document. -
@mpergand Thanks.
I try to use the sentence above, but the effect is not very good.
Antialiasing has no effect on QImage?
But I didn't see similar conclusions in the help document.@inthewind2004 said in QPainter Performance:
Antialiasing has no effect on QImage?
Don't know,
try to convert to pixmap with:
QPixelMap::fromImage(const QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor) -
@inthewind2004 said in QPainter Performance:
Antialiasing has no effect on QImage?
Don't know,
try to convert to pixmap with:
QPixelMap::fromImage(const QImage &image, Qt::ImageConversionFlags flags = Qt::AutoColor)@mpergand I've tried, but the result is the same
-
@mpergand I've tried, but the result is the same
@inthewind2004
Antialiasing works as expected with QImage ...Show us what result you have with your image.
-
@inthewind2004
Antialiasing works as expected with QImage ...Show us what result you have with your image.
@mpergand Thanks.
I drew two circles on QImage, one with anti-aliasing on and one with anti-aliasing off. As a result, anti-aliasing is effective on QImage . The code is as follows:
void RenderArea::drawMyEllipse()
{
QPainter painter(&mImage);
//painter.setRenderHint(QPainter::Antialiasing, true);
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.setPen(QPen(Qt::red, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
painter.drawEllipse(300, 300, 300, 300);
update();
}
The result:
1.Antialiasing off
2.Antialiasing on
However, the Bezier curve drawn on QImage is not very smooth (compared to QWidget).The source code:
void RenderArea::drawToImage(MyLine *line)
{
QPainter painter(&mImage);
painter.setRenderHint(QPainter::Antialiasing, true);
painter.setCompositionMode(QPainter::CompositionMode_Source);
painter.setPen(QPen(Qt::red, 5, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
QPainterPath path = line->makeStrokePath(5);
painter.fillPath(path,Qt::red);
QRectF rect = path.boundingRect();
update(rect.x(), rect.y(), rect.width(), rect.height());
}
The result: