diff --git a/krita/image/kis_image.cc b/krita/image/kis_image.cc
index 94761ca..d62e54d 100644
--- a/krita/image/kis_image.cc
+++ b/krita/image/kis_image.cc
@@ -842,6 +842,17 @@ void KisImage::renderToPainter(qint32 srcX,
     painter.drawImage(dstX, dstY, image, 0, 0, width, height);
 }
 
+QImage KisImage::convertToQImage(QRect imageRect,
+                                 const KoColorProfile * profile)
+{
+    qint32 x;
+    qint32 y;
+    qint32 w;
+    qint32 h;
+    imageRect.getRect(&x, &y, &w, &h);
+    return convertToQImage(x, y, w, h, profile);
+}
+
 QImage KisImage::convertToQImage(qint32 x,
                                  qint32 y,
                                  qint32 w,
diff --git a/krita/image/kis_image.h b/krita/image/kis_image.h
index 5b7fec7..e83af97 100644
--- a/krita/image/kis_image.h
+++ b/krita/image/kis_image.h
@@ -99,6 +99,13 @@ public:
                            qint32 height,
                            const KoColorProfile * profile);
 
+    /**
+     * Render the projection onto a QImage.
+     * (this is an overloaded function)
+     */
+    QImage convertToQImage(QRect imageRect,
+                           const KoColorProfile * profile);
+
 
     /**
      * XXX: docs!
diff --git a/krita/image/kis_projection.cc b/krita/image/kis_projection.cc
index 3e5e551..829d3ce 100644
--- a/krita/image/kis_projection.cc
+++ b/krita/image/kis_projection.cc
@@ -65,8 +65,11 @@ KisProjection::KisProjection(KisImageWSP image)
             m_d->updater, SLOT(startFullRefresh(KisNodeSP, QRect)),
             Qt::BlockingQueuedConnection);
 
-    connect(this, SIGNAL(sigUpdateProjection(KisNodeSP, QRect, QRect)), m_d->updater, SLOT(startUpdate(KisNodeSP, QRect, QRect)));
-    connect(m_d->updater, SIGNAL(updateDone(QRect)), m_d->image, SLOT(slotProjectionUpdated(QRect)));
+    connect(this, SIGNAL(sigUpdateProjection(KisNodeSP, QRect, QRect)),
+            m_d->updater, SLOT(startUpdate(KisNodeSP, QRect, QRect)));
+    connect(m_d->updater, SIGNAL(updateDone(QRect)),
+            m_d->image, SLOT(slotProjectionUpdated(QRect)),
+            Qt::DirectConnection);
 }
 
 
diff --git a/krita/ui/CMakeLists.txt b/krita/ui/CMakeLists.txt
index 28ba624..81dc8fa 100644
--- a/krita/ui/CMakeLists.txt
+++ b/krita/ui/CMakeLists.txt
@@ -28,6 +28,8 @@ set(kritaui_LIB_SRCS
     canvas/kis_projection_cache.cpp
     canvas/kis_qpainter_canvas.cpp
     canvas/kis_projection_backend.cpp
+    canvas/kis_update_info.cpp
+    canvas/kis_image_patch.cpp
     canvas/kis_image_pyramid.cpp
     dialogs/kis_dlg_adj_layer_props.cc 
     dialogs/kis_dlg_adjustment_layer.cc
diff --git a/krita/ui/canvas/kis_canvas2.cpp b/krita/ui/canvas/kis_canvas2.cpp
index bf93e75..623d44b 100644
--- a/krita/ui/canvas/kis_canvas2.cpp
+++ b/krita/ui/canvas/kis_canvas2.cpp
@@ -112,7 +112,8 @@ KisCanvas2::KisCanvas2(KoViewConverter * viewConverter, KisView2 * view, KoShape
     : KoCanvasBase(sc)
     , m_d(new KisCanvas2Private(this, viewConverter, view))
 {
-    createCanvas();
+    KisConfig cfg;
+    createCanvas(cfg.useOpenGL());
     connect(view->canvasController(), SIGNAL(moveDocumentOffset(const QPoint&)), SLOT(documentOffsetMoved(const QPoint&)));
     connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
     connect(this, SIGNAL(canvasDestroyed(QWidget *)), this, SLOT(slotCanvasDestroyed(QWidget *)));
@@ -125,6 +126,8 @@ KisCanvas2::~KisCanvas2()
 
 void KisCanvas2::setCanvasWidget(QWidget * widget)
 {
+    connect(widget, SIGNAL(documentOriginChanged(const QPoint&)), this, SLOT(updateRulers())); 
+
     KisAbstractCanvasWidget * tmp = dynamic_cast<KisAbstractCanvasWidget*>(widget);
     Q_ASSERT_X(tmp, "setCanvasWidget", "Cannot cast the widget to a KisAbstractCanvasWidget");
     emit canvasDestroyed(widget);
@@ -166,6 +169,11 @@ bool KisCanvas2::isCanvasMirrored()
     return m_d->mirrorMode;
 }
 
+void KisCanvas2::mirrorCanvas(bool enable)
+{
+    m_d->mirrorMode = enable;
+    updateCanvas();
+}
 
 void KisCanvas2::addCommand(QUndoCommand *command)
 {
@@ -207,16 +215,6 @@ KoShapeManager * KisCanvas2::globalShapeManager() const
     return m_d->shapeManager;
 }
 
-void KisCanvas2::updateCanvas(const QRectF& rc)
-{
-    // updateCanvas is called from tools, never from the projection
-    // updates, so no need to prescale!
-    QRect vRect = viewRectFromDoc(rc);
-    if (!vRect.isEmpty()) {
-        m_d->canvasWidget->widget()->update(vRect);
-    }
-}
-
 void KisCanvas2::updateInputMethodInfo()
 {
     // TODO call (the protected) QWidget::updateMicroFocus() on the proper canvas widget...
@@ -250,9 +248,6 @@ KoToolProxy * KisCanvas2::toolProxy() const
 
 void KisCanvas2::createQPainterCanvas()
 {
-#ifdef HAVE_OPENGL
-    m_d->openGLImageTextures = 0;
-#endif
     m_d->currentCanvasIsOpenGL = false;
 
     KisQPainterCanvas * canvasWidget = new KisQPainterCanvas(this, m_d->view);
@@ -260,181 +255,238 @@ void KisCanvas2::createQPainterCanvas()
     m_d->prescaledProjection->setViewConverter(m_d->viewConverter);
     m_d->prescaledProjection->setMonitorProfile(monitorProfile());
     canvasWidget->setPrescaledProjection(m_d->prescaledProjection);
-
-    connect(canvasWidget, SIGNAL(documentOriginChanged(const QPoint&)), this, SLOT(updateRulers()));
-
     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);
-        KisOpenGLCanvas2 * canvasWidget = new KisOpenGLCanvas2(this, m_d->view, m_d->openGLImageTextures);
-        setCanvasWidget(canvasWidget);
-        m_d->currentCanvasIsOpenGL = true;
-        m_d->currentCanvasUsesOpenGLShaders = m_d->openGLImageTextures->usingHDRExposureProgram();
-
-        connect(canvasWidget, SIGNAL(documentOriginChanged(const QPoint&)), this, SLOT(updateRulers()));
+    m_d->currentCanvasIsOpenGL = true;
 
-    } else {
-        warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n";
-        createQPainterCanvas();
-    }
+    // XXX: The image isn't done loading here!
+    m_d->openGLImageTextures = KisOpenGLImageTextures::getImageTextures(m_d->view->image(), m_d->monitorProfile);
+    KisOpenGLCanvas2 * canvasWidget = new KisOpenGLCanvas2(this, m_d->view, m_d->openGLImageTextures);
+    m_d->currentCanvasUsesOpenGLShaders = m_d->openGLImageTextures->usingHDRExposureProgram();
+    setCanvasWidget(canvasWidget);
+#else
+    qFatal() << "Bad use of createOpenGLCanvas(). It shouldn't have happened =(";
 #endif
 }
 
-void KisCanvas2::createCanvas()
+void KisCanvas2::createCanvas(bool useOpenGL)
 {
     KisConfig cfg;
     slotSetDisplayProfile(KoColorSpaceRegistry::instance()->profileByName(cfg.monitorProfile()));
 
-    if (cfg.useOpenGL()) {
+    if (useOpenGL) {
 #ifdef HAVE_OPENGL
-        createOpenGLCanvas();
+        if (QGLFormat::hasOpenGL()) {
+            createOpenGLCanvas();
+        } else {
+            warnKrita << "Tried to create OpenGL widget when system doesn't have OpenGL\n";
+            createQPainterCanvas();
+        }
 #else
         warnKrita << "OpenGL requested while its not available, starting qpainter canvas";
         createQPainterCanvas();
 #endif
     } else {
+#ifdef HAVE_OPENGL
+        // Free shared pointer
+        m_d->openGLImageTextures = 0;
+#endif
         createQPainterCanvas();
     }
-
 }
 
-KisView2* KisCanvas2::view()
+void KisCanvas2::connectCurrentImage()
 {
-    return m_d->view;
+
+    if (m_d->currentCanvasIsOpenGL) {
+#ifdef HAVE_OPENGL
+        Q_ASSERT(m_d->openGLImageTextures);
+
+        connect(m_d->view->image(), SIGNAL(sigSizeChanged(qint32, qint32)),
+                m_d->openGLImageTextures, SLOT(slotImageSizeChanged(qint32, qint32)));
+#else
+        qFatal() << "Bad use of connectCurrentImage(). It shouldn't have happened =(";
+#endif
+    } else {
+        connect(m_d->view->image(), SIGNAL(sigSizeChanged(qint32, qint32)),
+                SLOT(setImageSize(qint32, qint32)));
+
+        Q_ASSERT(m_d->prescaledProjection);
+        m_d->prescaledProjection->setImage(m_d->view->image());
+
+    }
+
+    connect(m_d->view->image(), SIGNAL(sigImageUpdated(const QRect &)),
+            SLOT(startUpdateCanvasProjection(const QRect &)),
+            Qt::DirectConnection);
+    connect(this, SIGNAL(sigCanvasCacheUpdated(KisUpdateInfoSP)),
+            this, SLOT(updateCanvasProjection(KisUpdateInfoSP)));
+
+    emit imageChanged(m_d->view->image());
 }
 
+void KisCanvas2::disconnectCurrentImage()
+{
+    if (m_d->currentCanvasIsOpenGL) {
+#ifdef HAVE_OPENGL
+        Q_ASSERT(m_d->openGLImageTextures);
+        m_d->openGLImageTextures->disconnect(this);
+        m_d->openGLImageTextures->disconnect(m_d->view->image());
+#else
+        qFatal() << "Bad use of disconnectCurrentImage(). It shouldn't have happened =(";
+#endif
+    }
 
-QRect KisCanvas2::viewRectFromDoc(const QRectF & rc)
+    disconnect(SIGNAL(sigImageUpdated(const QRect &)));
+    disconnect(SIGNAL(sigCanvasCacheUpdated(KisUpdateInfoSP)));
+
+    // for sigSizeChanged()
+    m_d->view->image()->disconnect(this);
+}
+
+void KisCanvas2::resetCanvas(bool useOpenGL)
 {
-    QRect viewRect = m_d->viewConverter->documentToView(rc).toAlignedRect();
-    viewRect = viewRect.translated(-m_d->documentOffset);
-    // comment out this line if zou want to see the preview outside of the canvas
-    // viewRect = viewRect.intersected(QRect(0, 0, m_d->canvasWidget->widget()->width(), m_d->canvasWidget->widget()->height()));
-    viewRect.translate(documentOrigin());
-    return viewRect;
+#ifdef HAVE_OPENGL
+    KisConfig cfg;
+
+    if (   (useOpenGL != m_d->currentCanvasIsOpenGL)
+        || (   m_d->currentCanvasIsOpenGL
+               && (cfg.useOpenGLShaders() != m_d->currentCanvasUsesOpenGLShaders))) {
+
+        disconnectCurrentImage();
+        createCanvas(useOpenGL);
+        connectCurrentImage();
+    }
+
+    if (useOpenGL) {
+        Q_ASSERT(m_d->openGLImageTextures);
+        m_d->openGLImageTextures->setMonitorProfile(monitorProfile());
+    } else {
+        if (image()) {
+            startUpdateCanvasProjection(image()->bounds());
+        }
+    }
+
+#endif
+    m_d->canvasWidget->widget()->update();
 }
 
+void KisCanvas2::startUpdateCanvasProjection(const QRect & rc)
+{
+    if (m_d->currentCanvasIsOpenGL) {
+#ifdef HAVE_OPENGL
+        Q_ASSERT(m_d->openGLImageTextures);
+        KisUpdateInfoSP info = m_d->openGLImageTextures->updateCache(rc);
+
+        emit sigCanvasCacheUpdated(info);
+#else
+        Q_ASSERT_X(0, "startUpdateCanvasProjection()", "Bad use of startUpdateCanvasProjection(). It shouldn't have happened =(");
+#endif
+    } else {
+        Q_ASSERT(m_d->prescaledProjection);
+        KisUpdateInfoSP info = m_d->prescaledProjection->updateCache(rc);
 
-void KisCanvas2::updateCanvasProjection(const QRect & rc)
+        emit sigCanvasCacheUpdated(info);
+    }
+
+
+}
+
+void KisCanvas2::updateCanvasProjection(KisUpdateInfoSP info)
 {
-    if (m_d->prescaledProjection) {
-        QRect vRect = m_d->prescaledProjection->updateCanvasProjection(rc);
-        
-        // attemp to mirror the rectangle in image coordinates, this does not work to fix correct updates in mirroring mode, 
-        // so use full canvas update so far 
-        // kDebug() << vRect;
-        // vRect = QRect(image()->width() - vRect.x() - vRect.width(),vRect.top(),vRect.width(), vRect.height());
-        // kDebug() << vRect;
-        // kDebug();
+    if (m_d->currentCanvasIsOpenGL) {
+#ifdef HAVE_OPENGL
+        Q_ASSERT(m_d->openGLImageTextures);
+        m_d->openGLImageTextures->recalculateCache(info);
+
+        /**
+         * FIXME: Please not update entire canvas
+         * Implement info->dirtyViewportRect()
+         */
+        m_d->canvasWidget->widget()->update();
+#else
+        Q_ASSERT_X(0, "updateCanvasProjection()", "Bad use of updateCanvasProjection(). It shouldn't have happened =(");
+#endif
+    }
+    else {
+
+        // See comment in startUpdateCanvasProjection()
+        Q_ASSERT(m_d->prescaledProjection);
+
+        m_d->prescaledProjection->recalculateCache(info);
+
+        QRect vRect = info->dirtyViewportRect();
         if (m_d->mirrorMode){
             m_d->canvasWidget->widget()->update();
-        }else
-        if (!vRect.isEmpty()) {
-            vRect.translate( m_d->canvasWidget->documentOrigin());
+        }else if (!vRect.isEmpty()) {
+            vRect.translate(m_d->canvasWidget->documentOrigin());
             m_d->canvasWidget->widget()->update(vRect);
         }
     }
 }
 
