[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-kimageshop
Subject:    stuckness :-(
From:       Boudewijn Rempt <boud () valdyas ! org>
Date:       2007-09-27 20:56:23
Message-ID: 200709272256.25863.boud () valdyas ! org
[Download RAW message or body]

[Attachment #2 (multipart/signed)]

[Attachment #4 (multipart/mixed)]


I've been busy before I lost my last laptop and after I got my new one working 
on the qpainter canvas. This canvas is pretty essential, since it's the 
default and there's a lot of variation in what different systems support. 
I've made it extremely configurable:

* it can either get the image pixels from KisImage using nearest neighbour, or 
cache the KisImage projection as a QImage (with a size limit)
* it can smootscale using blitz or qt for scale < 1.0
* it can optionally smooth using Casper's fixed routine between 1.0 and 2.0
* it can create a qpixmap or a qimage (at least, htere's api for that, but no 
implementation yet)
* and there are more settings and options to make sure it works on 32 and 64 
bit systems, xrender and no xrender

But I'm stuck; even with the "normal" path (caching, blitz < 1.0, sample 
1.0-2.0, qpainter scaling > 2.0), I get artefacts and the canvas thinks the 
image is bigger than it really is.

I don't want to commit, since it breaks Krita horribly, but I would appreciate 
any help! So here's the patch, if the mailing list lets it through...

-- 
Boudewijn Rempt 
http://www.valdyas.org/fading/index.cgi

["projection.diff" (text/x-diff)]

Index: kis_prescaled_projection.cpp
===================================================================
--- kis_prescaled_projection.cpp	(revision 717978)
+++ kis_prescaled_projection.cpp	(working copy)
@@ -25,6 +25,7 @@
 #include <QPoint>
 #include <QSize>
 #include <QPainter>
+#include <QTimer>
 
 #include <qimageblitz.h>
 
@@ -95,10 +96,10 @@
     /*
       Sample each row.
     */
-    j=(-1);
-    for (y=0; y < (long) sample_image.height(); y++)
+    j = (-1);
+    for (y = 0; y < (long) sample_image.height(); y++)
     {
-        q= sample_image.scanLine( y );
+        q = sample_image.scanLine( y );
         if (j != y_offset[y] )
         {
             /*
@@ -111,9 +112,9 @@
         /*
           Sample each column.
         */
-        for (x=0; x < (long) sample_image.width(); x++)
+        for (x = 0; x < (long) sample_image.width(); x++)
         {
-            *(QRgb*)q=((QRgb*)pixels)[ x_offset[x] ];
+            *(QRgb*)q = ((QRgb*)pixels)[ x_offset[x] ];
             q += 4;
         }
     }
@@ -124,8 +125,6 @@
     return sample_image;
 }
 
-
-
 struct KisPrescaledProjection::Private
 {
     Private()
@@ -134,7 +133,7 @@
         , useNearestNeighbour( false )
         , useQtScaling( false )
         , useSampling( false )
-        , useSmoothScaling( true ) // Default
+        , smoothBetween100And200Percent( true )
         , drawCheckers( false )
         , scrollCheckers( false )
         , drawMaskVisualisationOnUnscaledCanvasCache( false )
@@ -148,6 +147,7 @@
         , monitorProfile( 0 )
         , exposure( 0.0 )
         {
+            smoothingTimer.setSingleShot( true );
         }
     bool updateAllOfQPainterCanvas;
     bool useDeferredSmoothing; // first sample, then smoothscale when
@@ -158,26 +158,30 @@
                        // 1.0
     bool useSampling; // use the above sample function instead
                       // qpainter's built-in scaling when zoom > 1.0
-    bool useSmoothScaling; // Use blitz' smootscale when zoom < 1.0
+    bool smoothBetween100And200Percent; // if true, when zoom is
+                                        // between 1.0 and 2.0,
+                                        // smoothscale
     bool drawCheckers;
     bool scrollCheckers;
     bool drawMaskVisualisationOnUnscaledCanvasCache;
     bool cacheKisImageAsQImage;
     bool showMask;
+
     QColor checkersColor;
     qint32 checkSize;
     QImage unscaledCache;
     QImage prescaledQImage;
     QPixmap prescaledPixmap;
-    QPoint documentOffset;
-    QSize canvasSize;
-    QSize imageSize;
+    QPoint documentOffset; // in view pixels
+    QSize canvasSize; // in view pixels
+    QSize imageSize; // in kisimage pixels
     KisImageSP image;
     KoViewConverter * viewConverter;
-    KoColorProfile * monitorProfile;
+    const KoColorProfile * monitorProfile;
     float exposure;
     KisNodeSP currentNode;
-    QTimer t;
+    QTimer smoothingTimer;
+    QRegion rectsToSmooth;
 };
 
 KisPrescaledProjection::KisPrescaledProjection()
@@ -185,7 +189,7 @@
     , m_d( new Private() )
 {
     updateSettings();
-    connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), \
SLOT(updateSettings())); +    connect( &m_d->smoothingTimer, SIGNAL( timeout() ), \
this, SLOT( slotDoSmoothScale() ) );  }
 
 KisPrescaledProjection::~KisPrescaledProjection()
@@ -196,9 +200,28 @@
 
 void KisPrescaledProjection::setImage( KisImageSP image )
 {
+    Q_ASSERT( image );
     m_d->image = image;
+    setImageSize( image->width(), image->height() );
 }
 
+void KisPrescaledProjection::setImageSize(qint32 w, qint32 h)
+{
+    kDebug() << "Setting image size from " << m_d->imageSize << " to " << w << ", " \
<< h; +    // XXX: Make the limit of 50 megs configurable
+    if ( w * h * 4 > 50 * 1024 * 1024 ) {
+        m_d->prescaledQImage = QImage();
+        m_d->cacheKisImageAsQImage = false;
+    }
+
+    m_d->imageSize = QSize( w, h );
+    if ( !m_d->useNearestNeighbour && m_d->cacheKisImageAsQImage ) {
+        m_d->unscaledCache = QImage( w, h, QImage::Format_ARGB32 );
+        updateCanvasProjection( QRect( 0, 0, w, h ) );
+    }
+}
+
+
 bool KisPrescaledProjection::drawCheckers() const
 {
     return m_d->drawCheckers;
@@ -230,78 +253,114 @@
     KisConfig cfg;
     m_d->updateAllOfQPainterCanvas = cfg.updateAllOfQPainterCanvas();
     m_d->useDeferredSmoothing = cfg.useDeferredSmoothing();
-    m_d->useNearestNeighbour = cfg.fastZoom();
+    m_d->useNearestNeighbour = cfg.useNearestNeigbour();
     m_d->useQtScaling = cfg.useQtSmoothScaling();
     m_d->useSampling = cfg.useSampling();
     // If any of the above are true, we don't use our own smooth scaling
-    m_d->useSmoothScaling = !( m_d->useNearestNeighbour ||
-                               m_d->useSampling ||
-                               m_d->useQtScaling ||
-                               m_d->useDeferredSmoothing );
     m_d->scrollCheckers = cfg.scrollCheckers();
     m_d->checkSize = cfg.checkSize();
     m_d->checkersColor = cfg.checkersColor();
+
     m_d->cacheKisImageAsQImage = cfg.cacheKisImageAsQImage();
+    if ( !m_d->cacheKisImageAsQImage ) m_d->unscaledCache = QImage();
+
     m_d->drawMaskVisualisationOnUnscaledCanvasCache = \
cfg.drawMaskVisualisationOnUnscaledCanvasCache(); +
+    // XXX: Make config setting
+    m_d->smoothBetween100And200Percent = true;
+
+    kDebug(41010) << "QPainter canvas will render with the following options:\n"
+                  << "\t updateAllOfQPainterCanvas: " << \
m_d->updateAllOfQPainterCanvas << "\n" +                  << "\t \
useDeferredSmoothing: " << m_d->useDeferredSmoothing << "\n" +                  << \
"\t useNearestNeighbour: " << m_d->useNearestNeighbour << "\n" +                  << \
"\t useQtScaling: " << m_d->useQtScaling << "\n" +                  << "\t \
useSampling: " << m_d->useSampling << "\n" +                  << "\t \
smoothBetween100And200Percent: " << m_d->smoothBetween100And200Percent;  }
 
 void KisPrescaledProjection::documentOffsetMoved( const QPoint &documentOffset )
 {
+    qDebug() << "documentOffsetMoved " << m_d->documentOffset << ", to " << \
documentOffset;  m_d->documentOffset = documentOffset;
-}
 
-void KisPrescaledProjection::updateCanvasProjection( const QRect & rc )
-{
+    // We've called documentOffsetMoved before even updating the projection
+    if ( m_d->prescaledQImage.isNull() ) {
 
-    // We cache the KisImage as a QImage
-    if ( !m_d->useNearestNeighbour ) {
+        return;
+    }
 
-        if ( m_d->cacheKisImageAsQImage ) {
+    preScale();
+// Let someone else figure out the optimization where we copy the
+// still visible part of the image after moving the offset and then
+// only draw the newly visible parts
+#if 0
 
-            QPainter p( &m_d->unscaledCache );
-            p.setCompositionMode( QPainter::CompositionMode_Source );
+    qint32 width = m_d->prescaledQImage.width();
+    qint32 height = m_d->prescaledQImage.height();
 
-            QImage updateImage = m_d->image->convertToQImage(rc.x(), rc.y(), \
                rc.width(), rc.height(),
-                                                             m_d->monitorProfile,
-                                                             m_d->exposure);
+    QRegion exposedRegion = QRect(0, 0, width, height);
 
-            if ( m_d->showMask && m_d->drawMaskVisualisationOnUnscaledCanvasCache ) \
{ +    qint32 oldCanvasXOffset = m_d->documentOffset.x();
+    qint32 oldCanvasYOffset = m_d->documentOffset.y();
 
-                // XXX: Also visualize the global selection
+    qDebug() << "w: " << width << ", h" << height << ", oldCanvasXOffset " << \
oldCanvasXOffset << ", oldCanvasYOffset " << oldCanvasYOffset +             << ", new \
offset: " << documentOffset;  
-                KisSelectionSP selection = 0;
-                if ( m_d->currentNode->inherits( "KisMask" ) ) {
-                    selection = dynamic_cast<const KisMask*>( \
                m_d->currentNode.data() )->selection();
-                }
-                else if ( m_d->currentNode->inherits( "KisLayer" ) ) {
+    m_d->documentOffset = documentOffset;
 
-                    KisLayerSP layer = dynamic_cast<KisLayer*>( \
                m_d->currentNode.data() );
-                    if ( KisSelectionMaskSP selectionMask = layer->selectionMask() ) \
                {
-                        selection = selectionMask->selection();
-                    }
+    QImage img = QImage( width, height, QImage::Format_ARGB32 );
+    QPainter gc( &img );
+    gc.fillRect( 0, 0, width, height, QColor( 0, 0, 0, 0 ) );
+    gc.setCompositionMode( QPainter::CompositionMode_Source );
 
-                    // XXX: transitional! Remove when we use
-                    // KisSelectionMask instead of the selection in the
-                    // paintdevice. That way we can also select on groups etc.
-                    if ( !selection && m_d->currentNode->inherits( "KisPaintLayer" ) \
                ) {
-                        KisPaintDeviceSP dev = ( dynamic_cast<KisPaintLayer*>( \
                m_d->currentNode.data() ) )->paintDevice();
-                        if ( dev ) {
-                            selection = dev->selection();
-                        }
-                    }
-                }
+    if (oldCanvasXOffset != m_d->documentOffset.x() || oldCanvasYOffset != \
m_d->documentOffset.y()) {  
-                QTime t;
-                t.start();
-                selection->paint(&updateImage, rc);
-                kDebug(41010) << "Mask visualisation rendering took: " << \
                t.elapsed();
-            }
+        qint32 deltaX =  oldCanvasXOffset - m_d->documentOffset.x();
+        qint32 deltaY = oldCanvasYOffset - m_d->documentOffset.y();
 
-            p.drawImage( rc.x(), rc.y(), updateImage, 0, 0, rc.width(), rc.height() \
                );
-            p.end();
+        qDebug() << "deltaX: " << deltaX << ", deltaY: " << deltaY;
+
+        gc.drawImage( deltaX, deltaY, m_d->prescaledQImage );
+        exposedRegion -= QRegion(QRect(0, 0, width - deltaX, height - deltaY));
+    }
+
+
+    if (!exposedRegion.isEmpty()) {
+
+        QVector<QRect> rects = exposedRegion.rects();
+
+        for (int i = 0; i < rects.count(); i++) {
+            QRect r = rects[i];
+            // Set the areas to empty. Who knows, there may be not
+            // enough image to draw in them.
+            gc.fillRect( r, QColor( 0, 0, 0, 0 ) );
+            qDebug() << "rect" << r;
+            // And conver the rect to document pixels, because that's
+            // what drawScaledImage expects.
+            drawScaledImage( imageRectFromViewPortPixels( r ), gc);
         }
     }
+    m_d->prescaledQImage = img;
+#endif
+}
 
+void KisPrescaledProjection::updateCanvasProjection( const QRect & rc )
+{
+    qDebug() << "updateCanvasProjection " << rc;
+    if ( !m_d->image ) {
+        kDebug() << "Calling updateCanvasProjection without an image: " << \
kBacktrace() << endl; +        return;
+    }
+
+    // We cache the KisImage as a QImage
+    if ( !m_d->useNearestNeighbour ) {
+
+        if ( m_d->cacheKisImageAsQImage ) {
+
+            updateUnscaledCache( rc );
+        }
+    }
+
     QRect vRect;
 
     if ( m_d->updateAllOfQPainterCanvas ) {
@@ -317,15 +376,8 @@
 
 }
 
-void KisPrescaledProjection::setImageSize(qint32 w, qint32 h)
-{
-    m_d->imageSize = QSize( w, h );
-    if ( !m_d->useNearestNeighbour || !m_d->cacheKisImageAsQImage ) {
-        m_d->unscaledCache = QImage( w, h, QImage::Format_ARGB32 );
-    }
-}
 
-void KisPrescaledProjection::setMonitorProfile( KoColorProfile * profile )
+void KisPrescaledProjection::setMonitorProfile( const KoColorProfile * profile )
 {
     m_d->monitorProfile = profile;
 }
@@ -348,6 +400,7 @@
 
 void KisPrescaledProjection::preScale()
 {
+    Q_ASSERT( m_d->canvasSize.isValid() );
     preScale( QRect( QPoint( 0, 0 ), m_d->canvasSize ) );
 }
 
@@ -358,18 +411,31 @@
         t.start();
         QPainter gc( &m_d->prescaledQImage );
         gc.setCompositionMode( QPainter::CompositionMode_Source );
+        gc.fillRect( rc, QColor( 0, 0, 0, 0 ) );
         drawScaledImage( rc, gc);
         kDebug(41010) <<"Prescaling took" << t.elapsed();
     }
 
 }
 
-void KisPrescaledProjection::resizePrescaledImage( QSize newSize, QSize oldSize )
+void KisPrescaledProjection::resizePrescaledImage( QSize newSize )
 {
 
+
     QTime t;
     t.start();
 
+    QSize oldSize;
+
+    if ( m_d->prescaledQImage.isNull() ) {
+        oldSize = QSize( 0, 0 );
+    }
+    else {
+        oldSize = m_d->prescaledQImage.size();
+    }
+
+    qDebug() << "resizePrescaledImage from " << oldSize << " to " << newSize << \
endl; +
     QImage img = QImage(newSize, QImage::Format_ARGB32);
     QPainter gc( &img );
     gc.setCompositionMode( QPainter::CompositionMode_Source );
@@ -382,7 +448,6 @@
 
         QRegion r( QRect( 0, 0, newSize.width(), newSize.height() ) );
         r -= QRegion( QRect( 0, 0, m_d->prescaledQImage.width(), \
                m_d->prescaledQImage.height() ) );
-
         foreach( QRect rc, r.rects() ) {
             drawScaledImage( rc, gc );
         }
@@ -391,6 +456,7 @@
         gc.drawImage( 0, 0, m_d->prescaledQImage,
                       0, 0, m_d->prescaledQImage.width(), \
m_d->prescaledQImage.height() );  }
+
     m_d->prescaledQImage = img;
     m_d->canvasSize = newSize;
 
@@ -398,11 +464,13 @@
 
 }
 
-void KisPrescaledProjection::drawScaledImage( const QRect & rc,  QPainter & gc )
+void KisPrescaledProjection::drawScaledImage( const QRect & rc,  QPainter & gc, bool \
isDeferredAction )  {
     if ( !m_d->image )
         return;
 
+    Q_ASSERT( m_d->viewConverter );
+
     // get the x and y zoom level
     double zoomX, zoomY;
     m_d->viewConverter->zoom(&zoomX, &zoomY);
@@ -421,6 +489,8 @@
     // compute how large a fully scaled image is in viewpixels
     QSize dstSize = QSize(int(imagePixelSize.width() * scaleX ), int( \
imagePixelSize.height() * scaleY));  
+    qDebug() << ">>>>>>>>>>>>>>>>>>>> dstSize: " << dstSize;
+
     // Don't go outside the image (will crash the sampleImage method below)
     QRect drawRect = rc.translated( m_d->documentOffset ).intersected(QRect( \
QPoint(0, 0), dstSize ) );  
@@ -437,75 +507,103 @@
     // the size of the rect after scaling
     QSize scaledSize = QSize( ( int )( alignedImageRect.width() * scaleX ), ( int )( \
alignedImageRect.height() * scaleY ));  
+    // Apparently we have never before tried to draw the image
+    if ( m_d->cacheKisImageAsQImage && m_d->unscaledCache.isNull() ) {
+        updateUnscaledCache( alignedImageRect );
+    }
 
     // And now for deciding what to do and when -- the complicated bit
-
     if ( scaleX > 1.0 - EPSILON && scaleY > 1.0 - EPSILON ) {
-        kDebug(41010 ) << "Scale is 1.0, don't scale";
-        QImage img;
 
-        // Get the image directly from the KisImage
-        if ( m_d->useNearestNeighbour || !m_d->cacheKisImageAsQImage ) {
-            img = m_d->image->convertToQImage( drawRect, 1.0, 1.0, \
m_d->monitorProfile, m_d->exposure ); +        // Between 1.0 and 2.0, a box filter \
often gives a much nicer +        // result, according to pippin. The default blitz \
filter is +        // called "blackman"
+        if ( m_d->smoothBetween100And200Percent && scaleX < 2.0 - EPSILON && scaleY \
< 2.0 - EPSILON  ) { +            QImage img;
+            if ( !m_d->cacheKisImageAsQImage ) {
+                img = m_d->image->convertToQImage( drawRect, scaleX, scaleY, \
m_d->monitorProfile, m_d->exposure ); +                gc.drawImage( rc.topLeft(), \
img ); +            }
+            else {
+                img = m_d->unscaledCache.copy( drawRect );
+                gc.save();
+                gc.scale(scaleX, scaleY);
+                gc.drawImage(rc.topLeft(), img);
+                gc.restore();
+            }
         }
         else {
-            // Crop the canvascache
-            img = m_d->unscaledCache.copy( drawRect );
-        }
+            QImage img;
 
-        // If so desired, use the sampleImage originally taken from
-        // gwenview, which got it from mosfet, who got it from
-        // ImageMagick
-        if ( m_d->useSampling ) {
-            gc.drawImage( rc.topLeft(), sampleImage(img, dstSize.width(), \
dstSize.height(), drawRect) ); +            // Get the image directly from the \
KisImage +            if ( m_d->useNearestNeighbour || !m_d->cacheKisImageAsQImage ) \
{ +                img = m_d->image->convertToQImage( drawRect, 1.0, 1.0, \
m_d->monitorProfile, m_d->exposure ); +            }
+            else {
+                // Crop the canvascache
+                img = m_d->unscaledCache.copy( drawRect );
+            }
+
+            // If so desired, use the sampleImage originally taken from
+            // gwenview, which got it from mosfet, who got it from
+            // ImageMagick
+            if ( m_d->useSampling ) {
+                gc.drawImage( rc.topLeft(), sampleImage(img, dstSize.width(), \
dstSize.height(), drawRect) ); +            }
+            else {
+                // Else, let QPainter do the scaling, like we did in 1.6
+                gc.save();
+                gc.scale(scaleX, scaleY);
+                gc.drawImage(rc.topLeft(), img);
+                gc.restore();
+            }
         }
-        else {
-            // Else, let QPainter do the scaling, like we did in 1.6
-            gc.save();
-            gc.scale(scaleX, scaleY);
-            gc.drawImage(rc.topLeft(), img);
-            gc.restore();
-        }
     }
     else {
+        QImage croppedImage = m_d->unscaledCache.copy( alignedImageRect );
+
+        // Short circuit if we're in the deferred smoothing stage.
+        if ( isDeferredAction ) {
+            gc.drawImage( rc.topLeft(), Blitz::smoothScale( croppedImage, dstSize ) \
); +            return;
+        }
+
         // Use nearest neighbour interpolation from the raw KisImage
         if ( m_d->useNearestNeighbour || m_d->useDeferredSmoothing ) {
             if ( m_d->useDeferredSmoothing ) {
-                // Start smoothing job. The job will be replaced by
-                // the next smoothing job if it is added before this
-                // one is done.
+                // XXX: Start smoothing job. The job will be replaced
+                // by the next smoothing job if it is added before
+                // this one is done.
+                m_d->rectsToSmooth += rc;
+                m_d->smoothingTimer.start(50);
             }
             QImage tmpImage = m_d->image->convertToQImage( alignedImageRect, scaleX, \
scaleY, m_d->monitorProfile, m_d->exposure );  gc.drawImage( rc.topLeft(), tmpImage \
);  }
         else {
-
-
-            QImage croppedImage = m_d->unscaledCache.copy( alignedImageRect );
-
             // If we don't cache the image as an unscaled QImage, get
             // an unscaled QImage for this rect from KisImage.
             if ( !m_d->cacheKisImageAsQImage ) {
-
+                croppedImage = m_d->image->convertToQImage( alignedImageRect.x(), \
alignedImageRect.y(), alignedImageRect.width(), alignedImageRect.height(), \
m_d->monitorProfile, m_d->exposure );  }
 
             if ( m_d->useQtScaling ) {
+                gc.drawImage( rc.topLeft(), croppedImage.scaled( dstSize, \
Qt::KeepAspectRatio, Qt::SmoothTransformation ) );  }
             else if ( m_d->useSampling ) {
-
+                gc.drawImage( rc.topLeft(), sampleImage(croppedImage, \
dstSize.width(), dstSize.height(), drawRect) );  }
             else { // Smooth scaling using blitz
+                gc.drawImage( rc.topLeft(), Blitz::smoothScale( croppedImage, \
dstSize ) );  }
         }
     }
-
 }
 
 QRect KisPrescaledProjection::viewRectFromImagePixels( const QRect & rc )
 {
-    double xRes,yRes;
-    xRes = m_d->image->xRes();
-    yRes = m_d->image->yRes();
+    double xRes = m_d->image->xRes();
+    double yRes = m_d->image->yRes();
 
     QRectF docRect;
     docRect.setCoords((rc.left() - 2) / xRes, (rc.top() - 2) / yRes, (rc.right() + \
2) / xRes, (rc.bottom() + 2) / yRes); @@ -515,7 +613,80 @@
     viewRect = viewRect.intersected( QRect( 0, 0, m_d->canvasSize.width(), \
m_d->canvasSize.width() ) );  
     return viewRect;
+}
 
+QRect KisPrescaledProjection::imageRectFromViewPortPixels( const QRect & \
viewportRect ) +{
+    QRect intersectedRect = viewportRect.intersected( QRect( 0, 0, \
m_d->canvasSize.width(), m_d->canvasSize.width() ) ); +    QRect translatedRect = \
intersectedRect.translated( -m_d->documentOffset ); +    QRectF docRect = \
m_d->viewConverter->viewToDocument( translatedRect ); +
+    return m_d->image->documentToIntPixel( docRect ).intersected( \
m_d->image->bounds() );  }
 
+void KisPrescaledProjection::slotDoSmoothScale()
+{
+    QRect rc = m_d->rectsToSmooth.boundingRect();
+    QPainter gc( &m_d->prescaledQImage );
+    gc.setCompositionMode( QPainter::CompositionMode_Source );
+    drawScaledImage( rc, gc, true);
+    m_d->rectsToSmooth = QRegion();
+    emit sigPrescaledProjectionUpdated( rc );
+}
+
+void KisPrescaledProjection::updateUnscaledCache( const QRect & rc )
+{
+    qDebug() << rc;
+    if ( m_d->unscaledCache.isNull() ) {
+        m_d->unscaledCache = QImage( m_d->image->width(), m_d->image->height(), \
QImage::Format_ARGB32 ); +    }
+
+    QPainter p( &m_d->unscaledCache );
+    p.setCompositionMode( QPainter::CompositionMode_Source );
+
+    QImage updateImage = m_d->image->convertToQImage(rc.x(), rc.y(), rc.width(), \
rc.height(), +                                                     \
m_d->monitorProfile, +                                                     \
m_d->exposure); +
+    if ( m_d->showMask && m_d->drawMaskVisualisationOnUnscaledCanvasCache ) {
+
+        // XXX: Also visualize the global selection
+
+        KisSelectionSP selection = 0;
+
+        if ( m_d->currentNode ) {
+            if ( m_d->currentNode->inherits( "KisMask" ) ) {
+
+                selection = dynamic_cast<const KisMask*>( m_d->currentNode.data() \
)->selection(); +            }
+            else if ( m_d->currentNode->inherits( "KisLayer" ) ) {
+
+                KisLayerSP layer = dynamic_cast<KisLayer*>( m_d->currentNode.data() \
); +                if ( KisSelectionMaskSP selectionMask = layer->selectionMask() ) \
{ +                    selection = selectionMask->selection();
+                }
+
+                // XXX: transitional! Remove when we use
+                // KisSelectionMask instead of the selection in
+                // the paintdevice. That way we can also select on
+                // groups etc.
+                if ( !selection && m_d->currentNode->inherits( "KisPaintLayer" ) ) {
+                    KisPaintDeviceSP dev = ( dynamic_cast<KisPaintLayer*>( \
m_d->currentNode.data() ) )->paintDevice(); +                    if ( dev ) {
+                        selection = dev->selection();
+                    }
+                }
+            }
+        }
+        QTime t;
+        t.start();
+        selection->paint(&updateImage, rc);
+        kDebug(41010) << "Mask visualisation rendering took: " << t.elapsed();
+    }
+
+    p.drawImage( rc.x(), rc.y(), updateImage, 0, 0, rc.width(), rc.height() );
+    p.end();
+
+}
+
 #include "kis_prescaled_projection.moc"
Index: kis_qpainter_canvas.h
===================================================================
--- kis_qpainter_canvas.h	(revision 717978)
+++ kis_qpainter_canvas.h	(working copy)
@@ -21,6 +21,7 @@
 #include <QWidget>
 
 #include "kis_abstract_canvas_widget.h"
+#include "kis_prescaled_projection.h"
 
 class QImage;
 class QPaintEvent;
@@ -47,7 +48,7 @@
 
     virtual ~KisQPainterCanvas();
 
-
+    void setPrescaledProjection( KisPrescaledProjectionSP prescaledProjection );
 public: // QWidget
 
     /// reimplemented method from superclass
@@ -97,22 +98,8 @@
 
     void documentOffsetMoved( QPoint );
 
-    void preScale();
-
-    void preScale( const QRect & rc );
-
 private:
 
-    /**
-     * draw a scaled QImage of the projection onto the gc
-     *
-     * @param rc The desired rect in pixels
-     * @param gc The painter we draw on
-     */
-    void  drawScaledImage( const QRect & rc, QPainter &gc);
-
-private:
-
     class Private;
     Private * const m_d;
 };
Index: kis_canvas2.h
===================================================================
--- kis_canvas2.h	(revision 717978)
+++ kis_canvas2.h	(working copy)
@@ -95,8 +95,6 @@
 
     QWidget* canvasWidget();
 
-    QImage canvasCache();
-
     virtual KoUnit unit() const;
 
     virtual KoToolProxy* toolProxy() const;
@@ -125,7 +123,6 @@
     KisView2* view();
 
     bool usingHDRExposureProgram();
-    bool useFastZooming();
 
 public slots:
 
@@ -153,9 +150,13 @@
      */
     void slotConfigChanged();
 
+    /**
+     * Called whenever the display monitor profile resource changes
+     */
+    void slotSetDisplayProfile( KoColorProfile * profile );
+
 private:
 
-    void resetMonitorProfile();
     void resetCanvas();
 
     KisCanvas2(const KisCanvas2&);
@@ -170,7 +171,6 @@
      * the widget.
      */
     QRect viewRectFromDoc( const QRectF & docRect );
-    QRect viewRectFromImagePixels( const QRect & imageRect );
 
 private:
 
Index: kis_canvas2.cpp
===================================================================
--- kis_canvas2.cpp	(revision 717978)
+++ kis_canvas2.cpp	(working copy)
@@ -36,6 +36,7 @@
 #include "KoToolProxy.h"
 #include "KoSelection.h"
 
+#include "kis_prescaled_projection.h"
 #include "kis_image.h"
 #include "kis_doc2.h"
 #include "kis_shape_layer.h"
@@ -69,7 +70,6 @@
         , currentCanvasIsOpenGL( false )
         , currentCanvasUsesOpenGLShaders( false )
         , toolProxy( new KoToolProxy(parent) )
-        , fastZooming( false )
         {
         }
 
@@ -88,38 +88,22 @@
     bool currentCanvasIsOpenGL;
     bool currentCanvasUsesOpenGLShaders;
     KoToolProxy * toolProxy;
-    QImage canvasCache; // XXX: use KisQPainterImageContext to share
-                        // cache data between views. Finish that
-                        // class. Or another way to tile the canvas
-                        // cache instead of having a QImage as big as
-                        // the image in pixels.
     QPoint documentOffset;
     KoShapeControllerBase * sc;
-    bool fastZooming;
-
 #ifdef HAVE_OPENGL
     KisOpenGLImageTexturesSP openGLImageTextures;
 #endif
     bool updateAllOfQPainterCanvas;
+    KisPrescaledProjectionSP prescaledProjection;
 };
 
 KisCanvas2::KisCanvas2(KoViewConverter * viewConverter, KisView2 * view, \
KoShapeControllerBase * sc)  : KoCanvasBase(sc)
     , m_d ( new KisCanvas2Private(this, viewConverter, view) )
 {
-    KisConfig cfg;
-
-    m_d->updateAllOfQPainterCanvas = cfg.updateAllOfQPainterCanvas();
-    m_d->fastZooming = cfg.fastZoom();
-    if ( m_d->canvasWidget && m_d->canvasWidget->widget() )
-        m_d->canvasWidget->widget()->update();
-
-    resetMonitorProfile();
     createCanvas();
-    connect( view->canvasController(), SIGNAL( moveDocumentOffset( const QPoint& ) \
                ),
-             this, SLOT( documentOffsetMoved( const QPoint& ) ) );
-    connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), \
                SLOT(slotConfigChanged()));
