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