Index: krita/plugins/extensions/scripting/scripts/decorations/VanishingPoints.rb
===================================================================
--- krita/plugins/extensions/scripting/scripts/decorations/VanishingPoints.rb	(revision 1163293)
+++ krita/plugins/extensions/scripting/scripts/decorations/VanishingPoints.rb	(working copy)
@@ -14,7 +14,7 @@
 #  along with this program; if not, write to the Free Software
 #  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 
-def drawDecoration( gc, documentOffset, area, converter)
+def drawDecoration( gc, updateRect, converter)
     x_c = 800
     y_c = 600
     painter = Qt::Internal.kross2smoke( gc, Qt::Painter )
Index: krita/plugins/extensions/scripting/kis_script_decoration.cc
===================================================================
--- krita/plugins/extensions/scripting/kis_script_decoration.cc	(revision 1163293)
+++ krita/plugins/extensions/scripting/kis_script_decoration.cc	(working copy)
@@ -39,8 +39,14 @@
     delete d;
 }
 
-void KisScriptDecoration::drawDecoration(QPainter& gc, const QPoint & documentOffset, const QRect& area, const KoViewConverter &converter)
+void KisScriptDecoration::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter)
 {
     dbgScript << "Call to drawDecoration";
-    d->action->callFunction("drawDecoration", QVariantList() << qVariantFromValue((void*)&gc) << documentOffset << area << qVariantFromValue((void*)&converter));
+
+    QVariantList list;
+    list << qVariantFromValue((void*)&gc)
+         << updateRect
+         << qVariantFromValue((void*)converter);
+
+    d->action->callFunction("drawDecoration", list);
 }
Index: krita/plugins/extensions/scripting/kis_script_decoration.h
===================================================================
--- krita/plugins/extensions/scripting/kis_script_decoration.h	(revision 1163293)
+++ krita/plugins/extensions/scripting/kis_script_decoration.h	(working copy)
@@ -37,7 +37,7 @@
     KisScriptDecoration(Kross::Action* _action, KisView2* _view);
     virtual ~KisScriptDecoration();
 protected:
-    virtual void drawDecoration(QPainter& gc, const QPoint & documentOffset, const QRect& area, const KoViewConverter &converter);
+    void drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter);
 private:
     struct Private;
     Private* const d;
