mirror of https://github.com/GNOME/gimp.git
839 lines
19 KiB
C
839 lines
19 KiB
C
/* GIMP - The GNU 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 3 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <string.h>
|
|
|
|
#include <glib-object.h>
|
|
|
|
#include "base-types.h"
|
|
|
|
#include "tile.h"
|
|
#include "tile-cache.h"
|
|
#include "tile-manager.h"
|
|
#include "tile-manager-private.h"
|
|
#include "tile-rowhints.h"
|
|
#include "tile-swap.h"
|
|
#include "tile-private.h"
|
|
|
|
|
|
static void tile_manager_allocate_tiles (TileManager *tm);
|
|
|
|
#ifdef TILE_PROFILING
|
|
extern gint tile_exist_peak;
|
|
extern gint tile_exist_count;
|
|
#endif
|
|
|
|
GType
|
|
gimp_tile_manager_get_type (void)
|
|
{
|
|
static GType type = 0;
|
|
|
|
if (! type)
|
|
type = g_boxed_type_register_static ("TileManager",
|
|
(GBoxedCopyFunc) tile_manager_ref,
|
|
(GBoxedFreeFunc) tile_manager_unref);
|
|
|
|
return type;
|
|
}
|
|
|
|
|
|
static inline gint
|
|
tile_manager_get_tile_num (TileManager *tm,
|
|
gint xpixel,
|
|
gint ypixel)
|
|
{
|
|
if ((xpixel < 0) || (xpixel >= tm->width) ||
|
|
(ypixel < 0) || (ypixel >= tm->height))
|
|
return -1;
|
|
|
|
return (ypixel / TILE_HEIGHT) * tm->ntile_cols + (xpixel / TILE_WIDTH);
|
|
}
|
|
|
|
|
|
TileManager *
|
|
tile_manager_new (gint width,
|
|
gint height,
|
|
gint bpp)
|
|
{
|
|
TileManager *tm;
|
|
|
|
g_return_val_if_fail (width > 0 && height > 0, NULL);
|
|
g_return_val_if_fail (bpp > 0 && bpp <= 4, NULL);
|
|
|
|
tm = g_slice_new0 (TileManager);
|
|
|
|
tm->ref_count = 1;
|
|
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->cached_num = -1;
|
|
|
|
return tm;
|
|
}
|
|
|
|
TileManager *
|
|
tile_manager_ref (TileManager *tm)
|
|
{
|
|
g_return_val_if_fail (tm != NULL, NULL);
|
|
|
|
tm->ref_count++;
|
|
|
|
return tm;
|
|
}
|
|
|
|
void
|
|
tile_manager_unref (TileManager *tm)
|
|
{
|
|
g_return_if_fail (tm != NULL);
|
|
|
|
tm->ref_count--;
|
|
|
|
if (tm->ref_count < 1)
|
|
{
|
|
if (tm->cached_tile)
|
|
tile_release (tm->cached_tile, FALSE);
|
|
|
|
if (tm->tiles)
|
|
{
|
|
gint ntiles = tm->ntile_rows * tm->ntile_cols;
|
|
gint i;
|
|
|
|
for (i = 0; i < ntiles; i++)
|
|
tile_detach (tm->tiles[i], tm, i);
|
|
|
|
g_free (tm->tiles);
|
|
}
|
|
|
|
g_slice_free (TileManager, tm);
|
|
}
|
|
}
|
|
|
|
void
|
|
tile_manager_set_validate_proc (TileManager *tm,
|
|
TileValidateProc proc,
|
|
gpointer user_data)
|
|
{
|
|
g_return_if_fail (tm != NULL);
|
|
|
|
tm->validate_proc = proc;
|
|
tm->user_data = user_data;
|
|
}
|
|
|
|
Tile *
|
|
tile_manager_get_tile (TileManager *tm,
|
|
gint xpixel,
|
|
gint ypixel,
|
|
gboolean wantread,
|
|
gboolean wantwrite)
|
|
{
|
|
g_return_val_if_fail (tm != NULL, NULL);
|
|
|
|
return tile_manager_get (tm,
|
|
tile_manager_get_tile_num (tm, xpixel, ypixel),
|
|
wantread, wantwrite);
|
|
}
|
|
|
|
Tile *
|
|
tile_manager_get (TileManager *tm,
|
|
gint tile_num,
|
|
gboolean wantread,
|
|
gboolean wantwrite)
|
|
{
|
|
Tile *tile;
|
|
gint ntiles;
|
|
|
|
g_return_val_if_fail (tm != NULL, NULL);
|
|
|
|
ntiles = tm->ntile_rows * tm->ntile_cols;
|
|
|
|
if ((tile_num < 0) || (tile_num >= ntiles))
|
|
return NULL;
|
|
|
|
if (! tm->tiles)
|
|
tile_manager_allocate_tiles (tm);
|
|
|
|
tile = tm->tiles[tile_num];
|
|
|
|
if (G_UNLIKELY (wantwrite && ! wantread))
|
|
g_warning ("WRITE-ONLY TILE... UNTESTED!");
|
|
|
|
#ifdef DEBUG_TILE_MANAGER
|
|
if (G_UNLIKELY (tile->share_count && tile->write_count))
|
|
g_printerr (">> MEEPITY %d,%d <<\n", tile->share_count, tile->write_count);
|
|
#endif
|
|
|
|
if (wantread)
|
|
{
|
|
if (wantwrite)
|
|
{
|
|
if (tile_num == tm->cached_num)
|
|
{
|
|
tile_release (tm->cached_tile, FALSE);
|
|
|
|
tm->cached_tile = NULL;
|
|
tm->cached_num = -1;
|
|
}
|
|
|
|
if (tile->share_count > 1)
|
|
{
|
|
/* Copy-on-write required */
|
|
Tile *new = tile_new (tile->bpp);
|
|
|
|
new->ewidth = tile->ewidth;
|
|
new->eheight = tile->eheight;
|
|
new->valid = tile->valid;
|
|
|
|
new->size = new->ewidth * new->eheight * new->bpp;
|
|
new->data = g_new (guchar, new->size);
|
|
|
|
#ifdef TILE_PROFILING
|
|
tile_exist_count++;
|
|
if (tile_exist_count > tile_exist_peak)
|
|
tile_exist_peak = tile_exist_count;
|
|
#endif
|
|
|
|
if (tile->rowhint)
|
|
{
|
|
tile_allocate_rowhints (new);
|
|
|
|
memcpy (new->rowhint, tile->rowhint,
|
|
new->eheight * sizeof (TileRowHint));
|
|
}
|
|
|
|
if (tile->data)
|
|
{
|
|
memcpy (new->data, tile->data, new->size);
|
|
}
|
|
else
|
|
{
|
|
tile_lock (tile);
|
|
memcpy (new->data, tile->data, new->size);
|
|
tile_release (tile, FALSE);
|
|
}
|
|
|
|
tile_detach (tile, tm, tile_num);
|
|
tile_attach (new, tm, tile_num);
|
|
|
|
tile = new;
|
|
tm->tiles[tile_num] = tile;
|
|
}
|
|
|
|
/* must lock before marking dirty */
|
|
tile_lock (tile);
|
|
tile->write_count++;
|
|
tile->dirty = TRUE;
|
|
}
|
|
else
|
|
{
|
|
#ifdef DEBUG_TILE_MANAGER
|
|
if (G_UNLIKELY (tile->write_count))
|
|
g_printerr ("STINK! r/o on r/w tile (%d)\n", tile->write_count);
|
|
#endif
|
|
tile_lock (tile);
|
|
}
|
|
}
|
|
|
|
return tile;
|
|
}
|
|
|
|
Tile *
|
|
tile_manager_get_at (TileManager *tm,
|
|
gint tile_col,
|
|
gint tile_row,
|
|
gboolean wantread,
|
|
gboolean wantwrite)
|
|
{
|
|
g_return_val_if_fail (tm != NULL, NULL);
|
|
|
|
if (tile_col < 0 || tile_col >= tm->ntile_cols ||
|
|
tile_row < 0 || tile_row >= tm->ntile_rows)
|
|
return NULL;
|
|
|
|
return tile_manager_get (tm,
|
|
tile_row * tm->ntile_cols + tile_col,
|
|
wantread, wantwrite);
|
|
}
|
|
|
|
void
|
|
tile_manager_validate_tile (TileManager *tm,
|
|
Tile *tile)
|
|
{
|
|
g_return_if_fail (tm != NULL);
|
|
g_return_if_fail (tile != NULL);
|
|
|
|
tile->valid = TRUE;
|
|
|
|
if (tm->validate_proc)
|
|
{
|
|
(* tm->validate_proc) (tm, tile, tm->user_data);
|
|
}
|
|
else
|
|
{
|
|
/* Set the contents of the tile to empty */
|
|
memset (tile->data, 0, tile_size (tile));
|
|
}
|
|
|
|
#ifdef DEBUG_TILE_MANAGER
|
|
g_printerr ("%c", tm->user_data ? 'V' : 'v');
|
|
#endif
|
|
}
|
|
|
|
static void
|
|
tile_manager_allocate_tiles (TileManager *tm)
|
|
{
|
|
Tile **tiles;
|
|
const gint nrows = tm->ntile_rows;
|
|
const gint ncols = tm->ntile_cols;
|
|
const gint right_tile = tm->width - ((ncols - 1) * TILE_WIDTH);
|
|
const gint bottom_tile = tm->height - ((nrows - 1) * TILE_HEIGHT);
|
|
gint i, j, k;
|
|
|
|
g_assert (tm->tiles == NULL);
|
|
|
|
tiles = g_new (Tile *, nrows * ncols);
|
|
|
|
for (i = 0, k = 0; i < nrows; i++)
|
|
{
|
|
for (j = 0; j < ncols; j++, k++)
|
|
{
|
|
Tile *new = tile_new (tm->bpp);
|
|
|
|
tile_attach (new, tm, k);
|
|
|
|
if (j == (ncols - 1))
|
|
new->ewidth = right_tile;
|
|
|
|
if (i == (nrows - 1))
|
|
new->eheight = bottom_tile;
|
|
|
|
new->size = new->ewidth * new->eheight * new->bpp;
|
|
|
|
tiles[k] = new;
|
|
}
|
|
}
|
|
|
|
tm->tiles = tiles;
|
|
}
|
|
|
|
static void
|
|
tile_manager_invalidate_tile (TileManager *tm,
|
|
gint tile_num)
|
|
{
|
|
Tile *tile = tm->tiles[tile_num];
|
|
|
|
if (! tile->valid)
|
|
return;
|
|
|
|
if (tile_num == tm->cached_num)
|
|
{
|
|
tile_release (tm->cached_tile, FALSE);
|
|
|
|
tm->cached_tile = NULL;
|
|
tm->cached_num = -1;
|
|
}
|
|
|
|
if (tile->cached)
|
|
tile_cache_flush (tile);
|
|
|
|
if (G_UNLIKELY (tile->share_count > 1))
|
|
{
|
|
/* This tile is shared. Replace it with a new invalid tile. */
|
|
Tile *new = tile_new (tile->bpp);
|
|
|
|
new->ewidth = tile->ewidth;
|
|
new->eheight = tile->eheight;
|
|
new->size = tile->size;
|
|
|
|
tile_detach (tile, tm, tile_num);
|
|
tile_attach (new, tm, tile_num);
|
|
|
|
tile = new;
|
|
tm->tiles[tile_num] = tile;
|
|
}
|
|
|
|
tile->valid = FALSE;
|
|
|
|
if (tile->data)
|
|
{
|
|
g_free (tile->data);
|
|
tile->data = NULL;
|
|
#ifdef TILE_PROFILING
|
|
tile_exist_count--;
|
|
#endif
|
|
}
|
|
|
|
if (tile->swap_offset != -1)
|
|
{
|
|
/* If the tile is on disk, then delete its
|
|
* presence there.
|
|
*/
|
|
tile_swap_delete (tile);
|
|
}
|
|
}
|
|
|
|
static void
|
|
tile_manager_invalidate_pixel (TileManager *tm,
|
|
gint xpixel,
|
|
gint ypixel)
|
|
{
|
|
gint num = tile_manager_get_tile_num (tm, xpixel, ypixel);
|
|
|
|
if (num < 0)
|
|
return;
|
|
|
|
tile_manager_invalidate_tile (tm, num);
|
|
}
|
|
|
|
void
|
|
tile_manager_map_tile (TileManager *tm,
|
|
gint xpixel,
|
|
gint ypixel,
|
|
Tile *srctile)
|
|
{
|
|
g_return_if_fail (tm != NULL);
|
|
g_return_if_fail (srctile != NULL);
|
|
|
|
tile_manager_map (tm,
|
|
tile_manager_get_tile_num (tm, xpixel, ypixel),
|
|
srctile);
|
|
}
|
|
|
|
void
|
|
tile_manager_map (TileManager *tm,
|
|
gint tile_num,
|
|
Tile *srctile)
|
|
{
|
|
Tile *tile;
|
|
|
|
g_return_if_fail (tm != NULL);
|
|
g_return_if_fail (srctile != NULL);
|
|
g_return_if_fail (tile_num >= 0);
|
|
g_return_if_fail (tile_num < tm->ntile_rows * tm->ntile_cols);
|
|
|
|
if (G_UNLIKELY (! tm->tiles))
|
|
{
|
|
g_warning ("%s: empty tile level - initializing", G_STRLOC);
|
|
|
|
tile_manager_allocate_tiles (tm);
|
|
}
|
|
|
|
tile = tm->tiles[tile_num];
|
|
|
|
#ifdef DEBUG_TILE_MANAGER
|
|
g_printerr (")");
|
|
#endif
|
|
|
|
if (G_UNLIKELY (! srctile->valid))
|
|
g_warning("%s: srctile not validated yet! please report", G_STRLOC);
|
|
|
|
if (G_UNLIKELY (tile->ewidth != srctile->ewidth ||
|
|
tile->eheight != srctile->eheight ||
|
|
tile->bpp != srctile->bpp))
|
|
{
|
|
g_warning ("%s: nonconformant map (%p -> %p)",
|
|
G_STRLOC, srctile, tile);
|
|
}
|
|
|
|
tile_detach (tile, tm, tile_num);
|
|
|
|
#ifdef DEBUG_TILE_MANAGER
|
|
g_printerr (">");
|
|
#endif
|
|
|
|
#ifdef DEBUG_TILE_MANAGER
|
|
g_printerr (" [src:%p tm:%p tn:%d] ", srctile, tm, tile_num);
|
|
#endif
|
|
|
|
tile_attach (srctile, tm, tile_num);
|
|
|
|
tm->tiles[tile_num] = srctile;
|
|
|
|
#ifdef DEBUG_TILE_MANAGER
|
|
g_printerr ("}\n");
|
|
#endif
|
|
}
|
|
|
|
void
|
|
tile_manager_invalidate_area (TileManager *tm,
|
|
gint x,
|
|
gint y,
|
|
gint w,
|
|
gint h)
|
|
{
|
|
gint i;
|
|
gint j;
|
|
|
|
/* if no tiles have been allocated, there's no need to invalidate any */
|
|
if (! tm->tiles)
|
|
return;
|
|
|
|
for (i = y; i < (y + h); i += (TILE_HEIGHT - (i % TILE_HEIGHT)))
|
|
for (j = x; j < (x + w); j += (TILE_WIDTH - (j % TILE_WIDTH)))
|
|
{
|
|
tile_manager_invalidate_pixel (tm, j, i);
|
|
}
|
|
}
|
|
|
|
gint
|
|
tile_manager_width (const TileManager *tm)
|
|
{
|
|
g_return_val_if_fail (tm != NULL, 0);
|
|
|
|
return tm->width;
|
|
}
|
|
|
|
gint
|
|
tile_manager_height (const TileManager *tm)
|
|
{
|
|
g_return_val_if_fail (tm != NULL, 0);
|
|
|
|
return tm->height;
|
|
}
|
|
|
|
gint
|
|
tile_manager_bpp (const TileManager *tm)
|
|
{
|
|
g_return_val_if_fail (tm != NULL, 0);
|
|
|
|
return tm->bpp;
|
|
}
|
|
|
|
gint
|
|
tile_manager_tiles_per_col (const TileManager *tm)
|
|
{
|
|
g_return_val_if_fail (tm != NULL, 0);
|
|
|
|
return tm->ntile_cols;
|
|
}
|
|
|
|
gint
|
|
tile_manager_tiles_per_row (const TileManager *tm)
|
|
{
|
|
g_return_val_if_fail (tm != NULL, 0);
|
|
|
|
return tm->ntile_rows;
|
|
}
|
|
|
|
void
|
|
tile_manager_get_offsets (const TileManager *tm,
|
|
gint *x,
|
|
gint *y)
|
|
{
|
|
g_return_if_fail (tm != NULL);
|
|
g_return_if_fail (x != NULL && y != NULL);
|
|
|
|
*x = tm->x;
|
|
*y = tm->y;
|
|
}
|
|
|
|
void
|
|
tile_manager_set_offsets (TileManager *tm,
|
|
gint x,
|
|
gint y)
|
|
{
|
|
g_return_if_fail (tm != NULL);
|
|
|
|
tm->x = x;
|
|
tm->y = y;
|
|
}
|
|
|
|
gint64
|
|
tile_manager_get_memsize (const TileManager *tm,
|
|
gboolean sparse)
|
|
{
|
|
/* the tile manager itself */
|
|
gint64 memsize = sizeof (TileManager);
|
|
|
|
if (! tm)
|
|
return 0;
|
|
|
|
/* the array of tiles */
|
|
memsize += (gint64) tm->ntile_rows * tm->ntile_cols * (sizeof (Tile) +
|
|
sizeof (gpointer));
|
|
|
|
/* the memory allocated for the tiles */
|
|
if (sparse)
|
|
{
|
|
if (tm->tiles)
|
|
{
|
|
Tile **tiles = tm->tiles;
|
|
gint64 size = TILE_WIDTH * TILE_HEIGHT * tm->bpp;
|
|
gint i, j;
|
|
|
|
for (i = 0; i < tm->ntile_rows; i++)
|
|
for (j = 0; j < tm->ntile_cols; j++, tiles++)
|
|
{
|
|
if (tile_is_valid (*tiles))
|
|
memsize += size;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
memsize += (gint64) tm->width * tm->height * tm->bpp;
|
|
}
|
|
|
|
return memsize;
|
|
}
|
|
|
|
static inline gint
|
|
tile_manager_locate_tile (TileManager *tm,
|
|
Tile *tile)
|
|
{
|
|
TileLink *tl;
|
|
|
|
for (tl = tile->tlink; tl; tl = tl->next)
|
|
{
|
|
if (tl->tm == tm)
|
|
break;
|
|
}
|
|
|
|
if (G_UNLIKELY (tl == NULL))
|
|
{
|
|
g_warning ("%s: tile not attached to manager", G_STRLOC);
|
|
return 0;
|
|
}
|
|
|
|
return tl->tile_num;
|
|
}
|
|
|
|
void
|
|
tile_manager_get_tile_col_row (TileManager *tm,
|
|
Tile *tile,
|
|
gint *tile_col,
|
|
gint *tile_row)
|
|
{
|
|
gint tile_num;
|
|
|
|
g_return_if_fail (tm != NULL);
|
|
g_return_if_fail (tile != NULL);
|
|
g_return_if_fail (tile_col != NULL && tile_row != NULL);
|
|
|
|
tile_num = tile_manager_locate_tile (tm, tile);
|
|
|
|
*tile_col = tile_num % tm->ntile_cols;
|
|
*tile_row = tile_num / tm->ntile_cols;
|
|
}
|
|
|
|
void
|
|
tile_manager_get_tile_coordinates (TileManager *tm,
|
|
Tile *tile,
|
|
gint *x,
|
|
gint *y)
|
|
{
|
|
gint tile_col;
|
|
gint tile_row;
|
|
|
|
g_return_if_fail (tm != NULL);
|
|
g_return_if_fail (tile != NULL);
|
|
g_return_if_fail (x != NULL && y != NULL);
|
|
|
|
tile_manager_get_tile_col_row (tm, tile, &tile_col, &tile_row);
|
|
|
|
*x = TILE_WIDTH * tile_col;
|
|
*y = TILE_HEIGHT * tile_row;
|
|
}
|
|
|
|
void
|
|
tile_manager_map_over_tile (TileManager *tm,
|
|
Tile *tile,
|
|
Tile *srctile)
|
|
{
|
|
TileLink *tl;
|
|
|
|
g_return_if_fail (tm != NULL);
|
|
g_return_if_fail (tile != NULL);
|
|
g_return_if_fail (srctile != NULL);
|
|
|
|
for (tl = tile->tlink; tl; tl = tl->next)
|
|
{
|
|
if (tl->tm == tm)
|
|
break;
|
|
}
|
|
|
|
if (G_UNLIKELY (tl == NULL))
|
|
{
|
|
g_warning ("%s: tile not attached to manager", G_STRLOC);
|
|
return;
|
|
}
|
|
|
|
tile_manager_map (tm, tl->tile_num, srctile);
|
|
}
|
|
|
|
void
|
|
read_pixel_data (TileManager *tm,
|
|
gint x1,
|
|
gint y1,
|
|
gint x2,
|
|
gint y2,
|
|
guchar *buffer,
|
|
guint stride)
|
|
{
|
|
guint x, y;
|
|
|
|
for (y = y1; y <= y2; y += TILE_HEIGHT - (y % TILE_HEIGHT))
|
|
for (x = x1; x <= x2; x += TILE_WIDTH - (x % TILE_WIDTH))
|
|
{
|
|
Tile *tile = tile_manager_get_tile (tm, x, y, TRUE, FALSE);
|
|
const guchar *s = TILE_DATA_POINTER (tile, x, y);
|
|
guchar *d = buffer + stride * (y - y1) + tm->bpp * (x - x1);
|
|
guint rows, cols;
|
|
guint srcstride;
|
|
|
|
rows = tile->eheight - y % TILE_HEIGHT;
|
|
if (rows > (y2 - y + 1))
|
|
rows = y2 - y + 1;
|
|
|
|
cols = tile->ewidth - x % TILE_WIDTH;
|
|
if (cols > (x2 - x + 1))
|
|
cols = x2 - x + 1;
|
|
|
|
srcstride = tile->ewidth * tile->bpp;
|
|
|
|
while (rows--)
|
|
{
|
|
memcpy (d, s, cols * tm->bpp);
|
|
|
|
s += srcstride;
|
|
d += stride;
|
|
}
|
|
|
|
tile_release (tile, FALSE);
|
|
}
|
|
}
|
|
|
|
void
|
|
write_pixel_data (TileManager *tm,
|
|
gint x1,
|
|
gint y1,
|
|
gint x2,
|
|
gint y2,
|
|
const guchar *buffer,
|
|
guint stride)
|
|
{
|
|
guint x, y;
|
|
|
|
for (y = y1; y <= y2; y += TILE_HEIGHT - (y % TILE_HEIGHT))
|
|
for (x = x1; x <= x2; x += TILE_WIDTH - (x % TILE_WIDTH))
|
|
{
|
|
Tile *tile = tile_manager_get_tile (tm, x, y, TRUE, TRUE);
|
|
const guchar *s = buffer + stride * (y - y1) + tm->bpp * (x - x1);
|
|
guchar *d = TILE_DATA_POINTER (tile, x, y);
|
|
guint rows, cols;
|
|
guint dststride;
|
|
|
|
rows = tile->eheight - y % TILE_HEIGHT;
|
|
if (rows > (y2 - y + 1))
|
|
rows = y2 - y + 1;
|
|
|
|
cols = tile->ewidth - x % TILE_WIDTH;
|
|
if (cols > (x2 - x + 1))
|
|
cols = x2 - x + 1;
|
|
|
|
dststride = tile->ewidth * tile->bpp;
|
|
|
|
while (rows--)
|
|
{
|
|
memcpy (d, s, cols * tm->bpp);
|
|
|
|
s += stride;
|
|
d += dststride;
|
|
}
|
|
|
|
tile_release (tile, TRUE);
|
|
}
|
|
}
|
|
|
|
void
|
|
read_pixel_data_1 (TileManager *tm,
|
|
gint x,
|
|
gint y,
|
|
guchar *buffer)
|
|
{
|
|
const gint num = tile_manager_get_tile_num (tm, x, y);
|
|
|
|
if (num < 0)
|
|
return;
|
|
|
|
if (num != tm->cached_num) /* must fetch a new tile */
|
|
{
|
|
Tile *tile;
|
|
|
|
if (tm->cached_tile)
|
|
tile_release (tm->cached_tile, FALSE);
|
|
|
|
tm->cached_num = -1;
|
|
tm->cached_tile = NULL;
|
|
|
|
/* use a temporary variable instead of assigning to
|
|
* tm->cached_tile directly to make sure tm->cached_num
|
|
* and tm->cached_tile are always in a consistent state.
|
|
* (the requested tile might be invalid and needs to be
|
|
* validated, which would call tile_manager_get() recursively,
|
|
* which in turn would clear the cached tile) See bug #472770.
|
|
*/
|
|
tile = tile_manager_get (tm, num, TRUE, FALSE);
|
|
|
|
tm->cached_num = num;
|
|
tm->cached_tile = tile;
|
|
}
|
|
|
|
{
|
|
const guchar *src = TILE_DATA_POINTER (tm->cached_tile, x, y);
|
|
|
|
switch (tm->bpp)
|
|
{
|
|
case 4:
|
|
*buffer++ = *src++;
|
|
case 3:
|
|
*buffer++ = *src++;
|
|
case 2:
|
|
*buffer++ = *src++;
|
|
case 1:
|
|
*buffer++ = *src++;
|
|
}
|
|
}
|
|
}
|
|
|
|
void
|
|
write_pixel_data_1 (TileManager *tm,
|
|
gint x,
|
|
gint y,
|
|
const guchar *buffer)
|
|
{
|
|
Tile *tile = tile_manager_get_tile (tm, x, y, TRUE, TRUE);
|
|
guchar *dest = TILE_DATA_POINTER (tile, x, y);
|
|
|
|
switch (tm->bpp)
|
|
{
|
|
case 4:
|
|
*dest++ = *buffer++;
|
|
case 3:
|
|
*dest++ = *buffer++;
|
|
case 2:
|
|
*dest++ = *buffer++;
|
|
case 1:
|
|
*dest++ = *buffer++;
|
|
}
|
|
|
|
tile_release (tile, TRUE);
|
|
}
|