← Back to team overview

ubuntu-touch-coreapps-reviewers team mailing list archive

[Merge] lp:~mrqtros/ubuntu-docviewer-app/ubuntu-docviewer-app-async-lok-loading into lp:ubuntu-docviewer-app/reboot

 

Roman Shchekin has proposed merging lp:~mrqtros/ubuntu-docviewer-app/ubuntu-docviewer-app-async-lok-loading into lp:ubuntu-docviewer-app/reboot.

Commit message:
Async lok loading 

Requested reviews:
  Ubuntu Document Viewer Developers (ubuntu-docviewer-dev)
Related bugs:
  Bug #1495069 in Ubuntu Document Viewer App: "Load LibreOffice program asynchronously"
  https://bugs.launchpad.net/ubuntu-docviewer-app/+bug/1495069

For more details, see:
https://code.launchpad.net/~mrqtros/ubuntu-docviewer-app/ubuntu-docviewer-app-async-lok-loading/+merge/271737

Async lok loading (Must be merged after https://code.launchpad.net/~mrqtros/ubuntu-docviewer-app/ubuntu-docviewer-app-render-engine/+merge/271733)
-- 
Your team Ubuntu Document Viewer Developers is requested to review the proposed merge of lp:~mrqtros/ubuntu-docviewer-app/ubuntu-docviewer-app-async-lok-loading into lp:ubuntu-docviewer-app/reboot.
=== modified file 'po/com.ubuntu.docviewer.pot'
--- po/com.ubuntu.docviewer.pot	2015-09-16 19:13:16 +0000
+++ po/com.ubuntu.docviewer.pot	2015-09-19 14:21:26 +0000
@@ -8,7 +8,7 @@
 msgstr ""
 "Project-Id-Version: \n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2015-09-09 19:15+0200\n"
+"POT-Creation-Date: 2015-09-19 17:18+0300\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@xxxxxx>\n"
@@ -33,7 +33,7 @@
 msgstr ""
 
 #: ../src/app/docviewer-application.cpp:171
-#: /tmp/build-reboot-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_15_04_vivid-Default/po/com.ubuntu.docviewer.desktop.in.in.h:1
+#: /home/qtros/dev/build-ubuntu-docviewer-app-async-lok-loading-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:1
 msgid "Document Viewer"
 msgstr ""
 
@@ -323,13 +323,6 @@
 msgid "Reverse order"
 msgstr ""
 
-#. TRANSLATORS: the first argument (%1) refers to the page currently shown on the screen,
-#. while the second one (%2) refers to the total pages count.
-#: ../src/app/qml/loView/LOView.qml:34 ../src/app/qml/pdfView/PdfView.qml:34
-#, qt-format
-msgid "Page %1 of %2"
-msgstr ""
-
 #: ../src/app/qml/loView/LOViewDefaultHeader.qml:57
 msgid "LibreOffice text document"
 msgstr ""
@@ -397,6 +390,13 @@
 msgid "Hide table of contents"
 msgstr ""
 
+#. TRANSLATORS: the first argument (%1) refers to the page currently shown on the screen,
+#. while the second one (%2) refers to the total pages count.
+#: ../src/app/qml/pdfView/PdfView.qml:34
+#, qt-format
+msgid "Page %1 of %2"
+msgstr ""
+
 #: ../src/app/qml/textView/TextView.qml:42
 msgid "Loading..."
 msgstr ""
@@ -411,6 +411,6 @@
 msgid "Open"
 msgstr ""
 
-#: /tmp/build-reboot-UbuntuSDK_for_armhf_GCC_ubuntu_sdk_15_04_vivid-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2
+#: /home/qtros/dev/build-ubuntu-docviewer-app-async-lok-loading-Desktop-Default/po/com.ubuntu.docviewer.desktop.in.in.h:2
 msgid "documents;viewer;pdf;reader;"
 msgstr ""

=== modified file 'src/app/qml/common/loadComponent.js'
--- src/app/qml/common/loadComponent.js	2015-07-14 01:35:59 +0000
+++ src/app/qml/common/loadComponent.js	2015-09-19 14:21:26 +0000
@@ -35,7 +35,7 @@
             || mimetype === "application/msword"
             || mimetype === "application/vnd.ms-excel"
             || mimetype === "application/vnd.ms-powerpoint")
