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_matrix.h"
23
24 #define _GNU_SOURCE
25 #include "ewk_tiled_backing_store.h"
26 #include "ewk_tiled_private.h"
27 #include <Eina.h>
28 #include <errno.h>
29 #include <inttypes.h>
30 #include <math.h>
31 #include <stdio.h> // XXX remove me later
32 #include <stdlib.h>
33 #include <string.h>
34
35 static const Evas_Coord TILE_MATRIX_BASE_TILE_SIZE = 256;
36
37 struct _Ewk_Tile_Matrix {
38 Eina_Matrixsparse *matrix;
39 Ewk_Tile_Unused_Cache *tuc;
40 Evas_Colorspace cspace;
41 struct {
42 void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update);
43 void *data;
44 } render;
45 unsigned int frozen;
46 Eina_List *updates;
47 #ifdef DEBUG_MEM_LEAKS
48 struct {
49 struct {
50 uint64_t allocated, freed;
51 } tiles, bytes;
52 } stats;
53 #endif
54 };
55
56 #ifdef DEBUG_MEM_LEAKS
57 static uint64_t tiles_leaked = 0;
58 static uint64_t bytes_leaked = 0;
59 #endif
60
61 /* called when matrixsparse is resized or freed */
_ewk_tile_matrix_cell_free(void * user_data,void * cell_data)62 static void _ewk_tile_matrix_cell_free(void *user_data, void *cell_data)
63 {
64 Ewk_Tile_Matrix *tm = user_data;
65 Eina_Inlist *l = cell_data;
66
67 if (!l)
68 return;
69
70 ewk_tile_unused_cache_freeze(tm->tuc);
71
72 while (l) {
73 Ewk_Tile *t = (Ewk_Tile *)l;
74 l = l->next;
75
76 if (t->updates || t->stats.full_update)
77 tm->updates = eina_list_remove(tm->updates, t);
78
79 if (t->visible)
80 ERR("freeing cell that is visible, leaking tile %p", t);
81 else {
82 if (!ewk_tile_unused_cache_tile_get(tm->tuc, t))
83 ERR("tile %p was not in cache %p? leaking...", t, tm->tuc);
84 else {
85 DBG("tile cell does not exist anymore, free it %p", t);
86
87 #ifdef DEBUG_MEM_LEAKS
88 tm->stats.bytes.freed += t->bytes;
89 tm->stats.tiles.freed++;
90 #endif
91
92 ewk_tile_free(t);
93 }
94 }
95 }
96
97 ewk_tile_unused_cache_thaw(tm->tuc);
98 }
99
100 /* called when cache of unused tile is flushed */
_ewk_tile_matrix_tile_free(void * data,Ewk_Tile * t)101 static void _ewk_tile_matrix_tile_free(void *data, Ewk_Tile *t)
102 {
103 Ewk_Tile_Matrix *tm = data;
104 Eina_Matrixsparse_Cell *cell;
105 Eina_Inlist *l, *old;
106
107 if (!eina_matrixsparse_cell_idx_get(tm->matrix, t->row, t->col, &cell)) {
108 ERR("removing tile %p that was not in the matrix? Leaking...", t);
109 return;
110 }
111
112 if (t->updates || t->stats.full_update)
113 tm->updates = eina_list_remove(tm->updates, t);
114
115 old = eina_matrixsparse_cell_data_get(cell);
116 l = eina_inlist_remove(old, EINA_INLIST_GET(t));
117 if (!l) {
118 /* set to null to avoid double free */
119 eina_matrixsparse_cell_data_replace(cell, NULL, NULL);
120 eina_matrixsparse_cell_clear(cell);
121 } else if (old != l)
122 eina_matrixsparse_cell_data_replace(cell, l, NULL);
123
124 if (EINA_UNLIKELY(!!t->visible)) {
125 ERR("cache of unused tiles requesting deletion of used tile %p? "
126 "Leaking...", t);
127 return;
128 }
129
130 #ifdef DEBUG_MEM_LEAKS
131 tm->stats.bytes.freed += t->bytes;
132 tm->stats.tiles.freed++;
133 #endif
134
135 ewk_tile_free(t);
136 }
137
138 /**
139 * Creates a new matrix of tiles.
140 *
141 * The tile matrix is responsible for keeping tiles around and
142 * providing fast access to them. One can use it to retrieve new or
143 * existing tiles and give them back, allowing them to be
144 * freed/replaced by the cache.
145 *
146 * @param tuc cache of unused tiles or @c NULL to create one
147 * automatically.
148 * @param cols number of columns in the matrix.
149 * @param rows number of rows in the matrix.
150 * @param cspace the color space used to create tiles in this matrix.
151 * @param render_cb function used to render given tile update.
152 * @param data context to give back to @a render_cb.
153 *
154 * @return newly allocated instance on success, @c NULL on failure.
155 */
ewk_tile_matrix_new(Ewk_Tile_Unused_Cache * tuc,unsigned long cols,unsigned long rows,Evas_Colorspace cspace,void (* render_cb)(void * data,Ewk_Tile * t,const Eina_Rectangle * update),const void * data)156 Ewk_Tile_Matrix *ewk_tile_matrix_new(Ewk_Tile_Unused_Cache *tuc, unsigned long cols, unsigned long rows, Evas_Colorspace cspace, void (*render_cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data)
157 {
158 Ewk_Tile_Matrix *tm;
159
160 CALLOC_OR_OOM_RET(tm, sizeof(Ewk_Tile_Matrix), NULL);
161
162 tm->matrix = eina_matrixsparse_new(rows, cols, _ewk_tile_matrix_cell_free, tm);
163 if (!tm->matrix) {
164 ERR("could not create sparse matrix.");
165 free(tm);
166 return NULL;
167 }
168
169 if (tuc)
170 tm->tuc = ewk_tile_unused_cache_ref(tuc);
171 else {
172 tm->tuc = ewk_tile_unused_cache_new(40960000);
173 if (!tm->tuc) {
174 ERR("no cache of unused tile!");
175 eina_matrixsparse_free(tm->matrix);
176 free(tm);
177 return NULL;
178 }
179 }
180
181 tm->cspace = cspace;
182 tm->render.cb = render_cb;
183 tm->render.data = (void *)data;
184
185 return tm;
186 }
187
188 /**
189 * Destroys tiles matrix, releasing its resources.
190 *
191 * The cache instance is unreferenced, possibly freeing it.
192 */
ewk_tile_matrix_free(Ewk_Tile_Matrix * tm)193 void ewk_tile_matrix_free(Ewk_Tile_Matrix *tm)
194 {
195 #ifdef DEBUG_MEM_LEAKS
196 uint64_t tiles, bytes;
197 #endif
198
199 EINA_SAFETY_ON_NULL_RETURN(tm);
200 ewk_tile_unused_cache_freeze(tm->tuc);
201
202 eina_matrixsparse_free(tm->matrix);
203
204 ewk_tile_unused_cache_thaw(tm->tuc);
205 ewk_tile_unused_cache_unref(tm->tuc);
206
207 #ifdef DEBUG_MEM_LEAKS
208 tiles = tm->stats.tiles.allocated - tm->stats.tiles.freed;
209 bytes = tm->stats.bytes.allocated - tm->stats.bytes.freed;
210
211 tiles_leaked += tiles;
212 bytes_leaked += bytes;
213
214 if (tiles || bytes)
215 ERR("tiled matrix leaked: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
216 "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]",
217 tm->stats.tiles.allocated, tm->stats.tiles.freed, tiles,
218 tm->stats.bytes.allocated, tm->stats.bytes.freed, bytes);
219 else if (tiles_leaked || bytes_leaked)
220 WRN("tiled matrix had no leaks: tiles[+%"PRIu64",-%"PRIu64"] "
221 "bytes[+%"PRIu64",-%"PRIu64"], but some other leaked "
222 "%"PRIu64" tiles (%"PRIu64" bytes)",
223 tm->stats.tiles.allocated, tm->stats.tiles.freed,
224 tm->stats.bytes.allocated, tm->stats.bytes.freed,
225 tiles_leaked, bytes_leaked);
226 else
227 INF("tiled matrix had no leaks: tiles[+%"PRIu64",-%"PRIu64"] "
228 "bytes[+%"PRIu64",-%"PRIu64"]",
229 tm->stats.tiles.allocated, tm->stats.tiles.freed,
230 tm->stats.bytes.allocated, tm->stats.bytes.freed);
231 #endif
232
233 free(tm);
234 }
235
236 /**
237 * Resize matrix to given number of rows and columns.
238 */
ewk_tile_matrix_resize(Ewk_Tile_Matrix * tm,unsigned long cols,unsigned long rows)239 void ewk_tile_matrix_resize(Ewk_Tile_Matrix *tm, unsigned long cols, unsigned long rows)
240 {
241 EINA_SAFETY_ON_NULL_RETURN(tm);
242 eina_matrixsparse_size_set(tm->matrix, rows, cols);
243 }
244
245 /**
246 * Get the cache of unused tiles in use by given matrix.
247 *
248 * No reference is taken to the cache.
249 */
ewk_tile_matrix_unused_cache_get(const Ewk_Tile_Matrix * tm)250 Ewk_Tile_Unused_Cache *ewk_tile_matrix_unused_cache_get(const Ewk_Tile_Matrix *tm)
251 {
252 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL);
253 return tm->tuc;
254 }
255
256 /**
257 * Get the exact tile for the given position and zoom.
258 *
259 * If the tile was unused then it's removed from the cache.
260 *
261 * After usage, please give it back using
262 * ewk_tile_matrix_tile_put(). If you just want to check if it exists,
263 * then use ewk_tile_matrix_tile_exact_exists().
264 *
265 * @param tm the tile matrix to get tile from.
266 * @param col the column number.
267 * @param row the row number.
268 * @param zoom the exact zoom to use.
269 *
270 * @return The tile instance or @c NULL if none is found. If the tile
271 * was in the unused cache it will be @b removed (thus
272 * considered used) and one should give it back with
273 * ewk_tile_matrix_tile_put() afterwards.
274 *
275 * @see ewk_tile_matrix_tile_nearest_get()
276 * @see ewk_tile_matrix_tile_exact_get()
277 */
ewk_tile_matrix_tile_exact_get(Ewk_Tile_Matrix * tm,unsigned long col,unsigned int row,float zoom)278 Ewk_Tile *ewk_tile_matrix_tile_exact_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom)
279 {
280 Ewk_Tile *t, *item, *item_found = NULL;
281 Eina_Inlist *inl;
282
283 t = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
284 if (!t)
285 return NULL;
286
287 if (t->zoom == zoom)
288 goto end;
289
290 EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) {
291 if (item->zoom != zoom)
292 continue;
293 item_found = item;
294 break;
295 }
296
297 if (!item_found)
298 return NULL;
299
300 inl = eina_inlist_promote(EINA_INLIST_GET(t), EINA_INLIST_GET(item_found));
301 eina_matrixsparse_data_idx_replace(tm->matrix, row, col, inl, NULL);
302
303 end:
304 if (!t->visible) {
305 if (!ewk_tile_unused_cache_tile_get(tm->tuc, t))
306 WRN("Ewk_Tile was unused but not in cache? bug!");
307 }
308
309 return t;
310 }
311
312 /**
313 * Checks if tile of given zoom exists in matrix.
314 *
315 * @param tm the tile matrix to check tile existence.
316 * @param col the column number.
317 * @param row the row number.
318 * @param zoom the exact zoom to query.
319 *
320 * @return @c EINA_TRUE if found, @c EINA_FALSE otherwise.
321 *
322 * @see ewk_tile_matrix_tile_exact_get()
323 */
ewk_tile_matrix_tile_exact_exists(Ewk_Tile_Matrix * tm,unsigned long col,unsigned int row,float zoom)324 Eina_Bool ewk_tile_matrix_tile_exact_exists(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom)
325 {
326 Ewk_Tile *t, *item;
327
328 t = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
329 if (!t)
330 return EINA_FALSE;
331
332 EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) {
333 if (item->zoom == zoom)
334 return EINA_TRUE;
335 }
336
337 return EINA_FALSE;
338 }
339
340 /**
341 * Get the nearest tile for given position and zoom.
342 *
343 * The nearest tile is the one at the given position but with the
344 * closest zoom, this being the division of the tile zoom by the
345 * desired zoom, the closest to 1.0 gets it.
346 *
347 * If the tile was unused then it's removed from the cache.
348 *
349 * After usage, please give it back using ewk_tile_matrix_tile_put().
350 *
351 * @param tm the tile matrix to get tile from.
352 * @param col the column number.
353 * @param row the row number.
354 * @param zoom the exact zoom to use.
355 *
356 * @return The tile instance or @c NULL if none is found. If the tile
357 * was in the unused cache it will be @b removed (thus
358 * considered used) and one should give it back with
359 * ewk_tile_matrix_tile_put() afterwards.
360 */
ewk_tile_matrix_tile_nearest_get(Ewk_Tile_Matrix * tm,unsigned long col,unsigned int row,float zoom)361 Ewk_Tile *ewk_tile_matrix_tile_nearest_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom)
362 {
363 Ewk_Tile *t, *item, *item_found = NULL;
364 Eina_Inlist *inl;
365 float zoom_found = 0;
366
367 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL);
368 EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, NULL);
369
370 t = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
371 if (!t)
372 return NULL;
373
374 if (t->zoom == zoom) {
375 item_found = t;
376 goto end;
377 }
378
379 EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) {
380 float cur_zoom = item->zoom;
381
382 if (cur_zoom == zoom) {
383 item_found = item;
384 break;
385 }
386
387 if (cur_zoom > zoom)
388 cur_zoom = zoom / cur_zoom;
389 else
390 cur_zoom = cur_zoom / zoom;
391
392 if (cur_zoom > zoom_found) {
393 item_found = item;
394 zoom_found = cur_zoom;
395 }
396 }
397
398 if (!item_found)
399 return NULL;
400
401 inl = eina_inlist_promote(EINA_INLIST_GET(t), EINA_INLIST_GET(item_found));
402 eina_matrixsparse_data_idx_replace(tm->matrix, row, col, inl, NULL);
403
404 end:
405 if (!item_found->visible) {
406 if (!ewk_tile_unused_cache_tile_get(tm->tuc, item_found))
407 WRN("Ewk_Tile was unused but not in cache? bug!");
408 }
409
410 return item_found;
411 }
412
413 /**
414 * Create a new tile at given position and zoom level in the matrix.
415 *
416 * The newly created tile is considered in use and not put into cache
417 * of unused tiles. After it is used one should call
418 * ewk_tile_matrix_tile_put() to give it back to matrix.
419 *
420 * @param tm the tile matrix to create tile on.
421 * @param col the column number.
422 * @param row the row number.
423 * @param zoom the level to create tile, used to determine tile size.
424 */
ewk_tile_matrix_tile_new(Ewk_Tile_Matrix * tm,Evas * evas,unsigned long col,unsigned int row,float zoom)425 Ewk_Tile *ewk_tile_matrix_tile_new(Ewk_Tile_Matrix *tm, Evas *evas, unsigned long col, unsigned int row, float zoom)
426 {
427 Evas_Coord s;
428 Eina_Inlist *old;
429 Ewk_Tile *t;
430
431 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL);
432 EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, NULL);
433
434 s = TILE_SIZE_AT_ZOOM(TILE_MATRIX_BASE_TILE_SIZE, zoom);
435 zoom = (float)s / (float)TILE_MATRIX_BASE_TILE_SIZE;
436
437 t = ewk_tile_new(evas, s, s, zoom, tm->cspace);
438 if (!t) {
439 ERR("could not create tile %dx%d at %f, cspace=%d",
440 s, s, (double)zoom, tm->cspace);
441 return NULL;
442 }
443
444 old = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
445 old = eina_inlist_prepend(old, EINA_INLIST_GET(t));
446 if (!eina_matrixsparse_data_idx_replace(tm->matrix, row, col, t, NULL)) {
447 ERR("could not set matrix cell, row/col outside matrix dimensions!");
448 ewk_tile_free(t);
449 return NULL;
450 }
451
452 t->col = col;
453 t->row = row;
454 t->x = col * s;
455 t->y = row * s;
456
457 cairo_translate(t->cairo, -t->x, -t->y);
458
459 t->stats.full_update = EINA_TRUE;
460 tm->updates = eina_list_append(tm->updates, t);
461
462 #ifdef DEBUG_MEM_LEAKS
463 tm->stats.bytes.allocated += t->bytes;
464 tm->stats.tiles.allocated++;
465 #endif
466
467 return t;
468 }
469
470 /**
471 * Gives back the tile to the tile matrix.
472 *
473 * This will report the tile is no longer in use by the one that got
474 * it with ewk_tile_matrix_tile_nearest_get() or
475 * ewk_tile_matrix_tile_exact_get().
476 *
477 * Any previous reference to tile should be released
478 * (ewk_tile_hide()) before calling this function, so it will
479 * be known if it is not visibile anymore and thus can be put into the
480 * unused cache.
481 *
482 * @param tm the tile matrix to return tile to.
483 * @param t the tile instance to return, must @b not be @c NULL.
484 * @param last_used time in which tile was last used.
485 *
486 * @return #EINA_TRUE on success or #EINA_FALSE on failure.
487 */
ewk_tile_matrix_tile_put(Ewk_Tile_Matrix * tm,Ewk_Tile * t,double last_used)488 Eina_Bool ewk_tile_matrix_tile_put(Ewk_Tile_Matrix *tm, Ewk_Tile *t, double last_used)
489 {
490 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
491 EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE);
492
493 if (t->visible)
494 return EINA_TRUE;
495
496 t->stats.last_used = last_used;
497 return ewk_tile_unused_cache_tile_put(tm->tuc, t, _ewk_tile_matrix_tile_free, tm);
498 }
499
ewk_tile_matrix_tile_update(Ewk_Tile_Matrix * tm,unsigned long col,unsigned int row,const Eina_Rectangle * update)500 Eina_Bool ewk_tile_matrix_tile_update(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, const Eina_Rectangle *update)
501 {
502 Ewk_Tile *l, *t;
503 Eina_Rectangle new_update;
504 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
505 EINA_SAFETY_ON_NULL_RETURN_VAL(update, EINA_FALSE);
506
507 memcpy(&new_update, update, sizeof(new_update));
508 // check update is valid, otherwise return EINA_FALSE
509 if (update->x < 0 || update->y < 0 || update->w <= 0 || update->h <= 0) {
510 ERR("invalid update region.");
511 return EINA_FALSE;
512 }
513
514 if (update->x + update->w - 1 >= TILE_MATRIX_BASE_TILE_SIZE)
515 new_update.w = TILE_MATRIX_BASE_TILE_SIZE - update->x;
516 if (update->y + update->h - 1 >= TILE_MATRIX_BASE_TILE_SIZE)
517 new_update.h = TILE_MATRIX_BASE_TILE_SIZE - update->y;
518
519 l = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
520
521 if (!l)
522 return EINA_TRUE;
523
524 EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) {
525 if (!t->updates && !t->stats.full_update)
526 tm->updates = eina_list_append(tm->updates, t);
527 new_update.x = roundf(t->zoom * new_update.x);
528 new_update.y = roundf(t->zoom * new_update.y);
529 new_update.w = roundf(t->zoom * new_update.w);
530 new_update.h = roundf(t->zoom * new_update.h);
531 ewk_tile_update_area(t, &new_update);
532 }
533
534 return EINA_TRUE;
535 }
536
ewk_tile_matrix_tile_update_full(Ewk_Tile_Matrix * tm,unsigned long col,unsigned int row)537 Eina_Bool ewk_tile_matrix_tile_update_full(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row)
538 {
539 Ewk_Tile *l, *t;
540 Eina_Matrixsparse_Cell *cell;
541 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
542
543 if (!eina_matrixsparse_cell_idx_get(tm->matrix, row, col, &cell))
544 return EINA_FALSE;
545
546 if (!cell)
547 return EINA_TRUE;
548
549 l = eina_matrixsparse_cell_data_get(cell);
550 if (!l) {
551 CRITICAL("matrix cell with no tile!");
552 return EINA_TRUE;
553 }
554
555 EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) {
556 if (!t->updates && !t->stats.full_update)
557 tm->updates = eina_list_append(tm->updates, t);
558 ewk_tile_update_full(t);
559 }
560
561 return EINA_TRUE;
562 }
563
ewk_tile_matrix_tile_updates_clear(Ewk_Tile_Matrix * tm,Ewk_Tile * t)564 void ewk_tile_matrix_tile_updates_clear(Ewk_Tile_Matrix *tm, Ewk_Tile *t)
565 {
566 EINA_SAFETY_ON_NULL_RETURN(tm);
567 if (!t->updates && !t->stats.full_update)
568 return;
569 ewk_tile_updates_clear(t);
570 tm->updates = eina_list_remove(tm->updates, t);
571 }
572
_ewk_tile_matrix_slicer_setup(Ewk_Tile_Matrix * tm,const Eina_Rectangle * area,float zoom,Eina_Tile_Grid_Slicer * slicer)573 static Eina_Bool _ewk_tile_matrix_slicer_setup(Ewk_Tile_Matrix *tm, const Eina_Rectangle *area, float zoom, Eina_Tile_Grid_Slicer *slicer)
574 {
575 unsigned long rows, cols;
576 Evas_Coord x, y, w, h, tw, th;
577
578 if (area->w <= 0 || area->h <= 0) {
579 WRN("invalid area region: %d,%d+%dx%d.",
580 area->x, area->y, area->w, area->h);
581 return EINA_FALSE;
582 }
583
584 x = area->x;
585 y = area->y;
586 w = area->w;
587 h = area->h;
588
589 tw = TILE_SIZE_AT_ZOOM(TILE_W, zoom);
590 th = TILE_SIZE_AT_ZOOM(TILE_H, zoom);
591
592 // cropping area region to fit matrix
593 eina_matrixsparse_size_get(tm->matrix, &rows, &cols);
594 if (x < 0) {
595 w += x;
596 x = 0;
597 }
598 if (y < 0) {
599 h += y;
600 y = 0;
601 }
602
603 if (y + h - 1 > (long)(rows * th))
604 h = rows * th - y;
605 if (x + w - 1 > (long)(cols * tw))
606 w = cols * tw - x;
607
608 return eina_tile_grid_slicer_setup(slicer, x, y, w, h, tw, th);
609 }
610
611
ewk_tile_matrix_update(Ewk_Tile_Matrix * tm,const Eina_Rectangle * update,float zoom)612 Eina_Bool ewk_tile_matrix_update(Ewk_Tile_Matrix *tm, const Eina_Rectangle *update, float zoom)
613 {
614 const Eina_Tile_Grid_Info *info;
615 Eina_Tile_Grid_Slicer slicer;
616 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
617 EINA_SAFETY_ON_NULL_RETURN_VAL(update, EINA_FALSE);
618
619 if (update->w < 1 || update->h < 1) {
620 DBG("Why we get updates with empty areas? %d,%d+%dx%d at zoom %f",
621 update->x, update->y, update->w, update->h, zoom);
622 return EINA_TRUE;
623 }
624
625 if (!_ewk_tile_matrix_slicer_setup(tm, update, zoom, &slicer)) {
626 ERR("Could not setup slicer for update %d,%d+%dx%d at zoom %f",
627 update->x, update->y, update->w, update->h, zoom);
628 return EINA_FALSE;
629 }
630
631 while (eina_tile_grid_slicer_next(&slicer, &info)) {
632 unsigned long col, row;
633 Ewk_Tile *l, *t;
634 col = info->col;
635 row = info->row;
636
637 l = eina_matrixsparse_data_idx_get(tm->matrix, row, col);
638 if (!l)
639 continue;
640
641 EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) {
642 if (!t->updates && !t->stats.full_update)
643 tm->updates = eina_list_append(tm->updates, t);
644 if (info->full)
645 ewk_tile_update_full(t);
646 else {
647 if (t->zoom != zoom)
648 ewk_tile_update_full(t);
649 else
650 ewk_tile_update_area(t, &info->rect);
651 }
652 }
653 }
654
655
656 return EINA_TRUE;
657 }
658
ewk_tile_matrix_updates_process(Ewk_Tile_Matrix * tm)659 void ewk_tile_matrix_updates_process(Ewk_Tile_Matrix *tm)
660 {
661 Eina_List *l, *l_next;
662 Ewk_Tile *t;
663 EINA_SAFETY_ON_NULL_RETURN(tm);
664
665 // process updates, unflag tiles
666 EINA_LIST_FOREACH_SAFE(tm->updates, l, l_next, t) {
667 ewk_tile_updates_process(t, tm->render.cb, tm->render.data);
668 if (t->visible) {
669 ewk_tile_updates_clear(t);
670 tm->updates = eina_list_remove_list(tm->updates, l);
671 }
672 }
673 }
674
ewk_tile_matrix_updates_clear(Ewk_Tile_Matrix * tm)675 void ewk_tile_matrix_updates_clear(Ewk_Tile_Matrix *tm)
676 {
677 Ewk_Tile *t;
678 EINA_SAFETY_ON_NULL_RETURN(tm);
679
680 EINA_LIST_FREE(tm->updates, t)
681 ewk_tile_updates_clear(t);
682 tm->updates = NULL;
683 }
684
685 // remove me later!
ewk_tile_matrix_dbg(const Ewk_Tile_Matrix * tm)686 void ewk_tile_matrix_dbg(const Ewk_Tile_Matrix *tm)
687 {
688 Eina_Iterator *it = eina_matrixsparse_iterator_complete_new(tm->matrix);
689 Eina_Matrixsparse_Cell *cell;
690 Eina_Bool last_empty = EINA_FALSE;
691
692 #ifdef DEBUG_MEM_LEAKS
693 printf("Ewk_Tile Matrix: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
694 "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]\n",
695 tm->stats.tiles.allocated, tm->stats.tiles.freed,
696 tm->stats.tiles.allocated - tm->stats.tiles.freed,
697 tm->stats.bytes.allocated, tm->stats.bytes.freed,
698 tm->stats.bytes.allocated - tm->stats.bytes.freed);
699 #else
700 printf("Ewk_Tile Matrix:\n");
701 #endif
702
703 EINA_ITERATOR_FOREACH(it, cell) {
704 unsigned long row, col;
705 Eina_Inlist *l;
706 eina_matrixsparse_cell_position_get(cell, &row, &col);
707 l = eina_matrixsparse_cell_data_get(cell);
708
709 if (!l) {
710 if (!last_empty) {
711 last_empty = EINA_TRUE;
712 printf("Empty:");
713 }
714 printf(" [%lu,%lu]", col, row);
715 } else {
716 if (last_empty) {
717 last_empty = EINA_FALSE;
718 printf("\n");
719 }
720 Ewk_Tile *t;
721
722 printf("%3lu,%3lu %10p:", col, row, l);
723 EINA_INLIST_FOREACH(l, t)
724 printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c",
725 t->col, t->row, t->w, t->h, t->zoom,
726 t->visible ? '*': ' ');
727 printf("\n");
728 }
729 }
730 if (last_empty)
731 printf("\n");
732 eina_iterator_free(it);
733
734 ewk_tile_unused_cache_dbg(tm->tuc);
735 }
736
737 /**
738 * Freeze matrix to not do maintenance tasks.
739 *
740 * Maintenance tasks optimize usage, but maybe we know we should hold
741 * on them until we do the last operation, in this case we freeze
742 * while operating and then thaw when we're done.
743 *
744 * @see ewk_tile_matrix_thaw()
745 */
ewk_tile_matrix_freeze(Ewk_Tile_Matrix * tm)746 void ewk_tile_matrix_freeze(Ewk_Tile_Matrix *tm)
747 {
748 EINA_SAFETY_ON_NULL_RETURN(tm);
749 if (!tm->frozen)
750 ewk_tile_unused_cache_freeze(tm->tuc);
751 tm->frozen++;
752 }
753
754 /**
755 * Unfreezes maintenance tasks.
756 *
757 * If this is the last counterpart of freeze, then maintenance tasks
758 * will run immediately.
759 */
ewk_tile_matrix_thaw(Ewk_Tile_Matrix * tm)760 void ewk_tile_matrix_thaw(Ewk_Tile_Matrix *tm)
761 {
762 EINA_SAFETY_ON_NULL_RETURN(tm);
763 if (!tm->frozen) {
764 ERR("thawing more than freezing!");
765 return;
766 }
767
768 tm->frozen--;
769 if (!tm->frozen)
770 ewk_tile_unused_cache_thaw(tm->tuc);
771 }
772