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