-        qmlToLoad = Qt.resolvedUrl("../loView/LOView.qml")
+        qmlToLoad = Qt.resolvedUrl("../loView/LOViewPage.qml")
 
     if (qmlToLoad != "") {
        pageStack.push(qmlToLoad);

=== removed file 'src/app/qml/loView/LOView.qml'
--- src/app/qml/loView/LOView.qml	2015-07-04 16:00:33 +0000
+++ src/app/qml/loView/LOView.qml	1970-01-01 00:00:00 +0000
@@ -1,82 +0,0 @@
-/*
- * Copyright (C) 2013-2015 Canonical, Ltd.
- *
- * 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; version 3.
- *
- * 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, see <http://www.gnu.org/licenses/>.
- */
-
-import QtQuick 2.3
-import Ubuntu.Components 1.1
-import DocumentViewer.LibreOffice 1.0 as LO
-
-import "../common/utils.js" as Utils
-import "../upstreamComponents"
-
-Page {
-    id: loPage
-    title: Utils.getNameOfFile(file.path);
-
-    // Disable header auto-hide.
-    // TODO: Show/hide header if a user taps the page
-    flickable: null
-
-    // TRANSLATORS: the first argument (%1) refers to the page currently shown on the screen,
-    // while the second one (%2) refers to the total pages count.
-    property string currentPage: i18n.tr("Page %1 of %2").arg(loView.currentPageIndex + 1).arg(loView.count)
-
-    // Reset night mode shader settings when closing the page
-    // Component.onDestruction: mainView.nightModeEnabled = false
-
-    LO.Viewer {
-        id: loView
-        objectName: "loView"
-        anchors.fill: parent
-
-        clip: true
-        document: loDocument
-
-        Component.onCompleted: {
-            // WORKAROUND: Fix for wrong grid unit size
-            flickDeceleration = 1500 * units.gridUnit / 8
-            maximumFlickVelocity = 2500 * units.gridUnit / 8
-        }
-    }
-
-    Scrollbar { flickableItem: loView }
-    Scrollbar { flickableItem: loView; align: Qt.AlignBottom }
-
-    LO.Document {
-        id: loDocument
-
-        property bool isLoading: true
-        path: file.path
-
-       /* onPagesLoaded: {
-            isLoading = false;
-
-            var title = getDocumentInfo("Title")
-            if (title !== "")
-                loPage.title = title;
-
-            // Hide header when the document is ready
-            mainView.setHeaderVisibility(false);
-        }*/
-    }
-
-    // *** HEADER ***
-    state: "default"
-    states: LOViewDefaultHeader {
-        name: "default"
-        targetPage: loPage
-        //activityRunning: loView.currentPageItem.status == Image.Loading || loDocument.isLoading
-    }
-}

=== added file 'src/app/qml/loView/LOViewPage.qml'
--- src/app/qml/loView/LOViewPage.qml	1970-01-01 00:00:00 +0000
+++ src/app/qml/loView/LOViewPage.qml	2015-09-19 14:21:26 +0000
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2013-2015 Canonical, Ltd.
+ *
+ * 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; version 3.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ */
+
+import QtQuick 2.3
+import Ubuntu.Components 1.1
+import DocumentViewer.LibreOffice 1.0 as LO
+
+import "../common/utils.js" as Utils
+import "../upstreamComponents"
+
+Page {
+    id: loPage
+    title: Utils.getNameOfFile(file.path);
+
+    // Disable header auto-hide.
+    flickable: null
+
+    Loader {
+        id: contentLoader
+
+        asynchronous: true
+        anchors.fill: parent
+        sourceComponent: loPageContentComponent
+    }
+
+    ActivityIndicator {
+        running: contentLoader.status != Loader.Ready
+        visible: running
+        anchors.centerIn: parent
+    }
+
+    Component {
+        id: loPageContentComponent
+
+        Item {
+            property alias loDocument: loView.document
+
+            LO.Viewer {
+                id: loView
+                objectName: "loView"
+                anchors.fill: parent
+
+                clip: true
+                documentPath: file.path
+
+                Component.onCompleted: {
+                    // WORKAROUND: Fix for wrong grid unit size
+                    flickDeceleration = 1500 * units.gridUnit / 8
+                    maximumFlickVelocity = 2500 * units.gridUnit / 8
+                }
+            }
+
+            Scrollbar { flickableItem: loView }
+            Scrollbar { flickableItem: loView; align: Qt.AlignBottom }
+
+            // *** HEADER ***
+            state: "default"
+            states: LOViewDefaultHeader {
+                name: "default"
+                targetPage: loPage
+            }
+        }
+    }
+}

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/CMakeLists.txt'
--- src/plugin/libreofficetoolkit-qml-plugin/CMakeLists.txt	2015-09-10 12:22:49 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/CMakeLists.txt	2015-09-19 14:21:26 +0000
@@ -17,6 +17,7 @@
     lodocument.cpp
     loview.cpp
     sgtileitem.cpp
+    renderengine.cpp
     ${QML_SRCS}
 )
 

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/lodocument.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/lodocument.cpp	2015-09-02 11:31:45 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/lodocument.cpp	2015-09-19 14:21:26 +0000
@@ -48,7 +48,7 @@
 }
 
 // Set the path of the document, then it tries to load it.