-
+    connect( view->canvasController(), SIGNAL( moveDocumentOffset( const QPoint& ) \
), SLOT( documentOffsetMoved( const QPoint& ) ) ); +    connect( \
KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));  \
}  
 KisCanvas2::~KisCanvas2()
@@ -127,52 +111,6 @@
     delete m_d;
 }
 
-void KisCanvas2::createQPainterCanvas()
-{
-#ifdef HAVE_OPENGL
-    m_d->openGLImageTextures = 0;
-#endif
-    setCanvasWidget( new KisQPainterCanvas( this, m_d->view ) );
-    m_d->currentCanvasIsOpenGL = false;
-     KisConfig cfg;
-     m_d->updateAllOfQPainterCanvas = cfg.updateAllOfQPainterCanvas();
-}
-
-void KisCanvas2::createOpenGLCanvas()
-{
-#ifdef HAVE_OPENGL
-    if ( QGLFormat::hasOpenGL() ) {
-        // XXX: The image isn't done loading here!
-        m_d->openGLImageTextures = \
                KisOpenGLImageTextures::getImageTextures(m_d->view->image(), \
                m_d->monitorProfile);
-        setCanvasWidget( new KisOpenGLCanvas2( this, m_d->view, \
                m_d->openGLImageTextures ) );
-        m_d->currentCanvasIsOpenGL = true;
-        m_d->currentCanvasUsesOpenGLShaders = \
                m_d->openGLImageTextures->usingHDRExposureProgram();
-    }
-    else {
-        kWarning() << "Tried to create OpenGL widget when system doesn't have \
                OpenGL\n";
-        createQPainterCanvas();
-    }
-#endif
-}
-
-void KisCanvas2::createCanvas()
-{
-    KisConfig cfg;
-    if ( cfg.useOpenGL() ) {
-#ifdef HAVE_OPENGL
-        createOpenGLCanvas();
-#else
-        kWarning() << "OpenGL requested while its not available, starting qpainter \
                canvas";
-        createQPainterCanvas();
-#endif
-    }
-    else {
-        createQPainterCanvas();
-
-    }
-
-}
-
 void KisCanvas2::setCanvasWidget(QWidget * widget)
 {
     KisAbstractCanvasWidget * tmp = dynamic_cast<KisAbstractCanvasWidget*>( widget \
); @@ -189,11 +127,6 @@
     }
 }
 