Index: krita/plugins/tools/defaulttools/kis_tool_gradient.cc
===================================================================
--- krita/plugins/tools/defaulttools/kis_tool_gradient.cc	(revision 1163293)
+++ krita/plugins/tools/defaulttools/kis_tool_gradient.cc	(working copy)
@@ -119,7 +119,7 @@
 
                 if (canvasWidget) {
                     beginOpenGL();
-                    canvasWidget->setPixelToViewTransformation();
+                    canvasWidget->setupImageToWidgetTransformation();
 
                     glMatrixMode(GL_MODELVIEW);
                     glTranslatef(0.5, 0.5, 0.0);
Index: krita/plugins/tools/defaulttools/kis_tool_pan.cpp
===================================================================
--- krita/plugins/tools/defaulttools/kis_tool_pan.cpp	(revision 0)
+++ krita/plugins/tools/defaulttools/kis_tool_pan.cpp	(revision 0)
@@ -0,0 +1,202 @@
+/*
+ *  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_tool_pan.h"
+
+#include <QScrollBar>
+
+#include <KoPointerEvent.h>
+#include <KoCanvasBase.h>
+#include <KoCanvasController.h>
+#include <KoCanvasControllerWidget.h>
+
+#include <kis_canvas2.h>
+
+#include "kis_cursor.h"
+#include "kis_coordinates_converter.h"
+
+
+const qreal KisToolPan::m_checkerRadius = 50;
+
+KisToolPan::KisToolPan(KoCanvasBase * canvas)
+        :  KisTool(canvas, KisCursor::openHandCursor())
+{
+    setObjectName("tool_pan");
+    m_rotationMode = false;
+    m_defaultCursor = KisCursor::openHandCursor();
+}
+
+KisToolPan::~KisToolPan()
+{
+}
+
+bool KisToolPan::wantsAutoScroll() const
+{
+    return false;
+}
+
+void KisToolPan::paint(QPainter& gc, const KoViewConverter &converter)
+{
+    Q_UNUSED(converter);
+    if(m_rotationMode) {
+        const KisCoordinatesConverter *converter = kritaCanvas()->coordinatesConverter();
+        QPointF centerPoint = converter->flakeCenterPoint();
+
+        QBrush fillBrush(QColor(0,0,0,100));
+        QPen checkerPen(QColor(255,255,255,100), 5., Qt::SolidLine, Qt::RoundCap);
+
+        gc.save();
+
+        gc.setPen(Qt::NoPen);
+        gc.setBrush(fillBrush);
+        gc.drawEllipse(centerPoint, m_checkerRadius, m_checkerRadius);
+
+        gc.setPen(checkerPen);
+        gc.drawLine(centerPoint, centerPoint + QPointF(0, -m_checkerRadius));
+
+        gc.restore();
+    }
+}
+
+KisCanvas2* KisToolPan::kritaCanvas() const
+{
+    KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
+    Q_ASSERT(kritaCanvas);
+    return kritaCanvas;
+}
+
+bool KisToolPan::isInCheckerArea(QPointF pt)
+{
+    QPointF centerPoint = widgetCenterInWidgetPixels();
+    pt -= centerPoint;
+
+    return pt.x() * pt.x() + pt.y() * pt.y() <= m_checkerRadius * m_checkerRadius;
+}
+
+qreal KisToolPan::calculateAngle(QPointF oldPoint,
+                                 QPointF newPoint)
+{
+    QPointF centerPoint = widgetCenterInWidgetPixels();
+    oldPoint -= centerPoint;
+    newPoint -= centerPoint;
+
+    qreal oldAngle = atan2(oldPoint.y(), oldPoint.x());
+    qreal newAngle = atan2(newPoint.y(), newPoint.x());
+
+    return (180 / 3.14) * (newAngle - oldAngle);
+}
+
+void KisToolPan::mousePressEvent(KoPointerEvent *e)
+{
+    m_lastPosition = convertDocumentToWidget(e->point);
+    e->accept();
+
+    m_defaultCursor = KisCursor::closedHandCursor();
+    adjustCursor();
+
+    if(isInCheckerArea(m_lastPosition)) {
+        kritaCanvas()->resetCanvasTransformations();
+    }
+}
+
+void KisToolPan::adjustCursor()
+{
+    QPoint pt = canvas()->canvasWidget()->mapFromGlobal(QCursor::pos());
+
+    if(m_rotationMode && isInCheckerArea(pt)) {
+        useCursor(KisCursor::pointingHandCursor());
+    }
+    else {
+        useCursor(m_defaultCursor);
+    }
+}
+
+void KisToolPan::mouseMoveEvent(KoPointerEvent *e)
+{
+    Q_ASSERT(canvas());
+    Q_ASSERT(canvas()->canvasController());
+
+    QPointF actualPosition = convertDocumentToWidget(e->point);
+
+    adjustCursor();
+
+    if (!e->buttons())
+        return;
+    e->accept();
+
+    if(e->modifiers() & Qt::ShiftModifier) {
+        if(!isInCheckerArea(actualPosition)) {
+            qreal angle = calculateAngle(m_lastPosition, actualPosition);
+            kritaCanvas()->rotateCanvas(angle);
+        }
+    }
+    else {
+        QPointF distance(m_lastPosition - actualPosition);
+        canvas()->canvasController()->pan(distance.toPoint());
+    }
+
+    m_lastPosition = actualPosition;
+}
+
+void KisToolPan::mouseReleaseEvent(KoPointerEvent *e)
+{
+    Q_UNUSED(e);
+    m_defaultCursor = KisCursor::openHandCursor();
+    adjustCursor();
+}
+
+void KisToolPan::keyPressEvent(QKeyEvent *event)
+{
+    KoCanvasControllerWidget *canvasControllerWidget = dynamic_cast<KoCanvasControllerWidget*>(canvas()->canvasController());
+    if (!canvasControllerWidget) {
+        return;
+    }
+    switch (event->key()) {
+        case Qt::Key_Up:
+            canvasControllerWidget->pan(QPoint(0, -canvasControllerWidget->verticalScrollBar()->singleStep()));
+            break;
+        case Qt::Key_Down:
+            canvasControllerWidget->pan(QPoint(0, canvasControllerWidget->verticalScrollBar()->singleStep()));
+            break;
+        case Qt::Key_Left:
+            canvasControllerWidget->pan(QPoint(-canvasControllerWidget->horizontalScrollBar()->singleStep(), 0));
+            break;
+        case Qt::Key_Right:
+            canvasControllerWidget->pan(QPoint(canvasControllerWidget->horizontalScrollBar()->singleStep(), 0));
+            break;
+        case Qt::Key_Shift:
+            m_rotationMode = true;
+            kritaCanvas()->updateCanvas();
+            adjustCursor();
+            break;
+    }
+    event->accept();
+}
+
+void KisToolPan::keyReleaseEvent(QKeyEvent *event)
+{
+    switch (event->key()) {
+        case Qt::Key_Shift:
+            m_rotationMode = false;
+            kritaCanvas()->updateCanvas();
+            adjustCursor();
+            break;
+    }
+}
+
+#include "kis_tool_pan.moc"
Index: krita/plugins/tools/defaulttools/CMakeLists.txt
===================================================================
--- krita/plugins/tools/defaulttools/CMakeLists.txt	(revision 1163293)
+++ krita/plugins/tools/defaulttools/CMakeLists.txt	(working copy)
@@ -10,6 +10,7 @@
     kis_tool_measure.cc
     kis_tool_path.cc
     kis_tool_move.cc
+    kis_tool_pan.cpp
     )
 
 kde4_add_ui_files(kritadefaulttools_PART_SRCS wdgcolorpicker.ui wdgmovetool.ui)
@@ -45,8 +46,6 @@
     tool_freehand_cursor.png
     tool_fill_cursor.png
     tool_gradient_cursor.png
-    openhand_cursor.xpm
-    closedhand_cursor.xpm
     DESTINATION ${DATA_INSTALL_DIR}/krita/pics)
 
 install( FILES  kritadefaulttools.desktop  DESTINATION ${SERVICES_INSTALL_DIR})
Index: krita/plugins/tools/defaulttools/default_tools.cc
===================================================================
--- krita/plugins/tools/defaulttools/default_tools.cc	(revision 1163293)
+++ krita/plugins/tools/defaulttools/default_tools.cc	(working copy)
@@ -39,6 +39,7 @@
 #include "kis_tool_measure.h"
 #include "kis_tool_path.h"
 #include "kis_tool_move.h"
+#include "kis_tool_pan.h"
 
 K_PLUGIN_FACTORY(DefaultToolsFactory, registerPlugin<DefaultTools>();)
 K_EXPORT_PLUGIN(DefaultToolsFactory("krita"))
@@ -59,6 +60,7 @@
     r->add(new KisToolMeasureFactory(r, QStringList()));
     r->add(new KisToolPathFactory(r, QStringList()));
     r->add(new KisToolMoveFactory(r, QStringList()));
+    r->add(new KisToolPanFactory(r, QStringList()));
 }
 
 DefaultTools::~DefaultTools()
Index: krita/plugins/tools/defaulttools/kis_tool_pan.h
===================================================================
--- krita/plugins/tools/defaulttools/kis_tool_pan.h	(revision 0)
+++ krita/plugins/tools/defaulttools/kis_tool_pan.h	(revision 0)
@@ -0,0 +1,91 @@
+/*
+ *  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_TOOL_PAN_H_
+#define KIS_TOOL_PAN_H_
+
+#include <KoPanTool.h>
+#include <KoToolFactoryBase.h>
+#include <kis_tool.h>
+
+class KoCanvasBase;
+class KisCanvas2;
+
+class KisToolPan : public KisTool
+{
+
+    Q_OBJECT
+
+public:
+    KisToolPan(KoCanvasBase * canvas);
+    virtual ~KisToolPan();
+
+
+public:
+
+    bool wantsAutoScroll() const;
+    virtual void mousePressEvent(KoPointerEvent *event);
+    virtual void mouseMoveEvent(KoPointerEvent *event);
+    virtual void mouseReleaseEvent(KoPointerEvent *event);
+    void keyPressEvent(QKeyEvent *event);
+    void keyReleaseEvent(QKeyEvent *event);
+
+    virtual void paint(QPainter& gc, const KoViewConverter &converter);
+
+//    virtual QWidget* createOptionWidget();
+//    virtual QWidget* optionWidget();
+
+private:
+    void adjustCursor();
+    bool isInCheckerArea(QPointF pt);
+    qreal calculateAngle(QPointF oldPoint, QPointF newPoint);
+    KisCanvas2* kritaCanvas() const;
+
+private:
+    static const qreal m_checkerRadius;
+
+    QCursor m_defaultCursor;
+    QPointF m_lastPosition;
+    bool m_rotationMode;
+};
+
+
+class KisToolPanFactory : public KoToolFactoryBase
+{
+
+public:
+    KisToolPanFactory(QObject *parent, const QStringList&)
+            : KoToolFactoryBase(parent, KoPanTool_ID) {
+        setToolTip(i18n("Move and rotate your canvas"));
+        setToolType(navigationToolType());
+        setActivationShapeId(KRITA_TOOL_ACTIVATION_ID_ALWAYS_ACTIVE);
+        setPriority(5);
+        setIcon("krita_tool_pan");
+        //setShortcut( QKeySequence( Qt::SHIFT + Qt::Key_V ) );
+    }
+
+    virtual ~KisToolPanFactory() {}
+
+    virtual KoToolBase * createTool(KoCanvasBase *canvas) {
+        return new KisToolPan(canvas);
+    }
+
+};
+
+#endif // KIS_TOOL_PAN_H_
+
Index: krita/plugins/assistants/RulerAssistant/RulerAssistant.cc
===================================================================
--- krita/plugins/assistants/RulerAssistant/RulerAssistant.cc	(revision 1163293)
+++ krita/plugins/assistants/RulerAssistant/RulerAssistant.cc	(working copy)
@@ -22,8 +22,9 @@
 
 #include <QPainter>
 #include <QLinearGradient>
+#include <QTransform>
 
-#include <KoViewConverter.h>
+#include "kis_coordinates_converter.h"
 
 #include <math.h>
 
@@ -67,29 +68,39 @@
     return sqrt(p.x() * p.x() + p.y() * p.y());
 }
 
-void RulerAssistant::drawAssistant(QPainter& _painter, const QPoint& documentOffset,  const QRect& _area, const KoViewConverter &_converter) const
+void RulerAssistant::drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter)
 {
-    Q_UNUSED(documentOffset);
-    Q_UNUSED(_area);
+    Q_UNUSED(updateRect);
     Q_ASSERT(handles().size() == 2);
+
+    QTransform initialTransform = converter->documentToWidgetTransform();
+
     // Draw the gradient
     QPointF p1 = *handles()[0];
     QPointF p2 = *handles()[1];
-    _painter.save();
+    gc.save();
     {
-        _painter.translate(_converter.documentToView(p1));
-        _painter.rotate(angle(p1, p2) / M_PI * 180);
+        QTransform gradientTransform = initialTransform;
+
+        gradientTransform.translate(p1.x(), p1.y());
+        gradientTransform.rotate(angle(p1, p2) / M_PI * 180);
+        gc.setTransform(gradientTransform);
+
         QLinearGradient gradient(0, -30, 0, 30);
         gradient.setColorAt(0, QColor(0, 0, 0, 0));
         gradient.setColorAt(0.5, QColor(0, 0, 0, 100));
         gradient.setColorAt(1, QColor(0, 0, 0, 0));
-        _painter.setBrush(gradient);
-        _painter.setPen(QPen(Qt::NoPen));
-        _painter.drawRect(_converter.documentToView(QRectF(0, -50, norm2(p2 - p1), 100)));
+        gc.setBrush(gradient);
+        gc.setPen(QPen(Qt::NoPen));
+        gc.drawRect(QRectF(0, -50, norm2(p2 - p1), 100));
     }
-    _painter.restore();
-    _painter.drawLine(_converter.documentToView(p1),
-                      _converter.documentToView(p2));
+    gc.restore();
+
+    gc.save();
+    gc.setTransform(initialTransform);
+    gc.setPen(QColor(0, 0, 0, 125));
+    gc.drawLine(p1,p2);
+    gc.restore();
 }
 
 RulerAssistantFactory::RulerAssistantFactory()
Index: krita/plugins/assistants/RulerAssistant/RulerAssistant.h
===================================================================
--- krita/plugins/assistants/RulerAssistant/RulerAssistant.h	(revision 1163293)
+++ krita/plugins/assistants/RulerAssistant/RulerAssistant.h	(working copy)
@@ -27,7 +27,7 @@
 public:
     RulerAssistant();
     virtual QPointF adjustPosition(const QPointF& point) const;
-    virtual void drawAssistant(QPainter& gc, const QPoint& documentOffset,  const QRect& area, const KoViewConverter &converter) const;
+    void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter);
 private:
     QPointF project(const QPointF& pt) const;
 };
Index: krita/plugins/assistants/RulerAssistant/kis_ruler_assistant_tool.cc
===================================================================
--- krita/plugins/assistants/RulerAssistant/kis_ruler_assistant_tool.cc	(revision 1163293)
+++ krita/plugins/assistants/RulerAssistant/kis_ruler_assistant_tool.cc	(working copy)
@@ -109,11 +109,15 @@
 
 void KisRulerAssistantTool::paint(QPainter& _gc, const KoViewConverter &_converter)
 {
+    QColor handlesColor(0, 0, 0, 125);
+
     foreach(const KisPaintingAssistantHandleSP handle, m_handles) {
         if (handle == m_handleDrag) {
-            _gc.setBrush(QColor(0, 0, 0, 125));
+            _gc.setPen(handlesColor);
+            _gc.setBrush(handlesColor);
         } else {
-            _gc.setBrush(QColor(0, 0, 0, 0));
+            _gc.setPen(handlesColor);
+            _gc.setBrush(Qt::transparent);
         }
         _gc.drawEllipse(QRectF(_converter.documentToView(*handle) -  QPointF(5, 5), QSizeF(10, 10)));
     }
Index: krita/ui/kis_view2.cpp
===================================================================
--- krita/ui/kis_view2.cpp	(revision 1163293)
+++ krita/ui/kis_view2.cpp	(working copy)
@@ -79,6 +79,7 @@
 #include "kis_config_notifier.h"
 #include "kis_statusbar.h"
 #include "canvas/kis_canvas2.h"
+#include "kis_coordinates_converter.h"
 #include "kis_doc2.h"
 #include "kis_factory2.h"
 #include "kis_filter_manager.h"
@@ -210,11 +211,14 @@
     m_d->viewConverter = new KoZoomHandler();
 
     KoCanvasControllerWidget *canvasController = new KoCanvasControllerWidget(this);
+    canvasController->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+    canvasController->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
+    canvasController->setDrawShadow(false);
+    canvasController->setCanvasMode(KoCanvasController::Infinite);
+
     m_d->canvasController = canvasController;
-    m_d->canvasController->setDrawShadow(false);
-    m_d->canvasController->setMargin(10);
-    m_d->canvasController->setCanvasMode(KoCanvasController::Infinite);
 
+
     m_d->canvas = new KisCanvas2(m_d->viewConverter, this, doc->shapeController());
     m_d->canvasController->setCanvas(m_d->canvas);
 
@@ -238,6 +242,21 @@
     m_d->mirrorCanvas->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_I));
     connect(m_d->mirrorCanvas, SIGNAL(toggled(bool)),m_d->canvas, SLOT(mirrorCanvas(bool)));
 
+    KAction *rotateCanvasRight = new KAction(i18n("Rotate Canvas Right"), this);
+    actionCollection()->addAction("rotate_canvas_right", rotateCanvasRight);
+    rotateCanvasRight->setShortcut(QKeySequence("Ctrl+]"));
+    connect(rotateCanvasRight, SIGNAL(triggered()),m_d->canvas, SLOT(rotateCanvasRight15()));
+
+    KAction *rotateCanvasLeft = new KAction(i18n("Rotate Canvas Left"), this);
+    actionCollection()->addAction("rotate_canvas_left", rotateCanvasLeft);
+    rotateCanvasLeft->setShortcut(QKeySequence("Ctrl+["));
+    connect(rotateCanvasLeft, SIGNAL(triggered()),m_d->canvas, SLOT(rotateCanvasLeft15()));
+
+    KAction *resetCanvasTransformations = new KAction(i18n("Reset Canvas Transformations"), this);
+    actionCollection()->addAction("reset_canvas_transformations", resetCanvasTransformations);
+    resetCanvasTransformations->setShortcut(QKeySequence("Ctrl+'"));
+    connect(resetCanvasTransformations, SIGNAL(triggered()),m_d->canvas, SLOT(resetCanvasTransformations()));
+
     if (shell())
     {
         KoToolBoxFactory toolBoxFactory(m_d->canvasController, i18n("Tools"));
@@ -278,6 +297,7 @@
 
     // canvas sends signal that origin is changed
     connect(m_d->canvas, SIGNAL(documentOriginChanged()), m_d->zoomManager, SLOT(pageOffsetChanged()));
+    connect(m_d->canvas, SIGNAL(scrollAreaSizeChanged()), m_d->zoomManager, SLOT(slotScrollAreaSizeChanged()));
 
     setAcceptDrops(true);
 }
@@ -680,15 +700,13 @@
     m_d->zoomManager->zoomController()->setPageSize(size);
     m_d->zoomManager->zoomController()->setDocumentSize(size);
 
-    QSize documentSize(int(ceil(m_d->viewConverter->documentToViewX(image()->width()  / image()->xRes()))),
-                       int(ceil(m_d->viewConverter->documentToViewY(image()->height() / image()->yRes()))));
-    m_d->canvasController->updateDocumentSize(documentSize, true);
+    canvasBase()->notifyZoomChanged();
+    QSize widgetSize = canvasBase()->coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect().size();
+    m_d->canvasController->updateDocumentSize(widgetSize, true);
 
     m_d->zoomManager->updateGUI();
 
     //update view
-    canvasBase()->preScale();
-    canvasBase()->adjustOrigin();
     canvas()->update();
 }
 
Index: krita/ui/kis_selection_decoration.cc
===================================================================
--- krita/ui/kis_selection_decoration.cc	(revision 1163293)
+++ krita/ui/kis_selection_decoration.cc	(working copy)
@@ -23,8 +23,6 @@
 #include <kdebug.h>
 #include <klocale.h>
 
-#include <KoViewConverter.h>
-
 #include "kis_types.h"
 #include "kis_view2.h"
 #include "kis_selection.h"
@@ -34,6 +32,7 @@
 #include "kis_selection_manager.h"
 #include "canvas/kis_canvas2.h"
 #include "kis_canvas_resource_provider.h"
+#include "kis_coordinates_converter.h"
 
 KisSelectionDecoration::KisSelectionDecoration(KisView2* view)
     : KisCanvasDecoration("selection", i18n("Selection decoration"), view), m_mode(Ants)
@@ -169,77 +168,71 @@
     }
 }
 
-void KisSelectionDecoration::drawDecoration(QPainter& painter, const QPoint& documentOffset, const QRect& area, const KoViewConverter& converter)
+void KisSelectionDecoration::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter)
 {
+    Q_UNUSED(updateRect);
 
-    Q_UNUSED(documentOffset)
-    Q_UNUSED(area);
     KisSelectionSP selection = view()->selection();
     if (!selection || selection->isDeselected() || !selection->isVisible())
         return;
 
-    qreal sx, sy;
-    converter.zoom(&sx, &sy);
-
     if (m_mode == Mask) {
         Q_ASSERT_X(0, "KisSelectionDecoration.cc", "MASK MODE NOT SUPPORTED YET!");
     }
-    if (m_mode == Ants && selection && selection->hasPixelSelection()) {
 
-        QTransform matrix;
-        matrix.scale(sx / view()->image()->xRes(), sy / view()->image()->yRes());
+    if (m_mode == Ants && selection->hasPixelSelection()) {
 
-        QTransform oldWorldMatrix = painter.worldTransform();
-        painter.setWorldTransform(matrix, true);
+        QTransform transform = converter->imageToWidgetTransform();
 
-        QTime t;
-        t.start();
-        painter.setRenderHints(0);
+        qreal scaleX, scaleY;
+        converter->imageScale(&scaleX, &scaleY);
 
+        gc.save();
+        gc.setTransform(transform);
+        gc.setRenderHints(0);
+
         QPen pen(m_brushes[m_offset], 0);
 
         int i = 0;
-        painter.setPen(pen);
-        if (1 / view()->image()->xRes()*sx < 3)
+        gc.setPen(pen);
+        if (0.5 * (scaleX + scaleY) < 3) {
             foreach(const QPolygon & polygon, m_simpleOutline) {
-            painter.drawPolygon(polygon);
-            i++;
+                gc.drawPolygon(polygon);
+                i++;
+            }
         } else {
             foreach(const QPolygon & polygon, m_outline) {
-                painter.drawPolygon(polygon);
+                gc.drawPolygon(polygon);
                 i++;
             }
         }
 
-        dbgRender << "Polygons :" << i;
-        dbgRender << "Painting marching ants :" << t.elapsed();
-
-        painter.setWorldTransform(oldWorldMatrix);
+        gc.restore();
     }
-    if (m_mode == Ants && selection && selection->hasShapeSelection()) {
+
+    if (m_mode == Ants && selection->hasShapeSelection()) {
         KisShapeSelection* shapeSelection = static_cast<KisShapeSelection*>(selection->shapeSelection());
 
         QVector<qreal> dashes;
-        qreal space = 4;
-        dashes << 4 << space;
+        dashes << 4 << 4;
 
+        QPen backgroundPen(Qt::white);
+        backgroundPen.setCosmetic(true);
+
         QPainterPathStroker stroker;
         stroker.setWidth(0);
         stroker.setDashPattern(dashes);
         stroker.setDashOffset(m_offset - 4);
+        QPainterPath stroke = stroker.createStroke(shapeSelection->selectionOutline());
 
-        painter.setRenderHint(QPainter::Antialiasing);
-        QColor outlineColor = Qt::black;
+        QTransform transform = converter->documentToWidgetTransform();
 
-        QTransform zoomMatrix;
-        zoomMatrix.scale(sx, sy);
-
-        QPen backgroundPen(Qt::white);
-        backgroundPen.setCosmetic(true);
-        painter.strokePath(zoomMatrix.map(shapeSelection->selectionOutline()), backgroundPen);
-
-        QPainterPath stroke = stroker.createStroke(zoomMatrix.map(shapeSelection->selectionOutline()));
-        painter.fillPath(stroke, outlineColor);
+        gc.save();
+        gc.setTransform(transform);
+        gc.setRenderHint(QPainter::Antialiasing);
+        gc.strokePath(shapeSelection->selectionOutline(), backgroundPen);
+        gc.fillPath(stroke, Qt::black);
+        gc.restore();
     }
 }
 
Index: krita/ui/kis_cursor.cc
===================================================================
--- krita/ui/kis_cursor.cc	(revision 1163293)
+++ krita/ui/kis_cursor.cc	(working copy)
@@ -330,12 +330,12 @@
 
 QCursor KisCursor::openHandCursor()
 {
-    return load("openhand_cursor.xpm");
+    return Qt::OpenHandCursor;
 }
 
 QCursor KisCursor::closedHandCursor()
 {
-    return load("closedhand_cursor.xpm");
+    return Qt::ClosedHandCursor;
 }
 
 QCursor KisCursor::rotateCursor()
Index: krita/ui/kis_zoom_manager.h
===================================================================
--- krita/ui/kis_zoom_manager.h	(revision 1163293)
+++ krita/ui/kis_zoom_manager.h	(working copy)
@@ -60,6 +60,7 @@
 private slots:
 
     void slotZoomChanged(KoZoomMode::Mode mode, qreal zoom);
+    void slotScrollAreaSizeChanged();
     void toggleShowRulers(bool show);
     void mousePositionChanged(const QPoint &pos);
     void changeAspectMode(bool aspectMode);
Index: krita/ui/kis_painting_assistants_manager.h
===================================================================
--- krita/ui/kis_painting_assistants_manager.h	(revision 1163293)
+++ krita/ui/kis_painting_assistants_manager.h	(working copy)
@@ -42,7 +42,8 @@
     void setup(KActionCollection * collection);
     QList<KisPaintingAssistantHandleSP> handles();
 protected:
-    virtual void drawDecoration(QPainter& gc, const QPoint& documentOffset,  const QRect& area, const KoViewConverter &converter);
+    void drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter);
+
 private:
     struct Private;
     Private* const d;
Index: krita/ui/opengl/kis_opengl_canvas2.cpp
===================================================================
--- krita/ui/opengl/kis_opengl_canvas2.cpp	(revision 1163293)
+++ krita/ui/opengl/kis_opengl_canvas2.cpp	(working copy)
@@ -31,6 +31,7 @@
 #include <QPaintEvent>
 #include <QPoint>
 #include <QPainter>
+#include <QTransform>
 
 #include <kxmlguifactory.h>
 
@@ -42,12 +43,14 @@
 #include "kis_types.h"
 #include <ko_favorite_resource_manager.h>
 #include "canvas/kis_canvas2.h"
+#include "kis_coordinates_converter.h"
 #include "kis_image.h"
 #include "opengl/kis_opengl.h"
 #include "opengl/kis_opengl_image_textures.h"
 #include "kis_view2.h"
 #include "kis_canvas_resource_provider.h"
 #include "kis_config.h"
+#include "kis_config_notifier.h"
 #include "kis_debug.h"
 #include "kis_selection_manager.h"
 #include "kis_group_layer.h"
@@ -74,9 +77,9 @@
     bool GLStateSaved;
 };
 
-KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 * canvas, QWidget * parent, KisOpenGLImageTexturesSP imageTextures)
+KisOpenGLCanvas2::KisOpenGLCanvas2(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent, KisOpenGLImageTexturesSP imageTextures)
         : QGLWidget(QGLFormat(QGL::SampleBuffers), parent, KisOpenGL::sharedContextWidget())
-        , KisCanvasWidgetBase(canvas)
+        , KisCanvasWidgetBase(canvas, coordinatesConverter)
         , m_d(new Private())
 {
     m_d->openGLImageTextures = imageTextures;
@@ -92,6 +95,9 @@
     } else {
         dbgUI << "Created QGLWidget with no sharing";
     }
+
+    connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
+    slotConfigChanged();
 }
 
 KisOpenGLCanvas2::~KisOpenGLCanvas2()
@@ -103,11 +109,11 @@
 {
 }
 
-void KisOpenGLCanvas2::resizeGL(int w, int h)
+void KisOpenGLCanvas2::resizeGL(int width, int height)
 {
-    glViewport(0, 0, (GLint)w, (GLint)h);
-    adjustOrigin();
-    update();
+    glViewport(0, 0, (GLint)width, (GLint)height);
+    coordinatesConverter()->setCanvasWidgetSize(QSize(width, height));
+    emit needAdjustOrigin();
 }
 
 void KisOpenGLCanvas2::paintEvent(QPaintEvent *)
@@ -118,34 +124,16 @@
 
     drawBorder();
 
+    Q_ASSERT(canvas()->image());
+
     if (canvas()->image()) {
         drawBackground();
-
-        if(canvas()->image()->rootLayer()->childCount()>0) {
-            drawImage();
-        }
-
+        drawImage();
         restoreGLState();
 
-        // XXX: make settable
-        bool drawTools = true;
 
-        gc.save();
-        if (canvas()->isCanvasMirrored()){
-            QTransform m = gc.transform();
-            m.translate( documentOrigin().x(), 0);
-            m.scale(-1,1 );
-            m.translate( -documentSize().width(),0 );
-            m.translate( -documentOrigin().x(), 0);
-            gc.setTransform(m);
-        }
-
-        drawDecorations(gc, drawTools,
-                        documentOffset(),
-                        QRect(QPoint(0, 0), documentSize()),
-                        canvas());
-                        
-        gc.restore();
+        QRect boundingRect = coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect();
+        drawDecorations(gc, boundingRect);
     } else {
         restoreGLState();
     }
@@ -153,6 +141,28 @@
     gc.end();
 }
 
+void KisOpenGLCanvas2::loadQTransform(QTransform transform)
+{
+    GLfloat matrix[16];
+    memset(matrix, 0, sizeof(GLfloat) * 16);
+
+    matrix[0] = transform.m11();
+    matrix[1] = transform.m12();
+
+    matrix[4] = transform.m21();
+    matrix[5] = transform.m22();
+
+    matrix[12] = transform.m31();
+    matrix[13] = transform.m32();
+
+    matrix[3] = transform.m13();
+    matrix[7] = transform.m23();
+
+    matrix[15] = transform.m33();
+
+    glLoadMatrixf(matrix);
+}
+
 void KisOpenGLCanvas2::drawBorder()
 {
     QColor widgetBackgroundColor = borderColor();
@@ -163,9 +173,21 @@
 
 void KisOpenGLCanvas2::drawBackground()
 {
-    const qreal scaleX = zoomScaleX();
-    const qreal scaleY = zoomScaleY();
+    KisCoordinatesConverter *converter = coordinatesConverter();
 
+    QTransform textureTransform;
+    QTransform modelTransform;
+    QRectF textureRect;
+    QRectF modelRect;
+    converter->getOpenGLCheckersInfo(&textureTransform, &modelTransform, &textureRect, &modelRect);
+
+    KisConfig cfg;
+    GLfloat checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast<GLfloat>(cfg.checkSize());
+
+    textureTransform *= QTransform::fromScale(checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE,
+                                               checkSizeScale / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE);
+
+
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
     glViewport(0, 0, width(), height());
@@ -173,46 +195,30 @@
 
     glMatrixMode(GL_TEXTURE);
     glLoadIdentity();
+    loadQTransform(textureTransform);
 
-    KisConfig cfg;
-    GLfloat checkSizeScale = KisOpenGLImageTextures::BACKGROUND_TEXTURE_CHECK_SIZE / static_cast<GLfloat>(cfg.checkSize());
-
-    glScalef(checkSizeScale, checkSizeScale, 1.0);
-
-    if (cfg.scrollCheckers()) {
-        glTranslatef(static_cast<GLfloat>(documentOffset().x()) / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE,
-                     static_cast<GLfloat>(documentOffset().y()) / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE,
-                     0.0);
-    }
-
-    glScalef(scaleX, scaleY, 1.0);
-
     glMatrixMode(GL_MODELVIEW);
     glLoadIdentity();
-    glTranslatef(documentOrigin().x(), documentOrigin().y(), 0.0);
-    glScalef(scaleX, scaleY, 1.0);
+    loadQTransform(modelTransform);
 
     glBindTexture(GL_TEXTURE_2D, m_d->openGLImageTextures->backgroundTexture());
     glEnable(GL_TEXTURE_2D);
     glShadeModel(GL_FLAT);
 
     glBegin(GL_QUADS);
-
     glColor3f(1.0, 1.0, 1.0);
-    glTexCoord2f(0.0, 0.0);
-    glVertex2f(0.0, 0.0);
 
-    KisImageWSP image = canvas()->image();
+    glTexCoord2f(textureRect.left(), textureRect.top());
+    glVertex2f(modelRect.left(), modelRect.top());
 
-    glTexCoord2f(image->width() / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE, 0.0);
-    glVertex2f(image->width(), 0.0);
+    glTexCoord2f(textureRect.right(), textureRect.top());
+    glVertex2f(modelRect.right(), modelRect.top());
 
-    glTexCoord2f(image->width() / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE,
-                 image->height() / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE);
-    glVertex2f(image->width(), image->height());
+    glTexCoord2f(textureRect.right(), textureRect.bottom());
+    glVertex2f(modelRect.right(), modelRect.bottom());
 
-    glTexCoord2f(0.0, image->height() / KisOpenGLImageTextures::BACKGROUND_TEXTURE_SIZE);
-    glVertex2f(0.0, image->height());
+    glTexCoord2f(textureRect.left(), textureRect.bottom());
+    glVertex2f(modelRect.left(), modelRect.bottom());
 
     glEnd();
 
@@ -222,24 +228,26 @@
 
 void KisOpenGLCanvas2::drawImage()
 {
-    setPixelToViewTransformation();
+    setupImageToWidgetTransformation();
 
-    const qreal scaleX = zoomScaleX();
-
     glEnable(GL_TEXTURE_2D);
-
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
-    QRectF documentRect = viewConverter()->viewToDocument(QRectF(documentOffset().x(), 
-                                                                    documentOffset().y(), 
-                                                                    width(), 
-                                                                    height()));
+
     KisImageWSP image = canvas()->image();
+    KisCoordinatesConverter *converter = coordinatesConverter();
 
-    QRect wr = image->documentToIntPixel(documentRect);
-    wr &= QRect(0, 0, image->width(), image->height());
+    QRectF widgetRect(0,0, width(), height());
+    QRectF widgetRectInImagePixels = converter->
+        documentToImage(converter->widgetToDocument(widgetRect));
 
+    qreal scaleX, scaleY;
+    converter->imageScale(&scaleX, &scaleY);
+
+    QRect wr = widgetRectInImagePixels.toAlignedRect() & image->bounds();
+
+
     if (image->colorSpace()->hasHighDynamicRange()) {
         if (m_d->openGLImageTextures->usingHDRExposureProgram()) {
             m_d->openGLImageTextures->activateHDRExposureProgram();
@@ -249,11 +257,6 @@
 
     makeCurrent();
 
-    if (canvas()->isCanvasMirrored()){
-        glScalef(-1,1,1);
-        glTranslatef(-image->width(),0,0);
-    }
-    
     for (int x = (wr.left() / m_d->openGLImageTextures->imageTextureTileWidth()) * m_d->openGLImageTextures->imageTextureTileWidth();
         x <= wr.right();
         x += m_d->openGLImageTextures->imageTextureTileWidth()) {
@@ -352,7 +355,6 @@
 void KisOpenGLCanvas2::beginOpenGL(void)
 {
     saveGLState();
-    setupMatrices();
 }
 
 void KisOpenGLCanvas2::endOpenGL(void)
@@ -360,7 +362,7 @@
     restoreGLState();
 }
 
-void KisOpenGLCanvas2::setupMatrices(void)
+void KisOpenGLCanvas2::setupImageToWidgetTransformation()
 {
     glMatrixMode(GL_PROJECTION);
     glLoadIdentity();
@@ -371,44 +373,25 @@
     glLoadIdentity();
 
     glMatrixMode(GL_MODELVIEW);
-    glLoadIdentity();
-    glTranslatef(-documentOffset().x(), -documentOffset().y(), 0.0);
-    glTranslatef(documentOrigin().x(),  documentOrigin().y(), 0.0);
-}
 
-void KisOpenGLCanvas2::applyZoomScalingToModelView(void)
-{
-    KisImageWSP image = canvas()->image();
-    
-    if (!image) return;
-    
-    const qreal scaleX = zoomScaleX();
-    const qreal scaleY = zoomScaleY();
-    
-    glMatrixMode(GL_MODELVIEW);
-    glScalef(scaleX, scaleY, 1.0);
+    QTransform transform = coordinatesConverter()->imageToWidgetTransform();
+    loadQTransform(transform);
 }
 
-void KisOpenGLCanvas2::setPixelToViewTransformation(void)
+void KisOpenGLCanvas2::setupFlakeToWidgetTransformation()
 {
-    setupMatrices();
-    applyZoomScalingToModelView();
-}
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    glViewport(0, 0, width(), height());
+    glOrtho(0, width(), height(), 0, NEAR_VAL, FAR_VAL);
 
-qreal KisOpenGLCanvas2::zoomScaleX() const
-{
-    qreal sx, sy;
-    viewConverter()->zoom(&sx, &sy);
+    glMatrixMode(GL_TEXTURE);
+    glLoadIdentity();
 
-    return sx / canvas()->image()->xRes();
-}
+    glMatrixMode(GL_MODELVIEW);
 
-qreal KisOpenGLCanvas2::zoomScaleY() const
-{
-    qreal sx, sy;
-    viewConverter()->zoom(&sx, &sy);
-
-    return sy / canvas()->image()->yRes();
+    QTransform transform = coordinatesConverter()->flakeToWidgetTransform();
+    loadQTransform(transform);
 }
 
 void KisOpenGLCanvas2::enterEvent(QEvent* e)
@@ -422,6 +405,11 @@
     QWidget::leaveEvent(e);
 }
 
+void KisOpenGLCanvas2::slotConfigChanged()
+{
+    notifyConfigChanged();
+}
+
 void KisOpenGLCanvas2::mouseMoveEvent(QMouseEvent *e)
 {
     processMouseMoveEvent(e);
@@ -477,17 +465,6 @@
     processWheelEvent(e);
 }
 
-void KisOpenGLCanvas2::documentOffsetMoved(const QPoint & pt)
-{
-    KisCanvasWidgetBase::documentOffsetMoved(pt);
-    update();
-}
-
-void KisOpenGLCanvas2::emitDocumentOriginChangedSignal()
-{
-    emit documentOriginChanged(documentOrigin());
-}
-
 bool KisOpenGLCanvas2::callFocusNextPrevChild(bool next)
 {
     return focusNextPrevChild(next);
Index: krita/ui/opengl/kis_opengl_canvas2.h
===================================================================
--- krita/ui/opengl/kis_opengl_canvas2.h	(revision 1163293)
+++ krita/ui/opengl/kis_opengl_canvas2.h	(working copy)
@@ -51,7 +51,7 @@
 
 public:
 
-    KisOpenGLCanvas2(KisCanvas2 * canvas, QWidget * parent, KisOpenGLImageTexturesSP imageTextures);
+    KisOpenGLCanvas2(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent, KisOpenGLImageTexturesSP imageTextures);
 
     virtual ~KisOpenGLCanvas2();
 
@@ -61,22 +61,28 @@
      * that primitives can be rendered using coordinates returned 
      * from pixelToView(). 
      */ 
-    void beginOpenGL(void);
+    void beginOpenGL();
 
     /** 
      * Notify the canvas that rendering using native OpenGL commands
      * has finished. This restores the state so that the canvas can 
      * be painted on using a QPainter. 
      */ 
-    void endOpenGL(void);
+    void endOpenGL();
 
     /**
      * Set the projection and model view matrices so that primitives can be
-     * rendered using image pixel coordinates. This handles zooming and scrolling
-     * of the canvas.
+     * rendered using image pixel coordinates. This handles zooming and
+     * scrolling of the canvas.
      */
-    void setPixelToViewTransformation(void);
+    void setupImageToWidgetTransformation();
 
+    /**
+     * The same as \ref setupImageToWidgetTransformation(), but input
+     * coordinate system is flake
+     */
+    void setupFlakeToWidgetTransformation();
+
 public: // QWidget
 
     /// reimplemented method from superclass
@@ -122,19 +128,16 @@
     /// reimplemented method from superclass
     virtual void inputMethodEvent(QInputMethodEvent *event);
 
+private slots:
+    void slotConfigChanged();
+
 signals:
-    /**
-     * This signal is emitted when the document origin has changed.
-     * The document origin is the point (in pixel) on the virtual
-     * canvas where the documents origin (0,0) or the top left
-     * corner of the page is. Copied from Karbon
-     */
-    void documentOriginChanged(const QPoint &origin);
+    void needAdjustOrigin();
 
 protected:
 
+    void resizeGL(int width, int height);
     void initializeGL();
-    void resizeGL(int w, int h);
 
 public: // KisAbstractCanvasWidget
 
@@ -142,30 +145,21 @@
         return this;
     }
 
-    void documentOffsetMoved(const QPoint & pt);
-
 protected: // KisCanvasWidgetBase
-
-    virtual void emitDocumentOriginChangedSignal();
     virtual bool callFocusNextPrevChild(bool next);
 
 private:
     class Private;
     Private * const m_d;
 
