From 9b34bfb45eee81bf486c02eaed23d20a1ba68446 Mon Sep 17 00:00:00 2001 From: Oliver Sander Date: Sat, 29 Jun 2019 21:53:17 +0200 Subject: [PATCH] Make page boundary rendering more robust Okular renders a simple 'shadow' at the right and the bottom of each page. The code for this is a bit fragile: After the page is rendered, Okular paints a black outline, and then the shadow. The shadow is a hand-implemented gradient, painted line by line. Finally, the remaining area is painted in the background color. No pixel is ever touched twice. Unfortunately, the code is buggy, and in hidpi / fractional scaling situations, some pixels are never touched. This results in rendering glitches as reported in https://bugs.kde.org/show_bug.cgi?id=383943 Instead of trying to fix the previous approach, this patch makes the code more robust by changing the order of the painting operations. After painting the page, the code now first paints the background, which is now enlarged to cover everything off the page. Finally, the black outline and shadow are drawn on the previously drawn background. This makes sures that no pixel is ever left uninitialized. It also allows to simplify the shadow drawing operation a bit. BUG: 383943 --- ui/pageview.cpp | 82 ++++++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 35 deletions(-) diff --git a/ui/pageview.cpp b/ui/pageview.cpp index 1cbcefafe..741ac2ef5 100644 --- a/ui/pageview.cpp +++ b/ui/pageview.cpp @@ -3500,55 +3500,24 @@ void PageView::drawDocumentOnPainter( const QRect & contentsRect, QPainter * p ) else backColor = viewport()->palette().color( QPalette::Dark ); - // when checking if an Item is contained in contentsRect, instead of - // growing PageViewItems rects (for keeping outline into account), we - // grow the contentsRect - QRect checkRect = contentsRect; - checkRect.adjust( -3, -3, 1, 1 ); - // create a region from which we'll subtract painted rects QRegion remainingArea( contentsRect ); + // This loop draws the actual pages // iterate over all items painting the ones intersecting contentsRect for ( const PageViewItem * item : qAsConst( d->items ) ) { // check if a piece of the page intersects the contents rect - if ( !item->isVisible() || !item->croppedGeometry().intersects( checkRect ) ) + if ( !item->isVisible() || !item->croppedGeometry().intersects( contentsRect ) ) continue; // get item and item's outline geometries - QRect itemGeometry = item->croppedGeometry(), - outlineGeometry = itemGeometry; - outlineGeometry.adjust( -1, -1, 3, 3 ); + QRect itemGeometry = item->croppedGeometry(); // move the painter to the top-left corner of the real page p->save(); p->translate( itemGeometry.left(), itemGeometry.top() ); - // draw the page outline (black border and 2px bottom-right shadow) - if ( !itemGeometry.contains( contentsRect ) ) - { - int itemWidth = itemGeometry.width(), - itemHeight = itemGeometry.height(); - // draw simple outline - p->setPen( Qt::black ); - p->drawRect( -1, -1, itemWidth + 1, itemHeight + 1 ); - // draw bottom/right gradient - static const int levels = 2; - int r = backColor.red() / (levels + 2) + 6, - g = backColor.green() / (levels + 2) + 6, - b = backColor.blue() / (levels + 2) + 6; - for ( int i = 0; i < levels; i++ ) - { - p->setPen( QColor( r * (i+2), g * (i+2), b * (i+2) ) ); - p->drawLine( i, i + itemHeight + 1, i + itemWidth + 1, i + itemHeight + 1 ); - p->drawLine( i + itemWidth + 1, i, i + itemWidth + 1, i + itemHeight ); - p->setPen( backColor ); - p->drawLine( -1, i + itemHeight + 1, i - 1, i + itemHeight + 1 ); - p->drawLine( i + itemWidth + 1, -1, i + itemWidth + 1, i - 1 ); - } - } - // draw the page using the PagePainter with all flags active if ( contentsRect.intersects( itemGeometry ) ) { @@ -3567,13 +3536,56 @@ void PageView::drawDocumentOnPainter( const QRect & contentsRect, QPainter * p ) } // remove painted area from 'remainingArea' and restore painter - remainingArea -= outlineGeometry.intersected( contentsRect ); + remainingArea -= itemGeometry; p->restore(); } // fill the visible area around the page with the background color for (const QRect& backRect : remainingArea ) p->fillRect( backRect, backColor ); + + // take outline and shadow into account when testing whether a repaint is necessary + auto dpr = devicePixelRatioF(); + QRect checkRect = contentsRect; + checkRect.adjust( -3, -3, 1, 1 ); + + // iterate over all items painting a black outline and a simple bottom/right gradient + for ( const PageViewItem * item : qAsConst( d->items ) ) + { + // check if a piece of the page intersects the contents rect + if ( !item->isVisible() || !item->croppedGeometry().intersects( checkRect ) ) + continue; + + // get item and item's outline geometries + QRect itemGeometry = item->croppedGeometry(); + + // move the painter to the top-left corner of the real page + p->save(); + p->translate( itemGeometry.left(), itemGeometry.top() ); + + // draw the page outline (black border and 2px bottom-right shadow) + if ( !itemGeometry.contains( contentsRect ) ) + { + int itemWidth = itemGeometry.width(); + int itemHeight = itemGeometry.height(); + // draw simple outline + p->setPen( Qt::black ); + p->drawRect( -1, -1, itemWidth + 1, itemHeight + 1 ); + // draw bottom/right gradient + static const int levels = 2; + int r = backColor.red() / (levels + 2) + 6, + g = backColor.green() / (levels + 2) + 6, + b = backColor.blue() / (levels + 2) + 6; + for ( int i = 0; i < levels; i++ ) + { + p->setPen( QColor( r * (i+2), g * (i+2), b * (i+2) ) ); + p->drawLine( i, i + itemHeight + 1, i + itemWidth + 1, i + itemHeight + 1 ); + p->drawLine( i + itemWidth + 1, i, i + itemWidth + 1, i + itemHeight ); + } + } + + p->restore(); + } } void PageView::updateItemSize( PageViewItem * item, int colWidth, int rowHeight )