-KisView2* KisCanvas2::view()
-{
-    return m_d->view;
-}
-
 void KisCanvas2::gridSize(double *horizontal, double *vertical) const
 {
     Q_ASSERT(horizontal);
@@ -231,32 +164,6 @@
     return m_d->shapeManager;
 }
 
-QRect KisCanvas2::viewRectFromDoc( const QRectF & rc )
-{
-    QRect viewRect = m_d->viewConverter->documentToView(rc).toAlignedRect();
-    viewRect = viewRect.translated( -m_d->documentOffset );
-    viewRect = viewRect.intersected( QRect( 0, 0, \
                m_d->canvasWidget->widget()->width(), \
                m_d->canvasWidget->widget()->height() ) );
-    return viewRect;
-}
-
-
-QRect KisCanvas2::viewRectFromImagePixels( const QRect & rc )
-{
-    double pppx,pppy;
-    pppx = image()->xRes();
-    pppy = image()->yRes();
-
-    QRectF docRect;
-    docRect.setCoords((rc.left() - 2) / pppx, (rc.top() - 2) / pppy, (rc.right() + \
                2) / pppx, (rc.bottom() + 2) / pppy);
-
-    QRect viewRect = m_d->viewConverter->documentToView(docRect).toAlignedRect();
-    viewRect = viewRect.translated( -m_d->documentOffset );
-    viewRect = viewRect.intersected( QRect( 0, 0, \
                m_d->canvasWidget->widget()->width(), \
                m_d->canvasWidget->widget()->height() ) );
-
-    return viewRect;
-
-}
-
 void KisCanvas2::updateCanvas(const QRectF& rc)
 {
     // updateCanvas is called from tools, never from the projection
@@ -274,90 +181,141 @@
     }
 }
 
