[EFL] Added DEFAULT_CACHE_SIZE define.
[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     eina_matrixsparse_free(entry->matrix);    
306     free(entry);
307     tileMatrix->matrices = 0;
308
309     ewk_tile_unused_cache_thaw(tileMatrix->tilieUnusedCache);
310     ewk_tile_unused_cache_unref(tileMatrix->tilieUnusedCache);
311
312 #ifdef DEBUG_MEM_LEAKS
313     tiles = tileMatrix->stats.tiles.allocated - tileMatrix->stats.tiles.freed;
314     bytes = tileMatrix->stats.bytes.allocated - tileMatrix->stats.bytes.freed;
315
316     tiles_leaked += tiles;
317     bytes_leaked += bytes;
318
319     if (tiles || bytes)
320         ERR("tiled matrix leaked: tiles[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "] "
321             "bytes[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "]",
322             tileMatrix->stats.tiles.allocated, tileMatrix->stats.tiles.freed, tiles,
323             tileMatrix->stats.bytes.allocated, tileMatrix->stats.bytes.freed, bytes);
324     else if (tiles_leaked || bytes_leaked)
325         WRN("tiled matrix had no leaks: tiles[+%" PRIu64 ",-%" PRIu64 "] "
326             "bytes[+%" PRIu64 ",-%" PRIu64 "], but some other leaked "
327             "%" PRIu64 " tiles (%" PRIu64 " bytes)",
328             tileMatrix->stats.tiles.allocated, tileMatrix->stats.tiles.freed,
329             tileMatrix->stats.bytes.allocated, tileMatrix->stats.bytes.freed,
330             tiles_leaked, bytes_leaked);
331     else
332         INF("tiled matrix had no leaks: tiles[+%" PRIu64 ",-%" PRIu64 "] "
333             "bytes[+%" PRIu64 ",-%" PRIu64 "]",
334             tileMatrix->stats.tiles.allocated, tileMatrix->stats.tiles.freed,
335             tileMatrix->stats.bytes.allocated, tileMatrix->stats.bytes.freed);
336 #endif
337
338     free(tileMatrix);
339 }
340
341 /**
342  * Resize matrix to given number of rows and columns.
343  */
344 void ewk_tile_matrix_resize(Ewk_Tile_Matrix* tileMatrix, unsigned long cols, unsigned long rows)
345 {
346     EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
347     eina_matrixsparse_size_set(tileMatrix->matrix, rows, cols);
348 }
349
350 /**
351  * Get the cache of unused tiles in use by given matrix.
352  *
353  * No reference is taken to the cache.
354  */
355 Ewk_Tile_Unused_Cache* ewk_tile_matrix_unused_cache_get(const Ewk_Tile_Matrix* tileMatrix)
356 {
357     EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, 0);
358     return tileMatrix->tilieUnusedCache;
359 }
360
361 /**
362  * Get the exact tile for the given position and zoom.
363  *
364  * If the tile.widthas unused then it's removed from the cache.
365  *
366  * After usage, please give it back using
367  * ewk_tile_matrix_tile_put(). If you just want to check if it exists,
368  * then use ewk_tile_matrix_tile_exact_exists().
369  *
370  * @param tileMatrix the tile matrix to get tile from.
371  * @param column the column number.
372  * @param row the row number.
373  * @param zoom the exact zoom to use.
374  *
375  * @return The tile instance or @c 0 if none is found. If the tile
376  *         was in the unused cache it will be @b removed (thus
377  *         considered used) and one should give it back with
378  *         ewk_tile_matrix_tile_put() afterwards.
379  *
380  * @see ewk_tile_matrix_tile_exact_get()
381  */
382 Ewk_Tile* ewk_tile_matrix_tile_exact_get(Ewk_Tile_Matrix* tileMatrix, unsigned long column, unsigned long row, float zoom)
383 {
384     Ewk_Tile* tile = static_cast<Ewk_Tile*>(eina_matrixsparse_data_idx_get(tileMatrix->matrix, row, column));
385     if (!tile)
386         return 0;
387
388     if (tile->zoom == zoom)
389         goto end;
390
391 end:
392     if (!tile->visible) {
393         if (!ewk_tile_unused_cache_tile_get(tileMatrix->tilieUnusedCache, tile))
394             WRN("Ewk_Tile was unused but not in cache? bug!");
395     }
396
397     return tile;
398 }
399
400 /**
401  * Checks if tile of given zoom exists in matrix.
402  *
403  * @param tileMatrix the tile matrix to check tile existence.
404  * @param column the column number.
405  * @param row the row number.
406  * @param zoom the exact zoom to query.
407  *
408  * @return @c true if found, @c false otherwise.
409  *
410  * @see ewk_tile_matrix_tile_exact_get()
411  */
412 Eina_Bool ewk_tile_matrix_tile_exact_exists(Ewk_Tile_Matrix* tileMatrix, unsigned long column, unsigned long row, float zoom)
413 {
414     if (!eina_matrixsparse_data_idx_get(tileMatrix->matrix, row, column))
415         return false;
416
417     return true;
418 }
419
420 /**
421  * Create a new tile at given position and zoom level in the matrix.
422  *
423  * The newly created tile is considered in use and not put into cache
424  * of unused tiles. After it is used one should call
425  * ewk_tile_matrix_tile_put() to give it back to matrix.
426  *
427  * @param tileMatrix the tile matrix to create tile on.
428  * @param column the column number.
429  * @param row the row number.
430  * @param zoom the level to create tile, used to determine tile size.
431  */
432 Ewk_Tile* ewk_tile_matrix_tile_new(Ewk_Tile_Matrix* tileMatrix, Evas* canvas, unsigned long column, unsigned long row, float zoom)
433 {
434     Evas_Coord tileWidth, tileHeight;
435     Ewk_Tile* tile;
436     Ewk_Tile_Matrix_Entry* entry;
437
438     EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, 0);
439     EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, 0);
440     entry = ewk_tile_matrix_entry_get(tileMatrix, zoom);
441     if (!entry) {
442         ERR("could not get matrix at zoom %f for tile", zoom);
443         return 0;
444     }
445     entry->count++;
446
447     tileWidth = tileMatrix->tile.width;
448     tileHeight = tileMatrix->tile.height;
449
450     tile = ewk_tile_new(canvas, tileWidth, tileHeight, zoom, tileMatrix->cspace);
451     if (!tile) {
452         ERR("could not create tile %dx%d at %f, cspace=%d", tileWidth, tileHeight, (double)zoom, tileMatrix->cspace);
453         return 0;
454     }
455
456     if (!eina_matrixsparse_data_idx_set(tileMatrix->matrix, row, column, tile)) {
457         ERR("could not set matrix cell, row/col outside matrix dimensions!");
458         ewk_tile_free(tile);
459         return 0;
460     }
461
462     tile->col = column;
463     tile->row = row;
464     tile->x = column * tileWidth;
465     tile->y = row * tileHeight;
466
467     cairo_translate(tile->cairo, -tile->x, -tile->y);
468
469     tile->stats.full_update = true;
470     tileMatrix->updates = eina_list_append(tileMatrix->updates, tile);
471
472 #ifdef DEBUG_MEM_LEAKS
473     tileMatrix->stats.bytes.allocated += tile->bytes;
474     tileMatrix->stats.tiles.allocated++;
475 #endif
476
477     return tile;
478 }
479
480 /**
481  * Gives back the tile to the tile matrix.
482  *
483  * This will report the tile is no longer in use by the one that got
484  * it with ewk_tile_matrix_tile_exact_get().
485  *
486  * Any previous reference to tile should be released
487  * (ewk_tile.heightide()) before calling this function, so it will
488  * be known if it is not visibile anymore and thus can be put into the
489  * unused cache.
490  *
491  * @param tileMatrix the tile matrix to return tile to.
492  * @param t the tile instance to return, must @b not be @c 0.
493  * @param last_used time in which tile.widthas last used.
494  *
495  * @return #true on success or #false on failure.
496  */
497 Eina_Bool ewk_tile_matrix_tile_put(Ewk_Tile_Matrix* tileMatrix, Ewk_Tile* tile, double lastUsed)
498 {
499     EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, false);
500     EINA_SAFETY_ON_NULL_RETURN_VAL(tile, false);
501
502     if (tile->visible)
503         return true;
504
505     tile->stats.last_used = lastUsed;
506     return ewk_tile_unused_cache_tile_put(tileMatrix->tilieUnusedCache, tile, _ewk_tile_matrix_tile_free, tileMatrix);
507 }
508
509 Eina_Bool ewk_tile_matrix_tile_update(Ewk_Tile_Matrix* tileMatrix, unsigned long col, unsigned long row, const Eina_Rectangle* update)
510 {
511     Eina_Rectangle newUpdate;
512     EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, false);
513     EINA_SAFETY_ON_NULL_RETURN_VAL(update, false);
514
515     memcpy(&newUpdate, update, sizeof(newUpdate));
516     // check update is valid, otherwise return false
517     if (update->x < 0 || update->y < 0 || update->w <= 0 || update->h <= 0) {
518         ERR("invalid update region.");
519         return false;
520     }
521
522     if (update->x + update->w - 1 >= tileMatrix->tile.width)
523         newUpdate.w = tileMatrix->tile.width - update->x;
524     if (update->y + update->h - 1 >= tileMatrix->tile.height)
525         newUpdate.h = tileMatrix->tile.height - update->y;
526
527     Ewk_Tile* tile = static_cast<Ewk_Tile*>(eina_matrixsparse_data_idx_get(tileMatrix->matrix, row, col));
528     if (!tile)
529         return true;
530
531     if (!tile->updates && !tile->stats.full_update)
532         tileMatrix->updates = eina_list_append(tileMatrix->updates, tile);
533     ewk_tile_update_area(tile, &newUpdate);
534
535     return true;
536 }
537
538 Eina_Bool ewk_tile_matrix_tile_update_full(Ewk_Tile_Matrix* tileMatrix, unsigned long column, unsigned long row)
539 {
540     Eina_Matrixsparse_Cell* cell;
541     EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, false);
542
543     if (!eina_matrixsparse_cell_idx_get(tileMatrix->matrix, row, column, &cell))
544         return false;
545
546     if (!cell)
547         return true;
548
549     Ewk_Tile* tile = static_cast<Ewk_Tile*>(eina_matrixsparse_cell_data_get(cell));
550     if (!tile) {
551         CRITICAL("matrix cell with no tile!");
552         return true;
553     }
554
555     if (!tile->updates && !tile->stats.full_update)
556         tileMatrix->updates = eina_list_append(tileMatrix->updates, tile);
557     ewk_tile_update_full(tile);
558
559     return true;
560 }
561
562 void ewk_tile_matrix_tile_updates_clear(Ewk_Tile_Matrix* tileMatrix, Ewk_Tile* tile)
563 {
564     EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
565     if (!tile->updates && !tile->stats.full_update)
566         return;
567     ewk_tile_updates_clear(tile);
568     tileMatrix->updates = eina_list_remove(tileMatrix->updates, tile);
569 }
570
571 static Eina_Bool _ewk_tile_matrix_slicer_setup(Ewk_Tile_Matrix* tileMatrix, const Eina_Rectangle* area, float zoom, Eina_Tile_Grid_Slicer* slicer)
572 {
573     unsigned long rows, cols;
574     Evas_Coord x, y, width, height, tileWidth, tileHeight;
575
576     if (area->w <= 0 || area->h <= 0) {
577         WRN("invalid area region: %d,%d+%dx%d.",
578             area->x, area->y, area->w, area->h);
579         return false;
580     }
581
582     x = area->x;
583     y = area->y;
584     width = area->w;
585     height = area->h;
586
587     tileWidth = tileMatrix->tile.width;
588     tileHeight = tileMatrix->tile.height;
589
590     // cropping area region to fit matrix
591     eina_matrixsparse_size_get(tileMatrix->matrix, &rows, &cols);
592     if (x < 0) {
593         width += x;
594         x = 0;
595     }
596     if (y < 0) {
597         height += y;
598         y = 0;
599     }
600
601     if (y + height - 1 > (long)(rows * tileHeight))
602         height = rows * tileHeight - y;
603     if (x + width - 1 > (long)(cols * tileWidth))
604         width = cols * tileWidth - x;
605
606     return eina_tile_grid_slicer_setup(slicer, x, y, width, height, tileWidth, tileHeight);
607 }
608
609
610 Eina_Bool ewk_tile_matrix_update(Ewk_Tile_Matrix* tileMatrix, const Eina_Rectangle* update, float zoom)
611 {
612     const Eina_Tile_Grid_Info* info;
613     Eina_Tile_Grid_Slicer slicer;
614     EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, false);
615     EINA_SAFETY_ON_NULL_RETURN_VAL(update, false);
616
617     if (update->w < 1 || update->h < 1) {
618         DBG("Why we get updates with empty areas? %d,%d+%dx%d at zoom %f",
619             update->x, update->y, update->w, update->h, zoom);
620         return true;
621     }
622
623     if (!_ewk_tile_matrix_slicer_setup(tileMatrix, update, zoom, &slicer)) {
624         ERR("Could not setup slicer for update %d,%d+%dx%d at zoom %f",
625             update->x, update->y, update->w, update->h, zoom);
626         return false;
627     }
628
629     while (eina_tile_grid_slicer_next(&slicer, &info)) {
630         unsigned long col, row;
631         col = info->col;
632         row = info->row;
633
634         Ewk_Tile* tile = static_cast<Ewk_Tile*>(eina_matrixsparse_data_idx_get(tileMatrix->matrix, row, col));
635         if (!tile)
636             continue;
637
638         if (!tile->updates && !tile->stats.full_update)
639             tileMatrix->updates = eina_list_append(tileMatrix->updates, tile);
640         if (info->full)
641             ewk_tile_update_full(tile);
642         else
643             ewk_tile_update_area(tile, &info->rect);
644     }
645
646
647     return true;
648 }
649
650 void ewk_tile_matrix_updates_process(Ewk_Tile_Matrix* tileMatrix)
651 {
652     Eina_List* list, *listNext;
653     void* item;
654     EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
655
656     // process updates, unflag tiles
657     EINA_LIST_FOREACH_SAFE(tileMatrix->updates, list, listNext, item) {
658         Ewk_Tile* tile = static_cast<Ewk_Tile*>(item);
659         ewk_tile_updates_process(tile, tileMatrix->render.callback, tileMatrix->render.data);
660         if (tile->visible) {
661             ewk_tile_updates_clear(tile);
662             tileMatrix->updates = eina_list_remove_list(tileMatrix->updates, list);
663         }
664     }
665 }
666
667 void ewk_tile_matrix_updates_clear(Ewk_Tile_Matrix* tileMatrix)
668 {
669     void* item;
670     EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
671
672     EINA_LIST_FREE(tileMatrix->updates, item)
673         ewk_tile_updates_clear(static_cast<Ewk_Tile*>(item));
674     tileMatrix->updates = 0;
675 }
676
677 // remove me later!
678 void ewk_tile_matrix_dbg(const Ewk_Tile_Matrix* tileMatrix)
679 {
680     Eina_Iterator* iterator = eina_matrixsparse_iterator_complete_new(tileMatrix->matrix);
681     Eina_Matrixsparse_Cell* cell;
682     Eina_Bool last_empty = false;
683
684 #ifdef DEBUG_MEM_LEAKS
685     printf("Ewk_Tile Matrix: tiles[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "] "
686            "bytes[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "]\n",
687            tileMatrix->stats.tiles.allocated, tileMatrix->stats.tiles.freed,
688            tileMatrix->stats.tiles.allocated - tileMatrix->stats.tiles.freed,
689            tileMatrix->stats.bytes.allocated, tileMatrix->stats.bytes.freed,
690            tileMatrix->stats.bytes.allocated - tileMatrix->stats.bytes.freed);
691 #else
692     printf("Ewk_Tile Matrix:\n");
693 #endif
694
695     EINA_ITERATOR_FOREACH(iterator, cell) {
696         unsigned long row, column;
697         eina_matrixsparse_cell_position_get(cell, &row, &column);
698
699         Ewk_Tile* tile = static_cast<Ewk_Tile*>(eina_matrixsparse_cell_data_get(cell));
700         if (!tile) {
701             if (!last_empty) {
702                 last_empty = true;
703                 printf("Empty:");
704             }
705             printf(" [%lu,%lu]", column, row);
706         } else {
707             if (last_empty) {
708                 last_empty = false;
709                 printf("\n");
710             }
711             printf("%3lu,%3lu %10p:", column, row, tile);
712             printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c", tile->col, tile->row, tile->width, tile->height, tile->zoom, tile->visible ? '*' : ' ');
713             printf("\n");
714         }
715     }
716     if (last_empty)
717         printf("\n");
718     eina_iterator_free(iterator);
719
720     ewk_tile_unused_cache_dbg(tileMatrix->tilieUnusedCache);
721 }
722
723 /**
724  * Freeze matrix to not do maintenance tasks.
725  *
726  * Maintenance tasks optimize usage, but maybe we know we should hold
727  * on them until we do the last operation, in this case we freeze
728  * while operating and then thaw when we're done.
729  *
730  * @see ewk_tile_matrix_thaw()
731  */
732 void ewk_tile_matrix_freeze(Ewk_Tile_Matrix* tileMatrix)
733 {
734     EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
735     if (!tileMatrix->frozen)
736         ewk_tile_unused_cache_freeze(tileMatrix->tilieUnusedCache);
737     tileMatrix->frozen++;
738 }
739
740 /**
741  * Unfreezes maintenance tasks.
742  *
743  * If this is the last counterpart of freeze, then maintenance tasks
744  * will run immediately.
745  */
746 void ewk_tile_matrix_thaw(Ewk_Tile_Matrix* tileMatrix)
747 {
748     EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
749     if (!tileMatrix->frozen) {
750         ERR("thawing more than freezing!");
751         return;
752     }
753
754     tileMatrix->frozen--;
755     if (!tileMatrix->frozen)
756         ewk_tile_unused_cache_thaw(tileMatrix->tilieUnusedCache);
757 }