-void LODocument::setPath(QString &pathName)
+void LODocument::setPath(const QString& pathName)
 {
     if (pathName.isEmpty())
         return;
@@ -61,7 +61,7 @@
 }
 
 // Load the document
-bool LODocument::loadDocument(QString &pathName)
+bool LODocument::loadDocument(const QString &pathName)
 {
     qDebug() << "Loading document...";
 
@@ -106,7 +106,7 @@
 
 // Paint a tile, with size=canvasSize, of the part of the document defined by
 // the rect tileSize.
-QImage LODocument::paintTile(QSize canvasSize, QRect tileSize)
+QImage LODocument::paintTile(const QSize& canvasSize, const QRect& tileSize)
 {
     QImage result = QImage(canvasSize.width(), canvasSize.height(),  QImage::Format_RGB32);
 
@@ -152,4 +152,7 @@
 LODocument::~LODocument()
 {
     delete m_document;
+#ifdef DEBUG_VERBOSE
+    qDebug() << " ---- ~LODocument";
+#endif
 }

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/lodocument.h'
--- src/plugin/libreofficetoolkit-qml-plugin/lodocument.h	2015-07-23 01:05:20 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/lodocument.h	2015-09-19 14:21:26 +0000
@@ -47,12 +47,12 @@
     };
 
     QString path() const;
-    void setPath(QString &pathName);
+    void setPath(const QString& pathName);
 
     DocumentType documentType() const;
 
     QSize documentSize() const;
-    QImage paintTile(QSize canvasSize, QRect tileSize);
+    QImage paintTile(const QSize& canvasSize, const QRect& tileSize);
 
     Q_INVOKABLE bool saveAs(QString url, QString format, QString filterOptions);
 
@@ -64,7 +64,7 @@
     QString m_path;
     DocumentType m_docType;
 
-    bool loadDocument(QString &pathNAme);
+    bool loadDocument(const QString &pathNAme);
 
     lok::Document *m_document;
 

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/loview.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/loview.cpp	2015-09-11 12:35:10 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/loview.cpp	2015-09-19 14:21:26 +0000
@@ -41,6 +41,7 @@
     connect(this, SIGNAL(parentFlickableChanged()), this, SLOT(updateVisibleRect()));
     connect(this, SIGNAL(cacheBufferChanged()), this, SLOT(updateVisibleRect()));
     connect(&m_updateTimer, SIGNAL(timeout()), this, SLOT(updateVisibleRect()));
+    connect(RenderEngine::instance(), SIGNAL(renderFinished(int,QImage)), this, SLOT(renderResultReceived(int,QImage)));
 }
 
 // Returns the parent QML Flickable
@@ -69,20 +70,17 @@
     Q_EMIT parentFlickableChanged();
 }
 
+void LOView::initializeDocument(const QString &path)
+{
+    m_document = QSharedPointer<LODocument>(new LODocument());
+    m_document->setPath(path);
+    Q_EMIT documentChanged();
+}
+
 // Return the LODocument rendered by this class
 LODocument* LOView::document() const
 {
-    return m_document;
-}
-
-// Set the LODocument
-void LOView::setDocument(LODocument *doc)
-{
-    if (m_document == doc)
-        return;
-
-    m_document = doc;
-    Q_EMIT documentChanged();
+    return m_document.data();
 }
 
 // Not used yet.