+    void loadQTransform(QTransform transform);
+
     void drawBorder();
     void drawImage();
     void drawBackground();
 
-    void setupMatrices();
-    void applyZoomScalingToModelView();
-
-    qreal zoomScaleX() const;
-    qreal zoomScaleY() const;
-
     void saveGLState();
     void restoreGLState();
-
 };
 
 #endif // HAVE_OPENGL
Index: krita/ui/kis_selection_decoration.h
===================================================================
--- krita/ui/kis_selection_decoration.h	(revision 1163293)
+++ krita/ui/kis_selection_decoration.h	(working copy)
@@ -38,7 +38,7 @@
 
     void setMode(Mode mode);
 protected:
-    virtual void drawDecoration(QPainter& gc, const QPoint & documentOffset, const QRect& area, const KoViewConverter &converter);
+    void drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter);
 public slots:
     void selectionChanged();
     void selectionTimerEvent();
Index: krita/ui/kis_zoom_manager.cc
===================================================================
--- krita/ui/kis_zoom_manager.cc	(revision 1163293)
+++ krita/ui/kis_zoom_manager.cc	(working copy)
@@ -41,6 +41,7 @@
 
 #include "kis_view2.h"
 #include "canvas/kis_canvas2.h"
+#include "kis_coordinates_converter.h"
 #include "kis_image.h"
 #include "kis_statusbar.h"
 #include "kis_config.h"
@@ -156,11 +157,16 @@
     Q_UNUSED(zoom);
     KisImageWSP image = m_view->image();
 
-    m_view->canvasBase()->preScale();
-    m_view->canvasBase()->adjustOrigin();
+    m_view->canvasBase()->notifyZoomChanged();
     m_view->canvas()->update();
 }
 
+void KisZoomManager::slotScrollAreaSizeChanged()
+{
+    QSize widgetSize = m_view->canvasBase()->coordinatesConverter()->imageRectInWidgetPixels().toAlignedRect().size();
+    m_canvasController->updateDocumentSize(widgetSize, true);
+}
+
 void KisZoomManager::changeAspectMode(bool aspectMode)
 {
     KisImageWSP image = m_view->image();
Index: krita/ui/canvas/kis_perspective_grid_manager.cpp
===================================================================
--- krita/ui/canvas/kis_perspective_grid_manager.cpp	(revision 1163293)
+++ krita/ui/canvas/kis_perspective_grid_manager.cpp	(working copy)
@@ -27,7 +27,7 @@
 #include <ktoggleaction.h>
 #include <kactioncollection.h>
 
-#include <KoViewConverter.h>
+#include <QTransform>
 
 #include "canvas/kis_canvas2.h"
 #include "kis_config.h"
@@ -36,7 +36,9 @@
 #include "canvas/kis_grid_painter_configuration.h"
 #include "kis_view2.h"
 #include "kis_canvas_resource_provider.h"
+#include "kis_coordinates_converter.h"
 
+
 KisPerspectiveGridManager::KisPerspectiveGridManager(KisView2 * parent)
         : KisCanvasDecoration("perspectiveGrid", i18n("Perspective grid"), parent)
         , m_toggleEdition(false)
@@ -104,65 +106,66 @@
     }
 }
 
-#define pixelToView(point) \
-    converter.documentToView(image->pixelToDocument(point))
-
-void KisPerspectiveGridManager::drawDecoration(QPainter& gc, const QPoint& documentOffset, const QRect& area, const KoViewConverter &converter)
+void KisPerspectiveGridManager::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter)
 {
-    Q_UNUSED(documentOffset);
-    Q_UNUSED(area);
+    Q_UNUSED(updateRect);
 
+    if (m_toggleEdition) return;
+
     KisImageWSP image = m_view->resourceProvider()->currentImage();
+    Q_ASSERT(image);
 
+    KisPerspectiveGrid* pGrid = image->perspectiveGrid();
 
-    if (image && !m_toggleEdition) {
-        KisPerspectiveGrid* pGrid = image->perspectiveGrid();
+    QPen mainPen = KisGridPainterConfiguration::mainPen();
+    QPen subdivisionPen = KisGridPainterConfiguration::subdivisionPen();
 
-        KisConfig cfg;
-        QPen mainPen = KisGridPainterConfiguration::mainPen();
-        QPen subdivisionPen = KisGridPainterConfiguration::subdivisionPen();
 
-        for (QList<KisSubPerspectiveGrid*>::const_iterator it = pGrid->begin(); it != pGrid->end(); ++it) {
-            const KisSubPerspectiveGrid* grid = *it;
-            gc.setPen(subdivisionPen);
-            // 1 -> top-left corner
-            // 2 -> top-right corner
-            // 3 -> bottom-right corner
-            // 4 -> bottom-left corner
-            // d12 line from top-left to top-right
-            // note that the notion of top-left is purely theorical
-            LineEquation d12 = LineEquation::Through(toKisVector2D(*grid->topLeft()), toKisVector2D(*grid->topRight()));
-            QPointF v12 = QPointF(*grid->topLeft() - *grid->topRight());
-            v12.setX(v12.x() / grid->subdivisions()); v12.setY(v12.y() / grid->subdivisions());
-            LineEquation d23 = LineEquation::Through(toKisVector2D(*grid->topRight()), toKisVector2D(*grid->bottomRight()));
-            QPointF v23 = QPointF(*grid->topRight() - *grid->bottomRight());
-            v23.setX(v23.x() / grid->subdivisions()); v23.setY(v23.y() / grid->subdivisions());
-            LineEquation d34 = LineEquation::Through(toKisVector2D(*grid->bottomRight()), toKisVector2D(*grid->bottomLeft()));
-            LineEquation d41 = LineEquation::Through(toKisVector2D(*grid->bottomLeft()), toKisVector2D(*grid->topLeft()));
+    QTransform transform = converter->imageToWidgetTransform();
+    gc.save();
+    gc.setTransform(transform);
 
-            KisVector2D horizVanishingPoint = d12.intersection(d34);
-            KisVector2D vertVanishingPoint = d23.intersection(d41);
+    for (QList<KisSubPerspectiveGrid*>::const_iterator it = pGrid->begin(); it != pGrid->end(); ++it) {
+        const KisSubPerspectiveGrid* grid = *it;
 
-            for (int i = 1; i < grid->subdivisions(); i ++) {
-                KisVector2D pol1 = toKisVector2D(*grid->topRight() + i * v12);
-                LineEquation d1 = LineEquation::Through(pol1, vertVanishingPoint);
-                KisVector2D pol1b =  d1.intersection(d34);
-                gc.drawLine(pixelToView(toQPointF(pol1)), pixelToView(toQPointF(pol1b)));
+        gc.setPen(subdivisionPen);
+        // 1 -> top-left corner
+        // 2 -> top-right corner
+        // 3 -> bottom-right corner
+        // 4 -> bottom-left corner
+        // d12 line from top-left to top-right
+        // note that the notion of top-left is purely theorical
+        LineEquation d12 = LineEquation::Through(toKisVector2D(*grid->topLeft()), toKisVector2D(*grid->topRight()));
+        QPointF v12 = QPointF(*grid->topLeft() - *grid->topRight());
+        v12.setX(v12.x() / grid->subdivisions()); v12.setY(v12.y() / grid->subdivisions());
+        LineEquation d23 = LineEquation::Through(toKisVector2D(*grid->topRight()), toKisVector2D(*grid->bottomRight()));
+        QPointF v23 = QPointF(*grid->topRight() - *grid->bottomRight());
+        v23.setX(v23.x() / grid->subdivisions()); v23.setY(v23.y() / grid->subdivisions());
+        LineEquation d34 = LineEquation::Through(toKisVector2D(*grid->bottomRight()), toKisVector2D(*grid->bottomLeft()));
+        LineEquation d41 = LineEquation::Through(toKisVector2D(*grid->bottomLeft()), toKisVector2D(*grid->topLeft()));
 
-                KisVector2D pol2 = toKisVector2D(*grid->bottomRight() + i * v23);
-                LineEquation d2 = LineEquation::Through(pol2, horizVanishingPoint);
-                KisVector2D pol2b = d2.intersection(d41);
-                gc.drawLine(pixelToView(toQPointF(pol2)), pixelToView(toQPointF(pol2b)));
-            }
-            gc.setPen(mainPen);
-            gc.drawLine(pixelToView(*grid->topLeft()), pixelToView(*grid->topRight()));
-            gc.drawLine(pixelToView(*grid->topRight()), pixelToView(*grid->bottomRight()));
-            gc.drawLine(pixelToView(*grid->bottomRight()), pixelToView(*grid->bottomLeft()));
-            gc.drawLine(pixelToView(*grid->bottomLeft()), pixelToView(*grid->topLeft()));
+        KisVector2D horizVanishingPoint = d12.intersection(d34);
+        KisVector2D vertVanishingPoint = d23.intersection(d41);
 
+        for (int i = 1; i < grid->subdivisions(); i ++) {
+            KisVector2D pol1 = toKisVector2D(*grid->topRight() + i * v12);
+            LineEquation d1 = LineEquation::Through(pol1, vertVanishingPoint);
+            KisVector2D pol1b =  d1.intersection(d34);
+            gc.drawLine(toQPointF(pol1), toQPointF(pol1b));
+
+            KisVector2D pol2 = toKisVector2D(*grid->bottomRight() + i * v23);
+            LineEquation d2 = LineEquation::Through(pol2, horizVanishingPoint);
+            KisVector2D pol2b = d2.intersection(d41);
+            gc.drawLine(toQPointF(pol2), toQPointF(pol2b));
         }
+
+        gc.setPen(mainPen);
+        gc.drawLine(*grid->topLeft(), *grid->topRight());
+        gc.drawLine(*grid->topRight(), *grid->bottomRight());
+        gc.drawLine(*grid->bottomRight(), *grid->bottomLeft());
+        gc.drawLine(*grid->bottomLeft(), *grid->topLeft());
     }
+    gc.restore();
 }
 
-
 #include "kis_perspective_grid_manager.moc"
Index: krita/ui/canvas/kis_update_info.cpp
===================================================================
--- krita/ui/canvas/kis_update_info.cpp	(revision 1163293)
+++ krita/ui/canvas/kis_update_info.cpp	(working copy)
@@ -34,7 +34,7 @@
 
 QRect KisOpenGLUpdateInfo::dirtyViewportRect() {
     qFatal("Not implemented yet!");
-    return viewportRect.toAlignedRect();
+    return QRect();
 }
 
 #endif /* HAVE_OPENGL */
Index: krita/ui/canvas/kis_canvas2.cpp
===================================================================
--- krita/ui/canvas/kis_canvas2.cpp	(revision 1163293)
+++ krita/ui/canvas/kis_canvas2.cpp	(working copy)
@@ -39,6 +39,7 @@
 #include <KoToolProxy.h>
 #include <KoSelection.h>
 
+#include "kis_coordinates_converter.h"
 #include "kis_prescaled_projection.h"
 #include "kis_image.h"
 #include "kis_doc2.h"
@@ -72,7 +73,8 @@
 public:
 
     KisCanvas2Private(KoCanvasBase * parent, KoViewConverter * viewConverter, KisView2 * view)
-        : viewConverter(viewConverter)
+        : coordinatesConverter(new KisCoordinatesConverter(viewConverter))
+        , viewConverter(viewConverter)
         , view(view)
         , canvasWidget(0)
         , shapeManager(new KoShapeManager(parent))
@@ -81,15 +83,17 @@
         , currentCanvasUsesOpenGLShaders(false)
         , toolProxy(new KoToolProxy(parent))
         , favoriteResourceManager(0)
-        , mirrorMode(false){
+        , canvasMirroredY(false) {
     }
 
     ~KisCanvas2Private() {
+        delete coordinatesConverter;
         delete favoriteResourceManager;
         delete shapeManager;
         delete toolProxy;
     }
 
+    KisCoordinatesConverter *coordinatesConverter;
     KoViewConverter *viewConverter;
     KisView2 *view;
     KisAbstractCanvasWidget *canvasWidget;
@@ -98,14 +102,12 @@
     bool currentCanvasIsOpenGL;
     bool currentCanvasUsesOpenGLShaders;
     KoToolProxy *toolProxy;
-    QPoint documentOffset;
-    KoShapeControllerBase *sc;
     KoFavoriteResourceManager *favoriteResourceManager;
-    bool mirrorMode;
 #ifdef HAVE_OPENGL
     KisOpenGLImageTexturesSP openGLImageTextures;
 #endif
     KisPrescaledProjectionSP prescaledProjection;
+    bool canvasMirroredY;
 };
 
 KisCanvas2::KisCanvas2(KoViewConverter * viewConverter, KisView2 * view, KoShapeControllerBase * sc)
@@ -114,13 +116,7 @@
 {
     KisConfig cfg;
     createCanvas(cfg.useOpenGL());
-    KisCanvasWidgetBase* baseWidget = dynamic_cast<KisCanvasWidgetBase*>(m_d->canvasWidget);
-    if (baseWidget){
-        baseWidget->setBorderColor(cfg.canvasBorderColor());
-    }else{
-        kWarning() << "Something is wrong with canvas! It is no longer KisCanvasWidgetBase";
-    }
-    
+
     connect(view->canvasController()->proxyObject, SIGNAL(moveDocumentOffset(const QPoint&)), SLOT(documentOffsetMoved(const QPoint&)));
     connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
     connect(this, SIGNAL(canvasDestroyed(QWidget *)), this, SLOT(slotCanvasDestroyed(QWidget *)));
@@ -133,7 +129,7 @@
 
 void KisCanvas2::setCanvasWidget(QWidget * widget)
 {
-    connect(widget, SIGNAL(documentOriginChanged(const QPoint&)), this, SLOT(updateRulers()));
+    connect(widget, SIGNAL(needAdjustOrigin()), this, SLOT(adjustOrigin()), Qt::DirectConnection);
 
     KisAbstractCanvasWidget * tmp = dynamic_cast<KisAbstractCanvasWidget*>(widget);
     Q_ASSERT_X(tmp, "setCanvasWidget", "Cannot cast the widget to a KisAbstractCanvasWidget");
@@ -150,10 +146,6 @@
     KoCanvasControllerWidget *controller = dynamic_cast<KoCanvasControllerWidget*>(canvasController());
     if (controller) {
         Q_ASSERT(controller->canvas() == this);
-        // Avoids jumping and redrawing when changing zoom means the image fits in the area completely
-        controller->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
-        controller->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
-
         controller->changeCanvasWidget(widget);
     }
 }
@@ -176,17 +168,64 @@
     return m_d->view->document()->gridData().snapToGrid();
 }
 
-bool KisCanvas2::isCanvasMirrored()
+void KisCanvas2::pan(QPoint shift)
 {
-    return m_d->mirrorMode;
+    KoCanvasControllerWidget* controller =
+        dynamic_cast<KoCanvasControllerWidget*>(canvasController());
+    controller->pan(shift);
 }
 
 void KisCanvas2::mirrorCanvas(bool enable)
 {
-    m_d->mirrorMode = enable;
-    updateCanvas();
+    if(enable != m_d->canvasMirroredY) {
+        QPointF oldCenterPoint = m_d->coordinatesConverter->flakeCenterPoint();
+
+        QTransform newTransform = m_d->coordinatesConverter->postprocessingTransform();
+        newTransform *= QTransform::fromScale(-1,1);
+        m_d->coordinatesConverter->setPostprocessingTransform(newTransform);
+        m_d->canvasMirroredY = enable;
+        notifyZoomChanged();
+
+        QPoint shift = m_d->coordinatesConverter->shiftFromFlakeCenterPoint(oldCenterPoint);
+        pan(shift);
+    }
 }
 
+void KisCanvas2::rotateCanvas(qreal angle)
+{
+    QPointF oldCenterPoint = m_d->coordinatesConverter->flakeCenterPoint();
+
+    QTransform newTransform = m_d->coordinatesConverter->postprocessingTransform();
+    QTransform temp; temp.rotate(angle);
+    newTransform *= temp;
+    m_d->coordinatesConverter->setPostprocessingTransform(newTransform);
+    notifyZoomChanged();
+
+    QPoint shift = m_d->coordinatesConverter->shiftFromFlakeCenterPoint(oldCenterPoint);
+    pan(shift);
+}
+
+void KisCanvas2::rotateCanvasRight15()
+{
+    rotateCanvas(15.);
+}
+
+void KisCanvas2::rotateCanvasLeft15()
+{
+    rotateCanvas(-15.);
+}
+
+void KisCanvas2::resetCanvasTransformations()
+{
+    QPointF oldCenterPoint = m_d->coordinatesConverter->flakeCenterPoint();
+
+    m_d->coordinatesConverter->setPostprocessingTransform(QTransform());
+    notifyZoomChanged();
+
+    QPoint shift = m_d->coordinatesConverter->shiftFromFlakeCenterPoint(oldCenterPoint);
+    pan(shift);
+}
+
 void KisCanvas2::addCommand(QUndoCommand *command)
 {
     m_d->view->koDocument()->addCommand(command);
@@ -232,6 +271,11 @@
     // TODO call (the protected) QWidget::updateMicroFocus() on the proper canvas widget...
 }
 
+const KisCoordinatesConverter* KisCanvas2::coordinatesConverter() const
+{
+    return m_d->coordinatesConverter;
+}
+
 const KoViewConverter* KisCanvas2::viewConverter() const
 {
     return m_d->viewConverter;
@@ -262,9 +306,9 @@
 {
     m_d->currentCanvasIsOpenGL = false;
 
-    KisQPainterCanvas * canvasWidget = new KisQPainterCanvas(this, m_d->view);
+    KisQPainterCanvas * canvasWidget = new KisQPainterCanvas(this, m_d->coordinatesConverter, m_d->view);
     m_d->prescaledProjection = new KisPrescaledProjection();
-    m_d->prescaledProjection->setViewConverter(m_d->viewConverter);
+    m_d->prescaledProjection->setCoordinatesConverter(m_d->coordinatesConverter);
     m_d->prescaledProjection->setMonitorProfile(monitorProfile());
     canvasWidget->setPrescaledProjection(m_d->prescaledProjection);
     setCanvasWidget(canvasWidget);
@@ -277,7 +321,7 @@
 
     // 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);
+    KisOpenGLCanvas2 * canvasWidget = new KisOpenGLCanvas2(this, m_d->coordinatesConverter, m_d->view, m_d->openGLImageTextures);
     m_d->currentCanvasUsesOpenGLShaders = m_d->openGLImageTextures->usingHDRExposureProgram();
     setCanvasWidget(canvasWidget);
 #else
@@ -289,7 +333,7 @@
 {
     KisConfig cfg;
     slotSetDisplayProfile(KoColorSpaceRegistry::instance()->profileByName(cfg.monitorProfile()));
-    
+
     if (useOpenGL) {
 #ifdef HAVE_OPENGL
         if (QGLFormat::hasOpenGL()) {
@@ -313,7 +357,9 @@
 
 void KisCanvas2::connectCurrentImage()
 {
+    m_d->coordinatesConverter->setImage(m_d->view->image());
 
+
     if (m_d->currentCanvasIsOpenGL) {
 #ifdef HAVE_OPENGL
         Q_ASSERT(m_d->openGLImageTextures);
@@ -343,6 +389,8 @@
 
 void KisCanvas2::disconnectCurrentImage()
 {
+    m_d->coordinatesConverter->setImage(0);
+
     if (m_d->currentCanvasIsOpenGL) {
 #ifdef HAVE_OPENGL
         Q_ASSERT(m_d->openGLImageTextures);
@@ -371,6 +419,7 @@
         disconnectCurrentImage();
         createCanvas(useOpenGL);
         connectCurrentImage();
+        notifyZoomChanged();
     }
 
     if (useOpenGL) {
@@ -383,6 +432,7 @@
     }
 
 #endif
+
     m_d->canvasWidget->widget()->update();
 }
 
@@ -430,11 +480,10 @@
 
         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());
+        QRect vRect = m_d->coordinatesConverter->
+            viewportToWidget(info->dirtyViewportRect()).toAlignedRect();
+
+        if (!vRect.isEmpty()) {
             m_d->canvasWidget->widget()->update(vRect);
         }
     }
@@ -445,32 +494,34 @@
     m_d->canvasWidget->widget()->update();
 }
 
-void KisCanvas2::updateCanvas(const QRectF& rc)
+void KisCanvas2::updateCanvas(const QRectF& documentRect)
 {
     // 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);
+    QRect widgetRect = m_d->coordinatesConverter->documentToWidget(documentRect).toAlignedRect();
+    if (!widgetRect.isEmpty()) {
+        m_d->canvasWidget->widget()->update(widgetRect);
     }
 }
 
-void KisCanvas2::preScale()
+void KisCanvas2::notifyZoomChanged()
 {
+    m_d->coordinatesConverter->notifyZoomChanged();
+    adjustOrigin();
+
     if (!m_d->currentCanvasIsOpenGL) {
         Q_ASSERT(m_d->prescaledProjection);
-        m_d->prescaledProjection->preScale();
+        m_d->prescaledProjection->notifyZoomChanged();
     }
+    emit scrollAreaSizeChanged();
 }
 
-QRect KisCanvas2::viewRectFromDoc(const QRectF & rc)
+void KisCanvas2::preScale()
 {
-    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;
+    if (!m_d->currentCanvasIsOpenGL) {
+        Q_ASSERT(m_d->prescaledProjection);
+        m_d->prescaledProjection->preScale();
+    }
 }
 
 KoColorProfile *  KisCanvas2::monitorProfile()
@@ -502,8 +553,16 @@
 
 void KisCanvas2::documentOffsetMoved(const QPoint &documentOffset)
 {
-    m_d->documentOffset = documentOffset;
-    m_d->canvasWidget->documentOffsetMoved(documentOffset);
+    QPointF offsetBefore = m_d->coordinatesConverter->widgetRectInFlakePixels().topLeft();
+    m_d->coordinatesConverter->setDocumentOffset(documentOffset);
+    QPointF offsetAfter = m_d->coordinatesConverter->widgetRectInFlakePixels().topLeft();
+
+    QPointF moveOffset = offsetAfter - offsetBefore;
+
+    if (!m_d->currentCanvasIsOpenGL)
+        m_d->prescaledProjection->viewportMoved(moveOffset);
+
+    updateCanvas();
 }
 
 bool KisCanvas2::usingHDRExposureProgram()
@@ -530,15 +589,6 @@
     if (useOpenGL) {
         cfg.setCanvasState("OPENGL_SUCCESS");
     }
-    
-    // due to canvas border update the canvas
-    KisCanvasWidgetBase * base = dynamic_cast<KisCanvasWidgetBase*>(m_d->canvasWidget);
-    if (base){
-        base->setBorderColor(cfg.canvasBorderColor());
-        m_d->canvasWidget->widget()->update();
-    }else{
-        kWarning() << "Something is wrong with canvas! It is no longer KisCanvasWidgetBase";
-    }
 }
 
 void KisCanvas2::slotSetDisplayProfile(const KoColorProfile * profile)
@@ -559,25 +609,33 @@
 
 QPoint KisCanvas2::documentOrigin() const
 {
-    return m_d->canvasWidget->documentOrigin();
+    return m_d->coordinatesConverter->documentOrigin();
 }
 
 
 void KisCanvas2::adjustOrigin()
 {
-    m_d->canvasWidget->adjustOrigin();
-}
+    QPoint newOrigin;
 
+    QSize documentSize = m_d->coordinatesConverter->imageRectInWidgetPixels().toAlignedRect().size();
+    QSize widgetSize = m_d->canvasWidget->widget()->size();
 
-void KisCanvas2::updateRulers()
-{
+    int widthDiff = widgetSize.width() - documentSize.width();
+    int heightDiff = widgetSize.height() - documentSize.height();
+
+    if (widthDiff > 0)
+        newOrigin.rx() = qRound(0.5 * widthDiff);
+    if (heightDiff > 0)
+        newOrigin.ry() = qRound(0.5 * heightDiff);
+
+    m_d->coordinatesConverter->setDocumentOrigin(newOrigin);
+
     emit documentOriginChanged();
 }
 
-
 QPoint KisCanvas2::documentOffset() const
 {
-    return m_d->documentOffset;
+    return m_d->coordinatesConverter->documentOffset();
 }
 
 void KisCanvas2::createFavoriteResourceManager(KisPaintopBox* paintopbox)
Index: krita/ui/canvas/kis_perspective_grid_manager.h
===================================================================
--- krita/ui/canvas/kis_perspective_grid_manager.h	(revision 1163293)
+++ krita/ui/canvas/kis_perspective_grid_manager.h	(working copy)
@@ -56,7 +56,7 @@
      */
     void clearPerspectiveGrid();
 protected:
-    virtual void drawDecoration(QPainter& gc, const QPoint& documentOffset,  const QRect& area, const KoViewConverter &converter);
+    void drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter);
 private:
     bool m_toggleEdition;
     KisView2* m_view;
Index: krita/ui/canvas/kis_prescaled_projection.cpp
===================================================================
--- krita/ui/canvas/kis_prescaled_projection.cpp	(revision 1163293)
+++ krita/ui/canvas/kis_prescaled_projection.cpp	(working copy)
@@ -36,6 +36,7 @@
 #include "kis_config_notifier.h"
 #include "kis_image.h"
 
+#include "kis_coordinates_converter.h"
 #include "kis_projection_backend.h"
 #include "kis_image_pyramid.h"
 
@@ -82,11 +83,7 @@
     Private()
             : useNearestNeighbour(false)
             , cacheKisImageAsQImage(true)
-            , documentOffset(0, 0)
-            , documentOrigin(0, 0)
-            , canvasSize(0, 0)
-            , imageSize(0, 0)
-            , viewConverter(0)
+            , viewportSize(0, 0)
             , monitorProfile(0)
             , projectionBackend(0) {
     }
@@ -100,13 +97,10 @@
 
     QImage prescaledQImage;
 
-    QPoint documentOffset; // in view pixels
-    QPoint documentOrigin; // in view pixels too
-
-    QSize canvasSize; // in view pixels
-    QSize imageSize; // in kisimage pixels
+    QSize canvasSize;
+    QSize viewportSize;
     KisImageWSP image;
-    KoViewConverter * viewConverter;
+    KisCoordinatesConverter *coordinatesConverter;
     const KoColorProfile* monitorProfile;
     KisProjectionBackend* projectionBackend;
 };
@@ -134,6 +128,7 @@
     m_d->image = image;
     m_d->projectionBackend->setImage(image);
 
+
     setImageSize(image->width(), image->height());
 }
 
@@ -142,9 +137,9 @@
     return m_d->prescaledQImage;
 }
 
-void KisPrescaledProjection::setViewConverter(KoViewConverter * viewConverter)
+void KisPrescaledProjection::setCoordinatesConverter(KisCoordinatesConverter *coordinatesConverter)
 {
-    m_d->viewConverter = viewConverter;
+    m_d->coordinatesConverter = coordinatesConverter;
 }
 
 void KisPrescaledProjection::initBackend(bool cacheKisImageAsQImage)
@@ -176,84 +171,63 @@
     setMonitorProfile(KoColorSpaceRegistry::instance()->profileByName(cfg.monitorProfile()));
 }
 
