2006-12-10 05:33:38 +08:00
|
|
|
/* GIMP - The GNU Image Manipulation Program
|
2001-03-31 22:10:22 +08:00
|
|
|
* Copyright (C) 1995-2001 Spencer Kimball, Peter Mattis, and others
|
|
|
|
*
|
|
|
|
* 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 "config.h"
|
|
|
|
|
2007-03-09 18:18:25 +08:00
|
|
|
#include <string.h>
|
|
|
|
|
2001-12-08 00:10:53 +08:00
|
|
|
#include <glib-object.h>
|
2001-03-31 22:10:22 +08:00
|
|
|
|
2001-05-15 19:25:25 +08:00
|
|
|
#include "base-types.h"
|
2001-03-31 22:10:22 +08:00
|
|
|
|
2001-05-15 19:25:25 +08:00
|
|
|
#include "pixel-region.h"
|
|
|
|
#include "pixel-surround.h"
|
|
|
|
#include "tile-manager.h"
|
2001-03-31 22:10:22 +08:00
|
|
|
#include "tile.h"
|
|
|
|
|
|
|
|
|
2007-02-23 17:50:42 +08:00
|
|
|
struct _PixelSurround
|
|
|
|
{
|
2007-03-14 17:13:12 +08:00
|
|
|
TileManager *mgr; /* tile manager to access tiles from */
|
2008-08-26 04:38:52 +08:00
|
|
|
gint xmax; /* largest x coordinate in tile manager */
|
|
|
|
gint ymax; /* largest y coordinate in tile manager */
|
2007-03-14 17:13:12 +08:00
|
|
|
gint bpp; /* bytes per pixel in tile manager */
|
|
|
|
gint w; /* width of pixel surround area */
|
|
|
|
gint h; /* height of pixel surround area */
|
|
|
|
Tile *tile; /* locked tile (may be NULL) */
|
|
|
|
gint tile_x; /* origin of locked tile */
|
|
|
|
gint tile_y; /* origin of locked tile */
|
|
|
|
gint tile_w; /* width of locked tile */
|
|
|
|
gint tile_h; /* height of locked tile */
|
|
|
|
gint rowstride; /* rowstride of buffers */
|
|
|
|
guchar *bg; /* buffer filled with background color */
|
|
|
|
guchar *buf; /* buffer used for combining tile data */
|
2008-08-26 04:38:52 +08:00
|
|
|
PixelSurroundMode mode;
|
2007-02-23 17:50:42 +08:00
|
|
|
};
|
|
|
|
|
2008-08-26 15:24:55 +08:00
|
|
|
static const guchar * pixel_surround_get_data (PixelSurround *surround,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint *w,
|
|
|
|
gint *h,
|
|
|
|
gint *rowstride);
|
2007-02-23 17:50:42 +08:00
|
|
|
|
2007-02-24 00:19:42 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* pixel_surround_new:
|
2007-04-16 23:02:51 +08:00
|
|
|
* @tiles: tile manager
|
2007-02-24 00:19:42 +08:00
|
|
|
* @width: width of surround region
|
|
|
|
* @height: height of surround region
|
|
|
|
* @bg: color to use for pixels that are not covered by the tile manager
|
|
|
|
*
|
|
|
|
* Return value: a new #PixelSurround.
|
|
|
|
*/
|
2007-02-23 17:50:42 +08:00
|
|
|
PixelSurround *
|
2008-08-26 04:38:52 +08:00
|
|
|
pixel_surround_new (TileManager *tiles,
|
|
|
|
gint width,
|
|
|
|
gint height,
|
|
|
|
PixelSurroundMode mode)
|
2001-03-31 22:10:22 +08:00
|
|
|
{
|
2007-02-24 00:19:42 +08:00
|
|
|
PixelSurround *surround;
|
2001-03-31 22:10:22 +08:00
|
|
|
|
2007-02-24 00:19:42 +08:00
|
|
|
g_return_val_if_fail (tiles != NULL, NULL);
|
2008-08-26 15:41:53 +08:00
|
|
|
g_return_val_if_fail (width < TILE_WIDTH, NULL);
|
|
|
|
g_return_val_if_fail (height < TILE_WIDTH, NULL);
|
2007-02-23 17:50:42 +08:00
|
|
|
|
2007-05-23 02:29:33 +08:00
|
|
|
surround = g_slice_new0 (PixelSurround);
|
2007-02-23 18:10:44 +08:00
|
|
|
|
2007-03-07 07:32:25 +08:00
|
|
|
surround->mgr = tiles;
|
2008-08-26 04:38:52 +08:00
|
|
|
surround->xmax = tile_manager_width (surround->mgr) - 1;
|
|
|
|
surround->ymax = tile_manager_height (surround->mgr) - 1;
|
2007-03-07 07:32:25 +08:00
|
|
|
surround->bpp = tile_manager_bpp (tiles);
|
|
|
|
surround->w = width;
|
|
|
|
surround->h = height;
|
|
|
|
surround->rowstride = width * surround->bpp;
|
2008-08-26 04:38:52 +08:00
|
|
|
surround->bg = g_new0 (guchar, surround->rowstride * height);
|
2007-03-07 07:32:25 +08:00
|
|
|
surround->buf = g_new (guchar, surround->rowstride * height);
|
2008-08-26 04:38:52 +08:00
|
|
|
surround->mode = mode;
|
|
|
|
|
|
|
|
return surround;
|
|
|
|
}
|
2001-03-31 22:10:22 +08:00
|
|
|
|
2008-08-26 04:38:52 +08:00
|
|
|
void
|
|
|
|
pixel_surround_set_bg (PixelSurround *surround,
|
|
|
|
const guchar *bg)
|
|
|
|
{
|
|
|
|
guchar *dest = surround->bg;
|
|
|
|
gint pixels = surround->w * surround->h;
|
2007-02-23 17:50:42 +08:00
|
|
|
|
2007-03-07 07:32:25 +08:00
|
|
|
while (pixels--)
|
|
|
|
{
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
for (i = 0; i < surround->bpp; i++)
|
|
|
|
*dest++ = bg[i];
|
|
|
|
}
|
2001-03-31 22:10:22 +08:00
|
|
|
}
|
|
|
|
|
2007-02-24 00:19:42 +08:00
|
|
|
/**
|
|
|
|
* pixel_surround_lock:
|
|
|
|
* @surround: a #PixelSurround
|
|
|
|
* @x: X coordinate of upper left corner
|
|
|
|
* @y: Y coordinate of upper left corner
|
|
|
|
* @rowstride: return location for rowstride
|
|
|
|
*
|
|
|
|
* Gives access to a region of pixels. The upper left corner is
|
|
|
|
* specified by the @x and @y parameters. The size of the region
|
|
|
|
* is determined by the dimensions given when creating the @surround.
|
|
|
|
*
|
|
|
|
* When you don't need to read from the pixels any longer, you should
|
|
|
|
* unlock the @surround using pixel_surround_unlock(). If you need a
|
|
|
|
* different region, just call pixel_surround_lock() again.
|
|
|
|
*
|
|
|
|
* Return value: pointer to pixel data (read-only)
|
|
|
|
*/
|
2007-02-23 17:50:42 +08:00
|
|
|
const guchar *
|
2007-02-24 00:19:42 +08:00
|
|
|
pixel_surround_lock (PixelSurround *surround,
|
2006-04-12 20:49:29 +08:00
|
|
|
gint x,
|
2007-02-23 17:50:42 +08:00
|
|
|
gint y,
|
|
|
|
gint *rowstride)
|
2001-03-31 22:10:22 +08:00
|
|
|
{
|
2007-03-07 07:32:25 +08:00
|
|
|
const guchar *src;
|
|
|
|
gint w, h;
|
2005-07-09 20:04:23 +08:00
|
|
|
|
2007-03-07 07:32:25 +08:00
|
|
|
src = pixel_surround_get_data (surround, x, y, &w, &h, rowstride);
|
|
|
|
|
|
|
|
if (w >= surround->w && h >= surround->h)
|
2001-03-31 22:10:22 +08:00
|
|
|
{
|
2007-03-09 00:38:53 +08:00
|
|
|
/* return a pointer to the data if it covers the whole region */
|
|
|
|
return src;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* otherwise, copy region to our internal buffer */
|
|
|
|
guchar *dest = surround->buf;
|
2007-03-09 17:53:21 +08:00
|
|
|
gint inc = surround->w;
|
2007-03-09 00:38:53 +08:00
|
|
|
gint i = 0;
|
|
|
|
gint j = 0;
|
|
|
|
|
|
|
|
/* These loops are somewhat twisted. The idea is to make as few
|
|
|
|
* calls to pixel_surround_get_data() as possible. Thus whenever we
|
|
|
|
* have source data, we copy all of it to the destination buffer.
|
|
|
|
* The inner loops that copy data are nested into outer loops that
|
|
|
|
* make sure that the destination area is completley filled.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* jump right into the loops since we already have source data */
|
|
|
|
goto start;
|
|
|
|
|
|
|
|
while (i < surround->w)
|
2006-04-12 20:49:29 +08:00
|
|
|
{
|
2007-03-09 00:38:53 +08:00
|
|
|
dest = surround->buf + i * surround->bpp;
|
2007-03-07 07:32:25 +08:00
|
|
|
|
2007-03-09 00:38:53 +08:00
|
|
|
for (j = 0; j < surround->h;)
|
|
|
|
{
|
|
|
|
gint rows;
|
2007-03-07 07:32:25 +08:00
|
|
|
|
2007-03-09 00:38:53 +08:00
|
|
|
src = pixel_surround_get_data (surround,
|
|
|
|
x + i, y + j, &w, &h, rowstride);
|
2007-03-07 07:32:25 +08:00
|
|
|
|
2007-03-09 00:38:53 +08:00
|
|
|
start:
|
2007-03-09 17:19:57 +08:00
|
|
|
|
2007-03-09 00:38:53 +08:00
|
|
|
w = MIN (w, surround->w - i);
|
|
|
|
h = MIN (h, surround->h - j);
|
|
|
|
|
|
|
|
rows = h;
|
2007-03-09 17:19:57 +08:00
|
|
|
|
2007-03-09 00:38:53 +08:00
|
|
|
while (rows--)
|
|
|
|
{
|
2007-03-09 18:18:25 +08:00
|
|
|
memcpy (dest, src, w * surround->bpp);
|
2007-03-07 07:32:25 +08:00
|
|
|
|
2007-03-09 00:38:53 +08:00
|
|
|
src += *rowstride;
|
|
|
|
dest += surround->rowstride;
|
|
|
|
}
|
|
|
|
|
|
|
|
j += h;
|
2007-03-09 17:53:21 +08:00
|
|
|
inc = MIN (inc, w);
|
2007-03-08 01:57:10 +08:00
|
|
|
}
|
|
|
|
|
2007-03-09 17:53:21 +08:00
|
|
|
i += inc;
|
2006-04-12 20:49:29 +08:00
|
|
|
}
|
2001-03-31 22:10:22 +08:00
|
|
|
}
|
2005-09-04 03:03:53 +08:00
|
|
|
|
2007-03-08 01:57:10 +08:00
|
|
|
*rowstride = surround->rowstride;
|
2001-03-31 22:10:22 +08:00
|
|
|
|
2007-02-28 05:55:52 +08:00
|
|
|
return surround->buf;
|
2001-03-31 22:10:22 +08:00
|
|
|
}
|
|
|
|
|
2007-02-24 00:19:42 +08:00
|
|
|
/**
|
|
|
|
* pixel_surround_release:
|
|
|
|
* @surround: #PixelSurround
|
|
|
|
*
|
|
|
|
* Unlocks pixels locked by @surround. See pixel_surround_lock().
|
|
|
|
*/
|
2001-03-31 22:10:22 +08:00
|
|
|
void
|
2007-02-24 00:19:42 +08:00
|
|
|
pixel_surround_release (PixelSurround *surround)
|
2001-03-31 22:10:22 +08:00
|
|
|
{
|
2007-02-24 00:19:42 +08:00
|
|
|
if (surround->tile)
|
2001-03-31 22:10:22 +08:00
|
|
|
{
|
2007-02-24 00:19:42 +08:00
|
|
|
tile_release (surround->tile, FALSE);
|
|
|
|
surround->tile = NULL;
|
2001-03-31 22:10:22 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-02-24 00:19:42 +08:00
|
|
|
/**
|
|
|
|
* pixel_surround_destroy:
|
|
|
|
* @surround: #PixelSurround
|
|
|
|
*
|
|
|
|
* Unlocks pixels and frees any resources allocated for @surround. You
|
|
|
|
* must not use @surround any longer after calling this function.
|
|
|
|
*/
|
2001-03-31 22:10:22 +08:00
|
|
|
void
|
2007-02-24 00:19:42 +08:00
|
|
|
pixel_surround_destroy (PixelSurround *surround)
|
2001-03-31 22:10:22 +08:00
|
|
|
{
|
2007-02-24 00:19:42 +08:00
|
|
|
g_return_if_fail (surround != NULL);
|
2007-02-23 18:59:02 +08:00
|
|
|
|
2007-02-24 00:19:42 +08:00
|
|
|
pixel_surround_release (surround);
|
2007-03-07 07:32:25 +08:00
|
|
|
|
|
|
|
g_free (surround->buf);
|
|
|
|
g_free (surround->bg);
|
2007-05-23 02:29:33 +08:00
|
|
|
|
|
|
|
g_slice_free (PixelSurround, surround);
|
2001-03-31 22:10:22 +08:00
|
|
|
}
|
2008-08-26 15:24:55 +08:00
|
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
LEFT = 1 << 0,
|
|
|
|
RIGHT = 1 << 1,
|
|
|
|
TOP = 1 << 2,
|
|
|
|
BOTTOM = 1 << 3
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
pixel_surround_fill_row (PixelSurround *surround,
|
|
|
|
const guchar *src,
|
|
|
|
gint w)
|
|
|
|
{
|
|
|
|
guchar *dest = surround->bg;
|
|
|
|
gint bytes = MIN (w, surround->w) * surround->bpp;
|
|
|
|
gint rows = surround->h;
|
|
|
|
|
|
|
|
while (rows--)
|
|
|
|
{
|
|
|
|
memcpy (dest, src, bytes);
|
|
|
|
dest += surround->rowstride;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
pixel_surround_fill_col (PixelSurround *surround,
|
|
|
|
const guchar *src,
|
|
|
|
gint rowstride,
|
|
|
|
gint h)
|
|
|
|
{
|
|
|
|
guchar *dest = surround->bg;
|
|
|
|
gint cols = surround->w;
|
|
|
|
gint rows = MIN (h, surround->h);
|
|
|
|
|
|
|
|
while (cols--)
|
|
|
|
{
|
|
|
|
const guchar *s = src;
|
|
|
|
guchar *d = dest;
|
|
|
|
gint r = rows;
|
|
|
|
|
|
|
|
while (r--)
|
|
|
|
{
|
|
|
|
memcpy (d, s, surround->bpp);
|
|
|
|
|
|
|
|
s += rowstride;
|
|
|
|
d += surround->rowstride;
|
|
|
|
}
|
|
|
|
|
|
|
|
dest += surround->bpp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const guchar *
|
|
|
|
pixel_surround_get_data (PixelSurround *surround,
|
|
|
|
gint x,
|
|
|
|
gint y,
|
|
|
|
gint *w,
|
|
|
|
gint *h,
|
|
|
|
gint *rowstride)
|
|
|
|
{
|
|
|
|
/* do we still have a tile lock that we can use? */
|
|
|
|
if (surround->tile)
|
|
|
|
{
|
|
|
|
if (x < surround->tile_x || x >= surround->tile_x + surround->tile_w ||
|
|
|
|
y < surround->tile_y || y >= surround->tile_y + surround->tile_h)
|
|
|
|
{
|
|
|
|
tile_release (surround->tile, FALSE);
|
|
|
|
surround->tile = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if not, try to get one for the target pixel */
|
|
|
|
if (! surround->tile)
|
|
|
|
{
|
|
|
|
surround->tile = tile_manager_get_tile (surround->mgr, x, y, TRUE, FALSE);
|
|
|
|
|
|
|
|
if (surround->tile)
|
|
|
|
{
|
|
|
|
/* store offset and size of the locked tile */
|
|
|
|
surround->tile_x = x & ~(TILE_WIDTH - 1);
|
|
|
|
surround->tile_y = y & ~(TILE_HEIGHT - 1);
|
|
|
|
surround->tile_w = tile_ewidth (surround->tile);
|
|
|
|
surround->tile_h = tile_eheight (surround->tile);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (surround->tile)
|
|
|
|
{
|
|
|
|
*w = surround->tile_x + surround->tile_w - x;
|
|
|
|
*h = surround->tile_y + surround->tile_h - y;
|
|
|
|
|
|
|
|
*rowstride = surround->tile_w * surround->bpp;
|
|
|
|
|
|
|
|
return tile_data_pointer (surround->tile, x, y);
|
|
|
|
}
|
|
|
|
|
2008-08-26 15:41:53 +08:00
|
|
|
if (surround->mode == PIXEL_SURROUND_SMEAR)
|
2008-08-26 15:24:55 +08:00
|
|
|
{
|
2008-08-26 15:41:53 +08:00
|
|
|
const guchar *edata;
|
|
|
|
gint ex = x;
|
|
|
|
gint ey = y;
|
|
|
|
gint ew, eh;
|
|
|
|
gint estride;
|
|
|
|
gint ecode = 0;
|
|
|
|
|
|
|
|
if (ex < 0)
|
|
|
|
{
|
|
|
|
ex = 0;
|
|
|
|
ecode |= LEFT;
|
|
|
|
}
|
|
|
|
else if (ex > surround->xmax)
|
|
|
|
{
|
|
|
|
ex = surround->xmax;
|
|
|
|
ecode |= RIGHT;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ey < 0)
|
|
|
|
{
|
|
|
|
ey = 0;
|
|
|
|
ecode |= TOP;
|
|
|
|
}
|
|
|
|
else if (ey > surround->ymax)
|
|
|
|
{
|
|
|
|
ey = surround->ymax;
|
|
|
|
ecode |= BOTTOM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* call ourselves with corrected coordinates */
|
|
|
|
edata = pixel_surround_get_data (surround, ex, ey, &ew, &eh, &estride);
|
|
|
|
|
|
|
|
/* fill the virtual background tile */
|
|
|
|
switch (ecode)
|
|
|
|
{
|
|
|
|
case (TOP | LEFT):
|
|
|
|
case (TOP | RIGHT):
|
|
|
|
case (BOTTOM | LEFT):
|
|
|
|
case (BOTTOM | RIGHT):
|
|
|
|
pixel_surround_set_bg (surround, edata);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (TOP):
|
|
|
|
case (BOTTOM):
|
|
|
|
pixel_surround_fill_row (surround, edata, ew);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (LEFT):
|
|
|
|
case (RIGHT):
|
|
|
|
pixel_surround_fill_col (surround, edata, estride, eh);
|
|
|
|
break;
|
|
|
|
}
|
2008-08-26 15:24:55 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* return a pointer to the virtual background tile */
|
|
|
|
|
2008-08-26 15:41:53 +08:00
|
|
|
if (x < 0)
|
|
|
|
*w = MIN (- x, surround->w);
|
|
|
|
else
|
|
|
|
*w = surround->w;
|
|
|
|
|
|
|
|
if (y < 0)
|
|
|
|
*h = MIN (- y, surround->h);
|
|
|
|
else
|
|
|
|
*h = surround->h;
|
|
|
|
|
2008-08-26 15:24:55 +08:00
|
|
|
*rowstride = surround->rowstride;
|
|
|
|
|
|
|
|
return surround->bg;
|
|
|
|
}
|