2 Copyright (C) 2009-2010 Samsung Electronics
3 Copyright (C) 2009-2010 ProFUSION embedded systems
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.
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.
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.
22 #include "ewk_tiled_backing_store.h"
25 #include "ewk_tiled_private.h"
30 #include <stdio.h> // XXX REMOVE ME LATER
34 #define IDX(col, row, rowspan) (col + (row * rowspan))
37 # define MIN(a, b) ((a < b) ? a : b)
41 # define MAX(a, b) ((a > b) ? a : b)
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;
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 */
54 struct _Ewk_Tiled_Backing_Store_Item {
58 Evas_Coord x, y, w, h;
60 Eina_Bool smooth_scale;
63 struct _Ewk_Tiled_Backing_Store_Pre_Render_Request {
65 unsigned long col, row;
69 struct _Ewk_Tiled_Backing_Store_Data {
70 Evas_Object_Smart_Clipped_Data base;
72 Evas_Object *contents_clipper;
75 Evas_Coord x, y, w, h;
80 Eina_Bool zoom_weak_smooth_scale:1;
85 } cur, old, base, zoom_center;
88 Evas_Colorspace cspace;
90 Ewk_Tile_Matrix *matrix;
92 unsigned long col, row;
95 unsigned long cols, rows;
97 Evas_Coord width, height;
100 Eina_Bool (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *area);
102 Eina_Inlist *pre_render_requests;
108 void *(*pre_cb)(void *data, Evas_Object *o);
110 void *(*post_cb)(void *data, void *pre_data, Evas_Object *o);
120 #ifdef DEBUG_MEM_LEAKS
121 Ecore_Event_Handler *sig_usr;
125 static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL;
126 int _ewk_tiled_log_dom = -1;
128 #define PRIV_DATA_GET_OR_RETURN(obj, ptr, ...) \
129 Ewk_Tiled_Backing_Store_Data *ptr = evas_object_smart_data_get(obj); \
131 CRITICAL("no private data in obj=%p", obj); \
132 return __VA_ARGS__; \
135 static void _ewk_tiled_backing_store_fill_renderers(Ewk_Tiled_Backing_Store_Data *priv);
136 static inline void _ewk_tiled_backing_store_view_dbg(const Ewk_Tiled_Backing_Store_Data *priv);
137 static inline void _ewk_tiled_backing_store_changed(Ewk_Tiled_Backing_Store_Data *priv);
139 static inline void _ewk_tiled_backing_store_updates_process(Ewk_Tiled_Backing_Store_Data *priv)
143 /* Do not process updates. Note that we still want to get updates requests
144 * in the queue in order to not miss any updates after the render is
147 if (priv->render.suspend || !evas_object_visible_get(priv->self))
150 if (priv->process.pre_cb)
151 data = priv->process.pre_cb(priv->process.pre_data, priv->self);
153 ewk_tile_matrix_updates_process(priv->model.matrix);
155 if (priv->process.post_cb)
156 priv->process.post_cb(priv->process.post_data, data, priv->self);
159 static int _ewk_tiled_backing_store_flush(void *data)
161 Ewk_Tiled_Backing_Store_Data *priv = data;
162 Ewk_Tile_Unused_Cache *tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
165 DBG("flush unused tile cache.");
166 ewk_tile_unused_cache_auto_flush(tuc);
173 static Ewk_Tile *_ewk_tiled_backing_store_tile_new(Ewk_Tiled_Backing_Store_Data *priv, unsigned long col, unsigned long row, float zoom)
176 Evas *evas = evas_object_evas_get(priv->self);
178 CRITICAL("evas_object_evas_get failed!");
182 t = ewk_tile_matrix_tile_new
183 (priv->model.matrix, evas, col, row, zoom);
186 CRITICAL("ewk_tile_matrix_tile_new failed!");
193 static void _ewk_tiled_backing_store_item_move(Ewk_Tiled_Backing_Store_Item *it, Evas_Coord x, Evas_Coord y)
199 evas_object_move(it->tile->image, x, y);
202 static void _ewk_tiled_backing_store_item_resize(Ewk_Tiled_Backing_Store_Item *it, Evas_Coord w, Evas_Coord h)
208 evas_object_resize(it->tile->image, w, h);
209 evas_object_image_fill_set(it->tile->image, 0, 0, w, h);
213 static void _ewk_tiled_backing_store_tile_associate(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tile *t, Ewk_Tiled_Backing_Store_Item *it)
216 CRITICAL("it->tile=%p, but it should be NULL!", it->tile);
218 evas_object_move(it->tile->image, it->geometry.x, it->geometry.y);
219 evas_object_resize(it->tile->image, it->geometry.w, it->geometry.h);
220 evas_object_image_fill_set
221 (it->tile->image, 0, 0, it->geometry.w, it->geometry.h);
222 evas_object_image_smooth_scale_set(it->tile->image, it->smooth_scale);
224 if (!ewk_tile_visible_get(t))
225 evas_object_smart_member_add(t->image, priv->self);
230 static void _ewk_tiled_backing_store_tile_dissociate(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it, double last_used)
232 Ewk_Tile_Unused_Cache *tuc;
233 ewk_tile_hide(it->tile);
234 if (!ewk_tile_visible_get(it->tile))
235 evas_object_smart_member_del(it->tile->image);
236 ewk_tile_matrix_tile_put(priv->model.matrix, it->tile, last_used);
237 tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
238 ewk_tile_unused_cache_auto_flush(tuc);
243 static void _ewk_tiled_backing_store_tile_dissociate_all(Ewk_Tiled_Backing_Store_Data *priv)
246 Ewk_Tiled_Backing_Store_Item *item;
248 double last_used = ecore_loop_time_get();
250 for (i = 0; i < priv->view.rows; i++) {
251 it = priv->view.items[i];
252 EINA_INLIST_FOREACH(it, item)
254 _ewk_tiled_backing_store_tile_dissociate(priv, item, last_used);
258 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)
260 Ewk_Tiled_Backing_Store_Pre_Render_Request *r;
262 MALLOC_OR_OOM_RET(r, sizeof(*r), EINA_FALSE);
264 if (priority == PRE_RENDER_PRIORITY_HIGH)
265 priv->render.pre_render_requests = eina_inlist_prepend
266 (priv->render.pre_render_requests, EINA_INLIST_GET(r));
268 priv->render.pre_render_requests = eina_inlist_append
269 (priv->render.pre_render_requests, EINA_INLIST_GET(r));
278 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)
280 priv->render.pre_render_requests = eina_inlist_remove
281 (priv->render.pre_render_requests, EINA_INLIST_GET(r));
285 static inline Ewk_Tiled_Backing_Store_Pre_Render_Request *_ewk_tiled_backing_store_pre_render_request_first(const Ewk_Tiled_Backing_Store_Data *priv)
287 return EINA_INLIST_CONTAINER_GET(
288 priv->render.pre_render_requests,
289 Ewk_Tiled_Backing_Store_Pre_Render_Request);
292 static void _ewk_tiled_backing_store_pre_render_request_flush(Ewk_Tiled_Backing_Store_Data *priv)
294 Eina_Inlist **pl = &priv->render.pre_render_requests;
296 Ewk_Tiled_Backing_Store_Pre_Render_Request *r;
297 r = _ewk_tiled_backing_store_pre_render_request_first(priv);
298 *pl = eina_inlist_remove(*pl, *pl);
303 static void _ewk_tiled_backing_store_pre_render_request_clear(Ewk_Tiled_Backing_Store_Data *priv)
305 Eina_Inlist **pl = &priv->render.pre_render_requests;
306 Eina_Inlist *iter = *pl, *tmp;
308 Ewk_Tiled_Backing_Store_Pre_Render_Request *r =
309 EINA_INLIST_CONTAINER_GET(
310 iter, Ewk_Tiled_Backing_Store_Pre_Render_Request);
312 *pl = eina_inlist_remove(*pl, iter);
318 /* assumes priv->process.pre_cb was called if required! */
319 static void _ewk_tiled_backing_store_pre_render_request_process_single(Ewk_Tiled_Backing_Store_Data *priv)
321 Ewk_Tiled_Backing_Store_Pre_Render_Request *req;
323 Ewk_Tile_Matrix *tm = priv->model.matrix;
325 Ewk_Tile_Unused_Cache *tuc;
326 unsigned long col, row;
328 double last_used = ecore_loop_time_get();
330 req = _ewk_tiled_backing_store_pre_render_request_first(priv);
338 if (ewk_tile_matrix_tile_exact_exists(tm, col, row, zoom)) {
339 DBG("no pre-render required for tile %lu,%lu @ %f.", col, row, zoom);
343 t = _ewk_tiled_backing_store_tile_new(priv, col, row, zoom);
349 area.w = priv->view.tile.w;
350 area.h = priv->view.tile.h;
352 priv->render.cb(priv->render.data, t, &area);
353 evas_object_image_data_update_add(
355 area.x, area.y, area.w, area.h);
356 ewk_tile_matrix_tile_updates_clear(tm, t);
358 ewk_tile_matrix_tile_put(tm, t, last_used);
361 _ewk_tiled_backing_store_pre_render_request_del(priv, req);
362 tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
363 ewk_tile_unused_cache_auto_flush(tuc);
366 static Eina_Bool _ewk_tiled_backing_store_item_process_idler_cb(void *data)
368 Ewk_Tiled_Backing_Store_Data *priv = data;
370 if (priv->process.pre_cb)
371 data = priv->process.pre_cb(priv->process.pre_data, priv->self);
373 _ewk_tiled_backing_store_pre_render_request_process_single(priv);
375 if (priv->process.post_cb)
376 priv->process.post_cb(priv->process.post_data, data, priv->self);
378 if (!priv->render.pre_render_requests) {
379 priv->render.idler = NULL;
386 static inline void _ewk_tiled_backing_store_item_process_idler_stop(Ewk_Tiled_Backing_Store_Data *priv)
388 if (!priv->render.idler)
391 ecore_idler_del(priv->render.idler);
392 priv->render.idler = NULL;
395 static inline void _ewk_tiled_backing_store_item_process_idler_start(Ewk_Tiled_Backing_Store_Data *priv)
397 if (priv->render.idler)
399 priv->render.idler = ecore_idler_add(
400 _ewk_tiled_backing_store_item_process_idler_cb, priv);
403 static Eina_Bool _ewk_tiled_backing_store_disable_render(Ewk_Tiled_Backing_Store_Data *priv)
405 if (priv->render.suspend)
408 priv->render.suspend = EINA_TRUE;
409 _ewk_tiled_backing_store_item_process_idler_stop(priv);
413 static Eina_Bool _ewk_tiled_backing_store_enable_render(Ewk_Tiled_Backing_Store_Data *priv)
415 if (!priv->render.suspend)
418 priv->render.suspend = EINA_FALSE;
420 _ewk_tiled_backing_store_fill_renderers(priv);
421 _ewk_tiled_backing_store_item_process_idler_start(priv);
426 static inline Eina_Bool _ewk_tiled_backing_store_item_fill(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it, unsigned long col, unsigned long row)
428 unsigned long m_col = priv->model.base.col + col;
429 unsigned long m_row = priv->model.base.row + row;
430 double last_used = ecore_loop_time_get();
432 if (m_col >= priv->model.cur.cols || m_row >= priv->model.cur.rows) {
434 _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used);
437 const float zoom = priv->view.tile.zoom;
440 Ewk_Tile *old = it->tile;
441 if (old->row != m_row || old->col != m_col || old->zoom != zoom)
442 _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used);
443 else if (old->row == m_row && old->col == m_col && old->zoom == zoom)
447 t = ewk_tile_matrix_tile_exact_get(priv->model.matrix, m_col, m_row, zoom);
450 /* NOTE: it never returns NULL if it->tile was set! */
452 CRITICAL("it->tile=%p, but it should be NULL!", it->tile);
453 _ewk_tiled_backing_store_tile_dissociate(priv, it,
457 /* Do not add new requests to the render queue */
458 if (!priv->render.suspend) {
459 t = _ewk_tiled_backing_store_tile_new(priv, m_col, m_row, zoom);
462 _ewk_tiled_backing_store_tile_associate(priv, t, it);
464 } else if (t != it->tile) {
466 _ewk_tiled_backing_store_tile_dissociate(priv,
468 _ewk_tiled_backing_store_tile_associate(priv, t, it);
479 static Ewk_Tiled_Backing_Store_Item *_ewk_tiled_backing_store_item_add(Ewk_Tiled_Backing_Store_Data *priv, unsigned long col, unsigned long row)
481 Ewk_Tiled_Backing_Store_Item *it;
482 Evas_Coord x, y, tw, th;
484 DBG("o=%p", priv->self);
486 MALLOC_OR_OOM_RET(it, sizeof(*it), NULL);
488 tw = priv->view.tile.w;
489 th = priv->view.tile.h;
490 x = priv->view.offset.base.x + priv->view.x + tw *col;
491 y = priv->view.offset.base.y + priv->view.y + th *row;
495 it->smooth_scale = priv->view.tile.zoom_weak_smooth_scale;
496 _ewk_tiled_backing_store_item_move(it, x, y);
497 _ewk_tiled_backing_store_item_resize(it, tw, th);
498 if (!_ewk_tiled_backing_store_item_fill(priv, it, col, row)) {
506 static void _ewk_tiled_backing_store_item_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it)
509 double last_used = ecore_loop_time_get();
510 _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used);
516 static void _ewk_tiled_backing_store_item_smooth_scale_set(Ewk_Tiled_Backing_Store_Item *it, Eina_Bool smooth_scale)
518 if (it->smooth_scale == smooth_scale)
522 evas_object_image_smooth_scale_set(it->tile->image, smooth_scale);
525 static inline void _ewk_tiled_backing_store_changed(Ewk_Tiled_Backing_Store_Data *priv)
527 if (priv->changed.any)
529 evas_object_smart_changed(priv->self);
530 priv->changed.any = EINA_TRUE;
533 static void _ewk_tiled_backing_store_view_cols_end_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist **p_row, unsigned int count)
543 for (i = 0; i < count; i++) {
544 Ewk_Tiled_Backing_Store_Item *it;
545 it = EINA_INLIST_CONTAINER_GET(n, Ewk_Tiled_Backing_Store_Item);
547 *p_row = eina_inlist_remove(*p_row, EINA_INLIST_GET(it));
548 _ewk_tiled_backing_store_item_del(priv, it);
552 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)
554 unsigned int i, r = p_row - priv->view.items;
556 for (i = 0; i < count; i++, base_col++) {
557 Ewk_Tiled_Backing_Store_Item *it;
559 it = _ewk_tiled_backing_store_item_add(priv, base_col, r);
561 CRITICAL("failed to add column %u of %u in row %u.", i, count, r);
562 _ewk_tiled_backing_store_view_cols_end_del(priv, p_row, i);
566 *p_row = eina_inlist_append(*p_row, EINA_INLIST_GET(it));
571 static void _ewk_tiled_backing_store_view_row_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist *row)
574 Ewk_Tiled_Backing_Store_Item *it;
575 it = EINA_INLIST_CONTAINER_GET(row, Ewk_Tiled_Backing_Store_Item);
577 _ewk_tiled_backing_store_item_del(priv, it);
581 static void _ewk_tiled_backing_store_view_rows_range_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist **start, Eina_Inlist **end)
583 for (; start < end; start++) {
584 _ewk_tiled_backing_store_view_row_del(priv, *start);
589 static void _ewk_tiled_backing_store_view_rows_all_del(Ewk_Tiled_Backing_Store_Data *priv)
594 start = priv->view.items;
595 end = priv->view.items + priv->view.rows;
596 _ewk_tiled_backing_store_view_rows_range_del(priv, start, end);
598 free(priv->view.items);
599 priv->view.items = NULL;
604 static void _ewk_tiled_backing_store_render(void *data, Ewk_Tile *t, const Eina_Rectangle *area)
606 Ewk_Tiled_Backing_Store_Data *priv = data;
608 INF("TODO %p (visible? %d) [%lu,%lu] %d,%d + %dx%d",
609 t, t->visible, t->col, t->row, area->x, area->y, area->w, area->h);
614 if (priv->view.tile.w != t->w || priv->view.tile.h != t->h)
615 return; // todo: remove me later, don't even flag as dirty!
617 EINA_SAFETY_ON_NULL_RETURN(priv->render.cb);
618 if (!priv->render.cb(priv->render.data, t, area))
621 evas_object_image_data_update_add(t->image, area->x, area->y, area->w, area->h);
624 static inline void _ewk_tiled_backing_store_model_matrix_create(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tile_Unused_Cache *tuc)
626 if (priv->model.matrix) {
627 _ewk_tiled_backing_store_view_rows_all_del(priv);
629 priv->changed.offset = EINA_FALSE;
630 priv->changed.size = EINA_TRUE;
632 ewk_tile_matrix_free(priv->model.matrix);
635 priv->model.matrix = ewk_tile_matrix_new(tuc, priv->model.cur.cols, priv->model.cur.rows, priv->cspace, _ewk_tiled_backing_store_render, priv);
638 static void _ewk_tiled_backing_store_smart_member_del(Evas_Object *o, Evas_Object *member)
640 PRIV_DATA_GET_OR_RETURN(o, priv);
641 if (!priv->contents_clipper)
643 evas_object_clip_unset(member);
644 if (!evas_object_clipees_get(priv->contents_clipper))
645 evas_object_hide(priv->contents_clipper);
648 static void _ewk_tiled_backing_store_smart_member_add(Evas_Object *o, Evas_Object *member)
650 PRIV_DATA_GET_OR_RETURN(o, priv);
651 if (!priv->contents_clipper)
653 evas_object_clip_set(member, priv->contents_clipper);
654 if (evas_object_visible_get(o))
655 evas_object_show(priv->contents_clipper);
658 #ifdef DEBUG_MEM_LEAKS
659 static void _ewk_tiled_backing_store_mem_dbg(Ewk_Tiled_Backing_Store_Data *priv)
665 printf("\n--- BEGIN DEBUG TILED BACKING STORE MEMORY [%d] --\n"
666 "t=%0.2f, obj=%p, priv=%p, view.items=%p, matrix=%p\n",
667 run, ecore_loop_time_get(),
668 priv->self, priv, priv->view.items, priv->model.matrix);
670 ewk_tile_matrix_dbg(priv->model.matrix);
671 ewk_tile_accounting_dbg();
673 printf("--- END DEBUG TILED BACKING STORE MEMORY [%d] --\n\n", run);
676 static Eina_Bool _ewk_tiled_backing_store_sig_usr(void *data, int type, void *event)
678 Ecore_Event_Signal_User *sig = (Ecore_Event_Signal_User*)event;
679 Ewk_Tiled_Backing_Store_Data *priv = (Ewk_Tiled_Backing_Store_Data*)data;
681 if (sig->number == 2) {
682 Ewk_Tile_Unused_Cache *tuc;
683 tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
684 ewk_tile_unused_cache_auto_flush(tuc);
687 _ewk_tiled_backing_store_view_dbg(priv);
688 _ewk_tiled_backing_store_mem_dbg(priv);
693 static void _ewk_tiled_backing_store_smart_add(Evas_Object *o)
695 Ewk_Tiled_Backing_Store_Data *priv;
699 CALLOC_OR_OOM_RET(priv, sizeof(*priv));
702 priv->view.tile.zoom = 1.0;
703 priv->view.tile.w = DEFAULT_TILE_W;
704 priv->view.tile.h = DEFAULT_TILE_H;
705 priv->view.offset.cur.x = 0;
706 priv->view.offset.cur.y = 0;
707 priv->view.offset.old.x = 0;
708 priv->view.offset.old.y = 0;
709 priv->view.offset.base.x = 0;
710 priv->view.offset.base.y = 0;
712 priv->model.base.col = 0;
713 priv->model.base.row = 0;
714 priv->model.cur.cols = 1;
715 priv->model.cur.rows = 1;
716 priv->model.old.cols = 0;
717 priv->model.old.rows = 0;
718 priv->model.width = 0;
719 priv->model.height = 0;
720 priv->render.suspend = EINA_FALSE;
721 priv->cspace = EVAS_COLORSPACE_ARGB8888; // TODO: detect it.
723 evas_object_smart_data_set(o, priv);
726 priv->contents_clipper = evas_object_rectangle_add(
727 evas_object_evas_get(o));
728 evas_object_move(priv->contents_clipper, 0, 0);
729 evas_object_resize(priv->contents_clipper,
730 priv->model.width, priv->model.height);
731 evas_object_color_set(priv->contents_clipper, 255, 255, 255, 255);
732 evas_object_show(priv->contents_clipper);
733 evas_object_smart_member_add(priv->contents_clipper, o);
735 _ewk_tiled_backing_store_model_matrix_create(priv, NULL);
736 evas_object_move(priv->base.clipper, 0, 0);
737 evas_object_resize(priv->base.clipper, 0, 0);
738 evas_object_clip_set(priv->contents_clipper, priv->base.clipper);
740 #ifdef DEBUG_MEM_LEAKS
741 priv->sig_usr = ecore_event_handler_add
742 (ECORE_EVENT_SIGNAL_USER, _ewk_tiled_backing_store_sig_usr, priv);
746 static void _ewk_tiled_backing_store_smart_del(Evas_Object *o)
748 PRIV_DATA_GET_OR_RETURN(o, priv);
750 Ewk_Tile_Unused_Cache *tuc;
752 tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
753 ewk_tile_unused_cache_unlock_area(tuc);
755 _ewk_tiled_backing_store_flush(priv);
757 _ewk_tiled_backing_store_pre_render_request_flush(priv);
758 _ewk_tiled_backing_store_item_process_idler_stop(priv);
759 _ewk_tiled_backing_store_view_rows_all_del(priv);
761 #ifdef DEBUG_MEM_LEAKS
762 _ewk_tiled_backing_store_mem_dbg(priv);
764 priv->sig_usr = ecore_event_handler_del(priv->sig_usr);
767 ewk_tile_matrix_free(priv->model.matrix);
768 evas_object_smart_member_del(priv->contents_clipper);
769 evas_object_del(priv->contents_clipper);
773 #ifdef DEBUG_MEM_LEAKS
774 printf("\nIMPORTANT: TILED BACKING STORE DELETED (may be real leaks)\n");
775 ewk_tile_accounting_dbg();
779 static void _ewk_tiled_backing_store_smart_move(Evas_Object *o, Evas_Coord x, Evas_Coord y)
781 DBG("o=%p, new pos: %dx%d", o, x, y);
783 PRIV_DATA_GET_OR_RETURN(o, priv);
785 if (priv->changed.pos)
788 if (priv->view.x == x && priv->view.y == y)
791 priv->changed.pos = EINA_TRUE;
792 _ewk_tiled_backing_store_changed(priv);
795 static void _ewk_tiled_backing_store_smart_resize(Evas_Object *o, Evas_Coord w, Evas_Coord h)
797 DBG("o=%p, new size: %dx%d", o, w, h);
799 PRIV_DATA_GET_OR_RETURN(o, priv);
801 if (priv->changed.size)
804 if (priv->view.w == w && priv->view.h == h)
807 priv->changed.size = EINA_TRUE;
808 _ewk_tiled_backing_store_changed(priv);
811 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)
813 long cols, rows, old_rows, old_cols;
814 INF("o=%p, new size: %dx%d", priv->self, w, h);
816 cols = 1 + (int)ceil((float)w / (float)tw);
817 rows = 1 + (int)ceil((float)h / (float)th);
819 INF("o=%p new grid size cols: %ld, rows: %ld, was %ld, %ld",
820 priv->self, cols, rows, priv->view.cols, priv->view.rows);
822 if (priv->view.cols == cols && priv->view.rows == rows)
825 old_cols = priv->view.cols;
826 old_rows = priv->view.rows;
828 if (rows < old_rows) {
829 Eina_Inlist **start, **end;
830 start = priv->view.items + rows;
831 end = priv->view.items + old_rows;
832 _ewk_tiled_backing_store_view_rows_range_del(priv, start, end);
834 REALLOC_OR_OOM_RET(priv->view.items, sizeof(Eina_Inlist*) * (int)rows);
835 priv->view.rows = rows;
836 priv->view.cols = cols;
837 if (rows > old_rows) {
838 Eina_Inlist **start, **end;
839 start = priv->view.items + old_rows;
840 end = priv->view.items + rows;
841 for (; start < end; start++) {
844 r = _ewk_tiled_backing_store_view_cols_end_add
845 (priv, start, 0, cols);
847 CRITICAL("failed to allocate %ld columns", cols);
848 _ewk_tiled_backing_store_view_rows_range_del
849 (priv, priv->view.items + old_rows, start);
850 priv->view.rows = old_rows;
856 if (cols != old_cols) {
857 Eina_Inlist **start, **end;
858 int todo = cols - old_cols;
859 start = priv->view.items;
860 end = start + MIN(old_rows, rows);
862 for (; start < end; start++) {
864 r = _ewk_tiled_backing_store_view_cols_end_add
865 (priv, start, old_cols, todo);
867 CRITICAL("failed to allocate %d columns!", todo);
869 for (start--; start >= priv->view.items; start--)
870 _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo);
871 if (rows > old_rows) {
872 start = priv->view.items + old_rows;
873 end = priv->view.items + rows;
874 for (; start < end; start++)
875 _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo);
880 } else if (todo < 0) {
882 for (; start < end; start++)
883 _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo);
887 _ewk_tiled_backing_store_fill_renderers(priv);
890 static void _ewk_tiled_backing_store_smart_calculate_size(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord w, Evas_Coord h)
892 evas_object_resize(priv->base.clipper, w, h);
897 _ewk_tiled_backing_store_recalc_renderers(
898 priv, w, h, priv->view.tile.w, priv->view.tile.h);
901 // TODO: remove me later.
902 static inline void _ewk_tiled_backing_store_view_dbg(const Ewk_Tiled_Backing_Store_Data *priv)
904 Eina_Inlist **start, **end;
905 printf("tiles=%2ld,%2ld model=%2ld,%2ld [%dx%d] base=%+3ld,%+4ld offset=%+4d,%+4d old=%+4d,%+4d base=%+3d,%+3d\n",
906 priv->view.cols, priv->view.rows,
907 priv->model.cur.cols, priv->model.cur.rows,
908 priv->model.width, priv->model.height,
909 priv->model.base.col, priv->model.base.row,
910 priv->view.offset.cur.x, priv->view.offset.cur.y,
911 priv->view.offset.old.x, priv->view.offset.old.y,
912 priv->view.offset.base.x, priv->view.offset.base.y);
914 start = priv->view.items;
915 end = priv->view.items + priv->view.rows;
916 for (; start < end; start++) {
917 const Ewk_Tiled_Backing_Store_Item *it;
919 EINA_INLIST_FOREACH(*start, it) {
920 printf(" %+4d,%+4d ", it->geometry.x, it->geometry.y);
925 printf("%8p %lu,%lu;", it->tile, it->tile->col, it->tile->row);
934 * Move top row down as last.
936 * The final result is visually the same, but logically the top that
937 * went out of screen is now at bottom and filled with new model items.
939 * This is worth just when @a count is smaller than @c
940 * priv->view.rows, after that one is refilling the whole matrix so it
941 * is better to trigger full refill.
943 * @param count the number of times to repeat the process.
945 static void _ewk_tiled_backing_store_view_wrap_up(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
947 unsigned int last_row = priv->view.rows - 1;
948 Evas_Coord tw = priv->view.tile.w;
949 Evas_Coord th = priv->view.tile.h;
950 Evas_Coord off_y = priv->view.offset.base.y + count * th;
951 Evas_Coord oy = y + (last_row - count + 1) * th + off_y;
952 Eina_Inlist **itr_start, **itr_end;
954 itr_start = priv->view.items;
955 itr_end = itr_start + last_row;
957 for (; count > 0; count--) {
959 Eina_Inlist *tmp = *itr_start;
960 Ewk_Tiled_Backing_Store_Item *it;
961 Evas_Coord ox = x + priv->view.offset.base.x;
964 for (itr = itr_start; itr < itr_end; itr++)
968 priv->model.base.row++;
969 EINA_INLIST_FOREACH(tmp, it) {
970 _ewk_tiled_backing_store_item_move(it, ox, oy);
972 _ewk_tiled_backing_store_item_fill(priv, it, c, last_row);
977 priv->view.offset.base.y = off_y;
982 * Move bottom row up as first.
984 * The final result is visually the same, but logically the bottom that
985 * went out of screen is now at top and filled with new model items.
987 * This is worth just when @a count is smaller than @c
988 * priv->view.rows, after that one is refilling the whole matrix so it
989 * is better to trigger full refill.
991 * @param count the number of times to repeat the process.
993 static void _ewk_tiled_backing_store_view_wrap_down(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
995 Evas_Coord tw = priv->view.tile.w;
996 Evas_Coord th = priv->view.tile.h;
997 Evas_Coord off_y = priv->view.offset.base.y - count * th;
998 Evas_Coord oy = y + off_y + (count - 1) * th;
999 Eina_Inlist **itr_start, **itr_end;
1001 itr_start = priv->view.items + priv->view.rows - 1;
1002 itr_end = priv->view.items;
1004 for (; count > 0; count--) {
1006 Eina_Inlist *tmp = *itr_start;
1007 Ewk_Tiled_Backing_Store_Item *it;
1008 Evas_Coord ox = x + priv->view.offset.base.x;
1011 for (itr = itr_start; itr > itr_end; itr--)
1015 priv->model.base.row--;
1016 EINA_INLIST_FOREACH(tmp, it) {
1017 _ewk_tiled_backing_store_item_move(it, ox, oy);
1019 _ewk_tiled_backing_store_item_fill(priv, it, c, 0);
1024 priv->view.offset.base.y = off_y;
1029 * Move left-most (first) column right as last (right-most).
1031 * The final result is visually the same, but logically the first col that
1032 * went out of screen is now at last and filled with new model items.
1034 * This is worth just when @a count is smaller than @c
1035 * priv->view.cols, after that one is refilling the whole matrix so it
1036 * is better to trigger full refill.
1038 * @param count the number of times to repeat the process.
1040 static void _ewk_tiled_backing_store_view_wrap_left(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
1042 unsigned int r, last_col = priv->view.cols - 1;
1043 Evas_Coord tw = priv->view.tile.w;
1044 Evas_Coord th = priv->view.tile.h;
1045 Evas_Coord off_x = priv->view.offset.base.x + count * tw;
1046 Evas_Coord oy = y + priv->view.offset.base.y;
1048 Eina_Inlist **itr_end;
1050 itr = priv->view.items;
1051 itr_end = itr + priv->view.rows;
1054 priv->model.base.col += count;
1056 for (; itr < itr_end; itr++, r++) {
1057 Evas_Coord ox = x + (last_col - count + 1) * tw + off_x;
1058 unsigned int i, c = last_col - count + 1;
1060 for (i = 0; i < count; i++, c++, ox += tw) {
1061 Ewk_Tiled_Backing_Store_Item *it;
1062 it = EINA_INLIST_CONTAINER_GET(*itr, Ewk_Tiled_Backing_Store_Item);
1063 *itr = eina_inlist_demote(*itr, *itr);
1065 _ewk_tiled_backing_store_item_move(it, ox, oy);
1066 _ewk_tiled_backing_store_item_fill(priv, it, c, r);
1071 priv->view.offset.base.x = off_x;
1076 * Move right-most (last) column left as first (left-most).
1078 * The final result is visually the same, but logically the last col that
1079 * went out of screen is now at first and filled with new model items.
1081 * This is worth just when @a count is smaller than @c
1082 * priv->view.cols, after that one is refilling the whole matrix so it
1083 * is better to trigger full refill.
1085 * @param count the number of times to repeat the process.
1087 static void _ewk_tiled_backing_store_view_wrap_right(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count)
1090 Evas_Coord tw = priv->view.tile.w;
1091 Evas_Coord th = priv->view.tile.h;
1092 Evas_Coord off_x = priv->view.offset.base.x - count * tw;
1093 Evas_Coord oy = y + priv->view.offset.base.y;
1094 Eina_Inlist **itr, **itr_end;
1096 itr = priv->view.items;
1097 itr_end = itr + priv->view.rows;
1100 priv->model.base.col -= count;
1102 for (; itr < itr_end; itr++, r++) {
1103 Evas_Coord ox = x + (count - 1) * tw + off_x;
1104 unsigned int i, c = count - 1;
1106 for (i = 0; i < count; i++, c--, ox -= tw) {
1107 Ewk_Tiled_Backing_Store_Item *it;
1108 it = EINA_INLIST_CONTAINER_GET((*itr)->last, Ewk_Tiled_Backing_Store_Item);
1109 *itr = eina_inlist_promote(*itr, (*itr)->last);
1111 _ewk_tiled_backing_store_item_move(it, ox, oy);
1112 _ewk_tiled_backing_store_item_fill(priv, it, c, r);
1117 priv->view.offset.base.x = off_x;
1120 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)
1122 Eina_Inlist **itr, **itr_end;
1123 Evas_Coord base_ox, oy, tw, th;
1126 evas_object_move(priv->base.clipper, x, y);
1128 tw = priv->view.tile.w;
1129 th = priv->view.tile.h;
1131 base_ox = x + priv->view.offset.base.x;
1132 oy = y + priv->view.offset.base.y;
1134 itr = priv->view.items;
1135 itr_end = itr + priv->view.rows;
1138 priv->model.base.col -= step_x;
1139 priv->model.base.row -= step_y;
1141 for (; itr < itr_end; itr++, r++) {
1142 Ewk_Tiled_Backing_Store_Item *it;
1143 Evas_Coord ox = base_ox;
1145 EINA_INLIST_FOREACH(*itr, it) {
1146 _ewk_tiled_backing_store_item_fill(priv, it, c, r);
1147 _ewk_tiled_backing_store_item_move(it, ox, oy);
1155 static void _ewk_tiled_backing_store_view_pos_apply(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
1157 Eina_Inlist **itr, **itr_end;
1158 Evas_Coord base_ox, oy, tw, th;
1160 evas_object_move(priv->base.clipper, x, y);
1162 tw = priv->view.tile.w;
1163 th = priv->view.tile.h;
1165 base_ox = x + priv->view.offset.base.x;
1166 oy = y + priv->view.offset.base.y;
1168 itr = priv->view.items;
1169 itr_end = itr + priv->view.rows;
1170 for (; itr < itr_end; itr++) {
1171 Ewk_Tiled_Backing_Store_Item *it;
1172 Evas_Coord ox = base_ox;
1173 EINA_INLIST_FOREACH(*itr, it) {
1174 _ewk_tiled_backing_store_item_move(it, ox, oy);
1181 static void _ewk_tiled_backing_store_smart_calculate_offset_force(Ewk_Tiled_Backing_Store_Data *priv)
1183 Evas_Coord dx = priv->view.offset.cur.x - priv->view.offset.old.x;
1184 Evas_Coord dy = priv->view.offset.cur.y - priv->view.offset.old.y;
1188 INF("o=%p, offset: %+4d, %+4d (%+4d, %+4d)",
1189 priv->self, dx, dy, priv->view.offset.cur.x, priv->view.offset.cur.y);
1191 tw = priv->view.tile.w;
1192 th = priv->view.tile.h;
1194 long new_col = -priv->view.offset.cur.x / tw;
1195 step_x = priv->model.base.col - new_col;
1196 long new_row = -priv->view.offset.cur.y / th;
1197 step_y = priv->model.base.row - new_row;
1199 priv->view.offset.old.x = priv->view.offset.cur.x;
1200 priv->view.offset.old.y = priv->view.offset.cur.y;
1202 priv->contents_clipper,
1203 priv->view.offset.cur.x + priv->view.x,
1204 priv->view.offset.cur.y + priv->view.y);
1206 priv->view.offset.base.x += dx - step_x * tw;
1207 priv->view.offset.base.y += dy - step_y * th;
1209 _ewk_tiled_backing_store_view_refill
1210 (priv, priv->view.x, priv->view.y, step_x, step_y);
1213 static void _ewk_tiled_backing_store_smart_calculate_offset(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
1215 Evas_Coord dx = priv->view.offset.cur.x - priv->view.offset.old.x;
1216 Evas_Coord dy = priv->view.offset.cur.y - priv->view.offset.old.y;
1220 INF("o=%p, offset: %+4d, %+4d (%+4d, %+4d)",
1221 priv->self, dx, dy, priv->view.offset.cur.x, priv->view.offset.cur.y);
1226 tw = priv->view.tile.w;
1227 th = priv->view.tile.h;
1229 long new_col = -priv->view.offset.cur.x / tw;
1230 step_x = priv->model.base.col - new_col;
1231 long new_row = -priv->view.offset.cur.y / th;
1232 step_y = priv->model.base.row - new_row;
1234 priv->view.offset.old.x = priv->view.offset.cur.x;
1235 priv->view.offset.old.y = priv->view.offset.cur.y;
1237 priv->contents_clipper,
1238 priv->view.offset.cur.x + priv->view.x,
1239 priv->view.offset.cur.y + priv->view.y);
1241 if ((step_x < 0 && step_x <= -priv->view.cols)
1242 || (step_x > 0 && step_x >= priv->view.cols)
1243 || (step_y < 0 && step_y <= -priv->view.rows)
1244 || (step_y > 0 && step_y >= priv->view.rows)) {
1246 priv->view.offset.base.x += dx - step_x * tw;
1247 priv->view.offset.base.y += dy - step_y * th;
1249 _ewk_tiled_backing_store_view_refill
1250 (priv, priv->view.x, priv->view.y, step_x, step_y);
1254 priv->view.offset.base.x += dx;
1255 priv->view.offset.base.y += dy;
1258 _ewk_tiled_backing_store_view_wrap_up(priv, x, y, -step_y);
1259 else if (step_y > 0)
1260 _ewk_tiled_backing_store_view_wrap_down(priv, x, y, step_y);
1263 _ewk_tiled_backing_store_view_wrap_left(priv, x, y, -step_x);
1264 else if (step_x > 0)
1265 _ewk_tiled_backing_store_view_wrap_right(priv, x, y, step_x);
1267 _ewk_tiled_backing_store_view_pos_apply(priv, x, y);
1270 static void _ewk_tiled_backing_store_smart_calculate_pos(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
1272 _ewk_tiled_backing_store_view_pos_apply(priv, x, y);
1276 priv->contents_clipper,
1277 priv->view.offset.cur.x + priv->view.x,
1278 priv->view.offset.cur.y + priv->view.y);
1281 static void _ewk_tiled_backing_store_fill_renderers(Ewk_Tiled_Backing_Store_Data *priv)
1284 Ewk_Tiled_Backing_Store_Item *item;
1287 for (i = 0; i < priv->view.rows; i++) {
1288 it = priv->view.items[i];
1290 EINA_INLIST_FOREACH(it, item)
1291 _ewk_tiled_backing_store_item_fill(priv, item, j++, i);
1295 static void _ewk_tiled_backing_store_smart_calculate(Evas_Object *o)
1297 Evas_Coord x, y, w, h;
1299 evas_object_geometry_get(o, &x, &y, &w, &h);
1300 DBG("o=%p at %d,%d + %dx%d", o, x, y, w, h);
1302 PRIV_DATA_GET_OR_RETURN(o, priv);
1304 priv->changed.any = EINA_FALSE;
1306 ewk_tile_matrix_freeze(priv->model.matrix);
1308 if (!priv->render.suspend && priv->changed.model) {
1309 unsigned long cols, rows;
1311 cols = priv->model.width / priv->view.tile.w + 1;
1312 rows = priv->model.height / priv->view.tile.h + 1;
1314 priv->model.old.cols = priv->model.cur.cols;
1315 priv->model.old.rows = priv->model.cur.rows;
1316 priv->model.cur.cols = cols;
1317 priv->model.cur.rows = rows;
1318 if (priv->model.old.cols > cols)
1319 cols = priv->model.old.cols;
1320 if (priv->model.old.rows > rows)
1321 rows = priv->model.old.rows;
1322 ewk_tile_matrix_resize(priv->model.matrix, cols, rows);
1325 if (priv->changed.pos && (priv->view.x != x || priv->view.y != y)) {
1326 _ewk_tiled_backing_store_smart_calculate_pos(priv, x, y);
1327 priv->changed.pos = EINA_FALSE;
1329 if (priv->changed.offset) {
1330 _ewk_tiled_backing_store_smart_calculate_offset(priv, x, y);
1331 priv->changed.offset = EINA_FALSE;
1334 if (priv->changed.size) {
1335 _ewk_tiled_backing_store_smart_calculate_size(priv, w, h);
1336 priv->changed.size = EINA_FALSE;
1339 if (!priv->render.suspend && priv->changed.model) {
1340 Eina_Rectangle rect;
1343 rect.w = priv->model.width;
1344 rect.h = priv->model.height;
1345 _ewk_tiled_backing_store_fill_renderers(priv);
1346 ewk_tile_matrix_resize(priv->model.matrix,
1347 priv->model.cur.cols,
1348 priv->model.cur.rows);
1349 priv->changed.model = EINA_FALSE;
1350 evas_object_resize(priv->contents_clipper, priv->model.width, priv->model.height);
1351 _ewk_tiled_backing_store_smart_calculate_offset_force(priv);
1353 /* Make sure we do not miss any important repaint by
1354 * repainting the whole viewport */
1355 const Eina_Rectangle r =
1356 { 0, 0, priv->model.width, priv->model.height };
1357 ewk_tile_matrix_update(priv->model.matrix, &r,
1358 priv->view.tile.zoom);
1361 ewk_tile_matrix_thaw(priv->model.matrix);
1363 _ewk_tiled_backing_store_updates_process(priv);
1365 if (priv->view.offset.base.x > 0
1366 || priv->view.offset.base.x <= - priv->view.tile.w
1367 || priv->view.offset.base.y > 0
1368 || priv->view.offset.base.y <= - priv->view.tile.h)
1369 ERR("incorrect base offset %+4d,%+4d, tile=%dx%d, cur=%+4d,%+4d\n",
1370 priv->view.offset.base.x, priv->view.offset.base.y,
1371 priv->view.tile.w, priv->view.tile.h,
1372 priv->view.offset.cur.x, priv->view.offset.cur.y);
1376 Evas_Object *ewk_tiled_backing_store_add(Evas *e)
1378 static Evas_Smart *smart = NULL;
1380 if (_ewk_tiled_log_dom < 0)
1381 _ewk_tiled_log_dom = eina_log_domain_register("Ewk_Tiled_Backing_Store", NULL);
1384 static Evas_Smart_Class sc =
1385 EVAS_SMART_CLASS_INIT_NAME_VERSION("Ewk_Tiled_Backing_Store");
1387 evas_object_smart_clipped_smart_set(&sc);
1390 sc.add = _ewk_tiled_backing_store_smart_add;
1391 sc.del = _ewk_tiled_backing_store_smart_del;
1392 sc.resize = _ewk_tiled_backing_store_smart_resize;
1393 sc.move = _ewk_tiled_backing_store_smart_move;
1394 sc.calculate = _ewk_tiled_backing_store_smart_calculate;
1395 sc.member_add = _ewk_tiled_backing_store_smart_member_add;
1396 sc.member_del = _ewk_tiled_backing_store_smart_member_del;
1398 smart = evas_smart_class_new(&sc);
1401 return evas_object_smart_add(e, smart);
1404 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)
1406 EINA_SAFETY_ON_NULL_RETURN(cb);
1407 PRIV_DATA_GET_OR_RETURN(o, priv);
1408 priv->render.cb = cb;
1409 priv->render.data = (void*)data;
1412 Ewk_Tile_Unused_Cache *ewk_tiled_backing_store_tile_unused_cache_get(const Evas_Object *o)
1414 PRIV_DATA_GET_OR_RETURN(o, priv, NULL);
1415 return ewk_tile_matrix_unused_cache_get(priv->model.matrix);
1418 void ewk_tiled_backing_store_tile_unused_cache_set(Evas_Object *o, Ewk_Tile_Unused_Cache *tuc)
1420 PRIV_DATA_GET_OR_RETURN(o, priv);
1422 if (ewk_tile_matrix_unused_cache_get(priv->model.matrix) == tuc)
1425 _ewk_tiled_backing_store_model_matrix_create(priv, tuc);
1428 static Eina_Bool _ewk_tiled_backing_store_scroll_full_offset_set_internal(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y)
1430 /* TODO: check offset go out of bounds, clamp */
1431 if (priv->render.disabled)
1434 priv->view.offset.cur.x = x;
1435 priv->view.offset.cur.y = y;
1437 priv->changed.offset = EINA_TRUE;
1438 _ewk_tiled_backing_store_changed(priv);
1443 Eina_Bool ewk_tiled_backing_store_scroll_full_offset_set(Evas_Object *o, Evas_Coord x, Evas_Coord y)
1445 DBG("o=%p, x=%d, y=%d", o, x, y);
1447 PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
1448 if (x == priv->view.offset.cur.x && y == priv->view.offset.cur.y)
1451 return _ewk_tiled_backing_store_scroll_full_offset_set_internal(priv, x, y);
1454 Eina_Bool ewk_tiled_backing_store_scroll_full_offset_add(Evas_Object *o, Evas_Coord dx, Evas_Coord dy)
1456 DBG("o=%p, dx=%d, dy=%d", o, dx, dy);
1458 PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
1462 return _ewk_tiled_backing_store_scroll_full_offset_set_internal
1463 (priv, priv->view.offset.cur.x + dx, priv->view.offset.cur.y + dy);
1466 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)
1468 *offx = priv->view.offset.cur.x;
1469 *offy = priv->view.offset.cur.y;
1471 if (fabsf(priv->view.tile.zoom - *zoom) < ZOOM_STEP_MIN) {
1472 DBG("ignored as zoom difference is < %f: %f",
1473 (double)ZOOM_STEP_MIN, fabsf(priv->view.tile.zoom - *zoom));
1477 _ewk_tiled_backing_store_pre_render_request_flush(priv);
1480 *zoom = ROUNDED_ZOOM(priv->view.tile.w, *zoom);
1482 tw = priv->view.tile.w;
1483 th = priv->view.tile.h;
1485 float scale = *zoom / priv->view.tile.zoom;
1487 priv->view.tile.zoom = *zoom;
1488 // todo: check cx [0, w]...
1489 priv->view.offset.zoom_center.x = cx;
1490 priv->view.offset.zoom_center.y = cy;
1493 if (!priv->view.w || !priv->view.h) {
1494 priv->view.offset.base.x = 0;
1495 priv->view.offset.base.y = 0;
1498 Eina_Inlist **itr, **itr_end;
1499 Ewk_Tiled_Backing_Store_Item *it;
1501 Evas_Coord new_x = cx + (priv->view.offset.cur.x - cx) * scale;
1502 Evas_Coord new_y = cy + (priv->view.offset.cur.y - cy) * scale;
1503 Evas_Coord bx = cx + (priv->view.offset.base.x - cx) * scale;
1504 Evas_Coord by = cy + (priv->view.offset.base.y - cy) * scale;
1506 Evas_Coord model_width = priv->model.width * scale;
1507 Evas_Coord model_height = priv->model.height * scale;
1509 if (model_width < priv->view.w || new_x >= 0)
1511 else if (-new_x + priv->view.w >= model_width)
1512 new_x = -model_width + priv->view.w;
1514 if (model_height < priv->view.h || new_y >= 0)
1516 else if (-new_y + priv->view.h >= model_height)
1517 new_y = -model_height + priv->view.h;
1520 priv->model.base.col = - new_x / tw;
1522 priv->model.base.row = - new_y / th;
1524 priv->changed.size = EINA_TRUE;
1525 priv->changed.model = EINA_TRUE;
1526 _ewk_tiled_backing_store_changed(priv);
1528 priv->view.offset.cur.x = new_x;
1529 priv->view.offset.cur.y = new_y;
1530 priv->view.offset.base.x = bx;
1531 priv->view.offset.base.y = by;
1533 priv->view.offset.old.x = priv->view.offset.cur.x;
1534 priv->view.offset.old.y = priv->view.offset.cur.y;
1535 *offx = priv->view.offset.cur.x;
1536 *offy = priv->view.offset.cur.y;
1539 priv->contents_clipper,
1540 new_x + priv->view.x,
1541 new_y + priv->view.y);
1543 _ewk_tiled_backing_store_fill_renderers(priv);
1545 Evas_Coord oy = priv->view.offset.base.y + priv->view.y;
1546 Evas_Coord base_ox = priv->view.x + priv->view.offset.base.x;
1548 itr = priv->view.items;
1549 itr_end = itr + priv->view.rows;
1551 for (; itr < itr_end; itr++) {
1552 Evas_Coord ox = base_ox;
1553 Eina_Inlist *lst = *itr;
1555 EINA_INLIST_FOREACH(lst, it) {
1556 _ewk_tiled_backing_store_item_move(it, ox, oy);
1557 _ewk_tiled_backing_store_item_resize(it, tw, th);
1566 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)
1568 DBG("o=%p, zoom=%f", o, (double)*zoom);
1570 PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
1572 return _ewk_tiled_backing_store_zoom_set_internal(priv, zoom, cx, cy, offx, offy);
1575 Eina_Bool ewk_tiled_backing_store_zoom_weak_set(Evas_Object *o, float zoom, Evas_Coord cx, Evas_Coord cy)
1577 DBG("o=%p, zoom=%f", o, (double)zoom);
1578 PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
1579 if (!priv->view.w || !priv->view.h)
1581 Eina_Inlist **itr, **itr_end;
1582 Ewk_Tiled_Backing_Store_Item *it;
1584 Eina_Bool recalc = EINA_FALSE;
1586 float scale = zoom / priv->view.tile.zoom;
1588 tw = TILE_SIZE_AT_ZOOM(priv->view.tile.w, scale);
1589 scale = TILE_ZOOM_AT_SIZE(tw, priv->view.tile.w);
1590 th = TILE_SIZE_AT_ZOOM(priv->view.tile.h, scale);
1591 zoom = scale * priv->view.tile.zoom;
1593 Evas_Coord model_width = priv->model.width * scale;
1594 Evas_Coord model_height = priv->model.height * scale;
1596 evas_object_resize(priv->contents_clipper, model_width, model_height);
1598 int vrows = ceil((float)priv->view.h / (float)th) + 1;
1599 int vcols = ceil((float)priv->view.w / (float)tw) + 1;
1600 Evas_Coord new_x = cx + (priv->view.offset.cur.x - cx) * scale;
1601 Evas_Coord new_y = cy + (priv->view.offset.cur.y - cy) * scale;
1602 Evas_Coord bx = new_x % tw;
1603 Evas_Coord by = new_y % th;
1604 unsigned long base_row = -new_y / th;
1605 unsigned long base_col = -new_x / tw;
1607 if (base_row != priv->model.base.row || base_col != priv->model.base.col) {
1608 priv->model.base.row = base_row;
1609 priv->model.base.col = base_col;
1613 if (vrows > priv->view.rows || vcols > priv->view.cols)
1618 evas_object_geometry_get(o, NULL, NULL, &w, &h);
1619 _ewk_tiled_backing_store_recalc_renderers(priv, w, h, tw, th);
1620 _ewk_tiled_backing_store_fill_renderers(priv);
1621 _ewk_tiled_backing_store_updates_process(priv);
1624 Evas_Coord base_ox = bx + priv->view.x;
1625 Evas_Coord oy = by + priv->view.y;
1627 evas_object_move(priv->contents_clipper,
1628 new_x + priv->view.x,
1629 new_y + priv->view.y);
1631 itr = priv->view.items;
1632 itr_end = itr + priv->view.rows;
1634 for (; itr < itr_end; itr++) {
1635 Evas_Coord ox = base_ox;
1636 Eina_Inlist *lst = *itr;
1638 EINA_INLIST_FOREACH(lst, it) {
1639 _ewk_tiled_backing_store_item_move(it, ox, oy);
1640 _ewk_tiled_backing_store_item_resize(it, tw, th);
1649 void ewk_tiled_backing_store_fix_offsets(Evas_Object *o, Evas_Coord w, Evas_Coord h)
1651 PRIV_DATA_GET_OR_RETURN(o, priv);
1652 Eina_Inlist **itr, **itr_end;
1653 Ewk_Tiled_Backing_Store_Item *it;
1654 Evas_Coord new_x = priv->view.offset.cur.x;
1655 Evas_Coord new_y = priv->view.offset.cur.y;
1656 Evas_Coord bx = priv->view.offset.base.x;
1657 Evas_Coord by = priv->view.offset.base.y;
1658 Evas_Coord tw = priv->view.tile.w;
1659 Evas_Coord th = priv->view.tile.h;
1664 priv->model.base.col = -new_x / tw;
1670 priv->model.base.row = -new_y / th;
1673 if (bx >= 0 || bx <= -2 * priv->view.tile.w) {
1675 priv->model.base.col = -new_x / tw;
1678 if (by >= 0 || by <= -2 * priv->view.tile.h) {
1680 priv->model.base.row = -new_y / th;
1683 priv->view.offset.cur.x = new_x;
1684 priv->view.offset.cur.y = new_y;
1685 priv->view.offset.old.x = new_x;
1686 priv->view.offset.old.y = new_y;
1687 priv->view.offset.base.x = bx;
1688 priv->view.offset.base.y = by;
1689 evas_object_move(priv->contents_clipper,
1690 new_x + priv->view.x,
1691 new_y + priv->view.y);
1693 Evas_Coord oy = priv->view.offset.base.y + priv->view.y;
1694 Evas_Coord base_ox = priv->view.x + priv->view.offset.base.x;
1696 itr = priv->view.items;
1697 itr_end = itr + priv->view.rows;
1699 for (; itr < itr_end; itr++) {
1700 Evas_Coord ox = base_ox;
1701 Eina_Inlist *lst = *itr;
1703 EINA_INLIST_FOREACH(lst, it) {
1704 _ewk_tiled_backing_store_item_move(it, ox, oy);
1705 _ewk_tiled_backing_store_item_resize(it, tw, th);
1712 void ewk_tiled_backing_store_zoom_weak_smooth_scale_set(Evas_Object *o, Eina_Bool smooth_scale)
1714 PRIV_DATA_GET_OR_RETURN(o, priv);
1715 Eina_Inlist **itr, **itr_end;
1717 itr = priv->view.items;
1718 itr_end = itr + priv->view.rows;
1719 priv->view.tile.zoom_weak_smooth_scale = smooth_scale;
1721 for (; itr< itr_end; itr++) {
1722 Ewk_Tiled_Backing_Store_Item *it;
1723 EINA_INLIST_FOREACH(*itr, it)
1725 _ewk_tiled_backing_store_item_smooth_scale_set
1730 Eina_Bool ewk_tiled_backing_store_update(Evas_Object *o, const Eina_Rectangle *update)
1732 PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
1734 if (priv->render.disabled)
1737 return ewk_tile_matrix_update(priv->model.matrix, update,
1738 priv->view.tile.zoom);
1741 void ewk_tiled_backing_store_updates_process_pre_set(Evas_Object *o, void *(*cb)(void *data, Evas_Object *o), const void *data)
1743 PRIV_DATA_GET_OR_RETURN(o, priv);
1744 priv->process.pre_cb = cb;
1745 priv->process.pre_data = (void*)data;
1748 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)
1750 PRIV_DATA_GET_OR_RETURN(o, priv);
1751 priv->process.post_cb = cb;
1752 priv->process.post_data = (void*)data;
1755 void ewk_tiled_backing_store_updates_process(Evas_Object *o)
1757 PRIV_DATA_GET_OR_RETURN(o, priv);
1758 _ewk_tiled_backing_store_updates_process(priv);
1761 void ewk_tiled_backing_store_updates_clear(Evas_Object *o)
1763 PRIV_DATA_GET_OR_RETURN(o, priv);
1765 ewk_tile_matrix_updates_clear(priv->model.matrix);
1768 void ewk_tiled_backing_store_contents_resize(Evas_Object *o, Evas_Coord width, Evas_Coord height)
1770 PRIV_DATA_GET_OR_RETURN(o, priv);
1772 if (width == priv->model.width && height == priv->model.height)
1775 priv->model.width = width;
1776 priv->model.height = height;
1777 priv->changed.model = EINA_TRUE;
1779 DBG("width,height=%d, %d", width, height);
1780 _ewk_tiled_backing_store_changed(priv);
1783 void ewk_tiled_backing_store_disabled_update_set(Evas_Object *o, Eina_Bool value)
1785 PRIV_DATA_GET_OR_RETURN(o, priv);
1787 if (value != priv->render.disabled)
1788 priv->render.disabled = value;
1791 void ewk_tiled_backing_store_flush(Evas_Object *o)
1793 PRIV_DATA_GET_OR_RETURN(o, priv);
1794 Ewk_Tile_Unused_Cache *tuc = NULL;
1796 priv->view.offset.cur.x = 0;
1797 priv->view.offset.cur.y = 0;
1798 priv->view.offset.old.x = 0;
1799 priv->view.offset.old.y = 0;
1800 priv->view.offset.base.x = 0;
1801 priv->view.offset.base.y = 0;
1802 priv->model.base.col = 0;
1803 priv->model.base.row = 0;
1804 priv->model.cur.cols = 1;
1805 priv->model.cur.rows = 1;
1806 priv->model.old.cols = 0;
1807 priv->model.old.rows = 0;
1808 priv->changed.size = EINA_TRUE;
1810 #ifdef DEBUG_MEM_LEAKS
1811 printf("\nFLUSHED BACKING STORE, STATUS BEFORE DELETING TILE MATRIX:\n");
1812 _ewk_tiled_backing_store_mem_dbg(priv);
1815 _ewk_tiled_backing_store_pre_render_request_flush(priv);
1816 _ewk_tiled_backing_store_tile_dissociate_all(priv);
1817 tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
1818 ewk_tile_unused_cache_clear(tuc);
1820 #ifdef DEBUG_MEM_LEAKS
1821 printf("\nFLUSHED BACKING STORE, STATUS AFTER RECREATING TILE MATRIX:\n");
1822 _ewk_tiled_backing_store_mem_dbg(priv);
1826 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)
1828 PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
1829 Eina_Tile_Grid_Slicer slicer;
1830 const Eina_Tile_Grid_Info *info;
1832 Ewk_Tile_Unused_Cache *tuc;
1834 tw = priv->view.tile.w;
1835 th = priv->view.tile.h;
1836 zoom = ROUNDED_ZOOM(priv->view.tile.w, zoom);
1838 if (!eina_tile_grid_slicer_setup(&slicer, x, y, w, h, tw, th)) {
1839 ERR("could not setup grid slicer for %d,%d+%dx%d tile=%dx%d",
1840 x, y, w, h, tw, th);
1844 while (eina_tile_grid_slicer_next(&slicer, &info)) {
1845 const unsigned long c = info->col;
1846 const unsigned long r = info->row;
1847 if (!_ewk_tiled_backing_store_pre_render_request_add(priv, c, r, zoom, PRE_RENDER_PRIORITY_LOW))
1851 _ewk_tiled_backing_store_item_process_idler_start(priv);
1853 tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
1854 ewk_tile_unused_cache_lock_area(tuc, x, y, w, h, zoom);
1858 Eina_Bool ewk_tiled_backing_store_pre_render_relative_radius(Evas_Object *o, unsigned int n, float zoom)
1860 PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
1861 unsigned long start_row, end_row, start_col, end_col, i, j, w, h;
1862 Ewk_Tile_Unused_Cache *tuc;
1864 INF("priv->model.base.row =%ld, n=%u priv->view.rows=%lu",
1865 priv->model.base.row, n, priv->view.rows);
1866 start_row = (long)priv->model.base.row - n;
1867 start_col = (long)priv->model.base.col - n;
1868 end_row = MIN(priv->model.cur.rows - 1,
1869 priv->model.base.row + priv->view.rows + n - 1);
1870 end_col = MIN(priv->model.cur.cols - 1,
1871 priv->model.base.col + priv->view.cols + n - 1);
1873 INF("start_row=%lu, end_row=%lu, start_col=%lu, end_col=%lu",
1874 start_row, end_row, start_col, end_col);
1876 zoom = ROUNDED_ZOOM(priv->view.tile.w, zoom);
1878 for (i = start_row; i <= end_row; i++)
1879 for (j = start_col; j <= end_col; j++)
1880 if (!_ewk_tiled_backing_store_pre_render_request_add(priv, j, i, zoom, PRE_RENDER_PRIORITY_LOW))
1881 goto start_processing;
1884 _ewk_tiled_backing_store_item_process_idler_start(priv);
1886 tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
1887 h = (end_row - start_row + 1) * TILE_SIZE_AT_ZOOM(priv->view.tile.h, zoom);
1888 w = (end_col - start_col + 1) * TILE_SIZE_AT_ZOOM(priv->view.tile.w, zoom);
1889 ewk_tile_unused_cache_lock_area(tuc,
1890 start_col * TILE_SIZE_AT_ZOOM(priv->view.tile.w, zoom),
1891 start_row * TILE_SIZE_AT_ZOOM(priv->view.tile.h, zoom), w, h, zoom);
1896 void ewk_tiled_backing_store_pre_render_cancel(Evas_Object *o)
1898 PRIV_DATA_GET_OR_RETURN(o, priv);
1899 Ewk_Tile_Unused_Cache *tuc;
1901 _ewk_tiled_backing_store_pre_render_request_clear(priv);
1903 tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix);
1904 ewk_tile_unused_cache_unlock_area(tuc);
1907 Eina_Bool ewk_tiled_backing_store_disable_render(Evas_Object *o)
1909 PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
1910 return _ewk_tiled_backing_store_disable_render(priv);
1913 Eina_Bool ewk_tiled_backing_store_enable_render(Evas_Object *o)
1915 PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE);
1916 _ewk_tiled_backing_store_changed(priv);
1917 return _ewk_tiled_backing_store_enable_render(priv);