gimp/libgimp/gimppixelrgn.c
Michael Natterer 09454fb2a4 some general cleanup.
2005-09-03  Michael Natterer  <mitch@gimp.org>

	* app/base/pixel-region.[ch]: some general cleanup.

	(pixel_region_init_temp_buf)
	(pixel_region_init_data): new functions which initialize pixel
	regions on TempBufs and on raw contiguous arrays of pixel data.

	(pixel_region_configure): fixed a bug that has probably been there
	forever: when processing contiguous (non-tiled) data, interpret
	the original x and y coordinates of the region as offsets into
	the data. Before this fix, the initial x and y were simply ignored
	(by using them in a broken way), thus always forcing the upper
	left corner of the region being the beginning of the passed data.

	Lots of code was working around this problem by setting the
	pixel_region's data pointer to the proper starting pixel of the
	region in the middle the buffer.

	* libgimp/gimppixelrgn.c: some general cleanup.

	(gimp_pixel_rgn_configure): same fix as above. Fortunately, nobody
	seems to know that libgimp pixel regions can be used on arrays of
	data, just as core ones. Only two plug-ins were using this
	feature, and they are antique and written by spencer and federico,
	respectively. They both don't use offsets into the buffers and are
	not affected by this change. It's highly unlikely that anybody out
	there knows/uses this feature, so it can IMHO be safely changed.

	* app/base/temp-buf.c
	* app/core/gimpbuffer.c
	* app/core/gimpdrawable-combine.c
	* app/core/gimpdrawable-preview.c
	* app/core/gimpimage-preview.c
	* app/core/gimplayer.c
	* app/paint/gimpbrushcore.c
	* app/paint/gimpclone.c
	* app/paint/gimpconvolve.c
	* app/paint/gimpdodgeburn.c
	* app/paint/gimppaintcore.c
	* app/paint/gimpsmudge.c
	* app/tools/gimpiscissorstool.c
	* app/tools/gimppainttool.c: use the pixel_region_init_foo()
	functions instead of initializing regions of TempBufs and raw data
	manually. Removed lots of workarounds for the broken offset
	handling. The changed places of code are much more readable now.
2005-09-03 17:16:58 +00:00

1004 lines
30 KiB
C