@@ -168,7 +166,8 @@
             qDebug() << "Removing tile indexed as" << i.key();
 #endif
 
-            sgtile->dispose();
+            RenderEngine::instance()->dequeueTask(sgtile->id());
+            sgtile->deleteLater();
             i = m_tiles.erase(i);
         }
     }
@@ -224,8 +223,9 @@
         qDebug() << "Creating tile indexed as" << index;
 #endif
 
-        auto tile = new SGTileItem(rect, m_document, this);
+        auto tile = new SGTileItem(rect, this);
         m_tiles.insert(index, tile);
+        RenderEngine::instance()->enqueueTask(m_document, rect, tile->id());
     }
 #ifdef DEBUG_VERBOSE
     else {
@@ -240,7 +240,22 @@
         m_updateTimer.start(20);
 }
 
+void LOView::renderResultReceived(int id, QImage img)
+{
+    for (auto i = m_tiles.begin(); i != m_tiles.end(); ++i) {
+        SGTileItem* sgtile = i.value();
+        if (sgtile->id() == id) {
+            sgtile->setData(img);
+            break;
+        }
+    }
+}
+
 LOView::~LOView()
 {
-    //
+    disconnect(RenderEngine::instance(), SIGNAL(renderFinished(int,QImage)), this, SLOT(renderResultReceived(int,QImage)));
+
+    // Remove all tasks from rendering queue.
+    for (auto i = m_tiles.begin(); i != m_tiles.end(); ++i)
+        RenderEngine::instance()->dequeueTask(i.value()->id());
 }

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/loview.h'
--- src/plugin/libreofficetoolkit-qml-plugin/loview.h	2015-09-11 12:42:47 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/loview.h	2015-09-19 14:21:26 +0000
@@ -20,6 +20,9 @@
 #include <QQuickPaintedItem>
 #include <QQuickItem>
 #include <QTimer>
+#include <QSharedPointer>
+
+#include "renderengine.h"
 
 class LODocument;
 class SGTileItem;
@@ -28,7 +31,7 @@
 {
     Q_OBJECT
     Q_PROPERTY(QQuickItem* parentFlickable READ parentFlickable WRITE setParentFlickable NOTIFY parentFlickableChanged)
-    Q_PROPERTY(LODocument* document        READ document        WRITE setDocument        NOTIFY documentChanged)
+    Q_PROPERTY(LODocument* document        READ document        /*WRITE setDocument*/    NOTIFY documentChanged)
 
     // TODO: Implement zoom!
     Q_PROPERTY(qreal       zoomFactor      READ zoomFactor      WRITE setZoomFactor      NOTIFY zoomFactorChanged)
@@ -41,8 +44,9 @@
     QQuickItem* parentFlickable() const;
     void        setParentFlickable(QQuickItem* flickable);
 
+    Q_INVOKABLE void initializeDocument(const QString& path);
+
     LODocument* document() const;
-    void        setDocument(LODocument* doc);
 
     qreal       zoomFactor() const;
     void        setZoomFactor(qreal zoom);
@@ -60,23 +64,24 @@
     void updateViewSize();
     void updateVisibleRect();
     void scheduleVisibleRectUpdate();
+    void renderResultReceived(int id, QImage img);
 
 private:
-    QQuickItem*             m_parentFlickable;
-    LODocument*             m_document;
-
-    qreal                   m_zoomFactor;
-    int                     m_cacheBuffer;
-
-    QRect                   m_visibleArea;
-    QRect                   m_bufferArea;
-
-    QTimer                  m_updateTimer;
-
-    QMap<int, SGTileItem*>    m_tiles;
-
-    void                    generateTiles(int x1, int y1, int x2, int y2, int tilesPerWidth);
-    void                    createTile(int index, QRect rect);
+    QQuickItem*                 m_parentFlickable;
+    QSharedPointer<LODocument>  m_document;
+
+    qreal                       m_zoomFactor;
+    int                         m_cacheBuffer;
+
+    QRect                       m_visibleArea;
+    QRect                       m_bufferArea;
+
+    QTimer                      m_updateTimer;
+
+    QMap<int, SGTileItem*>      m_tiles;
+
+    void generateTiles(int x1, int y1, int x2, int y2, int tilesPerWidth);
+    void createTile(int index, QRect rect);
 };
 
 #endif // LOVIEW_H

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/qml/Viewer.qml'
--- src/plugin/libreofficetoolkit-qml-plugin/qml/Viewer.qml	2015-09-10 12:22:49 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/qml/Viewer.qml	2015-09-19 14:21:26 +0000
@@ -24,6 +24,13 @@
     property alias zoomFactor:  view.zoomFactor
     property alias cacheBuffer: view.cacheBuffer
 
