Blob Blame History Raw
From 2b35ac76de22c9aa23c2245e49fc9e0c823b739c Mon Sep 17 00:00:00 2001
From: Pranav Kant <pranavk@gnome.org>
Date: Thu, 4 Jun 2015 00:06:46 +0530
Subject: [PATCH 012/398] Add tile buffering support

The TileBuffer class now manages all the tiles. The tile rendering calls
to LO core is also managed by this class.

Change-Id: Ic667a93dcf1c097e0601c0496e8a083c4742e8cb
(cherry picked from commit 42dc4f3ed8b7b41597dd9851c31dee4d0e352f46)
---
 libreofficekit/Library_libreofficekitgtk.mk |  1 +
 libreofficekit/source/gtk/lokdocview.cxx    | 76 ++++++++++++------------
 libreofficekit/source/gtk/tilebuffer.cxx    | 90 +++++++++++++++++++++++++++++
 libreofficekit/source/gtk/tilebuffer.hxx    | 78 +++++++++++++++++++++++++
 4 files changed, 209 insertions(+), 36 deletions(-)
 create mode 100644 libreofficekit/source/gtk/tilebuffer.cxx
 create mode 100644 libreofficekit/source/gtk/tilebuffer.hxx

diff --git a/libreofficekit/Library_libreofficekitgtk.mk b/libreofficekit/Library_libreofficekitgtk.mk
index ff800d0f1d96..92409537f4e8 100644
--- a/libreofficekit/Library_libreofficekitgtk.mk
+++ b/libreofficekit/Library_libreofficekitgtk.mk
@@ -17,6 +17,7 @@ $(eval $(call gb_Library_use_externals,libreofficekitgtk,\
 
 $(eval $(call gb_Library_add_exception_objects,libreofficekitgtk,\
     libreofficekit/source/gtk/lokdocview \
+    libreofficekit/source/gtk/tilebuffer \
 ))
 
 ifeq ($(OS),LINUX)
diff --git a/libreofficekit/source/gtk/lokdocview.cxx b/libreofficekit/source/gtk/lokdocview.cxx
index c487ac8dd906..413054cf873c 100644
--- a/libreofficekit/source/gtk/lokdocview.cxx
+++ b/libreofficekit/source/gtk/lokdocview.cxx
@@ -22,6 +22,8 @@
 #include <LibreOfficeKit/LibreOfficeKitGtk.h>
 #include <rsc/rsc-vcl-shared-types.hxx>
 
+#include "tilebuffer.hxx"
+
 #if !GLIB_CHECK_VERSION(2,32,0)
 #define G_SOURCE_REMOVE FALSE
 #define G_SOURCE_CONTINUE TRUE
@@ -37,6 +39,8 @@
 
 // We know that VirtualDevices use a DPI of 96.
 static const int DPI = 96;
+// Lets use a square of side 256 pixels.
+static const int nTileSizePixels = 256;
 
 namespace {
 
@@ -62,12 +66,8 @@ void payloadToSize(const char* pPayload, long& rWidth, long& rHeight)
 struct LOKDocView_Impl
 {
     LOKDocView* m_pDocView;
-    GtkWidget* m_pEventBox;
-    GtkWidget* m_pTable;
-    GtkWidget** m_pCanvas;
-    GtkWidget *darea;
-
-    TileBuffer *mTileBuffer;
+    GtkWidget *m_pDrawingArea;
+    TileBuffer *m_pTileBuffer;
 
     float m_fZoom;
 
@@ -262,10 +262,7 @@ LOKDocView_Impl::CallbackData::CallbackData(int nType, const std::string& rPaylo
 
 LOKDocView_Impl::LOKDocView_Impl(LOKDocView* pDocView)
     : m_pDocView(pDocView),
-    m_pEventBox(gtk_event_box_new()),
-    darea(gtk_drawing_area_new()),
-    m_pTable(0),
-    m_pCanvas(0),
+    m_pDrawingArea(gtk_drawing_area_new()),
     m_fZoom(1),
     m_pOffice(0),
     m_pDocument(0),
@@ -313,7 +310,7 @@ void LOKDocView_Impl::destroy(LOKDocView* pDocView, gpointer /*pData*/)
     delete pDocView->m_pImpl;
 }
 
-void LOKDocView_Impl::on_exposed(GtkWidget *widget, GdkEvent *event, gpointer userdata)
+void LOKDocView_Impl::on_exposed(GtkWidget* /*widget*/, GdkEvent* /*event*/, gpointer userdata)
 {
     LOKDocView *pDocView = LOK_DOCVIEW (userdata);
     pDocView->m_pImpl->renderDocument(0);
@@ -773,7 +770,7 @@ gboolean LOKDocView_Impl::handleTimeoutImpl()
             m_bCursorOverlayVisible = false;
         else
             m_bCursorOverlayVisible = true;
-        gtk_widget_queue_draw(GTK_WIDGET(m_pEventBox));
+        gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea));
     }
 
     return G_SOURCE_CONTINUE;
@@ -781,8 +778,6 @@ gboolean LOKDocView_Impl::handleTimeoutImpl()
 
 void LOKDocView_Impl::renderDocument(GdkRectangle* pPartial)
 {
-    const int nTileSizePixels = 256;
-
     GdkRectangle visibleArea;
     lok_docview_get_visarea (m_pDocView, &visibleArea);
 
@@ -792,8 +787,8 @@ void LOKDocView_Impl::renderDocument(GdkRectangle* pPartial)
     guint nRows = ceil((double)nDocumentHeightPixels / nTileSizePixels);
     guint nColumns = ceil((double)nDocumentWidthPixels / nTileSizePixels);
 
-    gtk_widget_set_size_request(darea, nDocumentWidthPixels, nDocumentHeightPixels);
-    cairo_t *pcairo = gdk_cairo_create(darea->window);
+    gtk_widget_set_size_request(m_pDrawingArea, nDocumentWidthPixels, nDocumentHeightPixels);
+    cairo_t *pcairo = gdk_cairo_create(m_pDrawingArea->window);
 
     // Render the tiles.
     for (guint nRow = 0; nRow < nRows; ++nRow)
@@ -826,20 +821,10 @@ void LOKDocView_Impl::renderDocument(GdkRectangle* pPartial)
 
             if (bPaint)
             {
-                // Index of the current tile.
-                guint nTile = nRow * nColumns + nColumn;
-
-                GdkPixbuf* pPixBuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, aTileRectanglePixels.width, aTileRectanglePixels.height);
-                unsigned char* pBuffer = gdk_pixbuf_get_pixels(pPixBuf);
-                g_info("renderDocument: paintTile(%d, %d)", nRow, nColumn);
-                m_pDocument->pClass->paintTile(m_pDocument,
-                                               // Buffer and its size, depends on the position only.
-                                               pBuffer,
-                                               aTileRectanglePixels.width, aTileRectanglePixels.height,
-                                               // Position of the tile.
-                                               aTileRectangleTwips.x, aTileRectangleTwips.y,
-                                               // Size of the tile, depends on the zoom factor and the tile position only.
-                                               aTileRectangleTwips.width, aTileRectangleTwips.height);
+                g_info("gettile: (%d %d)", nRow, nColumn);
+
+                Tile& currentTile = m_pTileBuffer->tile_buffer_get_tile(nRow, nColumn);
+                GdkPixbuf* pPixBuf = currentTile.tile_get_buffer();
 
                 gdk_cairo_set_source_pixbuf (pcairo, pPixBuf, twipToPixel(aTileRectangleTwips.x), twipToPixel(aTileRectangleTwips.y));
                 cairo_paint(pcairo);
@@ -959,7 +944,7 @@ gboolean LOKDocView_Impl::callbackImpl(CallbackData* pCallback)
     {
         m_aVisibleCursor = LOKDocView_Impl::payloadToRectangle(pCallback->m_aPayload.c_str());
         m_bCursorOverlayVisible = true;
-        gtk_widget_queue_draw(GTK_WIDGET(m_pEventBox));
+        gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea));
     }
     break;
     case LOK_CALLBACK_TEXT_SELECTION:
@@ -976,7 +961,7 @@ gboolean LOKDocView_Impl::callbackImpl(CallbackData* pCallback)
         }
         else
             memset(&m_aHandleMiddleRect, 0, sizeof(m_aHandleMiddleRect));