-
-void KisPrescaledProjection::documentOffsetMoved(const QPoint &documentOffset)
+void KisPrescaledProjection::viewportMoved(const QPointF &offset)
 {
-    dbgRender << "documentOffsetMoved " << m_d->documentOffset << ", to " << documentOffset;
-    QPoint oldDocumentOffset = m_d->documentOffset;
-    m_d->documentOffset = documentOffset;
+    // FIXME: \|/
+    if(m_d->prescaledQImage.isNull()) return;
+    if(offset.isNull()) return;
 
-    // We've called documentOffsetMoved before even updating the projection
-    if (m_d->prescaledQImage.isNull()) {
+    QPoint alignedOffset = offset.toPoint();
 
+    if(offset != alignedOffset) {
+        /**
+         * We can't optimize anything whe offset is float :(
+         * Just prescale entire image.
+         */
+        preScale();
         return;
     }
 
-    qint32 width = m_d->prescaledQImage.width();
-    qint32 height = m_d->prescaledQImage.height();
+    QImage newImage = QImage(m_d->viewportSize, QImage::Format_ARGB32);
 
-    QRegion exposedRegion = QRect(0, 0, width, height);
+    /**
+     * TODO: viewport rects should be cropped by the borders of
+     * the image, because it may be requested to read/write
+     * outside QImage and copyQImage will not chatch it
+     */
+    QRect newViewportRect = QRect(QPoint(0,0), m_d->viewportSize);
+    QRect oldViewportRect = newViewportRect.translated(-alignedOffset);
 
-    qint32 oldCanvasXOffset = oldDocumentOffset.x();
-    qint32 oldCanvasYOffset = oldDocumentOffset.y();
+    QRegion updateRegion = newViewportRect;
 
-    dbgRender << "w: " << width << ", h" << height << ", oldCanvasXOffset " << oldCanvasXOffset << ", oldCanvasYOffset " << oldCanvasYOffset
-    << ", new offset: " << documentOffset;
+    QRect savedArea = newViewportRect & oldViewportRect;
+    if(!savedArea.isEmpty()) {
+        copyQImage(-alignedOffset.x(), -alignedOffset.y(), &newImage, m_d->prescaledQImage);
+        updateRegion -= savedArea;
+    }
 
-    QImage image = QImage(width, height, QImage::Format_ARGB32);
-    QPainter gc(&image);
 
-    if (oldCanvasXOffset != m_d->documentOffset.x() || oldCanvasYOffset != m_d->documentOffset.y()) {
+    QPainter gc(&newImage);
+    QVector<QRect> rects = updateRegion.rects();
 
-        qint32 deltaX =  oldCanvasXOffset - m_d->documentOffset.x();
-        qint32 deltaY = oldCanvasYOffset - m_d->documentOffset.y();
-
-        dbgRender << "deltaX: " << deltaX << ", deltaY: " << deltaY;
-        if (qAbs(deltaX) < width && qAbs(deltaY) < height) {
-            dbgRender << "Copy old data";
-            copyQImage(deltaX, deltaY, &image, m_d->prescaledQImage);
-
-            dbgRender << "exposedRegion: " << exposedRegion;
-            dbgRender << "Preexistant data: " << QRegion(QRect(deltaX, deltaY, width , height));
-            exposedRegion -= QRegion(QRect(deltaX, deltaY, width , height));
-            dbgRender << "exposedRegion: " << exposedRegion;
-        }
-        dbgRender << "exposedRegion: " << exposedRegion;
-        if (!exposedRegion.isEmpty()) {
-
-            QVector<QRect> rects = exposedRegion.rects();
-
-            for (int i = 0; i < rects.count(); i++) {
-                QRect r = rects[i];
-                // Set the areas to empty. Who knows, there may be not
-                // enough image to draw in them.
-                gc.fillRect(r, QColor(0, 0, 0, 0));
-                dbgRender << "render on rect" << r;
-                /**
-                 * FIXME: It can happen that we will need access to KisImage here
-                 */
-                KisPPUpdateInfoSP info = getUpdateInformation(r, QRect());
-                drawUsingBackend(gc, info);
-            }
-        }
-        m_d->prescaledQImage = image;
-    } else {
-        // Don't do "anything" if this function is called while the offset hasn't changed
-        // because that usually happen when the scrollbars appears
-        dbgRender << "Document Offset Moved but without moving !";
+    foreach(QRect rect, rects) {
+        //gc.fillRect(r, QColor(0, 0, 0, 0));
+        KisPPUpdateInfoSP info = getUpdateInformation(rect, QRect());
+        drawUsingBackend(gc, info);
     }
+
+    m_d->prescaledQImage = newImage;
 }
 
 void KisPrescaledProjection::setImageSize(qint32 w, qint32 h)
 {
     m_d->projectionBackend->setImageSize(w, h);
 
-    m_d->imageSize = QSize(w, h);
+    QRect viewportRect = m_d->coordinatesConverter->imageToViewport(QRect(0, 0, w, h)).toAlignedRect();
+    viewportRect = viewportRect.intersected(QRect(QPoint(0, 0), m_d->viewportSize));
 
-    QRect vRect = viewRectFromImagePixels(QRect(0, 0, w, h)).toAlignedRect();
-    vRect = vRect.intersected(QRect(QPoint(0, 0), m_d->canvasSize));
-    if (!vRect.isEmpty()) {
-        preScale(vRect);
+    if (!viewportRect.isEmpty()) {
+        preScale(viewportRect);
     }
 }
 
@@ -273,7 +247,10 @@
     if (!dirtyImageRect.intersects(m_d->image->bounds()))
         return new KisPPUpdateInfo();
 
-    QRect rawViewRect = viewRectFromImagePixels(dirtyImageRect).toAlignedRect();
+
+    QRect rawViewRect =
+        m_d->coordinatesConverter->imageToViewport(dirtyImageRect).toAlignedRect();
+
     KisPPUpdateInfoSP info = getUpdateInformation(rawViewRect, dirtyImageRect);
 
     m_d->projectionBackend->updateCache(info);
@@ -294,8 +271,7 @@
 
 void KisPrescaledProjection::preScale()
 {
-    Q_ASSERT(m_d->canvasSize.isValid());
-    preScale(QRect(QPoint(0, 0), m_d->canvasSize));
+    preScale(QRect(QPoint(0, 0), m_d->viewportSize));
 }
 
 QRect KisPrescaledProjection::preScale(const QRect & rc)
@@ -322,64 +298,63 @@
     m_d->projectionBackend->setMonitorProfile(profile);
 }
 
-void KisPrescaledProjection::resizePrescaledImage(const QSize & newSize)
+
+void KisPrescaledProjection::updateViewportSize()
 {
+    QRectF imageRect = m_d->coordinatesConverter->imageRectInWidgetPixels();
+    QRectF canvasRect(QPointF(0,0), m_d->canvasSize);
+    QRectF minimalRect = imageRect & canvasRect;
 
-    QSize oldSize;
+    m_d->viewportSize = m_d->coordinatesConverter->widgetToViewport(minimalRect).toAlignedRect().size();
 
-    if (m_d->prescaledQImage.isNull()) {
-        oldSize = QSize(0, 0);
-    } else {
-        oldSize = m_d->prescaledQImage.size();
+    if (m_d->prescaledQImage.isNull() ||
+        m_d->prescaledQImage.size() != m_d->viewportSize) {
+
+        m_d->prescaledQImage = QImage(m_d->viewportSize, QImage::Format_ARGB32);
     }
+}
 
-    dbgRender << "KisPrescaledProjection::resizePrescaledImage from " << oldSize << " to " << newSize << endl;
+void KisPrescaledProjection::notifyZoomChanged()
+{
+    updateViewportSize();
+    preScale();
+}
 
-    QImage image = QImage(newSize, QImage::Format_ARGB32);
-//     m_d->prescaledQImage.fill( QColor( 255, 0, 0, 128 ).rgba() );
+void KisPrescaledProjection::notifyCanvasSizeChanged(const QSize &widgetSize)
+{
+    // 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
 
-// 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
-
-    m_d->prescaledQImage = image;
-    m_d->canvasSize = newSize;
+    m_d->canvasSize = widgetSize;
+    updateViewportSize();
     preScale();
 }
 
+
 KisPPUpdateInfoSP
 KisPrescaledProjection::getUpdateInformation(const QRect &viewportRect,
                                              const QRect &dirtyImageRect)
 {
-    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);
+    m_d->coordinatesConverter->imageScale(&info->scaleX, &info->scaleY);
 
-    // Get the KisImage resolution
-    qreal resX = m_d->image->xRes();
-    qreal resY = m_d->image->yRes();
-
-    // Compute the scale factors
-    info->scaleX = zoomX / resX;
-    info->scaleY = zoomY / resY;
-
     // save it for future
     info->dirtyImageRect = dirtyImageRect;
 
     // 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));
+    QRect croppedViewRect = viewportRect.intersected(QRect(QPoint(0, 0), m_d->viewportSize));
 
     // second, align this rect to the KisImage's pixels and pixels
     // of projection backend
-    info->imageRect = imageRectFromViewPortPixels(QRectF(croppedViewRect));
+    info->imageRect = m_d->coordinatesConverter->viewportToImage(QRectF(croppedViewRect)).toAlignedRect();
+    info->imageRect = info->imageRect & m_d->image->bounds();
+
     m_d->projectionBackend->alignSourceRect(info->imageRect, info->scaleX);
 
     // finally, compute the dirty rect of the canvas
-    info->viewportRect = viewRectFromImagePixels(info->imageRect);
+    info->viewportRect = m_d->coordinatesConverter->imageToViewport(info->imageRect);
 
     info->borderWidth = 0;
     if (SCALE_MORE_OR_EQUAL_TO(info->scaleX, info->scaleY, 1.0)) {
@@ -396,8 +371,6 @@
     }
 
     dbgRender << "#####################################";
-    dbgRender << ppVar(resX) << ppVar(resY);
-    dbgRender << ppVar(zoomX) << ppVar(zoomY);
     dbgRender << ppVar(info->scaleX) << ppVar(info->scaleY);
     dbgRender << ppVar(info->borderWidth) << ppVar(info->renderHints);
     dbgRender << ppVar(info->transfer);
@@ -432,47 +405,4 @@
     }
 }
 
-QRectF KisPrescaledProjection::viewRectFromImagePixels(const QRect& rc)
-{
-    Q_ASSERT(m_d->viewConverter);
-
-    qreal xRes = m_d->image->xRes();
-    qreal yRes = m_d->image->yRes();
-
-    QRectF imageRect(rc);
-
-    QRectF docRect;
-    docRect.setCoords(imageRect.left() / xRes, imageRect.top() / yRes,
-                      imageRect.right() / xRes, imageRect.bottom() / yRes);
-
-    QRectF viewRect = m_d->viewConverter->documentToView(docRect);
-    viewRect = viewRect.translated(-m_d->documentOffset);
-
-    QRectF viewRectInter = viewRect.intersected(QRectF(0, 0, m_d->canvasSize.width(), m_d->canvasSize.height()));
-
-    if (viewRect != viewRectInter && !viewRectInter.isEmpty()) {
-        dbgRender << "*** Bad alignment of viewport rect ***";
-        dbgRender << "* Calculated: " << viewRect;
-        dbgRender << "* Must be:    " << viewRectInter;
-        dbgRender << "*** ****************************** ***";
-    }
-
-    return viewRect;
-}
-
-QRect KisPrescaledProjection::imageRectFromViewPortPixels(const QRectF& viewportRect)
-{
-    QRectF intersectedRect = viewportRect;//.intersected(QRectF(0, 0, m_d->canvasSize.width(), m_d->canvasSize.height()));
-    QRectF translatedRect = intersectedRect.translated(m_d->documentOffset);
-    QRectF docRect = m_d->viewConverter->viewToDocument(translatedRect);
-
-    return m_d->image->documentToPixel(docRect).toAlignedRect().intersected(m_d->image->bounds());
-}
-
-
-void KisPrescaledProjection::updateDocumentOrigin(const QPoint& documentOrigin)
-{
-    m_d->documentOrigin = documentOrigin;
-}
-
 #include "kis_prescaled_projection.moc"
Index: krita/ui/canvas/kis_coordinates_converter.cpp
===================================================================
--- krita/ui/canvas/kis_coordinates_converter.cpp	(revision 0)
+++ krita/ui/canvas/kis_coordinates_converter.cpp	(revision 0)
@@ -0,0 +1,293 @@
+/*
+ *  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_coordinates_converter.h"
+
+#include <QPolygonF>
+#include <QTransform>
+#include <QSizeF>
+
+#include <KoViewConverter.h>
+
+#include <kis_config.h>
+#include <kis_image.h>
+
+
+struct KisCoordinatesConverter::Private {
+    KisImageWSP image;
+    QSize canvasWidgetSize;
+    KoViewConverter *viewConverter;
+    QPoint documentOffset;
+    QPoint documentOrigin;
+    QTransform postprocessingTransform;
+
+    QTransform imageToDocument;
+    QTransform documentToFlake;
+    QTransform flakeToPostprocessedFlake;
+    QTransform postprocessedFlakeToWidget;
+    QTransform widgetToViewport;
+};
+
+void KisCoordinatesConverter::recalculateTransformations() const
+{
+    if(!m_d->image) return;
+
+    m_d->imageToDocument = QTransform::fromScale(1 / m_d->image->xRes(),
+                                                 1 / m_d->image->yRes());
+
+    qreal zoomX, zoomY;
+    m_d->viewConverter->zoom(&zoomX, &zoomY);
+    m_d->documentToFlake = QTransform::fromScale(zoomX, zoomY);
+
+    // Make new coordinate system not go to negative values
+    QSizeF flakeSize = imageSizeInFlakePixels();
+    QPointF shift = -m_d->postprocessingTransform.mapRect(QRectF(QPointF(0,0), flakeSize)).topLeft();
+
+    m_d->flakeToPostprocessedFlake = m_d->postprocessingTransform
+        * QTransform::fromTranslate(shift.x(), shift.y());
+
+    m_d->postprocessedFlakeToWidget =
+        QTransform::fromTranslate(-m_d->documentOffset.x() + m_d->documentOrigin.x(),
+                                  -m_d->documentOffset.y() + m_d->documentOrigin.y());
+
+
+    QTransform reversedTransform = (m_d->flakeToPostprocessedFlake * m_d->postprocessedFlakeToWidget).inverted();
+
+    QRectF irect = imageRectInWidgetPixels();
+    QRectF wrect = QRectF(QPoint(0,0), m_d->canvasWidgetSize);
+
+    QRectF rrect = irect & wrect;
+
+    QRectF canvasBounds = reversedTransform.mapRect(rrect);
+    QPointF offset = canvasBounds.topLeft();
+
+    reversedTransform = reversedTransform * QTransform::fromTranslate(-offset.x(), -offset.y());
+
+    m_d->widgetToViewport = reversedTransform;
+
+    // qDebug() << "***********";
+    // qDebug() << ppVar(m_d->flakeToPostprocessedFlake);
+    // qDebug() << ppVar(m_d->postprocessedFlakeToWidget);
+    // qDebug() << ppVar(m_d->widgetToViewport);
+    // qDebug() << ppVar(documentOrigin());
+    // qDebug() << ppVar(irect) << ppVar(wrect) << ppVar(rrect);
+    // qDebug() << ppVar(canvasBounds);
+    // qDebug() << ppVar(m_d->widgetToViewport.mapRect(irect));
+}
+
+
+KisCoordinatesConverter::KisCoordinatesConverter(KoViewConverter *viewConverter)
+    : m_d(new Private)
+{
+    m_d->viewConverter = viewConverter;
+}
+
+KisCoordinatesConverter::~KisCoordinatesConverter()
+{
+    delete m_d;
+}
+
+void KisCoordinatesConverter::notifyZoomChanged()
+{
+    recalculateTransformations();
+}
+
+void KisCoordinatesConverter::setCanvasWidgetSize(QSize size)
+{
+    m_d->canvasWidgetSize = size;
+    recalculateTransformations();
+}
+
+void KisCoordinatesConverter::setImage(KisImageWSP image)
+{
+    m_d->image = image;
+    recalculateTransformations();
+}
+
+void KisCoordinatesConverter::setDocumentOrigin(const QPoint &origin)
+{
+    m_d->documentOrigin = origin;
+    recalculateTransformations();
+}
+
+void KisCoordinatesConverter::setDocumentOffset(const QPoint &offset)
+{
+    m_d->documentOffset = offset;
+    recalculateTransformations();
+}
+
+QPoint KisCoordinatesConverter::documentOrigin() const
+{
+    return m_d->documentOrigin;
+}
+
+QPoint KisCoordinatesConverter::documentOffset() const
+{
+    return m_d->documentOffset;
+}
+
+void KisCoordinatesConverter::setPostprocessingTransform(const QTransform &transform)
+{
+    m_d->postprocessingTransform = transform;
+    recalculateTransformations();
+}
+
+QTransform KisCoordinatesConverter::postprocessingTransform() const
+{
+    return m_d->postprocessingTransform;
+}
+
+
+// see a comment in header file
+DEFINE_TRANSFORM_METHODS(imageToDocument, documentToImage,
+                         m_d->imageToDocument);
+
+DEFINE_TRANSFORM_METHODS(flakeToWidget, widgetToFlake,
+                         m_d->flakeToPostprocessedFlake
+                         * m_d->postprocessedFlakeToWidget);
+
+DEFINE_TRANSFORM_METHODS(documentToWidget, widgetToDocument,
+                         m_d->documentToFlake * m_d->flakeToPostprocessedFlake
+                         * m_d->postprocessedFlakeToWidget);
+
+DEFINE_TRANSFORM_METHODS(widgetToViewport, viewportToWidget,
+                         m_d->widgetToViewport);
+
+DEFINE_TRANSFORM_METHODS(imageToViewport, viewportToImage,
+                         m_d->imageToDocument * m_d->documentToFlake
+                         * m_d->flakeToPostprocessedFlake * m_d->postprocessedFlakeToWidget
+                         * m_d->widgetToViewport);
+
+QTransform KisCoordinatesConverter::imageToWidgetTransform() const
+{
+    return m_d->imageToDocument * m_d->documentToFlake
+        * m_d->flakeToPostprocessedFlake * m_d->postprocessedFlakeToWidget;
+}
+
+QTransform KisCoordinatesConverter::documentToWidgetTransform() const
+{
+    return m_d->documentToFlake
+        * m_d->flakeToPostprocessedFlake * m_d->postprocessedFlakeToWidget;
+}
+
+QTransform KisCoordinatesConverter::flakeToWidgetTransform() const
+{
+    return m_d->flakeToPostprocessedFlake * m_d->postprocessedFlakeToWidget;
+}
+
+QTransform KisCoordinatesConverter::viewportToWidgetTransform() const
+{
+    return m_d->widgetToViewport.inverted();
+}
+
+void KisCoordinatesConverter::getQPainterCheckersInfo(QTransform *transform,
+                                                      QPointF *brushOrigin,
+                                                      QPolygonF *polygon) const
+{
+    KisConfig cfg;
+    if (cfg.scrollCheckers()) {
+        *transform = viewportToWidgetTransform();
+        *polygon = imageRectInViewportPixels();
+        *brushOrigin = imageToViewport(QPointF(0,0));
+    }
+    else {
+        *transform = QTransform();
+        *polygon = viewportToWidgetTransform().map(imageRectInViewportPixels());
+        *brushOrigin = QPoint(0,0);
+    }
+}
+
+void KisCoordinatesConverter::getOpenGLCheckersInfo(QTransform *textureTransform,
+                                                    QTransform *modelTransform,
+                                                    QRectF *textureRect,
+                                                    QRectF *modelRect) const
+{
+    KisConfig cfg;
+    QRectF viewportRect = imageRectInViewportPixels();
+
+    *textureTransform = cfg.scrollCheckers() ? QTransform() :
+        m_d->widgetToViewport.inverted() * m_d->postprocessedFlakeToWidget;
+
+    *modelTransform = viewportToWidgetTransform();;
+    *modelRect = viewportRect;
+    *textureRect = QRectF(0, 0, viewportRect.width(),viewportRect.height());
+}
+
+
+// these functions return a bounding rect if the canvas is rotated
+
+QRectF KisCoordinatesConverter::imageRectInWidgetPixels() const
+{
+    if(!m_d->image) return QRectF();
+    return viewportToWidget(imageToViewport(m_d->image->bounds()));
+}
+
+QRectF KisCoordinatesConverter::imageRectInViewportPixels() const
+{
+    if(!m_d->image) return QRectF();
+    return imageToViewport(m_d->image->bounds());
+}
+
+QSizeF KisCoordinatesConverter::imageSizeInFlakePixels() const
+{
+    if(!m_d->image) return QSizeF();
+
+    qreal scaleX, scaleY;
+    imageScale(&scaleX, &scaleY);
+    QSize imageSize = m_d->image->size();
+
+    return QSizeF(imageSize.width() * scaleX, imageSize.height() * scaleY);
+}
+
+QRectF KisCoordinatesConverter::widgetRectInFlakePixels() const
+{
+    // NOTE: this duplicates part of recalculateTransformations()
+    QTransform reversedTransform = (m_d->flakeToPostprocessedFlake * m_d->postprocessedFlakeToWidget).inverted();
+    return reversedTransform.mapRect(QRectF(QPoint(0,0), m_d->canvasWidgetSize));
+}
+
+QPoint KisCoordinatesConverter::shiftFromFlakeCenterPoint(const QPointF &pt) const
+{
+    QTransform transform = m_d->flakeToPostprocessedFlake * m_d->postprocessedFlakeToWidget;
+    QPointF newWidgetCenter = transform.map(pt);
+    QPointF oldWidgetCenter = QPointF(m_d->canvasWidgetSize.width() / 2, m_d->canvasWidgetSize.height() / 2);
+
+    return (newWidgetCenter - oldWidgetCenter).toPoint();
+}
+
+QPointF KisCoordinatesConverter::flakeCenterPoint() const
+{
+    QRectF widgetRect = widgetRectInFlakePixels();
+    return QPointF(widgetRect.left() + widgetRect.width() / 2,
+                   widgetRect.top() + widgetRect.height() / 2);
+}
+
+void KisCoordinatesConverter::imageScale(qreal *scaleX, qreal *scaleY) const
+{
+    // get the x and y zoom level of the canvas
+    qreal zoomX, zoomY;
+    m_d->viewConverter->zoom(&zoomX, &zoomY);
+
+    // Get the KisImage resolution
+    qreal resX = m_d->image->xRes();
+    qreal resY = m_d->image->yRes();
+
+    // Compute the scale factors
+    *scaleX = zoomX / resX;
+    *scaleY = zoomY / resY;
+}
Index: krita/ui/canvas/kis_update_info.h
===================================================================
--- krita/ui/canvas/kis_update_info.h	(revision 1163293)
+++ krita/ui/canvas/kis_update_info.h	(working copy)
@@ -53,8 +53,6 @@
 public:
     KisTextureTileUpdateInfoList tileList;
     QRect dirtyViewportRect();
-
-    QRectF viewportRect;
 };
 #endif /* HAVE_OPENGL */
 
Index: krita/ui/canvas/kis_canvas2.h
===================================================================
--- krita/ui/canvas/kis_canvas2.h	(revision 1163293)
+++ krita/ui/canvas/kis_canvas2.h	(working copy)
@@ -44,6 +44,7 @@
     OPENGL
 };
 