+    property string documentPath: ""
+
+    onDocumentPathChanged: {
+        if (documentPath)
+            view.initializeDocument(documentPath)
+    }
+
     contentHeight: view.height * view.zoomFactor
     contentWidth: view.width * view.zoomFactor
 

=== added file 'src/plugin/libreofficetoolkit-qml-plugin/renderengine.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/renderengine.cpp	1970-01-01 00:00:00 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/renderengine.cpp	2015-09-19 14:21:26 +0000
@@ -0,0 +1,56 @@
+#include "renderengine.h"
+#include <QtConcurrent/QtConcurrent>
+#include <QThread>
+
+RenderEngine* RenderEngine::s_instance = nullptr;
+
+RenderEngine::RenderEngine():
+    QObject(nullptr),
+    m_activeTaskCount(0)
+{
+    int itc = QThread::idealThreadCount();
+    m_idealThreadCount = itc == -1 ? DefaultIdealThreadCount : itc;
+}
+
+void RenderEngine::enqueueTask(const QSharedPointer<LODocument>& doc, const QRect& area, int id)
+{
+    Q_ASSERT(doc != nullptr);
+
+    m_queue.enqueue(EngineTask(doc, area, id));
+
+    doNextTask();
+}
+
+void RenderEngine::dequeueTask(int id)
+{
+    for (int i = 0; i < m_queue.size(); i++)
+        if (m_queue.at(i).id == id) {
+            m_queue.removeAt(i);
+            break;
+        }
+}
+
+void RenderEngine::internalRenderCallback(int id, QImage img)
+{
+    m_activeTaskCount--;
+    Q_EMIT renderFinished(id, img);
+    doNextTask();
+}
+
+void RenderEngine::doNextTask()
+{
+#ifdef DEBUG_VERBOSE
+    qDebug() << " ---- doNextTask" << m_activeTaskCount << m_queue.count();
+#endif
+
+    if (m_activeTaskCount >= m_idealThreadCount || !m_queue.count())
+        return;
+
+    m_activeTaskCount++;
+    auto task = m_queue.dequeue();
+
+    QtConcurrent::run( [=] {
+        QImage img = task.document->paintTile(task.area.size(), task.area);
+        QMetaObject::invokeMethod(this, "internalRenderCallback", Q_ARG(int, task.id), Q_ARG(QImage, img));
+    });
+}

=== added file 'src/plugin/libreofficetoolkit-qml-plugin/renderengine.h'
--- src/plugin/libreofficetoolkit-qml-plugin/renderengine.h	1970-01-01 00:00:00 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/renderengine.h	2015-09-19 14:21:26 +0000
@@ -0,0 +1,59 @@
+#ifndef RENDERENGINE_H
+#define RENDERENGINE_H
+
+#include <QObject>
+#include <QImage>
+#include <QSharedPointer>
+#include <QHash>
+#include <QQueue>
+
+#include "lodocument.h"
+
+struct EngineTask
+{
+    int id;
+    QRect area;
+    QSharedPointer<LODocument> document;
+
+public:
+    EngineTask(const QSharedPointer<LODocument>& d, const QRect& a, int i):
+    id(i),
+    area(a),
+    document(d)
+    { }
+};
+
+class RenderEngine : public QObject
+{
+    Q_OBJECT
+    Q_DISABLE_COPY(RenderEngine)
+
+    static RenderEngine* s_instance;
+    RenderEngine();
+
+    const int DefaultIdealThreadCount = 2;
+
+public:
+    void enqueueTask(const QSharedPointer<LODocument>& doc, const QRect& area, int id);
+    void dequeueTask(int id);
+
+    static RenderEngine* instance() {
+        if(!s_instance)
+            s_instance = new RenderEngine();
+        return s_instance;
+    }
+
+Q_SIGNALS:
+    void renderFinished(int id, QImage img);
+
+private:
+    Q_INVOKABLE void internalRenderCallback(int id, QImage img);
+    void doNextTask();
+
+private:
+    QQueue<EngineTask> m_queue;
+    int m_activeTaskCount;
+    int m_idealThreadCount;
+};
+
+#endif // RENDERENGINE_H

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.cpp'
--- src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.cpp	2015-09-11 12:35:10 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.cpp	2015-09-19 14:21:26 +0000
@@ -8,11 +8,12 @@
 #include <QSGFlatColorMaterial>
 #endif
 
