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