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