-
 void KisCanvas2::updateCanvas()
 {
     m_d->canvasWidget->widget()->update();
 }
 
-
-KisImageWSP KisCanvas2::image()
+void KisCanvas2::updateCanvas(const QRectF& rc)
 {
-    return m_d->view->image();
-
+    // updateCanvas is called from tools, never from the projection
+    // updates, so no need to prescale!
+    QRect vRect = viewRectFromDoc(rc);
+    if (!vRect.isEmpty()) {
+        m_d->canvasWidget->widget()->update(vRect);
+    }
 }
 
-KoColorProfile *  KisCanvas2::monitorProfile()
+void KisCanvas2::preScale()
 {
-    return m_d->monitorProfile;
+    if (!m_d->currentCanvasIsOpenGL) {
+        Q_ASSERT(m_d->prescaledProjection);
+        m_d->prescaledProjection->preScale();
+    }
 }
 
-KisImageWSP KisCanvas2::currentImage()
+QRect KisCanvas2::viewRectFromDoc(const QRectF & rc)
 {
-    return m_d->view->image();
+    QRect viewRect = m_d->viewConverter->documentToView(rc).toAlignedRect();
+    viewRect = viewRect.translated(-m_d->documentOffset);
+    // comment out this line if you want to see the preview outside of the canvas
+    // viewRect = viewRect.intersected(QRect(0, 0, m_d->canvasWidget->widget()->width(), m_d->canvasWidget->widget()->height()));
+    viewRect.translate(documentOrigin());
+    return viewRect;
 }
 
-void KisCanvas2::setImageSize(qint32 w, qint32 h)
+KoColorProfile *  KisCanvas2::monitorProfile()
 {
-    if (m_d->prescaledProjection)
-        m_d->prescaledProjection->setImageSize(w, h);
+    return m_d->monitorProfile;
 }
 
-void KisCanvas2::connectCurrentImage()
+KisView2* KisCanvas2::view()
 {
-#ifdef HAVE_OPENGL
-    if (m_d->openGLImageTextures) {
-        connect(m_d->openGLImageTextures, SIGNAL(sigImageUpdated(const QRect &)), SLOT(updateCanvas()));
-        connect(m_d->openGLImageTextures, SIGNAL(sigSizeChanged(qint32, qint32)), SLOT(setImageSize(qint32, qint32)));
-    } else {
-#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)));
+    return m_d->view;
+}
 
-        if (m_d->prescaledProjection) {
-            m_d->prescaledProjection->setImage(m_d->view->image());
-        }
+KisImageWSP KisCanvas2::image()
+{
+    return m_d->view->image();
 
-#ifdef HAVE_OPENGL
-    }
-#endif
-    emit imageChanged(m_d->view->image());
 }
 
-void KisCanvas2::disconnectCurrentImage()
+KisImageWSP KisCanvas2::currentImage()
 {
-#ifdef HAVE_OPENGL
-    if (m_d->openGLImageTextures) {
-        m_d->openGLImageTextures->disconnect(this);
-    }
-#endif
-    m_d->view->image()->disconnect(this);
+    return m_d->view->image();
 }
 
-void KisCanvas2::resetCanvas(bool useOpenGL)
+void KisCanvas2::setImageSize(qint32 w, qint32 h)
 {
-    KisConfig cfg;
-#if HAVE_OPENGL
-
-    if (   (useOpenGL != m_d->currentCanvasIsOpenGL)
-        || (   m_d->currentCanvasIsOpenGL
-               && (cfg.useOpenGLShaders() != m_d->currentCanvasUsesOpenGLShaders))) {
-
-        disconnectCurrentImage();
-        slotSetDisplayProfile(KoColorSpaceRegistry::instance()->profileByName(cfg.monitorProfile()));
-        if (useOpenGL) {
-#ifdef HAVE_OPENGL
-            createOpenGLCanvas();
-#else
-            warnKrita << "OpenGL requested while its not available, starting qpainter canvas";
-            createQPainterCanvas();
-#endif
-        } else {
-            createQPainterCanvas();
-        }
-        connectCurrentImage();
-    }
-
-    if (useOpenGL) {
-        m_d->openGLImageTextures->setMonitorProfile(monitorProfile());
-    } else {
-        if (image()) {
-            updateCanvasProjection(image()->bounds());
-        }
-    }
-#endif
-    m_d->canvasWidget->widget()->update();
+    if (m_d->prescaledProjection)
+        m_d->prescaledProjection->setImageSize(w, h);
 }
 
 void KisCanvas2::documentOffsetMoved(const QPoint &documentOffset)
@@ -443,12 +495,6 @@ void KisCanvas2::documentOffsetMoved(const QPoint &documentOffset)
     m_d->canvasWidget->documentOffsetMoved(documentOffset);
 }
 
-void KisCanvas2::preScale()
-{
-    if (!m_d->currentCanvasIsOpenGL && m_d->prescaledProjection)
-        m_d->prescaledProjection->preScale();
-}
-
 bool KisCanvas2::usingHDRExposureProgram()
 {
 #ifdef HAVE_OPENGL
@@ -551,11 +597,4 @@ bool KisCanvas2::handlePopupPaletteIsVisible(KoPointerEvent* e)
     return false;
 }
 
-void KisCanvas2::mirrorCanvas(bool enable)
-{
-    m_d->mirrorMode = enable;
-    updateCanvas();
-}
-
-
 #include "kis_canvas2.moc"
diff --git a/krita/ui/canvas/kis_canvas2.h b/krita/ui/canvas/kis_canvas2.h
index 4022ec9..cd2f3f8 100644
--- a/krita/ui/canvas/kis_canvas2.h
+++ b/krita/ui/canvas/kis_canvas2.h
@@ -29,6 +29,8 @@
 #include <kis_types.h>
 #include <KoPointerEvent.h>
 
+#include "kis_prescaled_projection.h"
+
 class KoToolProxy;
 class KoColorProfile;
 
@@ -118,10 +120,7 @@ public: // KoCanvasBase implementation
      */
     void preScale();
 
-    void connectCurrentImage();
-
-    void disconnectCurrentImage();
-
+    // FIXME:
     // Temporary! Either get the current layer and image from the
     // resource provider, or use this, which gets them from the
     // current shape selection.
@@ -148,14 +147,18 @@ signals:
 
     void favoritePaletteCalled(const QPoint&);
 
+    void sigCanvasCacheUpdated(KisUpdateInfoSP);
+
 public slots:
 
     /// Update the entire canvas area
     void updateCanvas();
 
-    /// The image projection has changed, now update the canvas
-    /// representation of it.
-    void updateCanvasProjection(const QRect & rc);
+    /// The image projection has changed, now start an update
+    /// of the canvas representation.
+    void startUpdateCanvasProjection(const QRect & rc);
+
+    void updateCanvasProjection(KisUpdateInfoSP info);
 
     void setImageSize(qint32 w, qint32 h);
 
@@ -191,21 +194,24 @@ private slots:
     void slotCanvasDestroyed(QWidget* w);
 
 public:
+//    friend class KisView2;
 
-    friend class KisView2;
-
+    // interface for KisView2 only
+    void connectCurrentImage();
+    void disconnectCurrentImage();
     void resetCanvas(bool useOpenGL);
 
-    KisCanvas2(const KisCanvas2&);
-
-    void createCanvas();
-    void createQPainterCanvas();
-    void createOpenGLCanvas();
     void createFavoriteResourceManager(KisPaintopBox*);
-
     KoFavoriteResourceManager* favoriteResourceManager();
     bool handlePopupPaletteIsVisible(KoPointerEvent *);
 
+private:
+    Q_DISABLE_COPY(KisCanvas2);
+
+    void createCanvas(bool useOpenGL);
+    void createQPainterCanvas();
+    void createOpenGLCanvas();
+
     /**
      * Returns a rect in widget pixels that is translated for document
      * offset, resolution and zoom and that is guaranteed to be inside
diff --git a/krita/ui/canvas/kis_image_patch.cpp b/krita/ui/canvas/kis_image_patch.cpp
new file mode 100644
index 0000000..163feda
--- /dev/null
+++ b/krita/ui/canvas/kis_image_patch.cpp
@@ -0,0 +1,146 @@
+/*
+ *  Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "kis_image_patch.h"
+
+#include <QPainter>
+#include "kis_debug.h"
+
+#include <math.h>
+#include <qimageblitz.h>
+
+/****** Some helper functions *******/
+
+inline void scaleRect(QRectF &rc, qreal scaleX, qreal scaleY)
+{
+    qreal x, y, w, h;
+    rc.getRect(&x, &y, &w, &h);
+
+    x *= scaleX;
+    y *= scaleY;
+    w *= scaleX;
+    h *= scaleY;
+
+    rc.setRect(x, y, w, h);
+}
+
+inline void scaleRect(QRect &rc, qreal scaleX, qreal scaleY)
+{
+    qint32 x, y, w, h;
+    rc.getRect(&x, &y, &w, &h);
+
+    x *= scaleX;
+    y *= scaleY;
+    w *= scaleX;
+    h *= scaleY;
+
+    rc.setRect(x, y, w, h);
+}
+
+/*********** KisImagePatch ************/
+
+KisImagePatch::KisImagePatch()
+    : m_scaleX(0), m_scaleY(0)
+{
+}
+
+KisImagePatch::KisImagePatch(QRect imageRect, qint32 borderWidth,
+                             qreal scaleX, qreal scaleY)
+    : m_scaleX(scaleX),
+      m_scaleY(scaleY)
+{
+    // First we get unscaled rects
+    m_interestRect = QRectF(borderWidth, borderWidth,
+                            imageRect.width(), imageRect.height());
+    m_patchRect = imageRect.adjusted(-borderWidth, -borderWidth,
+                                     borderWidth, borderWidth);
+    // And then we scale them
+    scaleRect(m_interestRect, scaleX, scaleY);
+    scaleRect(m_patchRect, scaleX, scaleY);
+
+    dbgRender << "A new patch has been created:";
+    dbgRender << ppVar(scaleX) << ppVar(scaleY);
+    dbgRender << ppVar(m_interestRect);
+    dbgRender << ppVar(m_patchRect);
+}
+
+void KisImagePatch::setImage(QImage image)
+{
+    m_image = image;
+}
+
+QRect KisImagePatch::patchRect()
+{
+    return m_patchRect;
+}
+
+bool KisImagePatch::isValid()
+{
+    return !m_image.isNull();
+}
+
+void KisImagePatch::drawMe(QPainter &gc,
+                           const QRectF &dstRect,
+                           QPainter::RenderHints renderHints)
+{
+    gc.save();
+    gc.setCompositionMode(QPainter::CompositionMode_Source);
+    gc.setRenderHints(renderHints, true);
+    gc.drawImage(dstRect, m_image, m_interestRect);
+    gc.restore();
+
+    /**
+     * Just for debugging purposes
+     */
+    qreal scaleX = dstRect.width() / m_interestRect.width();
+    qreal scaleY = dstRect.height() / m_interestRect.height();
+    dbgRender << "## PATCH.DRAWME #####################";
+    dbgRender << ppVar(scaleX) << ppVar(scaleY);
+    dbgRender << ppVar(m_patchRect);
+    dbgRender << ppVar(m_interestRect);
+    dbgRender << ppVar(dstRect);
+    dbgRender << "## EODM #############################";
+}
+
+#define ceiledSize(sz) QSize(ceil((sz).width()), ceil((sz).height()))
+
+void KisImagePatch::prescaleWithBlitz(QRectF dstRect)
+{
+    qreal scaleX = dstRect.width() / m_interestRect.width();
+    qreal scaleY = dstRect.height() / m_interestRect.height();
+
+    QSize newImageSize = QSize(ceil(m_image.width() * scaleX),
+                               ceil(m_image.height() * scaleY));
+
+    // Calculating new _aligned_ scale
+    scaleX = qreal(newImageSize.width()) / m_image.width();
+    scaleY = qreal(newImageSize.height()) / m_image.height();
+
+    m_scaleX *= scaleX;
+    m_scaleY *= scaleY;
+    scaleRect(m_interestRect, scaleX, scaleY);
+
+    m_image = Blitz::smoothScale(m_image,
+                                 newImageSize,
+                                 Qt::IgnoreAspectRatio);
+
+    dbgRender << "## PATCH.PRESCALEBLITZ ############";
+    dbgRender << ppVar(scaleX) << ppVar(scaleY);
+    dbgRender << ppVar(newImageSize);
+    dbgRender << "## EOB ############################";
+}
diff --git a/krita/ui/canvas/kis_image_patch.h b/krita/ui/canvas/kis_image_patch.h
new file mode 100644
index 0000000..d8c4882
--- /dev/null
+++ b/krita/ui/canvas/kis_image_patch.h
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (c) 2010 Dmitry Kazakov <dimula73@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KIS_IMAGE_PATCH_H_
+#define KIS_IMAGE_PATCH_H_
+
+#include <QPainter>
+#include <QImage>
+#include <kis_types.h>
+
+
+class KisImagePatch
+{
+public:
+    /**
+     * A default constuctor initializing invalid patch
+     */
+    KisImagePatch();
+
+    /**
+     * Initializes a new patch with given values.
+     * Be careful, because the constructor does not fill
+     * QImage of the patch, as the patch rect is not known yet
+     *
+     * \see setImage
+     */
+    KisImagePatch(QRect imageRect, qint32 borderWidth,
+                  qreal scaleX, qreal scaleY);
+
+    /**
+     * Sets the image of the patch
+     * Should be called right after the constructor
+     * to finish initializing the object
+     */
+    void setImage(QImage image);
+
+    /**
+     * Returns the rect of KisImage covered by the image
+     * of the patch (in KisImage pixels)
+     *
+     * \see m_patchRect
+     */
+    QRect patchRect();
+
+    /**
+     * Draws an m_interestRect of the patch onto @gc
+     * By the way it fits this rect into @dstRect
+     * @renderHints are directly tranmitted to QPainter
+     */
+    void drawMe(QPainter &gc,
+                const QRectF &dstRect,
+                QPainter::RenderHints renderHints);
+
+    /**
+     * Prescales an interestRect a bit with Blitz
+     * It's usefulness is dispulable and should be
+     * tested, so - FIXME
+     */
+    void prescaleWithBlitz(QRectF dstRect);
+
+    /**
+     * Checks whether the patch can be used for drawing the image
+     */
+    bool isValid();
+
+private:
+    /**
+     * The scale of the image stored in the patch
+     */
+    qreal m_scaleX;
+    qreal m_scaleY;
+
+    /**
+     * The rect of KisImage covered by the image
+     * of the patch (in KisImage pixels)
+     */
+    QRect m_patchRect;
+
+    /**
+     * The rect that was requested during creation
+     * of the patch. It equals to patchRect withount
+     * borders
+     * These borders are introdused for more accurate
+     * smooth scaling to reduce border effects
+     * (IN m_image PIXELS, relative to m_image's topLeft);
+
+     */
+    QRectF m_interestRect;
+
+    QImage m_image;
+};
+
+#endif /* KIS_IMAGE_PATCH_H_ */
diff --git a/krita/ui/canvas/kis_image_pyramid.cpp b/krita/ui/canvas/kis_image_pyramid.cpp
index b04a5d8..4d32d71 100644
--- a/krita/ui/canvas/kis_image_pyramid.cpp
+++ b/krita/ui/canvas/kis_image_pyramid.cpp
@@ -23,9 +23,6 @@
 #include "kis_iterators_pixel.h"
 #include "kis_datamanager.h"
 
