2010-11-22 Alex Grilo <abgrilo@profusion.mobi>
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Nov 2010 02:35:10 +0000 (02:35 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 23 Nov 2010 02:35:10 +0000 (02:35 +0000)
        Reviewed by Kenneth Rohde Christiansen.

        [EFL] Tiled Backing Store for Webkit-Efl
        https://bugs.webkit.org/show_bug.cgi?id=45397

        This tile based backing store is a grid of tiles where each tile is
        an Evas_Object showing a piece of the page content. The
        new ewk_view_tiled.c is an implementation of some of the functions of
        the original ewk_view, that makes calls to the Tiled Backing Store
        specific functions.

        The rendering occurs in the pixels stored in each tile (Evas_Object),
        in the same way as the previous single backing store. Tiles not being
        shown in the viewport are stored in a cache, and can be reused later
        to avoid having to be rendered again.

        Scrolling occurs as just moving the tiles, using Evas move operations,
        and when more than one tile size is scrolled, a shift of rows or
        columns needs to be done.

        * CMakeListsEfl.txt:
        * ewk/ewk_tiled_backing_store.c: Added.
        (_ewk_tiled_backing_store_updates_process):
        (_ewk_tiled_backing_store_flush):
        (_ewk_tiled_backing_store_tile_new):
        (_ewk_tiled_backing_store_item_move):
        (_ewk_tiled_backing_store_item_resize):
        (_ewk_tiled_backing_store_tile_associate):
        (_ewk_tiled_backing_store_tile_dissociate):
        (_ewk_tiled_backing_store_tile_dissociate_all):
        (_ewk_tiled_backing_store_pre_render_request_add):
        (_ewk_tiled_backing_store_pre_render_request_del):
        (_ewk_tiled_backing_store_pre_render_request_first):
        (_ewk_tiled_backing_store_pre_render_request_flush):
        (_ewk_tiled_backing_store_pre_render_request_remove_unassociated):
        (_ewk_tiled_backing_store_pre_render_request_remove_associated):
        (_ewk_tiled_backing_store_pre_render_request_process_single):
        (_ewk_tiled_backing_store_item_process_idler_cb):
        (_ewk_tiled_backing_store_item_process_idler_stop):
        (_ewk_tiled_backing_store_item_process_idler_start):
        (_ewk_tiled_backing_store_item_request_del):
        (_ewk_tiled_backing_store_item_request_add):
        (_ewk_tiled_backing_store_disable_render):
        (_ewk_tiled_backing_store_enable_render):
        (_ewk_tiled_backing_store_visible_tiles_rect):
        (_ewk_tiled_backing_store_tile_is_inside_viewport):
        (_ewk_tiled_backing_store_tile_is_adjacent_to_viewport):
        (_ewk_tiled_backing_store_item_fill):
        (_ewk_tiled_backing_store_item_add):
        (_ewk_tiled_backing_store_item_del):
        (_ewk_tiled_backing_store_item_smooth_scale_set):
        (_ewk_tiled_backing_store_changed):
        (_ewk_tiled_backing_store_view_cols_end_del):
        (_ewk_tiled_backing_store_view_cols_end_add):
        (_ewk_tiled_backing_store_view_row_del):
        (_ewk_tiled_backing_store_view_rows_range_del):
        (_ewk_tiled_backing_store_view_rows_all_del):
        (_ewk_tiled_backing_store_render):
        (_ewk_tiled_backing_store_model_matrix_create):
        (_ewk_tiled_backing_store_smart_member_del):
        (_ewk_tiled_backing_store_smart_member_add):
        (_ewk_tiled_backing_store_mem_dbg):
        (_ewk_tiled_backing_store_sig_usr):
        (_ewk_tiled_backing_store_smart_add):
        (_ewk_tiled_backing_store_smart_del):
        (_ewk_tiled_backing_store_smart_move):
        (_ewk_tiled_backing_store_smart_resize):
        (_ewk_tiled_backing_store_recalc_renderers):
        (_ewk_tiled_backing_store_smart_calculate_size):
        (_ewk_tiled_backing_store_view_dbg):
        (_ewk_tiled_backing_store_view_wrap_up):
        (_ewk_tiled_backing_store_view_wrap_down):
        (_ewk_tiled_backing_store_view_wrap_left):
        (_ewk_tiled_backing_store_view_wrap_right):
        (_ewk_tiled_backing_store_view_refill):
        (_ewk_tiled_backing_store_view_pos_apply):
        (_ewk_tiled_backing_store_smart_calculate_offset_force):
        (_ewk_tiled_backing_store_smart_calculate_offset):
        (_ewk_tiled_backing_store_smart_calculate_pos):
        (_ewk_tiled_backing_store_fill_renderers):
        (_ewk_tiled_backing_store_smart_calculate):
        (ewk_tiled_backing_store_add):
        (ewk_tiled_backing_store_render_cb_set):
        (ewk_tiled_backing_store_tile_unused_cache_get):
        (ewk_tiled_backing_store_tile_unused_cache_set):
        (_ewk_tiled_backing_store_scroll_full_offset_set_internal):
        (ewk_tiled_backing_store_scroll_full_offset_set):
        (ewk_tiled_backing_store_scroll_full_offset_add):
        (_ewk_tiled_backing_store_zoom_set_internal):
        (ewk_tiled_backing_store_zoom_set):
        (ewk_tiled_backing_store_zoom_weak_set):
        (ewk_tiled_backing_store_fix_offsets):
        (ewk_tiled_backing_store_zoom_weak_smooth_scale_set):
        (ewk_tiled_backing_store_update):
        (ewk_tiled_backing_store_updates_process_pre_set):
        (ewk_tiled_backing_store_updates_process_post_set):
        (ewk_tiled_backing_store_updates_process):
        (ewk_tiled_backing_store_updates_clear):
        (ewk_tiled_backing_store_contents_resize):
        (ewk_tiled_backing_store_disabled_update_set):
        (ewk_tiled_backing_store_flush):
        (ewk_tiled_backing_store_pre_render_region):
        (ewk_tiled_backing_store_pre_render_relative_radius):
        (ewk_tiled_backing_store_pre_render_cancel):
        (ewk_tiled_backing_store_disable_render):
        (ewk_tiled_backing_store_enable_render):
        (ewk_tiled_backing_store_process_entire_queue_set):
        * ewk/ewk_tiled_backing_store.h: Added.
        * ewk/ewk_tiled_matrix.c: Added.
        (_ewk_tile_matrix_cell_free):
        (_ewk_tile_matrix_tile_free):
        (ewk_tile_matrix_new):
        (ewk_tile_matrix_free):
        (ewk_tile_matrix_resize):
        (ewk_tile_matrix_unused_cache_get):
        (ewk_tile_matrix_tile_exact_get):
        (ewk_tile_matrix_tile_exact_exists):
        (ewk_tile_matrix_tile_nearest_get):
        (ewk_tile_matrix_tile_new):
        (ewk_tile_matrix_tile_put):
        (ewk_tile_matrix_tile_update):
        (ewk_tile_matrix_tile_update_full):
        (ewk_tile_matrix_tile_updates_clear):
        (_ewk_tile_matrix_slicer_setup):
        (ewk_tile_matrix_update):
        (ewk_tile_matrix_updates_process):
        (ewk_tile_matrix_updates_clear):
        (ewk_tile_matrix_dbg):
        (ewk_tile_matrix_freeze):
        (ewk_tile_matrix_thaw):
        * ewk/ewk_tiled_matrix.h: Added.
        * ewk/ewk_tiled_model.c: Added.
        (_ewk_tile_account_get):
        (_ewk_tile_account_allocated):
        (_ewk_tile_account_freed):
        (ewk_tile_accounting_dbg):
        (_ewk_tile_paint_rgb888):
        (_ewk_tile_paint_rgb565):
        (_ewk_tile_paint):
        (ewk_tile_new):
        (ewk_tile_free):
        (ewk_tile_show):
        (ewk_tile_hide):
        (ewk_tile_visible_get):
        (ewk_tile_update_full):
        (ewk_tile_update_area):
        (ewk_tile_updates_process):
        (ewk_tile_updates_clear):
        (ewk_tile_unused_cache_new):
        (ewk_tile_unused_cache_lock_area):
        (ewk_tile_unused_cache_unlock_area):
        (ewk_tile_unused_cache_free):
        (ewk_tile_unused_cache_clear):
        (ewk_tile_unused_cache_ref):
        (ewk_tile_unused_cache_unref):
        (ewk_tile_unused_cache_max_set):
        (ewk_tile_unused_cache_max_get):
        (ewk_tile_unused_cache_used_get):
        (ewk_tile_unused_cache_flush):
        (ewk_tile_unused_cache_auto_flush):
        (ewk_tile_unused_cache_dirty):
        (ewk_tile_unused_cache_freeze):
        (ewk_tile_unused_cache_thaw):
        (ewk_tile_unused_cache_tile_get):
        (ewk_tile_unused_cache_tile_put):
        (ewk_tile_unused_cache_dbg):
        * ewk/ewk_tiled_model.h: Added.
        * ewk/ewk_tiled_private.h: Added.
        * ewk/ewk_view.cpp:
        (_ewk_view_smart_add):
        (_ewk_view_smart_calculate):
        (_ewk_view_smart_show):
        (_ewk_view_smart_hide):
        (_ewk_view_smart_pre_render_relative_radius):
        (_ewk_view_zoom_animator_cb):
        (_ewk_view_smart_disable_render):
        (_ewk_view_smart_enable_render):
        (ewk_view_base_smart_set):
        (ewk_view_pre_render_region):
        (ewk_view_pre_render_relative_radius):
        (ewk_view_enable_render):
        (ewk_view_disable_render):
        (ewk_view_scroll):
        (ewk_view_did_first_visually_nonempty_layout):
        (ewk_view_dispatch_did_finish_loading):
        (ewk_view_transition_to_commited_for_newpage):
        * ewk/ewk_view.h:
        * ewk/ewk_view_tiled.c: Added.
        (_ewk_view_tiled_render_cb):
        (_ewk_view_tiled_updates_process_pre):
        (_ewk_view_tiled_smart_backing_store_add):
        (_ewk_view_tiled_contents_size_changed_cb):
        (_ewk_view_tiled_smart_add):
        (_ewk_view_tiled_smart_scrolls_process):
        (_ewk_view_tiled_smart_repaints_process):
        (_ewk_view_tiled_smart_contents_resize):
        (_ewk_view_tiled_smart_zoom_set):
        (_ewk_view_tiled_smart_zoom_weak_set):
        (_ewk_view_tiled_smart_zoom_weak_smooth_scale_set):
        (_ewk_view_tiled_smart_flush):
        (_ewk_view_tiled_smart_pre_render_region):
        (_ewk_view_tiled_smart_pre_render_relative_radius):
        (_ewk_view_tiled_smart_pre_render_cancel):
        (_ewk_view_tiled_smart_disable_render):
        (_ewk_view_tiled_smart_enable_render):
        (ewk_view_tiled_smart_set):
        (_ewk_view_tiled_smart_class_new):
        (ewk_view_tiled_add):
        (ewk_view_tiled_unused_cache_get):
        (ewk_view_tiled_unused_cache_set):
        (ewk_view_tiled_process_entire_queue_set):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@72579 268f45cc-cd09-0410-ab3c-d52691b4dbfc

12 files changed:
WebKit/efl/CMakeListsEfl.txt
WebKit/efl/ChangeLog
WebKit/efl/ewk/ewk_tiled_backing_store.c [new file with mode: 0644]
WebKit/efl/ewk/ewk_tiled_backing_store.h [new file with mode: 0644]
WebKit/efl/ewk/ewk_tiled_matrix.c [new file with mode: 0644]
WebKit/efl/ewk/ewk_tiled_matrix.h [new file with mode: 0644]
WebKit/efl/ewk/ewk_tiled_model.c [new file with mode: 0644]
WebKit/efl/ewk/ewk_tiled_model.h [new file with mode: 0644]
WebKit/efl/ewk/ewk_tiled_private.h [new file with mode: 0644]
WebKit/efl/ewk/ewk_view.cpp
WebKit/efl/ewk/ewk_view.h
WebKit/efl/ewk/ewk_view_tiled.c [new file with mode: 0644]

index 7abbc17..fb0ccc4 100644 (file)
@@ -63,9 +63,13 @@ LIST(APPEND WebKit_SOURCES
     efl/ewk/ewk_history.cpp
     efl/ewk/ewk_main.cpp
     efl/ewk/ewk_settings.cpp
+    efl/ewk/ewk_tiled_backing_store.c
+    efl/ewk/ewk_tiled_matrix.c
+    efl/ewk/ewk_tiled_model.c
     efl/ewk/ewk_util.cpp
     efl/ewk/ewk_view.cpp
     efl/ewk/ewk_view_single.c
+    efl/ewk/ewk_view_tiled.c
     efl/ewk/ewk_window_features.cpp
 )
 
index bb2cf79..7a12fd7 100644 (file)
@@ -1,3 +1,217 @@
+2010-11-22  Alex Grilo  <abgrilo@profusion.mobi>
+
+        Reviewed by Kenneth Rohde Christiansen.
+
+        [EFL] Tiled Backing Store for Webkit-Efl
+        https://bugs.webkit.org/show_bug.cgi?id=45397
+
+        This tile based backing store is a grid of tiles where each tile is
+        an Evas_Object showing a piece of the page content. The
+        new ewk_view_tiled.c is an implementation of some of the functions of
+        the original ewk_view, that makes calls to the Tiled Backing Store
+        specific functions.
+
+        The rendering occurs in the pixels stored in each tile (Evas_Object),
+        in the same way as the previous single backing store. Tiles not being
+        shown in the viewport are stored in a cache, and can be reused later
+        to avoid having to be rendered again.
+
+        Scrolling occurs as just moving the tiles, using Evas move operations,
+        and when more than one tile size is scrolled, a shift of rows or
+        columns needs to be done.
+
+        * CMakeListsEfl.txt:
+        * ewk/ewk_tiled_backing_store.c: Added.
+        (_ewk_tiled_backing_store_updates_process):
+        (_ewk_tiled_backing_store_flush):
+        (_ewk_tiled_backing_store_tile_new):
+        (_ewk_tiled_backing_store_item_move):
+        (_ewk_tiled_backing_store_item_resize):
+        (_ewk_tiled_backing_store_tile_associate):
+        (_ewk_tiled_backing_store_tile_dissociate):
+        (_ewk_tiled_backing_store_tile_dissociate_all):
+        (_ewk_tiled_backing_store_pre_render_request_add):
+        (_ewk_tiled_backing_store_pre_render_request_del):
+        (_ewk_tiled_backing_store_pre_render_request_first):
+        (_ewk_tiled_backing_store_pre_render_request_flush):
+        (_ewk_tiled_backing_store_pre_render_request_remove_unassociated):
+        (_ewk_tiled_backing_store_pre_render_request_remove_associated):
+        (_ewk_tiled_backing_store_pre_render_request_process_single):
+        (_ewk_tiled_backing_store_item_process_idler_cb):
+        (_ewk_tiled_backing_store_item_process_idler_stop):
+        (_ewk_tiled_backing_store_item_process_idler_start):
+        (_ewk_tiled_backing_store_item_request_del):
+        (_ewk_tiled_backing_store_item_request_add):
+        (_ewk_tiled_backing_store_disable_render):
+        (_ewk_tiled_backing_store_enable_render):
+        (_ewk_tiled_backing_store_visible_tiles_rect):
+        (_ewk_tiled_backing_store_tile_is_inside_viewport):
+        (_ewk_tiled_backing_store_tile_is_adjacent_to_viewport):
+        (_ewk_tiled_backing_store_item_fill):
+        (_ewk_tiled_backing_store_item_add):
+        (_ewk_tiled_backing_store_item_del):
+        (_ewk_tiled_backing_store_item_smooth_scale_set):
+        (_ewk_tiled_backing_store_changed):
+        (_ewk_tiled_backing_store_view_cols_end_del):
+        (_ewk_tiled_backing_store_view_cols_end_add):
+        (_ewk_tiled_backing_store_view_row_del):
+        (_ewk_tiled_backing_store_view_rows_range_del):
+        (_ewk_tiled_backing_store_view_rows_all_del):
+        (_ewk_tiled_backing_store_render):
+        (_ewk_tiled_backing_store_model_matrix_create):
+        (_ewk_tiled_backing_store_smart_member_del):
+        (_ewk_tiled_backing_store_smart_member_add):
+        (_ewk_tiled_backing_store_mem_dbg):
+        (_ewk_tiled_backing_store_sig_usr):
+        (_ewk_tiled_backing_store_smart_add):
+        (_ewk_tiled_backing_store_smart_del):
+        (_ewk_tiled_backing_store_smart_move):
+        (_ewk_tiled_backing_store_smart_resize):
+        (_ewk_tiled_backing_store_recalc_renderers):
+        (_ewk_tiled_backing_store_smart_calculate_size):
+        (_ewk_tiled_backing_store_view_dbg):
+        (_ewk_tiled_backing_store_view_wrap_up):
+        (_ewk_tiled_backing_store_view_wrap_down):
+        (_ewk_tiled_backing_store_view_wrap_left):
+        (_ewk_tiled_backing_store_view_wrap_right):
+        (_ewk_tiled_backing_store_view_refill):
+        (_ewk_tiled_backing_store_view_pos_apply):
+        (_ewk_tiled_backing_store_smart_calculate_offset_force):
+        (_ewk_tiled_backing_store_smart_calculate_offset):
+        (_ewk_tiled_backing_store_smart_calculate_pos):
+        (_ewk_tiled_backing_store_fill_renderers):
+        (_ewk_tiled_backing_store_smart_calculate):
+        (ewk_tiled_backing_store_add):
+        (ewk_tiled_backing_store_render_cb_set):
+        (ewk_tiled_backing_store_tile_unused_cache_get):
+        (ewk_tiled_backing_store_tile_unused_cache_set):
+        (_ewk_tiled_backing_store_scroll_full_offset_set_internal):
+        (ewk_tiled_backing_store_scroll_full_offset_set):
+        (ewk_tiled_backing_store_scroll_full_offset_add):
+        (_ewk_tiled_backing_store_zoom_set_internal):
+        (ewk_tiled_backing_store_zoom_set):
+        (ewk_tiled_backing_store_zoom_weak_set):
+        (ewk_tiled_backing_store_fix_offsets):
+        (ewk_tiled_backing_store_zoom_weak_smooth_scale_set):
+        (ewk_tiled_backing_store_update):
+        (ewk_tiled_backing_store_updates_process_pre_set):
+        (ewk_tiled_backing_store_updates_process_post_set):
+        (ewk_tiled_backing_store_updates_process):
+        (ewk_tiled_backing_store_updates_clear):
+        (ewk_tiled_backing_store_contents_resize):
+        (ewk_tiled_backing_store_disabled_update_set):
+        (ewk_tiled_backing_store_flush):
+        (ewk_tiled_backing_store_pre_render_region):
+        (ewk_tiled_backing_store_pre_render_relative_radius):
+        (ewk_tiled_backing_store_pre_render_cancel):
+        (ewk_tiled_backing_store_disable_render):
+        (ewk_tiled_backing_store_enable_render):
+        (ewk_tiled_backing_store_process_entire_queue_set):
+        * ewk/ewk_tiled_backing_store.h: Added.
+        * ewk/ewk_tiled_matrix.c: Added.
+        (_ewk_tile_matrix_cell_free):
+        (_ewk_tile_matrix_tile_free):
+        (ewk_tile_matrix_new):
+        (ewk_tile_matrix_free):
+        (ewk_tile_matrix_resize):
+        (ewk_tile_matrix_unused_cache_get):
+        (ewk_tile_matrix_tile_exact_get):
+        (ewk_tile_matrix_tile_exact_exists):
+        (ewk_tile_matrix_tile_nearest_get):
+        (ewk_tile_matrix_tile_new):
+        (ewk_tile_matrix_tile_put):
+        (ewk_tile_matrix_tile_update):
+        (ewk_tile_matrix_tile_update_full):
+        (ewk_tile_matrix_tile_updates_clear):
+        (_ewk_tile_matrix_slicer_setup):
+        (ewk_tile_matrix_update):
+        (ewk_tile_matrix_updates_process):
+        (ewk_tile_matrix_updates_clear):
+        (ewk_tile_matrix_dbg):
+        (ewk_tile_matrix_freeze):
+        (ewk_tile_matrix_thaw):
+        * ewk/ewk_tiled_matrix.h: Added.
+        * ewk/ewk_tiled_model.c: Added.
+        (_ewk_tile_account_get):
+        (_ewk_tile_account_allocated):
+        (_ewk_tile_account_freed):
+        (ewk_tile_accounting_dbg):
+        (_ewk_tile_paint_rgb888):
+        (_ewk_tile_paint_rgb565):
+        (_ewk_tile_paint):
+        (ewk_tile_new):
+        (ewk_tile_free):
+        (ewk_tile_show):
+        (ewk_tile_hide):
+        (ewk_tile_visible_get):
+        (ewk_tile_update_full):
+        (ewk_tile_update_area):
+        (ewk_tile_updates_process):
+        (ewk_tile_updates_clear):
+        (ewk_tile_unused_cache_new):
+        (ewk_tile_unused_cache_lock_area):
+        (ewk_tile_unused_cache_unlock_area):
+        (ewk_tile_unused_cache_free):
+        (ewk_tile_unused_cache_clear):
+        (ewk_tile_unused_cache_ref):
+        (ewk_tile_unused_cache_unref):
+        (ewk_tile_unused_cache_max_set):
+        (ewk_tile_unused_cache_max_get):
+        (ewk_tile_unused_cache_used_get):
+        (ewk_tile_unused_cache_flush):
+        (ewk_tile_unused_cache_auto_flush):
+        (ewk_tile_unused_cache_dirty):
+        (ewk_tile_unused_cache_freeze):
+        (ewk_tile_unused_cache_thaw):
+        (ewk_tile_unused_cache_tile_get):
+        (ewk_tile_unused_cache_tile_put):
+        (ewk_tile_unused_cache_dbg):
+        * ewk/ewk_tiled_model.h: Added.
+        * ewk/ewk_tiled_private.h: Added.
+        * ewk/ewk_view.cpp:
+        (_ewk_view_smart_add):
+        (_ewk_view_smart_calculate):
+        (_ewk_view_smart_show):
+        (_ewk_view_smart_hide):
+        (_ewk_view_smart_pre_render_relative_radius):
+        (_ewk_view_zoom_animator_cb):
+        (_ewk_view_smart_disable_render):
+        (_ewk_view_smart_enable_render):
+        (ewk_view_base_smart_set):
+        (ewk_view_pre_render_region):
+        (ewk_view_pre_render_relative_radius):
+        (ewk_view_enable_render):
+        (ewk_view_disable_render):
+        (ewk_view_scroll):
+        (ewk_view_did_first_visually_nonempty_layout):
+        (ewk_view_dispatch_did_finish_loading):
+        (ewk_view_transition_to_commited_for_newpage):
+        * ewk/ewk_view.h:
+        * ewk/ewk_view_tiled.c: Added.
+        (_ewk_view_tiled_render_cb):
+        (_ewk_view_tiled_updates_process_pre):
+        (_ewk_view_tiled_smart_backing_store_add):
+        (_ewk_view_tiled_contents_size_changed_cb):
+        (_ewk_view_tiled_smart_add):
+        (_ewk_view_tiled_smart_scrolls_process):
+        (_ewk_view_tiled_smart_repaints_process):
+        (_ewk_view_tiled_smart_contents_resize):
+        (_ewk_view_tiled_smart_zoom_set):
+        (_ewk_view_tiled_smart_zoom_weak_set):
+        (_ewk_view_tiled_smart_zoom_weak_smooth_scale_set):
+        (_ewk_view_tiled_smart_flush):
+        (_ewk_view_tiled_smart_pre_render_region):
+        (_ewk_view_tiled_smart_pre_render_relative_radius):
+        (_ewk_view_tiled_smart_pre_render_cancel):
+        (_ewk_view_tiled_smart_disable_render):
+        (_ewk_view_tiled_smart_enable_render):
+        (ewk_view_tiled_smart_set):
+        (_ewk_view_tiled_smart_class_new):
+        (ewk_view_tiled_add):
+        (ewk_view_tiled_unused_cache_get):
+        (ewk_view_tiled_unused_cache_set):
+        (ewk_view_tiled_process_entire_queue_set):
+
 2010-11-16  Leandro Pereira  <leandro@profusion.mobi>
 
         [EFL] Unreviewed. Build fix.
diff --git a/WebKit/efl/ewk/ewk_tiled_backing_store.c b/WebKit/efl/ewk/ewk_tiled_backing_store.c
new file mode 100644 (file)
index 0000000..0e8898b
--- /dev/null
@@ -0,0 +1,2137 @@
+/*
+    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;
+    Ewk_Tiled_Backing_Store_Pre_Render_Request *pre_render;
+    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_Item *it;
+};
+
+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_Item *it, 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;
+    r->it = it;
+
+    if (it)
+        it->pre_render = r;
+    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);
+        if (r->it && r->it->pre_render)
+            r->it->pre_render = NULL;
+        *pl = eina_inlist_remove(*pl, *pl);
+        free(r);
+    }
+}
+
+static void _ewk_tiled_backing_store_pre_render_request_remove_unassociated(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);
+        if (!r->it) {
+            tmp = iter->next;
+            *pl = eina_inlist_remove(*pl, iter);
+            iter = tmp;
+            free(r);
+        } else
+            iter = iter->next;
+    }
+}
+
+static void _ewk_tiled_backing_store_pre_render_request_remove_associated(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);
+        if (r->it) {
+            if (r->it->pre_render)
+                r->it->pre_render = NULL;
+            tmp = iter->next;
+            *pl = eina_inlist_remove(*pl, iter);
+            iter = tmp;
+            free(r);
+        } else
+            iter = iter->next;
+    }
+}
+
+/* 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;
+    }
+
+    if (req->it && req->it->tile) {
+        CRITICAL("it->tile = %p (%lu, %lu), but should be NULL", req->it->tile, req->it->tile->row, req->it->tile->col);
+        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);
+
+    if (req->it) {
+        _ewk_tiled_backing_store_tile_associate(priv, t, req->it);
+        if (req->it->pre_render)
+            req->it->pre_render = NULL;
+    } else
+        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;
+}
+
+/**
+ * Returns a rectangle whose coordinates indicate which tiles are
+ * currently inside the viewport.
+ *
+ * Specifically, the returned rectangle's coordinates have the
+ * following meaning:
+ *  - x: Column number for the top-level tile inside the viewport.
+ *  - y: Row number for the top-level tile inside the viewport.
+ *  - w: Number of tiles horizontally inside the viewport.
+ *  - h: Number of tiles vertically inside the viewport.
+ */
+static Eina_Rectangle _ewk_tiled_backing_store_visible_tiles_rect(Ewk_Tiled_Backing_Store_Data *priv)
+{
+     Eina_Rectangle r;
+
+     const Evas_Coord ox = -priv->view.offset.cur.x;
+     const Evas_Coord oy = -priv->view.offset.cur.y;
+     const Evas_Coord tw = priv->view.tile.w;
+     const Evas_Coord th = priv->view.tile.h;
+
+     r.x = MAX(0, ox / tw);
+     r.y = MAX(0, oy / th);
+     r.w = MIN(priv->model.cur.cols,
+               ceil((float)(priv->view.w + (ox % tw)) / tw));
+     r.h = MIN(priv->model.cur.rows,
+               ceil((float)(priv->view.h + (oy % th)) / th));
+
+     DBG("Returning %d,%d+%dx%d", r.x, r.y, r.w, r.h);
+
+     return r;
+}
+
+static Eina_Bool _ewk_tiled_backing_store_tile_is_inside_viewport(Ewk_Tiled_Backing_Store_Data *priv, int col, int row)
+{
+    const Eina_Rectangle r = _ewk_tiled_backing_store_visible_tiles_rect(priv);
+
+    return eina_rectangle_coords_inside(&r, col, row);
+}
+
+static Eina_Bool _ewk_tiled_backing_store_tile_is_adjacent_to_viewport(Ewk_Tiled_Backing_Store_Data *priv, int col, int row)
+{
+    const Eina_Rectangle r = _ewk_tiled_backing_store_visible_tiles_rect(priv);
+
+    if (row == (r.y - 1) || row == (r.y + r.h))
+        return (col >= (r.x - 1) && col <= (r.x + r.w));
+
+    if (col == (r.x - 1) || col == (r.x + r.w))
+        return (row >= (r.y - 1) && row <= (r.y + r.h));
+
+    return EINA_FALSE;
+}
+
+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)
+{
+    int m_col = priv->model.base.col + col;
+    int m_row = priv->model.base.row + row;
+    double last_used = ecore_loop_time_get();
+
+    if (m_col < 0 || m_row < 0
+        || m_col >= priv->model.cur.cols || 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 == m_row
+                && it->update.col == m_col
+                && it->update.zoom == zoom)
+                return EINA_TRUE;
+
+            _ewk_tiled_backing_store_item_request_del(priv, it);
+        }
+
+        if (it->pre_render) {
+            _ewk_tiled_backing_store_pre_render_request_del(
+                priv, it->pre_render);
+            it->pre_render = NULL;
+        }
+
+        if (it->tile) {
+            Ewk_Tile *old = it->tile;
+            if (old->row != m_row || old->col != 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 == m_row && old->col == 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) {
+                if (!_ewk_tiled_backing_store_tile_is_inside_viewport(
+                        priv, m_col, m_row)) {
+                    DBG("%d,%d is not inside the viewport", m_col, m_row);
+                    if (_ewk_tiled_backing_store_tile_is_adjacent_to_viewport(
+                            priv, m_col, m_row))
+                        _ewk_tiled_backing_store_pre_render_request_add(
+                            priv, m_col, m_row, zoom, it,
+                            PRE_RENDER_PRIORITY_HIGH);
+                    _ewk_tiled_backing_store_item_process_idler_start(priv);
+
+                    goto end;
+                }
+
+                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;
+    it->pre_render = NULL;
+    _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);
+    if (it->pre_render) {
+        _ewk_tiled_backing_store_pre_render_request_del(
+            priv, it->pre_render);
+        it->pre_render = NULL;
+    }
+    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 = -TILE_W;
+    priv->view.offset.base.y = -TILE_H;
+
+    priv->model.base.col = -1;
+    priv->model.base.row = -1;
+    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 = 2 + (int)ceil((float)w / (float)tw);
+    rows = 2 + (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;
+
+    _ewk_tiled_backing_store_pre_render_request_remove_associated(priv);
+
+    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 % th) - 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 % th) - 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 % tw) - 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 % tw) - 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;
+
+    step_x = (dx + priv->view.offset.base.x + tw) / tw;
+    step_y = (dy + priv->view.offset.base.y + th) / th;
+
+    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;
+
+    step_x = (dx + priv->view.offset.base.x + tw) / tw;
+    step_y = (dy + priv->view.offset.base.y + th) / th;
+
+    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) {
+        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 <= -2 * priv->view.tile.w
+        || priv->view.offset.base.y >= 0
+        || priv->view.offset.base.y <= -2 * 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 = -tw;
+        priv->view.offset.base.y = -th;
+        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 - tw;
+    priv->model.base.col = - new_x / tw - 1;
+    by = new_y % th - th;
+    priv->model.base.row = - new_y / th - 1;
+
+    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) + 2;
+    int vcols = ceil((float)priv->view.w / (float)tw) + 2;
+    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 - tw;
+    Evas_Coord by = new_y % th - th;
+    unsigned long base_row = -new_y / th - 1;
+    unsigned long base_col = -new_x / tw - 1;
+
+    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 - tw;
+        priv->model.base.col = -new_x / tw - 1;
+    }
+
+    if (-new_y > h) {
+        new_y = -h;
+        by = new_y % th - th;
+        priv->model.base.row = -new_y / th - 1;
+    }
+
+    if (bx >= 0 || bx <= -2 * priv->view.tile.w) {
+        bx = new_x % tw - tw;
+        priv->model.base.col = -new_x / tw - 1;
+    }
+
+    if (by >= 0 || by <= -2 * priv->view.tile.h) {
+        by = new_y % th - th;
+        priv->model.base.row = -new_y / th - 1;
+    }
+
+    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 = -priv->view.tile.w;
+    priv->view.offset.base.y = -priv->view.tile.h;
+    priv->model.base.col = -1;
+    priv->model.base.row = -1;
+    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, NULL, 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, NULL, 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_remove_unassociated(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;
+}
diff --git a/WebKit/efl/ewk/ewk_tiled_backing_store.h b/WebKit/efl/ewk/ewk_tiled_backing_store.h
new file mode 100644 (file)
index 0000000..c8cdbb8
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+    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.
+*/
+
+#ifndef ewk_tiled_backing_store_h
+#define ewk_tiled_backing_store_h
+
+#include "EWebKit.h"
+
+/* Enable accounting of render time in tile statistics */
+// #define TILE_STATS_ACCOUNT_RENDER_TIME
+
+
+/* If define ewk will do more accounting to check for memory leaks
+ * try "kill -USR1 $PID" to get instantaneous debug
+ * try "kill -USR2 $PID" to get instantaneous debug and force flush of cache
+ */
+#define DEBUG_MEM_LEAKS 1
+
+#define TILE_W (256)
+#define TILE_H (256)
+
+#define ZOOM_STEP_MIN (0.01)
+
+#define TILE_SIZE_AT_ZOOM(SIZE, ZOOM) ((int)roundf((SIZE) * (ZOOM)))
+#define TILE_W_ZOOM_AT_SIZE(SIZE) ((float)SIZE / (float)TILE_W)
+#define TILE_H_ZOOM_AT_SIZE(SIZE) ((float)SIZE / (float)TILE_H)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <Evas.h>
+#include <cairo.h>
+
+typedef struct _Ewk_Tile                     Ewk_Tile;
+typedef struct _Ewk_Tile_Stats               Ewk_Tile_Stats;
+typedef struct _Ewk_Tile_Matrix              Ewk_Tile_Matrix;
+
+struct _Ewk_Tile_Stats {
+    double last_used;        /**< time of last use */
+#ifdef TILE_STATS_ACCOUNT_RENDER_TIME
+    double render_time;      /**< amount of time this tile took to render */
+#endif
+    unsigned int area;       /**< cache for (w * h) */
+    unsigned int misses;     /**< number of times it became dirty but not
+                              * repainted at all since it was not visible.
+                              */
+    Eina_Bool full_update:1; /**< tile requires full size update */
+};
+
+struct _Ewk_Tile {
+    EINA_INLIST;            /**< sibling tiles at same (i, j) but other zoom */
+    Eina_Tiler *updates;    /**< updated/dirty areas */
+    Ewk_Tile_Stats stats;       /**< tile usage statistics */
+    unsigned long col, row; /**< tile tile position */
+    Evas_Coord x, y;        /**< tile coordinate position */
+
+    /* TODO: does it worth to keep those or create on demand? */
+    cairo_surface_t *surface;
+    cairo_t *cairo;
+
+    /** Never ever change those after tile is created (respect const!) */
+    const Evas_Coord w, h;        /**< tile size (see TILE_SIZE_AT_ZOOM()) */
+    const Evas_Colorspace cspace; /**< colorspace */
+    const float zoom;             /**< zoom level contents were rendered at */
+    const size_t bytes;           /**< bytes used in pixels. keep
+                                   * before pixels to guarantee
+                                   * alignement!
+                                   */
+    int visible;                  /**< visibility counter of this tile */
+    Evas_Object *image;           /**< Evas Image, the tile to be rendered */
+    uint8_t *pixels;
+};
+
+#include "ewk_tiled_matrix.h"
+#include "ewk_tiled_model.h"
+
+/* view */
+EAPI Evas_Object *ewk_tiled_backing_store_add(Evas *e);
+
+EAPI 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);
+
+EAPI Eina_Bool    ewk_tiled_backing_store_scroll_full_offset_set(Evas_Object *o, Evas_Coord x, Evas_Coord y);
+EAPI Eina_Bool    ewk_tiled_backing_store_scroll_full_offset_add(Evas_Object *o, Evas_Coord dx, Evas_Coord dy);
+EAPI Eina_Bool    ewk_tiled_backing_store_scroll_inner_offset_add(Evas_Object *o, Evas_Coord dx, Evas_Coord dy, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h);
+
+EAPI 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);
+EAPI Eina_Bool    ewk_tiled_backing_store_zoom_weak_set(Evas_Object *o, float zoom, Evas_Coord cx, Evas_Coord cy);
+EAPI void ewk_tiled_backing_store_fix_offsets(Evas_Object *o, Evas_Coord w, Evas_Coord h);
+EAPI Eina_Bool    ewk_tiled_backing_store_update(Evas_Object *o, const Eina_Rectangle *update);
+EAPI void ewk_tiled_backing_store_updates_process_pre_set(Evas_Object *o, void *(*cb)(void *data, Evas_Object *o), const void *data);
+EAPI 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);
+EAPI void ewk_tiled_backing_store_process_entire_queue_set(Evas_Object *o, Eina_Bool value);
+EAPI void ewk_tiled_backing_store_updates_process(Evas_Object *o);
+EAPI void ewk_tiled_backing_store_updates_clear(Evas_Object *o);
+EAPI void ewk_tiled_backing_store_contents_resize(Evas_Object *o, Evas_Coord width, Evas_Coord height);
+EAPI void ewk_tiled_backing_store_disabled_update_set(Evas_Object *o, Eina_Bool value);
+EAPI void ewk_tiled_backing_store_flush(Evas_Object *o);
+EAPI void ewk_tiled_backing_store_enable_scale_set(Evas_Object *o, Eina_Bool value);
+
+EAPI Ewk_Tile_Unused_Cache *ewk_tiled_backing_store_tile_unused_cache_get(const Evas_Object *o);
+EAPI void                   ewk_tiled_backing_store_tile_unused_cache_set(Evas_Object *o, Ewk_Tile_Unused_Cache *tuc);
+
+EAPI 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);
+EAPI Eina_Bool ewk_tiled_backing_store_pre_render_relative_radius(Evas_Object *o, unsigned int n, float zoom);
+EAPI void ewk_tiled_backing_store_pre_render_cancel(Evas_Object *o);
+
+EAPI Eina_Bool ewk_tiled_backing_store_disable_render(Evas_Object *o);
+EAPI Eina_Bool ewk_tiled_backing_store_enable_render(Evas_Object *o);
+#ifdef __cplusplus
+}
+#endif
+#endif // ewk_tiled_backing_store_h
diff --git a/WebKit/efl/ewk/ewk_tiled_matrix.c b/WebKit/efl/ewk/ewk_tiled_matrix.c
new file mode 100644 (file)
index 0000000..fccbfa1
--- /dev/null
@@ -0,0 +1,771 @@
+/*
+    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_matrix.h"
+
+#define _GNU_SOURCE
+#include "ewk_tiled_backing_store.h"
+#include "ewk_tiled_private.h"
+#include <Eina.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <math.h>
+#include <stdio.h> // XXX remove me later
+#include <stdlib.h>
+#include <string.h>
+
+static const Evas_Coord TILE_MATRIX_BASE_TILE_SIZE = 256;
+
+struct _Ewk_Tile_Matrix {
+    Eina_Matrixsparse *matrix;
+    Ewk_Tile_Unused_Cache *tuc;
+    Evas_Colorspace cspace;
+    struct {
+        void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update);
+        void *data;
+    } render;
+    unsigned int frozen;
+    Eina_List *updates;
+#ifdef DEBUG_MEM_LEAKS
+    struct {
+        struct {
+            uint64_t allocated, freed;
+        } tiles, bytes;
+    } stats;
+#endif
+};
+
+#ifdef DEBUG_MEM_LEAKS
+static uint64_t tiles_leaked = 0;
+static uint64_t bytes_leaked = 0;
+#endif
+
+/* called when matrixsparse is resized or freed */
+static void _ewk_tile_matrix_cell_free(void *user_data, void *cell_data)
+{
+    Ewk_Tile_Matrix *tm = user_data;
+    Eina_Inlist *l = cell_data;
+
+    if (!l)
+        return;
+
+    ewk_tile_unused_cache_freeze(tm->tuc);
+
+    while (l) {
+        Ewk_Tile *t = (Ewk_Tile *)l;
+        l = l->next;
+
+        if (t->updates || t->stats.full_update)
+            tm->updates = eina_list_remove(tm->updates, t);
+
+        if (t->visible)
+            ERR("freeing cell that is visible, leaking tile %p", t);
+        else {
+            if (!ewk_tile_unused_cache_tile_get(tm->tuc, t))
+                ERR("tile %p was not in cache %p? leaking...", t, tm->tuc);
+            else {
+                DBG("tile cell does not exist anymore, free it %p", t);
+
+#ifdef DEBUG_MEM_LEAKS
+                tm->stats.bytes.freed += t->bytes;
+                tm->stats.tiles.freed++;
+#endif
+
+                ewk_tile_free(t);
+            }
+        }
+    }
+
+    ewk_tile_unused_cache_thaw(tm->tuc);
+}
+
+/* called when cache of unused tile is flushed */
+static void _ewk_tile_matrix_tile_free(void *data, Ewk_Tile *t)
+{
+    Ewk_Tile_Matrix *tm = data;
+    Eina_Matrixsparse_Cell *cell;
+    Eina_Inlist *l, *old;
+
+    if (!eina_matrixsparse_cell_idx_get(tm->matrix, t->row, t->col, &cell)) {
+        ERR("removing tile %p that was not in the matrix? Leaking...", t);
+        return;
+    }
+
+    if (t->updates || t->stats.full_update)
+        tm->updates = eina_list_remove(tm->updates, t);
+
+    old = eina_matrixsparse_cell_data_get(cell);
+    l = eina_inlist_remove(old, EINA_INLIST_GET(t));
+    if (!l) {
+        /* set to null to avoid double free */
+        eina_matrixsparse_cell_data_replace(cell, NULL, NULL);
+        eina_matrixsparse_cell_clear(cell);
+    } else if (old != l)
+        eina_matrixsparse_cell_data_replace(cell, l, NULL);
+
+    if (EINA_UNLIKELY(!!t->visible)) {
+        ERR("cache of unused tiles requesting deletion of used tile %p? "
+            "Leaking...", t);
+        return;
+    }
+
+#ifdef DEBUG_MEM_LEAKS
+    tm->stats.bytes.freed += t->bytes;
+    tm->stats.tiles.freed++;
+#endif
+
+    ewk_tile_free(t);
+}
+
+/**
+ * Creates a new matrix of tiles.
+ *
+ * The tile matrix is responsible for keeping tiles around and
+ * providing fast access to them. One can use it to retrieve new or
+ * existing tiles and give them back, allowing them to be
+ * freed/replaced by the cache.
+ *
+ * @param tuc cache of unused tiles or @c NULL to create one
+ *        automatically.
+ * @param cols number of columns in the matrix.
+ * @param rows number of rows in the matrix.
+ * @param cspace the color space used to create tiles in this matrix.
+ * @param render_cb function used to render given tile update.
+ * @param data context to give back to @a render_cb.
+ *
+ * @return newly allocated instance on success, @c NULL on failure.
+ */
+Ewk_Tile_Matrix *ewk_tile_matrix_new(Ewk_Tile_Unused_Cache *tuc, unsigned long cols, unsigned long rows, Evas_Colorspace cspace, void (*render_cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data)
+{
+    Ewk_Tile_Matrix *tm;
+
+    CALLOC_OR_OOM_RET(tm, sizeof(Ewk_Tile_Matrix), NULL);
+
+    tm->matrix = eina_matrixsparse_new(rows, cols, _ewk_tile_matrix_cell_free, tm);
+    if (!tm->matrix) {
+        ERR("could not create sparse matrix.");
+        free(tm);
+        return NULL;
+    }
+
+    if (tuc)
+        tm->tuc = ewk_tile_unused_cache_ref(tuc);
+    else {
+        tm->tuc = ewk_tile_unused_cache_new(40960000);
+        if (!tm->tuc) {
+            ERR("no cache of unused tile!");
+            eina_matrixsparse_free(tm->matrix);
+            free(tm);
+            return NULL;
+        }
+    }
+
+    tm->cspace = cspace;
+    tm->render.cb = render_cb;
+    tm->render.data = (void *)data;
+
+    return tm;
+}
+
+/**
+ * Destroys tiles matrix, releasing its resources.
+ *
+ * The cache instance is unreferenced, possibly freeing it.
+ */
+void ewk_tile_matrix_free(Ewk_Tile_Matrix *tm)
+{
+#ifdef DEBUG_MEM_LEAKS
+    uint64_t tiles, bytes;
+#endif
+
+    EINA_SAFETY_ON_NULL_RETURN(tm);
+    ewk_tile_unused_cache_freeze(tm->tuc);
+
+    eina_matrixsparse_free(tm->matrix);
+
+    ewk_tile_unused_cache_thaw(tm->tuc);
+    ewk_tile_unused_cache_unref(tm->tuc);
+
+#ifdef DEBUG_MEM_LEAKS
+    tiles = tm->stats.tiles.allocated - tm->stats.tiles.freed;
+    bytes = tm->stats.bytes.allocated - tm->stats.bytes.freed;
+
+    tiles_leaked += tiles;
+    bytes_leaked += bytes;
+
+    if (tiles || bytes)
+        ERR("tiled matrix leaked: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
+           "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]",
+            tm->stats.tiles.allocated, tm->stats.tiles.freed, tiles,
+            tm->stats.bytes.allocated, tm->stats.bytes.freed, bytes);
+    else if (tiles_leaked || bytes_leaked)
+        WRN("tiled matrix had no leaks: tiles[+%"PRIu64",-%"PRIu64"] "
+           "bytes[+%"PRIu64",-%"PRIu64"], but some other leaked "
+            "%"PRIu64" tiles (%"PRIu64" bytes)",
+            tm->stats.tiles.allocated, tm->stats.tiles.freed,
+            tm->stats.bytes.allocated, tm->stats.bytes.freed,
+            tiles_leaked, bytes_leaked);
+    else
+        INF("tiled matrix had no leaks: tiles[+%"PRIu64",-%"PRIu64"] "
+           "bytes[+%"PRIu64",-%"PRIu64"]",
+            tm->stats.tiles.allocated, tm->stats.tiles.freed,
+            tm->stats.bytes.allocated, tm->stats.bytes.freed);
+#endif
+
+    free(tm);
+}
+
+/**
+ * Resize matrix to given number of rows and columns.
+ */
+void ewk_tile_matrix_resize(Ewk_Tile_Matrix *tm, unsigned long cols, unsigned long rows)
+{
+    EINA_SAFETY_ON_NULL_RETURN(tm);
+    eina_matrixsparse_size_set(tm->matrix, rows, cols);
+}
+
+/**
+ * Get the cache of unused tiles in use by given matrix.
+ *
+ * No reference is taken to the cache.
+ */
+Ewk_Tile_Unused_Cache *ewk_tile_matrix_unused_cache_get(const Ewk_Tile_Matrix *tm)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL);
+    return tm->tuc;
+}
+
+/**
+ * Get the exact tile for the given position and zoom.
+ *
+ * If the tile was unused then it's removed from the cache.
+ *
+ * After usage, please give it back using
+ * ewk_tile_matrix_tile_put(). If you just want to check if it exists,
+ * then use ewk_tile_matrix_tile_exact_exists().
+ *
+ * @param tm the tile matrix to get tile from.
+ * @param col the column number.
+ * @param row the row number.
+ * @param zoom the exact zoom to use.
+ *
+ * @return The tile instance or @c NULL if none is found. If the tile
+ *         was in the unused cache it will be @b removed (thus
+ *         considered used) and one should give it back with
+ *         ewk_tile_matrix_tile_put() afterwards.
+ *
+ * @see ewk_tile_matrix_tile_nearest_get()
+ * @see ewk_tile_matrix_tile_exact_get()
+ */
+Ewk_Tile *ewk_tile_matrix_tile_exact_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom)
+{
+    Ewk_Tile *t, *item, *item_found = NULL;
+    Eina_Inlist *inl;
+
+    t = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+    if (!t)
+        return NULL;
+
+    if (t->zoom == zoom)
+        goto end;
+
+    EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) {
+        if (item->zoom != zoom)
+            continue;
+        item_found = item;
+        break;
+    }
+
+    if (!item_found)
+        return NULL;
+
+    inl = eina_inlist_promote(EINA_INLIST_GET(t), EINA_INLIST_GET(item_found));
+    eina_matrixsparse_data_idx_replace(tm->matrix, row, col, inl, NULL);
+
+  end:
+    if (!t->visible) {
+        if (!ewk_tile_unused_cache_tile_get(tm->tuc, t))
+            WRN("Ewk_Tile was unused but not in cache? bug!");
+    }
+
+    return t;
+}
+
+/**
+ * Checks if tile of given zoom exists in matrix.
+ *
+ * @param tm the tile matrix to check tile existence.
+ * @param col the column number.
+ * @param row the row number.
+ * @param zoom the exact zoom to query.
+ *
+ * @return @c EINA_TRUE if found, @c EINA_FALSE otherwise.
+ *
+ * @see ewk_tile_matrix_tile_exact_get()
+ */
+Eina_Bool ewk_tile_matrix_tile_exact_exists(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom)
+{
+    Ewk_Tile *t, *item;
+
+    t = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+    if (!t)
+        return EINA_FALSE;
+
+    EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) {
+        if (item->zoom == zoom)
+            return EINA_TRUE;
+    }
+
+    return EINA_FALSE;
+}
+
+/**
+ * Get the nearest tile for given position and zoom.
+ *
+ * The nearest tile is the one at the given position but with the
+ * closest zoom, this being the division of the tile zoom by the
+ * desired zoom, the closest to 1.0 gets it.
+ *
+ * If the tile was unused then it's removed from the cache.
+ *
+ * After usage, please give it back using ewk_tile_matrix_tile_put().
+ *
+ * @param tm the tile matrix to get tile from.
+ * @param col the column number.
+ * @param row the row number.
+ * @param zoom the exact zoom to use.
+ *
+ * @return The tile instance or @c NULL if none is found. If the tile
+ *         was in the unused cache it will be @b removed (thus
+ *         considered used) and one should give it back with
+ *         ewk_tile_matrix_tile_put() afterwards.
+ */
+Ewk_Tile *ewk_tile_matrix_tile_nearest_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom)
+{
+    Ewk_Tile *t, *item, *item_found = NULL;
+    Eina_Inlist *inl;
+    float zoom_found = 0;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL);
+    EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, NULL);
+
+    t = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+    if (!t)
+        return NULL;
+
+    if (t->zoom == zoom) {
+        item_found = t;
+        goto end;
+    }
+
+    EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) {
+        float cur_zoom = item->zoom;
+
+        if (cur_zoom == zoom) {
+            item_found = item;
+            break;
+        }
+        
+        if (cur_zoom > zoom)
+            cur_zoom = zoom / cur_zoom;
+        else
+            cur_zoom = cur_zoom / zoom;
+
+        if (cur_zoom > zoom_found) {
+            item_found = item;
+            zoom_found = cur_zoom;
+        }
+    }
+
+    if (!item_found)
+        return NULL;
+
+    inl = eina_inlist_promote(EINA_INLIST_GET(t), EINA_INLIST_GET(item_found));
+    eina_matrixsparse_data_idx_replace(tm->matrix, row, col, inl, NULL);
+
+  end:
+    if (!item_found->visible) {
+        if (!ewk_tile_unused_cache_tile_get(tm->tuc, item_found))
+            WRN("Ewk_Tile was unused but not in cache? bug!");
+    }
+
+    return item_found;
+}
+
+/**
+ * Create a new tile at given position and zoom level in the matrix.
+ *
+ * The newly created tile is considered in use and not put into cache
+ * of unused tiles. After it is used one should call
+ * ewk_tile_matrix_tile_put() to give it back to matrix.
+ *
+ * @param tm the tile matrix to create tile on.
+ * @param col the column number.
+ * @param row the row number.
+ * @param zoom the level to create tile, used to determine tile size.
+ */
+Ewk_Tile *ewk_tile_matrix_tile_new(Ewk_Tile_Matrix *tm, Evas *evas, unsigned long col, unsigned int row, float zoom)
+{
+    Evas_Coord s;
+    Eina_Inlist *old;
+    Ewk_Tile *t;
+
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL);
+    EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, NULL);
+
+    s = TILE_SIZE_AT_ZOOM(TILE_MATRIX_BASE_TILE_SIZE, zoom);
+    zoom = (float)s / (float)TILE_MATRIX_BASE_TILE_SIZE;
+
+    t = ewk_tile_new(evas, s, s, zoom, tm->cspace);
+    if (!t) {
+        ERR("could not create tile %dx%d at %f, cspace=%d",
+            s, s, (double)zoom, tm->cspace);
+        return NULL;
+    }
+
+    old = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+    old = eina_inlist_prepend(old, EINA_INLIST_GET(t));
+    if (!eina_matrixsparse_data_idx_replace(tm->matrix, row, col, t, NULL)) {
+        ERR("could not set matrix cell, row/col outside matrix dimensions!");
+        ewk_tile_free(t);
+        return NULL;
+    }
+
+    t->col = col;
+    t->row = row;
+    t->x = col * s;
+    t->y = row * s;
+
+    cairo_translate(t->cairo, -t->x, -t->y);
+
+    t->stats.full_update = EINA_TRUE;
+    tm->updates = eina_list_append(tm->updates, t);
+
+#ifdef DEBUG_MEM_LEAKS
+    tm->stats.bytes.allocated += t->bytes;
+    tm->stats.tiles.allocated++;
+#endif
+
+    return t;
+}
+
+/**
+ * Gives back the tile to the tile matrix.
+ *
+ * This will report the tile is no longer in use by the one that got
+ * it with ewk_tile_matrix_tile_nearest_get() or
+ * ewk_tile_matrix_tile_exact_get().
+ *
+ * Any previous reference to tile should be released
+ * (ewk_tile_hide()) before calling this function, so it will
+ * be known if it is not visibile anymore and thus can be put into the
+ * unused cache.
+ *
+ * @param tm the tile matrix to return tile to.
+ * @param t the tile instance to return, must @b not be @c NULL.
+ * @param last_used time in which tile was last used.
+ *
+ * @return #EINA_TRUE on success or #EINA_FALSE on failure.
+ */
+Eina_Bool ewk_tile_matrix_tile_put(Ewk_Tile_Matrix *tm, Ewk_Tile *t, double last_used)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE);
+
+    if (t->visible)
+        return EINA_TRUE;
+
+    t->stats.last_used = last_used;
+    return ewk_tile_unused_cache_tile_put(tm->tuc, t, _ewk_tile_matrix_tile_free, tm);
+}
+
+Eina_Bool ewk_tile_matrix_tile_update(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, const Eina_Rectangle *update)
+{
+    Ewk_Tile *l, *t;
+    Eina_Rectangle new_update;
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(update, EINA_FALSE);
+
+    memcpy(&new_update, update, sizeof(new_update));
+    // check update is valid, otherwise return EINA_FALSE
+    if (update->x < 0 || update->y < 0 || update->w <= 0 || update->h <= 0) {
+        ERR("invalid update region.");
+        return EINA_FALSE;
+    }
+
+    if (update->x + update->w - 1 >= TILE_MATRIX_BASE_TILE_SIZE)
+        new_update.w = TILE_MATRIX_BASE_TILE_SIZE - update->x;
+    if (update->y + update->h - 1 >= TILE_MATRIX_BASE_TILE_SIZE)
+        new_update.h = TILE_MATRIX_BASE_TILE_SIZE - update->y;
+
+    l = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+
+    if (!l)
+        return EINA_TRUE;
+
+    EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) {
+        if (!t->updates && !t->stats.full_update)
+            tm->updates = eina_list_append(tm->updates, t);
+        new_update.x = roundf(t->zoom * new_update.x);
+        new_update.y = roundf(t->zoom * new_update.y);
+        new_update.w = roundf(t->zoom * new_update.w);
+        new_update.h = roundf(t->zoom * new_update.h);
+        ewk_tile_update_area(t, &new_update);
+    }
+
+    return EINA_TRUE;
+}
+
+Eina_Bool ewk_tile_matrix_tile_update_full(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row)
+{
+    Ewk_Tile *l, *t;
+    Eina_Matrixsparse_Cell *cell;
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
+
+    if (!eina_matrixsparse_cell_idx_get(tm->matrix, row, col, &cell))
+        return EINA_FALSE;
+
+    if (!cell)
+        return EINA_TRUE;
+
+    l = eina_matrixsparse_cell_data_get(cell);
+    if (!l) {
+        CRITICAL("matrix cell with no tile!");
+        return EINA_TRUE;
+    }
+
+    EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) {
+        if (!t->updates && !t->stats.full_update)
+            tm->updates = eina_list_append(tm->updates, t);
+        ewk_tile_update_full(t);
+    }
+
+    return EINA_TRUE;
+}
+
+void ewk_tile_matrix_tile_updates_clear(Ewk_Tile_Matrix *tm, Ewk_Tile *t)
+{
+    EINA_SAFETY_ON_NULL_RETURN(tm);
+    if (!t->updates && !t->stats.full_update)
+        return;
+    ewk_tile_updates_clear(t);
+    tm->updates = eina_list_remove(tm->updates, t);
+}
+
+static Eina_Bool _ewk_tile_matrix_slicer_setup(Ewk_Tile_Matrix *tm, const Eina_Rectangle *area, float zoom, Eina_Tile_Grid_Slicer *slicer)
+{
+    unsigned long rows, cols;
+    Evas_Coord x, y, w, h, tw, th;
+
+    if (area->w <= 0 || area->h <= 0) {
+        WRN("invalid area region: %d,%d+%dx%d.",
+            area->x, area->y, area->w, area->h);
+        return EINA_FALSE;
+    }
+
+    x = area->x;
+    y = area->y;
+    w = area->w;
+    h = area->h;
+
+    tw = TILE_SIZE_AT_ZOOM(TILE_W, zoom);
+    th = TILE_SIZE_AT_ZOOM(TILE_H, zoom);
+
+    // cropping area region to fit matrix
+    eina_matrixsparse_size_get(tm->matrix, &rows, &cols);
+    if (x < 0) {
+        w += x;
+        x = 0;
+    }
+    if (y < 0) {
+        h += y;
+        y = 0;
+    }
+
+    if (y + h - 1 > rows * th)
+        h = rows * th - y;
+    if (x + w - 1 > cols * tw)
+        w = cols * tw - x;
+
+    return eina_tile_grid_slicer_setup(slicer, x, y, w, h, tw, th);
+}
+
+
+Eina_Bool ewk_tile_matrix_update(Ewk_Tile_Matrix *tm, const Eina_Rectangle *update, float zoom)
+{
+    const Eina_Tile_Grid_Info *info;
+    Eina_Tile_Grid_Slicer slicer;
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(update, EINA_FALSE);
+
+    if (update->w < 1 || update->h < 1) {
+        DBG("Why we get updates with empty areas? %d,%d+%dx%d at zoom %f",
+            update->x, update->y, update->w, update->h, zoom);
+        return EINA_TRUE;
+    }
+
+    if (!_ewk_tile_matrix_slicer_setup(tm, update, zoom, &slicer)) {
+        ERR("Could not setup slicer for update %d,%d+%dx%d at zoom %f",
+            update->x, update->y, update->w, update->h, zoom);
+        return EINA_FALSE;
+    }
+
+    while (eina_tile_grid_slicer_next(&slicer, &info)) {
+        unsigned long col, row;
+        Ewk_Tile *l, *t;
+        col = info->col;
+        row = info->row;
+
+        l = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
+        if (!l)
+            continue;
+
+        EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) {
+            if (!t->updates && !t->stats.full_update)
+                tm->updates = eina_list_append(tm->updates, t);
+            if (info->full)
+                ewk_tile_update_full(t);
+            else {
+                if (t->zoom != zoom)
+                    ewk_tile_update_full(t);
+                else
+                    ewk_tile_update_area(t, &info->rect);
+            }
+        }
+    }
+
+
+    return EINA_TRUE;
+}
+
+void ewk_tile_matrix_updates_process(Ewk_Tile_Matrix *tm)
+{
+    Eina_List *l, *l_next;
+    Ewk_Tile *t;
+    EINA_SAFETY_ON_NULL_RETURN(tm);
+
+    // process updates, unflag tiles
+    EINA_LIST_FOREACH_SAFE(tm->updates, l, l_next, t) {
+        ewk_tile_updates_process(t, tm->render.cb, tm->render.data);
+        if (t->visible) {
+            ewk_tile_updates_clear(t);
+            tm->updates = eina_list_remove_list(tm->updates, l);
+        }
+    }
+}
+
+void ewk_tile_matrix_updates_clear(Ewk_Tile_Matrix *tm)
+{
+    Ewk_Tile *t;
+    EINA_SAFETY_ON_NULL_RETURN(tm);
+
+    EINA_LIST_FREE(tm->updates, t)
+        ewk_tile_updates_clear(t);
+    tm->updates = NULL;
+}
+
+// remove me later!
+void ewk_tile_matrix_dbg(const Ewk_Tile_Matrix *tm)
+{
+    Eina_Iterator *it = eina_matrixsparse_iterator_complete_new(tm->matrix);
+    Eina_Matrixsparse_Cell *cell;
+    Eina_Bool last_empty = EINA_FALSE;
+
+#ifdef DEBUG_MEM_LEAKS
+    printf("Ewk_Tile Matrix: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
+           "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]\n",
+           tm->stats.tiles.allocated, tm->stats.tiles.freed,
+           tm->stats.tiles.allocated - tm->stats.tiles.freed,
+           tm->stats.bytes.allocated, tm->stats.bytes.freed,
+           tm->stats.bytes.allocated - tm->stats.bytes.freed);
+#else
+    printf("Ewk_Tile Matrix:\n");
+#endif
+
+    EINA_ITERATOR_FOREACH(it, cell) {
+        unsigned long row, col;
+        Eina_Inlist *l;
+        eina_matrixsparse_cell_position_get(cell, &row, &col);
+        l = eina_matrixsparse_cell_data_get(cell);
+
+        if (!l) {
+            if (!last_empty) {
+                last_empty = EINA_TRUE;
+                printf("Empty:");
+            }
+            printf(" [%lu,%lu]", col, row);
+        } else {
+            if (last_empty) {
+                last_empty = EINA_FALSE;
+                printf("\n");
+            }
+            Ewk_Tile *t;
+
+            printf("%3lu,%3lu %10p:", col, row, l);
+            EINA_INLIST_FOREACH(l, t)
+                printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c",
+                       t->col, t->row, t->w, t->h, t->zoom,
+                       t->visible ? '*': ' ');
+            printf("\n");
+        }
+    }
+    if (last_empty)
+        printf("\n");
+    eina_iterator_free(it);
+
+    ewk_tile_unused_cache_dbg(tm->tuc);
+}
+
+/**
+ * Freeze matrix to not do maintenance tasks.
+ *
+ * Maintenance tasks optimize usage, but maybe we know we should hold
+ * on them until we do the last operation, in this case we freeze
+ * while operating and then thaw when we're done.
+ *
+ * @see ewk_tile_matrix_thaw()
+ */
+void ewk_tile_matrix_freeze(Ewk_Tile_Matrix *tm)
+{
+    EINA_SAFETY_ON_NULL_RETURN(tm);
+    if (!tm->frozen)
+        ewk_tile_unused_cache_freeze(tm->tuc);
+    tm->frozen++;
+}
+
+/**
+ * Unfreezes maintenance tasks.
+ *
+ * If this is the last counterpart of freeze, then maintenance tasks
+ * will run immediately.
+ */
+void ewk_tile_matrix_thaw(Ewk_Tile_Matrix *tm)
+{
+    EINA_SAFETY_ON_NULL_RETURN(tm);
+    if (!tm->frozen) {
+        ERR("thawing more than freezing!");
+        return;
+    }
+
+    tm->frozen--;
+    if (!tm->frozen)
+        ewk_tile_unused_cache_thaw(tm->tuc);
+}
diff --git a/WebKit/efl/ewk/ewk_tiled_matrix.h b/WebKit/efl/ewk/ewk_tiled_matrix.h
new file mode 100644 (file)
index 0000000..07b8612
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+    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.
+*/
+
+#ifndef ewk_tiled_matrix_h
+#define ewk_tiled_matrix_h
+
+#include "ewk_eapi.h"
+#include "ewk_tiled_backing_store.h"
+
+#include <Evas.h>
+
+/* matrix of tiles */
+EAPI Ewk_Tile_Matrix *ewk_tile_matrix_new(Ewk_Tile_Unused_Cache *tuc, unsigned long cols, unsigned long rows, Evas_Colorspace cspace, void (*render_cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data);
+EAPI void         ewk_tile_matrix_free(Ewk_Tile_Matrix *tm);
+
+EAPI void         ewk_tile_matrix_resize(Ewk_Tile_Matrix *tm, unsigned long cols, unsigned long rows);
+
+EAPI Ewk_Tile_Unused_Cache *ewk_tile_matrix_unused_cache_get(const Ewk_Tile_Matrix *tm);
+
+EAPI Ewk_Tile     *ewk_tile_matrix_tile_exact_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom);
+EAPI Eina_Bool     ewk_tile_matrix_tile_exact_exists(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom);
+EAPI Ewk_Tile     *ewk_tile_matrix_tile_nearest_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom);
+EAPI Ewk_Tile     *ewk_tile_matrix_tile_new(Ewk_Tile_Matrix *tm, Evas *evas, unsigned long col, unsigned int row, float zoom);
+EAPI Eina_Bool ewk_tile_matrix_tile_put(Ewk_Tile_Matrix *tm, Ewk_Tile *t, double last_used);
+
+EAPI Eina_Bool ewk_tile_matrix_tile_update(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, const Eina_Rectangle *update);
+EAPI Eina_Bool ewk_tile_matrix_tile_update_full(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row);
+EAPI void ewk_tile_matrix_tile_updates_clear(Ewk_Tile_Matrix *tm, Ewk_Tile *t);
+
+EAPI Eina_Bool ewk_tile_matrix_update(Ewk_Tile_Matrix *tm, const Eina_Rectangle *update, float zoom);
+EAPI void      ewk_tile_matrix_updates_process(Ewk_Tile_Matrix *tm);
+EAPI void      ewk_tile_matrix_updates_clear(Ewk_Tile_Matrix *tm);
+EAPI void      ewk_tile_matrix_freeze(Ewk_Tile_Matrix *tm);
+EAPI void      ewk_tile_matrix_thaw(Ewk_Tile_Matrix *tm);
+
+// remove me!
+    void ewk_tile_matrix_dbg(const Ewk_Tile_Matrix *tm);
+    void ewk_tile_unused_cache_dbg(const Ewk_Tile_Unused_Cache *tuc);
+    void ewk_tile_accounting_dbg(void);
+
+
+#endif // ewk_tiled_matrix_h
+
diff --git a/WebKit/efl/ewk/ewk_tiled_model.c b/WebKit/efl/ewk/ewk_tiled_model.c
new file mode 100644 (file)
index 0000000..641b93e
--- /dev/null
@@ -0,0 +1,905 @@
+/*
+    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_model.h"
+
+#define _GNU_SOURCE
+#include "ewk_tiled_backing_store.h"
+#include "ewk_tiled_private.h"
+#include <Eina.h>
+#include <eina_safety_checks.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdio.h> // XXX REMOVE ME LATER
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef TILE_STATS_ACCOUNT_RENDER_TIME
+#include <sys/time.h>
+#endif
+
+#ifndef CAIRO_FORMAT_RGB16_565
+#define CAIRO_FORMAT_RGB16_565 4
+#endif
+
+#define IDX(col, row, rowspan) (col + (row * rowspan))
+#define MIN(a, b) ((a < b) ? a : b)
+#define MAX(a, b) ((a > b) ? a : b)
+
+#ifdef DEBUG_MEM_LEAKS
+static uint64_t tiles_allocated = 0;
+static uint64_t tiles_freed = 0;
+static uint64_t bytes_allocated = 0;
+static uint64_t bytes_freed = 0;
+
+struct tile_account {
+    Evas_Coord size;
+    struct {
+        uint64_t allocated;
+        uint64_t freed;
+    } tiles, bytes;
+};
+
+static size_t accounting_len = 0;
+static struct tile_account *accounting = NULL;
+
+static inline struct tile_account *_ewk_tile_account_get(const Ewk_Tile *t)
+{
+    struct tile_account *acc;
+    size_t i;
+
+    for (i = 0; i < accounting_len; i++) {
+        if (accounting[i].size == t->w)
+            return accounting + i;
+    }
+
+    i = (accounting_len + 1) * sizeof(struct tile_account);
+    REALLOC_OR_OOM_RET(accounting, i, NULL);
+
+    acc = accounting + accounting_len;
+    acc->size = t->w;
+    acc->tiles.allocated = 0;
+    acc->tiles.freed = 0;
+    acc->bytes.allocated = 0;
+    acc->bytes.freed = 0;
+
+    accounting_len++;
+
+    return acc;
+}
+
+static inline void _ewk_tile_account_allocated(const Ewk_Tile *t)
+{
+    struct tile_account *acc = _ewk_tile_account_get(t);
+    if (!acc)
+        return;
+    acc->bytes.allocated += t->bytes;
+    acc->tiles.allocated++;
+
+    bytes_allocated += t->bytes;
+    tiles_allocated++;
+}
+
+static inline void _ewk_tile_account_freed(const Ewk_Tile *t)
+{
+    struct tile_account *acc = _ewk_tile_account_get(t);
+    if (!acc)
+        return;
+
+    acc->bytes.freed += t->bytes;
+    acc->tiles.freed++;
+
+    bytes_freed += t->bytes;
+    tiles_freed++;
+}
+
+void ewk_tile_accounting_dbg(void)
+{
+    struct tile_account *acc;
+    struct tile_account *acc_end;
+
+    printf("TILE BALANCE: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
+           "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]\n",
+            tiles_allocated, tiles_freed, tiles_allocated - tiles_freed,
+            bytes_allocated, bytes_freed, bytes_allocated - bytes_freed);
+
+    if (!accounting_len)
+        return;
+
+    acc = accounting;
+    acc_end = acc + accounting_len;
+    printf("BEGIN: TILE BALANCE DETAILS (TO THIS MOMENT!):\n");
+    for (; acc < acc_end; acc++) {
+        uint64_t tiles, bytes;
+
+        tiles = acc->tiles.allocated - acc->tiles.freed;
+        bytes = acc->bytes.allocated - acc->bytes.freed;
+
+        printf("   %4d: tiles[+%4"PRIu64",-%4"PRIu64":%4"PRIu64"] "
+               "bytes[+%8"PRIu64",-%8"PRIu64":%8"PRIu64"]%s\n",
+               acc->size,
+               acc->tiles.allocated, acc->tiles.freed, tiles,
+               acc->bytes.allocated, acc->bytes.freed, bytes,
+               (bytes || tiles) ? " POSSIBLE LEAK" : "");
+    }
+    printf("END: TILE BALANCE DETAILS (TO THIS MOMENT!):\n");
+}
+#else
+
+static inline void _ewk_tile_account_allocated(const Ewk_Tile *t) { }
+static inline void _ewk_tile_account_freed(const Ewk_Tile *t) { }
+
+void ewk_tile_accounting_dbg(void)
+{
+    printf("compile webkit with DEBUG_MEM_LEAKS defined!\n");
+}
+#endif
+
+static inline void _ewk_tile_paint_rgb888(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
+{
+    uint32_t *dst32, *dst32_end, c1;
+    uint64_t *dst64, *dst64_end, c2;
+
+    c1 = 0xff000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
+    c2 = ((uint64_t)c1 << 32) | c1;
+
+    dst64 = (uint64_t *)t->pixels;
+    dst64_end = dst64 + ((t->bytes / 8) & ~7);
+    for (; dst64 < dst64_end; dst64 += 8) {
+        /* TODO: ARM add pld or NEON instructions */
+        dst64[0] = c2;
+        dst64[1] = c2;
+        dst64[2] = c2;
+        dst64[3] = c2;
+        dst64[4] = c2;
+        dst64[5] = c2;
+        dst64[6] = c2;
+        dst64[7] = c2;
+    }
+
+    dst32 = (uint32_t *)dst64_end;
+    dst32_end = (uint32_t *)(t->pixels + t->bytes);
+    for (; dst32 < dst32_end; dst32++)
+        *dst32 = c1;
+}
+
+static inline void _ewk_tile_paint_rgb565(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
+{
+    uint16_t *dst16, *dst16_end, c1;
+    uint64_t *dst64, *dst64_end, c2;
+
+    c1 = ((((r >> 3) & 0x1f) << 11) |
+          (((g >> 2) & 0x3f) << 5) |
+          ((b >> 3) & 0x1f));
+
+    c2 = (((uint64_t)c1 << 48) | ((uint64_t)c1 << 32) |
+          ((uint64_t)c1 << 16) | c1);
+
+    dst64 = (uint64_t *)t->pixels;
+    dst64_end = dst64 + ((t->bytes / 8) & ~7);
+    for (; dst64 < dst64_end; dst64 += 8) {
+        /* TODO: ARM add pld or NEON instructions */
+        dst64[0] = c2;
+        dst64[1] = c2;
+        dst64[2] = c2;
+        dst64[3] = c2;
+        dst64[4] = c2;
+        dst64[5] = c2;
+        dst64[6] = c2;
+        dst64[7] = c2;
+    }
+
+    dst16 = (uint16_t *)dst16_end;
+    dst16_end = (uint16_t *)(t->pixels + t->bytes);
+    for (; dst16 < dst16_end; dst16++)
+        *dst16 = c1;
+}
+
+static inline void _ewk_tile_paint(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
+{
+    if (t->cspace == EVAS_COLORSPACE_ARGB8888)
+        _ewk_tile_paint_rgb888(t, r, g, b);
+    else if (t->cspace == EVAS_COLORSPACE_RGB565_A5P)
+        _ewk_tile_paint_rgb565(t, r, g, b);
+    else
+        ERR("unknown color space: %d", t->cspace);
+}
+
+/**
+ * Create a new tile of given size, zoom level and colorspace.
+ *
+ * After created these properties are immutable as they're the basic
+ * characteristic of the tile and any change will lead to invalid
+ * memory access.
+ *
+ * Other members are of free-access and no getters/setters are
+ * provided in orderr to avoid expensive operations on those, however
+ * some are better manipulated with provided functions, such as
+ * ewk_tile_show() and ewk_tile_hide() to change
+ * @c visible or ewk_tile_update_full(), ewk_tile_update_area(),
+ * ewk_tile_updates_clear() to change @c stats.misses,
+ * @c stats.full_update and @c updates.
+ */
+Ewk_Tile *ewk_tile_new(Evas *evas, Evas_Coord w, Evas_Coord h, float zoom, Evas_Colorspace cspace)
+{
+    Eina_Inlist *l;
+    Evas_Coord *ec;
+    Evas_Colorspace *ecs;
+    float *f;
+    size_t *s;
+    Ewk_Tile *t;
+    unsigned int area;
+    size_t bytes;
+    cairo_format_t format;
+    cairo_status_t status;
+    int stride;
+
+    area = w * h;
+
+    if (cspace == EVAS_COLORSPACE_ARGB8888) {
+        bytes = area * 4;
+        stride = w * 4;
+        format = CAIRO_FORMAT_RGB24;
+    } else if (cspace == EVAS_COLORSPACE_RGB565_A5P) {
+        bytes = area * 2;
+        stride = w * 2;
+        format = CAIRO_FORMAT_RGB16_565;
+    } else {
+        ERR("unknown color space: %d", cspace);
+        return NULL;
+    }
+
+    DBG("size: %dx%d (%d), zoom: %f, cspace=%d",
+        w, h, area, (double)zoom, cspace);
+
+    MALLOC_OR_OOM_RET(t, sizeof(Ewk_Tile), NULL);
+    t->image = evas_object_image_add(evas);
+
+    l = EINA_INLIST_GET(t);
+    l->prev = NULL;
+    l->next = NULL;
+
+    t->visible = 0;
+    t->updates = NULL;
+
+    memset(&t->stats, 0, sizeof(Ewk_Tile_Stats));
+    t->stats.area = area;
+
+    /* ugly, but let's avoid at all costs having users to modify those */
+    ec = (Evas_Coord *)&t->w;
+    *ec = w;
+
+    ec = (Evas_Coord *)&t->h;
+    *ec = h;
+
+    ecs = (Evas_Colorspace *)&t->cspace;
+    *ecs = cspace;
+
+    f = (float *)&t->zoom;
+    *f = zoom;
+
+    s = (size_t *)&t->bytes;
+    *s = bytes;
+
+    evas_object_image_size_set(t->image, t->w, t->h);
+    evas_object_image_colorspace_set(t->image, t->cspace);
+    t->pixels = evas_object_image_data_get(t->image, EINA_TRUE);
+    t->surface = cairo_image_surface_create_for_data
+        (t->pixels, format, w, h, stride);
+    status = cairo_surface_status(t->surface);
+    if (status != CAIRO_STATUS_SUCCESS) {
+        ERR("failed to create cairo surface: %s",
+            cairo_status_to_string(status));
+        free(t);
+        return NULL;
+    }
+
+    t->cairo = cairo_create(t->surface);
+    status = cairo_status(t->cairo);
+    if (status != CAIRO_STATUS_SUCCESS) {
+        ERR("failed to create cairo: %s", cairo_status_to_string(status));
+        cairo_surface_destroy(t->surface);
+        evas_object_del(t->image);
+        free(t);
+        return NULL;
+    }
+
+    _ewk_tile_account_allocated(t);
+
+    return t;
+}
+
+/**
+ * Free tile memory.
+ */
+void ewk_tile_free(Ewk_Tile *t)
+{
+    _ewk_tile_account_freed(t);
+
+    if (t->updates)
+        eina_tiler_free(t->updates);
+
+    cairo_surface_destroy(t->surface);
+    cairo_destroy(t->cairo);
+    evas_object_del(t->image);
+    free(t);
+}
+
+/**
+ * Make the tile visible, incrementing its counter.
+ */
+void ewk_tile_show(Ewk_Tile *t)
+{
+    t->visible++;
+    evas_object_show(t->image);
+}
+
+/**
+ * Decrement the visibility counter, making it invisible if necessary.
+ */
+void ewk_tile_hide(Ewk_Tile *t)
+{
+    t->visible--;
+    if (!t->visible)
+        evas_object_hide(t->image);
+}
+
+/**
+ * Returns EINA_TRUE if the tile is visible, EINA_FALSE otherwise.
+ */
+Eina_Bool ewk_tile_visible_get(Ewk_Tile *t)
+{
+    return !!t->visible;
+}
+
+/**
+ * Mark whole tile as dirty and requiring update.
+ */
+void ewk_tile_update_full(Ewk_Tile *t)
+{
+    /* TODO: list of tiles pending updates? */
+    t->stats.misses++;
+
+    if (!t->stats.full_update) {
+        t->stats.full_update = EINA_TRUE;
+        if (t->updates) {
+            eina_tiler_free(t->updates);
+            t->updates = NULL;
+        }
+    }
+}
+
+/**
+ * Mark the specific subarea as dirty and requiring update.
+ */
+void ewk_tile_update_area(Ewk_Tile *t, const Eina_Rectangle *r)
+{
+    /* TODO: list of tiles pending updates? */
+    t->stats.misses++;
+
+    if (t->stats.full_update)
+        return;
+
+    if (!r->x && !r->y && r->w == t->w && r->h == t->h) {
+        t->stats.full_update = EINA_TRUE;
+        if (t->updates) {
+            eina_tiler_free(t->updates);
+            t->updates = NULL;
+        }
+        return;
+    }
+
+    if (!t->updates) {
+        t->updates = eina_tiler_new(t->w, t->h);
+        if (!t->updates) {
+            CRITICAL("could not create eina_tiler %dx%d.", t->w, t->h);
+            return;
+        }
+    }
+
+    eina_tiler_rect_add(t->updates, r);
+}
+
+/**
+ * For each updated region, call the given function.
+ *
+ * This will not change the tile statistics or clear the processed
+ * updates, use ewk_tile_updates_clear() for that.
+ */
+void ewk_tile_updates_process(Ewk_Tile *t, void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data)
+{
+    if (t->stats.full_update) {
+        Eina_Rectangle r;
+        r.x = 0;
+        r.y = 0;
+        r.w = t->w;
+        r.h = t->h;
+#ifdef TILE_STATS_ACCOUNT_RENDER_TIME
+        struct timeval timev;
+        double render_start;
+        gettimeofday(&timev, NULL);
+        render_start = (double)timev.tv_sec +
+            (((double)timev.tv_usec) / 1000000);
+#endif
+        cb((void *)data, t, &r);
+#ifdef TILE_STATS_ACCOUNT_RENDER_TIME
+        gettimeofday(&timev, NULL);
+        t->stats.render_time = (double)timev.tv_sec +
+            (((double)timev.tv_usec) / 1000000) - render_start;
+#endif
+    } else if (t->updates) {
+        Eina_Iterator *itr = eina_tiler_iterator_new(t->updates);
+        Eina_Rectangle r = {0, 0, 0, 0};
+        if (!itr) {
+            CRITICAL("could not create tiler iterator!");
+            return;
+        }
+        EINA_ITERATOR_FOREACH(itr, r)
+            cb((void *)data, t, &r);
+        eina_iterator_free(itr);
+    }
+}
+
+/**
+ * Clear all updates in region, if any.
+ *
+ * This will change the tile statistics, specially zero stat.misses
+ * and unset stats.full_update. If t->updates existed, then it will be
+ * destroyed.
+ *
+ * This function is usually called after ewk_tile_updates_process() is
+ * called.
+ */
+void ewk_tile_updates_clear(Ewk_Tile *t)
+{
+    /* TODO: remove from list of pending updates? */
+    t->stats.misses = 0;
+
+    if (t->stats.full_update)
+        t->stats.full_update = 0;
+    else if (t->updates) {
+        eina_tiler_free(t->updates);
+        t->updates = NULL;
+    }
+}
+
+typedef struct _Ewk_Tile_Unused_Cache_Entry Ewk_Tile_Unused_Cache_Entry;
+struct _Ewk_Tile_Unused_Cache_Entry {
+    Ewk_Tile *tile;
+    int weight;
+    struct {
+        void (*cb)(void *data, Ewk_Tile *t);
+        void *data;
+    } tile_free;
+};
+
+struct _Ewk_Tile_Unused_Cache {
+    struct {
+        Eina_List *list;
+        size_t count;
+        size_t allocated;
+    } entries;
+    struct {
+        size_t max;  /**< watermark (in bytes) to start freeing tiles */
+        size_t used; /**< in bytes, maybe more than max. */
+    } memory;
+    struct {
+        Evas_Coord x, y, w, h;
+        float zoom;
+        Eina_Bool locked;
+    } locked;
+    int references;
+    unsigned int frozen;
+    Eina_Bool dirty:1;
+};
+
+static const size_t TILE_UNUSED_CACHE_ALLOCATE_INITIAL = 128;
+static const size_t TILE_UNUSED_CACHE_ALLOCATE_STEP = 16;
+static const size_t TILE_UNUSED_CACHE_MAX_FREE = 32;
+
+/**
+ * Cache of unused tiles (those that are not visible).
+ *
+ * The cache of unused tiles.
+ *
+ * @param max cache size in bytes.
+ *
+ * @return newly allocated cache of unused tiles, use
+ *         ewk_tile_unused_cache_free() to release resources. If not
+ *         possible to allocate memory, @c NULL is returned.
+ */
+Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_new(size_t max)
+{
+    Ewk_Tile_Unused_Cache *tuc;
+
+    CALLOC_OR_OOM_RET(tuc, sizeof(Ewk_Tile_Unused_Cache), NULL);
+
+    DBG("tuc=%p", tuc);
+    tuc->memory.max = max;
+    tuc->references = 1;
+    return tuc;
+}
+
+void ewk_tile_unused_cache_lock_area(Ewk_Tile_Unused_Cache *tuc, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom)
+{
+    EINA_SAFETY_ON_NULL_RETURN(tuc);
+
+    tuc->locked.locked = EINA_TRUE;
+    tuc->locked.x = x;
+    tuc->locked.y = y;
+    tuc->locked.w = w;
+    tuc->locked.h = h;
+    tuc->locked.zoom = zoom;
+}
+
+void ewk_tile_unused_cache_unlock_area(Ewk_Tile_Unused_Cache *tuc)
+{
+    EINA_SAFETY_ON_NULL_RETURN(tuc);
+
+    tuc->locked.locked = EINA_FALSE;
+}
+
+/**
+ * Free cache of unused tiles.
+ *
+ * Those tiles that are still visible will remain live. The unused
+ * tiles will be freed.
+ *
+ * @see ewk_tile_unused_cache_unref()
+ */
+void ewk_tile_unused_cache_free(Ewk_Tile_Unused_Cache *tuc)
+{
+    EINA_SAFETY_ON_NULL_RETURN(tuc);
+
+    DBG("tuc=%p, "
+        "entries=(count:%zd, allocated:%zd), "
+        "memory=(max:%zd, used:%zd)",
+        tuc, tuc->entries.count, tuc->entries.allocated,
+        tuc->memory.max, tuc->memory.used);
+
+    ewk_tile_unused_cache_clear(tuc);
+    free(tuc);
+}
+
+/**
+ * Clear cache of unused tiles.
+ *
+ * Any tiles that are in the cache are freed. The only tiles that are
+ * kept are those that aren't in the cache (i.e. that are visible).
+ */
+void ewk_tile_unused_cache_clear(Ewk_Tile_Unused_Cache *tuc)
+{
+    Ewk_Tile_Unused_Cache_Entry *itr;
+    EINA_SAFETY_ON_NULL_RETURN(tuc);
+
+    if (!tuc->entries.count)
+        return;
+
+    EINA_LIST_FREE(tuc->entries.list, itr) {
+        itr->tile_free.cb(itr->tile_free.data, itr->tile);
+        free(itr);
+    }
+
+    tuc->memory.used = 0;
+    tuc->entries.count = 0;
+    tuc->dirty = EINA_FALSE;
+}
+
+/**
+ * Hold reference to cache.
+ *
+ * @return same pointer as taken.
+ *
+ * @see ewk_tile_unused_cache_unref()
+ */
+Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_ref(Ewk_Tile_Unused_Cache *tuc)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, NULL);
+    tuc->references++;
+    return tuc;
+}
+
+/**
+ * Release cache reference, freeing it if it drops to zero.
+ *
+ * @see ewk_tile_unused_cache_ref()
+ * @see ewk_tile_unused_cache_free()
+ */
+void ewk_tile_unused_cache_unref(Ewk_Tile_Unused_Cache *tuc)
+{
+    EINA_SAFETY_ON_NULL_RETURN(tuc);
+    tuc->references--;
+    if (!tuc->references)
+        ewk_tile_unused_cache_free(tuc);
+}
+
+/**
+ * Change cache capacity, in bytes.
+ *
+ * This will not flush cache, use ewk_tile_unused_cache_flush() or
+ * ewk_tile_unused_cache_auto_flush() to do so.
+ */
+void ewk_tile_unused_cache_max_set(Ewk_Tile_Unused_Cache *tuc, size_t max)
+{
+    EINA_SAFETY_ON_NULL_RETURN(tuc);
+    tuc->memory.max = max;
+}
+
+/**
+ * Retrieve maximum cache capacity, in bytes.
+ */
+size_t ewk_tile_unused_cache_max_get(const Ewk_Tile_Unused_Cache *tuc)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
+    return tuc->memory.max;
+}
+
+/**
+ * Retrieve the used cache capacity, in bytes.
+ */
+size_t ewk_tile_unused_cache_used_get(const Ewk_Tile_Unused_Cache *tuc)
+{
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
+    return tuc->memory.used;
+}
+
+/**
+ * Flush given amount of bytes from cache.
+ *
+ * After calling this function, near @a bytes are freed from cache. It
+ * may be less if cache did not contain that amount of bytes (ie: an
+ * empty cache has nothing to free!) or more if the cache just
+ * contained objects that were larger than the requested amount (this
+ * is usually the case).
+ *
+ * @param tuc cache of unused tiles.
+ * @param bytes amount to free.
+ *
+ * @return amount really freed.
+ *
+ * @see ewk_tile_unused_cache_used_get()
+ */
+size_t ewk_tile_unused_cache_flush(Ewk_Tile_Unused_Cache *tuc, size_t bytes)
+{
+    Ewk_Tile_Unused_Cache_Entry *itr;
+    Eina_List *l, *l_next;
+    EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
+    size_t done;
+    unsigned int count;
+
+    if (!tuc->entries.count)
+        return 0;
+    if (bytes < 1)
+        return 0;
+
+    /*
+     * NOTE: the cache is a FIFO queue currently.
+     * Don't need to sort any more.
+     */
+
+    if (tuc->dirty)
+        tuc->dirty = EINA_FALSE;
+
+    done = 0;
+    count = 0;
+    EINA_LIST_FOREACH_SAFE(tuc->entries.list, l, l_next, itr) {
+        Ewk_Tile *t = itr->tile;
+        if (done > bytes)
+            break;
+        if (tuc->locked.locked
+            && t->x + t->w > tuc->locked.x
+            && t->y + t->h > tuc->locked.y
+            && t->x < tuc->locked.x + tuc->locked.w
+            && t->y < tuc->locked.y + tuc->locked.h
+            && t->zoom == tuc->locked.zoom) {
+            continue;
+        }
+        done += sizeof(Ewk_Tile) + itr->tile->bytes;
+        itr->tile_free.cb(itr->tile_free.data, itr->tile);
+        tuc->entries.list = eina_list_remove_list(tuc->entries.list, l);
+        free(itr);
+        count++;
+    }
+
+    tuc->memory.used -= done;
+    tuc->entries.count -= count;
+
+    return done;
+}
+
+/**
+ * Flush enough bytes to make cache usage lower than maximum.
+ *
+ * Just like ewk_tile_unused_cache_flush(), but this will make the cache
+ * free enough tiles to respect maximum cache size as defined with
+ * ewk_tile_unused_cache_max_set().
+ *
+ * This function is usually called when system becomes idle. This way
+ * we keep memory low but do not impact performance when
+ * creating/deleting tiles.
+ */
+void ewk_tile_unused_cache_auto_flush(Ewk_Tile_Unused_Cache *tuc)
+{
+    EINA_SAFETY_ON_NULL_RETURN(tuc);
+    if (tuc->memory.used <= tuc->memory.max)
+        return;
+    ewk_tile_unused_cache_flush(tuc, tuc->memory.used - tuc->memory.max);
+    if (tuc->memory.used > tuc->memory.max)
+        CRITICAL("Cache still using too much memory: %zd KB; max: %zd KB",
+                 tuc->memory.used, tuc->memory.max);
+}
+
+/**
+ * Flag cache as dirty.
+ *
+ * If cache is dirty then next flush operations will have to recompute
+ * weight and sort again to find the best tiles to expire.
+ *
+ * One must call this function when tile properties that may change
+ * likeness of tile to be flushed change, like Tile::stats.
+ */
+void ewk_tile_unused_cache_dirty(Ewk_Tile_Unused_Cache *tuc)
+{
+    tuc->dirty = EINA_TRUE;
+}
+
+/**
+ * Freeze cache to not do maintenance tasks.
+ *
+ * Maintenance tasks optimize cache usage, but maybe we know we should
+ * hold on them until we do the last operation, in this case we freeze
+ * while operating and then thaw when we're done.
+ *
+ * @see ewk_tile_unused_cache_thaw()
+ */
+void ewk_tile_unused_cache_freeze(Ewk_Tile_Unused_Cache *tuc)
+{
+    tuc->frozen++;
+}
+
+/**
+ * Unfreezes maintenance tasks.
+ *
+ * If this is the last counterpart of freeze, then maintenance tasks
+ * will run immediately.
+ */
+void ewk_tile_unused_cache_thaw(Ewk_Tile_Unused_Cache *tuc)
+{
+    if (!tuc->frozen) {
+        ERR("thawing more than freezing!");
+        return;
+    }
+
+    tuc->frozen--;
+}
+
+/**
+ * Get tile from cache of unused tiles, removing it from the cache.
+ *
+ * If the tile is used, then it's not in cache of unused tiles, so it
+ * is removed from the cache and may be given back with
+ * ewk_tile_unused_cache_tile_put().
+ *
+ * @param tuc cache of unused tiles
+ * @param t the tile to be removed from Ewk_Tile_Unused_Cache.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
+ */
+Eina_Bool ewk_tile_unused_cache_tile_get(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t)
+{
+    Ewk_Tile_Unused_Cache_Entry *entry;
+    Eina_List *e, *l;
+
+    e = NULL;
+    EINA_LIST_FOREACH(tuc->entries.list, l, entry)
+    {
+        if (entry->tile == t) {
+            e = l;
+            break;
+        }
+    }
+    if (!e) {
+        ERR("tile %p not found in cache %p", t, tuc);
+        return EINA_FALSE;
+    }
+
+    tuc->entries.count--;
+    tuc->memory.used -= sizeof(Ewk_Tile) + t->bytes;
+    tuc->entries.list = eina_list_remove_list(tuc->entries.list, e);
+    free(entry);
+    // TODO assume dirty for now, but may it's not,
+    // if the item was at the beginning of the queue
+    tuc->dirty = EINA_TRUE;
+
+    return EINA_TRUE;
+}
+
+/**
+ * Put tile into cache of unused tiles, adding it to the cache.
+ *
+ * This should be called when @c t->visible is @c 0 and no objects are
+ * using the tile anymore, making it available to be expired and have
+ * its memory replaced.
+ *
+ * Note that tiles are not automatically deleted if cache is full,
+ * instead the cache will have more bytes used than maximum and one
+ * can call ewk_tile_unused_cache_auto_flush() to free them. This is done
+ * because usually we want a lazy operation for better performance.
+ *
+ * @param tuc cache of unused tiles
+ * @param t tile to be added to cache.
+ * @param tile_free_cb function used to free tiles.
+ * @param data context to give back to @a tile_free_cb as first argument.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE otherwise. If @c t->visible
+ *         is not #EINA_FALSE, then it will return #EINA_FALSE.
+ *
+ * @see ewk_tile_unused_cache_auto_flush()
+ */
+Eina_Bool ewk_tile_unused_cache_tile_put(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t, void (*tile_free_cb)(void *data, Ewk_Tile *t), const void *data)
+{
+    Ewk_Tile_Unused_Cache_Entry *e;
+
+    if (t->visible) {
+        ERR("tile=%p is not unused (visible=%d)", t, t->visible);
+        return EINA_FALSE;
+    }
+
+    MALLOC_OR_OOM_RET(e, sizeof(Ewk_Tile_Unused_Cache_Entry), EINA_FALSE);
+    tuc->entries.list = eina_list_append(tuc->entries.list, e);
+    if (eina_error_get()) {
+        ERR("List allocation failed");
+        return EINA_FALSE;
+    }
+
+    e->tile = t;
+    e->weight = 0; /* calculated just before sort */
+    e->tile_free.cb = tile_free_cb;
+    e->tile_free.data = (void *)data;
+
+    tuc->entries.count++;
+    tuc->memory.used += sizeof(Ewk_Tile) + t->bytes;
+    tuc->dirty = EINA_TRUE;
+
+    return EINA_TRUE;
+}
+
+void ewk_tile_unused_cache_dbg(const Ewk_Tile_Unused_Cache *tuc)
+{
+    Ewk_Tile_Unused_Cache_Entry *itr;
+    Eina_List *l;
+    int count = 0;
+    printf("Cache of unused tiles: entries: %zu/%zu, memory: %zu/%zu\n",
+           tuc->entries.count, tuc->entries.allocated,
+           tuc->memory.used, tuc->memory.max);
+
+    EINA_LIST_FOREACH(tuc->entries.list, l, itr) {
+        const Ewk_Tile *t = itr->tile;
+        printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c",
+               t->col, t->row, t->w, t->h, t->zoom,
+               t->visible ? '*': ' ');
+
+        if (!(count % 4))
+            printf("\n");
+    }
+
+    printf("\n");
+}
diff --git a/WebKit/efl/ewk/ewk_tiled_model.h b/WebKit/efl/ewk/ewk_tiled_model.h
new file mode 100644 (file)
index 0000000..a546cee
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+    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.
+*/
+
+#ifndef ewk_tiled_model_h
+#define ewk_tiled_model_h
+
+#include "ewk_eapi.h"
+#include "ewk_tiled_backing_store.h"
+
+#include <Evas.h>
+
+/* model */
+EAPI Ewk_Tile *ewk_tile_new(Evas *evas, Evas_Coord w, Evas_Coord h, float zoom, Evas_Colorspace cspace);
+EAPI void ewk_tile_free(Ewk_Tile *t);
+EAPI void ewk_tile_unused_cache_clear(Ewk_Tile_Unused_Cache *tuc);
+EAPI void ewk_tile_show(Ewk_Tile *t);
+EAPI void ewk_tile_hide(Ewk_Tile *t);
+Eina_Bool ewk_tile_visible_get(Ewk_Tile *t);
+EAPI void ewk_tile_update_full(Ewk_Tile *t);
+EAPI void ewk_tile_update_area(Ewk_Tile *t, const Eina_Rectangle *r);
+EAPI void ewk_tile_updates_process(Ewk_Tile *t, void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data);
+EAPI void ewk_tile_updates_clear(Ewk_Tile *t);
+
+/* cache of unused tiles */
+EAPI Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_new(size_t max);
+EAPI void ewk_tile_unused_cache_lock_area(Ewk_Tile_Unused_Cache *tuc, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom);
+EAPI void ewk_tile_unused_cache_unlock_area(Ewk_Tile_Unused_Cache *tuc);
+EAPI void ewk_tile_unused_cache_free(Ewk_Tile_Unused_Cache *tuc);
+EAPI Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_ref(Ewk_Tile_Unused_Cache *tuc);
+EAPI void ewk_tile_unused_cache_unref(Ewk_Tile_Unused_Cache *tuc);
+
+EAPI void ewk_tile_unused_cache_dirty(Ewk_Tile_Unused_Cache *tuc);
+
+EAPI void ewk_tile_unused_cache_freeze(Ewk_Tile_Unused_Cache *tuc);
+EAPI void ewk_tile_unused_cache_thaw(Ewk_Tile_Unused_Cache *tuc);
+
+EAPI Eina_Bool ewk_tile_unused_cache_tile_get(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t);
+EAPI Eina_Bool ewk_tile_unused_cache_tile_put(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t, void (*tile_free_cb)(void *data, Ewk_Tile *t), const void *data);
+
+#endif // ewk_tiled_model_h
+
diff --git a/WebKit/efl/ewk/ewk_tiled_private.h b/WebKit/efl/ewk/ewk_tiled_private.h
new file mode 100644 (file)
index 0000000..ce65f43
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+    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.
+*/
+
+#ifndef ewk_tiled_private_h
+#define ewk_tiled_private_h
+
+/* logging */
+extern int _ewk_tiled_log_dom;
+
+#define CRITICAL(...) EINA_LOG_DOM_CRIT(_ewk_tiled_log_dom, __VA_ARGS__)
+#define ERR(...) EINA_LOG_DOM_ERR(_ewk_tiled_log_dom, __VA_ARGS__)
+#define WRN(...) EINA_LOG_DOM_WARN(_ewk_tiled_log_dom, __VA_ARGS__)
+#define INF(...) EINA_LOG_DOM_INFO(_ewk_tiled_log_dom, __VA_ARGS__)
+#define DBG(...) EINA_LOG_DOM_DBG(_ewk_tiled_log_dom, __VA_ARGS__)
+#define OOM(op, size) CRITICAL("could not %s %zd bytes: %s", op, size, strerror(errno))
+#define MALLOC_OR_OOM_RET(ptr, size, ...)       \
+    do {                                        \
+        ptr = malloc(size);                     \
+        if (!ptr && (size) > 0) {               \
+            OOM("malloc", (size));              \
+            return __VA_ARGS__;                 \
+        }                                       \
+    } while (0)
+
+#define CALLOC_OR_OOM_RET(ptr, size, ...)       \
+    do {                                        \
+        ptr = calloc(1, size);                  \
+        if (!ptr && (size) > 0) {               \
+            OOM("calloc", (size));              \
+            return __VA_ARGS__;                 \
+        }                                       \
+    } while (0)
+
+#define REALLOC_OR_OOM_RET(ptr, size, ...)      \
+    do {                                        \
+        void *__tmp_ptr;                        \
+        __tmp_ptr = realloc(ptr, size);         \
+        if (!__tmp_ptr && (size) > 0) {         \
+            OOM("realloc", (size));             \
+            return __VA_ARGS__;                 \
+        }                                       \
+        ptr = __tmp_ptr;                        \
+    } while (0)
+
+#endif // ewk_tiled_private_h
index 5ff3ade..5147b6d 100644 (file)
@@ -94,6 +94,9 @@ struct _Ewk_View_Private_Data {
     } scrolls;
     unsigned int imh; /**< input method hints */
     struct {
+        Eina_Bool view_cleared:1;
+    } flags;
+    struct {
         const char* user_agent;
         const char* user_stylesheet;
         const char* encoding_default;
@@ -725,6 +728,12 @@ static void _ewk_view_smart_add(Evas_Object* o)
 
     evas_object_smart_member_add(sd->backing_store, o);
     evas_object_show(sd->backing_store);
+    evas_object_pass_events_set(sd->backing_store, EINA_TRUE);
+
+    sd->events_rect = evas_object_rectangle_add(sd->base.evas);
+    evas_object_color_set(sd->events_rect, 0, 0, 0, 0);
+    evas_object_smart_member_add(sd->events_rect, o);
+    evas_object_show(sd->events_rect);
 
     sd->main_frame = ewk_frame_add(sd->base.evas);
     if (!sd->main_frame) {
@@ -815,6 +824,7 @@ static void _ewk_view_smart_calculate(Evas_Object* o)
             view->adjustViewSize();
         }
         evas_object_resize(sd->main_frame, w, h);
+        evas_object_resize(sd->events_rect, w, h);
         sd->changed.frame_rect = EINA_TRUE;
         sd->view.w = w;
         sd->view.h = h;
@@ -827,6 +837,7 @@ static void _ewk_view_smart_calculate(Evas_Object* o)
     if (sd->changed.position && ((x != sd->view.x) || (y != sd->view.y))) {
         evas_object_move(sd->main_frame, x, y);
         evas_object_move(sd->backing_store, x, y);
+        evas_object_move(sd->events_rect, x, y);
         sd->changed.frame_rect = EINA_TRUE;
         sd->view.x = x;
         sd->view.y = y;
@@ -850,6 +861,25 @@ static void _ewk_view_smart_calculate(Evas_Object* o)
     }
 }
 