+class KisCoordinatesConverter;
 class KoViewConverter;
 
 /**
@@ -71,6 +72,8 @@
 
     void setCanvasWidget(QWidget * widget);
 
+    void notifyZoomChanged();
+
 public: // KoCanvasBase implementation
 
     bool canvasIsOpenGL();
@@ -104,6 +107,7 @@
 
     virtual void updateInputMethodInfo();
 
+    const KisCoordinatesConverter* coordinatesConverter() const;
     virtual const KoViewConverter *viewConverter() const;
 
     virtual QWidget* canvasWidget();
@@ -135,13 +139,13 @@
 
     bool usingHDRExposureProgram();
     /// @return true if the canvas image should be displayed in vertically mirrored mode
-    bool isCanvasMirrored();
     void addDecoration(KisCanvasDecoration* deco);
     KisCanvasDecoration* decoration(const QString& id);
 
 signals:
 
     void documentOriginChanged();
+    void scrollAreaSizeChanged();
 
     void imageChanged(KisImageWSP image);
 
@@ -167,11 +171,12 @@
     /// adjust the origin of the document
     void adjustOrigin();
 
-    /// documentOffset changed, now update the rulers in zoom manager
-    void updateRulers();
-
     /// slot for setting the mirroring
     void mirrorCanvas(bool mirror);
+    void rotateCanvas(qreal angle);
+    void rotateCanvasRight15();
+    void rotateCanvasLeft15();
+    void resetCanvasTransformations();
 
 private slots:
 
@@ -212,17 +217,11 @@
 private:
     Q_DISABLE_COPY(KisCanvas2);
 
+    void pan(QPoint shift);
     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
-     * the widget.
-     */
-    QRect viewRectFromDoc(const QRectF & docRect);
-
 private:
 
     class KisCanvas2Private;
Index: krita/ui/canvas/kis_prescaled_projection.h
===================================================================
--- krita/ui/canvas/kis_prescaled_projection.h	(revision 1163293)
+++ krita/ui/canvas/kis_prescaled_projection.h	(working copy)
@@ -32,8 +32,8 @@
 class QSize;
 class QPainter;
 
-class KoViewConverter;
 class KoColorProfile;
+class KisCoordinatesConverter;
 
 #include <kis_types.h>
 
@@ -88,19 +88,8 @@
      */
     QImage prescaledQImage() const;
 
-    /**
-     * Set the view converter, the object that is responsible for
-     * translating between image pixels, document points and view
-     * pixels, keeping track of zoom levels.
-     */
-    void setViewConverter(KoViewConverter * viewConverter);
+    void setCoordinatesConverter(KisCoordinatesConverter *coordinatesConverter);
 
-    /**
-     * Return the intersection of the widget size and the given rect
-     * in image pixels converted to widget pixels.
-     */
-    void updateDocumentOrigin(const QPoint &documentOrigin);
-
 public slots:
 
     /**
@@ -128,10 +117,8 @@
     /**
      * Called whenever the view widget needs to show a different part of
      * the document
-     *
-     * @param documentOffset the offset in widget pixels
      */
-    void documentOffsetMoved(const QPoint &documentOffset);
+    void viewportMoved(const QPointF &offset);
 
     /**
      * Called whenever the size of the KisImage changes
@@ -139,11 +126,13 @@
     void setImageSize(qint32 w, qint32 h);
 
     /**
-     * Resize the prescaled image. The size is given in canvas
-     * widget pixels.
+     * Checks whether it is needed to resize the prescaled image and
+     * updates it. The size is given in canvas widget pixels.
      */
-    void resizePrescaledImage(const QSize & newSize);
+    void notifyCanvasSizeChanged(const QSize &widgetSize);
 
+    void notifyZoomChanged();
+
     /**
      * Set the current monitor profile
      */
@@ -168,6 +157,8 @@
      */
     void initBackend(bool cacheKisImageAsQImage);
 
+    void updateViewportSize();
+
     /**
      * preScale and draw onto the scaled projection the specified rect,
      * in canvas view pixels.
@@ -197,24 +188,6 @@
      */
     void drawUsingBackend(QPainter &gc, KisPPUpdateInfoSP info);
 
-    /**
-     * Converts image pixels into widget pixels
-     * viewRect will always correspond to imageRect (no border
-     * adjustment is done)
-     * Note: caller should check whether viewRect is inside
-     *       canvas area himself
-     */
-    QRectF viewRectFromImagePixels(const QRect& imageRect);
-
-    /**
-     * Converts widget pixels into image pixels
-     * Note1: imageRect will always be adjusted to be inside
-     *        an image, so it _may not_ correspond viewportRect
-     * Note2: caller should check whether viewportRect is inside
-     *        canvas area himself before calling.
-     */
-    QRect imageRectFromViewPortPixels(const QRectF& viewportRect);
-
     struct Private;
     Private * const m_d;
 };
Index: krita/ui/canvas/kis_abstract_canvas_widget.h
===================================================================
--- krita/ui/canvas/kis_abstract_canvas_widget.h	(revision 1163293)
+++ krita/ui/canvas/kis_abstract_canvas_widget.h	(working copy)
@@ -42,19 +42,10 @@
 
     virtual KoToolProxy * toolProxy() = 0;
 
-    virtual void documentOffsetMoved(const QPoint &) = 0;
-
-    virtual QPoint documentOrigin() const = 0;
-
-    virtual void adjustOrigin() = 0;
-
     /**
      * Draw the specified decorations on the view.
      */
-    virtual void drawDecorations(QPainter & gc, bool tools,
-                                 const QPoint & documentOffset,
-                                 const QRect & clipRect,
-                                 KisCanvas2 * canvas) = 0;
+    virtual void drawDecorations(QPainter & gc, const QRect &updateWidgetRect) = 0;
 
     virtual void addDecoration(KisCanvasDecoration* deco) = 0;
     virtual KisCanvasDecoration* decoration(const QString& id) = 0;
Index: krita/ui/canvas/kis_coordinates_converter.h
===================================================================
--- krita/ui/canvas/kis_coordinates_converter.h	(revision 0)
+++ krita/ui/canvas/kis_coordinates_converter.h	(revision 0)
@@ -0,0 +1,132 @@
+/*
+ *  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_COORDINATES_CONVERTER_H
+#define KIS_COORDINATES_CONVERTER_H
+
+#include "krita_export.h"
+#include "kis_types.h"
+
+
+class QSize;
+class QSizeF;
+class QRectF;
+class QPoint;
+class QPolygonF;
+class QTransform;
+class KoViewConverter;
+
+
+/**
+ * Automatic generation of QRectF and QPointF transformation methods.
+ * They will use pre-generated QTransform objects to perform the change.
+ */
+#define DEFINE_RECT_METHOD(name,transform)                              \
+    QRectF KisCoordinatesConverter::name(const QRectF &rc) const        \
+    {                                                                   \
+        return (transform).mapRect(rc);                                 \
+    }
+
+#define DEFINE_POINT_METHOD(name,transform)                             \
+    QPointF KisCoordinatesConverter::name(const QPointF &pt) const      \
+    {                                                                   \
+        return (transform).map(pt);                                     \
+    }
+
+#define DECLARE_RECT_METHOD(name)               \
+    QRectF name(const QRectF &rc) const
+
+#define DECLARE_POINT_METHOD(name)              \
+    QPointF name(const QPointF &rc) const
+
+
+#define DEFINE_TRANSFORM_METHODS(name,invertedName,transform)         \
+    DEFINE_RECT_METHOD(name, transform);                              \
+    DEFINE_RECT_METHOD(invertedName, (transform).inverted());         \
+    DEFINE_POINT_METHOD(name, transform);                             \
+    DEFINE_POINT_METHOD(invertedName, (transform).inverted())         \
+
+#define DECLARE_TRANSFORM_METHODS(name,invertedName)                   \
+    DECLARE_RECT_METHOD(name);                                         \
+    DECLARE_RECT_METHOD(invertedName);                                 \
+    DECLARE_POINT_METHOD(name);                                        \
+    DECLARE_POINT_METHOD(invertedName)                                 \
+
+
+
+
+class KRITAUI_EXPORT KisCoordinatesConverter
+{
+public:
+    KisCoordinatesConverter(KoViewConverter *viewConverter);
+    ~KisCoordinatesConverter();
+
+    void notifyZoomChanged();
+    void setCanvasWidgetSize(QSize size);
+    void setImage(KisImageWSP image);
+    void setDocumentOrigin(const QPoint &origin);
+    void setDocumentOffset(const QPoint &offset);
+
+    QPoint documentOrigin() const;
+    QPoint documentOffset() const;
+
+    void setPostprocessingTransform(const QTransform &transform);
+    QTransform postprocessingTransform() const;
+
+
+    // Automatic methods generation. See a comment above.
+    DECLARE_TRANSFORM_METHODS(imageToViewport, viewportToImage);
+    DECLARE_TRANSFORM_METHODS(flakeToWidget, widgetToFlake);
+    DECLARE_TRANSFORM_METHODS(widgetToViewport, viewportToWidget);
+    DECLARE_TRANSFORM_METHODS(documentToWidget, widgetToDocument);
+    DECLARE_TRANSFORM_METHODS(imageToDocument, documentToImage);
+
+
+    QTransform imageToWidgetTransform() const;
+    QTransform documentToWidgetTransform() const;
+    QTransform flakeToWidgetTransform() const;
+    QTransform viewportToWidgetTransform() const;
+
+    void getQPainterCheckersInfo(QTransform *transform,
+                                 QPointF *brushOrigin,
+                                 QPolygonF *poligon) const;
+
+    void getOpenGLCheckersInfo(QTransform *textureTransform,
+                               QTransform *modelTransform,
+                               QRectF *textureRect,
+                               QRectF *modelRect) const;
+
+    QRectF imageRectInWidgetPixels() const;
+    QRectF imageRectInViewportPixels() const;
+    QSizeF imageSizeInFlakePixels() const;
+    QRectF widgetRectInFlakePixels() const;
+
+    QPoint shiftFromFlakeCenterPoint(const QPointF &pt) const;
+    QPointF flakeCenterPoint() const;
+
+    void imageScale(qreal *scaleX, qreal *scaleY) const;
+
+private:
+    void recalculateTransformations() const;
+
+private:
+    struct Private;
+    Private * const m_d;
+};
+
+#endif /* KIS_COORDINATES_CONVERTER_H */
Index: krita/ui/canvas/kis_canvas_widget_base.cpp
===================================================================
--- krita/ui/canvas/kis_canvas_widget_base.cpp	(revision 1163293)
+++ krita/ui/canvas/kis_canvas_widget_base.cpp	(working copy)
@@ -29,6 +29,8 @@
 #include <KoViewConverter.h>
 #include <KoToolProxy.h>
 
+
+#include "kis_coordinates_converter.h"
 #include "kis_canvas_decoration.h"
 #include "../kis_config.h"
 #include "kis_canvas2.h"
@@ -38,8 +40,9 @@
 class KisCanvasWidgetBase::Private
 {
 public:
-    Private(KisCanvas2 *newCanvas)
+    Private(KisCanvas2 *newCanvas, KisCoordinatesConverter *newCoordinatesConverter)
         : canvas(newCanvas)
+        , coordinatesConverter(newCoordinatesConverter)
         , viewConverter(newCanvas->viewConverter())
         , toolProxy(newCanvas->toolProxy())
         , ignorenextMouseEventExceptRightMiddleClick(0)
@@ -48,20 +51,17 @@
 
     QList<KisCanvasDecoration*> decorations;
     KisCanvas2 * canvas;
+    KisCoordinatesConverter *coordinatesConverter;
     const KoViewConverter * viewConverter;
     KoToolProxy * toolProxy;
-    /// the offset of the view in the document, expressed in the view reference (not in the document reference)
-    QPoint documentOffset;
-    /// the origin of the image rect
-    QPoint origin;
     QTimer blockMouseEvent;
     
     bool ignorenextMouseEventExceptRightMiddleClick; // HACK work around Qt bug not sending tablet right/dblclick http://bugreports.qt.nokia.com/browse/QTBUG-8598
     QColor borderColor;
 };
 
-KisCanvasWidgetBase::KisCanvasWidgetBase(KisCanvas2 * canvas)
-    : m_d(new Private(canvas))
+KisCanvasWidgetBase::KisCanvasWidgetBase(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter)
+    : m_d(new Private(canvas, coordinatesConverter))
 {
     m_d->blockMouseEvent.setSingleShot(true);
 }
@@ -71,39 +71,41 @@
     delete m_d;
 }
 
-void KisCanvasWidgetBase::drawDecorations(QPainter & gc, bool tools,
-                                          const QPoint & documentOffset,
-                                          const QRect & clipRect,
-                                          KisCanvas2 * canvas)
+void KisCanvasWidgetBase::drawDecorations(QPainter & gc, const QRect &updateWidgetRect)
 {
+    gc.save();
+
     // Setup the painter to take care of the offset; all that the
     // classes that do painting need to keep track of is resolution
     gc.setRenderHint(QPainter::Antialiasing);
     gc.setRenderHint(QPainter::TextAntialiasing);
-//     gc.setRenderHint(QPainter::HighQualityAntialiasing); // http://www.archivum.info/qt-interest@trolltech.com/2010-01/00481/Re:-(Qt-interest)-Is-QPainter::HighQualityAntialiasing-render-hint-broken-in-Qt-4.6.html <- this option does not do anything anymore with Qt4.6, so don't reenable it since it seems to break display
+
+    // This option does not do anything anymore with Qt4.6, so don't reenable it since it seems to break display
+    // http://www.archivum.info/qt-interest@trolltech.com/2010-01/00481/Re:-(Qt-interest)-Is-QPainter::HighQualityAntialiasing-render-hint-broken-in-Qt-4.6.html
+    // gc.setRenderHint(QPainter::HighQualityAntialiasing);
+
     gc.setRenderHint(QPainter::SmoothPixmapTransform);
-    gc.translate(QPoint(-documentOffset.x(), -documentOffset.y()));
-    gc.translate(documentOrigin());
 
-    // Paint the shapes (other than the layers)
+
     gc.save();
-    gc.setClipRect(clipRect);
-    canvas->globalShapeManager()->paint(gc, *canvas->viewConverter(), false);
+    gc.setClipRect(updateWidgetRect);
+
+    QTransform transform = m_d->coordinatesConverter->flakeToWidgetTransform();
+    gc.setTransform(transform);
+
+    // Paint the shapes (other than the layers)
+    m_d->canvas->globalShapeManager()->paint(gc, *m_d->viewConverter, false);
+
+    // some tools do not restore gc, but that is not important here
+    toolProxy()->paint(gc, *m_d->viewConverter);
     gc.restore();
 
     // ask the decorations to paint themselves
     foreach(KisCanvasDecoration* deco, m_d->decorations) {
-        gc.save();
-        deco->paint(gc, documentOffset, clipRect, *canvas->viewConverter());
-        gc.restore();
+        deco->paint(gc, m_d->coordinatesConverter->widgetToDocument(updateWidgetRect), m_d->coordinatesConverter);
     }
 
-    // Give the tool a chance to paint its stuff
-    if (tools) {
-        gc.save();
-        toolProxy()->paint(gc, *canvas->viewConverter());
-        gc.restore();
-    }
+    gc.restore();
 }
 
 void KisCanvasWidgetBase::addDecoration(KisCanvasDecoration* deco)