-SGTileItem::SGTileItem(const QRect &area, LODocument *document, QQuickItem *parent)
+int SGTileItem::s_idCounter = 0xDEAD0000;
+
+SGTileItem::SGTileItem(const QRect& area, QQuickItem *parent)
     : QQuickItem(parent)
     , m_area(area)
-    , m_document(document)
-    , m_state(SgstInitial)
+    , m_id (s_idCounter++)
 {
     setFlag(ItemHasContents, true);
 }
@@ -20,83 +21,22 @@
 SGTileItem::~SGTileItem()
 { }
 
-void SGTileItem::dispose()
-{
-    if (m_state.loadAcquire() != SgstRendering)
-        deleteLater();
-    m_state.storeRelease(SgstDisposed);
-
-#ifdef DEBUG_VERBOSE
-    qDebug() << "---- dispose called: " << this << m_state;
-#endif
-}
-
 QSGNode *SGTileItem::updatePaintNode(QSGNode *oldNode, QQuickItem::UpdatePaintNodeData *)
 {
     QSGSimpleTextureNode* node = static_cast<QSGSimpleTextureNode*>(oldNode);
     QQuickWindow* wnd = window();
 
-    if (!node && wnd) {
-        if (this->m_state.loadAcquire() == SgstInitial) {
-            m_state.storeRelease(SgstRendering);
-
-            QtConcurrent::run( [=] {
-                if (!m_document)
-                    return;
-
-                QImage img;
-
-                // By this time already can be disposed, so it's better to ckeck again.
-                if (this->m_state.loadAcquire() == SgstRendering)
-                    img = m_document->paintTile(this->area().size(), this->area());
-
-#ifdef DEBUG_VERBOSE
-                else if (this->m_state.loadAcquire() == SgstDisposed)
-                    qDebug() << "Already disposed:" << m_state.loadAcquire();
-#endif
-
-                QMetaObject::invokeMethod(this, "renderCallback", Q_ARG(QImage, img));
-            });
-        } else if (m_state.loadAcquire() == SgstActive) {
-            QImage image = m_data;
-            auto texture = wnd->createTextureFromImage(image);
-            node = new QSGSimpleTextureNode();
-            node->setTexture(texture);
-            node->setOwnsTexture(true);
-            node->setRect(m_area);
+    if (!node && wnd && !m_data.isNull()) {
+        QImage image = m_data;
+        auto texture = wnd->createTextureFromImage(image);
+        node = new QSGSimpleTextureNode();
+        node->setTexture(texture);
+        node->setOwnsTexture(true);
+        node->setRect(m_area);
 
 #ifdef DEBUG_SHOW_TILE_BORDER
-            auto tileBorderGeometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 8);
-            tileBorderGeometry->setDrawingMode(GL_LINES);
-            tileBorderGeometry->setLineWidth(4);
-
-            QSGGeometry::Point2D* vertex = tileBorderGeometry->vertexDataAsPoint2D();
-            vertex[0].set(node->rect().left(), node->rect().top());
-            vertex[1].set(node->rect().left(), node->rect().bottom());
-
-            vertex[2].set(node->rect().right(), node->rect().top());
-            vertex[3].set(node->rect().right(), node->rect().bottom());
-
-            vertex[4].set(vertex[0].x, vertex[0].y);
-            vertex[5].set(vertex[2].x, vertex[2].y);
-
-            vertex[6].set(vertex[1].x, vertex[1].y);
-            vertex[7].set(vertex[3].x, vertex[3].y);
-
-            auto tileBorderMaterial = new QSGFlatColorMaterial;
-            tileBorderMaterial->setColor(Qt::red);
-
-            auto tileBorderNode = new QSGGeometryNode;
-
-            tileBorderNode->setGeometry(tileBorderGeometry);
-            tileBorderNode->setFlag(QSGNode::OwnsGeometry);
-
-            tileBorderNode->setMaterial(tileBorderMaterial);
-            tileBorderNode->setFlag(QSGNode::OwnsMaterial);
-
-            node->appendChildNode(tileBorderNode);
+        drawTileBorders(node);
 #endif
-        }
     }
 
     return node;
@@ -107,12 +47,38 @@
     QQuickItem::geometryChanged(newGeometry, oldGeometry);
 }
 