-void KisCanvas2::updateCanvasProjection( const QRect & rc )
+void KisCanvas2::updateInputMethodInfo() {
+    // TODO call (the protected) QWidget::updateMicroFocus() on the proper canvas \
widget... +}
+
+const KoViewConverter* KisCanvas2::viewConverter() const
 {
-#ifdef HAVE_OPENGL
-    // Should never have an OpenGL image context and get here as that
-    // connects to the image directly.
-    Q_ASSERT( m_d->openGLImageTextures.isNull() );
-#endif
-    // XXX: Use the KisQPainterImageContext here
+    return m_d->viewConverter;
+}
 
-    // When using fast zoom, we don't have a canvas cache
-    if ( !m_d->fastZooming ) {
-        QPainter p( &m_d->canvasCache );
-        p.setCompositionMode( QPainter::CompositionMode_Source );
+QWidget* KisCanvas2::canvasWidget()
+{
+    return m_d->canvasWidget->widget();
+}
 
-        QImage updateImage = image()->convertToQImage(rc.x(), rc.y(), rc.width(), \
                rc.height(),
-                                                      m_d->monitorProfile,
-                                                      \
m_d->view->resourceProvider()->HDRExposure());  
-        KisLayerSP layer = resourceProvider()->resource( \
                KisResourceProvider::CurrentKritaLayer ).value<KisLayerSP>();
-        if (!layer) return;
+KoUnit KisCanvas2::unit() const
+{
+    return KoUnit(KoUnit::Pixel);
+}
 
-        KisPaintDeviceSP dev = layer->paintDevice();
-        if (!dev) return;
+KoToolProxy * KisCanvas2::toolProxy() const {
+    return m_d->toolProxy;
+}
 
-        if (dev->hasSelection()){
-            KisSelectionSP selection = dev->selection();
+void KisCanvas2::createQPainterCanvas()
+{
+#ifdef HAVE_OPENGL
+    m_d->openGLImageTextures = 0;
+#endif
+    m_d->currentCanvasIsOpenGL = false;
 
-            QTime t;
-            t.start();
-            selection->paint(&updateImage, rc);
-            kDebug(41010) << "Mask visualisation rendering took: " << t.elapsed();
+    KisQPainterCanvas * canvasWidget = new KisQPainterCanvas( this, m_d->view );
+    m_d->prescaledProjection = new KisPrescaledProjection();
+    m_d->prescaledProjection->setViewConverter( m_d->viewConverter );
 
-        }
+    canvasWidget->setPrescaledProjection( m_d->prescaledProjection );
 
-        p.drawImage( rc.x(), rc.y(), updateImage, 0, 0, rc.width(), rc.height() );
-        p.end();
+    KisConfig cfg;
+    m_d->updateAllOfQPainterCanvas = cfg.updateAllOfQPainterCanvas();
 
+    connect( KisConfigNotifier::instance(), SIGNAL(configChanged()), \
m_d->prescaledProjection, SLOT(updateSettings())); +    connect( \
m_d->view->resourceProvider(), SIGNAL( sigDisplayProfileChanged( const KoColorProfile \
* profile ) ), m_d->prescaledProjection, SLOT( setMonitorProfile( const \
KoColorProfile * ) ) ); +
+    setCanvasWidget( canvasWidget );
+}
+
+void KisCanvas2::createOpenGLCanvas()
+{
+#ifdef HAVE_OPENGL
+    if ( QGLFormat::hasOpenGL() ) {
+        // XXX: The image isn't done loading here!
+        m_d->openGLImageTextures = \
KisOpenGLImageTextures::getImageTextures(m_d->view->image(), m_d->monitorProfile); +  \
setCanvasWidget( new KisOpenGLCanvas2( this, m_d->view, m_d->openGLImageTextures ) ); \
+        m_d->currentCanvasIsOpenGL = true; +        \
m_d->currentCanvasUsesOpenGLShaders = \
m_d->openGLImageTextures->usingHDRExposureProgram();  }
     else {
-        // XXX: Draw the selection also if we're using fast scaling
-        // instead of the canvas cache
+        kWarning() << "Tried to create OpenGL widget when system doesn't have \
OpenGL\n"; +        createQPainterCanvas();
     }
+#endif
+}
 
-    QRect vRect = viewRectFromImagePixels( rc );
+void KisCanvas2::createCanvas()
+{
+    KisConfig cfg;
+    if ( cfg.useOpenGL() ) {
+#ifdef HAVE_OPENGL
+        createOpenGLCanvas();
+#else
+        kWarning() << "OpenGL requested while its not available, starting qpainter \
canvas"; +        createQPainterCanvas();
+#endif
+    }
+    else {
+        createQPainterCanvas();
 
-    if ( !vRect.isEmpty() ) {
-
-        m_d->canvasWidget->preScale( vRect );
-
-        if ( m_d->updateAllOfQPainterCanvas ) {
-            m_d->canvasWidget->widget()->update();
-        }
-        else {
-            m_d->canvasWidget->widget()->update( vRect );
-        }
     }
+
 }
 
-
-void KisCanvas2::updateCanvas()
+KisView2* KisCanvas2::view()
 {
-    m_d->canvasWidget->widget()->update();
+    return m_d->view;
 }
 
 
-const KoViewConverter* KisCanvas2::viewConverter() const
+QRect KisCanvas2::viewRectFromDoc( const QRectF & rc )
 {
-    return m_d->viewConverter;
+    QRect viewRect = m_d->viewConverter->documentToView(rc).toAlignedRect();
+    viewRect = viewRect.translated( -m_d->documentOffset );
+    viewRect = viewRect.intersected( QRect( 0, 0, \
m_d->canvasWidget->widget()->width(), m_d->canvasWidget->widget()->height() ) ); +    \
return viewRect;  }
 
-QWidget* KisCanvas2::canvasWidget()
+
+void KisCanvas2::updateCanvasProjection( const QRect & rc )
 {
-    return m_d->canvasWidget->widget();
+#ifdef HAVE_OPENGL
+    // Should never have an OpenGL image context and get here as that
+    // connects to the image directly.
+    Q_ASSERT( m_d->openGLImageTextures.isNull() );
+#endif
+    // XXX: Use the KisQPainterImageContext here
+
+    // If this does anything, it updates the pixel-for-pixel
+    // projection of the KisImage
+    m_d->prescaledProjection->updateCanvasProjection( rc );
+
+    QRect vRect = m_d->prescaledProjection->viewRectFromImagePixels( rc );
+
+    if ( !vRect.isEmpty() ) {
+
+        // If so desired, rescale all of the visible area so we don't
+        // see any jitter when using algorithms that cannot handle the
+        // +/- half pixel inaccuracy of our algorithms
+        if ( m_d->updateAllOfQPainterCanvas ) {
+            m_d->prescaledProjection->preScale();
+        }
+        else {
+            m_d->prescaledProjection->preScale( vRect );
+        }
+
+        // Regardless, the actual
+        m_d->canvasWidget->widget()->update( vRect );
+
+    }
 }
 
 
-KoUnit KisCanvas2::unit() const
+void KisCanvas2::updateCanvas()
 {
-    return KoUnit(KoUnit::Pixel);
+    m_d->canvasWidget->widget()->update();
 }
 
-KoToolProxy * KisCanvas2::toolProxy() const {
-    return m_d->toolProxy;
-}
 
 KisImageSP KisCanvas2::image()
 {
@@ -365,32 +323,11 @@
 
 }
 
-QImage KisCanvas2::canvasCache()
-{
-    return m_d->canvasCache;
-}
-
 KoColorProfile *  KisCanvas2::monitorProfile()
 {
-    if (m_d->monitorProfile == 0) {
-        resetMonitorProfile();
-    }
     return m_d->monitorProfile;
 }
 
-
-void KisCanvas2::resetMonitorProfile()
-{
-    // XXX: The X11 monitor profile overrides the settings
-    m_d->monitorProfile = KoIccColorProfile::getScreenProfile();
-
-    if (m_d->monitorProfile == 0) {
-        KisConfig cfg;
-        QString monitorProfileName = cfg.monitorProfile();
-        m_d->monitorProfile = \
                KoColorSpaceRegistry::instance()->profileByName(monitorProfileName);
-    }
-}
-
 KisImageSP KisCanvas2::currentImage()
 {
     return m_d->view->image();
@@ -398,10 +335,8 @@
 
 void KisCanvas2::setImageSize( qint32 w, qint32 h )
 {
-    // Fast zoom takes the pixels directly from the QImage and doesn't
-    // cache the image in a qimage
-    if ( !m_d->fastZooming )
-        m_d->canvasCache = QImage( w, h, QImage::Format_ARGB32 );
+    if ( m_d->prescaledProjection )
+        m_d->prescaledProjection->setImageSize( w, h );
 }
 
 void KisCanvas2::connectCurrentImage()
@@ -414,6 +349,11 @@
 #endif
         connect(m_d->view->image(), SIGNAL(sigImageUpdated(const QRect &)), \
                SLOT(updateCanvasProjection(const QRect &)));
         connect(m_d->view->image(), SIGNAL(sigSizeChanged(qint32, qint32)), \
SLOT(setImageSize( qint32, qint32)) ); +
+        if ( m_d->prescaledProjection ) {
+            m_d->prescaledProjection->setImage( m_d->view->image() );
+        }
+
 #ifdef HAVE_OPENGL
     }
 #endif
@@ -431,7 +371,6 @@
 
 void KisCanvas2::resetCanvas()
 {
-    resetMonitorProfile();
     KisConfig cfg;
 
 #if HAVE_OPENGL
@@ -453,7 +392,6 @@
     }
 #endif
     m_d->updateAllOfQPainterCanvas = cfg.updateAllOfQPainterCanvas();
-    m_d->fastZooming = cfg.fastZoom();
     m_d->canvasWidget->widget()->update();
 }
 
@@ -465,7 +403,9 @@
 
 void KisCanvas2::preScale()
 {
-    m_d->canvasWidget->preScale();
+    Q_ASSERT( m_d->prescaledProjection );
+
+    m_d->prescaledProjection->preScale();
 }
 
 bool KisCanvas2::usingHDRExposureProgram()
@@ -480,18 +420,14 @@
     return false;
 }
 
