| /* |
| Copyright (C) 2009-2010 Samsung Electronics |
| Copyright (C) 2009-2010 ProFUSION embedded systems |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Library General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public License |
| along with this library; see the file COPYING.LIB. If not, write to |
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "ewk_tiled_backing_store.h" |
| |
| #define _GNU_SOURCE |
| #include "ewk_tiled_private.h" |
| #include <Ecore.h> |
| #include <Eina.h> |
| #include <errno.h> |
| #include <math.h> |
| #include <stdio.h> // XXX REMOVE ME LATER |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #define IDX(col, row, rowspan) (col + (row * rowspan)) |
| |
| #if !defined(MIN) |
| # define MIN(a, b) ((a < b) ? a : b) |
| #endif |
| |
| #if !defined(MAX) |
| # define MAX(a, b) ((a > b) ? a : b) |
| #endif |
| |
| typedef enum _Ewk_Tiled_Backing_Store_Pre_Render_Priority Ewk_Tiled_Backing_Store_Pre_Render_Priority; |
| typedef struct _Ewk_Tiled_Backing_Store_Data Ewk_Tiled_Backing_Store_Data; |
| typedef struct _Ewk_Tiled_Backing_Store_Item Ewk_Tiled_Backing_Store_Item; |
| typedef struct _Ewk_Tiled_Backing_Store_Pre_Render_Request Ewk_Tiled_Backing_Store_Pre_Render_Request; |
| |
| enum _Ewk_Tiled_Backing_Store_Pre_Render_Priority { |
| PRE_RENDER_PRIORITY_LOW = 0, /**< Append the request to the list */ |
| PRE_RENDER_PRIORITY_HIGH /**< Prepend the request to the list */ |
| }; |
| |
| struct _Ewk_Tiled_Backing_Store_Item { |
| EINA_INLIST; |
| Ewk_Tile *tile; |
| struct { |
| Evas_Coord x, y, w, h; |
| } geometry; |
| struct { |
| Eina_List *process; |
| unsigned long row, col; |
| float zoom; |
| } update; |
| Eina_Bool smooth_scale; |
| }; |
| |
| struct _Ewk_Tiled_Backing_Store_Pre_Render_Request { |
| EINA_INLIST; |
| unsigned long col, row; |
| float zoom; |
| }; |
| |
| struct _Ewk_Tiled_Backing_Store_Data { |
| Evas_Object_Smart_Clipped_Data base; |
| Evas_Object *self; |
| Evas_Object *contents_clipper; |
| struct { |
| Eina_Inlist **items; |
| Evas_Coord x, y, w, h; |
| long cols, rows; |
| struct { |
| Evas_Coord w, h; |
| float zoom; |
| Eina_Bool zoom_weak_smooth_scale:1; |
| } tile; |
| struct { |
| struct { |
| Evas_Coord x, y; |
| } cur, old, base, zoom_center; |
| } offset; |
| } view; |
| Evas_Colorspace cspace; |
| struct { |
| Ewk_Tile_Matrix *matrix; |
| struct { |
| unsigned long col, row; |
| } base; |
| struct { |
| unsigned long cols, rows; |
| } cur, old; |
| Evas_Coord width, height; |
| } model; |
| struct { |
| Eina_Bool (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *area); |
| void *data; |
| Eina_List *queue; |
| Eina_Bool process_entire_queue; |
| Eina_Inlist *pre_render_requests; |
| Ecore_Idler *idler; |
| Eina_Bool disabled; |
| Eina_Bool suspend:1; |
| } render; |
| struct { |
| void *(*pre_cb)(void *data, Evas_Object *o); |
| void *pre_data; |
| void *(*post_cb)(void *data, void *pre_data, Evas_Object *o); |
| void *post_data; |
| } process; |
| struct { |
| Eina_Bool any:1; |
| Eina_Bool pos:1; |
| Eina_Bool size:1; |
| Eina_Bool model:1; |
| Eina_Bool offset:1; |
| } changed; |
| #ifdef DEBUG_MEM_LEAKS |
| Ecore_Event_Handler *sig_usr; |
| #endif |
| }; |
| |
| static Evas_Smart_Class _parent_sc = EVAS_SMART_CLASS_INIT_NULL; |
| int _ewk_tiled_log_dom = -1; |
| |
| #define PRIV_DATA_GET_OR_RETURN(obj, ptr, ...) \ |
| Ewk_Tiled_Backing_Store_Data *ptr = evas_object_smart_data_get(obj); \ |
| if (!ptr) { \ |
| CRITICAL("no private data in obj=%p", obj); \ |
| return __VA_ARGS__; \ |
| } |
| |
| static inline void _ewk_tiled_backing_store_item_request_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it); |
| 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); |
| static void _ewk_tiled_backing_store_fill_renderers(Ewk_Tiled_Backing_Store_Data *priv); |
| static inline void _ewk_tiled_backing_store_view_dbg(const Ewk_Tiled_Backing_Store_Data *priv); |
| static inline void _ewk_tiled_backing_store_changed(Ewk_Tiled_Backing_Store_Data *priv); |
| |
| static inline void _ewk_tiled_backing_store_updates_process(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| void *data = NULL; |
| |
| /* Do not process updates. Note that we still want to get updates requests |
| * in the queue in order to not miss any updates after the render is |
| * resumed. |
| */ |
| if (priv->render.suspend || !evas_object_visible_get(priv->self)) |
| return; |
| |
| if (priv->process.pre_cb) |
| data = priv->process.pre_cb(priv->process.pre_data, priv->self); |
| |
| ewk_tile_matrix_updates_process(priv->model.matrix); |
| |
| if (priv->process.post_cb) |
| priv->process.post_cb(priv->process.post_data, data, priv->self); |
| } |
| |
| static int _ewk_tiled_backing_store_flush(void *data) |
| { |
| Ewk_Tiled_Backing_Store_Data *priv = data; |
| Ewk_Tile_Unused_Cache *tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix); |
| |
| if (tuc) { |
| DBG("flush unused tile cache."); |
| ewk_tile_unused_cache_auto_flush(tuc); |
| } else |
| ERR("no cache?!"); |
| |
| return 0; |
| } |
| |
| static Ewk_Tile *_ewk_tiled_backing_store_tile_new(Ewk_Tiled_Backing_Store_Data *priv, unsigned long col, unsigned long row, float zoom) |
| { |
| Ewk_Tile *t; |
| Evas *evas = evas_object_evas_get(priv->self); |
| if (!evas) { |
| CRITICAL("evas_object_evas_get failed!"); |
| return NULL; |
| } |
| |
| t = ewk_tile_matrix_tile_new |
| (priv->model.matrix, evas, col, row, zoom); |
| |
| if (!t) { |
| CRITICAL("ewk_tile_matrix_tile_new failed!"); |
| return NULL; |
| } |
| |
| return t; |
| } |
| |
| static void _ewk_tiled_backing_store_item_move(Ewk_Tiled_Backing_Store_Item *it, Evas_Coord x, Evas_Coord y) |
| { |
| it->geometry.x = x; |
| it->geometry.y = y; |
| |
| if (it->tile) |
| evas_object_move(it->tile->image, x, y); |
| } |
| |
| static void _ewk_tiled_backing_store_item_resize(Ewk_Tiled_Backing_Store_Item *it, Evas_Coord w, Evas_Coord h) |
| { |
| it->geometry.w = w; |
| it->geometry.h = h; |
| |
| if (it->tile) { |
| evas_object_resize(it->tile->image, w, h); |
| evas_object_image_fill_set(it->tile->image, 0, 0, w, h); |
| } |
| } |
| |
| static void _ewk_tiled_backing_store_tile_associate(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tile *t, Ewk_Tiled_Backing_Store_Item *it) |
| { |
| if (it->tile) |
| CRITICAL("it->tile=%p, but it should be NULL!", it->tile); |
| it->tile = t; |
| evas_object_move(it->tile->image, it->geometry.x, it->geometry.y); |
| evas_object_resize(it->tile->image, it->geometry.w, it->geometry.h); |
| evas_object_image_fill_set |
| (it->tile->image, 0, 0, it->geometry.w, it->geometry.h); |
| evas_object_image_smooth_scale_set(it->tile->image, it->smooth_scale); |
| |
| if (!ewk_tile_visible_get(t)) |
| evas_object_smart_member_add(t->image, priv->self); |
| |
| ewk_tile_show(t); |
| } |
| |
| static void _ewk_tiled_backing_store_tile_dissociate(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it, double last_used) |
| { |
| Ewk_Tile_Unused_Cache *tuc; |
| ewk_tile_hide(it->tile); |
| if (!ewk_tile_visible_get(it->tile)) |
| evas_object_smart_member_del(it->tile->image); |
| ewk_tile_matrix_tile_put(priv->model.matrix, it->tile, last_used); |
| tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix); |
| ewk_tile_unused_cache_auto_flush(tuc); |
| |
| it->tile = NULL; |
| } |
| |
| static void _ewk_tiled_backing_store_tile_dissociate_all(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| Eina_Inlist *it; |
| Ewk_Tiled_Backing_Store_Item *item; |
| int i; |
| double last_used = ecore_loop_time_get(); |
| |
| for (i = 0; i < priv->view.rows; i++) { |
| it = priv->view.items[i]; |
| EINA_INLIST_FOREACH(it, item) |
| if (item->tile) |
| _ewk_tiled_backing_store_tile_dissociate(priv, item, last_used); |
| } |
| } |
| |
| 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) |
| { |
| Ewk_Tiled_Backing_Store_Pre_Render_Request *r; |
| |
| MALLOC_OR_OOM_RET(r, sizeof(*r), EINA_FALSE); |
| |
| if (priority == PRE_RENDER_PRIORITY_HIGH) |
| priv->render.pre_render_requests = eina_inlist_prepend |
| (priv->render.pre_render_requests, EINA_INLIST_GET(r)); |
| else |
| priv->render.pre_render_requests = eina_inlist_append |
| (priv->render.pre_render_requests, EINA_INLIST_GET(r)); |
| |
| r->col = col; |
| r->row = row; |
| r->zoom = zoom; |
| |
| return EINA_TRUE; |
| } |
| |
| 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) |
| { |
| priv->render.pre_render_requests = eina_inlist_remove |
| (priv->render.pre_render_requests, EINA_INLIST_GET(r)); |
| free(r); |
| } |
| |
| static inline Ewk_Tiled_Backing_Store_Pre_Render_Request *_ewk_tiled_backing_store_pre_render_request_first(const Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| return EINA_INLIST_CONTAINER_GET( |
| priv->render.pre_render_requests, |
| Ewk_Tiled_Backing_Store_Pre_Render_Request); |
| } |
| |
| static void _ewk_tiled_backing_store_pre_render_request_flush(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| Eina_Inlist **pl = &priv->render.pre_render_requests; |
| while (*pl) { |
| Ewk_Tiled_Backing_Store_Pre_Render_Request *r; |
| r = _ewk_tiled_backing_store_pre_render_request_first(priv); |
| *pl = eina_inlist_remove(*pl, *pl); |
| free(r); |
| } |
| } |
| |
| static void _ewk_tiled_backing_store_pre_render_request_clear(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| Eina_Inlist **pl = &priv->render.pre_render_requests; |
| Eina_Inlist *iter = *pl, *tmp; |
| while (iter) { |
| Ewk_Tiled_Backing_Store_Pre_Render_Request *r = |
| EINA_INLIST_CONTAINER_GET( |
| iter, Ewk_Tiled_Backing_Store_Pre_Render_Request); |
| tmp = iter->next; |
| *pl = eina_inlist_remove(*pl, iter); |
| iter = tmp; |
| free(r); |
| } |
| } |
| |
| /* assumes priv->process.pre_cb was called if required! */ |
| static void _ewk_tiled_backing_store_pre_render_request_process_single(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| Ewk_Tiled_Backing_Store_Pre_Render_Request *req; |
| Eina_Rectangle area; |
| Ewk_Tile_Matrix *tm = priv->model.matrix; |
| Ewk_Tile *t; |
| Ewk_Tile_Unused_Cache *tuc; |
| unsigned long col, row; |
| float zoom; |
| double last_used = ecore_loop_time_get(); |
| |
| req = _ewk_tiled_backing_store_pre_render_request_first(priv); |
| if (!req) |
| return; |
| |
| col = req->col; |
| row = req->row; |
| zoom = req->zoom; |
| |
| if (ewk_tile_matrix_tile_exact_exists(tm, col, row, zoom)) { |
| DBG("no pre-render required for tile %lu,%lu @ %f.", col, row, zoom); |
| goto end; |
| } |
| |
| t = _ewk_tiled_backing_store_tile_new(priv, col, row, zoom); |
| if (!t) |
| goto end; |
| |
| area.x = 0; |
| area.y = 0; |
| area.w = priv->view.tile.w; |
| area.h = priv->view.tile.h; |
| |
| priv->render.cb(priv->render.data, t, &area); |
| evas_object_image_data_update_add( |
| t->image, |
| area.x, area.y, area.w, area.h); |
| ewk_tile_matrix_tile_updates_clear(tm, t); |
| |
| ewk_tile_matrix_tile_put(tm, t, last_used); |
| |
| end: |
| _ewk_tiled_backing_store_pre_render_request_del(priv, req); |
| tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix); |
| ewk_tile_unused_cache_auto_flush(tuc); |
| } |
| |
| static Eina_Bool _ewk_tiled_backing_store_item_process_idler_cb(void *data) |
| { |
| Ewk_Tiled_Backing_Store_Data *priv = data; |
| Ewk_Tiled_Backing_Store_Item *it = NULL; |
| |
| while (priv->render.queue) { |
| it = priv->render.queue->data; |
| if (it->tile->zoom == priv->view.tile.zoom) { |
| _ewk_tiled_backing_store_item_request_del(priv, it); |
| it = NULL; |
| } else { |
| unsigned long row, col; |
| float zoom; |
| Ewk_Tile *t; |
| if (it->tile) { |
| double last_used = ecore_loop_time_get(); |
| _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used); |
| } |
| |
| row = it->update.row; |
| col = it->update.col; |
| zoom = it->update.zoom; |
| t = _ewk_tiled_backing_store_tile_new(priv, col, row, zoom); |
| if (!t) { |
| priv->render.idler = NULL; |
| return EINA_FALSE; |
| } |
| |
| _ewk_tiled_backing_store_tile_associate(priv, t, it); |
| it->update.process = NULL; |
| priv->render.queue = eina_list_remove_list(priv->render.queue, |
| priv->render.queue); |
| if (!priv->render.process_entire_queue) |
| break; |
| } |
| } |
| |
| if (priv->process.pre_cb) |
| data = priv->process.pre_cb(priv->process.pre_data, priv->self); |
| |
| ewk_tile_matrix_updates_process(priv->model.matrix); |
| |
| if (!it) |
| _ewk_tiled_backing_store_pre_render_request_process_single(priv); |
| |
| if (priv->process.post_cb) |
| priv->process.post_cb(priv->process.post_data, data, priv->self); |
| |
| if (!priv->render.queue && !priv->render.pre_render_requests) { |
| priv->render.idler = NULL; |
| return EINA_FALSE; |
| } |
| |
| return EINA_TRUE; |
| } |
| |
| static inline void _ewk_tiled_backing_store_item_process_idler_stop(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| if (!priv->render.idler) |
| return; |
| |
| ecore_idler_del(priv->render.idler); |
| priv->render.idler = NULL; |
| } |
| |
| static inline void _ewk_tiled_backing_store_item_process_idler_start(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| if (priv->render.idler) |
| return; |
| priv->render.idler = ecore_idler_add( |
| _ewk_tiled_backing_store_item_process_idler_cb, priv); |
| } |
| |
| static inline void _ewk_tiled_backing_store_item_request_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it) |
| { |
| priv->render.queue = eina_list_remove_list(priv->render.queue, |
| it->update.process); |
| it->update.process = NULL; |
| } |
| |
| 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) |
| { |
| if (it->update.process) |
| return; |
| |
| it->update.col = m_col; |
| it->update.row = m_row; |
| it->update.zoom = zoom; |
| |
| priv->render.queue = eina_list_append(priv->render.queue, it); |
| it->update.process = eina_list_last(priv->render.queue); |
| |
| if (!priv->render.suspend) |
| _ewk_tiled_backing_store_item_process_idler_start(priv); |
| } |
| |
| static Eina_Bool _ewk_tiled_backing_store_disable_render(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| if (priv->render.suspend) |
| return EINA_TRUE; |
| |
| priv->render.suspend = EINA_TRUE; |
| _ewk_tiled_backing_store_item_process_idler_stop(priv); |
| return EINA_TRUE; |
| } |
| |
| static Eina_Bool _ewk_tiled_backing_store_enable_render(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| if (!priv->render.suspend) |
| return EINA_TRUE; |
| |
| priv->render.suspend = EINA_FALSE; |
| |
| _ewk_tiled_backing_store_fill_renderers(priv); |
| _ewk_tiled_backing_store_item_process_idler_start(priv); |
| |
| return EINA_TRUE; |
| } |
| |
| 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) |
| { |
| long m_col = priv->model.base.col + col; |
| long m_row = priv->model.base.row + row; |
| double last_used = ecore_loop_time_get(); |
| |
| if (m_col < 0 || m_row < 0 |
| || (unsigned long)(m_col) >= priv->model.cur.cols |
| || (unsigned long)(m_row) >= priv->model.cur.rows) { |
| |
| if (it->tile) { |
| _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used); |
| if (it->update.process) |
| _ewk_tiled_backing_store_item_request_del(priv, it); |
| } |
| } else { |
| Ewk_Tile *t; |
| const float zoom = priv->view.tile.zoom; |
| |
| if (it->update.process) { |
| if (it->update.row == (unsigned long)(m_row) |
| && it->update.col == (unsigned long)(m_col) |
| && it->update.zoom == zoom) |
| return EINA_TRUE; |
| |
| _ewk_tiled_backing_store_item_request_del(priv, it); |
| } |
| |
| if (it->tile) { |
| Ewk_Tile *old = it->tile; |
| if (old->row != (unsigned long)(m_row) |
| || old->col != (unsigned long)(m_col) |
| || old->zoom != zoom) { |
| _ewk_tiled_backing_store_tile_dissociate(priv, it, |
| last_used); |
| if (it->update.process) |
| _ewk_tiled_backing_store_item_request_del(priv, it); |
| } else if (old->row == (unsigned long)(m_row) |
| && old->col == (unsigned long)(m_col) |
| && old->zoom == zoom) |
| goto end; |
| } |
| |
| t = ewk_tile_matrix_tile_exact_get |
| (priv->model.matrix, m_col, m_row, zoom); |
| if (!t) { |
| /* NOTE: it never returns NULL if it->tile was set! */ |
| if (it->tile) { |
| CRITICAL("it->tile=%p, but it should be NULL!", it->tile); |
| _ewk_tiled_backing_store_tile_dissociate(priv, it, |
| last_used); |
| } |
| |
| /* Do not add new requests to the render queue */ |
| if (!priv->render.suspend) { |
| t = _ewk_tiled_backing_store_tile_new(priv, m_col, m_row, zoom); |
| if (!t) |
| return EINA_FALSE; |
| _ewk_tiled_backing_store_tile_associate(priv, t, it); |
| } |
| } else if (t != it->tile) { |
| if (!it->update.process) { |
| if (it->tile) |
| _ewk_tiled_backing_store_tile_dissociate(priv, |
| it, last_used); |
| _ewk_tiled_backing_store_tile_associate(priv, t, it); |
| } |
| } |
| |
| end: |
| |
| return EINA_TRUE; |
| } |
| |
| return EINA_TRUE; |
| } |
| |
| static Ewk_Tiled_Backing_Store_Item *_ewk_tiled_backing_store_item_add(Ewk_Tiled_Backing_Store_Data *priv, long col, int row) |
| { |
| Ewk_Tiled_Backing_Store_Item *it; |
| Evas_Coord x, y, tw, th; |
| |
| DBG("o=%p", priv->self); |
| |
| MALLOC_OR_OOM_RET(it, sizeof(*it), NULL); |
| |
| tw = priv->view.tile.w; |
| th = priv->view.tile.h; |
| x = priv->view.offset.base.x + priv->view.x + tw *col; |
| y = priv->view.offset.base.y + priv->view.y + th *row; |
| |
| it->tile = NULL; |
| it->update.process = NULL; |
| it->smooth_scale = priv->view.tile.zoom_weak_smooth_scale; |
| _ewk_tiled_backing_store_item_move(it, x, y); |
| _ewk_tiled_backing_store_item_resize(it, tw, th); |
| if (!_ewk_tiled_backing_store_item_fill(priv, it, col, row)) { |
| free(it); |
| return NULL; |
| } |
| |
| return it; |
| } |
| |
| static void _ewk_tiled_backing_store_item_del(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tiled_Backing_Store_Item *it) |
| { |
| if (it->tile) { |
| double last_used = ecore_loop_time_get(); |
| _ewk_tiled_backing_store_tile_dissociate(priv, it, last_used); |
| } |
| if (it->update.process) |
| _ewk_tiled_backing_store_item_request_del(priv, it); |
| free(it); |
| } |
| |
| static void _ewk_tiled_backing_store_item_smooth_scale_set(Ewk_Tiled_Backing_Store_Item *it, Eina_Bool smooth_scale) |
| { |
| if (it->smooth_scale == smooth_scale) |
| return; |
| |
| if (it->tile) |
| evas_object_image_smooth_scale_set(it->tile->image, smooth_scale); |
| } |
| |
| static inline void _ewk_tiled_backing_store_changed(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| if (priv->changed.any) |
| return; |
| evas_object_smart_changed(priv->self); |
| priv->changed.any = EINA_TRUE; |
| } |
| |
| static void _ewk_tiled_backing_store_view_cols_end_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist **p_row, unsigned int count) |
| { |
| Eina_Inlist *n; |
| unsigned int i; |
| |
| if (!count) |
| return; |
| |
| n = (*p_row)->last; |
| |
| for (i = 0; i < count; i++) { |
| Ewk_Tiled_Backing_Store_Item *it; |
| it = EINA_INLIST_CONTAINER_GET(n, Ewk_Tiled_Backing_Store_Item); |
| n = n->prev; |
| *p_row = eina_inlist_remove(*p_row, EINA_INLIST_GET(it)); |
| _ewk_tiled_backing_store_item_del(priv, it); |
| } |
| } |
| |
| 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) |
| { |
| unsigned int i, r = p_row - priv->view.items; |
| |
| for (i = 0; i < count; i++, base_col++) { |
| Ewk_Tiled_Backing_Store_Item *it; |
| |
| it = _ewk_tiled_backing_store_item_add(priv, base_col, r); |
| if (!it) { |
| CRITICAL("failed to add column %u of %u in row %u.", i, count, r); |
| _ewk_tiled_backing_store_view_cols_end_del(priv, p_row, i); |
| return EINA_FALSE; |
| } |
| |
| *p_row = eina_inlist_append(*p_row, EINA_INLIST_GET(it)); |
| } |
| return EINA_TRUE; |
| } |
| |
| static void _ewk_tiled_backing_store_view_row_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist *row) |
| { |
| while (row) { |
| Ewk_Tiled_Backing_Store_Item *it; |
| it = EINA_INLIST_CONTAINER_GET(row, Ewk_Tiled_Backing_Store_Item); |
| row = row->next; |
| _ewk_tiled_backing_store_item_del(priv, it); |
| } |
| } |
| |
| static void _ewk_tiled_backing_store_view_rows_range_del(Ewk_Tiled_Backing_Store_Data *priv, Eina_Inlist **start, Eina_Inlist **end) |
| { |
| for (; start < end; start++) { |
| _ewk_tiled_backing_store_view_row_del(priv, *start); |
| *start = NULL; |
| } |
| } |
| |
| static void _ewk_tiled_backing_store_view_rows_all_del(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| Eina_Inlist **start; |
| Eina_Inlist **end; |
| |
| start = priv->view.items; |
| end = priv->view.items + priv->view.rows; |
| _ewk_tiled_backing_store_view_rows_range_del(priv, start, end); |
| |
| free(priv->view.items); |
| priv->view.items = NULL; |
| priv->view.cols = 0; |
| priv->view.rows = 0; |
| } |
| |
| static void _ewk_tiled_backing_store_render(void *data, Ewk_Tile *t, const Eina_Rectangle *area) |
| { |
| Ewk_Tiled_Backing_Store_Data *priv = data; |
| |
| INF("TODO %p (visible? %d) [%lu,%lu] %d,%d + %dx%d", |
| t, t->visible, t->col, t->row, area->x, area->y, area->w, area->h); |
| |
| if (!t->visible) |
| return; |
| |
| if (priv->view.tile.w != t->w || priv->view.tile.h != t->h) |
| return; // todo: remove me later, don't even flag as dirty! |
| |
| EINA_SAFETY_ON_NULL_RETURN(priv->render.cb); |
| if (!priv->render.cb(priv->render.data, t, area)) |
| return; |
| |
| evas_object_image_data_update_add(t->image, area->x, area->y, area->w, area->h); |
| } |
| |
| static inline void _ewk_tiled_backing_store_model_matrix_create(Ewk_Tiled_Backing_Store_Data *priv, Ewk_Tile_Unused_Cache *tuc) |
| { |
| if (priv->model.matrix) { |
| _ewk_tiled_backing_store_view_rows_all_del(priv); |
| |
| priv->changed.offset = EINA_FALSE; |
| priv->changed.size = EINA_TRUE; |
| |
| ewk_tile_matrix_free(priv->model.matrix); |
| } |
| |
| priv->model.matrix = ewk_tile_matrix_new |
| (tuc, priv->model.cur.cols, priv->model.cur.rows, priv->cspace, |
| _ewk_tiled_backing_store_render, priv); |
| } |
| |
| static void _ewk_tiled_backing_store_smart_member_del(Evas_Object *o, Evas_Object *member) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| if (!priv->contents_clipper) |
| return; |
| evas_object_clip_unset(member); |
| if (!evas_object_clipees_get(priv->contents_clipper)) |
| evas_object_hide(priv->contents_clipper); |
| } |
| |
| static void _ewk_tiled_backing_store_smart_member_add(Evas_Object *o, Evas_Object *member) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| if (!priv->contents_clipper) |
| return; |
| evas_object_clip_set(member, priv->contents_clipper); |
| if (evas_object_visible_get(o)) |
| evas_object_show(priv->contents_clipper); |
| } |
| |
| #ifdef DEBUG_MEM_LEAKS |
| static void _ewk_tiled_backing_store_mem_dbg(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| static int run = 0; |
| |
| run++; |
| |
| printf("\n--- BEGIN DEBUG TILED BACKING STORE MEMORY [%d] --\n" |
| "t=%0.2f, obj=%p, priv=%p, view.items=%p, matrix=%p\n", |
| run, ecore_loop_time_get(), |
| priv->self, priv, priv->view.items, priv->model.matrix); |
| |
| ewk_tile_matrix_dbg(priv->model.matrix); |
| ewk_tile_accounting_dbg(); |
| |
| printf("--- END DEBUG TILED BACKING STORE MEMORY [%d] --\n\n", run); |
| } |
| |
| static Eina_Bool _ewk_tiled_backing_store_sig_usr(void *data, int type, void *event) |
| { |
| Ecore_Event_Signal_User *sig = (Ecore_Event_Signal_User*)event; |
| Ewk_Tiled_Backing_Store_Data *priv = (Ewk_Tiled_Backing_Store_Data*)data; |
| |
| if (sig->number == 2) { |
| Ewk_Tile_Unused_Cache *tuc; |
| tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix); |
| ewk_tile_unused_cache_auto_flush(tuc); |
| } |
| |
| _ewk_tiled_backing_store_view_dbg(priv); |
| _ewk_tiled_backing_store_mem_dbg(priv); |
| return EINA_TRUE; |
| } |
| #endif |
| |
| static void _ewk_tiled_backing_store_smart_add(Evas_Object *o) |
| { |
| Ewk_Tiled_Backing_Store_Data *priv; |
| |
| DBG("o=%p", o); |
| |
| CALLOC_OR_OOM_RET(priv, sizeof(*priv)); |
| |
| priv->self = o; |
| priv->view.tile.zoom = 1.0; |
| priv->view.tile.w = TILE_W; |
| priv->view.tile.h = TILE_H; |
| priv->view.offset.cur.x = 0; |
| priv->view.offset.cur.y = 0; |
| priv->view.offset.old.x = 0; |
| priv->view.offset.old.y = 0; |
| priv->view.offset.base.x = 0; |
| priv->view.offset.base.y = 0; |
| |
| priv->model.base.col = 0; |
| priv->model.base.row = 0; |
| priv->model.cur.cols = 1; |
| priv->model.cur.rows = 1; |
| priv->model.old.cols = 0; |
| priv->model.old.rows = 0; |
| priv->model.width = 0; |
| priv->model.height = 0; |
| priv->render.process_entire_queue = EINA_TRUE; |
| priv->render.suspend = EINA_FALSE; |
| priv->cspace = EVAS_COLORSPACE_ARGB8888; // TODO: detect it. |
| |
| evas_object_smart_data_set(o, priv); |
| _parent_sc.add(o); |
| |
| priv->contents_clipper = evas_object_rectangle_add( |
| evas_object_evas_get(o)); |
| evas_object_move(priv->contents_clipper, 0, 0); |
| evas_object_resize(priv->contents_clipper, |
| priv->model.width, priv->model.height); |
| evas_object_color_set(priv->contents_clipper, 255, 255, 255, 255); |
| evas_object_show(priv->contents_clipper); |
| evas_object_smart_member_add(priv->contents_clipper, o); |
| |
| _ewk_tiled_backing_store_model_matrix_create(priv, NULL); |
| evas_object_move(priv->base.clipper, 0, 0); |
| evas_object_resize(priv->base.clipper, 0, 0); |
| evas_object_clip_set(priv->contents_clipper, priv->base.clipper); |
| |
| #ifdef DEBUG_MEM_LEAKS |
| priv->sig_usr = ecore_event_handler_add |
| (ECORE_EVENT_SIGNAL_USER, _ewk_tiled_backing_store_sig_usr, priv); |
| #endif |
| } |
| |
| static void _ewk_tiled_backing_store_smart_del(Evas_Object *o) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| DBG("o=%p", o); |
| Ewk_Tile_Unused_Cache *tuc; |
| |
| tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix); |
| ewk_tile_unused_cache_unlock_area(tuc); |
| |
| _ewk_tiled_backing_store_flush(priv); |
| |
| _ewk_tiled_backing_store_pre_render_request_flush(priv); |
| _ewk_tiled_backing_store_item_process_idler_stop(priv); |
| _ewk_tiled_backing_store_view_rows_all_del(priv); |
| |
| #ifdef DEBUG_MEM_LEAKS |
| _ewk_tiled_backing_store_mem_dbg(priv); |
| if (priv->sig_usr) |
| priv->sig_usr = ecore_event_handler_del(priv->sig_usr); |
| #endif |
| |
| ewk_tile_matrix_free(priv->model.matrix); |
| evas_object_smart_member_del(priv->contents_clipper); |
| evas_object_del(priv->contents_clipper); |
| |
| _parent_sc.del(o); |
| |
| #ifdef DEBUG_MEM_LEAKS |
| printf("\nIMPORTANT: TILED BACKING STORE DELETED (may be real leaks)\n"); |
| ewk_tile_accounting_dbg(); |
| #endif |
| } |
| |
| static void _ewk_tiled_backing_store_smart_move(Evas_Object *o, Evas_Coord x, Evas_Coord y) |
| { |
| DBG("o=%p, new pos: %dx%d", o, x, y); |
| |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| |
| if (priv->changed.pos) |
| return; |
| |
| if (priv->view.x == x && priv->view.y == y) |
| return; |
| |
| priv->changed.pos = EINA_TRUE; |
| _ewk_tiled_backing_store_changed(priv); |
| } |
| |
| static void _ewk_tiled_backing_store_smart_resize(Evas_Object *o, Evas_Coord w, Evas_Coord h) |
| { |
| DBG("o=%p, new size: %dx%d", o, w, h); |
| |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| |
| if (priv->changed.size) |
| return; |
| |
| if (priv->view.w == w && priv->view.h == h) |
| return; |
| |
| priv->changed.size = EINA_TRUE; |
| _ewk_tiled_backing_store_changed(priv); |
| } |
| |
| 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) |
| { |
| long cols, rows, old_rows, old_cols; |
| INF("o=%p, new size: %dx%d", priv->self, w, h); |
| |
| cols = 1 + (int)ceil((float)w / (float)tw); |
| rows = 1 + (int)ceil((float)h / (float)th); |
| |
| INF("o=%p new grid size cols: %ld, rows: %ld, was %ld, %ld", |
| priv->self, cols, rows, priv->view.cols, priv->view.rows); |
| |
| if (priv->view.cols == cols && priv->view.rows == rows) |
| return; |
| |
| old_cols = priv->view.cols; |
| old_rows = priv->view.rows; |
| |
| if (rows < old_rows) { |
| Eina_Inlist **start, **end; |
| start = priv->view.items + rows; |
| end = priv->view.items + old_rows; |
| _ewk_tiled_backing_store_view_rows_range_del(priv, start, end); |
| } |
| REALLOC_OR_OOM_RET(priv->view.items, sizeof(Eina_Inlist*) * (int)rows); |
| priv->view.rows = rows; |
| priv->view.cols = cols; |
| if (rows > old_rows) { |
| Eina_Inlist **start, **end; |
| start = priv->view.items + old_rows; |
| end = priv->view.items + rows; |
| for (; start < end; start++) { |
| Eina_Bool r; |
| *start = NULL; |
| r = _ewk_tiled_backing_store_view_cols_end_add |
| (priv, start, 0, cols); |
| if (!r) { |
| CRITICAL("failed to allocate %ld columns", cols); |
| _ewk_tiled_backing_store_view_rows_range_del |
| (priv, priv->view.items + old_rows, start); |
| priv->view.rows = old_rows; |
| return; |
| } |
| } |
| } |
| |
| if (cols != old_cols) { |
| Eina_Inlist **start, **end; |
| int todo = cols - old_cols; |
| start = priv->view.items; |
| end = start + MIN(old_rows, rows); |
| if (todo > 0) { |
| for (; start < end; start++) { |
| Eina_Bool r; |
| r = _ewk_tiled_backing_store_view_cols_end_add |
| (priv, start, old_cols, todo); |
| if (!r) { |
| CRITICAL("failed to allocate %d columns!", todo); |
| |
| for (start--; start >= priv->view.items; start--) |
| _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo); |
| if (rows > old_rows) { |
| start = priv->view.items + old_rows; |
| end = priv->view.items + rows; |
| for (; start < end; start++) |
| _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo); |
| } |
| return; |
| } |
| } |
| } else if (todo < 0) { |
| todo = -todo; |
| for (; start < end; start++) |
| _ewk_tiled_backing_store_view_cols_end_del(priv, start, todo); |
| } |
| } |
| |
| _ewk_tiled_backing_store_fill_renderers(priv); |
| } |
| |
| static void _ewk_tiled_backing_store_smart_calculate_size(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord w, Evas_Coord h) |
| { |
| evas_object_resize(priv->base.clipper, w, h); |
| |
| priv->view.w = w; |
| priv->view.h = h; |
| |
| _ewk_tiled_backing_store_recalc_renderers( |
| priv, w, h, priv->view.tile.w, priv->view.tile.h); |
| } |
| |
| // TODO: remove me later. |
| static inline void _ewk_tiled_backing_store_view_dbg(const Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| Eina_Inlist **start, **end; |
| printf("tiles=%2ld,%2ld model=%2ld,%2ld [%dx%d] base=%+3ld,%+4ld offset=%+4d,%+4d old=%+4d,%+4d base=%+3d,%+3d\n", |
| priv->view.cols, priv->view.rows, |
| priv->model.cur.cols, priv->model.cur.rows, |
| priv->model.width, priv->model.height, |
| priv->model.base.col, priv->model.base.row, |
| priv->view.offset.cur.x, priv->view.offset.cur.y, |
| priv->view.offset.old.x, priv->view.offset.old.y, |
| priv->view.offset.base.x, priv->view.offset.base.y); |
| |
| start = priv->view.items; |
| end = priv->view.items + priv->view.rows; |
| for (; start < end; start++) { |
| const Ewk_Tiled_Backing_Store_Item *it; |
| |
| EINA_INLIST_FOREACH(*start, it) { |
| printf(" %+4d,%+4d ", it->geometry.x, it->geometry.y); |
| |
| if (!it->tile) |
| printf(" ;"); |
| else |
| printf("%8p %lu,%lu;", it->tile, it->tile->col, it->tile->row); |
| } |
| printf("\n"); |
| } |
| printf("---\n"); |
| } |
| |
| /** |
| * @internal |
| * Move top row down as last. |
| * |
| * The final result is visually the same, but logically the top that |
| * went out of screen is now at bottom and filled with new model items. |
| * |
| * This is worth just when @a count is smaller than @c |
| * priv->view.rows, after that one is refilling the whole matrix so it |
| * is better to trigger full refill. |
| * |
| * @param count the number of times to repeat the process. |
| */ |
| static void _ewk_tiled_backing_store_view_wrap_up(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count) |
| { |
| unsigned int last_row = priv->view.rows - 1; |
| Evas_Coord tw = priv->view.tile.w; |
| Evas_Coord th = priv->view.tile.h; |
| Evas_Coord off_y = priv->view.offset.base.y + count * th; |
| Evas_Coord oy = y + (last_row - count + 1) * th + off_y; |
| Eina_Inlist **itr_start, **itr_end; |
| |
| itr_start = priv->view.items; |
| itr_end = itr_start + last_row; |
| |
| for (; count > 0; count--) { |
| Eina_Inlist **itr; |
| Eina_Inlist *tmp = *itr_start; |
| Ewk_Tiled_Backing_Store_Item *it; |
| Evas_Coord ox = x + priv->view.offset.base.x; |
| int c = 0; |
| |
| for (itr = itr_start; itr < itr_end; itr++) |
| *itr = *(itr + 1); |
| *itr = tmp; |
| |
| priv->model.base.row++; |
| EINA_INLIST_FOREACH(tmp, it) { |
| _ewk_tiled_backing_store_item_move(it, ox, oy); |
| ox += tw; |
| _ewk_tiled_backing_store_item_fill(priv, it, c, last_row); |
| c++; |
| } |
| oy += th; |
| } |
| priv->view.offset.base.y = off_y; |
| } |
| |
| /** |
| * @internal |
| * Move bottom row up as first. |
| * |
| * The final result is visually the same, but logically the bottom that |
| * went out of screen is now at top and filled with new model items. |
| * |
| * This is worth just when @a count is smaller than @c |
| * priv->view.rows, after that one is refilling the whole matrix so it |
| * is better to trigger full refill. |
| * |
| * @param count the number of times to repeat the process. |
| */ |
| static void _ewk_tiled_backing_store_view_wrap_down(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count) |
| { |
| Evas_Coord tw = priv->view.tile.w; |
| Evas_Coord th = priv->view.tile.h; |
| Evas_Coord off_y = priv->view.offset.base.y - count * th; |
| Evas_Coord oy = y + off_y + (count - 1) * th; |
| Eina_Inlist **itr_start, **itr_end; |
| |
| itr_start = priv->view.items + priv->view.rows - 1; |
| itr_end = priv->view.items; |
| |
| for (; count > 0; count--) { |
| Eina_Inlist **itr; |
| Eina_Inlist *tmp = *itr_start; |
| Ewk_Tiled_Backing_Store_Item *it; |
| Evas_Coord ox = x + priv->view.offset.base.x; |
| int c = 0; |
| |
| for (itr = itr_start; itr > itr_end; itr--) |
| *itr = *(itr - 1); |
| *itr = tmp; |
| |
| priv->model.base.row--; |
| EINA_INLIST_FOREACH(tmp, it) { |
| _ewk_tiled_backing_store_item_move(it, ox, oy); |
| ox += tw; |
| _ewk_tiled_backing_store_item_fill(priv, it, c, 0); |
| c++; |
| } |
| oy -= th; |
| } |
| priv->view.offset.base.y = off_y; |
| } |
| |
| /** |
| * @internal |
| * Move left-most (first) column right as last (right-most). |
| * |
| * The final result is visually the same, but logically the first col that |
| * went out of screen is now at last and filled with new model items. |
| * |
| * This is worth just when @a count is smaller than @c |
| * priv->view.cols, after that one is refilling the whole matrix so it |
| * is better to trigger full refill. |
| * |
| * @param count the number of times to repeat the process. |
| */ |
| static void _ewk_tiled_backing_store_view_wrap_left(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count) |
| { |
| unsigned int r, last_col = priv->view.cols - 1; |
| Evas_Coord tw = priv->view.tile.w; |
| Evas_Coord th = priv->view.tile.h; |
| Evas_Coord off_x = priv->view.offset.base.x + count * tw; |
| Evas_Coord oy = y + priv->view.offset.base.y; |
| Eina_Inlist **itr; |
| Eina_Inlist **itr_end; |
| |
| itr = priv->view.items; |
| itr_end = itr + priv->view.rows; |
| r = 0; |
| |
| priv->model.base.col += count; |
| |
| for (; itr < itr_end; itr++, r++) { |
| Evas_Coord ox = x + (last_col - count + 1) * tw + off_x; |
| unsigned int i, c = last_col - count + 1; |
| |
| for (i = 0; i < count; i++, c++, ox += tw) { |
| Ewk_Tiled_Backing_Store_Item *it; |
| it = EINA_INLIST_CONTAINER_GET(*itr, Ewk_Tiled_Backing_Store_Item); |
| *itr = eina_inlist_demote(*itr, *itr); |
| |
| _ewk_tiled_backing_store_item_move(it, ox, oy); |
| _ewk_tiled_backing_store_item_fill(priv, it, c, r); |
| } |
| oy += th; |
| } |
| |
| priv->view.offset.base.x = off_x; |
| } |
| |
| /** |
| * @internal |
| * Move right-most (last) column left as first (left-most). |
| * |
| * The final result is visually the same, but logically the last col that |
| * went out of screen is now at first and filled with new model items. |
| * |
| * This is worth just when @a count is smaller than @c |
| * priv->view.cols, after that one is refilling the whole matrix so it |
| * is better to trigger full refill. |
| * |
| * @param count the number of times to repeat the process. |
| */ |
| static void _ewk_tiled_backing_store_view_wrap_right(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y, unsigned int count) |
| { |
| unsigned int r; |
| Evas_Coord tw = priv->view.tile.w; |
| Evas_Coord th = priv->view.tile.h; |
| Evas_Coord off_x = priv->view.offset.base.x - count * tw; |
| Evas_Coord oy = y + priv->view.offset.base.y; |
| Eina_Inlist **itr, **itr_end; |
| |
| itr = priv->view.items; |
| itr_end = itr + priv->view.rows; |
| r = 0; |
| |
| priv->model.base.col -= count; |
| |
| for (; itr < itr_end; itr++, r++) { |
| Evas_Coord ox = x + (count - 1) * tw + off_x; |
| unsigned int i, c = count - 1; |
| |
| for (i = 0; i < count; i++, c--, ox -= tw) { |
| Ewk_Tiled_Backing_Store_Item *it; |
| it = EINA_INLIST_CONTAINER_GET((*itr)->last, Ewk_Tiled_Backing_Store_Item); |
| *itr = eina_inlist_promote(*itr, (*itr)->last); |
| |
| _ewk_tiled_backing_store_item_move(it, ox, oy); |
| _ewk_tiled_backing_store_item_fill(priv, it, c, r); |
| } |
| oy += th; |
| } |
| |
| priv->view.offset.base.x = off_x; |
| } |
| |
| 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) |
| { |
| Eina_Inlist **itr, **itr_end; |
| Evas_Coord base_ox, oy, tw, th; |
| unsigned int r; |
| |
| evas_object_move(priv->base.clipper, x, y); |
| |
| tw = priv->view.tile.w; |
| th = priv->view.tile.h; |
| |
| base_ox = x + priv->view.offset.base.x; |
| oy = y + priv->view.offset.base.y; |
| |
| itr = priv->view.items; |
| itr_end = itr + priv->view.rows; |
| r = 0; |
| |
| priv->model.base.col -= step_x; |
| priv->model.base.row -= step_y; |
| |
| for (; itr < itr_end; itr++, r++) { |
| Ewk_Tiled_Backing_Store_Item *it; |
| Evas_Coord ox = base_ox; |
| unsigned int c = 0; |
| EINA_INLIST_FOREACH(*itr, it) { |
| _ewk_tiled_backing_store_item_fill(priv, it, c, r); |
| _ewk_tiled_backing_store_item_move(it, ox, oy); |
| c++; |
| ox += tw; |
| } |
| oy += th; |
| } |
| } |
| |
| static void _ewk_tiled_backing_store_view_pos_apply(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y) |
| { |
| Eina_Inlist **itr, **itr_end; |
| Evas_Coord base_ox, oy, tw, th; |
| |
| evas_object_move(priv->base.clipper, x, y); |
| |
| tw = priv->view.tile.w; |
| th = priv->view.tile.h; |
| |
| base_ox = x + priv->view.offset.base.x; |
| oy = y + priv->view.offset.base.y; |
| |
| itr = priv->view.items; |
| itr_end = itr + priv->view.rows; |
| for (; itr < itr_end; itr++) { |
| Ewk_Tiled_Backing_Store_Item *it; |
| Evas_Coord ox = base_ox; |
| EINA_INLIST_FOREACH(*itr, it) { |
| _ewk_tiled_backing_store_item_move(it, ox, oy); |
| ox += tw; |
| } |
| oy += th; |
| } |
| } |
| |
| static void _ewk_tiled_backing_store_smart_calculate_offset_force(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| Evas_Coord dx = priv->view.offset.cur.x - priv->view.offset.old.x; |
| Evas_Coord dy = priv->view.offset.cur.y - priv->view.offset.old.y; |
| Evas_Coord tw, th; |
| int step_y, step_x; |
| |
| INF("o=%p, offset: %+4d, %+4d (%+4d, %+4d)", |
| priv->self, dx, dy, priv->view.offset.cur.x, priv->view.offset.cur.y); |
| |
| tw = priv->view.tile.w; |
| th = priv->view.tile.h; |
| |
| long new_col = -priv->view.offset.cur.x / tw; |
| step_x = priv->model.base.col - new_col; |
| long new_row = -priv->view.offset.cur.y / th; |
| step_y = priv->model.base.row - new_row; |
| |
| priv->view.offset.old.x = priv->view.offset.cur.x; |
| priv->view.offset.old.y = priv->view.offset.cur.y; |
| evas_object_move( |
| priv->contents_clipper, |
| priv->view.offset.cur.x + priv->view.x, |
| priv->view.offset.cur.y + priv->view.y); |
| |
| priv->view.offset.base.x += dx - step_x * tw; |
| priv->view.offset.base.y += dy - step_y * th; |
| |
| _ewk_tiled_backing_store_view_refill |
| (priv, priv->view.x, priv->view.y, step_x, step_y); |
| } |
| |
| static void _ewk_tiled_backing_store_smart_calculate_offset(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y) |
| { |
| Evas_Coord dx = priv->view.offset.cur.x - priv->view.offset.old.x; |
| Evas_Coord dy = priv->view.offset.cur.y - priv->view.offset.old.y; |
| Evas_Coord tw, th; |
| int step_y, step_x; |
| |
| INF("o=%p, offset: %+4d, %+4d (%+4d, %+4d)", |
| priv->self, dx, dy, priv->view.offset.cur.x, priv->view.offset.cur.y); |
| |
| if (!dx && !dy) |
| return; |
| |
| tw = priv->view.tile.w; |
| th = priv->view.tile.h; |
| |
| long new_col = -priv->view.offset.cur.x / tw; |
| step_x = priv->model.base.col - new_col; |
| long new_row = -priv->view.offset.cur.y / th; |
| step_y = priv->model.base.row - new_row; |
| |
| priv->view.offset.old.x = priv->view.offset.cur.x; |
| priv->view.offset.old.y = priv->view.offset.cur.y; |
| evas_object_move( |
| priv->contents_clipper, |
| priv->view.offset.cur.x + priv->view.x, |
| priv->view.offset.cur.y + priv->view.y); |
| |
| if ((step_x < 0 && step_x <= -priv->view.cols) |
| || (step_x > 0 && step_x >= priv->view.cols) |
| || (step_y < 0 && step_y <= -priv->view.rows) |
| || (step_y > 0 && step_y >= priv->view.rows)) { |
| |
| priv->view.offset.base.x += dx - step_x * tw; |
| priv->view.offset.base.y += dy - step_y * th; |
| |
| _ewk_tiled_backing_store_view_refill |
| (priv, priv->view.x, priv->view.y, step_x, step_y); |
| return; |
| } |
| |
| priv->view.offset.base.x += dx; |
| priv->view.offset.base.y += dy; |
| |
| if (step_y < 0) |
| _ewk_tiled_backing_store_view_wrap_up(priv, x, y, -step_y); |
| else if (step_y > 0) |
| _ewk_tiled_backing_store_view_wrap_down(priv, x, y, step_y); |
| |
| if (step_x < 0) |
| _ewk_tiled_backing_store_view_wrap_left(priv, x, y, -step_x); |
| else if (step_x > 0) |
| _ewk_tiled_backing_store_view_wrap_right(priv, x, y, step_x); |
| |
| _ewk_tiled_backing_store_view_pos_apply(priv, x, y); |
| } |
| |
| static void _ewk_tiled_backing_store_smart_calculate_pos(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y) |
| { |
| _ewk_tiled_backing_store_view_pos_apply(priv, x, y); |
| priv->view.x = x; |
| priv->view.y = y; |
| evas_object_move( |
| priv->contents_clipper, |
| priv->view.offset.cur.x + priv->view.x, |
| priv->view.offset.cur.y + priv->view.y); |
| } |
| |
| static void _ewk_tiled_backing_store_fill_renderers(Ewk_Tiled_Backing_Store_Data *priv) |
| { |
| Eina_Inlist *it; |
| Ewk_Tiled_Backing_Store_Item *item; |
| int i, j; |
| |
| for (i = 0; i < priv->view.rows; i++) { |
| it = priv->view.items[i]; |
| j = 0; |
| EINA_INLIST_FOREACH(it, item) |
| _ewk_tiled_backing_store_item_fill(priv, item, j++, i); |
| } |
| } |
| |
| static void _ewk_tiled_backing_store_smart_calculate(Evas_Object *o) |
| { |
| Evas_Coord x, y, w, h; |
| |
| evas_object_geometry_get(o, &x, &y, &w, &h); |
| DBG("o=%p at %d,%d + %dx%d", o, x, y, w, h); |
| |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| |
| priv->changed.any = EINA_FALSE; |
| |
| ewk_tile_matrix_freeze(priv->model.matrix); |
| |
| if (!priv->render.suspend && priv->changed.model) { |
| unsigned long cols, rows; |
| |
| cols = priv->model.width / priv->view.tile.w + 1; |
| rows = priv->model.height / priv->view.tile.h + 1; |
| |
| priv->model.old.cols = priv->model.cur.cols; |
| priv->model.old.rows = priv->model.cur.rows; |
| priv->model.cur.cols = cols; |
| priv->model.cur.rows = rows; |
| if (priv->model.old.cols > cols) |
| cols = priv->model.old.cols; |
| if (priv->model.old.rows > rows) |
| rows = priv->model.old.rows; |
| ewk_tile_matrix_resize(priv->model.matrix, cols, rows); |
| } |
| |
| if (priv->changed.pos && (priv->view.x != x || priv->view.y != y)) { |
| _ewk_tiled_backing_store_smart_calculate_pos(priv, x, y); |
| priv->changed.pos = EINA_FALSE; |
| } else if (priv->changed.offset) { |
| _ewk_tiled_backing_store_smart_calculate_offset(priv, x, y); |
| priv->changed.offset = EINA_FALSE; |
| } |
| |
| if (priv->changed.size) { |
| _ewk_tiled_backing_store_smart_calculate_size(priv, w, h); |
| priv->changed.size = EINA_FALSE; |
| } |
| |
| if (!priv->render.suspend && priv->changed.model) { |
| Eina_Rectangle rect; |
| rect.x = 0; |
| rect.y = 0; |
| rect.w = priv->model.width; |
| rect.h = priv->model.height; |
| _ewk_tiled_backing_store_fill_renderers(priv); |
| ewk_tile_matrix_resize(priv->model.matrix, |
| priv->model.cur.cols, |
| priv->model.cur.rows); |
| priv->changed.model = EINA_FALSE; |
| evas_object_resize(priv->contents_clipper, |
| priv->model.width, priv->model.height); |
| _ewk_tiled_backing_store_smart_calculate_offset_force(priv); |
| |
| /* Make sure we do not miss any important repaint by |
| * repainting the whole viewport */ |
| const Eina_Rectangle r = |
| { 0, 0, priv->model.width, priv->model.height }; |
| ewk_tile_matrix_update(priv->model.matrix, &r, |
| priv->view.tile.zoom); |
| } |
| |
| ewk_tile_matrix_thaw(priv->model.matrix); |
| |
| _ewk_tiled_backing_store_updates_process(priv); |
| |
| if (priv->view.offset.base.x > 0 |
| || priv->view.offset.base.x <= - priv->view.tile.w |
| || priv->view.offset.base.y > 0 |
| || priv->view.offset.base.y <= - priv->view.tile.h) |
| ERR("incorrect base offset %+4d,%+4d, tile=%dx%d, cur=%+4d,%+4d\n", |
| priv->view.offset.base.x, priv->view.offset.base.y, |
| priv->view.tile.w, priv->view.tile.h, |
| priv->view.offset.cur.x, priv->view.offset.cur.y); |
| |
| } |
| |
| Evas_Object *ewk_tiled_backing_store_add(Evas *e) |
| { |
| static Evas_Smart *smart = NULL; |
| |
| if (_ewk_tiled_log_dom < 0) |
| _ewk_tiled_log_dom = eina_log_domain_register("Ewk_Tiled_Backing_Store", NULL); |
| |
| if (!smart) { |
| static Evas_Smart_Class sc = |
| EVAS_SMART_CLASS_INIT_NAME_VERSION("Ewk_Tiled_Backing_Store"); |
| |
| evas_object_smart_clipped_smart_set(&sc); |
| _parent_sc = sc; |
| |
| sc.add = _ewk_tiled_backing_store_smart_add; |
| sc.del = _ewk_tiled_backing_store_smart_del; |
| sc.resize = _ewk_tiled_backing_store_smart_resize; |
| sc.move = _ewk_tiled_backing_store_smart_move; |
| sc.calculate = _ewk_tiled_backing_store_smart_calculate; |
| sc.member_add = _ewk_tiled_backing_store_smart_member_add; |
| sc.member_del = _ewk_tiled_backing_store_smart_member_del; |
| |
| smart = evas_smart_class_new(&sc); |
| } |
| |
| return evas_object_smart_add(e, smart); |
| } |
| |
| 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) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(cb); |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| priv->render.cb = cb; |
| priv->render.data = (void*)data; |
| } |
| |
| Ewk_Tile_Unused_Cache *ewk_tiled_backing_store_tile_unused_cache_get(const Evas_Object *o) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv, NULL); |
| return ewk_tile_matrix_unused_cache_get(priv->model.matrix); |
| } |
| |
| void ewk_tiled_backing_store_tile_unused_cache_set(Evas_Object *o, Ewk_Tile_Unused_Cache *tuc) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| |
| if (ewk_tile_matrix_unused_cache_get(priv->model.matrix) == tuc) |
| return; |
| |
| _ewk_tiled_backing_store_model_matrix_create(priv, tuc); |
| } |
| |
| static Eina_Bool _ewk_tiled_backing_store_scroll_full_offset_set_internal(Ewk_Tiled_Backing_Store_Data *priv, Evas_Coord x, Evas_Coord y) |
| { |
| /* TODO: check offset go out of bounds, clamp */ |
| if (priv->render.disabled) |
| return EINA_FALSE; |
| |
| priv->view.offset.cur.x = x; |
| priv->view.offset.cur.y = y; |
| |
| priv->changed.offset = EINA_TRUE; |
| _ewk_tiled_backing_store_changed(priv); |
| |
| return EINA_TRUE; |
| } |
| |
| Eina_Bool ewk_tiled_backing_store_scroll_full_offset_set(Evas_Object *o, Evas_Coord x, Evas_Coord y) |
| { |
| DBG("o=%p, x=%d, y=%d", o, x, y); |
| |
| PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE); |
| if (x == priv->view.offset.cur.x && y == priv->view.offset.cur.y) |
| return EINA_TRUE; |
| |
| return _ewk_tiled_backing_store_scroll_full_offset_set_internal(priv, x, y); |
| } |
| |
| Eina_Bool ewk_tiled_backing_store_scroll_full_offset_add(Evas_Object *o, Evas_Coord dx, Evas_Coord dy) |
| { |
| DBG("o=%p, dx=%d, dy=%d", o, dx, dy); |
| |
| PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE); |
| if (!dx && !dy) |
| return EINA_TRUE; |
| |
| return _ewk_tiled_backing_store_scroll_full_offset_set_internal |
| (priv, priv->view.offset.cur.x + dx, priv->view.offset.cur.y + dy); |
| } |
| |
| 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) |
| { |
| *offx = priv->view.offset.cur.x; |
| *offy = priv->view.offset.cur.y; |
| |
| if (fabsf(priv->view.tile.zoom - *zoom) < ZOOM_STEP_MIN) { |
| DBG("ignored as zoom difference is < %f: %f", |
| (double)ZOOM_STEP_MIN, fabsf(priv->view.tile.zoom - *zoom)); |
| return EINA_TRUE; |
| } |
| |
| _ewk_tiled_backing_store_pre_render_request_flush(priv); |
| Evas_Coord tw, th; |
| tw = TILE_SIZE_AT_ZOOM(TILE_W, *zoom); |
| tw = (tw >> 1) << 1; |
| *zoom = TILE_W_ZOOM_AT_SIZE(tw); |
| /* WARNING: assume reverse zoom is the same for both axis */ |
| th = TILE_SIZE_AT_ZOOM(TILE_H, *zoom); |
| |
| float scale = *zoom / priv->view.tile.zoom; |
| |
| priv->view.tile.zoom = *zoom; |
| // todo: check cx [0, w]... |
| priv->view.offset.zoom_center.x = cx; |
| priv->view.offset.zoom_center.y = cy; |
| |
| priv->view.tile.w = tw; |
| priv->view.tile.h = th; |
| |
| if (!priv->view.w || !priv->view.h) { |
| priv->view.offset.base.x = 0; |
| priv->view.offset.base.y = 0; |
| return EINA_TRUE; |
| } |
| Eina_Inlist **itr, **itr_end; |
| Ewk_Tiled_Backing_Store_Item *it; |
| |
| Evas_Coord new_x = cx + (priv->view.offset.cur.x - cx) * scale; |
| Evas_Coord new_y = cy + (priv->view.offset.cur.y - cy) * scale; |
| Evas_Coord bx = cx + (priv->view.offset.base.x - cx) * scale; |
| Evas_Coord by = cy + (priv->view.offset.base.y - cy) * scale; |
| |
| Evas_Coord model_width = priv->model.width * scale; |
| Evas_Coord model_height = priv->model.height * scale; |
| |
| if (model_width < priv->view.w || new_x >= 0) |
| new_x = 0; |
| else if (-new_x + priv->view.w >= model_width) |
| new_x = -model_width + priv->view.w; |
| |
| if (model_height < priv->view.h || new_y >= 0) |
| new_y = 0; |
| else if (-new_y + priv->view.h >= model_height) |
| new_y = -model_height + priv->view.h; |
| |
| bx = new_x % tw; |
| priv->model.base.col = - new_x / tw; |
| by = new_y % th; |
| priv->model.base.row = - new_y / th; |
| |
| priv->changed.size = EINA_TRUE; |
| _ewk_tiled_backing_store_changed(priv); |
| |
| priv->view.offset.cur.x = new_x; |
| priv->view.offset.cur.y = new_y; |
| priv->view.offset.base.x = bx; |
| priv->view.offset.base.y = by; |
| |
| priv->view.offset.old.x = priv->view.offset.cur.x; |
| priv->view.offset.old.y = priv->view.offset.cur.y; |
| *offx = priv->view.offset.cur.x; |
| *offy = priv->view.offset.cur.y; |
| |
| evas_object_move( |
| priv->contents_clipper, |
| new_x + priv->view.x, |
| new_y + priv->view.y); |
| |
| _ewk_tiled_backing_store_fill_renderers(priv); |
| |
| Evas_Coord oy = priv->view.offset.base.y + priv->view.y; |
| Evas_Coord base_ox = priv->view.x + priv->view.offset.base.x; |
| |
| itr = priv->view.items; |
| itr_end = itr + priv->view.rows; |
| |
| for (; itr < itr_end; itr++) { |
| Evas_Coord ox = base_ox; |
| Eina_Inlist *lst = *itr; |
| |
| EINA_INLIST_FOREACH(lst, it) { |
| _ewk_tiled_backing_store_item_move(it, ox, oy); |
| _ewk_tiled_backing_store_item_resize(it, tw, th); |
| ox += tw; |
| } |
| oy += th; |
| } |
| |
| return EINA_TRUE; |
| } |
| |
| 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) |
| { |
| DBG("o=%p, zoom=%f", o, (double)*zoom); |
| |
| PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE); |
| |
| return _ewk_tiled_backing_store_zoom_set_internal(priv, zoom, cx, cy, offx, offy); |
| } |
| |
| Eina_Bool ewk_tiled_backing_store_zoom_weak_set(Evas_Object *o, float zoom, Evas_Coord cx, Evas_Coord cy) |
| { |
| DBG("o=%p, zoom=%f", o, (double)zoom); |
| PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE); |
| if (!priv->view.w || !priv->view.h) |
| return EINA_FALSE; |
| Eina_Inlist **itr, **itr_end; |
| Ewk_Tiled_Backing_Store_Item *it; |
| Evas_Coord tw, th; |
| Eina_Bool recalc = EINA_FALSE; |
| |
| tw = TILE_SIZE_AT_ZOOM(TILE_W, zoom); |
| zoom = TILE_W_ZOOM_AT_SIZE(tw); |
| /* WARNING: assume reverse zoom is the same for both axis */ |
| th = TILE_SIZE_AT_ZOOM(TILE_H, zoom); |
| |
| float scale = zoom / priv->view.tile.zoom; |
| |
| Evas_Coord model_width = priv->model.width * scale; |
| Evas_Coord model_height = priv->model.height * scale; |
| |
| evas_object_resize(priv->contents_clipper, |
| model_width, model_height); |
| |
| int vrows = ceil((float)priv->view.h / (float)th) + 1; |
| int vcols = ceil((float)priv->view.w / (float)tw) + 1; |
| Evas_Coord new_x = cx + (priv->view.offset.cur.x - cx) * scale; |
| Evas_Coord new_y = cy + (priv->view.offset.cur.y - cy) * scale; |
| Evas_Coord bx = new_x % tw; |
| Evas_Coord by = new_y % th; |
| unsigned long base_row = -new_y / th; |
| unsigned long base_col = -new_x / tw; |
| |
| if (base_row != priv->model.base.row || base_col != priv->model.base.col) { |
| priv->model.base.row = base_row; |
| priv->model.base.col = base_col; |
| recalc = EINA_TRUE; |
| } |
| |
| if (vrows > priv->view.rows || vcols > priv->view.cols) |
| recalc = EINA_TRUE; |
| |
| if (recalc) { |
| Evas_Coord w, h; |
| evas_object_geometry_get(o, NULL, NULL, &w, &h); |
| _ewk_tiled_backing_store_recalc_renderers(priv, w, h, tw, th); |
| _ewk_tiled_backing_store_fill_renderers(priv); |
| _ewk_tiled_backing_store_updates_process(priv); |
| } |
| |
| Evas_Coord base_ox = bx + priv->view.x; |
| Evas_Coord oy = by + priv->view.y; |
| |
| evas_object_move(priv->contents_clipper, |
| new_x + priv->view.x, |
| new_y + priv->view.y); |
| |
| itr = priv->view.items; |
| itr_end = itr + priv->view.rows; |
| |
| for (; itr < itr_end; itr++) { |
| Evas_Coord ox = base_ox; |
| Eina_Inlist *lst = *itr; |
| |
| EINA_INLIST_FOREACH(lst, it) { |
| _ewk_tiled_backing_store_item_move(it, ox, oy); |
| _ewk_tiled_backing_store_item_resize(it, tw, th); |
| ox += tw; |
| } |
| oy += th; |
| } |
| |
| return EINA_TRUE; |
| } |
| |
| void ewk_tiled_backing_store_fix_offsets(Evas_Object *o, Evas_Coord w, Evas_Coord h) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| Eina_Inlist **itr, **itr_end; |
| Ewk_Tiled_Backing_Store_Item *it; |
| Evas_Coord new_x = priv->view.offset.cur.x; |
| Evas_Coord new_y = priv->view.offset.cur.y; |
| Evas_Coord bx = priv->view.offset.base.x; |
| Evas_Coord by = priv->view.offset.base.y; |
| Evas_Coord tw = priv->view.tile.w; |
| Evas_Coord th = priv->view.tile.h; |
| |
| if (-new_x > w) { |
| new_x = -w; |
| bx = new_x % tw; |
| priv->model.base.col = -new_x / tw; |
| } |
| |
| if (-new_y > h) { |
| new_y = -h; |
| by = new_y % th; |
| priv->model.base.row = -new_y / th; |
| } |
| |
| if (bx >= 0 || bx <= -2 * priv->view.tile.w) { |
| bx = new_x % tw; |
| priv->model.base.col = -new_x / tw; |
| } |
| |
| if (by >= 0 || by <= -2 * priv->view.tile.h) { |
| by = new_y % th; |
| priv->model.base.row = -new_y / th; |
| } |
| |
| priv->view.offset.cur.x = new_x; |
| priv->view.offset.cur.y = new_y; |
| priv->view.offset.old.x = new_x; |
| priv->view.offset.old.y = new_y; |
| priv->view.offset.base.x = bx; |
| priv->view.offset.base.y = by; |
| evas_object_move(priv->contents_clipper, |
| new_x + priv->view.x, |
| new_y + priv->view.y); |
| |
| Evas_Coord oy = priv->view.offset.base.y + priv->view.y; |
| Evas_Coord base_ox = priv->view.x + priv->view.offset.base.x; |
| |
| itr = priv->view.items; |
| itr_end = itr + priv->view.rows; |
| |
| for (; itr < itr_end; itr++) { |
| Evas_Coord ox = base_ox; |
| Eina_Inlist *lst = *itr; |
| |
| EINA_INLIST_FOREACH(lst, it) { |
| _ewk_tiled_backing_store_item_move(it, ox, oy); |
| _ewk_tiled_backing_store_item_resize(it, tw, th); |
| ox += tw; |
| } |
| oy += th; |
| } |
| } |
| |
| void ewk_tiled_backing_store_zoom_weak_smooth_scale_set(Evas_Object *o, Eina_Bool smooth_scale) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| Eina_Inlist **itr, **itr_end; |
| |
| itr = priv->view.items; |
| itr_end = itr + priv->view.rows; |
| priv->view.tile.zoom_weak_smooth_scale = smooth_scale; |
| |
| for (; itr< itr_end; itr++) { |
| Ewk_Tiled_Backing_Store_Item *it; |
| EINA_INLIST_FOREACH(*itr, it) |
| if (it->tile) |
| _ewk_tiled_backing_store_item_smooth_scale_set |
| (it, smooth_scale); |
| } |
| } |
| |
| Eina_Bool ewk_tiled_backing_store_update(Evas_Object *o, const Eina_Rectangle *update) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE); |
| |
| if (priv->render.disabled) |
| return EINA_FALSE; |
| |
| return ewk_tile_matrix_update(priv->model.matrix, update, |
| priv->view.tile.zoom); |
| } |
| |
| void ewk_tiled_backing_store_updates_process_pre_set(Evas_Object *o, void *(*cb)(void *data, Evas_Object *o), const void *data) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| priv->process.pre_cb = cb; |
| priv->process.pre_data = (void*)data; |
| } |
| |
| 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) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| priv->process.post_cb = cb; |
| priv->process.post_data = (void*)data; |
| } |
| |
| void ewk_tiled_backing_store_updates_process(Evas_Object *o) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| _ewk_tiled_backing_store_updates_process(priv); |
| } |
| |
| void ewk_tiled_backing_store_updates_clear(Evas_Object *o) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| |
| ewk_tile_matrix_updates_clear(priv->model.matrix); |
| } |
| |
| void ewk_tiled_backing_store_contents_resize(Evas_Object *o, Evas_Coord width, Evas_Coord height) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| |
| if (width == priv->model.width && height == priv->model.height) |
| return; |
| |
| priv->model.width = width; |
| priv->model.height = height; |
| priv->changed.model = EINA_TRUE; |
| |
| DBG("width,height=%d, %d", width, height); |
| _ewk_tiled_backing_store_changed(priv); |
| } |
| |
| void ewk_tiled_backing_store_disabled_update_set(Evas_Object *o, Eina_Bool value) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| |
| if (value != priv->render.disabled) |
| priv->render.disabled = value; |
| } |
| |
| void ewk_tiled_backing_store_flush(Evas_Object *o) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| Ewk_Tile_Unused_Cache *tuc = NULL; |
| |
| priv->view.offset.cur.x = 0; |
| priv->view.offset.cur.y = 0; |
| priv->view.offset.old.x = 0; |
| priv->view.offset.old.y = 0; |
| priv->view.offset.base.x = 0; |
| priv->view.offset.base.y = 0; |
| priv->model.base.col = 0; |
| priv->model.base.row = 0; |
| priv->changed.size = EINA_TRUE; |
| |
| #ifdef DEBUG_MEM_LEAKS |
| printf("\nFLUSHED BACKING STORE, STATUS BEFORE DELETING TILE MATRIX:\n"); |
| _ewk_tiled_backing_store_mem_dbg(priv); |
| #endif |
| |
| _ewk_tiled_backing_store_pre_render_request_flush(priv); |
| _ewk_tiled_backing_store_tile_dissociate_all(priv); |
| tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix); |
| ewk_tile_unused_cache_clear(tuc); |
| |
| #ifdef DEBUG_MEM_LEAKS |
| printf("\nFLUSHED BACKING STORE, STATUS AFTER RECREATING TILE MATRIX:\n"); |
| _ewk_tiled_backing_store_mem_dbg(priv); |
| #endif |
| } |
| |
| 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) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE); |
| Eina_Tile_Grid_Slicer slicer; |
| const Eina_Tile_Grid_Info *info; |
| Evas_Coord tw, th; |
| Ewk_Tile_Unused_Cache *tuc; |
| |
| tw = TILE_SIZE_AT_ZOOM(TILE_W, zoom); |
| tw = (tw >> 1) << 1; |
| zoom = TILE_W_ZOOM_AT_SIZE(tw); |
| /* WARNING: assume reverse zoom is the same for both axis */ |
| th = TILE_SIZE_AT_ZOOM(TILE_H, zoom); |
| |
| if (!eina_tile_grid_slicer_setup(&slicer, x, y, w, h, tw, th)) { |
| ERR("could not setup grid slicer for %d,%d+%dx%d tile=%dx%d", |
| x, y, w, h, tw, th); |
| return EINA_FALSE; |
| } |
| |
| while (eina_tile_grid_slicer_next(&slicer, &info)) { |
| const unsigned long c = info->col; |
| const unsigned long r = info->row; |
| if (!_ewk_tiled_backing_store_pre_render_request_add(priv, c, r, zoom, PRE_RENDER_PRIORITY_LOW)) |
| break; |
| } |
| |
| _ewk_tiled_backing_store_item_process_idler_start(priv); |
| |
| tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix); |
| ewk_tile_unused_cache_lock_area(tuc, x, y, w, h, zoom); |
| return EINA_TRUE; |
| } |
| |
| Eina_Bool ewk_tiled_backing_store_pre_render_relative_radius(Evas_Object *o, unsigned int n, float zoom) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE); |
| unsigned long start_row, end_row, start_col, end_col, i, j, w, h; |
| Ewk_Tile_Unused_Cache *tuc; |
| |
| INF("priv->model.base.row =%ld, n=%u priv->view.rows=%lu", |
| priv->model.base.row, n, priv->view.rows); |
| start_row = (long)priv->model.base.row - n; |
| start_col = (long)priv->model.base.col - n; |
| end_row = MIN(priv->model.cur.rows - 1, |
| priv->model.base.row + priv->view.rows + n - 1); |
| end_col = MIN(priv->model.cur.cols - 1, |
| priv->model.base.col + priv->view.cols + n - 1); |
| |
| INF("start_row=%lu, end_row=%lu, start_col=%lu, end_col=%lu", |
| start_row, end_row, start_col, end_col); |
| |
| for (i = start_row; i <= end_row; i++) |
| for (j = start_col; j <= end_col; j++) |
| if (!_ewk_tiled_backing_store_pre_render_request_add(priv, j, i, zoom, PRE_RENDER_PRIORITY_LOW)) |
| goto start_processing; |
| |
| start_processing: |
| _ewk_tiled_backing_store_item_process_idler_start(priv); |
| |
| tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix); |
| h = (end_row - start_row + 1) * TILE_SIZE_AT_ZOOM(TILE_H, zoom); |
| w = (end_col - start_col + 1) * TILE_SIZE_AT_ZOOM(TILE_W, zoom); |
| ewk_tile_unused_cache_lock_area(tuc, |
| start_col * TILE_SIZE_AT_ZOOM(TILE_W, zoom), |
| start_row * TILE_SIZE_AT_ZOOM(TILE_H, zoom), w, h, zoom); |
| |
| return EINA_TRUE; |
| } |
| |
| void ewk_tiled_backing_store_pre_render_cancel(Evas_Object *o) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| Ewk_Tile_Unused_Cache *tuc; |
| |
| _ewk_tiled_backing_store_pre_render_request_clear(priv); |
| |
| tuc = ewk_tile_matrix_unused_cache_get(priv->model.matrix); |
| ewk_tile_unused_cache_unlock_area(tuc); |
| } |
| |
| Eina_Bool ewk_tiled_backing_store_disable_render(Evas_Object *o) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE); |
| return _ewk_tiled_backing_store_disable_render(priv); |
| } |
| |
| Eina_Bool ewk_tiled_backing_store_enable_render(Evas_Object *o) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv, EINA_FALSE); |
| _ewk_tiled_backing_store_changed(priv); |
| return _ewk_tiled_backing_store_enable_render(priv); |
| } |
| |
| /** |
| * Set the process_entire_queue flag of the renderer idler. |
| * |
| * |
| * @param o the tiled backing store object |
| * @param value EINA_TRUE if we want to process all the request of our queue. EINA_FALSE otherwise. |
| */ |
| void ewk_tiled_backing_store_process_entire_queue_set(Evas_Object *o, Eina_Bool value) |
| { |
| PRIV_DATA_GET_OR_RETURN(o, priv); |
| if (priv->render.process_entire_queue != value) |
| priv->render.process_entire_queue = value; |
| } |