| /* |
| Copyright (C) 2009-2010 Samsung Electronics |
| Copyright (C) 2009-2010 ProFUSION embedded systems |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Library General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public License |
| along with this library; see the file COPYING.LIB. If not, write to |
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| Boston, MA 02110-1301, USA. |
| */ |
| |
| #include "config.h" |
| #include "ewk_tiled_matrix.h" |
| |
| #define _GNU_SOURCE |
| #include "ewk_tiled_backing_store.h" |
| #include "ewk_tiled_private.h" |
| #include <Eina.h> |
| #include <errno.h> |
| #include <inttypes.h> |
| #include <math.h> |
| #include <stdio.h> // XXX remove me later |
| #include <stdlib.h> |
| #include <string.h> |
| |
| static const Evas_Coord TILE_MATRIX_BASE_TILE_SIZE = 256; |
| |
| struct _Ewk_Tile_Matrix { |
| Eina_Matrixsparse *matrix; |
| Ewk_Tile_Unused_Cache *tuc; |
| Evas_Colorspace cspace; |
| struct { |
| void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update); |
| void *data; |
| } render; |
| unsigned int frozen; |
| Eina_List *updates; |
| #ifdef DEBUG_MEM_LEAKS |
| struct { |
| struct { |
| uint64_t allocated, freed; |
| } tiles, bytes; |
| } stats; |
| #endif |
| }; |
| |
| #ifdef DEBUG_MEM_LEAKS |
| static uint64_t tiles_leaked = 0; |
| static uint64_t bytes_leaked = 0; |
| #endif |
| |
| /* called when matrixsparse is resized or freed */ |
| static void _ewk_tile_matrix_cell_free(void *user_data, void *cell_data) |
| { |
| Ewk_Tile_Matrix *tm = user_data; |
| Eina_Inlist *l = cell_data; |
| |
| if (!l) |
| return; |
| |
| ewk_tile_unused_cache_freeze(tm->tuc); |
| |
| while (l) { |
| Ewk_Tile *t = (Ewk_Tile *)l; |
| l = l->next; |
| |
| if (t->updates || t->stats.full_update) |
| tm->updates = eina_list_remove(tm->updates, t); |
| |
| if (t->visible) |
| ERR("freeing cell that is visible, leaking tile %p", t); |
| else { |
| if (!ewk_tile_unused_cache_tile_get(tm->tuc, t)) |
| ERR("tile %p was not in cache %p? leaking...", t, tm->tuc); |
| else { |
| DBG("tile cell does not exist anymore, free it %p", t); |
| |
| #ifdef DEBUG_MEM_LEAKS |
| tm->stats.bytes.freed += t->bytes; |
| tm->stats.tiles.freed++; |
| #endif |
| |
| ewk_tile_free(t); |
| } |
| } |
| } |
| |
| ewk_tile_unused_cache_thaw(tm->tuc); |
| } |
| |
| /* called when cache of unused tile is flushed */ |
| static void _ewk_tile_matrix_tile_free(void *data, Ewk_Tile *t) |
| { |
| Ewk_Tile_Matrix *tm = data; |
| Eina_Matrixsparse_Cell *cell; |
| Eina_Inlist *l, *old; |
| |
| if (!eina_matrixsparse_cell_idx_get(tm->matrix, t->row, t->col, &cell)) { |
| ERR("removing tile %p that was not in the matrix? Leaking...", t); |
| return; |
| } |
| |
| if (t->updates || t->stats.full_update) |
| tm->updates = eina_list_remove(tm->updates, t); |
| |
| old = eina_matrixsparse_cell_data_get(cell); |
| l = eina_inlist_remove(old, EINA_INLIST_GET(t)); |
| if (!l) { |
| /* set to null to avoid double free */ |
| eina_matrixsparse_cell_data_replace(cell, NULL, NULL); |
| eina_matrixsparse_cell_clear(cell); |
| } else if (old != l) |
| eina_matrixsparse_cell_data_replace(cell, l, NULL); |
| |
| if (EINA_UNLIKELY(!!t->visible)) { |
| ERR("cache of unused tiles requesting deletion of used tile %p? " |
| "Leaking...", t); |
| return; |
| } |
| |
| #ifdef DEBUG_MEM_LEAKS |
| tm->stats.bytes.freed += t->bytes; |
| tm->stats.tiles.freed++; |
| #endif |
| |
| ewk_tile_free(t); |
| } |
| |
| /** |
| * Creates a new matrix of tiles. |
| * |
| * The tile matrix is responsible for keeping tiles around and |
| * providing fast access to them. One can use it to retrieve new or |
| * existing tiles and give them back, allowing them to be |
| * freed/replaced by the cache. |
| * |
| * @param tuc cache of unused tiles or @c NULL to create one |
| * automatically. |
| * @param cols number of columns in the matrix. |
| * @param rows number of rows in the matrix. |
| * @param cspace the color space used to create tiles in this matrix. |
| * @param render_cb function used to render given tile update. |
| * @param data context to give back to @a render_cb. |
| * |
| * @return newly allocated instance on success, @c NULL on failure. |
| */ |
| 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) |
| { |
| Ewk_Tile_Matrix *tm; |
| |
| CALLOC_OR_OOM_RET(tm, sizeof(Ewk_Tile_Matrix), NULL); |
| |
| tm->matrix = eina_matrixsparse_new(rows, cols, _ewk_tile_matrix_cell_free, tm); |
| if (!tm->matrix) { |
| ERR("could not create sparse matrix."); |
| free(tm); |
| return NULL; |
| } |
| |
| if (tuc) |
| tm->tuc = ewk_tile_unused_cache_ref(tuc); |
| else { |
| tm->tuc = ewk_tile_unused_cache_new(40960000); |
| if (!tm->tuc) { |
| ERR("no cache of unused tile!"); |
| eina_matrixsparse_free(tm->matrix); |
| free(tm); |
| return NULL; |
| } |
| } |
| |
| tm->cspace = cspace; |
| tm->render.cb = render_cb; |
| tm->render.data = (void *)data; |
| |
| return tm; |
| } |
| |
| /** |
| * Destroys tiles matrix, releasing its resources. |
| * |
| * The cache instance is unreferenced, possibly freeing it. |
| */ |
| void ewk_tile_matrix_free(Ewk_Tile_Matrix *tm) |
| { |
| #ifdef DEBUG_MEM_LEAKS |
| uint64_t tiles, bytes; |
| #endif |
| |
| EINA_SAFETY_ON_NULL_RETURN(tm); |
| ewk_tile_unused_cache_freeze(tm->tuc); |
| |
| eina_matrixsparse_free(tm->matrix); |
| |
| ewk_tile_unused_cache_thaw(tm->tuc); |
| ewk_tile_unused_cache_unref(tm->tuc); |
| |
| #ifdef DEBUG_MEM_LEAKS |
| tiles = tm->stats.tiles.allocated - tm->stats.tiles.freed; |
| bytes = tm->stats.bytes.allocated - tm->stats.bytes.freed; |
| |
| tiles_leaked += tiles; |
| bytes_leaked += bytes; |
| |
| if (tiles || bytes) |
| ERR("tiled matrix leaked: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] " |
| "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]", |
| tm->stats.tiles.allocated, tm->stats.tiles.freed, tiles, |
| tm->stats.bytes.allocated, tm->stats.bytes.freed, bytes); |
| else if (tiles_leaked || bytes_leaked) |
| WRN("tiled matrix had no leaks: tiles[+%"PRIu64",-%"PRIu64"] " |
| "bytes[+%"PRIu64",-%"PRIu64"], but some other leaked " |
| "%"PRIu64" tiles (%"PRIu64" bytes)", |
| tm->stats.tiles.allocated, tm->stats.tiles.freed, |
| tm->stats.bytes.allocated, tm->stats.bytes.freed, |
| tiles_leaked, bytes_leaked); |
| else |
| INF("tiled matrix had no leaks: tiles[+%"PRIu64",-%"PRIu64"] " |
| "bytes[+%"PRIu64",-%"PRIu64"]", |
| tm->stats.tiles.allocated, tm->stats.tiles.freed, |
| tm->stats.bytes.allocated, tm->stats.bytes.freed); |
| #endif |
| |
| free(tm); |
| } |
| |
| /** |
| * Resize matrix to given number of rows and columns. |
| */ |
| void ewk_tile_matrix_resize(Ewk_Tile_Matrix *tm, unsigned long cols, unsigned long rows) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tm); |
| eina_matrixsparse_size_set(tm->matrix, rows, cols); |
| } |
| |
| /** |
| * Get the cache of unused tiles in use by given matrix. |
| * |
| * No reference is taken to the cache. |
| */ |
| Ewk_Tile_Unused_Cache *ewk_tile_matrix_unused_cache_get(const Ewk_Tile_Matrix *tm) |
| { |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL); |
| return tm->tuc; |
| } |
| |
| /** |
| * Get the exact tile for the given position and zoom. |
| * |
| * If the tile was unused then it's removed from the cache. |
| * |
| * After usage, please give it back using |
| * ewk_tile_matrix_tile_put(). If you just want to check if it exists, |
| * then use ewk_tile_matrix_tile_exact_exists(). |
| * |
| * @param tm the tile matrix to get tile from. |
| * @param col the column number. |
| * @param row the row number. |
| * @param zoom the exact zoom to use. |
| * |
| * @return The tile instance or @c NULL if none is found. If the tile |
| * was in the unused cache it will be @b removed (thus |
| * considered used) and one should give it back with |
| * ewk_tile_matrix_tile_put() afterwards. |
| * |
| * @see ewk_tile_matrix_tile_nearest_get() |
| * @see ewk_tile_matrix_tile_exact_get() |
| */ |
| Ewk_Tile *ewk_tile_matrix_tile_exact_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom) |
| { |
| Ewk_Tile *t, *item, *item_found = NULL; |
| Eina_Inlist *inl; |
| |
| t = eina_matrixsparse_data_idx_get(tm->matrix, row, col); |
| if (!t) |
| return NULL; |
| |
| if (t->zoom == zoom) |
| goto end; |
| |
| EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) { |
| if (item->zoom != zoom) |
| continue; |
| item_found = item; |
| break; |
| } |
| |
| if (!item_found) |
| return NULL; |
| |
| inl = eina_inlist_promote(EINA_INLIST_GET(t), EINA_INLIST_GET(item_found)); |
| eina_matrixsparse_data_idx_replace(tm->matrix, row, col, inl, NULL); |
| |
| end: |
| if (!t->visible) { |
| if (!ewk_tile_unused_cache_tile_get(tm->tuc, t)) |
| WRN("Ewk_Tile was unused but not in cache? bug!"); |
| } |
| |
| return t; |
| } |
| |
| /** |
| * Checks if tile of given zoom exists in matrix. |
| * |
| * @param tm the tile matrix to check tile existence. |
| * @param col the column number. |
| * @param row the row number. |
| * @param zoom the exact zoom to query. |
| * |
| * @return @c EINA_TRUE if found, @c EINA_FALSE otherwise. |
| * |
| * @see ewk_tile_matrix_tile_exact_get() |
| */ |
| Eina_Bool ewk_tile_matrix_tile_exact_exists(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom) |
| { |
| Ewk_Tile *t, *item; |
| |
| t = eina_matrixsparse_data_idx_get(tm->matrix, row, col); |
| if (!t) |
| return EINA_FALSE; |
| |
| EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) { |
| if (item->zoom == zoom) |
| return EINA_TRUE; |
| } |
| |
| return EINA_FALSE; |
| } |
| |
| /** |
| * Get the nearest tile for given position and zoom. |
| * |
| * The nearest tile is the one at the given position but with the |
| * closest zoom, this being the division of the tile zoom by the |
| * desired zoom, the closest to 1.0 gets it. |
| * |
| * If the tile was unused then it's removed from the cache. |
| * |
| * After usage, please give it back using ewk_tile_matrix_tile_put(). |
| * |
| * @param tm the tile matrix to get tile from. |
| * @param col the column number. |
| * @param row the row number. |
| * @param zoom the exact zoom to use. |
| * |
| * @return The tile instance or @c NULL if none is found. If the tile |
| * was in the unused cache it will be @b removed (thus |
| * considered used) and one should give it back with |
| * ewk_tile_matrix_tile_put() afterwards. |
| */ |
| Ewk_Tile *ewk_tile_matrix_tile_nearest_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, float zoom) |
| { |
| Ewk_Tile *t, *item, *item_found = NULL; |
| Eina_Inlist *inl; |
| float zoom_found = 0; |
| |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL); |
| EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, NULL); |
| |
| t = eina_matrixsparse_data_idx_get(tm->matrix, row, col); |
| if (!t) |
| return NULL; |
| |
| if (t->zoom == zoom) { |
| item_found = t; |
| goto end; |
| } |
| |
| EINA_INLIST_FOREACH(EINA_INLIST_GET(t), item) { |
| float cur_zoom = item->zoom; |
| |
| if (cur_zoom == zoom) { |
| item_found = item; |
| break; |
| } |
| |
| if (cur_zoom > zoom) |
| cur_zoom = zoom / cur_zoom; |
| else |
| cur_zoom = cur_zoom / zoom; |
| |
| if (cur_zoom > zoom_found) { |
| item_found = item; |
| zoom_found = cur_zoom; |
| } |
| } |
| |
| if (!item_found) |
| return NULL; |
| |
| inl = eina_inlist_promote(EINA_INLIST_GET(t), EINA_INLIST_GET(item_found)); |
| eina_matrixsparse_data_idx_replace(tm->matrix, row, col, inl, NULL); |
| |
| end: |
| if (!item_found->visible) { |
| if (!ewk_tile_unused_cache_tile_get(tm->tuc, item_found)) |
| WRN("Ewk_Tile was unused but not in cache? bug!"); |
| } |
| |
| return item_found; |
| } |
| |
| /** |
| * Create a new tile at given position and zoom level in the matrix. |
| * |
| * The newly created tile is considered in use and not put into cache |
| * of unused tiles. After it is used one should call |
| * ewk_tile_matrix_tile_put() to give it back to matrix. |
| * |
| * @param tm the tile matrix to create tile on. |
| * @param col the column number. |
| * @param row the row number. |
| * @param zoom the level to create tile, used to determine tile size. |
| */ |
| Ewk_Tile *ewk_tile_matrix_tile_new(Ewk_Tile_Matrix *tm, Evas *evas, unsigned long col, unsigned int row, float zoom) |
| { |
| Evas_Coord s; |
| Eina_Inlist *old; |
| Ewk_Tile *t; |
| |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tm, NULL); |
| EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, NULL); |
| |
| s = TILE_SIZE_AT_ZOOM(TILE_MATRIX_BASE_TILE_SIZE, zoom); |
| zoom = (float)s / (float)TILE_MATRIX_BASE_TILE_SIZE; |
| |
| t = ewk_tile_new(evas, s, s, zoom, tm->cspace); |
| if (!t) { |
| ERR("could not create tile %dx%d at %f, cspace=%d", |
| s, s, (double)zoom, tm->cspace); |
| return NULL; |
| } |
| |
| old = eina_matrixsparse_data_idx_get(tm->matrix, row, col); |
| old = eina_inlist_prepend(old, EINA_INLIST_GET(t)); |
| if (!eina_matrixsparse_data_idx_replace(tm->matrix, row, col, t, NULL)) { |
| ERR("could not set matrix cell, row/col outside matrix dimensions!"); |
| ewk_tile_free(t); |
| return NULL; |
| } |
| |
| t->col = col; |
| t->row = row; |
| t->x = col * s; |
| t->y = row * s; |
| |
| cairo_translate(t->cairo, -t->x, -t->y); |
| |
| t->stats.full_update = EINA_TRUE; |
| tm->updates = eina_list_append(tm->updates, t); |
| |
| #ifdef DEBUG_MEM_LEAKS |
| tm->stats.bytes.allocated += t->bytes; |
| tm->stats.tiles.allocated++; |
| #endif |
| |
| return t; |
| } |
| |
| /** |
| * Gives back the tile to the tile matrix. |
| * |
| * This will report the tile is no longer in use by the one that got |
| * it with ewk_tile_matrix_tile_nearest_get() or |
| * ewk_tile_matrix_tile_exact_get(). |
| * |
| * Any previous reference to tile should be released |
| * (ewk_tile_hide()) before calling this function, so it will |
| * be known if it is not visibile anymore and thus can be put into the |
| * unused cache. |
| * |
| * @param tm the tile matrix to return tile to. |
| * @param t the tile instance to return, must @b not be @c NULL. |
| * @param last_used time in which tile was last used. |
| * |
| * @return #EINA_TRUE on success or #EINA_FALSE on failure. |
| */ |
| Eina_Bool ewk_tile_matrix_tile_put(Ewk_Tile_Matrix *tm, Ewk_Tile *t, double last_used) |
| { |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE); |
| EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE); |
| |
| if (t->visible) |
| return EINA_TRUE; |
| |
| t->stats.last_used = last_used; |
| return ewk_tile_unused_cache_tile_put(tm->tuc, t, _ewk_tile_matrix_tile_free, tm); |
| } |
| |
| Eina_Bool ewk_tile_matrix_tile_update(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row, const Eina_Rectangle *update) |
| { |
| Ewk_Tile *l, *t; |
| Eina_Rectangle new_update; |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE); |
| EINA_SAFETY_ON_NULL_RETURN_VAL(update, EINA_FALSE); |
| |
| memcpy(&new_update, update, sizeof(new_update)); |
| // check update is valid, otherwise return EINA_FALSE |
| if (update->x < 0 || update->y < 0 || update->w <= 0 || update->h <= 0) { |
| ERR("invalid update region."); |
| return EINA_FALSE; |
| } |
| |
| if (update->x + update->w - 1 >= TILE_MATRIX_BASE_TILE_SIZE) |
| new_update.w = TILE_MATRIX_BASE_TILE_SIZE - update->x; |
| if (update->y + update->h - 1 >= TILE_MATRIX_BASE_TILE_SIZE) |
| new_update.h = TILE_MATRIX_BASE_TILE_SIZE - update->y; |
| |
| l = eina_matrixsparse_data_idx_get(tm->matrix, row, col); |
| |
| if (!l) |
| return EINA_TRUE; |
| |
| EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) { |
| if (!t->updates && !t->stats.full_update) |
| tm->updates = eina_list_append(tm->updates, t); |
| new_update.x = roundf(t->zoom * new_update.x); |
| new_update.y = roundf(t->zoom * new_update.y); |
| new_update.w = roundf(t->zoom * new_update.w); |
| new_update.h = roundf(t->zoom * new_update.h); |
| ewk_tile_update_area(t, &new_update); |
| } |
| |
| return EINA_TRUE; |
| } |
| |
| Eina_Bool ewk_tile_matrix_tile_update_full(Ewk_Tile_Matrix *tm, unsigned long col, unsigned int row) |
| { |
| Ewk_Tile *l, *t; |
| Eina_Matrixsparse_Cell *cell; |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE); |
| |
| if (!eina_matrixsparse_cell_idx_get(tm->matrix, row, col, &cell)) |
| return EINA_FALSE; |
| |
| if (!cell) |
| return EINA_TRUE; |
| |
| l = eina_matrixsparse_cell_data_get(cell); |
| if (!l) { |
| CRITICAL("matrix cell with no tile!"); |
| return EINA_TRUE; |
| } |
| |
| EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) { |
| if (!t->updates && !t->stats.full_update) |
| tm->updates = eina_list_append(tm->updates, t); |
| ewk_tile_update_full(t); |
| } |
| |
| return EINA_TRUE; |
| } |
| |
| void ewk_tile_matrix_tile_updates_clear(Ewk_Tile_Matrix *tm, Ewk_Tile *t) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tm); |
| if (!t->updates && !t->stats.full_update) |
| return; |
| ewk_tile_updates_clear(t); |
| tm->updates = eina_list_remove(tm->updates, t); |
| } |
| |
| static Eina_Bool _ewk_tile_matrix_slicer_setup(Ewk_Tile_Matrix *tm, const Eina_Rectangle *area, float zoom, Eina_Tile_Grid_Slicer *slicer) |
| { |
| unsigned long rows, cols; |
| Evas_Coord x, y, w, h, tw, th; |
| |
| if (area->w <= 0 || area->h <= 0) { |
| WRN("invalid area region: %d,%d+%dx%d.", |
| area->x, area->y, area->w, area->h); |
| return EINA_FALSE; |
| } |
| |
| x = area->x; |
| y = area->y; |
| w = area->w; |
| h = area->h; |
| |
| tw = TILE_SIZE_AT_ZOOM(TILE_W, zoom); |
| th = TILE_SIZE_AT_ZOOM(TILE_H, zoom); |
| |
| // cropping area region to fit matrix |
| eina_matrixsparse_size_get(tm->matrix, &rows, &cols); |
| if (x < 0) { |
| w += x; |
| x = 0; |
| } |
| if (y < 0) { |
| h += y; |
| y = 0; |
| } |
| |
| if (y + h - 1 > rows * th) |
| h = rows * th - y; |
| if (x + w - 1 > cols * tw) |
| w = cols * tw - x; |
| |
| return eina_tile_grid_slicer_setup(slicer, x, y, w, h, tw, th); |
| } |
| |
| |
| Eina_Bool ewk_tile_matrix_update(Ewk_Tile_Matrix *tm, const Eina_Rectangle *update, float zoom) |
| { |
| const Eina_Tile_Grid_Info *info; |
| Eina_Tile_Grid_Slicer slicer; |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE); |
| EINA_SAFETY_ON_NULL_RETURN_VAL(update, EINA_FALSE); |
| |
| if (update->w < 1 || update->h < 1) { |
| DBG("Why we get updates with empty areas? %d,%d+%dx%d at zoom %f", |
| update->x, update->y, update->w, update->h, zoom); |
| return EINA_TRUE; |
| } |
| |
| if (!_ewk_tile_matrix_slicer_setup(tm, update, zoom, &slicer)) { |
| ERR("Could not setup slicer for update %d,%d+%dx%d at zoom %f", |
| update->x, update->y, update->w, update->h, zoom); |
| return EINA_FALSE; |
| } |
| |
| while (eina_tile_grid_slicer_next(&slicer, &info)) { |
| unsigned long col, row; |
| Ewk_Tile *l, *t; |
| col = info->col; |
| row = info->row; |
| |
| l = eina_matrixsparse_data_idx_get(tm->matrix, row, col); |
| if (!l) |
| continue; |
| |
| EINA_INLIST_FOREACH(EINA_INLIST_GET(l), t) { |
| if (!t->updates && !t->stats.full_update) |
| tm->updates = eina_list_append(tm->updates, t); |
| if (info->full) |
| ewk_tile_update_full(t); |
| else { |
| if (t->zoom != zoom) |
| ewk_tile_update_full(t); |
| else |
| ewk_tile_update_area(t, &info->rect); |
| } |
| } |
| } |
| |
| |
| return EINA_TRUE; |
| } |
| |
| void ewk_tile_matrix_updates_process(Ewk_Tile_Matrix *tm) |
| { |
| Eina_List *l, *l_next; |
| Ewk_Tile *t; |
| EINA_SAFETY_ON_NULL_RETURN(tm); |
| |
| // process updates, unflag tiles |
| EINA_LIST_FOREACH_SAFE(tm->updates, l, l_next, t) { |
| ewk_tile_updates_process(t, tm->render.cb, tm->render.data); |
| if (t->visible) { |
| ewk_tile_updates_clear(t); |
| tm->updates = eina_list_remove_list(tm->updates, l); |
| } |
| } |
| } |
| |
| void ewk_tile_matrix_updates_clear(Ewk_Tile_Matrix *tm) |
| { |
| Ewk_Tile *t; |
| EINA_SAFETY_ON_NULL_RETURN(tm); |
| |
| EINA_LIST_FREE(tm->updates, t) |
| ewk_tile_updates_clear(t); |
| tm->updates = NULL; |
| } |
| |
| // remove me later! |
| void ewk_tile_matrix_dbg(const Ewk_Tile_Matrix *tm) |
| { |
| Eina_Iterator *it = eina_matrixsparse_iterator_complete_new(tm->matrix); |
| Eina_Matrixsparse_Cell *cell; |
| Eina_Bool last_empty = EINA_FALSE; |
| |
| #ifdef DEBUG_MEM_LEAKS |
| printf("Ewk_Tile Matrix: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] " |
| "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]\n", |
| tm->stats.tiles.allocated, tm->stats.tiles.freed, |
| tm->stats.tiles.allocated - tm->stats.tiles.freed, |
| tm->stats.bytes.allocated, tm->stats.bytes.freed, |
| tm->stats.bytes.allocated - tm->stats.bytes.freed); |
| #else |
| printf("Ewk_Tile Matrix:\n"); |
| #endif |
| |
| EINA_ITERATOR_FOREACH(it, cell) { |
| unsigned long row, col; |
| Eina_Inlist *l; |
| eina_matrixsparse_cell_position_get(cell, &row, &col); |
| l = eina_matrixsparse_cell_data_get(cell); |
| |
| if (!l) { |
| if (!last_empty) { |
| last_empty = EINA_TRUE; |
| printf("Empty:"); |
| } |
| printf(" [%lu,%lu]", col, row); |
| } else { |
| if (last_empty) { |
| last_empty = EINA_FALSE; |
| printf("\n"); |
| } |
| Ewk_Tile *t; |
| |
| printf("%3lu,%3lu %10p:", col, row, l); |
| EINA_INLIST_FOREACH(l, t) |
| printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c", |
| t->col, t->row, t->w, t->h, t->zoom, |
| t->visible ? '*': ' '); |
| printf("\n"); |
| } |
| } |
| if (last_empty) |
| printf("\n"); |
| eina_iterator_free(it); |
| |
| ewk_tile_unused_cache_dbg(tm->tuc); |
| } |
| |
| /** |
| * Freeze matrix to not do maintenance tasks. |
| * |
| * Maintenance tasks optimize usage, but maybe we know we should hold |
| * on them until we do the last operation, in this case we freeze |
| * while operating and then thaw when we're done. |
| * |
| * @see ewk_tile_matrix_thaw() |
| */ |
| void ewk_tile_matrix_freeze(Ewk_Tile_Matrix *tm) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tm); |
| if (!tm->frozen) |
| ewk_tile_unused_cache_freeze(tm->tuc); |
| tm->frozen++; |
| } |
| |
| /** |
| * Unfreezes maintenance tasks. |
| * |
| * If this is the last counterpart of freeze, then maintenance tasks |
| * will run immediately. |
| */ |
| void ewk_tile_matrix_thaw(Ewk_Tile_Matrix *tm) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tm); |
| if (!tm->frozen) { |
| ERR("thawing more than freezing!"); |
| return; |
| } |
| |
| tm->frozen--; |
| if (!tm->frozen) |
| ewk_tile_unused_cache_thaw(tm->tuc); |
| } |