-// for toAlignedRectWorkaround()
-#include "kis_prescaled_projection.h"
-
 #include "kis_debug.h"
 
 //#define DEBUG_PYRAMID
@@ -131,7 +128,8 @@ void KisImagePyramid::setImage(KisImageWSP newImage)
 
         clearPyramid();
         setImageSize(m_originalImage->width(), m_originalImage->height());
-        setDirty(m_originalImage->projection()->exactBounds());
+        retrieveImageData(m_originalImage->projection()->exactBounds());
+        //TODO: check whether there is needed recalculateCache()
     }
 }
 
@@ -142,19 +140,27 @@ void KisImagePyramid::setImageSize(qint32 w, qint32 h)
     /* nothing interesting */
 }
 
-void KisImagePyramid::setDirty(const QRect &rc)
+void KisImagePyramid::updateCache(KisPPUpdateInfoSP info)
+{
+    retrieveImageData(info->dirtyImageRect);
+}
+
+void KisImagePyramid::retrieveImageData(const QRect &rect)
 {
     KisPaintDeviceSP originalProjection = m_originalImage->projection();
 
     KisPainter gc(m_pyramid[ORIGINAL_INDEX]);
     gc.setCompositeOp(m_monitorColorSpace->compositeOp(COMPOSITE_COPY));
     gc.setOpacity(OPACITY_OPAQUE_U8);
-    gc.bitBlt(rc.topLeft(), originalProjection, rc);
+    gc.bitBlt(rect.topLeft(), originalProjection, rect);
     gc.end();
+}
 
+void KisImagePyramid::recalculateCache(KisPPUpdateInfoSP info)
+{
     KisPaintDevice *src;
     KisPaintDevice *dst;
-    QRect currentSrcRect = rc;
+    QRect currentSrcRect = info->dirtyImageRect;
 
     for (int i = FIRST_NOT_ORIGINAL_INDEX; i < m_pyramidHeight; i++) {
         src = m_pyramid[i-1].data();
@@ -304,53 +310,27 @@ void KisImagePyramid::alignSourceRect(QRect& rect, qreal scale)
     dbgRender << "After alignment:\t" << rect;
 }
 
-KisImagePatch KisImagePyramid::getNearestPatch(qreal scaleX, qreal scaleY,
-        const QRect& requestedRect,
-        qint32 borderWidth)
+KisImagePatch KisImagePyramid::getNearestPatch(KisPPUpdateInfoSP info)
 {
-    qint32 index = findFirstGoodPlaneIndex(qMax(scaleX, scaleY),
-                                           requestedRect.size());
+    qint32 index = findFirstGoodPlaneIndex(qMax(info->scaleX, info->scaleY),
+                                           info->imageRect.size());
     qreal planeScale = SCALE_FROM_INDEX(index);
     qint32 alignment = 1 << index;
 
-    KisImagePatch patch;
-
-    patch.m_scaleX = planeScale;
-    patch.m_scaleY = planeScale;
-
-    alignByPow2Hi(borderWidth, alignment);
-
-    patch.m_interestRect = toFloatRectWorkaround(
-                               QRect(borderWidth, borderWidth,
-                                     requestedRect.width(),
-                                     requestedRect.height())
-                           );
-
-    QRect adjustedRect = requestedRect.adjusted(-borderWidth, -borderWidth,
-                         borderWidth, borderWidth);
-    patch.m_patchRect = adjustedRect;
-
-    scaleRect(patch.m_interestRect, planeScale, planeScale);
-    scaleRect(patch.m_patchRect, planeScale, planeScale);
-
-    patch.m_image = convertToQImageFast(m_pyramid[index],
-                                        patch.m_patchRect);
+    alignByPow2Hi(info->borderWidth, alignment);
 
-    dbgRender << ppVar(planeScale);
-    dbgRender << ppVar(patch.m_interestRect);
-    dbgRender << ppVar(patch.m_patchRect);
+    KisImagePatch patch(info->imageRect, info->borderWidth,
+                        planeScale, planeScale);
 
+    patch.setImage(convertToQImageFast(m_pyramid[index],
+                                       patch.patchRect()));
     return patch;
 }
 
-void KisImagePyramid::drawFromOriginalImage(QPainter& gc,
-        const QRect& imageRect,
-        const QRectF& viewportRect,
-        qint32 borderWidth,
-        QPainter::RenderHints renderHints)
+void KisImagePyramid::drawFromOriginalImage(QPainter& gc, KisPPUpdateInfoSP info)
 {
-    KisImagePatch patch = getNearestPatch(1, 1, imageRect, borderWidth);
-    patch.drawMe(gc, viewportRect, renderHints);
+    KisImagePatch patch = getNearestPatch(info);
+    patch.drawMe(gc, info->viewportRect, info->renderHints);
 }
 
 QImage KisImagePyramid::convertToQImageFast(KisPaintDeviceSP paintDevice,
diff --git a/krita/ui/canvas/kis_image_pyramid.h b/krita/ui/canvas/kis_image_pyramid.h
index c863e95..a197850 100644
--- a/krita/ui/canvas/kis_image_pyramid.h
+++ b/krita/ui/canvas/kis_image_pyramid.h
@@ -28,7 +28,6 @@
 #include <kis_paint_device.h>
 #include "kis_projection_backend.h"
 
-
 class KisImagePyramid : QObject, public KisProjectionBackend
 {
     Q_OBJECT
@@ -40,17 +39,11 @@ public:
     void setImage(KisImageWSP newImage);
     void setImageSize(qint32 w, qint32 h);
     void setMonitorProfile(const KoColorProfile* monitorProfile);
-    void setDirty(const QRect& rc);
-
-    KisImagePatch getNearestPatch(qreal scaleX, qreal scaleY,
-                                  const QRect& requestedRect,
-                                  qint32 borderWidth);
+    void updateCache(KisPPUpdateInfoSP info);
+    void recalculateCache(KisPPUpdateInfoSP info);
 
-    void drawFromOriginalImage(QPainter& gc,
-                               const QRect& imageRect,
-                               const QRectF& viewportRect,
-                               qint32 borderWidth,
-                               QPainter::RenderHints renderHints);
+    KisImagePatch getNearestPatch(KisPPUpdateInfoSP info);
+    void drawFromOriginalImage(QPainter& gc, KisPPUpdateInfoSP info);
 
     /**
      * Render the projection onto a QImage.
@@ -91,6 +84,7 @@ private:
     qint32 m_pyramidHeight;
 
 private:
+    void retrieveImageData(const QRect &rect);
     void rebuildPyramid();
     void clearPyramid();
 
diff --git a/krita/ui/canvas/kis_prescaled_projection.cpp b/krita/ui/canvas/kis_prescaled_projection.cpp
index 2ed08df..10d3605 100644
--- a/krita/ui/canvas/kis_prescaled_projection.cpp
+++ b/krita/ui/canvas/kis_prescaled_projection.cpp
@@ -22,13 +22,11 @@
 #include <math.h>
 
 #include <QImage>
-#include <QPixmap>
 #include <QColor>
 #include <QRect>
 #include <QPoint>
 #include <QSize>
 #include <QPainter>
-#include <QTimer>
 
 #include <KoColorProfile.h>
 #include <KoColorSpaceRegistry.h>
@@ -37,21 +35,21 @@
 #include "kis_config.h"
 #include "kis_config_notifier.h"
 #include "kis_image.h"
-#include "kis_layer.h"
-#include "kis_mask.h"
-#include "kis_node.h"
-#include "kis_paint_device.h"
-#include "kis_paint_layer.h"
-#include "kis_selection.h"
-#include "kis_selection_mask.h"
-#include "kis_types.h"
 
+#include "kis_projection_backend.h"
 #include "kis_projection_cache.h"
 #include "kis_image_pyramid.h"
 
 
 #define EPSILON 1e-10
 
+#define ceiledSize(sz) QSize(ceil((sz).width()), ceil((sz).height()))
+#define SCALE_LESS_THAN(scX, scY, value) \
+    (scX < (value) - EPSILON && scY < (value) - EPSILON)
+#define SCALE_MORE_OR_EQUAL_TO(scX, scY, value) \
+    (scX > (value) - EPSILON && scY > (value) - EPSILON)
+#define BORDER_SIZE(scale) (ceil(scale * 2))
+
 inline void copyQImageBuffer(uchar* dst, const uchar* src , qint32 deltaX, qint32 width)
 {
     if (deltaX >= 0) {
@@ -84,37 +82,25 @@ void copyQImage(qint32 deltaX, qint32 deltaY, QImage* dstImage, const QImage& sr
 struct KisPrescaledProjection::Private {
     Private()
             : useNearestNeighbour(false)
-            , usePixmapNotQImage(false)
-            , drawCheckers(false)
-            , scrollCheckers(false)
-            , drawMaskVisualisationOnUnscaledCanvasCache(false)
-            , showMask(true)
             , cacheKisImageAsQImage(true)
-            , checkSize(32)
             , documentOffset(0, 0)
             , documentOrigin(0, 0)
             , canvasSize(0, 0)
             , imageSize(0, 0)
             , viewConverter(0)
             , monitorProfile(0)
-            , counter(0)
             , projectionBackend(0) {
     }
 
+    /**
+     * TODO: useNearestNeighbour is not used at the moment
+     * thought this option worth implementing (ok, yeah.. reimplementing)
+     */
     bool useNearestNeighbour;
-    bool usePixmapNotQImage;
     bool useMipmapping;
-    bool drawCheckers;
-    bool scrollCheckers;
-    bool drawMaskVisualisationOnUnscaledCanvasCache;
-    bool showMask;
     bool cacheKisImageAsQImage;
 
-    QColor checkersColor;
-    qint32 checkSize;
     QImage prescaledQImage;
-    QPixmap prescaledPixmap;
-
 
     QPoint documentOffset; // in view pixels
     QPoint documentOrigin; // in view pixels too
@@ -123,10 +109,7 @@ struct KisPrescaledProjection::Private {
     QSize imageSize; // in kisimage pixels
     KisImageWSP image;
     KoViewConverter * viewConverter;
-    KisNodeSP currentNode;
-    QRegion rectsToSmooth;
     const KoColorProfile* monitorProfile;
-    int counter;
     KisProjectionBackend* projectionBackend;
 };
 
@@ -156,23 +139,6 @@ void KisPrescaledProjection::setImage(KisImageWSP image)
     setImageSize(image->width(), image->height());
 }
 
-
-bool KisPrescaledProjection::drawCheckers() const
-{
-    return m_d->drawCheckers;
-}
-
-
-void KisPrescaledProjection::setDrawCheckers(bool drawCheckers)
-{
-    m_d->drawCheckers = drawCheckers;
-}
-
-QPixmap KisPrescaledProjection::prescaledPixmap() const
-{
-    return m_d->prescaledPixmap;
-}
-
 QImage KisPrescaledProjection::prescaledQImage() const
 {
     return m_d->prescaledQImage;
@@ -204,7 +170,6 @@ void KisPrescaledProjection::updateSettings()
 {
     KisConfig cfg;
 
-
     if (m_d->useNearestNeighbour != cfg.useNearestNeighbour() ||
         m_d->useMipmapping != cfg.useMipmapping() ||
         m_d->cacheKisImageAsQImage != cfg.cacheKisImageAsQImage() ||
@@ -217,15 +182,6 @@ void KisPrescaledProjection::updateSettings()
         initBackend(m_d->useMipmapping, m_d->cacheKisImageAsQImage);
     }
 
-    m_d->usePixmapNotQImage = cfg.noXRender();
-
-    // If any of the above are true, we don't use our own smooth scaling
-    m_d->scrollCheckers = cfg.scrollCheckers();
-    m_d->checkSize = cfg.checkSize();
-    m_d->checkersColor = cfg.checkersColor();
-
-    m_d->drawMaskVisualisationOnUnscaledCanvasCache = cfg.drawMaskVisualisationOnUnscaledCanvasCache();
-
     setMonitorProfile(KoColorSpaceRegistry::instance()->profileByName(cfg.monitorProfile()));
 }
 
@@ -282,7 +238,11 @@ void KisPrescaledProjection::documentOffsetMoved(const QPoint &documentOffset)
                 // enough image to draw in them.
                 gc.fillRect(r, QColor(0, 0, 0, 0));
                 dbgRender << "render on rect" << r;
-                drawScaledImage(r , gc);
+                /**
+                 * FIXME: It can happen that we will need access to KisImage here
+                 */
+                KisPPUpdateInfoSP info = getUpdateInformation(r, QRect());
+                drawUsingBackend(gc, info);
             }
         }
         m_d->prescaledQImage = image;
@@ -295,73 +255,52 @@ void KisPrescaledProjection::documentOffsetMoved(const QPoint &documentOffset)
 
 void KisPrescaledProjection::setImageSize(qint32 w, qint32 h)
 {
-
     m_d->projectionBackend->setImageSize(w, h);
 
     m_d->imageSize = QSize(w, h);
 
-    QRect vRect = toAlignedRectWorkaround(viewRectFromImagePixels(QRect(0, 0, w, h)));
+    QRect vRect = viewRectFromImagePixels(QRect(0, 0, w, h)).toAlignedRect();
     vRect = vRect.intersected(QRect(QPoint(0, 0), m_d->canvasSize));
     if (!vRect.isEmpty()) {
         preScale(vRect);
     }
 }
 
-QRect KisPrescaledProjection::updateCanvasProjection(const QRect & rc)
+KisUpdateInfoSP
+KisPrescaledProjection::updateCache(const QRect &dirtyImageRect)
 {
     if (!m_d->image) {
-        dbgRender << "Calling updateCanvasProjection without an image: " << kBacktrace() << endl;
-        return QRect();
+        dbgRender << "Calling updateCache without an image: " << kBacktrace() << endl;
+        // return invalid info
+        return new KisPPUpdateInfo();
     }
 
     /**
-     * FIXME: leave only one of these intersections: canvas or image
-     */
-
-    /**
      * We needn't this stuff ouside KisImage's area. Lets user
      * paint there, anyway we won't show him anything =)
      */
-    if (!rc.intersects(m_d->image->bounds()))
-        return QRect();
-
-    m_d->projectionBackend->setDirty(rc);
-
-    QRect viewRect = toAlignedRectWorkaround(viewRectFromImagePixels(rc));
-    viewRect = viewRect.intersected(QRect(QPoint(0, 0), m_d->canvasSize));
-    QRect newViewRect;
+    if (!dirtyImageRect.intersects(m_d->image->bounds()))
+        return new KisPPUpdateInfo();
 
-    if (!viewRect.isEmpty()) {
-        newViewRect = preScale(viewRect);
+    QRect rawViewRect = viewRectFromImagePixels(dirtyImageRect).toAlignedRect();
+    KisPPUpdateInfoSP info = getUpdateInformation(rawViewRect, dirtyImageRect);
 
-        if (newViewRect != viewRect) {
-            dbgRender << "canvas size:" << m_d->canvasSize;
-            dbgRender << "viewRect before alignment:" << viewRect;
-            dbgRender << "viewRect after alignment: " << newViewRect;
-        }
-    }
+    m_d->projectionBackend->updateCache(info);
 
-    return newViewRect;
+    return info;
 }
 
-
-void KisPrescaledProjection::setMonitorProfile(const KoColorProfile * profile)
+void KisPrescaledProjection::recalculateCache(KisUpdateInfoSP info)
 {
-    m_d->monitorProfile = profile;
-    m_d->projectionBackend->setMonitorProfile(profile);
-}
+    KisPPUpdateInfoSP ppInfo = dynamic_cast<KisPPUpdateInfo*>(info.data());
+    if(!ppInfo) return;
 
-void KisPrescaledProjection::setCurrentNode(const KisNodeSP node)
-{
-    m_d->currentNode = node;
-}
+    m_d->projectionBackend->recalculateCache(ppInfo);
 
-void KisPrescaledProjection::showCurrentMask(bool showMask)
-{
-    m_d->showMask = showMask;
+    if(!info->dirtyViewportRect().isEmpty())
+        updateScaledImage(ppInfo);
 }
 
-
 void KisPrescaledProjection::preScale()
 {
     Q_ASSERT(m_d->canvasSize.isValid());
@@ -370,14 +309,26 @@ void KisPrescaledProjection::preScale()
 
 QRect KisPrescaledProjection::preScale(const QRect & rc)
 {
-    QRect retval;
-    if (!rc.isEmpty()) {
+    if (!rc.isEmpty() && m_d->image) {
+        /**
+         * FIXME: It can happen that we will need access to KisImage here
+         */
+        KisPPUpdateInfoSP info = getUpdateInformation(rc, QRect());
+
         QPainter gc(&m_d->prescaledQImage);
         gc.setCompositionMode(QPainter::CompositionMode_Source);
         gc.fillRect(rc, QColor(0, 0, 0, 0));
-        retval = drawScaledImage(rc, gc);
+        drawUsingBackend(gc, info);
+        //FIXME: leave one of those rects, probably, first.
+        return rc | info->viewportRect.toAlignedRect();
     }
-    return retval;
+    return QRect();
+}
+
+void KisPrescaledProjection::setMonitorProfile(const KoColorProfile * profile)
+{
+    m_d->monitorProfile = profile;
+    m_d->projectionBackend->setMonitorProfile(profile);
 }
 
 void KisPrescaledProjection::resizePrescaledImage(const QSize & newSize)
@@ -396,45 +347,23 @@ void KisPrescaledProjection::resizePrescaledImage(const QSize & newSize)
     QImage image = QImage(newSize, QImage::Format_ARGB32);
 //     m_d->prescaledQImage.fill( QColor( 255, 0, 0, 128 ).rgba() );
 
-// Let someone else figure out the optimization where we copy the
+// FIXME: 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 gc(&image);
-    gc.setCompositionMode(QPainter::CompositionMode_Source);
-
-    if (newSize.width() > oldSize.width() || newSize.height() > oldSize.height()) {
-
-        gc.drawImage(0, 0,
-                     m_d->prescaledQImage,
-                     0, 0, m_d->prescaledQImage.width(), m_d->prescaledQImage.height());
-
-        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);
-        }
-    } else {
-        gc.drawImage(0, 0, m_d->prescaledQImage,
-                     0, 0, m_d->prescaledQImage.width(), m_d->prescaledQImage.height());
-    }
-#endif
 
-#if 1
     m_d->prescaledQImage = image;
     m_d->canvasSize = newSize;
     preScale();
-#endif
-
 }
 