-void SGTileItem::renderCallback(QImage image)
+#ifdef DEBUG_SHOW_TILE_BORDER
+void SGTileItem::drawTileBorders(QSGNode* basicNode)
 {
-    if (m_state.loadAcquire() == SgstRendering) {
-        m_data = image;
-        m_state.storeRelease(SgstActive);
-        update();
-    } else deleteLater();
+    auto node = basicNode;
+    auto tileBorderGeometry = new QSGGeometry(QSGGeometry::defaultAttributes_Point2D(), 8);
+    tileBorderGeometry->setDrawingMode(GL_LINES);
+    tileBorderGeometry->setLineWidth(4);
+
+    QSGGeometry::Point2D* vertex = tileBorderGeometry->vertexDataAsPoint2D();
+    vertex[0].set(node->rect().left(), node->rect().top());
+    vertex[1].set(node->rect().left(), node->rect().bottom());
+
+    vertex[2].set(node->rect().right(), node->rect().top());
+    vertex[3].set(node->rect().right(), node->rect().bottom());
+
+    vertex[4].set(vertex[0].x, vertex[0].y);
+    vertex[5].set(vertex[2].x, vertex[2].y);
+
+    vertex[6].set(vertex[1].x, vertex[1].y);
+    vertex[7].set(vertex[3].x, vertex[3].y);
+
+    auto tileBorderMaterial = new QSGFlatColorMaterial;
+    tileBorderMaterial->setColor(Qt::red);
+
+    auto tileBorderNode = new QSGGeometryNode;
+
+    tileBorderNode->setGeometry(tileBorderGeometry);
+    tileBorderNode->setFlag(QSGNode::OwnsGeometry);
+
+    tileBorderNode->setMaterial(tileBorderMaterial);
+    tileBorderNode->setFlag(QSGNode::OwnsMaterial);
+
+    node->appendChildNode(tileBorderNode);
 }
-
+#endif

=== modified file 'src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.h'
--- src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.h	2015-09-02 11:31:45 +0000
+++ src/plugin/libreofficetoolkit-qml-plugin/sgtileitem.h	2015-09-19 14:21:26 +0000
@@ -8,47 +8,39 @@
 #include <QtConcurrent/QtConcurrent>
 #include <QAtomicInteger>
 
-enum SGTileItemState
-{
-    SgstInitial = 1,
-    SgstRendering,
-    SgstActive,
-    SgstDisposed
-};
-
 class LODocument;
 
 class SGTileItem : public QQuickItem
 {
     Q_OBJECT
 public:
-    SGTileItem(const QRect &area, LODocument *document, QQuickItem *parent = 0);
+    SGTileItem(const QRect& area, QQuickItem *parent = 0);
     ~SGTileItem();
 
-    inline QRect area() const { return m_area; }
-    inline void setArea(const QRect &area) { m_area = area; }
-
-    inline LODocument* document() const { return m_document; }
-    inline void setDocument(LODocument* document) { m_document = document; }
-
-    void dispose();
+    inline const QRect& area() { return m_area; }
+    inline void setArea(const QRect& rect) { m_area = rect; }
+
+    inline int id() { return m_id; }
+    inline void setId(int id) { m_id = id; }
+
+    inline QImage data() { return m_data; }
+    inline void setData(QImage data) { m_data = data; update(); }
 
 protected:
     virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
     virtual void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry);
 
 private:
-    Q_INVOKABLE void renderCallback(QImage image);
-
-signals:
+#ifdef DEBUG_SHOW_TILE_BORDER
+    void drawTileBorders(QSGNode* basicNode);
+#endif
 
 private:
     QRect m_area;
-    LODocument* m_document;
     QImage m_data;
-    QAtomicInteger<int> m_state;
+    int m_id;
 
-public slots:
+    static int s_idCounter;
 };
 
 #endif // SGTILEITEM_H


Follow ups