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