@@ -135,19 +137,25 @@
 {
     KisConfig cfg;
 
+    if(checkSize < 0)
+        checkSize = cfg.checkSize();
+
+    QColor checkColor = cfg.checkersColor();
+
     QImage tile(checkSize * 2, checkSize * 2, QImage::Format_RGB32);
     QPainter pt(&tile);
     pt.fillRect(tile.rect(), Qt::white);
-    pt.fillRect(0, 0, checkSize, checkSize, cfg.checkersColor());
-    pt.fillRect(checkSize, checkSize, checkSize, checkSize, cfg.checkersColor());
+    pt.fillRect(0, 0, checkSize, checkSize, checkColor);
+    pt.fillRect(checkSize, checkSize, checkSize, checkSize, checkColor);
     pt.end();
 
     return tile;
 }
 
-void KisCanvasWidgetBase::setBorderColor(const QColor& color)
+void KisCanvasWidgetBase::notifyConfigChanged()
 {
-    m_d->borderColor = color;
+    KisConfig cfg;
+    m_d->borderColor = cfg.canvasBorderColor();
 }
 
 QColor KisCanvasWidgetBase::borderColor() const
@@ -160,9 +168,9 @@
     return m_d->canvas;
 }
 
-const KoViewConverter *KisCanvasWidgetBase::viewConverter() const
+KisCoordinatesConverter* KisCanvasWidgetBase::coordinatesConverter()
 {
-    return m_d->viewConverter;
+    return m_d->coordinatesConverter;
 }
 
 KoToolProxy *KisCanvasWidgetBase::toolProxy()
@@ -170,104 +178,15 @@
     return m_d->toolProxy;
 }
 
-QPoint KisCanvasWidgetBase::documentOffset() const
-{
-    return m_d->documentOffset;
-}
-
-QPoint KisCanvasWidgetBase::documentOrigin() const
-{
-    return m_d->origin;
-}
-
-void KisCanvasWidgetBase::documentOffsetMoved(const QPoint & pt)
-{
-    m_d->documentOffset = pt;
-}
-
-QSize KisCanvasWidgetBase::documentSize() const
-{
-    KisImageWSP image = m_d->canvas->image();
-    return QSize(int(ceil(m_d->viewConverter->documentToViewX(image->width()  / image->xRes()))),
-                 int(ceil(m_d->viewConverter->documentToViewY(image->height() / image->yRes()))));
-}
-
-void KisCanvasWidgetBase::adjustOrigin()
-{
-    KisImageWSP image = m_d->canvas->image();
-    if (image == 0) return;
-
-    QRect documentRect = QRect(QPoint(0, 0), documentSize());
-
-    // save the old origin to see if it has changed
-    QPoint oldOrigin = m_d->origin;
-
-    // set the origin to the zoom document rect origin
-    m_d->origin = -documentRect.topLeft();
-
-    // the document bounding rect is always centered on the virtual canvas
-    // if there are margins left around the zoomed document rect then
-    // distribute them evenly on both sides
-    int widthDiff = widget()->size().width() - documentRect.width();
-    if (widthDiff > 0)
-        m_d->origin.rx() += qRound(0.5 * widthDiff);
-    int heightDiff = widget()->size().height() - documentRect.height();
-    if (heightDiff > 0)
-        m_d->origin.ry() += qRound(0.5 * heightDiff);
-
-    emitDocumentOriginChangedSignal();
-}
-
-
-QPointF KisCanvasWidgetBase::widgetToDocument(const QPointF& p) const
-{
-    if (m_d->canvas->isCanvasMirrored())
-    {   
-        return mirror(m_d->viewConverter->viewToDocument(widgetToView(p + m_d->documentOffset)));
-    }else{
-        return m_d->viewConverter->viewToDocument(widgetToView(p + m_d->documentOffset));
-    }
-}
-
-QPointF KisCanvasWidgetBase::mirror(const QPointF& pos) const
-{
-    QSizeF imageSize = m_d->viewConverter->viewToDocument(documentSize());
-    return QPointF(imageSize.width() - pos.x(), pos.y() );
-}
-
 QPointF KisCanvasWidgetBase::mouseEventWidgetToDocument(const QPoint& mousePosition) const
 {
     const qreal PIXEL_CENTRE_OFFSET = 0.5;
     const QPointF pixelCentre(mousePosition.x() + PIXEL_CENTRE_OFFSET,
                               mousePosition.y() + PIXEL_CENTRE_OFFSET);
-    return widgetToDocument(pixelCentre);
-}
 
-
-QPointF KisCanvasWidgetBase::widgetToView(const QPointF& p) const
-{
-    return p - m_d->origin;
+    return m_d->coordinatesConverter->widgetToDocument(pixelCentre);
 }
 
