gimp/libgimp/gimppixelrgn.c

997 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 (pr->data != NULL)
pr->data += ((y - pr->y) * pr->rowstride +
(x - pr->x) * pr->bpp);
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)
{
GimpPixelRgn *pr;
GimpPixelRgnHolder *prh;
GimpPixelRgnIterator *pri;
gboolean found;
g_return_val_if_fail (nrgns > 0, NULL);
g_return_val_if_fail (prs != NULL, NULL);
pri = g_new (GimpPixelRgnIterator, 1);
pri->pixel_regions = NULL;
pri->process_count = 0;
found = FALSE;
while (nrgns --)
{
pr = prs[nrgns];
prh = g_new (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->starty) * prh->pr->rowstride +
(prh->pr->x - prh->startx) * prh->pr->bpp);
}
prh->pr->w = pri->portion_width;
prh->pr->h = pri->portion_height;
}