From ee2670460af760bf07c47950a8cca37bd6814f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B8rn=20Erik=20Nilsen?= Date: Thu, 20 Aug 2009 20:29:56 +0200 Subject: Fix the most obvious rendering bugs when applying an effect to QWidget. --- src/gui/kernel/qwidget.cpp | 95 ++++++++++++++++++++++++++++++++------ src/gui/kernel/qwidget_p.h | 8 +--- src/gui/kernel/qwidget_qws.cpp | 8 ++-- src/gui/kernel/qwidget_win.cpp | 4 +- src/gui/kernel/qwidget_x11.cpp | 4 +- src/gui/painting/qbackingstore.cpp | 37 +++++++++------ src/gui/painting/qbackingstore_p.h | 8 +++- 7 files changed, 118 insertions(+), 46 deletions(-) diff --git a/src/gui/kernel/qwidget.cpp b/src/gui/kernel/qwidget.cpp index a314cf19..a9ef6aa 100644 --- a/src/gui/kernel/qwidget.cpp +++ b/src/gui/kernel/qwidget.cpp @@ -1551,9 +1551,9 @@ bool QWidgetPrivate::isOverlapped(const QRect &rect) const continue; } - if (qRectIntersects(sibling->data->crect, r)) { + if (qRectIntersects(sibling->d_func()->effectiveRectFor(sibling->data->crect), r)) { const QWExtra *siblingExtra = sibling->d_func()->extra; - if (siblingExtra && siblingExtra->hasMask + if (siblingExtra && siblingExtra->hasMask && !sibling->d_func()->graphicsEffect && !siblingExtra->mask.translated(sibling->data->crect.topLeft()).intersects(r)) { continue; } @@ -1794,12 +1794,14 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt break; QWidgetPrivate *pd = w->parentWidget()->d_func(); const int myIndex = pd->children.indexOf(const_cast(w)); + const QRect widgetGeometry = w->d_func()->effectiveRectFor(w->data->crect); for (int i = myIndex + 1; i < pd->children.size(); ++i) { QWidget *sibling = qobject_cast(pd->children.at(i)); if (!sibling || !sibling->isVisible() || sibling->isWindow()) continue; - if (!qRectIntersects(sibling->data->crect, w->data->crect)) + const QRect siblingGeometry = sibling->d_func()->effectiveRectFor(sibling->data->crect); + if (!qRectIntersects(siblingGeometry, widgetGeometry)) continue; if (dirtyClipBoundingRect) { @@ -1807,7 +1809,7 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt dirtyClipBoundingRect = false; } - if (!qRectIntersects(sibling->data->crect, clipBoundingRect.translated(parentOffset))) + if (!qRectIntersects(siblingGeometry, clipBoundingRect.translated(parentOffset))) continue; if (dirtyParentClip) { @@ -1819,7 +1821,8 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt const QRect siblingClipRect(sibling->d_func()->clipRect()); QRegion siblingDirty(parentClip); siblingDirty &= (siblingClipRect.translated(siblingPos)); - const bool hasMask = sibling->d_func()->extra && sibling->d_func()->extra->hasMask; + const bool hasMask = sibling->d_func()->extra && sibling->d_func()->extra->hasMask + && !sibling->d_func()->graphicsEffect; if (hasMask) siblingDirty &= sibling->d_func()->extra->mask.translated(siblingPos); if (siblingDirty.isEmpty()) @@ -1830,7 +1833,7 @@ void QWidgetPrivate::subtractOpaqueSiblings(QRegion &sourceRegion, bool *hasDirt siblingDirty.translate(-parentOffset); sourceRegion -= siblingDirty; } else { - sourceRegion -= sibling->data->crect.translated(-parentOffset); + sourceRegion -= siblingGeometry.translated(-parentOffset); } } else { if (hasDirtySiblingsAbove) @@ -1861,6 +1864,11 @@ void QWidgetPrivate::clipToEffectiveMask(QRegion ®ion) const const QWidget *w = q; QPoint offset; + if (graphicsEffect) { + w = q->parentWidget(); + offset -= data.crect.topLeft(); + } + while (w) { const QWidgetPrivate *wd = w->d_func(); if (wd->extra && wd->extra->hasMask) @@ -5046,7 +5054,7 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP sourced->context = &context; if (!sharedPainter) { QPaintEngine *paintEngine = pdev->paintEngine(); - paintEngine->d_func()->systemClip = clipRect().translated(offset); + paintEngine->d_func()->systemClip = rgn.translated(offset); QPainter p(pdev); p.translate(offset); context.painter = &p; @@ -5054,7 +5062,10 @@ void QWidgetPrivate::drawWidget(QPaintDevice *pdev, const QRegion &rgn, const QP paintEngine->d_func()->systemClip = QRegion(); } else { context.painter = sharedPainter; + sharedPainter->save(); + sharedPainter->translate(offset); graphicsEffect->draw(sharedPainter, source); + sharedPainter->restore(); } sourced->context = 0; return; @@ -5195,7 +5206,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis dirtyBoundingRect = false; } - if (qRectIntersects(boundingRect, x->data->crect)) { + if (qRectIntersects(boundingRect, x->d_func()->effectiveRectFor(x->data->crect))) { #ifdef Q_BACKINGSTORE_SUBSURFACES if (x->windowSurface() == currentSurface) #endif @@ -5213,7 +5224,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis QWidgetPrivate *wd = w->d_func(); const QPoint widgetPos(w->data->crect.topLeft()); - const bool hasMask = wd->extra && wd->extra->hasMask; + const bool hasMask = wd->extra && wd->extra->hasMask && !wd->graphicsEffect; if (index > 0) { QRegion wr(rgn); @@ -5228,7 +5239,7 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis if (w->updatesEnabled() && (!w->d_func()->extra || !w->d_func()->extra->proxyWidget)) { QRegion wRegion(rgn); - wRegion &= w->data->crect; + wRegion &= wd->effectiveRectFor(w->data->crect); wRegion.translate(-widgetPos); if (hasMask) wRegion &= wd->extra->mask; @@ -5236,6 +5247,20 @@ void QWidgetPrivate::paintSiblingsRecursive(QPaintDevice *pdev, const QObjectLis } } +QRectF QWidgetEffectSourcePrivate::boundingRect(Qt::CoordinateSystem system) const +{ + if (system != Qt::DeviceCoordinates) + return m_widget->rect(); + + if (!context) { + // Device coordinates without context not yet supported. + qWarning("QGraphicsEffectSource::boundingRect: Not yet implemented, lacking device context"); + return QRectF(); + } + + return context->painter->worldTransform().mapRect(m_widget->rect()); +} + void QWidgetEffectSourcePrivate::draw(QPainter *painter) { if (!context || context->painter != painter) { @@ -5243,24 +5268,64 @@ void QWidgetEffectSourcePrivate::draw(QPainter *painter) return; } - qt_widget_private(m_widget)->drawWidget(context->pdev, context->rgn, context->offset, context->flags, - context->sharedPainter, context->backingStore); + // The region saved in the context is neither clipped to the rect + // nor the mask, so we have to clip it here before calling drawWidget. + QRegion toBePainted = context->rgn; + toBePainted &= m_widget->rect(); + QWidgetPrivate *wd = qt_widget_private(m_widget); + if (wd->extra && wd->extra->hasMask) + toBePainted &= wd->extra->mask; + + wd->drawWidget(context->pdev, toBePainted, context->offset, context->flags, + context->sharedPainter, context->backingStore); } QPixmap QWidgetEffectSourcePrivate::pixmap(Qt::CoordinateSystem system, QPoint *offset) const { const bool deviceCoordinates = (system == Qt::DeviceCoordinates); + if (!context && deviceCoordinates) { + // Device coordinates without context not yet supported. + qWarning("QGraphicsEffectSource::pixmap: Not yet implemented, lacking device context"); + return QPixmap(); + } + QPoint pixmapOffset; + QRectF sourceRect = m_widget->rect(); - QRect sourceRect(m_widget->rect()); if (deviceCoordinates) { - pixmapOffset = m_widget->mapTo(m_widget->window(), QPoint()); - sourceRect.translate(pixmapOffset); + const QTransform &painterTransform = context->painter->worldTransform(); + sourceRect = painterTransform.mapRect(sourceRect); + pixmapOffset = painterTransform.map(pixmapOffset); } QRect effectRect = m_widget->graphicsEffect()->boundingRectFor(sourceRect).toAlignedRect(); if (offset) *offset = effectRect.topLeft(); + + if (deviceCoordinates) { + // Clip to device rect. + int left, top, right, bottom; + effectRect.getCoords(&left, &top, &right, &bottom); + if (left < 0) { + if (offset) + offset->rx() += -left; + effectRect.setX(0); + } + if (top < 0) { + if (offset) + offset->ry() += -top; + effectRect.setY(0); + } + // NB! We use +-1 for historical reasons (see QRect documentation). + QPaintDevice *device = context->painter->device(); + const int deviceWidth = device->width(); + const int deviceHeight = device->height(); + if (right + 1 > deviceWidth) + effectRect.setRight(deviceWidth - 1); + if (bottom + 1 > deviceHeight) + effectRect.setBottom(deviceHeight -1); + } + pixmapOffset -= effectRect.topLeft(); QPixmap pixmap(effectRect.size()); diff --git a/src/gui/kernel/qwidget_p.h b/src/gui/kernel/qwidget_p.h index acfb6c3..69fcbbc 100644 --- a/src/gui/kernel/qwidget_p.h +++ b/src/gui/kernel/qwidget_p.h @@ -694,13 +694,7 @@ public: inline QRect deviceRect() const { return m_widget->window()->rect(); } - inline QRectF boundingRect(Qt::CoordinateSystem system) const - { - if (system == Qt::LogicalCoordinates) - return m_widget->rect(); - return m_widget->rect().translated(m_widget->mapTo(m_widget->window(), QPoint())); - } - + QRectF boundingRect(Qt::CoordinateSystem system) const; void draw(QPainter *p); QPixmap pixmap(Qt::CoordinateSystem system, QPoint *offset) const; diff --git a/src/gui/kernel/qwidget_qws.cpp b/src/gui/kernel/qwidget_qws.cpp index ea3cef2..98d48de 100644 --- a/src/gui/kernel/qwidget_qws.cpp +++ b/src/gui/kernel/qwidget_qws.cpp @@ -258,7 +258,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) Q_D(QWidget); if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(geometry()); + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); d->deactivateWidgetCleanup(); if (testAttribute(Qt::WA_WState_Created)) { @@ -312,7 +312,7 @@ void QWidgetPrivate::setParent_sys(QWidget *newparent, Qt::WindowFlags f) Q_Q(QWidget); bool wasCreated = q->testAttribute(Qt::WA_WState_Created); if (q->isVisible() && q->parentWidget() && parent != q->parentWidget()) - q->parentWidget()->d_func()->invalidateBuffer(q->geometry()); + q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); #ifndef QT_NO_CURSOR QCursor oldcurs; bool setcurs=q->testAttribute(Qt::WA_SetCursor); @@ -805,7 +805,7 @@ void QWidgetPrivate::lower_sys() QWSChangeAltitudeCommand::Lower); } else if (QWidget *p = q->parentWidget()) { setDirtyOpaqueRegion(); - p->d_func()->invalidateBuffer(q->geometry()); + p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); } } @@ -814,7 +814,7 @@ void QWidgetPrivate::stackUnder_sys(QWidget*) Q_Q(QWidget); if (QWidget *p = q->parentWidget()) { setDirtyOpaqueRegion(); - p->d_func()->invalidateBuffer(q->geometry()); + p->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); } } diff --git a/src/gui/kernel/qwidget_win.cpp b/src/gui/kernel/qwidget_win.cpp index 77ab590..5f7bb5a 100644 --- a/src/gui/kernel/qwidget_win.cpp +++ b/src/gui/kernel/qwidget_win.cpp @@ -535,7 +535,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) { Q_D(QWidget); if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(geometry()); + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); d->deactivateWidgetCleanup(); if (testAttribute(Qt::WA_WState_Created)) { setAttribute(Qt::WA_WState_Created, false); @@ -597,7 +597,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) Q_Q(QWidget); bool wasCreated = q->testAttribute(Qt::WA_WState_Created); if (q->isVisible() && q->parentWidget() && parent != q->parentWidget()) - q->parentWidget()->d_func()->invalidateBuffer(q->geometry()); + q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); WId old_winid = data.winid; // hide and reparent our own window away. Otherwise we might get diff --git a/src/gui/kernel/qwidget_x11.cpp b/src/gui/kernel/qwidget_x11.cpp index de38b4c..a4ee21b 100644 --- a/src/gui/kernel/qwidget_x11.cpp +++ b/src/gui/kernel/qwidget_x11.cpp @@ -971,7 +971,7 @@ void QWidget::destroy(bool destroyWindow, bool destroySubWindows) { Q_D(QWidget); if (!isWindow() && parentWidget()) - parentWidget()->d_func()->invalidateBuffer(geometry()); + parentWidget()->d_func()->invalidateBuffer(d->effectiveRectFor(geometry())); d->deactivateWidgetCleanup(); if (testAttribute(Qt::WA_WState_Created)) { setAttribute(Qt::WA_WState_Created, false); @@ -1050,7 +1050,7 @@ void QWidgetPrivate::setParent_sys(QWidget *parent, Qt::WindowFlags f) QTLWExtra *topData = maybeTopData(); bool wasCreated = q->testAttribute(Qt::WA_WState_Created); if (q->isVisible() && q->parentWidget() && parent != q->parentWidget()) - q->parentWidget()->d_func()->invalidateBuffer(q->geometry()); + q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(q->geometry())); extern void qPRCreate(const QWidget *, Window); #ifndef QT_NO_CURSOR QCursor oldcurs; diff --git a/src/gui/painting/qbackingstore.cpp b/src/gui/painting/qbackingstore.cpp index fdbdef0..1c31e33 100644 --- a/src/gui/painting/qbackingstore.cpp +++ b/src/gui/painting/qbackingstore.cpp @@ -559,7 +559,8 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up } const QPoint offset = widget->mapTo(tlw, QPoint()); - if (qt_region_strictContains(dirty, widget->rect().translated(offset))) { + const QRect widgetRect = widget->d_func()->effectiveRectFor(widget->rect()); + if (qt_region_strictContains(dirty, widgetRect.translated(offset))) { if (updateImmediately) sendUpdateRequest(tlw, updateImmediately); return; // Already dirty. @@ -567,7 +568,10 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up if (invalidateBuffer) { const bool eventAlreadyPosted = !dirty.isEmpty(); - dirty += rgn.translated(offset); + if (widget->d_func()->graphicsEffect) + dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()).translated(offset); + else + dirty += rgn.translated(offset); if (!eventAlreadyPosted || updateImmediately) sendUpdateRequest(tlw, updateImmediately); return; @@ -580,8 +584,12 @@ void QWidgetBackingStore::markDirty(const QRegion &rgn, QWidget *widget, bool up } if (widget->d_func()->inDirtyList) { - if (!qt_region_strictContains(widget->d_func()->dirty, widget->rect())) - widget->d_func()->dirty += rgn; + if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) { + if (widget->d_func()->graphicsEffect) + widget->d_func()->dirty += widget->d_func()->effectiveRectFor(rgn.boundingRect()); + else + widget->d_func()->dirty += rgn; + } } else { addDirtyWidget(widget, rgn); } @@ -625,7 +633,8 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool upd return; } - const QRect translatedRect(rect.translated(widget->mapTo(tlw, QPoint()))); + const QRect widgetRect = widget->d_func()->effectiveRectFor(rect); + const QRect translatedRect(widgetRect.translated(widget->mapTo(tlw, QPoint()))); if (qt_region_strictContains(dirty, translatedRect)) { if (updateImmediately) sendUpdateRequest(tlw, updateImmediately); @@ -647,8 +656,8 @@ void QWidgetBackingStore::markDirty(const QRect &rect, QWidget *widget, bool upd } if (widget->d_func()->inDirtyList) { - if (!qt_region_strictContains(widget->d_func()->dirty, rect)) - widget->d_func()->dirty += rect; + if (!qt_region_strictContains(widget->d_func()->dirty, widgetRect)) + widget->d_func()->dirty += widgetRect; } else { addDirtyWidget(widget, rect); } @@ -880,7 +889,7 @@ void QWidgetPrivate::moveRect(const QRect &rect, int dx, int dy) && !isOverlapped(sourceRect) && !isOverlapped(destRect); if (!accelerateMove) { - QRegion parentR(parentRect); + QRegion parentR(effectiveRectFor(parentRect)); if (!extra || !extra->hasMask) { parentR -= newRect; } else { @@ -1384,7 +1393,7 @@ void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const Q const QRect newWidgetRect(q->rect()); const QRect oldWidgetRect(0, 0, oldSize.width(), oldSize.height()); - if (!staticContents) { + if (!staticContents || graphicsEffect) { QRegion staticChildren; QWidgetBackingStore *bs = 0; if (offset.isNull() && (bs = maybeBackingStore())) @@ -1404,19 +1413,19 @@ void QWidgetPrivate::invalidateBuffer_resizeHelper(const QPoint &oldPos, const Q return; // Invalidate newly exposed area of the parent. - if (extra && extra->hasMask) { + if (!graphicsEffect && extra && extra->hasMask) { QRegion parentExpose(extra->mask.translated(oldPos)); parentExpose &= QRect(oldPos, oldSize); if (hasStaticChildren) parentExpose -= data.crect; // Offset is unchanged, safe to do this. q->parentWidget()->d_func()->invalidateBuffer(parentExpose); } else { - if (hasStaticChildren) { + if (hasStaticChildren && !graphicsEffect) { QRegion parentExpose(QRect(oldPos, oldSize)); parentExpose -= data.crect; // Offset is unchanged, safe to do this. q->parentWidget()->d_func()->invalidateBuffer(parentExpose); } else { - q->parentWidget()->d_func()->invalidateBuffer(QRect(oldPos, oldSize)); + q->parentWidget()->d_func()->invalidateBuffer(effectiveRectFor(QRect(oldPos, oldSize))); } } return; @@ -1474,7 +1483,7 @@ void QWidgetPrivate::invalidateBuffer(const QRegion &rgn) QRegion wrgn(rgn); wrgn &= clipRect(); - if (extra && extra->hasMask) + if (!graphicsEffect && extra && extra->hasMask) wrgn &= extra->mask; if (wrgn.isEmpty()) return; @@ -1502,7 +1511,7 @@ void QWidgetPrivate::invalidateBuffer(const QRect &rect) if (wRect.isEmpty()) return; - if (!extra || !extra->hasMask) { + if (graphicsEffect || !extra || !extra->hasMask) { tlwExtra->backingStore->markDirty(wRect, q, false, true); return; } diff --git a/src/gui/painting/qbackingstore_p.h b/src/gui/painting/qbackingstore_p.h index ddc0a59..b21d504 100644 --- a/src/gui/painting/qbackingstore_p.h +++ b/src/gui/painting/qbackingstore_p.h @@ -144,9 +144,13 @@ private: inline void addDirtyWidget(QWidget *widget, const QRegion &rgn) { if (widget && !widget->d_func()->inDirtyList && !widget->data->in_destructor) { - widget->d_func()->dirty = rgn; + QWidgetPrivate *widgetPrivate = widget->d_func(); + if (widgetPrivate->graphicsEffect) + widgetPrivate->dirty = widgetPrivate->effectiveRectFor(rgn.boundingRect()); + else + widgetPrivate->dirty = rgn; dirtyWidgets.append(widget); - widget->d_func()->inDirtyList = true; + widgetPrivate->inDirtyList = true; } } -- cgit v0.12