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