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