-bool KisCanvas2::useFastZooming()
-{
-    return m_d->fastZooming;
-}
-
 void KisCanvas2::slotConfigChanged()
 {
     resetCanvas();
 }
 
-void KisCanvas2::updateInputMethodInfo() {
-    // TODO call (the protected) QWidget::updateMicroFocus() on the proper canvas \
widget... +void KisCanvas2::slotSetDisplayProfile( KoColorProfile * profile )
+{
+    m_d->monitorProfile = profile;
 }
 
 #include "kis_canvas2.moc"
Index: kis_qpainter_canvas.cpp
===================================================================
--- kis_qpainter_canvas.cpp	(revision 717978)
+++ kis_qpainter_canvas.cpp	(working copy)
@@ -18,6 +18,8 @@
 
 #include "kis_qpainter_canvas.h"
 
+#include "kis_canvas2.h"
+
 #include <QPaintEvent>
 #include <QRect>
 #include <QPainter>
@@ -45,10 +47,9 @@
 #include <kis_layer.h>
 
 
+#include "kis_prescaled_projection.h"
 #include "kis_config.h"
-#include "kis_canvas2.h"
 #include "kis_resource_provider.h"
-#include <qimageblitz.h>
 #include "kis_doc2.h"
 #include "kis_grid_drawer.h"
 #include "kis_selection_manager.h"
@@ -71,11 +72,11 @@
         {
         }
 
+    KisPrescaledProjectionSP prescaledProjection;
     KoToolProxy * toolProxy;
     KisCanvas2 * canvas;
     const KoViewConverter * viewConverter;
     QBrush checkBrush;
-    QImage prescaledImage;
     QPoint documentOffset;
     KisGridDrawer* gridDrawer;
     double currentExposure;
@@ -106,7 +107,10 @@
     delete m_d;
 }
 