-
-QRect KisCanvasWidgetBase::widgetToView(const QRect& r) const
-{
-    return r.translated(- m_d->origin);
-}
-
-
-QPoint KisCanvasWidgetBase::viewToWidget(const QPoint& p) const
-{
-    return p + m_d->origin;
-}
-
-
-QRect KisCanvasWidgetBase::viewToWidget(const QRect& r) const
-{
-    return r.translated(m_d->origin);
-}
-
-
 void KisCanvasWidgetBase::processMouseMoveEvent(QMouseEvent *e)
 {
     if (m_d->ignorenextMouseEventExceptRightMiddleClick )
@@ -376,7 +295,7 @@
     m_d->blockMouseEvent.start(100);
 
     const QPointF pos = e->hiResGlobalPos() - widget()->mapToGlobal(QPoint(0, 0));
-    m_d->toolProxy->tabletEvent(e, widgetToDocument(pos));
+    m_d->toolProxy->tabletEvent(e, m_d->coordinatesConverter->widgetToDocument(pos));
 
     // HACK
     e->ignore();
@@ -386,7 +305,7 @@
 
 void KisCanvasWidgetBase::processWheelEvent(QWheelEvent *e)
 {
-    m_d->toolProxy->wheelEvent(e, widgetToDocument(e->pos()));
+    m_d->toolProxy->wheelEvent(e, m_d->coordinatesConverter->widgetToDocument(e->pos()));
 }
 
 
Index: krita/ui/canvas/kis_canvas_widget_base.h
===================================================================
--- krita/ui/canvas/kis_canvas_widget_base.h	(revision 1163293)
+++ krita/ui/canvas/kis_canvas_widget_base.h	(working copy)
@@ -37,11 +37,12 @@
 class QVariant;
 
 class KoViewConverter;
+class KisCoordinatesConverter;
 
 class KisCanvasWidgetBase : public KisAbstractCanvasWidget
 {
 public:
-    KisCanvasWidgetBase(KisCanvas2 * canvas);
+    KisCanvasWidgetBase(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter);
 
     virtual ~KisCanvasWidgetBase();
 
@@ -49,19 +50,11 @@
 
     virtual KoToolProxy * toolProxy();
 
-    virtual void documentOffsetMoved(const QPoint &);
-
-    virtual QPoint documentOrigin() const;
-
-    virtual void adjustOrigin();
-
     /**
      * Draw the specified decorations on the view.
      */
-    virtual void drawDecorations(QPainter & gc, bool tools,
-                                 const QPoint & documentOffset,
-                                 const QRect & clipRect,
-                                 KisCanvas2 * canvas);
+    virtual void drawDecorations(QPainter & gc, const QRect &updateWidgetRect);
+
     virtual void addDecoration(KisCanvasDecoration* deco);
     virtual KisCanvasDecoration* decoration(const QString& id);
 
@@ -69,42 +62,22 @@
     virtual QList<KisCanvasDecoration*> decorations();
 
     /**
-     * Returns the color of the border, i.e. the part of the canvas 
-     * outside the image contents. 
+     * Returns the color of the border, i.e. the part of the canvas
+     * outside the image contents.
      *
      */
     QColor borderColor() const;
-    void setBorderColor(const QColor& color);
 
-    
 protected:
     /**
      * Returns one check of the background checkerboard pattern.
-     *
-     * @param checkSize the size of the check
      */
-    QImage checkImage(qint32 checkSize);
+    QImage checkImage(qint32 checkSize = -1);
 
     KisCanvas2 *canvas() const;
 
-    const KoViewConverter *viewConverter() const;
+    KisCoordinatesConverter* coordinatesConverter();
 
-    QPoint documentOffset() const;
-
-    /// document size in widget pixels
-    QSize documentSize() const;
-
-    /// these methods take origin coordinate into account, basically it means (point - origin)
-    QPointF widgetToView(const QPointF& p) const;
-    QRect widgetToView(const QRect& r) const;
-    QPoint viewToWidget(const QPoint& p) const;
-    QRect viewToWidget(const QRect& r) const;
-
-    /// Convert widget coords to document, taking into account document origin and offset.
-    QPointF widgetToDocument(const QPointF& p) const;
-
-    QPointF mirror(const QPointF& pos) const;
-    
     /**
      * Convert a mouse event widget coordinate to a document 
      * coordinate, applying an offset to convert the integer 
@@ -130,9 +103,9 @@
     void processWheelEvent(QWheelEvent *e);
     QVariant processInputMethodQuery(Qt::InputMethodQuery query) const;
     void processInputMethodEvent(QInputMethodEvent *event);
+    void notifyConfigChanged();
 
     /// To be implemented by the derived canvas 
-    virtual void emitDocumentOriginChangedSignal() = 0;
     virtual bool callFocusNextPrevChild(bool next) = 0;
 
 private:
Index: krita/ui/canvas/kis_qpainter_canvas.cpp
===================================================================
--- krita/ui/canvas/kis_qpainter_canvas.cpp	(revision 1163293)
+++ krita/ui/canvas/kis_qpainter_canvas.cpp	(working copy)
@@ -40,6 +40,7 @@
 #include <KoColorSpace.h>
 #include <KoColorSpaceRegistry.h>
 #include <KoShapeManager.h>
+#include "kis_coordinates_converter.h"
 #include <KoZoomHandler.h>
 #include <KoToolManager.h>
 #include <KoToolProxy.h>
@@ -71,17 +72,11 @@
     QBrush checkBrush;
 };
 
-KisQPainterCanvas::KisQPainterCanvas(KisCanvas2 * canvas, QWidget * parent)
+KisQPainterCanvas::KisQPainterCanvas(KisCanvas2 *canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent)
         : QWidget(parent)
-        , KisCanvasWidgetBase(canvas)
+        , KisCanvasWidgetBase(canvas, coordinatesConverter)
         , m_d(new Private())
 {
-    // XXX: Reset pattern size and color when the properties change!
-
-    KisConfig cfg;
-
-    connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
-
     setAutoFillBackground(true);
     setAcceptDrops(true);
     setFocusPolicy(Qt::StrongFocus);
@@ -89,7 +84,8 @@
     setAttribute(Qt::WA_StaticContents);
     setAttribute(Qt::WA_OpaquePaintEvent);
 
-    m_d->checkBrush = QBrush(checkImage(cfg.checkSize()));
+    connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), SLOT(slotConfigChanged()));
+    slotConfigChanged();
 }
 
 KisQPainterCanvas::~KisQPainterCanvas()
@@ -122,63 +118,42 @@
     QPainter gc(this);
 #endif
 
+    KisCoordinatesConverter *converter = coordinatesConverter();
+    QTransform imageTransform = converter->viewportToWidgetTransform();
+
+    gc.save();
+
     gc.setCompositionMode(QPainter::CompositionMode_Source);
     gc.fillRect(QRect(QPoint(0, 0), size()), borderColor());
 
-    // Don't draw the checks if we draw a cached pixmap, because we
-    // need alpha transparency for checks. The precached pixmap
-    // already should contain checks.
-    QRect documentRect = QRect(QPoint(0, 0), documentSize());
-    QRect fillRect = documentRect.translated(documentOrigin());
-    if (!cfg.noXRender()) {
-        if (cfg.scrollCheckers()) {
-            if (documentOffset().x() > 0) {
-                fillRect.adjust(0, 0, documentOffset().x(), 0);
-            } else {
-                fillRect.adjust(documentOffset().x(), 0, 0, 0);
-            }
-            if (documentOffset().y() > 0) {
-                fillRect.adjust(0, 0, 0, documentOffset().y());
-            } else {
-                fillRect.adjust(0, documentOffset().y(), 0, 0);
-            }
-            gc.save();
-            gc.translate(-documentOffset());
-            gc.fillRect(fillRect, m_d->checkBrush);
-            gc.restore();
-        } else {
-            // Checks
-            gc.fillRect(fillRect, m_d->checkBrush);
-        }
-    }
+    QTransform checkersTransform;
+    QPointF brushOrigin;
+    QPolygonF polygon;
 
-    if(canvas()->image()->rootLayer()->childCount()>0) {
-        gc.setCompositionMode(QPainter::CompositionMode_SourceOver);
+    converter->getQPainterCheckersInfo(&checkersTransform, &brushOrigin, &polygon);
+    gc.setPen(Qt::NoPen);
+    gc.setBrush(m_d->checkBrush);
+    gc.setBrushOrigin(brushOrigin);
+    gc.setTransform(checkersTransform);
+    gc.drawPolygon(polygon);
 
-        gc.save();
-        if (canvas()->isCanvasMirrored()){
-            QTransform m = gc.transform();
-            m.translate( documentOrigin().x(), 0);
-            m.scale(-1,1 );
-            m.translate( -documentSize().width(),0 );
-            m.translate( -documentOrigin().x(), 0);
-            gc.setTransform(m);
-        }
-//            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()));
-//            }
-    }
+    gc.setTransform(imageTransform);
+    QRectF viewportRect = converter->widgetToViewport(ev->rect());
 
+    gc.setCompositionMode(QPainter::CompositionMode_SourceOver);
+    gc.drawImage(viewportRect, m_d->prescaledProjection->prescaledQImage(),
+                 viewportRect);
+
+    gc.restore();
+
+
 #ifdef DEBUG_REPAINT
     QColor color = QColor(random() % 255, random() % 255, random() % 255, 150);
     gc.fillRect(ev->rect(), color);
 #endif
-    drawDecorations(gc, true, documentOffset(), fillRect.translated(-documentOrigin()), canvas());
-    gc.restore();
+
+    QRect boundingRect = converter->imageRectInWidgetPixels().toAlignedRect();
+    drawDecorations(gc, boundingRect);
     gc.end();
 
 #ifdef INDEPENDENT_CANVAS
@@ -253,13 +228,6 @@
     processWheelEvent(e);
 }
 
-void KisQPainterCanvas::documentOffsetMoved(const QPoint & pt)
-{
-    KisCanvasWidgetBase::documentOffsetMoved(pt);
-    m_d->prescaledProjection->documentOffsetMoved(pt);
-    update();
-}
-
 void KisQPainterCanvas::resizeEvent(QResizeEvent *e)
 {
     QSize size(e->size());
@@ -269,28 +237,18 @@
     if (size.height() <= 0) {
         size.setHeight(1);
     }
-    m_d->prescaledProjection->resizePrescaledImage(size);
-    adjustOrigin();
+
+    coordinatesConverter()->setCanvasWidgetSize(size);
+    m_d->prescaledProjection->notifyCanvasSizeChanged(size);
+    emit needAdjustOrigin();
 }
 
 void KisQPainterCanvas::slotConfigChanged()
 {
-    KisConfig cfg;
-
-    /**
-     * FIXME: Please pay attention to these options:
-     *   - cfg.checkSize();
-     *   - cfg.checkersColor();
-     */
-
-    m_d->checkBrush = QBrush(checkImage(cfg.checkSize()));
+    m_d->checkBrush = QBrush(checkImage());
+    notifyConfigChanged();
 }
 
-void KisQPainterCanvas::emitDocumentOriginChangedSignal()
-{
-    emit documentOriginChanged(documentOrigin());
-}
-
 bool KisQPainterCanvas::callFocusNextPrevChild(bool next)
 {
     return focusNextPrevChild(next);
Index: krita/ui/canvas/kis_grid_manager.cpp
===================================================================
--- krita/ui/canvas/kis_grid_manager.cpp	(revision 1163293)
+++ krita/ui/canvas/kis_grid_manager.cpp	(working copy)
@@ -26,15 +26,16 @@
 #include <qgl.h>
 #endif
 
-#include <QRadioButton>
 
+#include <QTransform>
+
 #include <kaction.h>
 #include <ktoggleaction.h>
 #include <kactioncollection.h>
 #include <kdialog.h>
 #include <klocale.h>
 
-#include <KoViewConverter.h>
+#include "kis_coordinates_converter.h"
 
 #include "kis_config.h"
 #include "kis_grid_painter_configuration.h"
@@ -159,36 +160,34 @@
     m_view->canvas()->update();
 }
 
-#define pixelToView(point) \
-    converter.documentToView( m_view->document()->image()->pixelToDocument(point))
-
-#define viewToPixel(point) \
-    m_view->document()->image()->documentToPixel( converter.viewToDocument( point))
-
-void KisGridManager::drawDecoration(QPainter& gc, const QPoint& documentOffset, const QRect& area, const KoViewConverter &converter)
+void KisGridManager::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter)
 {
-    dbgRender << "drawDecoration";
     KisConfig cfg;
 
     quint32 offsetx = cfg.getGridOffsetX();
     quint32 offsety = cfg.getGridOffsetY();
-    quint32 hspacing = cfg.getGridHSpacing(); // m_doc->gridData().gridX(); // use koffice grid when KOffice grid is on par with Krita grid, and is configurable by whatever mean Krita has to manipulate the grid
-    quint32 vspacing = cfg.getGridVSpacing(); // m_doc->gridData().gridY(); // use koffice grid when KOffice grid is on par with Krita grid, and is configurable by whatever mean Krita has to manipulate the grid
+    quint32 hspacing = cfg.getGridHSpacing();
+    quint32 vspacing = cfg.getGridVSpacing();
     quint32 subdivision = cfg.getGridSubdivisions() - 1;
 
-    // Draw vertical line
     QPen mainPen = KisGridPainterConfiguration::mainPen();
     QPen subdivisionPen = KisGridPainterConfiguration::subdivisionPen();
-    quint32 i = subdivision - (offsetx / hspacing) % (subdivision + 1);
 
-    QPointF bottomRight = viewToPixel(area.bottomRight() + documentOffset);
-    QPointF topLeft = viewToPixel(area.topLeft() + documentOffset);
+    qreal x1, y1, x2, y2;
+    QRectF imageRect = converter->documentToImage(updateRect);
+    imageRect.getCoords(&x1, &y1, &x2, &y2);
 
-    dbgRender << "area = " << area << "topLeft = " << topLeft << " bottomRight = " << bottomRight << " documentOffset = " << documentOffset;
+    QTransform transform = converter->imageToWidgetTransform();
 
+    gc.save();
+    gc.setTransform(transform);
+
+    quint32 i;
+
+    // Draw vertical line
+    i = subdivision - (offsetx / hspacing) % (subdivision + 1);
     double x = offsetx % hspacing;
-    dbgRender << " x = " << x << " hspacing = " << hspacing;
-    while (x <= bottomRight.x()) {
+    while (x <= x2) {
         if (i == subdivision) {
             gc.setPen(mainPen);
             i = 0;
@@ -196,19 +195,18 @@
             gc.setPen(subdivisionPen);
             i++;
         }
-        if (x >= topLeft.x()) {
+        if (x >= x1) {
             // Always draw the full line otherwise the line stippling varies
             // with the location of area and we get glitchy patterns.
-            dbgRender << pixelToView(QPointF(x, topLeft.y())) << " " << pixelToView(QPointF(x, bottomRight.y())) << (bottomRight.y());
-            gc.drawLine(pixelToView(QPointF(x, topLeft.y())), pixelToView(QPointF(x, bottomRight.y())));
+            gc.drawLine(QPointF(x, y1),QPointF(x, y2));
         }
         x += hspacing;
     }
+
     // Draw horizontal line
     i = subdivision - (offsety / vspacing) % (subdivision + 1);
-    double y = offsety % vspacing;
-    dbgRender << " y = " << x << " vspacing = " << vspacing;
-    while (y <= bottomRight.y()) {
+    qreal y = offsety % vspacing;
+    while (y <= y2) {
         if (i == subdivision) {
             gc.setPen(mainPen);
             i = 0;
@@ -216,12 +214,13 @@
             gc.setPen(subdivisionPen);
             i++;
         }
-        if (y >= topLeft.y()) {
-            gc.drawLine(pixelToView(QPointF(topLeft.x(), y)), pixelToView(QPointF(bottomRight.x(), y)));
+        if (y >= y1) {
+            gc.drawLine(QPointF(x1, y), QPointF(x2, y));
         }
         y += vspacing;
     }
 
+    gc.restore();
 }
 
 #include "kis_grid_manager.moc"
Index: krita/ui/canvas/kis_qpainter_canvas.h
===================================================================
--- krita/ui/canvas/kis_qpainter_canvas.h	(revision 1163293)
+++ krita/ui/canvas/kis_qpainter_canvas.h	(working copy)
@@ -49,7 +49,7 @@
 
 public:
 
-    KisQPainterCanvas(KisCanvas2 * canvas, QWidget * parent);
+    KisQPainterCanvas(KisCanvas2 * canvas, KisCoordinatesConverter *coordinatesConverter, QWidget * parent);
 
     virtual ~KisQPainterCanvas();
 
@@ -109,23 +109,13 @@
         return this;
     }
 
-    void documentOffsetMoved(const QPoint &);
-
-signals:
-
-    /**
-     * This signal is emitted when the document origin has changed.
-     * The document origin is the point (in pixel) on the virtual
-     * canvas where the documents origin (0,0) or the top left
-     * corner of the page is. Copied from Karbon
-     */
-    void documentOriginChanged(const QPoint &origin);
-
 protected: // KisCanvasWidgetBase
 
-    virtual void emitDocumentOriginChangedSignal();
     virtual bool callFocusNextPrevChild(bool next);
 
+signals:
+    void needAdjustOrigin();
+
 private slots:
     void slotConfigChanged();
 
Index: krita/ui/canvas/kis_grid_manager.h
===================================================================
--- krita/ui/canvas/kis_grid_manager.h	(revision 1163293)
+++ krita/ui/canvas/kis_grid_manager.h	(working copy)
@@ -56,7 +56,7 @@
     void fastConfig20x20();
     void fastConfig40x40();
 protected:
-    virtual void drawDecoration(QPainter& gc, const QPoint& documentOffset, const QRect& area, const KoViewConverter &converter);
+    void drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter);
 
 private:
 
Index: krita/ui/canvas/kis_canvas_decoration.cc
===================================================================
--- krita/ui/canvas/kis_canvas_decoration.cc	(revision 1163293)
+++ krita/ui/canvas/kis_canvas_decoration.cc	(working copy)
@@ -66,12 +66,10 @@
     d->visible = !d->visible;
 }
 
-void KisCanvasDecoration::paint(QPainter& gc, const QPoint & documentOffset, const QRect& area, const KoViewConverter &converter)
+void KisCanvasDecoration::paint(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter *converter)
 {
-    if (visible()) {
-        //dbgKrita << "Decoration " << name() << " is visible";
-        drawDecoration(gc, documentOffset, area, converter);
-    }
+    if (visible())
+        drawDecoration(gc, updateArea, converter);
 }
 
 KisView2* KisCanvasDecoration::view() const
Index: krita/ui/canvas/kis_canvas_decoration.h
===================================================================
--- krita/ui/canvas/kis_canvas_decoration.h	(revision 1163293)
+++ krita/ui/canvas/kis_canvas_decoration.h	(working copy)
@@ -24,8 +24,10 @@
 
 class QPoint;
 class QRect;
+class QRectF;
 class QPainter;
 class KoViewConverter;
+class KisCoordinatesConverter;
 class KisView2;
 
 /**
@@ -44,12 +46,14 @@
      * @return whether the decoration is visible.
      */
     bool visible() const;
+
     /**
      * Will paint the decoration on the QPainter, if the visible is set to true.
      *
-     * @param documentOffset the offset of the view in the document, expressed in the view reference (not in the document reference)
+     * @param updateRect dirty rect in document pixels
      */
-    void paint(QPainter& gc, const QPoint & documentOffset, const QRect& area, const KoViewConverter &converter);
+    void paint(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter);
+
 public slots:
     /**
      * Set if the decoration is visible or not.
@@ -60,7 +64,8 @@
      */
     void toggleVisibility();
 protected:
-    virtual void drawDecoration(QPainter& gc, const QPoint & documentOffset, const QRect& area, const KoViewConverter &converter) = 0;
+    virtual void drawDecoration(QPainter& gc, const QRectF& updateArea, const KisCoordinatesConverter *converter) = 0;
+
     /**
      * @return the parent KisView
      */
Index: krita/ui/kis_painting_assistant.h
===================================================================
--- krita/ui/kis_painting_assistant.h	(revision 1163293)
+++ krita/ui/kis_painting_assistant.h	(working copy)
@@ -28,7 +28,7 @@
 class QPainter;
 class QRect;
 class QRectF;
-class KoViewConverter;
+class KisCoordinatesConverter;
 
 #include <kis_shared_ptr.h>
 #include <KoGenericRegistry.h>
@@ -77,7 +77,7 @@
      * @param point the coordinates in point in the document reference
      */
     virtual QPointF adjustPosition(const QPointF& point) const = 0;
-    virtual void drawAssistant(QPainter& gc, const QPoint& documentOffset,  const QRect& area, const KoViewConverter &converter) const = 0;
+    virtual void drawAssistant(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter) = 0;
     void replaceHandle(KisPaintingAssistantHandleSP _handle, KisPaintingAssistantHandleSP _with);
     const QList<KisPaintingAssistantHandleSP>& handles() const;
     QList<KisPaintingAssistantHandleSP> handles();
Index: krita/ui/tests/kis_coordinates_converter_test.cpp
===================================================================
--- krita/ui/tests/kis_coordinates_converter_test.cpp	(revision 0)
+++ krita/ui/tests/kis_coordinates_converter_test.cpp	(revision 0)
@@ -0,0 +1,325 @@
+/*
+ *  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_coordinates_converter_test.h"
+#include <QTest>
+
+#include <qtest_kde.h>
+
+#include <QTransform>
+
+#include <KoZoomHandler.h>
+#include <KoColorSpaceRegistry.h>
+#include <kis_image.h>
+
+#include "kis_coordinates_converter.h"
+
+
+void initImage(KisImageSP *image, KoZoomHandler **zoomHandler)
+{
+    const KoColorSpace *cs = KoColorSpaceRegistry::instance()->rgb8();
+    *image = new KisImage(0, 1000, 1000, cs, "projection test");
+    (*image)->setResolution(100, 100);
+
+    *zoomHandler = new KoZoomHandler();
+    (*zoomHandler)->setResolution(100, 100);
+}
+
+void KisCoordinatesConverterTest::testConversion()
+{
+    KisImageSP image;
+    KoZoomHandler *zoomHandler;
+    initImage(&image, &zoomHandler);
+
+    QRectF testRect(100,100,100,100);
+    KisCoordinatesConverter converter(zoomHandler);
+
+    converter.setImage(image);
+    converter.setDocumentOrigin(QPoint(10,10));
+    converter.setDocumentOffset(QPoint(30,30));
+    converter.setCanvasWidgetSize(QSize(500,500));
+
+    zoomHandler->setZoom(1.);
+    converter.notifyZoomChanged();
+
+    QCOMPARE(converter.imageToViewport(testRect), QRectF(80,80,100,100));
+    QCOMPARE(converter.viewportToImage(testRect), QRectF(120,120,100,100));
+
+    QCOMPARE(converter.widgetToViewport(testRect), QRectF(100,100,100,100));
+    QCOMPARE(converter.viewportToWidget(testRect), QRectF(100,100,100,100));
+
+    QCOMPARE(converter.widgetToDocument(testRect), QRectF(1.20,1.20,1,1));
+    QCOMPARE(converter.documentToWidget(testRect), QRectF(9980,9980,10000,10000));
+
+    QCOMPARE(converter.imageToDocument(testRect), QRectF(1,1,1,1));
+    QCOMPARE(converter.documentToImage(testRect), QRectF(10000,10000,10000,10000));
+
+    zoomHandler->setZoom(0.5);
+    converter.notifyZoomChanged();
+
+    QCOMPARE(converter.imageToViewport(testRect), QRectF(30,30,50,50));
+    QCOMPARE(converter.viewportToImage(testRect), QRectF(240,240,200,200));
+
+    QCOMPARE(converter.widgetToViewport(testRect), QRectF(100,100,100,100));
+    QCOMPARE(converter.viewportToWidget(testRect), QRectF(100,100,100,100));
+
+    QCOMPARE(converter.widgetToDocument(testRect), QRectF(2.4,2.4,2,2));
+    QCOMPARE(converter.documentToWidget(testRect), QRectF(4980,4980,5000,5000));
+
+    QCOMPARE(converter.imageToDocument(testRect), QRectF(1,1,1,1));
+    QCOMPARE(converter.documentToImage(testRect), QRectF(10000,10000,10000,10000));
+
+    delete zoomHandler;
+}
+
+void KisCoordinatesConverterTest::testImageCropping()
+{
+    KisImageSP image;
+    KoZoomHandler *zoomHandler;
+    initImage(&image, &zoomHandler);
+
+    KisCoordinatesConverter converter(zoomHandler);
+
+    converter.setImage(image);
+    converter.setDocumentOrigin(QPoint(0,0));
+    converter.setDocumentOffset(QPoint(0,0));
+    converter.setCanvasWidgetSize(QSize(500,500));
+
+    zoomHandler->setZoom(1.);
+    converter.notifyZoomChanged();
+
+    // we do NOT crop here
+    QCOMPARE(converter.viewportToImage(QRectF(900,900,200,200)),
+             QRectF(900,900,200,200));
+
+    delete zoomHandler;
+}
+
+#define CHECK_TRANSFORM(trans,test,ref) QCOMPARE(trans.map(test).boundingRect(), ref)
+//#define CHECK_TRANSFORM(trans,test,ref) qDebug() << trans.map(test).boundingRect()
+
+void KisCoordinatesConverterTest::testTransformations()
+{
+    KisImageSP image;
+    KoZoomHandler *zoomHandler;
+    initImage(&image, &zoomHandler);
+
+    KisCoordinatesConverter converter(zoomHandler);
+
+    converter.setImage(image);
+    converter.setDocumentOrigin(QPoint(10,20));
+    converter.setDocumentOffset(QPoint(30,50));
+    converter.setCanvasWidgetSize(QSize(500,500));
+
+    QRectF testRect(100,100,100,100);
+    QTransform imageToWidget;
+    QTransform documentToWidget;
+    QTransform flakeToWidget;
+    QTransform viewportToWidget;
+
+
+    zoomHandler->setZoom(1.);
+    converter.notifyZoomChanged();
+
+    imageToWidget = converter.imageToWidgetTransform();
+    documentToWidget = converter.documentToWidgetTransform();
+    flakeToWidget = converter.flakeToWidgetTransform();
+    viewportToWidget = converter.viewportToWidgetTransform();
+
+    CHECK_TRANSFORM(imageToWidget, testRect, QRectF(80,70,100,100));
+    CHECK_TRANSFORM(documentToWidget, testRect, QRectF(9980,9970,10000,10000));
+    CHECK_TRANSFORM(flakeToWidget, testRect, QRectF(80,70,100,100));
+    CHECK_TRANSFORM(viewportToWidget, testRect, QRectF(100,100,100,100));
+
+    zoomHandler->setZoom(0.5);
+    converter.notifyZoomChanged();
+
+    imageToWidget = converter.imageToWidgetTransform();
+    documentToWidget = converter.documentToWidgetTransform();
+    flakeToWidget = converter.flakeToWidgetTransform();
+    viewportToWidget = converter.viewportToWidgetTransform();
+
+    CHECK_TRANSFORM(imageToWidget, testRect, QRectF(30,20,50,50));
+    CHECK_TRANSFORM(documentToWidget, testRect, QRectF(4980,4970,5000,5000));
+    CHECK_TRANSFORM(flakeToWidget, testRect, QRectF(80,70,100,100));
+    CHECK_TRANSFORM(viewportToWidget, testRect, QRectF(100,100,100,100));
+
+    delete zoomHandler;
+}
+
+void KisCoordinatesConverterTest::testConsistency()
+{
+    KisImageSP image;
+    KoZoomHandler *zoomHandler;
+    initImage(&image, &zoomHandler);
+
+    KisCoordinatesConverter converter(zoomHandler);
+
+    converter.setImage(image);
+    converter.setDocumentOrigin(QPoint(10,20));
+    converter.setDocumentOffset(QPoint(30,50));
+    converter.setCanvasWidgetSize(QSize(500,500));
+
+    QRectF testRect(100,100,100,100);
+    QTransform imageToWidget;
+    QTransform documentToWidget;
+    QTransform viewportToWidget;
+
+    zoomHandler->setZoom(0.5);
+    converter.notifyZoomChanged();
+
+    imageToWidget = converter.imageToWidgetTransform();
+    documentToWidget = converter.documentToWidgetTransform();
+    viewportToWidget = converter.viewportToWidgetTransform();
+
+    QRectF fromImage = converter.viewportToWidget(converter.imageToViewport(testRect));
+    QRectF fromDocument = converter.documentToWidget(testRect);
+    QRectF fromViewport = converter.viewportToWidget(testRect);
+
+    CHECK_TRANSFORM(imageToWidget, testRect, fromImage);
+    CHECK_TRANSFORM(documentToWidget, testRect, fromDocument);
+    CHECK_TRANSFORM(viewportToWidget, testRect, fromViewport);
+
+    delete zoomHandler;
+}
+
+void KisCoordinatesConverterTest::testRotation()
+{
+    KisImageSP image;
+    KoZoomHandler *zoomHandler;
+    initImage(&image, &zoomHandler);
+
+    KisCoordinatesConverter converter(zoomHandler);
+
+    QSize widgetSize(1000,500);
+    QRectF testRect(800, 100, 300, 300);
+
+    converter.setImage(image);
+    converter.setDocumentOrigin(QPoint(0,0));
+    converter.setDocumentOffset(QPoint(0,0));
+    converter.setCanvasWidgetSize(widgetSize);
+
+    QTransform postprocessingTransform;
+    postprocessingTransform.rotate(30);
+    converter.setPostprocessingTransform(postprocessingTransform);
+
+    zoomHandler->setZoom(1.);
+    converter.notifyZoomChanged();
+
+    QTransform viewportToWidget = converter.viewportToWidgetTransform();
+
+    QRectF boundingRect = viewportToWidget.mapRect(testRect);
+    QRectF directRect = converter.viewportToWidget(testRect);
+
+    QCOMPARE(boundingRect, directRect);
+
+    QRectF referenceRect(QPointF(742.82,53.5898), QSizeF(409.808,409.808));
+
+#define FUZZY(a,b) ((a)-(b) < 0.01)
+
+    QVERIFY(FUZZY(boundingRect.top(), referenceRect.top()));
+    QVERIFY(FUZZY(boundingRect.left(), referenceRect.left()));
+    QVERIFY(FUZZY(boundingRect.width(), referenceRect.width()));
+    QVERIFY(FUZZY(boundingRect.height(), referenceRect.height()));
+
+    delete zoomHandler;
+}
+
+void KisCoordinatesConverterTest::testMirroring()
+{
+    KisImageSP image;
+    KoZoomHandler *zoomHandler;
+    initImage(&image, &zoomHandler);
+
+    KisCoordinatesConverter converter(zoomHandler);
+
+    QSize widgetSize(500,400);
+    QSize flakeSize(1000,1000);
+    QRectF testRect(300, 100, 200, 200);
+
+    converter.setImage(image);
+    converter.setDocumentOrigin(QPoint(0,0));
+    converter.setDocumentOffset(QPoint(200,100));
+    converter.setCanvasWidgetSize(widgetSize);
+
+    QTransform postprocessingTransform;
+    postprocessingTransform.scale(-1,1);
+    converter.setPostprocessingTransform(postprocessingTransform);
+
+    QTransform imageToWidget;
+    QTransform documentToWidget;
+    QTransform flakeToWidget;
+    QTransform viewportToWidget;
+
+    zoomHandler->setZoom(1.);
+    converter.notifyZoomChanged();
+
+    // image pixels == flake pixels
+
+    QRectF viewportRect = converter.imageToViewport(testRect);
+    QRectF widgetRect = converter.viewportToWidget(viewportRect);
+
+    QCOMPARE(widgetRect, QRectF(300,0,200,200));
+    QCOMPARE(viewportRect, QRectF(0,0,200,200));
+
+    QRectF roundTrip = converter.viewportToWidget(widgetRect);
+    QCOMPARE(roundTrip, viewportRect);
+}
+
+void KisCoordinatesConverterTest::testMirroringCanvasBiggerThanImage()
+{
+    KisImageSP image;
+    KoZoomHandler *zoomHandler;
+    initImage(&image, &zoomHandler);
+
+    KisCoordinatesConverter converter(zoomHandler);
+
+    QSize widgetSize(2000,2000);
+    QSize flakeSize(1000,1000);
+    QRectF testRect(300, 100, 200, 200);
+
+    converter.setImage(image);
+    converter.setDocumentOrigin(QPoint(50,50));
+    converter.setDocumentOffset(QPoint(0,0));
+    converter.setCanvasWidgetSize(widgetSize);
+
+    QTransform postprocessingTransform;
+    postprocessingTransform.scale(-1,1);
+    converter.setPostprocessingTransform(postprocessingTransform);
+
+    QTransform imageToWidget;
+    QTransform documentToWidget;
+    QTransform flakeToWidget;
+    QTransform viewportToWidget;
+
+    zoomHandler->setZoom(1.);
+    converter.notifyZoomChanged();
+
+    // image pixels == flake pixels
+
+    QRectF viewportRect = converter.imageToViewport(testRect);
+    QRectF widgetRect = converter.viewportToWidget(viewportRect);
+
+    QCOMPARE(widgetRect, QRectF(550,150,200,200));
+    QCOMPARE(viewportRect, QRectF(300,100,200,200));
+}
+
+
+QTEST_KDEMAIN(KisCoordinatesConverterTest, GUI)
+#include "kis_coordinates_converter_test.moc"
+
Index: krita/ui/tests/kis_image_view_converter_test.cpp
===================================================================
--- krita/ui/tests/kis_image_view_converter_test.cpp	(revision 1163293)
+++ krita/ui/tests/kis_image_view_converter_test.cpp	(working copy)
@@ -34,7 +34,7 @@
 void KisImageViewConverterTest::testDocumentToView()
 {
     const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
-    KisImageWSP image = new KisImage(0, 512, 512, colorSpace, "test");
+    KisImageSP image = new KisImage(0, 512, 512, colorSpace, "test");
     KisImageViewConverter viewConverter(image);
 
     image->setResolution(1.38888888, 1.38888888);
@@ -64,7 +64,7 @@
 void KisImageViewConverterTest::testViewToDocument()
 {
     const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
-    KisImageWSP image = new KisImage(0, 512, 512, colorSpace, "test");
+    KisImageSP image = new KisImage(0, 512, 512, colorSpace, "test");
     KisImageViewConverter viewConverter(image);
 
     image->setResolution(1.38888888, 1.38888888);
@@ -94,7 +94,7 @@
 void KisImageViewConverterTest::testZoom()
 {
     const KoColorSpace * colorSpace = KoColorSpaceRegistry::instance()->rgb8();
-    KisImageWSP image = new KisImage(0, 512, 512, colorSpace, "test");
+    KisImageSP image = new KisImage(0, 512, 512, colorSpace, "test");
     KisImageViewConverter viewConverter(image);
 
     image->setResolution(1.38888888, 5.38888888);
Index: krita/ui/tests/kis_prescaled_projection_test.h
===================================================================
--- krita/ui/tests/kis_prescaled_projection_test.h	(revision 1163293)
+++ krita/ui/tests/kis_prescaled_projection_test.h	(working copy)
@@ -43,8 +43,6 @@
 
     void testCreation();
 
-    void testCoordinateConversionRoundTrip();
-
     // Doesn't fail yet, but at least writes out several versions
     // of a scaled image. Make them compare with the results when
     // we're done and have everything okay for regressions
@@ -53,6 +51,8 @@
     void testScalingUndeferredSmoothing();
 
     void benchmarkUpdate();
+
+    void testScaling();
 };
 
 #endif
Index: krita/ui/tests/kis_shape_selection_test.cpp
===================================================================
--- krita/ui/tests/kis_shape_selection_test.cpp	(revision 1163293)
+++ krita/ui/tests/kis_shape_selection_test.cpp	(working copy)
@@ -35,7 +35,7 @@
 void KisShapeSelectionTest::testAddChild()
 {
     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
-    KisImageWSP image = new KisImage(new KisUndoAdapter(0), 300, 300, cs, "test");
+    KisImageSP image = new KisImage(new KisUndoAdapter(0), 300, 300, cs, "test");
 
     KisSelectionSP selection = new KisSelection();
     QVERIFY(selection->hasPixelSelection() == false);
Index: krita/ui/tests/kis_node_model_test.cpp
===================================================================
--- krita/ui/tests/kis_node_model_test.cpp	(revision 1163293)
+++ krita/ui/tests/kis_node_model_test.cpp	(working copy)
@@ -54,7 +54,7 @@
 */
 void kisnodemodel_test::testRowcount()
 {
-    KisImageWSP image = new KisImage(0, 100, 100,  KoColorSpaceRegistry::instance()->rgb8(), "testimage");
+    KisImageSP image = new KisImage(0, 100, 100,  KoColorSpaceRegistry::instance()->rgb8(), "testimage");
 
     KisNodeModel model(0);
     ModelTest(&model, this);
@@ -102,7 +102,7 @@
 */
 void kisnodemodel_test::testModelIndex()
 {
-    KisImageWSP image = new KisImage(0, 100, 100,  KoColorSpaceRegistry::instance()->rgb8(), "testimage");
+    KisImageSP image = new KisImage(0, 100, 100,  KoColorSpaceRegistry::instance()->rgb8(), "testimage");
 
     KisNodeModel model(0);
     ModelTest(&model, this);
@@ -180,7 +180,7 @@
 
 void kisnodemodel_test::testGroupLayers()
 {
-    KisImageWSP image = new KisImage(0, 100, 100,  KoColorSpaceRegistry::instance()->rgb8(), "testimage");
+    KisImageSP image = new KisImage(0, 100, 100,  KoColorSpaceRegistry::instance()->rgb8(), "testimage");
 
     KisNodeModel model(0);
     ModelTest(&model, this);
Index: krita/ui/tests/CMakeLists.txt
===================================================================
--- krita/ui/tests/CMakeLists.txt	(revision 1163293)
+++ krita/ui/tests/CMakeLists.txt	(working copy)
@@ -95,3 +95,9 @@
 set(kis_doc2_test_SRCS kis_doc2_test.cpp )
 kde4_add_unit_test(KisDoc2Test TESTNAME krita-ui-KisDoc2Test  ${kis_doc2_test_SRCS})
 target_link_libraries(KisDoc2Test  ${KDE4_KDEUI_LIBS}  komain kritaui kritaimage ${QT_QTTEST_LIBRARY})
+
+########### next target ###############
+
+set(kis_coordinates_converter_test_SRCS kis_coordinates_converter_test.cpp )
+kde4_add_unit_test(KisCoordinatesConverterTest TESTNAME krita-ui-KisCoordinatesConverterTest  ${kis_coordinates_converter_test_SRCS})
+target_link_libraries(KisCoordinatesConverterTest  ${KDE4_KDEUI_LIBS}  komain kritaui kritaimage ${QT_QTTEST_LIBRARY})
Index: krita/ui/tests/kis_prescaled_projection_test.cpp
===================================================================
--- krita/ui/tests/kis_prescaled_projection_test.cpp	(revision 1163293)
+++ krita/ui/tests/kis_prescaled_projection_test.cpp	(working copy)
@@ -37,15 +37,17 @@
 #include <kis_paint_layer.h>
 #include <kis_group_layer.h>
 
+#include "canvas/kis_coordinates_converter.h"
 #include "canvas/kis_prescaled_projection.h"
 
+#include "../../sdk/tests/testutil.h"
 
 bool KisPrescaledProjectionTest::testProjectionScenario(KisPrescaledProjection & projection,
         KoZoomHandler * viewConverter,
         const QString & name)
 {
 
-    projection.resizePrescaledImage(QSize(1000, 1000));
+    projection.notifyCanvasSizeChanged(QSize(1000, 1000));
     projection.prescaledQImage().save(name + "_prescaled_projection_01.png");
 
     viewConverter->setZoom(0.5);
@@ -88,57 +90,57 @@
     projection.preScale();
     projection.prescaledQImage().save(name + "_prescaled_projection_07.png");
 
-    projection.documentOffsetMoved(QPoint(50, 50));
+    projection.viewportMoved(QPoint(50, 50));
     projection.prescaledQImage().save(name + "_prescaled_projection_08.png");
 
-    projection.documentOffsetMoved(QPoint(100, 100));
+    projection.viewportMoved(QPoint(100, 100));
     projection.prescaledQImage().save(name + "_prescaled_projection_081.png");
 
-    projection.documentOffsetMoved(QPoint(200, 200));
+    projection.viewportMoved(QPoint(200, 200));
     projection.prescaledQImage().save(name + "_prescaled_projection_082.png");
 
-    projection.documentOffsetMoved(QPoint(250, 250));
+    projection.viewportMoved(QPoint(250, 250));
     projection.prescaledQImage().save(name + "_prescaled_projection_083.png");
 
-    projection.documentOffsetMoved(QPoint(150, 200));
+    projection.viewportMoved(QPoint(150, 200));
     projection.prescaledQImage().save(name + "_prescaled_projection_084.png");
 
-    projection.documentOffsetMoved(QPoint(100, 200));
+    projection.viewportMoved(QPoint(100, 200));
     projection.prescaledQImage().save(name + "_prescaled_projection_085.png");
 
-    projection.documentOffsetMoved(QPoint(50, 200));
+    projection.viewportMoved(QPoint(50, 200));
     projection.prescaledQImage().save(name + "_prescaled_projection_086.png");
 
-    projection.documentOffsetMoved(QPoint(0, 200));
+    projection.viewportMoved(QPoint(0, 200));
     projection.prescaledQImage().save(name + "_prescaled_projection_087.png");
 
-    projection.resizePrescaledImage(QSize(750, 750));
+    projection.notifyCanvasSizeChanged(QSize(750, 750));
     projection.prescaledQImage().save(name + "_prescaled_projection_09.png");
 
     viewConverter->setZoom(1.0);
     projection.preScale();
     projection.prescaledQImage().save(name + "_prescaled_projection_10.png");
 
-    projection.resizePrescaledImage(QSize(350, 350));
+    projection.notifyCanvasSizeChanged(QSize(350, 350));
     projection.prescaledQImage().save(name + "_prescaled_projection_11.png");
 
-    projection.documentOffsetMoved(QPoint(100, 100));
+    projection.viewportMoved(QPoint(100, 100));
     projection.prescaledQImage().save(name + "_prescaled_projection_12.png");
 
     viewConverter->setZoom(0.75);
     projection.preScale();
     projection.prescaledQImage().save(name + "_prescaled_projection_13.png");
 
-    projection.documentOffsetMoved(QPoint(10, 10));
+    projection.viewportMoved(QPoint(10, 10));
     projection.prescaledQImage().save(name + "_prescaled_projection_14.png");
 
-    projection.documentOffsetMoved(QPoint(0, 0));
+    projection.viewportMoved(QPoint(0, 0));
     projection.prescaledQImage().save(name + "_prescaled_projection_15.png");
 
-    projection.documentOffsetMoved(QPoint(10, 10));
+    projection.viewportMoved(QPoint(10, 10));
     projection.prescaledQImage().save(name + "_prescaled_projection_16.png");
 
-    projection.documentOffsetMoved(QPoint(30, 50));
+    projection.viewportMoved(QPoint(30, 50));
     projection.prescaledQImage().save(name + "_prescaled_projection_17.png");
 
     return true;
@@ -153,37 +155,6 @@
     delete prescaledProjection;
 }
 
-
-void KisPrescaledProjectionTest::testCoordinateConversionRoundTrip()
-{
-    KisPrescaledProjection projection;
-
-    const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
-    KisImageWSP image = new KisImage(0, 100, 100, cs, "projection test");
-    image->setResolution(300, 300);
-
-    KoZoomHandler * viewConverter = new KoZoomHandler();
-    viewConverter->setResolution(120, 120);
-
-    projection.setViewConverter(viewConverter);
-    projection.setImage(image);
-    projection.resizePrescaledImage(QSize(100, 100));
-
-    QRect viewRect = projection.viewRectFromImagePixels(QRect(0, 0, 100, 100)).toAlignedRect();
-
-    QCOMPARE(viewRect, QRect(0, 0, 40, 40));
-
-    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 = projection.viewRectFromImagePixels(imageRect).toAlignedRect();
-    QCOMPARE(viewRect3, viewRect);
-}
-
-
 void KisPrescaledProjectionTest::testScalingUndeferredSmoothingPixelForPixel()
 {
     // Set up a nice image
@@ -191,7 +162,7 @@
 
     // Undo adapter not necessary
     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
-    KisImageWSP image = new KisImage(0, qimage.width(), qimage.height(), cs, "projection test");
+    KisImageSP image = new KisImage(0, qimage.width(), qimage.height(), cs, "projection test");
 
     // 300 dpi recalculated to pixels per point (of which there are 72
     // to the inch)
@@ -203,7 +174,11 @@
 
     KisPrescaledProjection projection;
     KoZoomHandler * viewConverter = new KoZoomHandler();
-    projection.setViewConverter(viewConverter);
+
+    KisCoordinatesConverter converter(viewConverter);
+    converter.setImage(image);
+    projection.setCoordinatesConverter(&converter);
+
     projection.setImage(image);
 
     // pixel-for-pixel, at 100% zoom
@@ -221,19 +196,23 @@
 
     // Undo adapter not necessary
     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
-    KisImageWSP image = new KisImage(0, qimage.width(), qimage.height(), cs, "projection test");
+    KisImageSP image = new KisImage(0, qimage.width(), qimage.height(), cs, "projection test");
 
     // 300 dpi recalculated to pixels per point (of which there are 72
     // to the inch)
-    image->setResolution(300 / 72 , 300 / 72);
+    image->setResolution(100, 100);
 
     KisPaintLayerSP layer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8, cs);
     image->addNode(layer.data(), image->rootLayer(), 0);
     layer->paintDevice()->convertFromQImage(qimage, "");
 
     KisPrescaledProjection projection;
-    KoZoomHandler * viewConverter = new KoZoomHandler();
-    projection.setViewConverter(viewConverter);
+    KoZoomHandler *viewConverter = new KoZoomHandler();
+
+    KisCoordinatesConverter converter(viewConverter);
+    converter.setImage(image);
+    projection.setCoordinatesConverter(&converter);
+
     projection.setImage(image);
 
     testProjectionScenario(projection, viewConverter, "120dpi");
@@ -248,7 +227,7 @@
     QRect imageRect = QRect(QPoint(0,0), referenceImage.size());
 
     const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
-    KisImageWSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "projection test");
+    KisImageSP image = new KisImage(0, imageRect.width(), imageRect.height(), cs, "projection test");
 
     // set up 300dpi
     image->setResolution(300 / 72 , 300 / 72);
@@ -261,7 +240,11 @@
 
     KoZoomHandler * viewConverter = new KoZoomHandler();
     KisPrescaledProjection projection;
-    projection.setViewConverter(viewConverter);
+
+    KisCoordinatesConverter converter(viewConverter);
+    converter.setImage(image);
+    projection.setCoordinatesConverter(&converter);
+
     projection.setImage(image);
 
     // Emulate "Use same aspect as pixels"
@@ -293,6 +276,58 @@
 
 }
 
+
+void KisPrescaledProjectionTest::testScaling()
+{
+    QImage sourceImage(QString(FILES_DATA_DIR) + QDir::separator() + "lena.png");
+
+    const KoColorSpace * cs = KoColorSpaceRegistry::instance()->rgb8();
+    KisImageSP image = new KisImage(0, sourceImage.width(), sourceImage.height(), cs, "projection test");
+    image->setResolution(100, 100);
+
+    KisPaintLayerSP layer = new KisPaintLayer(image, "test", OPACITY_OPAQUE_U8, cs);
+    layer->paintDevice()->convertFromQImage(sourceImage, "");
+
+    image->addNode(layer, image->rootLayer(), 0);
+
+
+    KoZoomHandler *zoomHandler = new KoZoomHandler();
+    zoomHandler->setResolution(100, 100);
+    zoomHandler->setZoom(1.);
+
+    KisCoordinatesConverter converter(zoomHandler);
+    converter.setImage(image);
+    converter.setCanvasWidgetSize(QSize(100,100));
+
+    KisPrescaledProjection projection;
+    projection.setCoordinatesConverter(&converter);
+    projection.setImage(image);
+
+    converter.setDocumentOffset(QPoint(100,100));
+    converter.setDocumentOrigin(QPoint(0,0));
+
+
+    projection.notifyCanvasSizeChanged(QSize(100,100));
+    projection.notifyZoomChanged();
+
+    QImage result = projection.prescaledQImage();
+    QImage reference = sourceImage.copy(QRect(100,100,100,100));
+
+    QPoint pt;
+    QVERIFY(TestUtil::compareQImages(pt, result, reference));
+
+
+    // Test scrolling
+    converter.setDocumentOffset(QPoint(150,150));
+    projection.viewportMoved(QPoint(50,50));
+
+    result = projection.prescaledQImage();
+    reference = sourceImage.copy(QRect(150,150,100,100));
+
+    QVERIFY(TestUtil::compareQImages(pt, result, reference));
+}
+
+
 QTEST_KDEMAIN(KisPrescaledProjectionTest, GUI)
 
 #include "kis_prescaled_projection_test.moc"
Index: krita/ui/tests/kis_shape_controller_test.cpp
===================================================================
--- krita/ui/tests/kis_shape_controller_test.cpp	(revision 1163293)
+++ krita/ui/tests/kis_shape_controller_test.cpp	(working copy)
@@ -47,7 +47,7 @@
     KisNameServer nameServer;
     KisShapeController shapeController(&doc, &nameServer);
 
-    KisImageWSP image = new KisImage(0, 512, 512, 0, "shape controller test");
+    KisImageSP image = new KisImage(0, 512, 512, 0, "shape controller test");
     QCOMPARE((int)image->rootLayer()->childCount(), 0);
 
     KisLayerSP layer = new KisPaintLayer(image, "test1", OPACITY_OPAQUE_U8);
@@ -69,7 +69,7 @@
     KisNameServer nameServer;
     KisShapeController shapeController(&doc, &nameServer);
 
-    KisImageWSP image = new KisImage(0, 512, 512, 0, "shape controller test");
+    KisImageSP image = new KisImage(0, 512, 512, 0, "shape controller test");
     QCOMPARE((int)image->rootLayer()->childCount(), 0);
 
     KisLayerSP layer = new KisPaintLayer(image, "test1", OPACITY_OPAQUE_U8);
@@ -107,7 +107,7 @@
     KisNameServer nameServer;
     KisShapeController shapeController(&doc, &nameServer);
 
-    KisImageWSP image = new KisImage(0, 512, 512, 0, "shape controller test");
+    KisImageSP image = new KisImage(0, 512, 512, 0, "shape controller test");
     QCOMPARE((int)image->rootLayer()->childCount(), 0);
 
     KisLayerSP layer = new KisPaintLayer(image, "test1", OPACITY_OPAQUE_U8);
Index: krita/ui/tests/kis_coordinates_converter_test.h
===================================================================
--- krita/ui/tests/kis_coordinates_converter_test.h	(revision 0)
+++ krita/ui/tests/kis_coordinates_converter_test.h	(revision 0)
@@ -0,0 +1,39 @@
+/*
+ *  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_COORDINATES_CONVERTER_TEST_H
+#define KIS_COORDINATES_CONVERTER_TEST_H
+
+#include <QtTest/QtTest>
+
+class KisCoordinatesConverterTest : public QObject
+{
+    Q_OBJECT
+
+private slots:
+    void testConversion();
+    void testImageCropping();
+    void testTransformations();
+    void testConsistency();
+    void testRotation();
+    void testMirroring();
+    void testMirroringCanvasBiggerThanImage();
+};
+
+#endif /* KIS_COORDINATES_CONVERTER_TEST_H */
+
Index: krita/ui/kis_painting_assistants_manager.cc
===================================================================
--- krita/ui/kis_painting_assistants_manager.cc	(revision 1163293)
+++ krita/ui/kis_painting_assistants_manager.cc	(working copy)
@@ -90,11 +90,11 @@
     d->updateAction();
 }
 
-void KisPaintingAssistantsManager::drawDecoration(QPainter& gc, const QPoint& documentOffset,  const QRect& area, const KoViewConverter &converter)
+void KisPaintingAssistantsManager::drawDecoration(QPainter& gc, const QRectF& updateRect, const KisCoordinatesConverter *converter)
 {
     foreach(KisPaintingAssistant* assistant, d->assistants) {
         gc.save();
-        assistant->drawAssistant(gc, documentOffset, area, converter);
+        assistant->drawAssistant(gc, updateRect, converter);
         gc.restore();
     }
 }
Index: krita/ui/CMakeLists.txt
===================================================================
--- krita/ui/CMakeLists.txt	(revision 1163293)
+++ krita/ui/CMakeLists.txt	(working copy)
@@ -21,6 +21,7 @@
     canvas/kis_canvas_widget_base.cpp 
     canvas/kis_canvas2.cpp 
     canvas/kis_canvas_decoration.cc
+    canvas/kis_coordinates_converter.cpp 
     canvas/kis_grid_manager.cpp 
     canvas/kis_grid_painter_configuration.cpp
     canvas/kis_perspective_grid_manager.cpp  
Index: krita/ui/tool/kis_tool_freehand.cc
===================================================================
--- krita/ui/tool/kis_tool_freehand.cc	(revision 1163293)
+++ krita/ui/tool/kis_tool_freehand.cc	(working copy)
@@ -655,7 +655,7 @@
 void KisToolFreehand::initPan(KoPointerEvent *event)
 {
     m_mode = PAN;
-    m_lastPosition = documentToViewport(event->point);
+    m_lastPosition = convertDocumentToWidget(event->point);
     event->accept();
     useCursor(QCursor(Qt::ClosedHandCursor));
 }
@@ -670,7 +670,7 @@
         return;
     event->accept();
 
-    QPointF actualPosition = documentToViewport(event->point);
+    QPointF actualPosition = convertDocumentToWidget(event->point);
     QPointF distance(m_lastPosition - actualPosition);
     canvas()->canvasController()->pan(distance.toPoint());
 
@@ -692,16 +692,6 @@
     event->ignore();
 }
 