+static void _ewk_view_smart_show(Evas_Object *o)
+{
+    EWK_VIEW_SD_GET(o, sd);
+    EWK_VIEW_PRIV_GET(sd, priv);
+
+    if (evas_object_clipees_get(sd->base.clipper))
+        evas_object_show(sd->base.clipper);
+    evas_object_show(sd->backing_store);
+}
+
+static void _ewk_view_smart_hide(Evas_Object *o)
+{
+    EWK_VIEW_SD_GET(o, sd);
+    EWK_VIEW_PRIV_GET(sd, priv);
+
+    evas_object_hide(sd->base.clipper);
+    evas_object_hide(sd->backing_store);
+}
+
 static Eina_Bool _ewk_view_smart_contents_resize(Ewk_View_Smart_Data* sd, int w, int h)
 {
     return EINA_TRUE;
@@ -897,6 +927,13 @@ static Eina_Bool _ewk_view_smart_pre_render_region(Ewk_View_Smart_Data* sd, Evas
     return EINA_FALSE;
 }
 
+static Eina_Bool _ewk_view_smart_pre_render_relative_radius(Ewk_View_Smart_Data* sd, unsigned int n, float zoom)
+{
+    WRN("not supported by engine. sd=%p, n=%u zoom=%f",
+        sd, n, zoom);
+    return EINA_FALSE;
+}
+
 static void _ewk_view_smart_pre_render_cancel(Ewk_View_Smart_Data* sd)
 {
     WRN("not supported by engine. sd=%p", sd);
@@ -954,6 +991,7 @@ static Eina_Bool _ewk_view_zoom_animator_cb(void* data)
         || (now < priv->animated_zoom.time.start)) {
         _ewk_view_zoom_animated_finish(sd);
         ewk_view_zoom_set(sd->self, priv->animated_zoom.zoom.end, cx, cy);
+        sd->api->sc.calculate(sd->self);
         return EINA_FALSE;
     }
 
@@ -991,6 +1029,18 @@ static WebCore::ViewportAttributes _ewk_view_viewport_attributes_compute(Evas_Ob
     return attributes;
 }
 
+static Eina_Bool _ewk_view_smart_disable_render(Ewk_View_Smart_Data *sd)
+{
+    WRN("not supported by engine. sd=%p", sd);
+    return EINA_FALSE;
+}
+
+static Eina_Bool _ewk_view_smart_enable_render(Ewk_View_Smart_Data *sd)
+{
+    WRN("not supported by engine. sd=%p", sd);
+    return EINA_FALSE;
+}
+
 /**
  * Sets the smart class api without any backing store, enabling view
  * to be inherited.
@@ -1030,13 +1080,18 @@ Eina_Bool ewk_view_base_smart_set(Ewk_View_Smart_Class* api)
     api->sc.resize = _ewk_view_smart_resize;
     api->sc.move = _ewk_view_smart_move;
     api->sc.calculate = _ewk_view_smart_calculate;
+    api->sc.show = _ewk_view_smart_show;
+    api->sc.hide = _ewk_view_smart_hide;
     api->sc.data = EWK_VIEW_TYPE_STR; /* used by type checking */
 
     api->contents_resize = _ewk_view_smart_contents_resize;
     api->zoom_set = _ewk_view_smart_zoom_set;
     api->flush = _ewk_view_smart_flush;
     api->pre_render_region = _ewk_view_smart_pre_render_region;
+    api->pre_render_relative_radius = _ewk_view_smart_pre_render_relative_radius;
     api->pre_render_cancel = _ewk_view_smart_pre_render_cancel;
+    api->disable_render = _ewk_view_smart_disable_render;
+    api->enable_render = _ewk_view_smart_enable_render;
 
     api->focus_in = _ewk_view_smart_focus_in;
     api->focus_out = _ewk_view_smart_focus_out;
@@ -2171,10 +2226,19 @@ Eina_Bool ewk_view_zoom_text_only_set(Evas_Object* o, Eina_Bool setting)
 Eina_Bool ewk_view_pre_render_region(Evas_Object* o, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom)
 {
     EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE);
+    EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE);
     EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->pre_render_region, EINA_FALSE);