-#define EPSILON 1e-6
+void KisQPainterCanvas::setPrescaledProjection( KisPrescaledProjectionSP \
prescaledProjection ) +{
+    m_d->prescaledProjection = prescaledProjection;
+}
 
 void KisQPainterCanvas::paintEvent( QPaintEvent * ev )
 {
@@ -120,7 +124,7 @@
 
     if (img->colorSpace()->hasHighDynamicRange() &&
         (m_d->currentExposure != \
                m_d->canvas->view()->resourceProvider()->HDRExposure())) {
-        // XXX: If we had a dirty region we could just update areas as
+        // XXX: If we had a dirty region we could just recomposite areas as
         // they become visible.
         // YYY: As soon as we start using
         // KisProjection::setRegionOfInterest(), only the visible
@@ -171,7 +175,7 @@
 
     t.restart();
     gc.setCompositionMode( QPainter::CompositionMode_SourceOver );
-    gc.drawImage( ev->rect(), m_d->prescaledImage, ev->rect() );
+    gc.drawImage( ev->rect(), m_d->prescaledProjection->prescaledQImage(), \
ev->rect() );  kDebug(41010) <<"Drawing image:" << t.elapsed();
 
 #ifdef DEBUG_REPAINT
@@ -275,192 +279,13 @@
 
 void KisQPainterCanvas::documentOffsetMoved( QPoint pt )
 {
-    qint32 width = m_d->prescaledImage.width();
-    qint32 height = m_d->prescaledImage.height();
-
-    QRegion exposedRegion = QRect(0, 0, width, height);
-
-    qint32 oldCanvasXOffset = m_d->documentOffset.x();
-    qint32 oldCanvasYOffset = m_d->documentOffset.y();
-
-    m_d->documentOffset = pt;
-
-    QImage img = QImage( width, height, QImage::Format_ARGB32 );
-    QPainter gc( &img );
-    gc.setCompositionMode( QPainter::CompositionMode_Source );
-
-    if (!m_d->prescaledImage.isNull()) {
-
-        if (oldCanvasXOffset != m_d->documentOffset.x() || oldCanvasYOffset != \
                m_d->documentOffset.y()) {
-
-            qint32 deltaX = m_d->documentOffset.x() - oldCanvasXOffset;
-            qint32 deltaY = m_d->documentOffset.y() - oldCanvasYOffset;
-
-            gc.drawImage( -deltaX, -deltaY, m_d->prescaledImage );
-            exposedRegion -= QRegion(QRect(-deltaX, -deltaY, width - deltaX, height \
                - deltaY));
-        }
-    }
-
-
-    if (!m_d->prescaledImage.isNull() && !exposedRegion.isEmpty()) {
-
-        QVector<QRect> rects = exposedRegion.rects();
-
-        for (int i = 0; i < rects.count(); i++) {
-            QRect r = rects[i];
-            drawScaledImage( r, gc);
-        }
-    }
-    m_d->prescaledImage = img;
+    m_d->prescaledProjection->documentOffsetMoved( pt );
     update();
 }
 
-
-void KisQPainterCanvas::drawScaledImage( const QRect & r, QPainter &gc )
-{
-    KisImageSP img = m_d->canvas->image();
-    if (img == 0) return;
-    QRect rc = r;
-
-    double sx, sy;
-    m_d->viewConverter->zoom(&sx, &sy);
-
-    // Compute the scale factors
-    double scaleX = sx / img->xRes();
-    double scaleY = sy / img->yRes();
-
-    QImage canvasImage = m_d->canvas->canvasCache();
-
-    // compute how large a fully scaled image is
-    QSize dstSize = QSize(int(canvasImage.width() * scaleX ), int( \
                canvasImage.height() * scaleY));
-
-    // Don't go outside the image (will crash the sampleImage method below)
-    QRect drawRect = rc.translated( \
                m_d->documentOffset).intersected(QRect(QPoint(),dstSize));
-
-    // Go from the widget coordinates to points
-    QRectF imageRect = m_d->viewConverter->viewToDocument( rc.translated( \
                m_d->documentOffset ) );
-
-    double pppx,pppy;
-    pppx = img->xRes();
-    pppy = img->yRes();
-
-    // Go from points to pixels
-    imageRect.setCoords(imageRect.left() * pppx, imageRect.top() * pppy,
-                        imageRect.right() * pppx, imageRect.bottom() * pppy);
-
-    // Don't go outside the image and convert to whole pixels
-    QRect alignedImageRect = imageRect.intersected( canvasImage.rect() \
                ).toAlignedRect();
-
-    if ( m_d->canvas->useFastZooming() ) {
-
-        // XXX: Check whether the right coordinates are used
-
-        QTime t;
-        t.start();
-        QImage tmpImage = img->convertToQImage( alignedImageRect, scaleX, scaleY, \
                m_d->canvas->monitorProfile(), m_d->currentExposure );
-        kDebug(41010 ) << "KisImage::convertToQImage" << t.elapsed();
-        gc.drawImage( rc.topLeft(), tmpImage );
-
-    }
-    else {
-
-        // Don't scale if not necessary;
-        if ( scaleX == 1.0 && scaleY == 1.0 ) {
-            gc.drawImage( rc.topLeft(), canvasImage.copy( drawRect ) );
-        }
-        else {
-            QSize sz = QSize( ( int )( alignedImageRect.width() * scaleX ), ( int )( \
                alignedImageRect.height() * scaleY ));
-            QImage croppedImage = canvasImage.copy( alignedImageRect );
-
-            if ( sx >= 1.0 && sy >= 1.0 ) {
-                QTime t;
-                t.start();
-                QImage img2 = croppedImage.scaled( sz, Qt::KeepAspectRatio, \
                Qt::FastTransformation );
-                kDebug(41010) << "QImage fast scaling " << t.elapsed();
-                gc.drawImage( rc.topLeft(), img2 );
-            }
-            else {
-
-
-                QTime t;
-                t.start();
-#ifndef USE_QT_SCALING
-                QImage img2 = Blitz::smoothScale( croppedImage, sz );
-                kDebug(41010) <<"Blitz scale:" << t.elapsed();
-#else
-                t.restart();
-                QImage img2 = croppedImage.scaled( sz, Qt::KeepAspectRatio, \
                Qt::SmoothTransformation );
-                kDebug(41010) <<"qimage smooth scale:" << t.elapsed();
-#endif
-                gc.drawImage( rc.topLeft(), img2 );
-
-
-                //gc.drawImage( rc.topLeft(), ImageUtils::scale(croppedImage, \
                sz.width(), sz.height() ));
-
-            }
-        }
-
-    }
-}
-
 void KisQPainterCanvas::resizeEvent( QResizeEvent *e )
 {
-    QTime t;
-    t.start();
-
-
-    QSize newSize = e->size();
-    QSize oldSize = m_d->prescaledImage.size();
-
-    QImage img = QImage(e->size(), QImage::Format_ARGB32);
-    QPainter gc( &img );
-    gc.setCompositionMode( QPainter::CompositionMode_Source );
-    gc.drawImage( 0, 0, m_d->prescaledImage, 0, 0, m_d->prescaledImage.width(), \
                m_d->prescaledImage.height() );
-
-    if ( newSize.width() > oldSize.width() || newSize.height() > oldSize.height() ) \
                {
-
-        QRect right( oldSize.width(), 0, newSize.width() - oldSize.width(), \
                newSize.height() );
-        if ( right.width() > 0 ) {
-            drawScaledImage( right, gc);
-        }
-        // Subtract the right hand overlap part (right.width() from
-        // the bottom band so we don't scale the same area twice.
-        QRect bottom( 0, oldSize.height(), newSize.width() - right.width(), \
                newSize.height() - oldSize.height() );
-        if ( bottom.height() > 0 ) {
-            drawScaledImage( bottom, gc);
-        }
-
-    }
-    m_d->prescaledImage = img;
-
-    kDebug(41010) <<"Resize event:" << t.elapsed();
+    m_d->prescaledProjection->resizePrescaledImage( e->size() );
 }
 
-void KisQPainterCanvas::preScale()
-{
-    // Thread this!
-    QTime t;
-    t.start();
-    m_d->prescaledImage = QImage( size(), QImage::Format_ARGB32);
-
-    QPainter gc( &m_d->prescaledImage );
-    gc.setCompositionMode( QPainter::CompositionMode_Source );
-
-    drawScaledImage( QRect( QPoint( 0, 0 ), size() ), gc);
-    kDebug(41010) <<"preScale():" << t.elapsed();
-
-}
-
-void KisQPainterCanvas::preScale( const QRect & rc )
-{
-    if ( !rc.isEmpty() ) {
-        QTime t;
-        t.start();
-        QPainter gc( &m_d->prescaledImage );
-        gc.setCompositionMode( QPainter::CompositionMode_Source );
-        drawScaledImage( rc, gc);
-        kDebug(41010) <<"Prescaling took" << t.elapsed();
-    }
-}
-
 #include "kis_qpainter_canvas.moc"
Index: kis_zoom_manager.cc
===================================================================
--- kis_zoom_manager.cc	(revision 717978)
+++ kis_zoom_manager.cc	(working copy)
@@ -112,7 +112,9 @@
     connect(m_canvasController, SIGNAL(canvasOffsetYChanged(int)),
             m_verticalRuler, SLOT(setOffset(int)));
 
-    connect( m_canvasController, SIGNAL( canvasMousePositionChanged(const QPoint & ) \
), this, SLOT( mousePositionChanged( const QPoint & ) ) ); +    connect( \
m_canvasController, +             SIGNAL( canvasMousePositionChanged(const QPoint & ) \
), +             SLOT( mousePositionChanged( const QPoint & ) ) );
 
     connect(m_zoomController, SIGNAL(zoomChanged(KoZoomMode::Mode, double)),
             this, SLOT(slotZoomChanged(KoZoomMode::Mode, double)));
@@ -166,9 +168,10 @@
     m_view->canvasBase()->preScale();
     m_view->canvas()->update();
 
