Commit 99111b63 authored by jaycox's avatar jaycox
Browse files

added InterpolationType enum. replaced cubic_interpolation flag with


	* app/apptypes.h: added InterpolationType enum.
	* app/gimprc.[ch], app/preferences_dialog.c: replaced
	cubic_interpolation flag with interpolation_type variable.

	* app/pixel_region.[ch]: added pixel_region_has_alpha function.

	* app/paint_funcs.c: rewrote scale_region.  It now behaves
	correctly on images with alpha, no longer leaves an artifact on the
	right edge of images when scailing up, and runs signifigantly
	faster.
parent 6322090d
Fri Aug 20 02:17:18 1999 Jay Cox (jaycox@earthlink.net)
* app/apptypes.h: added InterpolationType enum.
* app/gimprc.[ch], app/preferences_dialog.c: replaced
cubic_interpolation flag with interpolation_type variable.
* app/pixel_region.[ch]: added pixel_region_has_alpha function.
* app/paint_funcs.c: rewrote scale_region. It now behaves
correctly on images with alpha, no longer leaves an artifact on the
right edge of images when scailing up, and runs signifigantly
faster.
Fri Aug 20 11:21:57 1999 ape@gandalf.spacetec.no (Asbjorn Pettersen)
* app/tips_dialog.c (read_tips_file): open tips file with "rt".
......
......@@ -127,6 +127,14 @@ typedef enum {
} GradientPaintMode;
/* gradient paint modes */
typedef enum
{
LINEAR_INTERPOLATION,
CUBIC_INTERPOLATION,
NEAREST_NEIGHBOR_INTERPOLATION
} InterpolationType;
typedef struct _GimpChannel GimpChannel;
typedef struct _GimpChannelClass GimpChannelClass;
......
......@@ -271,6 +271,14 @@ pixel_region_set_col (PixelRegion *PR,
}
}
int
pixel_region_has_alpha(PixelRegion *PR)
{
if (PR->bytes == 2 || PR->bytes == 4)
return 1;
return 0;
}
void *
pixel_regions_register (int num_regions,
...)
......
......@@ -47,8 +47,12 @@ void pixel_region_get_row (PixelRegion *, int, int, int, unsigned char *,
void pixel_region_set_row (PixelRegion *, int, int, int, unsigned char *);
void pixel_region_get_col (PixelRegion *, int, int, int, unsigned char *, int);
void pixel_region_set_col (PixelRegion *, int, int, int, unsigned char *);
int pixel_region_has_alpha (PixelRegion *);
void *pixel_regions_register (int, ...);
void *pixel_regions_process (void *);
void pixel_regions_process_stop (void *);
#endif /* __PIXEL_REGION_H__ */
......@@ -68,7 +68,7 @@ static int old_no_cursor_updating;
static int old_show_tool_tips;
static int old_show_rulers;
static int old_show_statusbar;
static int old_cubic_interpolation;
static InterpolationType old_interpolation_type;
static int old_confirm_on_close;
static int old_save_session_info;
static int old_save_device_status;
......@@ -360,8 +360,8 @@ file_prefs_save_callback (GtkWidget *widget,
update = g_list_append (update, "show-statusbar");
remove = g_list_append (remove, "dont-show-statusbar");
}
if (cubic_interpolation != old_cubic_interpolation)
update = g_list_append (update, "cubic-interpolation");
if (interpolation_type != old_interpolation_type)
update = g_list_append (update, "interpolation-type");
if (confirm_on_close != old_confirm_on_close)
{
update = g_list_append (update, "confirm-on-close");
......@@ -556,7 +556,7 @@ file_prefs_cancel_callback (GtkWidget *widget,
show_tool_tips = old_show_tool_tips;
show_rulers = old_show_rulers;
show_statusbar = old_show_statusbar;
cubic_interpolation = old_cubic_interpolation;
interpolation_type = old_interpolation_type;
confirm_on_close = old_confirm_on_close;
save_session_info = old_save_session_info;
save_device_status = old_save_device_status;
......@@ -634,8 +634,6 @@ file_prefs_toggle_callback (GtkWidget *widget,
show_rulers = GTK_TOGGLE_BUTTON (widget)->active;
else if (data == &show_statusbar)
show_statusbar = GTK_TOGGLE_BUTTON (widget)->active;
else if (data == &cubic_interpolation)
cubic_interpolation = GTK_TOGGLE_BUTTON (widget)->active;
else if (data == &confirm_on_close)
confirm_on_close = GTK_TOGGLE_BUTTON (widget)->active;
else if (data == &save_session_info)
......@@ -669,7 +667,7 @@ file_prefs_toggle_callback (GtkWidget *widget,
context_manager_set_global_paint_options (GTK_TOGGLE_BUTTON (widget)->active);
else if (data == &show_indicators)
show_indicators = GTK_TOGGLE_BUTTON (widget)->active;
else if (data == &thumbnail_mode)
else if (data == &thumbnail_mode || data == &interpolation_type)
{
val = data;
*val = (long) gtk_object_get_user_data (GTK_OBJECT (widget));
......@@ -1426,7 +1424,7 @@ file_pref_cmd_callback (GtkWidget *widget,
old_show_tool_tips = show_tool_tips;
old_show_rulers = show_rulers;
old_show_statusbar = show_statusbar;
old_cubic_interpolation = cubic_interpolation;
old_interpolation_type = interpolation_type;
old_confirm_on_close = confirm_on_close;
old_save_session_info = save_session_info;
old_save_device_status = save_device_status;
......@@ -1705,14 +1703,20 @@ file_pref_cmd_callback (GtkWidget *widget,
gtk_container_add (GTK_CONTAINER (frame), vbox2);
gtk_widget_show (vbox2);
button = gtk_check_button_new_with_label(_("Cubic Interpolation"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
cubic_interpolation);
gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
gtk_signal_connect (GTK_OBJECT (button), "toggled",
(GtkSignalFunc) file_prefs_toggle_callback,
&cubic_interpolation);
gtk_widget_show (button);
optionmenu =
gimp_option_menu_new (file_prefs_toggle_callback,
(gpointer) interpolation_type,
_("Nearest Neighbor"), &interpolation_type,
(gpointer) NEAREST_NEIGHBOR_INTERPOLATION,
_("Linear"), &interpolation_type,
(gpointer) LINEAR_INTERPOLATION,
_("Cubic"), &interpolation_type,
(gpointer) CUBIC_INTERPOLATION,
NULL);
gtk_box_pack_start (GTK_BOX (vbox2), optionmenu, FALSE, FALSE, 0);
gtk_widget_show (optionmenu);
/* Interface */
vbox = file_prefs_notebook_append_page (GTK_NOTEBOOK (notebook),
......
......@@ -64,6 +64,7 @@ typedef enum {
TT_POSITION,
TT_MEMSIZE,
TT_IMAGETYPE,
TT_INTERP,
TT_XCOLORCUBE,
TT_XPREVSIZE,
TT_XUNIT,
......@@ -130,6 +131,7 @@ int show_statusbar = TRUE;
GUnit default_units = UNIT_INCH;
int auto_save = TRUE;
int cubic_interpolation = FALSE;
InterpolationType interpolation_type = LINEAR_INTERPOLATION;
int confirm_on_close = TRUE;
int save_session_info = TRUE;
int save_device_status = FALSE;
......@@ -168,6 +170,7 @@ static int parse_boolean (gpointer val1p, gpointer val2p);
static int parse_position (gpointer val1p, gpointer val2p);
static int parse_mem_size (gpointer val1p, gpointer val2p);
static int parse_image_type (gpointer val1p, gpointer val2p);
static int parse_interpolation_type (gpointer val1p, gpointer val2p);
static int parse_color_cube (gpointer val1p, gpointer val2p);
static int parse_preview_size (gpointer val1p, gpointer val2p);
static int parse_units (gpointer val1p, gpointer val2p);
......@@ -259,7 +262,7 @@ static ParseFunc funcs[] =
{ "default-units", TT_XUNIT, &default_units, NULL },
{ "auto-save", TT_BOOLEAN, &auto_save, NULL },
{ "dont-auto-save", TT_BOOLEAN, NULL, &auto_save },
{ "cubic-interpolation", TT_BOOLEAN, &cubic_interpolation, NULL },
{ "interpolation-type", TT_INTERP, &interpolation_type, NULL },
{ "confirm-on-close", TT_BOOLEAN, &confirm_on_close, NULL },
{ "dont-confirm-on-close", TT_BOOLEAN, NULL, &confirm_on_close },
{ "save-session-info", TT_BOOLEAN, &save_session_info, NULL },
......@@ -768,6 +771,8 @@ parse_statement ()
return parse_mem_size (funcs[i].val1p, funcs[i].val2p);
case TT_IMAGETYPE:
return parse_image_type (funcs[i].val1p, funcs[i].val2p);
case TT_INTERP:
return parse_interpolation_type (funcs[i].val1p, funcs[i].val2p);
case TT_XCOLORCUBE:
return parse_color_cube (funcs[i].val1p, funcs[i].val2p);
case TT_XPREVSIZE:
......@@ -1105,6 +1110,38 @@ parse_image_type (gpointer val1p,
return OK;
}
static int
parse_interpolation_type (gpointer val1p,
gpointer val2p)
{
int token;
InterpolationType *typep;
g_assert (val1p != NULL);
typep = (InterpolationType *)val1p;
token = peek_next_token ();
if (!token || (token != TOKEN_SYMBOL))
return ERROR;
token = get_next_token ();
if (strcmp (token_sym, "nearest-neighbor") == 0)
*typep = NEAREST_NEIGHBOR_INTERPOLATION;
else if (strcmp (token_sym, "linear") == 0)
*typep = LINEAR_INTERPOLATION;
else if (strcmp (token_sym, "cubic") == 0)
*typep = CUBIC_INTERPOLATION;
else
return ERROR;
token = peek_next_token ();
if (!token || (token != TOKEN_RIGHT_PAREN))
return ERROR;
token = get_next_token ();
return OK;
}
static int
parse_color_cube (gpointer val1p,
gpointer val2p)
......@@ -2303,6 +2340,8 @@ value_to_str (char *name)
return mem_size_to_str (funcs[i].val1p, funcs[i].val2p);
case TT_IMAGETYPE:
return image_type_to_str (funcs[i].val1p, funcs[i].val2p);
case TT_INTERP:
return interpolation_type_to_str (funcs[i].val1p, funcs[i].val2p);
case TT_XCOLORCUBE:
return color_cube_to_str (funcs[i].val1p, funcs[i].val2p);
case TT_XPREVSIZE:
......@@ -2417,6 +2456,26 @@ image_type_to_str (gpointer val1p,
return g_strdup ("rgb");
}
static inline char *
interpolation_type_to_str (gpointer val1p,
gpointer val2p)
{
InterpolationType type;
type = *((InterpolationType *)val1p);
switch (type)
{
case LINEAR_INTERPOLATION:
return g_strdup ("linear");
case CUBIC_INTERPOLATION:
return g_strdup ("cubic");
case NEAREST_NEIGHBOR_INTERPOLATION:
return g_strdup ("nearest-neighbor");
default:
return g_strdup ("bad interpolation type");
}
}
static inline char *
color_cube_to_str (gpointer val1p,
gpointer val2p)
......
......@@ -20,6 +20,7 @@
#include <glib.h>
#include "libgimp/gimpunit.h"
#include "apptypes.h"
/* global gimprc variables */
extern char * plug_in_path;
......@@ -56,6 +57,7 @@ extern GUnit default_units;
extern int show_statusbar;
extern int auto_save;
extern int cubic_interpolation;
extern InterpolationType interpolation_type;
extern int confirm_on_close;
extern int default_width, default_height;
extern int default_type;
......
......@@ -68,7 +68,7 @@ static int old_no_cursor_updating;
static int old_show_tool_tips;
static int old_show_rulers;
static int old_show_statusbar;
static int old_cubic_interpolation;
static InterpolationType old_interpolation_type;
static int old_confirm_on_close;
static int old_save_session_info;
static int old_save_device_status;
......@@ -360,8 +360,8 @@ file_prefs_save_callback (GtkWidget *widget,
update = g_list_append (update, "show-statusbar");
remove = g_list_append (remove, "dont-show-statusbar");
}
if (cubic_interpolation != old_cubic_interpolation)
update = g_list_append (update, "cubic-interpolation");
if (interpolation_type != old_interpolation_type)
update = g_list_append (update, "interpolation-type");
if (confirm_on_close != old_confirm_on_close)
{
update = g_list_append (update, "confirm-on-close");
......@@ -556,7 +556,7 @@ file_prefs_cancel_callback (GtkWidget *widget,
show_tool_tips = old_show_tool_tips;
show_rulers = old_show_rulers;
show_statusbar = old_show_statusbar;
cubic_interpolation = old_cubic_interpolation;
interpolation_type = old_interpolation_type;
confirm_on_close = old_confirm_on_close;
save_session_info = old_save_session_info;
save_device_status = old_save_device_status;
......@@ -634,8 +634,6 @@ file_prefs_toggle_callback (GtkWidget *widget,
show_rulers = GTK_TOGGLE_BUTTON (widget)->active;
else if (data == &show_statusbar)
show_statusbar = GTK_TOGGLE_BUTTON (widget)->active;
else if (data == &cubic_interpolation)
cubic_interpolation = GTK_TOGGLE_BUTTON (widget)->active;
else if (data == &confirm_on_close)
confirm_on_close = GTK_TOGGLE_BUTTON (widget)->active;
else if (data == &save_session_info)
......@@ -669,7 +667,7 @@ file_prefs_toggle_callback (GtkWidget *widget,
context_manager_set_global_paint_options (GTK_TOGGLE_BUTTON (widget)->active);
else if (data == &show_indicators)
show_indicators = GTK_TOGGLE_BUTTON (widget)->active;
else if (data == &thumbnail_mode)
else if (data == &thumbnail_mode || data == &interpolation_type)
{
val = data;
*val = (long) gtk_object_get_user_data (GTK_OBJECT (widget));
......@@ -1426,7 +1424,7 @@ file_pref_cmd_callback (GtkWidget *widget,
old_show_tool_tips = show_tool_tips;
old_show_rulers = show_rulers;
old_show_statusbar = show_statusbar;
old_cubic_interpolation = cubic_interpolation;
old_interpolation_type = interpolation_type;
old_confirm_on_close = confirm_on_close;
old_save_session_info = save_session_info;
old_save_device_status = save_device_status;
......@@ -1705,14 +1703,20 @@ file_pref_cmd_callback (GtkWidget *widget,
gtk_container_add (GTK_CONTAINER (frame), vbox2);
gtk_widget_show (vbox2);
button = gtk_check_button_new_with_label(_("Cubic Interpolation"));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button),
cubic_interpolation);
gtk_box_pack_start (GTK_BOX (vbox2), button, FALSE, FALSE, 0);
gtk_signal_connect (GTK_OBJECT (button), "toggled",
(GtkSignalFunc) file_prefs_toggle_callback,
&cubic_interpolation);
gtk_widget_show (button);
optionmenu =
gimp_option_menu_new (file_prefs_toggle_callback,
(gpointer) interpolation_type,
_("Nearest Neighbor"), &interpolation_type,
(gpointer) NEAREST_NEIGHBOR_INTERPOLATION,
_("Linear"), &interpolation_type,
(gpointer) LINEAR_INTERPOLATION,
_("Cubic"), &interpolation_type,
(gpointer) CUBIC_INTERPOLATION,
NULL);
gtk_box_pack_start (GTK_BOX (vbox2), optionmenu, FALSE, FALSE, 0);
gtk_widget_show (optionmenu);
/* Interface */
vbox = file_prefs_notebook_append_page (GTK_NOTEBOOK (notebook),
......
......@@ -44,7 +44,7 @@
#define INT_MULT(a,b,t) ((t) = (a) * (b) + 0x80, ((((t) >> 8) + (t)) >> 8))
/* This version of INT_MULT3 is very fast, but suffers from some
slight roundoff errors. It returns the correct result 99.987%
slight roundoff errors. It returns the correct result 99.987
percent of the time */
#define INT_MULT3(a,b,c,t) ((t) = (a) * (b) * (c)+ 0x7F5B, \
((((t) >> 7) + (t)) >> 16))
......@@ -131,6 +131,7 @@ static void apply_layer_mode_replace (unsigned char *, unsigned char *,
unsigned char *, unsigned char *,
int, int, int,
int, int, int, int *);
static void rotate_pointers(void **p, guint32 n);
void
......@@ -474,6 +475,7 @@ draw_segments (PixelRegion *destPR,
}
#endif
/* Note: cubic function no longer clips result */
static double
cubic (double dx,
int jm1,
......@@ -483,14 +485,23 @@ cubic (double dx,
{
double result;
#if 0
/* Equivalent to Gimp 1.1.1 and earlier - some ringing */
result = ((( ( - jm1 + j - jp1 + jp2 ) * dx +
( jm1 + jm1 - j - j + jp1 - jp2 ) ) * dx +
( - jm1 + jp1 ) ) * dx + j );
/* Recommended by Mitchell and Netravali - too blurred? */
result = ((( ( - 7 * jm1 + 21 * j - 21 * jp1 + 7 * jp2 ) * dx +
( 15 * jm1 - 36 * j + 27 * jp1 - 6 * jp2 ) ) * dx +
( - 9 * jm1 + 9 * jp1 ) ) * dx + (jm1 + 16 * j + jp1) ) / 18.0;
#else
if (result < 0.0)
result = 0.0;
if (result > 255.0)
result = 255.0;
/* Catmull-Rom - not bad */
result = ((( ( - jm1 + 3 * j - 3 * jp1 + jp2 ) * dx +
( 2 * jm1 - 5 * j + 4 * jp1 - jp2 ) ) * dx +
( - jm1 + jp1 ) ) * dx + (j + j) ) / 2.0;
#endif
return result;
}
......@@ -3812,28 +3823,183 @@ scale_region_no_resample (PixelRegion *srcPR,
}
static void
get_premultiplied_double_row(PixelRegion *srcPR, int x, int y,
int w, double *row, guchar *tmp_src, int n,
int extra)
{
int b;
int 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 */
double *irow = row;
int alpha = bytes - 1;
double 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 < extra * bytes; b++)
row[-extra*bytes + b] = row[(b%bytes)];
for (b = 0; b < bytes * extra; b++)
row[w*bytes + b] = row[(w - 1) * bytes + (b%bytes)];
}
static
void expand_line(double *dest, double *src, int bytes,
int old_width, int width, InterpolationType interp)
{
double ratio;
int x,b;
int src_col;
double frac;
double *s;
ratio = old_width/(double)width;
/* this could be opimized much more by precalculating the coeficients for
each x */
switch(interp)
{
case CUBIC_INTERPOLATION:
for (x = 0; x < width; x++)
{
src_col = ((int)((x - 0.5) * ratio + 2.0)) - 2;
/* +2, -2 is there because (int) rounds towards 0 and we need
to round down */
frac = ((x - 0.5) * ratio) - 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 LINEAR_INTERPOLATION:
for (x = 0; x < width; x++)
{
src_col = ((int)((x - 0.5) * ratio + 2.0)) - 2;
/* +2, -2 is there because (int) rounds towards 0 and we need
to round down */
frac = ((x - 0.5) * ratio) - 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 NEAREST_NEIGHBOR_INTERPOLATION:
g_error("sampling_type can't be "
"NEAREST_NEIGHBOR_INTERPOLATION");
}
}
static void shrink_line(double *dest, double *src, int bytes,
int old_width, int width, InterpolationType interp)
{
int x, b;
double *source, *destp;
register double accum;
register unsigned int max;
register double mant, tmp;
register const double step = old_width/(double)width;
register const double inv_step = 1.0/step;
double position;
for (b = 0; b < bytes; b++)
{
source = &src[b];
destp = &dest[b];
position = -1;
mant = *source;
for (x = 0; x < width; x++)
{
source+= bytes;
accum = 0;
max = ((int)(position+step)) - ((int)(position));
max--;
while (max)
{
accum += *source;
source += bytes;
max--;
}
tmp = accum + mant;
mant = ((position+step) - (int)(position + step));
mant *= *source;
tmp += mant;
tmp *= inv_step;
mant = *source - mant;
*(destp) = tmp;
destp += bytes;
position += step;
}
}
}
static void
get_scaled_row(void **src, int y, int new_width, PixelRegion *srcPR, double *row,
guchar *src_tmp, int extra_pixels)
{
/* get the necesary lines from the source image, scale them,
and put them into src[] */
rotate_pointers(src, 4);
if (y < 0)
y = 0;
if (y < srcPR->h)
{
if (new_width == srcPR->w) /* no scailing so load it straight to it's */
row = src[3]; /* destination */
get_premultiplied_double_row(srcPR, 0, y, srcPR->w,
row, src_tmp, 1, extra_pixels);
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
memcpy(src[3], src[2], sizeof (double) * new_width);
}
void
scale_region (PixelRegion *srcPR,
PixelRegion *destPR)
{
unsigned char * src_m1, * src, * src_p1, * src_p2;
unsigned char * s_m1, * s, * s_p1, * s_p2;
unsigned char * dest, * d;
double * row, * r;
int src_row, src_col;
double *src[4];
unsigned char * src_tmp;
unsigned char * dest;
double * row, *accum;
int bytes, b;
int width, height;
int orig_width, orig_height;
double x_rat, y_rat;
double x_cum, y_cum;
double x_last, y_last;
double * x_frac, y_frac, tot_frac;
float dx, dy;
int i, j;
int frac;
int advance_dest_x, advance_dest_y;
int minus_x, plus_x, plus2_x;
ScaleType scale_type;
int i;
int old_y = -3, new_y;
int x, y;
if (interpolation_type == NEAREST_NEIGHBOR_INTERPOLATION)
{
scale_region_no_resample (srcPR, destPR);
return;
}
orig_width = srcPR->w;
orig_height = srcPR->h;
......@@ -3841,234 +4007,166 @@ scale_region (PixelRegion *srcPR,
width = destPR->w;