/* LIBGIMP - The GIMP Library
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
*
* gimppixelrgn.c
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser 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 Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
/* Experimental: comment-out the following #define if a memcpy() call is
slower than compiler-optimized memory copies for transfers of approx.
64-256 bytes.
FYI this #define is a win on Linux486/libc5. Unbenchmarked on other
architectures. --adam
*/
#define MEMCPY_IS_NICE
#ifdef MEMCPY_IS_NICE
#include <string.h>
#endif
#include <stdarg.h>
#include "gimp.h"
#define TILE_WIDTH gimp_tile_width()
#define TILE_HEIGHT gimp_tile_height()
typedef struct _GimpPixelRgnHolder GimpPixelRgnHolder;
typedef struct _GimpPixelRgnIterator GimpPixelRgnIterator;
struct _GimpPixelRgnHolder
{
GimpPixelRgn *pr;
guchar *original_data;
gint startx;
gint starty;
gint count;
};
struct _GimpPixelRgnIterator
{
GSList *pixel_regions;
gint region_width;
gint region_height;
gint portion_width;
gint portion_height;
gint process_count;
};
static gint gimp_get_portion_width (GimpPixelRgnIterator *pri);
static gint gimp_get_portion_height (GimpPixelRgnIterator *pri);
static gpointer gimp_pixel_rgns_configure (GimpPixelRgnIterator *pri);
static void gimp_pixel_rgn_configure (GimpPixelRgnHolder *prh,
GimpPixelRgnIterator *pri);
/**
* gimp_pixel_rgn_init:
* @pr: a pointer to a #GimpPixelRgn variable.
* @drawable: the #GimpDrawable the new region will be attached to.
* @x: the x coordinate of the top-left pixel of the region in the
* @drawable.
* @y: the y coordinate of the top-left pixel of the region in the
* @drawable.
* @width: the width of the region.
* @height: the height of the region.
* @dirty: a #gboolean indicating whether the @drawable should be marked
* as "dirty".
* @shadow: a #gboolean indicating whether the region is attached to the
* shadow tiles or the real @drawable tiles.
*
* Initialize the pixel region pointed by @pr with the specified parameters.
*
* The @dirty and @shadow flags can be used as follows:
*
* - @dirty = FALSE, @shadow = FALSE: the region will be used to read
* the actual drawable datas. This
* is useful for save plug-ins or for
* filters.
*
* - @dirty = FALSE, @shadow = TRUE: the region will be used to read the
* shadow tiles. This is used in
* some filter plug-ins which operate
* in two passes such as gaussian
* blur. The first pass reads the
* actual drawable data and writes to
* the shadow tiles, and the second
* one reads from and writes to the
* shadow tiles.
*
* - @dirty = TRUE, @shadow = TRUE: the region will be used to write to
* the shadow tiles. It is common
* practice to write to the shadow
* tiles and then use
* #gimp_drawable_merge_shadow () to
* merge the changes from the shadow
* tiles using the current selection
* as a mask.
*
* - @dirty = TRUE, @shadow = FALSE: the region will be used to directly
* change the drawable content. Don't
* do this, since this could prevent
* the Undo-System from working as
* expected.
**/
void
gimp_pixel_rgn_init (GimpPixelRgn *pr,
GimpDrawable *drawable,
gint x,
gint y,
gint width,
gint height,
gboolean dirty,
gboolean shadow)
{
g_return_if_fail (pr != NULL);
g_return_if_fail (drawable != NULL);
g_return_if_fail (x >= 0 && x + width <= drawable->width);
g_return_if_fail (y >= 0 && y + height <= drawable->height);
pr->data = NULL;
pr->drawable = drawable;
pr->bpp = drawable->bpp;
pr->rowstride = pr->bpp * TILE_WIDTH;
pr->x = x;
pr->y = y;
pr->w = width;
pr->h = height;
pr->dirty = dirty;
pr->shadow = shadow;
}
/**
* gimp_pixel_rgn_resize:
* @pr: a pointer to a previously initialized #GimpPixelRgn.
* @x: the x coordinate of the new position of the region's
* top-left corner.
* @y: the y coordinate of the new position of the region's
* top-left corner.
* @width: the new width of the region.
* @height: the new height of the region.
*
* Change the position and size of a previously initialized pixel region.
**/
void
gimp_pixel_rgn_resize (GimpPixelRgn *pr,
gint x,
gint y,
gint width,
gint height)
{
g_return_if_fail (pr != NULL && pr->drawable != NULL);
g_return_if_fail (x >= 0 && x + width <= pr->drawable->width);
g_return_if_fail (y >= 0 && y + height <= pr->drawable->height);
/* If the data is non-null, data is contiguous--need to advance */
if (pr->data != NULL)
pr->data += ((y - pr->y) * pr->rowstride +
(x - pr->x) * pr->bpp);
/* update sizes for both contiguous and tiled regions */
pr->x = x;
pr->y = y;
pr->w = width;
pr->h = height;
}
/**
* gimp_pixel_rgn_get_pixel:
* @pr: a pointer to a previously initialized #GimpPixelRgn.
* @buf: a pointer to an array of #guchar
* @x: the x coordinate of the wanted pixel (relative to the drawable)
* @y: the y coordinate of the wanted pixel (relative to the drawable)
*
* Fill the buffer pointed by @buf with the value of the pixel at (@x, @y)
* in the region @pr. @buf should be large enough to hold the pixel value
* (1 #guchar for an indexed or grayscale drawable, 2 #guchar for
* indexed with alpha or grayscale with alpha drawable, 3 #guchar for
* rgb drawable and 4 #guchar for rgb with alpha drawable.
**/
void
gimp_pixel_rgn_get_pixel (GimpPixelRgn *pr,
guchar *buf,
gint x,
gint y)
{
GimpTile *tile;
guchar *tile_data;
gint b;
g_return_if_fail (pr != NULL && pr->drawable != NULL);
g_return_if_fail (x >= 0 && x < pr->drawable->width);
g_return_if_fail (y >= 0 && y < pr->drawable->height);
tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
gimp_tile_ref (tile);
tile_data = tile->data + tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH));
for (b = 0; b < tile->bpp; b++)
*buf++ = *tile_data++;
gimp_tile_unref (tile, FALSE);
}
/**
* gimp_pixel_rgn_get_row:
* @pr: a pointer to a previously initialized #GimpPixelRgn.
* @buf: a pointer to an array of #guchar
* @x: the x coordinate of the first pixel (relative to the drawable).
* @y: the y coordinate of the first pixel (relative to the drawable).
* @width: the number of pixels to get.
*
* Get several pixels of a region in a row. This function fills the buffer
* @buf with the values of the pixels from (@x, @y) to (@x+@width-1, @y).
* @buf should be large enough to hold all these values.
**/
void
gimp_pixel_rgn_get_row (GimpPixelRgn *pr,
guchar *buf,
gint x,
gint y,
gint width)
{
GimpTile *tile;
guchar *tile_data;
gint bpp, inc, min;
gint end;
gint boundary;
#ifndef MEMCPY_IS_NICE
gint b;
#endif
g_return_if_fail (pr != NULL && pr->drawable != NULL);
g_return_if_fail (buf != NULL);
g_return_if_fail (x >= 0 && x + width <= pr->drawable->width);
g_return_if_fail (y >= 0 && y < pr->drawable->height);
g_return_if_fail (width >= 0);
end = x + width;
while (x < end)
{
tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
gimp_tile_ref (tile);
tile_data = tile->data + (int)tile->bpp * (int)(tile->ewidth * (int)(y % TILE_HEIGHT) + (x % TILE_WIDTH));
boundary = x + (tile->ewidth - (x % TILE_WIDTH));
bpp = tile->bpp;
#ifdef MEMCPY_IS_NICE
memcpy ((void *)buf,
(const void *)tile_data,
inc = (bpp *
( (min = MIN (end, boundary)) -x) ) );
x = min;
buf += inc;
#else
for ( ; x < end && x < boundary; x++)
{
for (b = 0; b < tile->bpp; b++)
*buf++ = tile_data[b];
tile_data += bpp;
}
#endif /* MEMCPY_IS_NICE */
gimp_tile_unref (tile, FALSE);
}
}
/**
* gimp_pixel_rgn_get_col:
* @pr: a pointer to a previously initialized #GimpPixelRgn.
* @buf: a pointer to an array of #guchar
* @x: the x coordinate of the first pixel (relative to the drawable).
* @y: the y coordinate of the first pixel (relative to the drawable).
* @height: the number of pixels to get.
*
* Get several pixels of a region's column. This function fills the buffer
* @buf with the values of the pixels from (@x, @y) to (@x, @y+@height-1).
* @buf should be large enough to hold all these values.
*
**/
void
gimp_pixel_rgn_get_col (GimpPixelRgn *pr,
guchar *buf,
gint x,
gint y,
gint height)
{
GimpTile *tile;
guchar *tile_data;
gint inc;
gint end;
gint boundary;
gint b;
g_return_if_fail (pr != NULL && pr->drawable != NULL);
g_return_if_fail (buf != NULL);
g_return_if_fail (x >= 0 && x < pr->drawable->width);
g_return_if_fail (y >= 0 && y + height <= pr->drawable->height);
g_return_if_fail (height >= 0);
end = y + height;
while (y < end)
{
tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
gimp_tile_ref (tile);
tile_data = tile->data + tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH));
boundary = y + (tile->eheight - (y % TILE_HEIGHT));
inc = tile->bpp * tile->ewidth;
for ( ; y < end && y < boundary; y++)
{
for (b = 0; b < tile->bpp; b++)
*buf++ = tile_data[b];
tile_data += inc;
}
gimp_tile_unref (tile, FALSE);
}
}
/**
* gimp_pixel_rgn_get_rect:
* @pr: a pointer to a previously initialized #GimpPixelRgn.
* @buf: a pointer to an array of #guchar
* @x: the x coordinate of the first pixel (relative to the drawable).
* @y: the y coordinate of the first pixel (relative to the drawable).
* @width: the width of the rectangle.
* @height: the height of the rectangle.
*
* Get all the pixel values from the rectangle defined by @x, @y, @width and
* @height. This function fills the buffer @buf with the values of the pixels
* from (@x, @y) to (@x+@width-1, @y+@height-1).
* @buf should be large enough to hold all these values (@width*@height*bpp).
**/
void
gimp_pixel_rgn_get_rect (GimpPixelRgn *pr,
guchar *buf,
gint x,
gint y,
gint width,
gint height)
{
GimpTile *tile;
guchar *src;
guchar *dest;
gulong bufstride;
gint xstart, ystart;
gint xend, yend;
gint xboundary;
gint yboundary;
gint xstep, ystep;
gint ty, bpp;
#ifndef MEMCPY_IS_NICE
gint b, tx;
#endif
g_return_if_fail (pr != NULL && pr->drawable != NULL);
g_return_if_fail (buf != NULL);
g_return_if_fail (x >= 0 && x + width <= pr->drawable->width);
g_return_if_fail (y >= 0 && y + height <= pr->drawable->height);
g_return_if_fail (width >= 0);
g_return_if_fail (height >= 0);
bpp = pr->bpp;
bufstride = bpp * width;
xstart = x;
ystart = y;
xend = x + width;
yend = y + height;
ystep = 0;
while (y < yend)
{
x = xstart;
while (x < xend)
{
tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
gimp_tile_ref (tile);
xstep = tile->ewidth - (x % TILE_WIDTH);
ystep = tile->eheight - (y % TILE_HEIGHT);
xboundary = x + xstep;
yboundary = y + ystep;
xboundary = MIN (xboundary, xend);
yboundary = MIN (yboundary, yend);
for (ty = y; ty < yboundary; ty++)
{
src = tile->data + tile->bpp * (tile->ewidth * (ty % TILE_HEIGHT) + (x % TILE_WIDTH));
dest = buf + bufstride * (ty - ystart) + bpp * (x - xstart);
#ifdef MEMCPY_IS_NICE
memcpy ((void *)dest, (const void *)src, (xboundary-x)*bpp);
#else
for (tx = x; tx < xboundary; tx++)
{
for (b = 0; b < bpp; b++)
*dest++ = *src++;
}
#endif /* MEMCPY_IS_NICE */
}
gimp_tile_unref (tile, FALSE);
x += xstep;
}
y += ystep;
}
}
/**
* gimp_pixel_rgn_set_pixel:
* @pr: a pointer to a previously initialized #GimpPixelRgn.
* @buf: a pointer to an array of #guchar.
* @x: the x coordinate of the pixel (relative to the drawable).
* @y: the y coordinate of the pixel (relative to the drawable).
*
* Set the pixel at (@x, @y) to the values from @buf.
**/
void
gimp_pixel_rgn_set_pixel (GimpPixelRgn *pr,
const guchar *buf,
gint x,
gint y)
{
GimpTile *tile;
guchar *tile_data;
gint b;
g_return_if_fail (pr != NULL && pr->drawable != NULL);
g_return_if_fail (buf != NULL);
g_return_if_fail (x >= 0 && x < pr->drawable->width);
g_return_if_fail (y >= 0 && y < pr->drawable->height);
tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
gimp_tile_ref (tile);
tile_data = tile->data + tile->bpp * (tile->ewidth *
(y % TILE_HEIGHT) + (x % TILE_WIDTH));
for (b = 0; b < tile->bpp; b++)
*tile_data++ = *buf++;
gimp_tile_unref (tile, TRUE);
}
/**
* gimp_pixel_rgn_set_row:
* @pr: a pointer to a previously initialized #GimpPixelRgn.
* @buf: a pointer to an array of #guchar
* @x: the x coordinate of the first pixel (relative to the drawable).
* @y: the y coordinate of the first pixel (relative to the drawable).
* @width: the number of pixels to set.
*
* Set several pixels of a region in a row. This function draws the pixels
* from (@x, @y) to (@x+@width-1, @y) using the values of the buffer @buf.
* @buf should be large enough to hold all these values.
**/
void
gimp_pixel_rgn_set_row (GimpPixelRgn *pr,
const guchar *buf,
gint x,
gint y,
gint width)
{
GimpTile *tile;
guchar *tile_data;
gint inc, min;
gint end;
gint boundary;
#ifndef MEMCPY_IS_NICE
gint b;
#endif
g_return_if_fail (pr != NULL && pr->drawable != NULL);
g_return_if_fail (buf != NULL);
g_return_if_fail (x >= 0 && x + width <= pr->drawable->width);
g_return_if_fail (y >= 0 && y < pr->drawable->height);
g_return_if_fail (width >= 0);
end = x + width;
while (x < end)
{
tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
gimp_tile_ref (tile);
tile_data = tile->data + tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH));
boundary = x + (tile->ewidth - (x % TILE_WIDTH));
#ifdef MEMCPY_IS_NICE
memcpy ((void *)tile_data,
(const void *)buf,
inc = (tile->bpp *
( (min = MIN (end, boundary)) -x) ) );
x = min;
buf += inc;
#else
for ( ; x < end && x < boundary; x++)
{
for (b = 0; b < tile->bpp; b++)
*tile_data++ = *buf++;
}
#endif /* MEMCPY_IS_NICE */
gimp_tile_unref (tile, TRUE);
}
}
/**
* gimp_pixel_rgn_set_col:
* @pr: a pointer to a previously initialized #GimpPixelRgn.
* @buf: a pointer to an array of #guchar
* @x: the x coordinate of the first pixel (relative to the drawable).
* @y: the y coordinate of the first pixel (relative to the drawable).
* @height: the number of pixels to set.
*
* Set several pixels of a region's column. This function draws the pixels
* from (@x, @y) to (@x, @y+@height-1) using the values from the buffer @buf.
* @buf should be large enough to hold all these values.
**/
void
gimp_pixel_rgn_set_col (GimpPixelRgn *pr,
const guchar *buf,
gint x,
gint y,
gint height)
{
GimpTile *tile;
guchar *tile_data;
gint inc;
gint end;
gint boundary;
gint b;
g_return_if_fail (pr != NULL && pr->drawable != NULL);
g_return_if_fail (buf != NULL);
g_return_if_fail (x >= 0 && x < pr->drawable->width);
g_return_if_fail (y >= 0 && y + height <= pr->drawable->height);
g_return_if_fail (height >= 0);
end = y + height;
while (y < end)
{
tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
gimp_tile_ref (tile);
tile_data = tile->data + tile->bpp * (tile->ewidth * (y % TILE_HEIGHT) + (x % TILE_WIDTH));
boundary = y + (tile->eheight - (y % TILE_HEIGHT));
inc = tile->bpp * tile->ewidth;
for ( ; y < end && y < boundary; y++)
{
for (b = 0; b < tile->bpp; b++)
tile_data[b] = *buf++;
tile_data += inc;
}
gimp_tile_unref (tile, TRUE);
}
}
/**
* gimp_pixel_rgn_set_rect:
* @pr: a pointer to a previously initialized #GimpPixelRgn.
* @buf: a pointer to an array of #guchar
* @x: the x coordinate of the first pixel (relative to the drawable).
* @y: the y coordinate of the first pixel (relative to the drawable).
* @width: the width of the rectangle.
* @height: the height of the rectangle.
*
* Set all the pixel of the rectangle defined by @x, @y, @width and
* @height. This function draws the rectangle from (@x, @y) to
* (@x+@width-1, @y+@height-1), using the pixel values from the buffer @buf.
* @buf should be large enough to hold all these values (@width*@height*bpp).
**/
void
gimp_pixel_rgn_set_rect (GimpPixelRgn *pr,
const guchar *buf,
gint x,
gint y,
gint width,
gint height)
{
GimpTile *tile;
const guchar *src;
guchar *dest;
gulong bufstride;
gint xstart, ystart;
gint xend, yend;
gint xboundary;
gint yboundary;
gint xstep, ystep;
gint ty, bpp;
#ifndef MEMCPY_IS_NICE
gint b, tx;
#endif
g_return_if_fail (pr != NULL && pr->drawable != NULL);
g_return_if_fail (buf != NULL);
g_return_if_fail (x >= 0 && x + width <= pr->drawable->width);
g_return_if_fail (y >= 0 && y + height <= pr->drawable->height);
g_return_if_fail (width >= 0);
g_return_if_fail (height >= 0);
bpp = pr->bpp;
bufstride = bpp * width;
xstart = x;
ystart = y;
xend = x + width;
yend = y + height;
ystep = 0;
while (y < yend)
{
x = xstart;
while (x < xend)
{
tile = gimp_drawable_get_tile2 (pr->drawable, pr->shadow, x, y);
gimp_tile_ref (tile);
xstep = tile->ewidth - (x % TILE_WIDTH);
ystep = tile->eheight - (y % TILE_HEIGHT);
xboundary = x + xstep;
yboundary = y + ystep;
xboundary = MIN (xboundary, xend);
yboundary = MIN (yboundary, yend);
for (ty = y; ty < yboundary; ty++)
{
src = buf + bufstride * (ty - ystart) + bpp * (x - xstart);
dest = tile->data + tile->bpp * (tile->ewidth *
(ty % TILE_HEIGHT) + (x % TILE_WIDTH));
#ifdef MEMCPY_IS_NICE
memcpy ((void *)dest, (const void *)src, (xboundary-x)*bpp);
#else
for (tx = x; tx < xboundary; tx++)
{
for (b = 0; b < bpp; b++)
*dest++ = *src++;
}
#endif /* MEMCPY_IS_NICE */
}
gimp_tile_unref (tile, TRUE);
x += xstep;
}
y += ystep;
}
}
/**
* gimp_pixel_rgns_register2:
* @nrgns: the number of regions to register.
* @prs: an array of @nrgns pointers to initialized #GimpPixelRgn.
*
* It takes a number of initialized regions of the same size and provides a
* pixel region iterator the iterator can be used to iterate over the
* registered pixel regions. While iterating the registered pixel regions will
* cover subsets of the original pixel regions, chosen for optimized access to
* the image data.
*
* Note that the given regions themselves are changed by this function, so
* they are resized to the first subsets.
*
* This function has to be used together with gimp_pixel_rgns_process in a loop.
*
* Returns: a #gpointer to a regions iterator.
**/
gpointer
gimp_pixel_rgns_register2 (gint nrgns,
GimpPixelRgn **prs)
{
GimpPixelRgnIterator *pri;
gboolean found;
g_return_val_if_fail (nrgns > 0, NULL);
g_return_val_if_fail (prs != NULL, NULL);
pri = g_new0 (GimpPixelRgnIterator, 1);
found = FALSE;
while (nrgns --)
{
GimpPixelRgnHolder *prh;
GimpPixelRgn *pr;
pr = prs[nrgns];
prh = g_new0 (GimpPixelRgnHolder, 1);
prh->pr = pr;
if (pr != NULL)
{
/* If there is a defined value for data, make sure tiles is NULL */
if (pr->data)
pr->drawable = NULL;
prh->original_data = pr->data;
prh->startx = pr->x;
prh->starty = pr->y;
prh->pr->process_count = 0;
if (!found)
{
found = TRUE;
pri->region_width = pr->w;
pri->region_height = pr->h;
}
}
/* Add the pixel Rgn holder to the list */
pri->pixel_regions = g_slist_prepend (pri->pixel_regions, prh);
}
return gimp_pixel_rgns_configure (pri);
}
/**
* gimp_pixel_rgns_register:
* @nrgns: the number of regions to register.
* @Varargs: @nrgns pointers to #GimpPixelRgn.
*
* This is the varargs version of #gimp_pixel_rgns_register2.
*
* Returns: a #gpointer to a regions iterator.
**/
gpointer
gimp_pixel_rgns_register (gint nrgns,
...)
{
GimpPixelRgn **prs;
gpointer pri;
gint n;
va_list ap;
g_return_val_if_fail (nrgns > 0, NULL);
prs = g_new (GimpPixelRgn *, nrgns);
va_start (ap, nrgns);
for (n = nrgns; n--; )
prs[n] = va_arg (ap, GimpPixelRgn *);
va_end (ap);
pri = gimp_pixel_rgns_register2 (nrgns, prs);
g_free (prs);
return pri;
}
/**
* gimp_pixel_rgns_process:
* @pri_ptr: a regions iterator returned by #gimp_pixel_rgns_register,
* #gimp_pixel_rgns_register2 or #gimp_pixel_rgns_process.
*
* This function update the regions registered previously with one of the
* #gimp_pixel_rgns_register* functions to their next tile.
*
* Returns: a #gpointer to a new regions iterator or #NULL if there isn't
* any tiles left.
**/
gpointer
gimp_pixel_rgns_process (gpointer pri_ptr)
{
GimpPixelRgnIterator *pri;
GSList *list;
g_return_val_if_fail (pri_ptr != NULL, NULL);
pri = (GimpPixelRgnIterator*) pri_ptr;
pri->process_count++;
/* Unref all referenced tiles and increment the offsets */
for (list = pri->pixel_regions; list; list = list->next)
{
GimpPixelRgnHolder *prh = list->data;
if ((prh->pr != NULL) && (prh->pr->process_count != pri->process_count))
{
/* This eliminates the possibility of incrementing the
* same region twice
*/
prh->pr->process_count++;
/* Unref the last referenced tile if the underlying region
* is a tile manager
*/
if (prh->pr->drawable)
{
GimpTile *tile = gimp_drawable_get_tile2 (prh->pr->drawable,
prh->pr->shadow,
prh->pr->x,
prh->pr->y);
gimp_tile_unref (tile, prh->pr->dirty);
}
prh->pr->x += pri->portion_width;
if ((prh->pr->x - prh->startx) >= pri->region_width)
{
prh->pr->x = prh->startx;
prh->pr->y += pri->portion_height;
}
}
}
return gimp_pixel_rgns_configure (pri);
}
static gint
gimp_get_portion_width (GimpPixelRgnIterator *pri)
{
GSList *list;
gint min_width = G_MAXINT;
gint width;
/* Find the minimum width to the next vertical tile (in the case of
* a tile manager) or to the end of the pixel region (in the case of
* no tile manager)
*/
for (list = pri->pixel_regions; list; list = list->next)
{
GimpPixelRgnHolder *prh = list->data;
if (prh->pr)
{
/* Check if we're past the point of no return */
if ((prh->pr->x - prh->startx) >= pri->region_width)
return 0;
if (prh->pr->drawable)
{
width = TILE_WIDTH - (prh->pr->x % TILE_WIDTH);
width = CLAMP (width,
0,
(pri->region_width - (prh->pr->x - prh->startx)));
}
else
{
width = (pri->region_width - (prh->pr->x - prh->startx));
}
if (width < min_width)
min_width = width;
}
}
return min_width;
}
static gint
gimp_get_portion_height (GimpPixelRgnIterator *pri)
{
GSList *list;
gint min_height = G_MAXINT;
gint height;
/* Find the minimum height to the next vertical tile (in the case of
* a tile manager) or to the end of the pixel region (in the case of
* no tile manager)
*/
for (list = pri->pixel_regions; list; list = list->next)
{
GimpPixelRgnHolder *prh = list->data;
if (prh->pr)
{
/* Check if we're past the point of no return */
if ((prh->pr->y - prh->starty) >= pri->region_height)
return 0;
if (prh->pr->drawable)
{
height = TILE_HEIGHT - (prh->pr->y % TILE_HEIGHT);
height = CLAMP (height,
0,
(pri->region_height - (prh->pr->y - prh->starty)));
}
else
{
height = (pri->region_height - (prh->pr->y - prh->starty));
}
if (height < min_height)
min_height = height;
}
}
return min_height;
}
static gpointer
gimp_pixel_rgns_configure (GimpPixelRgnIterator *pri)
{
GSList *list;
/* Determine the portion width and height */
pri->portion_width = gimp_get_portion_width (pri);
pri->portion_height = gimp_get_portion_height (pri);
if (pri->portion_width == 0 ||
pri->portion_height == 0)
{
/* free the pixel regions list */
for (list = pri->pixel_regions; list; list = list->next)
g_free (list->data);
g_slist_free (pri->pixel_regions);
g_free (pri);
return NULL;
}
pri->process_count++;
for (list = pri->pixel_regions; list; list = list->next)
{
GimpPixelRgnHolder *prh = list->data;
if ((prh->pr != NULL) && (prh->pr->process_count != pri->process_count))
{
prh->pr->process_count++;
gimp_pixel_rgn_configure (prh, pri);
}
}
return pri;
}
static void
gimp_pixel_rgn_configure (GimpPixelRgnHolder *prh,
GimpPixelRgnIterator *pri)
{
/* Configure the rowstride and data pointer for the pixel region
* based on the current offsets into the region and whether the
* region is represented by a tile manager or not
*/
if (prh->pr->drawable)
{
GimpTile *tile;
gint offx;
gint offy;
tile = gimp_drawable_get_tile2 (prh->pr->drawable,
prh->pr->shadow,
prh->pr->x,
prh->pr->y);
gimp_tile_ref (tile);
offx = prh->pr->x % TILE_WIDTH;
offy = prh->pr->y % TILE_HEIGHT;
prh->pr->rowstride = tile->ewidth * prh->pr->bpp;
prh->pr->data = (tile->data +
offy * prh->pr->rowstride + offx * prh->pr->bpp);
}
else
{
prh->pr->data = (prh->original_data +
prh->pr->y * prh->pr->rowstride +
prh->pr->x * prh->pr->bpp);
}
prh->pr->w = pri->portion_width;
prh->pr->h = pri->portion_height;
}