-QRect KisPrescaledProjection::drawScaledImage(const QRect & rc,  QPainter & gc)
+KisPPUpdateInfoSP
+KisPrescaledProjection::getUpdateInformation(const QRect &viewportRect,
+                                             const QRect &dirtyImageRect)
 {
-    if (!m_d->image)
-        return QRect();
-
     Q_ASSERT(m_d->viewConverter);
 
+    KisPPUpdateInfoSP info = new KisPPUpdateInfo();
+
     // get the x and y zoom level of the canvas
     qreal zoomX, zoomY;
     m_d->viewConverter->zoom(&zoomX, &zoomY);
@@ -444,67 +373,71 @@ QRect KisPrescaledProjection::drawScaledImage(const QRect & rc,  QPainter & gc)
     qreal resY = m_d->image->yRes();
 
     // Compute the scale factors
-    qreal scaleX = zoomX / resX;
-    qreal scaleY = zoomY / resY;
+    info->scaleX = zoomX / resX;
+    info->scaleY = zoomY / resY;
 
-    QRect updateRectInImagePixels = imageRectFromViewPortPixels(toFloatRectWorkaround(rc));
+    // save it for future
+    info->dirtyImageRect = dirtyImageRect;
 
-    m_d->projectionBackend->alignSourceRect(updateRectInImagePixels, scaleX);
+    // first, crop the part of the view rect that is outside of the canvas
+    QRect croppedViewRect = viewportRect.intersected(QRect(QPoint(0, 0), m_d->canvasSize));
 
-    QRectF updateRect = viewRectFromImagePixels(updateRectInImagePixels);
+    // second, align this rect to the KisImage's pixels and pixels
+    // of projection backend
+    info->imageRect = imageRectFromViewPortPixels(QRectF(croppedViewRect));
+    m_d->projectionBackend->alignSourceRect(info->imageRect, info->scaleX);
 
+    // finally, compute the dirty rect of the canvas
+    info->viewportRect = viewRectFromImagePixels(info->imageRect);
 
-    drawUsingBackend(m_d->projectionBackend, gc, scaleX, scaleY,
-                     updateRectInImagePixels, updateRect);
+    info->borderWidth = 0;
+    if (SCALE_MORE_OR_EQUAL_TO(info->scaleX, info->scaleY, 1.0)) {
+        if (SCALE_LESS_THAN(info->scaleX, info->scaleY, 2.0)) {
+            dbgRender << "smoothBetween100And200Percent" << endl;
+            info->renderHints = QPainter::SmoothPixmapTransform;
+            info->borderWidth = BORDER_SIZE(qMax(info->scaleX, info->scaleY));
+        }
+        info->transfer = KisPPUpdateInfo::DIRECT;
+    } else { // <100%
+        info->renderHints = QPainter::SmoothPixmapTransform;
+        info->borderWidth = BORDER_SIZE(qMax(info->scaleX, info->scaleY));
+        info->transfer = KisPPUpdateInfo::PATCH;
+    }
 
     dbgRender << "#####################################";
     dbgRender << ppVar(resX) << ppVar(resY);
     dbgRender << ppVar(zoomX) << ppVar(zoomY);
-    dbgRender << ppVar(scaleX) << ppVar(scaleY);
-    dbgRender << ppVar(rc);
-    dbgRender << "Update rect in KisImage's pixels:\t" << updateRectInImagePixels;
-    dbgRender << "Update rect in canvas' pixels:\t" << updateRect;
+    dbgRender << ppVar(info->scaleX) << ppVar(info->scaleY);
+    dbgRender << ppVar(info->borderWidth) << ppVar(info->renderHints);
+    dbgRender << ppVar(info->transfer);
+    dbgRender << ppVar(dirtyImageRect);
+    dbgRender << "Not aligned rect of the canvas (raw):\t" << croppedViewRect;
+    dbgRender << "Update rect in KisImage's pixels:\t" << info->imageRect;
+    dbgRender << "Update rect in canvas' pixels:\t" << info->viewportRect;
     dbgRender << "#####################################";
 
-    return toAlignedRectWorkaround(updateRect);
+    return info;
 }
 
-
-#define ceiledSize(sz) QSize(ceil((sz).width()), ceil((sz).height()))
-#define SCALE_LESS_THAN(scX, scY, value) \
-    (scX < (value) - EPSILON && scY < (value) - EPSILON)
-#define SCALE_MORE_OR_EQUAL_TO(scX, scY, value) \
-    (scX > (value) - EPSILON && scY > (value) - EPSILON)
-#define BORDER_SIZE(scale) (ceil(scale * 2))
-
-void KisPrescaledProjection::drawUsingBackend(KisProjectionBackend *backend,
-        QPainter &gc,
-        qreal &scaleX, qreal &scaleY,
-        const QRect   &imageRect,
-        const QRectF  &viewportRect)
+void KisPrescaledProjection::updateScaledImage(KisPPUpdateInfoSP info)
 {
-    if (imageRect.isEmpty()) return;
+    QPainter gc(&m_d->prescaledQImage);
+    gc.setCompositionMode(QPainter::CompositionMode_Source);
+//    gc.fillRect(viewRectFromImagePixels(info->dirtyImageRect).toAlignedRect(), QColor(255, 0, 0, 255));
+    drawUsingBackend(gc, info);
+}
 