-    m_canvasController->setDocumentSize(
-            QSize( int(0.5 + m_zoomHandler->documentToViewX(img->width() / \
                img->xRes())),
-                   int(0.5 + m_zoomHandler->documentToViewY(img->height() / \
img->yRes())) ) ); +    QSize sz = QSize( int(0.5 + \
m_zoomHandler->documentToViewX(img->width() / img->xRes())), +                      \
int(0.5 + m_zoomHandler->documentToViewY(img->height() / img->yRes())) ); +    \
qDebug() << "Resizing after change in aspect mode to to " << sz; +    \
m_canvasController->setDocumentSize( sz );  
     // Finally ask the canvasController to recenter
     m_canvasController->recenterPreferred();
Index: kis_abstract_canvas_widget.h
===================================================================
--- kis_abstract_canvas_widget.h	(revision 717978)
+++ kis_abstract_canvas_widget.h	(working copy)
@@ -52,20 +52,6 @@
                           KisCanvas2 * canvas, KisGridDrawer * gridDrawer );
 
     /**
-     * Prescale the canvas represention of the image (if necessary, it
-     * is for QPainter, not for OpenGL).
-     */
-    virtual void preScale() {}
-
-    /**
-     * Prescale the canvas represetation of the image.
-     *
-     * @param rc The target rect in view coordinates of the prescaled
-     * image, pre-translated with the document offset
-     */
-    virtual void preScale( const QRect & rc ) { Q_UNUSED( rc ); }
-
-    /**
      * Returns one check of the background checkerboard pattern.
      *
      * @param checkSize the size of the check
Index: tests/kis_prescaled_projection_test.cpp
===================================================================
--- tests/kis_prescaled_projection_test.cpp	(revision 717978)
+++ tests/kis_prescaled_projection_test.cpp	(working copy)
@@ -20,9 +20,134 @@
 #include <QCoreApplication>
 
 #include <qtest_kde.h>
+
+#include <QSize>
+#include <QImage>
+
+#include <KoZoomHandler.h>
+#include <KoColorSpaceRegistry.h>
+
+#include <kis_types.h>
+#include <kis_image.h>
+#include <kis_paint_layer.h>
+#include <kis_group_layer.h>
+
 #include "kis_prescaled_projection_test.h"
 #include "kis_prescaled_projection.h"
 
+
+bool KisPrescaledProjectionTest::testProjectionScenario( KisPrescaledProjection & \
projection, +                                                     KoZoomHandler * \
viewConverter, +                                                     const QString & \
name ) +{
+
+    qDebug() << 1;
+    projection.resizePrescaledImage( QSize( 1000, 1000 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_01.png" );
+
+    qDebug() << 21;
+    viewConverter->setZoom( 0.5 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_021.png" );
+
+    qDebug() << 22;
+    viewConverter->setZoom( 0.6 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_022.png" );
+
+    qDebug() << 23;
+    viewConverter->setZoom( 0.71 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_023.png" );
+
+    qDebug() << 24;
+    viewConverter->setZoom( 0.84 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_024.png" );
+
+    qDebug() << 25;
+    viewConverter->setZoom( 0.9 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_025.png" );
+
+    qDebug() << 3;
+    viewConverter->setZoom( 1.9 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_03.png" );
+
+    qDebug() << 4;
+    viewConverter->setZoom( 2.0 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_04.png" );
+
+    viewConverter->setZoom( 2.5 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_05.png" );
+
+    viewConverter->setZoom( 16.0 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_06.png" );
+
+    viewConverter->setZoom( 1.0 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_07.png" );
+
+    projection.documentOffsetMoved( QPoint( 50, 50 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_08.png" );
+
+    projection.documentOffsetMoved( QPoint( 100, 100 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_081.png" );
+
+    projection.documentOffsetMoved( QPoint( 200, 200 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_082.png" );
+
+    projection.documentOffsetMoved( QPoint( 250, 250 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_083.png" );
+
+    projection.documentOffsetMoved( QPoint( 150, 200 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_084.png" );
+
+    projection.documentOffsetMoved( QPoint( 100, 200 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_085.png" );
+
+    projection.documentOffsetMoved( QPoint( 50, 200 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_086.png" );
+
+    projection.documentOffsetMoved( QPoint( 0, 200 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_087.png" );
+
+    projection.resizePrescaledImage( QSize( 750, 750 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_09.png" );
+
+    viewConverter->setZoom( 1.0 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_10.png" );
+
+    projection.resizePrescaledImage( QSize( 350, 350 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_11.png" );
+
+    projection.documentOffsetMoved( QPoint( 100, 100 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_12.png" );
+
+    viewConverter->setZoom( 0.75 );
+    projection.preScale();
+    projection.prescaledQImage().save( name + "_prescaled_projection_13.png" );
+
+    projection.documentOffsetMoved( QPoint( 10, 10 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_14.png" );
+
+    projection.documentOffsetMoved( QPoint( 0, 0 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_15.png" );
+
+    projection.documentOffsetMoved( QPoint( 10, 10 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_16.png" );
+
+    projection.documentOffsetMoved( QPoint( 30, 50 ) );
+    projection.prescaledQImage().save( name + "_prescaled_projection_17.png" );
+
+
+}
+
 void KisPrescaledProjectionTest::testCreation()
 {
     KisPrescaledProjection * prescaledProjection = 0;
@@ -34,6 +159,93 @@
     delete prescaledProjection;
 }
 
+
+void KisPrescaledProjectionTest::testCoordinateConversionRoundTrip()
+{
+    KisPrescaledProjection projection;
+
+    KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
+    KisImageSP image = new KisImage( 0, 100, 100, cs, "projection test" );
+    image->setResolution( 300, 300 );
+
+    KoZoomHandler * viewConverter = new KoZoomHandler();
+    viewConverter->setResolution(120, 120);
+
+    projection.setImage( image );
+    projection.setViewConverter( viewConverter );
+    projection.resizePrescaledImage( QSize( 100, 100 ) );
+
+    QRect viewRect = projection.viewRectFromImagePixels( QRect( 0, 0, 100, 100 ) );
+    QCOMPARE( viewRect, QRect( 0, 0, 41, 41 ) );
+
+    QRect viewRect2 = projection.viewRectFromImagePixels( QRect( 0, 0, 200, 200 ) );
+    QCOMPARE( viewRect2, QRect( 0, 0, 81, 81 ) );
+
+    QRect imageRect = projection.imageRectFromViewPortPixels( viewRect );
+    QCOMPARE( imageRect, QRect( 0, 0, 100, 100 ) );
+
+    QRect viewRect3 = projection.viewRectFromImagePixels( imageRect );
+    QCOMPARE( viewRect3, viewRect );
+}
+
+
+void KisPrescaledProjectionTest::testScalingUndeferredSmoothingPixelForPixel()
+{
+    // Set up a nice image
+    QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png");
+
+    // Undo adapter not necessary
+    KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
+    KisImageSP image = new KisImage( 0, qimage.width(), qimage.height(), cs, \
"projection test" ); +
+    // 300 dpi recalculated to pixels per point (of which there are 72
+    // to the inch)
+    image->setResolution( 100 / 72 , 100 / 72 );
+
+    KisPaintLayerSP layer = new KisPaintLayer( image, "test", OPACITY_OPAQUE, cs );
+    image->addNode(layer.data(), image->rootLayer(), 0);
+    layer->paintDevice()->convertFromQImage( qimage, "");
+
+    KisPrescaledProjection projection;
+    projection.setImage( image );
+
+    KoZoomHandler * viewConverter = new KoZoomHandler();
+    projection.setViewConverter( viewConverter );
+    // pixel-for-pixel, at 100% zoom
+    viewConverter->setResolution(image->xRes(), image->yRes());
+
+    testProjectionScenario( projection, viewConverter, "pixel_for_pixel" );
+
+}
+
+
+void KisPrescaledProjectionTest::testScalingUndeferredSmoothing()
+{
+    // Set up a nice image
+    QImage qimage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png");
+
+    // Undo adapter not necessary
+    KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
+    KisImageSP image = new KisImage( 0, qimage.width(), qimage.height(), cs, \
"projection test" ); +
+    // 300 dpi recalculated to pixels per point (of which there are 72
+    // to the inch)
+    image->setResolution( 300 / 72 , 300 / 72 );
+
+    KisPaintLayerSP layer = new KisPaintLayer( image, "test", OPACITY_OPAQUE, cs );
+    image->addNode(layer.data(), image->rootLayer(), 0);
+    layer->paintDevice()->convertFromQImage( qimage, "");
+
+    KisPrescaledProjection projection;
+    projection.setImage( image );
+
+    KoZoomHandler * viewConverter = new KoZoomHandler();
+    projection.setViewConverter( viewConverter );
+
+    testProjectionScenario( projection, viewConverter, "120dpi" );
+
+}
+
 QTEST_KDEMAIN(KisPrescaledProjectionTest, GUI)
 
 #include "kis_prescaled_projection_test.moc"
Index: tests/kis_prescaled_projection_test.h
===================================================================
--- tests/kis_prescaled_projection_test.h	(revision 717978)
+++ tests/kis_prescaled_projection_test.h	(working copy)
@@ -21,13 +21,36 @@
 
 #include <QtTest/QtTest>
 
+
+class KisPrescaledProjection;
+class KoZoomHandler;
+class QString;
+
 class KisPrescaledProjectionTest : public QObject
 {
     Q_OBJECT
 
+private:
+
+    /**
+     * test the projection through a normal, but complicated scenario.
+     * The prefix is used to save the result qimages and compare them
+     * to the prepared correct images.
+     */
+    bool testProjectionScenario( KisPrescaledProjection & projection, KoZoomHandler \
* viewConverter, const QString & name ); +
 private slots:
 
     void testCreation();