-    float cur_zoom = ewk_frame_zoom_get(sd->main_frame);
+    float cur_zoom;
     Evas_Coord cw, ch;
 
+    /* When doing animated zoom it's not possible to call pre-render since it
+     * would screw up parameters that animation is currently using
+     */
+    if (priv->animated_zoom.animator)
+        return EINA_FALSE;
+
+    cur_zoom = ewk_frame_zoom_get(sd->main_frame);
+
     if (cur_zoom < 0.00001)
         return EINA_FALSE;
     if (!ewk_frame_contents_size_get(sd->main_frame, &cw, &ch))
@@ -2203,6 +2267,35 @@ Eina_Bool ewk_view_pre_render_region(Evas_Object* o, Evas_Coord x, Evas_Coord y,
 }
 
 /**
+ * Hint engine to pre-render region, given n extra cols/rows
+ *
+ * This is an alternative method to ewk_view_pre_render_region(). It does not
+ * make sense in all engines and therefore it might not be implemented at all.
+ *
+ * It's only useful if engine divide the area being rendered in smaller tiles,
+ * forming a grid. Then, browser could call this function to pre-render @param n
+ * rows/cols involving the current viewport.
+ *
+ * @param o view to ask pre-render on.
+ * @param n number of cols/rows that must be part of the region pre-rendered
+ *
+ * @see ewk_view_pre_render_region()
+ */
+Eina_Bool ewk_view_pre_render_relative_radius(Evas_Object* o, unsigned int n)
+{
+    EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE);
+    EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv, EINA_FALSE);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->pre_render_relative_radius, EINA_FALSE);
+    float cur_zoom;
+
+    if (priv->animated_zoom.animator)
+        return EINA_FALSE;
+
+    cur_zoom = ewk_frame_zoom_get(sd->main_frame);
+    return sd->api->pre_render_relative_radius(sd, n, cur_zoom);
+}
+
+/**
  * Get input method hints
  *
  * @param o View.
@@ -2228,6 +2321,36 @@ void ewk_view_pre_render_cancel(Evas_Object* o)
     sd->api->pre_render_cancel(sd);
 }
 
+/**
+  * Enable processing of update requests.
+  *
+  * @param o view to enable rendering.
+  *
+  * @return @c EINA_TRUE if render was enabled, @c EINA_FALSE
+            otherwise (errors, rendering suspension not supported).
+  */
+Eina_Bool ewk_view_enable_render(const Evas_Object *o)
+{
+    EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->enable_render, EINA_FALSE);
+    return sd->api->enable_render(sd);
+}
+
+/**
+  * Disable processing of update requests.
+  *
+  * @param o view to disable rendering.
+  *
+  * @return @c EINA_TRUE if render was disabled, @c EINA_FALSE
+            otherwise (errors, rendering suspension not supported).
+  */
+Eina_Bool ewk_view_disable_render(const Evas_Object *o)
+{
+    EWK_VIEW_SD_GET_OR_RETURN(o, sd, EINA_FALSE);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(sd->api->disable_render, EINA_FALSE);
+    return sd->api->disable_render(sd);
+}
+
 const char* ewk_view_setting_user_agent_get(const Evas_Object* o)
 {
     EWK_VIEW_SD_GET_OR_RETURN(o, sd, 0);
@@ -3952,7 +4075,6 @@ void ewk_view_scroll(Evas_Object* o, Evas_Coord dx, Evas_Coord dy, Evas_Coord sx
     EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv);
     EINA_SAFETY_ON_TRUE_RETURN(!dx && !dy);
 
-    _ewk_view_scroll_add(priv, dx, dy, sx, sy, sw, sh, main_frame);
     _ewk_view_smart_changed(sd);
 }
 
