ed Sep 1 21:27:27 BST 1999 Adam D. Moss <adam@gimp.org>

* app/convert.c app/convert.h:
        Optional low-bleed FS dithering

        * app/fsdither.h: Tables are const

        * app/convert_cmds.c app/internal_procs.c tools/pdbgen/enums.pl
        tools/pdbgen/pdb/convert.pdb:
        Clean up 'convert' PDB interface cruft.
This commit is contained in:
Adam D. Moss 1999-09-01 20:41:10 +00:00
parent e98b563a93
commit a437ece36f
13 changed files with 503 additions and 685 deletions

View File

@ -1,3 +1,14 @@
Wed Sep 1 21:27:27 BST 1999 Adam D. Moss <adam@gimp.org>
* app/convert.c app/convert.h:
Optional low-bleed FS dithering
* app/fsdither.h: Tables are const
* app/convert_cmds.c app/internal_procs.c tools/pdbgen/enums.pl
tools/pdbgen/pdb/convert.pdb:
Clean up 'convert' PDB interface cruft.
1999-09-01 Tor Lillqvist <tml@iki.fi> 1999-09-01 Tor Lillqvist <tml@iki.fi>
* app/appenv.h * app/appenv.h

View File

@ -25,6 +25,8 @@
*/ */
/* /*
* 99/09/01 - Created a low-bleed FS-dither option. [Adam]
*
* 99/08/29 - Deterministic colour dithering to arbitrary palettes. * 99/08/29 - Deterministic colour dithering to arbitrary palettes.
* Ideal for animations that are going to be delta-optimized or simply * Ideal for animations that are going to be delta-optimized or simply
* don't want to look 'busy' in static areas. Also a bunch of bugfixes * don't want to look 'busy' in static areas. Also a bunch of bugfixes
@ -163,6 +165,8 @@ static const unsigned char webpal[] =
0,51,0,0,0,255,0,0,204,0,0,153,0,0,102,0,0,51,0,0,0 0,51,0,0,0,255,0,0,204,0,0,153,0,0,102,0,0,51,0,0,0
}; };
/* Note: convert.c code currently makes assumptions about some of the
below defines, so small fixes are needed if they change... */
#define DM_WIDTH 128 #define DM_WIDTH 128
#define DM_WIDTHMASK 127 #define DM_WIDTHMASK 127
#define DM_WIDTH_SHIFT 7 #define DM_WIDTH_SHIFT 7
@ -333,6 +337,7 @@ struct _QuantizeObj
Histogram histogram; /* holds the histogram */ Histogram histogram; /* holds the histogram */
int want_alpha_dither; int want_alpha_dither;
int error_freedom; /* 0=much bleed, 1=controlled bleed */
}; };
typedef struct typedef struct
@ -373,6 +378,7 @@ typedef struct
GimpImage* gimage; GimpImage* gimage;
int nodither_flag; int nodither_flag;
int fsdither_flag; int fsdither_flag;
int fslowbleeddither_flag;
int fixeddither_flag; int fixeddither_flag;
int alphadither; /* flag */ int alphadither; /* flag */
int remdups; /* flag */ int remdups; /* flag */
@ -423,17 +429,18 @@ PaletteEntriesP theCustomPalette = NULL;
/* Defaults */ /* Defaults */
static int snum_cols = 256; static int snum_cols = 256;
static gboolean sfsdither_flag = TRUE; static gboolean sfsdither_flag = TRUE;
static gboolean snodither_flag = FALSE; static gboolean sfslowbleeddither_flag = TRUE;
static gboolean sfixeddither_flag = FALSE; static gboolean snodither_flag = FALSE;
static gboolean smakepal_flag = TRUE; static gboolean sfixeddither_flag = FALSE;
static gboolean salphadither_flag = FALSE; static gboolean smakepal_flag = TRUE;
static gboolean sremdups_flag = TRUE; static gboolean salphadither_flag = FALSE;
static gboolean swebpal_flag = FALSE; static gboolean sremdups_flag = TRUE;
static gboolean scustompal_flag = FALSE; static gboolean swebpal_flag = FALSE;
static gboolean smonopal_flag = FALSE; static gboolean scustompal_flag = FALSE;
static gboolean sreusepal_flag = FALSE; static gboolean smonopal_flag = FALSE;
static gboolean sreusepal_flag = FALSE;
void void
@ -479,6 +486,7 @@ convert_to_indexed (GimpImage *gimage)
dialog->num_cols = snum_cols; dialog->num_cols = snum_cols;
dialog->nodither_flag = snodither_flag; dialog->nodither_flag = snodither_flag;
dialog->fsdither_flag = sfsdither_flag; dialog->fsdither_flag = sfsdither_flag;
dialog->fslowbleeddither_flag = sfslowbleeddither_flag;
dialog->fixeddither_flag = sfixeddither_flag; dialog->fixeddither_flag = sfixeddither_flag;
dialog->alphadither = salphadither_flag; dialog->alphadither = salphadither_flag;
dialog->remdups = sremdups_flag; dialog->remdups = sremdups_flag;
@ -683,7 +691,24 @@ convert_to_indexed (GimpImage *gimage)
{ {
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
toggle = gtk_radio_button_new_with_label (group, _("Floyd-Steinberg colour dithering")); toggle = gtk_radio_button_new_with_label (group, _("Floyd-Steinberg colour dithering (reduced colour bleeding)"));
group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
(GtkSignalFunc) indexed_radio_update,
&(dialog->fslowbleeddither_flag));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), dialog->fslowbleeddither_flag);
gtk_widget_show (toggle);
}
gtk_widget_show (hbox);
hbox = gtk_hbox_new (FALSE, 1);
{
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
toggle = gtk_radio_button_new_with_label (group, _("Floyd-Steinberg colour dithering (normal)"));
group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle)); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
@ -897,11 +922,13 @@ indexed_ok_callback (GtkWidget *widget,
else else
palette_type = REUSE_PALETTE; palette_type = REUSE_PALETTE;
if (dialog->nodither_flag) dither_type = NODITHER; if (dialog->nodither_flag) dither_type = NO_DITHER;
else else
if (dialog->fsdither_flag) dither_type = FSDITHER; if (dialog->fsdither_flag) dither_type = FS_DITHER;
else else
dither_type = FIXEDDITHER; if (dialog->fslowbleeddither_flag) dither_type = FSLOWBLEED_DITHER;
else
dither_type = FIXED_DITHER;
/* Convert the image to indexed color */ /* Convert the image to indexed color */
convert_image (dialog->gimage, INDEXED, dialog->num_cols, convert_image (dialog->gimage, INDEXED, dialog->num_cols,
@ -914,6 +941,7 @@ indexed_ok_callback (GtkWidget *widget,
snum_cols = dialog->num_cols; snum_cols = dialog->num_cols;
snodither_flag = dialog->nodither_flag; snodither_flag = dialog->nodither_flag;
sfsdither_flag = dialog->fsdither_flag; sfsdither_flag = dialog->fsdither_flag;
sfslowbleeddither_flag = dialog->fslowbleeddither_flag;
sfixeddither_flag = dialog->fixeddither_flag; sfixeddither_flag = dialog->fixeddither_flag;
salphadither_flag = dialog->alphadither; salphadither_flag = dialog->alphadither;
sremdups_flag = dialog->remdups; sremdups_flag = dialog->remdups;
@ -1232,7 +1260,7 @@ convert_image (GImage *gimage,
/* don't dither if the input is grayscale and we are simply mapping every color */ /* don't dither if the input is grayscale and we are simply mapping every color */
if (old_type == GRAY && num_cols == 256 && palette_type == MAKE_PALETTE) if (old_type == GRAY && num_cols == 256 && palette_type == MAKE_PALETTE)
dither = NODITHER; dither = NO_DITHER;
quantobj = initialize_median_cut (old_type, num_cols, dither, quantobj = initialize_median_cut (old_type, num_cols, dither,
palette_type, alpha_dither); palette_type, alpha_dither);
@ -1288,7 +1316,7 @@ convert_image (GImage *gimage,
quantobj->delete_func (quantobj); quantobj->delete_func (quantobj);
quantobj = initialize_median_cut (old_type, num_cols, quantobj = initialize_median_cut (old_type, num_cols,
NODESTRUCTDITHER, palette_type, NODESTRUCT_DITHER, palette_type,
alpha_dither); alpha_dither);
/* We can skip the first pass (palette creation) */ /* We can skip the first pass (palette creation) */
@ -3423,55 +3451,66 @@ median_cut_pass2_nodestruct_dither_rgb (QuantizeObj *quantobj,
*/ */
static int * static int *
init_error_limit (void) init_error_limit (const int error_freedom)
/* Allocate and fill in the error_limiter table */ /* Allocate and fill in the error_limiter table */
{ {
int *table; int *table;
int in, out; int in, out;
/* #define STEPSIZE 16 */
/* #define STEPSIZE 200 */
table = g_malloc (sizeof (int) * (255 * 2 + 1)); table = g_malloc (sizeof (int) * (255 * 2 + 1));
table += 255; /* so we can index -255 ... +255 */ table += 255; /* so we can index -255 ... +255 */
/* #define STEPSIZE 16 */ if (error_freedom == 0)
#define STEPSIZE 200
for (in = 0; in < STEPSIZE; in++)
{ {
table[in] = in; /* Coarse function, much bleeding. */
table[-in] = -in;
const int STEPSIZE = 190;
for (in = 0; in < STEPSIZE; in++)
{
table[in] = in;
table[-in] = -in;
}
for (; in <= 255; in++)
{
table[in] = STEPSIZE;
table[-in] = -STEPSIZE;
}
return (table);
} }
for (; in <= 255; in++) else
{ {
table[in] = STEPSIZE; /* Smooth function, bleeding more constrained */
table[-in] = -STEPSIZE;
const int STEPSIZE = 24;
/* Map errors 1:1 up to +- STEPSIZE */
out = 0;
for (in = 0; in < STEPSIZE; in++, out++)
{
table[in] = out;
table[-in] = -out;
}
/* Map errors 1:2 up to +- 3*STEPSIZE */
for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1)
{
table[in] = out;
table[-in] = -out;
}
/* Clamp the rest to final out value (which is STEPSIZE*2) */
for (; in <= 255; in++)
{
table[in] = out;
table[-in] = -out;
}
return table;
} }
return (table);
/* Map errors 1:1 up to +- 16 */
out = 0;
for (in = 0; in < STEPSIZE; in++, out++)
{
table[in] = out;
table[-in] = -out;
}
/* Map errors 1:2 up to +- 3*16 */
for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1)
{
table[in] = out;
table[-in] = -out;
}
/* Clamp the rest to final out value (which is 32) */
for (; in <= 255; in++)
{
table[in] = out;
table[-in] = -out;
}
#undef STEPSIZE
return table;
} }
@ -3490,9 +3529,9 @@ median_cut_pass2_fs_dither_gray (QuantizeObj *quantobj,
ColorFreq *cachep; ColorFreq *cachep;
Color *color; Color *color;
int *error_limiter; int *error_limiter;
short *fs_err1, *fs_err2; const short *fs_err1, *fs_err2;
short *fs_err3, *fs_err4; const short *fs_err3, *fs_err4;
short *range_limiter; const short *range_limiter;
int src_bytes, dest_bytes; int src_bytes, dest_bytes;
unsigned char *src, *dest; unsigned char *src, *dest;
unsigned char *src_buf, *dest_buf; unsigned char *src_buf, *dest_buf;
@ -3521,7 +3560,7 @@ median_cut_pass2_fs_dither_gray (QuantizeObj *quantobj,
width = GIMP_DRAWABLE(layer)->width; width = GIMP_DRAWABLE(layer)->width;
height = GIMP_DRAWABLE(layer)->height; height = GIMP_DRAWABLE(layer)->height;
error_limiter = init_error_limit (); error_limiter = init_error_limit (quantobj->error_freedom);
range_limiter = range_array + 256; range_limiter = range_array + 256;
src_buf = g_malloc (width * src_bytes); src_buf = g_malloc (width * src_bytes);
@ -3686,9 +3725,9 @@ median_cut_pass2_fs_dither_rgb (QuantizeObj *quantobj,
ColorFreq *cachep; ColorFreq *cachep;
Color *color; Color *color;
int *error_limiter; int *error_limiter;
short *fs_err1, *fs_err2; const short *fs_err1, *fs_err2;
short *fs_err3, *fs_err4; const short *fs_err3, *fs_err4;
short *range_limiter; const short *range_limiter;
int src_bytes, dest_bytes; int src_bytes, dest_bytes;
unsigned char *src, *dest; unsigned char *src, *dest;
unsigned char *src_buf, *dest_buf; unsigned char *src_buf, *dest_buf;
@ -3733,7 +3772,7 @@ median_cut_pass2_fs_dither_rgb (QuantizeObj *quantobj,
width = GIMP_DRAWABLE(layer)->width; width = GIMP_DRAWABLE(layer)->width;
height = GIMP_DRAWABLE(layer)->height; height = GIMP_DRAWABLE(layer)->height;
error_limiter = init_error_limit (); error_limiter = init_error_limit (quantobj->error_freedom);
range_limiter = range_array + 256; range_limiter = range_array + 256;
src_buf = g_malloc (width * src_bytes); src_buf = g_malloc (width * src_bytes);
@ -3986,15 +4025,24 @@ initialize_median_cut (int type,
palette_type == MONO_PALETTE || palette_type == CUSTOM_PALETTE) palette_type == MONO_PALETTE || palette_type == CUSTOM_PALETTE)
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NODESTRUCT_DITHER:
default:
g_warning("Uh-oh, bad dither type, W1");
case NO_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_no_dither_rgb; quantobj->second_pass = median_cut_pass2_no_dither_rgb;
break; break;
case FSDITHER: case FS_DITHER:
quantobj->error_freedom = 0;
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb; quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break; break;
case FIXEDDITHER: case FSLOWBLEED_DITHER:
quantobj->error_freedom = 1;
quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break;
case FIXED_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fixed_dither_rgb; quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
break; break;
@ -4002,15 +4050,24 @@ initialize_median_cut (int type,
else else
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NODESTRUCT_DITHER:
default:
g_warning("Uh-oh, bad dither type, W2");
case NO_DITHER:
quantobj->second_pass_init = median_cut_pass2_gray_init; quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_no_dither_gray; quantobj->second_pass = median_cut_pass2_no_dither_gray;
break; break;
case FSDITHER: case FS_DITHER:
quantobj->error_freedom = 0;
quantobj->second_pass_init = median_cut_pass2_gray_init; quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_fs_dither_gray; quantobj->second_pass = median_cut_pass2_fs_dither_gray;
break; break;
case FIXEDDITHER: case FSLOWBLEED_DITHER:
quantobj->error_freedom = 1;
quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_fs_dither_gray;
break;
case FIXED_DITHER:
quantobj->second_pass_init = median_cut_pass2_gray_init; quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_fixed_dither_gray; quantobj->second_pass = median_cut_pass2_fixed_dither_gray;
break; break;
@ -4036,19 +4093,25 @@ initialize_median_cut (int type,
} }
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NO_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_no_dither_rgb; quantobj->second_pass = median_cut_pass2_no_dither_rgb;
break; break;
case FSDITHER: case FS_DITHER:
quantobj->error_freedom = 0;
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb; quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break; break;
case NODESTRUCTDITHER: case FSLOWBLEED_DITHER:
quantobj->error_freedom = 1;
quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break;
case NODESTRUCT_DITHER:
quantobj->second_pass_init = NULL; quantobj->second_pass_init = NULL;
quantobj->second_pass = median_cut_pass2_nodestruct_dither_rgb; quantobj->second_pass = median_cut_pass2_nodestruct_dither_rgb;
break; break;
case FIXEDDITHER: case FIXED_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fixed_dither_rgb; quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
break; break;

View File

@ -34,11 +34,12 @@ typedef enum {
/* adam's extra dither stuff */ /* adam's extra dither stuff */
typedef enum { typedef enum {
NODITHER = 0, NO_DITHER = 0,
FSDITHER = 1, FS_DITHER = 1,
FIXEDDITHER = 3, FSLOWBLEED_DITHER = 2,
FIXED_DITHER = 3,
NODESTRUCTDITHER = 2 /* NEVER USE NODESTRUCTDITHER EXPLICITLY */ NODESTRUCT_DITHER = 4 /* NEVER USE NODESTRUCT_DITHER EXPLICITLY */
} ConvertDitherType; } ConvertDitherType;
#define MAXNUMCOLORS 256 #define MAXNUMCOLORS 256

View File

@ -29,8 +29,6 @@
static ProcRecord convert_rgb_proc; static ProcRecord convert_rgb_proc;
static ProcRecord convert_grayscale_proc; static ProcRecord convert_grayscale_proc;
static ProcRecord convert_indexed_proc; static ProcRecord convert_indexed_proc;
static ProcRecord convert_indexed_palette_proc;
static ProcRecord convert_indexed2_proc;
void void
register_convert_procs (void) register_convert_procs (void)
@ -38,8 +36,6 @@ register_convert_procs (void)
procedural_db_register (&convert_rgb_proc); procedural_db_register (&convert_rgb_proc);
procedural_db_register (&convert_grayscale_proc); procedural_db_register (&convert_grayscale_proc);
procedural_db_register (&convert_indexed_proc); procedural_db_register (&convert_indexed_proc);
procedural_db_register (&convert_indexed_palette_proc);
procedural_db_register (&convert_indexed2_proc);
} }
static Argument * static Argument *
@ -128,240 +124,6 @@ static ProcRecord convert_grayscale_proc =
static Argument * static Argument *
convert_indexed_invoker (Argument *args) convert_indexed_invoker (Argument *args)
{
gboolean success = TRUE;
GimpImage *gimage;
gint32 dither;
gint32 num_cols;
gimage = pdb_id_to_image (args[0].value.pdb_int);
if (gimage == NULL)
success = FALSE;
dither = args[1].value.pdb_int;
switch (dither)
{
case NODITHER:
case FSDITHER:
case FIXEDDITHER:
case NODESTRUCTDITHER:
break;
default:
success = FALSE;
break;
}
num_cols = args[2].value.pdb_int;
if (success)
{
if ((success = gimage_base_type (gimage) != INDEXED))
{
switch (dither)
{
case NODITHER:
case FSDITHER:
case FIXEDDITHER:
break;
default:
success = FALSE;
break;
}
}
if (num_cols < 1 || num_cols > MAXNUMCOLORS)
success = FALSE;
if (success)
convert_image ((void *) gimage, INDEXED, num_cols, dither, 0, 1, 0);
}
return procedural_db_return_args (&convert_indexed_proc, success);
}
static ProcArg convert_indexed_inargs[] =
{
{
PDB_IMAGE,
"image",
"The image"
},
{
PDB_INT32,
"dither",
"dither type (0=none, 1=fs, 3=fixed)"
},
{
PDB_INT32,
"num_cols",
"the number of colors to quantize to"
}
};
static ProcRecord convert_indexed_proc =
{
"gimp_convert_indexed",
"Convert specified image to indexed color",
"(Note: This procedure is deprecated in favour of convert_indexed2.) This procedure converts the specified image to indexed color. This process requires an image of type GRAY or RGB. The 'num_cols' arguments specifies how many colors the resulting image should be quantized to (1-256).",
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996",
PDB_INTERNAL,
3,
convert_indexed_inargs,
0,
NULL,
{ { convert_indexed_invoker } }
};
static Argument *
convert_indexed_palette_invoker (Argument *args)
{
gboolean success = TRUE;
GimpImage *gimage;
gint32 dither;
gint32 palette_type;
gint32 num_cols;
gchar *palette_name;
gimage = pdb_id_to_image (args[0].value.pdb_int);
if (gimage == NULL)
success = FALSE;
dither = args[1].value.pdb_int;
switch (dither)
{
case NODITHER:
case FSDITHER:
case FIXEDDITHER:
case NODESTRUCTDITHER:
break;
default:
success = FALSE;
break;
}
palette_type = args[2].value.pdb_int;
num_cols = args[3].value.pdb_int;
palette_name = (gchar *) args[4].value.pdb_pointer;
if (palette_name == NULL)
success = FALSE;
if (success)
{
if ((success = (gimage_base_type (gimage) != INDEXED)))
{
PaletteEntriesP entries, the_palette = NULL;
GSList *list;
switch (dither)
{
case NODITHER:
case FSDITHER:
case FIXEDDITHER:
break;
default:
success = FALSE;
break;
}
switch (palette_type)
{
case MAKE_PALETTE:
if (num_cols < 1 || num_cols > MAXNUMCOLORS)
success = FALSE;
break;
case REUSE_PALETTE:
case WEB_PALETTE:
case MONO_PALETTE:
break;
case CUSTOM_PALETTE:
if (!palette_entries_list)
palette_init_palettes (FALSE);
for (list = palette_entries_list; list; list = list->next)
{
entries = (PaletteEntriesP) list->data;
if (!strcmp (palette_name, entries->name))
{
the_palette = entries;
break;
}
}
if (the_palette == NULL)
success = FALSE;
else
theCustomPalette = the_palette;
break;
default:
success = FALSE;
}
}
if (success)
convert_image ((void *) gimage, INDEXED, num_cols, dither, 0, 1,
palette_type);
}
return procedural_db_return_args (&convert_indexed_palette_proc, success);
}
static ProcArg convert_indexed_palette_inargs[] =
{
{
PDB_IMAGE,
"image",
"The image"
},
{
PDB_INT32,
"dither",
"dither type (0=none, 1=fs, 3=fixed)"
},
{
PDB_INT32,
"palette_type",
"The type of palette to use: { MAKE_PALETTE (0), REUSE_PALETTE (1), WEB_PALETTE (2), MONO_PALETTE (3), CUSTOM_PALETTE (4) }"
},
{
PDB_INT32,
"num_cols",
"the number of colors to quantize to, ignored unless (palette_type == MAKE_PALETTE)"
},
{
PDB_STRING,
"palette",
"The name of the custom palette to use, ignored unless (palette_type == CUSTOM_PALETTE)"
}
};
static ProcRecord convert_indexed_palette_proc =
{
"gimp_convert_indexed_palette",
"Convert specified image to indexed color",
"(Note: This procedure is deprecated in favour of convert_indexed2.) This procedure converts the specified image to indexed color. This process requires an image of type GRAY or RGB. The 'palette_type' specifies what kind of palette to use, A type of '0' means to use an optimal palette of 'num_cols' generated from the colors in the image. A type of '1' means to re-use the previous palette (not currently implemented). A type of '2' means to use the so-called WWW-optimized palette. Type '3' means to use only black and white colors. A type of '4' means to use a palette from the gimp palettes directories.",
"Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis",
"1995-1996",
PDB_INTERNAL,
5,
convert_indexed_palette_inargs,
0,
NULL,
{ { convert_indexed_palette_invoker } }
};
static Argument *
convert_indexed2_invoker (Argument *args)
{ {
gboolean success = TRUE; gboolean success = TRUE;
GimpImage *gimage; GimpImage *gimage;
@ -377,18 +139,8 @@ convert_indexed2_invoker (Argument *args)
success = FALSE; success = FALSE;
dither_type = args[1].value.pdb_int; dither_type = args[1].value.pdb_int;
switch (dither_type) if (dither_type < NO_DITHER || dither_type > NODESTRUCT_DITHER)
{ success = FALSE;
case NODITHER:
case FSDITHER:
case FIXEDDITHER:
case NODESTRUCTDITHER:
break;
default:
success = FALSE;
break;
}
palette_type = args[2].value.pdb_int; palette_type = args[2].value.pdb_int;
@ -411,9 +163,10 @@ convert_indexed2_invoker (Argument *args)
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NO_DITHER:
case FSDITHER: case FS_DITHER:
case FIXEDDITHER: case FSLOWBLEED_DITHER:
case FIXED_DITHER:
break; break;
default: default:
success = FALSE; success = FALSE;
@ -463,10 +216,10 @@ convert_indexed2_invoker (Argument *args)
alpha_dither, remove_unused, palette_type); alpha_dither, remove_unused, palette_type);
} }
return procedural_db_return_args (&convert_indexed2_proc, success); return procedural_db_return_args (&convert_indexed_proc, success);
} }
static ProcArg convert_indexed2_inargs[] = static ProcArg convert_indexed_inargs[] =
{ {
{ {
PDB_IMAGE, PDB_IMAGE,
@ -476,7 +229,7 @@ static ProcArg convert_indexed2_inargs[] =
{ {
PDB_INT32, PDB_INT32,
"dither_type", "dither_type",
"dither type (0=none, 1=fs, 3=fixed)" "dither type (0=none, 1=fs, 2=fs/low-bleed 3=fixed)"
}, },
{ {
PDB_INT32, PDB_INT32,
@ -505,18 +258,18 @@ static ProcArg convert_indexed2_inargs[] =
} }
}; };
static ProcRecord convert_indexed2_proc = static ProcRecord convert_indexed_proc =
{ {
"gimp_convert_indexed2", "gimp_convert_indexed",
"Convert specified image to indexed color", "Convert specified image to and Indexed image",
"This procedure converts the specified image to indexed color. This process requires an image of type GRAY or RGB. The 'palette_type' specifies what kind of palette to use, A type of '0' means to use an optimal palette of 'num_cols' generated from the colors in the image. A type of '1' means to re-use the previous palette (not currently implemented). A type of '2' means to use the so-called WWW-optimized palette. Type '3' means to use only black and white colors. A type of '4' means to use a palette from the gimp palettes directories. The 'dither type' specifies what kind of dithering to use. '0' means no dithering, '1' means standard Floyd-Steinberg error diffusion, '3' means dithering based on pixel location ('Fixed' dithering).", "This procedure converts the specified image to 'indexed' color. This process requires an image of type GRAY or RGB. The 'palette_type' specifies what kind of palette to use, A type of '0' means to use an optimal palette of 'num_cols' generated from the colors in the image. A type of '1' means to re-use the previous palette (not currently implemented). A type of '2' means to use the so-called WWW-optimized palette. Type '3' means to use only black and white colors. A type of '4' means to use a palette from the gimp palettes directories. The 'dither type' specifies what kind of dithering to use. '0' means no dithering, '1' means standard Floyd-Steinberg error diffusion, '2' means Floyd-Steinberg error diffusion with reduced bleeding, '3' means dithering based on pixel location ('Fixed' dithering).",
"Spencer Kimball & Peter Mattis", "Spencer Kimball & Peter Mattis",
"Spencer Kimball & Peter Mattis", "Spencer Kimball & Peter Mattis",
"1995-1996", "1995-1996",
PDB_INTERNAL, PDB_INTERNAL,
7, 7,
convert_indexed2_inargs, convert_indexed_inargs,
0, 0,
NULL, NULL,
{ { convert_indexed2_invoker } } { { convert_indexed_invoker } }
}; };

View File

@ -25,7 +25,8 @@
* south-west pixels surrounding the current pixel respectively. * south-west pixels surrounding the current pixel respectively.
*/ */
short range_array[] = {
const short range_array[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -131,7 +132,7 @@ short range_array[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
}; };
short floyd_steinberg_error1[] = { const short floyd_steinberg_error1[] = {
-223, -223, -222, -222, -221, -221, -220, -220, -220, -219, -223, -223, -222, -222, -221, -221, -220, -220, -220, -219,
-219, -218, -218, -217, -217, -217, -216, -216, -215, -215, -219, -218, -218, -217, -217, -217, -216, -216, -215, -215,
-214, -214, -213, -213, -213, -212, -212, -211, -211, -210, -214, -214, -213, -213, -213, -212, -212, -211, -211, -210,
@ -237,7 +238,7 @@ short floyd_steinberg_error1[] = {
222, 223, 223, 224, 224, 222, 223, 223, 224, 224,
}; };
short floyd_steinberg_error2[] = { const short floyd_steinberg_error2[] = {
-95, -95, -95, -95, -95, -94, -94, -94, -94, -94, -95, -95, -95, -95, -95, -94, -94, -94, -94, -94,
-93, -93, -93, -93, -93, -93, -92, -92, -92, -92, -93, -93, -93, -93, -93, -93, -92, -92, -92, -92,
-92, -91, -91, -91, -91, -91, -90, -90, -90, -90, -92, -91, -91, -91, -91, -91, -90, -90, -90, -90,
@ -343,7 +344,7 @@ short floyd_steinberg_error2[] = {
95, 95, 95, 96, 96, 95, 95, 95, 96, 96,
}; };
short floyd_steinberg_error3[] = { const short floyd_steinberg_error3[] = {
-159, -159, -159, -158, -158, -158, -157, -157, -157, -156, -159, -159, -159, -158, -158, -158, -157, -157, -157, -156,
-156, -156, -155, -155, -155, -155, -154, -154, -154, -153, -156, -156, -155, -155, -155, -155, -154, -154, -154, -153,
-153, -153, -152, -152, -152, -151, -151, -151, -150, -150, -153, -153, -152, -152, -152, -151, -151, -151, -150, -150,
@ -449,7 +450,7 @@ short floyd_steinberg_error3[] = {
159, 159, 159, 160, 160, 159, 159, 159, 160, 160,
}; };
short floyd_steinberg_error4[] = { const short floyd_steinberg_error4[] = {
-34, -33, -33, -33, -33, -33, -34, -33, -32, -33, -34, -33, -33, -33, -33, -33, -34, -33, -32, -33,
-33, -33, -33, -33, -32, -31, -33, -32, -32, -32, -33, -33, -33, -33, -32, -31, -33, -32, -32, -32,
-32, -32, -33, -32, -31, -32, -32, -32, -32, -32, -32, -32, -33, -32, -31, -32, -32, -32, -32, -32,

View File

@ -25,6 +25,8 @@
*/ */
/* /*
* 99/09/01 - Created a low-bleed FS-dither option. [Adam]
*
* 99/08/29 - Deterministic colour dithering to arbitrary palettes. * 99/08/29 - Deterministic colour dithering to arbitrary palettes.
* Ideal for animations that are going to be delta-optimized or simply * Ideal for animations that are going to be delta-optimized or simply
* don't want to look 'busy' in static areas. Also a bunch of bugfixes * don't want to look 'busy' in static areas. Also a bunch of bugfixes
@ -163,6 +165,8 @@ static const unsigned char webpal[] =
0,51,0,0,0,255,0,0,204,0,0,153,0,0,102,0,0,51,0,0,0 0,51,0,0,0,255,0,0,204,0,0,153,0,0,102,0,0,51,0,0,0
}; };
/* Note: convert.c code currently makes assumptions about some of the
below defines, so small fixes are needed if they change... */
#define DM_WIDTH 128 #define DM_WIDTH 128
#define DM_WIDTHMASK 127 #define DM_WIDTHMASK 127
#define DM_WIDTH_SHIFT 7 #define DM_WIDTH_SHIFT 7
@ -333,6 +337,7 @@ struct _QuantizeObj
Histogram histogram; /* holds the histogram */ Histogram histogram; /* holds the histogram */
int want_alpha_dither; int want_alpha_dither;
int error_freedom; /* 0=much bleed, 1=controlled bleed */
}; };
typedef struct typedef struct
@ -373,6 +378,7 @@ typedef struct
GimpImage* gimage; GimpImage* gimage;
int nodither_flag; int nodither_flag;
int fsdither_flag; int fsdither_flag;
int fslowbleeddither_flag;
int fixeddither_flag; int fixeddither_flag;
int alphadither; /* flag */ int alphadither; /* flag */
int remdups; /* flag */ int remdups; /* flag */
@ -423,17 +429,18 @@ PaletteEntriesP theCustomPalette = NULL;
/* Defaults */ /* Defaults */
static int snum_cols = 256; static int snum_cols = 256;
static gboolean sfsdither_flag = TRUE; static gboolean sfsdither_flag = TRUE;
static gboolean snodither_flag = FALSE; static gboolean sfslowbleeddither_flag = TRUE;
static gboolean sfixeddither_flag = FALSE; static gboolean snodither_flag = FALSE;
static gboolean smakepal_flag = TRUE; static gboolean sfixeddither_flag = FALSE;
static gboolean salphadither_flag = FALSE; static gboolean smakepal_flag = TRUE;
static gboolean sremdups_flag = TRUE; static gboolean salphadither_flag = FALSE;
static gboolean swebpal_flag = FALSE; static gboolean sremdups_flag = TRUE;
static gboolean scustompal_flag = FALSE; static gboolean swebpal_flag = FALSE;
static gboolean smonopal_flag = FALSE; static gboolean scustompal_flag = FALSE;
static gboolean sreusepal_flag = FALSE; static gboolean smonopal_flag = FALSE;
static gboolean sreusepal_flag = FALSE;
void void
@ -479,6 +486,7 @@ convert_to_indexed (GimpImage *gimage)
dialog->num_cols = snum_cols; dialog->num_cols = snum_cols;
dialog->nodither_flag = snodither_flag; dialog->nodither_flag = snodither_flag;
dialog->fsdither_flag = sfsdither_flag; dialog->fsdither_flag = sfsdither_flag;
dialog->fslowbleeddither_flag = sfslowbleeddither_flag;
dialog->fixeddither_flag = sfixeddither_flag; dialog->fixeddither_flag = sfixeddither_flag;
dialog->alphadither = salphadither_flag; dialog->alphadither = salphadither_flag;
dialog->remdups = sremdups_flag; dialog->remdups = sremdups_flag;
@ -683,7 +691,24 @@ convert_to_indexed (GimpImage *gimage)
{ {
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
toggle = gtk_radio_button_new_with_label (group, _("Floyd-Steinberg colour dithering")); toggle = gtk_radio_button_new_with_label (group, _("Floyd-Steinberg colour dithering (reduced colour bleeding)"));
group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
(GtkSignalFunc) indexed_radio_update,
&(dialog->fslowbleeddither_flag));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), dialog->fslowbleeddither_flag);
gtk_widget_show (toggle);
}
gtk_widget_show (hbox);
hbox = gtk_hbox_new (FALSE, 1);
{
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
toggle = gtk_radio_button_new_with_label (group, _("Floyd-Steinberg colour dithering (normal)"));
group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle)); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
@ -897,11 +922,13 @@ indexed_ok_callback (GtkWidget *widget,
else else
palette_type = REUSE_PALETTE; palette_type = REUSE_PALETTE;
if (dialog->nodither_flag) dither_type = NODITHER; if (dialog->nodither_flag) dither_type = NO_DITHER;
else else
if (dialog->fsdither_flag) dither_type = FSDITHER; if (dialog->fsdither_flag) dither_type = FS_DITHER;
else else
dither_type = FIXEDDITHER; if (dialog->fslowbleeddither_flag) dither_type = FSLOWBLEED_DITHER;
else
dither_type = FIXED_DITHER;
/* Convert the image to indexed color */ /* Convert the image to indexed color */
convert_image (dialog->gimage, INDEXED, dialog->num_cols, convert_image (dialog->gimage, INDEXED, dialog->num_cols,
@ -914,6 +941,7 @@ indexed_ok_callback (GtkWidget *widget,
snum_cols = dialog->num_cols; snum_cols = dialog->num_cols;
snodither_flag = dialog->nodither_flag; snodither_flag = dialog->nodither_flag;
sfsdither_flag = dialog->fsdither_flag; sfsdither_flag = dialog->fsdither_flag;
sfslowbleeddither_flag = dialog->fslowbleeddither_flag;
sfixeddither_flag = dialog->fixeddither_flag; sfixeddither_flag = dialog->fixeddither_flag;
salphadither_flag = dialog->alphadither; salphadither_flag = dialog->alphadither;
sremdups_flag = dialog->remdups; sremdups_flag = dialog->remdups;
@ -1232,7 +1260,7 @@ convert_image (GImage *gimage,
/* don't dither if the input is grayscale and we are simply mapping every color */ /* don't dither if the input is grayscale and we are simply mapping every color */
if (old_type == GRAY && num_cols == 256 && palette_type == MAKE_PALETTE) if (old_type == GRAY && num_cols == 256 && palette_type == MAKE_PALETTE)
dither = NODITHER; dither = NO_DITHER;
quantobj = initialize_median_cut (old_type, num_cols, dither, quantobj = initialize_median_cut (old_type, num_cols, dither,
palette_type, alpha_dither); palette_type, alpha_dither);
@ -1288,7 +1316,7 @@ convert_image (GImage *gimage,
quantobj->delete_func (quantobj); quantobj->delete_func (quantobj);
quantobj = initialize_median_cut (old_type, num_cols, quantobj = initialize_median_cut (old_type, num_cols,
NODESTRUCTDITHER, palette_type, NODESTRUCT_DITHER, palette_type,
alpha_dither); alpha_dither);
/* We can skip the first pass (palette creation) */ /* We can skip the first pass (palette creation) */
@ -3423,55 +3451,66 @@ median_cut_pass2_nodestruct_dither_rgb (QuantizeObj *quantobj,
*/ */
static int * static int *
init_error_limit (void) init_error_limit (const int error_freedom)
/* Allocate and fill in the error_limiter table */ /* Allocate and fill in the error_limiter table */
{ {
int *table; int *table;
int in, out; int in, out;
/* #define STEPSIZE 16 */
/* #define STEPSIZE 200 */
table = g_malloc (sizeof (int) * (255 * 2 + 1)); table = g_malloc (sizeof (int) * (255 * 2 + 1));
table += 255; /* so we can index -255 ... +255 */ table += 255; /* so we can index -255 ... +255 */
/* #define STEPSIZE 16 */ if (error_freedom == 0)
#define STEPSIZE 200
for (in = 0; in < STEPSIZE; in++)
{ {
table[in] = in; /* Coarse function, much bleeding. */
table[-in] = -in;
const int STEPSIZE = 190;
for (in = 0; in < STEPSIZE; in++)
{
table[in] = in;
table[-in] = -in;
}
for (; in <= 255; in++)
{
table[in] = STEPSIZE;
table[-in] = -STEPSIZE;
}
return (table);
} }
for (; in <= 255; in++) else
{ {
table[in] = STEPSIZE; /* Smooth function, bleeding more constrained */
table[-in] = -STEPSIZE;
const int STEPSIZE = 24;
/* Map errors 1:1 up to +- STEPSIZE */
out = 0;
for (in = 0; in < STEPSIZE; in++, out++)
{
table[in] = out;
table[-in] = -out;
}
/* Map errors 1:2 up to +- 3*STEPSIZE */
for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1)
{
table[in] = out;
table[-in] = -out;
}
/* Clamp the rest to final out value (which is STEPSIZE*2) */
for (; in <= 255; in++)
{
table[in] = out;
table[-in] = -out;
}
return table;
} }
return (table);
/* Map errors 1:1 up to +- 16 */
out = 0;
for (in = 0; in < STEPSIZE; in++, out++)
{
table[in] = out;
table[-in] = -out;
}
/* Map errors 1:2 up to +- 3*16 */
for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1)
{
table[in] = out;
table[-in] = -out;
}
/* Clamp the rest to final out value (which is 32) */
for (; in <= 255; in++)
{
table[in] = out;
table[-in] = -out;
}
#undef STEPSIZE
return table;
} }
@ -3490,9 +3529,9 @@ median_cut_pass2_fs_dither_gray (QuantizeObj *quantobj,
ColorFreq *cachep; ColorFreq *cachep;
Color *color; Color *color;
int *error_limiter; int *error_limiter;
short *fs_err1, *fs_err2; const short *fs_err1, *fs_err2;
short *fs_err3, *fs_err4; const short *fs_err3, *fs_err4;
short *range_limiter; const short *range_limiter;
int src_bytes, dest_bytes; int src_bytes, dest_bytes;
unsigned char *src, *dest; unsigned char *src, *dest;
unsigned char *src_buf, *dest_buf; unsigned char *src_buf, *dest_buf;
@ -3521,7 +3560,7 @@ median_cut_pass2_fs_dither_gray (QuantizeObj *quantobj,
width = GIMP_DRAWABLE(layer)->width; width = GIMP_DRAWABLE(layer)->width;
height = GIMP_DRAWABLE(layer)->height; height = GIMP_DRAWABLE(layer)->height;
error_limiter = init_error_limit (); error_limiter = init_error_limit (quantobj->error_freedom);
range_limiter = range_array + 256; range_limiter = range_array + 256;
src_buf = g_malloc (width * src_bytes); src_buf = g_malloc (width * src_bytes);
@ -3686,9 +3725,9 @@ median_cut_pass2_fs_dither_rgb (QuantizeObj *quantobj,
ColorFreq *cachep; ColorFreq *cachep;
Color *color; Color *color;
int *error_limiter; int *error_limiter;
short *fs_err1, *fs_err2; const short *fs_err1, *fs_err2;
short *fs_err3, *fs_err4; const short *fs_err3, *fs_err4;
short *range_limiter; const short *range_limiter;
int src_bytes, dest_bytes; int src_bytes, dest_bytes;
unsigned char *src, *dest; unsigned char *src, *dest;
unsigned char *src_buf, *dest_buf; unsigned char *src_buf, *dest_buf;
@ -3733,7 +3772,7 @@ median_cut_pass2_fs_dither_rgb (QuantizeObj *quantobj,
width = GIMP_DRAWABLE(layer)->width; width = GIMP_DRAWABLE(layer)->width;
height = GIMP_DRAWABLE(layer)->height; height = GIMP_DRAWABLE(layer)->height;
error_limiter = init_error_limit (); error_limiter = init_error_limit (quantobj->error_freedom);
range_limiter = range_array + 256; range_limiter = range_array + 256;
src_buf = g_malloc (width * src_bytes); src_buf = g_malloc (width * src_bytes);
@ -3986,15 +4025,24 @@ initialize_median_cut (int type,
palette_type == MONO_PALETTE || palette_type == CUSTOM_PALETTE) palette_type == MONO_PALETTE || palette_type == CUSTOM_PALETTE)
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NODESTRUCT_DITHER:
default:
g_warning("Uh-oh, bad dither type, W1");
case NO_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_no_dither_rgb; quantobj->second_pass = median_cut_pass2_no_dither_rgb;
break; break;
case FSDITHER: case FS_DITHER:
quantobj->error_freedom = 0;
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb; quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break; break;
case FIXEDDITHER: case FSLOWBLEED_DITHER:
quantobj->error_freedom = 1;
quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break;
case FIXED_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fixed_dither_rgb; quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
break; break;
@ -4002,15 +4050,24 @@ initialize_median_cut (int type,
else else
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NODESTRUCT_DITHER:
default:
g_warning("Uh-oh, bad dither type, W2");
case NO_DITHER:
quantobj->second_pass_init = median_cut_pass2_gray_init; quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_no_dither_gray; quantobj->second_pass = median_cut_pass2_no_dither_gray;
break; break;
case FSDITHER: case FS_DITHER:
quantobj->error_freedom = 0;
quantobj->second_pass_init = median_cut_pass2_gray_init; quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_fs_dither_gray; quantobj->second_pass = median_cut_pass2_fs_dither_gray;
break; break;
case FIXEDDITHER: case FSLOWBLEED_DITHER:
quantobj->error_freedom = 1;
quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_fs_dither_gray;
break;
case FIXED_DITHER:
quantobj->second_pass_init = median_cut_pass2_gray_init; quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_fixed_dither_gray; quantobj->second_pass = median_cut_pass2_fixed_dither_gray;
break; break;
@ -4036,19 +4093,25 @@ initialize_median_cut (int type,
} }
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NO_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_no_dither_rgb; quantobj->second_pass = median_cut_pass2_no_dither_rgb;
break; break;
case FSDITHER: case FS_DITHER:
quantobj->error_freedom = 0;
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb; quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break; break;
case NODESTRUCTDITHER: case FSLOWBLEED_DITHER:
quantobj->error_freedom = 1;
quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break;
case NODESTRUCT_DITHER:
quantobj->second_pass_init = NULL; quantobj->second_pass_init = NULL;
quantobj->second_pass = median_cut_pass2_nodestruct_dither_rgb; quantobj->second_pass = median_cut_pass2_nodestruct_dither_rgb;
break; break;
case FIXEDDITHER: case FIXED_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fixed_dither_rgb; quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
break; break;

View File

@ -34,11 +34,12 @@ typedef enum {
/* adam's extra dither stuff */ /* adam's extra dither stuff */
typedef enum { typedef enum {
NODITHER = 0, NO_DITHER = 0,
FSDITHER = 1, FS_DITHER = 1,
FIXEDDITHER = 3, FSLOWBLEED_DITHER = 2,
FIXED_DITHER = 3,
NODESTRUCTDITHER = 2 /* NEVER USE NODESTRUCTDITHER EXPLICITLY */ NODESTRUCT_DITHER = 4 /* NEVER USE NODESTRUCT_DITHER EXPLICITLY */
} ConvertDitherType; } ConvertDitherType;
#define MAXNUMCOLORS 256 #define MAXNUMCOLORS 256

View File

@ -25,7 +25,8 @@
* south-west pixels surrounding the current pixel respectively. * south-west pixels surrounding the current pixel respectively.
*/ */
short range_array[] = {
const short range_array[] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -131,7 +132,7 @@ short range_array[] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
}; };
short floyd_steinberg_error1[] = { const short floyd_steinberg_error1[] = {
-223, -223, -222, -222, -221, -221, -220, -220, -220, -219, -223, -223, -222, -222, -221, -221, -220, -220, -220, -219,
-219, -218, -218, -217, -217, -217, -216, -216, -215, -215, -219, -218, -218, -217, -217, -217, -216, -216, -215, -215,
-214, -214, -213, -213, -213, -212, -212, -211, -211, -210, -214, -214, -213, -213, -213, -212, -212, -211, -211, -210,
@ -237,7 +238,7 @@ short floyd_steinberg_error1[] = {
222, 223, 223, 224, 224, 222, 223, 223, 224, 224,
}; };
short floyd_steinberg_error2[] = { const short floyd_steinberg_error2[] = {
-95, -95, -95, -95, -95, -94, -94, -94, -94, -94, -95, -95, -95, -95, -95, -94, -94, -94, -94, -94,
-93, -93, -93, -93, -93, -93, -92, -92, -92, -92, -93, -93, -93, -93, -93, -93, -92, -92, -92, -92,
-92, -91, -91, -91, -91, -91, -90, -90, -90, -90, -92, -91, -91, -91, -91, -91, -90, -90, -90, -90,
@ -343,7 +344,7 @@ short floyd_steinberg_error2[] = {
95, 95, 95, 96, 96, 95, 95, 95, 96, 96,
}; };
short floyd_steinberg_error3[] = { const short floyd_steinberg_error3[] = {
-159, -159, -159, -158, -158, -158, -157, -157, -157, -156, -159, -159, -159, -158, -158, -158, -157, -157, -157, -156,
-156, -156, -155, -155, -155, -155, -154, -154, -154, -153, -156, -156, -155, -155, -155, -155, -154, -154, -154, -153,
-153, -153, -152, -152, -152, -151, -151, -151, -150, -150, -153, -153, -152, -152, -152, -151, -151, -151, -150, -150,
@ -449,7 +450,7 @@ short floyd_steinberg_error3[] = {
159, 159, 159, 160, 160, 159, 159, 159, 160, 160,
}; };
short floyd_steinberg_error4[] = { const short floyd_steinberg_error4[] = {
-34, -33, -33, -33, -33, -33, -34, -33, -32, -33, -34, -33, -33, -33, -33, -33, -34, -33, -32, -33,
-33, -33, -33, -33, -32, -31, -33, -32, -32, -32, -33, -33, -33, -33, -32, -31, -33, -32, -32, -32,
-32, -32, -33, -32, -31, -32, -32, -32, -32, -32, -32, -32, -33, -32, -31, -32, -32, -32, -32, -32,

View File

@ -25,6 +25,8 @@
*/ */
/* /*
* 99/09/01 - Created a low-bleed FS-dither option. [Adam]
*
* 99/08/29 - Deterministic colour dithering to arbitrary palettes. * 99/08/29 - Deterministic colour dithering to arbitrary palettes.
* Ideal for animations that are going to be delta-optimized or simply * Ideal for animations that are going to be delta-optimized or simply
* don't want to look 'busy' in static areas. Also a bunch of bugfixes * don't want to look 'busy' in static areas. Also a bunch of bugfixes
@ -163,6 +165,8 @@ static const unsigned char webpal[] =
0,51,0,0,0,255,0,0,204,0,0,153,0,0,102,0,0,51,0,0,0 0,51,0,0,0,255,0,0,204,0,0,153,0,0,102,0,0,51,0,0,0
}; };
/* Note: convert.c code currently makes assumptions about some of the
below defines, so small fixes are needed if they change... */
#define DM_WIDTH 128 #define DM_WIDTH 128
#define DM_WIDTHMASK 127 #define DM_WIDTHMASK 127
#define DM_WIDTH_SHIFT 7 #define DM_WIDTH_SHIFT 7
@ -333,6 +337,7 @@ struct _QuantizeObj
Histogram histogram; /* holds the histogram */ Histogram histogram; /* holds the histogram */
int want_alpha_dither; int want_alpha_dither;
int error_freedom; /* 0=much bleed, 1=controlled bleed */
}; };
typedef struct typedef struct
@ -373,6 +378,7 @@ typedef struct
GimpImage* gimage; GimpImage* gimage;
int nodither_flag; int nodither_flag;
int fsdither_flag; int fsdither_flag;
int fslowbleeddither_flag;
int fixeddither_flag; int fixeddither_flag;
int alphadither; /* flag */ int alphadither; /* flag */
int remdups; /* flag */ int remdups; /* flag */
@ -423,17 +429,18 @@ PaletteEntriesP theCustomPalette = NULL;
/* Defaults */ /* Defaults */
static int snum_cols = 256; static int snum_cols = 256;
static gboolean sfsdither_flag = TRUE; static gboolean sfsdither_flag = TRUE;
static gboolean snodither_flag = FALSE; static gboolean sfslowbleeddither_flag = TRUE;
static gboolean sfixeddither_flag = FALSE; static gboolean snodither_flag = FALSE;
static gboolean smakepal_flag = TRUE; static gboolean sfixeddither_flag = FALSE;
static gboolean salphadither_flag = FALSE; static gboolean smakepal_flag = TRUE;
static gboolean sremdups_flag = TRUE; static gboolean salphadither_flag = FALSE;
static gboolean swebpal_flag = FALSE; static gboolean sremdups_flag = TRUE;
static gboolean scustompal_flag = FALSE; static gboolean swebpal_flag = FALSE;
static gboolean smonopal_flag = FALSE; static gboolean scustompal_flag = FALSE;
static gboolean sreusepal_flag = FALSE; static gboolean smonopal_flag = FALSE;
static gboolean sreusepal_flag = FALSE;
void void
@ -479,6 +486,7 @@ convert_to_indexed (GimpImage *gimage)
dialog->num_cols = snum_cols; dialog->num_cols = snum_cols;
dialog->nodither_flag = snodither_flag; dialog->nodither_flag = snodither_flag;
dialog->fsdither_flag = sfsdither_flag; dialog->fsdither_flag = sfsdither_flag;
dialog->fslowbleeddither_flag = sfslowbleeddither_flag;
dialog->fixeddither_flag = sfixeddither_flag; dialog->fixeddither_flag = sfixeddither_flag;
dialog->alphadither = salphadither_flag; dialog->alphadither = salphadither_flag;
dialog->remdups = sremdups_flag; dialog->remdups = sremdups_flag;
@ -683,7 +691,24 @@ convert_to_indexed (GimpImage *gimage)
{ {
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
toggle = gtk_radio_button_new_with_label (group, _("Floyd-Steinberg colour dithering")); toggle = gtk_radio_button_new_with_label (group, _("Floyd-Steinberg colour dithering (reduced colour bleeding)"));
group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
gtk_signal_connect (GTK_OBJECT (toggle), "toggled",
(GtkSignalFunc) indexed_radio_update,
&(dialog->fslowbleeddither_flag));
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (toggle), dialog->fslowbleeddither_flag);
gtk_widget_show (toggle);
}
gtk_widget_show (hbox);
hbox = gtk_hbox_new (FALSE, 1);
{
gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
toggle = gtk_radio_button_new_with_label (group, _("Floyd-Steinberg colour dithering (normal)"));
group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle)); group = gtk_radio_button_group (GTK_RADIO_BUTTON (toggle));
gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0); gtk_box_pack_start (GTK_BOX (hbox), toggle, FALSE, FALSE, 0);
@ -897,11 +922,13 @@ indexed_ok_callback (GtkWidget *widget,
else else
palette_type = REUSE_PALETTE; palette_type = REUSE_PALETTE;
if (dialog->nodither_flag) dither_type = NODITHER; if (dialog->nodither_flag) dither_type = NO_DITHER;
else else
if (dialog->fsdither_flag) dither_type = FSDITHER; if (dialog->fsdither_flag) dither_type = FS_DITHER;
else else
dither_type = FIXEDDITHER; if (dialog->fslowbleeddither_flag) dither_type = FSLOWBLEED_DITHER;
else
dither_type = FIXED_DITHER;
/* Convert the image to indexed color */ /* Convert the image to indexed color */
convert_image (dialog->gimage, INDEXED, dialog->num_cols, convert_image (dialog->gimage, INDEXED, dialog->num_cols,
@ -914,6 +941,7 @@ indexed_ok_callback (GtkWidget *widget,
snum_cols = dialog->num_cols; snum_cols = dialog->num_cols;
snodither_flag = dialog->nodither_flag; snodither_flag = dialog->nodither_flag;
sfsdither_flag = dialog->fsdither_flag; sfsdither_flag = dialog->fsdither_flag;
sfslowbleeddither_flag = dialog->fslowbleeddither_flag;
sfixeddither_flag = dialog->fixeddither_flag; sfixeddither_flag = dialog->fixeddither_flag;
salphadither_flag = dialog->alphadither; salphadither_flag = dialog->alphadither;
sremdups_flag = dialog->remdups; sremdups_flag = dialog->remdups;
@ -1232,7 +1260,7 @@ convert_image (GImage *gimage,
/* don't dither if the input is grayscale and we are simply mapping every color */ /* don't dither if the input is grayscale and we are simply mapping every color */
if (old_type == GRAY && num_cols == 256 && palette_type == MAKE_PALETTE) if (old_type == GRAY && num_cols == 256 && palette_type == MAKE_PALETTE)
dither = NODITHER; dither = NO_DITHER;
quantobj = initialize_median_cut (old_type, num_cols, dither, quantobj = initialize_median_cut (old_type, num_cols, dither,
palette_type, alpha_dither); palette_type, alpha_dither);
@ -1288,7 +1316,7 @@ convert_image (GImage *gimage,
quantobj->delete_func (quantobj); quantobj->delete_func (quantobj);
quantobj = initialize_median_cut (old_type, num_cols, quantobj = initialize_median_cut (old_type, num_cols,
NODESTRUCTDITHER, palette_type, NODESTRUCT_DITHER, palette_type,
alpha_dither); alpha_dither);
/* We can skip the first pass (palette creation) */ /* We can skip the first pass (palette creation) */
@ -3423,55 +3451,66 @@ median_cut_pass2_nodestruct_dither_rgb (QuantizeObj *quantobj,
*/ */
static int * static int *
init_error_limit (void) init_error_limit (const int error_freedom)
/* Allocate and fill in the error_limiter table */ /* Allocate and fill in the error_limiter table */
{ {
int *table; int *table;
int in, out; int in, out;
/* #define STEPSIZE 16 */
/* #define STEPSIZE 200 */
table = g_malloc (sizeof (int) * (255 * 2 + 1)); table = g_malloc (sizeof (int) * (255 * 2 + 1));
table += 255; /* so we can index -255 ... +255 */ table += 255; /* so we can index -255 ... +255 */
/* #define STEPSIZE 16 */ if (error_freedom == 0)
#define STEPSIZE 200
for (in = 0; in < STEPSIZE; in++)
{ {
table[in] = in; /* Coarse function, much bleeding. */
table[-in] = -in;
const int STEPSIZE = 190;
for (in = 0; in < STEPSIZE; in++)
{
table[in] = in;
table[-in] = -in;
}
for (; in <= 255; in++)
{
table[in] = STEPSIZE;
table[-in] = -STEPSIZE;
}
return (table);
} }
for (; in <= 255; in++) else
{ {
table[in] = STEPSIZE; /* Smooth function, bleeding more constrained */
table[-in] = -STEPSIZE;
const int STEPSIZE = 24;
/* Map errors 1:1 up to +- STEPSIZE */
out = 0;
for (in = 0; in < STEPSIZE; in++, out++)
{
table[in] = out;
table[-in] = -out;
}
/* Map errors 1:2 up to +- 3*STEPSIZE */
for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1)
{
table[in] = out;
table[-in] = -out;
}
/* Clamp the rest to final out value (which is STEPSIZE*2) */
for (; in <= 255; in++)
{
table[in] = out;
table[-in] = -out;
}
return table;
} }
return (table);
/* Map errors 1:1 up to +- 16 */
out = 0;
for (in = 0; in < STEPSIZE; in++, out++)
{
table[in] = out;
table[-in] = -out;
}
/* Map errors 1:2 up to +- 3*16 */
for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1)
{
table[in] = out;
table[-in] = -out;
}
/* Clamp the rest to final out value (which is 32) */
for (; in <= 255; in++)
{
table[in] = out;
table[-in] = -out;
}
#undef STEPSIZE
return table;
} }
@ -3490,9 +3529,9 @@ median_cut_pass2_fs_dither_gray (QuantizeObj *quantobj,
ColorFreq *cachep; ColorFreq *cachep;
Color *color; Color *color;
int *error_limiter; int *error_limiter;
short *fs_err1, *fs_err2; const short *fs_err1, *fs_err2;
short *fs_err3, *fs_err4; const short *fs_err3, *fs_err4;
short *range_limiter; const short *range_limiter;
int src_bytes, dest_bytes; int src_bytes, dest_bytes;
unsigned char *src, *dest; unsigned char *src, *dest;
unsigned char *src_buf, *dest_buf; unsigned char *src_buf, *dest_buf;
@ -3521,7 +3560,7 @@ median_cut_pass2_fs_dither_gray (QuantizeObj *quantobj,
width = GIMP_DRAWABLE(layer)->width; width = GIMP_DRAWABLE(layer)->width;
height = GIMP_DRAWABLE(layer)->height; height = GIMP_DRAWABLE(layer)->height;
error_limiter = init_error_limit (); error_limiter = init_error_limit (quantobj->error_freedom);
range_limiter = range_array + 256; range_limiter = range_array + 256;
src_buf = g_malloc (width * src_bytes); src_buf = g_malloc (width * src_bytes);
@ -3686,9 +3725,9 @@ median_cut_pass2_fs_dither_rgb (QuantizeObj *quantobj,
ColorFreq *cachep; ColorFreq *cachep;
Color *color; Color *color;
int *error_limiter; int *error_limiter;
short *fs_err1, *fs_err2; const short *fs_err1, *fs_err2;
short *fs_err3, *fs_err4; const short *fs_err3, *fs_err4;
short *range_limiter; const short *range_limiter;
int src_bytes, dest_bytes; int src_bytes, dest_bytes;
unsigned char *src, *dest; unsigned char *src, *dest;
unsigned char *src_buf, *dest_buf; unsigned char *src_buf, *dest_buf;
@ -3733,7 +3772,7 @@ median_cut_pass2_fs_dither_rgb (QuantizeObj *quantobj,
width = GIMP_DRAWABLE(layer)->width; width = GIMP_DRAWABLE(layer)->width;
height = GIMP_DRAWABLE(layer)->height; height = GIMP_DRAWABLE(layer)->height;
error_limiter = init_error_limit (); error_limiter = init_error_limit (quantobj->error_freedom);
range_limiter = range_array + 256; range_limiter = range_array + 256;
src_buf = g_malloc (width * src_bytes); src_buf = g_malloc (width * src_bytes);
@ -3986,15 +4025,24 @@ initialize_median_cut (int type,
palette_type == MONO_PALETTE || palette_type == CUSTOM_PALETTE) palette_type == MONO_PALETTE || palette_type == CUSTOM_PALETTE)
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NODESTRUCT_DITHER:
default:
g_warning("Uh-oh, bad dither type, W1");
case NO_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_no_dither_rgb; quantobj->second_pass = median_cut_pass2_no_dither_rgb;
break; break;
case FSDITHER: case FS_DITHER:
quantobj->error_freedom = 0;
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb; quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break; break;
case FIXEDDITHER: case FSLOWBLEED_DITHER:
quantobj->error_freedom = 1;
quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break;
case FIXED_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fixed_dither_rgb; quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
break; break;
@ -4002,15 +4050,24 @@ initialize_median_cut (int type,
else else
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NODESTRUCT_DITHER:
default:
g_warning("Uh-oh, bad dither type, W2");
case NO_DITHER:
quantobj->second_pass_init = median_cut_pass2_gray_init; quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_no_dither_gray; quantobj->second_pass = median_cut_pass2_no_dither_gray;
break; break;
case FSDITHER: case FS_DITHER:
quantobj->error_freedom = 0;
quantobj->second_pass_init = median_cut_pass2_gray_init; quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_fs_dither_gray; quantobj->second_pass = median_cut_pass2_fs_dither_gray;
break; break;
case FIXEDDITHER: case FSLOWBLEED_DITHER:
quantobj->error_freedom = 1;
quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_fs_dither_gray;
break;
case FIXED_DITHER:
quantobj->second_pass_init = median_cut_pass2_gray_init; quantobj->second_pass_init = median_cut_pass2_gray_init;
quantobj->second_pass = median_cut_pass2_fixed_dither_gray; quantobj->second_pass = median_cut_pass2_fixed_dither_gray;
break; break;
@ -4036,19 +4093,25 @@ initialize_median_cut (int type,
} }
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NO_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_no_dither_rgb; quantobj->second_pass = median_cut_pass2_no_dither_rgb;
break; break;
case FSDITHER: case FS_DITHER:
quantobj->error_freedom = 0;
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb; quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break; break;
case NODESTRUCTDITHER: case FSLOWBLEED_DITHER:
quantobj->error_freedom = 1;
quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
break;
case NODESTRUCT_DITHER:
quantobj->second_pass_init = NULL; quantobj->second_pass_init = NULL;
quantobj->second_pass = median_cut_pass2_nodestruct_dither_rgb; quantobj->second_pass = median_cut_pass2_nodestruct_dither_rgb;
break; break;
case FIXEDDITHER: case FIXED_DITHER:
quantobj->second_pass_init = median_cut_pass2_rgb_init; quantobj->second_pass_init = median_cut_pass2_rgb_init;
quantobj->second_pass = median_cut_pass2_fixed_dither_rgb; quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
break; break;

View File

@ -34,11 +34,12 @@ typedef enum {
/* adam's extra dither stuff */ /* adam's extra dither stuff */
typedef enum { typedef enum {
NODITHER = 0, NO_DITHER = 0,
FSDITHER = 1, FS_DITHER = 1,
FIXEDDITHER = 3, FSLOWBLEED_DITHER = 2,
FIXED_DITHER = 3,
NODESTRUCTDITHER = 2 /* NEVER USE NODESTRUCTDITHER EXPLICITLY */ NODESTRUCT_DITHER = 4 /* NEVER USE NODESTRUCT_DITHER EXPLICITLY */
} ConvertDitherType; } ConvertDitherType;
#define MAXNUMCOLORS 256 #define MAXNUMCOLORS 256

View File

@ -59,7 +59,7 @@ void register_tools_procs (void);
void register_undo_procs (void); void register_undo_procs (void);
void register_unit_procs (void); void register_unit_procs (void);
/* 311 procedures registered total */ /* 309 procedures registered total */
void void
internal_procs_init (void) internal_procs_init (void)
@ -73,87 +73,87 @@ internal_procs_init (void)
app_init_update_status (NULL, _("Channel"), 0.045); app_init_update_status (NULL, _("Channel"), 0.045);
register_channel_procs (); register_channel_procs ();
app_init_update_status (NULL, _("Channel Ops"), 0.09); app_init_update_status (NULL, _("Channel Ops"), 0.091);
register_channel_ops_procs (); register_channel_ops_procs ();
app_init_update_status (NULL, _("Color"), 0.096); app_init_update_status (NULL, _("Color"), 0.097);
register_color_procs (); register_color_procs ();
app_init_update_status (NULL, _("Convert"), 0.135); app_init_update_status (NULL, _("Convert"), 0.136);
register_convert_procs (); register_convert_procs ();
app_init_update_status (NULL, _("Drawable procedures"), 0.151); app_init_update_status (NULL, _("Drawable procedures"), 0.146);
register_drawable_procs (); register_drawable_procs ();
app_init_update_status (NULL, _("Edit procedures"), 0.222); app_init_update_status (NULL, _("Edit procedures"), 0.217);
register_edit_procs (); register_edit_procs ();
app_init_update_status (NULL, _("File Operations"), 0.241); app_init_update_status (NULL, _("File Operations"), 0.236);
register_fileops_procs (); register_fileops_procs ();
app_init_update_status (NULL, _("Floating selections"), 0.26); app_init_update_status (NULL, _("Floating selections"), 0.256);
register_floating_sel_procs (); register_floating_sel_procs ();
app_init_update_status (NULL, _("GDisplay procedures"), 0.28); app_init_update_status (NULL, _("GDisplay procedures"), 0.275);
register_gdisplay_procs (); register_gdisplay_procs ();
app_init_update_status (NULL, _("Image"), 0.289); app_init_update_status (NULL, _("Image"), 0.285);
register_gimage_procs (); register_gimage_procs ();
app_init_update_status (NULL, _("Image mask"), 0.469); app_init_update_status (NULL, _("Image mask"), 0.466);
register_gimage_mask_procs (); register_gimage_mask_procs ();
app_init_update_status (NULL, _("Gimprc procedures"), 0.524); app_init_update_status (NULL, _("Gimprc procedures"), 0.521);
register_gimprc_procs (); register_gimprc_procs ();
app_init_update_status (NULL, _("Gradients"), 0.534); app_init_update_status (NULL, _("Gradients"), 0.531);
register_gradient_procs (); register_gradient_procs ();
app_init_update_status (NULL, _("Gradient UI"), 0.55); app_init_update_status (NULL, _("Gradient UI"), 0.547);
register_gradient_select_procs (); register_gradient_select_procs ();
app_init_update_status (NULL, _("Guide procedures"), 0.563); app_init_update_status (NULL, _("Guide procedures"), 0.56);
register_guides_procs (); register_guides_procs ();
app_init_update_status (NULL, _("Interface"), 0.582); app_init_update_status (NULL, _("Interface"), 0.579);
register_interface_procs (); register_interface_procs ();
app_init_update_status (NULL, _("Layer"), 0.592); app_init_update_status (NULL, _("Layer"), 0.589);
register_layer_procs (); register_layer_procs ();
app_init_update_status (NULL, _("Miscellaneous"), 0.688); app_init_update_status (NULL, _("Miscellaneous"), 0.686);
register_misc_procs (); register_misc_procs ();
app_init_update_status (NULL, _("Palette"), 0.695); app_init_update_status (NULL, _("Palette"), 0.693);
register_palette_procs (); register_palette_procs ();
app_init_update_status (NULL, _("Parasite procedures"), 0.717); app_init_update_status (NULL, _("Parasite procedures"), 0.715);
register_parasite_procs (); register_parasite_procs ();
app_init_update_status (NULL, _("Paths"), 0.759); app_init_update_status (NULL, _("Paths"), 0.757);
register_paths_procs (); register_paths_procs ();
app_init_update_status (NULL, _("Pattern UI"), 0.791); app_init_update_status (NULL, _("Pattern UI"), 0.79);
register_pattern_select_procs (); register_pattern_select_procs ();
app_init_update_status (NULL, _("Patterns"), 0.801); app_init_update_status (NULL, _("Patterns"), 0.799);
register_patterns_procs (); register_patterns_procs ();
app_init_update_status (NULL, _("Plug-in"), 0.814); app_init_update_status (NULL, _("Plug-in"), 0.812);
register_plug_in_procs (); register_plug_in_procs ();
app_init_update_status (NULL, _("Procedural database"), 0.826); app_init_update_status (NULL, _("Procedural database"), 0.825);
register_procedural_db_procs (); register_procedural_db_procs ();
app_init_update_status (NULL, _("Text procedures"), 0.852); app_init_update_status (NULL, _("Text procedures"), 0.851);
register_text_tool_procs (); register_text_tool_procs ();
app_init_update_status (NULL, _("Tool procedures"), 0.865); app_init_update_status (NULL, _("Tool procedures"), 0.864);
register_tools_procs (); register_tools_procs ();
app_init_update_status (NULL, _("Undo"), 0.958); app_init_update_status (NULL, _("Undo"), 0.958);
register_undo_procs (); register_undo_procs ();
app_init_update_status (NULL, _("Units"), 0.965); app_init_update_status (NULL, _("Units"), 0.964);
register_unit_procs (); register_unit_procs ();
} }

View File

@ -249,13 +249,15 @@ package Gimp::CodeGen::enums;
CUSTOM_PALETTE => '4' } CUSTOM_PALETTE => '4' }
}, },
ConvertDitherType => ConvertDitherType =>
{ contig => 0, { contig => 1,
header => 'convert.h', header => 'convert.h',
symbols => [ qw(NODITHER FSDITHER FIXEDDITHER NODESTRUCTDITHER) ], symbols => [ qw(NO_DITHER FS_DITHER FSLOWBLEED_DITHER FIXED_DITHER
mapping => { NODITHER => '0', NODESTRUCT_DITHER) ],
FSDITHER => '1', mapping => { NO_DITHER => '0',
FIXEDDITHER => '3', FS_DITHER => '1',
NODESTRUCTDITHER => '2' } FSLOWBLEED_DITHER => '2',
FIXED_DITHER => '3',
NODESTRUCT_DITHER => '4' }
}, },
ConvolveType => ConvolveType =>
{ contig => 1, { contig => 1,

View File

@ -61,159 +61,17 @@ HELP
} }
sub convert_indexed { sub convert_indexed {
$blurb = 'Convert specified image to indexed color'; $blurb = 'Convert specified image to and Indexed image';
$help = <<'HELP'; $help = <<'HELP';
(Note: This procedure is deprecated in favour of convert_indexed2.) This procedure converts the specified image to indexed color. This process This procedure converts the specified image to 'indexed' color. This process
requires an image of type GRAY or RGB. The 'num_cols' arguments specifies how
many colors the resulting image should be quantized to (1-256).
HELP
&std_pdb_misc;
@inargs = (
&std_image_arg,
{ name => 'dither', type => 'enum ConvertDitherType',
desc => 'dither type (0=none, 1=fs, 3=fixed)' },
{ name => 'num_cols', type => 'int32',
desc => 'the number of colors to quantize to' }
);
%invoke = (
code => <<'CODE'
{
if ((success = gimage_base_type (gimage) != INDEXED))
{
switch (dither)
{
case NODITHER:
case FSDITHER:
case FIXEDDITHER:
break;
default:
success = FALSE;
break;
}
}
if (num_cols < 1 || num_cols > MAXNUMCOLORS)
success = FALSE;
if (success)
convert_image ((void *) gimage, INDEXED, num_cols, dither, 0, 1, 0);
}
CODE
);
}
sub convert_indexed_palette {
$blurb = 'Convert specified image to indexed color';
$help = <<'HELP';
(Note: This procedure is deprecated in favour of convert_indexed2.) This procedure converts the specified image to indexed color. This process
requires an image of type GRAY or RGB. The 'palette_type' specifies what kind
of palette to use, A type of '0' means to use an optimal palette of 'num_cols'
generated from the colors in the image. A type of '1' means to re-use the
previous palette (not currently implemented). A type of '2' means to use the so-called WWW-optimized palette. Type
'3' means to use only black and white colors. A type of '4' means to use a
palette from the gimp palettes directories.
HELP
&std_pdb_misc;
@inargs = (
&std_image_arg,
{ name => 'dither', type => 'enum ConvertDitherType',
desc => 'dither type (0=none, 1=fs, 3=fixed)' },
{ name => 'palette_type', type => 'enum ConvertPaletteType',
desc => 'The type of palette to use: { %%desc%% }', no_success => 1 },
{ name => 'num_cols', type => 'int32',
desc => 'the number of colors to quantize to, ignored unless
(palette_type == MAKE_PALETTE)' },
{ name => 'palette', type => 'string',
desc => 'The name of the custom palette to use, ignored unless
(palette_type == CUSTOM_PALETTE)',
alias => 'palette_name' }
);
%invoke = (
headers => [ qw(<string.h> "palette.h") ],
code => <<'CODE'
{
if ((success = (gimage_base_type (gimage) != INDEXED)))
{
PaletteEntriesP entries, the_palette = NULL;
GSList *list;
switch (dither)
{
case NODITHER:
case FSDITHER:
case FIXEDDITHER:
break;
default:
success = FALSE;
break;
}
switch (palette_type)
{
case MAKE_PALETTE:
if (num_cols < 1 || num_cols > MAXNUMCOLORS)
success = FALSE;
break;
case REUSE_PALETTE:
case WEB_PALETTE:
case MONO_PALETTE:
break;
case CUSTOM_PALETTE:
if (!palette_entries_list)
palette_init_palettes (FALSE);
for (list = palette_entries_list; list; list = list->next)
{
entries = (PaletteEntriesP) list->data;
if (!strcmp (palette_name, entries->name))
{
the_palette = entries;
break;
}
}
if (the_palette == NULL)
success = FALSE;
else
theCustomPalette = the_palette;
break;
default:
success = FALSE;
}
}
if (success)
convert_image ((void *) gimage, INDEXED, num_cols, dither, 0, 1,
palette_type);
}
CODE
);
}
sub convert_indexed2 {
$blurb = 'Convert specified image to indexed color';
$help = <<'HELP';
This procedure converts the specified image to indexed color. This process
requires an image of type GRAY or RGB. The 'palette_type' specifies what kind requires an image of type GRAY or RGB. The 'palette_type' specifies what kind
of palette to use, A type of '0' means to use an optimal palette of 'num_cols' of palette to use, A type of '0' means to use an optimal palette of 'num_cols'
generated from the colors in the image. A type of '1' means to re-use the generated from the colors in the image. A type of '1' means to re-use the
previous palette (not currently implemented). A type of '2' means to use the so-called WWW-optimized palette. Type previous palette (not currently implemented). A type of '2' means to use the so-called WWW-optimized palette. Type
'3' means to use only black and white colors. A type of '4' means to use a '3' means to use only black and white colors. A type of '4' means to use a
palette from the gimp palettes directories. The 'dither type' specifies palette from the gimp palettes directories. The 'dither type' specifies
what kind of dithering to use. '0' means no dithering, '1' means standard Floyd-Steinberg error diffusion, '3' means dithering based on pixel location ('Fixed' dithering). what kind of dithering to use. '0' means no dithering, '1' means standard Floyd-Steinberg error diffusion, '2' means Floyd-Steinberg error diffusion with reduced bleeding, '3' means dithering based on pixel location ('Fixed' dithering).
HELP HELP
&std_pdb_misc; &std_pdb_misc;
@ -221,7 +79,7 @@ HELP
@inargs = ( @inargs = (
&std_image_arg, &std_image_arg,
{ name => 'dither_type', type => 'enum ConvertDitherType', { name => 'dither_type', type => 'enum ConvertDitherType',
desc => 'dither type (0=none, 1=fs, 3=fixed)' }, desc => 'dither type (0=none, 1=fs, 2=fs/low-bleed 3=fixed)' },
{ name => 'palette_type', type => 'enum ConvertPaletteType', { name => 'palette_type', type => 'enum ConvertPaletteType',
desc => 'The type of palette to use: { %%desc%% }', no_success => 1 }, desc => 'The type of palette to use: { %%desc%% }', no_success => 1 },
{ name => 'num_cols', type => 'int32', { name => 'num_cols', type => 'int32',
@ -248,9 +106,10 @@ HELP
switch (dither_type) switch (dither_type)
{ {
case NODITHER: case NO_DITHER:
case FSDITHER: case FS_DITHER:
case FIXEDDITHER: case FSLOWBLEED_DITHER:
case FIXED_DITHER:
break; break;
default: default:
success = FALSE; success = FALSE;
@ -305,8 +164,7 @@ CODE
@headers = qw("gimage.h" "convert.h"); @headers = qw("gimage.h" "convert.h");
@procs = qw(convert_rgb convert_grayscale convert_indexed @procs = qw(convert_rgb convert_grayscale convert_indexed);
convert_indexed_palette convert_indexed2);
%exports = (app => [@procs]); %exports = (app => [@procs]);
$desc = 'Convert'; $desc = 'Convert';