/* The GIMP -- an image manipulation program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include "tile_cache.h" #include "tile_manager.h" #include "tile_swap.h" #include "tile_manager_pvt.h" #include "tile_pvt.h" /* ick. */ #include "libgimp/gimpintl.h" static int tile_manager_get_tile_num (TileManager *tm, int xpixel, int ypixel); TileManager* tile_manager_new (int toplevel_width, int toplevel_height, int bpp) { TileManager *tm; int width, height; tm = g_new (TileManager, 1); tm->user_data = NULL; tm->validate_proc = NULL; width = toplevel_width; height = toplevel_height; tm->width = width; tm->height = height; tm->bpp = bpp; tm->ntile_rows = (height + TILE_HEIGHT - 1) / TILE_HEIGHT; tm->ntile_cols = (width + TILE_WIDTH - 1) / TILE_WIDTH; tm->tiles = NULL; return tm; } void tile_manager_destroy (TileManager *tm) { int ntiles; int i; if (tm->tiles) { ntiles = tm->ntile_rows * tm->ntile_cols; for (i = 0; i < ntiles; i++) { TILE_MUTEX_LOCK (tm->tiles[i]); tile_detach (tm->tiles[i], tm, i); } g_free (tm->tiles); } g_free (tm); } void tile_manager_set_validate_proc (TileManager *tm, TileValidateProc proc) { tm->validate_proc = proc; } Tile* tile_manager_get_tile (TileManager *tm, int xpixel, int ypixel, int wantread, int wantwrite) { int tile_num; tile_num = tile_manager_get_tile_num (tm, xpixel, ypixel); if (tile_num < 0) return NULL; return tile_manager_get (tm, tile_num, wantread, wantwrite); } Tile* tile_manager_get (TileManager *tm, int tile_num, int wantread, int wantwrite) { Tile **tiles; Tile **tile_ptr; int ntiles; int nrows, ncols; int right_tile; int bottom_tile; int i, j, k; ntiles = tm->ntile_rows * tm->ntile_cols; if ((tile_num < 0) || (tile_num >= ntiles)) return NULL; if (!tm->tiles) { tm->tiles = g_new (Tile*, ntiles); tiles = tm->tiles; nrows = tm->ntile_rows; ncols = tm->ntile_cols; right_tile = tm->width - ((ncols - 1) * TILE_WIDTH); bottom_tile = tm->height - ((nrows - 1) * TILE_HEIGHT); for (i = 0, k = 0; i < nrows; i++) { for (j = 0; j < ncols; j++, k++) { tiles[k] = g_new (Tile, 1); tile_init (tiles[k], tm->bpp); tile_attach (tiles[k], tm, k); if (j == (ncols - 1)) tiles[k]->ewidth = right_tile; if (i == (nrows - 1)) tiles[k]->eheight = bottom_tile; } } } tile_ptr = &tm->tiles[tile_num]; if (wantwrite && !wantread) { g_warning("WRITE-ONLY TILE... UNTESTED!"); } /* if ((*tile_ptr)->share_count && (*tile_ptr)->write_count) fprintf(stderr," >> MEEPITY %d,%d << ", (*tile_ptr)->share_count, (*tile_ptr)->write_count ); */ if (wantread) { TILE_MUTEX_LOCK (*tile_ptr); if (wantwrite) { if ((*tile_ptr)->share_count > 1) { /* Copy-on-write required */ Tile *newtile = g_new (Tile, 1); tile_init (newtile, (*tile_ptr)->bpp); newtile->ewidth = (*tile_ptr)->ewidth; newtile->eheight = (*tile_ptr)->eheight; newtile->valid = (*tile_ptr)->valid; newtile->data = g_new (guchar, tile_size (newtile)); if (!newtile->valid) g_warning ("Oh boy, r/w tile is invalid... we suck. Please report."); if ((*tile_ptr)->rowhint) { tile_sanitize_rowhints (newtile); i = newtile->eheight; while (i--) { newtile->rowhint[i] = (*tile_ptr)->rowhint[i]; } } if ((*tile_ptr)->data != NULL) { memcpy (newtile->data, (*tile_ptr)->data, tile_size (newtile)); } else { tile_lock (*tile_ptr); memcpy (newtile->data, (*tile_ptr)->data, tile_size (newtile)); tile_release (*tile_ptr, FALSE); } tile_detach (*tile_ptr, tm, tile_num); TILE_MUTEX_LOCK (newtile); tile_attach (newtile, tm, tile_num); *tile_ptr = newtile; } (*tile_ptr)->write_count++; (*tile_ptr)->dirty = TRUE; } /* else { if ((*tile_ptr)->write_count) fprintf(stderr,"STINK! r/o on r/w tile /%d\007 ",(*tile_ptr)->write_count); } */ TILE_MUTEX_UNLOCK (*tile_ptr); tile_lock (*tile_ptr); } return *tile_ptr; } void tile_manager_get_async (TileManager *tm, int xpixel, int ypixel) { Tile *tile_ptr; int tile_num; tile_num = tile_manager_get_tile_num (tm, xpixel, ypixel); if (tile_num < 0) return; tile_ptr = tm->tiles[tile_num]; tile_swap_in_async (tile_ptr); } void tile_manager_validate (TileManager *tm, Tile *tile) { tile->valid = TRUE; if (tm->validate_proc) (* tm->validate_proc) (tm, tile); /* DEBUG STUFF -> if (tm->user_data) { // fprintf(stderr,"V%p ",tm->user_data); fprintf(stderr,"V"); } else { fprintf(stderr,"v"); } */ } void tile_manager_invalidate_tiles (TileManager *tm, Tile *toplevel_tile) { double x, y; int row, col; int num; col = toplevel_tile->tlink->tile_num % tm->ntile_cols; row = toplevel_tile->tlink->tile_num / tm->ntile_cols; x = (col * TILE_WIDTH + toplevel_tile->ewidth / 2.0) / (double) tm->width; y = (row * TILE_HEIGHT + toplevel_tile->eheight / 2.0) / (double) tm->height; if (tm->tiles) { col = x * tm->width / TILE_WIDTH; row = y * tm->height / TILE_HEIGHT; num = row * tm->ntile_cols + col; tile_invalidate (&tm->tiles[num], tm, num); } } void tile_invalidate_tile (Tile **tile_ptr, TileManager *tm, int xpixel, int ypixel) { int tile_num; tile_num = tile_manager_get_tile_num (tm, xpixel, ypixel); if (tile_num < 0) return; tile_invalidate (tile_ptr, tm, tile_num); } void tile_invalidate (Tile **tile_ptr, TileManager *tm, int tile_num) { Tile *tile = *tile_ptr; TILE_MUTEX_LOCK (tile); if (!tile->valid) goto leave; if (tile->share_count > 1) { /* This tile is shared. Replace it with a new, invalid tile. */ Tile *newtile = g_new (Tile, 1); tile_init (newtile, tile->bpp); newtile->ewidth = tile->ewidth; newtile->eheight = tile->eheight; tile_detach (tile, tm, tile_num); TILE_MUTEX_LOCK (newtile); tile_attach (newtile, tm, tile_num); tile = *tile_ptr = newtile; } if (tile->listhead) tile_cache_flush (tile); tile->valid = FALSE; if (tile->data) { g_free (tile->data); tile->data = NULL; } if (tile->swap_offset != -1) { /* If the tile is on disk, then delete its * presence there. */ tile_swap_delete (tile); } leave: TILE_MUTEX_UNLOCK (tile); } void tile_manager_map_tile (TileManager *tm, int xpixel, int ypixel, Tile *srctile) { int tile_row; int tile_col; int tile_num; if ((xpixel < 0) || (xpixel >= tm->width) || (ypixel < 0) || (ypixel >= tm->height)) { g_warning ("tile_manager_map_tile: tile co-ord out of range."); return; } tile_row = ypixel / TILE_HEIGHT; tile_col = xpixel / TILE_WIDTH; tile_num = tile_row * tm->ntile_cols + tile_col; tile_manager_map (tm, tile_num, srctile); } void tile_manager_map (TileManager *tm, int tile_num, Tile *srctile) { Tile **tiles; Tile **tile_ptr; int ntiles; int nrows, ncols; int right_tile; int bottom_tile; int i, j, k; ntiles = tm->ntile_rows * tm->ntile_cols; if ((tile_num < 0) || (tile_num >= ntiles)) { g_warning ("tile_manager_map: tile out of range."); return; } if (!tm->tiles) { g_warning ("tile_manager_map: empty tile level - init'ing."); tm->tiles = g_new (Tile*, ntiles); tiles = tm->tiles; nrows = tm->ntile_rows; ncols = tm->ntile_cols; right_tile = tm->width - ((ncols - 1) * TILE_WIDTH); bottom_tile = tm->height - ((nrows - 1) * TILE_HEIGHT); for (i = 0, k = 0; i < nrows; i++) { for (j = 0; j < ncols; j++, k++) { /* printf(",");fflush(stdout);*/ tiles[k] = g_new (Tile, 1); tile_init (tiles[k], tm->bpp); tile_attach (tiles[k], tm, k); if (j == (ncols - 1)) tiles[k]->ewidth = right_tile; if (i == (nrows - 1)) tiles[k]->eheight = bottom_tile; } } /* g_warning ("tile_manager_map: empty tile level - done.");*/ } tile_ptr = &tm->tiles[tile_num]; /* printf(")");fflush(stdout);*/ if (!srctile->valid) g_warning("tile_manager_map: srctile not validated yet! please report."); TILE_MUTEX_LOCK (*tile_ptr); if ((*tile_ptr)->ewidth != srctile->ewidth || (*tile_ptr)->eheight != srctile->eheight || (*tile_ptr)->bpp != srctile->bpp) { g_warning ("tile_manager_map: nonconformant map (%p -> %p)", srctile, *tile_ptr); } tile_detach (*tile_ptr, tm, tile_num); /* printf(">");fflush(stdout);*/ TILE_MUTEX_LOCK (srctile); /* printf(" [src:%p tm:%p tn:%d] ", srctile, tm, tile_num); fflush(stdout);*/ tile_attach (srctile, tm, tile_num); *tile_ptr = srctile; TILE_MUTEX_UNLOCK (srctile); /* printf("}");fflush(stdout);*/ } static int tile_manager_get_tile_num (TileManager *tm, int xpixel, int ypixel) { int tile_row; int tile_col; int tile_num; if ((xpixel < 0) || (xpixel >= tm->width) || (ypixel < 0) || (ypixel >= tm->height)) return -1; tile_row = ypixel / TILE_HEIGHT; tile_col = xpixel / TILE_WIDTH; tile_num = tile_row * tm->ntile_cols + tile_col; return tile_num; } void tile_manager_set_user_data (TileManager *tm, void *user_data) { tm->user_data = user_data; } void * tile_manager_get_user_data (TileManager *tm) { return tm->user_data; } int tile_manager_level_width (TileManager *tm) { return tm->width; } int tile_manager_level_height (TileManager *tm) { return tm->height; } int tile_manager_level_bpp (TileManager *tm) { return tm->bpp; } void tile_manager_get_tile_coordinates (TileManager *tm, Tile *tile, int *x, int *y) { TileLink *tl; for (tl = tile->tlink; tl; tl = tl->next) { if (tl->tm == tm) break; } if (tl == NULL) { g_warning ("tile_manager_get_tile_coordinates: tile not attached to manager"); return; } *x = TILE_WIDTH * (tl->tile_num % tm->ntile_cols); *y = TILE_HEIGHT * (tl->tile_num / tm->ntile_cols); } void tile_manager_map_over_tile (TileManager *tm, Tile *tile, Tile *srctile) { TileLink *tl; for (tl = tile->tlink; tl; tl = tl->next) { if (tl->tm == tm) break; } if (tl == NULL) { g_warning ("tile_manager_map_over_tile: tile not attached to manager"); return; } tile_manager_map (tm, tl->tile_num, srctile); }