@@ -4284,6 +4406,42 @@ float ewk_view_device_pixel_ratio_get(Evas_Object* o)
     return priv->settings.device_pixel_ratio;
 }
 
+void ewk_view_did_first_visually_nonempty_layout(Evas_Object *o)
+{
+    EWK_VIEW_SD_GET_OR_RETURN(o, sd);
+    EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv);
+    if (!priv->flags.view_cleared) {
+        ewk_view_frame_main_cleared(o);
+        ewk_view_enable_render(o);
+        priv->flags.view_cleared = EINA_TRUE;
+    }
+}
+
+/**
+ * @internal
+ * Dispatch finished loading.
+ *
+ * @param o view.
+ */
+void ewk_view_dispatch_did_finish_loading(Evas_Object *o)
+{
+    /* If we reach this point and rendering is still disabled, WebCore will not
+     * trigger the didFirstVisuallyNonEmptyLayout signal anymore. So, we
+     * forcefully re-enable the rendering.
+     */
+    ewk_view_did_first_visually_nonempty_layout(o);
+}
+
+void ewk_view_transition_to_commited_for_newpage(Evas_Object *o)
+{
+    EWK_VIEW_SD_GET_OR_RETURN(o, sd);
+    EWK_VIEW_PRIV_GET_OR_RETURN(sd, priv);
+
+    ewk_view_disable_render(o);
+    priv->flags.view_cleared = EINA_FALSE;
+}
+
+
 /**
  * @internal
  * Reports a requeset will be loaded. It's client responsibility to decide if
index 1c4de34..6902949 100644 (file)
@@ -114,7 +114,11 @@ struct _Ewk_View_Smart_Class {
     void (*bg_color_set)(Ewk_View_Smart_Data *sd, unsigned char r, unsigned char g, unsigned char b, unsigned char a);
     void (*flush)(Ewk_View_Smart_Data *sd);
     Eina_Bool (*pre_render_region)(Ewk_View_Smart_Data *sd, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom);
+    Eina_Bool (*pre_render_relative_radius)(Ewk_View_Smart_Data *sd, unsigned int n, float zoom);
     void (*pre_render_cancel)(Ewk_View_Smart_Data *sd);
+    Eina_Bool (*disable_render)(Ewk_View_Smart_Data *sd);
+    Eina_Bool (*enable_render)(Ewk_View_Smart_Data *sd);
+
     // event handling:
     //  - returns true if handled
     //  - if overridden, have to call parent method if desired
@@ -151,7 +155,7 @@ struct _Ewk_View_Smart_Class {
  * @see EWK_VIEW_SMART_CLASS_INIT_VERSION
  * @see EWK_VIEW_SMART_CLASS_INIT_NAME_VERSION
  */
