Home | History | Annotate | Download | only in ewk
      1 /*
      2     Copyright (C) 2009-2010 Samsung Electronics
      3     Copyright (C) 2009-2010 ProFUSION embedded systems
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Library General Public
      7     License as published by the Free Software Foundation; either
      8     version 2 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Library General Public License for more details.
     14 
     15     You should have received a copy of the GNU Library General Public License
     16     along with this library; see the file COPYING.LIB.  If not, write to
     17     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18     Boston, MA 02110-1301, USA.
     19 */
     20 
     21 #include "config.h"
     22 #include "ewk_tiled_backing_store.h"
     23 
     24 #define _GNU_SOURCE
     25 #include "ewk_tiled_private.h"
     26 #include <Ecore.h>
     27 #include <Eina.h>
     28 #include <errno.h>
     29 #include <math.h>
     30 #include <stdio.h> // XXX REMOVE ME LATER
     31 #include <stdlib.h>
     32 #include <string.h>
     33 
     34 #define IDX(col, row, rowspan) (col + (row * rowspan))
     35 
     36 #if !defined(MIN)
     37 # define MIN(a, b) ((a < b) ? a : b)
     38 #endif
     39 
     40 #if !defined(MAX)
     41 # define MAX(a, b) ((a > b) ? a : b)
     42 #endif
     43 
     44 typedef enum _Ewk_Tiled_Backing_Store_Pre_Render_Priority Ewk_Tiled_Backing_Store_Pre_Render_Priority;
     45 typedef struct _Ewk_Tiled_Backing_Store_Data Ewk_Tiled_Backing_Store_Data;
     46 typedef struct _Ewk_Tiled_Backing_Store_Item Ewk_Tiled_Backing_Store_Item;
     47 typedef struct _Ewk_Tiled_Backing_Store_Pre_Render_Request Ewk_Tiled_Backing_Store_Pre_Render_Request;
     48 
     49 enum _Ewk_Tiled_Backing_Store_Pre_Render_Priority {
     50     PRE_RENDER_PRIORITY_LOW = 0, /**< Append the request to the list */
     51     PRE_RENDER_PRIORITY_HIGH     /**< Prepend the request to the list */
     52 };
     53 
     54 struct _Ewk_Tiled_Backing_Store_Item {
     55     EINA_INLIST;
     56     Ewk_Tile *tile;
     57     struct {
     58         Evas_Coord x, y, w, h;
     59     } geometry;
     60     struct {
     61         Eina_List *process;
     62         unsigned long row, col;
     63         float zoom;
     64     } update;
     65     Eina_Bool smooth_scale;
     66 };
     67 
     68 struct _Ewk_Tiled_Backing_Store_Pre_Render_Request {
     69     EINA_INLIST;
     70     unsigned long col, row;
     71     float zoom;
     72 };
     73 
     74 struct _Ewk_Tiled_Backing_Store_Data {
     75     Evas_Object_Smart_Clipped_Data base;
     76     Evas_Object *self;
     77     Evas_Object *contents_clipper;
     78     struct {
     79         Eina_Inlist **items;
     80         Evas_Coord x, y, w, h;
     81         long cols, rows;
     82         struct {
     83             Evas_Coord w, h;
     84             float zoom;
     85             Eina_Bool zoom_weak_smooth_scale:1;
     86         } tile;
     87         struct {
     88             struct {
     89                 Evas_Coord x, y;
     90             } cur, old, base, zoom_center;
     91         } offset;
     92     } view;
     93     Evas_Colorspace cspace;
     94     struct {
     95         Ewk_Tile_Matrix *matrix;
     96         struct {
     97             unsigned long col, row;
     98         } base;
     99         struct {
    100             unsigned long cols, rows;
    101         } cur, old;
    102         Evas_Coord width, height;
    103     } model;
    104     struct {
    105         Eina_Bool (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *area);
    106         void *data;
    107         Eina_List *queue;
    108         Eina_Bool process_entire_queue;
    109         Eina_Inlist *pre_render_requests;
    110         Ecore_Idler *idler;
    111         Eina_Bool disabled;
    112         Eina_Bool suspend:1;
    113     } render;
    114     struct {
    115         void *(*pre_cb)(void *data, Evas_Object *o);
    116         void *pre_data;
    117         void *(*post_cb)(void *data, void *pre_data, Evas_Object *o);
    118         void *post_data;
    119     } process;
    120     struct {
    121         Eina_Bool any:1;
    122         Eina_Bool pos:1;
    123         Eina_Bool size:1;
    124         Eina_Bool model:1;
    125         Eina_Bool offset:1;
    126     } changed;
    127 #ifdef DEBUG_MEM_LEAKS
    128     Ecore_Event_Handler *sig_usr;
    129 #endif
    130 };
    131 
    132 static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL;
    133 int _ewk_tiled_log_dom = -1;
    134 
    135 #define PRIV_DATA_GET_OR_RETURN(obj, ptr, ...)                       \
    136     Ewk_Tiled_Backing_Store_Data *ptr = evas_object_smart_data_get(obj); \
    137     if (!ptr) {                                                      \
    138         CRITICAL("no private data in obj=%p", obj);                  \
    139         return __VA_ARGS__;                                          \
    140     }
    141 
    142 static inline void _ewk_tiled_backing_store_item_request_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it);
    143 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);
    144 static void _ewk_tiled_backing_store_fill_renderers(Ewk_Tiled_Backing_Store_Data *priv);
    145 static inline void _ewk_tiled_backing_store_view_dbg(const Ewk_Tiled_Backing_Store_Data *priv);
    146 static inline void _ewk_tiled_backing_store_changed(Ewk_Tiled_Backing_Store_Data *priv);
    147 
    148 static inline void _ewk_tiled_backing_store_updates_process(Ewk_Tiled_Backing_Store_Data *priv)
    149 {
    150     void *data = NULL;
    151 
    152     /* Do not process updates. Note that we still want to get updates requests
    153      * in the queue in order to not miss any updates after the render is
    154      * resumed.
    155      */
    156     if (priv->render.suspend || !evas_object_visible_get(priv->self))
    157         return;
    158 
    159     if (priv->process.pre_cb)
    160         data = priv->process.pre_cb(priv->process.pre_data, priv->self);
    161 
    162     ewk_tile_matrix_updates_process(priv->model.matrix);
    163 
    164     if (priv->process.post_cb)
    165         priv->process.post_cb(priv->process.post_data, data, priv->self);
    166 }
    167 
    168 static int _ewk_tiled_backing_store_flush(void *data)
    169 {
    170     Ewk_Tiled_Backing_Store_Data *priv = data;
    171     Ewk_Tile_Unused_Cache *tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
    172 
    173     if (tuc) {
    174         DBG("flush unused tile cache.");
    175         ewk_tile_unused_cache_auto_flush(tuc);
    176     } else
    177         ERR("no cache?!");
    178 
    179     return 0;
    180 }
    181 
    182 static Ewk_Tile *_ewk_tiled_backing_store_tile_new(Ewk_Tiled_Backing_Store_Data *priv, unsigned long col, unsigned long row, float zoom)
    183 {
    184     Ewk_Tile *t;
    185     Evas *evas = evas_object_evas_get(priv->self);
    186     if (!evas) {
    187         CRITICAL("evas_object_evas_get failed!");
    188         return NULL;
    189     }
    190 
    191     t = ewk_tile_matrix_tile_new
    192         (priv->model.matrix, evas, col, row, zoom);
    193 
    194     if (!t) {
    195         CRITICAL("ewk_tile_matrix_tile_new failed!");
    196         return NULL;
    197     }
    198 
    199     return t;
    200 }
    201 
    202 static void _ewk_tiled_backing_store_item_move(Ewk_Tiled_Backing_Store_Item *it, Evas_Coord x, Evas_Coord y)
    203 {
    204     it->geometry.x = x;
    205     it->geometry.y = y;
    206 
    207     if (it->tile)
    208         evas_object_move(it->tile->image, x, y);
    209 }
    210 
    211 static void _ewk_tiled_backing_store_item_resize(Ewk_Tiled_Backing_Store_Item *it, Evas_Coord w, Evas_Coord h)
    212 {
    213     it->geometry.w = w;
    214     it->geometry.h = h;
    215 
    216     if (it->tile) {
    217         evas_object_resize(it->tile->image, w, h);
    218         evas_object_image_fill_set(it->tile->image, 0, 0, w, h);
    219     }
    220 }
    221 
    222 static void _ewk_tiled_backing_store_tile_associate(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tile *t, Ewk_Tiled_Backing_Store_Item *it)
    223 {
    224     if (it->tile)
    225         CRITICAL("it->tile=%p, but it should be NULL!", it->tile);
    226     it->tile = t;
    227     evas_object_move(it->tile->image, it->geometry.x, it->geometry.y);
    228     evas_object_resize(it->tile->image, it->geometry.w, it->geometry.h);
    229     evas_object_image_fill_set
    230         (it->tile->image, 0, 0, it->geometry.w, it->geometry.h);
    231     evas_object_image_smooth_scale_set(it->tile->image, it->smooth_scale);
    232 
    233     if (!ewk_tile_visible_get(t))
    234         evas_object_smart_member_add(t->image, priv->self);
    235 
    236     ewk_tile_show(t);
    237 }
    238 
    239 static void _ewk_tiled_backing_store_tile_dissociate(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it, double last_used)
    240 {
    241     Ewk_Tile_Unused_Cache *tuc;
    242     ewk_tile_hide(it->tile);
    243     if (!ewk_tile_visible_get(it->tile))
    244         evas_object_smart_member_del(it->tile->image);
    245     ewk_tile_matrix_tile_put(priv->model.matrix, it->tile, last_used);
    246     tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
    247     ewk_tile_unused_cache_auto_flush(tuc);
    248 
    249     it->tile = NULL;
    250 }
    251 
    252 static void _ewk_tiled_backing_store_tile_dissociate_all(Ewk_Tiled_Backing_Store_Data *priv)
    253 {
    254     Eina_Inlist *it;
    255     Ewk_Tiled_Backing_Store_Item *item;
    256     int i;
    257     double last_used = ecore_loop_time_get();
    258 
    259     for (i = 0; i < priv->view.rows; i++) {
    260         it = priv->view.items[i];
    261         EINA_INLIST_FOREACH(it, item)
    262             if (item->tile)
    263                 _ewk_tiled_backing_store_tile_dissociate(priv, item, last_used);
    264     }
    265 }
    266 
    267 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_Pre_Render_Priority priority)
    268 {
    269     Ewk_Tiled_Backing_Store_Pre_Render_Request *r;
    270 
    271     MALLOC_OR_OOM_RET(r, sizeof(*r), EINA_FALSE);
    272 
    273     if (priority == PRE_RENDER_PRIORITY_HIGH)
    274         priv->render.pre_render_requests = eina_inlist_prepend
    275             (priv->render.pre_render_requests, EINA_INLIST_GET(r));
    276     else
    277         priv->render.pre_render_requests = eina_inlist_append
    278             (priv->render.pre_render_requests, EINA_INLIST_GET(r));
    279 
    280     r->col = col;
    281     r->row = row;
    282     r->zoom = zoom;
    283 
    284     return EINA_TRUE;
    285 }
    286 
    287 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)
    288 {
    289     priv->render.pre_render_requests = eina_inlist_remove
    290         (priv->render.pre_render_requests, EINA_INLIST_GET(r));
    291     free(r);
    292 }
    293 
    294 static inline Ewk_Tiled_Backing_Store_Pre_Render_Request *_ewk_tiled_backing_store_pre_render_request_first(const Ewk_Tiled_Backing_Store_Data *priv)
    295 {
    296     return EINA_INLIST_CONTAINER_GET(
    297         priv->render.pre_render_requests,
    298         Ewk_Tiled_Backing_Store_Pre_Render_Request);
    299 }
    300 
    301 static void _ewk_tiled_backing_store_pre_render_request_flush(Ewk_Tiled_Backing_Store_Data *priv)
    302 {
    303     Eina_Inlist **pl = &priv->render.pre_render_requests;
    304     while (*pl) {
    305         Ewk_Tiled_Backing_Store_Pre_Render_Request *r;
    306         r = _ewk_tiled_backing_store_pre_render_request_first(priv);
    307         *pl = eina_inlist_remove(*pl, *pl);
    308         free(r);
    309     }
    310 }
    311 
    312 static void _ewk_tiled_backing_store_pre_render_request_clear(Ewk_Tiled_Backing_Store_Data *priv)
    313 {
    314     Eina_Inlist **pl = &priv->render.pre_render_requests;
    315     Eina_Inlist *iter = *pl, *tmp;
    316     while (iter) {
    317         Ewk_Tiled_Backing_Store_Pre_Render_Request *r =
    318             EINA_INLIST_CONTAINER_GET(
    319                 iter, Ewk_Tiled_Backing_Store_Pre_Render_Request);
    320         tmp = iter->next;
    321         *pl = eina_inlist_remove(*pl, iter);
    322         iter = tmp;
    323         free(r);
    324     }
    325 }
    326 
    327 /* assumes priv->process.pre_cb was called if required! */
    328 static void _ewk_tiled_backing_store_pre_render_request_process_single(Ewk_Tiled_Backing_Store_Data *priv)
    329 {
    330     Ewk_Tiled_Backing_Store_Pre_Render_Request *req;
    331     Eina_Rectangle area;
    332     Ewk_Tile_Matrix *tm = priv->model.matrix;
    333     Ewk_Tile *t;
    334     Ewk_Tile_Unused_Cache *tuc;
    335     unsigned long col, row;
    336     float zoom;
    337     double last_used = ecore_loop_time_get();
    338 
    339     req = _ewk_tiled_backing_store_pre_render_request_first(priv);
    340     if (!req)
    341         return;
    342 
    343     col = req->col;
    344     row = req->row;
    345     zoom = req->zoom;
    346 
    347     if (ewk_tile_matrix_tile_exact_exists(tm, col, row, zoom)) {
    348         DBG("no pre-render required for tile %lu,%lu @ %f.", col, row, zoom);
    349         goto end;
    350     }
    351 
    352     t = _ewk_tiled_backing_store_tile_new(priv, col, row, zoom);
    353     if (!t)
    354         goto end;
    355 
    356     area.x = 0;
    357     area.y = 0;
    358     area.w = priv->view.tile.w;
    359     area.h = priv->view.tile.h;
    360 
    361     priv->render.cb(priv->render.data, t, &area);
    362     evas_object_image_data_update_add(
    363         t->image,
    364         area.x, area.y, area.w, area.h);
    365     ewk_tile_matrix_tile_updates_clear(tm, t);
    366 
    367     ewk_tile_matrix_tile_put(tm, t, last_used);
    368 
    369 end:
    370     _ewk_tiled_backing_store_pre_render_request_del(priv, req);
    371     tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
    372     ewk_tile_unused_cache_auto_flush(tuc);
    373 }
    374 
    375 static Eina_Bool _ewk_tiled_backing_store_item_process_idler_cb(void *data)
    376 {
    377     Ewk_Tiled_Backing_Store_Data *priv = data;
    378     Ewk_Tiled_Backing_Store_Item *it = NULL;
    379 
    380     while (priv->render.queue) {
    381         it = priv->render.queue->data;
    382         if (it->tile->zoom == priv->view.tile.zoom) {
    383             _ewk_tiled_backing_store_item_request_del(priv, it);
    384             it = NULL;
    385         } else {
    386             unsigned long row, col;
    387             float zoom;
    388             Ewk_Tile *t;
    389             if (it->tile) {
    390                 double last_used = ecore_loop_time_get();
    391                 _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used);
    392             }
    393 
    394             row = it->update.row;
    395             col = it->update.col;
    396             zoom = it->update.zoom;
    397             t = _ewk_tiled_backing_store_tile_new(priv, col, row, zoom);
    398             if (!t) {
    399                 priv->render.idler = NULL;
    400                 return EINA_FALSE;
    401             }
    402 
    403             _ewk_tiled_backing_store_tile_associate(priv, t, it);
    404             it->update.process = NULL;
    405             priv->render.queue = eina_list_remove_list(priv->render.queue,
    406                                                        priv->render.queue);
    407             if (!priv->render.process_entire_queue)
    408                 break;
    409         }
    410     }
    411 
    412     if (priv->process.pre_cb)
    413         data = priv->process.pre_cb(priv->process.pre_data, priv->self);
    414 
    415     ewk_tile_matrix_updates_process(priv->model.matrix);
    416 
    417     if (!it)
    418         _ewk_tiled_backing_store_pre_render_request_process_single(priv);
    419 
    420     if (priv->process.post_cb)
    421         priv->process.post_cb(priv->process.post_data, data, priv->self);
    422 
    423     if (!priv->render.queue && !priv->render.pre_render_requests) {
    424         priv->render.idler = NULL;
    425         return EINA_FALSE;
    426     }
    427 
    428     return EINA_TRUE;
    429 }
    430 
    431 static inline void _ewk_tiled_backing_store_item_process_idler_stop(Ewk_Tiled_Backing_Store_Data *priv)
    432 {
    433     if (!priv->render.idler)
    434         return;
    435 
    436     ecore_idler_del(priv->render.idler);
    437     priv->render.idler = NULL;
    438 }
    439 
    440 static inline void _ewk_tiled_backing_store_item_process_idler_start(Ewk_Tiled_Backing_Store_Data *priv)
    441 {
    442     if (priv->render.idler)
    443         return;
    444     priv->render.idler = ecore_idler_add(
    445         _ewk_tiled_backing_store_item_process_idler_cb, priv);
    446 }
    447 
    448 static inline void _ewk_tiled_backing_store_item_request_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it)
    449 {
    450     priv->render.queue = eina_list_remove_list(priv->render.queue,
    451                                                it->update.process);
    452     it->update.process = NULL;
    453 }
    454 
    455 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)
    456 {
    457     if (it->update.process)
    458         return;
    459 
    460     it->update.col = m_col;
    461     it->update.row = m_row;
    462     it->update.zoom = zoom;
    463 
    464     priv->render.queue = eina_list_append(priv->render.queue, it);
    465     it->update.process = eina_list_last(priv->render.queue);
    466 
    467     if (!priv->render.suspend)
    468         _ewk_tiled_backing_store_item_process_idler_start(priv);
    469 }
    470 
    471 static Eina_Bool _ewk_tiled_backing_store_disable_render(Ewk_Tiled_Backing_Store_Data *priv)
    472 {
    473     if (priv->render.suspend)
    474         return EINA_TRUE;
    475 
    476     priv->render.suspend = EINA_TRUE;
    477     _ewk_tiled_backing_store_item_process_idler_stop(priv);
    478     return EINA_TRUE;
    479 }
    480 
    481 static Eina_Bool _ewk_tiled_backing_store_enable_render(Ewk_Tiled_Backing_Store_Data *priv)
    482 {
    483     if (!priv->render.suspend)
    484         return EINA_TRUE;
    485 
    486     priv->render.suspend = EINA_FALSE;
    487 
    488     _ewk_tiled_backing_store_fill_renderers(priv);
    489     _ewk_tiled_backing_store_item_process_idler_start(priv);
    490 
    491     return EINA_TRUE;
    492 }
    493 
    494 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)
    495 {
    496     long m_col = priv->model.base.col + col;
    497     long m_row = priv->model.base.row + row;
    498     double last_used = ecore_loop_time_get();
    499 
    500     if (m_col < 0 || m_row < 0
    501         || (unsigned long)(m_col) >= priv->model.cur.cols
    502         || (unsigned long)(m_row) >= priv->model.cur.rows) {
    503 
    504         if (it->tile) {
    505             _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used);
    506             if (it->update.process)
    507                 _ewk_tiled_backing_store_item_request_del(priv, it);
    508         }
    509     } else {
    510         Ewk_Tile *t;
    511         const float zoom = priv->view.tile.zoom;
    512 
    513         if (it->update.process) {
    514             if (it->update.row == (unsigned long)(m_row)
    515                 && it->update.col == (unsigned long)(m_col)
    516                 && it->update.zoom == zoom)
    517                 return EINA_TRUE;
    518 
    519             _ewk_tiled_backing_store_item_request_del(priv, it);
    520         }
    521 
    522         if (it->tile) {
    523             Ewk_Tile *old = it->tile;
    524             if (old->row != (unsigned long)(m_row)
    525                 || old->col != (unsigned long)(m_col)
    526                 || old->zoom != zoom) {
    527                 _ewk_tiled_backing_store_tile_dissociate(priv, it,
    528                                                          last_used);
    529                 if (it->update.process)
    530                     _ewk_tiled_backing_store_item_request_del(priv, it);
    531             } else if (old->row == (unsigned long)(m_row)
    532                        && old->col == (unsigned long)(m_col)
    533                        && old->zoom == zoom)
    534                 goto end;
    535         }
    536 
    537         t = ewk_tile_matrix_tile_exact_get
    538             (priv->model.matrix, m_col, m_row, zoom);
    539         if (!t) {
    540             /* NOTE: it never returns NULL if it->tile was set! */
    541             if (it->tile) {
    542                 CRITICAL("it->tile=%p, but it should be NULL!", it->tile);
    543                 _ewk_tiled_backing_store_tile_dissociate(priv, it,
    544                                                          last_used);
    545             }
    546 
    547             /* Do not add new requests to the render queue */
    548             if (!priv->render.suspend) {
    549                 t = _ewk_tiled_backing_store_tile_new(priv, m_col, m_row, zoom);
    550                 if (!t)
    551                     return EINA_FALSE;
    552                 _ewk_tiled_backing_store_tile_associate(priv, t, it);
    553             }
    554         } else if (t != it->tile) {
    555             if (!it->update.process) {
    556                 if (it->tile)
    557                     _ewk_tiled_backing_store_tile_dissociate(priv,
    558                                                              it, last_used);
    559                 _ewk_tiled_backing_store_tile_associate(priv, t, it);
    560             }
    561         }
    562 
    563       end:
    564 
    565         return EINA_TRUE;
    566     }
    567 
    568     return EINA_TRUE;
    569 }
    570 
    571 static Ewk_Tiled_Backing_Store_Item *_ewk_tiled_backing_store_item_add(Ewk_Tiled_Backing_Store_Data *priv, long col, int row)
    572 {
    573     Ewk_Tiled_Backing_Store_Item *it;
    574     Evas_Coord x, y, tw, th;
    575 
    576     DBG("o=%p", priv->self);
    577 
    578     MALLOC_OR_OOM_RET(it, sizeof(*it), NULL);
    579 
    580     tw = priv->view.tile.w;
    581     th = priv->view.tile.h;
    582     x = priv->view.offset.base.x + priv->view.x + tw  *col;
    583     y = priv->view.offset.base.y + priv->view.y + th  *row;
    584 
    585     it->tile = NULL;
    586     it->update.process = NULL;
    587     it->smooth_scale = priv->view.tile.zoom_weak_smooth_scale;
    588     _ewk_tiled_backing_store_item_move(it, x, y);
    589     _ewk_tiled_backing_store_item_resize(it, tw, th);
    590     if (!_ewk_tiled_backing_store_item_fill(priv, it, col, row)) {
    591         free(it);
    592         return NULL;
    593     }
    594 
    595     return it;
    596 }
    597 
    598 static void _ewk_tiled_backing_store_item_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it)
    599 {
    600     if (it->tile) {
    601         double last_used = ecore_loop_time_get();
    602         _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used);
    603     }
    604     if (it->update.process)
    605         _ewk_tiled_backing_store_item_request_del(priv, it);
    606     free(it);
    607 }
    608 
    609 static void _ewk_tiled_backing_store_item_smooth_scale_set(Ewk_Tiled_Backing_Store_Item *it, Eina_Bool smooth_scale)
    610 {
    611     if (it->smooth_scale == smooth_scale)
    612         return;
    613 
    614     if (it->tile)
    615         evas_object_image_smooth_scale_set(it->tile->image, smooth_scale);
    616 }
    617 
    618 static inline void _ewk_tiled_backing_store_changed(Ewk_Tiled_Backing_Store_Data *priv)
    619 {
    620     if (priv->changed.any)
    621         return;
    622     evas_object_smart_changed(priv->self);
    623     priv->changed.any = EINA_TRUE;
    624 }
    625 
    626 static void _ewk_tiled_backing_store_view_cols_end_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist **p_row, unsigned int count)
    627 {
    628     Eina_Inlist *n;
    629     unsigned int i;
    630 
    631     if (!count)
    632         return;
    633 
    634     n = (*p_row)->last;
    635 
    636     for (i = 0; i < count; i++) {
    637         Ewk_Tiled_Backing_Store_Item *it;
    638         it = EINA_INLIST_CONTAINER_GET(n, Ewk_Tiled_Backing_Store_Item);
    639         n = n->prev;
    640         *p_row = eina_inlist_remove(*p_row, EINA_INLIST_GET(it));
    641         _ewk_tiled_backing_store_item_del(priv, it);
    642     }
    643 }
    644 
    645 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)
    646 {
    647     unsigned int i, r = p_row - priv->view.items;
    648 
    649     for (i = 0; i < count; i++, base_col++) {
    650         Ewk_Tiled_Backing_Store_Item *it;
    651 
    652         it = _ewk_tiled_backing_store_item_add(priv, base_col, r);
    653         if (!it) {
    654             CRITICAL("failed to add column %u of %u in row %u.", i, count, r);
    655             _ewk_tiled_backing_store_view_cols_end_del(priv, p_row, i);
    656             return EINA_FALSE;
    657         }
    658 
    659         *p_row = eina_inlist_append(*p_row, EINA_INLIST_GET(it));
    660     }
    661     return EINA_TRUE;
    662 }
    663 
    664 static void _ewk_tiled_backing_store_view_row_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist *row)
    665 {
    666     while (row) {
    667         Ewk_Tiled_Backing_Store_Item *it;
    668         it = EINA_INLIST_CONTAINER_GET(row, Ewk_Tiled_Backing_Store_Item);
    669         row = row->next;
    670         _ewk_tiled_backing_store_item_del(priv, it);
    671     }
    672 }
    673 
    674 static void _ewk_tiled_backing_store_view_rows_range_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist **start, Eina_Inlist **end)
    675 {
    676     for (; start < end; start++) {
    677         _ewk_tiled_backing_store_view_row_del(priv, *start);
    678         *start = NULL;
    679     }
    680 }
    681 
    682 static void _ewk_tiled_backing_store_view_rows_all_del(Ewk_Tiled_Backing_Store_Data *priv)
    683 {
    684     Eina_Inlist **start;
    685     Eina_Inlist **end;
    686 
    687     start = priv->view.items;
    688     end = priv->view.items + priv->view.rows;
    689     _ewk_tiled_backing_store_view_rows_range_del(priv, start, end);
    690 
    691     free(priv->view.items);
    692     priv->view.items = NULL;
    693     priv->view.cols = 0;
    694     priv->view.rows = 0;
    695 }
    696 
    697 static void _ewk_tiled_backing_store_render(void *data, Ewk_Tile *t, const Eina_Rectangle *area)
    698 {
    699     Ewk_Tiled_Backing_Store_Data *priv = data;
    700 
    701     INF("TODO %p (visible? %d) [%lu,%lu] %d,%d + %dx%d",
    702         t, t->visible, t->col, t->row, area->x, area->y, area->w, area->h);
    703 
    704     if (!t->visible)
    705         return;
    706 
    707     if (priv->view.tile.w != t->w || priv->view.tile.h != t->h)
    708         return; // todo: remove me later, don't even flag as dirty!
    709 
    710     EINA_SAFETY_ON_NULL_RETURN(priv->render.cb);
    711     if (!priv->render.cb(priv->render.data, t, area))
    712         return;
    713 
    714     evas_object_image_data_update_add(t->image, area->x, area->y, area->w, area->h);
    715 }
    716 
    717 static inline void _ewk_tiled_backing_store_model_matrix_create(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tile_Unused_Cache *tuc)
    718 {
    719     if (priv->model.matrix) {
    720         _ewk_tiled_backing_store_view_rows_all_del(priv);
    721 
    722         priv->changed.offset = EINA_FALSE;
    723         priv->changed.size = EINA_TRUE;
    724 
    725         ewk_tile_matrix_free(priv->model.matrix);
    726     }
    727 
    728     priv->model.matrix = ewk_tile_matrix_new
    729         (tuc, priv->model.cur.cols, priv->model.cur.rows, priv->cspace,
    730          _ewk_tiled_backing_store_render, priv);
    731 }
    732 
    733 static void _ewk_tiled_backing_store_smart_member_del(Evas_Object *o, Evas_Object *member)
    734 {
    735     PRIV_DATA_GET_OR_RETURN(o, priv);
    736     if (!priv->contents_clipper)
    737         return;
    738     evas_object_clip_unset(member);
    739     if (!evas_object_clipees_get(priv->contents_clipper))
    740         evas_object_hide(priv->contents_clipper);
    741 }
    742 
    743 static void _ewk_tiled_backing_store_smart_member_add(Evas_Object *o, Evas_Object *member)
    744 {
    745     PRIV_DATA_GET_OR_RETURN(o, priv);
    746     if (!priv->contents_clipper)
    747         return;
    748     evas_object_clip_set(member, priv->contents_clipper);
    749     if (evas_object_visible_get(o))
    750         evas_object_show(priv->contents_clipper);
    751 }
    752 
    753 #ifdef DEBUG_MEM_LEAKS
    754 static void _ewk_tiled_backing_store_mem_dbg(Ewk_Tiled_Backing_Store_Data *priv)
    755 {
    756     static int run = 0;
    757 
    758     run++;
    759 
    760     printf("\n--- BEGIN DEBUG TILED BACKING STORE MEMORY [%d] --\n"
    761            "t=%0.2f, obj=%p, priv=%p, view.items=%p, matrix=%p\n",
    762            run, ecore_loop_time_get(),
    763            priv->self, priv, priv->view.items, priv->model.matrix);
    764 
    765     ewk_tile_matrix_dbg(priv->model.matrix);
    766     ewk_tile_accounting_dbg();
    767 
    768     printf("--- END DEBUG TILED BACKING STORE MEMORY [%d] --\n\n", run);
    769 }
    770 
    771 static Eina_Bool _ewk_tiled_backing_store_sig_usr(void *data, int type, void *event)
    772 {
    773     Ecore_Event_Signal_User *sig = (Ecore_Event_Signal_User*)event;
    774     Ewk_Tiled_Backing_Store_Data *priv = (Ewk_Tiled_Backing_Store_Data*)data;
    775 
    776     if (sig->number == 2) {
    777         Ewk_Tile_Unused_Cache *tuc;
    778         tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
    779         ewk_tile_unused_cache_auto_flush(tuc);
    780     }
    781 
    782     _ewk_tiled_backing_store_view_dbg(priv);
    783     _ewk_tiled_backing_store_mem_dbg(priv);
    784     return EINA_TRUE;
    785 }
    786 #endif
    787 
    788 static void _ewk_tiled_backing_store_smart_add(Evas_Object *o)
    789 {
    790     Ewk_Tiled_Backing_Store_Data *priv;
    791 
    792     DBG("o=%p", o);
    793 
    794     CALLOC_OR_OOM_RET(priv, sizeof(*priv));
    795 
    796     priv->self = o;
    797     priv->view.tile.zoom = 1.0;
    798     priv->view.tile.w = TILE_W;
    799     priv->view.tile.h = TILE_H;
    800     priv->view.offset.cur.x = 0;
    801     priv->view.offset.cur.y = 0;
    802     priv->view.offset.old.x = 0;
    803     priv->view.offset.old.y = 0;
    804     priv->view.offset.base.x = 0;
    805     priv->view.offset.base.y = 0;
    806 
    807     priv->model.base.col = 0;
    808     priv->model.base.row = 0;
    809     priv->model.cur.cols = 1;
    810     priv->model.cur.rows = 1;
    811     priv->model.old.cols = 0;
    812     priv->model.old.rows = 0;
    813     priv->model.width = 0;
    814     priv->model.height = 0;
    815     priv->render.process_entire_queue = EINA_TRUE;
    816     priv->render.suspend = EINA_FALSE;
    817     priv->cspace = EVAS_COLORSPACE_ARGB8888; // TODO: detect it.
    818 
    819     evas_object_smart_data_set(o, priv);
    820     _parent_sc.add(o);
    821 
    822     priv->contents_clipper = evas_object_rectangle_add(
    823         evas_object_evas_get(o));
    824     evas_object_move(priv->contents_clipper, 0, 0);
    825     evas_object_resize(priv->contents_clipper,
    826                        priv->model.width, priv->model.height);
    827     evas_object_color_set(priv->contents_clipper, 255, 255, 255, 255);
    828     evas_object_show(priv->contents_clipper);
    829     evas_object_smart_member_add(priv->contents_clipper, o);
    830 
    831     _ewk_tiled_backing_store_model_matrix_create(priv, NULL);
    832     evas_object_move(priv->base.clipper, 0, 0);
    833     evas_object_resize(priv->base.clipper, 0, 0);
    834     evas_object_clip_set(priv->contents_clipper, priv->base.clipper);
    835 
    836 #ifdef DEBUG_MEM_LEAKS
    837     priv->sig_usr = ecore_event_handler_add
    838         (ECORE_EVENT_SIGNAL_USER, _ewk_tiled_backing_store_sig_usr, priv);
    839 #endif
    840 }
    841 
    842 static void _ewk_tiled_backing_store_smart_del(Evas_Object *o)
    843 {
    844     PRIV_DATA_GET_OR_RETURN(o, priv);
    845     DBG("o=%p", o);
    846     Ewk_Tile_Unused_Cache *tuc;
    847 
    848     tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
    849     ewk_tile_unused_cache_unlock_area(tuc);
    850 
    851     _ewk_tiled_backing_store_flush(priv);
    852 
    853     _ewk_tiled_backing_store_pre_render_request_flush(priv);
    854     _ewk_tiled_backing_store_item_process_idler_stop(priv);
    855     _ewk_tiled_backing_store_view_rows_all_del(priv);
    856 
    857 #ifdef DEBUG_MEM_LEAKS
    858     _ewk_tiled_backing_store_mem_dbg(priv);
    859     if (priv->sig_usr)
    860         priv->sig_usr = ecore_event_handler_del(priv->sig_usr);
    861 #endif
    862 
    863     ewk_tile_matrix_free(priv->model.matrix);
    864     evas_object_smart_member_del(priv->contents_clipper);
    865     evas_object_del(priv->contents_clipper);
    866 
    867     _parent_sc.del(o);
    868 
    869 #ifdef DEBUG_MEM_LEAKS
    870     printf("\nIMPORTANT: TILED BACKING STORE DELETED (may be real leaks)\n");
    871     ewk_tile_accounting_dbg();
    872 #endif
    873 }
    874 
    875 static void _ewk_tiled_backing_store_smart_move(Evas_Object *o, Evas_Coord x, Evas_Coord y)
    876 {
    877     DBG("o=%p, new pos: %dx%d", o, x, y);
    878 
    879     PRIV_DATA_GET_OR_RETURN(o, priv);
    880 
    881     if (priv->changed.pos)
    882         return;
    883 
    884     if (priv->view.x == x && priv->view.y == y)
    885         return;
    886 
    887     priv->changed.pos = EINA_TRUE;
    888     _ewk_tiled_backing_store_changed(priv);
    889 }
    890 
    891 static void _ewk_tiled_backing_store_smart_resize(Evas_Object *o, Evas_Coord w, Evas_Coord h)
    892 {
    893     DBG("o=%p, new size: %dx%d", o, w, h);
    894 
    895     PRIV_DATA_GET_OR_RETURN(o, priv);
    896 
    897     if (priv->changed.size)
    898         return;
    899 
    900     if (priv->view.w == w && priv->view.h == h)
    901         return;
    902 
    903     priv->changed.size = EINA_TRUE;
    904     _ewk_tiled_backing_store_changed(priv);
    905 }
    906 
    907 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)
    908 {
    909     long cols, rows, old_rows, old_cols;
    910     INF("o=%p, new size: %dx%d", priv->self, w, h);
    911 
    912     cols = 1 + (int)ceil((float)w / (float)tw);
    913     rows = 1 + (int)ceil((float)h / (float)th);
    914 
    915     INF("o=%p new grid size cols: %ld, rows: %ld, was %ld, %ld",
    916         priv->self, cols, rows, priv->view.cols, priv->view.rows);
    917 
    918     if (priv->view.cols == cols && priv->view.rows == rows)
    919         return;
    920 
    921     old_cols = priv->view.cols;
    922     old_rows = priv->view.rows;
    923 
    924     if (rows < old_rows) {
    925         Eina_Inlist **start, **end;
    926         start = priv->view.items + rows;
    927         end = priv->view.items + old_rows;
    928         _ewk_tiled_backing_store_view_rows_range_del(priv, start, end);
    929     }
    930     REALLOC_OR_OOM_RET(priv->view.items, sizeof(Eina_Inlist*) * (int)rows);
    931     priv->view.rows = rows;
    932     priv->view.cols = cols;
    933     if (rows > old_rows) {
    934         Eina_Inlist **start, **end;
    935         start = priv->view.items + old_rows;
    936         end = priv->view.items + rows;
    937         for (; start < end; start++) {
    938             Eina_Bool r;
    939             *start = NULL;
    940             r = _ewk_tiled_backing_store_view_cols_end_add
    941                 (priv, start, 0, cols);
    942             if (!r) {
    943                 CRITICAL("failed to allocate %ld columns", cols);
    944                 _ewk_tiled_backing_store_view_rows_range_del
    945                     (priv, priv->view.items + old_rows, start);
    946                 priv->view.rows = old_rows;
    947                 return;
    948             }
    949         }
    950     }
    951 
    952     if (cols != old_cols) {
    953         Eina_Inlist **start, **end;
    954         int todo = cols - old_cols;
    955         start = priv->view.items;
    956         end = start + MIN(old_rows, rows);
    957         if (todo > 0) {
    958             for (; start < end; start++) {
    959                 Eina_Bool r;
    960                 r = _ewk_tiled_backing_store_view_cols_end_add
    961                     (priv, start, old_cols, todo);
    962                 if (!r) {
    963                     CRITICAL("failed to allocate %d columns!", todo);
    964 
    965                     for (start--; start >= priv->view.items; start--)
    966                         _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo);
    967                     if (rows > old_rows) {
    968                         start = priv->view.items + old_rows;
    969                         end = priv->view.items + rows;
    970                         for (; start < end; start++)
    971                             _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo);
    972                     }
    973                     return;
    974                 }
    975             }
    976         } else if (todo < 0) {
    977             todo = -todo;
    978             for (; start < end; start++)
    979                 _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo);
    980         }
    981     }
    982 
    983     _ewk_tiled_backing_store_fill_renderers(priv);
    984 }
    985 
    986 static void _ewk_tiled_backing_store_smart_calculate_size(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord w, Evas_Coord h)
    987 {
    988     evas_object_resize(priv->base.clipper, w, h);
    989 
    990     priv->view.w = w;
    991     priv->view.h = h;
    992 
    993     _ewk_tiled_backing_store_recalc_renderers(
    994         priv, w, h, priv->view.tile.w, priv->view.tile.h);
    995 }
    996 
    997 // TODO: remove me later.
    998 static inline void _ewk_tiled_backing_store_view_dbg(const Ewk_Tiled_Backing_Store_Data *priv)
    999 {
   1000     Eina_Inlist **start, **end;
   1001     printf("tiles=%2ld,%2ld  model=%2ld,%2ld [%dx%d] base=%+3ld,%+4ld offset=%+4d,%+4d old=%+4d,%+4d base=%+3d,%+3d\n",
   1002            priv->view.cols, priv->view.rows,
   1003            priv->model.cur.cols, priv->model.cur.rows,
   1004            priv->model.width, priv->model.height,
   1005            priv->model.base.col, priv->model.base.row,
   1006            priv->view.offset.cur.x, priv->view.offset.cur.y,
   1007            priv->view.offset.old.x, priv->view.offset.old.y,
   1008            priv->view.offset.base.x, priv->view.offset.base.y);
   1009 
   1010     start = priv->view.items;
   1011     end = priv->view.items + priv->view.rows;
   1012     for (; start < end; start++) {
   1013         const Ewk_Tiled_Backing_Store_Item *it;
   1014 
   1015         EINA_INLIST_FOREACH(*start, it) {
   1016             printf(" %+4d,%+4d ", it->geometry.x, it->geometry.y);
   1017 
   1018             if (!it->tile)
   1019                 printf("            ;");
   1020             else
   1021                 printf("%8p %lu,%lu;", it->tile, it->tile->col, it->tile->row);
   1022         }
   1023         printf("\n");
   1024     }
   1025     printf("---\n");
   1026 }
   1027 
   1028 /**
   1029  * @internal
   1030  * Move top row down as last.
   1031  *
   1032  * The final result is visually the same, but logically the top that
   1033  * went out of screen is now at bottom and filled with new model items.
   1034  *
   1035  * This is worth just when @a count is smaller than @c
   1036  * priv->view.rows, after that one is refilling the whole matrix so it
   1037  * is better to trigger full refill.
   1038  *
   1039  * @param count the number of times to repeat the process.
   1040  */
   1041 static void _ewk_tiled_backing_store_view_wrap_up(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
   1042 {
   1043     unsigned int last_row = priv->view.rows - 1;
   1044     Evas_Coord tw = priv->view.tile.w;
   1045     Evas_Coord th = priv->view.tile.h;
   1046     Evas_Coord off_y = priv->view.offset.base.y + count * th;
   1047     Evas_Coord oy = y + (last_row - count + 1) * th + off_y;
   1048     Eina_Inlist **itr_start, **itr_end;
   1049 
   1050     itr_start = priv->view.items;
   1051     itr_end = itr_start + last_row;
   1052 
   1053     for (; count > 0; count--) {
   1054         Eina_Inlist **itr;
   1055         Eina_Inlist *tmp = *itr_start;
   1056         Ewk_Tiled_Backing_Store_Item *it;
   1057         Evas_Coord ox = x + priv->view.offset.base.x;
   1058         int c = 0;
   1059 
   1060         for (itr = itr_start; itr < itr_end; itr++)
   1061             *itr = *(itr + 1);
   1062         *itr = tmp;
   1063 
   1064         priv->model.base.row++;
   1065         EINA_INLIST_FOREACH(tmp, it) {
   1066             _ewk_tiled_backing_store_item_move(it, ox, oy);
   1067             ox += tw;
   1068             _ewk_tiled_backing_store_item_fill(priv, it, c, last_row);
   1069             c++;
   1070         }
   1071         oy += th;
   1072     }
   1073     priv->view.offset.base.y = off_y;
   1074 }
   1075 
   1076 /**
   1077  * @internal
   1078  * Move bottom row up as first.
   1079  *
   1080  * The final result is visually the same, but logically the bottom that
   1081  * went out of screen is now at top and filled with new model items.
   1082  *
   1083  * This is worth just when @a count is smaller than @c
   1084  * priv->view.rows, after that one is refilling the whole matrix so it
   1085  * is better to trigger full refill.
   1086  *
   1087  * @param count the number of times to repeat the process.
   1088  */
   1089 static void _ewk_tiled_backing_store_view_wrap_down(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
   1090 {
   1091     Evas_Coord tw = priv->view.tile.w;
   1092     Evas_Coord th = priv->view.tile.h;
   1093     Evas_Coord off_y = priv->view.offset.base.y - count * th;
   1094     Evas_Coord oy = y + off_y + (count - 1) * th;
   1095     Eina_Inlist **itr_start, **itr_end;
   1096 
   1097     itr_start = priv->view.items + priv->view.rows - 1;
   1098     itr_end = priv->view.items;
   1099 
   1100     for (; count > 0; count--) {
   1101         Eina_Inlist **itr;
   1102         Eina_Inlist *tmp = *itr_start;
   1103         Ewk_Tiled_Backing_Store_Item *it;
   1104         Evas_Coord ox = x + priv->view.offset.base.x;
   1105         int c = 0;
   1106 
   1107         for (itr = itr_start; itr > itr_end; itr--)
   1108             *itr = *(itr - 1);
   1109         *itr = tmp;
   1110 
   1111         priv->model.base.row--;
   1112         EINA_INLIST_FOREACH(tmp, it) {
   1113             _ewk_tiled_backing_store_item_move(it, ox, oy);
   1114             ox += tw;
   1115             _ewk_tiled_backing_store_item_fill(priv, it, c, 0);
   1116             c++;
   1117         }
   1118         oy -= th;
   1119     }
   1120     priv->view.offset.base.y = off_y;
   1121 }
   1122 
   1123 /**
   1124  * @internal
   1125  * Move left-most (first) column right as last (right-most).
   1126  *
   1127  * The final result is visually the same, but logically the first col that
   1128  * went out of screen is now at last and filled with new model items.
   1129  *
   1130  * This is worth just when @a count is smaller than @c
   1131  * priv->view.cols, after that one is refilling the whole matrix so it
   1132  * is better to trigger full refill.
   1133  *
   1134  * @param count the number of times to repeat the process.
   1135  */
   1136 static void _ewk_tiled_backing_store_view_wrap_left(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
   1137 {
   1138     unsigned int r, last_col = priv->view.cols - 1;
   1139     Evas_Coord tw = priv->view.tile.w;
   1140     Evas_Coord th = priv->view.tile.h;
   1141     Evas_Coord off_x = priv->view.offset.base.x + count * tw;
   1142     Evas_Coord oy = y + priv->view.offset.base.y;
   1143     Eina_Inlist **itr;
   1144     Eina_Inlist **itr_end;
   1145 
   1146     itr = priv->view.items;
   1147     itr_end = itr + priv->view.rows;
   1148     r = 0;
   1149 
   1150     priv->model.base.col += count;
   1151 
   1152     for (; itr < itr_end; itr++, r++) {
   1153         Evas_Coord ox = x + (last_col - count + 1) * tw + off_x;
   1154         unsigned int i, c = last_col - count + 1;
   1155 
   1156         for (i = 0; i < count; i++, c++, ox += tw) {
   1157             Ewk_Tiled_Backing_Store_Item *it;
   1158             it = EINA_INLIST_CONTAINER_GET(*itr, Ewk_Tiled_Backing_Store_Item);
   1159             *itr = eina_inlist_demote(*itr, *itr);
   1160 
   1161             _ewk_tiled_backing_store_item_move(it, ox, oy);
   1162             _ewk_tiled_backing_store_item_fill(priv, it, c, r);
   1163         }
   1164         oy += th;
   1165     }
   1166 
   1167     priv->view.offset.base.x = off_x;
   1168 }
   1169 
   1170 /**
   1171  * @internal
   1172  * Move right-most (last) column left as first (left-most).
   1173  *
   1174  * The final result is visually the same, but logically the last col that
   1175  * went out of screen is now at first and filled with new model items.
   1176  *
   1177  * This is worth just when @a count is smaller than @c
   1178  * priv->view.cols, after that one is refilling the whole matrix so it
   1179  * is better to trigger full refill.
   1180  *
   1181  * @param count the number of times to repeat the process.
   1182  */
   1183 static void _ewk_tiled_backing_store_view_wrap_right(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
   1184 {
   1185     unsigned int r;
   1186     Evas_Coord tw = priv->view.tile.w;
   1187     Evas_Coord th = priv->view.tile.h;
   1188     Evas_Coord off_x = priv->view.offset.base.x - count * tw;
   1189     Evas_Coord oy = y + priv->view.offset.base.y;
   1190     Eina_Inlist **itr, **itr_end;
   1191 
   1192     itr = priv->view.items;
   1193     itr_end = itr + priv->view.rows;
   1194     r = 0;
   1195 
   1196     priv->model.base.col -= count;
   1197 
   1198     for (; itr < itr_end; itr++, r++) {
   1199         Evas_Coord ox = x + (count - 1) * tw + off_x;
   1200         unsigned int i, c = count - 1;
   1201 
   1202         for (i = 0; i < count; i++, c--, ox -= tw) {
   1203             Ewk_Tiled_Backing_Store_Item *it;
   1204             it = EINA_INLIST_CONTAINER_GET((*itr)->last, Ewk_Tiled_Backing_Store_Item);
   1205             *itr = eina_inlist_promote(*itr, (*itr)->last);
   1206 
   1207             _ewk_tiled_backing_store_item_move(it, ox, oy);
   1208             _ewk_tiled_backing_store_item_fill(priv, it, c, r);
   1209         }
   1210         oy += th;
   1211     }
   1212 
   1213     priv->view.offset.base.x = off_x;
   1214 }
   1215 
   1216 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)
   1217 {
   1218     Eina_Inlist **itr, **itr_end;
   1219     Evas_Coord base_ox, oy, tw, th;
   1220     unsigned int r;
   1221 
   1222     evas_object_move(priv->base.clipper, x, y);
   1223 
   1224     tw = priv->view.tile.w;
   1225     th = priv->view.tile.h;
   1226 
   1227     base_ox = x + priv->view.offset.base.x;
   1228     oy = y + priv->view.offset.base.y;
   1229 
   1230     itr = priv->view.items;
   1231     itr_end = itr + priv->view.rows;
   1232     r = 0;
   1233 
   1234     priv->model.base.col -= step_x;
   1235     priv->model.base.row -= step_y;
   1236 
   1237     for (; itr < itr_end; itr++, r++) {
   1238         Ewk_Tiled_Backing_Store_Item *it;
   1239         Evas_Coord ox = base_ox;
   1240         unsigned int c = 0;
   1241         EINA_INLIST_FOREACH(*itr, it) {
   1242             _ewk_tiled_backing_store_item_fill(priv, it, c, r);
   1243             _ewk_tiled_backing_store_item_move(it, ox, oy);
   1244             c++;
   1245             ox += tw;
   1246         }
   1247         oy += th;
   1248     }
   1249 }
   1250 
   1251 static void _ewk_tiled_backing_store_view_pos_apply(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
   1252 {
   1253     Eina_Inlist **itr, **itr_end;
   1254     Evas_Coord base_ox, oy, tw, th;
   1255 
   1256     evas_object_move(priv->base.clipper, x, y);
   1257 
   1258     tw = priv->view.tile.w;
   1259     th = priv->view.tile.h;
   1260 
   1261     base_ox = x + priv->view.offset.base.x;
   1262     oy = y + priv->view.offset.base.y;
   1263 
   1264     itr = priv->view.items;
   1265     itr_end = itr + priv->view.rows;
   1266     for (; itr < itr_end; itr++) {
   1267         Ewk_Tiled_Backing_Store_Item *it;
   1268         Evas_Coord ox = base_ox;
   1269         EINA_INLIST_FOREACH(*itr, it) {
   1270             _ewk_tiled_backing_store_item_move(it, ox, oy);
   1271             ox += tw;
   1272         }
   1273         oy += th;
   1274     }
   1275 }
   1276 
   1277 static void _ewk_tiled_backing_store_smart_calculate_offset_force(Ewk_Tiled_Backing_Store_Data *priv)
   1278 {
   1279     Evas_Coord dx = priv->view.offset.cur.x - priv->view.offset.old.x;
   1280     Evas_Coord dy = priv->view.offset.cur.y - priv->view.offset.old.y;
   1281     Evas_Coord tw, th;
   1282     int step_y, step_x;
   1283 
   1284     INF("o=%p, offset: %+4d, %+4d (%+4d, %+4d)",
   1285         priv->self, dx, dy, priv->view.offset.cur.x, priv->view.offset.cur.y);
   1286 
   1287     tw = priv->view.tile.w;
   1288     th = priv->view.tile.h;
   1289 
   1290     long new_col = -priv->view.offset.cur.x / tw;
   1291     step_x = priv->model.base.col - new_col;
   1292     long new_row = -priv->view.offset.cur.y / th;
   1293     step_y = priv->model.base.row - new_row;
   1294 
   1295     priv->view.offset.old.x = priv->view.offset.cur.x;
   1296     priv->view.offset.old.y = priv->view.offset.cur.y;
   1297     evas_object_move(
   1298         priv->contents_clipper,
   1299         priv->view.offset.cur.x + priv->view.x,
   1300         priv->view.offset.cur.y + priv->view.y);
   1301 
   1302     priv->view.offset.base.x += dx - step_x * tw;
   1303     priv->view.offset.base.y += dy - step_y * th;
   1304 
   1305     _ewk_tiled_backing_store_view_refill
   1306         (priv, priv->view.x, priv->view.y, step_x, step_y);
   1307 }
   1308 
   1309 static void _ewk_tiled_backing_store_smart_calculate_offset(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
   1310 {
   1311     Evas_Coord dx = priv->view.offset.cur.x - priv->view.offset.old.x;
   1312     Evas_Coord dy = priv->view.offset.cur.y - priv->view.offset.old.y;
   1313     Evas_Coord tw, th;
   1314     int step_y, step_x;
   1315 
   1316     INF("o=%p, offset: %+4d, %+4d (%+4d, %+4d)",
   1317         priv->self, dx, dy, priv->view.offset.cur.x, priv->view.offset.cur.y);
   1318 
   1319     if (!dx && !dy)
   1320         return;
   1321 
   1322     tw = priv->view.tile.w;
   1323     th = priv->view.tile.h;
   1324 
   1325     long new_col = -priv->view.offset.cur.x / tw;
   1326     step_x = priv->model.base.col - new_col;
   1327     long new_row = -priv->view.offset.cur.y / th;
   1328     step_y = priv->model.base.row - new_row;
   1329 
   1330     priv->view.offset.old.x = priv->view.offset.cur.x;
   1331     priv->view.offset.old.y = priv->view.offset.cur.y;
   1332     evas_object_move(
   1333         priv->contents_clipper,
   1334         priv->view.offset.cur.x + priv->view.x,
   1335         priv->view.offset.cur.y + priv->view.y);
   1336 
   1337     if ((step_x < 0 && step_x <= -priv->view.cols)
   1338         || (step_x > 0 && step_x >= priv->view.cols)
   1339         || (step_y < 0 && step_y <= -priv->view.rows)
   1340         || (step_y > 0 && step_y >= priv->view.rows)) {
   1341 
   1342         priv->view.offset.base.x += dx - step_x * tw;
   1343         priv->view.offset.base.y += dy - step_y * th;
   1344 
   1345         _ewk_tiled_backing_store_view_refill
   1346             (priv, priv->view.x, priv->view.y, step_x, step_y);
   1347         return;
   1348     }
   1349 
   1350     priv->view.offset.base.x += dx;
   1351     priv->view.offset.base.y += dy;
   1352 
   1353     if (step_y < 0)
   1354         _ewk_tiled_backing_store_view_wrap_up(priv, x, y, -step_y);
   1355     else if (step_y > 0)
   1356         _ewk_tiled_backing_store_view_wrap_down(priv, x, y, step_y);
   1357 
   1358     if (step_x < 0)
   1359         _ewk_tiled_backing_store_view_wrap_left(priv, x, y, -step_x);
   1360     else if (step_x > 0)
   1361         _ewk_tiled_backing_store_view_wrap_right(priv, x, y, step_x);
   1362 
   1363     _ewk_tiled_backing_store_view_pos_apply(priv, x, y);
   1364 }
   1365 
   1366 static void _ewk_tiled_backing_store_smart_calculate_pos(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
   1367 {
   1368     _ewk_tiled_backing_store_view_pos_apply(priv, x, y);
   1369     priv->view.x = x;
   1370     priv->view.y = y;
   1371     evas_object_move(
   1372         priv->contents_clipper,
   1373         priv->view.offset.cur.x + priv->view.x,
   1374         priv->view.offset.cur.y + priv->view.y);
   1375 }
   1376 
   1377 static void _ewk_tiled_backing_store_fill_renderers(Ewk_Tiled_Backing_Store_Data *priv)
   1378 {
   1379     Eina_Inlist *it;
   1380     Ewk_Tiled_Backing_Store_Item *item;
   1381     int i, j;
   1382 
   1383     for (i = 0; i < priv->view.rows; i++) {
   1384         it = priv->view.items[i];
   1385         j = 0;
   1386         EINA_INLIST_FOREACH(it, item)
   1387             _ewk_tiled_backing_store_item_fill(priv, item, j++, i);
   1388     }
   1389 }
   1390 
   1391 static void _ewk_tiled_backing_store_smart_calculate(Evas_Object *o)
   1392 {
   1393     Evas_Coord x, y, w, h;
   1394 
   1395     evas_object_geometry_get(o, &x, &y, &w, &h);
   1396     DBG("o=%p at %d,%d + %dx%d", o, x, y, w, h);
   1397 
   1398     PRIV_DATA_GET_OR_RETURN(o, priv);
   1399 
   1400     priv->changed.any = EINA_FALSE;
   1401 
   1402     ewk_tile_matrix_freeze(priv->model.matrix);
   1403 
   1404     if (!priv->render.suspend && priv->changed.model) {
   1405         unsigned long cols, rows;
   1406 
   1407         cols = priv->model.width / priv->view.tile.w + 1;
   1408         rows = priv->model.height / priv->view.tile.h + 1;
   1409 
   1410         priv->model.old.cols = priv->model.cur.cols;
   1411         priv->model.old.rows = priv->model.cur.rows;
   1412         priv->model.cur.cols = cols;
   1413         priv->model.cur.rows = rows;
   1414         if (priv->model.old.cols > cols)
   1415             cols = priv->model.old.cols;
   1416         if (priv->model.old.rows > rows)
   1417             rows = priv->model.old.rows;
   1418         ewk_tile_matrix_resize(priv->model.matrix, cols, rows);
   1419     }
   1420 
   1421     if (priv->changed.pos && (priv->view.x != x || priv->view.y != y)) {
   1422         _ewk_tiled_backing_store_smart_calculate_pos(priv, x, y);
   1423         priv->changed.pos = EINA_FALSE;
   1424     } else if (priv->changed.offset) {
   1425         _ewk_tiled_backing_store_smart_calculate_offset(priv, x, y);
   1426         priv->changed.offset = EINA_FALSE;
   1427     }
   1428 
   1429     if (priv->changed.size) {
   1430         _ewk_tiled_backing_store_smart_calculate_size(priv, w, h);
   1431         priv->changed.size = EINA_FALSE;
   1432     }
   1433 
   1434     if (!priv->render.suspend && priv->changed.model) {
   1435         Eina_Rectangle rect;
   1436         rect.x = 0;
   1437         rect.y = 0;
   1438         rect.w = priv->model.width;
   1439         rect.h = priv->model.height;
   1440         _ewk_tiled_backing_store_fill_renderers(priv);
   1441         ewk_tile_matrix_resize(priv->model.matrix,
   1442                            priv->model.cur.cols,
   1443                            priv->model.cur.rows);
   1444         priv->changed.model = EINA_FALSE;
   1445         evas_object_resize(priv->contents_clipper,
   1446                            priv->model.width, priv->model.height);
   1447         _ewk_tiled_backing_store_smart_calculate_offset_force(priv);
   1448 
   1449         /* Make sure we do not miss any important repaint by
   1450          * repainting the whole viewport */
   1451         const Eina_Rectangle r =
   1452             { 0, 0, priv->model.width, priv->model.height };
   1453         ewk_tile_matrix_update(priv->model.matrix, &r,
   1454                                priv->view.tile.zoom);
   1455     }
   1456 
   1457     ewk_tile_matrix_thaw(priv->model.matrix);
   1458 
   1459     _ewk_tiled_backing_store_updates_process(priv);
   1460 
   1461     if (priv->view.offset.base.x > 0
   1462         || priv->view.offset.base.x <= - priv->view.tile.w
   1463         || priv->view.offset.base.y > 0
   1464         || priv->view.offset.base.y <= - priv->view.tile.h)
   1465         ERR("incorrect base offset %+4d,%+4d, tile=%dx%d, cur=%+4d,%+4d\n",
   1466             priv->view.offset.base.x, priv->view.offset.base.y,
   1467             priv->view.tile.w, priv->view.tile.h,
   1468             priv->view.offset.cur.x, priv->view.offset.cur.y);
   1469 
   1470 }
   1471 
   1472 Evas_Object *ewk_tiled_backing_store_add(Evas *e)
   1473 {
   1474     static Evas_Smart *smart = NULL;
   1475 
   1476     if (_ewk_tiled_log_dom < 0)
   1477         _ewk_tiled_log_dom = eina_log_domain_register("Ewk_Tiled_Backing_Store", NULL);
   1478 
   1479     if (!smart) {
   1480         static Evas_Smart_Class sc =
   1481             EVAS_SMART_CLASS_INIT_NAME_VERSION("Ewk_Tiled_Backing_Store");
   1482 
   1483         evas_object_smart_clipped_smart_set(&sc);
   1484         _parent_sc = sc;
   1485 
   1486         sc.add = _ewk_tiled_backing_store_smart_add;
   1487         sc.del = _ewk_tiled_backing_store_smart_del;
   1488         sc.resize = _ewk_tiled_backing_store_smart_resize;
   1489         sc.move = _ewk_tiled_backing_store_smart_move;
   1490         sc.calculate = _ewk_tiled_backing_store_smart_calculate;
   1491         sc.member_add = _ewk_tiled_backing_store_smart_member_add;
   1492         sc.member_del = _ewk_tiled_backing_store_smart_member_del;
   1493 
   1494         smart = evas_smart_class_new(&sc);
   1495     }
   1496 
   1497     return evas_object_smart_add(e, smart);
   1498 }
   1499 
   1500 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)
   1501 {
   1502     EINA_SAFETY_ON_NULL_RETURN(cb);
   1503     PRIV_DATA_GET_OR_RETURN(o, priv);
   1504     priv->render.cb = cb;
   1505     priv->render.data = (void*)data;
   1506 }
   1507 
   1508 Ewk_Tile_Unused_Cache *ewk_tiled_backing_store_tile_unused_cache_get(const Evas_Object *o)
   1509 {
   1510     PRIV_DATA_GET_OR_RETURN(o, priv, NULL);
   1511     return ewk_tile_matrix_unused_cache_get(priv->model.matrix);
   1512 }
   1513 
   1514 void ewk_tiled_backing_store_tile_unused_cache_set(Evas_Object *o, Ewk_Tile_Unused_Cache *tuc)
   1515 {
   1516     PRIV_DATA_GET_OR_RETURN(o, priv);
   1517 
   1518     if (ewk_tile_matrix_unused_cache_get(priv->model.matrix) == tuc)
   1519         return;
   1520 
   1521     _ewk_tiled_backing_store_model_matrix_create(priv, tuc);
   1522 }
   1523 
   1524 static Eina_Bool _ewk_tiled_backing_store_scroll_full_offset_set_internal(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
   1525 {
   1526     /* TODO: check offset go out of bounds, clamp */
   1527     if (priv->render.disabled)
   1528         return EINA_FALSE;
   1529 
   1530     priv->view.offset.cur.x = x;
   1531     priv->view.offset.cur.y = y;
   1532 
   1533     priv->changed.offset = EINA_TRUE;
   1534     _ewk_tiled_backing_store_changed(priv);
   1535 
   1536     return EINA_TRUE;
   1537 }
   1538 
   1539 Eina_Bool ewk_tiled_backing_store_scroll_full_offset_set(Evas_Object *o, Evas_Coord x, Evas_Coord y)
   1540 {
   1541     DBG("o=%p, x=%d, y=%d", o, x, y);
   1542 
   1543     PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
   1544     if (x == priv->view.offset.cur.x && y == priv->view.offset.cur.y)
   1545         return EINA_TRUE;
   1546 
   1547     return _ewk_tiled_backing_store_scroll_full_offset_set_internal(priv, x, y);
   1548 }
   1549 
   1550 Eina_Bool ewk_tiled_backing_store_scroll_full_offset_add(Evas_Object *o, Evas_Coord dx, Evas_Coord dy)
   1551 {
   1552     DBG("o=%p, dx=%d, dy=%d", o, dx, dy);
   1553 
   1554     PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
   1555     if (!dx && !dy)
   1556         return EINA_TRUE;
   1557 
   1558     return _ewk_tiled_backing_store_scroll_full_offset_set_internal
   1559         (priv, priv->view.offset.cur.x + dx, priv->view.offset.cur.y + dy);
   1560 }
   1561 
   1562 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)
   1563 {
   1564     *offx = priv->view.offset.cur.x;
   1565     *offy = priv->view.offset.cur.y;
   1566 
   1567     if (fabsf(priv->view.tile.zoom - *zoom) < ZOOM_STEP_MIN) {
   1568         DBG("ignored as zoom difference is < %f: %f",
   1569             (double)ZOOM_STEP_MIN, fabsf(priv->view.tile.zoom - *zoom));
   1570         return EINA_TRUE;
   1571     }
   1572 
   1573     _ewk_tiled_backing_store_pre_render_request_flush(priv);
   1574     Evas_Coord tw, th;
   1575     tw = TILE_SIZE_AT_ZOOM(TILE_W, *zoom);
   1576     tw = (tw >> 1) << 1;
   1577     *zoom = TILE_W_ZOOM_AT_SIZE(tw);
   1578     /* WARNING: assume reverse zoom is the same for both axis */
   1579     th = TILE_SIZE_AT_ZOOM(TILE_H, *zoom);
   1580 
   1581     float scale = *zoom / priv->view.tile.zoom;
   1582 
   1583     priv->view.tile.zoom = *zoom;
   1584     // todo: check cx [0, w]...
   1585     priv->view.offset.zoom_center.x = cx;
   1586     priv->view.offset.zoom_center.y = cy;
   1587 
   1588     priv->view.tile.w = tw;
   1589     priv->view.tile.h = th;
   1590 
   1591     if (!priv->view.w || !priv->view.h) {
   1592         priv->view.offset.base.x = 0;
   1593         priv->view.offset.base.y = 0;
   1594         return EINA_TRUE;
   1595     }
   1596     Eina_Inlist **itr, **itr_end;
   1597     Ewk_Tiled_Backing_Store_Item *it;
   1598 
   1599     Evas_Coord new_x = cx + (priv->view.offset.cur.x - cx) * scale;
   1600     Evas_Coord new_y = cy + (priv->view.offset.cur.y - cy) * scale;
   1601     Evas_Coord bx = cx + (priv->view.offset.base.x - cx) * scale;
   1602     Evas_Coord by = cy + (priv->view.offset.base.y - cy) * scale;
   1603 
   1604     Evas_Coord model_width = priv->model.width * scale;
   1605     Evas_Coord model_height = priv->model.height * scale;
   1606 
   1607     if (model_width < priv->view.w || new_x >= 0)
   1608         new_x = 0;
   1609     else if (-new_x + priv->view.w >= model_width)
   1610         new_x = -model_width + priv->view.w;
   1611 
   1612     if (model_height < priv->view.h || new_y >= 0)
   1613         new_y = 0;
   1614     else if (-new_y + priv->view.h >= model_height)
   1615         new_y = -model_height + priv->view.h;
   1616 
   1617     bx = new_x % tw;
   1618     priv->model.base.col = - new_x / tw;
   1619     by = new_y % th;
   1620     priv->model.base.row = - new_y / th;
   1621 
   1622     priv->changed.size = EINA_TRUE;
   1623     _ewk_tiled_backing_store_changed(priv);
   1624 
   1625     priv->view.offset.cur.x = new_x;
   1626     priv->view.offset.cur.y = new_y;
   1627     priv->view.offset.base.x = bx;
   1628     priv->view.offset.base.y = by;
   1629 
   1630     priv->view.offset.old.x = priv->view.offset.cur.x;
   1631     priv->view.offset.old.y = priv->view.offset.cur.y;
   1632     *offx = priv->view.offset.cur.x;
   1633     *offy = priv->view.offset.cur.y;
   1634 
   1635     evas_object_move(
   1636         priv->contents_clipper,
   1637         new_x + priv->view.x,
   1638         new_y + priv->view.y);
   1639 
   1640     _ewk_tiled_backing_store_fill_renderers(priv);
   1641 
   1642     Evas_Coord oy = priv->view.offset.base.y + priv->view.y;
   1643     Evas_Coord base_ox = priv->view.x + priv->view.offset.base.x;
   1644 
   1645     itr = priv->view.items;
   1646     itr_end = itr + priv->view.rows;
   1647 
   1648     for (; itr < itr_end; itr++) {
   1649         Evas_Coord ox = base_ox;
   1650         Eina_Inlist *lst = *itr;
   1651 
   1652         EINA_INLIST_FOREACH(lst, it) {
   1653             _ewk_tiled_backing_store_item_move(it, ox, oy);
   1654             _ewk_tiled_backing_store_item_resize(it, tw, th);
   1655             ox += tw;
   1656         }
   1657         oy += th;
   1658     }
   1659 
   1660     return EINA_TRUE;
   1661 }
   1662 
   1663 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)
   1664 {
   1665     DBG("o=%p, zoom=%f", o, (double)*zoom);
   1666 
   1667     PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
   1668 
   1669     return _ewk_tiled_backing_store_zoom_set_internal(priv, zoom, cx, cy, offx, offy);
   1670 }
   1671 
   1672 Eina_Bool ewk_tiled_backing_store_zoom_weak_set(Evas_Object *o, float zoom, Evas_Coord cx, Evas_Coord cy)
   1673 {
   1674     DBG("o=%p, zoom=%f", o, (double)zoom);
   1675     PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
   1676     if (!priv->view.w || !priv->view.h)
   1677         return EINA_FALSE;
   1678     Eina_Inlist **itr, **itr_end;
   1679     Ewk_Tiled_Backing_Store_Item *it;
   1680     Evas_Coord tw, th;
   1681     Eina_Bool recalc = EINA_FALSE;
   1682 
   1683     tw = TILE_SIZE_AT_ZOOM(TILE_W, zoom);
   1684     zoom = TILE_W_ZOOM_AT_SIZE(tw);
   1685     /* WARNING: assume reverse zoom is the same for both axis */
   1686     th = TILE_SIZE_AT_ZOOM(TILE_H, zoom);
   1687 
   1688     float scale = zoom / priv->view.tile.zoom;
   1689 
   1690     Evas_Coord model_width = priv->model.width * scale;
   1691     Evas_Coord model_height = priv->model.height * scale;
   1692 
   1693     evas_object_resize(priv->contents_clipper,
   1694                        model_width, model_height);
   1695 
   1696     int vrows = ceil((float)priv->view.h / (float)th) + 1;
   1697     int vcols = ceil((float)priv->view.w / (float)tw) + 1;
   1698     Evas_Coord new_x = cx + (priv->view.offset.cur.x - cx) * scale;
   1699     Evas_Coord new_y = cy + (priv->view.offset.cur.y - cy) * scale;
   1700     Evas_Coord bx = new_x % tw;
   1701     Evas_Coord by = new_y % th;
   1702     unsigned long base_row = -new_y / th;
   1703     unsigned long base_col = -new_x / tw;
   1704 
   1705     if (base_row != priv->model.base.row || base_col != priv->model.base.col) {
   1706         priv->model.base.row = base_row;
   1707         priv->model.base.col = base_col;
   1708         recalc = EINA_TRUE;
   1709     }
   1710 
   1711     if (vrows > priv->view.rows || vcols > priv->view.cols)
   1712         recalc = EINA_TRUE;
   1713 
   1714     if (recalc) {
   1715         Evas_Coord w, h;
   1716         evas_object_geometry_get(o, NULL, NULL, &w, &h);
   1717         _ewk_tiled_backing_store_recalc_renderers(priv, w, h, tw, th);
   1718         _ewk_tiled_backing_store_fill_renderers(priv);
   1719         _ewk_tiled_backing_store_updates_process(priv);
   1720     }
   1721 
   1722     Evas_Coord base_ox = bx + priv->view.x;
   1723     Evas_Coord oy = by + priv->view.y;
   1724 
   1725     evas_object_move(priv->contents_clipper,
   1726                      new_x + priv->view.x,
   1727                      new_y + priv->view.y);
   1728 
   1729     itr = priv->view.items;
   1730     itr_end = itr + priv->view.rows;
   1731 
   1732     for (; itr < itr_end; itr++) {
   1733         Evas_Coord ox = base_ox;
   1734         Eina_Inlist *lst = *itr;
   1735 
   1736         EINA_INLIST_FOREACH(lst, it) {
   1737             _ewk_tiled_backing_store_item_move(it, ox, oy);
   1738             _ewk_tiled_backing_store_item_resize(it, tw, th);
   1739             ox += tw;
   1740         }
   1741         oy += th;
   1742     }
   1743 
   1744     return EINA_TRUE;
   1745 }
   1746 
   1747 void ewk_tiled_backing_store_fix_offsets(Evas_Object *o, Evas_Coord w, Evas_Coord h)
   1748 {
   1749     PRIV_DATA_GET_OR_RETURN(o, priv);
   1750     Eina_Inlist **itr, **itr_end;
   1751     Ewk_Tiled_Backing_Store_Item *it;
   1752     Evas_Coord new_x = priv->view.offset.cur.x;
   1753     Evas_Coord new_y = priv->view.offset.cur.y;
   1754     Evas_Coord bx = priv->view.offset.base.x;
   1755     Evas_Coord by = priv->view.offset.base.y;
   1756     Evas_Coord tw = priv->view.tile.w;
   1757     Evas_Coord th = priv->view.tile.h;
   1758 
   1759     if (-new_x > w) {
   1760         new_x = -w;
   1761         bx = new_x % tw;
   1762         priv->model.base.col = -new_x / tw;
   1763     }
   1764 
   1765     if (-new_y > h) {
   1766         new_y = -h;
   1767         by = new_y % th;
   1768         priv->model.base.row = -new_y / th;
   1769     }
   1770 
   1771     if (bx >= 0 || bx <= -2 * priv->view.tile.w) {
   1772         bx = new_x % tw;
   1773         priv->model.base.col = -new_x / tw;
   1774     }
   1775 
   1776     if (by >= 0 || by <= -2 * priv->view.tile.h) {
   1777         by = new_y % th;
   1778         priv->model.base.row = -new_y / th;
   1779     }
   1780 
   1781     priv->view.offset.cur.x = new_x;
   1782     priv->view.offset.cur.y = new_y;
   1783     priv->view.offset.old.x = new_x;
   1784     priv->view.offset.old.y = new_y;
   1785     priv->view.offset.base.x = bx;
   1786     priv->view.offset.base.y = by;
   1787     evas_object_move(priv->contents_clipper,
   1788                      new_x + priv->view.x,
   1789                      new_y + priv->view.y);
   1790 
   1791     Evas_Coord oy = priv->view.offset.base.y + priv->view.y;
   1792     Evas_Coord base_ox = priv->view.x + priv->view.offset.base.x;
   1793 
   1794     itr = priv->view.items;
   1795     itr_end = itr + priv->view.rows;
   1796 
   1797     for (; itr < itr_end; itr++) {
   1798         Evas_Coord ox = base_ox;
   1799         Eina_Inlist *lst = *itr;
   1800 
   1801         EINA_INLIST_FOREACH(lst, it) {
   1802             _ewk_tiled_backing_store_item_move(it, ox, oy);
   1803             _ewk_tiled_backing_store_item_resize(it, tw, th);
   1804             ox += tw;
   1805         }
   1806         oy += th;
   1807     }
   1808 }
   1809 
   1810 void ewk_tiled_backing_store_zoom_weak_smooth_scale_set(Evas_Object *o, Eina_Bool smooth_scale)
   1811 {
   1812     PRIV_DATA_GET_OR_RETURN(o, priv);
   1813     Eina_Inlist **itr, **itr_end;
   1814 
   1815     itr = priv->view.items;
   1816     itr_end = itr + priv->view.rows;
   1817     priv->view.tile.zoom_weak_smooth_scale = smooth_scale;
   1818 
   1819     for (; itr< itr_end; itr++) {
   1820         Ewk_Tiled_Backing_Store_Item *it;
   1821         EINA_INLIST_FOREACH(*itr, it)
   1822             if (it->tile)
   1823                 _ewk_tiled_backing_store_item_smooth_scale_set
   1824                     (it, smooth_scale);
   1825     }
   1826 }
   1827 
   1828 Eina_Bool ewk_tiled_backing_store_update(Evas_Object *o, const Eina_Rectangle *update)
   1829 {
   1830     PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
   1831 
   1832     if (priv->render.disabled)
   1833         return EINA_FALSE;
   1834 
   1835     return ewk_tile_matrix_update(priv->model.matrix, update,
   1836                                   priv->view.tile.zoom);
   1837 }
   1838 
   1839 void ewk_tiled_backing_store_updates_process_pre_set(Evas_Object *o, void *(*cb)(void *data, Evas_Object *o), const void *data)
   1840 {
   1841     PRIV_DATA_GET_OR_RETURN(o, priv);
   1842     priv->process.pre_cb = cb;
   1843     priv->process.pre_data = (void*)data;
   1844 }
   1845 
   1846 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)
   1847 {
   1848     PRIV_DATA_GET_OR_RETURN(o, priv);
   1849     priv->process.post_cb = cb;
   1850     priv->process.post_data = (void*)data;
   1851 }
   1852 
   1853 void ewk_tiled_backing_store_updates_process(Evas_Object *o)
   1854 {
   1855     PRIV_DATA_GET_OR_RETURN(o, priv);
   1856     _ewk_tiled_backing_store_updates_process(priv);
   1857 }
   1858 
   1859 void ewk_tiled_backing_store_updates_clear(Evas_Object *o)
   1860 {
   1861     PRIV_DATA_GET_OR_RETURN(o, priv);
   1862 
   1863     ewk_tile_matrix_updates_clear(priv->model.matrix);
   1864 }
   1865 
   1866 void ewk_tiled_backing_store_contents_resize(Evas_Object *o, Evas_Coord width, Evas_Coord height)
   1867 {
   1868     PRIV_DATA_GET_OR_RETURN(o, priv);
   1869 
   1870     if (width == priv->model.width && height == priv->model.height)
   1871         return;
   1872 
   1873     priv->model.width = width;
   1874     priv->model.height = height;
   1875     priv->changed.model = EINA_TRUE;
   1876 
   1877     DBG("width,height=%d, %d", width, height);
   1878     _ewk_tiled_backing_store_changed(priv);
   1879 }
   1880 
   1881 void ewk_tiled_backing_store_disabled_update_set(Evas_Object *o, Eina_Bool value)
   1882 {
   1883     PRIV_DATA_GET_OR_RETURN(o, priv);
   1884 
   1885     if (value != priv->render.disabled)
   1886         priv->render.disabled = value;
   1887 }
   1888 
   1889 void ewk_tiled_backing_store_flush(Evas_Object *o)
   1890 {
   1891     PRIV_DATA_GET_OR_RETURN(o, priv);
   1892     Ewk_Tile_Unused_Cache *tuc = NULL;
   1893 
   1894     priv->view.offset.cur.x = 0;
   1895     priv->view.offset.cur.y = 0;
   1896     priv->view.offset.old.x = 0;
   1897     priv->view.offset.old.y = 0;
   1898     priv->view.offset.base.x = 0;
   1899     priv->view.offset.base.y = 0;
   1900     priv->model.base.col = 0;
   1901     priv->model.base.row = 0;
   1902     priv->changed.size = EINA_TRUE;
   1903 
   1904 #ifdef DEBUG_MEM_LEAKS
   1905     printf("\nFLUSHED BACKING STORE, STATUS BEFORE DELETING TILE MATRIX:\n");
   1906     _ewk_tiled_backing_store_mem_dbg(priv);
   1907 #endif
   1908 
   1909     _ewk_tiled_backing_store_pre_render_request_flush(priv);
   1910     _ewk_tiled_backing_store_tile_dissociate_all(priv);
   1911     tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
   1912     ewk_tile_unused_cache_clear(tuc);
   1913 
   1914 #ifdef DEBUG_MEM_LEAKS
   1915     printf("\nFLUSHED BACKING STORE, STATUS AFTER RECREATING TILE MATRIX:\n");
   1916     _ewk_tiled_backing_store_mem_dbg(priv);
   1917 #endif
   1918 }
   1919 
   1920 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)
   1921 {
   1922     PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
   1923     Eina_Tile_Grid_Slicer slicer;
   1924     const Eina_Tile_Grid_Info *info;
   1925     Evas_Coord tw, th;
   1926     Ewk_Tile_Unused_Cache *tuc;
   1927 
   1928     tw = TILE_SIZE_AT_ZOOM(TILE_W, zoom);
   1929     tw = (tw >> 1) << 1;
   1930     zoom = TILE_W_ZOOM_AT_SIZE(tw);
   1931     /* WARNING: assume reverse zoom is the same for both axis */
   1932     th = TILE_SIZE_AT_ZOOM(TILE_H, zoom);
   1933 
   1934     if (!eina_tile_grid_slicer_setup(&slicer, x, y, w, h, tw, th)) {
   1935         ERR("could not setup grid slicer for %d,%d+%dx%d tile=%dx%d",
   1936             x, y, w, h, tw, th);
   1937         return EINA_FALSE;
   1938     }
   1939 
   1940     while (eina_tile_grid_slicer_next(&slicer, &info)) {
   1941         const unsigned long c = info->col;
   1942         const unsigned long r = info->row;
   1943         if (!_ewk_tiled_backing_store_pre_render_request_add(priv, c, r, zoom, PRE_RENDER_PRIORITY_LOW))
   1944             break;
   1945     }
   1946 
   1947     _ewk_tiled_backing_store_item_process_idler_start(priv);
   1948 
   1949     tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
   1950     ewk_tile_unused_cache_lock_area(tuc, x, y, w, h, zoom);
   1951     return EINA_TRUE;
   1952 }
   1953 
   1954 Eina_Bool ewk_tiled_backing_store_pre_render_relative_radius(Evas_Object *o, unsigned int n, float zoom)
   1955 {
   1956     PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
   1957     unsigned long start_row, end_row, start_col, end_col, i, j, w, h;
   1958     Ewk_Tile_Unused_Cache *tuc;
   1959 
   1960     INF("priv->model.base.row =%ld, n=%u priv->view.rows=%lu",
   1961             priv->model.base.row, n, priv->view.rows);
   1962     start_row = (long)priv->model.base.row - n;
   1963     start_col = (long)priv->model.base.col - n;
   1964     end_row = MIN(priv->model.cur.rows - 1,
   1965                   priv->model.base.row + priv->view.rows + n - 1);
   1966     end_col = MIN(priv->model.cur.cols - 1,
   1967                   priv->model.base.col + priv->view.cols + n - 1);
   1968 
   1969     INF("start_row=%lu, end_row=%lu, start_col=%lu, end_col=%lu",
   1970          start_row, end_row, start_col, end_col);
   1971 
   1972     for (i = start_row; i <= end_row; i++)
   1973         for (j = start_col; j <= end_col; j++)
   1974             if (!_ewk_tiled_backing_store_pre_render_request_add(priv, j, i, zoom, PRE_RENDER_PRIORITY_LOW))
   1975                 goto start_processing;
   1976 
   1977 start_processing:
   1978     _ewk_tiled_backing_store_item_process_idler_start(priv);
   1979 
   1980     tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
   1981     h = (end_row - start_row + 1) * TILE_SIZE_AT_ZOOM(TILE_H, zoom);
   1982     w = (end_col - start_col + 1) * TILE_SIZE_AT_ZOOM(TILE_W, zoom);
   1983     ewk_tile_unused_cache_lock_area(tuc,
   1984             start_col * TILE_SIZE_AT_ZOOM(TILE_W, zoom),
   1985             start_row * TILE_SIZE_AT_ZOOM(TILE_H, zoom), w, h, zoom);
   1986 
   1987     return EINA_TRUE;
   1988 }
   1989 
   1990 void ewk_tiled_backing_store_pre_render_cancel(Evas_Object *o)
   1991 {
   1992     PRIV_DATA_GET_OR_RETURN(o, priv);
   1993     Ewk_Tile_Unused_Cache *tuc;
   1994 
   1995     _ewk_tiled_backing_store_pre_render_request_clear(priv);
   1996 
   1997     tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
   1998     ewk_tile_unused_cache_unlock_area(tuc);
   1999 }
   2000 
   2001 Eina_Bool ewk_tiled_backing_store_disable_render(Evas_Object *o)
   2002 {
   2003     PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
   2004     return _ewk_tiled_backing_store_disable_render(priv);
   2005 }
   2006 
   2007 Eina_Bool ewk_tiled_backing_store_enable_render(Evas_Object *o)
   2008 {
   2009     PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
   2010     _ewk_tiled_backing_store_changed(priv);
   2011     return _ewk_tiled_backing_store_enable_render(priv);
   2012 }
   2013 
   2014 /**
   2015  * Set the process_entire_queue flag of the renderer idler.
   2016  *
   2017  *
   2018  * @param o the tiled backing store object
   2019  * @param value EINA_TRUE if we want to process all the request of our queue. EINA_FALSE otherwise.
   2020  */
   2021 void ewk_tiled_backing_store_process_entire_queue_set(Evas_Object *o, Eina_Bool value)
   2022 {
   2023     PRIV_DATA_GET_OR_RETURN(o, priv);
   2024     if (priv->render.process_entire_queue != value)
   2025         priv->render.process_entire_queue = value;
   2026 }
   2027