summaryrefslogtreecommitdiffstats
path: root/WebKit/efl/ewk
diff options
context:
space:
mode:
Diffstat (limited to 'WebKit/efl/ewk')
-rw-r--r--WebKit/efl/ewk/ewk_tiled_backing_store.c2137
-rw-r--r--WebKit/efl/ewk/ewk_tiled_backing_store.h130
-rw-r--r--WebKit/efl/ewk/ewk_tiled_matrix.c771
-rw-r--r--WebKit/efl/ewk/ewk_tiled_matrix.h60
-rw-r--r--WebKit/efl/ewk/ewk_tiled_model.c905
-rw-r--r--WebKit/efl/ewk/ewk_tiled_model.h58
-rw-r--r--WebKit/efl/ewk/ewk_tiled_private.h62
-rw-r--r--WebKit/efl/ewk/ewk_view.cpp162
-rw-r--r--WebKit/efl/ewk/ewk_view.h36
-rw-r--r--WebKit/efl/ewk/ewk_view_tiled.c346
10 files changed, 4664 insertions, 3 deletions
diff --git a/WebKit/efl/ewk/ewk_tiled_backing_store.c b/WebKit/efl/ewk/ewk_tiled_backing_store.c
new file mode 100644
index 0000000..0e8898b
--- /dev/null
+++ b/WebKit/efl/ewk/ewk_tiled_backing_store.c
@@ -0,0 +1,2137 @@
+/*
+ Copyright (C) 2009-2010 Samsung Electronics
+ Copyright (C) 2009-2010 ProFUSION embedded systems
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#include "ewk_tiled_backing_store.h"
+
+#define _GNU_SOURCE
+#include "ewk_tiled_private.h"
+#include <Ecore.h>
+#include <Eina.h>
+#include <errno.h>
+#include <math.h>
+#include <stdio.h> // XXX REMOVE ME LATER
+#include <stdlib.h>
+#include <string.h>
+
+#define IDX(col, row, rowspan) (col + (row * rowspan))
+
+#if !defined(MIN)
+# define MIN(a, b) ((a < b) ? a : b)
+#endif
+
+#if !defined(MAX)
+# define MAX(a, b) ((a > b) ? a : b)
+#endif
+
+typedef enum _Ewk_Tiled_Backing_Store_Pre_Render_Priority Ewk_Tiled_Backing_Store_Pre_Render_Priority;
+typedef struct _Ewk_Tiled_Backing_Store_Data Ewk_Tiled_Backing_Store_Data;
+typedef struct _Ewk_Tiled_Backing_Store_Item Ewk_Tiled_Backing_Store_Item;
+typedef struct _Ewk_Tiled_Backing_Store_Pre_Render_Request Ewk_Tiled_Backing_Store_Pre_Render_Request;
+
+enum _Ewk_Tiled_Backing_Store_Pre_Render_Priority {
+ PRE_RENDER_PRIORITY_LOW = 0, /**< Append the request to the list */
+ PRE_RENDER_PRIORITY_HIGH /**< Prepend the request to the list */
+};
+
+struct _Ewk_Tiled_Backing_Store_Item {
+ EINA_INLIST;
+ Ewk_Tile *tile;
+ struct {
+ Evas_Coord x, y, w, h;
+ } geometry;
+ struct {
+ Eina_List *process;
+ unsigned long row, col;
+ float zoom;
+ } update;
+ Ewk_Tiled_Backing_Store_Pre_Render_Request *pre_render;
+ Eina_Bool smooth_scale;
+};
+
+struct _Ewk_Tiled_Backing_Store_Pre_Render_Request {
+ EINA_INLIST;
+ unsigned long col, row;
+ float zoom;
+ struct _Ewk_Tiled_Backing_Store_Item *it;
+};
+
+struct _Ewk_Tiled_Backing_Store_Data {
+ Evas_Object_Smart_Clipped_Data base;
+ Evas_Object *self;
+ Evas_Object *contents_clipper;
+ struct {
+ Eina_Inlist **items;
+ Evas_Coord x, y, w, h;
+ long cols, rows;
+ struct {
+ Evas_Coord w, h;
+ float zoom;
+ Eina_Bool zoom_weak_smooth_scale:1;
+ } tile;
+ struct {
+ struct {
+ Evas_Coord x, y;
+ } cur, old, base, zoom_center;
+ } offset;
+ } view;
+ Evas_Colorspace cspace;
+ struct {
+ Ewk_Tile_Matrix *matrix;
+ struct {
+ unsigned long col, row;
+ } base;
+ struct {
+ unsigned long cols, rows;
+ } cur, old;
+ Evas_Coord width, height;
+ } model;
+ struct {
+ Eina_Bool (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *area);
+ void *data;
+ Eina_List *queue;
+ Eina_Bool process_entire_queue;
+ Eina_Inlist *pre_render_requests;
+ Ecore_Idler *idler;
+ Eina_Bool disabled;
+ Eina_Bool suspend:1;
+ } render;
+ struct {
+ void *(*pre_cb)(void *data, Evas_Object *o);
+ void *pre_data;
+ void *(*post_cb)(void *data, void *pre_data, Evas_Object *o);
+ void *post_data;
+ } process;
+ struct {
+ Eina_Bool any:1;
+ Eina_Bool pos:1;
+ Eina_Bool size:1;
+ Eina_Bool model:1;
+ Eina_Bool offset:1;
+ } changed;
+#ifdef DEBUG_MEM_LEAKS
+ Ecore_Event_Handler *sig_usr;
+#endif
+};
+
+static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL;
+int _ewk_tiled_log_dom = -1;
+
+#define PRIV_DATA_GET_OR_RETURN(obj, ptr, ...) \
+ Ewk_Tiled_Backing_Store_Data *ptr = evas_object_smart_data_get(obj); \
+ if (!ptr) { \
+ CRITICAL("no private data in obj=%p", obj); \
+ return __VA_ARGS__; \
+ }
+
+static inline void _ewk_tiled_backing_store_item_request_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it);
+static inline void _ewk_tiled_backing_store_item_request_add(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it, int m_col, int m_row, float zoom);
+static void _ewk_tiled_backing_store_fill_renderers(Ewk_Tiled_Backing_Store_Data *priv);
+static inline void _ewk_tiled_backing_store_view_dbg(const Ewk_Tiled_Backing_Store_Data *priv);
+static inline void _ewk_tiled_backing_store_changed(Ewk_Tiled_Backing_Store_Data *priv);
+
+static inline void _ewk_tiled_backing_store_updates_process(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ void *data = NULL;
+
+ /* Do not process updates. Note that we still want to get updates requests
+ * in the queue in order to not miss any updates after the render is
+ * resumed.
+ */
+ if (priv->render.suspend || !evas_object_visible_get(priv->self))
+ return;
+
+ if (priv->process.pre_cb)
+ data = priv->process.pre_cb(priv->process.pre_data, priv->self);
+
+ ewk_tile_matrix_updates_process(priv->model.matrix);
+
+ if (priv->process.post_cb)
+ priv->process.post_cb(priv->process.post_data, data, priv->self);
+}
+
+static int _ewk_tiled_backing_store_flush(void *data)
+{
+ Ewk_Tiled_Backing_Store_Data *priv = data;
+ Ewk_Tile_Unused_Cache *tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
+
+ if (tuc) {
+ DBG("flush unused tile cache.");
+ ewk_tile_unused_cache_auto_flush(tuc);
+ } else
+ ERR("no cache?!");
+
+ return 0;
+}
+
+static Ewk_Tile *_ewk_tiled_backing_store_tile_new(Ewk_Tiled_Backing_Store_Data *priv, unsigned long col, unsigned long row, float zoom)
+{
+ Ewk_Tile *t;
+ Evas *evas = evas_object_evas_get(priv->self);
+ if (!evas) {
+ CRITICAL("evas_object_evas_get failed!");
+ return NULL;
+ }
+
+ t = ewk_tile_matrix_tile_new
+ (priv->model.matrix, evas, col, row, zoom);
+
+ if (!t) {
+ CRITICAL("ewk_tile_matrix_tile_new failed!");
+ return NULL;
+ }
+
+ return t;
+}
+
+static void _ewk_tiled_backing_store_item_move(Ewk_Tiled_Backing_Store_Item *it, Evas_Coord x, Evas_Coord y)
+{
+ it->geometry.x = x;
+ it->geometry.y = y;
+
+ if (it->tile)
+ evas_object_move(it->tile->image, x, y);
+}
+
+static void _ewk_tiled_backing_store_item_resize(Ewk_Tiled_Backing_Store_Item *it, Evas_Coord w, Evas_Coord h)
+{
+ it->geometry.w = w;
+ it->geometry.h = h;
+
+ if (it->tile) {
+ evas_object_resize(it->tile->image, w, h);
+ evas_object_image_fill_set(it->tile->image, 0, 0, w, h);
+ }
+}
+
+static void _ewk_tiled_backing_store_tile_associate(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tile *t, Ewk_Tiled_Backing_Store_Item *it)
+{
+ if (it->tile)
+ CRITICAL("it->tile=%p, but it should be NULL!", it->tile);
+ it->tile = t;
+ evas_object_move(it->tile->image, it->geometry.x, it->geometry.y);
+ evas_object_resize(it->tile->image, it->geometry.w, it->geometry.h);
+ evas_object_image_fill_set
+ (it->tile->image, 0, 0, it->geometry.w, it->geometry.h);
+ evas_object_image_smooth_scale_set(it->tile->image, it->smooth_scale);
+
+ if (!ewk_tile_visible_get(t))
+ evas_object_smart_member_add(t->image, priv->self);
+
+ ewk_tile_show(t);
+}
+
+static void _ewk_tiled_backing_store_tile_dissociate(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it, double last_used)
+{
+ Ewk_Tile_Unused_Cache *tuc;
+ ewk_tile_hide(it->tile);
+ if (!ewk_tile_visible_get(it->tile))
+ evas_object_smart_member_del(it->tile->image);
+ ewk_tile_matrix_tile_put(priv->model.matrix, it->tile, last_used);
+ tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
+ ewk_tile_unused_cache_auto_flush(tuc);
+
+ it->tile = NULL;
+}
+
+static void _ewk_tiled_backing_store_tile_dissociate_all(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ Eina_Inlist *it;
+ Ewk_Tiled_Backing_Store_Item *item;
+ int i;
+ double last_used = ecore_loop_time_get();
+
+ for (i = 0; i < priv->view.rows; i++) {
+ it = priv->view.items[i];
+ EINA_INLIST_FOREACH(it, item)
+ if (item->tile)
+ _ewk_tiled_backing_store_tile_dissociate(priv, item, last_used);
+ }
+}
+
+static inline Eina_Bool _ewk_tiled_backing_store_pre_render_request_add(Ewk_Tiled_Backing_Store_Data *priv, unsigned long col, unsigned long row, float zoom, Ewk_Tiled_Backing_Store_Item *it, Ewk_Tiled_Backing_Store_Pre_Render_Priority priority)
+{
+ Ewk_Tiled_Backing_Store_Pre_Render_Request *r;
+
+ MALLOC_OR_OOM_RET(r, sizeof(*r), EINA_FALSE);
+
+ if (priority == PRE_RENDER_PRIORITY_HIGH)
+ priv->render.pre_render_requests = eina_inlist_prepend
+ (priv->render.pre_render_requests, EINA_INLIST_GET(r));
+ else
+ priv->render.pre_render_requests = eina_inlist_append
+ (priv->render.pre_render_requests, EINA_INLIST_GET(r));
+
+ r->col = col;
+ r->row = row;
+ r->zoom = zoom;
+ r->it = it;
+
+ if (it)
+ it->pre_render = r;
+ return EINA_TRUE;
+}
+
+static inline void _ewk_tiled_backing_store_pre_render_request_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Pre_Render_Request *r)
+{
+ priv->render.pre_render_requests = eina_inlist_remove
+ (priv->render.pre_render_requests, EINA_INLIST_GET(r));
+ free(r);
+}
+
+static inline Ewk_Tiled_Backing_Store_Pre_Render_Request *_ewk_tiled_backing_store_pre_render_request_first(const Ewk_Tiled_Backing_Store_Data *priv)
+{
+ return EINA_INLIST_CONTAINER_GET(
+ priv->render.pre_render_requests,
+ Ewk_Tiled_Backing_Store_Pre_Render_Request);
+}
+
+static void _ewk_tiled_backing_store_pre_render_request_flush(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ Eina_Inlist **pl = &priv->render.pre_render_requests;
+ while (*pl) {
+ Ewk_Tiled_Backing_Store_Pre_Render_Request *r;
+ r = _ewk_tiled_backing_store_pre_render_request_first(priv);
+ if (r->it && r->it->pre_render)
+ r->it->pre_render = NULL;
+ *pl = eina_inlist_remove(*pl, *pl);
+ free(r);
+ }
+}
+
+static void _ewk_tiled_backing_store_pre_render_request_remove_unassociated(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ Eina_Inlist **pl = &priv->render.pre_render_requests;
+ Eina_Inlist *iter = *pl, *tmp;
+ while (iter) {
+ Ewk_Tiled_Backing_Store_Pre_Render_Request *r =
+ EINA_INLIST_CONTAINER_GET(
+ iter, Ewk_Tiled_Backing_Store_Pre_Render_Request);
+ if (!r->it) {
+ tmp = iter->next;
+ *pl = eina_inlist_remove(*pl, iter);
+ iter = tmp;
+ free(r);
+ } else
+ iter = iter->next;
+ }
+}
+
+static void _ewk_tiled_backing_store_pre_render_request_remove_associated(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ Eina_Inlist **pl = &priv->render.pre_render_requests;
+ Eina_Inlist *iter = *pl, *tmp;
+ while (iter) {
+ Ewk_Tiled_Backing_Store_Pre_Render_Request *r =
+ EINA_INLIST_CONTAINER_GET(
+ iter, Ewk_Tiled_Backing_Store_Pre_Render_Request);
+ if (r->it) {
+ if (r->it->pre_render)
+ r->it->pre_render = NULL;
+ tmp = iter->next;
+ *pl = eina_inlist_remove(*pl, iter);
+ iter = tmp;
+ free(r);
+ } else
+ iter = iter->next;
+ }
+}
+
+/* assumes priv->process.pre_cb was called if required! */
+static void _ewk_tiled_backing_store_pre_render_request_process_single(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ Ewk_Tiled_Backing_Store_Pre_Render_Request *req;
+ Eina_Rectangle area;
+ Ewk_Tile_Matrix *tm = priv->model.matrix;
+ Ewk_Tile *t;
+ Ewk_Tile_Unused_Cache *tuc;
+ unsigned long col, row;
+ float zoom;
+ double last_used = ecore_loop_time_get();
+
+ req = _ewk_tiled_backing_store_pre_render_request_first(priv);
+ if (!req)
+ return;
+
+ col = req->col;
+ row = req->row;
+ zoom = req->zoom;
+
+ if (ewk_tile_matrix_tile_exact_exists(tm, col, row, zoom)) {
+ DBG("no pre-render required for tile %lu,%lu @ %f.", col, row, zoom);
+ goto end;
+ }
+
+ if (req->it && req->it->tile) {
+ CRITICAL("it->tile = %p (%lu, %lu), but should be NULL", req->it->tile, req->it->tile->row, req->it->tile->col);
+ goto end;
+ }
+
+ t = _ewk_tiled_backing_store_tile_new(priv, col, row, zoom);
+ if (!t)
+ goto end;
+
+ area.x = 0;
+ area.y = 0;
+ area.w = priv->view.tile.w;
+ area.h = priv->view.tile.h;
+
+ priv->render.cb(priv->render.data, t, &area);
+ evas_object_image_data_update_add(
+ t->image,
+ area.x, area.y, area.w, area.h);
+ ewk_tile_matrix_tile_updates_clear(tm, t);
+
+ if (req->it) {
+ _ewk_tiled_backing_store_tile_associate(priv, t, req->it);
+ if (req->it->pre_render)
+ req->it->pre_render = NULL;
+ } else
+ ewk_tile_matrix_tile_put(tm, t, last_used);
+
+end:
+ _ewk_tiled_backing_store_pre_render_request_del(priv, req);
+ tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
+ ewk_tile_unused_cache_auto_flush(tuc);
+}
+
+static Eina_Bool _ewk_tiled_backing_store_item_process_idler_cb(void *data)
+{
+ Ewk_Tiled_Backing_Store_Data *priv = data;
+ Ewk_Tiled_Backing_Store_Item *it = NULL;
+
+ while (priv->render.queue) {
+ it = priv->render.queue->data;
+ if (it->tile->zoom == priv->view.tile.zoom) {
+ _ewk_tiled_backing_store_item_request_del(priv, it);
+ it = NULL;
+ } else {
+ unsigned long row, col;
+ float zoom;
+ Ewk_Tile *t;
+ if (it->tile) {
+ double last_used = ecore_loop_time_get();
+ _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used);
+ }
+
+ row = it->update.row;
+ col = it->update.col;
+ zoom = it->update.zoom;
+ t = _ewk_tiled_backing_store_tile_new(priv, col, row, zoom);
+ if (!t) {
+ priv->render.idler = NULL;
+ return EINA_FALSE;
+ }
+
+ _ewk_tiled_backing_store_tile_associate(priv, t, it);
+ it->update.process = NULL;
+ priv->render.queue = eina_list_remove_list(priv->render.queue,
+ priv->render.queue);
+ if (!priv->render.process_entire_queue)
+ break;
+ }
+ }
+
+ if (priv->process.pre_cb)
+ data = priv->process.pre_cb(priv->process.pre_data, priv->self);
+
+ ewk_tile_matrix_updates_process(priv->model.matrix);
+
+ if (!it)
+ _ewk_tiled_backing_store_pre_render_request_process_single(priv);
+
+ if (priv->process.post_cb)
+ priv->process.post_cb(priv->process.post_data, data, priv->self);
+
+ if (!priv->render.queue && !priv->render.pre_render_requests) {
+ priv->render.idler = NULL;
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+static inline void _ewk_tiled_backing_store_item_process_idler_stop(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ if (!priv->render.idler)
+ return;
+
+ ecore_idler_del(priv->render.idler);
+ priv->render.idler = NULL;
+}
+
+static inline void _ewk_tiled_backing_store_item_process_idler_start(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ if (priv->render.idler)
+ return;
+ priv->render.idler = ecore_idler_add(
+ _ewk_tiled_backing_store_item_process_idler_cb, priv);
+}
+
+static inline void _ewk_tiled_backing_store_item_request_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it)
+{
+ priv->render.queue = eina_list_remove_list(priv->render.queue,
+ it->update.process);
+ it->update.process = NULL;
+}
+
+static inline void _ewk_tiled_backing_store_item_request_add(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it, int m_col, int m_row, float zoom)
+{
+ if (it->update.process)
+ return;
+
+ it->update.col = m_col;
+ it->update.row = m_row;
+ it->update.zoom = zoom;
+
+ priv->render.queue = eina_list_append(priv->render.queue, it);
+ it->update.process = eina_list_last(priv->render.queue);
+
+ if (!priv->render.suspend)
+ _ewk_tiled_backing_store_item_process_idler_start(priv);
+}
+
+static Eina_Bool _ewk_tiled_backing_store_disable_render(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ if (priv->render.suspend)
+ return EINA_TRUE;
+
+ priv->render.suspend = EINA_TRUE;
+ _ewk_tiled_backing_store_item_process_idler_stop(priv);
+ return EINA_TRUE;
+}
+
+static Eina_Bool _ewk_tiled_backing_store_enable_render(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ if (!priv->render.suspend)
+ return EINA_TRUE;
+
+ priv->render.suspend = EINA_FALSE;
+
+ _ewk_tiled_backing_store_fill_renderers(priv);
+ _ewk_tiled_backing_store_item_process_idler_start(priv);
+
+ return EINA_TRUE;
+}
+
+/**
+ * Returns a rectangle whose coordinates indicate which tiles are
+ * currently inside the viewport.
+ *
+ * Specifically, the returned rectangle's coordinates have the
+ * following meaning:
+ * - x: Column number for the top-level tile inside the viewport.
+ * - y: Row number for the top-level tile inside the viewport.
+ * - w: Number of tiles horizontally inside the viewport.
+ * - h: Number of tiles vertically inside the viewport.
+ */
+static Eina_Rectangle _ewk_tiled_backing_store_visible_tiles_rect(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ Eina_Rectangle r;
+
+ const Evas_Coord ox = -priv->view.offset.cur.x;
+ const Evas_Coord oy = -priv->view.offset.cur.y;
+ const Evas_Coord tw = priv->view.tile.w;
+ const Evas_Coord th = priv->view.tile.h;
+
+ r.x = MAX(0, ox / tw);
+ r.y = MAX(0, oy / th);
+ r.w = MIN(priv->model.cur.cols,
+ ceil((float)(priv->view.w + (ox % tw)) / tw));
+ r.h = MIN(priv->model.cur.rows,
+ ceil((float)(priv->view.h + (oy % th)) / th));
+
+ DBG("Returning %d,%d+%dx%d", r.x, r.y, r.w, r.h);
+
+ return r;
+}
+
+static Eina_Bool _ewk_tiled_backing_store_tile_is_inside_viewport(Ewk_Tiled_Backing_Store_Data *priv, int col, int row)
+{
+ const Eina_Rectangle r = _ewk_tiled_backing_store_visible_tiles_rect(priv);
+
+ return eina_rectangle_coords_inside(&r, col, row);
+}
+
+static Eina_Bool _ewk_tiled_backing_store_tile_is_adjacent_to_viewport(Ewk_Tiled_Backing_Store_Data *priv, int col, int row)
+{
+ const Eina_Rectangle r = _ewk_tiled_backing_store_visible_tiles_rect(priv);
+
+ if (row == (r.y - 1) || row == (r.y + r.h))
+ return (col >= (r.x - 1) && col <= (r.x + r.w));
+
+ if (col == (r.x - 1) || col == (r.x + r.w))
+ return (row >= (r.y - 1) && row <= (r.y + r.h));
+
+ return EINA_FALSE;
+}
+
+static inline Eina_Bool _ewk_tiled_backing_store_item_fill(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it, long col, int row)
+{
+ int m_col = priv->model.base.col + col;
+ int m_row = priv->model.base.row + row;
+ double last_used = ecore_loop_time_get();
+
+ if (m_col < 0 || m_row < 0
+ || m_col >= priv->model.cur.cols || m_row >= priv->model.cur.rows) {
+
+ if (it->tile) {
+ _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used);
+ if (it->update.process)
+ _ewk_tiled_backing_store_item_request_del(priv, it);
+ }
+ } else {
+ Ewk_Tile *t;
+ const float zoom = priv->view.tile.zoom;
+
+ if (it->update.process) {
+ if (it->update.row == m_row
+ && it->update.col == m_col
+ && it->update.zoom == zoom)
+ return EINA_TRUE;
+
+ _ewk_tiled_backing_store_item_request_del(priv, it);
+ }
+
+ if (it->pre_render) {
+ _ewk_tiled_backing_store_pre_render_request_del(
+ priv, it->pre_render);
+ it->pre_render = NULL;
+ }
+
+ if (it->tile) {
+ Ewk_Tile *old = it->tile;
+ if (old->row != m_row || old->col != m_col || old->zoom != zoom) {
+ _ewk_tiled_backing_store_tile_dissociate(priv, it,
+ last_used);
+ if (it->update.process)
+ _ewk_tiled_backing_store_item_request_del(priv, it);
+ } else if (old->row == m_row && old->col == m_col
+ && old->zoom == zoom)
+ goto end;
+ }
+
+ t = ewk_tile_matrix_tile_exact_get
+ (priv->model.matrix, m_col, m_row, zoom);
+ if (!t) {
+ /* NOTE: it never returns NULL if it->tile was set! */
+ if (it->tile) {
+ CRITICAL("it->tile=%p, but it should be NULL!", it->tile);
+ _ewk_tiled_backing_store_tile_dissociate(priv, it,
+ last_used);
+ }
+
+ /* Do not add new requests to the render queue */
+ if (!priv->render.suspend) {
+ if (!_ewk_tiled_backing_store_tile_is_inside_viewport(
+ priv, m_col, m_row)) {
+ DBG("%d,%d is not inside the viewport", m_col, m_row);
+ if (_ewk_tiled_backing_store_tile_is_adjacent_to_viewport(
+ priv, m_col, m_row))
+ _ewk_tiled_backing_store_pre_render_request_add(
+ priv, m_col, m_row, zoom, it,
+ PRE_RENDER_PRIORITY_HIGH);
+ _ewk_tiled_backing_store_item_process_idler_start(priv);
+
+ goto end;
+ }
+
+ t = _ewk_tiled_backing_store_tile_new(priv, m_col, m_row, zoom);
+ if (!t)
+ return EINA_FALSE;
+ _ewk_tiled_backing_store_tile_associate(priv, t, it);
+ }
+ } else if (t != it->tile) {
+ if (!it->update.process) {
+ if (it->tile)
+ _ewk_tiled_backing_store_tile_dissociate(priv,
+ it, last_used);
+ _ewk_tiled_backing_store_tile_associate(priv, t, it);
+ }
+ }
+
+ end:
+
+ return EINA_TRUE;
+ }
+
+ return EINA_TRUE;
+}
+
+static Ewk_Tiled_Backing_Store_Item *_ewk_tiled_backing_store_item_add(Ewk_Tiled_Backing_Store_Data *priv, long col, int row)
+{
+ Ewk_Tiled_Backing_Store_Item *it;
+ Evas_Coord x, y, tw, th;
+
+ DBG("o=%p", priv->self);
+
+ MALLOC_OR_OOM_RET(it, sizeof(*it), NULL);
+
+ tw = priv->view.tile.w;
+ th = priv->view.tile.h;
+ x = priv->view.offset.base.x + priv->view.x + tw *col;
+ y = priv->view.offset.base.y + priv->view.y + th *row;
+
+ it->tile = NULL;
+ it->update.process = NULL;
+ it->smooth_scale = priv->view.tile.zoom_weak_smooth_scale;
+ it->pre_render = NULL;
+ _ewk_tiled_backing_store_item_move(it, x, y);
+ _ewk_tiled_backing_store_item_resize(it, tw, th);
+ if (!_ewk_tiled_backing_store_item_fill(priv, it, col, row)) {
+ free(it);
+ return NULL;
+ }
+
+ return it;
+}
+
+static void _ewk_tiled_backing_store_item_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it)
+{
+ if (it->tile) {
+ double last_used = ecore_loop_time_get();
+ _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used);
+ }
+ if (it->update.process)
+ _ewk_tiled_backing_store_item_request_del(priv, it);
+ if (it->pre_render) {
+ _ewk_tiled_backing_store_pre_render_request_del(
+ priv, it->pre_render);
+ it->pre_render = NULL;
+ }
+ free(it);
+}
+
+static void _ewk_tiled_backing_store_item_smooth_scale_set(Ewk_Tiled_Backing_Store_Item *it, Eina_Bool smooth_scale)
+{
+ if (it->smooth_scale == smooth_scale)
+ return;
+
+ if (it->tile)
+ evas_object_image_smooth_scale_set(it->tile->image, smooth_scale);
+}
+
+static inline void _ewk_tiled_backing_store_changed(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ if (priv->changed.any)
+ return;
+ evas_object_smart_changed(priv->self);
+ priv->changed.any = EINA_TRUE;
+}
+
+static void _ewk_tiled_backing_store_view_cols_end_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist **p_row, unsigned int count)
+{
+ Eina_Inlist *n;
+ unsigned int i;
+
+ if (!count)
+ return;
+
+ n = (*p_row)->last;
+
+ for (i = 0; i < count; i++) {
+ Ewk_Tiled_Backing_Store_Item *it;
+ it = EINA_INLIST_CONTAINER_GET(n, Ewk_Tiled_Backing_Store_Item);
+ n = n->prev;
+ *p_row = eina_inlist_remove(*p_row, EINA_INLIST_GET(it));
+ _ewk_tiled_backing_store_item_del(priv, it);
+ }
+}
+
+static Eina_Bool _ewk_tiled_backing_store_view_cols_end_add(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist **p_row, unsigned int base_col, unsigned int count)
+{
+ unsigned int i, r = p_row - priv->view.items;
+
+ for (i = 0; i < count; i++, base_col++) {
+ Ewk_Tiled_Backing_Store_Item *it;
+
+ it = _ewk_tiled_backing_store_item_add(priv, base_col, r);
+ if (!it) {
+ CRITICAL("failed to add column %u of %u in row %u.", i, count, r);
+ _ewk_tiled_backing_store_view_cols_end_del(priv, p_row, i);
+ return EINA_FALSE;
+ }
+
+ *p_row = eina_inlist_append(*p_row, EINA_INLIST_GET(it));
+ }
+ return EINA_TRUE;
+}
+
+static void _ewk_tiled_backing_store_view_row_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist *row)
+{
+ while (row) {
+ Ewk_Tiled_Backing_Store_Item *it;
+ it = EINA_INLIST_CONTAINER_GET(row, Ewk_Tiled_Backing_Store_Item);
+ row = row->next;
+ _ewk_tiled_backing_store_item_del(priv, it);
+ }
+}
+
+static void _ewk_tiled_backing_store_view_rows_range_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist **start, Eina_Inlist **end)
+{
+ for (; start < end; start++) {
+ _ewk_tiled_backing_store_view_row_del(priv, *start);
+ *start = NULL;
+ }
+}
+
+static void _ewk_tiled_backing_store_view_rows_all_del(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ Eina_Inlist **start;
+ Eina_Inlist **end;
+
+ start = priv->view.items;
+ end = priv->view.items + priv->view.rows;
+ _ewk_tiled_backing_store_view_rows_range_del(priv, start, end);
+
+ free(priv->view.items);
+ priv->view.items = NULL;
+ priv->view.cols = 0;
+ priv->view.rows = 0;
+}
+
+static void _ewk_tiled_backing_store_render(void *data, Ewk_Tile *t, const Eina_Rectangle *area)
+{
+ Ewk_Tiled_Backing_Store_Data *priv = data;
+
+ INF("TODO %p (visible? %d) [%lu,%lu] %d,%d + %dx%d",
+ t, t->visible, t->col, t->row, area->x, area->y, area->w, area->h);
+
+ if (!t->visible)
+ return;
+
+ if (priv->view.tile.w != t->w || priv->view.tile.h != t->h)
+ return; // todo: remove me later, don't even flag as dirty!
+
+ EINA_SAFETY_ON_NULL_RETURN(priv->render.cb);
+ if (!priv->render.cb(priv->render.data, t, area))
+ return;
+
+ evas_object_image_data_update_add(t->image, area->x, area->y, area->w, area->h);
+}
+
+static inline void _ewk_tiled_backing_store_model_matrix_create(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tile_Unused_Cache *tuc)
+{
+ if (priv->model.matrix) {
+ _ewk_tiled_backing_store_view_rows_all_del(priv);
+
+ priv->changed.offset = EINA_FALSE;
+ priv->changed.size = EINA_TRUE;
+
+ ewk_tile_matrix_free(priv->model.matrix);
+ }
+
+ priv->model.matrix = ewk_tile_matrix_new
+ (tuc, priv->model.cur.cols, priv->model.cur.rows, priv->cspace,
+ _ewk_tiled_backing_store_render, priv);
+}
+
+static void _ewk_tiled_backing_store_smart_member_del(Evas_Object *o, Evas_Object *member)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ if (!priv->contents_clipper)
+ return;
+ evas_object_clip_unset(member);
+ if (!evas_object_clipees_get(priv->contents_clipper))
+ evas_object_hide(priv->contents_clipper);
+}
+
+static void _ewk_tiled_backing_store_smart_member_add(Evas_Object *o, Evas_Object *member)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ if (!priv->contents_clipper)
+ return;
+ evas_object_clip_set(member, priv->contents_clipper);
+ if (evas_object_visible_get(o))
+ evas_object_show(priv->contents_clipper);
+}
+
+#ifdef DEBUG_MEM_LEAKS
+static void _ewk_tiled_backing_store_mem_dbg(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ static int run = 0;
+
+ run++;
+
+ printf("\n--- BEGIN DEBUG TILED BACKING STORE MEMORY [%d] --\n"
+ "t=%0.2f, obj=%p, priv=%p, view.items=%p, matrix=%p\n",
+ run, ecore_loop_time_get(),
+ priv->self, priv, priv->view.items, priv->model.matrix);
+
+ ewk_tile_matrix_dbg(priv->model.matrix);
+ ewk_tile_accounting_dbg();
+
+ printf("--- END DEBUG TILED BACKING STORE MEMORY [%d] --\n\n", run);
+}
+
+static Eina_Bool _ewk_tiled_backing_store_sig_usr(void *data, int type, void *event)
+{
+ Ecore_Event_Signal_User *sig = (Ecore_Event_Signal_User*)event;
+ Ewk_Tiled_Backing_Store_Data *priv = (Ewk_Tiled_Backing_Store_Data*)data;
+
+ if (sig->number == 2) {
+ Ewk_Tile_Unused_Cache *tuc;
+ tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
+ ewk_tile_unused_cache_auto_flush(tuc);
+ }
+
+ _ewk_tiled_backing_store_view_dbg(priv);
+ _ewk_tiled_backing_store_mem_dbg(priv);
+ return EINA_TRUE;
+}
+#endif
+
+static void _ewk_tiled_backing_store_smart_add(Evas_Object *o)
+{
+ Ewk_Tiled_Backing_Store_Data *priv;
+
+ DBG("o=%p", o);
+
+ CALLOC_OR_OOM_RET(priv, sizeof(*priv));
+
+ priv->self = o;
+ priv->view.tile.zoom = 1.0;
+ priv->view.tile.w = TILE_W;
+ priv->view.tile.h = TILE_H;
+ priv->view.offset.cur.x = 0;
+ priv->view.offset.cur.y = 0;
+ priv->view.offset.old.x = 0;
+ priv->view.offset.old.y = 0;
+ priv->view.offset.base.x = -TILE_W;
+ priv->view.offset.base.y = -TILE_H;
+
+ priv->model.base.col = -1;
+ priv->model.base.row = -1;
+ priv->model.cur.cols = 1;
+ priv->model.cur.rows = 1;
+ priv->model.old.cols = 0;
+ priv->model.old.rows = 0;
+ priv->model.width = 0;
+ priv->model.height = 0;
+ priv->render.process_entire_queue = EINA_TRUE;
+ priv->render.suspend = EINA_FALSE;
+ priv->cspace = EVAS_COLORSPACE_ARGB8888; // TODO: detect it.
+
+ evas_object_smart_data_set(o, priv);
+ _parent_sc.add(o);
+
+ priv->contents_clipper = evas_object_rectangle_add(
+ evas_object_evas_get(o));
+ evas_object_move(priv->contents_clipper, 0, 0);
+ evas_object_resize(priv->contents_clipper,
+ priv->model.width, priv->model.height);
+ evas_object_color_set(priv->contents_clipper, 255, 255, 255, 255);
+ evas_object_show(priv->contents_clipper);
+ evas_object_smart_member_add(priv->contents_clipper, o);
+
+ _ewk_tiled_backing_store_model_matrix_create(priv, NULL);
+ evas_object_move(priv->base.clipper, 0, 0);
+ evas_object_resize(priv->base.clipper, 0, 0);
+ evas_object_clip_set(priv->contents_clipper, priv->base.clipper);
+
+#ifdef DEBUG_MEM_LEAKS
+ priv->sig_usr = ecore_event_handler_add
+ (ECORE_EVENT_SIGNAL_USER, _ewk_tiled_backing_store_sig_usr, priv);
+#endif
+}
+
+static void _ewk_tiled_backing_store_smart_del(Evas_Object *o)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ DBG("o=%p", o);
+ Ewk_Tile_Unused_Cache *tuc;
+
+ tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
+ ewk_tile_unused_cache_unlock_area(tuc);
+
+ _ewk_tiled_backing_store_flush(priv);
+
+ _ewk_tiled_backing_store_pre_render_request_flush(priv);
+ _ewk_tiled_backing_store_item_process_idler_stop(priv);
+ _ewk_tiled_backing_store_view_rows_all_del(priv);
+
+#ifdef DEBUG_MEM_LEAKS
+ _ewk_tiled_backing_store_mem_dbg(priv);
+ if (priv->sig_usr)
+ priv->sig_usr = ecore_event_handler_del(priv->sig_usr);
+#endif
+
+ ewk_tile_matrix_free(priv->model.matrix);
+ evas_object_smart_member_del(priv->contents_clipper);
+ evas_object_del(priv->contents_clipper);
+
+ _parent_sc.del(o);
+
+#ifdef DEBUG_MEM_LEAKS
+ printf("\nIMPORTANT: TILED BACKING STORE DELETED (may be real leaks)\n");
+ ewk_tile_accounting_dbg();
+#endif
+}
+
+static void _ewk_tiled_backing_store_smart_move(Evas_Object *o, Evas_Coord x, Evas_Coord y)
+{
+ DBG("o=%p, new pos: %dx%d", o, x, y);
+
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+
+ if (priv->changed.pos)
+ return;
+
+ if (priv->view.x == x && priv->view.y == y)
+ return;
+
+ priv->changed.pos = EINA_TRUE;
+ _ewk_tiled_backing_store_changed(priv);
+}
+
+static void _ewk_tiled_backing_store_smart_resize(Evas_Object *o, Evas_Coord w, Evas_Coord h)
+{
+ DBG("o=%p, new size: %dx%d", o, w, h);
+
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+
+ if (priv->changed.size)
+ return;
+
+ if (priv->view.w == w && priv->view.h == h)
+ return;
+
+ priv->changed.size = EINA_TRUE;
+ _ewk_tiled_backing_store_changed(priv);
+}
+
+static void _ewk_tiled_backing_store_recalc_renderers(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord w, Evas_Coord h, Evas_Coord tw, Evas_Coord th)
+{
+ long cols, rows, old_rows, old_cols;
+ INF("o=%p, new size: %dx%d", priv->self, w, h);
+
+ cols = 2 + (int)ceil((float)w / (float)tw);
+ rows = 2 + (int)ceil((float)h / (float)th);
+
+ INF("o=%p new grid size cols: %ld, rows: %ld, was %ld, %ld",
+ priv->self, cols, rows, priv->view.cols, priv->view.rows);
+
+ if (priv->view.cols == cols && priv->view.rows == rows)
+ return;
+
+ _ewk_tiled_backing_store_pre_render_request_remove_associated(priv);
+
+ old_cols = priv->view.cols;
+ old_rows = priv->view.rows;
+
+ if (rows < old_rows) {
+ Eina_Inlist **start, **end;
+ start = priv->view.items + rows;
+ end = priv->view.items + old_rows;
+ _ewk_tiled_backing_store_view_rows_range_del(priv, start, end);
+ }
+ REALLOC_OR_OOM_RET(priv->view.items, sizeof(Eina_Inlist*) * (int)rows);
+ priv->view.rows = rows;
+ priv->view.cols = cols;
+ if (rows > old_rows) {
+ Eina_Inlist **start, **end;
+ start = priv->view.items + old_rows;
+ end = priv->view.items + rows;
+ for (; start < end; start++) {
+ Eina_Bool r;
+ *start = NULL;
+ r = _ewk_tiled_backing_store_view_cols_end_add
+ (priv, start, 0, cols);
+ if (!r) {
+ CRITICAL("failed to allocate %ld columns", cols);
+ _ewk_tiled_backing_store_view_rows_range_del
+ (priv, priv->view.items + old_rows, start);
+ priv->view.rows = old_rows;
+ return;
+ }
+ }
+ }
+
+ if (cols != old_cols) {
+ Eina_Inlist **start, **end;
+ int todo = cols - old_cols;
+ start = priv->view.items;
+ end = start + MIN(old_rows, rows);
+ if (todo > 0) {
+ for (; start < end; start++) {
+ Eina_Bool r;
+ r = _ewk_tiled_backing_store_view_cols_end_add
+ (priv, start, old_cols, todo);
+ if (!r) {
+ CRITICAL("failed to allocate %d columns!", todo);
+
+ for (start--; start >= priv->view.items; start--)
+ _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo);
+ if (rows > old_rows) {
+ start = priv->view.items + old_rows;
+ end = priv->view.items + rows;
+ for (; start < end; start++)
+ _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo);
+ }
+ return;
+ }
+ }
+ } else if (todo < 0) {
+ todo = -todo;
+ for (; start < end; start++)
+ _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo);
+ }
+ }
+
+ _ewk_tiled_backing_store_fill_renderers(priv);
+}
+
+static void _ewk_tiled_backing_store_smart_calculate_size(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord w, Evas_Coord h)
+{
+ evas_object_resize(priv->base.clipper, w, h);
+
+ priv->view.w = w;
+ priv->view.h = h;
+
+ _ewk_tiled_backing_store_recalc_renderers(
+ priv, w, h, priv->view.tile.w, priv->view.tile.h);
+}
+
+// TODO: remove me later.
+static inline void _ewk_tiled_backing_store_view_dbg(const Ewk_Tiled_Backing_Store_Data *priv)
+{
+ Eina_Inlist **start, **end;
+ printf("tiles=%2ld,%2ld model=%2ld,%2ld [%dx%d] base=%+3ld,%+4ld offset=%+4d,%+4d old=%+4d,%+4d base=%+3d,%+3d\n",
+ priv->view.cols, priv->view.rows,
+ priv->model.cur.cols, priv->model.cur.rows,
+ priv->model.width, priv->model.height,
+ priv->model.base.col, priv->model.base.row,
+ priv->view.offset.cur.x, priv->view.offset.cur.y,
+ priv->view.offset.old.x, priv->view.offset.old.y,
+ priv->view.offset.base.x, priv->view.offset.base.y);
+
+ start = priv->view.items;
+ end = priv->view.items + priv->view.rows;
+ for (; start < end; start++) {
+ const Ewk_Tiled_Backing_Store_Item *it;
+
+ EINA_INLIST_FOREACH(*start, it) {
+ printf(" %+4d,%+4d ", it->geometry.x, it->geometry.y);
+
+ if (!it->tile)
+ printf(" ;");
+ else
+ printf("%8p %lu,%lu;", it->tile, it->tile->col, it->tile->row);
+ }
+ printf("\n");
+ }
+ printf("---\n");
+}
+
+/**
+ * @internal
+ * Move top row down as last.
+ *
+ * The final result is visually the same, but logically the top that
+ * went out of screen is now at bottom and filled with new model items.
+ *
+ * This is worth just when @a count is smaller than @c
+ * priv->view.rows, after that one is refilling the whole matrix so it
+ * is better to trigger full refill.
+ *
+ * @param count the number of times to repeat the process.
+ */
+static void _ewk_tiled_backing_store_view_wrap_up(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
+{
+ unsigned int last_row = priv->view.rows - 1;
+ Evas_Coord tw = priv->view.tile.w;
+ Evas_Coord th = priv->view.tile.h;
+ Evas_Coord off_y = (priv->view.offset.base.y % th) - th;
+ Evas_Coord oy = y + (last_row - count + 1) * th + off_y;
+ Eina_Inlist **itr_start, **itr_end;
+
+ itr_start = priv->view.items;
+ itr_end = itr_start + last_row;
+
+ for (; count > 0; count--) {
+ Eina_Inlist **itr;
+ Eina_Inlist *tmp = *itr_start;
+ Ewk_Tiled_Backing_Store_Item *it;
+ Evas_Coord ox = x + priv->view.offset.base.x;
+ int c = 0;
+
+ for (itr = itr_start; itr < itr_end; itr++)
+ *itr = *(itr + 1);
+ *itr = tmp;
+
+ priv->model.base.row++;
+ EINA_INLIST_FOREACH(tmp, it) {
+ _ewk_tiled_backing_store_item_move(it, ox, oy);
+ ox += tw;
+ _ewk_tiled_backing_store_item_fill(priv, it, c, last_row);
+ c++;
+ }
+ oy += th;
+ }
+ priv->view.offset.base.y = off_y;
+}
+
+/**
+ * @internal
+ * Move bottom row up as first.
+ *
+ * The final result is visually the same, but logically the bottom that
+ * went out of screen is now at top and filled with new model items.
+ *
+ * This is worth just when @a count is smaller than @c
+ * priv->view.rows, after that one is refilling the whole matrix so it
+ * is better to trigger full refill.
+ *
+ * @param count the number of times to repeat the process.
+ */
+static void _ewk_tiled_backing_store_view_wrap_down(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
+{
+ Evas_Coord tw = priv->view.tile.w;
+ Evas_Coord th = priv->view.tile.h;
+ Evas_Coord off_y = (priv->view.offset.base.y % th) - th;
+ Evas_Coord oy = y + off_y + (count - 1) * th;
+ Eina_Inlist **itr_start, **itr_end;
+
+ itr_start = priv->view.items + priv->view.rows - 1;
+ itr_end = priv->view.items;
+
+ for (; count > 0; count--) {
+ Eina_Inlist **itr;
+ Eina_Inlist *tmp = *itr_start;
+ Ewk_Tiled_Backing_Store_Item *it;
+ Evas_Coord ox = x + priv->view.offset.base.x;
+ int c = 0;
+
+ for (itr = itr_start; itr > itr_end; itr--)
+ *itr = *(itr - 1);
+ *itr = tmp;
+
+ priv->model.base.row--;
+ EINA_INLIST_FOREACH(tmp, it) {
+ _ewk_tiled_backing_store_item_move(it, ox, oy);
+ ox += tw;
+ _ewk_tiled_backing_store_item_fill(priv, it, c, 0);
+ c++;
+ }
+ oy -= th;
+ }
+ priv->view.offset.base.y = off_y;
+}
+
+/**
+ * @internal
+ * Move left-most (first) column right as last (right-most).
+ *
+ * The final result is visually the same, but logically the first col that
+ * went out of screen is now at last and filled with new model items.
+ *
+ * This is worth just when @a count is smaller than @c
+ * priv->view.cols, after that one is refilling the whole matrix so it
+ * is better to trigger full refill.
+ *
+ * @param count the number of times to repeat the process.
+ */
+static void _ewk_tiled_backing_store_view_wrap_left(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
+{
+ unsigned int r, last_col = priv->view.cols - 1;
+ Evas_Coord tw = priv->view.tile.w;
+ Evas_Coord th = priv->view.tile.h;
+ Evas_Coord off_x = (priv->view.offset.base.x % tw) - tw;
+ Evas_Coord oy = y + priv->view.offset.base.y;
+ Eina_Inlist **itr;
+ Eina_Inlist **itr_end;
+
+ itr = priv->view.items;
+ itr_end = itr + priv->view.rows;
+ r = 0;
+
+ priv->model.base.col += count;
+
+ for (; itr < itr_end; itr++, r++) {
+ Evas_Coord ox = x + (last_col - count + 1) * tw + off_x;
+ unsigned int i, c = last_col - count + 1;
+
+ for (i = 0; i < count; i++, c++, ox += tw) {
+ Ewk_Tiled_Backing_Store_Item *it;
+ it = EINA_INLIST_CONTAINER_GET(*itr, Ewk_Tiled_Backing_Store_Item);
+ *itr = eina_inlist_demote(*itr, *itr);
+
+ _ewk_tiled_backing_store_item_move(it, ox, oy);
+ _ewk_tiled_backing_store_item_fill(priv, it, c, r);
+ }
+ oy += th;
+ }
+
+ priv->view.offset.base.x = off_x;
+}
+
+/**
+ * @internal
+ * Move right-most (last) column left as first (left-most).
+ *
+ * The final result is visually the same, but logically the last col that
+ * went out of screen is now at first and filled with new model items.
+ *
+ * This is worth just when @a count is smaller than @c
+ * priv->view.cols, after that one is refilling the whole matrix so it
+ * is better to trigger full refill.
+ *
+ * @param count the number of times to repeat the process.
+ */
+static void _ewk_tiled_backing_store_view_wrap_right(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
+{
+ unsigned int r;
+ Evas_Coord tw = priv->view.tile.w;
+ Evas_Coord th = priv->view.tile.h;
+ Evas_Coord off_x = (priv->view.offset.base.x % tw) - tw;
+ Evas_Coord oy = y + priv->view.offset.base.y;
+ Eina_Inlist **itr, **itr_end;
+
+ itr = priv->view.items;
+ itr_end = itr + priv->view.rows;
+ r = 0;
+
+ priv->model.base.col -= count;
+
+ for (; itr < itr_end; itr++, r++) {
+ Evas_Coord ox = x + (count - 1) * tw + off_x;
+ unsigned int i, c = count - 1;
+
+ for (i = 0; i < count; i++, c--, ox -= tw) {
+ Ewk_Tiled_Backing_Store_Item *it;
+ it = EINA_INLIST_CONTAINER_GET((*itr)->last, Ewk_Tiled_Backing_Store_Item);
+ *itr = eina_inlist_promote(*itr, (*itr)->last);
+
+ _ewk_tiled_backing_store_item_move(it, ox, oy);
+ _ewk_tiled_backing_store_item_fill(priv, it, c, r);
+ }
+ oy += th;
+ }
+
+ priv->view.offset.base.x = off_x;
+}
+
+static void _ewk_tiled_backing_store_view_refill(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, int step_x, int step_y)
+{
+ Eina_Inlist **itr, **itr_end;
+ Evas_Coord base_ox, oy, tw, th;
+ unsigned int r;
+
+ evas_object_move(priv->base.clipper, x, y);
+
+ tw = priv->view.tile.w;
+ th = priv->view.tile.h;
+
+ base_ox = x + priv->view.offset.base.x;
+ oy = y + priv->view.offset.base.y;
+
+ itr = priv->view.items;
+ itr_end = itr + priv->view.rows;
+ r = 0;
+
+ priv->model.base.col -= step_x;
+ priv->model.base.row -= step_y;
+
+ for (; itr < itr_end; itr++, r++) {
+ Ewk_Tiled_Backing_Store_Item *it;
+ Evas_Coord ox = base_ox;
+ unsigned int c = 0;
+ EINA_INLIST_FOREACH(*itr, it) {
+ _ewk_tiled_backing_store_item_fill(priv, it, c, r);
+ _ewk_tiled_backing_store_item_move(it, ox, oy);
+ c++;
+ ox += tw;
+ }
+ oy += th;
+ }
+}
+
+static void _ewk_tiled_backing_store_view_pos_apply(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
+{
+ Eina_Inlist **itr, **itr_end;
+ Evas_Coord base_ox, oy, tw, th;
+
+ evas_object_move(priv->base.clipper, x, y);
+
+ tw = priv->view.tile.w;
+ th = priv->view.tile.h;
+
+ base_ox = x + priv->view.offset.base.x;
+ oy = y + priv->view.offset.base.y;
+
+ itr = priv->view.items;
+ itr_end = itr + priv->view.rows;
+ for (; itr < itr_end; itr++) {
+ Ewk_Tiled_Backing_Store_Item *it;
+ Evas_Coord ox = base_ox;
+ EINA_INLIST_FOREACH(*itr, it) {
+ _ewk_tiled_backing_store_item_move(it, ox, oy);
+ ox += tw;
+ }
+ oy += th;
+ }
+}
+
+static void _ewk_tiled_backing_store_smart_calculate_offset_force(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ Evas_Coord dx = priv->view.offset.cur.x - priv->view.offset.old.x;
+ Evas_Coord dy = priv->view.offset.cur.y - priv->view.offset.old.y;
+ Evas_Coord tw, th;
+ int step_y, step_x;
+
+ INF("o=%p, offset: %+4d, %+4d (%+4d, %+4d)",
+ priv->self, dx, dy, priv->view.offset.cur.x, priv->view.offset.cur.y);
+
+ tw = priv->view.tile.w;
+ th = priv->view.tile.h;
+
+ step_x = (dx + priv->view.offset.base.x + tw) / tw;
+ step_y = (dy + priv->view.offset.base.y + th) / th;
+
+ priv->view.offset.old.x = priv->view.offset.cur.x;
+ priv->view.offset.old.y = priv->view.offset.cur.y;
+ evas_object_move(
+ priv->contents_clipper,
+ priv->view.offset.cur.x + priv->view.x,
+ priv->view.offset.cur.y + priv->view.y);
+
+ priv->view.offset.base.x += dx - step_x * tw;
+ priv->view.offset.base.y += dy - step_y * th;
+
+ _ewk_tiled_backing_store_view_refill
+ (priv, priv->view.x, priv->view.y, step_x, step_y);
+}
+
+static void _ewk_tiled_backing_store_smart_calculate_offset(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
+{
+ Evas_Coord dx = priv->view.offset.cur.x - priv->view.offset.old.x;
+ Evas_Coord dy = priv->view.offset.cur.y - priv->view.offset.old.y;
+ Evas_Coord tw, th;
+ int step_y, step_x;
+
+ INF("o=%p, offset: %+4d, %+4d (%+4d, %+4d)",
+ priv->self, dx, dy, priv->view.offset.cur.x, priv->view.offset.cur.y);
+
+ if (!dx && !dy)
+ return;
+
+ tw = priv->view.tile.w;
+ th = priv->view.tile.h;
+
+ step_x = (dx + priv->view.offset.base.x + tw) / tw;
+ step_y = (dy + priv->view.offset.base.y + th) / th;
+
+ priv->view.offset.old.x = priv->view.offset.cur.x;
+ priv->view.offset.old.y = priv->view.offset.cur.y;
+ evas_object_move(
+ priv->contents_clipper,
+ priv->view.offset.cur.x + priv->view.x,
+ priv->view.offset.cur.y + priv->view.y);
+
+ if ((step_x < 0 && step_x <= -priv->view.cols)
+ || (step_x > 0 && step_x >= priv->view.cols)
+ || (step_y < 0 && step_y <= -priv->view.rows)
+ || (step_y > 0 && step_y >= priv->view.rows)) {
+
+ priv->view.offset.base.x += dx - step_x * tw;
+ priv->view.offset.base.y += dy - step_y * th;
+
+ _ewk_tiled_backing_store_view_refill
+ (priv, priv->view.x, priv->view.y, step_x, step_y);
+ return;
+ }
+
+ priv->view.offset.base.x += dx;
+ priv->view.offset.base.y += dy;
+
+ if (step_y < 0)
+ _ewk_tiled_backing_store_view_wrap_up(priv, x, y, -step_y);
+ else if (step_y > 0)
+ _ewk_tiled_backing_store_view_wrap_down(priv, x, y, step_y);
+
+ if (step_x < 0)
+ _ewk_tiled_backing_store_view_wrap_left(priv, x, y, -step_x);
+ else if (step_x > 0)
+ _ewk_tiled_backing_store_view_wrap_right(priv, x, y, step_x);
+
+ _ewk_tiled_backing_store_view_pos_apply(priv, x, y);
+}
+
+static void _ewk_tiled_backing_store_smart_calculate_pos(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
+{
+ _ewk_tiled_backing_store_view_pos_apply(priv, x, y);
+ priv->view.x = x;
+ priv->view.y = y;
+ evas_object_move(
+ priv->contents_clipper,
+ priv->view.offset.cur.x + priv->view.x,
+ priv->view.offset.cur.y + priv->view.y);
+}
+
+static void _ewk_tiled_backing_store_fill_renderers(Ewk_Tiled_Backing_Store_Data *priv)
+{
+ Eina_Inlist *it;
+ Ewk_Tiled_Backing_Store_Item *item;
+ int i, j;
+
+ for (i = 0; i < priv->view.rows; i++) {
+ it = priv->view.items[i];
+ j = 0;
+ EINA_INLIST_FOREACH(it, item)
+ _ewk_tiled_backing_store_item_fill(priv, item, j++, i);
+ }
+}
+
+static void _ewk_tiled_backing_store_smart_calculate(Evas_Object *o)
+{
+ Evas_Coord x, y, w, h;
+
+ evas_object_geometry_get(o, &x, &y, &w, &h);
+ DBG("o=%p at %d,%d + %dx%d", o, x, y, w, h);
+
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+
+ priv->changed.any = EINA_FALSE;
+
+ ewk_tile_matrix_freeze(priv->model.matrix);
+
+ if (!priv->render.suspend && priv->changed.model) {
+ long cols, rows;
+
+ cols = priv->model.width / priv->view.tile.w + 1;
+ rows = priv->model.height / priv->view.tile.h + 1;
+
+ priv->model.old.cols = priv->model.cur.cols;
+ priv->model.old.rows = priv->model.cur.rows;
+ priv->model.cur.cols = cols;
+ priv->model.cur.rows = rows;
+ if (priv->model.old.cols > cols)
+ cols = priv->model.old.cols;
+ if (priv->model.old.rows > rows)
+ rows = priv->model.old.rows;
+ ewk_tile_matrix_resize(priv->model.matrix, cols, rows);
+ }
+
+ if (priv->changed.pos && (priv->view.x != x || priv->view.y != y)) {
+ _ewk_tiled_backing_store_smart_calculate_pos(priv, x, y);
+ priv->changed.pos = EINA_FALSE;
+ } else if (priv->changed.offset) {
+ _ewk_tiled_backing_store_smart_calculate_offset(priv, x, y);
+ priv->changed.offset = EINA_FALSE;
+ }
+
+ if (priv->changed.size) {
+ _ewk_tiled_backing_store_smart_calculate_size(priv, w, h);
+ priv->changed.size = EINA_FALSE;
+ }
+
+ if (!priv->render.suspend && priv->changed.model) {
+ Eina_Rectangle rect;
+ rect.x = 0;
+ rect.y = 0;
+ rect.w = priv->model.width;
+ rect.h = priv->model.height;
+ _ewk_tiled_backing_store_fill_renderers(priv);
+ ewk_tile_matrix_resize(priv->model.matrix,
+ priv->model.cur.cols,
+ priv->model.cur.rows);
+ priv->changed.model = EINA_FALSE;
+ evas_object_resize(priv->contents_clipper,
+ priv->model.width, priv->model.height);
+ _ewk_tiled_backing_store_smart_calculate_offset_force(priv);
+
+ /* Make sure we do not miss any important repaint by
+ * repainting the whole viewport */
+ const Eina_Rectangle r =
+ { 0, 0, priv->model.width, priv->model.height };
+ ewk_tile_matrix_update(priv->model.matrix, &r,
+ priv->view.tile.zoom);
+ }
+
+ ewk_tile_matrix_thaw(priv->model.matrix);
+
+ _ewk_tiled_backing_store_updates_process(priv);
+
+ if (priv->view.offset.base.x >= 0
+ || priv->view.offset.base.x <= -2 * priv->view.tile.w
+ || priv->view.offset.base.y >= 0
+ || priv->view.offset.base.y <= -2 * priv->view.tile.h)
+ ERR("incorrect base offset %+4d,%+4d, tile=%dx%d, cur=%+4d,%+4d\n",
+ priv->view.offset.base.x, priv->view.offset.base.y,
+ priv->view.tile.w, priv->view.tile.h,
+ priv->view.offset.cur.x, priv->view.offset.cur.y);
+
+}
+
+Evas_Object *ewk_tiled_backing_store_add(Evas *e)
+{
+ static Evas_Smart *smart = NULL;
+
+ if (_ewk_tiled_log_dom < 0)
+ _ewk_tiled_log_dom = eina_log_domain_register("Ewk_Tiled_Backing_Store", NULL);
+
+ if (!smart) {
+ static Evas_Smart_Class sc =
+ EVAS_SMART_CLASS_INIT_NAME_VERSION("Ewk_Tiled_Backing_Store");
+
+ evas_object_smart_clipped_smart_set(&sc);
+ _parent_sc = sc;
+
+ sc.add = _ewk_tiled_backing_store_smart_add;
+ sc.del = _ewk_tiled_backing_store_smart_del;
+ sc.resize = _ewk_tiled_backing_store_smart_resize;
+ sc.move = _ewk_tiled_backing_store_smart_move;
+ sc.calculate = _ewk_tiled_backing_store_smart_calculate;
+ sc.member_add = _ewk_tiled_backing_store_smart_member_add;
+ sc.member_del = _ewk_tiled_backing_store_smart_member_del;
+
+ smart = evas_smart_class_new(&sc);
+ }
+
+ return evas_object_smart_add(e, smart);
+}
+
+void ewk_tiled_backing_store_render_cb_set(Evas_Object *o, Eina_Bool (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *area), const void *data)
+{
+ EINA_SAFETY_ON_NULL_RETURN(cb);
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ priv->render.cb = cb;
+ priv->render.data = (void*)data;
+}
+
+Ewk_Tile_Unused_Cache *ewk_tiled_backing_store_tile_unused_cache_get(const Evas_Object *o)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv, NULL);
+ return ewk_tile_matrix_unused_cache_get(priv->model.matrix);
+}
+
+void ewk_tiled_backing_store_tile_unused_cache_set(Evas_Object *o, Ewk_Tile_Unused_Cache *tuc)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+
+ if (ewk_tile_matrix_unused_cache_get(priv->model.matrix) == tuc)
+ return;
+
+ _ewk_tiled_backing_store_model_matrix_create(priv, tuc);
+}
+
+static Eina_Bool _ewk_tiled_backing_store_scroll_full_offset_set_internal(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
+{
+ /* TODO: check offset go out of bounds, clamp */
+ if (priv->render.disabled)
+ return EINA_FALSE;
+
+ priv->view.offset.cur.x = x;
+ priv->view.offset.cur.y = y;
+
+ priv->changed.offset = EINA_TRUE;
+ _ewk_tiled_backing_store_changed(priv);
+
+ return EINA_TRUE;
+}
+
+Eina_Bool ewk_tiled_backing_store_scroll_full_offset_set(Evas_Object *o, Evas_Coord x, Evas_Coord y)
+{
+ DBG("o=%p, x=%d, y=%d", o, x, y);
+
+ PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
+ if (x == priv->view.offset.cur.x && y == priv->view.offset.cur.y)
+ return EINA_TRUE;
+
+ return _ewk_tiled_backing_store_scroll_full_offset_set_internal(priv, x, y);
+}
+
+Eina_Bool ewk_tiled_backing_store_scroll_full_offset_add(Evas_Object *o, Evas_Coord dx, Evas_Coord dy)
+{
+ DBG("o=%p, dx=%d, dy=%d", o, dx, dy);
+
+ PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
+ if (!dx && !dy)
+ return EINA_TRUE;
+
+ return _ewk_tiled_backing_store_scroll_full_offset_set_internal
+ (priv, priv->view.offset.cur.x + dx, priv->view.offset.cur.y + dy);
+}
+
+static Eina_Bool _ewk_tiled_backing_store_zoom_set_internal(Ewk_Tiled_Backing_Store_Data *priv, float *zoom, Evas_Coord cx, Evas_Coord cy, Evas_Coord *offx, Evas_Coord *offy)
+{
+ *offx = priv->view.offset.cur.x;
+ *offy = priv->view.offset.cur.y;
+
+ if (fabsf(priv->view.tile.zoom - *zoom) < ZOOM_STEP_MIN) {
+ DBG("ignored as zoom difference is < %f: %f",
+ (double)ZOOM_STEP_MIN, fabsf(priv->view.tile.zoom - *zoom));
+ return EINA_TRUE;
+ }
+
+ _ewk_tiled_backing_store_pre_render_request_flush(priv);
+ Evas_Coord tw, th;
+ tw = TILE_SIZE_AT_ZOOM(TILE_W, *zoom);
+ tw = (tw >> 1) << 1;
+ *zoom = TILE_W_ZOOM_AT_SIZE(tw);
+ /* WARNING: assume reverse zoom is the same for both axis */
+ th = TILE_SIZE_AT_ZOOM(TILE_H, *zoom);
+
+ float scale = *zoom / priv->view.tile.zoom;
+
+ priv->view.tile.zoom = *zoom;
+ // todo: check cx [0, w]...
+ priv->view.offset.zoom_center.x = cx;
+ priv->view.offset.zoom_center.y = cy;
+
+ priv->view.tile.w = tw;
+ priv->view.tile.h = th;
+
+ if (!priv->view.w || !priv->view.h) {
+ priv->view.offset.base.x = -tw;
+ priv->view.offset.base.y = -th;
+ return EINA_TRUE;
+ }
+ Eina_Inlist **itr, **itr_end;
+ Ewk_Tiled_Backing_Store_Item *it;
+
+ Evas_Coord new_x = cx + (priv->view.offset.cur.x - cx) * scale;
+ Evas_Coord new_y = cy + (priv->view.offset.cur.y - cy) * scale;
+ Evas_Coord bx = cx + (priv->view.offset.base.x - cx) * scale;
+ Evas_Coord by = cy + (priv->view.offset.base.y - cy) * scale;
+
+ Evas_Coord model_width = priv->model.width * scale;
+ Evas_Coord model_height = priv->model.height * scale;
+
+ if (model_width < priv->view.w || new_x >= 0)
+ new_x = 0;
+ else if (-new_x + priv->view.w >= model_width)
+ new_x = -model_width + priv->view.w;
+
+ if (model_height < priv->view.h || new_y >= 0)
+ new_y = 0;
+ else if (-new_y + priv->view.h >= model_height)
+ new_y = -model_height + priv->view.h;
+
+ bx = new_x % tw - tw;
+ priv->model.base.col = - new_x / tw - 1;
+ by = new_y % th - th;
+ priv->model.base.row = - new_y / th - 1;
+
+ priv->changed.size = EINA_TRUE;
+ _ewk_tiled_backing_store_changed(priv);
+
+ priv->view.offset.cur.x = new_x;
+ priv->view.offset.cur.y = new_y;
+ priv->view.offset.base.x = bx;
+ priv->view.offset.base.y = by;
+
+ priv->view.offset.old.x = priv->view.offset.cur.x;
+ priv->view.offset.old.y = priv->view.offset.cur.y;
+ *offx = priv->view.offset.cur.x;
+ *offy = priv->view.offset.cur.y;
+
+ evas_object_move(
+ priv->contents_clipper,
+ new_x + priv->view.x,
+ new_y + priv->view.y);
+
+ _ewk_tiled_backing_store_fill_renderers(priv);
+
+ Evas_Coord oy = priv->view.offset.base.y + priv->view.y;
+ Evas_Coord base_ox = priv->view.x + priv->view.offset.base.x;
+
+ itr = priv->view.items;
+ itr_end = itr + priv->view.rows;
+
+ for (; itr < itr_end; itr++) {
+ Evas_Coord ox = base_ox;
+ Eina_Inlist *lst = *itr;
+
+ EINA_INLIST_FOREACH(lst, it) {
+ _ewk_tiled_backing_store_item_move(it, ox, oy);
+ _ewk_tiled_backing_store_item_resize(it, tw, th);
+ ox += tw;
+ }
+ oy += th;
+ }
+
+ return EINA_TRUE;
+}
+
+Eina_Bool ewk_tiled_backing_store_zoom_set(Evas_Object *o, float *zoom, Evas_Coord cx, Evas_Coord cy, Evas_Coord *offx, Evas_Coord *offy)
+{
+ DBG("o=%p, zoom=%f", o, (double)*zoom);
+
+ PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
+
+ return _ewk_tiled_backing_store_zoom_set_internal(priv, zoom, cx, cy, offx, offy);
+}
+
+Eina_Bool ewk_tiled_backing_store_zoom_weak_set(Evas_Object *o, float zoom, Evas_Coord cx, Evas_Coord cy)
+{
+ DBG("o=%p, zoom=%f", o, (double)zoom);
+ PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
+ if (!priv->view.w || !priv->view.h)
+ return EINA_FALSE;
+ Eina_Inlist **itr, **itr_end;
+ Ewk_Tiled_Backing_Store_Item *it;
+ Evas_Coord tw, th;
+ Eina_Bool recalc = EINA_FALSE;
+
+ tw = TILE_SIZE_AT_ZOOM(TILE_W, zoom);
+ zoom = TILE_W_ZOOM_AT_SIZE(tw);
+ /* WARNING: assume reverse zoom is the same for both axis */
+ th = TILE_SIZE_AT_ZOOM(TILE_H, zoom);
+
+ float scale = zoom / priv->view.tile.zoom;
+
+ Evas_Coord model_width = priv->model.width * scale;
+ Evas_Coord model_height = priv->model.height * scale;
+
+ evas_object_resize(priv->contents_clipper,
+ model_width, model_height);
+
+ int vrows = ceil((float)priv->view.h / (float)th) + 2;
+ int vcols = ceil((float)priv->view.w / (float)tw) + 2;
+ Evas_Coord new_x = cx + (priv->view.offset.cur.x - cx) * scale;
+ Evas_Coord new_y = cy + (priv->view.offset.cur.y - cy) * scale;
+ Evas_Coord bx = new_x % tw - tw;
+ Evas_Coord by = new_y % th - th;
+ unsigned long base_row = -new_y / th - 1;
+ unsigned long base_col = -new_x / tw - 1;
+
+ if (base_row != priv->model.base.row || base_col != priv->model.base.col) {
+ priv->model.base.row = base_row;
+ priv->model.base.col = base_col;
+ recalc = EINA_TRUE;
+ }
+
+ if (vrows > priv->view.rows || vcols > priv->view.cols)
+ recalc = EINA_TRUE;
+
+ if (recalc) {
+ Evas_Coord w, h;
+ evas_object_geometry_get(o, NULL, NULL, &w, &h);
+ _ewk_tiled_backing_store_recalc_renderers(priv, w, h, tw, th);
+ _ewk_tiled_backing_store_fill_renderers(priv);
+ _ewk_tiled_backing_store_updates_process(priv);
+ }
+
+ Evas_Coord base_ox = bx + priv->view.x;
+ Evas_Coord oy = by + priv->view.y;
+
+ evas_object_move(priv->contents_clipper,
+ new_x + priv->view.x,
+ new_y + priv->view.y);
+
+ itr = priv->view.items;
+ itr_end = itr + priv->view.rows;
+
+ for (; itr < itr_end; itr++) {
+ Evas_Coord ox = base_ox;
+ Eina_Inlist *lst = *itr;
+
+ EINA_INLIST_FOREACH(lst, it) {
+ _ewk_tiled_backing_store_item_move(it, ox, oy);
+ _ewk_tiled_backing_store_item_resize(it, tw, th);
+ ox += tw;
+ }
+ oy += th;
+ }
+
+ return EINA_TRUE;
+}
+
+void ewk_tiled_backing_store_fix_offsets(Evas_Object *o, Evas_Coord w, Evas_Coord h)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ Eina_Inlist **itr, **itr_end;
+ Ewk_Tiled_Backing_Store_Item *it;
+ Evas_Coord new_x = priv->view.offset.cur.x;
+ Evas_Coord new_y = priv->view.offset.cur.y;
+ Evas_Coord bx = priv->view.offset.base.x;
+ Evas_Coord by = priv->view.offset.base.y;
+ Evas_Coord tw = priv->view.tile.w;
+ Evas_Coord th = priv->view.tile.h;
+
+ if (-new_x > w) {
+ new_x = -w;
+ bx = new_x % tw - tw;
+ priv->model.base.col = -new_x / tw - 1;
+ }
+
+ if (-new_y > h) {
+ new_y = -h;
+ by = new_y % th - th;
+ priv->model.base.row = -new_y / th - 1;
+ }
+
+ if (bx >= 0 || bx <= -2 * priv->view.tile.w) {
+ bx = new_x % tw - tw;
+ priv->model.base.col = -new_x / tw - 1;
+ }
+
+ if (by >= 0 || by <= -2 * priv->view.tile.h) {
+ by = new_y % th - th;
+ priv->model.base.row = -new_y / th - 1;
+ }
+
+ priv->view.offset.cur.x = new_x;
+ priv->view.offset.cur.y = new_y;
+ priv->view.offset.old.x = new_x;
+ priv->view.offset.old.y = new_y;
+ priv->view.offset.base.x = bx;
+ priv->view.offset.base.y = by;
+ evas_object_move(priv->contents_clipper,
+ new_x + priv->view.x,
+ new_y + priv->view.y);
+
+ Evas_Coord oy = priv->view.offset.base.y + priv->view.y;
+ Evas_Coord base_ox = priv->view.x + priv->view.offset.base.x;
+
+ itr = priv->view.items;
+ itr_end = itr + priv->view.rows;
+
+ for (; itr < itr_end; itr++) {
+ Evas_Coord ox = base_ox;
+ Eina_Inlist *lst = *itr;
+
+ EINA_INLIST_FOREACH(lst, it) {
+ _ewk_tiled_backing_store_item_move(it, ox, oy);
+ _ewk_tiled_backing_store_item_resize(it, tw, th);
+ ox += tw;
+ }
+ oy += th;
+ }
+}
+
+void ewk_tiled_backing_store_zoom_weak_smooth_scale_set(Evas_Object *o, Eina_Bool smooth_scale)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ Eina_Inlist **itr, **itr_end;
+
+ itr = priv->view.items;
+ itr_end = itr + priv->view.rows;
+ priv->view.tile.zoom_weak_smooth_scale = smooth_scale;
+
+ for (; itr< itr_end; itr++) {
+ Ewk_Tiled_Backing_Store_Item *it;
+ EINA_INLIST_FOREACH(*itr, it)
+ if (it->tile)
+ _ewk_tiled_backing_store_item_smooth_scale_set
+ (it, smooth_scale);
+ }
+}
+
+Eina_Bool ewk_tiled_backing_store_update(Evas_Object *o, const Eina_Rectangle *update)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
+
+ if (priv->render.disabled)
+ return EINA_FALSE;
+
+ return ewk_tile_matrix_update(priv->model.matrix, update,
+ priv->view.tile.zoom);
+}
+
+void ewk_tiled_backing_store_updates_process_pre_set(Evas_Object *o, void *(*cb)(void *data, Evas_Object *o), const void *data)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ priv->process.pre_cb = cb;
+ priv->process.pre_data = (void*)data;
+}
+
+void ewk_tiled_backing_store_updates_process_post_set(Evas_Object *o, void *(*cb)(void *data, void *pre_data, Evas_Object *o), const void *data)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ priv->process.post_cb = cb;
+ priv->process.post_data = (void*)data;
+}
+
+void ewk_tiled_backing_store_updates_process(Evas_Object *o)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ _ewk_tiled_backing_store_updates_process(priv);
+}
+
+void ewk_tiled_backing_store_updates_clear(Evas_Object *o)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+
+ ewk_tile_matrix_updates_clear(priv->model.matrix);
+}
+
+void ewk_tiled_backing_store_contents_resize(Evas_Object *o, Evas_Coord width, Evas_Coord height)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+
+ if (width == priv->model.width && height == priv->model.height)
+ return;
+
+ priv->model.width = width;
+ priv->model.height = height;
+ priv->changed.model = EINA_TRUE;
+
+ DBG("width,height=%d, %d", width, height);
+ _ewk_tiled_backing_store_changed(priv);
+}
+
+void ewk_tiled_backing_store_disabled_update_set(Evas_Object *o, Eina_Bool value)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+
+ if (value != priv->render.disabled)
+ priv->render.disabled = value;
+}
+
+void ewk_tiled_backing_store_flush(Evas_Object *o)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ Ewk_Tile_Unused_Cache *tuc = NULL;
+
+ priv->view.offset.cur.x = 0;
+ priv->view.offset.cur.y = 0;
+ priv->view.offset.old.x = 0;
+ priv->view.offset.old.y = 0;
+ priv->view.offset.base.x = -priv->view.tile.w;
+ priv->view.offset.base.y = -priv->view.tile.h;
+ priv->model.base.col = -1;
+ priv->model.base.row = -1;
+ priv->changed.size = EINA_TRUE;
+
+#ifdef DEBUG_MEM_LEAKS
+ printf("\nFLUSHED BACKING STORE, STATUS BEFORE DELETING TILE MATRIX:\n");
+ _ewk_tiled_backing_store_mem_dbg(priv);
+#endif
+
+ _ewk_tiled_backing_store_pre_render_request_flush(priv);
+ _ewk_tiled_backing_store_tile_dissociate_all(priv);
+ tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
+ ewk_tile_unused_cache_clear(tuc);
+
+#ifdef DEBUG_MEM_LEAKS
+ printf("\nFLUSHED BACKING STORE, STATUS AFTER RECREATING TILE MATRIX:\n");
+ _ewk_tiled_backing_store_mem_dbg(priv);
+#endif
+}
+
+Eina_Bool ewk_tiled_backing_store_pre_render_region(Evas_Object *o, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
+ Eina_Tile_Grid_Slicer slicer;
+ const Eina_Tile_Grid_Info *info;
+ Evas_Coord tw, th;
+ Ewk_Tile_Unused_Cache *tuc;
+
+ tw = TILE_SIZE_AT_ZOOM(TILE_W, zoom);
+ tw = (tw >> 1) << 1;
+ zoom = TILE_W_ZOOM_AT_SIZE(tw);
+ /* WARNING: assume reverse zoom is the same for both axis */
+ th = TILE_SIZE_AT_ZOOM(TILE_H, zoom);
+
+ if (!eina_tile_grid_slicer_setup(&slicer, x, y, w, h, tw, th)) {
+ ERR("could not setup grid slicer for %d,%d+%dx%d tile=%dx%d",
+ x, y, w, h, tw, th);
+ return EINA_FALSE;
+ }
+
+ while (eina_tile_grid_slicer_next(&slicer, &info)) {
+ const unsigned long c = info->col;
+ const unsigned long r = info->row;
+ if (!_ewk_tiled_backing_store_pre_render_request_add(priv, c, r, zoom, NULL, PRE_RENDER_PRIORITY_LOW))
+ break;
+ }
+
+ _ewk_tiled_backing_store_item_process_idler_start(priv);
+
+ tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
+ ewk_tile_unused_cache_lock_area(tuc, x, y, w, h, zoom);
+ return EINA_TRUE;
+}
+
+Eina_Bool ewk_tiled_backing_store_pre_render_relative_radius(Evas_Object *o, unsigned int n, float zoom)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
+ unsigned long start_row, end_row, start_col, end_col, i, j, w, h;
+ Ewk_Tile_Unused_Cache *tuc;
+
+ INF("priv->model.base.row =%ld, n=%u priv->view.rows=%lu",
+ priv->model.base.row, n, priv->view.rows);
+ start_row = (long)priv->model.base.row - n;
+ start_col = (long)priv->model.base.col - n;
+ end_row = MIN(priv->model.cur.rows - 1,
+ priv->model.base.row + priv->view.rows + n - 1);
+ end_col = MIN(priv->model.cur.cols - 1,
+ priv->model.base.col + priv->view.cols + n - 1);
+
+ INF("start_row=%lu, end_row=%lu, start_col=%lu, end_col=%lu",
+ start_row, end_row, start_col, end_col);
+
+ for (i = start_row; i <= end_row; i++)
+ for (j = start_col; j <= end_col; j++)
+ if (!_ewk_tiled_backing_store_pre_render_request_add(priv, j, i, zoom, NULL, PRE_RENDER_PRIORITY_LOW))
+ goto start_processing;
+
+start_processing:
+ _ewk_tiled_backing_store_item_process_idler_start(priv);
+
+ tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
+ h = (end_row - start_row + 1) * TILE_SIZE_AT_ZOOM(TILE_H, zoom);
+ w = (end_col - start_col + 1) * TILE_SIZE_AT_ZOOM(TILE_W, zoom);
+ ewk_tile_unused_cache_lock_area(tuc,
+ start_col * TILE_SIZE_AT_ZOOM(TILE_W, zoom),
+ start_row * TILE_SIZE_AT_ZOOM(TILE_H, zoom), w, h, zoom);
+
+ return EINA_TRUE;
+}
+
+void ewk_tiled_backing_store_pre_render_cancel(Evas_Object *o)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ Ewk_Tile_Unused_Cache *tuc;
+
+ _ewk_tiled_backing_store_pre_render_request_remove_unassociated(priv);
+
+ tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
+ ewk_tile_unused_cache_unlock_area(tuc);
+}
+
+Eina_Bool ewk_tiled_backing_store_disable_render(Evas_Object *o)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
+ return _ewk_tiled_backing_store_disable_render(priv);
+}
+
+Eina_Bool ewk_tiled_backing_store_enable_render(Evas_Object *o)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
+ _ewk_tiled_backing_store_changed(priv);
+ return _ewk_tiled_backing_store_enable_render(priv);
+}
+
+/**
+ * Set the process_entire_queue flag of the renderer idler.
+ *
+ *
+ * @param o the tiled backing store object
+ * @param value EINA_TRUE if we want to process all the request of our queue. EINA_FALSE otherwise.
+ */
+void ewk_tiled_backing_store_process_entire_queue_set(Evas_Object *o, Eina_Bool value)
+{
+ PRIV_DATA_GET_OR_RETURN(o, priv);
+ if (priv->render.process_entire_queue != value)
+ priv->render.process_entire_queue = value;
+}
diff --git a/WebKit/efl/ewk/ewk_tiled_backing_store.h b/WebKit/efl/ewk/ewk_tiled_backing_store.h
new file mode 100644
index 0000000..c8cdbb8
--- /dev/null
+++ b/WebKit/efl/ewk/ewk_tiled_backing_store.h
@@ -0,0 +1,130 @@
+/*
+ Copyright (C) 2009-2010 Samsung Electronics
+ Copyright (C) 2009-2010 ProFUSION embedded systems
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef ewk_tiled_backing_store_h
+#define ewk_tiled_backing_store_h
+
+#include "EWebKit.h"
+
+/* Enable accounting of render time in tile statistics */
+// #define TILE_STATS_ACCOUNT_RENDER_TIME
+
+
+/* If define ewk will do more accounting to check for memory leaks
+ * try "kill -USR1 $PID" to get instantaneous debug
+ * try "kill -USR2 $PID" to get instantaneous debug and force flush of cache
+ */
+#define DEBUG_MEM_LEAKS 1
+
+#define TILE_W (256)
+#define TILE_H (256)
+
+#define ZOOM_STEP_MIN (0.01)
+
+#define TILE_SIZE_AT_ZOOM(SIZE, ZOOM) ((int)roundf((SIZE) * (ZOOM)))
+#define TILE_W_ZOOM_AT_SIZE(SIZE) ((float)SIZE / (float)TILE_W)
+#define TILE_H_ZOOM_AT_SIZE(SIZE) ((float)SIZE / (float)TILE_H)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <Evas.h>
+#include <cairo.h>
+
+typedef struct _Ewk_Tile Ewk_Tile;
+typedef struct _Ewk_Tile_Stats Ewk_Tile_Stats;
+typedef struct _Ewk_Tile_Matrix Ewk_Tile_Matrix;
+
+struct _Ewk_Tile_Stats {
+ double last_used; /**< time of last use */
+#ifdef TILE_STATS_ACCOUNT_RENDER_TIME
+ double render_time; /**< amount of time this tile took to render */
+#endif
+ unsigned int area; /**< cache for (w * h) */
+ unsigned int misses; /**< number of times it became dirty but not
+ * repainted at all since it was not visible.
+ */
+ Eina_Bool full_update:1; /**< tile requires full size update */
+};
+
+struct _Ewk_Tile {
+ EINA_INLIST; /**< sibling tiles at same (i, j) but other zoom */
+ Eina_Tiler *updates; /**< updated/dirty areas */
+ Ewk_Tile_Stats stats; /**< tile usage statistics */
+ unsigned long col, row; /**< tile tile position */
+ Evas_Coord x, y; /**< tile coordinate position */
+
+ /* TODO: does it worth to keep those or create on demand? */
+ cairo_surface_t *surface;
+ cairo_t *cairo;
+
+ /** Never ever change those after tile is created (respect const!) */
+ const Evas_Coord w, h; /**< tile size (see TILE_SIZE_AT_ZOOM()) */
+ const Evas_Colorspace cspace; /**< colorspace */
+ const float zoom; /**< zoom level contents were rendered at */
+ const size_t bytes; /**< bytes used in pixels. keep
+ * before pixels to guarantee
+ * alignement!
+ */
+ int visible; /**< visibility counter of this tile */
+ Evas_Object *image; /**< Evas Image, the tile to be rendered */
+ uint8_t *pixels;
+};
+
+#include "ewk_tiled_matrix.h"
+#include "ewk_tiled_model.h"
+
+/* view */
+EAPI Evas_Object *ewk_tiled_backing_store_add(Evas *e);
+
+EAPI void ewk_tiled_backing_store_render_cb_set(Evas_Object *o, Eina_Bool (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *area), const void *data);
+
+EAPI Eina_Bool ewk_tiled_backing_store_scroll_full_offset_set(Evas_Object *o, Evas_Coord x, Evas_Coord y);
+EAPI Eina_Bool ewk_tiled_backing_store_scroll_full_offset_add(Evas_Object *o, Evas_Coord dx, Evas_Coord dy);
+EAPI Eina_Bool ewk_tiled_backing_store_scroll_inner_offset_add(Evas_Object *o, Evas_Coord dx, Evas_Coord dy, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h);
+
+EAPI Eina_Bool ewk_tiled_backing_store_zoom_set(Evas_Object *o, float *zoom, Evas_Coord cx, Evas_Coord cy, Evas_Coord *offx, Evas_Coord *offy);
+EAPI Eina_Bool ewk_tiled_backing_store_zoom_weak_set(Evas_Object *o, float zoom, Evas_Coord cx, Evas_Coord cy);
+EAPI void ewk_tiled_backing_store_fix_offsets(Evas_Object *o, Evas_Coord w, Evas_Coord h);
+EAPI Eina_Bool ewk_tiled_backing_store_update(Evas_Object *o, const Eina_Rectangle *update);
+EAPI void ewk_tiled_backing_store_updates_process_pre_set(Evas_Object *o, void *(*cb)(void *data, Evas_Object *o), const void *data);
+EAPI void ewk_tiled_backing_store_updates_process_post_set(Evas_Object *o, void *(*cb)(void *data, void *pre_data, Evas_Object *o), const void *data);
+EAPI void ewk_tiled_backing_store_process_entire_queue_set(Evas_Object *o, Eina_Bool value);
+EAPI void ewk_tiled_backing_store_updates_process(Evas_Object *o);
+EAPI void ewk_tiled_backing_store_updates_clear(Evas_Object *o);
+EAPI void ewk_tiled_backing_store_contents_resize(Evas_Object *o, Evas_Coord width, Evas_Coord height);
+EAPI void ewk_tiled_backing_store_disabled_update_set(Evas_Object *o, Eina_Bool value);
+EAPI void ewk_tiled_backing_store_flush(Evas_Object *o);
+EAPI void ewk_tiled_backing_store_enable_scale_set(Evas_Object *o, Eina_Bool value);
+
+EAPI Ewk_Tile_Unused_Cache *ewk_tiled_backing_store_tile_unused_cache_get(const Evas_Object *o);
+EAPI void ewk_tiled_backing_store_tile_unused_cache_set(Evas_Object *o, Ewk_Tile_Unused_Cache *tuc);
+
+EAPI Eina_Bool ewk_tiled_backing_store_pre_render_region(Evas_Object *o, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom);
+EAPI Eina_Bool ewk_tiled_backing_store_pre_render_relative_radius(Evas_Object *o, unsigned int n, float zoom);
+EAPI void ewk_tiled_backing_store_pre_render_cancel(Evas_Object *o);
+
+EAPI Eina_Bool ewk_tiled_backing_store_disable_render(Evas_Object *o);
+EAPI Eina_Bool ewk_tiled_backing_store_enable_render(Evas_Object *o);
+#ifdef __cplusplus
+}
+#endif
+#endif // ewk_tiled_backing_store_h
diff --git a/WebKit/efl/ewk/ewk_tiled_matrix.c b/WebKit/efl/ewk/ewk_tiled_matrix.c
new file mode 100644
index 0000000..fccbfa1
--- /dev/null
+++ b/WebKit/efl/ewk/ewk_tiled_matrix.c
@@ -0,0 +1,771 @@
+/*
+ Copyright (C) 2009-2010 Samsung Electronics
+ Copyright (C) 2009-2010 ProFUSION embedded systems
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#include "ewk_tiled_matrix.h"
+
+#define _GNU_SOURCE
+#include "ewk_tiled_backing_store.h"
+#include "ewk_tiled_private.h"
+#include <Eina.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdio.h> // XXX remove me later
+#include <stdlib.h>
+#include <string.h>
+
+static const Evas_Coord TILE_MATRIX_BASE_TILE_SIZE = 256;
+
+struct _Ewk_Tile_Matrix {
+ Eina_Matrixsparse *matrix;
+ Ewk_Tile_Unused_Cache *tuc;
+ Evas_Colorspace cspace;
+ struct {
+ void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update);
+ void *data;
+ } render;
+ unsigned int frozen;
+ Eina_List *updates;
+#ifdef DEBUG_MEM_LEAKS
+ struct {
+ struct {
+ uint64_t allocated, freed;
+ } tiles, bytes;
+ } stats;
+#endif
+};
+
+#ifdef DEBUG_MEM_LEAKS
+static uint64_t tiles_leaked = 0;
+static uint64_t bytes_leaked = 0;
+#endif
+
+/* called when matrixsparse is resized or freed */
+static void _ewk_tile_matrix_cell_free(void *user_data, void *cell_data)
+{
+ Ewk_Tile_Matrix *tm = user_data;
+ Eina_Inlist *l = cell_data;
+
+ if (!l)
+ return;
+
+ ewk_tile_unused_cache_freeze(tm->tuc);
+
+ while (l) {
+ Ewk_Tile *t = (Ewk_Tile *)l;
+ l = l->next;
+
+ if (t->updates || t->stats.full_update)
+ tm->updates = eina_list_remove(tm->updates, t);
+
+ if (t->visible)
+ ERR("freeing cell that is visible, leaking tile %p", t);
+ else {
+ if (!ewk_tile_unused_cache_tile_get(tm->tuc, t))
+ ERR("tile %p was not in cache %p? leaking...", t, tm->tuc);
+ else {
+ DBG("tile cell does not exist anymore, free it %p", t);
+
+#ifdef DEBUG_MEM_LEAKS
+ tm->stats.bytes.freed += t->bytes;
+ tm->stats.tiles.freed++;
+#endif
+
+ ewk_tile_free(t);
+ }
+ }
+ }
+
+ ewk_tile_unused_cache_thaw(tm->tuc);
+}
+
+/* called when cache of unused tile is flushed */
+static void _ewk_tile_matrix_tile_free(void *data, Ewk_Tile *t)
+{
+ Ewk_Tile_Matrix *tm = data;
+ Eina_Matrixsparse_Cell *cell;
+ Eina_Inlist *l, *old;
+
+ if (!eina_matrixsparse_cell_idx_get(tm->matrix, t->row, t->col, &cell)) {
+ ERR("removing tile %p that was not in the matrix? Leaking...", t);
+ return;
+ }
+
+ if (t->updates || t->stats.full_update)
+ tm->updates = eina_list_remove(tm->updates, t);
+
+ old = eina_matrixsparse_cell_data_get(cell);
+ l = eina_inlist_remove(old, EINA_INLIST_GET(t));
+ if (!l) {
+ /* set to null to avoid double free */
+ eina_matrixsparse_cell_data_replace(cell, NULL, NULL);
+ eina_matrixsparse_cell_clear(cell);
+ } else if (old != l)
+ eina_matrixsparse_cell_data_replace(cell, l, NULL);
+
+ if (EINA_UNLIKELY(!!t->visible)) {
+ ERR("cache of unused tiles requesting deletion of used tile %p? "
+ "Leaking...", t);
+ return;
+ }
+
+#ifdef DEBUG_MEM_LEAKS
+ tm->stats.bytes.freed += t->bytes;
+ tm->stats.tiles.freed++;
+#endif
+
+ ewk_tile_free(t);
+}
+
+/**
+ * Creates a new matrix of tiles.
+ *
+ * The tile matrix is responsible for keeping tiles around and
+ * providing fast access to them. One can use it to retrieve new or
+ * existing tiles and give them back, allowing them to be
+ * freed/replaced by the cache.
+ *
+ * @param tuc cache of unused tiles or @c NULL to create one
+ * automatically.
+ * @param cols number of columns in the matrix.
+ * @param rows number of rows in the matrix.
+ * @param cspace the color space used to create tiles in this matrix.
+ * @param render_cb function used to render given tile update.
+ * @param data context to give back to @a render_cb.
+ *
+ * @return newly allocated instance on success, @c NULL on failure.
+ */
+Ewk_Tile_Matrix *ewk_tile_matrix_new(Ewk_Tile_Unused_Cache *tuc, unsigned long cols, unsigned long rows, Evas_Colorspace cspace, void (*render_cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data)
+{
+ Ewk_Tile_Matrix *tm;
+
+ CALLOC_OR_OOM_RET(tm, sizeof(Ewk_Tile_Matrix), NULL);
+
+ tm->matrix = eina_matrixsparse_new(rows, cols, _ewk_tile_matrix_cell_free, tm);
+ if (!tm->matrix) {
+ ERR("could not create sparse matrix.");
+ free(tm);
+ return NULL;
+ }
+
+ if (tuc)
+ tm->tuc = ewk_tile_unused_cache_ref(tuc);
+ else {
+ tm->tuc = ewk_tile_unused_cache_new(40960000);
+ if (!tm->tuc) {
+ ERR("no cache of unused tile!");
+ eina_matrixsparse_free(tm->matrix);
+ free(tm);
+ return NULL;
+ }
+ }
+
+ tm->cspace = cspace;
+ tm->render.cb = render_cb;
+ tm->render.data = (void *)data;
+
+ return tm;
+}
+
+/**
+ * Destroys tiles matrix, releasing its resources.
+ *
+ * The cache instance is unreferenced, possibly freeing it.
+ */
+void ewk_tile_matrix_free(Ewk_Tile_Matrix *tm)
+{
+#ifdef DEBUG_MEM_LEAKS
+ uint64_t tiles, bytes;
+#endif
+
+ EINA_SAFETY_ON_NULL_RETURN(tm);
+ ewk_tile_unused_cache_freeze(tm->tuc);
+
+ eina_matrixsparse_free(tm->matrix);
+
+ ewk_tile_unused_cache_thaw(tm->tuc);
+ ewk_tile_unused_cache_unref(tm->tuc);
+
+#ifdef DEBUG_MEM_LEAKS
+ tiles = tm->stats.tiles.allocated - tm->stats.tiles.freed;
+ bytes = tm->stats.bytes.allocated - tm->stats.bytes.freed;
+
+ tiles_leaked += tiles;
+ bytes_leaked += bytes;
+
+ if (tiles || bytes)
+ ERR("tiled matrix leaked: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
+ "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]",
+ tm->stats.tiles.allocated, tm->stats.tiles.freed, tiles,
+ tm->stats.bytes.allocated, tm->stats.bytes.freed, bytes);
+ else if (tiles_leaked || bytes_leaked)
+ WRN("tiled matrix had no leaks: tiles[+%"PRIu64",-%"PRIu64"] "
+ "bytes[+%"PRIu64",-%"PRIu64"], but some other leaked "
+ "%"PRIu64" tiles (%"PRIu64" bytes)",
+ tm->stats.tiles.allocated, tm->stats.tiles.freed,
+ tm->stats.bytes.allocated, tm->stats.bytes.freed,
+ tiles_leaked, bytes_leaked);
+ else
+ INF("tiled matrix had no leaks: tiles[+%"PRIu64",-%"PRIu64"] "
+ "bytes[+%"PRIu64",-%"PRIu64"]",
+ tm->stats.tiles.allocated, tm->stats.tiles.freed,
+ tm->stats.bytes.allocated, tm->stats.bytes.freed);
+#endif
+
+ free(tm);
+}
+
+/**
+ * Resize matrix to given number of rows and columns.
+ */
+void ewk_tile_matrix_resize(Ewk_Tile_Matrix *tm, unsigned long cols, unsigned long rows)
+{
+ EINA_SAFETY_ON_NULL_RETURN(tm);
+ eina_matrixsparse_size_set(tm->matrix, rows, cols);
+}
+
+/**
+ * Get the cache of unused tiles in use by given matrix.
+ *
+ * No reference is taken to the cache.
+ */
+Ewk_Tile_Unused_Cache *ewk_tile_matrix_unused_cache_get(const Ewk_Tile_Matrix *tm)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL);
+ return tm->tuc;
+}
+
+/**
+ * Get the exact tile for the given position and zoom.
+ *
+ * If the tile was unused then it's removed from the cache.
+ *
+ * After usage, please give it back using
+ * ewk_tile_matrix_tile_put(). If you just want to check if it exists,
+ * then use ewk_tile_matrix_tile_exact_exists().
+ *
+ * @param tm the tile matrix to get tile from.
+ * @param col the column number.
+ * @param row the row number.
+ * @param zoom the exact zoom to use.
+ *
+ * @return The tile instance or @c NULL if none is found. If the tile
+ * was in the unused cache it will be @b removed (thus
+ * considered used) and one should give it back with
+ * ewk_tile_matrix_tile_put() afterwards.
+ *
+ * @see ewk_tile_matrix_tile_nearest_get()
+ * @see ewk_tile_matrix_tile_exact_get()
+ */
+Ewk_Tile *ewk_tile_matrix_tile_exact_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom)
+{
+ Ewk_Tile *t, *item, *item_found = NULL;
+ Eina_Inlist *inl;
+
+ t = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+ if (!t)
+ return NULL;
+
+ if (t->zoom == zoom)
+ goto end;
+
+ EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) {
+ if (item->zoom != zoom)
+ continue;
+ item_found = item;
+ break;
+ }
+
+ if (!item_found)
+ return NULL;
+
+ inl = eina_inlist_promote(EINA_INLIST_GET(t), EINA_INLIST_GET(item_found));
+ eina_matrixsparse_data_idx_replace(tm->matrix, row, col, inl, NULL);
+
+ end:
+ if (!t->visible) {
+ if (!ewk_tile_unused_cache_tile_get(tm->tuc, t))
+ WRN("Ewk_Tile was unused but not in cache? bug!");
+ }
+
+ return t;
+}
+
+/**
+ * Checks if tile of given zoom exists in matrix.
+ *
+ * @param tm the tile matrix to check tile existence.
+ * @param col the column number.
+ * @param row the row number.
+ * @param zoom the exact zoom to query.
+ *
+ * @return @c EINA_TRUE if found, @c EINA_FALSE otherwise.
+ *
+ * @see ewk_tile_matrix_tile_exact_get()
+ */
+Eina_Bool ewk_tile_matrix_tile_exact_exists(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom)
+{
+ Ewk_Tile *t, *item;
+
+ t = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+ if (!t)
+ return EINA_FALSE;
+
+ EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) {
+ if (item->zoom == zoom)
+ return EINA_TRUE;
+ }
+
+ return EINA_FALSE;
+}
+
+/**
+ * Get the nearest tile for given position and zoom.
+ *
+ * The nearest tile is the one at the given position but with the
+ * closest zoom, this being the division of the tile zoom by the
+ * desired zoom, the closest to 1.0 gets it.
+ *
+ * If the tile was unused then it's removed from the cache.
+ *
+ * After usage, please give it back using ewk_tile_matrix_tile_put().
+ *
+ * @param tm the tile matrix to get tile from.
+ * @param col the column number.
+ * @param row the row number.
+ * @param zoom the exact zoom to use.
+ *
+ * @return The tile instance or @c NULL if none is found. If the tile
+ * was in the unused cache it will be @b removed (thus
+ * considered used) and one should give it back with
+ * ewk_tile_matrix_tile_put() afterwards.
+ */
+Ewk_Tile *ewk_tile_matrix_tile_nearest_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom)
+{
+ Ewk_Tile *t, *item, *item_found = NULL;
+ Eina_Inlist *inl;
+ float zoom_found = 0;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, NULL);
+
+ t = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+ if (!t)
+ return NULL;
+
+ if (t->zoom == zoom) {
+ item_found = t;
+ goto end;
+ }
+
+ EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) {
+ float cur_zoom = item->zoom;
+
+ if (cur_zoom == zoom) {
+ item_found = item;
+ break;
+ }
+
+ if (cur_zoom > zoom)
+ cur_zoom = zoom / cur_zoom;
+ else
+ cur_zoom = cur_zoom / zoom;
+
+ if (cur_zoom > zoom_found) {
+ item_found = item;
+ zoom_found = cur_zoom;
+ }
+ }
+
+ if (!item_found)
+ return NULL;
+
+ inl = eina_inlist_promote(EINA_INLIST_GET(t), EINA_INLIST_GET(item_found));
+ eina_matrixsparse_data_idx_replace(tm->matrix, row, col, inl, NULL);
+
+ end:
+ if (!item_found->visible) {
+ if (!ewk_tile_unused_cache_tile_get(tm->tuc, item_found))
+ WRN("Ewk_Tile was unused but not in cache? bug!");
+ }
+
+ return item_found;
+}
+
+/**
+ * Create a new tile at given position and zoom level in the matrix.
+ *
+ * The newly created tile is considered in use and not put into cache
+ * of unused tiles. After it is used one should call
+ * ewk_tile_matrix_tile_put() to give it back to matrix.
+ *
+ * @param tm the tile matrix to create tile on.
+ * @param col the column number.
+ * @param row the row number.
+ * @param zoom the level to create tile, used to determine tile size.
+ */
+Ewk_Tile *ewk_tile_matrix_tile_new(Ewk_Tile_Matrix *tm, Evas *evas, unsigned long col, unsigned int row, float zoom)
+{
+ Evas_Coord s;
+ Eina_Inlist *old;
+ Ewk_Tile *t;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, NULL);
+
+ s = TILE_SIZE_AT_ZOOM(TILE_MATRIX_BASE_TILE_SIZE, zoom);
+ zoom = (float)s / (float)TILE_MATRIX_BASE_TILE_SIZE;
+
+ t = ewk_tile_new(evas, s, s, zoom, tm->cspace);
+ if (!t) {
+ ERR("could not create tile %dx%d at %f, cspace=%d",
+ s, s, (double)zoom, tm->cspace);
+ return NULL;
+ }
+
+ old = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+ old = eina_inlist_prepend(old, EINA_INLIST_GET(t));
+ if (!eina_matrixsparse_data_idx_replace(tm->matrix, row, col, t, NULL)) {
+ ERR("could not set matrix cell, row/col outside matrix dimensions!");
+ ewk_tile_free(t);
+ return NULL;
+ }
+
+ t->col = col;
+ t->row = row;
+ t->x = col * s;
+ t->y = row * s;
+
+ cairo_translate(t->cairo, -t->x, -t->y);
+
+ t->stats.full_update = EINA_TRUE;
+ tm->updates = eina_list_append(tm->updates, t);
+
+#ifdef DEBUG_MEM_LEAKS
+ tm->stats.bytes.allocated += t->bytes;
+ tm->stats.tiles.allocated++;
+#endif
+
+ return t;
+}
+
+/**
+ * Gives back the tile to the tile matrix.
+ *
+ * This will report the tile is no longer in use by the one that got
+ * it with ewk_tile_matrix_tile_nearest_get() or
+ * ewk_tile_matrix_tile_exact_get().
+ *
+ * Any previous reference to tile should be released
+ * (ewk_tile_hide()) before calling this function, so it will
+ * be known if it is not visibile anymore and thus can be put into the
+ * unused cache.
+ *
+ * @param tm the tile matrix to return tile to.
+ * @param t the tile instance to return, must @b not be @c NULL.
+ * @param last_used time in which tile was last used.
+ *
+ * @return #EINA_TRUE on success or #EINA_FALSE on failure.
+ */
+Eina_Bool ewk_tile_matrix_tile_put(Ewk_Tile_Matrix *tm, Ewk_Tile *t, double last_used)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE);
+
+ if (t->visible)
+ return EINA_TRUE;
+
+ t->stats.last_used = last_used;
+ return ewk_tile_unused_cache_tile_put(tm->tuc, t, _ewk_tile_matrix_tile_free, tm);
+}
+
+Eina_Bool ewk_tile_matrix_tile_update(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, const Eina_Rectangle *update)
+{
+ Ewk_Tile *l, *t;
+ Eina_Rectangle new_update;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(update, EINA_FALSE);
+
+ memcpy(&new_update, update, sizeof(new_update));
+ // check update is valid, otherwise return EINA_FALSE
+ if (update->x < 0 || update->y < 0 || update->w <= 0 || update->h <= 0) {
+ ERR("invalid update region.");
+ return EINA_FALSE;
+ }
+
+ if (update->x + update->w - 1 >= TILE_MATRIX_BASE_TILE_SIZE)
+ new_update.w = TILE_MATRIX_BASE_TILE_SIZE - update->x;
+ if (update->y + update->h - 1 >= TILE_MATRIX_BASE_TILE_SIZE)
+ new_update.h = TILE_MATRIX_BASE_TILE_SIZE - update->y;
+
+ l = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+
+ if (!l)
+ return EINA_TRUE;
+
+ EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) {
+ if (!t->updates && !t->stats.full_update)
+ tm->updates = eina_list_append(tm->updates, t);
+ new_update.x = roundf(t->zoom * new_update.x);
+ new_update.y = roundf(t->zoom * new_update.y);
+ new_update.w = roundf(t->zoom * new_update.w);
+ new_update.h = roundf(t->zoom * new_update.h);
+ ewk_tile_update_area(t, &new_update);
+ }
+
+ return EINA_TRUE;
+}
+
+Eina_Bool ewk_tile_matrix_tile_update_full(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row)
+{
+ Ewk_Tile *l, *t;
+ Eina_Matrixsparse_Cell *cell;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
+
+ if (!eina_matrixsparse_cell_idx_get(tm->matrix, row, col, &cell))
+ return EINA_FALSE;
+
+ if (!cell)
+ return EINA_TRUE;
+
+ l = eina_matrixsparse_cell_data_get(cell);
+ if (!l) {
+ CRITICAL("matrix cell with no tile!");
+ return EINA_TRUE;
+ }
+
+ EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) {
+ if (!t->updates && !t->stats.full_update)
+ tm->updates = eina_list_append(tm->updates, t);
+ ewk_tile_update_full(t);
+ }
+
+ return EINA_TRUE;
+}
+
+void ewk_tile_matrix_tile_updates_clear(Ewk_Tile_Matrix *tm, Ewk_Tile *t)
+{
+ EINA_SAFETY_ON_NULL_RETURN(tm);
+ if (!t->updates && !t->stats.full_update)
+ return;
+ ewk_tile_updates_clear(t);
+ tm->updates = eina_list_remove(tm->updates, t);
+}
+
+static Eina_Bool _ewk_tile_matrix_slicer_setup(Ewk_Tile_Matrix *tm, const Eina_Rectangle *area, float zoom, Eina_Tile_Grid_Slicer *slicer)
+{
+ unsigned long rows, cols;
+ Evas_Coord x, y, w, h, tw, th;
+
+ if (area->w <= 0 || area->h <= 0) {
+ WRN("invalid area region: %d,%d+%dx%d.",
+ area->x, area->y, area->w, area->h);
+ return EINA_FALSE;
+ }
+
+ x = area->x;
+ y = area->y;
+ w = area->w;
+ h = area->h;
+
+ tw = TILE_SIZE_AT_ZOOM(TILE_W, zoom);
+ th = TILE_SIZE_AT_ZOOM(TILE_H, zoom);
+
+ // cropping area region to fit matrix
+ eina_matrixsparse_size_get(tm->matrix, &rows, &cols);
+ if (x < 0) {
+ w += x;
+ x = 0;
+ }
+ if (y < 0) {
+ h += y;
+ y = 0;
+ }
+
+ if (y + h - 1 > rows * th)
+ h = rows * th - y;
+ if (x + w - 1 > cols * tw)
+ w = cols * tw - x;
+
+ return eina_tile_grid_slicer_setup(slicer, x, y, w, h, tw, th);
+}
+
+
+Eina_Bool ewk_tile_matrix_update(Ewk_Tile_Matrix *tm, const Eina_Rectangle *update, float zoom)
+{
+ const Eina_Tile_Grid_Info *info;
+ Eina_Tile_Grid_Slicer slicer;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(update, EINA_FALSE);
+
+ if (update->w < 1 || update->h < 1) {
+ DBG("Why we get updates with empty areas? %d,%d+%dx%d at zoom %f",
+ update->x, update->y, update->w, update->h, zoom);
+ return EINA_TRUE;
+ }
+
+ if (!_ewk_tile_matrix_slicer_setup(tm, update, zoom, &slicer)) {
+ ERR("Could not setup slicer for update %d,%d+%dx%d at zoom %f",
+ update->x, update->y, update->w, update->h, zoom);
+ return EINA_FALSE;
+ }
+
+ while (eina_tile_grid_slicer_next(&slicer, &info)) {
+ unsigned long col, row;
+ Ewk_Tile *l, *t;
+ col = info->col;
+ row = info->row;
+
+ l = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+ if (!l)
+ continue;
+
+ EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) {
+ if (!t->updates && !t->stats.full_update)
+ tm->updates = eina_list_append(tm->updates, t);
+ if (info->full)
+ ewk_tile_update_full(t);
+ else {
+ if (t->zoom != zoom)
+ ewk_tile_update_full(t);
+ else
+ ewk_tile_update_area(t, &info->rect);
+ }
+ }
+ }
+
+
+ return EINA_TRUE;
+}
+
+void ewk_tile_matrix_updates_process(Ewk_Tile_Matrix *tm)
+{
+ Eina_List *l, *l_next;
+ Ewk_Tile *t;
+ EINA_SAFETY_ON_NULL_RETURN(tm);
+
+ // process updates, unflag tiles
+ EINA_LIST_FOREACH_SAFE(tm->updates, l, l_next, t) {
+ ewk_tile_updates_process(t, tm->render.cb, tm->render.data);
+ if (t->visible) {
+ ewk_tile_updates_clear(t);
+ tm->updates = eina_list_remove_list(tm->updates, l);
+ }
+ }
+}
+
+void ewk_tile_matrix_updates_clear(Ewk_Tile_Matrix *tm)
+{
+ Ewk_Tile *t;
+ EINA_SAFETY_ON_NULL_RETURN(tm);
+
+ EINA_LIST_FREE(tm->updates, t)
+ ewk_tile_updates_clear(t);
+ tm->updates = NULL;
+}
+
+// remove me later!
+void ewk_tile_matrix_dbg(const Ewk_Tile_Matrix *tm)
+{
+ Eina_Iterator *it = eina_matrixsparse_iterator_complete_new(tm->matrix);
+ Eina_Matrixsparse_Cell *cell;
+ Eina_Bool last_empty = EINA_FALSE;
+
+#ifdef DEBUG_MEM_LEAKS
+ printf("Ewk_Tile Matrix: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
+ "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]\n",
+ tm->stats.tiles.allocated, tm->stats.tiles.freed,
+ tm->stats.tiles.allocated - tm->stats.tiles.freed,
+ tm->stats.bytes.allocated, tm->stats.bytes.freed,
+ tm->stats.bytes.allocated - tm->stats.bytes.freed);
+#else
+ printf("Ewk_Tile Matrix:\n");
+#endif
+
+ EINA_ITERATOR_FOREACH(it, cell) {
+ unsigned long row, col;
+ Eina_Inlist *l;
+ eina_matrixsparse_cell_position_get(cell, &row, &col);
+ l = eina_matrixsparse_cell_data_get(cell);
+
+ if (!l) {
+ if (!last_empty) {
+ last_empty = EINA_TRUE;
+ printf("Empty:");
+ }
+ printf(" [%lu,%lu]", col, row);
+ } else {
+ if (last_empty) {
+ last_empty = EINA_FALSE;
+ printf("\n");
+ }
+ Ewk_Tile *t;
+
+ printf("%3lu,%3lu %10p:", col, row, l);
+ EINA_INLIST_FOREACH(l, t)
+ printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c",
+ t->col, t->row, t->w, t->h, t->zoom,
+ t->visible ? '*': ' ');
+ printf("\n");
+ }
+ }
+ if (last_empty)
+ printf("\n");
+ eina_iterator_free(it);
+
+ ewk_tile_unused_cache_dbg(tm->tuc);
+}
+
+/**
+ * Freeze matrix to not do maintenance tasks.
+ *
+ * Maintenance tasks optimize usage, but maybe we know we should hold
+ * on them until we do the last operation, in this case we freeze
+ * while operating and then thaw when we're done.
+ *
+ * @see ewk_tile_matrix_thaw()
+ */
+void ewk_tile_matrix_freeze(Ewk_Tile_Matrix *tm)
+{
+ EINA_SAFETY_ON_NULL_RETURN(tm);
+ if (!tm->frozen)
+ ewk_tile_unused_cache_freeze(tm->tuc);
+ tm->frozen++;
+}
+
+/**
+ * Unfreezes maintenance tasks.
+ *
+ * If this is the last counterpart of freeze, then maintenance tasks
+ * will run immediately.
+ */
+void ewk_tile_matrix_thaw(Ewk_Tile_Matrix *tm)
+{
+ EINA_SAFETY_ON_NULL_RETURN(tm);
+ if (!tm->frozen) {
+ ERR("thawing more than freezing!");
+ return;
+ }
+
+ tm->frozen--;
+ if (!tm->frozen)
+ ewk_tile_unused_cache_thaw(tm->tuc);
+}
diff --git a/WebKit/efl/ewk/ewk_tiled_matrix.h b/WebKit/efl/ewk/ewk_tiled_matrix.h
new file mode 100644
index 0000000..07b8612
--- /dev/null
+++ b/WebKit/efl/ewk/ewk_tiled_matrix.h
@@ -0,0 +1,60 @@
+/*
+ Copyright (C) 2009-2010 Samsung Electronics
+ Copyright (C) 2009-2010 ProFUSION embedded systems
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef ewk_tiled_matrix_h
+#define ewk_tiled_matrix_h
+
+#include "ewk_eapi.h"
+#include "ewk_tiled_backing_store.h"
+
+#include <Evas.h>
+
+/* matrix of tiles */
+EAPI Ewk_Tile_Matrix *ewk_tile_matrix_new(Ewk_Tile_Unused_Cache *tuc, unsigned long cols, unsigned long rows, Evas_Colorspace cspace, void (*render_cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data);
+EAPI void ewk_tile_matrix_free(Ewk_Tile_Matrix *tm);
+
+EAPI void ewk_tile_matrix_resize(Ewk_Tile_Matrix *tm, unsigned long cols, unsigned long rows);
+
+EAPI Ewk_Tile_Unused_Cache *ewk_tile_matrix_unused_cache_get(const Ewk_Tile_Matrix *tm);
+
+EAPI Ewk_Tile *ewk_tile_matrix_tile_exact_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom);
+EAPI Eina_Bool ewk_tile_matrix_tile_exact_exists(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom);
+EAPI Ewk_Tile *ewk_tile_matrix_tile_nearest_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom);
+EAPI Ewk_Tile *ewk_tile_matrix_tile_new(Ewk_Tile_Matrix *tm, Evas *evas, unsigned long col, unsigned int row, float zoom);
+EAPI Eina_Bool ewk_tile_matrix_tile_put(Ewk_Tile_Matrix *tm, Ewk_Tile *t, double last_used);
+
+EAPI Eina_Bool ewk_tile_matrix_tile_update(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, const Eina_Rectangle *update);
+EAPI Eina_Bool ewk_tile_matrix_tile_update_full(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row);
+EAPI void ewk_tile_matrix_tile_updates_clear(Ewk_Tile_Matrix *tm, Ewk_Tile *t);
+
+EAPI Eina_Bool ewk_tile_matrix_update(Ewk_Tile_Matrix *tm, const Eina_Rectangle *update, float zoom);
+EAPI void ewk_tile_matrix_updates_process(Ewk_Tile_Matrix *tm);
+EAPI void ewk_tile_matrix_updates_clear(Ewk_Tile_Matrix *tm);
+EAPI void ewk_tile_matrix_freeze(Ewk_Tile_Matrix *tm);
+EAPI void ewk_tile_matrix_thaw(Ewk_Tile_Matrix *tm);
+
+// remove me!
+ void ewk_tile_matrix_dbg(const Ewk_Tile_Matrix *tm);
+ void ewk_tile_unused_cache_dbg(const Ewk_Tile_Unused_Cache *tuc);
+ void ewk_tile_accounting_dbg(void);
+
+
+#endif // ewk_tiled_matrix_h
+
diff --git a/WebKit/efl/ewk/ewk_tiled_model.c b/WebKit/efl/ewk/ewk_tiled_model.c
new file mode 100644
index 0000000..641b93e
--- /dev/null
+++ b/WebKit/efl/ewk/ewk_tiled_model.c
@@ -0,0 +1,905 @@
+/*
+ Copyright (C) 2009-2010 Samsung Electronics
+ Copyright (C) 2009-2010 ProFUSION embedded systems
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#include "ewk_tiled_model.h"
+
+#define _GNU_SOURCE
+#include "ewk_tiled_backing_store.h"
+#include "ewk_tiled_private.h"
+#include <Eina.h>
+#include <eina_safety_checks.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h> // XXX REMOVE ME LATER
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef TILE_STATS_ACCOUNT_RENDER_TIME
+#include <sys/time.h>
+#endif
+
+#ifndef CAIRO_FORMAT_RGB16_565
+#define CAIRO_FORMAT_RGB16_565 4
+#endif
+
+#define IDX(col, row, rowspan) (col + (row * rowspan))
+#define MIN(a, b) ((a < b) ? a : b)
+#define MAX(a, b) ((a > b) ? a : b)
+
+#ifdef DEBUG_MEM_LEAKS
+static uint64_t tiles_allocated = 0;
+static uint64_t tiles_freed = 0;
+static uint64_t bytes_allocated = 0;
+static uint64_t bytes_freed = 0;
+
+struct tile_account {
+ Evas_Coord size;
+ struct {
+ uint64_t allocated;
+ uint64_t freed;
+ } tiles, bytes;
+};
+
+static size_t accounting_len = 0;
+static struct tile_account *accounting = NULL;
+
+static inline struct tile_account *_ewk_tile_account_get(const Ewk_Tile *t)
+{
+ struct tile_account *acc;
+ size_t i;
+
+ for (i = 0; i < accounting_len; i++) {
+ if (accounting[i].size == t->w)
+ return accounting + i;
+ }
+
+ i = (accounting_len + 1) * sizeof(struct tile_account);
+ REALLOC_OR_OOM_RET(accounting, i, NULL);
+
+ acc = accounting + accounting_len;
+ acc->size = t->w;
+ acc->tiles.allocated = 0;
+ acc->tiles.freed = 0;
+ acc->bytes.allocated = 0;
+ acc->bytes.freed = 0;
+
+ accounting_len++;
+
+ return acc;
+}
+
+static inline void _ewk_tile_account_allocated(const Ewk_Tile *t)
+{
+ struct tile_account *acc = _ewk_tile_account_get(t);
+ if (!acc)
+ return;
+ acc->bytes.allocated += t->bytes;
+ acc->tiles.allocated++;
+
+ bytes_allocated += t->bytes;
+ tiles_allocated++;
+}
+
+static inline void _ewk_tile_account_freed(const Ewk_Tile *t)
+{
+ struct tile_account *acc = _ewk_tile_account_get(t);
+ if (!acc)
+ return;
+
+ acc->bytes.freed += t->bytes;
+ acc->tiles.freed++;
+
+ bytes_freed += t->bytes;
+ tiles_freed++;
+}
+
+void ewk_tile_accounting_dbg(void)
+{
+ struct tile_account *acc;
+ struct tile_account *acc_end;
+
+ printf("TILE BALANCE: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
+ "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]\n",
+ tiles_allocated, tiles_freed, tiles_allocated - tiles_freed,
+ bytes_allocated, bytes_freed, bytes_allocated - bytes_freed);
+
+ if (!accounting_len)
+ return;
+
+ acc = accounting;
+ acc_end = acc + accounting_len;
+ printf("BEGIN: TILE BALANCE DETAILS (TO THIS MOMENT!):\n");
+ for (; acc < acc_end; acc++) {
+ uint64_t tiles, bytes;
+
+ tiles = acc->tiles.allocated - acc->tiles.freed;
+ bytes = acc->bytes.allocated - acc->bytes.freed;
+
+ printf(" %4d: tiles[+%4"PRIu64",-%4"PRIu64":%4"PRIu64"] "
+ "bytes[+%8"PRIu64",-%8"PRIu64":%8"PRIu64"]%s\n",
+ acc->size,
+ acc->tiles.allocated, acc->tiles.freed, tiles,
+ acc->bytes.allocated, acc->bytes.freed, bytes,
+ (bytes || tiles) ? " POSSIBLE LEAK" : "");
+ }
+ printf("END: TILE BALANCE DETAILS (TO THIS MOMENT!):\n");
+}
+#else
+
+static inline void _ewk_tile_account_allocated(const Ewk_Tile *t) { }
+static inline void _ewk_tile_account_freed(const Ewk_Tile *t) { }
+
+void ewk_tile_accounting_dbg(void)
+{
+ printf("compile webkit with DEBUG_MEM_LEAKS defined!\n");
+}
+#endif
+
+static inline void _ewk_tile_paint_rgb888(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
+{
+ uint32_t *dst32, *dst32_end, c1;
+ uint64_t *dst64, *dst64_end, c2;
+
+ c1 = 0xff000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
+ c2 = ((uint64_t)c1 << 32) | c1;
+
+ dst64 = (uint64_t *)t->pixels;
+ dst64_end = dst64 + ((t->bytes / 8) & ~7);
+ for (; dst64 < dst64_end; dst64 += 8) {
+ /* TODO: ARM add pld or NEON instructions */
+ dst64[0] = c2;
+ dst64[1] = c2;
+ dst64[2] = c2;
+ dst64[3] = c2;
+ dst64[4] = c2;
+ dst64[5] = c2;
+ dst64[6] = c2;
+ dst64[7] = c2;
+ }
+
+ dst32 = (uint32_t *)dst64_end;
+ dst32_end = (uint32_t *)(t->pixels + t->bytes);
+ for (; dst32 < dst32_end; dst32++)
+ *dst32 = c1;
+}
+
+static inline void _ewk_tile_paint_rgb565(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
+{
+ uint16_t *dst16, *dst16_end, c1;
+ uint64_t *dst64, *dst64_end, c2;
+
+ c1 = ((((r >> 3) & 0x1f) << 11) |
+ (((g >> 2) & 0x3f) << 5) |
+ ((b >> 3) & 0x1f));
+
+ c2 = (((uint64_t)c1 << 48) | ((uint64_t)c1 << 32) |
+ ((uint64_t)c1 << 16) | c1);
+
+ dst64 = (uint64_t *)t->pixels;
+ dst64_end = dst64 + ((t->bytes / 8) & ~7);
+ for (; dst64 < dst64_end; dst64 += 8) {
+ /* TODO: ARM add pld or NEON instructions */
+ dst64[0] = c2;
+ dst64[1] = c2;
+ dst64[2] = c2;
+ dst64[3] = c2;
+ dst64[4] = c2;
+ dst64[5] = c2;
+ dst64[6] = c2;
+ dst64[7] = c2;
+ }
+
+ dst16 = (uint16_t *)dst16_end;
+ dst16_end = (uint16_t *)(t->pixels + t->bytes);
+ for (; dst16 < dst16_end; dst16++)
+ *dst16 = c1;
+}
+
+static inline void _ewk_tile_paint(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
+{
+ if (t->cspace == EVAS_COLORSPACE_ARGB8888)
+ _ewk_tile_paint_rgb888(t, r, g, b);
+ else if (t->cspace == EVAS_COLORSPACE_RGB565_A5P)
+ _ewk_tile_paint_rgb565(t, r, g, b);
+ else
+ ERR("unknown color space: %d", t->cspace);
+}
+
+/**
+ * Create a new tile of given size, zoom level and colorspace.
+ *
+ * After created these properties are immutable as they're the basic
+ * characteristic of the tile and any change will lead to invalid
+ * memory access.
+ *
+ * Other members are of free-access and no getters/setters are
+ * provided in orderr to avoid expensive operations on those, however
+ * some are better manipulated with provided functions, such as
+ * ewk_tile_show() and ewk_tile_hide() to change
+ * @c visible or ewk_tile_update_full(), ewk_tile_update_area(),
+ * ewk_tile_updates_clear() to change @c stats.misses,
+ * @c stats.full_update and @c updates.
+ */
+Ewk_Tile *ewk_tile_new(Evas *evas, Evas_Coord w, Evas_Coord h, float zoom, Evas_Colorspace cspace)
+{
+ Eina_Inlist *l;
+ Evas_Coord *ec;
+ Evas_Colorspace *ecs;
+ float *f;
+ size_t *s;
+ Ewk_Tile *t;
+ unsigned int area;
+ size_t bytes;
+ cairo_format_t format;
+ cairo_status_t status;
+ int stride;
+
+ area = w * h;
+
+ if (cspace == EVAS_COLORSPACE_ARGB8888) {
+ bytes = area * 4;
+ stride = w * 4;
+ format = CAIRO_FORMAT_RGB24;
+ } else if (cspace == EVAS_COLORSPACE_RGB565_A5P) {
+ bytes = area * 2;
+ stride = w * 2;
+ format = CAIRO_FORMAT_RGB16_565;
+ } else {
+ ERR("unknown color space: %d", cspace);
+ return NULL;
+ }
+
+ DBG("size: %dx%d (%d), zoom: %f, cspace=%d",
+ w, h, area, (double)zoom, cspace);
+
+ MALLOC_OR_OOM_RET(t, sizeof(Ewk_Tile), NULL);
+ t->image = evas_object_image_add(evas);
+
+ l = EINA_INLIST_GET(t);
+ l->prev = NULL;
+ l->next = NULL;
+
+ t->visible = 0;
+ t->updates = NULL;
+
+ memset(&t->stats, 0, sizeof(Ewk_Tile_Stats));
+ t->stats.area = area;
+
+ /* ugly, but let's avoid at all costs having users to modify those */
+ ec = (Evas_Coord *)&t->w;
+ *ec = w;
+
+ ec = (Evas_Coord *)&t->h;
+ *ec = h;
+
+ ecs = (Evas_Colorspace *)&t->cspace;
+ *ecs = cspace;
+
+ f = (float *)&t->zoom;
+ *f = zoom;
+
+ s = (size_t *)&t->bytes;
+ *s = bytes;
+
+ evas_object_image_size_set(t->image, t->w, t->h);
+ evas_object_image_colorspace_set(t->image, t->cspace);
+ t->pixels = evas_object_image_data_get(t->image, EINA_TRUE);
+ t->surface = cairo_image_surface_create_for_data
+ (t->pixels, format, w, h, stride);
+ status = cairo_surface_status(t->surface);
+ if (status != CAIRO_STATUS_SUCCESS) {
+ ERR("failed to create cairo surface: %s",
+ cairo_status_to_string(status));
+ free(t);
+ return NULL;
+ }
+
+ t->cairo = cairo_create(t->surface);
+ status = cairo_status(t->cairo);
+ if (status != CAIRO_STATUS_SUCCESS) {
+ ERR("failed to create cairo: %s", cairo_status_to_string(status));
+ cairo_surface_destroy(t->surface);
+ evas_object_del(t->image);
+ free(t);
+ return NULL;
+ }
+
+ _ewk_tile_account_allocated(t);
+
+ return t;
+}
+
+/**
+ * Free tile memory.
+ */
+void ewk_tile_free(Ewk_Tile *t)
+{
+ _ewk_tile_account_freed(t);
+
+ if (t->updates)
+ eina_tiler_free(t->updates);
+
+ cairo_surface_destroy(t->surface);
+ cairo_destroy(t->cairo);
+ evas_object_del(t->image);
+ free(t);
+}
+
+/**
+ * Make the tile visible, incrementing its counter.
+ */
+void ewk_tile_show(Ewk_Tile *t)
+{
+ t->visible++;
+ evas_object_show(t->image);
+}
+
+/**
+ * Decrement the visibility counter, making it invisible if necessary.
+ */
+void ewk_tile_hide(Ewk_Tile *t)
+{
+ t->visible--;
+ if (!t->visible)
+ evas_object_hide(t->image);
+}
+
+/**
+ * Returns EINA_TRUE if the tile is visible, EINA_FALSE otherwise.
+ */
+Eina_Bool ewk_tile_visible_get(Ewk_Tile *t)
+{
+ return !!t->visible;
+}
+
+/**
+ * Mark whole tile as dirty and requiring update.
+ */
+void ewk_tile_update_full(Ewk_Tile *t)
+{
+ /* TODO: list of tiles pending updates? */
+ t->stats.misses++;
+
+ if (!t->stats.full_update) {
+ t->stats.full_update = EINA_TRUE;
+ if (t->updates) {
+ eina_tiler_free(t->updates);
+ t->updates = NULL;
+ }
+ }
+}
+
+/**
+ * Mark the specific subarea as dirty and requiring update.
+ */
+void ewk_tile_update_area(Ewk_Tile *t, const Eina_Rectangle *r)
+{
+ /* TODO: list of tiles pending updates? */
+ t->stats.misses++;
+
+ if (t->stats.full_update)
+ return;
+
+ if (!r->x && !r->y && r->w == t->w && r->h == t->h) {
+ t->stats.full_update = EINA_TRUE;
+ if (t->updates) {
+ eina_tiler_free(t->updates);
+ t->updates = NULL;
+ }
+ return;
+ }
+
+ if (!t->updates) {
+ t->updates = eina_tiler_new(t->w, t->h);
+ if (!t->updates) {
+ CRITICAL("could not create eina_tiler %dx%d.", t->w, t->h);
+ return;
+ }
+ }
+
+ eina_tiler_rect_add(t->updates, r);
+}
+
+/**
+ * For each updated region, call the given function.
+ *
+ * This will not change the tile statistics or clear the processed
+ * updates, use ewk_tile_updates_clear() for that.
+ */
+void ewk_tile_updates_process(Ewk_Tile *t, void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data)
+{
+ if (t->stats.full_update) {
+ Eina_Rectangle r;
+ r.x = 0;
+ r.y = 0;
+ r.w = t->w;
+ r.h = t->h;
+#ifdef TILE_STATS_ACCOUNT_RENDER_TIME
+ struct timeval timev;
+ double render_start;
+ gettimeofday(&timev, NULL);
+ render_start = (double)timev.tv_sec +
+ (((double)timev.tv_usec) / 1000000);
+#endif
+ cb((void *)data, t, &r);
+#ifdef TILE_STATS_ACCOUNT_RENDER_TIME
+ gettimeofday(&timev, NULL);
+ t->stats.render_time = (double)timev.tv_sec +
+ (((double)timev.tv_usec) / 1000000) - render_start;
+#endif
+ } else if (t->updates) {
+ Eina_Iterator *itr = eina_tiler_iterator_new(t->updates);
+ Eina_Rectangle r = {0, 0, 0, 0};
+ if (!itr) {
+ CRITICAL("could not create tiler iterator!");
+ return;
+ }
+ EINA_ITERATOR_FOREACH(itr, r)
+ cb((void *)data, t, &r);
+ eina_iterator_free(itr);
+ }
+}
+
+/**
+ * Clear all updates in region, if any.
+ *
+ * This will change the tile statistics, specially zero stat.misses
+ * and unset stats.full_update. If t->updates existed, then it will be
+ * destroyed.
+ *
+ * This function is usually called after ewk_tile_updates_process() is
+ * called.
+ */
+void ewk_tile_updates_clear(Ewk_Tile *t)
+{
+ /* TODO: remove from list of pending updates? */
+ t->stats.misses = 0;
+
+ if (t->stats.full_update)
+ t->stats.full_update = 0;
+ else if (t->updates) {
+ eina_tiler_free(t->updates);
+ t->updates = NULL;
+ }
+}
+
+typedef struct _Ewk_Tile_Unused_Cache_Entry Ewk_Tile_Unused_Cache_Entry;
+struct _Ewk_Tile_Unused_Cache_Entry {
+ Ewk_Tile *tile;
+ int weight;
+ struct {
+ void (*cb)(void *data, Ewk_Tile *t);
+ void *data;
+ } tile_free;
+};
+
+struct _Ewk_Tile_Unused_Cache {
+ struct {
+ Eina_List *list;
+ size_t count;
+ size_t allocated;
+ } entries;
+ struct {
+ size_t max; /**< watermark (in bytes) to start freeing tiles */
+ size_t used; /**< in bytes, maybe more than max. */
+ } memory;
+ struct {
+ Evas_Coord x, y, w, h;
+ float zoom;
+ Eina_Bool locked;
+ } locked;
+ int references;
+ unsigned int frozen;
+ Eina_Bool dirty:1;
+};
+
+static const size_t TILE_UNUSED_CACHE_ALLOCATE_INITIAL = 128;
+static const size_t TILE_UNUSED_CACHE_ALLOCATE_STEP = 16;
+static const size_t TILE_UNUSED_CACHE_MAX_FREE = 32;
+
+/**
+ * Cache of unused tiles (those that are not visible).
+ *
+ * The cache of unused tiles.
+ *
+ * @param max cache size in bytes.
+ *
+ * @return newly allocated cache of unused tiles, use
+ * ewk_tile_unused_cache_free() to release resources. If not
+ * possible to allocate memory, @c NULL is returned.
+ */
+Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_new(size_t max)
+{
+ Ewk_Tile_Unused_Cache *tuc;
+
+ CALLOC_OR_OOM_RET(tuc, sizeof(Ewk_Tile_Unused_Cache), NULL);
+
+ DBG("tuc=%p", tuc);
+ tuc->memory.max = max;
+ tuc->references = 1;
+ return tuc;
+}
+
+void ewk_tile_unused_cache_lock_area(Ewk_Tile_Unused_Cache *tuc, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom)
+{
+ EINA_SAFETY_ON_NULL_RETURN(tuc);
+
+ tuc->locked.locked = EINA_TRUE;
+ tuc->locked.x = x;
+ tuc->locked.y = y;
+ tuc->locked.w = w;
+ tuc->locked.h = h;
+ tuc->locked.zoom = zoom;
+}
+
+void ewk_tile_unused_cache_unlock_area(Ewk_Tile_Unused_Cache *tuc)
+{
+ EINA_SAFETY_ON_NULL_RETURN(tuc);
+
+ tuc->locked.locked = EINA_FALSE;
+}
+
+/**
+ * Free cache of unused tiles.
+ *
+ * Those tiles that are still visible will remain live. The unused
+ * tiles will be freed.
+ *
+ * @see ewk_tile_unused_cache_unref()
+ */
+void ewk_tile_unused_cache_free(Ewk_Tile_Unused_Cache *tuc)
+{
+ EINA_SAFETY_ON_NULL_RETURN(tuc);
+
+ DBG("tuc=%p, "
+ "entries=(count:%zd, allocated:%zd), "
+ "memory=(max:%zd, used:%zd)",
+ tuc, tuc->entries.count, tuc->entries.allocated,
+ tuc->memory.max, tuc->memory.used);
+
+ ewk_tile_unused_cache_clear(tuc);
+ free(tuc);
+}
+
+/**
+ * Clear cache of unused tiles.
+ *
+ * Any tiles that are in the cache are freed. The only tiles that are
+ * kept are those that aren't in the cache (i.e. that are visible).
+ */
+void ewk_tile_unused_cache_clear(Ewk_Tile_Unused_Cache *tuc)
+{
+ Ewk_Tile_Unused_Cache_Entry *itr;
+ EINA_SAFETY_ON_NULL_RETURN(tuc);
+
+ if (!tuc->entries.count)
+ return;
+
+ EINA_LIST_FREE(tuc->entries.list, itr) {
+ itr->tile_free.cb(itr->tile_free.data, itr->tile);
+ free(itr);
+ }
+
+ tuc->memory.used = 0;
+ tuc->entries.count = 0;
+ tuc->dirty = EINA_FALSE;
+}
+
+/**
+ * Hold reference to cache.
+ *
+ * @return same pointer as taken.
+ *
+ * @see ewk_tile_unused_cache_unref()
+ */
+Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_ref(Ewk_Tile_Unused_Cache *tuc)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, NULL);
+ tuc->references++;
+ return tuc;
+}
+
+/**
+ * Release cache reference, freeing it if it drops to zero.
+ *
+ * @see ewk_tile_unused_cache_ref()
+ * @see ewk_tile_unused_cache_free()
+ */
+void ewk_tile_unused_cache_unref(Ewk_Tile_Unused_Cache *tuc)
+{
+ EINA_SAFETY_ON_NULL_RETURN(tuc);
+ tuc->references--;
+ if (!tuc->references)
+ ewk_tile_unused_cache_free(tuc);
+}
+
+/**
+ * Change cache capacity, in bytes.
+ *
+ * This will not flush cache, use ewk_tile_unused_cache_flush() or
+ * ewk_tile_unused_cache_auto_flush() to do so.
+ */
+void ewk_tile_unused_cache_max_set(Ewk_Tile_Unused_Cache *tuc, size_t max)
+{
+ EINA_SAFETY_ON_NULL_RETURN(tuc);
+ tuc->memory.max = max;
+}
+
+/**
+ * Retrieve maximum cache capacity, in bytes.
+ */
+size_t ewk_tile_unused_cache_max_get(const Ewk_Tile_Unused_Cache *tuc)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
+ return tuc->memory.max;
+}
+
+/**
+ * Retrieve the used cache capacity, in bytes.
+ */
+size_t ewk_tile_unused_cache_used_get(const Ewk_Tile_Unused_Cache *tuc)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
+ return tuc->memory.used;
+}
+
+/**
+ * Flush given amount of bytes from cache.
+ *
+ * After calling this function, near @a bytes are freed from cache. It
+ * may be less if cache did not contain that amount of bytes (ie: an
+ * empty cache has nothing to free!) or more if the cache just
+ * contained objects that were larger than the requested amount (this
+ * is usually the case).
+ *
+ * @param tuc cache of unused tiles.
+ * @param bytes amount to free.
+ *
+ * @return amount really freed.
+ *
+ * @see ewk_tile_unused_cache_used_get()
+ */
+size_t ewk_tile_unused_cache_flush(Ewk_Tile_Unused_Cache *tuc, size_t bytes)
+{
+ Ewk_Tile_Unused_Cache_Entry *itr;
+ Eina_List *l, *l_next;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
+ size_t done;
+ unsigned int count;
+
+ if (!tuc->entries.count)
+ return 0;
+ if (bytes < 1)
+ return 0;
+
+ /*
+ * NOTE: the cache is a FIFO queue currently.
+ * Don't need to sort any more.
+ */
+
+ if (tuc->dirty)
+ tuc->dirty = EINA_FALSE;
+
+ done = 0;
+ count = 0;
+ EINA_LIST_FOREACH_SAFE(tuc->entries.list, l, l_next, itr) {
+ Ewk_Tile *t = itr->tile;
+ if (done > bytes)
+ break;
+ if (tuc->locked.locked
+ && t->x + t->w > tuc->locked.x
+ && t->y + t->h > tuc->locked.y
+ && t->x < tuc->locked.x + tuc->locked.w
+ && t->y < tuc->locked.y + tuc->locked.h
+ && t->zoom == tuc->locked.zoom) {
+ continue;
+ }
+ done += sizeof(Ewk_Tile) + itr->tile->bytes;
+ itr->tile_free.cb(itr->tile_free.data, itr->tile);
+ tuc->entries.list = eina_list_remove_list(tuc->entries.list, l);
+ free(itr);
+ count++;
+ }
+
+ tuc->memory.used -= done;
+ tuc->entries.count -= count;
+
+ return done;
+}
+
+/**
+ * Flush enough bytes to make cache usage lower than maximum.
+ *
+ * Just like ewk_tile_unused_cache_flush(), but this will make the cache
+ * free enough tiles to respect maximum cache size as defined with
+ * ewk_tile_unused_cache_max_set().
+ *
+ * This function is usually called when system becomes idle. This way
+ * we keep memory low but do not impact performance when
+ * creating/deleting tiles.
+ */
+void ewk_tile_unused_cache_auto_flush(Ewk_Tile_Unused_Cache *tuc)
+{
+ EINA_SAFETY_ON_NULL_RETURN(tuc);
+ if (tuc->memory.used <= tuc->memory.max)
+ return;
+ ewk_tile_unused_cache_flush(tuc, tuc->memory.used - tuc->memory.max);
+ if (tuc->memory.used > tuc->memory.max)
+ CRITICAL("Cache still using too much memory: %zd KB; max: %zd KB",
+ tuc->memory.used, tuc->memory.max);
+}
+
+/**
+ * Flag cache as dirty.
+ *
+ * If cache is dirty then next flush operations will have to recompute
+ * weight and sort again to find the best tiles to expire.
+ *
+ * One must call this function when tile properties that may change
+ * likeness of tile to be flushed change, like Tile::stats.
+ */
+void ewk_tile_unused_cache_dirty(Ewk_Tile_Unused_Cache *tuc)
+{
+ tuc->dirty = EINA_TRUE;
+}
+
+/**
+ * Freeze cache to not do maintenance tasks.
+ *
+ * Maintenance tasks optimize cache usage, but maybe we know we should
+ * hold on them until we do the last operation, in this case we freeze
+ * while operating and then thaw when we're done.
+ *
+ * @see ewk_tile_unused_cache_thaw()
+ */
+void ewk_tile_unused_cache_freeze(Ewk_Tile_Unused_Cache *tuc)
+{
+ tuc->frozen++;
+}
+
+/**
+ * Unfreezes maintenance tasks.
+ *
+ * If this is the last counterpart of freeze, then maintenance tasks
+ * will run immediately.
+ */
+void ewk_tile_unused_cache_thaw(Ewk_Tile_Unused_Cache *tuc)
+{
+ if (!tuc->frozen) {
+ ERR("thawing more than freezing!");
+ return;
+ }
+
+ tuc->frozen--;
+}
+
+/**
+ * Get tile from cache of unused tiles, removing it from the cache.
+ *
+ * If the tile is used, then it's not in cache of unused tiles, so it
+ * is removed from the cache and may be given back with
+ * ewk_tile_unused_cache_tile_put().
+ *
+ * @param tuc cache of unused tiles
+ * @param t the tile to be removed from Ewk_Tile_Unused_Cache.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ */
+Eina_Bool ewk_tile_unused_cache_tile_get(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t)
+{
+ Ewk_Tile_Unused_Cache_Entry *entry;
+ Eina_List *e, *l;
+
+ e = NULL;
+ EINA_LIST_FOREACH(tuc->entries.list, l, entry)
+ {
+ if (entry->tile == t) {
+ e = l;
+ break;
+ }
+ }
+ if (!e) {
+ ERR("tile %p not found in cache %p", t, tuc);
+ return EINA_FALSE;
+ }
+
+ tuc->entries.count--;
+ tuc->memory.used -= sizeof(Ewk_Tile) + t->bytes;
+ tuc->entries.list = eina_list_remove_list(tuc->entries.list, e);
+ free(entry);
+ // TODO assume dirty for now, but may it's not,
+ // if the item was at the beginning of the queue
+ tuc->dirty = EINA_TRUE;
+
+ return EINA_TRUE;
+}
+
+/**
+ * Put tile into cache of unused tiles, adding it to the cache.
+ *
+ * This should be called when @c t->visible is @c 0 and no objects are
+ * using the tile anymore, making it available to be expired and have
+ * its memory replaced.
+ *
+ * Note that tiles are not automatically deleted if cache is full,
+ * instead the cache will have more bytes used than maximum and one
+ * can call ewk_tile_unused_cache_auto_flush() to free them. This is done
+ * because usually we want a lazy operation for better performance.
+ *
+ * @param tuc cache of unused tiles
+ * @param t tile to be added to cache.
+ * @param tile_free_cb function used to free tiles.
+ * @param data context to give back to @a tile_free_cb as first argument.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise. If @c t->visible
+ * is not #EINA_FALSE, then it will return #EINA_FALSE.
+ *
+ * @see ewk_tile_unused_cache_auto_flush()
+ */
+Eina_Bool ewk_tile_unused_cache_tile_put(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t, void (*tile_free_cb)(void *data, Ewk_Tile *t), const void *data)
+{
+ Ewk_Tile_Unused_Cache_Entry *e;
+
+ if (t->visible) {
+ ERR("tile=%p is not unused (visible=%d)", t, t->visible);
+ return EINA_FALSE;
+ }
+
+ MALLOC_OR_OOM_RET(e, sizeof(Ewk_Tile_Unused_Cache_Entry), EINA_FALSE);
+ tuc->entries.list = eina_list_append(tuc->entries.list, e);
+ if (eina_error_get()) {
+ ERR("List allocation failed");
+ return EINA_FALSE;
+ }
+
+ e->tile = t;
+ e->weight = 0; /* calculated just before sort */
+ e->tile_free.cb = tile_free_cb;
+ e->tile_free.data = (void *)data;
+
+ tuc->entries.count++;
+ tuc->memory.used += sizeof(Ewk_Tile) + t->bytes;
+ tuc->dirty = EINA_TRUE;
+
+ return EINA_TRUE;
+}
+
+void ewk_tile_unused_cache_dbg(const Ewk_Tile_Unused_Cache *tuc)
+{
+ Ewk_Tile_Unused_Cache_Entry *itr;
+ Eina_List *l;
+ int count = 0;
+ printf("Cache of unused tiles: entries: %zu/%zu, memory: %zu/%zu\n",
+ tuc->entries.count, tuc->entries.allocated,
+ tuc->memory.used, tuc->memory.max);
+
+ EINA_LIST_FOREACH(tuc->entries.list, l, itr) {
+ const Ewk_Tile *t = itr->tile;
+ printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c",
+ t->col, t->row, t->w, t->h, t->zoom,
+ t->visible ? '*': ' ');
+
+ if (!(count % 4))
+ printf("\n");
+ }
+
+ printf("\n");
+}
diff --git a/WebKit/efl/ewk/ewk_tiled_model.h b/WebKit/efl/ewk/ewk_tiled_model.h
new file mode 100644
index 0000000..a546cee
--- /dev/null
+++ b/WebKit/efl/ewk/ewk_tiled_model.h
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 2009-2010 Samsung Electronics
+ Copyright (C) 2009-2010 ProFUSION embedded systems
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef ewk_tiled_model_h
+#define ewk_tiled_model_h
+
+#include "ewk_eapi.h"
+#include "ewk_tiled_backing_store.h"
+
+#include <Evas.h>
+
+/* model */
+EAPI Ewk_Tile *ewk_tile_new(Evas *evas, Evas_Coord w, Evas_Coord h, float zoom, Evas_Colorspace cspace);
+EAPI void ewk_tile_free(Ewk_Tile *t);
+EAPI void ewk_tile_unused_cache_clear(Ewk_Tile_Unused_Cache *tuc);
+EAPI void ewk_tile_show(Ewk_Tile *t);
+EAPI void ewk_tile_hide(Ewk_Tile *t);
+Eina_Bool ewk_tile_visible_get(Ewk_Tile *t);
+EAPI void ewk_tile_update_full(Ewk_Tile *t);
+EAPI void ewk_tile_update_area(Ewk_Tile *t, const Eina_Rectangle *r);
+EAPI void ewk_tile_updates_process(Ewk_Tile *t, void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data);
+EAPI void ewk_tile_updates_clear(Ewk_Tile *t);
+
+/* cache of unused tiles */
+EAPI Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_new(size_t max);
+EAPI void ewk_tile_unused_cache_lock_area(Ewk_Tile_Unused_Cache *tuc, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom);
+EAPI void ewk_tile_unused_cache_unlock_area(Ewk_Tile_Unused_Cache *tuc);
+EAPI void ewk_tile_unused_cache_free(Ewk_Tile_Unused_Cache *tuc);
+EAPI Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_ref(Ewk_Tile_Unused_Cache *tuc);
+EAPI void ewk_tile_unused_cache_unref(Ewk_Tile_Unused_Cache *tuc);
+
+EAPI void ewk_tile_unused_cache_dirty(Ewk_Tile_Unused_Cache *tuc);
+
+EAPI void ewk_tile_unused_cache_freeze(Ewk_Tile_Unused_Cache *tuc);
+EAPI void ewk_tile_unused_cache_thaw(Ewk_Tile_Unused_Cache *tuc);
+
+EAPI Eina_Bool ewk_tile_unused_cache_tile_get(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t);
+EAPI Eina_Bool ewk_tile_unused_cache_tile_put(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t, void (*tile_free_cb)(void *data, Ewk_Tile *t), const void *data);
+
+#endif // ewk_tiled_model_h
+
diff --git a/WebKit/efl/ewk/ewk_tiled_private.h b/WebKit/efl/ewk/ewk_tiled_private.h
new file mode 100644
index 0000000..ce65f43
--- /dev/null
+++ b/WebKit/efl/ewk/ewk_tiled_private.h
@@ -0,0 +1,62 @@
+/*
+ Copyright (C) 2009-2010 Samsung Electronics
+ Copyright (C) 2009-2010 ProFUSION embedded systems
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef ewk_tiled_private_h
+#define ewk_tiled_private_h
+
+/* logging */
+extern int _ewk_tiled_log_dom;
+
+#define CRITICAL(...) EINA_LOG_DOM_CRIT(_ewk_tiled_log_dom, __VA_ARGS__)
+#define ERR(...) EINA_LOG_DOM_ERR(_ewk_tiled_log_dom, __VA_ARGS__)
+#define WRN(...) EINA_LOG_DOM_WARN(_ewk_tiled_log_dom, __VA_ARGS__)
+#define INF(...) EINA_LOG_DOM_INFO(_ewk_tiled_log_dom, __VA_ARGS__)
+#define DBG(...) EINA_LOG_DOM_DBG(_ewk_tiled_log_dom, __VA_ARGS__)
+#define OOM(op, size) CRITICAL("could not %s %zd bytes: %s", op, size, strerror(errno))
+#define MALLOC_OR_OOM_RET(ptr, size, ...) \
+ do { \
+ ptr = malloc(size); \
+ if (!ptr && (size) > 0) { \
+ OOM("malloc", (size)); \
+ return __VA_ARGS__; \
+ } \
+ } while (0)
+
+#define CALLOC_OR_OOM_RET(ptr, size, ...) \
+ do { \
+ ptr = calloc(1, size); \
+ if (!ptr && (size) > 0) { \
+ OOM("calloc", (size)); \
+ return __VA_ARGS__; \
+ } \
+ } while (0)
+
+#define REALLOC_OR_OOM_RET(ptr, size, ...) \
+ do { \
+ void *__tmp_ptr; \
+ __tmp_ptr = realloc(ptr, size); \
+ if (!__tmp_ptr && (size) > 0) { \
+ OOM("realloc", (size)); \
+ return __VA_ARGS__; \
+ } \
+ ptr = __tmp_ptr; \
+ } while (0)
+
+#endif // ewk_tiled_private_h
diff --git a/WebKit/efl/ewk/ewk_view.cpp b/WebKit/efl/ewk/ewk_view.cpp
index 5ff3ade..5147b6d 100644
--- a/WebKit/efl/ewk/ewk_view.cpp
+++ b/WebKit/efl/ewk/ewk_view.cpp
@@ -94,6 +94,9 @@ struct _Ewk_View_Private_Data {
} scrolls;
unsigned int imh; /**< input method hints */
struct {
+ Eina_Bool view_cleared:1;
+ } flags;
+ struct {
const char* user_agent;
const char* user_stylesheet;
const char* encoding_default;
@@ -725,6 +728,12 @@ static void _ewk_view_smart_add(Evas_Object* o)
evas_object_smart_member_add(sd->backing_store, o);
evas_object_show(sd->backing_store);
+ evas_object_pass_events_set(sd->backing_store, EINA_TRUE);
+
+ sd->events_rect = evas_object_rectangle_add(sd->base.evas);
+ evas_object_color_set(sd->events_rect, 0, 0, 0, 0);
+ evas_object_smart_member_add(sd->events_rect, o);
+ evas_object_show(sd->events_rect);
sd->main_frame = ewk_frame_add(sd->base.evas);
if (!sd->main_frame) {
@@ -815,6 +824,7 @@ static void _ewk_view_smart_calculate(Evas_Object* o)
view->adjustViewSize();
}
evas_object_resize(sd->main_frame, w, h);
+ evas_object_resize(sd->events_rect, w, h);
sd->changed.frame_rect = EINA_TRUE;
sd->view.w = w;
sd->view.h = h;
@@ -827,6 +837,7 @@ static void _ewk_view_smart_calculate(Evas_Object* o)
if (sd->changed.position && ((x != sd->view.x) || (y != sd->view.y))) {
evas_object_move(sd->main_frame, x, y);
evas_object_move(sd->backing_store, x, y);
+ evas_object_move(sd->events_rect, x, y);
sd->changed.frame_rect = EINA_TRUE;
sd->view.x = x;
sd->view.y = y;
@@ -850,6 +861,25 @@ static void _ewk_view_smart_calculate(Evas_Object* o)
}
}
+static void _ewk_view_smart_show(Evas_Object *o)
+{
+ EWK_VIEW_SD_GET(o, sd);
+ EWK_VIEW_PRIV_GET(sd, priv);
+
+ if (evas_object_clipees_get(sd->base.clipper))
+ evas_object_show(sd->base.clipper);
+ evas_object_show(sd->backing_store);
+}
+
+static void _ewk_view_smart_hide(Evas_Object *o)
+{
+ EWK_VIEW_SD_GET(o, sd);
+ EWK_VIEW_PRIV_GET(sd, priv);
+
+ evas_object_hide(sd->base.clipper);
+ evas_object_hide(sd->backing_store);
+}
+
static Eina_Bool _ewk_view_smart_contents_resize(Ewk_View_Smart_Data* sd, int w, int h)
{
return EINA_TRUE;
@@ -897,6 +927,13 @@ static Eina_Bool _ewk_view_smart_pre_render_region(Ewk_View_Smart_Data* sd, Evas
return EINA_FALSE;
}
+static Eina_Bool _ewk_view_smart_pre_render_relative_radius(Ewk_View_Smart_Data* sd, unsigned int n, float zoom)
+{
+ WRN("not supported by engine. sd=%p, n=%u zoom=%f",
+ sd, n, zoom);
+ return EINA_FALSE;
+}
+
static void _ewk_view_smart_pre_render_cancel(Ewk_View_Smart_Data* sd)
{
WRN("not supported by engine. sd=%p", sd);
@@ -954,6 +991,7 @@ static Eina_Bool _ewk_view_zoom_animator_cb(void* data)
|| (now < priv->animated_zoom.time.start)) {
_ewk_view_zoom_animated_finish(sd);
ewk_view_zoom_set(sd->self, priv->animated_zoom.zoom.end, cx, cy);
+ sd->api->sc.calculate(sd->self);
return EINA_FALSE;
}
@@ -991,6 +1029,18 @@ static WebCore::ViewportAttributes _ewk_view_viewport_attributes_compute(Evas_Ob
return attributes;
}
+static Eina_Bool _ewk_view_smart_disable_render(Ewk_View_Smart_Data *sd)
+{
+ WRN("not supported by engine. sd=%p", sd);
+ return EINA_FALSE;
+}
+
+static Eina_Bool _ewk_view_smart_enable_render(Ewk_View_Smart_Data *sd)
+{
+ WRN("not supported by engine. sd=%p", sd);
+ return EINA_FALSE;
+}
+
/**
* Sets the smart class api without any backing store, enabling view
* to be inherited.
@@ -1030,13 +1080,18 @@ Eina_Bool ewk_view_base_smart_set(Ewk_View_Smart_Class* api)
api->sc.resize = _ewk_view_smart_resize;
api->sc.move = _ewk_view_smart_move;
api->sc.calculate = _ewk_view_smart_calculate;
+ api->sc.show = _ewk_view_smart_show;
+ api->sc.hide = _ewk_view_smart_hide;
api->sc.data = EWK_VIEW_TYPE_STR; /* used by type checking */
api->contents_resize = _ewk_view_smart_contents_resize;
api->zoom_set = _ewk_view_smart_zoom_set;
api->flush = _ewk_view_smart_flush;
api->pre_render_region = _ewk_view_smart_pre_render_region;
+ api->pre_render_relative_radius = _ewk_view_smart_pre_render_relative_radius;
api->pre_render_cancel = _ewk_view_smart_pre_render_cancel;
+ api->disable_render = _ewk_view_smart_disable_render;
+ api->enable_render = _ewk_view_smart_enable_render;
api->focus_in = _ewk_view_smart_focus_in;
api->focus_out = _ewk_view_smart_focus_out;
@@ -2171,10 +2226,19 @@ Eina_Bool ewk_view_zoom_text_only_set(Evas_Object* o, Eina_Bool setting)
Eina_Bool ewk_view_pre_render_region(Evas_Object* o, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom)
{
EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE);
+ EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE);
EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->pre_render_region, EINA_FALSE);
- float cur_zoom = ewk_frame_zoom_get(sd->main_frame);
+ float cur_zoom;
Evas_Coord cw, ch;
+ /* When doing animated zoom it's not possible to call pre-render since it
+ * would screw up parameters that animation is currently using
+ */
+ if (priv->animated_zoom.animator)
+ return EINA_FALSE;
+
+ cur_zoom = ewk_frame_zoom_get(sd->main_frame);
+
if (cur_zoom < 0.00001)
return EINA_FALSE;
if (!ewk_frame_contents_size_get(sd->main_frame, &cw, &ch))
@@ -2203,6 +2267,35 @@ Eina_Bool ewk_view_pre_render_region(Evas_Object* o, Evas_Coord x, Evas_Coord y,
}
/**
+ * Hint engine to pre-render region, given n extra cols/rows
+ *
+ * This is an alternative method to ewk_view_pre_render_region(). It does not
+ * make sense in all engines and therefore it might not be implemented at all.
+ *
+ * It's only useful if engine divide the area being rendered in smaller tiles,
+ * forming a grid. Then, browser could call this function to pre-render @param n
+ * rows/cols involving the current viewport.
+ *
+ * @param o view to ask pre-render on.
+ * @param n number of cols/rows that must be part of the region pre-rendered
+ *
+ * @see ewk_view_pre_render_region()
+ */
+Eina_Bool ewk_view_pre_render_relative_radius(Evas_Object* o, unsigned int n)
+{
+ EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE);
+ EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->pre_render_relative_radius, EINA_FALSE);
+ float cur_zoom;
+
+ if (priv->animated_zoom.animator)
+ return EINA_FALSE;
+
+ cur_zoom = ewk_frame_zoom_get(sd->main_frame);
+ return sd->api->pre_render_relative_radius(sd, n, cur_zoom);
+}
+
+/**
* Get input method hints
*
* @param o View.
@@ -2228,6 +2321,36 @@ void ewk_view_pre_render_cancel(Evas_Object* o)
sd->api->pre_render_cancel(sd);
}
+/**
+ * Enable processing of update requests.
+ *
+ * @param o view to enable rendering.
+ *
+ * @return @c EINA_TRUE if render was enabled, @c EINA_FALSE
+ otherwise (errors, rendering suspension not supported).
+ */
+Eina_Bool ewk_view_enable_render(const Evas_Object *o)
+{
+ EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->enable_render, EINA_FALSE);
+ return sd->api->enable_render(sd);
+}
+
+/**
+ * Disable processing of update requests.
+ *
+ * @param o view to disable rendering.
+ *
+ * @return @c EINA_TRUE if render was disabled, @c EINA_FALSE
+ otherwise (errors, rendering suspension not supported).
+ */
+Eina_Bool ewk_view_disable_render(const Evas_Object *o)
+{
+ EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->disable_render, EINA_FALSE);
+ return sd->api->disable_render(sd);
+}
+
const char* ewk_view_setting_user_agent_get(const Evas_Object* o)
{
EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0);
@@ -3952,7 +4075,6 @@ void ewk_view_scroll(Evas_Object* o, Evas_Coord dx, Evas_Coord dy, Evas_Coord sx
EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv);
EINA_SAFETY_ON_TRUE_RETURN(!dx && !dy);
- _ewk_view_scroll_add(priv, dx, dy, sx, sy, sw, sh, main_frame);
_ewk_view_smart_changed(sd);
}
@@ -4284,6 +4406,42 @@ float ewk_view_device_pixel_ratio_get(Evas_Object* o)
return priv->settings.device_pixel_ratio;
}
+void ewk_view_did_first_visually_nonempty_layout(Evas_Object *o)
+{
+ EWK_VIEW_SD_GET_OR_RETURN(o, sd);
+ EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv);
+ if (!priv->flags.view_cleared) {
+ ewk_view_frame_main_cleared(o);
+ ewk_view_enable_render(o);
+ priv->flags.view_cleared = EINA_TRUE;
+ }
+}
+
+/**
+ * @internal
+ * Dispatch finished loading.
+ *
+ * @param o view.
+ */
+void ewk_view_dispatch_did_finish_loading(Evas_Object *o)
+{
+ /* If we reach this point and rendering is still disabled, WebCore will not
+ * trigger the didFirstVisuallyNonEmptyLayout signal anymore. So, we
+ * forcefully re-enable the rendering.
+ */
+ ewk_view_did_first_visually_nonempty_layout(o);
+}
+
+void ewk_view_transition_to_commited_for_newpage(Evas_Object *o)
+{
+ EWK_VIEW_SD_GET_OR_RETURN(o, sd);
+ EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv);
+
+ ewk_view_disable_render(o);
+ priv->flags.view_cleared = EINA_FALSE;
+}
+
+
/**
* @internal
* Reports a requeset will be loaded. It's client responsibility to decide if
diff --git a/WebKit/efl/ewk/ewk_view.h b/WebKit/efl/ewk/ewk_view.h
index 1c4de34..6902949 100644
--- a/WebKit/efl/ewk/ewk_view.h
+++ b/WebKit/efl/ewk/ewk_view.h
@@ -114,7 +114,11 @@ struct _Ewk_View_Smart_Class {
void (*bg_color_set)(Ewk_View_Smart_Data *sd, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
void (*flush)(Ewk_View_Smart_Data *sd);
Eina_Bool (*pre_render_region)(Ewk_View_Smart_Data *sd, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom);
+ Eina_Bool (*pre_render_relative_radius)(Ewk_View_Smart_Data *sd, unsigned int n, float zoom);
void (*pre_render_cancel)(Ewk_View_Smart_Data *sd);
+ Eina_Bool (*disable_render)(Ewk_View_Smart_Data *sd);
+ Eina_Bool (*enable_render)(Ewk_View_Smart_Data *sd);
+
// event handling:
// - returns true if handled
// - if overridden, have to call parent method if desired
@@ -151,7 +155,7 @@ struct _Ewk_View_Smart_Class {
* @see EWK_VIEW_SMART_CLASS_INIT_VERSION
* @see EWK_VIEW_SMART_CLASS_INIT_NAME_VERSION
*/
-#define EWK_VIEW_SMART_CLASS_INIT(smart_class_init) {smart_class_init, EWK_VIEW_SMART_CLASS_VERSION, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define EWK_VIEW_SMART_CLASS_INIT(smart_class_init) {smart_class_init, EWK_VIEW_SMART_CLASS_VERSION, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
/**
* Initializer to zero a whole Ewk_View_Smart_Class structure.
@@ -268,6 +272,7 @@ struct _Ewk_View_Smart_Data {
Evas_Object *self; /**< reference to owner object */
Evas_Object *main_frame; /**< reference to main frame object */
Evas_Object *backing_store; /**< reference to backing store */
+ Evas_Object *events_rect; /**< rectangle that should receive mouse events */
Ewk_View_Private_Data *_priv; /**< should never be accessed, c++ stuff */
struct {
Evas_Coord x, y, w, h; /**< last used viewport */
@@ -291,10 +296,36 @@ struct _Ewk_View_Smart_Data {
} changed;
};
+/**
+ * Cache (pool) that contains unused tiles for ewk_view_tiled.
+ *
+ * This cache will maintain unused tiles and flush them when the total
+ * memory exceeds the set amount when
+ * ewk_tile_unused_cache_auto_flush() or explicitly set value when
+ * ewk_tile_unused_cache_flush() is called.
+ *
+ * The tile may be shared among different ewk_view_tiled instances to
+ * group maximum unused memory resident in the system.
+ */
+typedef struct _Ewk_Tile_Unused_Cache Ewk_Tile_Unused_Cache;
+EAPI void ewk_tile_unused_cache_max_set(Ewk_Tile_Unused_Cache *tuc, size_t max);
+EAPI size_t ewk_tile_unused_cache_max_get(const Ewk_Tile_Unused_Cache *tuc);
+EAPI size_t ewk_tile_unused_cache_used_get(const Ewk_Tile_Unused_Cache *tuc);
+EAPI size_t ewk_tile_unused_cache_flush(Ewk_Tile_Unused_Cache *tuc, size_t bytes);
+EAPI void ewk_tile_unused_cache_auto_flush(Ewk_Tile_Unused_Cache *tuc);
+
EAPI Eina_Bool ewk_view_base_smart_set(Ewk_View_Smart_Class *api);
EAPI Eina_Bool ewk_view_single_smart_set(Ewk_View_Smart_Class *api);
+EAPI Eina_Bool ewk_view_tiled_smart_set(Ewk_View_Smart_Class *api);
EAPI Evas_Object *ewk_view_single_add(Evas *e);
+EAPI Evas_Object *ewk_view_tiled_add(Evas *e);
+
+EAPI Ewk_Tile_Unused_Cache *ewk_view_tiled_unused_cache_get(const Evas_Object *o);
+EAPI void ewk_view_tiled_unused_cache_set(Evas_Object *o, Ewk_Tile_Unused_Cache *cache);
+
+// FIXME: this function should be removed later, when we find the best flag to use.
+EAPI void ewk_view_tiled_process_entire_queue_set(Evas_Object *o, Eina_Bool flag);
EAPI void ewk_view_fixed_layout_size_set(Evas_Object *o, Evas_Coord w, Evas_Coord h);
EAPI void ewk_view_fixed_layout_size_get(Evas_Object *o, Evas_Coord *w, Evas_Coord *h);
@@ -370,7 +401,10 @@ EAPI Eina_Bool ewk_view_zoom_text_only_get(const Evas_Object *o);
EAPI Eina_Bool ewk_view_zoom_text_only_set(Evas_Object *o, Eina_Bool setting);
EAPI Eina_Bool ewk_view_pre_render_region(Evas_Object *o, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom);
+EAPI Eina_Bool ewk_view_pre_render_relative_radius(Evas_Object *o, unsigned int n);
EAPI void ewk_view_pre_render_cancel(Evas_Object *o);
+EAPI Eina_Bool ewk_view_enable_render(const Evas_Object *o);
+EAPI Eina_Bool ewk_view_disable_render(const Evas_Object *o);
EAPI unsigned int ewk_view_imh_get(Evas_Object *o);
diff --git a/WebKit/efl/ewk/ewk_view_tiled.c b/WebKit/efl/ewk/ewk_view_tiled.c
new file mode 100644
index 0000000..d380e8e
--- /dev/null
+++ b/WebKit/efl/ewk/ewk_view_tiled.c
@@ -0,0 +1,346 @@
+/*
+ Copyright (C) 2009-2010 Samsung Electronics
+ Copyright (C) 2009-2010 ProFUSION embedded systems
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include "config.h"
+#include "ewk_view.h"
+
+#include "ewk_logging.h"
+
+#include <Evas.h>
+#include <eina_safety_checks.h>
+#include <ewk_tiled_backing_store.h>
+
+static Ewk_View_Smart_Class _parent_sc = EWK_VIEW_SMART_CLASS_INIT_NULL;
+
+static Eina_Bool _ewk_view_tiled_render_cb(void *data, Ewk_Tile *t, const Eina_Rectangle *area)
+{
+ Ewk_View_Private_Data *priv = (Ewk_View_Private_Data*)data;
+ Eina_Rectangle r = {area->x + t->x, area->y + t->y, area->w, area->h};
+
+ return ewk_view_paint_contents(priv, t->cairo, &r);
+}
+
+static void *_ewk_view_tiled_updates_process_pre(void *data, Evas_Object *o)
+{
+ Ewk_View_Private_Data *priv = (Ewk_View_Private_Data*)data;
+ ewk_view_layout_if_needed_recursive(priv);
+ return 0;
+}
+
+static Evas_Object *_ewk_view_tiled_smart_backing_store_add(Ewk_View_Smart_Data *sd)
+{
+ Evas_Object *bs = ewk_tiled_backing_store_add(sd->base.evas);
+ ewk_tiled_backing_store_render_cb_set
+ (bs, _ewk_view_tiled_render_cb, sd->_priv);
+ ewk_tiled_backing_store_updates_process_pre_set
+ (bs, _ewk_view_tiled_updates_process_pre, sd->_priv);
+ return bs;
+}
+
+static void
+_ewk_view_tiled_contents_size_changed_cb(void *data, Evas_Object *o, void *event_info)
+{
+ Evas_Coord *size = (Evas_Coord*)event_info;
+ Ewk_View_Smart_Data *sd = (Ewk_View_Smart_Data*)data;
+
+ ewk_tiled_backing_store_contents_resize
+ (sd->backing_store, size[0], size[1]);
+}
+
+static void _ewk_view_tiled_smart_add(Evas_Object *o)
+{
+ Ewk_View_Smart_Data *sd;
+
+ _parent_sc.sc.add(o);
+
+ sd = (Ewk_View_Smart_Data*)evas_object_smart_data_get(o);
+ evas_object_smart_callback_add(
+ sd->main_frame, "contents,size,changed",
+ _ewk_view_tiled_contents_size_changed_cb, sd);
+ ewk_frame_paint_full_set(sd->main_frame, EINA_TRUE);
+}
+
+static Eina_Bool _ewk_view_tiled_smart_scrolls_process(Ewk_View_Smart_Data *sd)
+{
+ const Ewk_Scroll_Request *sr;
+ const Ewk_Scroll_Request *sr_end;
+ size_t count;
+ Evas_Coord vw, vh;
+
+ ewk_frame_contents_size_get(sd->main_frame, &vw, &vh);
+
+ sr = ewk_view_scroll_requests_get(sd->_priv, &count);
+ sr_end = sr + count;
+ for (; sr < sr_end; sr++) {
+ if (sr->main_scroll)
+ ewk_tiled_backing_store_scroll_full_offset_add
+ (sd->backing_store, sr->dx, sr->dy);
+ else {
+ Evas_Coord sx, sy, sw, sh;
+
+ sx = sr->x;
+ sy = sr->y;
+ sw = sr->w;
+ sh = sr->h;
+
+ if (abs(sr->dx) >= sw || abs(sr->dy) >= sh) {
+ /* doubt webkit would be so stupid... */
+ DBG("full page scroll %+03d,%+03d. convert to repaint %d,%d + %dx%d",
+ sr->dx, sr->dy, sx, sy, sw, sh);
+ ewk_view_repaint_add(sd->_priv, sx, sy, sw, sh);
+ continue;
+ }
+
+ if (sx + sw > vw)
+ sw = vw - sx;
+ if (sy + sh > vh)
+ sh = vh - sy;
+
+ if (sw < 0)
+ sw = 0;
+ if (sh < 0)
+ sh = 0;
+
+ if (!sw || !sh)
+ continue;
+
+ sx -= abs(sr->dx);
+ sy -= abs(sr->dy);
+ sw += abs(sr->dx);
+ sh += abs(sr->dy);
+ ewk_view_repaint_add(sd->_priv, sx, sy, sw, sh);
+ INF("using repaint for inner frame scolling!");
+ }
+ }
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool _ewk_view_tiled_smart_repaints_process(Ewk_View_Smart_Data *sd)
+{
+ const Eina_Rectangle *pr, *pr_end;
+ size_t count;
+ int sx, sy;
+
+ ewk_frame_scroll_pos_get(sd->main_frame, &sx, &sy);
+
+ pr = ewk_view_repaints_get(sd->_priv, &count);
+ pr_end = pr + count;
+ for (; pr < pr_end; pr++) {
+ Eina_Rectangle r;
+ r.x = pr->x + sx;
+ r.y = pr->y + sy;
+ r.w = pr->w;
+ r.h = pr->h;
+ ewk_tiled_backing_store_update(sd->backing_store, &r);
+ }
+ ewk_tiled_backing_store_updates_process(sd->backing_store);
+
+ return EINA_TRUE;
+}
+
+static Eina_Bool _ewk_view_tiled_smart_contents_resize(Ewk_View_Smart_Data *sd, int w, int h)
+{
+ ewk_tiled_backing_store_contents_resize(sd->backing_store, w, h);
+ return EINA_TRUE;
+}
+
+static Eina_Bool _ewk_view_tiled_smart_zoom_set(Ewk_View_Smart_Data *sd, float zoom, Evas_Coord cx, Evas_Coord cy)
+{
+ Evas_Coord x, y, w, h;
+ Eina_Bool r;
+ r = ewk_tiled_backing_store_zoom_set(sd->backing_store,
+ &zoom, cx, cy, &x, &y);
+ if (!r)
+ return r;
+ ewk_tiled_backing_store_disabled_update_set(sd->backing_store, EINA_TRUE);
+ r = _parent_sc.zoom_set(sd, zoom, cx, cy);
+ ewk_frame_scroll_set(sd->main_frame, -x, -y);
+ ewk_frame_scroll_size_get(sd->main_frame, &w, &h);
+ ewk_tiled_backing_store_fix_offsets(sd->backing_store, w, h);
+ ewk_view_scrolls_process(sd);
+ evas_object_smart_calculate(sd->backing_store);
+ evas_object_smart_calculate(sd->self);
+ ewk_tiled_backing_store_disabled_update_set(sd->backing_store, EINA_FALSE);
+ return r;
+}
+
+static Eina_Bool _ewk_view_tiled_smart_zoom_weak_set(Ewk_View_Smart_Data *sd, float zoom, Evas_Coord cx, Evas_Coord cy)
+{
+ return ewk_tiled_backing_store_zoom_weak_set(sd->backing_store, zoom, cx, cy);
+}
+
+static void _ewk_view_tiled_smart_zoom_weak_smooth_scale_set(Ewk_View_Smart_Data *sd, Eina_Bool smooth_scale)
+{
+ ewk_tiled_backing_store_zoom_weak_smooth_scale_set(sd->backing_store, smooth_scale);
+}
+
+static void _ewk_view_tiled_smart_flush(Ewk_View_Smart_Data *sd)
+{
+ ewk_tiled_backing_store_flush(sd->backing_store);
+ _parent_sc.flush(sd);
+}
+
+static Eina_Bool _ewk_view_tiled_smart_pre_render_region(Ewk_View_Smart_Data *sd, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom)
+{
+ return ewk_tiled_backing_store_pre_render_region
+ (sd->backing_store, x, y, w, h, zoom);
+}
+
+static Eina_Bool _ewk_view_tiled_smart_pre_render_relative_radius(Ewk_View_Smart_Data *sd, unsigned int n, float zoom)
+{
+ return ewk_tiled_backing_store_pre_render_relative_radius
+ (sd->backing_store, n, zoom);
+}
+
+static void _ewk_view_tiled_smart_pre_render_cancel(Ewk_View_Smart_Data *sd)
+{
+ ewk_tiled_backing_store_pre_render_cancel(sd->backing_store);
+}
+
+static Eina_Bool _ewk_view_tiled_smart_disable_render(Ewk_View_Smart_Data *sd)
+{
+ return ewk_tiled_backing_store_disable_render(sd->backing_store);
+}
+
+static Eina_Bool _ewk_view_tiled_smart_enable_render(Ewk_View_Smart_Data *sd)
+{
+ return ewk_tiled_backing_store_enable_render(sd->backing_store);
+}
+
+/**
+ * Sets the smart class api using tiled backing store, enabling view
+ * to be inherited.
+ *
+ * @param api class definition to be set, all members with the
+ * exception of Evas_Smart_Class->data may be overridden. Must
+ * @b not be @c NULL.
+ *
+ * @note Evas_Smart_Class->data is used to implement type checking and
+ * is not supposed to be changed/overridden. If you need extra
+ * data for your smart class to work, just extend
+ * Ewk_View_Smart_Class instead.
+ *
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on failure (probably
+ * version mismatch).
+ *
+ * @see ewk_view_base_smart_set()
+ */
+Eina_Bool ewk_view_tiled_smart_set(Ewk_View_Smart_Class *api)
+{
+ if (!ewk_view_base_smart_set(api))
+ return EINA_FALSE;
+
+ if (EINA_UNLIKELY(!_parent_sc.sc.add))
+ ewk_view_base_smart_set(&_parent_sc);
+
+ api->sc.add = _ewk_view_tiled_smart_add;
+
+ api->backing_store_add = _ewk_view_tiled_smart_backing_store_add;
+ api->scrolls_process = _ewk_view_tiled_smart_scrolls_process;
+ api->repaints_process = _ewk_view_tiled_smart_repaints_process;
+ api->contents_resize = _ewk_view_tiled_smart_contents_resize;
+ api->zoom_set = _ewk_view_tiled_smart_zoom_set;
+ api->zoom_weak_set = _ewk_view_tiled_smart_zoom_weak_set;
+ api->zoom_weak_smooth_scale_set = _ewk_view_tiled_smart_zoom_weak_smooth_scale_set;
+ api->flush = _ewk_view_tiled_smart_flush;
+ api->pre_render_region = _ewk_view_tiled_smart_pre_render_region;
+ api->pre_render_relative_radius = _ewk_view_tiled_smart_pre_render_relative_radius;
+ api->pre_render_cancel = _ewk_view_tiled_smart_pre_render_cancel;
+ api->disable_render = _ewk_view_tiled_smart_disable_render;
+ api->enable_render = _ewk_view_tiled_smart_enable_render;
+ return EINA_TRUE;
+}
+
+static inline Evas_Smart *_ewk_view_tiled_smart_class_new(void)
+{
+ static Ewk_View_Smart_Class api = EWK_VIEW_SMART_CLASS_INIT_NAME_VERSION("EWK_View_Tiled");
+ static Evas_Smart *smart = 0;
+
+ if (EINA_UNLIKELY(!smart)) {
+ ewk_view_tiled_smart_set(&api);
+ smart = evas_smart_class_new(&api.sc);
+ }
+
+ return smart;
+}
+
+/**
+ * Creates a new EFL WebKit View object using tiled backing store.
+ *
+ * View objects are the recommended way to deal with EFL WebKit as it
+ * abstracts the complex pieces of the process.
+ *
+ * This object is almost the same as the one returned by the ewk_view_add()
+ * function, but it uses the tiled backing store instead of the default
+ * backing store.
+ *
+ * @param e canvas where to create the view object.
+ *
+ * @return view object or @c NULL if errors.
+ *
+ * @see ewk_view_uri_set()
+ */
+Evas_Object *ewk_view_tiled_add(Evas *e)
+{
+ return evas_object_smart_add(e, _ewk_view_tiled_smart_class_new());
+}
+
+/**
+ * Get the cache of unused tiles used by this view.
+ *
+ * @param o view object to get cache.
+ * @return instance of "cache of unused tiles" or @c NULL on errors.
+ */
+Ewk_Tile_Unused_Cache *ewk_view_tiled_unused_cache_get(const Evas_Object *o)
+{
+ Ewk_View_Smart_Data *sd = ewk_view_smart_data_get(o);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(sd, 0);
+ return ewk_tiled_backing_store_tile_unused_cache_get(sd->backing_store);
+}
+
+/**
+ * Set the cache of unused tiles used by this view.
+ *
+ * @param o view object to get cache.
+ * @param cache instance of "cache of unused tiles". This can be used
+ * to share a single cache amongst different views. The tiles
+ * from one view will not be used by the other! This is just to
+ * limit the group with amount of unused memory.
+ * If @c NULL is provided, then a new cache is created.
+ */
+void ewk_view_tiled_unused_cache_set(Evas_Object *o, Ewk_Tile_Unused_Cache *cache)
+{
+ Ewk_View_Smart_Data *sd = ewk_view_smart_data_get(o);
+ EINA_SAFETY_ON_NULL_RETURN(sd);
+ ewk_tiled_backing_store_tile_unused_cache_set(sd->backing_store, cache);
+}
+
+/**
+ * Set the function with the same name of the tiled backing store.
+ * @param o the tiled backing store object.
+ * @param flag value of the tiled backing store flag to set.
+ */
+void ewk_view_tiled_process_entire_queue_set(Evas_Object *o, Eina_Bool flag)
+{
+ Ewk_View_Smart_Data *sd = ewk_view_smart_data_get(o);
+ EINA_SAFETY_ON_NULL_RETURN(sd);
+ ewk_tiled_backing_store_process_entire_queue_set(sd->backing_store, flag);
+}