-#define EWK_VIEW_SMART_CLASS_INIT(smart_class_init) {smart_class_init, EWK_VIEW_SMART_CLASS_VERSION, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+#define EWK_VIEW_SMART_CLASS_INIT(smart_class_init) {smart_class_init, EWK_VIEW_SMART_CLASS_VERSION, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
 
 /**
  * Initializer to zero a whole Ewk_View_Smart_Class structure.
@@ -268,6 +272,7 @@ struct _Ewk_View_Smart_Data {
     Evas_Object *self; /**< reference to owner object */
     Evas_Object *main_frame; /**< reference to main frame object */
     Evas_Object *backing_store; /**< reference to backing store */
+    Evas_Object *events_rect; /**< rectangle that should receive mouse events */
     Ewk_View_Private_Data *_priv; /**< should never be accessed, c++ stuff */
     struct {
         Evas_Coord x, y, w, h; /**< last used viewport */
@@ -291,10 +296,36 @@ struct _Ewk_View_Smart_Data {
     } changed;
 };
 
+/**
+ * Cache (pool) that contains unused tiles for ewk_view_tiled.
+ *
+ * This cache will maintain unused tiles and flush them when the total
+ * memory exceeds the set amount when
+ * ewk_tile_unused_cache_auto_flush() or explicitly set value when
+ * ewk_tile_unused_cache_flush() is called.
+ *
+ * The tile may be shared among different ewk_view_tiled instances to
+ * group maximum unused memory resident in the system.
+ */
+typedef struct _Ewk_Tile_Unused_Cache Ewk_Tile_Unused_Cache;
+EAPI void   ewk_tile_unused_cache_max_set(Ewk_Tile_Unused_Cache *tuc, size_t max);
+EAPI size_t ewk_tile_unused_cache_max_get(const Ewk_Tile_Unused_Cache *tuc);
+EAPI size_t ewk_tile_unused_cache_used_get(const Ewk_Tile_Unused_Cache *tuc);
+EAPI size_t ewk_tile_unused_cache_flush(Ewk_Tile_Unused_Cache *tuc, size_t bytes);
+EAPI void   ewk_tile_unused_cache_auto_flush(Ewk_Tile_Unused_Cache *tuc);
+
 EAPI Eina_Bool    ewk_view_base_smart_set(Ewk_View_Smart_Class *api);
 EAPI Eina_Bool    ewk_view_single_smart_set(Ewk_View_Smart_Class *api);
