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