-
-QPointF KisToolFreehand::documentToViewport(const QPointF &p)
-{
-    QPointF viewportPoint = canvas()->viewConverter()->documentToView(p);
-    viewportPoint += canvas()->documentOrigin();
-    viewportPoint += QPoint(canvas()->canvasController()->canvasOffsetX(),
-                            canvas()->canvasController()->canvasOffsetY());
-    return viewportPoint;
-}
-
 void KisToolFreehand::increaseBrushSize()
 {
     currentPaintOpPreset()->settings()->changePaintOpSize(1, 0);
Index: krita/ui/tool/kis_tool_freehand.h
===================================================================
--- krita/ui/tool/kis_tool_freehand.h	(revision 1163293)
+++ krita/ui/tool/kis_tool_freehand.h	(working copy)
@@ -150,7 +150,6 @@
     QThreadPool* m_executor;
 
     // for panning
-    QPointF documentToViewport(const QPointF &p);
     QPointF m_lastPosition;
     QTime m_strokeTimeMeasure;
 
Index: krita/ui/tool/kis_tool.cc
===================================================================
--- krita/ui/tool/kis_tool.cc	(revision 1163293)
+++ krita/ui/tool/kis_tool.cc	(working copy)
@@ -63,6 +63,7 @@
 
 #include "kis_canvas_resource_provider.h"
 #include "canvas/kis_canvas2.h"
+#include "kis_coordinates_converter.h"
 #include "filter/kis_filter_configuration.h"
 #include "kis_config.h"
 #include "kis_config_notifier.h"
@@ -183,6 +184,23 @@
     };
 }
 
+QPointF KisTool::widgetCenterInWidgetPixels()
+{
+    KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
+    Q_ASSERT(kritaCanvas);
+
+    const KisCoordinatesConverter *converter = kritaCanvas->coordinatesConverter();
+    return converter->flakeToWidget(converter->flakeCenterPoint());
+}
+
+QPointF KisTool::convertDocumentToWidget(const QPointF& pt)
+{
+    KisCanvas2 *kritaCanvas = dynamic_cast<KisCanvas2*>(canvas());
+    Q_ASSERT(kritaCanvas);
+
+    return kritaCanvas->coordinatesConverter()->documentToWidget(pt);
+}
+
 QPointF KisTool::convertToPixelCoord(KoPointerEvent *e)
 {
     if (!image())
@@ -527,6 +545,7 @@
 
     if (canvasWidget) {
         canvasWidget->beginOpenGL();
+        canvasWidget->setupFlakeToWidgetTransformation();
     }
 #endif
 }
Index: krita/ui/tool/kis_tool.h
===================================================================
--- krita/ui/tool/kis_tool.h	(revision 1163293)
+++ krita/ui/tool/kis_tool.h	(working copy)
@@ -88,6 +88,9 @@
     /// reimplemented from superclass
     virtual void mouseDoubleClickEvent(KoPointerEvent *) {}  // when a krita tool is enabled, don't push double click on
 
+    QPointF widgetCenterInWidgetPixels();
+    QPointF convertDocumentToWidget(const QPointF& pt);
+
     /// Convert from native (postscript points) to image pixel
     /// coordinates.
     QPointF convertToPixelCoord(KoPointerEvent *e);
Index: libs/flake/KoToolRegistry.h
===================================================================
--- libs/flake/KoToolRegistry.h	(revision 1163293)
+++ libs/flake/KoToolRegistry.h	(working copy)
@@ -53,6 +53,7 @@
     KoToolRegistry(const KoToolRegistry&);
     KoToolRegistry operator=(const KoToolRegistry&);
     void init();
+    void registerGenericTools();
 
     class Private;
     Private *d;
Index: libs/flake/KoToolManager.cpp
===================================================================
--- libs/flake/KoToolManager.cpp	(revision 1163293)
+++ libs/flake/KoToolManager.cpp	(working copy)
@@ -27,10 +27,6 @@
 #include "KoToolProxy.h"
 #include "KoToolProxy_p.h"
 #include "KoSelection.h"
-#include "tools/KoCreatePathToolFactory.h"
-#include "tools/KoCreateShapesToolFactory.h"
-#include "tools/KoCreateShapesTool.h"
-#include "tools/KoPathToolFactory.h"
 #include "KoCanvasController.h"
 #include "KoCanvasControllerWidget.h"
 #include "KoShape.h"
@@ -41,10 +37,9 @@
 #include "KoInputDeviceHandlerRegistry.h"
 #include "KoInputDeviceHandlerEvent.h"
 #include "KoPointerEvent.h"
+#include "tools/KoCreateShapesTool.h"
 #include "tools/KoZoomTool.h"
-#include "tools/KoZoomToolFactory.h"
 #include "tools/KoPanTool.h"
-#include "tools/KoPanToolFactory.h"
 
 // Qt + kde
 #include <QWidget>
@@ -163,12 +158,6 @@
     if (tools.size() > 0)
         return;
 
-    tools.append(new ToolHelper(new KoCreatePathToolFactory(q)));
-    tools.append(new ToolHelper(new KoCreateShapesToolFactory(q)));
-    tools.append(new ToolHelper(new KoPathToolFactory(q)));
-    tools.append(new ToolHelper(new KoZoomToolFactory(q)));
-    tools.append(new ToolHelper(new KoPanToolFactory(q)));
-
     KoShapeRegistry::instance();
     KoToolRegistry *registry = KoToolRegistry::instance();
     foreach(const QString & id, registry->keys()) {
Index: libs/flake/KoToolRegistry.cpp
===================================================================
--- libs/flake/KoToolRegistry.cpp	(revision 1163293)
+++ libs/flake/KoToolRegistry.cpp	(working copy)
@@ -21,6 +21,15 @@
 #include "KoToolRegistry.h"
 #include <KGlobal>
 
+#include "tools/KoCreatePathToolFactory.h"
+#include "tools/KoCreateShapesToolFactory.h"
+#include "tools/KoCreateShapesTool.h"
+#include "tools/KoPathToolFactory.h"
+#include "tools/KoZoomTool.h"
+#include "tools/KoZoomToolFactory.h"
+#include "tools/KoPanTool.h"
+#include "tools/KoPanToolFactory.h"
+
 #include <KoPluginLoader.h>
 
 KoToolRegistry::KoToolRegistry()
@@ -39,6 +48,7 @@
     config.blacklist = "ToolPluginsDisabled";
     KoPluginLoader::instance()->load(QString::fromLatin1("KOffice/Tool"),
                                      QString::fromLatin1("[X-Flake-MinVersion] <= 0"));
+    registerGenericTools();
 }
 
 KoToolRegistry::~KoToolRegistry()
@@ -54,4 +64,13 @@
     return s_instance;
 }
 
+void KoToolRegistry::registerGenericTools()
+{
+    add(new KoCreatePathToolFactory(this));
+    add(new KoCreateShapesToolFactory(this));
+    add(new KoPathToolFactory(this));
+    add(new KoZoomToolFactory(this));
+    add(new KoPanToolFactory(this));
+}
+
 #include <KoToolRegistry.moc>
