app/base/base-enums.[ch] app/core/gimpdrawable-transform.c

2005-01-11  Sven Neumann  <neumann@jpk.com>

	* app/base/base-enums.[ch]
	* app/core/gimpdrawable-transform.c
	* app/core/gimpdrawable.c
	* app/paint-funcs/Makefile.am
	* app/paint-funcs/paint-funcs.[ch]
	* app/paint-funcs/scale-funcs.[ch]: applied patch by Geert Jordaens
	(after a good deal of reformatting for coding style compliance).
	This factors the scale routines into their own file and adds a
	sinc-based (Lanczos) interpolation routine (bug #162250).
This commit is contained in:
Sven Neumann 2005-01-11 18:31:07 +00:00 committed by Sven Neumann
parent 86b4d00a0e
commit f3166ba9f9
11 changed files with 1604 additions and 714 deletions

View File

@ -1,3 +1,15 @@
2005-01-11 Sven Neumann <neumann@jpk.com>
* app/base/base-enums.[ch]
* app/core/gimpdrawable-transform.c
* app/core/gimpdrawable.c
* app/paint-funcs/Makefile.am
* app/paint-funcs/paint-funcs.[ch]
* app/paint-funcs/scale-funcs.[ch]: applied patch by Geert Jordaens
(after a good deal of reformatting for coding style compliance).
This factors the scale routines into their own file and adds a
sinc-based (Lanczos) interpolation routine (bug #162250).
2005-01-11 Sven Neumann <sven@gimp.org>
* plug-ins/script-fu/siod/slib.c (help): removed wrong URL from

View File

@ -80,6 +80,7 @@ gimp_interpolation_type_get_type (void)
{ GIMP_INTERPOLATION_NONE, "GIMP_INTERPOLATION_NONE", "none" },
{ GIMP_INTERPOLATION_LINEAR, "GIMP_INTERPOLATION_LINEAR", "linear" },
{ GIMP_INTERPOLATION_CUBIC, "GIMP_INTERPOLATION_CUBIC", "cubic" },
{ GIMP_INTERPOLATION_LANCZOS, "GIMP_INTERPOLATION_LANCZOS", "lanczos" },
{ 0, NULL, NULL }
};
@ -87,7 +88,8 @@ gimp_interpolation_type_get_type (void)
{
{ GIMP_INTERPOLATION_NONE, N_("None (Fastest)"), NULL },
{ GIMP_INTERPOLATION_LINEAR, N_("Linear"), NULL },
{ GIMP_INTERPOLATION_CUBIC, N_("Cubic (Best)"), NULL },
{ GIMP_INTERPOLATION_CUBIC, N_("Cubic"), NULL },
{ GIMP_INTERPOLATION_LANCZOS, N_("Lanczos (Best)"), NULL },
{ 0, NULL, NULL }
};

View File

@ -69,7 +69,8 @@ typedef enum
{
GIMP_INTERPOLATION_NONE, /*< desc="None (Fastest)" >*/
GIMP_INTERPOLATION_LINEAR, /*< desc="Linear" >*/
GIMP_INTERPOLATION_CUBIC /*< desc="Cubic (Best)" >*/
GIMP_INTERPOLATION_CUBIC, /*< desc="Cubic" >*/
GIMP_INTERPOLATION_LANCZOS /*< desc="Lanczos (Best)" >*/
} GimpInterpolationType;

View File

@ -32,6 +32,7 @@
#include "base/tile.h"
#include "paint-funcs/paint-funcs.h"
#include "paint-funcs/scale-funcs.h"
#include "gimp.h"
#include "gimp-utils.h"
@ -98,6 +99,16 @@ static void sample_linear (PixelSurround *surround,
gint bytes,
gint alpha);
static void sample_lanczos (const gdouble *kernel,
gint su,
gint sv,
guchar *color,
guchar *buffer,
gint bytes,
gint alpha);
static gdouble * kernel_lanczos (void);
/* public functions */
@ -137,6 +148,11 @@ gimp_drawable_transform_tiles_affine (GimpDrawable *drawable,
gdouble tu[5],tv[5],tw[5]; /* undivided source coordinates and
divisor */
gdouble *kernel = NULL; /* Lanczos kernel */
gint su, sv; /* Lanczos kernel position */
guchar *buffer = NULL; /* Holds sliding window buffer */
gint coords;
gint width;
gint alpha;
@ -272,6 +288,14 @@ gimp_drawable_transform_tiles_affine (GimpDrawable *drawable,
0, 0, x2 - x1, y2 - y1, TRUE);
tile_manager_set_offsets (new_tiles, x1, y1);
width = tile_manager_width (new_tiles);
bytes = tile_manager_bpp (new_tiles);
/* If the image is too small for lanczos, switch to cubic interpolation */
if (interpolation_type == GIMP_INTERPOLATION_LANCZOS &&
(x2 - x1 < LANCZOS_WIDTH2 || y2 - y1 < LANCZOS_WIDTH2))
interpolation_type = GIMP_INTERPOLATION_CUBIC;
/* initialise the pixel_surround and pixel_cache accessors */
switch (interpolation_type)
{
@ -283,11 +307,12 @@ gimp_drawable_transform_tiles_affine (GimpDrawable *drawable,
case GIMP_INTERPOLATION_LINEAR:
pixel_surround_init (&surround, orig_tiles, 2, 2, bg_color);
break;
case GIMP_INTERPOLATION_LANCZOS:
kernel = kernel_lanczos ();
buffer = g_new0 (guchar, LANCZOS_WIDTH2 * bytes);
break;
}
width = tile_manager_width (new_tiles);
bytes = tile_manager_bpp (new_tiles);
dest = g_new (guchar, tile_manager_width (new_tiles) * bytes);
uinc = m.coeff[0][0];
@ -386,6 +411,47 @@ gimp_drawable_transform_tiles_affine (GimpDrawable *drawable,
*d++ = bg_color[b];
}
}
else if (interpolation_type == GIMP_INTERPOLATION_LANCZOS)
{
guchar color[MAX_CHANNELS];
gint iu = (gint) u [0];
gint iv = (gint) v [0];
gint fu,fv;
gint b;
if (iu >= u1 && iu < u2 &&
iv >= v1 && iv < v2)
{
/* u, v coordinates into source tiles */
gint u = iu - u1;
gint v = iv - v1;
read_pixel_data (orig_tiles,
u-LANCZOS_WIDTH+1,
v-LANCZOS_WIDTH+1,
u+LANCZOS_WIDTH,
v+LANCZOS_WIDTH,
buffer,
bytes*LANCZOS_WIDTH2);
fu = u-iu;
su = (gint)( fu * LANCZOS_SPP );
fv = v-iv;
sv = (gint)( fv * LANCZOS_SPP );
sample_lanczos (kernel, su, sv, color, buffer, bytes, alpha);
for (b = 0; b < bytes; b++)
*d++ = color[b];
}
else /* not in source range */
{
/* increment the destination pointers */
for (b = 0; b < bytes; b++)
*d++ = bg_color[b];
}
}
else
{
gint b;
@ -451,8 +517,19 @@ gimp_drawable_transform_tiles_affine (GimpDrawable *drawable,
pixel_region_set_row (&destPR, 0, (y - y1), width, dest);
}
if (interpolation_type != GIMP_INTERPOLATION_NONE)
pixel_surround_clear (&surround);
switch (interpolation_type)
{
case GIMP_INTERPOLATION_NONE:
break;
case GIMP_INTERPOLATION_CUBIC:
case GIMP_INTERPOLATION_LINEAR:
pixel_surround_clear (&surround);
break;
case GIMP_INTERPOLATION_LANCZOS:
g_free (kernel);
g_free (buffer);
break;
}
g_free (dest);
@ -1611,3 +1688,152 @@ sample_cubic (PixelSurround *surround,
pixel_surround_release (surround);
}
/* Lanczos */
static inline gdouble
sinc (gdouble x)
{
gdouble y = x * G_PI;
if (ABS (x) < EPSILON)
return 1.0;
return sin (y) / y;
}
static inline gdouble
lanczos_sum (const guchar *buffer,
gdouble *l,
gint row,
gint bytes,
gint byte)
{
gdouble sum = 0;
gint j, k, p;
p = row * bytes * LANCZOS_WIDTH2;
for (k = 0, j = 0; j < LANCZOS_WIDTH2; j++, k++)
sum += l[k] * (buffer + p)[j * bytes + byte];
return sum;
}
static inline gdouble
lanczos_sum_mul (guchar *buffer,
gdouble *l,
gint row,
gint bytes,
gint byte,
gint alpha)
{
gdouble sum = 0;
gint j, k, p;
p = row * bytes * LANCZOS_WIDTH2;
for (k = 0, j = 0; j < LANCZOS_WIDTH2; j++, k++)
sum += (l[k] *
(buffer + p)[j * bytes + byte] *
(buffer + p)[j * bytes + alpha]);
return sum;
}
static gdouble *
kernel_lanczos (void)
{
gdouble *kernel ;
gdouble x = 0.0;
gdouble dx = (gdouble) LANCZOS_WIDTH / (gdouble) (LANCZOS_SAMPLES - 1);
gint i;
kernel = g_new (gdouble, LANCZOS_SAMPLES);
for (i = 0 ;i < LANCZOS_SAMPLES; i++)
{
kernel[i] = ((ABS (x) < LANCZOS_WIDTH) ?
(sinc (x) * sinc (x / LANCZOS_WIDTH)) : 0.0);
x += dx;
}
return kernel;
}
static void
sample_lanczos (const gdouble *kernel,
gint su,
gint sv,
guchar *color,
guchar *buffer,
gint bytes,
gint alpha)
{
gdouble lu[LANCZOS_WIDTH2]; /* Lanczos sample value */
gdouble lv[LANCZOS_WIDTH2]; /* Lanczos sample value */
gdouble lusum, lvsum, weight; /* Lanczos weighting vars */
gint i,j,row, byte; /* loop vars to fill source window */
gdouble aval, arecip; /* Handle alpha values */
gdouble newval; /* New interpolated RGB value */
for (lusum = lvsum = i = 0, j = LANCZOS_WIDTH - 1;
j >= - LANCZOS_WIDTH;
j--, i++)
{
lusum += lu[i] = kernel[ABS (j * LANCZOS_SPP + su)];
lvsum += lv[i] = kernel[ABS (j * LANCZOS_SPP + sv)];
}
weight = lusum * lvsum;
if (alpha != 0)
{
for ( aval = 0, row = 0 ; row < LANCZOS_WIDTH2 ; row ++ )
aval += lv[row] * lanczos_sum (buffer, lu, row, bytes, alpha);
/* calculate alpha of result */
aval /= weight;
if ( aval <= 0.0 )
{
arecip = 0.0;
color[alpha] = 0;
}
else if ( aval > 255.0 )
{
arecip = 1.0 / aval;
color[alpha] = 255;
}
else
{
arecip = 1.0 / aval;
color[alpha] = RINT (aval);
}
for (byte = 0; byte < alpha; byte++)
{
for (newval = 0, row = 0; row < LANCZOS_WIDTH2; row ++)
newval += lv[row] * lanczos_sum_mul (buffer, lu,
row, bytes, byte, alpha);
newval *= arecip;
color[byte] = CLAMP (newval, 0, 255);
}
}
else
{
for (byte = 0; byte < bytes; byte++)
{
for (newval = 0, row = 0 ; row < LANCZOS_WIDTH2 ; row++ )
newval += lv[row] * lanczos_sum (buffer, lu, row, bytes, byte);
newval /= weight;
color[byte] = CLAMP ((gint) newval, 0, 255);
}
}
}
/* Lanczos */

View File

@ -32,6 +32,7 @@
#include "base/tile.h"
#include "paint-funcs/paint-funcs.h"
#include "paint-funcs/scale-funcs.h"
#include "gimp.h"
#include "gimp-utils.h"
@ -98,6 +99,16 @@ static void sample_linear (PixelSurround *surround,
gint bytes,
gint alpha);
static void sample_lanczos (const gdouble *kernel,
gint su,
gint sv,
guchar *color,
guchar *buffer,
gint bytes,
gint alpha);
static gdouble * kernel_lanczos (void);
/* public functions */
@ -137,6 +148,11 @@ gimp_drawable_transform_tiles_affine (GimpDrawable *drawable,
gdouble tu[5],tv[5],tw[5]; /* undivided source coordinates and
divisor */
gdouble *kernel = NULL; /* Lanczos kernel */
gint su, sv; /* Lanczos kernel position */
guchar *buffer = NULL; /* Holds sliding window buffer */
gint coords;
gint width;
gint alpha;
@ -272,6 +288,14 @@ gimp_drawable_transform_tiles_affine (GimpDrawable *drawable,
0, 0, x2 - x1, y2 - y1, TRUE);
tile_manager_set_offsets (new_tiles, x1, y1);
width = tile_manager_width (new_tiles);
bytes = tile_manager_bpp (new_tiles);
/* If the image is too small for lanczos, switch to cubic interpolation */
if (interpolation_type == GIMP_INTERPOLATION_LANCZOS &&
(x2 - x1 < LANCZOS_WIDTH2 || y2 - y1 < LANCZOS_WIDTH2))
interpolation_type = GIMP_INTERPOLATION_CUBIC;
/* initialise the pixel_surround and pixel_cache accessors */
switch (interpolation_type)
{
@ -283,11 +307,12 @@ gimp_drawable_transform_tiles_affine (GimpDrawable *drawable,
case GIMP_INTERPOLATION_LINEAR:
pixel_surround_init (&surround, orig_tiles, 2, 2, bg_color);
break;
case GIMP_INTERPOLATION_LANCZOS:
kernel = kernel_lanczos ();
buffer = g_new0 (guchar, LANCZOS_WIDTH2 * bytes);
break;
}
width = tile_manager_width (new_tiles);
bytes = tile_manager_bpp (new_tiles);
dest = g_new (guchar, tile_manager_width (new_tiles) * bytes);
uinc = m.coeff[0][0];
@ -386,6 +411,47 @@ gimp_drawable_transform_tiles_affine (GimpDrawable *drawable,
*d++ = bg_color[b];
}
}
else if (interpolation_type == GIMP_INTERPOLATION_LANCZOS)
{
guchar color[MAX_CHANNELS];
gint iu = (gint) u [0];
gint iv = (gint) v [0];
gint fu,fv;
gint b;
if (iu >= u1 && iu < u2 &&
iv >= v1 && iv < v2)
{
/* u, v coordinates into source tiles */
gint u = iu - u1;
gint v = iv - v1;
read_pixel_data (orig_tiles,
u-LANCZOS_WIDTH+1,
v-LANCZOS_WIDTH+1,
u+LANCZOS_WIDTH,
v+LANCZOS_WIDTH,
buffer,
bytes*LANCZOS_WIDTH2);
fu = u-iu;
su = (gint)( fu * LANCZOS_SPP );
fv = v-iv;
sv = (gint)( fv * LANCZOS_SPP );
sample_lanczos (kernel, su, sv, color, buffer, bytes, alpha);
for (b = 0; b < bytes; b++)
*d++ = color[b];
}
else /* not in source range */
{
/* increment the destination pointers */
for (b = 0; b < bytes; b++)
*d++ = bg_color[b];
}
}
else
{
gint b;
@ -451,8 +517,19 @@ gimp_drawable_transform_tiles_affine (GimpDrawable *drawable,
pixel_region_set_row (&destPR, 0, (y - y1), width, dest);
}
if (interpolation_type != GIMP_INTERPOLATION_NONE)
pixel_surround_clear (&surround);
switch (interpolation_type)
{
case GIMP_INTERPOLATION_NONE:
break;
case GIMP_INTERPOLATION_CUBIC:
case GIMP_INTERPOLATION_LINEAR:
pixel_surround_clear (&surround);
break;
case GIMP_INTERPOLATION_LANCZOS:
g_free (kernel);
g_free (buffer);
break;
}
g_free (dest);
@ -1611,3 +1688,152 @@ sample_cubic (PixelSurround *surround,
pixel_surround_release (surround);
}
/* Lanczos */
static inline gdouble
sinc (gdouble x)
{
gdouble y = x * G_PI;
if (ABS (x) < EPSILON)
return 1.0;
return sin (y) / y;
}
static inline gdouble
lanczos_sum (const guchar *buffer,
gdouble *l,
gint row,
gint bytes,
gint byte)
{
gdouble sum = 0;
gint j, k, p;
p = row * bytes * LANCZOS_WIDTH2;
for (k = 0, j = 0; j < LANCZOS_WIDTH2; j++, k++)
sum += l[k] * (buffer + p)[j * bytes + byte];
return sum;
}
static inline gdouble
lanczos_sum_mul (guchar *buffer,
gdouble *l,
gint row,
gint bytes,
gint byte,
gint alpha)
{
gdouble sum = 0;
gint j, k, p;
p = row * bytes * LANCZOS_WIDTH2;
for (k = 0, j = 0; j < LANCZOS_WIDTH2; j++, k++)
sum += (l[k] *
(buffer + p)[j * bytes + byte] *
(buffer + p)[j * bytes + alpha]);
return sum;
}
static gdouble *
kernel_lanczos (void)
{
gdouble *kernel ;
gdouble x = 0.0;
gdouble dx = (gdouble) LANCZOS_WIDTH / (gdouble) (LANCZOS_SAMPLES - 1);
gint i;
kernel = g_new (gdouble, LANCZOS_SAMPLES);
for (i = 0 ;i < LANCZOS_SAMPLES; i++)
{
kernel[i] = ((ABS (x) < LANCZOS_WIDTH) ?
(sinc (x) * sinc (x / LANCZOS_WIDTH)) : 0.0);
x += dx;
}
return kernel;
}
static void
sample_lanczos (const gdouble *kernel,
gint su,
gint sv,
guchar *color,
guchar *buffer,
gint bytes,
gint alpha)
{
gdouble lu[LANCZOS_WIDTH2]; /* Lanczos sample value */
gdouble lv[LANCZOS_WIDTH2]; /* Lanczos sample value */
gdouble lusum, lvsum, weight; /* Lanczos weighting vars */
gint i,j,row, byte; /* loop vars to fill source window */
gdouble aval, arecip; /* Handle alpha values */
gdouble newval; /* New interpolated RGB value */
for (lusum = lvsum = i = 0, j = LANCZOS_WIDTH - 1;
j >= - LANCZOS_WIDTH;
j--, i++)
{
lusum += lu[i] = kernel[ABS (j * LANCZOS_SPP + su)];
lvsum += lv[i] = kernel[ABS (j * LANCZOS_SPP + sv)];
}
weight = lusum * lvsum;
if (alpha != 0)
{
for ( aval = 0, row = 0 ; row < LANCZOS_WIDTH2 ; row ++ )
aval += lv[row] * lanczos_sum (buffer, lu, row, bytes, alpha);
/* calculate alpha of result */
aval /= weight;
if ( aval <= 0.0 )
{
arecip = 0.0;
color[alpha] = 0;
}
else if ( aval > 255.0 )
{
arecip = 1.0 / aval;
color[alpha] = 255;
}
else
{
arecip = 1.0 / aval;
color[alpha] = RINT (aval);
}
for (byte = 0; byte < alpha; byte++)
{
for (newval = 0, row = 0; row < LANCZOS_WIDTH2; row ++)
newval += lv[row] * lanczos_sum_mul (buffer, lu,
row, bytes, byte, alpha);
newval *= arecip;
color[byte] = CLAMP (newval, 0, 255);
}
}
else
{
for (byte = 0; byte < bytes; byte++)
{
for (newval = 0, row = 0 ; row < LANCZOS_WIDTH2 ; row++ )
newval += lv[row] * lanczos_sum (buffer, lu, row, bytes, byte);
newval /= weight;
color[byte] = CLAMP ((gint) newval, 0, 255);
}
}
}
/* Lanczos */

View File

@ -30,6 +30,7 @@
#include "base/tile-manager.h"
#include "paint-funcs/paint-funcs.h"
#include "paint-funcs/scale-funcs.h"
#include "gimp.h"
#include "gimp-utils.h"

View File

@ -6,7 +6,9 @@ libapppaint_funcs_a_SOURCES = \
paint-funcs-types.h \
paint-funcs.c \
paint-funcs.h \
paint-funcs-generic.h
paint-funcs-generic.h \
scale-funcs.c \
scale-funcs.h
INCLUDES = \
-I$(top_srcdir) \

View File

@ -43,15 +43,6 @@
#define EPSILON 0.0001
typedef enum
{
MinifyX_MinifyY,
MinifyX_MagnifyY,
MagnifyX_MinifyY,
MagnifyX_MagnifyY
} ScaleType;
/* Layer modes information */
typedef struct _LayerMode LayerMode;
struct _LayerMode
@ -154,8 +145,6 @@ static void apply_layer_mode_replace (guchar *src1,
static inline void rotate_pointers (guchar **p,
guint32 n);
static void
update_tile_rowhints (Tile *tile,
gint ymin,
@ -432,6 +421,7 @@ cubic (gdouble dx,
( - jm1 + jp1 ) ) * dx + (j + j) ) / 2.0;
}
/*********************/
/* FUNCTIONS */
/*********************/
@ -2829,275 +2819,6 @@ gaussian_blur_region (PixelRegion *srcR,
g_free (buf);
}
/* non-interpolating scale_region. [adam]
*/
static void
scale_region_no_resample (PixelRegion *srcPR,
PixelRegion *destPR)
{
gint *x_src_offsets;
gint *y_src_offsets;
guchar *src;
guchar *dest;
gint width, height, orig_width, orig_height;
gint last_src_y;
gint row_bytes;
gint x, y, b;
gchar bytes;
orig_width = srcPR->w;
orig_height = srcPR->h;
width = destPR->w;
height = destPR->h;
bytes = srcPR->bytes;
/* the data pointers... */
x_src_offsets = g_new (gint, width * bytes);
y_src_offsets = g_new (gint, height);
src = g_new (guchar, orig_width * bytes);
dest = g_new (guchar, width * bytes);
/* pre-calc the scale tables */
for (b = 0; b < bytes; b++)
for (x = 0; x < width; x++)
x_src_offsets [b + x * bytes] =
b + bytes * ((x * orig_width + orig_width / 2) / width);
for (y = 0; y < height; y++)
y_src_offsets [y] = (y * orig_height + orig_height / 2) / height;
/* do the scaling */
row_bytes = width * bytes;
last_src_y = -1;
for (y = 0; y < height; y++)
{
/* if the source of this line was the same as the source
* of the last line, there's no point in re-rescaling.
*/
if (y_src_offsets[y] != last_src_y)
{
pixel_region_get_row (srcPR, 0, y_src_offsets[y], orig_width, src, 1);
for (x = 0; x < row_bytes ; x++)
{
dest[x] = src[x_src_offsets[x]];
}
last_src_y = y_src_offsets[y];
}
pixel_region_set_row (destPR, 0, y, width, dest);
}
g_free (x_src_offsets);
g_free (y_src_offsets);
g_free (src);
g_free (dest);
}
static void
get_premultiplied_double_row (PixelRegion *srcPR,
gint x,
gint y,
gint w,
gdouble *row,
guchar *tmp_src,
gint n)
{
gint b;
gint bytes = srcPR->bytes;
pixel_region_get_row (srcPR, x, y, w, tmp_src, n);
if (pixel_region_has_alpha (srcPR))
{
/* premultiply the alpha into the double array */
gdouble *irow = row;
gint alpha = bytes - 1;
gdouble mod_alpha;
for (x = 0; x < w; x++)
{
mod_alpha = tmp_src[alpha] / 255.0;
for (b = 0; b < alpha; b++)
irow[b] = mod_alpha * tmp_src[b];
irow[b] = tmp_src[alpha];
irow += bytes;
tmp_src += bytes;
}
}
else /* no alpha */
{
for (x = 0; x < w * bytes; x++)
row[x] = tmp_src[x];
}
/* set the off edge pixels to their nearest neighbor */
for (b = 0; b < 2 * bytes; b++)
row[b - 2 * bytes] = row[b % bytes];
for (b = 0; b < bytes * 2; b++)
row[b + w * bytes] = row[(w - 1) * bytes + b % bytes];
}
static void
expand_line (gdouble *dest,
gdouble *src,
gint bytes,
gint old_width,
gint width,
GimpInterpolationType interp)
{
gdouble ratio;
gint x,b;
gint src_col;
gdouble frac;
gdouble *s;
ratio = old_width / (gdouble) width;
/* we can overflow src's boundaries, so we expect our caller to have
allocated extra space for us to do so safely (see scale_region ()) */
/* this could be optimized much more by precalculating the coefficients for
each x */
switch(interp)
{
case GIMP_INTERPOLATION_CUBIC:
for (x = 0; x < width; x++)
{
src_col = ((int) (x * ratio + 2.0 - 0.5)) - 2;
/* +2, -2 is there because (int) rounds towards 0 and we need
to round down */
frac = (x * ratio - 0.5) - src_col;
s = &src[src_col * bytes];
for (b = 0; b < bytes; b++)
dest[b] = cubic (frac, s[b - bytes], s[b], s[b + bytes],
s[b + bytes * 2]);
dest += bytes;
}
break;
case GIMP_INTERPOLATION_LINEAR:
for (x = 0; x < width; x++)
{
src_col = ((int) (x * ratio + 2.0 - 0.5)) - 2;
/* +2, -2 is there because (int) rounds towards 0 and we need
to round down */
frac = (x * ratio - 0.5) - src_col;
s = &src[src_col * bytes];
for (b = 0; b < bytes; b++)
dest[b] = ((s[b + bytes] - s[b]) * frac + s[b]);
dest += bytes;
}
break;
case GIMP_INTERPOLATION_NONE:
g_assert_not_reached ();
break;
}
}
static void
shrink_line (gdouble *dest,
gdouble *src,
gint bytes,
gint old_width,
gint width,
GimpInterpolationType interp)
{
gint x;
gint b;
gdouble *srcp;
gdouble *destp;
gdouble accum[4];
gdouble slice;
const gdouble avg_ratio = (gdouble) width / old_width;
const gdouble inv_width = 1.0 / width;
gint slicepos; /* slice position relative to width */
#if 0
g_printerr ("shrink_line bytes=%d old_width=%d width=%d interp=%d "
"avg_ratio=%f\n",
bytes, old_width, width, interp, avg_ratio);
#endif
g_return_if_fail (bytes <= 4);
/* This algorithm calculates the weighted average of pixel data that
each output pixel must receive, taking into account that it always
scales down, i.e. there's always more than one input pixel per each
output pixel. */
srcp = src;
destp = dest;
slicepos = 0;
/* Initialize accum to the first pixel slice. As there is no partial
pixel at start, that value is 0. The source data is interleaved, so
we maintain BYTES accumulators at the same time to deal with that
many channels simultaneously. */
for (b = 0; b < bytes; b++)
accum[b] = 0.0;
for (x = 0; x < width; x++)
{
/* Accumulate whole pixels. */
do
{
for (b = 0; b < bytes; b++)
accum[b] += *srcp++;
slicepos += width;
}
while (slicepos < old_width);
slicepos -= old_width;
if (! (slicepos < width))
g_warning ("Assertion (slicepos < width) failed. Please report.");
if (slicepos == 0)
{
/* Simplest case: we have reached a whole pixel boundary. Store
the average value per channel and reset the accumulators for
the next round.
The main reason to treat this case separately is to avoid an
access to out-of-bounds memory for the first pixel. */
for (b = 0; b < bytes; b++)
{
*destp++ = accum[b] * avg_ratio;
accum[b] = 0.0;
}
}
else
{
for (b = 0; b < bytes; b++)
{
/* We have accumulated a whole pixel per channel where just a
slice of it was needed. Subtract now the previous pixel's
extra slice. */
slice = srcp[- bytes + b] * slicepos * inv_width;
*destp++ = (accum[b] - slice) * avg_ratio;
/* That slice is the initial value for the next round. */
accum[b] = slice;
}
}
}
/* Sanity check: srcp should point to the next-to-last position, and
slicepos should be zero. */
if (! (srcp - src == old_width * bytes && slicepos == 0))
g_warning ("Assertion (srcp - src == old_width * bytes && slicepos == 0)"
" failed. Please report.");
}
static inline void
rotate_pointers (guchar **p,
guint32 n)
@ -3113,421 +2834,6 @@ rotate_pointers (guchar **p,
p[i] = tmp;
}
static void
get_scaled_row (gdouble **src,
gint y,
gint new_width,
PixelRegion *srcPR,
gdouble *row,
guchar *src_tmp,
GimpInterpolationType interpolation_type)
{
/* get the necesary lines from the source image, scale them,
and put them into src[] */
rotate_pointers ((gpointer) src, 4);
if (y < 0)
y = 0;
if (y < srcPR->h)
{
get_premultiplied_double_row (srcPR, 0, y, srcPR->w,
row, src_tmp, 1);
if (new_width > srcPR->w)
expand_line(src[3], row, srcPR->bytes,
srcPR->w, new_width, interpolation_type);
else if (srcPR->w > new_width)
shrink_line(src[3], row, srcPR->bytes,
srcPR->w, new_width, interpolation_type);
else /* no scailing needed */
memcpy(src[3], row, sizeof (gdouble) * new_width * srcPR->bytes);
}
else
memcpy(src[3], src[2], sizeof (gdouble) * new_width * srcPR->bytes);
}
void
scale_region (PixelRegion *srcPR,
PixelRegion *destPR,
GimpInterpolationType interpolation,
GimpProgressFunc progress_callback,
gpointer progress_data)
{
gdouble *src[4];
guchar *src_tmp;
guchar *dest;
gdouble *row, *accum;
gint bytes, b;
gint width, height;
gint orig_width, orig_height;
gdouble y_rat;
gint i;
gint old_y = -4;
gint new_y;
gint x, y;
if (interpolation == GIMP_INTERPOLATION_NONE)
{
scale_region_no_resample (srcPR, destPR);
return;
}
orig_width = srcPR->w;
orig_height = srcPR->h;
width = destPR->w;
height = destPR->h;
#if 0
g_printerr ("scale_region: (%d x %d) -> (%d x %d)\n",
orig_width, orig_height, width, height);
#endif
/* find the ratios of old y to new y */
y_rat = (gdouble) orig_height / (gdouble) height;
bytes = destPR->bytes;
/* the data pointers... */
for (i = 0; i < 4; i++)
src[i] = g_new (gdouble, width * bytes);
dest = g_new (guchar, width * bytes);
src_tmp = g_new (guchar, orig_width * bytes);
/* offset the row pointer by 2*bytes so the range of the array
is [-2*bytes] to [(orig_width + 2)*bytes] */
row = g_new (gdouble, (orig_width + 2 * 2) * bytes);
row += bytes * 2;
accum = g_new (gdouble, width * bytes);
/* Scale the selected region */
for (y = 0; y < height; y++)
{
if (progress_callback && !(y & 0xf))
(* progress_callback) (0, height, y, progress_data);
if (height < orig_height)
{
gint max;
gdouble frac;
const gdouble inv_ratio = 1.0 / y_rat;
if (y == 0) /* load the first row if this is the first time through */
get_scaled_row (&src[0], 0, width, srcPR, row,
src_tmp,
interpolation);
new_y = (int) (y * y_rat);
frac = 1.0 - (y * y_rat - new_y);
for (x = 0; x < width * bytes; x++)
accum[x] = src[3][x] * frac;
max = (int) ((y + 1) * y_rat) - new_y - 1;
get_scaled_row (&src[0], ++new_y, width, srcPR, row,
src_tmp,
interpolation);
while (max > 0)
{
for (x = 0; x < width * bytes; x++)
accum[x] += src[3][x];
get_scaled_row (&src[0], ++new_y, width, srcPR, row,
src_tmp,
interpolation);
max--;
}
frac = (y + 1) * y_rat - ((int) ((y + 1) * y_rat));
for (x = 0; x < width * bytes; x++)
{
accum[x] += frac * src[3][x];
accum[x] *= inv_ratio;
}
}
else if (height > orig_height)
{
new_y = floor (y * y_rat - 0.5);
while (old_y <= new_y)
{
/* get the necesary lines from the source image, scale them,
and put them into src[] */
get_scaled_row (&src[0], old_y + 2, width, srcPR, row,
src_tmp,
interpolation);
old_y++;
}
switch (interpolation)
{
case GIMP_INTERPOLATION_CUBIC:
{
gdouble p0, p1, p2, p3;
gdouble dy = (y * y_rat - 0.5) - new_y;
p0 = cubic (dy, 1, 0, 0, 0);
p1 = cubic (dy, 0, 1, 0, 0);
p2 = cubic (dy, 0, 0, 1, 0);
p3 = cubic (dy, 0, 0, 0, 1);
for (x = 0; x < width * bytes; x++)
accum[x] = (p0 * src[0][x] + p1 * src[1][x] +
p2 * src[2][x] + p3 * src[3][x]);
}
break;
case GIMP_INTERPOLATION_LINEAR:
{
gdouble idy = (y * y_rat - 0.5) - new_y;
gdouble dy = 1.0 - idy;
for (x = 0; x < width * bytes; x++)
accum[x] = dy * src[1][x] + idy * src[2][x];
}
break;
case GIMP_INTERPOLATION_NONE:
g_assert_not_reached ();
break;
}
}
else /* height == orig_height */
{
get_scaled_row (&src[0], y, width, srcPR, row,
src_tmp,
interpolation);
memcpy (accum, src[3], sizeof (gdouble) * width * bytes);
}
if (pixel_region_has_alpha (srcPR))
{
/* unmultiply the alpha */
gdouble inv_alpha;
gdouble *p = accum;
gint alpha = bytes - 1;
gint result;
guchar *d = dest;
for (x = 0; x < width; x++)
{
if (p[alpha] > 0.001)
{
inv_alpha = 255.0 / p[alpha];
for (b = 0; b < alpha; b++)
{
result = RINT (inv_alpha * p[b]);
if (result < 0)
d[b] = 0;
else if (result > 255)
d[b] = 255;
else
d[b] = result;
}
result = RINT (p[alpha]);
if (result > 255)
d[alpha] = 255;
else
d[alpha] = result;
}
else /* alpha <= 0 */
for (b = 0; b <= alpha; b++)
d[b] = 0;
d += bytes;
p += bytes;
}
}
else
{
gint w = width * bytes;
for (x = 0; x < w; x++)
{
if (accum[x] < 0.0)
dest[x] = 0;
else if (accum[x] > 255.0)
dest[x] = 255;
else
dest[x] = RINT (accum[x]);
}
}
pixel_region_set_row (destPR, 0, y, width, dest);
}
/* free up temporary arrays */
g_free (accum);
for (i = 0; i < 4; i++)
g_free (src[i]);
g_free (src_tmp);
g_free (dest);
row -= 2 * bytes;
g_free (row);
}
void
subsample_region (PixelRegion *srcPR,
PixelRegion *destPR,
gint subsample)
{
guchar *src, *s;
guchar *dest, *d;
gdouble *row, *r;
gint destwidth;
gint src_row, src_col;
gint bytes, b;
gint width, height;
gint orig_width, orig_height;
gdouble x_rat, y_rat;
gdouble x_cum, y_cum;
gdouble x_last, y_last;
gdouble * x_frac, y_frac, tot_frac;
gint i, j;
gint frac;
gint advance_dest;
orig_width = srcPR->w / subsample;
orig_height = srcPR->h / subsample;
width = destPR->w;
height = destPR->h;
#if 0
g_printerr ("subsample_region: (%d x %d) -> (%d x %d)\n",
orig_width, orig_height, width, height);
#endif
/* Some calculations... */
bytes = destPR->bytes;
destwidth = destPR->rowstride;
/* the data pointers... */
src = g_new (guchar, orig_width * bytes);
dest = destPR->data;
/* find the ratios of old x to new x and old y to new y */
x_rat = (gdouble) orig_width / (gdouble) width;
y_rat = (gdouble) orig_height / (gdouble) height;
/* allocate an array to help with the calculations */
row = g_new (gdouble, width * bytes);
x_frac = g_new (gdouble, width + orig_width);
/* initialize the pre-calculated pixel fraction array */
src_col = 0;
x_cum = (gdouble) src_col;
x_last = x_cum;
for (i = 0; i < width + orig_width; i++)
{
if (x_cum + x_rat <= (src_col + 1 + EPSILON))
{
x_cum += x_rat;
x_frac[i] = x_cum - x_last;
}
else
{
src_col ++;
x_frac[i] = src_col - x_last;
}
x_last += x_frac[i];
}
/* clear the "row" array */
memset (row, 0, sizeof (gdouble) * width * bytes);
/* counters... */
src_row = 0;
y_cum = (gdouble) src_row;
y_last = y_cum;
pixel_region_get_row (srcPR, 0, src_row * subsample, orig_width * subsample, src, subsample);
/* Scale the selected region */
for (i = 0; i < height; )
{
src_col = 0;
x_cum = (gdouble) src_col;
/* determine the fraction of the src pixel we are using for y */
if (y_cum + y_rat <= (src_row + 1 + EPSILON))
{
y_cum += y_rat;
y_frac = y_cum - y_last;
advance_dest = TRUE;
}
else
{
src_row ++;
y_frac = src_row - y_last;
advance_dest = FALSE;
}
y_last += y_frac;
s = src;
r = row;
frac = 0;
j = width;
while (j)
{
tot_frac = x_frac[frac++] * y_frac;
for (b = 0; b < bytes; b++)
r[b] += s[b] * tot_frac;
/* increment the destination */
if (x_cum + x_rat <= (src_col + 1 + EPSILON))
{
r += bytes;
x_cum += x_rat;
j--;
}
/* increment the source */
else
{
s += bytes;
src_col++;
}
}
if (advance_dest)
{
tot_frac = 1.0 / (x_rat * y_rat);
/* copy "row" to "dest" */
d = dest;
r = row;
j = width;
while (j--)
{
b = bytes;
while (b--)
*d++ = (guchar) (*r++ * tot_frac + 0.5);
}
dest += destwidth;
/* clear the "row" array */
memset (row, 0, sizeof (gdouble) * destwidth);
i++;
}
else
pixel_region_get_row (srcPR, 0, src_row * subsample, orig_width * subsample, src, subsample);
}
/* free up temporary arrays */
g_free (row);
g_free (x_frac);
g_free (src);
}
gfloat
shapeburst_region (PixelRegion *srcPR,

View File

@ -369,12 +369,6 @@ void border_region (PixelRegion *src,
gint16 xradius,
gint16 yradius);
void scale_region (PixelRegion *srcPR,
PixelRegion *destPR,
GimpInterpolationType interpolation,
GimpProgressFunc progress_callback,
gpointer progress_data);
void subsample_region (PixelRegion *srcPR,
PixelRegion *destPR,
gint subsample);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,37 @@
/* The GIMP -- an image manipulation program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
*
* 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.
*/
#ifndef __SCALE_FUNCS_H__
#define __SCALE_FUNCS_H__
#define EPSILON (0.0001)
#define LANCZOS_SPP (1000)
#define LANCZOS_WIDTH (2)
#define LANCZOS_SAMPLES (1 + (LANCZOS_SPP * LANCZOS_WIDTH))
#define LANCZOS_WIDTH2 (LANCZOS_WIDTH * 2)
void scale_region (PixelRegion *srcPR,
PixelRegion *destPR,
GimpInterpolationType interpolation,
GimpProgressFunc progress_callback,
gpointer progress_data);
#endif /* __SCALE_FUNCS_H__ */