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
44typedef enum _Ewk_Tiled_Backing_Store_Pre_Render_Priority Ewk_Tiled_Backing_Store_Pre_Render_Priority;
45typedef struct _Ewk_Tiled_Backing_Store_Data Ewk_Tiled_Backing_Store_Data;
46typedef struct _Ewk_Tiled_Backing_Store_Item Ewk_Tiled_Backing_Store_Item;
47typedef struct _Ewk_Tiled_Backing_Store_Pre_Render_Request Ewk_Tiled_Backing_Store_Pre_Render_Request;
48
49enum _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
54struct _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
68struct _Ewk_Tiled_Backing_Store_Pre_Render_Request {
69    EINA_INLIST;
70    unsigned long col, row;
71    float zoom;
72};
73
74struct _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
132static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL;
133int _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
142static inline void _ewk_tiled_backing_store_item_request_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it);
143static 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);
144static void _ewk_tiled_backing_store_fill_renderers(Ewk_Tiled_Backing_Store_Data *priv);
145static inline void _ewk_tiled_backing_store_view_dbg(const Ewk_Tiled_Backing_Store_Data *priv);
146static inline void _ewk_tiled_backing_store_changed(Ewk_Tiled_Backing_Store_Data *priv);
147
148static 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
168static 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
182static 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
202static 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
211static 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
222static 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
239static 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
252static 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
267static 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
287static 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
294static 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
301static 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
312static 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! */
328static 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
369end:
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
375static 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
431static 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
440static 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
448static 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
455static 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
471static 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
481static 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
494static 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
571static 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
598static 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
609static 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
618static 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
626static 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
645static 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
664static 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
674static 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
682static 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
697static 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
717static 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
733static 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
743static 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
754static 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
771static 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
788static 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
842static 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
875static 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
891static 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
907static 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
986static 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.
998static 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 */
1041static 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 */
1089static 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 */
1136static 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 */
1183static 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
1216static 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
1251static 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
1277static 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
1309static 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
1366static 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
1377static 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
1391static 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
1472Evas_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
1500void 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
1508Ewk_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
1514void 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
1524static 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
1539Eina_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
1550Eina_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
1562static 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
1663Eina_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
1672Eina_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
1747void 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
1810void 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
1828Eina_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
1839void 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
1846void 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
1853void 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
1859void 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
1866void 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
1881void 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
1889void 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
1920Eina_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
1954Eina_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
1977start_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
1990void 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
2001Eina_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
2007Eina_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 */
2021void 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