-    if (SCALE_MORE_OR_EQUAL_TO(scaleX, scaleY, 1.0)) {
-        QPainter::RenderHints renderHints;
-        qint32 borderWidth = 0;
+void KisPrescaledProjection::drawUsingBackend(QPainter &gc, KisPPUpdateInfoSP info)
+{
+    if (info->imageRect.isEmpty()) return;
 
-        if (SCALE_LESS_THAN(scaleX, scaleY, 2.0)) {
-            dbgRender << "smoothBetween100And200Percent" << endl;
-            renderHints = QPainter::SmoothPixmapTransform;
-            borderWidth = BORDER_SIZE(qMax(scaleX, scaleY));
-        }
-        backend->drawFromOriginalImage(gc, imageRect, viewportRect,
-                                       borderWidth,
-                                       renderHints);
-    } else { // <100%
-        KisImagePatch patch =
-            backend->getNearestPatch(scaleX, scaleY,
-                                     imageRect,
-                                     BORDER_SIZE(qMax(scaleX, scaleY)));
+    if (info->transfer == KisPPUpdateInfo::DIRECT) {
+        m_d->projectionBackend->drawFromOriginalImage(gc, info);
+    } else /* if info->transfer == KisPPUpdateInformation::PATCH */ {
+        KisImagePatch patch = m_d->projectionBackend->getNearestPatch(info);
 
         //patch.prescaleWithBlitz(viewportRect);
-        patch.drawMe(gc, viewportRect, QPainter::SmoothPixmapTransform);
+        patch.drawMe(gc, info->viewportRect, info->renderHints);
     }
 }
 
@@ -515,7 +448,7 @@ QRectF KisPrescaledProjection::viewRectFromImagePixels(const QRect& rc)
     qreal xRes = m_d->image->xRes();
     qreal yRes = m_d->image->yRes();
 
-    QRectF imageRect = toFloatRectWorkaround(rc);
+    QRectF imageRect(rc);
 
     QRectF docRect;
     docRect.setCoords(imageRect.left() / xRes, imageRect.top() / yRes,
@@ -542,7 +475,7 @@ QRect KisPrescaledProjection::imageRectFromViewPortPixels(const QRectF& viewport
     QRectF translatedRect = intersectedRect.translated(m_d->documentOffset);
     QRectF docRect = m_d->viewConverter->viewToDocument(translatedRect);
 
-    return toAlignedRectWorkaround(m_d->image->documentToPixel(docRect)).intersected(m_d->image->bounds());
+    return m_d->image->documentToPixel(docRect).toAlignedRect().intersected(m_d->image->bounds());
 }
 
 
@@ -551,44 +484,4 @@ void KisPrescaledProjection::updateDocumentOrigin(const QPoint& documentOrigin)
     m_d->documentOrigin = documentOrigin;
 }
 
-
-QRectF toFloatRectWorkaround(const QRect& rc)
-{
-    QRectF temp;
-
-    /**
-     * We can't use setCoords here due to "history reasons",
-     * mentioned in Qt documentation.
-     * More than that we make rounding process more stable
-     * with cropping the rect by EPSILON
-     */
-    temp.setRect(rc.left() + EPSILON, rc.top() + EPSILON,
-                 rc.width() - 2 * EPSILON, rc.height() - 2 * EPSILON);
-
-    return temp;
-}
-
-QRect toAlignedRectWorkaround(const QRectF& rc)
-{
-    qreal x1, y1, x2, y2;
-    rc.getCoords(&x1, &y1, &x2, &y2);
-
-    x1 = floor(x1);
-    y1 = floor(y1);
-    x2 = ceil(x2);
-    y2 = ceil(y2);
-
-    QRect ret;
-
-    /**
-     * We can't use setCoords here due to "history reasons",
-     * mentioned in Qt documentation
-     */
-    ret.setRect(x1, y1, x2 - x1, y2 - y1);
-
-    return ret;
-}
-
-
-
 #include "kis_prescaled_projection.moc"
diff --git a/krita/ui/canvas/kis_prescaled_projection.h b/krita/ui/canvas/kis_prescaled_projection.h
index 302e601..26e3753 100644
--- a/krita/ui/canvas/kis_prescaled_projection.h
+++ b/krita/ui/canvas/kis_prescaled_projection.h
@@ -23,7 +23,8 @@
 #include <krita_export.h>
 #include <kis_shared.h>
 
-class QPixmap;
+#include "kis_update_info.h"
+
 class QImage;
 class QPoint;
 class QRect;
@@ -34,8 +35,6 @@ class QPainter;
 class KoViewConverter;
 class KoColorProfile;
 
-class KisProjectionBackend;
-
 #include <kis_types.h>
 
 class KisPrescaledProjection;
@@ -46,11 +45,13 @@ typedef KisSharedPtr<KisPrescaledProjection> KisPrescaledProjectionSP;
  * prescaled QImage representation that is always suitable for
  * painting onto the canvas.
  *
+ * [deprecated]
  * Optionally, the KisPrescaledProjection can also provide a QPixmap
  * with the checkered background blended in.
  *
  * Optionally, the projection can also draw the mask and selection
  * masks and the selection outline.
+ * [/deprecated]
  *
  * The following scaling methods are supported:
  *
@@ -72,7 +73,6 @@ typedef KisSharedPtr<KisPrescaledProjection> KisPrescaledProjectionSP;
 class KRITAUI_EXPORT KisPrescaledProjection : public QObject, public KisShared
 {
     Q_OBJECT
-
 public:
 
     KisPrescaledProjection();
@@ -81,29 +81,6 @@ public:
     void setImage(KisImageWSP image);
 
     /**
-     * @return true if the prescaled projection is set to draw the
-     * checkers, too. In that case, prescaledPixmap returns a complete
-     * pixmap (which doesn't have transparency) and prescaledQImage
-     * returns an empty QImage. This setting is <i>false</i>
-     * initially.
-     */
-    bool drawCheckers() const;
-
-    /**
-     * Set the drawCheckers variable to @param drawCheckers. @see
-     * drawCheckers.
-     */
-    void setDrawCheckers(bool drawCheckers);
-
-    /**
-     * The pre-scaled pixmap includes the underlying checker
-     * represenation. It is only generated when the drawCheckers() is
-     * 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. The prescaled
@@ -127,6 +104,23 @@ public:
 public slots:
 
     /**
+     * Retrieves image's data from KisImage object and updates
+     * internal cache
+     * @param dirtyImageRect the rect changed on the image
+     * @return prefilled info object that should be used in
+     * the second stage of the update
+     * @see recalculateCache
+     */
+    KisUpdateInfoSP updateCache(const QRect &dirtyImageRect);
+
+    /**
+     * Updates the prescaled cache at current zoom level
+     * @param prefilled update structure returned by updateCache
+     * @see updateCache
+     */
+    void recalculateCache(KisUpdateInfoSP info);
+
+    /**
      * Called whenever the configuration settings change.
      */
     void updateSettings();
@@ -146,15 +140,6 @@ public slots:
     void documentOffsetMoved(const QPoint &documentOffset);
 
     /**
-     * The image projection has changed, now update the canvas
-     * representation of it.
-     *
-     * @param rc the are to be updated in image pixels
-     * @return a rect to be updated in widget pixels
-     */
-    QRect updateCanvasProjection(const QRect & rc);
-
-    /**
      * Called whenever the size of the KisImage changes
      */
     void setImageSize(qint32 w, qint32 h);
@@ -171,76 +156,46 @@ public slots:
     void setMonitorProfile(const KoColorProfile * profile);
 
     /**
-     * Set the current node
-     */
-    void setCurrentNode(const KisNodeSP node);
-
-    /**
-     * Toggle whether the selection should be displayed as a mask.
-     * (The display as ants should be a toggle, too, but is done
-     * elsewhere.)
-     */
-    void showCurrentMask(bool showMask);
-
-
-    /**
      * Called whenever the zoom level changes or another chunk of the
      * image becomes visible. The currently visible area of the image
      * is complete scaled again.
      */
     void preScale();
 
+private:
+    friend class KisPrescaledProjectionTest;
+
+    KisPrescaledProjection(const KisPrescaledProjection &);
+    KisPrescaledProjection operator=(const KisPrescaledProjection &);
+
     /**
      * preScale and draw onto the scaled projection the specified rect,
      * in canvas view pixels.
      */
     QRect preScale(const QRect & rc);
 
-
-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.
+     * Prepare all the information about rects needed during
+     * projection updating
      *
-     * @param rc the updated area in image pixels
+     * @param dirtyImageRect the part of the KisImage that is dirty
      */
-    // FIXME: seems to be not used
-    //void sigPrescaledProjectionUpdated(const QRect & rc);
-
-private:
-    friend class KisPrescaledProjectionTest;
-
-    KisPrescaledProjection(const KisPrescaledProjection &);
-    KisPrescaledProjection operator=(const KisPrescaledProjection &);
+    KisPPUpdateInfoSP getUpdateInformation(const QRect &viewportRect,
+                                           const QRect &dirtyImageRect);
 
     /**
-     * Draw the prescaled image onto the painter.
+     * Initiates the process of prescaled image update
      *
-     * @param rc The desired rect (NOT in KisImage pixels)
-     * It's in viewport pixels
-     * @param gc The painter we draw on
-     * directly to the blitz code
-     * @return a rect actually updated during drawing
-     * it can be greater that @rc do to KisImage alignment
+     * @param info prepared information
      */
-    QRect drawScaledImage(const QRect & rc,  QPainter & gc);
-
+    void updateScaledImage(KisPPUpdateInfoSP info);
 
     /**
-     * Actual drawing is done here
-     * @param imageRect - region of the KisImage to read from
-     * (in KisImage pixels)
-     * @param viewportRect - region of @gc to draw to. Of course
-     * it is in @gc's pixels. Actual image will be scaled down to
-     * fit viewportRect
+     * Atual drawing is done here
+     * @param info prepared information
+     * @param gc The painter we draw on
      */
-    void drawUsingBackend(KisProjectionBackend *backend,
-                          QPainter &gc,
-                          qreal &scaleX, qreal &scaleY,
-                          const QRect   &imageRect,
-                          const QRectF  &viewportRect);
+    void drawUsingBackend(QPainter &gc, KisPPUpdateInfoSP info);
 
     /**
      * Converts image pixels into widget pixels
@@ -264,24 +219,4 @@ private:
     Private * const m_d;
 };
 
-/**
- * A workaroung for Qt's strange behavior of QRect::toAlignedRect()
- */
-KRITAUI_EXPORT QRect toAlignedRectWorkaround(const QRectF& rc);
-
-/**
- * Heh.. "Due to hitory reasons" [1] Qt's QRect::right() doesn't return
- * the coordinate of rightmost side of the rect, it returns
- * the position of the last (rightmost) pixel owned by the rect.
- * However QRectF implements the former case.
- * We workaround it here AND help rounding process a bit.
- * Returned QRectF is ~2e-10 pixels smaller, than original @rc.
- * It doesn't play any role in drawing, but it helps to round up
- * back to @rc afterwards.
- *
- * [1] - see Qt documentation for more
- */
-KRITAUI_EXPORT QRectF toFloatRectWorkaround(const QRect& rc);
-
-
 #endif
diff --git a/krita/ui/canvas/kis_projection_backend.cpp b/krita/ui/canvas/kis_projection_backend.cpp
index 0622bb6..48e073e 100644
--- a/krita/ui/canvas/kis_projection_backend.cpp
+++ b/krita/ui/canvas/kis_projection_backend.cpp
@@ -18,14 +18,6 @@
 
 #include "kis_projection_backend.h"
 
-#include <QRect>
-#include <QRectF>
-#include <QPainter>
-#include "kis_debug.h"
-
-#include <math.h>
-#include <qimageblitz.h>
-
 KisProjectionBackend::~KisProjectionBackend()
 {
 }
@@ -35,54 +27,3 @@ void KisProjectionBackend::alignSourceRect(QRect& rect, qreal scale)
     Q_UNUSED(rect);
     Q_UNUSED(scale);
 }
-
-void KisImagePatch::drawMe(QPainter &gc,
-                           const QRectF &dstRect,
-                           QPainter::RenderHints renderHints)
-{
-    gc.save();
-    gc.setCompositionMode(QPainter::CompositionMode_Source);
-    gc.setRenderHints(renderHints, true);
-    gc.drawImage(dstRect, m_image, m_interestRect);
-    gc.restore();
-
-    /**
-     * Just for debugging purposes
-     */
-    qreal scaleX = dstRect.width() / m_interestRect.width();
-    qreal scaleY = dstRect.height() / m_interestRect.height();
-    dbgRender << "## PATCH.DRAWME #####################";
-    dbgRender << ppVar(scaleX) << ppVar(scaleY);
-    dbgRender << ppVar(m_patchRect);
-    dbgRender << ppVar(m_interestRect);
-    dbgRender << ppVar(dstRect);
-    dbgRender << "## EODM #############################";
-}
-
-#define ceiledSize(sz) QSize(ceil((sz).width()), ceil((sz).height()))
-
-void KisImagePatch::prescaleWithBlitz(QRectF dstRect)
-{
-    qreal scaleX = dstRect.width() / m_interestRect.width();
-    qreal scaleY = dstRect.height() / m_interestRect.height();
-
-    QSize newImageSize = QSize(ceil(m_image.width() * scaleX),
-                               ceil(m_image.height() * scaleY));
-
-    // Calculating new _aligned_ scale
-    scaleX = qreal(newImageSize.width()) / m_image.width();
-    scaleY = qreal(newImageSize.height()) / m_image.height();
-
-    m_scaleX *= scaleX;
-    m_scaleY *= scaleY;
-    scaleRect(m_interestRect, scaleX, scaleY);
-
-    m_image = Blitz::smoothScale(m_image,
-                                 newImageSize,
-                                 Qt::IgnoreAspectRatio);
-
-    dbgRender << "## PATCH.PRESCALEBLITZ ############";
-    dbgRender << ppVar(scaleX) << ppVar(scaleY);
-    dbgRender << ppVar(newImageSize);
-    dbgRender << "## EOB ############################";
-}
diff --git a/krita/ui/canvas/kis_projection_backend.h b/krita/ui/canvas/kis_projection_backend.h
index 6465b6b..a8abf40 100644
--- a/krita/ui/canvas/kis_projection_backend.h
+++ b/krita/ui/canvas/kis_projection_backend.h
@@ -19,14 +19,11 @@
 #ifndef KIS_PROJECTION_BACKEND
 #define KIS_PROJECTION_BACKEND
 
-#include <QPainter>
-#include <QImage>
-#include <QSize>
-#include <QRect>
-#include <kis_types.h>
+#include "kis_update_info.h"
 
 class KoColorProfile;
-struct KisImagePatch;
+class UpdateInformation;
+class KisImagePatch;
 
 /**
  * KisProjectionBackend ia an abstract class represinting
@@ -48,9 +45,19 @@ public:
     virtual void setMonitorProfile(const KoColorProfile* monitorProfile) = 0;
 
     /**
-     * Updates @rc (in KisImage pixels) from the base image
+     * Updates the cache of the backend by reading from
+     * an accociated image. All data transfers with
+     * KisImage should happen here
      */
-    virtual void setDirty(const QRect& rc) = 0;
+    virtual void updateCache(KisPPUpdateInfoSP info) = 0;
+
+    /**
+     * Prescales the cache of the backend. It is intended to be
+     * called from a separate thread where you can easily
+     * do the calculations. No data transfers with KisImage
+     * should happen during this phase
+     */
+    virtual void recalculateCache(KisPPUpdateInfoSP info) = 0;
 
     /**
      * Some backends cannot work with arbitrary areas due to
@@ -60,105 +67,30 @@ public:
     virtual void alignSourceRect(QRect& rect, qreal scale);
 
     /**
-     * Get a patch from a backend that can draw a @requestedRect on some
-     * QPainter in future. @scaleX and @scaleY are the scales of planned
-     * drawing, btw, it doesn't mean that an QImage inside the patch will
-     * have these scales - it'll have the nearest suitable scale or even
-     * original scale (e.g. KisProjectionCache)
+     * Gets a patch from a backend that can draw a info.imageRect on some
+     * QPainter in future. info.scaleX and info.scaleY are the scales
+     * of planned drawing, btw, it doesn't mean that an QImage inside
+     * the patch will have these scales - it'll have the nearest suitable
+     * scale or even original scale (e.g. KisProjectionCache)
      *
-     * If @borderWidth is non-zero, @requestedRect will be axpended by
-     * @borderWidth pixels to all directions and image of this rect will
-     * actually be written to the patch's QImage. That is done to eliminate
-     * border effects in smooth scaling.
+     * If info.borderWidth is non-zero, info.requestedRect will
+     * be axpended by info.borderWidth pixels to all directions and
+     * image of this rect will actually be written to the patch's QImage.
+     * That is done to eliminate border effects in smooth scaling.
      */
-    virtual KisImagePatch getNearestPatch(qreal scaleX, qreal scaleY,
-                                          const QRect& requestedRect,
-                                          qint32 borderWidth) = 0;
+    virtual KisImagePatch getNearestPatch(KisPPUpdateInfoSP info) = 0;
 
     /**
      * Draws a piece of original image onto @gc's canvas
-     * @imageRect - area in KisImage pixels where to read from
-     * @viewportRect - area in canvas pixels where to write to
-     * If @imageRect and @viewportRect don't agree, the image
+     * @param info.imageRect - area in KisImage pixels where to read from
+     * @param info.viewportRect - area in canvas pixels where to write to
+     * If info.imageRect and info.viewportRect don't agree, the image
      * will be scaled
-     * @borderWidth has the same meaning as in getNearestPatch
-     * @renderHints - hints, transmitted to QPainter during darwing
+     * @param info.borderWidth has the same meaning as in getNearestPatch
+     * @param info.renderHints - hints, transmitted to QPainter during darwing
      */
     virtual void drawFromOriginalImage(QPainter& gc,
-                                       const QRect& imageRect,
-                                       const QRectF& viewportRect,
-                                       qint32 borderWidth,
-                                       QPainter::RenderHints renderHints) = 0;
-};
-
-
-struct KisImagePatch {
-    /**
-     * The scale of the image stored in the patch
-     */
-    qreal m_scaleX;
-    qreal m_scaleY;
-
-    /**
-     * The rect of KisImage covered by the image
-     * of the patch (in KisImage pixels)
-     */
-    QRect m_patchRect;
-
-    /**
-     * The rect that was requested during creation
-     * of the patch. It equals to patchRect withount
-     * borders
-     * These borders are introdused for more accurate
-     * smooth scaling to reduce border effects
-     * (IN m_image PIXELS, relative to m_image's topLeft);
-
-     */
-    QRectF m_interestRect;
-
-    QImage m_image;
-
-    /**
-     * Darws an m_interestRect of the patch onto @gc
-     * By the way it fits this rect into @dstRect
-     * @renderHints are directly tranmitted to QPainter
-     */
-    void drawMe(QPainter &gc,
-                const QRectF &dstRect,
-                QPainter::RenderHints renderHints);
-
-    /**
-     * Prescales an interestRect a bit with Blitz
-     * It's usefulness is dispulable and should be
-     * tested, so - FIXME
-     */
-    void prescaleWithBlitz(QRectF dstRect);
+                                       KisPPUpdateInfoSP info) = 0;
 };
 
