summaryrefslogtreecommitdiffstats
path: root/libgimp/gimppixelrgn.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-27 16:23:22 +0000
commite42129241681dde7adae7d20697e7b421682fbb4 (patch)
treeaf1fe815a5e639e68e59fabd8395ec69458b3e5e /libgimp/gimppixelrgn.c
parentInitial commit. (diff)
downloadgimp-upstream.tar.xz
gimp-upstream.zip
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libgimp/gimppixelrgn.c')
-rw-r--r--libgimp/gimppixelrgn.c965
1 files changed, 965 insertions, 0 deletions
diff --git a/libgimp/gimppixelrgn.c b/libgimp/gimppixelrgn.c
new file mode 100644
index 0000000..c553089
--- /dev/null
+++ b/libgimp/gimppixelrgn.c
@@ -0,0 +1,965 @@
+/* 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 3 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, see
+ * <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <stdarg.h>
+
+#define GIMP_DISABLE_DEPRECATION_WARNINGS
+
+#include "gimp.h"
+
+
+/**
+ * SECTION: gimppixelrgn
+ * @title: gimppixelrgn
+ * @short_description: Functions for operating on pixel regions.
+ *
+ * Functions for operating on pixel regions. These functions provide
+ * fast ways of accessing and modifying portions of a drawable.
+ **/
+
+
+#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 = 0;
+ 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);
+
+ 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;
+ const 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)
+{
+ gint end;
+
+ 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)
+ {
+ GimpTile *tile;
+ const guchar *tile_data;
+ gint inc, min;
+ gint boundary;
+
+ 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));
+
+ min = MIN (end, boundary);
+ inc = tile->bpp * (min - x);
+
+ memcpy (buf, tile_data, inc);
+
+ x = min;
+ buf += inc;
+
+ 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)
+{
+ gint end;
+
+ 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)
+ {
+ GimpTile *tile;
+ const guchar *tile_data;
+ gint inc;
+ gint boundary;
+ gint b;
+
+ 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)
+{
+ gulong bufstride;
+ gint xstart, ystart;
+ gint xend, yend;
+ gint xboundary;
+ gint yboundary;
+ gint xstep, ystep;
+ gint ty, bpp;
+
+ 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)
+ {
+ GimpTile *tile;
+
+ 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++)
+ {
+ const guchar *src;
+ guchar *dest;
+
+ src = (tile->data +
+ tile->bpp * (tile->ewidth * (ty % TILE_HEIGHT) + (x % TILE_WIDTH)));
+ dest = buf + bufstride * (ty - ystart) + bpp * (x - xstart);
+
+ memcpy (dest, src, (xboundary - x) * bpp);
+ }
+
+ 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;
+
+ 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));
+
+ min = MIN (end, boundary);
+ inc = tile->bpp * (min - x);
+
+ memcpy (tile_data, buf, inc);
+
+ x = min;
+ buf += inc;
+
+ 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)
+{
+ gint end;
+
+ 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)
+ {
+ GimpTile *tile;
+ guchar *tile_data;
+ gint inc;
+ gint boundary;
+
+ 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++)
+ {
+ gint b;
+
+ 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)
+{
+ gulong bufstride;
+ gint xstart, ystart;
+ gint xend, yend;
+ gint xboundary;
+ gint yboundary;
+ gint xstep, ystep;
+ gint ty, bpp;
+
+ 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)
+ {
+ GimpTile *tile;
+
+ 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++)
+ {
+ const guchar *src;
+ guchar *dest;
+
+ src = buf + bufstride * (ty - ystart) + bpp * (x - xstart);
+ dest = tile->data + tile->bpp * (tile->ewidth *
+ (ty % TILE_HEIGHT) + (x % TILE_WIDTH));
+
+ memcpy (dest, src, (xboundary - x) * bpp);
+ }
+
+ 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_slice_new0 (GimpPixelRgnIterator);
+
+ found = FALSE;
+ while (nrgns --)
+ {
+ GimpPixelRgn *pr = prs[nrgns];
+ GimpPixelRgnHolder *prh = g_slice_new0 (GimpPixelRgnHolder);
+
+ 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.
+ * @...: @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;
+ gint n;
+ va_list ap;
+
+ g_return_val_if_fail (nrgns > 0, NULL);
+
+ prs = g_newa (GimpPixelRgn *, nrgns);
+
+ va_start (ap, nrgns);
+
+ for (n = nrgns; n--; )
+ prs[n] = va_arg (ap, GimpPixelRgn *);
+
+ va_end (ap);
+
+ return gimp_pixel_rgns_register2 (nrgns, prs);
+}
+
+/**
+ * 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_slice_free (GimpPixelRgnHolder, list->data);
+
+ g_slist_free (pri->pixel_regions);
+ g_slice_free (GimpPixelRgnIterator, 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;
+}