-        gtk_widget_queue_draw(GTK_WIDGET(m_pEventBox));
+        gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea));
     }
     break;
     case LOK_CALLBACK_TEXT_SELECTION_START:
@@ -1000,7 +985,7 @@ gboolean LOKDocView_Impl::callbackImpl(CallbackData* pCallback)
             m_aGraphicSelection = LOKDocView_Impl::payloadToRectangle(pCallback->m_aPayload.c_str());
         else
             memset(&m_aGraphicSelection, 0, sizeof(m_aGraphicSelection));
-        gtk_widget_queue_draw(GTK_WIDGET(m_pEventBox));
+        gtk_widget_queue_draw(GTK_WIDGET(m_pDrawingArea));
     }
     break;
     case LOK_CALLBACK_HYPERLINK_CLICKED:
@@ -1154,9 +1139,9 @@ static void lok_docview_init( GTypeInstance* pInstance, gpointer )
 
     pDocView->m_pImpl = new LOKDocView_Impl(pDocView);
     gtk_scrolled_window_add_with_viewport( GTK_SCROLLED_WINDOW(pDocView),
-                                           pDocView->m_pImpl->darea );
+                                           pDocView->m_pImpl->m_pDrawingArea );
 
-    g_signal_connect(GTK_OBJECT(pDocView->m_pImpl->darea),
+    g_signal_connect(GTK_OBJECT(pDocView->m_pImpl->m_pDrawingArea),
                      "expose-event",
                      GTK_SIGNAL_FUNC(LOKDocView_Impl::on_exposed), pDocView);
 
@@ -1218,6 +1203,18 @@ SAL_DLLPUBLIC_EXPORT gboolean lok_docview_open_document( LOKDocView* pDocView, c
         pDocView->m_pImpl->m_pDocument->pClass->registerCallback(pDocView->m_pImpl->m_pDocument, &LOKDocView_Impl::callbackWorker, pDocView);
         pDocView->m_pImpl->m_pDocument->pClass->getDocumentSize(pDocView->m_pImpl->m_pDocument, &pDocView->m_pImpl->m_nDocumentWidthTwips, &pDocView->m_pImpl->m_nDocumentHeightTwips);
         g_timeout_add(600, &LOKDocView_Impl::handleTimeout, pDocView);
+
+        long nDocumentWidthTwips = pDocView->m_pImpl->m_nDocumentWidthTwips;
+        long nDocumentHeightTwips = pDocView->m_pImpl->m_nDocumentHeightTwips;
+        long nDocumentWidthPixels = pDocView->m_pImpl->twipToPixel(nDocumentWidthTwips);
+        long nDocumentHeightPixels = pDocView->m_pImpl->twipToPixel(nDocumentHeightTwips);
+        // Total number of rows / columns in this document.
+        guint nRows = ceil((double)nDocumentHeightPixels / nTileSizePixels);
+        guint nColumns = ceil((double)nDocumentWidthPixels / nTileSizePixels);
+        pDocView->m_pImpl->m_pTileBuffer = new TileBuffer(pDocView->m_pImpl->m_pDocument,
+                                                          nTileSizePixels,
+                                                          nRows,
+                                                          nColumns);
         pDocView->m_pImpl->renderDocument(0);
     }
 
@@ -1232,6 +1229,13 @@ SAL_DLLPUBLIC_EXPORT LibreOfficeKitDocument* lok_docview_get_document(LOKDocView
 SAL_DLLPUBLIC_EXPORT void lok_docview_set_zoom ( LOKDocView* pDocView, float fZoom )
 {
     pDocView->m_pImpl->m_fZoom = fZoom;
+    long nDocumentWidthPixels = pDocView->m_pImpl->twipToPixel(pDocView->m_pImpl->m_nDocumentWidthTwips);
+    long nDocumentHeightPixels = pDocView->m_pImpl->twipToPixel(pDocView->m_pImpl->m_nDocumentHeightTwips);
+    // Total number of rows / columns in this document.
+    guint nRows = ceil((double)nDocumentHeightPixels / nTileSizePixels);
+    guint nColumns = ceil((double)nDocumentWidthPixels / nTileSizePixels);
+
+    pDocView->m_pImpl->m_pTileBuffer->tile_buffer_set_zoom(fZoom, nRows, nColumns);
 
     if ( pDocView->m_pImpl->m_pDocument )
         pDocView->m_pImpl->renderDocument(0);
@@ -1283,7 +1287,7 @@ SAL_DLLPUBLIC_EXPORT void lok_docview_set_edit( LOKDocView* pDocView,
     }
     pDocView->m_pImpl->m_bEdit = bEdit;
     g_signal_emit(pDocView, docview_signals[EDIT_CHANGED], 0, bWasEdit);
-    gtk_widget_queue_draw(GTK_WIDGET(pDocView->m_pImpl->m_pEventBox));
+    gtk_widget_queue_draw(GTK_WIDGET(pDocView->m_pImpl->m_pDrawingArea));
 }
 
 SAL_DLLPUBLIC_EXPORT gboolean lok_docview_get_edit(LOKDocView* pDocView)
diff --git a/libreofficekit/source/gtk/tilebuffer.cxx b/libreofficekit/source/gtk/tilebuffer.cxx
new file mode 100644
index 000000000000..ca66ae904f71
--- /dev/null
+++ b/libreofficekit/source/gtk/tilebuffer.cxx
@@ -0,0 +1,90 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include "tilebuffer.hxx"
+
+static const int DPI = 96;
+
+static float pixelToTwip(float fInput, float zoom)
+{
+    return (fInput / DPI / zoom) * 1440.0f;
+}
+
+static float twipToPixel(float fInput, float zoom)
+{
+    return fInput / 1440.0f * DPI * zoom;
+}
+
+GdkPixbuf* Tile::tile_get_buffer()
+{
+  return m_pBuffer;
+}
+
+void Tile::tile_release()
+{
+  gdk_pixbuf_unref(m_pBuffer);
+  m_pBuffer = NULL;
+}
+
+void TileBuffer::tile_buffer_set_zoom(float newZoomFactor, int rows, int columns)
+{
+  m_fZoomFactor = newZoomFactor;
+
+  tile_buffer_reset_all_tiles();
+
+  // set new buffer width and height
+  m_nWidth = columns;
+  m_nHeight = rows;
+  m_aTiles.resize(m_nWidth * m_nHeight);
+}
+
+void TileBuffer::tile_buffer_reset_all_tiles()
+{
+  for (size_t i = 0; i < m_aTiles.size(); i++)
+    {
+      m_aTiles[i].tile_release();
+    }
+  m_aTiles.clear();
+}
+
+Tile& TileBuffer::tile_buffer_get_tile(int x, int y)
+{
+  int index = x * m_nWidth + y;
+  if(!m_aTiles[index].valid)
+    {
+      GdkPixbuf* pPixBuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, TRUE, 8, m_nTileSize, m_nTileSize);
+      if (!pPixBuf){
+        g_info ("error allocating memory to pixbuf");
+      }
+      unsigned char* pBuffer = gdk_pixbuf_get_pixels(pPixBuf);
+      GdkRectangle aTileRectangle;
+      aTileRectangle.x = pixelToTwip(m_nTileSize, m_fZoomFactor) * y;
+      aTileRectangle.y = pixelToTwip(m_nTileSize, m_fZoomFactor) * x;
+
+      g_info ("rendering (%d %d)", x, y);
+      m_pLOKDocument->pClass->paintTile(m_pLOKDocument,
+                                        // Buffer and its size, depends on the position only.
+                                        pBuffer,
+                                        m_nTileSize, m_nTileSize,
+                                        // Position of the tile.
+                                        aTileRectangle.x, aTileRectangle.y,
+                                        // Size of the tile, depends on the zoom factor and the tile position only.
+                                        pixelToTwip(m_nTileSize, m_fZoomFactor), pixelToTwip(m_nTileSize, m_fZoomFactor));
+
+      m_aTiles[index].tile_set_pixbuf(pPixBuf);
+      m_aTiles[index].valid = 1;
+    }
+
+  return m_aTiles[index];
+}
+
+void Tile::tile_set_pixbuf(GdkPixbuf *buffer)
+{
+  m_pBuffer = buffer;
+}
diff --git a/libreofficekit/source/gtk/tilebuffer.hxx b/libreofficekit/source/gtk/tilebuffer.hxx
new file mode 100644
index 000000000000..a5ed0dc8ec61
--- /dev/null
+++ b/libreofficekit/source/gtk/tilebuffer.hxx
@@ -0,0 +1,78 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_TILEBUFFER_HXX
+#define INCLUDED_TILEBUFFER_HXX
+
+#include <gdk/gdkkeysyms.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <vector>
+
+#define LOK_USE_UNSTABLE_API
+#include <LibreOfficeKit/LibreOfficeKit.h>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <LibreOfficeKit/LibreOfficeKitGtk.h>
+
+/*
+  This class represents a single tile in the tile buffer.
+  TODO: Extend it to support features like double buffering
+*/
+class Tile
+{
+public:
+  Tile() : valid(0) {}
+  ~Tile() {
+    tile_release();
+  }
+
+  GdkPixbuf* tile_get_buffer();
+  void tile_release();
+  void tile_set_pixbuf(GdkPixbuf*);
+  bool valid;
+private:
+  GdkPixbuf *m_pBuffer;
+};
+
+/*
+  TileBuffer is the buffer caching all the recently rendered tiles.
+  The buffer is set to invalid when zoom factor changes.
+*/
+class TileBuffer
+{
+public:
+  TileBuffer(LibreOfficeKitDocument *document,
+             int tileSize,
+             int rows,
+             int columns)
+    : m_pLOKDocument(document)
+    , m_nTileSize(tileSize)
+    , m_fZoomFactor(1)
+    , m_nWidth(columns)
+    , m_nHeight(rows)
+  {
+    m_aTiles.resize(rows * columns);
+  }
+
+  ~TileBuffer() {}
+
+  void tile_buffer_set_zoom(float zoomFactor, int rows, int columns);
+  Tile& tile_buffer_get_tile(int x, int y);
+  void tile_buffer_update();
+  void tile_buffer_reset_all_tiles();
+private:
+  LibreOfficeKitDocument *m_pLOKDocument;
+  int m_nTileSize;
+  float m_fZoomFactor;
+  std::vector<Tile> m_aTiles;
+  //TODO: Also set width and height when document size changes
+  int m_nWidth;
+  int m_nHeight;
+};
+
+#endif // INCLUDED_TILEBUFFER_HXX
-- 
2.12.0