-inline void scaleRect(QRectF &rc, qreal scaleX, qreal scaleY)
-{
-    qreal x, y, w, h;
-    rc.getRect(&x, &y, &w, &h);
-
-    x *= scaleX;
-    y *= scaleY;
-    w *= scaleX;
-    h *= scaleY;
-
-    rc.setRect(x, y, w, h);
-}
-
-inline void scaleRect(QRect &rc, qreal scaleX, qreal scaleY)
-{
-    qint32 x, y, w, h;
-    rc.getRect(&x, &y, &w, &h);
-
-    x *= scaleX;
-    y *= scaleY;
-    w *= scaleX;
-    h *= scaleY;
-
-    rc.setRect(x, y, w, h);
-}
-
 #endif /* KIS_PROJECTION_BACKEND */
diff --git a/krita/ui/canvas/kis_projection_cache.cpp b/krita/ui/canvas/kis_projection_cache.cpp
index 84cb8a9..99ecb9a 100644
--- a/krita/ui/canvas/kis_projection_cache.cpp
+++ b/krita/ui/canvas/kis_projection_cache.cpp
@@ -19,13 +19,8 @@
 #include <KoColorProfile.h>
 
 #include "kis_projection_cache.h"
-#include "kis_config.h"
 #include <kis_image.h>
 
-// for toAlignedRectWorkaround()
-#include "kis_prescaled_projection.h"
-
-
 KisProjectionCache::KisProjectionCache()
         : m_cacheKisImageAsQImage(true)
         , m_monitorProfile(0)
@@ -49,7 +44,7 @@ void KisProjectionCache::setCacheKisImageAsQImage(bool toggle)
     if (toggle != m_cacheKisImageAsQImage) {
         if (toggle) {
             m_cacheKisImageAsQImage = true;
-            setDirty(QRect(0, 0, m_imageSize.width(), m_imageSize.height()));
+            updateCachedQImage(QRect(0, 0, m_imageSize.width(), m_imageSize.height()));
         } else {
             m_cacheKisImageAsQImage = false;
             m_unscaledCache = QImage();
@@ -76,7 +71,12 @@ void KisProjectionCache::setImageSize(qint32 w,  qint32 h)
 #endif
 
         m_unscaledCache = QImage(w, h, QImage::Format_ARGB32);
-        setDirty(QRect(0, 0, w, h));
+        /**
+         * FIXME: Check why not use m_image->bounds()
+         * instead of QRect(0, 0, w, h)
+         */
+        if (m_cacheKisImageAsQImage)
+            updateCachedQImage(QRect(0, 0, w, h));
 
 #ifndef ALWAYS_CACHE_AS_QIMAGE
     }
@@ -88,100 +88,79 @@ void KisProjectionCache::setMonitorProfile(const KoColorProfile* monitorProfile)
     m_monitorProfile = monitorProfile;
 }
 
-void KisProjectionCache::setDirty(const QRect & rc)
+void KisProjectionCache::updateCache(KisPPUpdateInfoSP info)
 {
-
-
     if (!m_image) return;
-    if (!m_cacheKisImageAsQImage) return;
-
-    if (m_unscaledCache.isNull()) {
-
-        m_unscaledCache = QImage(m_image->width(), m_image->height(), QImage::Format_ARGB32);
-
+    if (m_cacheKisImageAsQImage) {
+        updateCachedQImage(info->dirtyImageRect);
     }
+    else {
+        /**
+         * We can neither draw the image directly from the KisImage
+         * nor cache it in the internal QImage.
+         * The only way out left is to create a patch and store
+         * a dirty piece of image there
+         */
+        info->transfer = KisPPUpdateInfo::DIRECT;
+        info->patch = getNearestPatch(info);
+    }
+}
 
-    QPainter p(&m_unscaledCache);
-    p.setCompositionMode(QPainter::CompositionMode_Source);
-
-    QImage updateImage = m_image->convertToQImage(rc.x(), rc.y(), rc.width(), rc.height(),
-                         m_monitorProfile);
-
-//     if (m_showMask && m_drawMaskVisualisationOnUnscaledCanvasCache) {
-//
-//         // XXX: Also visualize the global selection
-//
-//         KisSelectionSP selection = 0;
-//
-//         if (m_currentNode) {
-//             if (m_currentNode->inherits("KisMask")) {
-//
-//                 selection = dynamic_cast<const KisMask*>(m_currentNode.data())->selection();
-//             } else if (m_currentNode->inherits("KisLayer")) {
-//
-//                 KisLayerSP layer = dynamic_cast<KisLayer*>(m_currentNode.data());
-//                 if (KisSelectionMaskSP selectionMask = layer->selectionMask()) {
-//                     selection = selectionMask->selection();
-//                 }
-//             }
-//         }
-//         selection->paint(&updateImage, rc);
-//     }
-
-
-    p.drawImage(rc.x(), rc.y(), updateImage, 0, 0, rc.width(), rc.height());
-    p.end();
-
+void KisProjectionCache::recalculateCache(KisPPUpdateInfoSP info)
+{
+    Q_UNUSED(info);
+    /* nothing to do, happily */
 }
-#include <QtDebug>
-KisImagePatch KisProjectionCache::getNearestPatch(qreal scaleX, qreal scaleY,
-        const QRect& requestedRect,
-        qint32 borderWidth)
+
+void KisProjectionCache::updateCachedQImage(const QRect &rect)
 {
-    Q_UNUSED(scaleX);
-    Q_UNUSED(scaleY);
-    KisImagePatch patch;
+    if (m_unscaledCache.isNull())
+        m_unscaledCache = QImage(m_image->width(), m_image->height(), QImage::Format_ARGB32);
 
-    patch.m_scaleX = 1.;
-    patch.m_scaleY = 1.;
+    QPainter gc(&m_unscaledCache);
+    gc.setCompositionMode(QPainter::CompositionMode_Source);
 
-    patch.m_interestRect = toFloatRectWorkaround(
-                               QRect(borderWidth, borderWidth,
-                                     requestedRect.width(),
-                                     requestedRect.height())
-                           );
+    QImage updateImage = m_image->convertToQImage(rect,
+                                                  m_monitorProfile);
 
-    QRect adjustedRect = requestedRect.adjusted(-borderWidth, -borderWidth,
-                         borderWidth, borderWidth);
-    patch.m_patchRect = adjustedRect;
+    gc.drawImage(rect.x(), rect.y(),
+                 updateImage, 0, 0,
+                 rect.width(), rect.height());
+    gc.end();
+}
 
-    if (m_cacheKisImageAsQImage) {
-        patch.m_image = m_unscaledCache.copy(patch.m_patchRect);
-    } else {
-        qint32 x, y, w, h;
-        patch.m_patchRect.getRect(&x, &y, &w, &h);
+KisImagePatch KisProjectionCache::getNearestPatch(KisPPUpdateInfoSP info)
+{
+    KisImagePatch patch(info->imageRect, info->borderWidth, 1., 1.);
 
-        patch.m_image = m_image->convertToQImage(x, y, w, h,
-                        m_monitorProfile);
-    }
+    if (m_cacheKisImageAsQImage)
+        patch.setImage(m_unscaledCache.copy(patch.patchRect()));
+    else
+        patch.setImage(m_image->convertToQImage(patch.patchRect(),
+                                                m_monitorProfile));
 
     return patch;
 }
 
-void KisProjectionCache::drawFromOriginalImage(QPainter& gc,
-        const QRect& imageRect,
-        const QRectF& viewportRect,
-        qint32 borderWidth,
-        QPainter::RenderHints renderHints)
+void KisProjectionCache::drawFromOriginalImage(QPainter& gc, KisPPUpdateInfoSP info)
 {
     if (m_cacheKisImageAsQImage) {
         gc.save();
         gc.setCompositionMode(QPainter::CompositionMode_Source);
-        gc.setRenderHints(renderHints, true);
-        gc.drawImage(viewportRect, m_unscaledCache, imageRect);
+        gc.setRenderHints(info->renderHints, true);
+        gc.drawImage(info->viewportRect, m_unscaledCache, info->imageRect);
         gc.restore();
     } else {
-        KisImagePatch patch = getNearestPatch(1, 1, imageRect, borderWidth);
-        patch.drawMe(gc, viewportRect, renderHints);
+        /**
+         * FIXME: In case this function is called from preScale() there
+         * will be no patch prepared. It means we need a direct access
+         * to the KisImage's data. That is not really what we wanted
+         * to achieve.
+         */
+        if(!info->patch.isValid()) {
+            info->patch = getNearestPatch(info);
+        }
+
+        info->patch.drawMe(gc, info->viewportRect, info->renderHints);
     }
 }
diff --git a/krita/ui/canvas/kis_projection_cache.h b/krita/ui/canvas/kis_projection_cache.h
index 19be1df..ef5e908 100644
--- a/krita/ui/canvas/kis_projection_cache.h
+++ b/krita/ui/canvas/kis_projection_cache.h
@@ -20,11 +20,6 @@
 #ifndef KIS_PROJECTION_CACHE
 #define KIS_PROJECTION_CACHE
 
-#include <QImage>
-#include <QSize>
-#include <QRect>
-#include <kis_types.h>
-
 #include "kis_projection_backend.h"
 
 /**
@@ -42,20 +37,16 @@ public:
     void setImage(KisImageWSP image);
     void setImageSize(qint32 w, qint32 h);
     void setMonitorProfile(const KoColorProfile* monitorProfile);
-    void setDirty(const QRect& rc);
-
-    KisImagePatch getNearestPatch(qreal scaleX, qreal scaleY,
-                                  const QRect& requestedRect,
-                                  qint32 borderWidth);
+    void updateCache(KisPPUpdateInfoSP info);
+    void recalculateCache(KisPPUpdateInfoSP info);
 
-    void drawFromOriginalImage(QPainter& gc,
-                               const QRect& imageRect,
-                               const QRectF& viewportRect,
-                               qint32 borderWidth,
-                               QPainter::RenderHints renderHints);
+    KisImagePatch getNearestPatch(KisPPUpdateInfoSP info);
+    void drawFromOriginalImage(QPainter& gc, KisPPUpdateInfoSP info);
 
     void setCacheKisImageAsQImage(bool toggle);
 
+private:
+    void updateCachedQImage(const QRect &rect);
 
 private:
 
diff --git a/krita/ui/canvas/kis_qpainter_canvas.cpp b/krita/ui/canvas/kis_qpainter_canvas.cpp
index 2e3d4dc..20feb0f 100644
--- a/krita/ui/canvas/kis_qpainter_canvas.cpp
+++ b/krita/ui/canvas/kis_qpainter_canvas.cpp
@@ -151,10 +151,10 @@ void KisQPainterCanvas::paintEvent(QPaintEvent * ev)
             gc.fillRect(fillRect, m_d->checkBrush);
         }
     }
-        
+
     if(canvas()->image()->rootLayer()->childCount()>0) {
         gc.setCompositionMode(QPainter::CompositionMode_SourceOver);
-    
+
         gc.save();
         if (canvas()->isCanvasMirrored()){
             QTransform m = gc.transform();
@@ -164,14 +164,15 @@ void KisQPainterCanvas::paintEvent(QPaintEvent * ev)
             m.translate( -documentOrigin().x(), 0);
             gc.setTransform(m);
         }
-            if (cfg.noXRender()) {
-                gc.drawPixmap(ev->rect(), m_d->prescaledProjection->prescaledPixmap(), 
-                            ev->rect().translated(-documentOrigin()));
-            } else {
+//            if (cfg.noXRender()) {
+//                gc.drawPixmap(ev->rect(), m_d->prescaledProjection->prescaledPixmap(), 
+//                            ev->rect().translated(-documentOrigin()));
+//            } else {
                 gc.drawImage(ev->rect(), m_d->prescaledProjection->prescaledQImage(), 
                             ev->rect().translated(-documentOrigin()));
-            }
+//            }
     }
+
 #ifdef DEBUG_REPAINT
     QColor color = QColor(random() % 255, random() % 255, random() % 255, 150);
     gc.fillRect(ev->rect(), color);
@@ -276,6 +277,12 @@ void KisQPainterCanvas::slotConfigChanged()
 {
     KisConfig cfg;
 
+    /**
+     * FIXME: Please pay attention to these options:
+     *   - cfg.checkSize();
+     *   - cfg.checkersColor();
+     */
+
     m_d->checkBrush = QBrush(checkImage(cfg.checkSize()));
 }
 
