forked from openkylin/gimp
335 lines
8.6 KiB
C
335 lines
8.6 KiB
C
|
/* LIBGIMP - The GIMP Library
|
||
|
* Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball
|
||
|
*
|
||
|
* gimppixelfetcher.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"
|
||
|
|
||
|
#define GIMP_DISABLE_DEPRECATION_WARNINGS
|
||
|
|
||
|
#include "gimp.h"
|
||
|
|
||
|
|
||
|
/**
|
||
|
* SECTION: gimppixelfetcher
|
||
|
* @title: gimppixelfetcher
|
||
|
* @short_description: Functions for operating on pixel regions.
|
||
|
*
|
||
|
* These functions provide neighbourhood-based algorithms which get
|
||
|
* dramatically slower on region boundaries, to the point where a
|
||
|
* special treatment for neighbourhoods which are completely inside a
|
||
|
* tile is called for. It hides the special treatment of tile borders,
|
||
|
* making plug-in code more readable and shorter.
|
||
|
**/
|
||
|
|
||
|
|
||
|
struct _GimpPixelFetcher
|
||
|
{
|
||
|
gint col, row;
|
||
|
gint img_width;
|
||
|
gint img_height;
|
||
|
gint sel_x1, sel_y1, sel_x2, sel_y2;
|
||
|
gint img_bpp;
|
||
|
gint tile_width, tile_height;
|
||
|
guchar bg_color[4];
|
||
|
GimpPixelFetcherEdgeMode mode;
|
||
|
GimpDrawable *drawable;
|
||
|
GimpTile *tile;
|
||
|
gboolean tile_dirty;
|
||
|
gboolean shadow;
|
||
|
};
|
||
|
|
||
|
|
||
|
/* local function prototypes */
|
||
|
|
||
|
static guchar * gimp_pixel_fetcher_provide_tile (GimpPixelFetcher *pf,
|
||
|
gint x,
|
||
|
gint y);
|
||
|
|
||
|
|
||
|
/* public functions */
|
||
|
|
||
|
/**
|
||
|
* gimp_pixel_fetcher_new:
|
||
|
* @drawable: the #GimpDrawable the new region will be attached to.
|
||
|
* @shadow: a #gboolean indicating whether the region is attached to
|
||
|
* the shadow tiles or the real @drawable tiles.
|
||
|
*
|
||
|
* Initialize a pixel region from the drawable.
|
||
|
*
|
||
|
* Return value: a pointer to a #GimpPixelRgn structure (or NULL).
|
||
|
**/
|
||
|
GimpPixelFetcher *
|
||
|
gimp_pixel_fetcher_new (GimpDrawable *drawable,
|
||
|
gboolean shadow)
|
||
|
{
|
||
|
GimpPixelFetcher *pf;
|
||
|
gint width;
|
||
|
gint height;
|
||
|
gint bpp;
|
||
|
|
||
|
g_return_val_if_fail (drawable != NULL, NULL);
|
||
|
|
||
|
width = gimp_drawable_width (drawable->drawable_id);
|
||
|
height = gimp_drawable_height (drawable->drawable_id);
|
||
|
bpp = gimp_drawable_bpp (drawable->drawable_id);
|
||
|
|
||
|
g_return_val_if_fail (width > 0 && height > 0 && bpp > 0, NULL);
|
||
|
|
||
|
pf = g_slice_new0 (GimpPixelFetcher);
|
||
|
|
||
|
gimp_drawable_mask_bounds (drawable->drawable_id,
|
||
|
&pf->sel_x1, &pf->sel_y1,
|
||
|
&pf->sel_x2, &pf->sel_y2);
|
||
|
|
||
|
pf->col = -1;
|
||
|
pf->row = -1;
|
||
|
pf->img_width = width;
|
||
|
pf->img_height = height;
|
||
|
pf->img_bpp = bpp;
|
||
|
pf->tile_width = gimp_tile_width ();
|
||
|
pf->tile_height = gimp_tile_height ();
|
||
|
pf->bg_color[0] = 0;
|
||
|
pf->bg_color[1] = 0;
|
||
|
pf->bg_color[2] = 0;
|
||
|
pf->bg_color[3] = 255;
|
||
|
pf->mode = GIMP_PIXEL_FETCHER_EDGE_NONE;
|
||
|
pf->drawable = drawable;
|
||
|
pf->tile = NULL;
|
||
|
pf->tile_dirty = FALSE;
|
||
|
pf->shadow = shadow;
|
||
|
|
||
|
return pf;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* gimp_pixel_fetcher_destroy:
|
||
|
* @pf: a pointer to a previously initialized #GimpPixelFetcher.
|
||
|
*
|
||
|
* Close a previously initialized pixel region.
|
||
|
**/
|
||
|
void
|
||
|
gimp_pixel_fetcher_destroy (GimpPixelFetcher *pf)
|
||
|
{
|
||
|
g_return_if_fail (pf != NULL);
|
||
|
|
||
|
if (pf->tile)
|
||
|
gimp_tile_unref (pf->tile, pf->tile_dirty);
|
||
|
|
||
|
g_slice_free (GimpPixelFetcher, pf);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* gimp_pixel_fetcher_set_edge_mode:
|
||
|
* @pf: a pointer to a previously initialized #GimpPixelFetcher.
|
||
|
* @mode: the new edge mode from #GimpPixelFetcherEdgeMode.
|
||
|
*
|
||
|
* Change the edge mode of a previously initialized pixel region.
|
||
|
**/
|
||
|
void
|
||
|
gimp_pixel_fetcher_set_edge_mode (GimpPixelFetcher *pf,
|
||
|
GimpPixelFetcherEdgeMode mode)
|
||
|
{
|
||
|
g_return_if_fail (pf != NULL);
|
||
|
|
||
|
pf->mode = mode;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* gimp_pixel_fetcher_set_bg_color:
|
||
|
* @pf: a pointer to a previously initialized #GimpPixelFetcher.
|
||
|
* @color: the color to be used as bg color.
|
||
|
*
|
||
|
* Change the background color of a previously initialized pixel region.
|
||
|
**/
|
||
|
void
|
||
|
gimp_pixel_fetcher_set_bg_color (GimpPixelFetcher *pf,
|
||
|
const GimpRGB *color)
|
||
|
{
|
||
|
g_return_if_fail (pf != NULL);
|
||
|
g_return_if_fail (color != NULL);
|
||
|
|
||
|
switch (pf->img_bpp)
|
||
|
{
|
||
|
case 2: pf->bg_color[1] = 255;
|
||
|
case 1:
|
||
|
pf->bg_color[0] = gimp_rgb_luminance_uchar (color);
|
||
|
break;
|
||
|
|
||
|
case 4: pf->bg_color[3] = 255;
|
||
|
case 3:
|
||
|
gimp_rgb_get_uchar (color,
|
||
|
pf->bg_color, pf->bg_color + 1, pf->bg_color + 2);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* gimp_pixel_fetcher_get_pixel:
|
||
|
* @pf: a pointer to a previously initialized #GimpPixelFetcher.
|
||
|
* @x: the x coordinate of the pixel to get.
|
||
|
* @y: the y coordinate of the pixel to get.
|
||
|
* @pixel: the memory location where to return the pixel.
|
||
|
*
|
||
|
* Get a pixel from the pixel region.
|
||
|
**/
|
||
|
void
|
||
|
gimp_pixel_fetcher_get_pixel (GimpPixelFetcher *pf,
|
||
|
gint x,
|
||
|
gint y,
|
||
|
guchar *pixel)
|
||
|
{
|
||
|
guchar *p;
|
||
|
gint i;
|
||
|
|
||
|
g_return_if_fail (pf != NULL);
|
||
|
g_return_if_fail (pixel != NULL);
|
||
|
|
||
|
if (pf->mode == GIMP_PIXEL_FETCHER_EDGE_NONE &&
|
||
|
(x < pf->sel_x1 || x >= pf->sel_x2 ||
|
||
|
y < pf->sel_y1 || y >= pf->sel_y2))
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (x < 0 || x >= pf->img_width ||
|
||
|
y < 0 || y >= pf->img_height)
|
||
|
{
|
||
|
switch (pf->mode)
|
||
|
{
|
||
|
case GIMP_PIXEL_FETCHER_EDGE_WRAP:
|
||
|
if (x < 0 || x >= pf->img_width)
|
||
|
{
|
||
|
x %= pf->img_width;
|
||
|
|
||
|
if (x < 0)
|
||
|
x += pf->img_width;
|
||
|
}
|
||
|
|
||
|
if (y < 0 || y >= pf->img_height)
|
||
|
{
|
||
|
y %= pf->img_height;
|
||
|
|
||
|
if (y < 0)
|
||
|
y += pf->img_height;
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case GIMP_PIXEL_FETCHER_EDGE_SMEAR:
|
||
|
x = CLAMP (x, 0, pf->img_width - 1);
|
||
|
y = CLAMP (y, 0, pf->img_height - 1);
|
||
|
break;
|
||
|
|
||
|
case GIMP_PIXEL_FETCHER_EDGE_BLACK:
|
||
|
for (i = 0; i < pf->img_bpp; i++)
|
||
|
pixel[i] = 0;
|
||
|
return;
|
||
|
|
||
|
case GIMP_PIXEL_FETCHER_EDGE_BACKGROUND:
|
||
|
for (i = 0; i < pf->img_bpp; i++)
|
||
|
pixel[i] = pf->bg_color[i];
|
||
|
return;
|
||
|
|
||
|
default:
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
p = gimp_pixel_fetcher_provide_tile (pf, x, y);
|
||
|
|
||
|
i = pf->img_bpp;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
*pixel++ = *p++;
|
||
|
}
|
||
|
while (--i);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* gimp_pixel_fetcher_put_pixel:
|
||
|
* @pf: a pointer to a previously initialized #GimpPixelFetcher.
|
||
|
* @x: the x coordinate of the pixel to set.
|
||
|
* @y: the y coordinate of the pixel to set.
|
||
|
* @pixel: the pixel to set.
|
||
|
*
|
||
|
* Set a pixel in the pixel region.
|
||
|
**/
|
||
|
void
|
||
|
gimp_pixel_fetcher_put_pixel (GimpPixelFetcher *pf,
|
||
|
gint x,
|
||
|
gint y,
|
||
|
const guchar *pixel)
|
||
|
{
|
||
|
guchar *p;
|
||
|
gint i;
|
||
|
|
||
|
g_return_if_fail (pf != NULL);
|
||
|
g_return_if_fail (pixel != NULL);
|
||
|
|
||
|
if (x < pf->sel_x1 || x >= pf->sel_x2 ||
|
||
|
y < pf->sel_y1 || y >= pf->sel_y2)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
p = gimp_pixel_fetcher_provide_tile (pf, x, y);
|
||
|
|
||
|
i = pf->img_bpp;
|
||
|
|
||
|
do
|
||
|
{
|
||
|
*p++ = *pixel++;
|
||
|
}
|
||
|
while (--i);
|
||
|
|
||
|
pf->tile_dirty = TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* private functions */
|
||
|
|
||
|
static guchar *
|
||
|
gimp_pixel_fetcher_provide_tile (GimpPixelFetcher *pf,
|
||
|
gint x,
|
||
|
gint y)
|
||
|
{
|
||
|
gint col, row;
|
||
|
gint coloff, rowoff;
|
||
|
|
||
|
col = x / pf->tile_width;
|
||
|
coloff = x % pf->tile_width;
|
||
|
row = y / pf->tile_height;
|
||
|
rowoff = y % pf->tile_height;
|
||
|
|
||
|
if ((col != pf->col) || (row != pf->row) || (pf->tile == NULL))
|
||
|
{
|
||
|
if (pf->tile != NULL)
|
||
|
gimp_tile_unref (pf->tile, pf->tile_dirty);
|
||
|
|
||
|
pf->tile = gimp_drawable_get_tile (pf->drawable, pf->shadow, row, col);
|
||
|
pf->tile_dirty = FALSE;
|
||
|
gimp_tile_ref (pf->tile);
|
||
|
|
||
|
pf->col = col;
|
||
|
pf->row = row;
|
||
|
}
|
||
|
|
||
|
return pf->tile->data + pf->img_bpp * (pf->tile->ewidth * rowoff + coloff);
|
||
|
}
|