OSDN Git Service

Merge WebKit at r73109: Initial merge by git.
[android-x86/external-webkit.git] / WebKit / efl / ewk / ewk_tiled_matrix.c
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 */
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 */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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  */
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
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
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
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
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 > rows * th)
604         h = rows * th - y;
605     if (x + w - 1 > 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
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
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
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!
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  */
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  */
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 }