diff --git a/krita/ui/canvas/kis_update_info.cpp b/krita/ui/canvas/kis_update_info.cpp
new file mode 100644
index 0000000..00d6c3a
--- /dev/null
+++ b/krita/ui/canvas/kis_update_info.cpp
@@ -0,0 +1,40 @@
+#include "kis_update_info.h"
+
+/**
+ * The connection in KisCanvas2 uses queued signals
+ * with an argument of KisNodeSP type, so we should
+ * register it beforehand
+ */
+struct KisUpdateInfoSPStaticRegistrar {
+    KisUpdateInfoSPStaticRegistrar() {
+        qRegisterMetaType<KisUpdateInfoSP>("KisUpdateInfoSP");
+    }
+};
+static KisUpdateInfoSPStaticRegistrar __registrar;
+
+KisUpdateInfo::KisUpdateInfo()
+{
+}
+
+KisUpdateInfo::~KisUpdateInfo()
+{
+}
+
+QRect KisUpdateInfo::dirtyViewportRect()
+{
+    return QRect();
+}
+
+QRect KisPPUpdateInfo::dirtyViewportRect() {
+    return viewportRect.toAlignedRect();
+}
+
+
+#ifdef HAVE_OPENGL
+
+QRect KisOpenGLUpdateInfo::dirtyViewportRect() {
+    qFatal("Not implemented yet!");
+    return viewportRect.toAlignedRect();
+}
+
+#endif /* HAVE_OPENGL */
diff --git a/krita/ui/canvas/kis_update_info.h b/krita/ui/canvas/kis_update_info.h
new file mode 100644
index 0000000..4bb5ae3
--- /dev/null
+++ b/krita/ui/canvas/kis_update_info.h
@@ -0,0 +1,114 @@
+/*
+ *  Copyright (c) 2010, Dmitry Kazakov <dimula73@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef KIS_UPDATE_INFO_H_
+#define KIS_UPDATE_INFO_H_
+
+#include <QPainter>
+#include "kis_shared.h"
+#include "kis_image_patch.h"
+#include <opengl/kis_opengl.h>
+
+
+class KisUpdateInfo;
+class KisPPUpdateInfo;
+typedef KisSharedPtr<KisUpdateInfo> KisUpdateInfoSP;
+typedef KisSharedPtr<KisPPUpdateInfo> KisPPUpdateInfoSP;
+
+
+class KisUpdateInfo : public KisShared
+{
+public:
+    KisUpdateInfo();
+    virtual ~KisUpdateInfo();
+
+    virtual QRect dirtyViewportRect();
+};
+
+Q_DECLARE_METATYPE(KisUpdateInfoSP);
+
+#ifdef HAVE_OPENGL
+#include "opengl/kis_texture_tile_update_info.h"
+
+class KisOpenGLUpdateInfo;
+typedef KisSharedPtr<KisOpenGLUpdateInfo> KisOpenGLUpdateInfoSP;
+
+class KisOpenGLUpdateInfo : public KisUpdateInfo
+{
+public:
+    KisTextureTileUpdateInfoList tileList;
+    QRect dirtyViewportRect();
+
+    QRectF viewportRect;
+};
+#endif /* HAVE_OPENGL */
+
+class KisPPUpdateInfo : public KisUpdateInfo
+{
+public:
+    enum TransferType {
+        DIRECT,
+        PATCH
+    };
+
+    QRect dirtyViewportRect();
+
+    /**
+     * The rect that was reported by KisImage as dirty
+     */
+    QRect dirtyImageRect;
+
+    /**
+     * Rect of KisImage corresponding to @viewportRect.
+     * It is cropped and aligned corresponding to the canvas.
+     */
+    QRect imageRect;
+
+    /**
+     * Rect of canvas widget corresponding to @imageRect
+     */
+    QRectF viewportRect;
+
+    qreal scaleX;
+    qreal scaleY;
+
+    /**
+     * Defines the way the source image is painted onto
+     * prescaled QImage
+     */
+    TransferType transfer;
+
+    /**
+     * Render hints for painting the direct painting/patch painting
+     */
+    QPainter::RenderHints renderHints;
+
+    /**
+     * The number of additional pixels those should be added
+     * to the patch
+     */
+    qint32 borderWidth;
+
+    /**
+     * Used for temporary sorage of KisImage's data
+     * by KisProjectionCache
+     */
+    KisImagePatch patch;
+};
+
+#endif /* KIS_UPDATE_INFO_H_ */
diff --git a/krita/ui/kis_canvas_resource_provider.cpp b/krita/ui/kis_canvas_resource_provider.cpp
index e2fe768..39cab7c 100644
--- a/krita/ui/kis_canvas_resource_provider.cpp
+++ b/krita/ui/kis_canvas_resource_provider.cpp
@@ -104,7 +104,7 @@ void KisCanvasResourceProvider::setHDRExposure(float exposure)
     m_view->image()->projection()->colorSpace()->profile()->setProperty("exposure", exposure);
     m_view->image()->rootLayer()->accept(eV);
     m_view->canvasBase()->updateCanvas();
-    m_view->canvasBase()->updateCanvasProjection(m_view->image()->bounds());
+    m_view->canvasBase()->startUpdateCanvasProjection(m_view->image()->bounds());
 }
 
 
diff --git a/krita/ui/opengl/kis_opengl_image_textures.cpp b/krita/ui/opengl/kis_opengl_image_textures.cpp
index 6f21ebd..3da3710 100644
--- a/krita/ui/opengl/kis_opengl_image_textures.cpp
+++ b/krita/ui/opengl/kis_opengl_image_textures.cpp
@@ -98,10 +98,8 @@ KisOpenGLImageTextures::KisOpenGLImageTextures(KisImageWSP image, KoColorProfile
 
     createImageTextureTiles();
 
-    connect(m_image, SIGNAL(sigImageUpdated(QRect)), SLOT(slotImageUpdated(QRect)));
-    connect(m_image, SIGNAL(sigSizeChanged(qint32, qint32)), SLOT(slotImageSizeChanged(qint32, qint32)));
-
-    updateImageTextureTiles(m_image->bounds());
+    KisOpenGLUpdateInfoSP info = updateCache(m_image->bounds());
+    recalculateCache(info);
 }
 
 KisOpenGLImageTextures::~KisOpenGLImageTextures()
@@ -205,107 +203,93 @@ void KisOpenGLImageTextures::destroyImageTextureTiles()
     }
 }
 