+EAPI Eina_Bool    ewk_view_tiled_smart_set(Ewk_View_Smart_Class *api);
 
 EAPI Evas_Object *ewk_view_single_add(Evas *e);
+EAPI Evas_Object *ewk_view_tiled_add(Evas *e);
+
+EAPI Ewk_Tile_Unused_Cache *ewk_view_tiled_unused_cache_get(const Evas_Object *o);
+EAPI void                   ewk_view_tiled_unused_cache_set(Evas_Object *o, Ewk_Tile_Unused_Cache *cache);
+
+// FIXME: this function should be removed later, when we find the best flag to use.
+EAPI void                   ewk_view_tiled_process_entire_queue_set(Evas_Object *o, Eina_Bool flag);
 
 EAPI void         ewk_view_fixed_layout_size_set(Evas_Object *o, Evas_Coord w, Evas_Coord h);
 EAPI void         ewk_view_fixed_layout_size_get(Evas_Object *o, Evas_Coord *w, Evas_Coord *h);
@@ -370,7 +401,10 @@ EAPI Eina_Bool    ewk_view_zoom_text_only_get(const Evas_Object *o);
 EAPI Eina_Bool    ewk_view_zoom_text_only_set(Evas_Object *o, Eina_Bool setting);
 
 EAPI Eina_Bool    ewk_view_pre_render_region(Evas_Object *o, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom);
