From 42dc4f3ed8b7b41597dd9851c31dee4d0e352f46 Mon Sep 17 00:00:00 2001 From: Pranav Kant Date: Thu, 4 Jun 2015 00:06:46 +0530 Subject: 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 --- 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 d5af092c9720..27fc2151578f 100644 --- a/libreofficekit/source/gtk/lokdocview.cxx +++ b/libreofficekit/source/gtk/lokdocview.cxx @@ -22,6 +22,8 @@ #include #include +#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 +#include +#include + +#define LOK_USE_UNSTABLE_API +#include +#include +#include + +/* + 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 m_aTiles; + //TODO: Also set width and height when document size changes + int m_nWidth; + int m_nHeight; +}; + +#endif // INCLUDED_TILEBUFFER_HXX -- cgit