+
+    void testCoordinateConversionRoundTrip();
+
+    // Doesn't fail yet, but at least writes out several versions
+    // of a scaled image. Make them compare with the results when
+    // we're done and have everything okay for regressions
+    void testScalingUndeferredSmoothingPixelForPixel();
+
+    void testScalingUndeferredSmoothing();
 };
 
 #endif
Index: tests/CMakeLists.txt
===================================================================
--- tests/CMakeLists.txt	(revision 717978)
+++ tests/CMakeLists.txt	(working copy)
@@ -1,6 +1,8 @@
 set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} )
 include_directories(  ${KOMAIN_INCLUDES} ${KOGUIUTILS_INCLUDES} )
 
+add_definitions(-DFILES_DATA_DIR="\\"${CMAKE_CURRENT_SOURCE_DIR}/data/\\"")
+
 ########### next target ###############
 
 set(kis_node_model_test_SRCS kis_node_model_test.cpp )
@@ -36,19 +38,19 @@
 
 ########### next target ###############
 
-set(kis_files_test_SRCS kis_files_test.cpp )
+set(kis_prescaled_projection_test_SRCS kis_prescaled_projection_test.cpp )
 
-kde4_add_unit_test(kis_files_test TESTNAME krita-ui-kis_files_test \
${kis_files_test_SRCS}) +kde4_add_unit_test(KisPrescaledProjectionTest TESTNAME \
krita-ui-kis_prescaled_projection_test ${kis_prescaled_projection_test_SRCS})  
-target_link_libraries(kis_files_test  ${KDE4_KDEUI_LIBS} kritaui \
                ${QT_QTTEST_LIBRARY})
-add_definitions(-DFILES_DATA_DIR="\\"${CMAKE_CURRENT_SOURCE_DIR}/data/\\"")
+target_link_libraries(KisPrescaledProjectionTest  ${KDE4_KDEUI_LIBS} kritaui \
${QT_QTTEST_LIBRARY})  
 ########### end ###############
+set(kis_files_test_SRCS kis_files_test.cpp )
 
-set(kis_prescaled_projection_test_SRCS kis_prescaled_projection_test.cpp )
+kde4_add_unit_test(kis_files_test TESTNAME krita-ui-kis_files_test \
${kis_files_test_SRCS})  
-kde4_add_unit_test(KisPrescaledProjectionTest TESTNAME \
krita-ui-kis_prescaled_projection_test ${kis_prescaled_projection_test_SRCS}) \
+target_link_libraries(kis_files_test  ${KDE4_KDEUI_LIBS} kritaui \
${QT_QTTEST_LIBRARY})  
-target_link_libraries(KisPrescaledProjectionTest  ${KDE4_KDEUI_LIBS} kritaui \
${QT_QTTEST_LIBRARY})  
 ########### end ###############
+
Index: kis_view2.cpp
===================================================================
--- kis_view2.cpp	(revision 717978)
+++ kis_view2.cpp	(working copy)
@@ -181,7 +181,12 @@
 
     m_d->canvas = new KisCanvas2( m_d->viewConverter, this, doc->shapeController() \
);  m_d->canvasController->setCanvas( m_d->canvas );
+
     m_d->resourceProvider = new KisResourceProvider( this );
+    m_d->resourceProvider->setCanvasResourceProvider( \
m_d->canvas->resourceProvider() ); +
+    connect( m_d->resourceProvider, SIGNAL( sigDisplayProfileChanged( const \
KoColorProfile * ) ), m_d->canvas, SLOT(slotSetDisplayProfile( const KoColorProfile * \
) ) ); +
     createManagers();
 
     createActions();
@@ -371,8 +376,6 @@
     disconnect(m_d->doc, SIGNAL(sigLoadingFinished()), this, \
SLOT(slotLoadingFinished()));  
     KisImageSP img = image();
-
-    m_d->canvas->setImageSize( img->width(), img->height() );
     slotSetImageSize( img->width(), img->height() );
 
     if(m_d->statusBar) {
@@ -648,8 +651,8 @@
 {
     if ( PreferencesDialog::editPreferences() ) {
         KisConfigNotifier::instance()->notifyConfigChanged();
+        m_d->resourceProvider->resetDisplayProfile();
 
-
         // Update the settings for all nodes -- they don't query
         // KisConfig directly because they need the settings during
         // compositing, and they don't connect to the confignotifier
Index: kis_prescaled_projection.h
===================================================================
--- kis_prescaled_projection.h	(revision 717978)
+++ kis_prescaled_projection.h	(working copy)
@@ -21,6 +21,7 @@
 #include <QObject>
 
 #include <krita_export.h>
+#include <kis_shared.h>
 
 class QPixmap;
 class QImage;
@@ -34,6 +35,9 @@
 
 #include <kis_types.h>
 
+class KisPrescaledProjection;
+typedef KisSharedPtr<KisPrescaledProjection> KisPrescaledProjectionSP;
+
 /**
  * KisPrescaledProjection is responsible for keeping around a
  * prescaled QImage representation that is always suitable for
@@ -62,7 +66,7 @@
  * should become either a QImage the size of the nearest pyramid level
  * or a tiled QImage representation like the OpenGL image textures.
  */
-class KRITAUI_EXPORT KisPrescaledProjection : QObject
+class KRITAUI_EXPORT KisPrescaledProjection : public QObject, public KisShared
 {
     Q_OBJECT
 
@@ -91,14 +95,16 @@
     /**
      * The pre-scaled pixmap includes the underlying checker
      * represenation. It is only generated when the drawCheckers() is
-     * true, otherwise it is empty.
+     * true, otherwise it is empty. The prescaled pixmal is exactly as
+     * big as the canvas widget in pixels.
      */
     QPixmap prescaledPixmap() const;
 
     /**
      * Return the prescaled QImage. This image has a transparency
      * channel and is therefore suitable for generated a prescaled
-     * representation of an image for the KritaShape.
+     * representation of an image for the KritaShape. The prescaled
+     * image is exactly as big as the canvas widget in pixels.
      */
     QImage prescaledQImage() const;
 
@@ -109,6 +115,13 @@
      */
     void setViewConverter( KoViewConverter * viewConverter );
 
+    /**
+     * Return the intersection of the widget size and the given rect
+     * in image pixels converted to widget pixels.
+     */
+    QRect viewRectFromImagePixels( const QRect & imageRect );
+
+
 public slots:
 
     /**
@@ -127,6 +140,8 @@
     /**
      * The image projection has changed, now update the canvas
      * representation of it.
+     *
+     * @param rc the are to be updated in image pixels
      */
     void updateCanvasProjection( const QRect & rc );
 
@@ -144,19 +159,20 @@
 
     /**
      * preScale and draw onto the scaled projection the specfied rect,
-     * in KisImage pixels.
+     * in canvas view pixels.
      */
     void preScale( const QRect & rc );
 
     /**
-     * Resize the prescaled image.
+     * Resize the prescaled image. The size is given in canvas
+     * widget pixels.
      */
-    void resizePrescaledImage( QSize newSize, QSize oldSize );
+    void resizePrescaledImage( QSize newSize );
 
     /**
      * Set the current monitor profile
      */
-    void setMonitorProfile( KoColorProfile * profile );
+    void setMonitorProfile( const KoColorProfile * profile );
 
     /**
      * Set the current HDR exposure
@@ -175,17 +191,43 @@
      */
     void showCurrentMask( bool showMask );
 
+
+
 signals:
 
     /**
      * emitted whenever the prescaled image is ready for painting.
      * This can happen in two stages: a coarse first stage and a
      * smooth second stage.
+     *
+     * @param rc the updated area in image pixels
      */
     void sigPrescaledProjectionUpdated( const QRect & rc );
 
+private slots:
+
+   /**
+    * The timer has fired, and we're going to smoothly scale the
+    * entire rect that's been aggregated, in the main thread (for
+    * now).
+    */
+    void slotDoSmoothScale();
+
 private:
 
+    friend class KisPrescaledProjectionTest;
+
+    void setSettingsForTests(bool updateAllQPainterCanvas,
+                             bool useDeferredSmoothing,
+                             bool useNearestNeighbour,
+                             bool useQtScaling,
+                             bool useSampling,
+                             bool smoothBetween100And200Percent,
+                             bool drawCheckers,
+                             bool drawMaskVisualisationOnUnscaledCanvasCache,
+                             bool cacheKisImageAsQImage,
+                             bool showMask);
+
     KisPrescaledProjection( const KisPrescaledProjection & );
     KisPrescaledProjection operator=( const KisPrescaledProjection & );
 
@@ -194,18 +236,28 @@
      *
      * @param rc The desired rect in KisImage pixels
      * @param gc The painter we draw on
+     * @param isDeferredAction we're in the smoothing cycle, so go
+     * directly to the blitz code
      */
-    void drawScaledImage( const QRect & rc,  QPainter & gc );
+    void drawScaledImage( const QRect & rc,  QPainter & gc, bool isDeferredAction = \
false );  
     /**
-     * Return the intersection of the widget size and the given rect
-     * in image pixels converted to widget pixels.
+     * Return the aligned rect in image pixels.
      */
-    QRect viewRectFromImagePixels( const QRect & imageRect );
+    QRect imageRectFromViewPortPixels( const QRect & viewportRect );
 
+    /**
+     * Update the internal unscaled canvas cache from the kisimage, if
+     * the settings allow that.
+     *
+     * @param imageRect the rect to be updated in image pixels
+     */
+    void updateUnscaledCache( const QRect & imageRect);
+
     struct Private;
     Private * const m_d;
 
 };
 
+
 #endif


["signature.asc" (application/pgp-signature)]

_______________________________________________
kimageshop mailing list
kimageshop@kde.org
https://mail.kde.org/mailman/listinfo/kimageshop


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic