1997-11-25 06:05:25 +08:00
/* 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
1998-04-13 13:44:11 +08:00
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
1997-11-25 06:05:25 +08:00
*/
/*
1999-02-27 05:30:11 +08:00
* ( out of date ) TODO for Convert :
1997-11-25 06:05:25 +08:00
*
* Use palette of another open INDEXED image
* Different dither types
* Alpha dithering
1999-02-27 05:30:11 +08:00
*
* Do error - splitting trick for GREY - > INDEXED
1997-11-25 06:05:25 +08:00
*/
/*
1999-02-27 05:30:11 +08:00
* 99 / 02 / 24 - Many revisions to the box - cut quantizer used in RGB - > INDEXED
* conversion . Box to be cut is chosen on the basis of posessing an axis
* with the largest sum of weighted perceptible error , rather than based on
* volume or population . The box is split along this axis rather than its
* longest axis , at the point of error mean rather than simply at its centre .
* Error - limiting in the F - S dither has been disabled - it may become optional
* again later . If you ' re convinced that you have an image where the old
* dither looks better , let me know . [ Adam ]
*
1999-01-11 07:36:29 +08:00
* 99 / 01 / 10 - Hourglass . . . [ Adam ]
*
1998-07-25 18:28:22 +08:00
* 98 / 07 / 25 - Convert - to - indexed now remembers the last invocation ' s
* settings . Also , GRAY - > INDEXED more flexible . [ Adam ]
*
1998-07-06 05:01:21 +08:00
* 98 / 07 / 05 - Sucked the warning about quantizing to too many colours into
* a text widget embedded in the dialog , improved intelligence of dialog
* to default ' custom palette ' selection to ' Web ' if available , and
* in this case not bother to present the native WWW - palette radio
* button . [ Adam ]
*
1998-04-13 22:26:46 +08:00
* 98 / 04 / 13 - avoid a division by zero when converting an empty gray - scale
* image ( who would like to do such a thing anyway ? ? ) [ Sven ]
*
1998-03-24 20:33:54 +08:00
* 98 / 03 / 23 - fixed a longstanding fencepost - hopefully the * right *
1998-07-06 05:01:21 +08:00
* way , * again * . [ Adam ]
1998-03-24 20:33:54 +08:00
*
1997-12-14 19:36:53 +08:00
* 97 / 11 / 14 - added a proper pdb interface and support for dithering
* to custom palettes ( based on a patch by Eric Hernes ) [ Yosh ]
*
1997-12-08 08:24:32 +08:00
* 97 / 11 / 04 - fixed the accidental use of the colour - counting case
* when palette_type is WEB or MONO . [ Adam ]
*
* 97 / 10 / 25 - colour - counting implemented ( could use some hashing , but
* performance actually seems okay ) - now RGB - > INDEXED conversion isn ' t
* destructive if it doesn ' t have to be . [ Adam ]
*
1997-11-25 06:05:25 +08:00
* 97 / 10 / 14 - fixed divide - by - zero when converting a completely transparent
* RGB image to indexed . [ Adam ]
*
* 97 / 07 / 01 - started todo / revision log . Put code back in to
* eliminate full - alpha pixels from RGB histogram .
* [ Adam D . Moss - adam @ gimp . org ]
*/
# include <stdlib.h>
1998-07-25 18:28:22 +08:00
# include <stdio.h>
1997-11-25 06:05:25 +08:00
# include <string.h>
# include <math.h>
1998-03-24 20:33:54 +08:00
1997-11-25 06:05:25 +08:00
# include "appenv.h"
# include "actionarea.h"
# include "convert.h"
1999-01-11 07:36:29 +08:00
# include "cursorutil.h"
1997-11-25 06:05:25 +08:00
# include "drawable.h"
# include "floating_sel.h"
# include "fsdither.h"
# include "gdisplay.h"
# include "interface.h"
# include "undo.h"
1997-12-14 19:36:53 +08:00
# include "palette.h"
1997-11-25 06:05:25 +08:00
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
# include "libgimp/gimpintl.h"
1998-01-22 15:02:57 +08:00
# include "layer_pvt.h" /* ick. */
# include "drawable_pvt.h" /* ick ick. */
# include "tile_manager_pvt.h" /* ick ick ick. */
1997-11-25 06:05:25 +08:00
# define NODITHER 0
# define FSDITHER 1
# define NODESTRUCTDITHER 2
# define PRECISION_R 6
# define PRECISION_G 6
# define PRECISION_B 5
# define HIST_R_ELEMS (1<<PRECISION_R)
# define HIST_G_ELEMS (1<<PRECISION_G)
# define HIST_B_ELEMS (1<<PRECISION_B)
# define MR HIST_G_ELEMS*HIST_B_ELEMS
# define MG HIST_B_ELEMS
# define BITS_IN_SAMPLE 8
# define R_SHIFT (BITS_IN_SAMPLE-PRECISION_R)
# define G_SHIFT (BITS_IN_SAMPLE-PRECISION_G)
# define B_SHIFT (BITS_IN_SAMPLE-PRECISION_B)
# define R_SCALE 30 /* scale R distances by this much */
# define G_SCALE 59 /* scale G distances by this much */
# define B_SCALE 11 /* and B by this much */
# define INTENSITY(r,g,b) (r * 0.30 + g * 0.59 + b * 0.11 + 0.001)
unsigned char webpal [ ] =
{
255 , 255 , 255 , 255 , 255 , 204 , 255 , 255 , 153 , 255 , 255 , 102 , 255 , 255 , 51 , 255 , 255 , 0 , 255 ,
204 , 255 , 255 , 204 , 204 , 255 , 204 , 153 , 255 , 204 , 102 , 255 , 204 , 51 , 255 , 204 , 0 , 255 , 153 ,
255 , 255 , 153 , 204 , 255 , 153 , 153 , 255 , 153 , 102 , 255 , 153 , 51 , 255 , 153 , 0 , 255 , 102 , 255 ,
255 , 102 , 204 , 255 , 102 , 153 , 255 , 102 , 102 , 255 , 102 , 51 , 255 , 102 , 0 , 255 , 51 , 255 , 255 ,
51 , 204 , 255 , 51 , 153 , 255 , 51 , 102 , 255 , 51 , 51 , 255 , 51 , 0 , 255 , 0 , 255 , 255 , 0 ,
204 , 255 , 0 , 153 , 255 , 0 , 102 , 255 , 0 , 51 , 255 , 0 , 0 , 204 , 255 , 255 , 204 , 255 , 204 ,
204 , 255 , 153 , 204 , 255 , 102 , 204 , 255 , 51 , 204 , 255 , 0 , 204 , 204 , 255 , 204 , 204 , 204 , 204 ,
204 , 153 , 204 , 204 , 102 , 204 , 204 , 51 , 204 , 204 , 0 , 204 , 153 , 255 , 204 , 153 , 204 , 204 , 153 ,
153 , 204 , 153 , 102 , 204 , 153 , 51 , 204 , 153 , 0 , 204 , 102 , 255 , 204 , 102 , 204 , 204 , 102 , 153 ,
204 , 102 , 102 , 204 , 102 , 51 , 204 , 102 , 0 , 204 , 51 , 255 , 204 , 51 , 204 , 204 , 51 , 153 , 204 ,
51 , 102 , 204 , 51 , 51 , 204 , 51 , 0 , 204 , 0 , 255 , 204 , 0 , 204 , 204 , 0 , 153 , 204 , 0 ,
102 , 204 , 0 , 51 , 204 , 0 , 0 , 153 , 255 , 255 , 153 , 255 , 204 , 153 , 255 , 153 , 153 , 255 , 102 ,
153 , 255 , 51 , 153 , 255 , 0 , 153 , 204 , 255 , 153 , 204 , 204 , 153 , 204 , 153 , 153 , 204 , 102 , 153 ,
204 , 51 , 153 , 204 , 0 , 153 , 153 , 255 , 153 , 153 , 204 , 153 , 153 , 153 , 153 , 153 , 102 , 153 , 153 ,
51 , 153 , 153 , 0 , 153 , 102 , 255 , 153 , 102 , 204 , 153 , 102 , 153 , 153 , 102 , 102 , 153 , 102 , 51 ,
153 , 102 , 0 , 153 , 51 , 255 , 153 , 51 , 204 , 153 , 51 , 153 , 153 , 51 , 102 , 153 , 51 , 51 , 153 ,
51 , 0 , 153 , 0 , 255 , 153 , 0 , 204 , 153 , 0 , 153 , 153 , 0 , 102 , 153 , 0 , 51 , 153 , 0 ,
0 , 102 , 255 , 255 , 102 , 255 , 204 , 102 , 255 , 153 , 102 , 255 , 102 , 102 , 255 , 51 , 102 , 255 , 0 ,
102 , 204 , 255 , 102 , 204 , 204 , 102 , 204 , 153 , 102 , 204 , 102 , 102 , 204 , 51 , 102 , 204 , 0 , 102 ,
153 , 255 , 102 , 153 , 204 , 102 , 153 , 153 , 102 , 153 , 102 , 102 , 153 , 51 , 102 , 153 , 0 , 102 , 102 ,
255 , 102 , 102 , 204 , 102 , 102 , 153 , 102 , 102 , 102 , 102 , 102 , 51 , 102 , 102 , 0 , 102 , 51 , 255 ,
102 , 51 , 204 , 102 , 51 , 153 , 102 , 51 , 102 , 102 , 51 , 51 , 102 , 51 , 0 , 102 , 0 , 255 , 102 ,
0 , 204 , 102 , 0 , 153 , 102 , 0 , 102 , 102 , 0 , 51 , 102 , 0 , 0 , 51 , 255 , 255 , 51 , 255 ,
204 , 51 , 255 , 153 , 51 , 255 , 102 , 51 , 255 , 51 , 51 , 255 , 0 , 51 , 204 , 255 , 51 , 204 , 204 ,
51 , 204 , 153 , 51 , 204 , 102 , 51 , 204 , 51 , 51 , 204 , 0 , 51 , 153 , 255 , 51 , 153 , 204 , 51 ,
153 , 153 , 51 , 153 , 102 , 51 , 153 , 51 , 51 , 153 , 0 , 51 , 102 , 255 , 51 , 102 , 204 , 51 , 102 ,
153 , 51 , 102 , 102 , 51 , 102 , 51 , 51 , 102 , 0 , 51 , 51 , 255 , 51 , 51 , 204 , 51 , 51 , 153 ,
51 , 51 , 102 , 51 , 51 , 51 , 51 , 51 , 0 , 51 , 0 , 255 , 51 , 0 , 204 , 51 , 0 , 153 , 51 ,
0 , 102 , 51 , 0 , 51 , 51 , 0 , 0 , 0 , 255 , 255 , 0 , 255 , 204 , 0 , 255 , 153 , 0 , 255 ,
102 , 0 , 255 , 51 , 0 , 255 , 0 , 0 , 204 , 255 , 0 , 204 , 204 , 0 , 204 , 153 , 0 , 204 , 102 ,
0 , 204 , 51 , 0 , 204 , 0 , 0 , 153 , 255 , 0 , 153 , 204 , 0 , 153 , 153 , 0 , 153 , 102 , 0 ,
153 , 51 , 0 , 153 , 0 , 0 , 102 , 255 , 0 , 102 , 204 , 0 , 102 , 153 , 0 , 102 , 102 , 0 , 102 ,
51 , 0 , 102 , 0 , 0 , 51 , 255 , 0 , 51 , 204 , 0 , 51 , 153 , 0 , 51 , 102 , 0 , 51 , 51 ,
0 , 51 , 0 , 0 , 0 , 255 , 0 , 0 , 204 , 0 , 0 , 153 , 0 , 0 , 102 , 0 , 0 , 51 , 0 , 0 , 0
} ;
typedef struct _Color Color ;
typedef struct _QuantizeObj QuantizeObj ;
typedef void ( * Pass1_Func ) ( QuantizeObj * ) ;
typedef void ( * Pass2_Func ) ( QuantizeObj * , Layer * , TileManager * ) ;
typedef void ( * Cleanup_Func ) ( QuantizeObj * ) ;
typedef unsigned long ColorFreq ;
typedef ColorFreq * Histogram ;
struct _Color
{
int red ;
int green ;
int blue ;
} ;
struct _QuantizeObj
{
Pass1_Func first_pass ; /* first pass over image data creates colormap */
Pass2_Func second_pass ; /* second pass maps from image data to colormap */
Cleanup_Func delete_func ; /* function to clean up data associated with private */
int desired_number_of_colors ; /* Number of colors we will allow */
int actual_number_of_colors ; /* Number of colors actually needed */
Color cmap [ 256 ] ; /* colormap created by quantization */
Histogram histogram ; /* holds the histogram */
} ;
typedef struct
{
/* The bounds of the box (inclusive); expressed as histogram indexes */
int Rmin , Rmax ;
1999-02-27 05:30:11 +08:00
int Rhalferror ;
1997-11-25 06:05:25 +08:00
int Gmin , Gmax ;
1999-02-27 05:30:11 +08:00
int Ghalferror ;
1997-11-25 06:05:25 +08:00
int Bmin , Bmax ;
1999-02-27 05:30:11 +08:00
int Bhalferror ;
1997-11-25 06:05:25 +08:00
/* The volume (actually 2-norm) of the box */
int volume ;
/* The number of nonzero histogram cells within this box */
long colorcount ;
1999-02-27 05:30:11 +08:00
/* The sum of the weighted error within this box */
guint64 error ;
/* The sum of the unweighted error within this box */
guint64 rerror ;
guint64 gerror ;
guint64 berror ;
1997-11-25 06:05:25 +08:00
} box , * boxptr ;
typedef struct
{
long ncolors ;
long dither ;
} Options ;
typedef struct
{
GtkWidget * shell ;
1998-06-29 08:24:44 +08:00
GimpImage * gimage ;
1997-11-25 06:05:25 +08:00
int dither ;
int num_cols ;
int palette ;
int makepal_flag ;
int webpal_flag ;
1997-12-14 19:36:53 +08:00
int custompal_flag ;
1997-11-25 06:05:25 +08:00
int monopal_flag ;
int reusepal_flag ;
} IndexedDialog ;
static void indexed_ok_callback ( GtkWidget * , gpointer ) ;
static void indexed_cancel_callback ( GtkWidget * , gpointer ) ;
static gint indexed_delete_callback ( GtkWidget * , GdkEvent * , gpointer ) ;
static void indexed_num_cols_update ( GtkWidget * , gpointer ) ;
static void indexed_radio_update ( GtkWidget * , gpointer ) ;
static void indexed_dither_update ( GtkWidget * , gpointer ) ;
static void rgb_converter ( Layer * , TileManager * , int ) ;
static void grayscale_converter ( Layer * , TileManager * , int ) ;
static void zero_histogram_gray ( Histogram ) ;
static void zero_histogram_rgb ( Histogram ) ;
static void generate_histogram_gray ( Histogram , Layer * ) ;
static void generate_histogram_rgb ( Histogram , Layer * , int col_limit ) ;
static QuantizeObj * initialize_median_cut ( int , int , int , int ) ;
1999-02-27 05:30:11 +08:00
static void
compute_color_rgb ( QuantizeObj * quantobj ,
Histogram histogram ,
boxptr boxp ,
int icolor ) ;
1997-11-25 06:05:25 +08:00
static unsigned char found_cols [ MAXNUMCOLORS ] [ 3 ] ;
static int num_found_cols ;
static gboolean needs_quantize ;
1997-12-14 19:36:53 +08:00
static GtkWidget * build_palette_menu ( int * default_palette ) ;
static void palette_entries_callback ( GtkWidget * w , gpointer client_data ) ;
1998-07-06 05:01:21 +08:00
static gboolean UserHasWebPal = FALSE ;
1997-11-25 06:05:25 +08:00
1999-03-28 14:55:29 +08:00
PaletteEntriesP theCustomPalette = NULL ;
1998-07-25 18:28:22 +08:00
/* Defaults */
static int snum_cols = 256 ;
static gboolean sdither = TRUE ;
static gboolean smakepal_flag = TRUE ;
static gboolean swebpal_flag = FALSE ;
static gboolean scustompal_flag = FALSE ;
static gboolean smonopal_flag = FALSE ;
static gboolean sreusepal_flag = FALSE ;
1997-11-25 06:05:25 +08:00
void
1998-06-29 08:24:44 +08:00
convert_to_rgb ( GimpImage * gimage )
1997-11-25 06:05:25 +08:00
{
convert_image ( gimage , RGB , 0 , 0 , 0 ) ;
gdisplays_flush ( ) ;
}
void
1998-06-29 08:24:44 +08:00
convert_to_grayscale ( GimpImage * gimage )
1997-11-25 06:05:25 +08:00
{
convert_image ( gimage , GRAY , 0 , 0 , 0 ) ;
gdisplays_flush ( ) ;
}
/* the action area structure */
static ActionAreaItem action_items [ ] =
{
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
{ N_ ( " OK " ) , indexed_ok_callback , NULL , NULL } ,
{ N_ ( " Cancel " ) , indexed_cancel_callback , NULL , NULL }
1997-11-25 06:05:25 +08:00
} ;
1999-01-24 02:49:52 +08:00
/*
1998-07-06 05:01:21 +08:00
static void
realize_text ( GtkWidget * text , gpointer data )
{
gtk_text_set_word_wrap ( GTK_TEXT ( text ) , TRUE ) ;
gtk_text_freeze ( GTK_TEXT ( text ) ) ;
gtk_text_insert ( GTK_TEXT ( text ) , NULL , & text - > style - > black , NULL ,
1998-10-10 02:01:35 +08:00
" You are attempting to convert an image with alpha/layers from RGB/GRAY to INDEXED. You should not generate a palette of more than 255 colors if you intend to create a transparent or animated GIF file from this image. " ,
1998-07-18 23:25:52 +08:00
- 1 ) ;
1998-07-06 05:01:21 +08:00
gtk_text_thaw ( GTK_TEXT ( text ) ) ;
}
1999-01-24 02:49:52 +08:00
*/
1998-07-06 05:01:21 +08:00
1997-11-25 06:05:25 +08:00
void
1998-06-29 08:24:44 +08:00
convert_to_indexed ( GimpImage * gimage )
1997-11-25 06:05:25 +08:00
{
IndexedDialog * dialog ;
GtkWidget * vbox ;
GtkWidget * hbox ;
GtkWidget * label ;
GtkWidget * text ;
GtkWidget * frame ;
GtkWidget * toggle ;
GSList * group = NULL ;
1998-06-29 08:24:44 +08:00
dialog = g_new ( IndexedDialog , 1 ) ;
dialog - > gimage = gimage ;
1998-07-25 18:28:22 +08:00
dialog - > num_cols = snum_cols ;
dialog - > dither = sdither ;
dialog - > makepal_flag = smakepal_flag ;
dialog - > webpal_flag = swebpal_flag ;
dialog - > custompal_flag = scustompal_flag ;
dialog - > monopal_flag = smonopal_flag ;
dialog - > reusepal_flag = sreusepal_flag ;
1997-11-25 06:05:25 +08:00
dialog - > shell = gtk_dialog_new ( ) ;
1998-01-26 06:13:00 +08:00
gtk_window_set_wmclass ( GTK_WINDOW ( dialog - > shell ) , " indexed_color_conversion " , " Gimp " ) ;
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
gtk_window_set_title ( GTK_WINDOW ( dialog - > shell ) , _ ( " Indexed Color Conversion " ) ) ;
1997-11-25 06:05:25 +08:00
gtk_signal_connect ( GTK_OBJECT ( dialog - > shell ) , " delete_event " ,
GTK_SIGNAL_FUNC ( indexed_delete_callback ) ,
dialog ) ;
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
frame = gtk_frame_new ( _ ( " Palette Options " ) ) ;
1997-11-25 06:05:25 +08:00
gtk_frame_set_shadow_type ( GTK_FRAME ( frame ) , GTK_SHADOW_ETCHED_IN ) ;
1998-12-04 07:01:44 +08:00
gtk_container_set_border_width ( GTK_CONTAINER ( frame ) , 2 ) ;
1997-11-25 06:05:25 +08:00
gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG ( dialog - > shell ) - > vbox ) , frame , TRUE , TRUE , 0 ) ;
gtk_widget_show ( frame ) ;
vbox = gtk_vbox_new ( FALSE , 1 ) ;
1998-12-04 07:01:44 +08:00
gtk_container_set_border_width ( GTK_CONTAINER ( vbox ) , 2 ) ;
gtk_container_set_border_width ( GTK_CONTAINER ( GTK_BOX ( GTK_DIALOG ( dialog - > shell ) - > vbox ) ) , 4 ) ;
1997-11-25 06:05:25 +08:00
/* put the vbox in the frame */
gtk_container_add ( GTK_CONTAINER ( frame ) , vbox ) ;
gtk_widget_show ( vbox ) ;
/* 'generate palette' */
hbox = gtk_hbox_new ( FALSE , 1 ) ;
1998-12-04 07:01:44 +08:00
gtk_container_set_border_width ( GTK_CONTAINER ( vbox ) , 2 ) ;
1997-11-25 06:05:25 +08:00
gtk_box_pack_start ( GTK_BOX ( vbox ) , hbox , FALSE , FALSE , 0 ) ;
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
toggle = gtk_radio_button_new_with_label ( group , _ ( " Generate optimal palette: " ) ) ;
1997-11-25 06:05:25 +08:00
group = gtk_radio_button_group ( GTK_RADIO_BUTTON ( toggle ) ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , toggle , TRUE , TRUE , 0 ) ;
gtk_signal_connect ( GTK_OBJECT ( toggle ) , " toggled " ,
( GtkSignalFunc ) indexed_radio_update ,
& ( dialog - > makepal_flag ) ) ;
1999-01-16 01:35:04 +08:00
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( toggle ) , dialog - > makepal_flag ) ;
1997-11-25 06:05:25 +08:00
gtk_widget_show ( toggle ) ;
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
label = gtk_label_new ( _ ( " # of colors: " ) ) ;
1997-11-25 06:05:25 +08:00
gtk_misc_set_alignment ( GTK_MISC ( label ) , 0.0 , 0.5 ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , label , TRUE , FALSE , 0 ) ;
gtk_widget_show ( label ) ;
text = gtk_entry_new ( ) ;
1998-07-06 05:01:21 +08:00
{
1998-07-25 18:28:22 +08:00
if ( dialog - > num_cols = = 256 )
1998-07-06 05:01:21 +08:00
{
1998-07-25 18:28:22 +08:00
if ( ( ! gimage_is_empty ( gimage ) )
& &
(
gimage - > layers - > next
| |
layer_has_alpha ( ( Layer * ) gimage - > layers - > data )
)
)
{
gtk_entry_set_text ( GTK_ENTRY ( text ) , " 255 " ) ;
dialog - > num_cols = 255 ;
}
else
{
gtk_entry_set_text ( GTK_ENTRY ( text ) , " 256 " ) ;
}
}
1998-07-06 05:01:21 +08:00
else
{
1998-07-25 18:28:22 +08:00
gchar tempstr [ 50 ] ;
sprintf ( tempstr , " %d " , dialog - > num_cols ) ;
gtk_entry_set_text ( GTK_ENTRY ( text ) , tempstr ) ;
1998-07-06 05:01:21 +08:00
}
gtk_widget_set_usize ( text , 50 , 25 ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , text , FALSE , FALSE , 0 ) ;
gtk_signal_connect ( GTK_OBJECT ( text ) , " changed " ,
( GtkSignalFunc ) indexed_num_cols_update ,
dialog ) ;
}
1997-11-25 06:05:25 +08:00
gtk_widget_show ( text ) ;
gtk_widget_show ( hbox ) ;
1998-07-25 18:28:22 +08:00
if ( TRUE /* gimage->base_type == RGB */ )
1997-11-25 06:05:25 +08:00
{
1998-07-06 05:01:21 +08:00
GtkWidget * menu ;
GtkWidget * palette_option_menu ;
int default_palette ;
1997-12-14 19:36:53 +08:00
1997-12-17 08:48:55 +08:00
menu = build_palette_menu ( & default_palette ) ;
1998-07-06 05:01:21 +08:00
if ( menu )
{
1997-12-14 19:36:53 +08:00
/* 'custom' palette from dialog */
1997-12-17 08:48:55 +08:00
hbox = gtk_hbox_new ( FALSE , 1 ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , hbox , FALSE , FALSE , 0 ) ;
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
toggle = gtk_radio_button_new_with_label ( group , _ ( " Use custom palette " ) ) ;
1997-12-17 08:48:55 +08:00
group = gtk_radio_button_group ( GTK_RADIO_BUTTON ( toggle ) ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , toggle , TRUE , TRUE , 0 ) ;
gtk_signal_connect ( GTK_OBJECT ( toggle ) , " toggled " ,
( GtkSignalFunc ) indexed_radio_update ,
& ( dialog - > custompal_flag ) ) ;
1999-01-16 01:35:04 +08:00
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( toggle ) ,
dialog - > custompal_flag ) ;
1997-12-17 08:48:55 +08:00
gtk_widget_show ( toggle ) ;
palette_option_menu = gtk_option_menu_new ( ) ;
gtk_option_menu_set_menu ( GTK_OPTION_MENU ( palette_option_menu ) , menu ) ;
gtk_option_menu_set_history ( GTK_OPTION_MENU ( palette_option_menu ) ,
default_palette ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , palette_option_menu , TRUE , TRUE , 2 ) ;
gtk_widget_show ( palette_option_menu ) ;
gtk_widget_show ( hbox ) ;
}
1997-11-25 06:05:25 +08:00
}
1998-07-25 18:28:22 +08:00
1998-07-06 05:01:21 +08:00
if ( ! UserHasWebPal )
{
/* 'web palette'
* Only presented as an option to the user if they do not
* already have the ' Web ' GIMP palette installed on their
* system .
*/
hbox = gtk_hbox_new ( FALSE , 1 ) ;
{
gtk_box_pack_start ( GTK_BOX ( vbox ) , hbox , FALSE , FALSE , 0 ) ;
toggle =
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
gtk_radio_button_new_with_label ( group , _ ( " Use WWW-optimised palette " ) ) ;
1998-07-06 05:01:21 +08:00
{
group = gtk_radio_button_group ( GTK_RADIO_BUTTON ( toggle ) ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , toggle , TRUE , TRUE , 0 ) ;
gtk_signal_connect ( GTK_OBJECT ( toggle ) , " toggled " ,
( GtkSignalFunc ) indexed_radio_update ,
& ( dialog - > webpal_flag ) ) ;
1999-01-16 01:35:04 +08:00
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( toggle ) , dialog - > webpal_flag ) ;
1998-07-06 05:01:21 +08:00
}
gtk_widget_show ( toggle ) ;
}
gtk_widget_show ( hbox ) ;
}
1997-11-25 06:05:25 +08:00
/* 'mono palette' */
hbox = gtk_hbox_new ( FALSE , 1 ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , hbox , FALSE , FALSE , 0 ) ;
toggle =
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
gtk_radio_button_new_with_label ( group , _ ( " Use black/white (1-bit) palette " ) ) ;
1997-11-25 06:05:25 +08:00
group = gtk_radio_button_group ( GTK_RADIO_BUTTON ( toggle ) ) ;
gtk_box_pack_start ( GTK_BOX ( hbox ) , toggle , TRUE , TRUE , 0 ) ;
gtk_signal_connect ( GTK_OBJECT ( toggle ) , " toggled " ,
( GtkSignalFunc ) indexed_radio_update ,
& ( dialog - > monopal_flag ) ) ;
1999-01-16 01:35:04 +08:00
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( toggle ) , dialog - > monopal_flag ) ;
1997-11-25 06:05:25 +08:00
gtk_widget_show ( toggle ) ;
gtk_widget_show ( hbox ) ;
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
frame = gtk_frame_new ( _ ( " Dither Options " ) ) ;
1998-07-06 05:01:21 +08:00
{
gtk_frame_set_shadow_type ( GTK_FRAME ( frame ) , GTK_SHADOW_ETCHED_IN ) ;
1998-12-04 07:01:44 +08:00
gtk_container_set_border_width ( GTK_CONTAINER ( frame ) , 2 ) ;
1998-07-06 05:01:21 +08:00
gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG ( dialog - > shell ) - > vbox ) , frame , TRUE , TRUE , 0 ) ;
vbox = gtk_vbox_new ( FALSE , 1 ) ;
1998-12-04 07:01:44 +08:00
gtk_container_set_border_width ( GTK_CONTAINER ( vbox ) , 1 ) ;
1998-07-06 05:01:21 +08:00
/* put the vbox in the frame */
gtk_container_add ( GTK_CONTAINER ( frame ) , vbox ) ;
gtk_widget_show ( vbox ) ;
/* The dither toggle */
hbox = gtk_hbox_new ( FALSE , 1 ) ;
gtk_box_pack_start ( GTK_BOX ( vbox ) , hbox , FALSE , FALSE , 0 ) ;
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
toggle = gtk_check_button_new_with_label ( _ ( " Enable Floyd-Steinberg dithering " ) ) ;
1999-01-16 01:35:04 +08:00
gtk_toggle_button_set_active ( GTK_TOGGLE_BUTTON ( toggle ) , dialog - > dither ) ;
1998-07-06 05:01:21 +08:00
gtk_box_pack_start ( GTK_BOX ( hbox ) , toggle , TRUE , FALSE , 0 ) ;
gtk_signal_connect ( GTK_OBJECT ( toggle ) , " toggled " ,
1997-11-25 06:05:25 +08:00
( GtkSignalFunc ) indexed_dither_update ,
dialog ) ;
1998-07-06 05:01:21 +08:00
gtk_widget_show ( label ) ;
gtk_widget_show ( toggle ) ;
gtk_widget_show ( hbox ) ;
}
gtk_widget_show ( frame ) ;
/* if the image isn't non-alpha/layered, set the default number of
colours to one less than max , to leave room for a transparent index
for transparent / animated GIFs */
if ( ( ! gimage_is_empty ( gimage ) )
& &
(
gimage - > layers - > next
| |
layer_has_alpha ( ( Layer * ) gimage - > layers - > data )
)
)
{
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
frame = gtk_frame_new ( _ ( " [ Warning ] " ) ) ;
1998-07-06 05:01:21 +08:00
{
gtk_frame_set_shadow_type ( GTK_FRAME ( frame ) , GTK_SHADOW_ETCHED_IN ) ;
1998-12-04 07:01:44 +08:00
gtk_container_set_border_width ( GTK_CONTAINER ( frame ) , 2 ) ;
1998-07-06 05:01:21 +08:00
gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG ( dialog - > shell ) - > vbox ) , frame , TRUE , TRUE , 0 ) ;
1999-01-24 02:49:52 +08:00
label = gtk_label_new ( " You are attempting to convert an image with alpha/layers from RGB/GRAY to INDEXED. \n "
" \t You should not generate a palette of more than 255 colors if you intend to create a transparent or animated GIF file from this image. " ) ;
gtk_label_set_justify ( GTK_LABEL ( label ) , GTK_JUSTIFY_FILL ) ;
gtk_label_set_line_wrap ( GTK_LABEL ( label ) , TRUE ) ;
gtk_container_add ( GTK_CONTAINER ( frame ) , label ) ;
gtk_widget_show ( label ) ;
/*
1998-07-06 05:01:21 +08:00
table = gtk_table_new ( 2 , 1 , FALSE ) ;
{
1998-12-04 07:01:44 +08:00
gtk_container_set_border_width ( GTK_CONTAINER ( table ) , 1 ) ;
1998-07-06 05:01:21 +08:00
gtk_container_add ( GTK_CONTAINER ( frame ) , table ) ;
text = gtk_text_new ( NULL , NULL ) ;
{
gtk_table_attach ( GTK_TABLE ( table ) , text , 0 , 1 , 0 , 1 ,
GTK_FILL | GTK_EXPAND ,
GTK_FILL | GTK_EXPAND | GTK_SHRINK , 0 , 0 ) ;
gtk_signal_connect ( GTK_OBJECT ( text ) , " realize " ,
GTK_SIGNAL_FUNC ( realize_text ) , NULL ) ;
}
gtk_widget_show ( text ) ;
scrollbar = gtk_vscrollbar_new ( GTK_TEXT ( text ) - > vadj ) ;
{
gtk_table_attach ( GTK_TABLE ( table ) , scrollbar , 1 , 2 , 0 , 1 ,
GTK_FILL , GTK_EXPAND | GTK_FILL | GTK_SHRINK ,
0 , 0 ) ;
}
gtk_widget_show ( scrollbar ) ;
}
gtk_widget_show ( table ) ;
1999-01-24 02:49:52 +08:00
*/
1998-07-06 05:01:21 +08:00
}
gtk_widget_show ( frame ) ;
}
1997-11-25 06:05:25 +08:00
/* The action area */
action_items [ 0 ] . user_data = dialog ;
action_items [ 1 ] . user_data = dialog ;
build_action_area ( GTK_DIALOG ( dialog - > shell ) , action_items , 2 , 0 ) ;
gtk_widget_show ( vbox ) ;
gtk_widget_show ( dialog - > shell ) ;
}
1997-12-14 19:36:53 +08:00
static GtkWidget *
1998-07-13 15:12:01 +08:00
build_palette_menu ( int * default_palette )
{
1997-12-14 19:36:53 +08:00
GtkWidget * menu ;
GtkWidget * menu_item ;
1998-01-29 16:03:27 +08:00
GSList * list ;
1997-12-14 19:36:53 +08:00
PaletteEntriesP entries ;
1998-07-13 15:12:01 +08:00
int i , item ;
1997-12-14 19:36:53 +08:00
1998-07-06 05:01:21 +08:00
UserHasWebPal = FALSE ;
if ( ! palette_entries_list )
{
palette_init_palettes ( FALSE ) ;
}
1997-12-14 19:36:53 +08:00
1997-12-17 08:48:55 +08:00
list = palette_entries_list ;
if ( ! list )
return NULL ;
menu = gtk_menu_new ( ) ;
1998-07-13 15:12:01 +08:00
for ( i = 0 , item = 0 , list = palette_entries_list , * default_palette = - 1 ;
1997-12-14 19:36:53 +08:00
list ;
1998-07-13 15:12:01 +08:00
i + + , list = g_slist_next ( list ) )
1997-12-14 19:36:53 +08:00
{
entries = ( PaletteEntriesP ) list - > data ;
1998-07-06 05:01:21 +08:00
/* Preferentially, the initial default is 'Web' if available */
if ( * default_palette = = - 1 & &
g_strcasecmp ( entries - > name , " Web " ) = = 0 )
{
theCustomPalette = entries ;
UserHasWebPal = TRUE ;
}
1998-07-07 03:06:02 +08:00
/* We can't dither to > 256 colors */
1998-07-13 15:12:01 +08:00
if ( entries - > n_colors < = 256 )
{
menu_item = gtk_menu_item_new_with_label ( entries - > name ) ;
gtk_signal_connect ( GTK_OBJECT ( menu_item ) , " activate " ,
( GtkSignalFunc ) palette_entries_callback ,
( gpointer ) entries ) ;
gtk_container_add ( GTK_CONTAINER ( menu ) , menu_item ) ;
gtk_widget_show ( menu_item ) ;
if ( theCustomPalette = = entries )
{
* default_palette = item ;
}
item + + ;
}
1997-12-14 19:36:53 +08:00
}
1998-07-06 05:01:21 +08:00
/* default to first one (only used if 'web' palette not avail.) */
if ( * default_palette = = - 1 )
{
theCustomPalette = ( PaletteEntriesP ) palette_entries_list - > data ;
* default_palette = 0 ;
}
1997-12-14 19:36:53 +08:00
return menu ;
}
static void
palette_entries_callback ( GtkWidget * w , gpointer client_data ) {
theCustomPalette = ( PaletteEntriesP ) client_data ;
}
1997-11-25 06:05:25 +08:00
static void
indexed_ok_callback ( GtkWidget * widget ,
gpointer client_data )
{
IndexedDialog * dialog ;
int palette_type ;
dialog = ( IndexedDialog * ) client_data ;
if ( dialog - > webpal_flag ) palette_type = WEB_PALETTE ;
else
1997-12-14 19:36:53 +08:00
if ( dialog - > custompal_flag ) palette_type = CUSTOM_PALETTE ;
1997-11-25 06:05:25 +08:00
else
1997-12-14 19:36:53 +08:00
if ( dialog - > monopal_flag ) palette_type = MONO_PALETTE ;
1997-11-25 06:05:25 +08:00
else
1997-12-14 19:36:53 +08:00
if ( dialog - > makepal_flag ) palette_type = MAKE_PALETTE ;
else
palette_type = REUSE_PALETTE ;
1997-11-25 06:05:25 +08:00
/* Convert the image to indexed color */
1998-06-29 08:24:44 +08:00
convert_image ( dialog - > gimage , INDEXED , dialog - > num_cols ,
dialog - > dither , palette_type ) ;
gdisplays_flush ( ) ;
1998-03-24 20:33:54 +08:00
1998-07-25 18:28:22 +08:00
/* Save defaults for next time */
snum_cols = dialog - > num_cols ;
sdither = dialog - > dither ;
smakepal_flag = dialog - > makepal_flag ;
swebpal_flag = dialog - > webpal_flag ;
scustompal_flag = dialog - > custompal_flag ;
smonopal_flag = dialog - > monopal_flag ;
sreusepal_flag = dialog - > reusepal_flag ;
1997-11-25 06:05:25 +08:00
gtk_widget_destroy ( dialog - > shell ) ;
g_free ( dialog ) ;
1998-03-13 06:01:43 +08:00
dialog = NULL ;
1997-11-25 06:05:25 +08:00
}
static gint
indexed_delete_callback ( GtkWidget * w ,
GdkEvent * e ,
gpointer client_data )
{
indexed_cancel_callback ( w , client_data ) ;
1998-03-13 06:01:43 +08:00
return TRUE ;
1997-11-25 06:05:25 +08:00
}
static void
indexed_cancel_callback ( GtkWidget * widget ,
gpointer client_data )
{
IndexedDialog * dialog ;
dialog = ( IndexedDialog * ) client_data ;
gtk_widget_destroy ( dialog - > shell ) ;
g_free ( dialog ) ;
1998-03-13 06:01:43 +08:00
dialog = NULL ;
1997-11-25 06:05:25 +08:00
}
static void
indexed_num_cols_update ( GtkWidget * w ,
gpointer data )
{
IndexedDialog * dialog ;
char * str ;
str = gtk_entry_get_text ( GTK_ENTRY ( w ) ) ;
dialog = ( IndexedDialog * ) data ;
dialog - > num_cols = BOUNDS ( ( ( int ) atof ( str ) ) , 1 , 256 ) ;
}
static void
indexed_radio_update ( GtkWidget * widget ,
gpointer data )
{
gint * toggle_val ;
toggle_val = ( int * ) data ;
if ( GTK_TOGGLE_BUTTON ( widget ) - > active )
* toggle_val = TRUE ;
else
* toggle_val = FALSE ;
}
1998-07-06 05:01:21 +08:00
1997-11-25 06:05:25 +08:00
static void
indexed_dither_update ( GtkWidget * w ,
gpointer data )
{
IndexedDialog * dialog ;
dialog = ( IndexedDialog * ) data ;
if ( GTK_TOGGLE_BUTTON ( w ) - > active )
dialog - > dither = TRUE ;
else
dialog - > dither = FALSE ;
}
1999-03-28 14:55:29 +08:00
void
1997-11-25 06:05:25 +08:00
convert_image ( GImage * gimage ,
int new_type ,
int num_cols , /* used only for new_type == INDEXED */
int dither , /* used only for new_type == INDEXED */
int palette_type ) /* used only for new_type == INDEXED */
{
QuantizeObj * quantobj ;
Layer * layer ;
Layer * floating_layer ;
int old_type ;
1998-01-29 16:03:27 +08:00
GSList * list ;
1997-11-25 06:05:25 +08:00
int new_layer_type ;
int new_layer_bytes ;
int has_alpha ;
TileManager * new_tiles ;
quantobj = NULL ;
new_layer_type = RGBA_GIMAGE ;
new_layer_bytes = 4 ;
1999-01-11 07:36:29 +08:00
gimp_add_busy_cursors ( ) ;
1997-11-25 06:05:25 +08:00
/* Get the floating layer if one exists */
floating_layer = gimage_floating_sel ( gimage ) ;
undo_push_group_start ( gimage , GIMAGE_MOD_UNDO ) ;
/* Relax the floating selection */
if ( floating_layer )
floating_sel_relax ( floating_layer , TRUE ) ;
/* Push the image size to the stack */
undo_push_gimage_mod ( gimage ) ;
/* Set the new base type */
old_type = gimage - > base_type ;
gimage - > base_type = new_type ;
/* Convert to indexed? Build histogram if necessary. */
if ( new_type = = INDEXED )
{
int i , j ;
/* 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 )
dither = FALSE ;
quantobj = initialize_median_cut ( old_type , num_cols , dither ? FSDITHER : NODITHER , palette_type ) ;
if ( palette_type = = MAKE_PALETTE )
{
if ( old_type = = GRAY )
zero_histogram_gray ( quantobj - > histogram ) ;
else
zero_histogram_rgb ( quantobj - > histogram ) ;
/* To begin, assume that there are fewer colours in
* the image than the user actually asked for . In that
* case , we don ' t need to quantize or dither .
*/
needs_quantize = FALSE ;
num_found_cols = 0 ;
/* Build the histogram */
list = gimage - > layers ;
while ( list )
{
layer = ( Layer * ) list - > data ;
1998-01-29 16:03:27 +08:00
list = g_slist_next ( list ) ;
1997-11-25 06:05:25 +08:00
if ( old_type = = GRAY )
generate_histogram_gray ( quantobj - > histogram , layer ) ;
else
generate_histogram_rgb ( quantobj - > histogram , layer , num_cols ) ;
1997-12-08 08:24:32 +08:00
/*
* Note : generate_histogram_rgb may set needs_quantize if
* the image contains more colours than the limit specified
* by the user .
*/
1997-11-25 06:05:25 +08:00
}
}
1997-12-08 08:24:32 +08:00
if (
( old_type = = RGB ) & &
( ! needs_quantize ) & &
( palette_type = = MAKE_PALETTE )
)
1997-11-25 06:05:25 +08:00
{
1997-12-08 08:24:32 +08:00
/* If this is an RGB image, and the user wanted a custom-built
* generated palette , and this image has no more colours than
* the user asked for , we don ' t need the first pass ( quantization ) .
*
* There ' s also no point in dithering , since there ' s no error to
* spread . So we destroy the old quantobj and make a new one
* with the remapping function set to a special LUT - based
* no - dither remapper .
*/
1997-11-25 06:05:25 +08:00
quantobj - > delete_func ( quantobj ) ;
quantobj = initialize_median_cut ( old_type , num_cols ,
NODESTRUCTDITHER , palette_type ) ;
/* We can skip the first pass (palette creation) */
quantobj - > actual_number_of_colors = num_found_cols ;
for ( i = 0 ; i < num_found_cols ; i + + )
{
quantobj - > cmap [ i ] . red = found_cols [ i ] [ 0 ] ;
quantobj - > cmap [ i ] . green = found_cols [ i ] [ 1 ] ;
quantobj - > cmap [ i ] . blue = found_cols [ i ] [ 2 ] ;
}
}
else
{
( * quantobj - > first_pass ) ( quantobj ) ;
}
if ( gimage - > cmap )
g_free ( gimage - > cmap ) ;
gimage - > cmap = ( unsigned char * ) g_malloc ( COLORMAP_SIZE ) ;
gimage - > num_cols = quantobj - > actual_number_of_colors ;
for ( i = 0 , j = 0 ; i < quantobj - > actual_number_of_colors ; i + + )
{
gimage - > cmap [ j + + ] = quantobj - > cmap [ i ] . red ;
gimage - > cmap [ j + + ] = quantobj - > cmap [ i ] . green ;
gimage - > cmap [ j + + ] = quantobj - > cmap [ i ] . blue ;
}
}
/* Convert all layers */
list = gimage - > layers ;
while ( list )
{
layer = ( Layer * ) list - > data ;
1998-01-29 16:03:27 +08:00
list = g_slist_next ( list ) ;
1997-11-25 06:05:25 +08:00
has_alpha = layer_has_alpha ( layer ) ;
switch ( new_type )
{
case RGB :
new_layer_type = ( has_alpha ) ? RGBA_GIMAGE : RGB_GIMAGE ;
new_layer_bytes = ( has_alpha ) ? 4 : 3 ;
break ;
case GRAY :
new_layer_type = ( has_alpha ) ? GRAYA_GIMAGE : GRAY_GIMAGE ;
new_layer_bytes = ( has_alpha ) ? 2 : 1 ;
break ;
case INDEXED :
new_layer_type = ( has_alpha ) ? INDEXEDA_GIMAGE : INDEXED_GIMAGE ;
new_layer_bytes = ( has_alpha ) ? 2 : 1 ;
break ;
default :
break ;
}
1998-01-22 15:02:57 +08:00
new_tiles = tile_manager_new ( GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , new_layer_bytes ) ;
1997-11-25 06:05:25 +08:00
switch ( new_type )
{
case RGB :
rgb_converter ( layer , new_tiles , old_type ) ;
break ;
case GRAY :
grayscale_converter ( layer , new_tiles , old_type ) ;
break ;
case INDEXED :
( * quantobj - > second_pass ) ( quantobj , layer , new_tiles ) ;
break ;
default :
break ;
}
/* Push the layer on the undo stack */
undo_push_layer_mod ( gimage , layer ) ;
1998-01-22 15:02:57 +08:00
GIMP_DRAWABLE ( layer ) - > tiles = new_tiles ;
GIMP_DRAWABLE ( layer ) - > bytes = new_layer_bytes ;
GIMP_DRAWABLE ( layer ) - > type = new_layer_type ;
GIMP_DRAWABLE ( layer ) - > has_alpha = TYPE_HAS_ALPHA ( new_layer_type ) ;
1997-11-25 06:05:25 +08:00
}
1997-12-18 10:34:43 +08:00
/* Delete the quantizer object, if there is one */
if ( quantobj )
quantobj - > delete_func ( quantobj ) ;
1997-11-25 06:05:25 +08:00
/* Make sure the projection is up to date */
gimage_projection_realloc ( gimage ) ;
/* Rigor the floating selection */
if ( floating_layer )
floating_sel_rigor ( floating_layer , TRUE ) ;
undo_push_group_end ( gimage ) ;
/* shrink wrap and update all views */
1998-06-29 08:24:44 +08:00
layer_invalidate_previews ( gimage ) ;
1997-11-25 06:05:25 +08:00
gimage_invalidate_preview ( gimage ) ;
1998-06-29 08:24:44 +08:00
gdisplays_update_title ( gimage ) ;
gdisplays_update_full ( gimage ) ;
1997-11-25 06:05:25 +08:00
1999-01-08 03:53:05 +08:00
gimp_image_colormap_changed ( gimage , - 1 ) ;
1999-01-11 07:36:29 +08:00
1999-01-18 01:03:54 +08:00
gimp_remove_busy_cursors ( NULL ) ;
1997-11-25 06:05:25 +08:00
}
static void
rgb_converter ( Layer * layer ,
TileManager * new_tiles ,
int old_type )
{
PixelRegion srcPR , destPR ;
int row , col ;
int offset ;
int has_alpha ;
unsigned char * src , * s ;
unsigned char * dest , * d ;
unsigned char * cmap ;
void * pr ;
has_alpha = layer_has_alpha ( layer ) ;
1998-01-22 15:02:57 +08:00
pixel_region_init ( & srcPR , GIMP_DRAWABLE ( layer ) - > tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , FALSE ) ;
pixel_region_init ( & destPR , new_tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , TRUE ) ;
1997-11-25 06:05:25 +08:00
for ( pr = pixel_regions_register ( 2 , & srcPR , & destPR ) ; pr ! = NULL ; pr = pixel_regions_process ( pr ) )
{
src = srcPR . data ;
dest = destPR . data ;
switch ( old_type )
{
case GRAY :
for ( row = 0 ; row < srcPR . h ; row + + )
{
s = src ;
d = dest ;
for ( col = 0 ; col < srcPR . w ; col + + )
{
d [ RED_PIX ] = * s ;
d [ GREEN_PIX ] = * s ;
d [ BLUE_PIX ] = * s ;
d + = 3 ;
s + + ;
if ( has_alpha )
* d + + = * s + + ;
}
src + = srcPR . rowstride ;
dest + = destPR . rowstride ;
}
break ;
case INDEXED :
1998-01-22 15:02:57 +08:00
cmap = drawable_cmap ( GIMP_DRAWABLE ( layer ) ) ;
1997-11-25 06:05:25 +08:00
for ( row = 0 ; row < srcPR . h ; row + + )
{
s = src ;
d = dest ;
for ( col = 0 ; col < srcPR . w ; col + + )
{
offset = * s + + * 3 ;
d [ RED_PIX ] = cmap [ offset + 0 ] ;
d [ GREEN_PIX ] = cmap [ offset + 1 ] ;
d [ BLUE_PIX ] = cmap [ offset + 2 ] ;
d + = 3 ;
if ( has_alpha )
* d + + = * s + + ;
}
src + = srcPR . rowstride ;
dest + = destPR . rowstride ;
}
break ;
default :
break ;
}
}
}
static void
grayscale_converter ( Layer * layer ,
TileManager * new_tiles ,
int old_type )
{
PixelRegion srcPR , destPR ;
int row , col ;
int offset , val ;
int has_alpha ;
unsigned char * src , * s ;
unsigned char * dest , * d ;
unsigned char * cmap ;
void * pr ;
has_alpha = layer_has_alpha ( layer ) ;
1998-01-22 15:02:57 +08:00
pixel_region_init ( & srcPR , GIMP_DRAWABLE ( layer ) - > tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , FALSE ) ;
pixel_region_init ( & destPR , new_tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , TRUE ) ;
1997-11-25 06:05:25 +08:00
for ( pr = pixel_regions_register ( 2 , & srcPR , & destPR ) ; pr ! = NULL ; pr = pixel_regions_process ( pr ) )
{
src = srcPR . data ;
dest = destPR . data ;
switch ( old_type )
{
case RGB :
for ( row = 0 ; row < srcPR . h ; row + + )
{
s = src ;
d = dest ;
for ( col = 0 ; col < srcPR . w ; col + + )
{
val = INTENSITY ( s [ RED_PIX ] , s [ GREEN_PIX ] , s [ BLUE_PIX ] ) ;
* d + + = ( unsigned char ) val ;
s + = 3 ;
if ( has_alpha )
* d + + = * s + + ;
}
src + = srcPR . rowstride ;
dest + = destPR . rowstride ;
}
break ;
case INDEXED :
1998-01-22 15:02:57 +08:00
cmap = drawable_cmap ( GIMP_DRAWABLE ( layer ) ) ;
1997-11-25 06:05:25 +08:00
for ( row = 0 ; row < srcPR . h ; row + + )
{
s = src ;
d = dest ;
for ( col = 0 ; col < srcPR . w ; col + + )
{
offset = * s + + * 3 ;
val = INTENSITY ( cmap [ offset + 0 ] , cmap [ offset + 1 ] , cmap [ offset + 2 ] ) ;
* d + + = ( unsigned char ) val ;
if ( has_alpha )
* d + + = * s + + ;
}
src + = srcPR . rowstride ;
dest + = destPR . rowstride ;
}
break ;
default :
break ;
}
}
}
/*
* Indexed color conversion machinery
*/
static void
zero_histogram_gray ( Histogram histogram )
{
int i ;
for ( i = 0 ; i < 256 ; i + + )
histogram [ i ] = 0 ;
}
static void
zero_histogram_rgb ( Histogram histogram )
{
int r , g , b ;
for ( r = 0 ; r < HIST_R_ELEMS ; r + + )
for ( g = 0 ; g < HIST_G_ELEMS ; g + + )
for ( b = 0 ; b < HIST_B_ELEMS ; b + + )
histogram [ r * MR + g * MG + b ] = 0 ;
}
static void
generate_histogram_gray ( Histogram histogram ,
Layer * layer )
{
PixelRegion srcPR ;
unsigned char * data ;
int size ;
void * pr ;
gboolean has_alpha ;
has_alpha = ( gboolean ) layer_has_alpha ( layer ) ;
1998-01-22 15:02:57 +08:00
pixel_region_init ( & srcPR , GIMP_DRAWABLE ( layer ) - > tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , FALSE ) ;
1997-11-25 06:05:25 +08:00
for ( pr = pixel_regions_register ( 1 , & srcPR ) ;
pr ! = NULL ;
pr = pixel_regions_process ( pr ) )
{
data = srcPR . data ;
size = srcPR . w * srcPR . h ;
while ( size - - )
{
histogram [ * data ] + + ;
data + = srcPR . bytes ;
}
}
}
static void
generate_histogram_rgb ( Histogram histogram ,
Layer * layer ,
int col_limit )
{
PixelRegion srcPR ;
unsigned char * data ;
int size ;
void * pr ;
ColorFreq * col ;
gboolean has_alpha ;
int nfc_iter ;
has_alpha = ( gboolean ) layer_has_alpha ( layer ) ;
1998-05-28 17:03:57 +08:00
/* g_print ("col_limit = %d, nfc = %d\n", col_limit, num_found_cols);*/
1997-11-25 06:05:25 +08:00
1998-03-24 20:33:54 +08:00
pixel_region_init ( & srcPR , GIMP_DRAWABLE ( layer ) - > tiles , 0 , 0 ,
GIMP_DRAWABLE ( layer ) - > width ,
GIMP_DRAWABLE ( layer ) - > height ,
FALSE ) ;
1997-11-25 06:05:25 +08:00
for ( pr = pixel_regions_register ( 1 , & srcPR ) ;
pr ! = NULL ;
pr = pixel_regions_process ( pr ) )
{
data = srcPR . data ;
size = srcPR . w * srcPR . h ;
if ( needs_quantize )
{
while ( size - - )
{
if ( ( has_alpha & & ( data [ ALPHA_PIX ] & 128 ) ) | | ( ! has_alpha ) )
{
col = & histogram [ ( data [ RED_PIX ] > > R_SHIFT ) * MR +
( data [ GREEN_PIX ] > > G_SHIFT ) * MG +
( data [ BLUE_PIX ] > > B_SHIFT ) ] ;
( * col ) + + ;
}
data + = srcPR . bytes ;
}
}
else
{
while ( size - - )
{
if ( ( has_alpha & & ( data [ ALPHA_PIX ] & 128 ) ) | | ( ! has_alpha ) )
{
col = & histogram [ ( data [ RED_PIX ] > > R_SHIFT ) * MR +
( data [ GREEN_PIX ] > > G_SHIFT ) * MG +
( data [ BLUE_PIX ] > > B_SHIFT ) ] ;
( * col ) + + ;
if ( ! needs_quantize )
{
for ( nfc_iter = 0 ;
nfc_iter < num_found_cols ;
nfc_iter + + )
{
if (
( data [ RED_PIX ] = = found_cols [ nfc_iter ] [ 0 ] )
& &
( data [ GREEN_PIX ] = = found_cols [ nfc_iter ] [ 1 ] )
& &
( data [ BLUE_PIX ] = = found_cols [ nfc_iter ] [ 2 ] )
)
goto already_found ;
}
/* Colour was not in the table of
* existing colours
*/
1998-03-24 20:33:54 +08:00
num_found_cols + + ;
1998-03-13 23:30:07 +08:00
if ( num_found_cols > col_limit )
1997-11-25 06:05:25 +08:00
{
/* There are more colours in the image
* than were allowed . We switch to plain
* histogram calculation with a view to
* quantizing at a later stage .
*/
needs_quantize = TRUE ;
Lots of ii8n stuff here and some additions to the de.po. Applied
Wed Oct 14 17:46:15 EDT 1998 Adrian Likins <adrian@gimp.org>
* app/*, po/de.po, de/POTFILES.in, libgimp/gimpintl.h:
Lots of ii8n stuff here and some additions to the de.po.
Applied gimp-egger-981005-1 ,gimp-egger-981006-1,
gimp-egger-981007-1, gimp-egger-981008-1,
gimp-egger-981009-1.patch, gimp-egger-981010-1.patch
* plug-in/guillotine/guillotine.c: added the coordinates
of the split images from the original image to the title.
ie foo.jpg (0,0) for the image in the topleft.
* plug-in/script-fu/scripts/neon-logo.scm,
perspective-shadow.scm, predator.scm,rendermap.scm,
ripply-anim.scm, select_to_image.scm,swirltile.scm,
xach-effect.scm: updated scripts to use new script-fu stuff
wooo boy! a big un!
in testing this, it looks like some of the po files are busted.
but the code stuff seems okay.
-adrian
1998-10-15 07:23:52 +08:00
/* g_print (_("\nmax colours exceeded - needs quantize.\n"));*/
1997-11-25 06:05:25 +08:00
goto already_found ;
}
1998-03-24 20:33:54 +08:00
else
{
/* Remember the new colour we just found.
*/
found_cols [ num_found_cols - 1 ] [ 0 ] = data [ RED_PIX ] ;
found_cols [ num_found_cols - 1 ] [ 1 ] = data [ GREEN_PIX ] ;
found_cols [ num_found_cols - 1 ] [ 2 ] = data [ BLUE_PIX ] ;
}
1997-11-25 06:05:25 +08:00
}
}
already_found :
data + = srcPR . bytes ;
}
}
}
1998-05-28 17:03:57 +08:00
/* g_print ("O: col_limit = %d, nfc = %d\n", col_limit, num_found_cols);*/
1997-11-25 06:05:25 +08:00
}
static boxptr
1999-02-27 05:30:11 +08:00
find_split_candidate ( boxptr boxlist ,
int numboxes )
1997-11-25 06:05:25 +08:00
{
boxptr boxp ;
int i ;
1999-02-27 05:30:11 +08:00
guint64 maxc = 0 ;
1997-11-25 06:05:25 +08:00
boxptr which = NULL ;
for ( i = 0 , boxp = boxlist ; i < numboxes ; i + + , boxp + + )
{
1999-02-27 05:30:11 +08:00
if ( boxp - > volume > 0 )
1997-11-25 06:05:25 +08:00
{
1999-02-27 05:30:11 +08:00
if ( boxp - > gerror * G_SCALE > maxc )
{
which = boxp ;
maxc = boxp - > gerror * G_SCALE ;
}
if ( boxp - > rerror * R_SCALE > maxc )
{
which = boxp ;
maxc = boxp - > rerror * R_SCALE ;
}
if ( boxp - > berror * B_SCALE > maxc )
{
which = boxp ;
maxc = boxp - > berror * B_SCALE ;
}
1997-11-25 06:05:25 +08:00
}
}
return which ;
}
1999-02-27 05:30:11 +08:00
#if 0
static boxptr
find_biggest_color_pop ( boxptr boxlist ,
int numboxes )
/* Find the splittable box with the largest color population */
/* Returns NULL if no splittable boxes remain */
{
}
# endif
1997-11-25 06:05:25 +08:00
static boxptr
find_biggest_volume ( boxptr boxlist ,
int numboxes )
/* Find the splittable box with the largest (scaled) volume */
/* Returns NULL if no splittable boxes remain */
{
boxptr boxp ;
int i ;
int maxv = 0 ;
boxptr which = NULL ;
for ( i = 0 , boxp = boxlist ; i < numboxes ; i + + , boxp + + )
{
if ( boxp - > volume > maxv )
{
which = boxp ;
maxv = boxp - > volume ;
}
}
return which ;
}
static void
update_box_gray ( Histogram histogram ,
boxptr boxp )
/* Shrink the min/max bounds of a box to enclose only nonzero elements, */
/* and recompute its volume and population */
{
int i , min , max , dist ;
long ccount ;
min = boxp - > Rmin ;
max = boxp - > Rmax ;
if ( max > min )
for ( i = min ; i < = max ; i + + )
{
if ( histogram [ i ] ! = 0 )
{
boxp - > Rmin = min = i ;
break ;
}
}
if ( max > min )
for ( i = max ; i > = min ; i - - )
{
if ( histogram [ i ] ! = 0 )
{
boxp - > Rmax = max = i ;
break ;
}
}
/* Update box volume.
* We use 2 - norm rather than real volume here ; this biases the method
* against making long narrow boxes , and it has the side benefit that
* a box is splittable iff norm > 0.
* Since the differences are expressed in histogram - cell units ,
* we have to shift back to JSAMPLE units to get consistent distances ;
* after which , we scale according to the selected distance scale factors .
*/
dist = max - min ;
boxp - > volume = dist * dist ;
/* Now scan remaining volume of box and compute population */
ccount = 0 ;
for ( i = min ; i < = max ; i + + )
if ( histogram [ i ] ! = 0 )
ccount + + ;
boxp - > colorcount = ccount ;
}
static void
update_box_rgb ( Histogram histogram ,
boxptr boxp )
/* Shrink the min/max bounds of a box to enclose only nonzero elements, */
1999-02-27 05:30:11 +08:00
/* and recompute its volume, population and error */
1997-11-25 06:05:25 +08:00
{
ColorFreq * histp ;
int R , G , B ;
int Rmin , Rmax , Gmin , Gmax , Bmin , Bmax ;
int dist0 , dist1 , dist2 ;
long ccount ;
1999-02-27 05:30:11 +08:00
guint64 tempRerror ;
guint64 tempGerror ;
guint64 tempBerror ;
QuantizeObj dummyqo ;
box dummybox ;
1997-11-25 06:05:25 +08:00
Rmin = boxp - > Rmin ; Rmax = boxp - > Rmax ;
Gmin = boxp - > Gmin ; Gmax = boxp - > Gmax ;
Bmin = boxp - > Bmin ; Bmax = boxp - > Bmax ;
if ( Rmax > Rmin )
for ( R = Rmin ; R < = Rmax ; R + + )
for ( G = Gmin ; G < = Gmax ; G + + )
{
histp = histogram + R * MR + G * MG + Bmin ;
for ( B = Bmin ; B < = Bmax ; B + + )
if ( * histp + + ! = 0 )
{
boxp - > Rmin = Rmin = R ;
goto have_Rmin ;
}
}
have_Rmin :
if ( Rmax > Rmin )
for ( R = Rmax ; R > = Rmin ; R - - )
for ( G = Gmin ; G < = Gmax ; G + + )
{
histp = histogram + R * MR + G * MG + Bmin ;
for ( B = Bmin ; B < = Bmax ; B + + )
if ( * histp + + ! = 0 )
{
boxp - > Rmax = Rmax = R ;
goto have_Rmax ;
}
}
have_Rmax :
if ( Gmax > Gmin )
for ( G = Gmin ; G < = Gmax ; G + + )
for ( R = Rmin ; R < = Rmax ; R + + )
{
histp = histogram + R * MR + G * MG + Bmin ;
for ( B = Bmin ; B < = Bmax ; B + + )
if ( * histp + + ! = 0 )
{
boxp - > Gmin = Gmin = G ;
goto have_Gmin ;
}
}
have_Gmin :
if ( Gmax > Gmin )
for ( G = Gmax ; G > = Gmin ; G - - )
for ( R = Rmin ; R < = Rmax ; R + + )
{
histp = histogram + R * MR + G * MG + Bmin ;
for ( B = Bmin ; B < = Bmax ; B + + )
if ( * histp + + ! = 0 )
{
boxp - > Gmax = Gmax = G ;
goto have_Gmax ;
}
}
have_Gmax :
if ( Bmax > Bmin )
for ( B = Bmin ; B < = Bmax ; B + + )
for ( R = Rmin ; R < = Rmax ; R + + )
{
histp = histogram + R * MR + Gmin * MG + B ;
for ( G = Gmin ; G < = Gmax ; G + + , histp + = MG )
if ( * histp ! = 0 )
{
boxp - > Bmin = Bmin = B ;
goto have_Bmin ;
}
}
have_Bmin :
if ( Bmax > Bmin )
for ( B = Bmax ; B > = Bmin ; B - - )
for ( R = Rmin ; R < = Rmax ; R + + )
{
histp = histogram + R * MR + Gmin * MG + B ;
for ( G = Gmin ; G < = Gmax ; G + + , histp + = MG )
if ( * histp ! = 0 )
{
boxp - > Bmax = Bmax = B ;
goto have_Bmax ;
}
}
have_Bmax :
/* Update box volume.
* We use 2 - norm rather than real volume here ; this biases the method
* against making long narrow boxes , and it has the side benefit that
* a box is splittable iff norm > 0.
* Since the differences are expressed in histogram - cell units ,
* we have to shift back to JSAMPLE units to get consistent distances ;
* after which , we scale according to the selected distance scale factors .
*/
1999-02-27 05:30:11 +08:00
dist0 = ( ( + Rmax - Rmin ) < < R_SHIFT ) * R_SCALE ;
dist1 = ( ( + Gmax - Gmin ) < < G_SHIFT ) * G_SCALE ;
dist2 = ( ( + Bmax - Bmin ) < < B_SHIFT ) * B_SCALE ;
1997-11-25 06:05:25 +08:00
boxp - > volume = dist0 * dist0 + dist1 * dist1 + dist2 * dist2 ;
1999-02-27 05:30:11 +08:00
compute_color_rgb ( & dummyqo , histogram , boxp , 0 ) ;
/*printf("(%d %d %d)\n", dummyqo.cmap[0].red,dummyqo.cmap[0].green,dummyqo.cmap[0].blue);
fflush ( stdout ) ; */
1997-11-25 06:05:25 +08:00
/* Now scan remaining volume of box and compute population */
ccount = 0 ;
1999-02-27 05:30:11 +08:00
boxp - > error = 0 ;
boxp - > rerror = 0 ;
boxp - > gerror = 0 ;
boxp - > berror = 0 ;
1997-11-25 06:05:25 +08:00
for ( R = Rmin ; R < = Rmax ; R + + )
for ( G = Gmin ; G < = Gmax ; G + + )
{
histp = histogram + R * MR + G * MG + Bmin ;
for ( B = Bmin ; B < = Bmax ; B + + , histp + + )
if ( * histp ! = 0 )
{
1999-02-27 05:30:11 +08:00
int ge , be , re ;
dummybox . Rmin = dummybox . Rmax = R ;
dummybox . Gmin = dummybox . Gmax = G ;
dummybox . Bmin = dummybox . Bmax = B ;
compute_color_rgb ( & dummyqo , histogram , & dummybox , 1 ) ;
re = dummyqo . cmap [ 0 ] . red - dummyqo . cmap [ 1 ] . red ;
ge = dummyqo . cmap [ 0 ] . green - dummyqo . cmap [ 1 ] . green ;
be = dummyqo . cmap [ 0 ] . blue - dummyqo . cmap [ 1 ] . blue ;
boxp - > rerror + = ( * histp ) * re * re ;
boxp - > gerror + = ( * histp ) * ge * ge ;
boxp - > berror + = ( * histp ) * be * be ;
boxp - > error + = ( * histp ) *
(
re * re * R_SCALE + ge * ge * G_SCALE + be * be * B_SCALE
) ;
ccount + = * histp ;
1997-11-25 06:05:25 +08:00
}
}
1999-02-27 05:30:11 +08:00
/* Scan again, taking note of halfway error point for red axis */
tempRerror = 0 ;
boxp - > Rhalferror = Rmin ;
for ( R = Rmin ; R < Rmax ; R + + )
{
dummybox . Rmin = dummybox . Rmax = R ;
for ( G = Gmin ; G < = Gmax ; G + + )
{
dummybox . Gmin = dummybox . Gmax = G ;
histp = histogram + R * MR + G * MG + Bmin ;
for ( B = Bmin ; B < = Bmax ; B + + , histp + + )
if ( * histp ! = 0 )
{
int re ;
dummybox . Bmin = dummybox . Bmax = B ;
compute_color_rgb ( & dummyqo , histogram , & dummybox , 1 ) ;
re = dummyqo . cmap [ 0 ] . red - dummyqo . cmap [ 1 ] . red ;
tempRerror + = ( * histp ) * re * re ;
if ( tempRerror * 2 > boxp - > rerror )
goto green_axisscan ;
else
boxp - > Rhalferror = R ;
}
}
}
green_axisscan :
/* Scan again, taking note of halfway error point for green axis */
tempGerror = 0 ;
boxp - > Ghalferror = Gmin ;
for ( G = Gmin ; G < Gmax ; G + + )
{
dummybox . Gmin = dummybox . Gmax = G ;
for ( R = Rmin ; R < = Rmax ; R + + )
{
dummybox . Rmin = dummybox . Rmax = R ;
histp = histogram + R * MR + G * MG + Bmin ;
for ( B = Bmin ; B < = Bmax ; B + + , histp + + )
if ( * histp ! = 0 )
{
int ge ;
dummybox . Bmin = dummybox . Bmax = B ;
compute_color_rgb ( & dummyqo , histogram , & dummybox , 1 ) ;
ge = dummyqo . cmap [ 0 ] . green - dummyqo . cmap [ 1 ] . green ;
tempGerror + = ( * histp ) * ge * ge ;
if ( tempGerror * 2 > boxp - > gerror )
goto blue_axisscan ;
else
boxp - > Ghalferror = G ;
}
}
}
blue_axisscan :
/* Scan again, taking note of halfway error point for blue axis */
tempBerror = 0 ;
boxp - > Bhalferror = Bmin ;
for ( B = Bmin ; B < Bmax ; B + + )
{
dummybox . Bmin = dummybox . Bmax = B ;
for ( R = Rmin ; R < = Rmax ; R + + )
{
dummybox . Rmin = dummybox . Rmax = R ;
for ( G = Gmin ; G < = Gmax ; G + + )
{
histp = histogram + R * MR + G * MG + B ;
if ( * histp ! = 0 )
{
int be ;
dummybox . Gmin = dummybox . Gmax = G ;
compute_color_rgb ( & dummyqo , histogram , & dummybox , 1 ) ;
be = dummyqo . cmap [ 0 ] . blue - dummyqo . cmap [ 1 ] . blue ;
tempBerror + = ( * histp ) * be * be ;
if ( tempBerror * 2 > boxp - > berror )
goto finished_axesscan ;
else
boxp - > Bhalferror = B ;
}
}
}
}
finished_axesscan :
/*
// boxp->Rhalferror = (Rmin+Rmax)/2;
// boxp->Ghalferror = (Gmin+Gmax)/2;
// boxp->Bhalferror = (Bmin+Bmax)/2;
*/
/*boxp->error = (sqrt((double)(boxp->error/ccount)));*/
/* boxp->rerror = (sqrt((double)((boxp->rerror)/ccount)));
boxp - > gerror = ( sqrt ( ( double ) ( ( boxp - > gerror ) / ccount ) ) ) ;
boxp - > berror = ( sqrt ( ( double ) ( ( boxp - > berror ) / ccount ) ) ) ; */
/*printf(":%lld / %ld: ", boxp->error, ccount);
printf ( " (%d-%d-%d)(%d-%d-%d)(%d-%d-%d) \n " ,
Rmin , boxp - > Rhalferror , Rmax ,
Gmin , boxp - > Ghalferror , Gmax ,
Bmin , boxp - > Bhalferror , Bmax
) ;
fflush ( stdout ) ; */
1997-11-25 06:05:25 +08:00
boxp - > colorcount = ccount ;
}
static int
median_cut_gray ( Histogram histogram ,
boxptr boxlist ,
int numboxes ,
int desired_colors )
/* Repeatedly select and split the largest box until we have enough boxes */
{
int lb ;
boxptr b1 , b2 ;
while ( numboxes < desired_colors )
{
/* Select box to split.
* Current algorithm : by population for first half , then by volume .
*/
1999-02-27 05:30:11 +08:00
#if 0
1997-11-25 06:05:25 +08:00
if ( numboxes * 2 < = desired_colors )
{
b1 = find_biggest_color_pop ( boxlist , numboxes ) ;
}
else
1999-02-27 05:30:11 +08:00
# endif
1997-11-25 06:05:25 +08:00
{
b1 = find_biggest_volume ( boxlist , numboxes ) ;
}
if ( b1 = = NULL ) /* no splittable boxes left! */
break ;
b2 = boxlist + numboxes ; /* where new box will go */
/* Copy the color bounds to the new box. */
b2 - > Rmax = b1 - > Rmax ;
b2 - > Rmin = b1 - > Rmin ;
/* Current algorithm: split at halfway point.
* ( Since the box has been shrunk to minimum volume ,
* any split will produce two nonempty subboxes . )
* Note that lb value is max for lower box , so must be < old max .
*/
lb = ( b1 - > Rmax + b1 - > Rmin ) / 2 ;
b1 - > Rmax = lb ;
b2 - > Rmin = lb + 1 ;
/* Update stats for boxes */
update_box_gray ( histogram , b1 ) ;
update_box_gray ( histogram , b2 ) ;
numboxes + + ;
}
return numboxes ;
}
static int
median_cut_rgb ( Histogram histogram ,
boxptr boxlist ,
int numboxes ,
int desired_colors )
/* Repeatedly select and split the largest box until we have enough boxes */
{
int n , lb ;
1999-02-27 05:30:11 +08:00
guint64 R , G , B , cmax ;
1997-11-25 06:05:25 +08:00
boxptr b1 , b2 ;
while ( numboxes < desired_colors ) {
1999-02-27 05:30:11 +08:00
#if 0
1997-11-25 06:05:25 +08:00
/* Select box to split.
* Current algorithm : by population for first half , then by volume .
*/
1999-02-27 05:30:11 +08:00
if ( 1 | | numboxes * 2 < = desired_colors )
1997-11-25 06:05:25 +08:00
{
1999-02-27 05:30:11 +08:00
printf ( " O " ) ;
1997-11-25 06:05:25 +08:00
b1 = find_biggest_color_pop ( boxlist , numboxes ) ;
}
else
{
1999-02-27 05:30:11 +08:00
printf ( " . " ) ;
1997-11-25 06:05:25 +08:00
b1 = find_biggest_volume ( boxlist , numboxes ) ;
}
1999-02-27 05:30:11 +08:00
# endif
b1 = find_split_candidate ( boxlist , numboxes ) ;
1997-11-25 06:05:25 +08:00
if ( b1 = = NULL ) /* no splittable boxes left! */
break ;
b2 = boxlist + numboxes ; /* where new box will go */
/* Copy the color bounds to the new box. */
b2 - > Rmax = b1 - > Rmax ; b2 - > Gmax = b1 - > Gmax ; b2 - > Bmax = b1 - > Bmax ;
b2 - > Rmin = b1 - > Rmin ; b2 - > Gmin = b1 - > Gmin ; b2 - > Bmin = b1 - > Bmin ;
/* Choose which axis to split the box on.
* See notes in update_box about scaling distances .
*/
1999-02-27 05:30:11 +08:00
/*
// R = ((b1->Rmax - b1->Rmin) << R_SHIFT) * R_SCALE;
// G = ((b1->Gmax - b1->Gmin) << G_SHIFT) * G_SCALE;
//B = ((b1->Bmax - b1->Bmin) << B_SHIFT) * B_SCALE;
*/
R = R_SCALE * b1 - > rerror ; /* * (((b1->Rmax - b1->Rmin) << R_SHIFT)) * R_SCALE; */
G = G_SCALE * b1 - > gerror ; /* * (((b1->Gmax - b1->Gmin) << G_SHIFT)) * G_SCALE; */
B = B_SCALE * b1 - > berror ; /* * (((b1->Bmax - b1->Bmin) << B_SHIFT)) * B_SCALE; */
1997-11-25 06:05:25 +08:00
/* We want to break any ties in favor of green, then red, blue last.
*/
cmax = G ; n = 1 ;
if ( R > cmax ) { cmax = R ; n = 0 ; }
if ( B > cmax ) { n = 2 ; }
/* Choose split point along selected axis, and update box bounds.
* Note that lb value is max for lower box , so must be < old max .
*/
switch ( n )
{
case 0 :
1999-02-27 05:30:11 +08:00
lb = b1 - > Rhalferror ; /* *0 + (b1->Rmax + b1->Rmin) / 2; */
1997-11-25 06:05:25 +08:00
b1 - > Rmax = lb ;
b2 - > Rmin = lb + 1 ;
1999-02-27 05:30:11 +08:00
g_assert ( b1 - > Rmax > = b1 - > Rmin ) ;
g_assert ( b2 - > Rmax > = b2 - > Rmin ) ;
1997-11-25 06:05:25 +08:00
break ;
case 1 :
1999-02-27 05:30:11 +08:00
lb = b1 - > Ghalferror ; /* *0 + (b1->Gmax + b1->Gmin) / 2; */
1997-11-25 06:05:25 +08:00
b1 - > Gmax = lb ;
b2 - > Gmin = lb + 1 ;
1999-02-27 05:30:11 +08:00
g_assert ( b1 - > Gmax > = b1 - > Gmin ) ;
g_assert ( b2 - > Gmax > = b2 - > Gmin ) ;
1997-11-25 06:05:25 +08:00
break ;
case 2 :
1999-02-27 05:30:11 +08:00
lb = b1 - > Bhalferror ; /* *0 + (b1->Bmax + b1->Bmin) / 2; */
1997-11-25 06:05:25 +08:00
b1 - > Bmax = lb ;
b2 - > Bmin = lb + 1 ;
1999-02-27 05:30:11 +08:00
g_assert ( b1 - > Bmax > = b1 - > Bmin ) ;
g_assert ( b2 - > Bmax > = b2 - > Bmin ) ;
1997-11-25 06:05:25 +08:00
break ;
}
/* Update stats for boxes */
update_box_rgb ( histogram , b1 ) ;
update_box_rgb ( histogram , b2 ) ;
numboxes + + ;
}
return numboxes ;
}
static void
compute_color_gray ( QuantizeObj * quantobj ,
Histogram histogram ,
boxptr boxp ,
int icolor )
/* Compute representative color for a box, put it in colormap[icolor] */
{
int i , min , max ;
long count ;
long total ;
long gtotal ;
min = boxp - > Rmin ;
max = boxp - > Rmax ;
total = 0 ;
gtotal = 0 ;
for ( i = min ; i < = max ; i + + )
{
count = histogram [ i ] ;
if ( count ! = 0 )
{
total + = count ;
gtotal + = i * count ;
}
}
1998-04-13 22:26:46 +08:00
if ( total ! = 0 )
{
quantobj - > cmap [ icolor ] . red = ( gtotal + ( total > > 1 ) ) / total ;
quantobj - > cmap [ icolor ] . green = quantobj - > cmap [ icolor ] . red ;
quantobj - > cmap [ icolor ] . blue = quantobj - > cmap [ icolor ] . red ;
}
else /* The only situation where total==0 is if the image was null or
* all - transparent . In that case we just put a dummy value in
* the colourmap .
*/
{
quantobj - > cmap [ icolor ] . red =
quantobj - > cmap [ icolor ] . green =
quantobj - > cmap [ icolor ] . blue = 0 ;
}
1997-11-25 06:05:25 +08:00
}
static void
compute_color_rgb ( QuantizeObj * quantobj ,
Histogram histogram ,
boxptr boxp ,
int icolor )
/* Compute representative color for a box, put it in colormap[icolor] */
{
/* Current algorithm: mean weighted by pixels (not colors) */
/* Note it is important to get the rounding correct! */
ColorFreq * histp ;
int R , G , B ;
int Rmin , Rmax ;
int Gmin , Gmax ;
int Bmin , Bmax ;
long count ;
long total = 0 ;
long Rtotal = 0 ;
long Gtotal = 0 ;
long Btotal = 0 ;
Rmin = boxp - > Rmin ; Rmax = boxp - > Rmax ;
Gmin = boxp - > Gmin ; Gmax = boxp - > Gmax ;
Bmin = boxp - > Bmin ; Bmax = boxp - > Bmax ;
for ( R = Rmin ; R < = Rmax ; R + + )
for ( G = Gmin ; G < = Gmax ; G + + )
{
histp = histogram + R * MR + G * MG + Bmin ;
for ( B = Bmin ; B < = Bmax ; B + + )
{
if ( ( count = * histp + + ) ! = 0 )
{
total + = count ;
Rtotal + = ( ( R < < R_SHIFT ) + ( ( 1 < < R_SHIFT ) > > 1 ) ) * count ;
Gtotal + = ( ( G < < G_SHIFT ) + ( ( 1 < < G_SHIFT ) > > 1 ) ) * count ;
Btotal + = ( ( B < < B_SHIFT ) + ( ( 1 < < B_SHIFT ) > > 1 ) ) * count ;
}
}
}
if ( total ! = 0 )
{
quantobj - > cmap [ icolor ] . red = ( Rtotal + ( total > > 1 ) ) / total ;
quantobj - > cmap [ icolor ] . green = ( Gtotal + ( total > > 1 ) ) / total ;
quantobj - > cmap [ icolor ] . blue = ( Btotal + ( total > > 1 ) ) / total ;
}
else /* The only situation where total==0 is if the image was null or
* all - transparent . In that case we just put a dummy value in
* the colourmap .
*/
{
quantobj - > cmap [ icolor ] . red =
quantobj - > cmap [ icolor ] . green =
quantobj - > cmap [ icolor ] . blue = 0 ;
}
}
static void
select_colors_gray ( QuantizeObj * quantobj ,
Histogram histogram )
/* Master routine for color selection */
{
boxptr boxlist ;
int numboxes ;
int desired = quantobj - > desired_number_of_colors ;
int i ;
/* Allocate workspace for box list */
boxlist = ( boxptr ) g_malloc ( desired * sizeof ( box ) ) ;
/* Initialize one box containing whole space */
numboxes = 1 ;
boxlist [ 0 ] . Rmin = 0 ;
boxlist [ 0 ] . Rmax = 255 ;
/* Shrink it to actually-used volume and set its statistics */
update_box_gray ( histogram , boxlist ) ;
/* Perform median-cut to produce final box list */
numboxes = median_cut_gray ( histogram , boxlist , numboxes , desired ) ;
quantobj - > actual_number_of_colors = numboxes ;
/* Compute the representative color for each box, fill colormap */
for ( i = 0 ; i < numboxes ; i + + )
compute_color_gray ( quantobj , histogram , boxlist + i , i ) ;
}
static void
select_colors_rgb ( QuantizeObj * quantobj ,
Histogram histogram )
/* Master routine for color selection */
{
boxptr boxlist ;
int numboxes ;
int desired = quantobj - > desired_number_of_colors ;
int i ;
/* Allocate workspace for box list */
boxlist = ( boxptr ) g_malloc ( desired * sizeof ( box ) ) ;
/* Initialize one box containing whole space */
numboxes = 1 ;
boxlist [ 0 ] . Rmin = 0 ;
boxlist [ 0 ] . Rmax = ( 1 < < PRECISION_R ) - 1 ;
boxlist [ 0 ] . Gmin = 0 ;
boxlist [ 0 ] . Gmax = ( 1 < < PRECISION_G ) - 1 ;
boxlist [ 0 ] . Bmin = 0 ;
boxlist [ 0 ] . Bmax = ( 1 < < PRECISION_B ) - 1 ;
/* Shrink it to actually-used volume and set its statistics */
update_box_rgb ( histogram , boxlist ) ;
/* Perform median-cut to produce final box list */
numboxes = median_cut_rgb ( histogram , boxlist , numboxes , desired ) ;
quantobj - > actual_number_of_colors = numboxes ;
/* Compute the representative color for each box, fill colormap */
for ( i = 0 ; i < numboxes ; i + + )
compute_color_rgb ( quantobj , histogram , boxlist + i , i ) ;
}
/*
* These routines are concerned with the time - critical task of mapping input
* colors to the nearest color in the selected colormap .
*
* We re - use the histogram space as an " inverse color map " , essentially a
* cache for the results of nearest - color searches . All colors within a
* histogram cell will be mapped to the same colormap entry , namely the one
* closest to the cell ' s center . This may not be quite the closest entry to
* the actual input color , but it ' s almost as good . A zero in the cache
* indicates we haven ' t found the nearest color for that cell yet ; the array
* is cleared to zeroes before starting the mapping pass . When we find the
* nearest color for a cell , its colormap index plus one is recorded in the
* cache for future use . The pass2 scanning routines call fill_inverse_cmap
* when they need to use an unfilled entry in the cache .
*
* Our method of efficiently finding nearest colors is based on the " locally
* sorted search " idea described by Heckbert and on the incremental distance
* calculation described by Spencer W . Thomas in chapter III .1 of Graphics
* Gems II ( James Arvo , ed . Academic Press , 1991 ) . Thomas points out that
* the distances from a given colormap entry to each cell of the histogram can
* be computed quickly using an incremental method : the differences between
* distances to adjacent cells themselves differ by a constant . This allows a
* fairly fast implementation of the " brute force " approach of computing the
* distance from every colormap entry to every histogram cell . Unfortunately ,
* it needs a work array to hold the best - distance - so - far for each histogram
* cell ( because the inner loop has to be over cells , not colormap entries ) .
* The work array elements have to be ints , so the work array would need
* 256 Kb at our recommended precision . This is not feasible in DOS machines .
*
* To get around these problems , we apply Thomas ' method to compute the
* nearest colors for only the cells within a small subbox of the histogram .
* The work array need be only as big as the subbox , so the memory usage
* problem is solved . Furthermore , we need not fill subboxes that are never
* referenced in pass2 ; many images use only part of the color gamut , so a
* fair amount of work is saved . An additional advantage of this
* approach is that we can apply Heckbert ' s locality criterion to quickly
* eliminate colormap entries that are far away from the subbox ; typically
* three - fourths of the colormap entries are rejected by Heckbert ' s criterion ,
* and we need not compute their distances to individual cells in the subbox .
* The speed of this approach is heavily influenced by the subbox size : too
* small means too much overhead , too big loses because Heckbert ' s criterion
* can ' t eliminate as many colormap entries . Empirically the best subbox
* size seems to be about 1 / 512 th of the histogram ( 1 / 8 th in each direction ) .
*
* Thomas ' article also describes a refined method which is asymptotically
* faster than the brute - force method , but it is also far more complex and
* cannot efficiently be applied to small subboxes . It is therefore not
* useful for programs intended to be portable to DOS machines . On machines
* with plenty of memory , filling the whole histogram in one shot with Thomas '
* refined method might be faster than the present code - - - but then again ,
* it might not be any faster , and it ' s certainly more complicated .
*/
/* log2(histogram cells in update box) for each axis; this can be adjusted */
# define BOX_R_LOG (PRECISION_R-3)
# define BOX_G_LOG (PRECISION_G-3)
# define BOX_B_LOG (PRECISION_B-3)
# define BOX_R_ELEMS (1<<BOX_R_LOG) /* # of hist cells in update box */
# define BOX_G_ELEMS (1<<BOX_G_LOG)
# define BOX_B_ELEMS (1<<BOX_B_LOG)
# define BOX_R_SHIFT (R_SHIFT + BOX_R_LOG)
# define BOX_G_SHIFT (G_SHIFT + BOX_G_LOG)
# define BOX_B_SHIFT (B_SHIFT + BOX_B_LOG)
/*
* The next three routines implement inverse colormap filling . They could
* all be folded into one big routine , but splitting them up this way saves
* some stack space ( the mindist [ ] and bestdist [ ] arrays need not coexist )
* and may allow some compilers to produce better code by registerizing more
* inner - loop variables .
*/
static int
find_nearby_colors ( QuantizeObj * quantobj ,
int minR ,
int minG ,
int minB ,
int colorlist [ ] )
/* Locate the colormap entries close enough to an update box to be candidates
* for the nearest entry to some cell ( s ) in the update box . The update box
* is specified by the center coordinates of its first cell . The number of
* candidate colormap entries is returned , and their colormap indexes are
* placed in colorlist [ ] .
* This routine uses Heckbert ' s " locally sorted search " criterion to select
* the colors that need further consideration .
*/
{
int numcolors = quantobj - > actual_number_of_colors ;
int maxR , maxG , maxB ;
int centerR , centerG , centerB ;
int i , x , ncolors ;
int minmaxdist , min_dist , max_dist , tdist ;
int mindist [ MAXNUMCOLORS ] ; /* min distance to colormap entry i */
/* Compute true coordinates of update box's upper corner and center.
* Actually we compute the coordinates of the center of the upper - corner
* histogram cell , which are the upper bounds of the volume we care about .
* Note that since " >> " rounds down , the " center " values may be closer to
* min than to max ; hence comparisons to them must be " <= " , not " < " .
*/
maxR = minR + ( ( 1 < < BOX_R_SHIFT ) - ( 1 < < R_SHIFT ) ) ;
centerR = ( minR + maxR ) > > 1 ;
maxG = minG + ( ( 1 < < BOX_G_SHIFT ) - ( 1 < < G_SHIFT ) ) ;
centerG = ( minG + maxG ) > > 1 ;
maxB = minB + ( ( 1 < < BOX_B_SHIFT ) - ( 1 < < B_SHIFT ) ) ;
centerB = ( minB + maxB ) > > 1 ;
/* For each color in colormap, find:
* 1. its minimum squared - distance to any point in the update box
* ( zero if color is within update box ) ;
* 2. its maximum squared - distance to any point in the update box .
* Both of these can be found by considering only the corners of the box .
* We save the minimum distance for each color in mindist [ ] ;
* only the smallest maximum distance is of interest .
*/
minmaxdist = 0x7FFFFFFFL ;
for ( i = 0 ; i < numcolors ; i + + ) {
/* We compute the squared-R-distance term, then add in the other two. */
x = quantobj - > cmap [ i ] . red ;
if ( x < minR ) {
tdist = ( x - minR ) * R_SCALE ;
min_dist = tdist * tdist ;
tdist = ( x - maxR ) * R_SCALE ;
max_dist = tdist * tdist ;
} else if ( x > maxR ) {
tdist = ( x - maxR ) * R_SCALE ;
min_dist = tdist * tdist ;
tdist = ( x - minR ) * R_SCALE ;
max_dist = tdist * tdist ;
} else {
/* within cell range so no contribution to min_dist */
min_dist = 0 ;
if ( x < = centerR ) {
tdist = ( x - maxR ) * R_SCALE ;
max_dist = tdist * tdist ;
} else {
tdist = ( x - minR ) * R_SCALE ;
max_dist = tdist * tdist ;
}
}
x = quantobj - > cmap [ i ] . green ;
if ( x < minG ) {
tdist = ( x - minG ) * G_SCALE ;
min_dist + = tdist * tdist ;
tdist = ( x - maxG ) * G_SCALE ;
max_dist + = tdist * tdist ;
} else if ( x > maxG ) {
tdist = ( x - maxG ) * G_SCALE ;
min_dist + = tdist * tdist ;
tdist = ( x - minG ) * G_SCALE ;
max_dist + = tdist * tdist ;
} else {
/* within cell range so no contribution to min_dist */
if ( x < = centerG ) {
tdist = ( x - maxG ) * G_SCALE ;
max_dist + = tdist * tdist ;
} else {
tdist = ( x - minG ) * G_SCALE ;
max_dist + = tdist * tdist ;
}
}
x = quantobj - > cmap [ i ] . blue ;
if ( x < minB ) {
tdist = ( x - minB ) * B_SCALE ;
min_dist + = tdist * tdist ;
tdist = ( x - maxB ) * B_SCALE ;
max_dist + = tdist * tdist ;
} else if ( x > maxB ) {
tdist = ( x - maxB ) * B_SCALE ;
min_dist + = tdist * tdist ;
tdist = ( x - minB ) * B_SCALE ;
max_dist + = tdist * tdist ;
} else {
/* within cell range so no contribution to min_dist */
if ( x < = centerB ) {
tdist = ( x - maxB ) * B_SCALE ;
max_dist + = tdist * tdist ;
} else {
tdist = ( x - minB ) * B_SCALE ;
max_dist + = tdist * tdist ;
}
}
mindist [ i ] = min_dist ; /* save away the results */
if ( max_dist < minmaxdist )
minmaxdist = max_dist ;
}
/* Now we know that no cell in the update box is more than minmaxdist
* away from some colormap entry . Therefore , only colors that are
* within minmaxdist of some part of the box need be considered .
*/
ncolors = 0 ;
for ( i = 0 ; i < numcolors ; i + + ) {
if ( mindist [ i ] < = minmaxdist )
colorlist [ ncolors + + ] = i ;
}
return ncolors ;
}
static void
find_best_colors ( QuantizeObj * quantobj ,
int minR ,
int minG ,
int minB ,
int numcolors ,
int colorlist [ ] ,
int bestcolor [ ] )
/* Find the closest colormap entry for each cell in the update box,
* given the list of candidate colors prepared by find_nearby_colors .
* Return the indexes of the closest entries in the bestcolor [ ] array .
* This routine uses Thomas ' incremental distance calculation method to
* find the distance from a colormap entry to successive cells in the box .
*/
{
int iR , iG , iB ;
int i , icolor ;
int * bptr ; /* pointer into bestdist[] array */
int * cptr ; /* pointer into bestcolor[] array */
int dist0 , dist1 ; /* initial distance values */
int dist2 ; /* current distance in inner loop */
int xx0 , xx1 ; /* distance increments */
int xx2 ;
int inR , inG , inB ; /* initial values for increments */
/* This array holds the distance to the nearest-so-far color for each cell */
int bestdist [ BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS ] ;
/* Initialize best-distance for each cell of the update box */
bptr = bestdist ;
for ( i = BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS - 1 ; i > = 0 ; i - - )
* bptr + + = 0x7FFFFFFFL ;
/* For each color selected by find_nearby_colors,
* compute its distance to the center of each cell in the box .
* If that ' s less than best - so - far , update best distance and color number .
*/
/* Nominal steps between cell centers ("x" in Thomas article) */
# define STEP_R ((1 << R_SHIFT) * R_SCALE)
# define STEP_G ((1 << G_SHIFT) * G_SCALE)
# define STEP_B ((1 << B_SHIFT) * B_SCALE)
for ( i = 0 ; i < numcolors ; i + + ) {
icolor = colorlist [ i ] ;
/* Compute (square of) distance from minR/G/B to this color */
inR = ( minR - quantobj - > cmap [ icolor ] . red ) * R_SCALE ;
dist0 = inR * inR ;
inG = ( minG - quantobj - > cmap [ icolor ] . green ) * G_SCALE ;
dist0 + = inG * inG ;
inB = ( minB - quantobj - > cmap [ icolor ] . blue ) * B_SCALE ;
dist0 + = inB * inB ;
/* Form the initial difference increments */
inR = inR * ( 2 * STEP_R ) + STEP_R * STEP_R ;
inG = inG * ( 2 * STEP_G ) + STEP_G * STEP_G ;
inB = inB * ( 2 * STEP_B ) + STEP_B * STEP_B ;
/* Now loop over all cells in box, updating distance per Thomas method */
bptr = bestdist ;
cptr = bestcolor ;
xx0 = inR ;
for ( iR = BOX_R_ELEMS - 1 ; iR > = 0 ; iR - - ) {
dist1 = dist0 ;
xx1 = inG ;
for ( iG = BOX_G_ELEMS - 1 ; iG > = 0 ; iG - - ) {
dist2 = dist1 ;
xx2 = inB ;
for ( iB = BOX_B_ELEMS - 1 ; iB > = 0 ; iB - - ) {
if ( dist2 < * bptr ) {
* bptr = dist2 ;
* cptr = icolor ;
}
dist2 + = xx2 ;
xx2 + = 2 * STEP_B * STEP_B ;
bptr + + ;
cptr + + ;
}
dist1 + = xx1 ;
xx1 + = 2 * STEP_G * STEP_G ;
}
dist0 + = xx0 ;
xx0 + = 2 * STEP_R * STEP_R ;
}
}
}
static void
fill_inverse_cmap_gray ( QuantizeObj * quantobj ,
Histogram histogram ,
int pixel )
/* Fill the inverse-colormap entries in the update box that contains */
/* histogram cell R/G/B. (Only that one cell MUST be filled, but */
/* we can fill as many others as we wish.) */
{
Color * cmap ;
long dist ;
long mindist ;
int mindisti ;
int i ;
cmap = quantobj - > cmap ;
mindist = 65536 ;
mindisti = - 1 ;
for ( i = 0 ; i < quantobj - > actual_number_of_colors ; i + + )
{
dist = pixel - cmap [ i ] . red ;
dist * = dist ;
if ( dist < mindist )
{
mindist = dist ;
mindisti = i ;
}
}
if ( i > = 0 )
histogram [ pixel ] = mindisti + 1 ;
}
static void
fill_inverse_cmap_rgb ( QuantizeObj * quantobj ,
Histogram histogram ,
int R ,
int G ,
int B )
/* Fill the inverse-colormap entries in the update box that contains */
/* histogram cell R/G/B. (Only that one cell MUST be filled, but */
/* we can fill as many others as we wish.) */
{
int minR , minG , minB ; /* lower left corner of update box */
int iR , iG , iB ;
int * cptr ; /* pointer into bestcolor[] array */
ColorFreq * cachep ; /* pointer into main cache array */
/* This array lists the candidate colormap indexes. */
int colorlist [ MAXNUMCOLORS ] ;
int numcolors ; /* number of candidate colors */
/* This array holds the actually closest colormap index for each cell. */
int bestcolor [ BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS ] ;
/* Convert cell coordinates to update box id */
R > > = BOX_R_LOG ;
G > > = BOX_G_LOG ;
B > > = BOX_B_LOG ;
/* Compute true coordinates of update box's origin corner.
* Actually we compute the coordinates of the center of the corner
* histogram cell , which are the lower bounds of the volume we care about .
*/
minR = ( R < < BOX_R_SHIFT ) + ( ( 1 < < R_SHIFT ) > > 1 ) ;
minG = ( G < < BOX_G_SHIFT ) + ( ( 1 < < G_SHIFT ) > > 1 ) ;
minB = ( B < < BOX_B_SHIFT ) + ( ( 1 < < B_SHIFT ) > > 1 ) ;
/* Determine which colormap entries are close enough to be candidates
* for the nearest entry to some cell in the update box .
*/
numcolors = find_nearby_colors ( quantobj , minR , minG , minB , colorlist ) ;
/* Determine the actually nearest colors. */
find_best_colors ( quantobj , minR , minG , minB , numcolors , colorlist ,
bestcolor ) ;
/* Save the best color numbers (plus 1) in the main cache array */
R < < = BOX_R_LOG ; /* convert id back to base cell indexes */
G < < = BOX_G_LOG ;
B < < = BOX_B_LOG ;
cptr = bestcolor ;
for ( iR = 0 ; iR < BOX_R_ELEMS ; iR + + ) {
for ( iG = 0 ; iG < BOX_G_ELEMS ; iG + + ) {
cachep = & histogram [ ( R + iR ) * MR + ( G + iG ) * MG + B ] ;
for ( iB = 0 ; iB < BOX_B_ELEMS ; iB + + ) {
* cachep + + = ( * cptr + + ) + 1 ;
}
}
}
}
/* This is pass 1 */
static void
median_cut_pass1_gray ( QuantizeObj * quantobj )
{
select_colors_gray ( quantobj , quantobj - > histogram ) ;
}
static void
median_cut_pass1_rgb ( QuantizeObj * quantobj )
{
select_colors_rgb ( quantobj , quantobj - > histogram ) ;
}
static void
monopal_pass1 ( QuantizeObj * quantobj )
{
quantobj - > actual_number_of_colors = 2 ;
quantobj - > cmap [ 0 ] . red = 0 ;
quantobj - > cmap [ 0 ] . green = 0 ;
quantobj - > cmap [ 0 ] . blue = 0 ;
quantobj - > cmap [ 1 ] . red = 255 ;
quantobj - > cmap [ 1 ] . green = 255 ;
quantobj - > cmap [ 1 ] . blue = 255 ;
}
static void
webpal_pass1 ( QuantizeObj * quantobj )
{
int i ;
quantobj - > actual_number_of_colors = 216 ;
for ( i = 0 ; i < 216 ; i + + )
{
quantobj - > cmap [ i ] . red = webpal [ i * 3 ] ;
quantobj - > cmap [ i ] . green = webpal [ i * 3 + 1 ] ;
quantobj - > cmap [ i ] . blue = webpal [ i * 3 + 2 ] ;
}
}
1997-12-14 19:36:53 +08:00
static void
custompal_pass1 ( QuantizeObj * quantobj )
{
int i ;
1998-01-29 16:03:27 +08:00
GSList * list ;
1997-12-14 19:36:53 +08:00
PaletteEntryP entry ;
/* fprintf(stderr, "custompal_pass1: using (theCustomPalette %s) from (file %s)\n",
theCustomPalette - > name , theCustomPalette - > filename ) ; */
for ( i = 0 , list = theCustomPalette - > colors ;
list ;
1998-01-29 16:03:27 +08:00
i + + , list = g_slist_next ( list ) )
1997-12-14 19:36:53 +08:00
{
entry = ( PaletteEntryP ) list - > data ;
quantobj - > cmap [ i ] . red = entry - > color [ 0 ] ;
quantobj - > cmap [ i ] . green = entry - > color [ 1 ] ;
quantobj - > cmap [ i ] . blue = entry - > color [ 2 ] ;
}
quantobj - > actual_number_of_colors = i ;
}
1997-11-25 06:05:25 +08:00
/*
* Map some rows of pixels to the output colormapped representation .
*/
static void
median_cut_pass2_no_dither_gray ( QuantizeObj * quantobj ,
Layer * layer ,
TileManager * new_tiles )
{
PixelRegion srcPR , destPR ;
Histogram histogram = quantobj - > histogram ;
ColorFreq * cachep ;
unsigned char * src , * dest ;
int row , col ;
int pixel ;
int has_alpha ;
void * pr ;
zero_histogram_gray ( histogram ) ;
has_alpha = layer_has_alpha ( layer ) ;
1998-01-22 15:02:57 +08:00
pixel_region_init ( & srcPR , GIMP_DRAWABLE ( layer ) - > tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , FALSE ) ;
pixel_region_init ( & destPR , new_tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , TRUE ) ;
1997-11-25 06:05:25 +08:00
for ( pr = pixel_regions_register ( 2 , & srcPR , & destPR ) ; pr ! = NULL ; pr = pixel_regions_process ( pr ) )
{
src = srcPR . data ;
dest = destPR . data ;
for ( row = 0 ; row < srcPR . h ; row + + )
{
for ( col = 0 ; col < srcPR . w ; col + + )
{
/* get pixel value and index into the cache */
pixel = src [ GRAY_PIX ] ;
cachep = & histogram [ pixel ] ;
/* If we have not seen this color before, find nearest colormap entry */
/* and update the cache */
if ( * cachep = = 0 )
fill_inverse_cmap_gray ( quantobj , histogram , pixel ) ;
/* Now emit the colormap index for this cell */
dest [ INDEXED_PIX ] = * cachep - 1 ;
if ( has_alpha )
dest [ ALPHA_I_PIX ] = ( src [ ALPHA_G_PIX ] > 127 ) ? 255 : 0 ;
src + = srcPR . bytes ;
dest + = destPR . bytes ;
}
}
}
}
static void
median_cut_pass2_no_dither_rgb ( QuantizeObj * quantobj ,
Layer * layer ,
TileManager * new_tiles )
{
PixelRegion srcPR , destPR ;
Histogram histogram = quantobj - > histogram ;
ColorFreq * cachep ;
unsigned char * src , * dest ;
int R , G , B ;
int row , col ;
int has_alpha ;
void * pr ;
int red_pix = RED_PIX ;
int green_pix = GREEN_PIX ;
int blue_pix = BLUE_PIX ;
int alpha_pix = ALPHA_PIX ;
/* In the case of web/mono palettes, we actually force
* grayscale drawables through the rgb pass2 functions
*/
1998-01-22 15:02:57 +08:00
if ( drawable_gray ( GIMP_DRAWABLE ( layer ) ) )
1997-11-25 06:05:25 +08:00
red_pix = green_pix = blue_pix = GRAY_PIX ;
zero_histogram_rgb ( histogram ) ;
has_alpha = layer_has_alpha ( layer ) ;
1998-01-22 15:02:57 +08:00
pixel_region_init ( & srcPR , GIMP_DRAWABLE ( layer ) - > tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , FALSE ) ;
pixel_region_init ( & destPR , new_tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , TRUE ) ;
1997-11-25 06:05:25 +08:00
for ( pr = pixel_regions_register ( 2 , & srcPR , & destPR ) ; pr ! = NULL ; pr = pixel_regions_process ( pr ) )
{
src = srcPR . data ;
dest = destPR . data ;
for ( row = 0 ; row < srcPR . h ; row + + )
{
for ( col = 0 ; col < srcPR . w ; col + + )
{
/* get pixel value and index into the cache */
R = ( src [ red_pix ] ) > > R_SHIFT ;
G = ( src [ green_pix ] ) > > G_SHIFT ;
B = ( src [ blue_pix ] ) > > B_SHIFT ;
cachep = & histogram [ R * MR + G * MG + B ] ;
/* If we have not seen this color before, find nearest colormap entry */
/* and update the cache */
if ( * cachep = = 0 )
fill_inverse_cmap_rgb ( quantobj , histogram , R , G , B ) ;
/* Now emit the colormap index for this cell */
dest [ INDEXED_PIX ] = * cachep - 1 ;
if ( has_alpha )
dest [ ALPHA_I_PIX ] = ( src [ alpha_pix ] > 127 ) ? 255 : 0 ;
src + = srcPR . bytes ;
dest + = destPR . bytes ;
}
}
}
}
static void
median_cut_pass2_nodestruct_dither_rgb ( QuantizeObj * quantobj ,
Layer * layer ,
TileManager * new_tiles )
{
PixelRegion srcPR , destPR ;
unsigned char * src , * dest ;
int row , col ;
int has_alpha ;
void * pr ;
int red_pix = RED_PIX ;
int green_pix = GREEN_PIX ;
int blue_pix = BLUE_PIX ;
int alpha_pix = ALPHA_PIX ;
int i ;
int lastindex = 0 ;
int lastred = - 1 ;
int lastgreen = - 1 ;
int lastblue = - 1 ;
has_alpha = layer_has_alpha ( layer ) ;
1998-01-22 15:02:57 +08:00
pixel_region_init ( & srcPR , GIMP_DRAWABLE ( layer ) - > tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , FALSE ) ;
pixel_region_init ( & destPR , new_tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , TRUE ) ;
1997-11-25 06:05:25 +08:00
for ( pr = pixel_regions_register ( 2 , & srcPR , & destPR ) ; pr ! = NULL ; pr = pixel_regions_process ( pr ) )
{
src = srcPR . data ;
dest = destPR . data ;
for ( row = 0 ; row < srcPR . h ; row + + )
{
for ( col = 0 ; col < srcPR . w ; col + + )
{
if ( ( has_alpha & & ( src [ alpha_pix ] > 127 ) )
| | ! has_alpha )
{
if ( ( lastred = = src [ red_pix ] ) & &
( lastgreen = = src [ green_pix ] ) & &
( lastblue = = src [ blue_pix ] ) )
{
/* same pixel colour as last time */
dest [ INDEXED_PIX ] = lastindex ;
if ( has_alpha )
dest [ ALPHA_I_PIX ] = 255 ;
}
else
{
for ( i = 0 ;
i < quantobj - > actual_number_of_colors ;
i + + )
{
if (
( quantobj - > cmap [ i ] . red = = src [ red_pix ] ) & &
( quantobj - > cmap [ i ] . green = = src [ green_pix ] ) & &
( quantobj - > cmap [ i ] . blue = = src [ blue_pix ] )
)
{
lastred = src [ red_pix ] ;
lastgreen = src [ green_pix ] ;
lastblue = src [ blue_pix ] ;
lastindex = i ;
goto got_colour ;
}
}
1999-05-30 00:35:47 +08:00
g_error ( " Non-existant colour was expected to "
" be in non-destructive colourmap. " ) ;
1997-11-25 06:05:25 +08:00
got_colour :
dest [ INDEXED_PIX ] = lastindex ;
if ( has_alpha )
dest [ ALPHA_I_PIX ] = 255 ;
}
}
else
{ /* have alpha, and transparent */
dest [ ALPHA_I_PIX ] = 0 ;
}
src + = srcPR . bytes ;
dest + = destPR . bytes ;
}
}
}
}
/*
* Initialize the error - limiting transfer function ( lookup table ) .
* The raw F - S error computation can potentially compute error values of up to
* + - MAXJSAMPLE . But we want the maximum correction applied to a pixel to be
* much less , otherwise obviously wrong pixels will be created . ( Typical
* effects include weird fringes at color - area boundaries , isolated bright
* pixels in a dark area , etc . ) The standard advice for avoiding this problem
* is to ensure that the " corners " of the color cube are allocated as output
* colors ; then repeated errors in the same direction cannot cause cascading
* error buildup . However , that only prevents the error from getting
* completely out of hand ; Aaron Giles reports that error limiting improves
* the results even with corner colors allocated .
* A simple clamping of the error values to about + - MAXJSAMPLE / 8 works pretty
* well , but the smoother transfer function used below is even better . Thanks
* to Aaron Giles for this idea .
*/
static int *
init_error_limit ( void )
/* Allocate and fill in the error_limiter table */
{
int * table ;
int in , out ;
table = g_malloc ( sizeof ( int ) * ( 255 * 2 + 1 ) ) ;
table + = 255 ; /* so we can index -255 ... +255 */
1999-02-27 05:30:11 +08:00
/* #define STEPSIZE 16 */
# define STEPSIZE 200
for ( in = 0 ; in < STEPSIZE ; in + + )
{
table [ in ] = in ;
table [ - in ] = - in ;
}
for ( ; in < = 255 ; in + + )
{
table [ in ] = STEPSIZE ;
table [ - in ] = - STEPSIZE ;
}
return ( table ) ;
1997-11-25 06:05:25 +08:00
/* 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 ;
}
/*
* Map some rows of pixels to the output colormapped representation .
* Perform floyd - steinberg dithering .
*/
static void
median_cut_pass2_fs_dither_gray ( QuantizeObj * quantobj ,
Layer * layer ,
TileManager * new_tiles )
{
PixelRegion srcPR , destPR ;
Histogram histogram = quantobj - > histogram ;
ColorFreq * cachep ;
Color * color ;
int * error_limiter ;
short * fs_err1 , * fs_err2 ;
short * fs_err3 , * fs_err4 ;
short * range_limiter ;
int src_bytes , dest_bytes ;
unsigned char * src , * dest ;
unsigned char * src_buf , * dest_buf ;
int * next_row , * prev_row ;
int * nr , * pr ;
int * tmp ;
int pixel ;
int pixele ;
int row , col ;
int index ;
int step_dest , step_src ;
int odd_row ;
int has_alpha ;
int width , height ;
zero_histogram_gray ( histogram ) ;
has_alpha = layer_has_alpha ( layer ) ;
1998-01-22 15:02:57 +08:00
pixel_region_init ( & srcPR , GIMP_DRAWABLE ( layer ) - > tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , FALSE ) ;
pixel_region_init ( & destPR , new_tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , TRUE ) ;
src_bytes = GIMP_DRAWABLE ( layer ) - > bytes ;
1998-08-16 03:17:36 +08:00
dest_bytes = new_tiles - > bpp ;
1998-01-22 15:02:57 +08:00
width = GIMP_DRAWABLE ( layer ) - > width ;
height = GIMP_DRAWABLE ( layer ) - > height ;
1997-11-25 06:05:25 +08:00
error_limiter = init_error_limit ( ) ;
range_limiter = range_array + 256 ;
src_buf = g_malloc ( width * src_bytes ) ;
dest_buf = g_malloc ( width * dest_bytes ) ;
next_row = g_malloc ( sizeof ( int ) * ( width + 2 ) ) ;
prev_row = g_malloc ( sizeof ( int ) * ( width + 2 ) ) ;
memset ( prev_row , 0 , ( width + 2 ) * sizeof ( int ) ) ;
fs_err1 = floyd_steinberg_error1 + 511 ;
fs_err2 = floyd_steinberg_error2 + 511 ;
fs_err3 = floyd_steinberg_error3 + 511 ;
fs_err4 = floyd_steinberg_error4 + 511 ;
odd_row = 0 ;
for ( row = 0 ; row < height ; row + + )
{
pixel_region_get_row ( & srcPR , 0 , row , width , src_buf , 1 ) ;
src = src_buf ;
dest = dest_buf ;
nr = next_row ;
pr = prev_row + 1 ;
if ( odd_row )
{
step_dest = - dest_bytes ;
step_src = - src_bytes ;
src + = ( width * src_bytes ) - src_bytes ;
dest + = ( width * dest_bytes ) - dest_bytes ;
nr + = width + 1 ;
pr + = width ;
* ( nr - 1 ) = 0 ;
}
else
{
step_dest = dest_bytes ;
step_src = src_bytes ;
* ( nr + 1 ) = 0 ;
}
* nr = 0 ;
for ( col = 0 ; col < width ; col + + )
{
pixel = range_limiter [ src [ GRAY_PIX ] + error_limiter [ * pr ] ] ;
cachep = & histogram [ pixel ] ;
/* If we have not seen this color before, find nearest colormap entry */
/* and update the cache */
if ( * cachep = = 0 )
fill_inverse_cmap_gray ( quantobj , histogram , pixel ) ;
index = * cachep - 1 ;
dest [ INDEXED_PIX ] = index ;
if ( has_alpha )
dest [ ALPHA_I_PIX ] = ( src [ ALPHA_G_PIX ] > 127 ) ? 255 : 0 ;
color = & quantobj - > cmap [ index ] ;
pixele = pixel - color - > red ;
if ( odd_row )
{
* ( - - pr ) + = fs_err1 [ pixele ] ;
* nr - - + = fs_err2 [ pixele ] ;
* nr + = fs_err3 [ pixele ] ;
* ( nr - 1 ) = fs_err4 [ pixele ] ;
}
else
{
* ( + + pr ) + = fs_err1 [ pixele ] ;
* nr + + + = fs_err2 [ pixele ] ;
* nr + = fs_err3 [ pixele ] ;
* ( nr + 1 ) = fs_err4 [ pixele ] ;
}
dest + = step_dest ;
src + = step_src ;
}
tmp = next_row ;
next_row = prev_row ;
prev_row = tmp ;
odd_row = ! odd_row ;
pixel_region_set_row ( & destPR , 0 , row , width , dest_buf ) ;
}
g_free ( error_limiter - 255 ) ;
g_free ( next_row ) ;
g_free ( prev_row ) ;
g_free ( src_buf ) ;
g_free ( dest_buf ) ;
}
static void
median_cut_pass2_fs_dither_rgb ( QuantizeObj * quantobj ,
Layer * layer ,
TileManager * new_tiles )
{
PixelRegion srcPR , destPR ;
Histogram histogram = quantobj - > histogram ;
ColorFreq * cachep ;
Color * color ;
int * error_limiter ;
short * fs_err1 , * fs_err2 ;
short * fs_err3 , * fs_err4 ;
short * range_limiter ;
int src_bytes , dest_bytes ;
unsigned char * src , * dest ;
unsigned char * src_buf , * dest_buf ;
int * red_n_row , * red_p_row ;
int * grn_n_row , * grn_p_row ;
int * blu_n_row , * blu_p_row ;
int * rnr , * rpr ;
int * gnr , * gpr ;
int * bnr , * bpr ;
int * tmp ;
int r , g , b ;
int re , ge , be ;
int row , col ;
int index ;
int step_dest , step_src ;
int odd_row ;
int has_alpha ;
int width , height ;
int red_pix = RED_PIX ;
int green_pix = GREEN_PIX ;
int blue_pix = BLUE_PIX ;
int alpha_pix = ALPHA_PIX ;
/* In the case of web/mono palettes, we actually force
* grayscale drawables through the rgb pass2 functions
*/
1998-01-22 15:02:57 +08:00
if ( drawable_gray ( GIMP_DRAWABLE ( layer ) ) )
1997-11-25 06:05:25 +08:00
red_pix = green_pix = blue_pix = GRAY_PIX ;
zero_histogram_rgb ( histogram ) ;
has_alpha = layer_has_alpha ( layer ) ;
1998-01-22 15:02:57 +08:00
pixel_region_init ( & srcPR , GIMP_DRAWABLE ( layer ) - > tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , FALSE ) ;
pixel_region_init ( & destPR , new_tiles , 0 , 0 , GIMP_DRAWABLE ( layer ) - > width , GIMP_DRAWABLE ( layer ) - > height , TRUE ) ;
src_bytes = GIMP_DRAWABLE ( layer ) - > bytes ;
1998-08-16 03:17:36 +08:00
dest_bytes = new_tiles - > bpp ;
1998-01-22 15:02:57 +08:00
width = GIMP_DRAWABLE ( layer ) - > width ;
height = GIMP_DRAWABLE ( layer ) - > height ;
1997-11-25 06:05:25 +08:00
error_limiter = init_error_limit ( ) ;
range_limiter = range_array + 256 ;
src_buf = g_malloc ( width * src_bytes ) ;
dest_buf = g_malloc ( width * dest_bytes ) ;
red_n_row = g_malloc ( sizeof ( int ) * ( width + 2 ) ) ;
red_p_row = g_malloc ( sizeof ( int ) * ( width + 2 ) ) ;
grn_n_row = g_malloc ( sizeof ( int ) * ( width + 2 ) ) ;
grn_p_row = g_malloc ( sizeof ( int ) * ( width + 2 ) ) ;
blu_n_row = g_malloc ( sizeof ( int ) * ( width + 2 ) ) ;
blu_p_row = g_malloc ( sizeof ( int ) * ( width + 2 ) ) ;
memset ( red_p_row , 0 , ( width + 2 ) * sizeof ( int ) ) ;
memset ( grn_p_row , 0 , ( width + 2 ) * sizeof ( int ) ) ;
memset ( blu_p_row , 0 , ( width + 2 ) * sizeof ( int ) ) ;
fs_err1 = floyd_steinberg_error1 + 511 ;
fs_err2 = floyd_steinberg_error2 + 511 ;
fs_err3 = floyd_steinberg_error3 + 511 ;
fs_err4 = floyd_steinberg_error4 + 511 ;
odd_row = 0 ;
for ( row = 0 ; row < height ; row + + )
{
pixel_region_get_row ( & srcPR , 0 , row , width , src_buf , 1 ) ;
src = src_buf ;
dest = dest_buf ;
rnr = red_n_row ;
gnr = grn_n_row ;
bnr = blu_n_row ;
rpr = red_p_row + 1 ;
gpr = grn_p_row + 1 ;
bpr = blu_p_row + 1 ;
if ( odd_row )
{
step_dest = - dest_bytes ;
step_src = - src_bytes ;
src + = ( width * src_bytes ) - src_bytes ;
dest + = ( width * dest_bytes ) - dest_bytes ;
rnr + = width + 1 ;
gnr + = width + 1 ;
bnr + = width + 1 ;
rpr + = width ;
gpr + = width ;
bpr + = width ;
* ( rnr - 1 ) = * ( gnr - 1 ) = * ( bnr - 1 ) = 0 ;
}
else
{
step_dest = dest_bytes ;
step_src = src_bytes ;
* ( rnr + 1 ) = * ( gnr + 1 ) = * ( bnr + 1 ) = 0 ;
}
* rnr = * gnr = * bnr = 0 ;
for ( col = 0 ; col < width ; col + + )
{
r = range_limiter [ src [ red_pix ] + error_limiter [ * rpr ] ] ;
g = range_limiter [ src [ green_pix ] + error_limiter [ * gpr ] ] ;
b = range_limiter [ src [ blue_pix ] + error_limiter [ * bpr ] ] ;
re = r > > R_SHIFT ;
ge = g > > G_SHIFT ;
be = b > > B_SHIFT ;
cachep = & histogram [ re * MR + ge * MG + be ] ;
/* If we have not seen this color before, find nearest colormap entry */
/* and update the cache */
if ( * cachep = = 0 )
fill_inverse_cmap_rgb ( quantobj , histogram , re , ge , be ) ;
index = * cachep - 1 ;
dest [ INDEXED_PIX ] = index ;
if ( has_alpha )
dest [ ALPHA_I_PIX ] = ( src [ alpha_pix ] > 127 ) ? 255 : 0 ;
/* Abortive initial attempt at alpha-dithering :) */
/* if (has_alpha)
dest [ ALPHA_I_PIX ] = ( src [ alpha_pix ] < = ( random ( ) & 255 ) ) ? 0 : 255 ; */
color = & quantobj - > cmap [ index ] ;
re = r - color - > red ;
ge = g - color - > green ;
be = b - color - > blue ;
if ( odd_row )
{
* ( - - rpr ) + = fs_err1 [ re ] ;
* ( - - gpr ) + = fs_err1 [ ge ] ;
* ( - - bpr ) + = fs_err1 [ be ] ;
* rnr - - + = fs_err2 [ re ] ;
* gnr - - + = fs_err2 [ ge ] ;
* bnr - - + = fs_err2 [ be ] ;
* rnr + = fs_err3 [ re ] ;
* gnr + = fs_err3 [ ge ] ;
* bnr + = fs_err3 [ be ] ;
* ( rnr - 1 ) = fs_err4 [ re ] ;
* ( gnr - 1 ) = fs_err4 [ ge ] ;
* ( bnr - 1 ) = fs_err4 [ be ] ;
}
else
{
* ( + + rpr ) + = fs_err1 [ re ] ;
* ( + + gpr ) + = fs_err1 [ ge ] ;
* ( + + bpr ) + = fs_err1 [ be ] ;
* rnr + + + = fs_err2 [ re ] ;
* gnr + + + = fs_err2 [ ge ] ;
* bnr + + + = fs_err2 [ be ] ;
* rnr + = fs_err3 [ re ] ;
* gnr + = fs_err3 [ ge ] ;
* bnr + = fs_err3 [ be ] ;
* ( rnr + 1 ) = fs_err4 [ re ] ;
* ( gnr + 1 ) = fs_err4 [ ge ] ;
* ( bnr + 1 ) = fs_err4 [ be ] ;
}
dest + = step_dest ;
src + = step_src ;
}
tmp = red_n_row ;
red_n_row = red_p_row ;
red_p_row = tmp ;
tmp = grn_n_row ;
grn_n_row = grn_p_row ;
grn_p_row = tmp ;
tmp = blu_n_row ;
blu_n_row = blu_p_row ;
blu_p_row = tmp ;
odd_row = ! odd_row ;
pixel_region_set_row ( & destPR , 0 , row , width , dest_buf ) ;
}
g_free ( error_limiter - 255 ) ;
g_free ( red_n_row ) ;
g_free ( red_p_row ) ;
g_free ( grn_n_row ) ;
g_free ( grn_p_row ) ;
g_free ( blu_n_row ) ;
g_free ( blu_p_row ) ;
g_free ( src_buf ) ;
g_free ( dest_buf ) ;
}
static void
delete_median_cut ( QuantizeObj * quantobj )
{
g_free ( quantobj - > histogram ) ;
g_free ( quantobj ) ;
}
/**************************************************************/
static QuantizeObj *
initialize_median_cut ( int type ,
int num_colors ,
int dither_type ,
int palette_type )
{
QuantizeObj * quantobj ;
/* Initialize the data structures */
quantobj = g_malloc ( sizeof ( QuantizeObj ) ) ;
if ( type = = GRAY & & palette_type = = MAKE_PALETTE )
quantobj - > histogram = g_malloc ( sizeof ( ColorFreq ) * 256 ) ;
else
quantobj - > histogram = g_malloc ( sizeof ( ColorFreq ) *
HIST_R_ELEMS *
HIST_G_ELEMS *
HIST_B_ELEMS ) ;
quantobj - > desired_number_of_colors = num_colors ;
switch ( type )
{
case GRAY :
switch ( palette_type )
{
case MAKE_PALETTE :
quantobj - > first_pass = median_cut_pass1_gray ;
break ;
case WEB_PALETTE :
quantobj - > first_pass = webpal_pass1 ;
break ;
1997-12-14 19:36:53 +08:00
case CUSTOM_PALETTE :
quantobj - > first_pass = custompal_pass1 ;
needs_quantize = TRUE ;
break ;
1997-11-25 06:05:25 +08:00
case MONO_PALETTE :
default :
quantobj - > first_pass = monopal_pass1 ;
}
if ( palette_type = = WEB_PALETTE | |
1997-12-14 19:36:53 +08:00
palette_type = = MONO_PALETTE | | palette_type = = CUSTOM_PALETTE )
1997-11-25 06:05:25 +08:00
switch ( dither_type )
{
case NODITHER :
quantobj - > second_pass = median_cut_pass2_no_dither_rgb ;
break ;
case FSDITHER :
quantobj - > second_pass = median_cut_pass2_fs_dither_rgb ;
break ;
}
else
switch ( dither_type )
{
case NODITHER :
quantobj - > second_pass = median_cut_pass2_no_dither_gray ;
break ;
case FSDITHER :
quantobj - > second_pass = median_cut_pass2_fs_dither_gray ;
break ;
}
break ;
case RGB :
switch ( palette_type )
{
case MAKE_PALETTE :
quantobj - > first_pass = median_cut_pass1_rgb ;
break ;
case WEB_PALETTE :
quantobj - > first_pass = webpal_pass1 ;
1997-12-14 19:36:53 +08:00
needs_quantize = TRUE ;
break ;
case CUSTOM_PALETTE :
quantobj - > first_pass = custompal_pass1 ;
needs_quantize = TRUE ;
1997-11-25 06:05:25 +08:00
break ;
case MONO_PALETTE :
default :
quantobj - > first_pass = monopal_pass1 ;
}
switch ( dither_type )
{
case NODITHER :
quantobj - > second_pass = median_cut_pass2_no_dither_rgb ;
break ;
case FSDITHER :
quantobj - > second_pass = median_cut_pass2_fs_dither_rgb ;
break ;
case NODESTRUCTDITHER :
quantobj - > second_pass = median_cut_pass2_nodestruct_dither_rgb ;
break ;
}
break ;
}
quantobj - > delete_func = delete_median_cut ;
return quantobj ;
}