+EAPI Eina_Bool    ewk_view_pre_render_relative_radius(Evas_Object *o, unsigned int n);
 EAPI void         ewk_view_pre_render_cancel(Evas_Object *o);
+EAPI Eina_Bool    ewk_view_enable_render(const Evas_Object *o);
+EAPI Eina_Bool    ewk_view_disable_render(const Evas_Object *o);
 
 EAPI unsigned int ewk_view_imh_get(Evas_Object *o);
 
diff --git a/WebKit/efl/ewk/ewk_view_tiled.c b/WebKit/efl/ewk/ewk_view_tiled.c
new file mode 100644 (file)
index 0000000..d380e8e
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+    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_view.h"
+
+#include "ewk_logging.h"
+
+#include <Evas.h>
+#include <eina_safety_checks.h>
+#include <ewk_tiled_backing_store.h>
+
+static Ewk_View_Smart_Class _parent_sc = EWK_VIEW_SMART_CLASS_INIT_NULL;
+
+static Eina_Bool _ewk_view_tiled_render_cb(void *data, Ewk_Tile *t, const Eina_Rectangle *area)
+{
+    Ewk_View_Private_Data *priv = (Ewk_View_Private_Data*)data;
+    Eina_Rectangle r = {area->x + t->x, area->y + t->y, area->w, area->h};
+
+    return ewk_view_paint_contents(priv, t->cairo, &r);
+}
+
+static void *_ewk_view_tiled_updates_process_pre(void *data, Evas_Object *o)
+{
+    Ewk_View_Private_Data *priv = (Ewk_View_Private_Data*)data;
+    ewk_view_layout_if_needed_recursive(priv);
+    return 0;
+}
+
+static Evas_Object *_ewk_view_tiled_smart_backing_store_add(Ewk_View_Smart_Data *sd)
+{
+    Evas_Object *bs = ewk_tiled_backing_store_add(sd->base.evas);
+    ewk_tiled_backing_store_render_cb_set
+        (bs, _ewk_view_tiled_render_cb, sd->_priv);
+    ewk_tiled_backing_store_updates_process_pre_set
+        (bs, _ewk_view_tiled_updates_process_pre, sd->_priv);
+    return bs;
+}
+
+static void
+_ewk_view_tiled_contents_size_changed_cb(void *data, Evas_Object *o, void *event_info)
+{
+    Evas_Coord *size = (Evas_Coord*)event_info;
+    Ewk_View_Smart_Data *sd = (Ewk_View_Smart_Data*)data;
+
+    ewk_tiled_backing_store_contents_resize
+        (sd->backing_store, size[0], size[1]);
+}
+
+static void _ewk_view_tiled_smart_add(Evas_Object *o)
+{
+    Ewk_View_Smart_Data *sd;
+
+    _parent_sc.sc.add(o);
+
+    sd = (Ewk_View_Smart_Data*)evas_object_smart_data_get(o);
+    evas_object_smart_callback_add(
+        sd->main_frame, "contents,size,changed",
+        _ewk_view_tiled_contents_size_changed_cb, sd);
+    ewk_frame_paint_full_set(sd->main_frame, EINA_TRUE);
+}
+
+static Eina_Bool _ewk_view_tiled_smart_scrolls_process(Ewk_View_Smart_Data *sd)
+{
+    const Ewk_Scroll_Request *sr;
+    const Ewk_Scroll_Request *sr_end;
+    size_t count;
+    Evas_Coord vw, vh;
+
+    ewk_frame_contents_size_get(sd->main_frame, &vw, &vh);
+
+    sr = ewk_view_scroll_requests_get(sd->_priv, &count);
+    sr_end = sr + count;
+    for (; sr < sr_end; sr++) {
+        if (sr->main_scroll)
+            ewk_tiled_backing_store_scroll_full_offset_add
+                (sd->backing_store, sr->dx, sr->dy);
+        else {
+            Evas_Coord sx, sy, sw, sh;
+
+            sx = sr->x;
+            sy = sr->y;
+            sw = sr->w;
+            sh = sr->h;
+
+            if (abs(sr->dx) >= sw || abs(sr->dy) >= sh) {
+                /* doubt webkit would be so     stupid... */
+                DBG("full page scroll %+03d,%+03d. convert to repaint %d,%d + %dx%d",
+                    sr->dx, sr->dy, sx, sy, sw, sh);
+                ewk_view_repaint_add(sd->_priv, sx, sy, sw, sh);
+                continue;
+            }
+
+            if (sx + sw > vw)
+                sw = vw - sx;
+            if (sy + sh > vh)
+                sh = vh - sy;
+
+            if (sw < 0)
+                sw = 0;
+            if (sh < 0)
+                sh = 0;
+
+            if (!sw || !sh)
+                continue;
+
+            sx -= abs(sr->dx);
+            sy -= abs(sr->dy);
+            sw += abs(sr->dx);
+            sh += abs(sr->dy);
+            ewk_view_repaint_add(sd->_priv, sx, sy, sw, sh);
+            INF("using repaint for inner frame scolling!");
+        }
+    }
+
+    return EINA_TRUE;
+}
+
+static Eina_Bool _ewk_view_tiled_smart_repaints_process(Ewk_View_Smart_Data *sd)
+{
+    const Eina_Rectangle *pr, *pr_end;
+    size_t count;
+    int sx, sy;
+
+    ewk_frame_scroll_pos_get(sd->main_frame, &sx, &sy);
+
+    pr = ewk_view_repaints_get(sd->_priv, &count);
+    pr_end = pr + count;
+    for (; pr < pr_end; pr++) {
+        Eina_Rectangle r;
+        r.x = pr->x + sx;
+        r.y = pr->y + sy;
+        r.w = pr->w;
+        r.h = pr->h;
+        ewk_tiled_backing_store_update(sd->backing_store, &r);
+    }
+    ewk_tiled_backing_store_updates_process(sd->backing_store);
+
+    return EINA_TRUE;
+}
+
+static Eina_Bool _ewk_view_tiled_smart_contents_resize(Ewk_View_Smart_Data *sd, int w, int h)
+{
+    ewk_tiled_backing_store_contents_resize(sd->backing_store, w, h);
+    return EINA_TRUE;
+}
+
+static Eina_Bool _ewk_view_tiled_smart_zoom_set(Ewk_View_Smart_Data *sd, float zoom, Evas_Coord cx, Evas_Coord cy)
+{
+    Evas_Coord x, y, w, h;
+    Eina_Bool r;
+    r = ewk_tiled_backing_store_zoom_set(sd->backing_store,
+                                         &zoom, cx, cy, &x, &y);
+    if (!r)
+        return r;
+    ewk_tiled_backing_store_disabled_update_set(sd->backing_store, EINA_TRUE);
+    r = _parent_sc.zoom_set(sd, zoom, cx, cy);
+    ewk_frame_scroll_set(sd->main_frame, -x, -y);
+    ewk_frame_scroll_size_get(sd->main_frame, &w, &h);
+    ewk_tiled_backing_store_fix_offsets(sd->backing_store, w, h);
+    ewk_view_scrolls_process(sd);
+    evas_object_smart_calculate(sd->backing_store);
+    evas_object_smart_calculate(sd->self);
+    ewk_tiled_backing_store_disabled_update_set(sd->backing_store, EINA_FALSE);
+    return r;
+}
+
+static Eina_Bool _ewk_view_tiled_smart_zoom_weak_set(Ewk_View_Smart_Data *sd, float zoom, Evas_Coord cx, Evas_Coord cy)
+{
+    return ewk_tiled_backing_store_zoom_weak_set(sd->backing_store, zoom, cx, cy);
+}
+
+static void _ewk_view_tiled_smart_zoom_weak_smooth_scale_set(Ewk_View_Smart_Data *sd, Eina_Bool smooth_scale)
+{
+    ewk_tiled_backing_store_zoom_weak_smooth_scale_set(sd->backing_store, smooth_scale);
+}
+
+static void _ewk_view_tiled_smart_flush(Ewk_View_Smart_Data *sd)
+{
+    ewk_tiled_backing_store_flush(sd->backing_store);
+    _parent_sc.flush(sd);
+}
+
+static Eina_Bool _ewk_view_tiled_smart_pre_render_region(Ewk_View_Smart_Data *sd, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom)
+{
+    return ewk_tiled_backing_store_pre_render_region
+        (sd->backing_store, x, y, w, h, zoom);
+}
+
+static Eina_Bool _ewk_view_tiled_smart_pre_render_relative_radius(Ewk_View_Smart_Data *sd, unsigned int n, float zoom)
+{
+    return ewk_tiled_backing_store_pre_render_relative_radius
+        (sd->backing_store, n, zoom);
+}
+
+static void _ewk_view_tiled_smart_pre_render_cancel(Ewk_View_Smart_Data *sd)
+{
+    ewk_tiled_backing_store_pre_render_cancel(sd->backing_store);
+}
+
+static Eina_Bool _ewk_view_tiled_smart_disable_render(Ewk_View_Smart_Data *sd)
+{
+    return ewk_tiled_backing_store_disable_render(sd->backing_store);
+}
+
+static Eina_Bool _ewk_view_tiled_smart_enable_render(Ewk_View_Smart_Data *sd)
+{
+    return ewk_tiled_backing_store_enable_render(sd->backing_store);
+}
+
+/**
+ * Sets the smart class api using tiled backing store, enabling view
+ * to be inherited.
+ *
+ * @param api class definition to be set, all members with the
+ *        exception of Evas_Smart_Class->data may be overridden. Must
+ *        @b not be @c NULL.
+ *
+ * @note Evas_Smart_Class->data is used to implement type checking and
+ *       is not supposed to be changed/overridden. If you need extra
+ *       data for your smart class to work, just extend
+ *       Ewk_View_Smart_Class instead.
+ *
+ * @return @c EINA_TRUE on success, @c EINA_FALSE on failure (probably
+ *         version mismatch).
+ *
+ * @see ewk_view_base_smart_set()
+ */
+Eina_Bool ewk_view_tiled_smart_set(Ewk_View_Smart_Class *api)
+{
+    if (!ewk_view_base_smart_set(api))
+        return EINA_FALSE;
+
+    if (EINA_UNLIKELY(!_parent_sc.sc.add))
+        ewk_view_base_smart_set(&_parent_sc);
+
+    api->sc.add = _ewk_view_tiled_smart_add;
+
+    api->backing_store_add = _ewk_view_tiled_smart_backing_store_add;
+    api->scrolls_process = _ewk_view_tiled_smart_scrolls_process;
+    api->repaints_process = _ewk_view_tiled_smart_repaints_process;
+    api->contents_resize = _ewk_view_tiled_smart_contents_resize;
+    api->zoom_set = _ewk_view_tiled_smart_zoom_set;
+    api->zoom_weak_set = _ewk_view_tiled_smart_zoom_weak_set;
+    api->zoom_weak_smooth_scale_set = _ewk_view_tiled_smart_zoom_weak_smooth_scale_set;
+    api->flush = _ewk_view_tiled_smart_flush;
+    api->pre_render_region = _ewk_view_tiled_smart_pre_render_region;
+    api->pre_render_relative_radius = _ewk_view_tiled_smart_pre_render_relative_radius;
+    api->pre_render_cancel = _ewk_view_tiled_smart_pre_render_cancel;
+    api->disable_render = _ewk_view_tiled_smart_disable_render;
+    api->enable_render = _ewk_view_tiled_smart_enable_render;
+    return EINA_TRUE;
+}
+
+static inline Evas_Smart *_ewk_view_tiled_smart_class_new(void)
+{
+    static Ewk_View_Smart_Class api = EWK_VIEW_SMART_CLASS_INIT_NAME_VERSION("EWK_View_Tiled");
+    static Evas_Smart *smart = 0;
+
+    if (EINA_UNLIKELY(!smart)) {
+        ewk_view_tiled_smart_set(&api);
+        smart = evas_smart_class_new(&api.sc);
+    }
+
+    return smart;
+}
+
+/**
+ * Creates a new EFL WebKit View object using tiled backing store.
+ *
+ * View objects are the recommended way to deal with EFL WebKit as it
+ * abstracts the complex pieces of the process.
+ *
+ * This object is almost the same as the one returned by the ewk_view_add()
+ * function, but it uses the tiled backing store instead of the default
+ * backing store.
+ *
+ * @param e canvas where to create the view object.
+ *
+ * @return view object or @c NULL if errors.
+ *
+ * @see ewk_view_uri_set()
+ */
+Evas_Object *ewk_view_tiled_add(Evas *e)
+{
+    return evas_object_smart_add(e, _ewk_view_tiled_smart_class_new());
+}
+
+/**
+ * Get the cache of unused tiles used by this view.
+ *
+ * @param o view object to get cache.
+ * @return instance of "cache of unused tiles" or @c NULL on errors.
+ */
+Ewk_Tile_Unused_Cache *ewk_view_tiled_unused_cache_get(const Evas_Object *o)
+{
+    Ewk_View_Smart_Data *sd = ewk_view_smart_data_get(o);
+    EINA_SAFETY_ON_NULL_RETURN_VAL(sd, 0);
+    return ewk_tiled_backing_store_tile_unused_cache_get(sd->backing_store);
+}
+
+/**
+ * Set the cache of unused tiles used by this view.
+ *
+ * @param o view object to get cache.
+ * @param cache instance of "cache of unused tiles". This can be used
+ *        to share a single cache amongst different views. The tiles
+ *        from one view will not be used by the other! This is just to
+ *        limit the group with amount of unused memory.
+ *        If @c NULL is provided, then a new cache is created.
+ */
+void ewk_view_tiled_unused_cache_set(Evas_Object *o, Ewk_Tile_Unused_Cache *cache)
+{
+    Ewk_View_Smart_Data *sd = ewk_view_smart_data_get(o);
+    EINA_SAFETY_ON_NULL_RETURN(sd);
+    ewk_tiled_backing_store_tile_unused_cache_set(sd->backing_store, cache);
+}
+
+/**
+ * Set the function with the same name of the tiled backing store.
+ * @param o the tiled backing store object.
+ * @param flag value of the tiled backing store flag to set.
+ */
+void ewk_view_tiled_process_entire_queue_set(Evas_Object *o, Eina_Bool flag)
+{
+    Ewk_View_Smart_Data *sd = ewk_view_smart_data_get(o);
+    EINA_SAFETY_ON_NULL_RETURN(sd);
+    ewk_tiled_backing_store_process_entire_queue_set(sd->backing_store, flag);
+}