Home | History | Annotate | Download | only in ewk
      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 > (long)(rows * th))
    604         h = rows * th - y;
    605     if (x + w - 1 > (long)(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 }
    772