-void KisOpenGLImageTextures::updateImageTextureTiles(const QRect& rect)
+KisOpenGLUpdateInfoSP
+KisOpenGLImageTextures::updateCache(const QRect& rect)
 {
-    //dbgUI << "updateImageTextureTiles" << rect;
+    KisOpenGLUpdateInfoSP info = new KisOpenGLUpdateInfo();
 
     QRect updateRect = rect & m_image->bounds();
+    if (updateRect.isEmpty()) return info;
 
-    if (!updateRect.isEmpty()) {
-
-        KisOpenGL::makeContextCurrent();
+    int firstColumn = updateRect.left() / m_imageTextureTileWidth;
+    int lastColumn = updateRect.right() / m_imageTextureTileWidth;
+    int firstRow = updateRect.top() / m_imageTextureTileHeight;
+    int lastRow = updateRect.bottom() / m_imageTextureTileHeight;
 
-        KIS_OPENGL_CLEAR_ERROR();
+    qint32 numItems = (lastColumn - firstColumn + 1) * (lastRow - firstRow + 1);
+    info->tileList.reserve(numItems);
 
-        int firstColumn = updateRect.left() / m_imageTextureTileWidth;
-        int lastColumn = updateRect.right() / m_imageTextureTileWidth;
-        int firstRow = updateRect.top() / m_imageTextureTileHeight;
-        int lastRow = updateRect.bottom() / m_imageTextureTileHeight;
+    for (int col = firstColumn; col <= lastColumn; col++) {
+        for (int row = firstRow; row <= lastRow; row++) {
 
-        for (int column = firstColumn; column <= lastColumn; column++) {
-            for (int row = firstRow; row <= lastRow; row++) {
+            QRect tileRect(col * m_imageTextureTileWidth, row * m_imageTextureTileHeight,
+                           m_imageTextureTileWidth, m_imageTextureTileHeight);
 
-                QRect tileRect(column * m_imageTextureTileWidth, row * m_imageTextureTileHeight,
-                               m_imageTextureTileWidth, m_imageTextureTileHeight);
+            KisTextureTileUpdateInfo tile(tileRect, updateRect);
+            tile.retrieveData(m_image);
+            info->tileList.append(tile);
+        }
+    }
+    return info;
+}
 
-                QRect tileUpdateRect = tileRect & updateRect;
+void KisOpenGLImageTextures::recalculateCache(KisUpdateInfoSP info)
+{
+    KisOpenGLUpdateInfoSP glInfo = dynamic_cast<KisOpenGLUpdateInfo*>(info.data());
+    if(!glInfo) return;
 
-                glBindTexture(GL_TEXTURE_2D, imageTextureTile(tileRect.x(), tileRect.y()));
-                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
-                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//GL_LINEAR);
-                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
-                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+    KisOpenGL::makeContextCurrent();
 
-                glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+    KIS_OPENGL_CLEAR_ERROR();
 
-                QImage tileUpdateImage;
-                quint8 *pixels;
-                bool deletePixelsAfterUse = false;
+    KisTextureTileUpdateInfo tile;
 
-                if (m_imageTextureInternalFormat == GL_RGBA8) {
-                    tileUpdateImage = m_image->convertToQImage(tileUpdateRect.x(), tileUpdateRect.y(),
-                                                               tileUpdateRect.width(), tileUpdateRect.height(),
-                                                               m_monitorProfile);
+    foreach(tile, glInfo->tileList) {
+        glBindTexture(GL_TEXTURE_2D, imageTextureTile(tile.tileRect().x(), tile.tileRect().y()));
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//GL_LINEAR);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
 
-                    pixels = tileUpdateImage.bits();
-                }
-                else {
-                    pixels = new quint8[tileUpdateRect.width() * tileUpdateRect.height() * m_image->colorSpace()->pixelSize()];
-                    Q_CHECK_PTR(pixels);
-                    deletePixelsAfterUse = true;
+        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
 
-                    m_image->projection()->readBytes(pixels, tileUpdateRect.x(), tileUpdateRect.y(),
-                                                      tileUpdateRect.width(), tileUpdateRect.height());
+        const KoColorSpace *dstCS;
 
+        switch(m_imageTextureType) {
+        case GL_UNSIGNED_BYTE:
+            dstCS = KoColorSpaceRegistry::instance()->rgb8(m_monitorProfile);
+            break;
 #if defined(HAVE_GLEW) && defined(HAVE_OPENEXR)
-
-                    if (m_imageTextureInternalFormat == GL_RGBA16) {
-                        // Convert to 16 bit rgba from the current colorspace (which is integer 16, float 16 or float 32, but not rgba)
-                        const KoColorSpace* rgb16 = KoColorSpaceRegistry::instance()->rgb16(m_monitorProfile);
-                        quint8* rgb16pixels = rgb16->allocPixelBuffer(tileUpdateRect.width() * tileUpdateRect.height());
-                        m_image->colorSpace()->convertPixelsTo(pixels, rgb16pixels, rgb16, tileUpdateRect.width() * tileUpdateRect.height());
-                        delete[] pixels;
-                        pixels = rgb16pixels;
-                    }
-                    else if (m_image->colorSpace()->colorDepthId() == Float16BitsColorDepthID && m_imageTextureType == GL_FLOAT) {
-                        // Convert half to float as we don't have ARB_half_float_pixel
-                        const int NUM_RGBA_COMPONENTS = 4;
-                        qint32 halfCount = tileUpdateRect.width() * tileUpdateRect.height() * NUM_RGBA_COMPONENTS;
-                        GLfloat *pixels_as_floats = new GLfloat[halfCount];
-                        const half *half_pixel = reinterpret_cast<const half *>(pixels);
-                        GLfloat *float_pixel = pixels_as_floats;
-
-                        while (halfCount > 0) {
-                            *float_pixel = *half_pixel;
-                            ++float_pixel;
-                            ++half_pixel;
-                            --halfCount;
-                        }
-                        delete [] pixels;
-                        pixels = reinterpret_cast<quint8 *>(pixels_as_floats);
-                    }
+        case GL_UNSIGNED_SHORT:
+            dstCS = KoColorSpaceRegistry::instance()->rgb16(m_monitorProfile);
+            break;
+        case GL_HALF_FLOAT_ARB:
+            dstCS = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F16", m_monitorProfile);
+            break;
+        case GL_FLOAT:
+            dstCS = KoColorSpaceRegistry::instance()->colorSpace("RGBA", "F32", m_monitorProfile);
+            break;
 #endif
-                }
-
-                if (tileUpdateRect.width() == m_imageTextureTileWidth && tileUpdateRect.height() == m_imageTextureTileHeight) {
+        default:
+            qFatal("Unknown m_imageTextureType");
+        }
 
-                    glTexImage2D(GL_TEXTURE_2D, 0, m_imageTextureInternalFormat, m_imageTextureTileWidth, m_imageTextureTileHeight, 0,
-                                 GL_BGRA, m_imageTextureType, pixels);
-                } else {
-                    int xOffset = tileUpdateRect.x() - tileRect.x();
-                    int yOffset = tileUpdateRect.y() - tileRect.y();
+        tile.convertTo(dstCS);
 
-                    glTexSubImage2D(GL_TEXTURE_2D, 0, xOffset, yOffset, tileUpdateRect.width(), tileUpdateRect.height(),
-                                    GL_BGRA, m_imageTextureType, pixels);
-                }
+        if (tile.isEntireTileUpdated()) {
+            glTexImage2D(GL_TEXTURE_2D, 0, m_imageTextureInternalFormat, m_imageTextureTileWidth, m_imageTextureTileHeight, 0,
+                         GL_BGRA, m_imageTextureType, tile.data());
+        } else {
+            QPoint patchOffset = tile.patchOffset();
+            QSize patchSize = tile.patchSize();
+            glTexSubImage2D(GL_TEXTURE_2D, 0, patchOffset.x(), patchOffset.y(),
+                            patchSize.width(), patchSize.height(),
+                            GL_BGRA, m_imageTextureType, tile.data());
+        }
 
-                if (deletePixelsAfterUse) {
-                    delete [] pixels;
-                }
+        tile.destroy();
 
-                KIS_OPENGL_PRINT_ERROR();
-            }
-        }
+        KIS_OPENGL_PRINT_ERROR();
     }
 }
 
@@ -363,7 +347,8 @@ int KisOpenGLImageTextures::imageTextureTileHeight() const
 
 void KisOpenGLImageTextures::update(const QRect& imageRect)
 {
-    updateImageTextureTiles(imageRect);
+    KisOpenGLUpdateInfoSP info = updateCache(imageRect);
+    recalculateCache(info);
 }
 
 void KisOpenGLImageTextures::setSelectionDisplayEnabled(bool enable)
@@ -375,14 +360,17 @@ void KisOpenGLImageTextures::slotImageUpdated(const QRect &rc)
 {
     QRect r = rc & m_image->bounds();
 
-    updateImageTextureTiles(r);
+    KisOpenGLUpdateInfoSP info = updateCache(r);
+    recalculateCache(info);
+
     emit sigImageUpdated(r);
 }
 
 void KisOpenGLImageTextures::slotImageSizeChanged(qint32 w, qint32 h)
 {
     createImageTextureTiles();
-    updateImageTextureTiles(m_image->bounds());
+    KisOpenGLUpdateInfoSP info = updateCache(m_image->bounds());
+    recalculateCache(info);
 
     emit sigSizeChanged(w, h);
 }
@@ -391,7 +379,8 @@ void KisOpenGLImageTextures::setMonitorProfile(KoColorProfile *monitorProfile)
 {
     if (monitorProfile != m_monitorProfile) {
         m_monitorProfile = monitorProfile;
-        updateImageTextureTiles(m_image->bounds());
+        KisOpenGLUpdateInfoSP info = updateCache(m_image->bounds());
+        recalculateCache(info);
     }
 }
 
@@ -407,7 +396,8 @@ void KisOpenGLImageTextures::setHDRExposure(float exposure)
             } else {
 #endif
                 QApplication::setOverrideCursor(Qt::WaitCursor);
-                updateImageTextureTiles(m_image->bounds());
+                KisOpenGLUpdateInfoSP info = updateCache(m_image->bounds());
+                recalculateCache(info);
                 QApplication::restoreOverrideCursor();
 #ifdef HAVE_GLEW
             }
@@ -553,7 +543,6 @@ bool KisOpenGLImageTextures::imageCanUseHDRExposureProgram(KisImageWSP image)
               && (GLEW_ARB_texture_float || GLEW_ATI_texture_float))) {
         return false;
     }
-
     return true;
 #else
     Q_UNUSED(image);
diff --git a/krita/ui/opengl/kis_opengl_image_textures.h b/krita/ui/opengl/kis_opengl_image_textures.h
index ca8ad8b..4289fbc 100644
--- a/krita/ui/opengl/kis_opengl_image_textures.h
+++ b/krita/ui/opengl/kis_opengl_image_textures.h
@@ -19,6 +19,8 @@
 #define KIS_OPENGL_IMAGE_TEXTURES_H_
 
 #include <opengl/kis_opengl.h>
+#include "canvas/kis_update_info.h"
+#include "opengl/kis_texture_tile_update_info.h"
 
 #ifdef HAVE_OPENGL
 
@@ -168,7 +170,6 @@ protected:
     void createImageTextureTiles();
     void destroyImageTextureTiles();
     int imageTextureTileIndex(int x, int y) const;
-    void updateImageTextureTiles(const QRect& rect);
 
     void setImageTextureFormat();
 
@@ -176,8 +177,11 @@ protected:
     static bool imageCanUseHDRExposureProgram(KisImageWSP image);
     static bool imageCanShareTextures(KisImageWSP image);
 
-protected slots:
-    void slotImageUpdated(const QRect &);
+public slots:
+    KisOpenGLUpdateInfoSP updateCache(const QRect& rect);
+    void recalculateCache(KisUpdateInfoSP info);
+
+    void slotImageUpdated(const QRect &rc);
     void slotImageSizeChanged(qint32 w, qint32 h);
 
 private:
diff --git a/krita/ui/opengl/kis_texture_tile_update_info.h b/krita/ui/opengl/kis_texture_tile_update_info.h
new file mode 100644
index 0000000..26f702d
--- /dev/null
+++ b/krita/ui/opengl/kis_texture_tile_update_info.h
@@ -0,0 +1,106 @@
+/*
+ *  Copyright (c) 2010, Dmitry Kazakov <dimula73@gmail.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef KIS_TEXTURE_TILE_UPDATE_INFO_H_
+#define KIS_TEXTURE_TILE_UPDATE_INFO_H_
+
+#ifdef HAVE_OPENGL
+
+#include <KoColorSpace.h>
+#include <KoColorSpaceRegistry.h>
+#include "kis_image.h"
+#include "kis_paint_device.h"
+
+class KisTextureTileUpdateInfo;
+typedef QVector<KisTextureTileUpdateInfo> KisTextureTileUpdateInfoList;
+
+class KisTextureTileUpdateInfo
+{
+public:
+    KisTextureTileUpdateInfo() {
+        m_patchPixels = 0;
+    }
+
+    KisTextureTileUpdateInfo(QRect tileRect, QRect updateRect) {
+        m_tileRect = tileRect;
+        m_patchRect = m_tileRect & updateRect;
+        m_patchPixels = 0;
+    }
+
+    ~KisTextureTileUpdateInfo() {
+    }
+
+    void destroy() {
+        delete[] m_patchPixels;
+    }
+
+    void retrieveData(KisImageWSP image) {
+        m_patchColorSpace = image->projection()->colorSpace();
+
+        m_patchPixels = m_patchColorSpace->allocPixelBuffer(m_patchRect.width() * m_patchRect.height()*2);
+        image->projection()->readBytes(m_patchPixels,
+                                       m_patchRect.x(), m_patchRect.y(),
+                                       m_patchRect.width(), m_patchRect.height());
+    }
+
+    void convertTo(const KoColorSpace* dstCS) {
+        const qint32 numPixels = m_patchRect.width() * m_patchRect.height();
+        /**
+         * FIXME: is it possible to do an in-place conversion?
+         */
+        quint8* dstBuffer = dstCS->allocPixelBuffer(numPixels);
+        // FIXME: rendering intent
+        Q_ASSERT(dstBuffer && m_patchPixels);
+        m_patchColorSpace->convertPixelsTo(m_patchPixels, dstBuffer, dstCS, numPixels);
+        delete[] m_patchPixels;
+        m_patchColorSpace = dstCS;
+        m_patchPixels = dstBuffer;
+    }
+
+    inline quint8* data() {
+        return m_patchPixels;
+    }
+
+    inline bool isEntireTileUpdated() {
+        return m_patchRect == m_tileRect;
+    }
+
+    inline QPoint patchOffset() {
+        return QPoint(m_patchRect.x() - m_tileRect.x(),
+                      m_patchRect.y() - m_tileRect.y());
+    }
+
+    inline QSize patchSize() {
+        return m_patchRect.size();
+    }
+
+    inline QRect tileRect() {
+        return m_tileRect;
+    }
+
+private:
+    QRect m_tileRect;
+    QRect m_patchRect;
+    const KoColorSpace* m_patchColorSpace;
+    quint8 *m_patchPixels;
+};
+
+
+#endif /* HAVE_OPENGL */
+
+#endif /* KIS_TEXTURE_TILE_UPDATE_INFO_H_ */
+
diff --git a/krita/ui/tests/kis_prescaled_projection_test.cpp b/krita/ui/tests/kis_prescaled_projection_test.cpp
index 584a1ff..346909a 100644
--- a/krita/ui/tests/kis_prescaled_projection_test.cpp
+++ b/krita/ui/tests/kis_prescaled_projection_test.cpp
@@ -149,8 +149,6 @@ void KisPrescaledProjectionTest::testCreation()
     KisPrescaledProjection * prescaledProjection = 0;
     prescaledProjection = new KisPrescaledProjection();
     QVERIFY(prescaledProjection != 0);
-    QVERIFY(prescaledProjection->drawCheckers() == false);
-    QVERIFY(prescaledProjection->prescaledPixmap().isNull());
     QVERIFY(prescaledProjection->prescaledQImage().isNull());
     delete prescaledProjection;
 }
@@ -171,17 +169,17 @@ void KisPrescaledProjectionTest::testCoordinateConversionRoundTrip()
     projection.setImage(image);
     projection.resizePrescaledImage(QSize(100, 100));
 
-    QRect viewRect = toAlignedRectWorkaround(projection.viewRectFromImagePixels(QRect(0, 0, 100, 100)));
+    QRect viewRect = projection.viewRectFromImagePixels(QRect(0, 0, 100, 100)).toAlignedRect();
 
     QCOMPARE(viewRect, QRect(0, 0, 40, 40));
 
-    QRect viewRect2 = toAlignedRectWorkaround(projection.viewRectFromImagePixels(QRect(0, 0, 200, 200)));
+    QRect viewRect2 = projection.viewRectFromImagePixels(QRect(0, 0, 200, 200)).toAlignedRect();
     QCOMPARE(viewRect2, QRect(0, 0, 80, 80));
 
     QRect imageRect = projection.imageRectFromViewPortPixels(viewRect);
     QCOMPARE(imageRect, QRect(0, 0, 100, 100));
 
-    QRect viewRect3 = toAlignedRectWorkaround(projection.viewRectFromImagePixels(imageRect));
+    QRect viewRect3 = projection.viewRectFromImagePixels(imageRect).toAlignedRect();
     QCOMPARE(viewRect3, viewRect);
 }
 
