[gimp] app: move indexed conversion to gimpimage-convert-indexed.[ch]
- From: Michael Natterer <mitch src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gimp] app: move indexed conversion to gimpimage-convert-indexed.[ch]
- Date: Sat, 30 Apr 2016 16:36:19 +0000 (UTC)
commit ef8b802032b5d36be4ef0046d8329e9411a860ad
Author: Michael Natterer <mitch gimp org>
Date: Fri Apr 29 10:34:03 2016 +0200
app: move indexed conversion to gimpimage-convert-indexed.[ch]
It's so much code and takes so many additional parameters over RGB and
GRAY conversion, it got its own place and function now.
app/actions/image-commands.c | 4 +-
app/core/Makefile.am | 2 +
app/core/gimpimage-convert-indexed.c | 4473 ++++++++++++++++++++++++++++++++++
app/core/gimpimage-convert-indexed.h | 41 +
app/core/gimpimage-convert-type.c | 4452 +---------------------------------
app/core/gimpimage-convert-type.h | 25 +-
app/dialogs/convert-type-dialog.c | 21 +-
app/pdb/image-convert-cmds.c | 21 +-
po/POTFILES.in | 1 +
tools/pdbgen/pdb/image_convert.pdb | 21 +-
10 files changed, 4584 insertions(+), 4477 deletions(-)
---
diff --git a/app/actions/image-commands.c b/app/actions/image-commands.c
index feb57e1..6a25535 100644
--- a/app/actions/image-commands.c
+++ b/app/actions/image-commands.c
@@ -195,9 +195,7 @@ image_convert_base_type_cmd_callback (GtkAction *action,
if (dialog)
gtk_widget_destroy (dialog);
- if (! gimp_image_convert_type (image, value,
- 0, 0, FALSE, FALSE, FALSE, 0, NULL,
- NULL, &error))
+ if (! gimp_image_convert_type (image, value, NULL, &error))
{
gimp_message_literal (image->gimp,
G_OBJECT (widget), GIMP_MESSAGE_WARNING,
diff --git a/app/core/Makefile.am b/app/core/Makefile.am
index 0fddeac..73bbd6b 100644
--- a/app/core/Makefile.am
+++ b/app/core/Makefile.am
@@ -218,6 +218,8 @@ libappcore_a_sources = \
gimpimage-color-profile.h \
gimpimage-colormap.c \
gimpimage-colormap.h \
+ gimpimage-convert-indexed.c \
+ gimpimage-convert-indexed.h \
gimpimage-convert-fsdither.h \
gimpimage-convert-data.h \
gimpimage-convert-precision.c \
diff --git a/app/core/gimpimage-convert-indexed.c b/app/core/gimpimage-convert-indexed.c
new file mode 100644
index 0000000..032f604
--- /dev/null
+++ b/app/core/gimpimage-convert-indexed.c
@@ -0,0 +1,4473 @@
+/* GIMP - The GNU Image Manipulation Program
+ * Copyright (C) 1995 Spencer Kimball and Peter Mattis
+ *
+ * gimpimage-convert-indexed.c
+ * Copyright (C) 1997-2004 Adam D. Moss <adam gimp org>
+ *
+ * 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * 2005-09-04 - Switch 'positional' dither matrix to a 32x32 Bayer,
+ * which generates results that compress somewhat better (and may look
+ * worse or better depending on what you enjoy...). [adam gimp org]
+ *
+ * 2004-12-12 - Use a slower but much nicer technique for finding the
+ * two best colors to dither between when using fixed/positional
+ * dither methods. Makes positional dither much less lame. [adam gimp org]
+ *
+ * 2002-02-10 - Quantizer version 3.0 (the rest of the commit started
+ * a year ago -- whoops). Divide colors within CIE L*a*b* space using
+ * CPercep module (cpercep.[ch]), color-match and dither likewise,
+ * change the underlying box selection criteria and division point
+ * logic, bump luminance precision upwards, etc.etc. Generally
+ * chooses a much richer color set, especially for low numbers of
+ * colors. n.b.: Less luminance-sloppy in straight remapping which is
+ * good for color but a bit worse for high-frequency detail (that's
+ * partly what fs-dithering is for -- use it). [adam gimp org]
+ *
+ * 2001-03-25 - Define accessor function/macro for histogram reads and
+ * writes. This slows us down a little because we avoid some of the
+ * dirty tricks we used when we knew that the histogram was a straight
+ * 3d array, so I've recovered some of the speed loss by implementing
+ * a 5d accessor function with good locality of reference. This change
+ * is the first step towards quantizing in a more interesting colorspace
+ * than frumpy old RGB. [Adam]
+ *
+ * 2000/01/30 - Use palette_selector instead of option_menu for custom
+ * palette. Use libgimp callback functions. [Sven]
+ *
+ * 99/09/01 - Created a low-bleed FS-dither option. [Adam]
+ *
+ * 99/08/29 - Deterministic color dithering to arbitrary palettes.
+ * Ideal for animations that are going to be delta-optimized or simply
+ * don't want to look 'busy' in static areas. Also a bunch of bugfixes
+ * and tweaks. [Adam]
+ *
+ * 99/08/28 - Deterministic alpha dithering over layers, reduced bleeding
+ * of transparent values into opaque values, added optional stage to
+ * remove duplicate or unused color entries from final colormap. [Adam]
+ *
+ * 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 possessing 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]
+ *
+ * 99/01/10 - Hourglass... [Adam]
+ *
+ * 98/07/25 - Convert-to-indexed now remembers the last invocation's
+ * settings. Also, GRAY->INDEXED is more flexible. [Adam]
+ *
+ * 98/07/05 - Sucked the warning about quantizing to too many colors 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]
+ *
+ * 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 ]
+ *
+ * 98/03/23 - fixed a longstanding fencepost - hopefully the *right*
+ * way, *again*. [Adam]
+ *
+ * 97/11/14 - added a proper pdb interface and support for dithering
+ * to custom palettes (based on a patch by Eric Hernes) [Yosh]
+ *
+ * 97/11/04 - fixed the accidental use of the color-counting case
+ * when palette_type is WEB or MONO. [Adam]
+ *
+ * 97/10/25 - color-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]
+ *
+ * 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]
+ */
+
+ /* TODO for Convert:
+ *
+ * . Tweak, tweak, tweak. Old RGB code was tuned muchly.
+ *
+ * . Re-enable Heckbert locality for matching, benchmark it
+ *
+ * . Try faster fixed-point sRGB<->L*a*b* pixel conversion (see cpercep.c)
+ *
+ * . Use palette of another open INDEXED image?
+ *
+ * . Do error-splitting trick for GREY->INDEXED (hardly worth it)
+ */
+
+ /* CODE READABILITY BUGS:
+ *
+ * . Most uses of variants of the R,G,B variable naming convention
+ * are referring to L*a*b* co-ordinates, not RGB co-ordinates!
+ *
+ * . Each said variable is usually one of the following, but it is
+ * rarely clear which one:
+ * - (assumed sRGB) raw non-linear 8-bit RGB co-ordinates
+ * - 'full'-precision (unshifted) 8-bit L*a*b* co-ordinates
+ * - box-space (reduced-precision shifted L*a*b*) co-ordinates
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <cairo.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+#include <gegl.h>
+
+#include "libgimpcolor/gimpcolor.h"
+#include "libgimpmath/gimpmath.h"
+
+#include "core-types.h"
+
+#include "gegl/gimp-gegl-utils.h"
+
+#include "gimp.h"
+#include "gimpcontainer.h"
+#include "gimpdrawable.h"
+#include "gimperror.h"
+#include "gimpimage.h"
+#include "gimpimage-color-profile.h"
+#include "gimpimage-colormap.h"
+#include "gimpimage-undo.h"
+#include "gimpimage-undo-push.h"
+#include "gimplayer.h"
+#include "gimppalette.h"
+#include "gimpprogress.h"
+
+#include "text/gimptextlayer.h"
+
+#include "gimpimage-convert-fsdither.h"
+#include "gimpimage-convert-data.h"
+#include "gimpimage-convert-indexed.h"
+
+#include "gimp-intl.h"
+
+
+/* basic memory/quality tradeoff */
+#define PRECISION_R 8
+#define PRECISION_G 6
+#define PRECISION_B 6
+
+#define HIST_R_ELEMS (1<<PRECISION_R)
+#define HIST_G_ELEMS (1<<PRECISION_G)
+#define HIST_B_ELEMS (1<<PRECISION_B)
+
+#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)
+
+/* we've stretched our non-cubic L*a*b* volume to touch the
+ * faces of the logical cube we've allocated for it, so re-scale
+ * again in inverse proportion to get back to linear proportions.
+ */
+#define R_SCALE 13 /* scale R (L*) distances by this much */
+#define G_SCALE 24 /* scale G (a*) distances by this much */
+#define B_SCALE 26 /* and B (b*) by this much */
+
+
+typedef struct _Color Color;
+typedef struct _QuantizeObj QuantizeObj;
+
+typedef void (* Pass1Func) (QuantizeObj *quantize_obj);
+typedef void (* Pass2InitFunc) (QuantizeObj *quantize_obj);
+typedef void (* Pass2Func) (QuantizeObj *quantize_obj,
+ GimpLayer *layer,
+ GeglBuffer *new_buffer);
+typedef void (* CleanupFunc) (QuantizeObj *quantize_obj);
+
+typedef gulong ColorFreq;
+typedef ColorFreq * CFHistogram;
+
+typedef enum { AXIS_UNDEF, AXIS_RED, AXIS_BLUE, AXIS_GREEN } AxisType;
+
+typedef double etype;
+
+
+/*
+ We provide two different histogram access interfaces. HIST_LIN()
+ accesses the histogram in histogram-native space, taking absolute
+ histogram co-ordinates. HIST_RGB() accesses the histogram in RGB
+ space. This latter takes unsigned 8-bit co-ordinates, internally
+ converts those co-ordinates to histogram-native space and returns
+ the access pointer to the corresponding histogram cell.
+
+ Using these two interfaces we can import RGB data into a more
+ interesting space and efficiently work in the latter space until
+ it is time to output the quantized values in RGB again. For
+ this final conversion we implement the function lin_to_rgb().
+
+ We effectively pull our three-dimensional space into five dimensions
+ such that the most-entropic bits lay in the lowest bits of the resulting
+ array index. This gives significantly better locality of reference
+ and hence a small speedup despite the extra work involved in calculating
+ the index.
+
+ Why not six dimensions? The expansion of dimensionality is good for random
+ access such as histogram population and the query pattern typical of
+ dithering but we have some code which iterates in a scanning manner, for
+ which the expansion is suboptimal. The compromise is to leave the B
+ dimension unmolested in the lower-order bits of the index, since this is
+ the dimension most commonly iterated through in the inner loop of the
+ scans.
+
+ --adam
+
+ RhGhRlGlB
+*/
+#define VOL_GBITS (PRECISION_G)
+#define VOL_BBITS (PRECISION_B)
+#define VOL_RBITS (PRECISION_R)
+#define VOL_GBITSh (VOL_GBITS - 3)
+#define VOL_GBITSl (VOL_GBITS - VOL_GBITSh)
+#define VOL_BBITSh (VOL_BBITS - 4)
+#define VOL_BBITSl (VOL_BBITS - VOL_BBITSh)
+#define VOL_RBITSh (VOL_RBITS - 3)
+#define VOL_RBITSl (VOL_RBITS - VOL_RBITSh)
+#define VOL_GMASKh (((1<<VOL_GBITSh)-1) << VOL_GBITSl)
+#define VOL_GMASKl ((1<<VOL_GBITSl)-1)
+#define VOL_BMASKh (((1<<VOL_BBITSh)-1) << VOL_BBITSl)
+#define VOL_BMASKl ((1<<VOL_BBITSl)-1)
+#define VOL_RMASKh (((1<<VOL_RBITSh)-1) << VOL_RBITSl)
+#define VOL_RMASKl ((1<<VOL_RBITSl)-1)
+/* The 5d compromise thing. */
+#define REF_FUNC(r,g,b) \
+( \
+ (((r) & VOL_RMASKh) << (VOL_BBITS + VOL_GBITS)) | \
+ (((r) & VOL_RMASKl) << (VOL_GBITSl + VOL_BBITS)) | \
+ (((g) & VOL_GMASKh) << (VOL_RBITSl + VOL_BBITS)) | \
+ (((g) & VOL_GMASKl) << (VOL_BBITS)) | \
+ (b) \
+)
+/* The full-on 6d thing. */
+/*
+#define REF_FUNC(r,g,b) \
+( \
+ (((r) & VOL_RMASKh) << (VOL_BBITS + VOL_GBITS)) | \
+ (((r) & VOL_RMASKl) << (VOL_GBITSl + VOL_BBITSl)) | \
+ (((g) & VOL_GMASKh) << (VOL_RBITSl + VOL_BBITS)) | \
+ (((g) & VOL_GMASKl) << (VOL_BBITSl)) | \
+ (((b) & VOL_BMASKh) << (VOL_RBITSl + VOL_GBITSl)) | \
+ ((b) & VOL_BMASKl) \
+)
+*/
+/* The boring old 3d thing. */
+/*
+#define REF_FUNC(r,g,b) (((r)<<(PRECISION_G+PRECISION_B)) | ((g)<<(PRECISION_B)) | (b))
+*/
+
+/* You even get to choose whether you want the accessor function
+ implemented as a macro or an inline function. Don't say I never
+ give you anything. */
+/*
+#define HIST_LIN(hist_ptr,r,g,b) (&(hist_ptr)[REF_FUNC((r),(g),(b))])
+*/
+static inline ColorFreq *
+HIST_LIN (ColorFreq *hist_ptr,
+ const gint r,
+ const gint g,
+ const gint b)
+{
+ return (&(hist_ptr) [REF_FUNC (r, g, b)]);
+}
+
+
+#define LOWA (-86.181F)
+#define LOWB (-107.858F)
+#define HIGHA (98.237F)
+#define HIGHB (94.480F)
+
+#if 1
+#define LRAT (2.55F)
+#define ARAT (255.0F / (HIGHA - LOWA))
+#define BRAT (255.0F / (HIGHB - LOWB))
+#else
+#define LRAT (1.0F)
+#define ARAT (1.0F)
+#define BRAT (1.0F)
+#endif
+
+static const Babl *rgb_to_lab_fish = NULL;
+static const Babl *lab_to_rgb_fish = NULL;
+
+static inline void
+rgb_to_unshifted_lin (const guchar r,
+ const guchar g,
+ const guchar b,
+ gint *hr,
+ gint *hg,
+ gint *hb)
+{
+ gint or, og, ob;
+ gfloat rgb[3] = { r / 255.0, g / 255.0, b / 255.0 };
+ gfloat lab[3];
+
+ babl_process (rgb_to_lab_fish, rgb, lab, 1);
+
+ /* fprintf(stderr, " %d-%d-%d -> %0.3f,%0.3f,%0.3f ", r, g, b, sL, sa, sb);*/
+
+ or = RINT(lab[0] * LRAT);
+ og = RINT((lab[1] - LOWA) * ARAT);
+ ob = RINT((lab[2] - LOWB) * BRAT);
+
+ *hr = CLAMP(or, 0, 255);
+ *hg = CLAMP(og, 0, 255);
+ *hb = CLAMP(ob, 0, 255);
+
+ /* fprintf(stderr, " %d:%d:%d ", *hr, *hg, *hb); */
+}
+
+
+static inline void
+rgb_to_lin (const guchar r,
+ const guchar g,
+ const guchar b,
+ gint *hr,
+ gint *hg,
+ gint *hb)
+{
+ gint or, og, ob;
+
+ /*
+ double sL, sa, sb;
+ {
+ double low_l = 999.0, low_a = 999.9, low_b = 999.0;
+ double high_l = -999.0, high_a = -999.0, high_b = -999.0;
+
+ int r,g,b;
+
+ for (r=0; r<256; r++)
+ for (g=0; g<256; g++)
+ for (b=0; b<256; b++)
+ {
+ cpercep_rgb_to_space(r,g,b, &sL, &sa, &sb);
+
+ if (sL > high_l)
+ high_l = sL;
+ if (sL < low_l)
+ low_l = sL;
+ if (sa > high_a)
+ high_a = sa;
+ if (sa < low_a)
+ low_a = sa;
+ if (sb > high_b)
+ high_b = sb;
+ if (sb < low_b)
+ low_b = sb;
+ }
+
+ fprintf(stderr, " [L: %0.3f -> %0.3f / a: %0.3f -> %0.3f / b: %0.3f -> %0.3f]\t", low_l, high_l, low_a,
high_a, low_b, high_b);
+
+ exit(-1);
+ }
+ */
+
+ rgb_to_unshifted_lin (r, g, b, &or, &og, &ob);
+
+#if 0
+#define RSDF(r) ((r) >= ((HIST_R_ELEMS-1) << R_SHIFT) ? HIST_R_ELEMS-1 : \
+ ((r) + ((1<<R_SHIFT)>>1) ) >> R_SHIFT)
+#define GSDF(g) ((g) >= ((HIST_G_ELEMS-1) << G_SHIFT) ? HIST_G_ELEMS-1 : \
+ ((g) + ((1<<G_SHIFT)>>1) ) >> G_SHIFT)
+#define BSDF(b) ((b) >= ((HIST_B_ELEMS-1) << B_SHIFT) ? HIST_B_ELEMS-1 : \
+ ((b) + ((1<<B_SHIFT)>>1) ) >> B_SHIFT)
+#else
+#define RSDF(r) ((r) >> R_SHIFT)
+#define GSDF(g) ((g) >> G_SHIFT)
+#define BSDF(b) ((b) >> B_SHIFT)
+#endif
+
+ or = RSDF (or);
+ og = GSDF (og);
+ ob = BSDF (ob);
+
+ *hr = or;
+ *hg = og;
+ *hb = ob;
+}
+
+
+static inline ColorFreq *
+HIST_RGB (ColorFreq *hist_ptr,
+ const gint r,
+ const gint g,
+ const gint b)
+{
+ gint hr, hg, hb;
+
+ rgb_to_lin (r, g, b, &hr, &hg, &hb);
+
+ return HIST_LIN (hist_ptr, hr, hg, hb);
+}
+
+
+static inline void
+lin_to_rgb (const gdouble hr,
+ const gdouble hg,
+ const gdouble hb,
+ guchar *r,
+ guchar *g,
+ guchar *b)
+{
+ gfloat rgb[3];
+ gfloat lab[3];
+ gdouble ir, ig, ib;
+
+ ir = ((gdouble) (hr)) * 255.0F / (gdouble) (HIST_R_ELEMS - 1);
+ ig = ((gdouble)( hg)) * 255.0F / (gdouble) (HIST_G_ELEMS - 1);
+ ib = ((gdouble)( hb)) * 255.0F / (gdouble) (HIST_B_ELEMS - 1);
+
+ ir = ir / LRAT;
+ ig = (ig / ARAT) + LOWA;
+ ib = (ib / BRAT) + LOWB;
+
+ lab[0] = ir;
+ lab[1] = ig;
+ lab[2] = ib;
+
+ babl_process (lab_to_rgb_fish, lab, rgb, 1);
+
+ *r = RINT (CLAMP (rgb[0] * 255, 0.0F, 255.0F));
+ *g = RINT (CLAMP (rgb[1] * 255, 0.0F, 255.0F));
+ *b = RINT (CLAMP (rgb[2] * 255, 0.0F, 255.0F));
+}
+
+
+
+struct _Color
+{
+ gint red;
+ gint green;
+ gint blue;
+};
+
+struct _QuantizeObj
+{
+ Pass1Func first_pass; /* first pass over image data creates colormap */
+ Pass2InitFunc second_pass_init; /* Initialize data which persists over invocations */
+ Pass2Func second_pass; /* second pass maps from image data to colormap */
+ CleanupFunc delete_func; /* function to clean up data associated with private */
+
+ GimpPalette *custom_palette; /* The custom palette, if any */
+
+ gint desired_number_of_colors; /* Number of colors we will allow */
+ gint actual_number_of_colors; /* Number of colors actually needed */
+ Color cmap[256]; /* colormap created by quantization */
+ Color clin[256]; /* .. converted back to linear space */
+ gulong index_used_count[256]; /* how many times an index was used */
+ CFHistogram histogram; /* holds the histogram */
+
+ gboolean want_alpha_dither;
+ gint error_freedom; /* 0=much bleed, 1=controlled bleed */
+
+ GimpProgress *progress;
+ gint nth_layer;
+ gint n_layers;
+};
+
+typedef struct
+{
+ /* The bounds of the box (inclusive); expressed as histogram indexes */
+ gint Rmin, Rmax;
+ gint Rhalferror;
+ gint Gmin, Gmax;
+ gint Ghalferror;
+ gint Bmin, Bmax;
+ gint Bhalferror;
+
+ /* The volume (actually 2-norm) of the box */
+ gint volume;
+
+ /* The number of nonzero histogram cells within this box */
+ glong colorcount;
+
+ /* 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;
+
+} box, *boxptr;
+
+
+static void zero_histogram_gray (CFHistogram histogram);
+static void zero_histogram_rgb (CFHistogram histogram);
+static void generate_histogram_gray (CFHistogram hostogram,
+ GimpLayer *layer,
+ gboolean alpha_dither);
+static void generate_histogram_rgb (CFHistogram histogram,
+ GimpLayer *layer,
+ gint col_limit,
+ gboolean alpha_dither,
+ GimpProgress *progress,
+ gint nth_layer,
+ gint n_layers);
+
+static QuantizeObj * initialize_median_cut (GimpImageBaseType old_type,
+ gint num_cols,
+ GimpConvertDitherType dither_type,
+ GimpConvertPaletteType palette_type,
+ GimpPalette *custom_palette,
+ gboolean alpha_dither,
+ GimpProgress *progress);
+
+static void compute_color_lin8 (QuantizeObj *quantobj,
+ CFHistogram histogram,
+ boxptr boxp,
+ const int icolor);
+
+
+static guchar found_cols[MAXNUMCOLORS][3];
+static gint num_found_cols;
+static gboolean needs_quantize;
+
+
+/**********************************************************/
+typedef struct
+{
+ glong used_count;
+ guchar initial_index;
+} PalEntry;
+
+static int
+mapping_compare (const void *a,
+ const void *b)
+{
+ PalEntry *m1 = (PalEntry *) a;
+ PalEntry *m2 = (PalEntry *) b;
+
+ return (m2->used_count - m1->used_count);
+}
+
+/* FWIW, the make_remap_table() and mapping_compare() function source
+ * and PalEntry may be re-used under the XFree86-style license.
+ * <adam gimp org>
+ */
+static void
+make_remap_table (const guchar old_palette[],
+ guchar new_palette[],
+ const gulong index_used_count[],
+ guchar remap_table[],
+ gint *num_entries)
+{
+ gint i, j, k;
+ guchar temppal[256 * 3];
+ gulong tempuse[256];
+ gulong transmap[256];
+ PalEntry *palentries;
+ gint used = 0;
+
+ memset (temppal, 0, 256 * 3);
+ memset (tempuse, 0, 256 * sizeof (gulong));
+ memset (transmap, 255, 256 * sizeof (gulong));
+
+ /* First pass - only collect entries which are marked as being used
+ * at all in index_used_count.
+ */
+ for (i = 0; i < *num_entries; i++)
+ {
+ if (index_used_count[i])
+ {
+ temppal[used*3 + 0] = old_palette[i*3 + 0];
+ temppal[used*3 + 1] = old_palette[i*3 + 1];
+ temppal[used*3 + 2] = old_palette[i*3 + 2];
+
+ tempuse[used] = index_used_count[i];
+ transmap[i] = used;
+
+ used++;
+ }
+ }
+
+ /* Second pass - remove duplicates. (O(n^3), could do better!) */
+ for (i = 0; i < used; i++)
+ {
+ for (j = 0; j < i; j++)
+ {
+ if ((temppal[i*3 + 1] == temppal[j*3 + 1]) &&
+ (temppal[i*3 + 0] == temppal[j*3 + 0]) &&
+ (temppal[i*3 + 2] == temppal[j*3 + 2]) &&
+ tempuse[j] &&
+ tempuse[i])
+ {
+ /* Move the 'used' tally from one to the other. */
+ tempuse[i] += tempuse[j];
+ /* zero one of them, deactivating its entry. */
+ tempuse[j] = 0;
+
+ /* change all mappings from this dead index to the live
+ * one.
+ */
+ for (k = 0; k < *num_entries; k++)
+ {
+ if (index_used_count[k] && (transmap[k] == j))
+ transmap[k] = i;
+ }
+ }
+ }
+ }
+
+ /* Third pass - rank all used indicies to the beginning of the
+ * palette.
+ */
+ palentries = g_new (PalEntry, used);
+
+ for (i = 0; i < used; i++)
+ {
+ palentries[i].initial_index = i;
+ palentries[i].used_count = tempuse[i];
+ }
+
+ qsort (palentries, used, sizeof (PalEntry), &mapping_compare);
+
+ for (i = 0; i < *num_entries; i++)
+ {
+ if (index_used_count[i])
+ {
+ for (j = 0; j < used; j++)
+ {
+ if ((transmap[i] == palentries[j].initial_index)
+ && (palentries[j].used_count))
+ {
+ remap_table[i] = j;
+ break;
+ }
+ }
+ }
+ }
+ for (i = 0; i < *num_entries; i++)
+ {
+ if (index_used_count[i])
+ {
+ new_palette[remap_table[i] * 3 + 0] = old_palette[i * 3 + 0];
+ new_palette[remap_table[i] * 3 + 1] = old_palette[i * 3 + 1];
+ new_palette[remap_table[i] * 3 + 2] = old_palette[i * 3 + 2];
+ }
+ }
+
+ *num_entries = 0;
+
+ for (j = 0; j < used; j++)
+ {
+ if (palentries[j].used_count)
+ {
+ (*num_entries)++;
+ }
+ }
+
+ g_free (palentries);
+}
+
+static void
+remap_indexed_layer (GimpLayer *layer,
+ const guchar *remap_table,
+ gint num_entries)
+{
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gint bpp;
+ gboolean has_alpha;
+
+ format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+ has_alpha = babl_format_has_alpha (format);
+
+ iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
+ NULL, 0, NULL,
+ GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ guchar *data = iter->data[0];
+ gint length = iter->length;
+
+ if (has_alpha)
+ {
+ while (length--)
+ {
+ if (data[ALPHA_I])
+ data[INDEXED] = remap_table[data[INDEXED]];
+
+ data += bpp;
+ }
+ }
+ else
+ {
+ while (length--)
+ {
+ data[INDEXED] = remap_table[data[INDEXED]];
+
+ data += bpp;
+ }
+ }
+ }
+}
+
+static gint
+color_quicksort (const void *c1,
+ const void *c2)
+{
+ Color *color1 = (Color *) c1;
+ Color *color2 = (Color *) c2;
+
+ gdouble v1 = GIMP_RGB_LUMINANCE (color1->red, color1->green, color1->blue);
+ gdouble v2 = GIMP_RGB_LUMINANCE (color2->red, color2->green, color2->blue);
+
+ if (v1 < v2)
+ return -1;
+ else if (v1 > v2)
+ return 1;
+ else
+ return 0;
+}
+
+gboolean
+gimp_image_convert_indexed (GimpImage *image,
+ gint num_cols,
+ GimpConvertDitherType dither,
+ gboolean alpha_dither,
+ gboolean text_layer_dither,
+ gboolean remove_dups,
+ GimpConvertPaletteType palette_type,
+ GimpPalette *custom_palette,
+ GimpProgress *progress,
+ GError **error)
+{
+ QuantizeObj *quantobj = NULL;
+ GimpImageBaseType old_type;
+ GList *all_layers;
+ GList *list;
+ GimpColorProfile *dest_profile = NULL;
+ gint nth_layer;
+ gint n_layers;
+
+ g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
+ g_return_val_if_fail (gimp_image_get_base_type (image) != GIMP_INDEXED, FALSE);
+ g_return_val_if_fail (custom_palette == NULL ||
+ GIMP_IS_PALETTE (custom_palette), FALSE);
+ g_return_val_if_fail (custom_palette == NULL ||
+ gimp_palette_get_n_colors (custom_palette) <= 256,
+ FALSE);
+ g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ if (palette_type == GIMP_CUSTOM_PALETTE)
+ {
+ if (! custom_palette)
+ palette_type = GIMP_MONO_PALETTE;
+
+ if (gimp_palette_get_n_colors (custom_palette) == 0)
+ {
+ g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
+ _("Cannot convert image: palette is empty."));
+ return FALSE;
+ }
+ }
+
+ gimp_set_busy (image->gimp);
+
+ all_layers = gimp_image_get_layer_list (image);
+
+ n_layers = g_list_length (all_layers);
+
+ g_object_freeze_notify (G_OBJECT (image));
+
+ gimp_image_undo_group_start (image, GIMP_UNDO_GROUP_IMAGE_CONVERT,
+ C_("undo-type", "Convert Image to Indexed"));
+
+ /* Push the image type to the stack */
+ gimp_image_undo_push_image_type (image, NULL);
+
+ /* Set the new base type */
+ old_type = gimp_image_get_base_type (image);
+
+ g_object_set (image, "base-type", GIMP_INDEXED, NULL);
+
+ /* when converting from GRAY, convert to the new type's builtin
+ * profile.
+ */
+ if (old_type == GIMP_GRAY)
+ {
+ if (gimp_image_get_color_profile (image))
+ dest_profile = gimp_image_get_builtin_color_profile (image);
+ }
+
+ /* Build histogram if necessary. */
+ rgb_to_lab_fish = babl_fish (babl_format ("R'G'B' float"),
+ babl_format ("CIE Lab float"));
+ lab_to_rgb_fish = babl_fish (babl_format ("CIE Lab float"),
+ babl_format ("R'G'B' float"));
+
+ /* don't dither if the input is grayscale and we are simply mapping
+ * every color
+ */
+ if (old_type == GIMP_GRAY &&
+ num_cols == 256 &&
+ palette_type == GIMP_MAKE_PALETTE)
+ {
+ dither = GIMP_NO_DITHER;
+ }
+
+ quantobj = initialize_median_cut (old_type, num_cols, dither,
+ palette_type, custom_palette,
+ alpha_dither,
+ progress);
+
+ if (palette_type == GIMP_MAKE_PALETTE)
+ {
+ if (old_type == GIMP_GRAY)
+ zero_histogram_gray (quantobj->histogram);
+ else
+ zero_histogram_rgb (quantobj->histogram);
+
+ /* To begin, assume that there are fewer colors in the image
+ * than the user actually asked for. In that case, we don't
+ * need to quantize or color-dither.
+ */
+ needs_quantize = FALSE;
+ num_found_cols = 0;
+
+ /* Build the histogram */
+ for (list = all_layers, nth_layer = 0;
+ list;
+ list = g_list_next (list), nth_layer++)
+ {
+ GimpLayer *layer = list->data;
+
+ if (old_type == GIMP_GRAY)
+ {
+ generate_histogram_gray (quantobj->histogram,
+ layer, alpha_dither);
+ }
+ else
+ {
+ /* Note: generate_histogram_rgb may set needs_quantize
+ * if the image contains more colors than the limit
+ * specified by the user.
+ */
+ generate_histogram_rgb (quantobj->histogram,
+ layer, num_cols, alpha_dither,
+ progress, nth_layer, n_layers);
+ }
+ }
+ }
+
+ if (progress)
+ gimp_progress_set_text_literal (progress,
+ _("Converting to indexed colors (stage 2)"));
+
+ if (old_type == GIMP_RGB &&
+ ! needs_quantize &&
+ palette_type == GIMP_MAKE_PALETTE)
+ {
+ gint i;
+
+ /* If this is an RGB image, and the user wanted a custom-built
+ * generated palette, and this image has no more colors 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.
+ */
+
+ quantobj->delete_func (quantobj);
+ quantobj = initialize_median_cut (old_type, num_cols,
+ GIMP_NODESTRUCT_DITHER,
+ palette_type,
+ custom_palette,
+ alpha_dither,
+ progress);
+ /* 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 (palette_type == GIMP_MAKE_PALETTE)
+ qsort (quantobj->cmap,
+ quantobj->actual_number_of_colors, sizeof (Color),
+ color_quicksort);
+
+ if (progress)
+ gimp_progress_set_text_literal (progress,
+ _("Converting to indexed colors (stage 3)"));
+
+ /* Initialise data which must persist across indexed layer iterations */
+ if (quantobj->second_pass_init)
+ quantobj->second_pass_init (quantobj);
+
+ /* Set the generated palette on the image, we need it to
+ * convert the layers. We optionally remove duplicate entries
+ * after the layer conversion.
+ */
+ {
+ guchar colormap[GIMP_IMAGE_COLORMAP_SIZE];
+ gint i, j;
+
+ for (i = 0, j = 0; i < quantobj->actual_number_of_colors; i++)
+ {
+ colormap[j++] = quantobj->cmap[i].red;
+ colormap[j++] = quantobj->cmap[i].green;
+ colormap[j++] = quantobj->cmap[i].blue;
+ }
+
+ gimp_image_set_colormap (image, colormap,
+ quantobj->actual_number_of_colors, TRUE);
+ }
+
+ /* Convert all layers */
+ if (quantobj)
+ quantobj->n_layers = n_layers;
+
+ for (list = all_layers, nth_layer = 0;
+ list;
+ list = g_list_next (list), nth_layer++)
+ {
+ GimpLayer *layer = list->data;
+ gboolean quantize;
+
+ if (gimp_item_is_text_layer (GIMP_ITEM (layer)))
+ quantize = text_layer_dither;
+ else
+ quantize = TRUE;
+
+ if (quantize)
+ {
+ GeglBuffer *new_buffer;
+ gboolean has_alpha;
+
+ has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
+
+ new_buffer =
+ gegl_buffer_new (GEGL_RECTANGLE (0, 0,
+ gimp_item_get_width (GIMP_ITEM (layer)),
+ gimp_item_get_height (GIMP_ITEM (layer))),
+ gimp_image_get_layer_format (image,
+ has_alpha));
+
+ quantobj->nth_layer = nth_layer;
+ quantobj->second_pass (quantobj, layer, new_buffer);
+
+ gimp_drawable_set_buffer (GIMP_DRAWABLE (layer), TRUE, NULL,
+ new_buffer);
+ g_object_unref (new_buffer);
+ }
+ else
+ {
+ gimp_drawable_convert_type (GIMP_DRAWABLE (layer), image, GIMP_INDEXED,
+ gimp_drawable_get_precision (GIMP_DRAWABLE (layer)),
+ dest_profile, 0, 0,
+ TRUE, NULL);
+ }
+ }
+
+ /* Set the final palette on the image */
+ if (remove_dups && (palette_type != GIMP_MAKE_PALETTE))
+ {
+ guchar colormap[GIMP_IMAGE_COLORMAP_SIZE];
+ gint i, j;
+ guchar old_palette[256 * 3];
+ guchar new_palette[256 * 3];
+ guchar remap_table[256];
+ gint num_entries;
+
+ for (i = 0, j = 0; i < quantobj->actual_number_of_colors; i++)
+ {
+ old_palette[j++] = quantobj->cmap[i].red;
+ old_palette[j++] = quantobj->cmap[i].green;
+ old_palette[j++] = quantobj->cmap[i].blue;
+ }
+
+ num_entries = quantobj->actual_number_of_colors;
+
+ /* Generate a remapping table */
+ make_remap_table (old_palette, new_palette,
+ quantobj->index_used_count,
+ remap_table, &num_entries);
+
+ /* Convert all layers */
+ for (list = all_layers; list; list = g_list_next (list))
+ {
+ remap_indexed_layer (list->data, remap_table, num_entries);
+ }
+
+ for (i = 0, j = 0; i < num_entries; i++)
+ {
+ colormap[j] = new_palette[j]; j++;
+ colormap[j] = new_palette[j]; j++;
+ colormap[j] = new_palette[j]; j++;
+ }
+
+ gimp_image_set_colormap (image, colormap, num_entries, TRUE);
+ }
+
+ /* When converting from GRAY, set the new profile.
+ */
+ if (old_type == GIMP_GRAY)
+ {
+ if (gimp_image_get_color_profile (image))
+ gimp_image_set_color_profile (image, dest_profile, NULL);
+ else
+ gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image));
+ }
+
+ /* Delete the quantizer object, if there is one */
+ if (quantobj)
+ quantobj->delete_func (quantobj);
+
+ gimp_image_undo_group_end (image);
+
+ gimp_image_mode_changed (image);
+ g_object_thaw_notify (G_OBJECT (image));
+
+ g_list_free (all_layers);
+
+ gimp_unset_busy (image->gimp);
+
+ return TRUE;
+}
+
+/*
+ * Indexed color conversion machinery
+ */
+
+static void
+zero_histogram_gray (CFHistogram histogram)
+{
+ gint i;
+
+ for (i = 0; i < 256; i++)
+ histogram[i] = 0;
+}
+
+
+static void
+zero_histogram_rgb (CFHistogram histogram)
+{
+ memset (histogram, 0,
+ HIST_R_ELEMS * HIST_G_ELEMS * HIST_B_ELEMS * sizeof (ColorFreq));
+}
+
+
+static void
+generate_histogram_gray (CFHistogram histogram,
+ GimpLayer *layer,
+ gboolean alpha_dither)
+{
+ GeglBufferIterator *iter;
+ const Babl *format;
+ gint bpp;
+ gboolean has_alpha;
+
+ format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+
+ g_return_if_fail (format == babl_format ("Y' u8") ||
+ format == babl_format ("Y'A u8"));
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+ has_alpha = babl_format_has_alpha (format);
+
+ iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
+ NULL, 0, format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *data = iter->data[0];
+ gint length = iter->length;
+
+ if (has_alpha)
+ {
+ while (length--)
+ {
+ if (data[ALPHA_G] > 127)
+ histogram[*data]++;
+
+ data += bpp;
+ }
+ }
+ else
+ {
+ while (length--)
+ {
+ histogram[*data]++;
+
+ data += bpp;
+ }
+ }
+ }
+}
+
+
+static void
+generate_histogram_rgb (CFHistogram histogram,
+ GimpLayer *layer,
+ gint col_limit,
+ gboolean alpha_dither,
+ GimpProgress *progress,
+ gint nth_layer,
+ gint n_layers)
+{
+ GeglBufferIterator *iter;
+ const Babl *format;
+ GeglRectangle *roi;
+ ColorFreq *colfreq;
+ gint nfc_iter;
+ gint row, col, coledge;
+ gint offsetx, offsety;
+ glong layer_size;
+ glong total_size = 0;
+ gint count = 0;
+ gint bpp;
+ gboolean has_alpha;
+
+ format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+
+ g_return_if_fail (format == babl_format ("R'G'B' u8") ||
+ format == babl_format ("R'G'B'A u8"));
+
+ bpp = babl_format_get_bytes_per_pixel (format);
+ has_alpha = babl_format_has_alpha (format);
+
+ gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
+
+ layer_size = (gimp_item_get_width (GIMP_ITEM (layer)) *
+ gimp_item_get_height (GIMP_ITEM (layer)));
+
+ /* g_printerr ("col_limit = %d, nfc = %d\n", col_limit, num_found_cols); */
+
+ iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
+ NULL, 0, format,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ roi = &iter->roi[0];
+
+ if (progress)
+ gimp_progress_set_value (progress, 0.0);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *data = iter->data[0];
+ gint length = iter->length;
+
+ total_size += length;
+
+ /* g_printerr (" [%d,%d - %d,%d]", srcPR.x, src_roi->y, offsetx, offsety); */
+
+ if (needs_quantize)
+ {
+ if (alpha_dither)
+ {
+ /* if alpha-dithering,
+ we need to be deterministic w.r.t. offsets */
+
+ col = roi->x + offsetx;
+ coledge = col + roi->width;
+ row = roi->y + offsety;
+
+ while (length--)
+ {
+ gboolean transparent = FALSE;
+
+ if (has_alpha &&
+ data[ALPHA] <
+ DM[col & DM_WIDTHMASK][row & DM_HEIGHTMASK])
+ transparent = TRUE;
+
+ if (! transparent)
+ {
+ colfreq = HIST_RGB (histogram,
+ data[RED],
+ data[GREEN],
+ data[BLUE]);
+ (*colfreq)++;
+ }
+
+ col++;
+ if (col == coledge)
+ {
+ col = roi->x + offsetx;
+ row++;
+ }
+
+ data += bpp;
+ }
+ }
+ else
+ {
+ while (length--)
+ {
+ if ((has_alpha && ((data[ALPHA] > 127)))
+ || (!has_alpha))
+ {
+ colfreq = HIST_RGB (histogram,
+ data[RED],
+ data[GREEN],
+ data[BLUE]);
+ (*colfreq)++;
+ }
+
+ data += bpp;
+ }
+ }
+ }
+ else
+ {
+ /* if alpha-dithering, we need to be deterministic w.r.t. offsets */
+ col = roi->x + offsetx;
+ coledge = col + roi->width;
+ row = roi->y + offsety;
+
+ while (length--)
+ {
+ gboolean transparent = FALSE;
+
+ if (has_alpha)
+ {
+ if (alpha_dither)
+ {
+ if (data[ALPHA] <
+ DM[col & DM_WIDTHMASK][row & DM_HEIGHTMASK])
+ transparent = TRUE;
+ }
+ else
+ {
+ if (data[ALPHA] <= 127)
+ transparent = TRUE;
+ }
+ }
+
+ if (! transparent)
+ {
+ colfreq = HIST_RGB (histogram,
+ data[RED],
+ data[GREEN],
+ data[BLUE]);
+ (*colfreq)++;
+
+ if (!needs_quantize)
+ {
+ for (nfc_iter = 0;
+ nfc_iter < num_found_cols;
+ nfc_iter++)
+ {
+ if ((data[RED] == found_cols[nfc_iter][0]) &&
+ (data[GREEN] == found_cols[nfc_iter][1]) &&
+ (data[BLUE] == found_cols[nfc_iter][2]))
+ goto already_found;
+ }
+
+ /* Color was not in the table of
+ * existing colors
+ */
+
+ num_found_cols++;
+
+ if (num_found_cols > col_limit)
+ {
+ /* There are more colors in the image than
+ * were allowed. We switch to plain
+ * histogram calculation with a view to
+ * quantizing at a later stage.
+ */
+ needs_quantize = TRUE;
+ /* g_print ("\nmax colors exceeded - needs quantize.\n");*/
+ goto already_found;
+ }
+ else
+ {
+ /* Remember the new color we just found.
+ */
+ found_cols[num_found_cols-1][0] = data[RED];
+ found_cols[num_found_cols-1][1] = data[GREEN];
+ found_cols[num_found_cols-1][2] = data[BLUE];
+ }
+ }
+ }
+ already_found:
+
+ col++;
+ if (col == coledge)
+ {
+ col = roi->x + offsetx;
+ row++;
+ }
+
+ data += bpp;
+ }
+ }
+
+ if (progress && (count % 16 == 0))
+ gimp_progress_set_value (progress,
+ (nth_layer + ((gdouble) total_size)/
+ layer_size) / (gdouble) n_layers);
+ }
+
+/* g_print ("O: col_limit = %d, nfc = %d\n", col_limit, num_found_cols);*/
+}
+
+
+
+static boxptr
+find_split_candidate (const boxptr boxlist,
+ const gint numboxes,
+ AxisType *which_axis,
+ const gint desired_colors)
+{
+ boxptr boxp;
+ gint i;
+ etype maxc = 0;
+ boxptr which = NULL;
+ gdouble Lbias;
+
+ *which_axis = AXIS_UNDEF;
+
+ /* we only perform the initial L-split bias /at all/ if the final
+ number of desired colors is quite low, otherwise it all comes
+ out in the wash anyway and this initial bias generally only hurts
+ us in the long run. */
+ if (desired_colors <= 16)
+ {
+#define BIAS_FACTOR 2.66F
+#define BIAS_NUMBER 2 /* 0 */
+
+ /* we bias towards splitting across L* for first few colors */
+ Lbias = (numboxes > BIAS_NUMBER) ? 1.0F : ((gdouble) (BIAS_NUMBER + 1) -
+ ((gdouble) numboxes)) /
+ ((gdouble) BIAS_NUMBER / BIAS_FACTOR);
+ /*Lbias = 1.0;
+ fprintf(stderr, " [[%d]] ", numboxes);
+ fprintf(stderr, "Using ramped L-split bias.\n");
+ fprintf(stderr, "R\n");
+ */
+ }
+ else
+ Lbias = 1.0F;
+
+ for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++)
+ {
+ if (boxp->volume > 0)
+ {
+#ifndef _MSC_VER
+ etype rpe = (double)((boxp->rerror) * R_SCALE * R_SCALE);
+ etype gpe = (double)((boxp->gerror) * G_SCALE * G_SCALE);
+ etype bpe = (double)((boxp->berror) * B_SCALE * B_SCALE);
+#else
+ /*
+ * Sorry about the mess, otherwise would get :
+ * error C2520: conversion from unsigned __int64 to double
+ * not implemented, use signed __int64
+ */
+ etype rpe = (double)(((__int64)boxp->rerror) * R_SCALE * R_SCALE);
+ etype gpe = (double)(((__int64)boxp->gerror) * G_SCALE * G_SCALE);
+ etype bpe = (double)(((__int64)boxp->berror) * B_SCALE * B_SCALE);
+#endif
+
+ if (Lbias * rpe > maxc &&
+ boxp->Rmin < boxp->Rmax)
+ {
+ which = boxp;
+ maxc = Lbias * rpe;
+ *which_axis = AXIS_RED;
+ }
+
+ if (gpe > maxc &&
+ boxp->Gmin < boxp->Gmax)
+ {
+ which = boxp;
+ maxc = gpe;
+ *which_axis = AXIS_GREEN;
+ }
+
+ if (bpe > maxc &&
+ boxp->Bmin < boxp->Bmax)
+ {
+ which = boxp;
+ maxc = bpe;
+ *which_axis = AXIS_BLUE;
+ }
+ }
+ }
+
+ /* fprintf(stderr, " %f,%p ", maxc, which); */
+ /* fprintf(stderr, " %llu ", maxc); */
+
+ return which;
+}
+
+
+/* Find the splittable box with the largest (scaled) volume Returns
+ * NULL if no splittable boxes remain
+ */
+static boxptr
+find_biggest_volume (const boxptr boxlist,
+ const gint numboxes)
+{
+ boxptr boxp;
+ gint i;
+ gint 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;
+}
+
+
+/* Shrink the min/max bounds of a box to enclose only nonzero
+ * elements, and recompute its volume and population
+ */
+static void
+update_box_gray (const CFHistogram histogram,
+ boxptr boxp)
+{
+ gint i, min, max, dist;
+ ColorFreq 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;
+}
+
+
+/* Shrink the min/max bounds of a box to enclose only nonzero
+ * elements, and recompute its volume, population and error
+ */
+static void
+update_box_rgb (const CFHistogram histogram,
+ boxptr boxp,
+ const gint cells_remaining)
+{
+ gint R, G, B;
+ gint Rmin, Rmax, Gmin, Gmax, Bmin, Bmax;
+ gint dist0, dist1, dist2;
+ ColorFreq ccount;
+ /*
+ guint64 tempRerror;
+ guint64 tempGerror;
+ guint64 tempBerror;
+ */
+ QuantizeObj dummyqo;
+ box dummybox;
+
+ /* fprintf(stderr, "U"); */
+
+ 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++)
+ {
+ for (B = Bmin; B <= Bmax; B++)
+ {
+ if (*HIST_LIN (histogram, R, G, B) != 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++)
+ {
+ for (B = Bmin; B <= Bmax; B++)
+ {
+ if (*HIST_LIN (histogram, R, G, B) != 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++)
+ {
+ for (B = Bmin; B <= Bmax; B++)
+ {
+ if (*HIST_LIN (histogram, R, G, B) != 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++)
+ {
+ for (B = Bmin; B <= Bmax; B++)
+ {
+ if (*HIST_LIN (histogram, R, G, B) != 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++)
+ {
+ for (G = Gmin; G <= Gmax; G++)
+ {
+ if (*HIST_LIN (histogram, R, G, B) != 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++)
+ {
+ for (G = Gmin; G <= Gmax; G++)
+ {
+ if (*HIST_LIN (histogram, R, G, B) != 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. (ADM: note: this isn't true.)
+ * 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.
+ */
+ dist0 = ((1 + Rmax - Rmin) << R_SHIFT) * R_SCALE;
+ dist1 = ((1 + Gmax - Gmin) << G_SHIFT) * G_SCALE;
+ dist2 = ((1 + Bmax - Bmin) << B_SHIFT) * B_SCALE;
+ boxp->volume = dist0*dist0 + dist1*dist1 + dist2*dist2;
+ /* boxp->volume = dist0 * dist1 * dist2; */
+
+ compute_color_lin8(&dummyqo, histogram, boxp, 0);
+
+ /*printf("(%d %d %d)\n", dummyqo.cmap[0].red,dummyqo.cmap[0].green,dummyqo.cmap[0].blue);
+ fflush(stdout);*/
+
+ /* Now scan remaining volume of box and compute population */
+ ccount = 0;
+ boxp->error = 0;
+ boxp->rerror = 0;
+ boxp->gerror = 0;
+ boxp->berror = 0;
+ for (R = Rmin; R <= Rmax; R++)
+ {
+ for (G = Gmin; G <= Gmax; G++)
+ {
+ for (B = Bmin; B <= Bmax; B++)
+ {
+ ColorFreq freq_here;
+
+ freq_here = *HIST_LIN (histogram, R, G, B);
+
+ if (freq_here != 0)
+ {
+ int ge, be, re;
+
+ dummybox.Rmin = dummybox.Rmax = R;
+ dummybox.Gmin = dummybox.Gmax = G;
+ dummybox.Bmin = dummybox.Bmax = B;
+ compute_color_lin8(&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 += freq_here * (re) * (re);
+ boxp->gerror += freq_here * (ge) * (ge);
+ boxp->berror += freq_here * (be) * (be);
+
+ ccount += freq_here;
+ }
+ }
+ }
+ }
+
+#if 0
+ fg d;flg fd;kg fld;gflkfld
+ /* Scan again, taking note of halfway error point for red axis */
+ tempRerror = 0;
+ boxp->Rhalferror = Rmin;
+#warning r<=?
+ for (R = Rmin; R <= Rmax; R++)
+ {
+ for (G = Gmin; G <= Gmax; G++)
+ {
+ for (B = Bmin; B <= Bmax; B++)
+ {
+ ColorFreq freq_here;
+ freq_here = *HIST_LIN(histogram, R, G, B);
+ if (freq_here != 0)
+ {
+ int re;
+ int idist;
+ double dist;
+
+ dummybox.Rmin = dummybox.Rmax = R;
+ dummybox.Gmin = dummybox.Gmax = G;
+ dummybox.Bmin = dummybox.Bmax = B;
+ compute_color_lin8(&dummyqo, histogram, &dummybox, 1);
+
+ re = dummyqo.cmap[0].red - dummyqo.cmap[1].red;
+
+ tempRerror += freq_here * (re) * (re);
+
+ if (tempRerror*2 >= boxp->rerror)
+ goto green_axisscan;
+ else
+ boxp->Rhalferror = R;
+ }
+ }
+ }
+ }
+ fprintf(stderr, " D:");
+ green_axisscan:
+
+ fprintf(stderr, "<%d: %llu/%llu> ", R, tempRerror, boxp->rerror);
+ /* Scan again, taking note of halfway error point for green axis */
+ tempGerror = 0;
+ boxp->Ghalferror = Gmin;
+#warning G<=?
+ for (G = Gmin; G <= Gmax; G++)
+ {
+ for (R = Rmin; R <= Rmax; R++)
+ {
+ for (B = Bmin; B <= Bmax; B++)
+ {
+ ColorFreq freq_here;
+ freq_here = *HIST_LIN(histogram, R, G, B);
+ if (freq_here != 0)
+ {
+ int ge;
+ dummybox.Rmin = dummybox.Rmax = R;
+ dummybox.Gmin = dummybox.Gmax = G;
+ dummybox.Bmin = dummybox.Bmax = B;
+ compute_color_lin8(&dummyqo, histogram, &dummybox, 1);
+
+ ge = dummyqo.cmap[0].green - dummyqo.cmap[1].green;
+
+ tempGerror += freq_here * (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;
+#warning B<=?
+ for (B = Bmin; B <= Bmax; B++)
+ {
+ for (R = Rmin; R <= Rmax; R++)
+ {
+ for (G = Gmin; G <= Gmax; G++)
+ {
+ ColorFreq freq_here;
+ freq_here = *HIST_LIN(histogram, R, G, B);
+ if (freq_here != 0)
+ {
+ int be;
+ dummybox.Rmin = dummybox.Rmax = R;
+ dummybox.Gmin = dummybox.Gmax = G;
+ dummybox.Bmin = dummybox.Bmax = B;
+ compute_color_lin8(&dummyqo, histogram, &dummybox, 1);
+
+ be = dummyqo.cmap[0].blue - dummyqo.cmap[1].blue;
+
+ tempBerror += freq_here * (be) * (be);
+
+ if (tempBerror*2 >= boxp->berror)
+ goto finished_axesscan;
+ else
+ boxp->Bhalferror = B;
+ }
+ }
+ }
+ }
+ finished_axesscan:
+#else
+
+ boxp->Rhalferror = Rmin + (Rmax - Rmin + 1) / 2;
+ boxp->Ghalferror = Gmin + (Gmax - Gmin + 1) / 2;
+ boxp->Bhalferror = Bmin + (Bmax - Bmin + 1) / 2;
+
+ if (dist0 && dist1 && dist2)
+ {
+ AxisType longest_ax = AXIS_UNDEF;
+ gint longest_length = 0;
+ gint longest_length2 = 0;
+ gint ratio;
+
+ /*
+ fprintf(stderr, "[%d,%d,%d=%d,%d,%d] ",
+ (Rmax - Rmin), (Gmax - Gmin), (Bmax - Bmin),
+ dist0, dist1, dist2);
+ */
+
+ if (dist0 >= longest_length)
+ {
+ longest_length2 = longest_length;
+ longest_length = dist0;
+ longest_ax = AXIS_RED;
+ }
+ else if (dist0 >= longest_length2)
+ {
+ longest_length2 = dist0;
+ }
+
+ if (dist1 >= longest_length)
+ {
+ longest_length2 = longest_length;
+ longest_length = dist1;
+ longest_ax = AXIS_GREEN;
+ }
+ else if (dist1 >= longest_length2)
+ {
+ longest_length2 = dist1;
+ }
+
+ if (dist2 >= longest_length)
+ {
+ longest_length2 = longest_length;
+ longest_length = dist2;
+ longest_ax = AXIS_BLUE;
+ }
+ else if (dist2 >= longest_length2)
+ {
+ longest_length2 = dist2;
+ }
+
+ if (longest_length2 == 0)
+ longest_length2 = 1;
+
+ ratio = (longest_length + longest_length2/2) / longest_length2;
+ /* fprintf(stderr, " ratio:(%d/%d)=%d ", longest_length, longest_length2, ratio);
+ fprintf(stderr, "C%d ", cells_remaining); */
+
+ if (ratio > cells_remaining + 1)
+ ratio = cells_remaining + 1;
+
+ if (ratio > 2)
+ {
+ switch (longest_ax)
+ {
+ case AXIS_RED:
+ if (Rmin + (Rmax - Rmin + ratio / 2) / ratio < Rmax)
+ {
+ /* fprintf(stderr, "FR%d \007\n",ratio);*/
+ boxp->Rhalferror = Rmin + (Rmax - Rmin + ratio / 2) / ratio;
+ }
+ break;
+ case AXIS_GREEN:
+ if (Gmin + (Gmax - Gmin + ratio / 2) / ratio < Gmax)
+ {
+ /* fprintf(stderr, "FG%d \007\n",ratio);*/
+ boxp->Ghalferror = Gmin + (Gmax - Gmin + ratio / 2) / ratio;
+ }
+ break;
+ case AXIS_BLUE:
+ if (Bmin + (Bmax - Bmin + ratio / 2) / ratio < Bmax)
+ {
+ /* fprintf(stderr, "FB%d \007\n",ratio);*/
+ boxp->Bhalferror = Bmin + (Bmax - Bmin + ratio / 2) / ratio;
+ }
+ break;
+ default:
+ g_warning ("GRR, UNDEF LONGEST AXIS\007\n");
+ }
+ }
+ }
+
+ if (boxp->Rhalferror == Rmax)
+ boxp->Rhalferror = Rmin;
+ if (boxp->Ghalferror == Gmax)
+ boxp->Ghalferror = Gmin;
+ if (boxp->Bhalferror == Bmax)
+ boxp->Bhalferror = Bmin;
+
+ /*
+ boxp->Rhalferror = RSDF(dummyqo.cmap[0].red);
+ boxp->Ghalferror = GSDF(dummyqo.cmap[0].green);
+ boxp->Bhalferror = BSDF(dummyqo.cmap[0].blue);
+ */
+
+ /*
+ boxp->Rhalferror = (RSDF(dummyqo.cmap[0].red) + (Rmin+Rmax)/2)/2;
+ boxp->Ghalferror = (GSDF(dummyqo.cmap[0].green) + (Gmin+Gmax)/2)/2;
+ boxp->Bhalferror = (BSDF(dummyqo.cmap[0].blue) + (Bmin+Bmax)/2)/2;
+ */
+
+
+#endif
+ /*
+ fprintf(stderr, " %d,%d", dummyqo.cmap[0].blue, boxp->Bmax);
+
+ g_assert(boxp->Rhalferror >= boxp->Rmin);
+ g_assert(boxp->Rhalferror < boxp->Rmax);
+ g_assert(boxp->Ghalferror >= boxp->Gmin);
+ g_assert(boxp->Ghalferror < boxp->Gmax);
+ g_assert(boxp->Bhalferror >= boxp->Bmin);
+ g_assert(boxp->Bhalferror < boxp->Bmax);*/
+
+ /*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);*/
+
+ boxp->colorcount = ccount;
+}
+
+
+/* Repeatedly select and split the largest box until we have enough
+ * boxes
+ */
+static gint
+median_cut_gray (CFHistogram histogram,
+ boxptr boxlist,
+ gint numboxes,
+ gint desired_colors)
+{
+ gint lb;
+ boxptr b1, b2;
+
+ while (numboxes < desired_colors)
+ {
+ /* Select box to split.
+ * Current algorithm: by population for first half, then by volume.
+ */
+
+ 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;
+}
+
+/* Repeatedly select and split the largest box until we have enough
+ * boxes
+ */
+static gint
+median_cut_rgb (CFHistogram histogram,
+ boxptr boxlist,
+ gint numboxes,
+ gint desired_colors,
+ GimpProgress *progress)
+{
+ gint lb;
+ boxptr b1, b2;
+ AxisType which_axis;
+
+ while (numboxes < desired_colors)
+ {
+ b1 = find_split_candidate (boxlist, numboxes, &which_axis, desired_colors);
+
+ 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 split point along selected axis, and update box bounds.
+ * Note that lb value is max for lower box, so must be < old max.
+ */
+ switch (which_axis)
+ {
+ case AXIS_RED:
+ lb = b1->Rhalferror;/* *0 + (b1->Rmax + b1->Rmin) / 2; */
+ b1->Rmax = lb;
+ b2->Rmin = lb+1;
+ g_assert (b1->Rmax >= b1->Rmin);
+ g_assert (b2->Rmax >= b2->Rmin);
+ break;
+ case AXIS_GREEN:
+ lb = b1->Ghalferror;/* *0 + (b1->Gmax + b1->Gmin) / 2; */
+ b1->Gmax = lb;
+ b2->Gmin = lb+1;
+ g_assert (b1->Gmax >= b1->Gmin);
+ g_assert (b2->Gmax >= b2->Gmin);
+ break;
+ case AXIS_BLUE:
+ lb = b1->Bhalferror;/* *0 + (b1->Bmax + b1->Bmin) / 2; */
+ b1->Bmax = lb;
+ b2->Bmin = lb+1;
+ g_assert (b1->Bmax >= b1->Bmin);
+ g_assert (b2->Bmax >= b2->Bmin);
+ break;
+ default:
+ g_error ("Uh-oh.");
+ }
+ /* Update stats for boxes */
+ numboxes++;
+
+ if (progress && (numboxes % 16 == 0))
+ gimp_progress_set_value (progress, (gdouble) numboxes / desired_colors);
+
+ update_box_rgb (histogram, b1, desired_colors - numboxes);
+ update_box_rgb (histogram, b2, desired_colors - numboxes);
+ }
+
+ return numboxes;
+}
+
+
+/* Compute representative color for a box, put it in colormap[icolor]
+ */
+static void
+compute_color_gray (QuantizeObj *quantobj,
+ CFHistogram histogram,
+ boxptr boxp,
+ int icolor)
+{
+ gint i, min, max;
+ guint64 count;
+ guint64 total;
+ guint64 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;
+ }
+ }
+
+ if (total != 0)
+ {
+ quantobj->cmap[icolor].red =
+ quantobj->cmap[icolor].green =
+ quantobj->cmap[icolor].blue = (gtotal + (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 colormap.
+ */
+ quantobj->cmap[icolor].red =
+ quantobj->cmap[icolor].green =
+ quantobj->cmap[icolor].blue = 0;
+ }
+}
+
+
+/* Compute representative color for a box, put it in colormap[icolor]
+ */
+static void
+compute_color_rgb (QuantizeObj *quantobj,
+ CFHistogram histogram,
+ boxptr boxp,
+ int icolor)
+{
+ /* Current algorithm: mean weighted by pixels (not colors) */
+ /* Note it is important to get the rounding correct! */
+ gint R, G, B;
+ gint Rmin, Rmax;
+ gint Gmin, Gmax;
+ gint Bmin, Bmax;
+ ColorFreq total = 0;
+ ColorFreq Rtotal = 0;
+ ColorFreq Gtotal = 0;
+ ColorFreq 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++)
+ {
+ for (B = Bmin; B <= Bmax; B++)
+ {
+ ColorFreq this_freq = *HIST_LIN (histogram, R, G, B);
+
+ if (this_freq != 0)
+ {
+ total += this_freq;
+ Rtotal += R * this_freq;
+ Gtotal += G * this_freq;
+ Btotal += B * this_freq;
+ }
+ }
+ }
+
+ if (total > 0)
+ {
+ guchar red, green, blue;
+
+ lin_to_rgb (/*(Rtotal + (total>>1)) / total,
+ (Gtotal + (total>>1)) / total,
+ (Btotal + (total>>1)) / total,*/
+ (double)Rtotal / (double)total,
+ (double)Gtotal / (double)total,
+ (double)Btotal / (double)total,
+ &red, &green, &blue);
+
+ quantobj->cmap[icolor].red = red;
+ quantobj->cmap[icolor].green = green;
+ quantobj->cmap[icolor].blue = blue;
+ }
+ 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 colormap.
+ */
+ quantobj->cmap[icolor].red = 0;
+ quantobj->cmap[icolor].green = 0;
+ quantobj->cmap[icolor].blue = 0;
+ }
+}
+
+
+/* Compute representative color for a box, put it in colormap[icolor]
+ */
+static void
+compute_color_lin8 (QuantizeObj *quantobj,
+ CFHistogram histogram,
+ boxptr boxp,
+ const gint icolor)
+{
+ /* Current algorithm: mean weighted by pixels (not colors) */
+ /* Note it is important to get the rounding correct! */
+ gint R, G, B;
+ gint Rmin, Rmax;
+ gint Gmin, Gmax;
+ gint Bmin, Bmax;
+ ColorFreq total = 0;
+ ColorFreq Rtotal = 0;
+ ColorFreq Gtotal = 0;
+ ColorFreq 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++)
+ {
+ for (B = Bmin; B <= Bmax; B++)
+ {
+ ColorFreq this_freq = *HIST_LIN (histogram, R, G, B);
+
+ if (this_freq != 0)
+ {
+ Rtotal += R * this_freq;
+ Gtotal += G * this_freq;
+ Btotal += B * this_freq;
+ total += this_freq;
+ }
+ }
+ }
+
+ if (total != 0)
+ {
+ quantobj->cmap[icolor].red = ((Rtotal << R_SHIFT) + (total>>1)) / total;
+ quantobj->cmap[icolor].green = ((Gtotal << G_SHIFT) + (total>>1)) / total;
+ quantobj->cmap[icolor].blue = ((Btotal << B_SHIFT) + (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 colormap.
+ */
+ g_warning ("eep.");
+ quantobj->cmap[icolor].red = 0;
+ quantobj->cmap[icolor].green = 128;
+ quantobj->cmap[icolor].blue = 128;
+ }
+}
+
+
+/* Master routine for color selection
+ */
+static void
+select_colors_gray (QuantizeObj *quantobj,
+ CFHistogram histogram)
+{
+ boxptr boxlist;
+ gint numboxes;
+ gint desired = quantobj->desired_number_of_colors;
+ gint i;
+
+ /* Allocate workspace for box list */
+ boxlist = g_new (box, desired);
+
+ /* 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);
+}
+
+
+/* Master routine for color selection
+ */
+static void
+select_colors_rgb (QuantizeObj *quantobj,
+ CFHistogram histogram)
+{
+ boxptr boxlist;
+ gint numboxes;
+ gint desired = quantobj->desired_number_of_colors;
+ gint i;
+
+ /* Allocate workspace for box list */
+ boxlist = g_new (box, desired);
+
+ /* Initialize one box containing whole space */
+ numboxes = 1;
+ boxlist[0].Rmin = 0;
+ boxlist[0].Rmax = HIST_R_ELEMS - 1;
+ boxlist[0].Gmin = 0;
+ boxlist[0].Gmax = HIST_G_ELEMS - 1;
+ boxlist[0].Bmin = 0;
+ boxlist[0].Bmax = HIST_B_ELEMS - 1;
+ /* Shrink it to actually-used volume and set its statistics */
+ update_box_rgb (histogram, &boxlist[0], quantobj->desired_number_of_colors);
+ /* Perform median-cut to produce final box list */
+ numboxes = median_cut_rgb (histogram, boxlist, numboxes, desired,
+ quantobj->progress);
+
+ 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);
+ }
+
+ g_free (boxlist);
+}
+
+
+/*
+ * 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
+ * 256Kb 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/512th of the histogram (1/8th 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)*/
+
+/*adam*/
+#define BOX_R_LOG 0
+#define BOX_G_LOG 0
+#define BOX_B_LOG 0
+
+#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.
+ */
+
+/* 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.
+ */
+static gint
+find_nearby_colors (QuantizeObj *quantobj,
+ int minR,
+ int minG,
+ int minB,
+ int colorlist[])
+{
+ 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) >> 1;
+ maxG = minG + ((1 << BOX_G_SHIFT) - (1 << G_SHIFT));
+ centerG = (minG + maxG + 1) >> 1;
+ maxB = minB + ((1 << BOX_B_SHIFT) - (1 << B_SHIFT));
+ centerB = (minB + maxB + 1) >> 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->clin[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->clin[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->clin[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;
+}
+
+
+/* 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.
+ */
+static void
+find_best_colors (QuantizeObj *quantobj,
+ gint minR,
+ gint minG,
+ gint minB,
+ gint numcolors,
+ gint colorlist[],
+ gint bestcolor[])
+{
+ gint iR, iG, iB;
+ gint i, icolor;
+ gint *bptr; /* pointer into bestdist[] array */
+ gint *cptr; /* pointer into bestcolor[] array */
+ gint dist0, dist1; /* initial distance values */
+ gint dist2; /* current distance in inner loop */
+ gint xx0, xx1; /* distance increments */
+ gint xx2;
+ gint inR, inG, inB; /* initial values for increments */
+
+ /* This array holds the distance to the nearest-so-far color for each cell */
+ gint bestdist[BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS] = { 0, };
+
+ /* 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->clin[icolor].red) * R_SCALE;
+ dist0 = inR*inR;
+ /* special-case for L*==0: chroma diffs irrelevant */
+ /* if (minR > 0 || quantobj->clin[icolor].red > 0) */
+ {
+ inG = (minG - quantobj->clin[icolor].green) * G_SCALE;
+ dist0 += inG*inG;
+ inB = (minB - quantobj->clin[icolor].blue) * B_SCALE;
+ dist0 += inB*inB;
+ }
+ /* else
+ {
+ inG = 0;
+ inB = 0;
+ } */
+ /* 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;
+ }
+ }
+}
+
+
+/* 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.)
+ */
+static void
+fill_inverse_cmap_gray (QuantizeObj *quantobj,
+ CFHistogram histogram,
+ gint pixel)
+{
+ Color *cmap = quantobj->cmap;
+ glong mindist;
+ gint mindisti;
+ gint i;
+
+ mindist = 65536;
+ mindisti = -1;
+
+ for (i = 0; i < quantobj->actual_number_of_colors; i++)
+ {
+ glong dist = ABS (pixel - cmap[i].red);
+
+ if (dist < mindist)
+ {
+ mindist = dist;
+ mindisti = i;
+ }
+ }
+
+ if (i >= 0)
+ histogram[pixel] = mindisti + 1;
+}
+
+
+/* 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.)
+ */
+static void
+fill_inverse_cmap_rgb (QuantizeObj *quantobj,
+ CFHistogram histogram,
+ gint R,
+ gint G,
+ gint B)
+{
+ gint minR, minG, minB; /* lower left corner of update box */
+ gint iR, iG, iB;
+ gint *cptr; /* pointer into bestcolor[] array */
+ /* This array lists the candidate colormap indexes. */
+ gint colorlist[MAXNUMCOLORS];
+ gint numcolors; /* number of candidate colors */
+ /* This array holds the actually closest colormap index for each cell. */
+ gint bestcolor[BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS] = { 0, };
+
+ /* 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++)
+ {
+ for (iB = 0; iB < BOX_B_ELEMS; iB++)
+ {
+ *HIST_LIN (histogram, R + iR, G + iG, B + iB) = (*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];
+ }
+}
+
+static void
+custompal_pass1 (QuantizeObj *quantobj)
+{
+ gint i;
+ GList *list;
+
+ /* fprintf(stderr,
+ "custompal_pass1: using (theCustomPalette %s) from (file %s)\n",
+ theCustomPalette->name, theCustomPalette->filename); */
+
+ for (i = 0, list = gimp_palette_get_colors (quantobj->custom_palette);
+ list;
+ i++, list = g_list_next (list))
+ {
+ GimpPaletteEntry *entry = list->data;
+ guchar r, g, b;
+
+ gimp_rgb_get_uchar (&entry->color, &r, &g, &b);
+
+ quantobj->cmap[i].red = (gint) r;
+ quantobj->cmap[i].green = (gint) g;
+ quantobj->cmap[i].blue = (gint) b;
+ }
+
+ quantobj -> actual_number_of_colors = i;
+}
+
+/*
+ * Map some rows of pixels to the output colormapped representation.
+ */
+
+static void
+median_cut_pass2_no_dither_gray (QuantizeObj *quantobj,
+ GimpLayer *layer,
+ GeglBuffer *new_buffer)
+{
+ GeglBufferIterator *iter;
+ CFHistogram histogram = quantobj->histogram;
+ ColorFreq *cachep;
+ const Babl *src_format;
+ const Babl *dest_format;
+ GeglRectangle *src_roi;
+ gint src_bpp;
+ gint dest_bpp;
+ gint has_alpha;
+ gulong *index_used_count = quantobj->index_used_count;
+ gboolean alpha_dither = quantobj->want_alpha_dither;
+ gint offsetx, offsety;
+
+ gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
+
+ src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+ dest_format = gegl_buffer_get_format (new_buffer);
+
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
+
+ has_alpha = babl_format_has_alpha (src_format);
+
+ iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
+ NULL, 0, NULL,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ src_roi = &iter->roi[0];
+
+ gegl_buffer_iterator_add (iter, new_buffer,
+ NULL, 0, NULL,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src = iter->data[0];
+ guchar *dest = iter->data[1];
+ gint row;
+
+ for (row = 0; row < src_roi->height; row++)
+ {
+ gint col;
+
+ for (col = 0; col < src_roi->width; col++)
+ {
+ /* get pixel value and index into the cache */
+ gint pixel = src[GRAY];
+
+ 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);
+
+ if (has_alpha)
+ {
+ gboolean transparent = FALSE;
+
+ if (alpha_dither)
+ {
+ gint dither_x = (col + offsetx + src_roi->x) & DM_WIDTHMASK;
+ gint dither_y = (row + offsety + src_roi->y) & DM_HEIGHTMASK;
+
+ if ((src[ALPHA_G]) < DM[dither_x][dither_y])
+ transparent = TRUE;
+ }
+ else
+ {
+ if (src[ALPHA_G] <= 127)
+ transparent = TRUE;
+ }
+
+ if (transparent)
+ {
+ dest[ALPHA_I] = 0;
+ }
+ else
+ {
+ dest[ALPHA_I] = 255;
+ index_used_count[dest[INDEXED] = *cachep - 1]++;
+ }
+ }
+ else
+ {
+ /* Now emit the colormap index for this cell */
+ index_used_count[dest[INDEXED] = *cachep - 1]++;
+ }
+
+ src += src_bpp;
+ dest += dest_bpp;
+ }
+ }
+ }
+}
+
+static void
+median_cut_pass2_fixed_dither_gray (QuantizeObj *quantobj,
+ GimpLayer *layer,
+ GeglBuffer *new_buffer)
+{
+ GeglBufferIterator *iter;
+ CFHistogram histogram = quantobj->histogram;
+ ColorFreq *cachep;
+ const Babl *src_format;
+ const Babl *dest_format;
+ GeglRectangle *src_roi;
+ gint src_bpp;
+ gint dest_bpp;
+ gboolean has_alpha;
+ gint pixval1 = 0;
+ gint pixval2 = 0;
+ gint err1;
+ gint err2;
+ Color *color1;
+ Color *color2;
+ gulong *index_used_count = quantobj->index_used_count;
+ gboolean alpha_dither = quantobj->want_alpha_dither;
+ gint offsetx, offsety;
+
+ gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
+
+ src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+ dest_format = gegl_buffer_get_format (new_buffer);
+
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
+
+ has_alpha = babl_format_has_alpha (src_format);
+
+ iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
+ NULL, 0, NULL,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ src_roi = &iter->roi[0];
+
+ gegl_buffer_iterator_add (iter, new_buffer,
+ NULL, 0, NULL,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src = iter->data[0];
+ guchar *dest = iter->data[1];
+ gint row;
+
+ for (row = 0; row < src_roi->height; row++)
+ {
+ gint col;
+
+ for (col = 0; col < src_roi->width; col++)
+ {
+ gint pixel;
+ const gint dmval =
+ DM[(col + offsetx + src_roi->x) & DM_WIDTHMASK]
+ [(row + offsety + src_roi->y) & DM_HEIGHTMASK];
+
+ /* get pixel value and index into the cache */
+ pixel = src[GRAY];
+
+ 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);
+
+ pixval1 = *cachep - 1;
+ color1 = &quantobj->cmap[pixval1];
+
+ if (quantobj->actual_number_of_colors > 2)
+ {
+ const int re = src[GRAY] - (int)color1->red;
+ int RV = src[GRAY] + re;
+
+ do
+ {
+ const gint R = CLAMP0255 (RV);
+
+ cachep = &histogram[R];
+ /* 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, R);
+
+ pixval2 = *cachep - 1;
+ RV += re;
+ }
+ while ((pixval1 == pixval2) &&
+ (! (RV>255 || RV<0) ) &&
+ re);
+ }
+ else
+ {
+ /* not enough colors to bother looking for an 'alternative'
+ color (we may fail to do so anyway), so decide that
+ the alternative color is simply the other cmap entry. */
+ pixval2 = (pixval1 + 1) %
+ (quantobj->actual_number_of_colors);
+ }
+
+ /* always deterministically sort pixval1 and pixval2, to
+ avoid artifacts in the dither range due to inverting our
+ relative color viewpoint -- most obvious in 1-bit dither. */
+ if (pixval1 > pixval2)
+ {
+ gint tmpval = pixval1;
+ pixval1 = pixval2;
+ pixval2 = tmpval;
+ color1 = &quantobj->cmap[pixval1];
+ }
+
+ color2 = &quantobj->cmap[pixval2];
+
+ err1 = ABS(color1->red - src[GRAY]);
+ err2 = ABS(color2->red - src[GRAY]);
+ if (err1 || err2)
+ {
+ const int proportion2 = (256 * 255 * err2) / (err1 + err2);
+
+ if ((dmval * 256) > proportion2)
+ {
+ pixval1 = pixval2; /* use color2 instead of color1*/
+ }
+ }
+
+ if (has_alpha)
+ {
+ gboolean transparent = FALSE;
+
+ if (alpha_dither)
+ {
+ if (src[ALPHA_G] < dmval)
+ transparent = TRUE;
+ }
+ else
+ {
+ if (src[ALPHA_G] <= 127)
+ transparent = TRUE;
+ }
+
+ if (transparent)
+ {
+ dest[ALPHA_I] = 0;
+ }
+ else
+ {
+ dest[ALPHA_I] = 255;
+ index_used_count[dest[INDEXED] = pixval1]++;
+ }
+ }
+ else
+ {
+ /* Now emit the colormap index for this cell, barfbarf */
+ index_used_count[dest[INDEXED] = pixval1]++;
+ }
+
+ src += src_bpp;
+ dest += dest_bpp;
+ }
+ }
+ }
+}
+
+static void
+median_cut_pass2_no_dither_rgb (QuantizeObj *quantobj,
+ GimpLayer *layer,
+ GeglBuffer *new_buffer)
+{
+ GeglBufferIterator *iter;
+ CFHistogram histogram = quantobj->histogram;
+ ColorFreq *cachep;
+ const Babl *src_format;
+ const Babl *dest_format;
+ GeglRectangle *src_roi;
+ gint src_bpp;
+ gint dest_bpp;
+ gint has_alpha;
+ gint R, G, B;
+ gint red_pix = RED;
+ gint green_pix = GREEN;
+ gint blue_pix = BLUE;
+ gint alpha_pix = ALPHA;
+ gboolean alpha_dither = quantobj->want_alpha_dither;
+ gint offsetx, offsety;
+ gulong *index_used_count = quantobj->index_used_count;
+ glong total_size = 0;
+ glong layer_size;
+ gint count = 0;
+ gint nth_layer = quantobj->nth_layer;
+ gint n_layers = quantobj->n_layers;
+
+ gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
+
+ src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+ dest_format = gegl_buffer_get_format (new_buffer);
+
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
+
+ has_alpha = babl_format_has_alpha (src_format);
+
+ /* In the case of web/mono palettes, we actually force
+ * grayscale drawables through the rgb pass2 functions
+ */
+ if (gimp_drawable_is_gray (GIMP_DRAWABLE (layer)))
+ {
+ red_pix = green_pix = blue_pix = GRAY;
+ alpha_pix = ALPHA_G;
+ }
+
+ iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
+ NULL, 0, NULL,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ src_roi = &iter->roi[0];
+
+ gegl_buffer_iterator_add (iter, new_buffer,
+ NULL, 0, NULL,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ layer_size = (gimp_item_get_width (GIMP_ITEM (layer)) *
+ gimp_item_get_height (GIMP_ITEM (layer)));
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src = iter->data[0];
+ guchar *dest = iter->data[1];
+ gint row;
+
+ total_size += src_roi->height * src_roi->width;
+
+ for (row = 0; row < src_roi->height; row++)
+ {
+ gint col;
+
+ for (col = 0; col < src_roi->width; col++)
+ {
+ if (has_alpha)
+ {
+ gboolean transparent = FALSE;
+
+ if (alpha_dither)
+ {
+ gint dither_x = (col + offsetx + src_roi->x) & DM_WIDTHMASK;
+ gint dither_y = (row + offsety + src_roi->y) & DM_HEIGHTMASK;
+ if ((src[alpha_pix]) < DM[dither_x][dither_y])
+ transparent = TRUE;
+ }
+ else
+ {
+ if (src[alpha_pix] <= 127)
+ transparent = TRUE;
+ }
+
+ if (transparent)
+ {
+ dest[ALPHA_I] = 0;
+ goto next_pixel;
+ }
+ else
+ {
+ dest[ALPHA_I] = 255;
+ }
+ }
+
+ /* get pixel value and index into the cache */
+ rgb_to_lin (src[red_pix], src[green_pix], src[blue_pix],
+ &R, &G, &B);
+
+ cachep = HIST_LIN (histogram, R, G, 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, barfbarf */
+ index_used_count[dest[INDEXED] = *cachep - 1]++;
+
+ next_pixel:
+
+ src += src_bpp;
+ dest += dest_bpp;
+ }
+ }
+
+ if (quantobj->progress && (count % 16 == 0))
+ gimp_progress_set_value (quantobj->progress,
+ (nth_layer + ((gdouble) total_size)/
+ layer_size) / (gdouble) n_layers);
+ }
+}
+
+static void
+median_cut_pass2_fixed_dither_rgb (QuantizeObj *quantobj,
+ GimpLayer *layer,
+ GeglBuffer *new_buffer)
+{
+ GeglBufferIterator *iter;
+ CFHistogram histogram = quantobj->histogram;
+ ColorFreq *cachep;
+ const Babl *src_format;
+ const Babl *dest_format;
+ GeglRectangle *src_roi;
+ gint src_bpp;
+ gint dest_bpp;
+ gint has_alpha;
+ gint pixval1 = 0;
+ gint pixval2 = 0;
+ Color *color1;
+ Color *color2;
+ gint R, G, B;
+ gint err1;
+ gint err2;
+ gint red_pix = RED;
+ gint green_pix = GREEN;
+ gint blue_pix = BLUE;
+ gint alpha_pix = ALPHA;
+ gboolean alpha_dither = quantobj->want_alpha_dither;
+ gint offsetx, offsety;
+ gulong *index_used_count = quantobj->index_used_count;
+ glong total_size = 0;
+ glong layer_size;
+ gint count = 0;
+ gint nth_layer = quantobj->nth_layer;
+ gint n_layers = quantobj->n_layers;
+
+ gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
+
+ src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+ dest_format = gegl_buffer_get_format (new_buffer);
+
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
+
+ has_alpha = babl_format_has_alpha (src_format);
+
+ /* In the case of web/mono palettes, we actually force
+ * grayscale drawables through the rgb pass2 functions
+ */
+ if (gimp_drawable_is_gray (GIMP_DRAWABLE (layer)))
+ {
+ red_pix = green_pix = blue_pix = GRAY;
+ alpha_pix = ALPHA_G;
+ }
+
+ iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
+ NULL, 0, NULL,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ src_roi = &iter->roi[0];
+
+ gegl_buffer_iterator_add (iter, new_buffer,
+ NULL, 0, NULL,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ layer_size = (gimp_item_get_width (GIMP_ITEM (layer)) *
+ gimp_item_get_height (GIMP_ITEM (layer)));
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src = iter->data[0];
+ guchar *dest = iter->data[1];
+ gint row;
+
+ total_size += src_roi->height * src_roi->width;
+
+ for (row = 0; row < src_roi->height; row++)
+ {
+ gint col;
+
+ for (col = 0; col < src_roi->width; col++)
+ {
+ const int dmval =
+ DM[(col + offsetx + src_roi->x) & DM_WIDTHMASK]
+ [(row + offsety + src_roi->y) & DM_HEIGHTMASK];
+
+ if (has_alpha)
+ {
+ gboolean transparent = FALSE;
+
+ if (alpha_dither)
+ {
+ if (src[alpha_pix] < dmval)
+ transparent = TRUE;
+ }
+ else
+ {
+ if (src[alpha_pix] <= 127)
+ transparent = TRUE;
+ }
+
+ if (transparent)
+ {
+ dest[ALPHA_I] = 0;
+ goto next_pixel;
+ }
+ else
+ {
+ dest[ALPHA_I] = 255;
+ }
+ }
+
+ /* get pixel value and index into the cache */
+ rgb_to_lin (src[red_pix], src[green_pix], src[blue_pix],
+ &R, &G, &B);
+
+ cachep = HIST_LIN (histogram, R, G, 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);
+
+ /* We now try to find a color which, when mixed in some
+ * fashion with the closest match, yields something
+ * closer to the desired color. We do this by
+ * repeatedly extrapolating the color vector from one to
+ * the other until we find another color cell. Then we
+ * assess the distance of both mixer colors from the
+ * intended color to determine their relative
+ * probabilities of being chosen.
+ */
+ pixval1 = *cachep - 1;
+ color1 = &quantobj->cmap[pixval1];
+
+ if (quantobj->actual_number_of_colors > 2)
+ {
+ const gint re = src[red_pix] - (gint) color1->red;
+ const gint ge = src[green_pix] - (gint) color1->green;
+ const gint be = src[blue_pix] - (gint) color1->blue;
+ gint RV = src[red_pix] + re;
+ gint GV = src[green_pix] + ge;
+ gint BV = src[blue_pix] + be;
+
+ do
+ {
+ rgb_to_lin ((CLAMP0255(RV)),
+ (CLAMP0255(GV)),
+ (CLAMP0255(BV)),
+ &R, &G, &B);
+
+ cachep = HIST_LIN (histogram, R, G, 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);
+
+ pixval2 = *cachep - 1;
+ RV += re; GV += ge; BV += be;
+ }
+ while ((pixval1 == pixval2) &&
+ (!( (RV>255 || RV<0) || (GV>255 || GV<0) || (BV>255 || BV<0) )) &&
+ (re || ge || be));
+ }
+
+ if (quantobj->actual_number_of_colors <= 2
+ /* || pixval1 == pixval2 */) {
+ /* not enough colors to bother looking for an 'alternative'
+ color (we may fail to do so anyway), so decide that
+ the alternative color is simply the other cmap entry. */
+ pixval2 = (pixval1 + 1) %
+ (quantobj->actual_number_of_colors);
+ }
+
+ /* always deterministically sort pixval1 and pixval2, to
+ avoid artifacts in the dither range due to inverting our
+ relative color viewpoint -- most obvious in 1-bit dither. */
+ if (pixval1 > pixval2)
+ {
+ gint tmpval = pixval1;
+ pixval1 = pixval2;
+ pixval2 = tmpval;
+ color1 = &quantobj->cmap[pixval1];
+ }
+
+ color2 = &quantobj->cmap[pixval2];
+
+ /* now figure out the relative probabilites of choosing
+ either of our candidates. */
+#define DISTP(R1,G1,B1,R2,G2,B2,D) do {D = sqrt( 30*SQR((R1)-(R2)) + \
+ 59*SQR((G1)-(G2)) + \
+ 11*SQR((B1)-(B2)) ); }while(0)
+#define LIN_DISTP(R1,G1,B1,R2,G2,B2,D) do { \
+ int spacer1, spaceg1, spaceb1; \
+ int spacer2, spaceg2, spaceb2; \
+ rgb_to_unshifted_lin (R1,G1,B1, &spacer1, &spaceg1, &spaceb1); \
+ rgb_to_unshifted_lin (R2,G2,B2, &spacer2, &spaceg2, &spaceb2); \
+ D = sqrt(R_SCALE * SQR((spacer1)-(spacer2)) + \
+ G_SCALE * SQR((spaceg1)-(spaceg2)) + \
+ B_SCALE * SQR((spaceb1)-(spaceb2))); \
+ } while(0)
+
+ /* although LIN_DISTP is more correct, DISTP is much faster and
+ barely distinguishable. */
+ DISTP (color1->red, color1->green, color1->blue,
+ src[red_pix], src[green_pix], src[blue_pix],
+ err1);
+ DISTP (color2->red, color2->green, color2->blue,
+ src[red_pix], src[green_pix], src[blue_pix],
+ err2);
+
+ if (err1 || err2)
+ {
+ const int proportion2 = (255 * err2) / (err1 + err2);
+ if (dmval > proportion2)
+ {
+ pixval1 = pixval2; /* use color2 instead of color1*/
+ }
+ }
+
+ /* Now emit the colormap index for this cell, barfbarf */
+ index_used_count[dest[INDEXED] = pixval1]++;
+
+ next_pixel:
+
+ src += src_bpp;
+ dest += dest_bpp;
+ }
+ }
+
+ if (quantobj->progress && (count % 16 == 0))
+ gimp_progress_set_value (quantobj->progress,
+ (nth_layer + ((gdouble) total_size)/
+ layer_size) / (gdouble) n_layers);
+ }
+}
+
+static void
+median_cut_pass2_nodestruct_dither_rgb (QuantizeObj *quantobj,
+ GimpLayer *layer,
+ GeglBuffer *new_buffer)
+{
+ GeglBufferIterator *iter;
+ const Babl *src_format;
+ const Babl *dest_format;
+ GeglRectangle *src_roi;
+ gint src_bpp;
+ gint dest_bpp;
+ gint has_alpha;
+ gboolean alpha_dither = quantobj->want_alpha_dither;
+ gint red_pix = RED;
+ gint green_pix = GREEN;
+ gint blue_pix = BLUE;
+ gint alpha_pix = ALPHA;
+ gint lastindex = 0;
+ gint lastred = -1;
+ gint lastgreen = -1;
+ gint lastblue = -1;
+ gint offsetx, offsety;
+
+ gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
+
+ src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+ dest_format = gegl_buffer_get_format (new_buffer);
+
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
+
+ has_alpha = babl_format_has_alpha (src_format);
+
+ iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
+ NULL, 0, NULL,
+ GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
+ src_roi = &iter->roi[0];
+
+ gegl_buffer_iterator_add (iter, new_buffer,
+ NULL, 0, NULL,
+ GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
+
+ while (gegl_buffer_iterator_next (iter))
+ {
+ const guchar *src = iter->data[0];
+ guchar *dest = iter->data[1];
+ gint row;
+
+ for (row = 0; row < src_roi->height; row++)
+ {
+ gint col;
+
+ for (col = 0; col < src_roi->width; col++)
+ {
+ gboolean transparent = FALSE;
+
+ if (has_alpha)
+ {
+ if (alpha_dither)
+ {
+ gint dither_x = (col + src_roi->x + offsetx) & DM_WIDTHMASK;
+ gint dither_y = (row + src_roi->y + offsety) & DM_HEIGHTMASK;
+
+ if ((src[alpha_pix]) < DM[dither_x][dither_y])
+ transparent = TRUE;
+ }
+ else
+ {
+ if (src[alpha_pix] < 128)
+ transparent = TRUE;
+ }
+ }
+
+ if (! transparent)
+ {
+ if ((lastred == src[red_pix]) &&
+ (lastgreen == src[green_pix]) &&
+ (lastblue == src[blue_pix]))
+ {
+ /* same pixel color as last time */
+ dest[INDEXED] = lastindex;
+ if (has_alpha)
+ dest[ALPHA_I] = 255;
+ }
+ else
+ {
+ gint i;
+
+ for (i = 0 ;
+ i < quantobj->actual_number_of_colors;
+ i++)
+ {
+ if ((quantobj->cmap[i].green == src[green_pix]) &&
+ (quantobj->cmap[i].red == src[red_pix]) &&
+ (quantobj->cmap[i].blue == src[blue_pix]))
+ {
+ lastred = src[red_pix];
+ lastgreen = src[green_pix];
+ lastblue = src[blue_pix];
+ lastindex = i;
+
+ goto got_color;
+ }
+ }
+ g_error ("Non-existant color was expected to "
+ "be in non-destructive colormap.");
+ got_color:
+ dest[INDEXED] = lastindex;
+ if (has_alpha)
+ dest[ALPHA_I] = 255;
+ }
+ }
+ else
+ { /* have alpha, and transparent */
+ dest[ALPHA_I] = 0;
+ }
+
+ src += src_bpp;
+ dest += dest_bpp;
+ }
+ }
+ }
+}
+
+
+/*
+ * 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 gint *
+init_error_limit (const int error_freedom)
+/* Allocate and fill in the error_limiter table */
+{
+ gint *table;
+ gint in, out;
+
+ /* #define STEPSIZE 16 */
+ /* #define STEPSIZE 200 */
+
+ table = g_new (gint, 255 * 2 + 1);
+ table += 255; /* so we can index -255 ... +255 */
+
+ if (error_freedom == 0)
+ {
+ /* Coarse function, much bleeding. */
+
+ const gint STEPSIZE = 190;
+
+ for (in = 0; in < STEPSIZE; in++)
+ {
+ table[in] = in;
+ table[-in] = -in;
+ }
+
+ for (; in <= 255; in++)
+ {
+ table[in] = STEPSIZE;
+ table[-in] = -STEPSIZE;
+ }
+
+ return (table);
+ }
+ else
+ {
+ /* Smooth function, bleeding more constrained */
+
+ const gint STEPSIZE = 24;
+
+ /* Map errors 1:1 up to +- STEPSIZE */
+ out = 0;
+ for (in = 0; in < STEPSIZE; in++, out++)
+ {
+ table[in] = out;
+ table[-in] = -out;
+ }
+
+ /* Map errors 1:2 up to +- 3*STEPSIZE */
+ for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1)
+ {
+ table[in] = out;
+ table[-in] = -out;
+ }
+
+ /* Clamp the rest to final out value (which is STEPSIZE*2) */
+ for (; in <= 255; in++)
+ {
+ table[in] = out;
+ table[-in] = -out;
+ }
+
+ return table;
+ }
+}
+
+
+/*
+ * Map some rows of pixels to the output colormapped representation.
+ * Perform floyd-steinberg dithering.
+ */
+
+static void
+median_cut_pass2_fs_dither_gray (QuantizeObj *quantobj,
+ GimpLayer *layer,
+ GeglBuffer *new_buffer)
+{
+ GeglBuffer *src_buffer;
+ CFHistogram histogram = quantobj->histogram;
+ ColorFreq *cachep;
+ Color *color;
+ gint *error_limiter;
+ const gshort *fs_err1, *fs_err2;
+ const gshort *fs_err3, *fs_err4;
+ const guchar *range_limiter;
+ const Babl *src_format;
+ const Babl *dest_format;
+ gint src_bpp;
+ gint dest_bpp;
+ guchar *src_buf, *dest_buf;
+ gint *next_row, *prev_row;
+ gint *nr, *pr;
+ gint *tmp;
+ gint pixel;
+ gint pixele;
+ gint row, col;
+ gint index;
+ gint step_dest, step_src;
+ gint odd_row;
+ gboolean has_alpha;
+ gint offsetx, offsety;
+ gboolean alpha_dither = quantobj->want_alpha_dither;
+ gint width, height;
+ gulong *index_used_count = quantobj->index_used_count;
+
+ src_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
+
+ gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
+
+ src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+ dest_format = gegl_buffer_get_format (new_buffer);
+
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
+
+ has_alpha = babl_format_has_alpha (src_format);
+
+ width = gimp_item_get_width (GIMP_ITEM (layer));
+ height = gimp_item_get_height (GIMP_ITEM (layer));
+
+ error_limiter = init_error_limit (quantobj->error_freedom);
+ range_limiter = range_array + 256;
+
+ src_buf = g_malloc (width * src_bpp);
+ dest_buf = g_malloc (width * dest_bpp);
+
+ next_row = g_new (gint, width + 2);
+ prev_row = g_new0 (gint, width + 2);
+
+ 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++)
+ {
+ const guchar *src;
+ guchar *dest;
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, row, width, 1),
+ 1.0, NULL, src_buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ src = src_buf;
+ dest = dest_buf;
+
+ nr = next_row;
+ pr = prev_row + 1;
+
+ if (odd_row)
+ {
+ step_dest = -dest_bpp;
+ step_src = -src_bpp;
+
+ src += (width * src_bpp) - src_bpp;
+ dest += (width * dest_bpp) - dest_bpp;
+
+ nr += width + 1;
+ pr += width;
+
+ *(nr - 1) = 0;
+ }
+ else
+ {
+ step_dest = dest_bpp;
+ step_src = src_bpp;
+
+ *(nr + 1) = 0;
+ }
+
+ *nr = 0;
+
+ for (col = 0; col < width; col++)
+ {
+ pixel = range_limiter[src[GRAY] + 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);
+
+ if (has_alpha)
+ {
+ gboolean transparent = FALSE;
+
+ if (odd_row)
+ {
+ if (alpha_dither)
+ {
+ gint dither_x = ((width-col)+offsetx-1) & DM_WIDTHMASK;
+ gint dither_y = (row+offsety) & DM_HEIGHTMASK;
+
+ if ((src[ALPHA_G]) < DM[dither_x][dither_y])
+ transparent = TRUE;
+ }
+ else
+ {
+ if (src[ALPHA_G] <= 127)
+ transparent = TRUE;
+ }
+
+ if (transparent)
+ {
+ dest[ALPHA_I] = 0;
+ pr--;
+ nr--;
+ *(nr - 1) = 0;
+ goto next_pixel;
+ }
+ else
+ {
+ dest[ALPHA_I] = 255;
+ }
+ }
+ else
+ {
+ if (alpha_dither)
+ {
+ gint dither_x = (col + offsetx) & DM_WIDTHMASK;
+ gint dither_y = (row + offsety) & DM_HEIGHTMASK;
+
+ if ((src[ALPHA_G]) < DM[dither_x][dither_y])
+ transparent = TRUE;
+ }
+ else
+ {
+ if (src[ALPHA_G] <= 127)
+ transparent = TRUE;
+ }
+
+ if (transparent)
+ {
+ dest[ALPHA_I] = 0;
+ pr++;
+ nr++;
+ *(nr + 1) = 0;
+ goto next_pixel;
+ }
+ else
+ {
+ dest[ALPHA_I] = 255;
+ }
+ }
+ }
+
+ index = *cachep - 1;
+ index_used_count[dest[INDEXED] = index]++;
+
+ 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];
+ }
+
+ next_pixel:
+
+ dest += step_dest;
+ src += step_src;
+ }
+
+ tmp = next_row;
+ next_row = prev_row;
+ prev_row = tmp;
+
+ odd_row = !odd_row;
+
+ gegl_buffer_set (new_buffer, GEGL_RECTANGLE (0, row, width, 1),
+ 0, NULL, dest_buf,
+ GEGL_AUTO_ROWSTRIDE);
+ }
+
+ g_free (error_limiter - 255); /* good lord. */
+ g_free (next_row);
+ g_free (prev_row);
+ g_free (src_buf);
+ g_free (dest_buf);
+}
+
+static void
+median_cut_pass2_rgb_init (QuantizeObj *quantobj)
+{
+ int i;
+
+ zero_histogram_rgb (quantobj->histogram);
+
+ /* Mark all indices as currently unused */
+ memset (quantobj->index_used_count, 0, 256 * sizeof (gulong));
+
+ /* Make a version of our discovered colormap in linear space */
+ for (i = 0; i < quantobj->actual_number_of_colors; i++)
+ {
+ rgb_to_unshifted_lin (quantobj->cmap[i].red,
+ quantobj->cmap[i].green,
+ quantobj->cmap[i].blue,
+ &quantobj->clin[i].red,
+ &quantobj->clin[i].green,
+ &quantobj->clin[i].blue);
+ }
+}
+
+static void
+median_cut_pass2_gray_init (QuantizeObj *quantobj)
+{
+ zero_histogram_gray (quantobj->histogram);
+
+ /* Mark all indices as currently unused */
+ memset (quantobj->index_used_count, 0, 256 * sizeof (gulong));
+}
+
+static void
+median_cut_pass2_fs_dither_rgb (QuantizeObj *quantobj,
+ GimpLayer *layer,
+ GeglBuffer *new_buffer)
+{
+ GeglBuffer *src_buffer;
+ CFHistogram histogram = quantobj->histogram;
+ ColorFreq *cachep;
+ Color *color;
+ gint *error_limiter;
+ const gshort *fs_err1, *fs_err2;
+ const gshort *fs_err3, *fs_err4;
+ const guchar *range_limiter;
+ const Babl *src_format;
+ const Babl *dest_format;
+ gint src_bpp;
+ gint dest_bpp;
+ guchar *src_buf, *dest_buf;
+ gint *red_n_row, *red_p_row;
+ gint *grn_n_row, *grn_p_row;
+ gint *blu_n_row, *blu_p_row;
+ gint *rnr, *rpr;
+ gint *gnr, *gpr;
+ gint *bnr, *bpr;
+ gint *tmp;
+ gint re, ge, be;
+ gint row, col;
+ gint index;
+ gint step_dest, step_src;
+ gint odd_row;
+ gboolean has_alpha;
+ gint width, height;
+ gint red_pix = RED;
+ gint green_pix = GREEN;
+ gint blue_pix = BLUE;
+ gint alpha_pix = ALPHA;
+ gint offsetx, offsety;
+ gboolean alpha_dither = quantobj->want_alpha_dither;
+ gulong *index_used_count = quantobj->index_used_count;
+ gint global_rmax = 0, global_rmin = G_MAXINT;
+ gint global_gmax = 0, global_gmin = G_MAXINT;
+ gint global_bmax = 0, global_bmin = G_MAXINT;
+ gint nth_layer = quantobj->nth_layer;
+ gint n_layers = quantobj->n_layers;
+
+ src_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
+
+ gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
+
+ /* In the case of web/mono palettes, we actually force
+ * grayscale drawables through the rgb pass2 functions
+ */
+ if (gimp_drawable_is_gray (GIMP_DRAWABLE (layer)))
+ red_pix = green_pix = blue_pix = GRAY;
+
+ src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
+ dest_format = gegl_buffer_get_format (new_buffer);
+
+ src_bpp = babl_format_get_bytes_per_pixel (src_format);
+ dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
+
+ has_alpha = babl_format_has_alpha (src_format);
+
+ width = gimp_item_get_width (GIMP_ITEM (layer));
+ height = gimp_item_get_height (GIMP_ITEM (layer));
+
+ error_limiter = init_error_limit (quantobj->error_freedom);
+ range_limiter = range_array + 256;
+
+ /* find the bounding box of the palette colors --
+ we use this for hard-clamping our error-corrected
+ values so that we can't continuously accelerate outside
+ of our attainable gamut, which looks icky. */
+ for (index = 0; index < quantobj->actual_number_of_colors; index++)
+ {
+ global_rmax = MAX(global_rmax, quantobj->clin[index].red);
+ global_rmin = MIN(global_rmin, quantobj->clin[index].red);
+ global_gmax = MAX(global_gmax, quantobj->clin[index].green);
+ global_gmin = MIN(global_gmin, quantobj->clin[index].green);
+ global_bmax = MAX(global_bmax, quantobj->clin[index].blue);
+ global_bmin = MIN(global_bmin, quantobj->clin[index].blue);
+ }
+
+ src_buf = g_malloc (width * src_bpp);
+ dest_buf = g_malloc (width * dest_bpp);
+
+ red_n_row = g_new (gint, width + 2);
+ red_p_row = g_new0 (gint, width + 2);
+ grn_n_row = g_new (gint, width + 2);
+ grn_p_row = g_new0 (gint, width + 2);
+ blu_n_row = g_new (gint, width + 2);
+ blu_p_row = g_new0 (gint, width + 2);
+
+ 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++)
+ {
+ const guchar *src;
+ guchar *dest;
+
+ gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, row, width, 1),
+ 1.0, NULL, src_buf,
+ GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ 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_bpp;
+ step_src = -src_bpp;
+
+ src += (width * src_bpp) - src_bpp;
+ dest += (width * dest_bpp) - dest_bpp;
+
+ 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_bpp;
+ step_src = src_bpp;
+
+ *(rnr + 1) = *(gnr + 1) = *(bnr + 1) = 0;
+ }
+
+ *rnr = *gnr = *bnr = 0;
+
+ for (col = 0; col < width; col++)
+ {
+ if (has_alpha)
+ {
+ gboolean transparent = FALSE;
+
+ if (odd_row)
+ {
+ if (alpha_dither)
+ {
+ gint dither_x = ((width-col)+offsetx-1) & DM_WIDTHMASK;
+ gint dither_y = (row+offsety) & DM_HEIGHTMASK;
+
+ if ((src[alpha_pix]) < DM[dither_x][dither_y])
+ transparent = TRUE;
+ }
+ else
+ {
+ if (src[alpha_pix] <= 127)
+ transparent = TRUE;
+ }
+
+ if (transparent)
+ {
+ dest[ALPHA_I] = 0;
+ rpr--; gpr--; bpr--;
+ rnr--; gnr--; bnr--;
+ *(rnr - 1) = *(gnr - 1) = *(bnr - 1) = 0;
+ goto next_pixel;
+ }
+ else
+ {
+ dest[ALPHA_I] = 255;
+ }
+ }
+ else
+ {
+ if (alpha_dither)
+ {
+ gint dither_x = (col + offsetx) & DM_WIDTHMASK;
+ gint dither_y = (row + offsety) & DM_HEIGHTMASK;
+
+ if ((src[alpha_pix]) < DM[dither_x][dither_y])
+ transparent = TRUE;
+ }
+ else
+ {
+ if (src[alpha_pix] <= 127)
+ transparent = TRUE;
+ }
+
+ if (transparent)
+ {
+ dest[ALPHA_I] = 0;
+ rpr++; gpr++; bpr++;
+ rnr++; gnr++; bnr++;
+ *(rnr + 1) = *(gnr + 1) = *(bnr + 1) = 0;
+ goto next_pixel;
+ }
+ else
+ {
+ dest[ALPHA_I] = 255;
+ }
+ }
+ }
+
+#if 0
+ /* hmm. */
+
+ 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;
+
+ rgb_to_lin (r, g, b, &re, &ge, &be);
+#endif
+ rgb_to_unshifted_lin (src[red_pix], src[green_pix], src[blue_pix],
+ &re, &ge, &be);
+
+ /*
+ re = CLAMP(re, global_rmin, global_rmax);
+ ge = CLAMP(ge, global_gmin, global_gmax);
+ be = CLAMP(be, global_bmin, global_bmax);*/
+
+ re = range_limiter[re + error_limiter[*rpr]];
+ ge = range_limiter[ge + error_limiter[*gpr]];
+ be = range_limiter[be + error_limiter[*bpr]];
+
+ cachep = HIST_LIN (histogram,
+ RSDF (re),
+ GSDF (ge),
+ BSDF (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,
+ RSDF (re),
+ GSDF (ge),
+ BSDF (be));
+
+ index = *cachep - 1;
+ index_used_count[index]++;
+ dest[INDEXED] = index;
+
+ /*if (re > global_rmax)
+ re = (re + 3*global_rmax) / 4;
+ else if (re < global_rmin)
+ re = (re + 3*global_rmin) / 4;*/
+
+ /* We constrain chroma error extra-hard so that it
+ doesn't run away and steal the thunder from the
+ lightness error where all the detail usually is. */
+ if (ge > global_gmax)
+ ge = (ge + 3*global_gmax) / 4;
+ else if (ge < global_gmin)
+ ge = (ge + 3*global_gmin) / 4;
+ if (be > global_bmax)
+ be = (be + 3*global_bmax) / 4;
+ else if (be < global_bmin)
+ be = (be + 3*global_bmin) / 4;
+
+ color = &quantobj->clin[index];
+
+#if 0
+ if ((re > 0 && re < 255) /* HMM &&
+ ge >= 0 && ge <= 255 &&
+ be >= 0 && be <= 255*/)
+ {
+ ge = ge - color->green;
+ be = be - color->blue;
+ re = re - color->red;
+ }
+ else
+ {
+ /* color pretty much undefined now; nullify error. */
+ re = ge = be = 0;
+ }
+#endif
+
+ if (re <= 0 || re >= 255)
+ re = ge = be = 0;
+ else
+ {
+ re = re - color->red;
+ ge = ge - color->green;
+ be = be - 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];
+ }
+
+ next_pixel:
+
+ 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;
+
+ gegl_buffer_set (new_buffer, GEGL_RECTANGLE (0, row, width, 1),
+ 0, NULL, dest_buf,
+ GEGL_AUTO_ROWSTRIDE);
+
+ if (quantobj->progress && (row % 16 == 0))
+ gimp_progress_set_value (quantobj->progress,
+ (nth_layer + ((gdouble) row) /
+ height) / (gdouble) n_layers);
+ }
+
+ 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);
+}
+
+
+void
+gimp_image_convert_indexed_set_dither_matrix (const guchar *matrix,
+ gint width,
+ gint height)
+{
+ gint x;
+ gint y;
+
+ /* if matrix is invalid, restore the default matrix */
+ if (matrix == NULL || width == 0 || height == 0)
+ {
+ matrix = (const guchar *) DM_ORIGINAL;
+ width = DM_WIDTH;
+ height = DM_HEIGHT;
+ }
+
+ g_return_if_fail ((DM_WIDTH % width) == 0);
+ g_return_if_fail ((DM_HEIGHT % height) == 0);
+
+ for (y = 0; y < DM_HEIGHT; y++)
+ {
+ for (x = 0; x < DM_WIDTH; x++)
+ {
+ DM[x][y] = matrix[((x % width) * height) + (y % height)];
+ }
+ }
+}
+
+
+/**************************************************************/
+static QuantizeObj *
+initialize_median_cut (GimpImageBaseType type,
+ gint num_colors,
+ GimpConvertDitherType dither_type,
+ GimpConvertPaletteType palette_type,
+ GimpPalette *custom_palette,
+ gboolean want_alpha_dither,
+ GimpProgress *progress)
+{
+ QuantizeObj *quantobj;
+
+ /* Initialize the data structures */
+ quantobj = g_new (QuantizeObj, 1);
+
+ if (type == GIMP_GRAY && palette_type == GIMP_MAKE_PALETTE)
+ quantobj->histogram = g_new (ColorFreq, 256);
+ else
+ quantobj->histogram = g_new (ColorFreq,
+ HIST_R_ELEMS * HIST_G_ELEMS * HIST_B_ELEMS);
+
+ quantobj->custom_palette = custom_palette;
+ quantobj->desired_number_of_colors = num_colors;
+ quantobj->want_alpha_dither = want_alpha_dither;
+ quantobj->progress = progress;
+
+ switch (type)
+ {
+ case GIMP_GRAY:
+ switch (palette_type)
+ {
+ case GIMP_MAKE_PALETTE:
+ quantobj->first_pass = median_cut_pass1_gray;
+ break;
+ case GIMP_WEB_PALETTE:
+ quantobj->first_pass = webpal_pass1;
+ break;
+ case GIMP_CUSTOM_PALETTE:
+ quantobj->first_pass = custompal_pass1;
+ needs_quantize = TRUE;
+ break;
+ case GIMP_MONO_PALETTE:
+ default:
+ quantobj->first_pass = monopal_pass1;
+ }
+
+ if (palette_type == GIMP_WEB_PALETTE ||
+ palette_type == GIMP_CUSTOM_PALETTE)
+ {
+ switch (dither_type)
+ {
+ case GIMP_NODESTRUCT_DITHER:
+ default:
+ g_warning("Uh-oh, bad dither type, W1");
+ case GIMP_NO_DITHER:
+ quantobj->second_pass_init = median_cut_pass2_rgb_init;
+ quantobj->second_pass = median_cut_pass2_no_dither_rgb;
+ break;
+ case GIMP_FS_DITHER:
+ quantobj->error_freedom = 0;
+ quantobj->second_pass_init = median_cut_pass2_rgb_init;
+ quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
+ break;
+ case GIMP_FSLOWBLEED_DITHER:
+ quantobj->error_freedom = 1;
+ quantobj->second_pass_init = median_cut_pass2_rgb_init;
+ quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
+ break;
+ case GIMP_FIXED_DITHER:
+ quantobj->second_pass_init = median_cut_pass2_rgb_init;
+ quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
+ break;
+ }
+ }
+ else
+ {
+ switch (dither_type)
+ {
+ case GIMP_NODESTRUCT_DITHER:
+ default:
+ g_warning("Uh-oh, bad dither type, W2");
+ case GIMP_NO_DITHER:
+ quantobj->second_pass_init = median_cut_pass2_gray_init;
+ quantobj->second_pass = median_cut_pass2_no_dither_gray;
+ break;
+ case GIMP_FS_DITHER:
+ quantobj->error_freedom = 0;
+ quantobj->second_pass_init = median_cut_pass2_gray_init;
+ quantobj->second_pass = median_cut_pass2_fs_dither_gray;
+ break;
+ case GIMP_FSLOWBLEED_DITHER:
+ quantobj->error_freedom = 1;
+ quantobj->second_pass_init = median_cut_pass2_gray_init;
+ quantobj->second_pass = median_cut_pass2_fs_dither_gray;
+ break;
+ case GIMP_FIXED_DITHER:
+ quantobj->second_pass_init = median_cut_pass2_gray_init;
+ quantobj->second_pass = median_cut_pass2_fixed_dither_gray;
+ break;
+ }
+ }
+ break;
+
+ case GIMP_RGB:
+ switch (palette_type)
+ {
+ case GIMP_MAKE_PALETTE:
+ quantobj->first_pass = median_cut_pass1_rgb;
+ break;
+ case GIMP_WEB_PALETTE:
+ quantobj->first_pass = webpal_pass1;
+ needs_quantize = TRUE;
+ break;
+ case GIMP_CUSTOM_PALETTE:
+ quantobj->first_pass = custompal_pass1;
+ needs_quantize = TRUE;
+ break;
+ case GIMP_MONO_PALETTE:
+ default:
+ quantobj->first_pass = monopal_pass1;
+ }
+
+ switch (dither_type)
+ {
+ case GIMP_NO_DITHER:
+ quantobj->second_pass_init = median_cut_pass2_rgb_init;
+ quantobj->second_pass = median_cut_pass2_no_dither_rgb;
+ break;
+ case GIMP_FS_DITHER:
+ quantobj->error_freedom = 0;
+ quantobj->second_pass_init = median_cut_pass2_rgb_init;
+ quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
+ break;
+ case GIMP_FSLOWBLEED_DITHER:
+ quantobj->error_freedom = 1;
+ quantobj->second_pass_init = median_cut_pass2_rgb_init;
+ quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
+ break;
+ case GIMP_NODESTRUCT_DITHER:
+ quantobj->second_pass_init = NULL;
+ quantobj->second_pass = median_cut_pass2_nodestruct_dither_rgb;
+ break;
+ case GIMP_FIXED_DITHER:
+ quantobj->second_pass_init = median_cut_pass2_rgb_init;
+ quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ quantobj->delete_func = delete_median_cut;
+
+ return quantobj;
+}
diff --git a/app/core/gimpimage-convert-indexed.h b/app/core/gimpimage-convert-indexed.h
new file mode 100644
index 0000000..c60747c
--- /dev/null
+++ b/app/core/gimpimage-convert-indexed.h
@@ -0,0 +1,41 @@
+/* GIMP - The GNU 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 3 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, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GIMP_IMAGE_CONVERT_INDEXED_H__
+#define __GIMP_IMAGE_CONVERT_INDEXED_H__
+
+
+#define MAXNUMCOLORS 256
+
+
+gboolean gimp_image_convert_indexed (GimpImage *image,
+ gint num_cols,
+ GimpConvertDitherType dither,
+ gboolean alpha_dither,
+ gboolean text_layer_dither,
+ gboolean remove_dups,
+ GimpConvertPaletteType palette_type,
+ GimpPalette *custom_palette,
+ GimpProgress *progress,
+ GError **error);
+
+void gimp_image_convert_indexed_set_dither_matrix (const guchar *matrix,
+ gint width,
+ gint height);
+
+
+#endif /* __GIMP_IMAGE_CONVERT_INDEXED_H__ */
diff --git a/app/core/gimpimage-convert-type.c b/app/core/gimpimage-convert-type.c
index ef5704c..cced88f 100644
--- a/app/core/gimpimage-convert-type.c
+++ b/app/core/gimpimage-convert-type.c
@@ -1,6 +1,5 @@
/* GIMP - The GNU Image Manipulation Program
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
- * Copyright (C) 1997-2004 Adam D. Moss <adam gimp org>
*
* 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
@@ -16,790 +15,60 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-/*
- * 2005-09-04 - Switch 'positional' dither matrix to a 32x32 Bayer,
- * which generates results that compress somewhat better (and may look
- * worse or better depending on what you enjoy...). [adam gimp org]
- *
- * 2004-12-12 - Use a slower but much nicer technique for finding the
- * two best colors to dither between when using fixed/positional
- * dither methods. Makes positional dither much less lame. [adam gimp org]
- *
- * 2002-02-10 - Quantizer version 3.0 (the rest of the commit started
- * a year ago -- whoops). Divide colors within CIE L*a*b* space using
- * CPercep module (cpercep.[ch]), color-match and dither likewise,
- * change the underlying box selection criteria and division point
- * logic, bump luminance precision upwards, etc.etc. Generally
- * chooses a much richer color set, especially for low numbers of
- * colors. n.b.: Less luminance-sloppy in straight remapping which is
- * good for color but a bit worse for high-frequency detail (that's
- * partly what fs-dithering is for -- use it). [adam gimp org]
- *
- * 2001-03-25 - Define accessor function/macro for histogram reads and
- * writes. This slows us down a little because we avoid some of the
- * dirty tricks we used when we knew that the histogram was a straight
- * 3d array, so I've recovered some of the speed loss by implementing
- * a 5d accessor function with good locality of reference. This change
- * is the first step towards quantizing in a more interesting colorspace
- * than frumpy old RGB. [Adam]
- *
- * 2000/01/30 - Use palette_selector instead of option_menu for custom
- * palette. Use libgimp callback functions. [Sven]
- *
- * 99/09/01 - Created a low-bleed FS-dither option. [Adam]
- *
- * 99/08/29 - Deterministic color dithering to arbitrary palettes.
- * Ideal for animations that are going to be delta-optimized or simply
- * don't want to look 'busy' in static areas. Also a bunch of bugfixes
- * and tweaks. [Adam]
- *
- * 99/08/28 - Deterministic alpha dithering over layers, reduced bleeding
- * of transparent values into opaque values, added optional stage to
- * remove duplicate or unused color entries from final colormap. [Adam]
- *
- * 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 possessing 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]
- *
- * 99/01/10 - Hourglass... [Adam]
- *
- * 98/07/25 - Convert-to-indexed now remembers the last invocation's
- * settings. Also, GRAY->INDEXED is more flexible. [Adam]
- *
- * 98/07/05 - Sucked the warning about quantizing to too many colors 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]
- *
- * 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 ]
- *
- * 98/03/23 - fixed a longstanding fencepost - hopefully the *right*
- * way, *again*. [Adam]
- *
- * 97/11/14 - added a proper pdb interface and support for dithering
- * to custom palettes (based on a patch by Eric Hernes) [Yosh]
- *
- * 97/11/04 - fixed the accidental use of the color-counting case
- * when palette_type is WEB or MONO. [Adam]
- *
- * 97/10/25 - color-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]
- *
- * 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]
- */
-
- /* TODO for Convert:
- *
- * . Tweak, tweak, tweak. Old RGB code was tuned muchly.
- *
- * . Re-enable Heckbert locality for matching, benchmark it
- *
- * . Try faster fixed-point sRGB<->L*a*b* pixel conversion (see cpercep.c)
- *
- * . Use palette of another open INDEXED image?
- *
- * . Do error-splitting trick for GREY->INDEXED (hardly worth it)
- */
-
- /* CODE READABILITY BUGS:
- *
- * . Most uses of variants of the R,G,B variable naming convention
- * are referring to L*a*b* co-ordinates, not RGB co-ordinates!
- *
- * . Each said variable is usually one of the following, but it is
- * rarely clear which one:
- * - (assumed sRGB) raw non-linear 8-bit RGB co-ordinates
- * - 'full'-precision (unshifted) 8-bit L*a*b* co-ordinates
- * - box-space (reduced-precision shifted L*a*b*) co-ordinates
- */
-
#include "config.h"
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
#include <cairo.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
#include <gegl.h>
#include "libgimpcolor/gimpcolor.h"
-#include "libgimpmath/gimpmath.h"
#include "core-types.h"
-#include "gegl/gimp-gegl-utils.h"
-
#include "gimp.h"
-#include "gimpcontainer.h"
#include "gimpdrawable.h"
-#include "gimperror.h"
#include "gimpimage.h"
#include "gimpimage-color-profile.h"
#include "gimpimage-colormap.h"
+#include "gimpimage-convert-type.h"
#include "gimpimage-undo.h"
#include "gimpimage-undo-push.h"
-#include "gimplayer.h"
-#include "gimppalette.h"
#include "gimpprogress.h"
-
-#include "text/gimptextlayer.h"
-
-#include "gimpimage-convert-fsdither.h"
-#include "gimpimage-convert-data.h"
-#include "gimpimage-convert-type.h"
+#include "gimpsubprogress.h"
#include "gimp-intl.h"
-/* basic memory/quality tradeoff */
-#define PRECISION_R 8
-#define PRECISION_G 6
-#define PRECISION_B 6
-
-#define HIST_R_ELEMS (1<<PRECISION_R)
-#define HIST_G_ELEMS (1<<PRECISION_G)
-#define HIST_B_ELEMS (1<<PRECISION_B)
-
-#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)
-
-/* we've stretched our non-cubic L*a*b* volume to touch the
- * faces of the logical cube we've allocated for it, so re-scale
- * again in inverse proportion to get back to linear proportions.
- */
-#define R_SCALE 13 /* scale R (L*) distances by this much */
-#define G_SCALE 24 /* scale G (a*) distances by this much */
-#define B_SCALE 26 /* and B (b*) by this much */
-
-
-typedef struct _Color Color;
-typedef struct _QuantizeObj QuantizeObj;
-
-typedef void (* Pass1Func) (QuantizeObj *quantize_obj);
-typedef void (* Pass2InitFunc) (QuantizeObj *quantize_obj);
-typedef void (* Pass2Func) (QuantizeObj *quantize_obj,
- GimpLayer *layer,
- GeglBuffer *new_buffer);
-typedef void (* CleanupFunc) (QuantizeObj *quantize_obj);
-
-typedef gulong ColorFreq;
-typedef ColorFreq * CFHistogram;
-
-typedef enum { AXIS_UNDEF, AXIS_RED, AXIS_BLUE, AXIS_GREEN } AxisType;
-
-typedef double etype;
-
-
-/*
- We provide two different histogram access interfaces. HIST_LIN()
- accesses the histogram in histogram-native space, taking absolute
- histogram co-ordinates. HIST_RGB() accesses the histogram in RGB
- space. This latter takes unsigned 8-bit co-ordinates, internally
- converts those co-ordinates to histogram-native space and returns
- the access pointer to the corresponding histogram cell.
-
- Using these two interfaces we can import RGB data into a more
- interesting space and efficiently work in the latter space until
- it is time to output the quantized values in RGB again. For
- this final conversion we implement the function lin_to_rgb().
-
- We effectively pull our three-dimensional space into five dimensions
- such that the most-entropic bits lay in the lowest bits of the resulting
- array index. This gives significantly better locality of reference
- and hence a small speedup despite the extra work involved in calculating
- the index.
-
- Why not six dimensions? The expansion of dimensionality is good for random
- access such as histogram population and the query pattern typical of
- dithering but we have some code which iterates in a scanning manner, for
- which the expansion is suboptimal. The compromise is to leave the B
- dimension unmolested in the lower-order bits of the index, since this is
- the dimension most commonly iterated through in the inner loop of the
- scans.
-
- --adam
-
- RhGhRlGlB
-*/
-#define VOL_GBITS (PRECISION_G)
-#define VOL_BBITS (PRECISION_B)
-#define VOL_RBITS (PRECISION_R)
-#define VOL_GBITSh (VOL_GBITS - 3)
-#define VOL_GBITSl (VOL_GBITS - VOL_GBITSh)
-#define VOL_BBITSh (VOL_BBITS - 4)
-#define VOL_BBITSl (VOL_BBITS - VOL_BBITSh)
-#define VOL_RBITSh (VOL_RBITS - 3)
-#define VOL_RBITSl (VOL_RBITS - VOL_RBITSh)
-#define VOL_GMASKh (((1<<VOL_GBITSh)-1) << VOL_GBITSl)
-#define VOL_GMASKl ((1<<VOL_GBITSl)-1)
-#define VOL_BMASKh (((1<<VOL_BBITSh)-1) << VOL_BBITSl)
-#define VOL_BMASKl ((1<<VOL_BBITSl)-1)
-#define VOL_RMASKh (((1<<VOL_RBITSh)-1) << VOL_RBITSl)
-#define VOL_RMASKl ((1<<VOL_RBITSl)-1)
-/* The 5d compromise thing. */
-#define REF_FUNC(r,g,b) \
-( \
- (((r) & VOL_RMASKh) << (VOL_BBITS + VOL_GBITS)) | \
- (((r) & VOL_RMASKl) << (VOL_GBITSl + VOL_BBITS)) | \
- (((g) & VOL_GMASKh) << (VOL_RBITSl + VOL_BBITS)) | \
- (((g) & VOL_GMASKl) << (VOL_BBITS)) | \
- (b) \
-)
-/* The full-on 6d thing. */
-/*
-#define REF_FUNC(r,g,b) \
-( \
- (((r) & VOL_RMASKh) << (VOL_BBITS + VOL_GBITS)) | \
- (((r) & VOL_RMASKl) << (VOL_GBITSl + VOL_BBITSl)) | \
- (((g) & VOL_GMASKh) << (VOL_RBITSl + VOL_BBITS)) | \
- (((g) & VOL_GMASKl) << (VOL_BBITSl)) | \
- (((b) & VOL_BMASKh) << (VOL_RBITSl + VOL_GBITSl)) | \
- ((b) & VOL_BMASKl) \
-)
-*/
-/* The boring old 3d thing. */
-/*
-#define REF_FUNC(r,g,b) (((r)<<(PRECISION_G+PRECISION_B)) | ((g)<<(PRECISION_B)) | (b))
-*/
-
-/* You even get to choose whether you want the accessor function
- implemented as a macro or an inline function. Don't say I never
- give you anything. */
-/*
-#define HIST_LIN(hist_ptr,r,g,b) (&(hist_ptr)[REF_FUNC((r),(g),(b))])
-*/
-static inline ColorFreq *
-HIST_LIN (ColorFreq *hist_ptr,
- const gint r,
- const gint g,
- const gint b)
-{
- return (&(hist_ptr) [REF_FUNC (r, g, b)]);
-}
-
-
-#define LOWA (-86.181F)
-#define LOWB (-107.858F)
-#define HIGHA (98.237F)
-#define HIGHB (94.480F)
-
-#if 1
-#define LRAT (2.55F)
-#define ARAT (255.0F / (HIGHA - LOWA))
-#define BRAT (255.0F / (HIGHB - LOWB))
-#else
-#define LRAT (1.0F)
-#define ARAT (1.0F)
-#define BRAT (1.0F)
-#endif
-
-static const Babl *rgb_to_lab_fish = NULL;
-static const Babl *lab_to_rgb_fish = NULL;
-
-static inline void
-rgb_to_unshifted_lin (const guchar r,
- const guchar g,
- const guchar b,
- gint *hr,
- gint *hg,
- gint *hb)
-{
- gint or, og, ob;
- gfloat rgb[3] = { r / 255.0, g / 255.0, b / 255.0 };
- gfloat lab[3];
-
- babl_process (rgb_to_lab_fish, rgb, lab, 1);
-
- /* fprintf(stderr, " %d-%d-%d -> %0.3f,%0.3f,%0.3f ", r, g, b, sL, sa, sb);*/
-
- or = RINT(lab[0] * LRAT);
- og = RINT((lab[1] - LOWA) * ARAT);
- ob = RINT((lab[2] - LOWB) * BRAT);
-
- *hr = CLAMP(or, 0, 255);
- *hg = CLAMP(og, 0, 255);
- *hb = CLAMP(ob, 0, 255);
-
- /* fprintf(stderr, " %d:%d:%d ", *hr, *hg, *hb); */
-}
-
-
-static inline void
-rgb_to_lin (const guchar r,
- const guchar g,
- const guchar b,
- gint *hr,
- gint *hg,
- gint *hb)
-{
- gint or, og, ob;
-
- /*
- double sL, sa, sb;
- {
- double low_l = 999.0, low_a = 999.9, low_b = 999.0;
- double high_l = -999.0, high_a = -999.0, high_b = -999.0;
-
- int r,g,b;
-
- for (r=0; r<256; r++)
- for (g=0; g<256; g++)
- for (b=0; b<256; b++)
- {
- cpercep_rgb_to_space(r,g,b, &sL, &sa, &sb);
-
- if (sL > high_l)
- high_l = sL;
- if (sL < low_l)
- low_l = sL;
- if (sa > high_a)
- high_a = sa;
- if (sa < low_a)
- low_a = sa;
- if (sb > high_b)
- high_b = sb;
- if (sb < low_b)
- low_b = sb;
- }
-
- fprintf(stderr, " [L: %0.3f -> %0.3f / a: %0.3f -> %0.3f / b: %0.3f -> %0.3f]\t", low_l, high_l, low_a,
high_a, low_b, high_b);
-
- exit(-1);
- }
- */
-
- rgb_to_unshifted_lin (r, g, b, &or, &og, &ob);
-
-#if 0
-#define RSDF(r) ((r) >= ((HIST_R_ELEMS-1) << R_SHIFT) ? HIST_R_ELEMS-1 : \
- ((r) + ((1<<R_SHIFT)>>1) ) >> R_SHIFT)
-#define GSDF(g) ((g) >= ((HIST_G_ELEMS-1) << G_SHIFT) ? HIST_G_ELEMS-1 : \
- ((g) + ((1<<G_SHIFT)>>1) ) >> G_SHIFT)
-#define BSDF(b) ((b) >= ((HIST_B_ELEMS-1) << B_SHIFT) ? HIST_B_ELEMS-1 : \
- ((b) + ((1<<B_SHIFT)>>1) ) >> B_SHIFT)
-#else
-#define RSDF(r) ((r) >> R_SHIFT)
-#define GSDF(g) ((g) >> G_SHIFT)
-#define BSDF(b) ((b) >> B_SHIFT)
-#endif
-
- or = RSDF (or);
- og = GSDF (og);
- ob = BSDF (ob);
-
- *hr = or;
- *hg = og;
- *hb = ob;
-}
-
-
-static inline ColorFreq *
-HIST_RGB (ColorFreq *hist_ptr,
- const gint r,
- const gint g,
- const gint b)
-{
- gint hr, hg, hb;
-
- rgb_to_lin (r, g, b, &hr, &hg, &hb);
-
- return HIST_LIN (hist_ptr, hr, hg, hb);
-}
-
-
-static inline void
-lin_to_rgb (const gdouble hr,
- const gdouble hg,
- const gdouble hb,
- guchar *r,
- guchar *g,
- guchar *b)
-{
- gfloat rgb[3];
- gfloat lab[3];
- gdouble ir, ig, ib;
-
- ir = ((gdouble) (hr)) * 255.0F / (gdouble) (HIST_R_ELEMS - 1);
- ig = ((gdouble)( hg)) * 255.0F / (gdouble) (HIST_G_ELEMS - 1);
- ib = ((gdouble)( hb)) * 255.0F / (gdouble) (HIST_B_ELEMS - 1);
-
- ir = ir / LRAT;
- ig = (ig / ARAT) + LOWA;
- ib = (ib / BRAT) + LOWB;
-
- lab[0] = ir;
- lab[1] = ig;
- lab[2] = ib;
-
- babl_process (lab_to_rgb_fish, lab, rgb, 1);
-
- *r = RINT (CLAMP (rgb[0] * 255, 0.0F, 255.0F));
- *g = RINT (CLAMP (rgb[1] * 255, 0.0F, 255.0F));
- *b = RINT (CLAMP (rgb[2] * 255, 0.0F, 255.0F));
-}
-
-
-
-struct _Color
-{
- gint red;
- gint green;
- gint blue;
-};
-
-struct _QuantizeObj
-{
- Pass1Func first_pass; /* first pass over image data creates colormap */
- Pass2InitFunc second_pass_init; /* Initialize data which persists over invocations */
- Pass2Func second_pass; /* second pass maps from image data to colormap */
- CleanupFunc delete_func; /* function to clean up data associated with private */
-
- GimpPalette *custom_palette; /* The custom palette, if any */
-
- gint desired_number_of_colors; /* Number of colors we will allow */
- gint actual_number_of_colors; /* Number of colors actually needed */
- Color cmap[256]; /* colormap created by quantization */
- Color clin[256]; /* .. converted back to linear space */
- gulong index_used_count[256]; /* how many times an index was used */
- CFHistogram histogram; /* holds the histogram */
-
- gboolean want_alpha_dither;
- gint error_freedom; /* 0=much bleed, 1=controlled bleed */
-
- GimpProgress *progress;
- gint nth_layer;
- gint n_layers;
-};
-
-typedef struct
-{
- /* The bounds of the box (inclusive); expressed as histogram indexes */
- gint Rmin, Rmax;
- gint Rhalferror;
- gint Gmin, Gmax;
- gint Ghalferror;
- gint Bmin, Bmax;
- gint Bhalferror;
-
- /* The volume (actually 2-norm) of the box */
- gint volume;
-
- /* The number of nonzero histogram cells within this box */
- glong colorcount;
-
- /* 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;
-
-} box, *boxptr;
-
-
-static void zero_histogram_gray (CFHistogram histogram);
-static void zero_histogram_rgb (CFHistogram histogram);
-static void generate_histogram_gray (CFHistogram hostogram,
- GimpLayer *layer,
- gboolean alpha_dither);
-static void generate_histogram_rgb (CFHistogram histogram,
- GimpLayer *layer,
- gint col_limit,
- gboolean alpha_dither,
- GimpProgress *progress,
- gint nth_layer,
- gint n_layers);
-
-static QuantizeObj * initialize_median_cut (GimpImageBaseType old_type,
- gint num_cols,
- GimpConvertDitherType dither_type,
- GimpConvertPaletteType palette_type,
- GimpPalette *custom_palette,
- gboolean alpha_dither,
- GimpProgress *progress);
-
-static void compute_color_lin8 (QuantizeObj *quantobj,
- CFHistogram histogram,
- boxptr boxp,
- const int icolor);
-
-
-static guchar found_cols[MAXNUMCOLORS][3];
-static gint num_found_cols;
-static gboolean needs_quantize;
-
-
-/**********************************************************/
-typedef struct
-{
- glong used_count;
- guchar initial_index;
-} PalEntry;
-
-static int
-mapping_compare (const void *a,
- const void *b)
-{
- PalEntry *m1 = (PalEntry *) a;
- PalEntry *m2 = (PalEntry *) b;
-
- return (m2->used_count - m1->used_count);
-}
-
-/* FWIW, the make_remap_table() and mapping_compare() function source
- * and PalEntry may be re-used under the XFree86-style license.
- * <adam gimp org>
- */
-static void
-make_remap_table (const guchar old_palette[],
- guchar new_palette[],
- const gulong index_used_count[],
- guchar remap_table[],
- gint *num_entries)
-{
- gint i, j, k;
- guchar temppal[256 * 3];
- gulong tempuse[256];
- gulong transmap[256];
- PalEntry *palentries;
- gint used = 0;
-
- memset (temppal, 0, 256 * 3);
- memset (tempuse, 0, 256 * sizeof (gulong));
- memset (transmap, 255, 256 * sizeof (gulong));
-
- /* First pass - only collect entries which are marked as being used
- * at all in index_used_count.
- */
- for (i = 0; i < *num_entries; i++)
- {
- if (index_used_count[i])
- {
- temppal[used*3 + 0] = old_palette[i*3 + 0];
- temppal[used*3 + 1] = old_palette[i*3 + 1];
- temppal[used*3 + 2] = old_palette[i*3 + 2];
-
- tempuse[used] = index_used_count[i];
- transmap[i] = used;
-
- used++;
- }
- }
-
- /* Second pass - remove duplicates. (O(n^3), could do better!) */
- for (i = 0; i < used; i++)
- {
- for (j = 0; j < i; j++)
- {
- if ((temppal[i*3 + 1] == temppal[j*3 + 1]) &&
- (temppal[i*3 + 0] == temppal[j*3 + 0]) &&
- (temppal[i*3 + 2] == temppal[j*3 + 2]) &&
- tempuse[j] &&
- tempuse[i])
- {
- /* Move the 'used' tally from one to the other. */
- tempuse[i] += tempuse[j];
- /* zero one of them, deactivating its entry. */
- tempuse[j] = 0;
-
- /* change all mappings from this dead index to the live
- * one.
- */
- for (k = 0; k < *num_entries; k++)
- {
- if (index_used_count[k] && (transmap[k] == j))
- transmap[k] = i;
- }
- }
- }
- }
-
- /* Third pass - rank all used indicies to the beginning of the
- * palette.
- */
- palentries = g_new (PalEntry, used);
-
- for (i = 0; i < used; i++)
- {
- palentries[i].initial_index = i;
- palentries[i].used_count = tempuse[i];
- }
-
- qsort (palentries, used, sizeof (PalEntry), &mapping_compare);
-
- for (i = 0; i < *num_entries; i++)
- {
- if (index_used_count[i])
- {
- for (j = 0; j < used; j++)
- {
- if ((transmap[i] == palentries[j].initial_index)
- && (palentries[j].used_count))
- {
- remap_table[i] = j;
- break;
- }
- }
- }
- }
- for (i = 0; i < *num_entries; i++)
- {
- if (index_used_count[i])
- {
- new_palette[remap_table[i] * 3 + 0] = old_palette[i * 3 + 0];
- new_palette[remap_table[i] * 3 + 1] = old_palette[i * 3 + 1];
- new_palette[remap_table[i] * 3 + 2] = old_palette[i * 3 + 2];
- }
- }
-
- *num_entries = 0;
-
- for (j = 0; j < used; j++)
- {
- if (palentries[j].used_count)
- {
- (*num_entries)++;
- }
- }
-
- g_free (palentries);
-}
-
-static void
-remap_indexed_layer (GimpLayer *layer,
- const guchar *remap_table,
- gint num_entries)
-{
- GeglBufferIterator *iter;
- const Babl *format;
- gint bpp;
- gboolean has_alpha;
-
- format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
-
- bpp = babl_format_get_bytes_per_pixel (format);
- has_alpha = babl_format_has_alpha (format);
-
- iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
- NULL, 0, NULL,
- GEGL_ACCESS_READWRITE, GEGL_ABYSS_NONE);
-
- while (gegl_buffer_iterator_next (iter))
- {
- guchar *data = iter->data[0];
- gint length = iter->length;
-
- if (has_alpha)
- {
- while (length--)
- {
- if (data[ALPHA_I])
- data[INDEXED] = remap_table[data[INDEXED]];
-
- data += bpp;
- }
- }
- else
- {
- while (length--)
- {
- data[INDEXED] = remap_table[data[INDEXED]];
-
- data += bpp;
- }
- }
- }
-}
-
-static gint
-color_quicksort (const void *c1,
- const void *c2)
-{
- Color *color1 = (Color *) c1;
- Color *color2 = (Color *) c2;
-
- gdouble v1 = GIMP_RGB_LUMINANCE (color1->red, color1->green, color1->blue);
- gdouble v2 = GIMP_RGB_LUMINANCE (color2->red, color2->green, color2->blue);
-
- if (v1 < v2)
- return -1;
- else if (v1 > v2)
- return 1;
- else
- return 0;
-}
-
gboolean
-gimp_image_convert_type (GimpImage *image,
- GimpImageBaseType new_type,
- /* The following are only used for
- * new_type == GIMP_INDEXED
- */
- gint num_cols,
- GimpConvertDitherType dither,
- gboolean alpha_dither,
- gboolean text_layer_dither,
- gboolean remove_dups,
- GimpConvertPaletteType palette_type,
- GimpPalette *custom_palette,
- GimpProgress *progress,
- GError **error)
+gimp_image_convert_type (GimpImage *image,
+ GimpImageBaseType new_type,
+ GimpProgress *progress,
+ GError **error)
{
- QuantizeObj *quantobj = NULL;
GimpImageBaseType old_type;
GList *all_layers;
GList *list;
const gchar *undo_desc = NULL;
+ GimpProgress *sub_progress = NULL;
GimpColorProfile *dest_profile = NULL;
gint nth_layer;
gint n_layers;
g_return_val_if_fail (GIMP_IS_IMAGE (image), FALSE);
g_return_val_if_fail (new_type != gimp_image_get_base_type (image), FALSE);
- g_return_val_if_fail (custom_palette == NULL ||
- GIMP_IS_PALETTE (custom_palette), FALSE);
- g_return_val_if_fail (custom_palette == NULL ||
- gimp_palette_get_n_colors (custom_palette) <= 256,
- FALSE);
+ g_return_val_if_fail (new_type != GIMP_INDEXED, FALSE);
g_return_val_if_fail (progress == NULL || GIMP_IS_PROGRESS (progress), FALSE);
g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
- if (palette_type == GIMP_CUSTOM_PALETTE)
- {
- if (! custom_palette)
- palette_type = GIMP_MONO_PALETTE;
-
- if (gimp_palette_get_n_colors (custom_palette) == 0)
- {
- g_set_error_literal (error, GIMP_ERROR, GIMP_FAILED,
- _("Cannot convert image: palette is empty."));
- return FALSE;
- }
- }
-
gimp_set_busy (image->gimp);
all_layers = gimp_image_get_layer_list (image);
n_layers = g_list_length (all_layers);
+ if (progress)
+ sub_progress = gimp_sub_progress_new (progress);
+
switch (new_type)
{
case GIMP_RGB:
@@ -810,9 +79,8 @@ gimp_image_convert_type (GimpImage *image,
undo_desc = C_("undo-type", "Convert Image to Grayscale");
break;
- case GIMP_INDEXED:
- undo_desc = C_("undo-type", "Convert Image to Indexed");
- break;
+ default:
+ g_return_val_if_reached (FALSE);
}
g_object_freeze_notify (G_OBJECT (image));
@@ -828,294 +96,48 @@ gimp_image_convert_type (GimpImage *image,
g_object_set (image, "base-type", new_type, NULL);
- if (gimp_image_get_color_profile (image))
- {
- if (old_type == GIMP_GRAY ||
- new_type == GIMP_GRAY)
- {
- /* when converting to/from GRAY, convert to the new
- * type's builtin profile.
- */
- dest_profile = gimp_image_get_builtin_color_profile (image);
- }
- }
-
- /* Convert to indexed? Build histogram if necessary. */
- if (new_type == GIMP_INDEXED)
+ /* when converting to/from GRAY, convert to the new type's builtin
+ * profile.
+ */
+ if (old_type == GIMP_GRAY ||
+ new_type == GIMP_GRAY)
{
- rgb_to_lab_fish = babl_fish (babl_format ("R'G'B' float"),
- babl_format ("CIE Lab float"));
- lab_to_rgb_fish = babl_fish (babl_format ("CIE Lab float"),
- babl_format ("R'G'B' float"));
-
- /* fprintf(stderr, " TO INDEXED(%d) ", num_cols); */
-
- /* don't dither if the input is grayscale and we are simply
- * mapping every color
- */
- if (old_type == GIMP_GRAY &&
- num_cols == 256 &&
- palette_type == GIMP_MAKE_PALETTE)
- {
- dither = GIMP_NO_DITHER;
- }
-
- quantobj = initialize_median_cut (old_type, num_cols, dither,
- palette_type, custom_palette,
- alpha_dither,
- progress);
-
- if (palette_type == GIMP_MAKE_PALETTE)
- {
- if (old_type == GIMP_GRAY)
- zero_histogram_gray (quantobj->histogram);
- else
- zero_histogram_rgb (quantobj->histogram);
-
- /* To begin, assume that there are fewer colors in
- * the image than the user actually asked for. In that
- * case, we don't need to quantize or color-dither.
- */
- needs_quantize = FALSE;
- num_found_cols = 0;
-
- /* Build the histogram */
- for (list = all_layers, nth_layer = 0;
- list;
- list = g_list_next (list), nth_layer++)
- {
- GimpLayer *layer = list->data;
-
- if (old_type == GIMP_GRAY)
- {
- generate_histogram_gray (quantobj->histogram,
- layer, alpha_dither);
- }
- else
- {
- /* Note: generate_histogram_rgb may set
- * needs_quantize if the image contains more colors
- * than the limit specified by the user.
- */
- generate_histogram_rgb (quantobj->histogram,
- layer, num_cols, alpha_dither,
- progress, nth_layer, n_layers);
- }
- }
- }
-
- if (progress)
- gimp_progress_set_text_literal (progress,
- _("Converting to indexed colors (stage 2)"));
-
- if (old_type == GIMP_RGB &&
- ! needs_quantize &&
- palette_type == GIMP_MAKE_PALETTE)
- {
- gint i;
-
- /* If this is an RGB image, and the user wanted a
- * custom-built generated palette, and this image has no
- * more colors 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.
- */
-
- quantobj->delete_func (quantobj);
- quantobj = initialize_median_cut (old_type, num_cols,
- GIMP_NODESTRUCT_DITHER,
- palette_type,
- custom_palette,
- alpha_dither,
- progress);
- /* 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 (palette_type == GIMP_MAKE_PALETTE)
- qsort (quantobj->cmap,
- quantobj->actual_number_of_colors, sizeof (Color),
- color_quicksort);
-
- if (progress)
- gimp_progress_set_text_literal (progress,
- _("Converting to indexed colors (stage 3)"));
-
- /* Initialise data which must persist across indexed layer iterations */
- if (quantobj->second_pass_init)
- quantobj->second_pass_init (quantobj);
-
- /* Set the generated palette on the image, we need it to
- * convert the layers. We optionally remove duplicate entries
- * after the layer conversion.
- */
- {
- guchar colormap[GIMP_IMAGE_COLORMAP_SIZE];
- gint i, j;
-
- for (i = 0, j = 0; i < quantobj->actual_number_of_colors; i++)
- {
- colormap[j++] = quantobj->cmap[i].red;
- colormap[j++] = quantobj->cmap[i].green;
- colormap[j++] = quantobj->cmap[i].blue;
- }
-
- gimp_image_set_colormap (image, colormap,
- quantobj->actual_number_of_colors, TRUE);
- }
+ if (gimp_image_get_color_profile (image))
+ dest_profile = gimp_image_get_builtin_color_profile (image);
}
- /* Convert all layers */
- if (quantobj)
- quantobj->n_layers = n_layers;
-
for (list = all_layers, nth_layer = 0;
list;
list = g_list_next (list), nth_layer++)
{
- GimpLayer *layer = list->data;
- gboolean quantize = FALSE;
-
- switch (new_type)
- {
- case GIMP_RGB:
- case GIMP_GRAY:
- quantize = FALSE;
- break;
-
- case GIMP_INDEXED:
- if (gimp_item_is_text_layer (GIMP_ITEM (layer)))
- quantize = text_layer_dither;
- else
- quantize = TRUE;
- break;
-
- default:
- break;
- }
-
- if (quantize)
- {
- GeglBuffer *new_buffer;
- gboolean has_alpha;
+ GimpDrawable *drawable = list->data;
- has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
+ if (sub_progress)
+ gimp_sub_progress_set_step (GIMP_SUB_PROGRESS (sub_progress),
+ nth_layer, n_layers);
- new_buffer =
- gegl_buffer_new (GEGL_RECTANGLE (0, 0,
- gimp_item_get_width (GIMP_ITEM (layer)),
- gimp_item_get_height (GIMP_ITEM (layer))),
- gimp_image_get_layer_format (image,
- has_alpha));
-
- quantobj->nth_layer = nth_layer;
- quantobj->second_pass (quantobj, layer, new_buffer);
-
- gimp_drawable_set_buffer (GIMP_DRAWABLE (layer), TRUE, NULL,
- new_buffer);
- g_object_unref (new_buffer);
- }
- else
- {
- gimp_drawable_convert_type (GIMP_DRAWABLE (layer), image, new_type,
- gimp_drawable_get_precision (GIMP_DRAWABLE (layer)),
- dest_profile, 0, 0,
- TRUE, NULL);
- }
+ gimp_drawable_convert_type (drawable, image, new_type,
+ gimp_drawable_get_precision (drawable),
+ dest_profile, 0, 0,
+ TRUE, sub_progress);
}
- /* Set the final palette on the image */
- switch (new_type)
- {
- case GIMP_RGB:
- case GIMP_GRAY:
- if (old_type == GIMP_INDEXED)
- gimp_image_unset_colormap (image, TRUE);
- break;
-
- case GIMP_INDEXED:
- if (remove_dups && (palette_type != GIMP_MAKE_PALETTE))
- {
- guchar colormap[GIMP_IMAGE_COLORMAP_SIZE];
- gint i, j;
- guchar old_palette[256 * 3];
- guchar new_palette[256 * 3];
- guchar remap_table[256];
- gint num_entries;
-
- for (i = 0, j = 0; i < quantobj->actual_number_of_colors; i++)
- {
- old_palette[j++] = quantobj->cmap[i].red;
- old_palette[j++] = quantobj->cmap[i].green;
- old_palette[j++] = quantobj->cmap[i].blue;
- }
-
- num_entries = quantobj->actual_number_of_colors;
-
- /* Generate a remapping table */
- make_remap_table (old_palette, new_palette,
- quantobj->index_used_count,
- remap_table, &num_entries);
-
- /* Convert all layers */
- for (list = all_layers; list; list = g_list_next (list))
- {
- remap_indexed_layer (list->data, remap_table, num_entries);
- }
-
- for (i = 0, j = 0; i < num_entries; i++)
- {
- colormap[j] = new_palette[j]; j++;
- colormap[j] = new_palette[j]; j++;
- colormap[j] = new_palette[j]; j++;
- }
-
- gimp_image_set_colormap (image, colormap, num_entries, TRUE);
- }
- break;
- }
+ if (old_type == GIMP_INDEXED)
+ gimp_image_unset_colormap (image, TRUE);
/* When converting to/from GRAY, set the new profile.
*/
- switch (new_type)
+ if (old_type == GIMP_GRAY ||
+ new_type == GIMP_GRAY)
{
- case GIMP_RGB:
- case GIMP_INDEXED:
- if (old_type == GIMP_GRAY)
- {
- if (gimp_image_get_color_profile (image))
- gimp_image_set_color_profile (image, dest_profile, NULL);
- else
- gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image));
- }
- break;
-
- case GIMP_GRAY:
if (gimp_image_get_color_profile (image))
gimp_image_set_color_profile (image, dest_profile, NULL);
else
gimp_color_managed_profile_changed (GIMP_COLOR_MANAGED (image));
- break;
}
- /* Delete the quantizer object, if there is one */
- if (quantobj)
- quantobj->delete_func (quantobj);
+ if (sub_progress)
+ gimp_progress_set_value (sub_progress, 1.0);
gimp_image_undo_group_end (image);
@@ -1128,3409 +150,3 @@ gimp_image_convert_type (GimpImage *image,
return TRUE;
}
-
-/*
- * Indexed color conversion machinery
- */
-
-static void
-zero_histogram_gray (CFHistogram histogram)
-{
- gint i;
-
- for (i = 0; i < 256; i++)
- histogram[i] = 0;
-}
-
-
-static void
-zero_histogram_rgb (CFHistogram histogram)
-{
- memset (histogram, 0,
- HIST_R_ELEMS * HIST_G_ELEMS * HIST_B_ELEMS * sizeof (ColorFreq));
-}
-
-
-static void
-generate_histogram_gray (CFHistogram histogram,
- GimpLayer *layer,
- gboolean alpha_dither)
-{
- GeglBufferIterator *iter;
- const Babl *format;
- gint bpp;
- gboolean has_alpha;
-
- format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
-
- g_return_if_fail (format == babl_format ("Y' u8") ||
- format == babl_format ("Y'A u8"));
-
- bpp = babl_format_get_bytes_per_pixel (format);
- has_alpha = babl_format_has_alpha (format);
-
- iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
- NULL, 0, format,
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
-
- while (gegl_buffer_iterator_next (iter))
- {
- const guchar *data = iter->data[0];
- gint length = iter->length;
-
- if (has_alpha)
- {
- while (length--)
- {
- if (data[ALPHA_G] > 127)
- histogram[*data]++;
-
- data += bpp;
- }
- }
- else
- {
- while (length--)
- {
- histogram[*data]++;
-
- data += bpp;
- }
- }
- }
-}
-
-
-static void
-generate_histogram_rgb (CFHistogram histogram,
- GimpLayer *layer,
- gint col_limit,
- gboolean alpha_dither,
- GimpProgress *progress,
- gint nth_layer,
- gint n_layers)
-{
- GeglBufferIterator *iter;
- const Babl *format;
- GeglRectangle *roi;
- ColorFreq *colfreq;
- gint nfc_iter;
- gint row, col, coledge;
- gint offsetx, offsety;
- glong layer_size;
- glong total_size = 0;
- gint count = 0;
- gint bpp;
- gboolean has_alpha;
-
- format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
-
- g_return_if_fail (format == babl_format ("R'G'B' u8") ||
- format == babl_format ("R'G'B'A u8"));
-
- bpp = babl_format_get_bytes_per_pixel (format);
- has_alpha = babl_format_has_alpha (format);
-
- gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
-
- layer_size = (gimp_item_get_width (GIMP_ITEM (layer)) *
- gimp_item_get_height (GIMP_ITEM (layer)));
-
- /* g_printerr ("col_limit = %d, nfc = %d\n", col_limit, num_found_cols); */
-
- iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
- NULL, 0, format,
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
- roi = &iter->roi[0];
-
- if (progress)
- gimp_progress_set_value (progress, 0.0);
-
- while (gegl_buffer_iterator_next (iter))
- {
- const guchar *data = iter->data[0];
- gint length = iter->length;
-
- total_size += length;
-
- /* g_printerr (" [%d,%d - %d,%d]", srcPR.x, src_roi->y, offsetx, offsety); */
-
- if (needs_quantize)
- {
- if (alpha_dither)
- {
- /* if alpha-dithering,
- we need to be deterministic w.r.t. offsets */
-
- col = roi->x + offsetx;
- coledge = col + roi->width;
- row = roi->y + offsety;
-
- while (length--)
- {
- gboolean transparent = FALSE;
-
- if (has_alpha &&
- data[ALPHA] <
- DM[col & DM_WIDTHMASK][row & DM_HEIGHTMASK])
- transparent = TRUE;
-
- if (! transparent)
- {
- colfreq = HIST_RGB (histogram,
- data[RED],
- data[GREEN],
- data[BLUE]);
- (*colfreq)++;
- }
-
- col++;
- if (col == coledge)
- {
- col = roi->x + offsetx;
- row++;
- }
-
- data += bpp;
- }
- }
- else
- {
- while (length--)
- {
- if ((has_alpha && ((data[ALPHA] > 127)))
- || (!has_alpha))
- {
- colfreq = HIST_RGB (histogram,
- data[RED],
- data[GREEN],
- data[BLUE]);
- (*colfreq)++;
- }
-
- data += bpp;
- }
- }
- }
- else
- {
- /* if alpha-dithering, we need to be deterministic w.r.t. offsets */
- col = roi->x + offsetx;
- coledge = col + roi->width;
- row = roi->y + offsety;
-
- while (length--)
- {
- gboolean transparent = FALSE;
-
- if (has_alpha)
- {
- if (alpha_dither)
- {
- if (data[ALPHA] <
- DM[col & DM_WIDTHMASK][row & DM_HEIGHTMASK])
- transparent = TRUE;
- }
- else
- {
- if (data[ALPHA] <= 127)
- transparent = TRUE;
- }
- }
-
- if (! transparent)
- {
- colfreq = HIST_RGB (histogram,
- data[RED],
- data[GREEN],
- data[BLUE]);
- (*colfreq)++;
-
- if (!needs_quantize)
- {
- for (nfc_iter = 0;
- nfc_iter < num_found_cols;
- nfc_iter++)
- {
- if ((data[RED] == found_cols[nfc_iter][0]) &&
- (data[GREEN] == found_cols[nfc_iter][1]) &&
- (data[BLUE] == found_cols[nfc_iter][2]))
- goto already_found;
- }
-
- /* Color was not in the table of
- * existing colors
- */
-
- num_found_cols++;
-
- if (num_found_cols > col_limit)
- {
- /* There are more colors in the image than
- * were allowed. We switch to plain
- * histogram calculation with a view to
- * quantizing at a later stage.
- */
- needs_quantize = TRUE;
- /* g_print ("\nmax colors exceeded - needs quantize.\n");*/
- goto already_found;
- }
- else
- {
- /* Remember the new color we just found.
- */
- found_cols[num_found_cols-1][0] = data[RED];
- found_cols[num_found_cols-1][1] = data[GREEN];
- found_cols[num_found_cols-1][2] = data[BLUE];
- }
- }
- }
- already_found:
-
- col++;
- if (col == coledge)
- {
- col = roi->x + offsetx;
- row++;
- }
-
- data += bpp;
- }
- }
-
- if (progress && (count % 16 == 0))
- gimp_progress_set_value (progress,
- (nth_layer + ((gdouble) total_size)/
- layer_size) / (gdouble) n_layers);
- }
-
-/* g_print ("O: col_limit = %d, nfc = %d\n", col_limit, num_found_cols);*/
-}
-
-
-
-static boxptr
-find_split_candidate (const boxptr boxlist,
- const gint numboxes,
- AxisType *which_axis,
- const gint desired_colors)
-{
- boxptr boxp;
- gint i;
- etype maxc = 0;
- boxptr which = NULL;
- gdouble Lbias;
-
- *which_axis = AXIS_UNDEF;
-
- /* we only perform the initial L-split bias /at all/ if the final
- number of desired colors is quite low, otherwise it all comes
- out in the wash anyway and this initial bias generally only hurts
- us in the long run. */
- if (desired_colors <= 16)
- {
-#define BIAS_FACTOR 2.66F
-#define BIAS_NUMBER 2 /* 0 */
-
- /* we bias towards splitting across L* for first few colors */
- Lbias = (numboxes > BIAS_NUMBER) ? 1.0F : ((gdouble) (BIAS_NUMBER + 1) -
- ((gdouble) numboxes)) /
- ((gdouble) BIAS_NUMBER / BIAS_FACTOR);
- /*Lbias = 1.0;
- fprintf(stderr, " [[%d]] ", numboxes);
- fprintf(stderr, "Using ramped L-split bias.\n");
- fprintf(stderr, "R\n");
- */
- }
- else
- Lbias = 1.0F;
-
- for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++)
- {
- if (boxp->volume > 0)
- {
-#ifndef _MSC_VER
- etype rpe = (double)((boxp->rerror) * R_SCALE * R_SCALE);
- etype gpe = (double)((boxp->gerror) * G_SCALE * G_SCALE);
- etype bpe = (double)((boxp->berror) * B_SCALE * B_SCALE);
-#else
- /*
- * Sorry about the mess, otherwise would get :
- * error C2520: conversion from unsigned __int64 to double
- * not implemented, use signed __int64
- */
- etype rpe = (double)(((__int64)boxp->rerror) * R_SCALE * R_SCALE);
- etype gpe = (double)(((__int64)boxp->gerror) * G_SCALE * G_SCALE);
- etype bpe = (double)(((__int64)boxp->berror) * B_SCALE * B_SCALE);
-#endif
-
- if (Lbias * rpe > maxc &&
- boxp->Rmin < boxp->Rmax)
- {
- which = boxp;
- maxc = Lbias * rpe;
- *which_axis = AXIS_RED;
- }
-
- if (gpe > maxc &&
- boxp->Gmin < boxp->Gmax)
- {
- which = boxp;
- maxc = gpe;
- *which_axis = AXIS_GREEN;
- }
-
- if (bpe > maxc &&
- boxp->Bmin < boxp->Bmax)
- {
- which = boxp;
- maxc = bpe;
- *which_axis = AXIS_BLUE;
- }
- }
- }
-
- /* fprintf(stderr, " %f,%p ", maxc, which); */
- /* fprintf(stderr, " %llu ", maxc); */
-
- return which;
-}
-
-
-/* Find the splittable box with the largest (scaled) volume Returns
- * NULL if no splittable boxes remain
- */
-static boxptr
-find_biggest_volume (const boxptr boxlist,
- const gint numboxes)
-{
- boxptr boxp;
- gint i;
- gint 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;
-}
-
-
-/* Shrink the min/max bounds of a box to enclose only nonzero
- * elements, and recompute its volume and population
- */
-static void
-update_box_gray (const CFHistogram histogram,
- boxptr boxp)
-{
- gint i, min, max, dist;
- ColorFreq 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;
-}
-
-
-/* Shrink the min/max bounds of a box to enclose only nonzero
- * elements, and recompute its volume, population and error
- */
-static void
-update_box_rgb (const CFHistogram histogram,
- boxptr boxp,
- const gint cells_remaining)
-{
- gint R, G, B;
- gint Rmin, Rmax, Gmin, Gmax, Bmin, Bmax;
- gint dist0, dist1, dist2;
- ColorFreq ccount;
- /*
- guint64 tempRerror;
- guint64 tempGerror;
- guint64 tempBerror;
- */
- QuantizeObj dummyqo;
- box dummybox;
-
- /* fprintf(stderr, "U"); */
-
- 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++)
- {
- for (B = Bmin; B <= Bmax; B++)
- {
- if (*HIST_LIN (histogram, R, G, B) != 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++)
- {
- for (B = Bmin; B <= Bmax; B++)
- {
- if (*HIST_LIN (histogram, R, G, B) != 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++)
- {
- for (B = Bmin; B <= Bmax; B++)
- {
- if (*HIST_LIN (histogram, R, G, B) != 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++)
- {
- for (B = Bmin; B <= Bmax; B++)
- {
- if (*HIST_LIN (histogram, R, G, B) != 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++)
- {
- for (G = Gmin; G <= Gmax; G++)
- {
- if (*HIST_LIN (histogram, R, G, B) != 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++)
- {
- for (G = Gmin; G <= Gmax; G++)
- {
- if (*HIST_LIN (histogram, R, G, B) != 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. (ADM: note: this isn't true.)
- * 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.
- */
- dist0 = ((1 + Rmax - Rmin) << R_SHIFT) * R_SCALE;
- dist1 = ((1 + Gmax - Gmin) << G_SHIFT) * G_SCALE;
- dist2 = ((1 + Bmax - Bmin) << B_SHIFT) * B_SCALE;
- boxp->volume = dist0*dist0 + dist1*dist1 + dist2*dist2;
- /* boxp->volume = dist0 * dist1 * dist2; */
-
- compute_color_lin8(&dummyqo, histogram, boxp, 0);
-
- /*printf("(%d %d %d)\n", dummyqo.cmap[0].red,dummyqo.cmap[0].green,dummyqo.cmap[0].blue);
- fflush(stdout);*/
-
- /* Now scan remaining volume of box and compute population */
- ccount = 0;
- boxp->error = 0;
- boxp->rerror = 0;
- boxp->gerror = 0;
- boxp->berror = 0;
- for (R = Rmin; R <= Rmax; R++)
- {
- for (G = Gmin; G <= Gmax; G++)
- {
- for (B = Bmin; B <= Bmax; B++)
- {
- ColorFreq freq_here;
-
- freq_here = *HIST_LIN (histogram, R, G, B);
-
- if (freq_here != 0)
- {
- int ge, be, re;
-
- dummybox.Rmin = dummybox.Rmax = R;
- dummybox.Gmin = dummybox.Gmax = G;
- dummybox.Bmin = dummybox.Bmax = B;
- compute_color_lin8(&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 += freq_here * (re) * (re);
- boxp->gerror += freq_here * (ge) * (ge);
- boxp->berror += freq_here * (be) * (be);
-
- ccount += freq_here;
- }
- }
- }
- }
-
-#if 0
- fg d;flg fd;kg fld;gflkfld
- /* Scan again, taking note of halfway error point for red axis */
- tempRerror = 0;
- boxp->Rhalferror = Rmin;
-#warning r<=?
- for (R = Rmin; R <= Rmax; R++)
- {
- for (G = Gmin; G <= Gmax; G++)
- {
- for (B = Bmin; B <= Bmax; B++)
- {
- ColorFreq freq_here;
- freq_here = *HIST_LIN(histogram, R, G, B);
- if (freq_here != 0)
- {
- int re;
- int idist;
- double dist;
-
- dummybox.Rmin = dummybox.Rmax = R;
- dummybox.Gmin = dummybox.Gmax = G;
- dummybox.Bmin = dummybox.Bmax = B;
- compute_color_lin8(&dummyqo, histogram, &dummybox, 1);
-
- re = dummyqo.cmap[0].red - dummyqo.cmap[1].red;
-
- tempRerror += freq_here * (re) * (re);
-
- if (tempRerror*2 >= boxp->rerror)
- goto green_axisscan;
- else
- boxp->Rhalferror = R;
- }
- }
- }
- }
- fprintf(stderr, " D:");
- green_axisscan:
-
- fprintf(stderr, "<%d: %llu/%llu> ", R, tempRerror, boxp->rerror);
- /* Scan again, taking note of halfway error point for green axis */
- tempGerror = 0;
- boxp->Ghalferror = Gmin;
-#warning G<=?
- for (G = Gmin; G <= Gmax; G++)
- {
- for (R = Rmin; R <= Rmax; R++)
- {
- for (B = Bmin; B <= Bmax; B++)
- {
- ColorFreq freq_here;
- freq_here = *HIST_LIN(histogram, R, G, B);
- if (freq_here != 0)
- {
- int ge;
- dummybox.Rmin = dummybox.Rmax = R;
- dummybox.Gmin = dummybox.Gmax = G;
- dummybox.Bmin = dummybox.Bmax = B;
- compute_color_lin8(&dummyqo, histogram, &dummybox, 1);
-
- ge = dummyqo.cmap[0].green - dummyqo.cmap[1].green;
-
- tempGerror += freq_here * (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;
-#warning B<=?
- for (B = Bmin; B <= Bmax; B++)
- {
- for (R = Rmin; R <= Rmax; R++)
- {
- for (G = Gmin; G <= Gmax; G++)
- {
- ColorFreq freq_here;
- freq_here = *HIST_LIN(histogram, R, G, B);
- if (freq_here != 0)
- {
- int be;
- dummybox.Rmin = dummybox.Rmax = R;
- dummybox.Gmin = dummybox.Gmax = G;
- dummybox.Bmin = dummybox.Bmax = B;
- compute_color_lin8(&dummyqo, histogram, &dummybox, 1);
-
- be = dummyqo.cmap[0].blue - dummyqo.cmap[1].blue;
-
- tempBerror += freq_here * (be) * (be);
-
- if (tempBerror*2 >= boxp->berror)
- goto finished_axesscan;
- else
- boxp->Bhalferror = B;
- }
- }
- }
- }
- finished_axesscan:
-#else
-
- boxp->Rhalferror = Rmin + (Rmax - Rmin + 1) / 2;
- boxp->Ghalferror = Gmin + (Gmax - Gmin + 1) / 2;
- boxp->Bhalferror = Bmin + (Bmax - Bmin + 1) / 2;
-
- if (dist0 && dist1 && dist2)
- {
- AxisType longest_ax = AXIS_UNDEF;
- gint longest_length = 0;
- gint longest_length2 = 0;
- gint ratio;
-
- /*
- fprintf(stderr, "[%d,%d,%d=%d,%d,%d] ",
- (Rmax - Rmin), (Gmax - Gmin), (Bmax - Bmin),
- dist0, dist1, dist2);
- */
-
- if (dist0 >= longest_length)
- {
- longest_length2 = longest_length;
- longest_length = dist0;
- longest_ax = AXIS_RED;
- }
- else if (dist0 >= longest_length2)
- {
- longest_length2 = dist0;
- }
-
- if (dist1 >= longest_length)
- {
- longest_length2 = longest_length;
- longest_length = dist1;
- longest_ax = AXIS_GREEN;
- }
- else if (dist1 >= longest_length2)
- {
- longest_length2 = dist1;
- }
-
- if (dist2 >= longest_length)
- {
- longest_length2 = longest_length;
- longest_length = dist2;
- longest_ax = AXIS_BLUE;
- }
- else if (dist2 >= longest_length2)
- {
- longest_length2 = dist2;
- }
-
- if (longest_length2 == 0)
- longest_length2 = 1;
-
- ratio = (longest_length + longest_length2/2) / longest_length2;
- /* fprintf(stderr, " ratio:(%d/%d)=%d ", longest_length, longest_length2, ratio);
- fprintf(stderr, "C%d ", cells_remaining); */
-
- if (ratio > cells_remaining + 1)
- ratio = cells_remaining + 1;
-
- if (ratio > 2)
- {
- switch (longest_ax)
- {
- case AXIS_RED:
- if (Rmin + (Rmax - Rmin + ratio / 2) / ratio < Rmax)
- {
- /* fprintf(stderr, "FR%d \007\n",ratio);*/
- boxp->Rhalferror = Rmin + (Rmax - Rmin + ratio / 2) / ratio;
- }
- break;
- case AXIS_GREEN:
- if (Gmin + (Gmax - Gmin + ratio / 2) / ratio < Gmax)
- {
- /* fprintf(stderr, "FG%d \007\n",ratio);*/
- boxp->Ghalferror = Gmin + (Gmax - Gmin + ratio / 2) / ratio;
- }
- break;
- case AXIS_BLUE:
- if (Bmin + (Bmax - Bmin + ratio / 2) / ratio < Bmax)
- {
- /* fprintf(stderr, "FB%d \007\n",ratio);*/
- boxp->Bhalferror = Bmin + (Bmax - Bmin + ratio / 2) / ratio;
- }
- break;
- default:
- g_warning ("GRR, UNDEF LONGEST AXIS\007\n");
- }
- }
- }
-
- if (boxp->Rhalferror == Rmax)
- boxp->Rhalferror = Rmin;
- if (boxp->Ghalferror == Gmax)
- boxp->Ghalferror = Gmin;
- if (boxp->Bhalferror == Bmax)
- boxp->Bhalferror = Bmin;
-
- /*
- boxp->Rhalferror = RSDF(dummyqo.cmap[0].red);
- boxp->Ghalferror = GSDF(dummyqo.cmap[0].green);
- boxp->Bhalferror = BSDF(dummyqo.cmap[0].blue);
- */
-
- /*
- boxp->Rhalferror = (RSDF(dummyqo.cmap[0].red) + (Rmin+Rmax)/2)/2;
- boxp->Ghalferror = (GSDF(dummyqo.cmap[0].green) + (Gmin+Gmax)/2)/2;
- boxp->Bhalferror = (BSDF(dummyqo.cmap[0].blue) + (Bmin+Bmax)/2)/2;
- */
-
-
-#endif
- /*
- fprintf(stderr, " %d,%d", dummyqo.cmap[0].blue, boxp->Bmax);
-
- g_assert(boxp->Rhalferror >= boxp->Rmin);
- g_assert(boxp->Rhalferror < boxp->Rmax);
- g_assert(boxp->Ghalferror >= boxp->Gmin);
- g_assert(boxp->Ghalferror < boxp->Gmax);
- g_assert(boxp->Bhalferror >= boxp->Bmin);
- g_assert(boxp->Bhalferror < boxp->Bmax);*/
-
- /*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);*/
-
- boxp->colorcount = ccount;
-}
-
-
-/* Repeatedly select and split the largest box until we have enough
- * boxes
- */
-static gint
-median_cut_gray (CFHistogram histogram,
- boxptr boxlist,
- gint numboxes,
- gint desired_colors)
-{
- gint lb;
- boxptr b1, b2;
-
- while (numboxes < desired_colors)
- {
- /* Select box to split.
- * Current algorithm: by population for first half, then by volume.
- */
-
- 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;
-}
-
-/* Repeatedly select and split the largest box until we have enough
- * boxes
- */
-static gint
-median_cut_rgb (CFHistogram histogram,
- boxptr boxlist,
- gint numboxes,
- gint desired_colors,
- GimpProgress *progress)
-{
- gint lb;
- boxptr b1, b2;
- AxisType which_axis;
-
- while (numboxes < desired_colors)
- {
- b1 = find_split_candidate (boxlist, numboxes, &which_axis, desired_colors);
-
- 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 split point along selected axis, and update box bounds.
- * Note that lb value is max for lower box, so must be < old max.
- */
- switch (which_axis)
- {
- case AXIS_RED:
- lb = b1->Rhalferror;/* *0 + (b1->Rmax + b1->Rmin) / 2; */
- b1->Rmax = lb;
- b2->Rmin = lb+1;
- g_assert (b1->Rmax >= b1->Rmin);
- g_assert (b2->Rmax >= b2->Rmin);
- break;
- case AXIS_GREEN:
- lb = b1->Ghalferror;/* *0 + (b1->Gmax + b1->Gmin) / 2; */
- b1->Gmax = lb;
- b2->Gmin = lb+1;
- g_assert (b1->Gmax >= b1->Gmin);
- g_assert (b2->Gmax >= b2->Gmin);
- break;
- case AXIS_BLUE:
- lb = b1->Bhalferror;/* *0 + (b1->Bmax + b1->Bmin) / 2; */
- b1->Bmax = lb;
- b2->Bmin = lb+1;
- g_assert (b1->Bmax >= b1->Bmin);
- g_assert (b2->Bmax >= b2->Bmin);
- break;
- default:
- g_error ("Uh-oh.");
- }
- /* Update stats for boxes */
- numboxes++;
-
- if (progress && (numboxes % 16 == 0))
- gimp_progress_set_value (progress, (gdouble) numboxes / desired_colors);
-
- update_box_rgb (histogram, b1, desired_colors - numboxes);
- update_box_rgb (histogram, b2, desired_colors - numboxes);
- }
-
- return numboxes;
-}
-
-
-/* Compute representative color for a box, put it in colormap[icolor]
- */
-static void
-compute_color_gray (QuantizeObj *quantobj,
- CFHistogram histogram,
- boxptr boxp,
- int icolor)
-{
- gint i, min, max;
- guint64 count;
- guint64 total;
- guint64 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;
- }
- }
-
- if (total != 0)
- {
- quantobj->cmap[icolor].red =
- quantobj->cmap[icolor].green =
- quantobj->cmap[icolor].blue = (gtotal + (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 colormap.
- */
- quantobj->cmap[icolor].red =
- quantobj->cmap[icolor].green =
- quantobj->cmap[icolor].blue = 0;
- }
-}
-
-
-/* Compute representative color for a box, put it in colormap[icolor]
- */
-static void
-compute_color_rgb (QuantizeObj *quantobj,
- CFHistogram histogram,
- boxptr boxp,
- int icolor)
-{
- /* Current algorithm: mean weighted by pixels (not colors) */
- /* Note it is important to get the rounding correct! */
- gint R, G, B;
- gint Rmin, Rmax;
- gint Gmin, Gmax;
- gint Bmin, Bmax;
- ColorFreq total = 0;
- ColorFreq Rtotal = 0;
- ColorFreq Gtotal = 0;
- ColorFreq 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++)
- {
- for (B = Bmin; B <= Bmax; B++)
- {
- ColorFreq this_freq = *HIST_LIN (histogram, R, G, B);
-
- if (this_freq != 0)
- {
- total += this_freq;
- Rtotal += R * this_freq;
- Gtotal += G * this_freq;
- Btotal += B * this_freq;
- }
- }
- }
-
- if (total > 0)
- {
- guchar red, green, blue;
-
- lin_to_rgb (/*(Rtotal + (total>>1)) / total,
- (Gtotal + (total>>1)) / total,
- (Btotal + (total>>1)) / total,*/
- (double)Rtotal / (double)total,
- (double)Gtotal / (double)total,
- (double)Btotal / (double)total,
- &red, &green, &blue);
-
- quantobj->cmap[icolor].red = red;
- quantobj->cmap[icolor].green = green;
- quantobj->cmap[icolor].blue = blue;
- }
- 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 colormap.
- */
- quantobj->cmap[icolor].red = 0;
- quantobj->cmap[icolor].green = 0;
- quantobj->cmap[icolor].blue = 0;
- }
-}
-
-
-/* Compute representative color for a box, put it in colormap[icolor]
- */
-static void
-compute_color_lin8 (QuantizeObj *quantobj,
- CFHistogram histogram,
- boxptr boxp,
- const gint icolor)
-{
- /* Current algorithm: mean weighted by pixels (not colors) */
- /* Note it is important to get the rounding correct! */
- gint R, G, B;
- gint Rmin, Rmax;
- gint Gmin, Gmax;
- gint Bmin, Bmax;
- ColorFreq total = 0;
- ColorFreq Rtotal = 0;
- ColorFreq Gtotal = 0;
- ColorFreq 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++)
- {
- for (B = Bmin; B <= Bmax; B++)
- {
- ColorFreq this_freq = *HIST_LIN (histogram, R, G, B);
-
- if (this_freq != 0)
- {
- Rtotal += R * this_freq;
- Gtotal += G * this_freq;
- Btotal += B * this_freq;
- total += this_freq;
- }
- }
- }
-
- if (total != 0)
- {
- quantobj->cmap[icolor].red = ((Rtotal << R_SHIFT) + (total>>1)) / total;
- quantobj->cmap[icolor].green = ((Gtotal << G_SHIFT) + (total>>1)) / total;
- quantobj->cmap[icolor].blue = ((Btotal << B_SHIFT) + (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 colormap.
- */
- g_warning ("eep.");
- quantobj->cmap[icolor].red = 0;
- quantobj->cmap[icolor].green = 128;
- quantobj->cmap[icolor].blue = 128;
- }
-}
-
-
-/* Master routine for color selection
- */
-static void
-select_colors_gray (QuantizeObj *quantobj,
- CFHistogram histogram)
-{
- boxptr boxlist;
- gint numboxes;
- gint desired = quantobj->desired_number_of_colors;
- gint i;
-
- /* Allocate workspace for box list */
- boxlist = g_new (box, desired);
-
- /* 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);
-}
-
-
-/* Master routine for color selection
- */
-static void
-select_colors_rgb (QuantizeObj *quantobj,
- CFHistogram histogram)
-{
- boxptr boxlist;
- gint numboxes;
- gint desired = quantobj->desired_number_of_colors;
- gint i;
-
- /* Allocate workspace for box list */
- boxlist = g_new (box, desired);
-
- /* Initialize one box containing whole space */
- numboxes = 1;
- boxlist[0].Rmin = 0;
- boxlist[0].Rmax = HIST_R_ELEMS - 1;
- boxlist[0].Gmin = 0;
- boxlist[0].Gmax = HIST_G_ELEMS - 1;
- boxlist[0].Bmin = 0;
- boxlist[0].Bmax = HIST_B_ELEMS - 1;
- /* Shrink it to actually-used volume and set its statistics */
- update_box_rgb (histogram, &boxlist[0], quantobj->desired_number_of_colors);
- /* Perform median-cut to produce final box list */
- numboxes = median_cut_rgb (histogram, boxlist, numboxes, desired,
- quantobj->progress);
-
- 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);
- }
-
- g_free (boxlist);
-}
-
-
-/*
- * 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
- * 256Kb 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/512th of the histogram (1/8th 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)*/
-
-/*adam*/
-#define BOX_R_LOG 0
-#define BOX_G_LOG 0
-#define BOX_B_LOG 0
-
-#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.
- */
-
-/* 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.
- */
-static gint
-find_nearby_colors (QuantizeObj *quantobj,
- int minR,
- int minG,
- int minB,
- int colorlist[])
-{
- 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) >> 1;
- maxG = minG + ((1 << BOX_G_SHIFT) - (1 << G_SHIFT));
- centerG = (minG + maxG + 1) >> 1;
- maxB = minB + ((1 << BOX_B_SHIFT) - (1 << B_SHIFT));
- centerB = (minB + maxB + 1) >> 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->clin[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->clin[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->clin[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;
-}
-
-
-/* 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.
- */
-static void
-find_best_colors (QuantizeObj *quantobj,
- gint minR,
- gint minG,
- gint minB,
- gint numcolors,
- gint colorlist[],
- gint bestcolor[])
-{
- gint iR, iG, iB;
- gint i, icolor;
- gint *bptr; /* pointer into bestdist[] array */
- gint *cptr; /* pointer into bestcolor[] array */
- gint dist0, dist1; /* initial distance values */
- gint dist2; /* current distance in inner loop */
- gint xx0, xx1; /* distance increments */
- gint xx2;
- gint inR, inG, inB; /* initial values for increments */
-
- /* This array holds the distance to the nearest-so-far color for each cell */
- gint bestdist[BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS] = { 0, };
-
- /* 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->clin[icolor].red) * R_SCALE;
- dist0 = inR*inR;
- /* special-case for L*==0: chroma diffs irrelevant */
- /* if (minR > 0 || quantobj->clin[icolor].red > 0) */
- {
- inG = (minG - quantobj->clin[icolor].green) * G_SCALE;
- dist0 += inG*inG;
- inB = (minB - quantobj->clin[icolor].blue) * B_SCALE;
- dist0 += inB*inB;
- }
- /* else
- {
- inG = 0;
- inB = 0;
- } */
- /* 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;
- }
- }
-}
-
-
-/* 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.)
- */
-static void
-fill_inverse_cmap_gray (QuantizeObj *quantobj,
- CFHistogram histogram,
- gint pixel)
-{
- Color *cmap = quantobj->cmap;
- glong mindist;
- gint mindisti;
- gint i;
-
- mindist = 65536;
- mindisti = -1;
-
- for (i = 0; i < quantobj->actual_number_of_colors; i++)
- {
- glong dist = ABS (pixel - cmap[i].red);
-
- if (dist < mindist)
- {
- mindist = dist;
- mindisti = i;
- }
- }
-
- if (i >= 0)
- histogram[pixel] = mindisti + 1;
-}
-
-
-/* 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.)
- */
-static void
-fill_inverse_cmap_rgb (QuantizeObj *quantobj,
- CFHistogram histogram,
- gint R,
- gint G,
- gint B)
-{
- gint minR, minG, minB; /* lower left corner of update box */
- gint iR, iG, iB;
- gint *cptr; /* pointer into bestcolor[] array */
- /* This array lists the candidate colormap indexes. */
- gint colorlist[MAXNUMCOLORS];
- gint numcolors; /* number of candidate colors */
- /* This array holds the actually closest colormap index for each cell. */
- gint bestcolor[BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS] = { 0, };
-
- /* 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++)
- {
- for (iB = 0; iB < BOX_B_ELEMS; iB++)
- {
- *HIST_LIN (histogram, R + iR, G + iG, B + iB) = (*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];
- }
-}
-
-static void
-custompal_pass1 (QuantizeObj *quantobj)
-{
- gint i;
- GList *list;
-
- /* fprintf(stderr,
- "custompal_pass1: using (theCustomPalette %s) from (file %s)\n",
- theCustomPalette->name, theCustomPalette->filename); */
-
- for (i = 0, list = gimp_palette_get_colors (quantobj->custom_palette);
- list;
- i++, list = g_list_next (list))
- {
- GimpPaletteEntry *entry = list->data;
- guchar r, g, b;
-
- gimp_rgb_get_uchar (&entry->color, &r, &g, &b);
-
- quantobj->cmap[i].red = (gint) r;
- quantobj->cmap[i].green = (gint) g;
- quantobj->cmap[i].blue = (gint) b;
- }
-
- quantobj -> actual_number_of_colors = i;
-}
-
-/*
- * Map some rows of pixels to the output colormapped representation.
- */
-
-static void
-median_cut_pass2_no_dither_gray (QuantizeObj *quantobj,
- GimpLayer *layer,
- GeglBuffer *new_buffer)
-{
- GeglBufferIterator *iter;
- CFHistogram histogram = quantobj->histogram;
- ColorFreq *cachep;
- const Babl *src_format;
- const Babl *dest_format;
- GeglRectangle *src_roi;
- gint src_bpp;
- gint dest_bpp;
- gint has_alpha;
- gulong *index_used_count = quantobj->index_used_count;
- gboolean alpha_dither = quantobj->want_alpha_dither;
- gint offsetx, offsety;
-
- gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
-
- src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
- dest_format = gegl_buffer_get_format (new_buffer);
-
- src_bpp = babl_format_get_bytes_per_pixel (src_format);
- dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
-
- has_alpha = babl_format_has_alpha (src_format);
-
- iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
- NULL, 0, NULL,
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
- src_roi = &iter->roi[0];
-
- gegl_buffer_iterator_add (iter, new_buffer,
- NULL, 0, NULL,
- GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
-
- while (gegl_buffer_iterator_next (iter))
- {
- const guchar *src = iter->data[0];
- guchar *dest = iter->data[1];
- gint row;
-
- for (row = 0; row < src_roi->height; row++)
- {
- gint col;
-
- for (col = 0; col < src_roi->width; col++)
- {
- /* get pixel value and index into the cache */
- gint pixel = src[GRAY];
-
- 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);
-
- if (has_alpha)
- {
- gboolean transparent = FALSE;
-
- if (alpha_dither)
- {
- gint dither_x = (col + offsetx + src_roi->x) & DM_WIDTHMASK;
- gint dither_y = (row + offsety + src_roi->y) & DM_HEIGHTMASK;
-
- if ((src[ALPHA_G]) < DM[dither_x][dither_y])
- transparent = TRUE;
- }
- else
- {
- if (src[ALPHA_G] <= 127)
- transparent = TRUE;
- }
-
- if (transparent)
- {
- dest[ALPHA_I] = 0;
- }
- else
- {
- dest[ALPHA_I] = 255;
- index_used_count[dest[INDEXED] = *cachep - 1]++;
- }
- }
- else
- {
- /* Now emit the colormap index for this cell */
- index_used_count[dest[INDEXED] = *cachep - 1]++;
- }
-
- src += src_bpp;
- dest += dest_bpp;
- }
- }
- }
-}
-
-static void
-median_cut_pass2_fixed_dither_gray (QuantizeObj *quantobj,
- GimpLayer *layer,
- GeglBuffer *new_buffer)
-{
- GeglBufferIterator *iter;
- CFHistogram histogram = quantobj->histogram;
- ColorFreq *cachep;
- const Babl *src_format;
- const Babl *dest_format;
- GeglRectangle *src_roi;
- gint src_bpp;
- gint dest_bpp;
- gboolean has_alpha;
- gint pixval1 = 0;
- gint pixval2 = 0;
- gint err1;
- gint err2;
- Color *color1;
- Color *color2;
- gulong *index_used_count = quantobj->index_used_count;
- gboolean alpha_dither = quantobj->want_alpha_dither;
- gint offsetx, offsety;
-
- gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
-
- src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
- dest_format = gegl_buffer_get_format (new_buffer);
-
- src_bpp = babl_format_get_bytes_per_pixel (src_format);
- dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
-
- has_alpha = babl_format_has_alpha (src_format);
-
- iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
- NULL, 0, NULL,
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
- src_roi = &iter->roi[0];
-
- gegl_buffer_iterator_add (iter, new_buffer,
- NULL, 0, NULL,
- GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
-
- while (gegl_buffer_iterator_next (iter))
- {
- const guchar *src = iter->data[0];
- guchar *dest = iter->data[1];
- gint row;
-
- for (row = 0; row < src_roi->height; row++)
- {
- gint col;
-
- for (col = 0; col < src_roi->width; col++)
- {
- gint pixel;
- const gint dmval =
- DM[(col + offsetx + src_roi->x) & DM_WIDTHMASK]
- [(row + offsety + src_roi->y) & DM_HEIGHTMASK];
-
- /* get pixel value and index into the cache */
- pixel = src[GRAY];
-
- 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);
-
- pixval1 = *cachep - 1;
- color1 = &quantobj->cmap[pixval1];
-
- if (quantobj->actual_number_of_colors > 2)
- {
- const int re = src[GRAY] - (int)color1->red;
- int RV = src[GRAY] + re;
-
- do
- {
- const gint R = CLAMP0255 (RV);
-
- cachep = &histogram[R];
- /* 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, R);
-
- pixval2 = *cachep - 1;
- RV += re;
- }
- while ((pixval1 == pixval2) &&
- (! (RV>255 || RV<0) ) &&
- re);
- }
- else
- {
- /* not enough colors to bother looking for an 'alternative'
- color (we may fail to do so anyway), so decide that
- the alternative color is simply the other cmap entry. */
- pixval2 = (pixval1 + 1) %
- (quantobj->actual_number_of_colors);
- }
-
- /* always deterministically sort pixval1 and pixval2, to
- avoid artifacts in the dither range due to inverting our
- relative color viewpoint -- most obvious in 1-bit dither. */
- if (pixval1 > pixval2)
- {
- gint tmpval = pixval1;
- pixval1 = pixval2;
- pixval2 = tmpval;
- color1 = &quantobj->cmap[pixval1];
- }
-
- color2 = &quantobj->cmap[pixval2];
-
- err1 = ABS(color1->red - src[GRAY]);
- err2 = ABS(color2->red - src[GRAY]);
- if (err1 || err2)
- {
- const int proportion2 = (256 * 255 * err2) / (err1 + err2);
-
- if ((dmval * 256) > proportion2)
- {
- pixval1 = pixval2; /* use color2 instead of color1*/
- }
- }
-
- if (has_alpha)
- {
- gboolean transparent = FALSE;
-
- if (alpha_dither)
- {
- if (src[ALPHA_G] < dmval)
- transparent = TRUE;
- }
- else
- {
- if (src[ALPHA_G] <= 127)
- transparent = TRUE;
- }
-
- if (transparent)
- {
- dest[ALPHA_I] = 0;
- }
- else
- {
- dest[ALPHA_I] = 255;
- index_used_count[dest[INDEXED] = pixval1]++;
- }
- }
- else
- {
- /* Now emit the colormap index for this cell, barfbarf */
- index_used_count[dest[INDEXED] = pixval1]++;
- }
-
- src += src_bpp;
- dest += dest_bpp;
- }
- }
- }
-}
-
-static void
-median_cut_pass2_no_dither_rgb (QuantizeObj *quantobj,
- GimpLayer *layer,
- GeglBuffer *new_buffer)
-{
- GeglBufferIterator *iter;
- CFHistogram histogram = quantobj->histogram;
- ColorFreq *cachep;
- const Babl *src_format;
- const Babl *dest_format;
- GeglRectangle *src_roi;
- gint src_bpp;
- gint dest_bpp;
- gint has_alpha;
- gint R, G, B;
- gint red_pix = RED;
- gint green_pix = GREEN;
- gint blue_pix = BLUE;
- gint alpha_pix = ALPHA;
- gboolean alpha_dither = quantobj->want_alpha_dither;
- gint offsetx, offsety;
- gulong *index_used_count = quantobj->index_used_count;
- glong total_size = 0;
- glong layer_size;
- gint count = 0;
- gint nth_layer = quantobj->nth_layer;
- gint n_layers = quantobj->n_layers;
-
- gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
-
- src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
- dest_format = gegl_buffer_get_format (new_buffer);
-
- src_bpp = babl_format_get_bytes_per_pixel (src_format);
- dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
-
- has_alpha = babl_format_has_alpha (src_format);
-
- /* In the case of web/mono palettes, we actually force
- * grayscale drawables through the rgb pass2 functions
- */
- if (gimp_drawable_is_gray (GIMP_DRAWABLE (layer)))
- {
- red_pix = green_pix = blue_pix = GRAY;
- alpha_pix = ALPHA_G;
- }
-
- iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
- NULL, 0, NULL,
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
- src_roi = &iter->roi[0];
-
- gegl_buffer_iterator_add (iter, new_buffer,
- NULL, 0, NULL,
- GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
-
- layer_size = (gimp_item_get_width (GIMP_ITEM (layer)) *
- gimp_item_get_height (GIMP_ITEM (layer)));
-
- while (gegl_buffer_iterator_next (iter))
- {
- const guchar *src = iter->data[0];
- guchar *dest = iter->data[1];
- gint row;
-
- total_size += src_roi->height * src_roi->width;
-
- for (row = 0; row < src_roi->height; row++)
- {
- gint col;
-
- for (col = 0; col < src_roi->width; col++)
- {
- if (has_alpha)
- {
- gboolean transparent = FALSE;
-
- if (alpha_dither)
- {
- gint dither_x = (col + offsetx + src_roi->x) & DM_WIDTHMASK;
- gint dither_y = (row + offsety + src_roi->y) & DM_HEIGHTMASK;
- if ((src[alpha_pix]) < DM[dither_x][dither_y])
- transparent = TRUE;
- }
- else
- {
- if (src[alpha_pix] <= 127)
- transparent = TRUE;
- }
-
- if (transparent)
- {
- dest[ALPHA_I] = 0;
- goto next_pixel;
- }
- else
- {
- dest[ALPHA_I] = 255;
- }
- }
-
- /* get pixel value and index into the cache */
- rgb_to_lin (src[red_pix], src[green_pix], src[blue_pix],
- &R, &G, &B);
-
- cachep = HIST_LIN (histogram, R, G, 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, barfbarf */
- index_used_count[dest[INDEXED] = *cachep - 1]++;
-
- next_pixel:
-
- src += src_bpp;
- dest += dest_bpp;
- }
- }
-
- if (quantobj->progress && (count % 16 == 0))
- gimp_progress_set_value (quantobj->progress,
- (nth_layer + ((gdouble) total_size)/
- layer_size) / (gdouble) n_layers);
- }
-}
-
-static void
-median_cut_pass2_fixed_dither_rgb (QuantizeObj *quantobj,
- GimpLayer *layer,
- GeglBuffer *new_buffer)
-{
- GeglBufferIterator *iter;
- CFHistogram histogram = quantobj->histogram;
- ColorFreq *cachep;
- const Babl *src_format;
- const Babl *dest_format;
- GeglRectangle *src_roi;
- gint src_bpp;
- gint dest_bpp;
- gint has_alpha;
- gint pixval1 = 0;
- gint pixval2 = 0;
- Color *color1;
- Color *color2;
- gint R, G, B;
- gint err1;
- gint err2;
- gint red_pix = RED;
- gint green_pix = GREEN;
- gint blue_pix = BLUE;
- gint alpha_pix = ALPHA;
- gboolean alpha_dither = quantobj->want_alpha_dither;
- gint offsetx, offsety;
- gulong *index_used_count = quantobj->index_used_count;
- glong total_size = 0;
- glong layer_size;
- gint count = 0;
- gint nth_layer = quantobj->nth_layer;
- gint n_layers = quantobj->n_layers;
-
- gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
-
- src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
- dest_format = gegl_buffer_get_format (new_buffer);
-
- src_bpp = babl_format_get_bytes_per_pixel (src_format);
- dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
-
- has_alpha = babl_format_has_alpha (src_format);
-
- /* In the case of web/mono palettes, we actually force
- * grayscale drawables through the rgb pass2 functions
- */
- if (gimp_drawable_is_gray (GIMP_DRAWABLE (layer)))
- {
- red_pix = green_pix = blue_pix = GRAY;
- alpha_pix = ALPHA_G;
- }
-
- iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
- NULL, 0, NULL,
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
- src_roi = &iter->roi[0];
-
- gegl_buffer_iterator_add (iter, new_buffer,
- NULL, 0, NULL,
- GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
-
- layer_size = (gimp_item_get_width (GIMP_ITEM (layer)) *
- gimp_item_get_height (GIMP_ITEM (layer)));
-
- while (gegl_buffer_iterator_next (iter))
- {
- const guchar *src = iter->data[0];
- guchar *dest = iter->data[1];
- gint row;
-
- total_size += src_roi->height * src_roi->width;
-
- for (row = 0; row < src_roi->height; row++)
- {
- gint col;
-
- for (col = 0; col < src_roi->width; col++)
- {
- const int dmval =
- DM[(col + offsetx + src_roi->x) & DM_WIDTHMASK]
- [(row + offsety + src_roi->y) & DM_HEIGHTMASK];
-
- if (has_alpha)
- {
- gboolean transparent = FALSE;
-
- if (alpha_dither)
- {
- if (src[alpha_pix] < dmval)
- transparent = TRUE;
- }
- else
- {
- if (src[alpha_pix] <= 127)
- transparent = TRUE;
- }
-
- if (transparent)
- {
- dest[ALPHA_I] = 0;
- goto next_pixel;
- }
- else
- {
- dest[ALPHA_I] = 255;
- }
- }
-
- /* get pixel value and index into the cache */
- rgb_to_lin (src[red_pix], src[green_pix], src[blue_pix],
- &R, &G, &B);
-
- cachep = HIST_LIN (histogram, R, G, 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);
-
- /* We now try to find a color which, when mixed in some
- * fashion with the closest match, yields something
- * closer to the desired color. We do this by
- * repeatedly extrapolating the color vector from one to
- * the other until we find another color cell. Then we
- * assess the distance of both mixer colors from the
- * intended color to determine their relative
- * probabilities of being chosen.
- */
- pixval1 = *cachep - 1;
- color1 = &quantobj->cmap[pixval1];
-
- if (quantobj->actual_number_of_colors > 2)
- {
- const gint re = src[red_pix] - (gint) color1->red;
- const gint ge = src[green_pix] - (gint) color1->green;
- const gint be = src[blue_pix] - (gint) color1->blue;
- gint RV = src[red_pix] + re;
- gint GV = src[green_pix] + ge;
- gint BV = src[blue_pix] + be;
-
- do
- {
- rgb_to_lin ((CLAMP0255(RV)),
- (CLAMP0255(GV)),
- (CLAMP0255(BV)),
- &R, &G, &B);
-
- cachep = HIST_LIN (histogram, R, G, 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);
-
- pixval2 = *cachep - 1;
- RV += re; GV += ge; BV += be;
- }
- while ((pixval1 == pixval2) &&
- (!( (RV>255 || RV<0) || (GV>255 || GV<0) || (BV>255 || BV<0) )) &&
- (re || ge || be));
- }
-
- if (quantobj->actual_number_of_colors <= 2
- /* || pixval1 == pixval2 */) {
- /* not enough colors to bother looking for an 'alternative'
- color (we may fail to do so anyway), so decide that
- the alternative color is simply the other cmap entry. */
- pixval2 = (pixval1 + 1) %
- (quantobj->actual_number_of_colors);
- }
-
- /* always deterministically sort pixval1 and pixval2, to
- avoid artifacts in the dither range due to inverting our
- relative color viewpoint -- most obvious in 1-bit dither. */
- if (pixval1 > pixval2)
- {
- gint tmpval = pixval1;
- pixval1 = pixval2;
- pixval2 = tmpval;
- color1 = &quantobj->cmap[pixval1];
- }
-
- color2 = &quantobj->cmap[pixval2];
-
- /* now figure out the relative probabilites of choosing
- either of our candidates. */
-#define DISTP(R1,G1,B1,R2,G2,B2,D) do {D = sqrt( 30*SQR((R1)-(R2)) + \
- 59*SQR((G1)-(G2)) + \
- 11*SQR((B1)-(B2)) ); }while(0)
-#define LIN_DISTP(R1,G1,B1,R2,G2,B2,D) do { \
- int spacer1, spaceg1, spaceb1; \
- int spacer2, spaceg2, spaceb2; \
- rgb_to_unshifted_lin (R1,G1,B1, &spacer1, &spaceg1, &spaceb1); \
- rgb_to_unshifted_lin (R2,G2,B2, &spacer2, &spaceg2, &spaceb2); \
- D = sqrt(R_SCALE * SQR((spacer1)-(spacer2)) + \
- G_SCALE * SQR((spaceg1)-(spaceg2)) + \
- B_SCALE * SQR((spaceb1)-(spaceb2))); \
- } while(0)
-
- /* although LIN_DISTP is more correct, DISTP is much faster and
- barely distinguishable. */
- DISTP (color1->red, color1->green, color1->blue,
- src[red_pix], src[green_pix], src[blue_pix],
- err1);
- DISTP (color2->red, color2->green, color2->blue,
- src[red_pix], src[green_pix], src[blue_pix],
- err2);
-
- if (err1 || err2)
- {
- const int proportion2 = (255 * err2) / (err1 + err2);
- if (dmval > proportion2)
- {
- pixval1 = pixval2; /* use color2 instead of color1*/
- }
- }
-
- /* Now emit the colormap index for this cell, barfbarf */
- index_used_count[dest[INDEXED] = pixval1]++;
-
- next_pixel:
-
- src += src_bpp;
- dest += dest_bpp;
- }
- }
-
- if (quantobj->progress && (count % 16 == 0))
- gimp_progress_set_value (quantobj->progress,
- (nth_layer + ((gdouble) total_size)/
- layer_size) / (gdouble) n_layers);
- }
-}
-
-static void
-median_cut_pass2_nodestruct_dither_rgb (QuantizeObj *quantobj,
- GimpLayer *layer,
- GeglBuffer *new_buffer)
-{
- GeglBufferIterator *iter;
- const Babl *src_format;
- const Babl *dest_format;
- GeglRectangle *src_roi;
- gint src_bpp;
- gint dest_bpp;
- gint has_alpha;
- gboolean alpha_dither = quantobj->want_alpha_dither;
- gint red_pix = RED;
- gint green_pix = GREEN;
- gint blue_pix = BLUE;
- gint alpha_pix = ALPHA;
- gint lastindex = 0;
- gint lastred = -1;
- gint lastgreen = -1;
- gint lastblue = -1;
- gint offsetx, offsety;
-
- gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
-
- src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
- dest_format = gegl_buffer_get_format (new_buffer);
-
- src_bpp = babl_format_get_bytes_per_pixel (src_format);
- dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
-
- has_alpha = babl_format_has_alpha (src_format);
-
- iter = gegl_buffer_iterator_new (gimp_drawable_get_buffer (GIMP_DRAWABLE (layer)),
- NULL, 0, NULL,
- GEGL_ACCESS_READ, GEGL_ABYSS_NONE);
- src_roi = &iter->roi[0];
-
- gegl_buffer_iterator_add (iter, new_buffer,
- NULL, 0, NULL,
- GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE);
-
- while (gegl_buffer_iterator_next (iter))
- {
- const guchar *src = iter->data[0];
- guchar *dest = iter->data[1];
- gint row;
-
- for (row = 0; row < src_roi->height; row++)
- {
- gint col;
-
- for (col = 0; col < src_roi->width; col++)
- {
- gboolean transparent = FALSE;
-
- if (has_alpha)
- {
- if (alpha_dither)
- {
- gint dither_x = (col + src_roi->x + offsetx) & DM_WIDTHMASK;
- gint dither_y = (row + src_roi->y + offsety) & DM_HEIGHTMASK;
-
- if ((src[alpha_pix]) < DM[dither_x][dither_y])
- transparent = TRUE;
- }
- else
- {
- if (src[alpha_pix] < 128)
- transparent = TRUE;
- }
- }
-
- if (! transparent)
- {
- if ((lastred == src[red_pix]) &&
- (lastgreen == src[green_pix]) &&
- (lastblue == src[blue_pix]))
- {
- /* same pixel color as last time */
- dest[INDEXED] = lastindex;
- if (has_alpha)
- dest[ALPHA_I] = 255;
- }
- else
- {
- gint i;
-
- for (i = 0 ;
- i < quantobj->actual_number_of_colors;
- i++)
- {
- if ((quantobj->cmap[i].green == src[green_pix]) &&
- (quantobj->cmap[i].red == src[red_pix]) &&
- (quantobj->cmap[i].blue == src[blue_pix]))
- {
- lastred = src[red_pix];
- lastgreen = src[green_pix];
- lastblue = src[blue_pix];
- lastindex = i;
-
- goto got_color;
- }
- }
- g_error ("Non-existant color was expected to "
- "be in non-destructive colormap.");
- got_color:
- dest[INDEXED] = lastindex;
- if (has_alpha)
- dest[ALPHA_I] = 255;
- }
- }
- else
- { /* have alpha, and transparent */
- dest[ALPHA_I] = 0;
- }
-
- src += src_bpp;
- dest += dest_bpp;
- }
- }
- }
-}
-
-
-/*
- * 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 gint *
-init_error_limit (const int error_freedom)
-/* Allocate and fill in the error_limiter table */
-{
- gint *table;
- gint in, out;
-
- /* #define STEPSIZE 16 */
- /* #define STEPSIZE 200 */
-
- table = g_new (gint, 255 * 2 + 1);
- table += 255; /* so we can index -255 ... +255 */
-
- if (error_freedom == 0)
- {
- /* Coarse function, much bleeding. */
-
- const gint STEPSIZE = 190;
-
- for (in = 0; in < STEPSIZE; in++)
- {
- table[in] = in;
- table[-in] = -in;
- }
-
- for (; in <= 255; in++)
- {
- table[in] = STEPSIZE;
- table[-in] = -STEPSIZE;
- }
-
- return (table);
- }
- else
- {
- /* Smooth function, bleeding more constrained */
-
- const gint STEPSIZE = 24;
-
- /* Map errors 1:1 up to +- STEPSIZE */
- out = 0;
- for (in = 0; in < STEPSIZE; in++, out++)
- {
- table[in] = out;
- table[-in] = -out;
- }
-
- /* Map errors 1:2 up to +- 3*STEPSIZE */
- for (; in < STEPSIZE*3; in++, out += (in&1) ? 0 : 1)
- {
- table[in] = out;
- table[-in] = -out;
- }
-
- /* Clamp the rest to final out value (which is STEPSIZE*2) */
- for (; in <= 255; in++)
- {
- table[in] = out;
- table[-in] = -out;
- }
-
- return table;
- }
-}
-
-
-/*
- * Map some rows of pixels to the output colormapped representation.
- * Perform floyd-steinberg dithering.
- */
-
-static void
-median_cut_pass2_fs_dither_gray (QuantizeObj *quantobj,
- GimpLayer *layer,
- GeglBuffer *new_buffer)
-{
- GeglBuffer *src_buffer;
- CFHistogram histogram = quantobj->histogram;
- ColorFreq *cachep;
- Color *color;
- gint *error_limiter;
- const gshort *fs_err1, *fs_err2;
- const gshort *fs_err3, *fs_err4;
- const guchar *range_limiter;
- const Babl *src_format;
- const Babl *dest_format;
- gint src_bpp;
- gint dest_bpp;
- guchar *src_buf, *dest_buf;
- gint *next_row, *prev_row;
- gint *nr, *pr;
- gint *tmp;
- gint pixel;
- gint pixele;
- gint row, col;
- gint index;
- gint step_dest, step_src;
- gint odd_row;
- gboolean has_alpha;
- gint offsetx, offsety;
- gboolean alpha_dither = quantobj->want_alpha_dither;
- gint width, height;
- gulong *index_used_count = quantobj->index_used_count;
-
- src_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
-
- gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
-
- src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
- dest_format = gegl_buffer_get_format (new_buffer);
-
- src_bpp = babl_format_get_bytes_per_pixel (src_format);
- dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
-
- has_alpha = babl_format_has_alpha (src_format);
-
- width = gimp_item_get_width (GIMP_ITEM (layer));
- height = gimp_item_get_height (GIMP_ITEM (layer));
-
- error_limiter = init_error_limit (quantobj->error_freedom);
- range_limiter = range_array + 256;
-
- src_buf = g_malloc (width * src_bpp);
- dest_buf = g_malloc (width * dest_bpp);
-
- next_row = g_new (gint, width + 2);
- prev_row = g_new0 (gint, width + 2);
-
- 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++)
- {
- const guchar *src;
- guchar *dest;
-
- gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, row, width, 1),
- 1.0, NULL, src_buf,
- GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
-
- src = src_buf;
- dest = dest_buf;
-
- nr = next_row;
- pr = prev_row + 1;
-
- if (odd_row)
- {
- step_dest = -dest_bpp;
- step_src = -src_bpp;
-
- src += (width * src_bpp) - src_bpp;
- dest += (width * dest_bpp) - dest_bpp;
-
- nr += width + 1;
- pr += width;
-
- *(nr - 1) = 0;
- }
- else
- {
- step_dest = dest_bpp;
- step_src = src_bpp;
-
- *(nr + 1) = 0;
- }
-
- *nr = 0;
-
- for (col = 0; col < width; col++)
- {
- pixel = range_limiter[src[GRAY] + 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);
-
- if (has_alpha)
- {
- gboolean transparent = FALSE;
-
- if (odd_row)
- {
- if (alpha_dither)
- {
- gint dither_x = ((width-col)+offsetx-1) & DM_WIDTHMASK;
- gint dither_y = (row+offsety) & DM_HEIGHTMASK;
-
- if ((src[ALPHA_G]) < DM[dither_x][dither_y])
- transparent = TRUE;
- }
- else
- {
- if (src[ALPHA_G] <= 127)
- transparent = TRUE;
- }
-
- if (transparent)
- {
- dest[ALPHA_I] = 0;
- pr--;
- nr--;
- *(nr - 1) = 0;
- goto next_pixel;
- }
- else
- {
- dest[ALPHA_I] = 255;
- }
- }
- else
- {
- if (alpha_dither)
- {
- gint dither_x = (col + offsetx) & DM_WIDTHMASK;
- gint dither_y = (row + offsety) & DM_HEIGHTMASK;
-
- if ((src[ALPHA_G]) < DM[dither_x][dither_y])
- transparent = TRUE;
- }
- else
- {
- if (src[ALPHA_G] <= 127)
- transparent = TRUE;
- }
-
- if (transparent)
- {
- dest[ALPHA_I] = 0;
- pr++;
- nr++;
- *(nr + 1) = 0;
- goto next_pixel;
- }
- else
- {
- dest[ALPHA_I] = 255;
- }
- }
- }
-
- index = *cachep - 1;
- index_used_count[dest[INDEXED] = index]++;
-
- 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];
- }
-
- next_pixel:
-
- dest += step_dest;
- src += step_src;
- }
-
- tmp = next_row;
- next_row = prev_row;
- prev_row = tmp;
-
- odd_row = !odd_row;
-
- gegl_buffer_set (new_buffer, GEGL_RECTANGLE (0, row, width, 1),
- 0, NULL, dest_buf,
- GEGL_AUTO_ROWSTRIDE);
- }
-
- g_free (error_limiter - 255); /* good lord. */
- g_free (next_row);
- g_free (prev_row);
- g_free (src_buf);
- g_free (dest_buf);
-}
-
-static void
-median_cut_pass2_rgb_init (QuantizeObj *quantobj)
-{
- int i;
-
- zero_histogram_rgb (quantobj->histogram);
-
- /* Mark all indices as currently unused */
- memset (quantobj->index_used_count, 0, 256 * sizeof (gulong));
-
- /* Make a version of our discovered colormap in linear space */
- for (i = 0; i < quantobj->actual_number_of_colors; i++)
- {
- rgb_to_unshifted_lin (quantobj->cmap[i].red,
- quantobj->cmap[i].green,
- quantobj->cmap[i].blue,
- &quantobj->clin[i].red,
- &quantobj->clin[i].green,
- &quantobj->clin[i].blue);
- }
-}
-
-static void
-median_cut_pass2_gray_init (QuantizeObj *quantobj)
-{
- zero_histogram_gray (quantobj->histogram);
-
- /* Mark all indices as currently unused */
- memset (quantobj->index_used_count, 0, 256 * sizeof (gulong));
-}
-
-static void
-median_cut_pass2_fs_dither_rgb (QuantizeObj *quantobj,
- GimpLayer *layer,
- GeglBuffer *new_buffer)
-{
- GeglBuffer *src_buffer;
- CFHistogram histogram = quantobj->histogram;
- ColorFreq *cachep;
- Color *color;
- gint *error_limiter;
- const gshort *fs_err1, *fs_err2;
- const gshort *fs_err3, *fs_err4;
- const guchar *range_limiter;
- const Babl *src_format;
- const Babl *dest_format;
- gint src_bpp;
- gint dest_bpp;
- guchar *src_buf, *dest_buf;
- gint *red_n_row, *red_p_row;
- gint *grn_n_row, *grn_p_row;
- gint *blu_n_row, *blu_p_row;
- gint *rnr, *rpr;
- gint *gnr, *gpr;
- gint *bnr, *bpr;
- gint *tmp;
- gint re, ge, be;
- gint row, col;
- gint index;
- gint step_dest, step_src;
- gint odd_row;
- gboolean has_alpha;
- gint width, height;
- gint red_pix = RED;
- gint green_pix = GREEN;
- gint blue_pix = BLUE;
- gint alpha_pix = ALPHA;
- gint offsetx, offsety;
- gboolean alpha_dither = quantobj->want_alpha_dither;
- gulong *index_used_count = quantobj->index_used_count;
- gint global_rmax = 0, global_rmin = G_MAXINT;
- gint global_gmax = 0, global_gmin = G_MAXINT;
- gint global_bmax = 0, global_bmin = G_MAXINT;
- gint nth_layer = quantobj->nth_layer;
- gint n_layers = quantobj->n_layers;
-
- src_buffer = gimp_drawable_get_buffer (GIMP_DRAWABLE (layer));
-
- gimp_item_get_offset (GIMP_ITEM (layer), &offsetx, &offsety);
-
- /* In the case of web/mono palettes, we actually force
- * grayscale drawables through the rgb pass2 functions
- */
- if (gimp_drawable_is_gray (GIMP_DRAWABLE (layer)))
- red_pix = green_pix = blue_pix = GRAY;
-
- src_format = gimp_drawable_get_format (GIMP_DRAWABLE (layer));
- dest_format = gegl_buffer_get_format (new_buffer);
-
- src_bpp = babl_format_get_bytes_per_pixel (src_format);
- dest_bpp = babl_format_get_bytes_per_pixel (dest_format);
-
- has_alpha = babl_format_has_alpha (src_format);
-
- width = gimp_item_get_width (GIMP_ITEM (layer));
- height = gimp_item_get_height (GIMP_ITEM (layer));
-
- error_limiter = init_error_limit (quantobj->error_freedom);
- range_limiter = range_array + 256;
-
- /* find the bounding box of the palette colors --
- we use this for hard-clamping our error-corrected
- values so that we can't continuously accelerate outside
- of our attainable gamut, which looks icky. */
- for (index = 0; index < quantobj->actual_number_of_colors; index++)
- {
- global_rmax = MAX(global_rmax, quantobj->clin[index].red);
- global_rmin = MIN(global_rmin, quantobj->clin[index].red);
- global_gmax = MAX(global_gmax, quantobj->clin[index].green);
- global_gmin = MIN(global_gmin, quantobj->clin[index].green);
- global_bmax = MAX(global_bmax, quantobj->clin[index].blue);
- global_bmin = MIN(global_bmin, quantobj->clin[index].blue);
- }
-
- src_buf = g_malloc (width * src_bpp);
- dest_buf = g_malloc (width * dest_bpp);
-
- red_n_row = g_new (gint, width + 2);
- red_p_row = g_new0 (gint, width + 2);
- grn_n_row = g_new (gint, width + 2);
- grn_p_row = g_new0 (gint, width + 2);
- blu_n_row = g_new (gint, width + 2);
- blu_p_row = g_new0 (gint, width + 2);
-
- 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++)
- {
- const guchar *src;
- guchar *dest;
-
- gegl_buffer_get (src_buffer, GEGL_RECTANGLE (0, row, width, 1),
- 1.0, NULL, src_buf,
- GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
-
- 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_bpp;
- step_src = -src_bpp;
-
- src += (width * src_bpp) - src_bpp;
- dest += (width * dest_bpp) - dest_bpp;
-
- 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_bpp;
- step_src = src_bpp;
-
- *(rnr + 1) = *(gnr + 1) = *(bnr + 1) = 0;
- }
-
- *rnr = *gnr = *bnr = 0;
-
- for (col = 0; col < width; col++)
- {
- if (has_alpha)
- {
- gboolean transparent = FALSE;
-
- if (odd_row)
- {
- if (alpha_dither)
- {
- gint dither_x = ((width-col)+offsetx-1) & DM_WIDTHMASK;
- gint dither_y = (row+offsety) & DM_HEIGHTMASK;
-
- if ((src[alpha_pix]) < DM[dither_x][dither_y])
- transparent = TRUE;
- }
- else
- {
- if (src[alpha_pix] <= 127)
- transparent = TRUE;
- }
-
- if (transparent)
- {
- dest[ALPHA_I] = 0;
- rpr--; gpr--; bpr--;
- rnr--; gnr--; bnr--;
- *(rnr - 1) = *(gnr - 1) = *(bnr - 1) = 0;
- goto next_pixel;
- }
- else
- {
- dest[ALPHA_I] = 255;
- }
- }
- else
- {
- if (alpha_dither)
- {
- gint dither_x = (col + offsetx) & DM_WIDTHMASK;
- gint dither_y = (row + offsety) & DM_HEIGHTMASK;
-
- if ((src[alpha_pix]) < DM[dither_x][dither_y])
- transparent = TRUE;
- }
- else
- {
- if (src[alpha_pix] <= 127)
- transparent = TRUE;
- }
-
- if (transparent)
- {
- dest[ALPHA_I] = 0;
- rpr++; gpr++; bpr++;
- rnr++; gnr++; bnr++;
- *(rnr + 1) = *(gnr + 1) = *(bnr + 1) = 0;
- goto next_pixel;
- }
- else
- {
- dest[ALPHA_I] = 255;
- }
- }
- }
-
-#if 0
- /* hmm. */
-
- 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;
-
- rgb_to_lin (r, g, b, &re, &ge, &be);
-#endif
- rgb_to_unshifted_lin (src[red_pix], src[green_pix], src[blue_pix],
- &re, &ge, &be);
-
- /*
- re = CLAMP(re, global_rmin, global_rmax);
- ge = CLAMP(ge, global_gmin, global_gmax);
- be = CLAMP(be, global_bmin, global_bmax);*/
-
- re = range_limiter[re + error_limiter[*rpr]];
- ge = range_limiter[ge + error_limiter[*gpr]];
- be = range_limiter[be + error_limiter[*bpr]];
-
- cachep = HIST_LIN (histogram,
- RSDF (re),
- GSDF (ge),
- BSDF (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,
- RSDF (re),
- GSDF (ge),
- BSDF (be));
-
- index = *cachep - 1;
- index_used_count[index]++;
- dest[INDEXED] = index;
-
- /*if (re > global_rmax)
- re = (re + 3*global_rmax) / 4;
- else if (re < global_rmin)
- re = (re + 3*global_rmin) / 4;*/
-
- /* We constrain chroma error extra-hard so that it
- doesn't run away and steal the thunder from the
- lightness error where all the detail usually is. */
- if (ge > global_gmax)
- ge = (ge + 3*global_gmax) / 4;
- else if (ge < global_gmin)
- ge = (ge + 3*global_gmin) / 4;
- if (be > global_bmax)
- be = (be + 3*global_bmax) / 4;
- else if (be < global_bmin)
- be = (be + 3*global_bmin) / 4;
-
- color = &quantobj->clin[index];
-
-#if 0
- if ((re > 0 && re < 255) /* HMM &&
- ge >= 0 && ge <= 255 &&
- be >= 0 && be <= 255*/)
- {
- ge = ge - color->green;
- be = be - color->blue;
- re = re - color->red;
- }
- else
- {
- /* color pretty much undefined now; nullify error. */
- re = ge = be = 0;
- }
-#endif
-
- if (re <= 0 || re >= 255)
- re = ge = be = 0;
- else
- {
- re = re - color->red;
- ge = ge - color->green;
- be = be - 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];
- }
-
- next_pixel:
-
- 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;
-
- gegl_buffer_set (new_buffer, GEGL_RECTANGLE (0, row, width, 1),
- 0, NULL, dest_buf,
- GEGL_AUTO_ROWSTRIDE);
-
- if (quantobj->progress && (row % 16 == 0))
- gimp_progress_set_value (quantobj->progress,
- (nth_layer + ((gdouble) row) /
- height) / (gdouble) n_layers);
- }
-
- 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);
-}
-
-
-void
-gimp_image_convert_type_set_dither_matrix (const guchar *matrix,
- gint width,
- gint height)
-{
- gint x;
- gint y;
-
- /* if matrix is invalid, restore the default matrix */
- if (matrix == NULL || width == 0 || height == 0)
- {
- matrix = (const guchar *) DM_ORIGINAL;
- width = DM_WIDTH;
- height = DM_HEIGHT;
- }
-
- g_return_if_fail ((DM_WIDTH % width) == 0);
- g_return_if_fail ((DM_HEIGHT % height) == 0);
-
- for (y = 0; y < DM_HEIGHT; y++)
- {
- for (x = 0; x < DM_WIDTH; x++)
- {
- DM[x][y] = matrix[((x % width) * height) + (y % height)];
- }
- }
-}
-
-
-/**************************************************************/
-static QuantizeObj *
-initialize_median_cut (GimpImageBaseType type,
- gint num_colors,
- GimpConvertDitherType dither_type,
- GimpConvertPaletteType palette_type,
- GimpPalette *custom_palette,
- gboolean want_alpha_dither,
- GimpProgress *progress)
-{
- QuantizeObj *quantobj;
-
- /* Initialize the data structures */
- quantobj = g_new (QuantizeObj, 1);
-
- if (type == GIMP_GRAY && palette_type == GIMP_MAKE_PALETTE)
- quantobj->histogram = g_new (ColorFreq, 256);
- else
- quantobj->histogram = g_new (ColorFreq,
- HIST_R_ELEMS * HIST_G_ELEMS * HIST_B_ELEMS);
-
- quantobj->custom_palette = custom_palette;
- quantobj->desired_number_of_colors = num_colors;
- quantobj->want_alpha_dither = want_alpha_dither;
- quantobj->progress = progress;
-
- switch (type)
- {
- case GIMP_GRAY:
- switch (palette_type)
- {
- case GIMP_MAKE_PALETTE:
- quantobj->first_pass = median_cut_pass1_gray;
- break;
- case GIMP_WEB_PALETTE:
- quantobj->first_pass = webpal_pass1;
- break;
- case GIMP_CUSTOM_PALETTE:
- quantobj->first_pass = custompal_pass1;
- needs_quantize = TRUE;
- break;
- case GIMP_MONO_PALETTE:
- default:
- quantobj->first_pass = monopal_pass1;
- }
-
- if (palette_type == GIMP_WEB_PALETTE ||
- palette_type == GIMP_CUSTOM_PALETTE)
- {
- switch (dither_type)
- {
- case GIMP_NODESTRUCT_DITHER:
- default:
- g_warning("Uh-oh, bad dither type, W1");
- case GIMP_NO_DITHER:
- quantobj->second_pass_init = median_cut_pass2_rgb_init;
- quantobj->second_pass = median_cut_pass2_no_dither_rgb;
- break;
- case GIMP_FS_DITHER:
- quantobj->error_freedom = 0;
- quantobj->second_pass_init = median_cut_pass2_rgb_init;
- quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
- break;
- case GIMP_FSLOWBLEED_DITHER:
- quantobj->error_freedom = 1;
- quantobj->second_pass_init = median_cut_pass2_rgb_init;
- quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
- break;
- case GIMP_FIXED_DITHER:
- quantobj->second_pass_init = median_cut_pass2_rgb_init;
- quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
- break;
- }
- }
- else
- {
- switch (dither_type)
- {
- case GIMP_NODESTRUCT_DITHER:
- default:
- g_warning("Uh-oh, bad dither type, W2");
- case GIMP_NO_DITHER:
- quantobj->second_pass_init = median_cut_pass2_gray_init;
- quantobj->second_pass = median_cut_pass2_no_dither_gray;
- break;
- case GIMP_FS_DITHER:
- quantobj->error_freedom = 0;
- quantobj->second_pass_init = median_cut_pass2_gray_init;
- quantobj->second_pass = median_cut_pass2_fs_dither_gray;
- break;
- case GIMP_FSLOWBLEED_DITHER:
- quantobj->error_freedom = 1;
- quantobj->second_pass_init = median_cut_pass2_gray_init;
- quantobj->second_pass = median_cut_pass2_fs_dither_gray;
- break;
- case GIMP_FIXED_DITHER:
- quantobj->second_pass_init = median_cut_pass2_gray_init;
- quantobj->second_pass = median_cut_pass2_fixed_dither_gray;
- break;
- }
- }
- break;
-
- case GIMP_RGB:
- switch (palette_type)
- {
- case GIMP_MAKE_PALETTE:
- quantobj->first_pass = median_cut_pass1_rgb;
- break;
- case GIMP_WEB_PALETTE:
- quantobj->first_pass = webpal_pass1;
- needs_quantize = TRUE;
- break;
- case GIMP_CUSTOM_PALETTE:
- quantobj->first_pass = custompal_pass1;
- needs_quantize = TRUE;
- break;
- case GIMP_MONO_PALETTE:
- default:
- quantobj->first_pass = monopal_pass1;
- }
-
- switch (dither_type)
- {
- case GIMP_NO_DITHER:
- quantobj->second_pass_init = median_cut_pass2_rgb_init;
- quantobj->second_pass = median_cut_pass2_no_dither_rgb;
- break;
- case GIMP_FS_DITHER:
- quantobj->error_freedom = 0;
- quantobj->second_pass_init = median_cut_pass2_rgb_init;
- quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
- break;
- case GIMP_FSLOWBLEED_DITHER:
- quantobj->error_freedom = 1;
- quantobj->second_pass_init = median_cut_pass2_rgb_init;
- quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
- break;
- case GIMP_NODESTRUCT_DITHER:
- quantobj->second_pass_init = NULL;
- quantobj->second_pass = median_cut_pass2_nodestruct_dither_rgb;
- break;
- case GIMP_FIXED_DITHER:
- quantobj->second_pass_init = median_cut_pass2_rgb_init;
- quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
- break;
- }
- break;
-
- default:
- break;
- }
-
- quantobj->delete_func = delete_median_cut;
-
- return quantobj;
-}
diff --git a/app/core/gimpimage-convert-type.h b/app/core/gimpimage-convert-type.h
index 69d4c2d..06250fb 100644
--- a/app/core/gimpimage-convert-type.h
+++ b/app/core/gimpimage-convert-type.h
@@ -19,27 +19,10 @@
#define __GIMP_IMAGE_CONVERT_TYPE_H__
-#define MAXNUMCOLORS 256
-
-
-gboolean gimp_image_convert_type (GimpImage *image,
- GimpImageBaseType new_type,
- /* The following params used only for
- * new_type == GIMP_INDEXED
- */
- gint num_cols,
- GimpConvertDitherType dither,
- gboolean alpha_dither,
- gboolean text_layer_dither,
- gboolean remove_dups,
- GimpConvertPaletteType palette_type,
- GimpPalette *custom_palette,
- GimpProgress *progress,
- GError **error);
-
-void gimp_image_convert_type_set_dither_matrix (const guchar *matrix,
- gint width,
- gint height);
+gboolean gimp_image_convert_type (GimpImage *image,
+ GimpImageBaseType new_type,
+ GimpProgress *progress,
+ GError **error);
#endif /* __GIMP_IMAGE_CONVERT_TYPE_H__ */
diff --git a/app/dialogs/convert-type-dialog.c b/app/dialogs/convert-type-dialog.c
index 75bd09a..f4f74b7 100644
--- a/app/dialogs/convert-type-dialog.c
+++ b/app/dialogs/convert-type-dialog.c
@@ -29,7 +29,7 @@
#include "core/gimpcontext.h"
#include "core/gimpdatafactory.h"
#include "core/gimpimage.h"
-#include "core/gimpimage-convert-type.h"
+#include "core/gimpimage-convert-indexed.h"
#include "core/gimplist.h"
#include "core/gimppalette.h"
#include "core/gimpprogress.h"
@@ -309,16 +309,15 @@ convert_dialog_response (GtkWidget *widget,
_("Converting to indexed colors"));
/* Convert the image to indexed color */
- if (! gimp_image_convert_type (dialog->image,
- GIMP_INDEXED,
- dialog->num_colors,
- dialog->dither_type,
- dialog->alpha_dither,
- dialog->text_layer_dither,
- dialog->remove_dups,
- dialog->palette_type,
- dialog->custom_palette,
- progress, &error))
+ if (! gimp_image_convert_indexed (dialog->image,
+ dialog->num_colors,
+ dialog->dither_type,
+ dialog->alpha_dither,
+ dialog->text_layer_dither,
+ dialog->remove_dups,
+ dialog->palette_type,
+ dialog->custom_palette,
+ progress, &error))
{
gimp_message_literal (dialog->image->gimp, G_OBJECT (dialog->dialog),
GIMP_MESSAGE_WARNING, error->message);
diff --git a/app/pdb/image-convert-cmds.c b/app/pdb/image-convert-cmds.c
index 80f23f5..197bbd2 100644
--- a/app/pdb/image-convert-cmds.c
+++ b/app/pdb/image-convert-cmds.c
@@ -28,6 +28,7 @@
#include "pdb-types.h"
#include "core/gimp.h"
+#include "core/gimpimage-convert-indexed.h"
#include "core/gimpimage-convert-precision.h"
#include "core/gimpimage-convert-type.h"
#include "core/gimpimage.h"
@@ -63,9 +64,7 @@ image_convert_rgb_invoker (GimpProcedure *procedure,
{
if (gimp_pdb_image_is_not_base_type (image, GIMP_RGB, error))
{
- success = gimp_image_convert_type (image, GIMP_RGB,
- 0, 0, FALSE, FALSE, FALSE, 0, NULL,
- NULL, error);
+ success = gimp_image_convert_type (image, GIMP_RGB, NULL, error);
}
else
{
@@ -94,9 +93,7 @@ image_convert_grayscale_invoker (GimpProcedure *procedure,
{
if (gimp_pdb_image_is_not_base_type (image, GIMP_GRAY, error))
{
- success = gimp_image_convert_type (image, GIMP_GRAY,
- 0, 0, FALSE, FALSE, FALSE, 0, NULL,
- NULL, error);
+ success = gimp_image_convert_type (image, GIMP_GRAY, NULL, error);
}
else
{
@@ -175,11 +172,11 @@ image_convert_indexed_invoker (GimpProcedure *procedure,
}
if (success)
- success = gimp_image_convert_type (image, GIMP_INDEXED,
- num_cols, dither_type,
- alpha_dither, FALSE, remove_unused,
- palette_type, pal,
- NULL, error);
+ success = gimp_image_convert_indexed (image,
+ num_cols, dither_type,
+ alpha_dither, FALSE, remove_unused,
+ palette_type, pal,
+ NULL, error);
}
return gimp_procedure_get_return_values (procedure, success,
@@ -209,7 +206,7 @@ image_convert_set_dither_matrix_invoker (GimpProcedure *procedure,
{
if (width == 0 || height == 0 || matrix_length == width * height)
{
- gimp_image_convert_type_set_dither_matrix (matrix, width, height);
+ gimp_image_convert_indexed_set_dither_matrix (matrix, width, height);
}
else
{
diff --git a/po/POTFILES.in b/po/POTFILES.in
index abbab92..7d615ac 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -141,6 +141,7 @@ app/core/gimpimage.c
app/core/gimpimage-arrange.c
app/core/gimpimage-color-profile.c
app/core/gimpimage-colormap.c
+app/core/gimpimage-convert-indexed.c
app/core/gimpimage-convert-precision.c
app/core/gimpimage-convert-type.c
app/core/gimpimage-crop.c
diff --git a/tools/pdbgen/pdb/image_convert.pdb b/tools/pdbgen/pdb/image_convert.pdb
index e0de7aa..77fcb90 100644
--- a/tools/pdbgen/pdb/image_convert.pdb
+++ b/tools/pdbgen/pdb/image_convert.pdb
@@ -37,9 +37,7 @@ HELP
{
if (gimp_pdb_image_is_not_base_type (image, GIMP_RGB, error))
{
- success = gimp_image_convert_type (image, GIMP_RGB,
- 0, 0, FALSE, FALSE, FALSE, 0, NULL,
- NULL, error);
+ success = gimp_image_convert_type (image, GIMP_RGB, NULL, error);
}
else
{
@@ -71,9 +69,7 @@ HELP
{
if (gimp_pdb_image_is_not_base_type (image, GIMP_GRAY, error))
{
- success = gimp_image_convert_type (image, GIMP_GRAY,
- 0, 0, FALSE, FALSE, FALSE, 0, NULL,
- NULL, error);
+ success = gimp_image_convert_type (image, GIMP_GRAY, NULL, error);
}
else
{
@@ -168,11 +164,11 @@ HELP
}
if (success)
- success = gimp_image_convert_type (image, GIMP_INDEXED,
- num_cols, dither_type,
- alpha_dither, FALSE, remove_unused,
- palette_type, pal,
- NULL, error);
+ success = gimp_image_convert_indexed (image,
+ num_cols, dither_type,
+ alpha_dither, FALSE, remove_unused,
+ palette_type, pal,
+ NULL, error);
}
CODE
);
@@ -205,7 +201,7 @@ HELP
{
if (width == 0 || height == 0 || matrix_length == width * height)
{
- gimp_image_convert_type_set_dither_matrix (matrix, width, height);
+ gimp_image_convert_indexed_set_dither_matrix (matrix, width, height);
}
else
{
@@ -259,6 +255,7 @@ CODE
@headers = qw("core/gimp.h"
"core/gimpimage.h"
+ "core/gimpimage-convert-indexed.h"
"core/gimpimage-convert-precision.h"
"core/gimpimage-convert-type.h"
"core/gimpitemstack.h"
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]