1997-11-25 06:05:25 +08:00
|
|
|
/* The GIMP -- an image manipulation program
|
|
|
|
* Copyright (C) 1995 Spencer Kimball and Peter Mattis
|
2002-02-11 02:33:16 +08:00
|
|
|
* Copyright (C) 1997-2002 Adam D. Moss <adam@gimp.org>
|
1997-11-25 06:05:25 +08:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
1998-04-13 13:44:11 +08:00
|
|
|
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
2002-02-11 02:33:16 +08:00
|
|
|
* 2002-02-10 - Quantizer version 3.0 (the rest of the commit started
|
|
|
|
* a year ago -- whoops). Divide colours within CIE L*a*b* space using
|
|
|
|
* CPercep module (cpercep.[ch]), colour-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 colour set, especially for low numbers of
|
|
|
|
* colours. n.b.: Less luminance-sloppy in straight remapping which is
|
|
|
|
* good for colour but a bit worse for high-frequency detail (that's
|
|
|
|
* partly what fs-dithering is for -- use it). [adam@gimp.org]
|
|
|
|
*
|
2001-03-26 02:41:54 +08:00
|
|
|
* 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 colourspace
|
|
|
|
* than frumpy old RGB. [Adam]
|
|
|
|
*
|
2000-01-30 07:06:06 +08:00
|
|
|
* 2000/01/30 - Use palette_selector instead of option_menu for custom
|
|
|
|
* palette. Use libgimp callback functions. [Sven]
|
|
|
|
*
|
1999-09-02 04:41:10 +08:00
|
|
|
* 99/09/01 - Created a low-bleed FS-dither option. [Adam]
|
|
|
|
*
|
1999-08-30 00:54:39 +08:00
|
|
|
* 99/08/29 - Deterministic colour 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]
|
|
|
|
*
|
1999-08-29 07:40:35 +08:00
|
|
|
* 99/08/28 - Deterministic alpha dithering over layers, reduced bleeding
|
|
|
|
* of transparent values into opaque values, added optional stage to
|
|
|
|
* remove duplicate or unused colour entries from final colourmap. [Adam]
|
|
|
|
*
|
1999-02-27 05:30:11 +08:00
|
|
|
* 99/02/24 - Many revisions to the box-cut quantizer used in RGB->INDEXED
|
|
|
|
* conversion. Box to be cut is chosen on the basis of posessing an axis
|
|
|
|
* with the largest sum of weighted perceptible error, rather than based on
|
|
|
|
* volume or population. The box is split along this axis rather than its
|
|
|
|
* longest axis, at the point of error mean rather than simply at its centre.
|
|
|
|
* Error-limiting in the F-S dither has been disabled - it may become optional
|
|
|
|
* again later. If you're convinced that you have an image where the old
|
|
|
|
* dither looks better, let me know. [Adam]
|
|
|
|
*
|
1999-01-11 07:36:29 +08:00
|
|
|
* 99/01/10 - Hourglass... [Adam]
|
|
|
|
*
|
1998-07-25 18:28:22 +08:00
|
|
|
* 98/07/25 - Convert-to-indexed now remembers the last invocation's
|
2002-02-11 02:33:16 +08:00
|
|
|
* settings. Also, GRAY->INDEXED is more flexible. [Adam]
|
1998-07-25 18:28:22 +08:00
|
|
|
*
|
1998-07-06 05:01:21 +08:00
|
|
|
* 98/07/05 - Sucked the warning about quantizing to too many colours into
|
|
|
|
* a text widget embedded in the dialog, improved intelligence of dialog
|
|
|
|
* to default 'custom palette' selection to 'Web' if available, and
|
|
|
|
* in this case not bother to present the native WWW-palette radio
|
|
|
|
* button. [Adam]
|
|
|
|
*
|
1998-04-13 22:26:46 +08:00
|
|
|
* 98/04/13 - avoid a division by zero when converting an empty gray-scale
|
|
|
|
* image (who would like to do such a thing anyway??) [Sven ]
|
|
|
|
*
|
1998-03-24 20:33:54 +08:00
|
|
|
* 98/03/23 - fixed a longstanding fencepost - hopefully the *right*
|
1998-07-06 05:01:21 +08:00
|
|
|
* way, *again*. [Adam]
|
1998-03-24 20:33:54 +08:00
|
|
|
*
|
1997-12-14 19:36:53 +08:00
|
|
|
* 97/11/14 - added a proper pdb interface and support for dithering
|
|
|
|
* to custom palettes (based on a patch by Eric Hernes) [Yosh]
|
|
|
|
*
|
1997-12-08 08:24:32 +08:00
|
|
|
* 97/11/04 - fixed the accidental use of the colour-counting case
|
|
|
|
* when palette_type is WEB or MONO. [Adam]
|
|
|
|
*
|
|
|
|
* 97/10/25 - colour-counting implemented (could use some hashing, but
|
|
|
|
* performance actually seems okay) - now RGB->INDEXED conversion isn't
|
|
|
|
* destructive if it doesn't have to be. [Adam]
|
|
|
|
*
|
1997-11-25 06:05:25 +08:00
|
|
|
* 97/10/14 - fixed divide-by-zero when converting a completely transparent
|
|
|
|
* RGB image to indexed. [Adam]
|
|
|
|
*
|
|
|
|
* 97/07/01 - started todo/revision log. Put code back in to
|
|
|
|
* eliminate full-alpha pixels from RGB histogram.
|
|
|
|
* [Adam D. Moss - adam@gimp.org]
|
|
|
|
*/
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/* 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
|
|
|
|
*/
|
|
|
|
|
app/appenv.h New file. Includes <math.h>. Move G_PI, RINT(), ROUND() etc
1999-09-01 Tor Lillqvist <tml@iki.fi>
* app/appenv.h
* libgimp/gimpmath.h: New file. Includes <math.h>. Move G_PI,
RINT(), ROUND() etc from app/appenv.h here, so plug-ins can
use them, too. Remove some commented-out old stuff in appenv.h.
* libgimp/gimp.h: Include gimpmath.h.
* libgimp/gimp.c (gimp_main): Win32: Don't install signal
handlers, we can't do anything useful in the handler ourselves
anyway (it would be nice to print out a backtrace, but that seems
pretty hard to do, even if not impossible). Let Windows inform the
user about the crash. If the plug-in was compiled with MSVC, and
the user also has it, she is offered a chance to start the
debugger automatically anyway.
* app/*several*.c: Include gimpmath.h for G_PI etc. Don't include
<math.h>, as gimpmath.h includes it.
* plug-ins/*/*many*.c: Include config.h. Don't include <math.h>.
Remove all the duplicated definitions of G_PI and rint(). Use
RINT() instead of rint().
* app/app_procs.[ch]: app_exit() takes a gboolean.
* app/batch.c
* app/commands.c
* app/interface.c: Call app_exit() with FALSE or TRUE.
* app/main.c (on_error): Call gimp_fatal_error. (main): Don't
install any signal handler on Win32 here, either.
* app/errors.c (gimp_fatal_error, gimp_terminate): Win32: Format
the message and call MessageBox with it. g_on_error_query doesn't
do anything useful on Win32, and printf'ing a message to stdout or
stderr doesn't do anything, either, in a windowing application.
1999-09-02 04:30:56 +08:00
|
|
|
#include "config.h"
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
#include <stdlib.h>
|
1999-10-27 02:27:27 +08:00
|
|
|
#include <stdio.h>
|
1997-11-25 06:05:25 +08:00
|
|
|
#include <string.h>
|
1998-03-24 20:33:54 +08:00
|
|
|
|
2001-08-14 22:53:55 +08:00
|
|
|
#include <glib-object.h>
|
2000-12-17 05:37:03 +08:00
|
|
|
|
2001-01-24 02:49:44 +08:00
|
|
|
#include "libgimpcolor/gimpcolor.h"
|
2001-01-24 07:56:18 +08:00
|
|
|
#include "libgimpmath/gimpmath.h"
|
2001-01-24 02:49:44 +08:00
|
|
|
|
2001-05-10 06:34:59 +08:00
|
|
|
#include "core-types.h"
|
2000-12-17 05:37:03 +08:00
|
|
|
|
2001-05-15 19:25:25 +08:00
|
|
|
#include "base/pixel-region.h"
|
|
|
|
#include "base/tile-manager.h"
|
|
|
|
|
removed the gimp_busy boolean, check whether user_installation is needed
2001-07-10 Michael Natterer <mitch@gimp.org>
* app/app_procs.[ch]: removed the gimp_busy boolean, check whether
user_installation is needed here, not in user_install.c, parse
gtkrc an friends only if(!no_interface), create the Gimp object
before parsing gimp's rc files an pas it to the parse functions,
many other cleanups.
* app/appenums.h: added MessageHandlerType and StackTraceMode.
* app/appenv.h: removed MessageHandlerType, declare all global
variables from main.c (no more hidden global stuff please).
* app/errors.[ch]: added the fatal message func here (from main.c),
removed the StackTraceMode enum.
* app/gimprc.[ch]: renamed functions to gimprc_*(), pass a Gimp
pointer to some functions.
* app/gimpunit.c
* app/unitrc.h: ok, this is ugly: renamed all functions to
_gimp_unit_*() and made them public. The unit list is part
of the Gimp object now, so pass a Gimp* to all functions.
* app/libgimp_glue.[ch]: added EEKy wrappers for all gimp_unit_*()
functions which are used by widgets.
* app/main.c: cleaned up the global variables, removed the fatal
message handler, call app_init() directly, not via the
user_install stuff, misc. cleanups.
* app/user_install.[ch]: removed the check if user_installation is
needed (done by app_procs.c now).
* app/core/gimp.[ch]: added the user_unit list and the "busy"
boolean. Moved gimp_[set|unset]_busy() here. Added
gimp_initialize() which is called after unitrc and gimprc are
parsed.
* app/batch.c
* app/colormaps.c
* app/devices.c
* app/disp_callbacks.c
* app/gdisplay_ops.c
* app/gimphelp.c
* app/module_db.c
* app/nav_window.c
* app/plug_in.c
* app/core/gimpcontext.c
* app/core/gimpdatafiles.c
* app/core/gimpimage-convert.c
* app/core/gimpimage-duplicate.c
* app/core/gimpimage.c
* app/core/gimpparasite.c
* app/core/gimpparasitelist.h
* app/gui/file-open-dialog.c
* app/gui/gui.[ch]
* app/gui/info-dialog.c
* app/gui/info-window.c
* app/gui/preferences-dialog.c
* app/gui/session.c
* app/gui/tips-dialog.c
* app/gui/toolbox.c
* app/tools/gimpblendtool.c
* app/tools/gimpbucketfilltool.c
* app/tools/gimpcolorpickertool.c
* app/tools/gimpfuzzyselecttool.c
* app/tools/gimptransformtool.c
* app/tools/tool_manager.c
* app/widgets/gimpcolorpanel.c
* app/widgets/gimpcursor.c
* app/xcf/xcf-load.c
* app/xcf/xcf-save.c
* app/xcf/xcf.c
* tools/pdbgen/Makefile.am
* tools/pdbgen/app.pl
* tools/pdbgen/enums.pl
* tools/pdbgen/pdb/image.pdb
* tools/pdbgen/pdb/message.pdb
* tools/pdbgen/pdb/unit.pdb
* app/pdb/image_cmds.c
* app/pdb/message_cmds.c
* app/pdb/unit_cmds.c: changed accordingly, minor cleanups.
2001-07-11 03:16:16 +08:00
|
|
|
#include "gimp.h"
|
2001-01-30 01:54:02 +08:00
|
|
|
#include "gimpdrawable.h"
|
2000-12-29 23:22:01 +08:00
|
|
|
#include "gimpimage.h"
|
2001-11-28 11:08:03 +08:00
|
|
|
#include "gimpimage-projection.h"
|
2001-02-12 00:14:25 +08:00
|
|
|
#include "gimplist.h"
|
2001-02-02 02:44:22 +08:00
|
|
|
#include "gimplayer.h"
|
2001-01-30 01:54:02 +08:00
|
|
|
#include "gimppalette.h"
|
2001-05-16 00:42:08 +08:00
|
|
|
|
|
|
|
#include "floating_sel.h"
|
2001-01-30 01:54:02 +08:00
|
|
|
#include "undo.h"
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
#include "cpercep.h"
|
|
|
|
#include "gimpimage-convert-fsdither.h"
|
|
|
|
#include "gimpimage-convert-data.h"
|
|
|
|
#include "gimpimage-convert.h"
|
1999-08-29 07:40:35 +08:00
|
|
|
|
2001-03-26 02:41:54 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/* basic memory/quality tradeoff */
|
|
|
|
#define PRECISION_R 8
|
2001-03-26 02:41:54 +08:00
|
|
|
#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 MR (HIST_G_ELEMS*HIST_B_ELEMS)
|
|
|
|
#define MG HIST_B_ELEMS
|
|
|
|
|
|
|
|
#define BITS_IN_SAMPLE 8
|
|
|
|
|
|
|
|
#define R_SHIFT (BITS_IN_SAMPLE-PRECISION_R)
|
|
|
|
#define G_SHIFT (BITS_IN_SAMPLE-PRECISION_G)
|
|
|
|
#define B_SHIFT (BITS_IN_SAMPLE-PRECISION_B)
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/* 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 */
|
2001-03-26 02:41:54 +08:00
|
|
|
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
typedef struct _Color Color;
|
|
|
|
typedef struct _QuantizeObj QuantizeObj;
|
|
|
|
typedef void (* Pass1_Func) (QuantizeObj *);
|
1999-08-29 07:40:35 +08:00
|
|
|
typedef void (* Pass2i_Func) (QuantizeObj *);
|
2001-01-29 07:25:25 +08:00
|
|
|
typedef void (* Pass2_Func) (QuantizeObj *, GimpLayer *, TileManager *);
|
1997-11-25 06:05:25 +08:00
|
|
|
typedef void (* Cleanup_Func) (QuantizeObj *);
|
|
|
|
typedef unsigned long ColorFreq;
|
2000-10-05 07:41:47 +08:00
|
|
|
typedef ColorFreq *CFHistogram;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
typedef enum {AXIS_UNDEF, AXIS_RED, AXIS_BLUE, AXIS_GREEN} axisType;
|
2001-03-26 02:41:54 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
typedef double etype;
|
2001-03-26 02:41:54 +08:00
|
|
|
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/*
|
|
|
|
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
|
2001-03-26 02:41:54 +08:00
|
|
|
*/
|
|
|
|
#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. */
|
|
|
|
/*
|
2002-02-11 02:33:16 +08:00
|
|
|
#define HIST_LIN(hist_ptr,r,g,b) (&(hist_ptr)[REF_FUNC((r),(g),(b))])
|
2001-03-26 02:41:54 +08:00
|
|
|
*/
|
|
|
|
static inline
|
2002-02-11 02:33:16 +08:00
|
|
|
ColorFreq* HIST_LIN(ColorFreq *hist_ptr,
|
|
|
|
const int r, const int g, const int b)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
|
|
|
return (&(hist_ptr)[
|
|
|
|
REF_FUNC(r,g,b)
|
|
|
|
]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
#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 inline
|
|
|
|
void rgb_to_unshifted_lin(const unsigned char r,
|
|
|
|
const unsigned char g,
|
|
|
|
const unsigned char b,
|
|
|
|
int *hr, int *hg, int *hb)
|
|
|
|
{
|
|
|
|
double sL, sa, sb;
|
|
|
|
int or, og, ob;
|
|
|
|
|
|
|
|
rgb_to_space(r,g,b, &sL, &sa, &sb);
|
|
|
|
|
|
|
|
/* fprintf(stderr, " %d-%d-%d -> %0.3f,%0.3f,%0.3f ", r, g, b, sL, sa, sb);*/
|
|
|
|
|
|
|
|
or = RINT(sL * LRAT);
|
|
|
|
og = RINT((sa - LOWA) * ARAT);
|
|
|
|
ob = RINT((sb - LOWB) * BRAT);
|
|
|
|
|
|
|
|
/* fprintf(stderr, " %d-%d-%d ", or, og, ob); */
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
if (or < 0 || or > 255)
|
|
|
|
fprintf(stderr, " \007R%d ", or);
|
|
|
|
if (og < 0 || og > 255)
|
|
|
|
fprintf(stderr, " \007G%d ", og);
|
|
|
|
if (ob < 0 || ob > 255)
|
|
|
|
fprintf(stderr, " \007B%d ", ob);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
*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 unsigned char r,
|
|
|
|
const unsigned char g,
|
|
|
|
const unsigned char b,
|
|
|
|
int *hr, int *hg, int *hb)
|
|
|
|
{
|
|
|
|
int 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++)
|
|
|
|
{
|
|
|
|
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 int r, const int g, const int b)
|
|
|
|
{
|
|
|
|
int 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 double hr, const double hg, const double hb,
|
|
|
|
unsigned char *r,
|
|
|
|
unsigned char *g,
|
|
|
|
unsigned char *b)
|
|
|
|
{
|
|
|
|
double sr,sg,sb;
|
|
|
|
double ir,ig,ib;
|
|
|
|
|
|
|
|
/* fprintf(stderr, "%d.%d.%d ", hr,hg,hb); */
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
ir = (hr * ((double) (1 << R_SHIFT))) + (((double)(1<<R_SHIFT))*0.5);
|
|
|
|
ig = (hg * ((double) (1 << G_SHIFT))) + (((double)(1<<G_SHIFT))*0.5);
|
|
|
|
ib = (hb * ((double) (1 << B_SHIFT))) + (((double)(1<<B_SHIFT))*0.5);
|
|
|
|
#else
|
|
|
|
/* w/ artificial widening of dynamic range */
|
|
|
|
ir = ((double)(hr)) * 255.0F / (double)(HIST_R_ELEMS-1);
|
|
|
|
ig = ((double)(hg)) * 255.0F / (double)(HIST_G_ELEMS-1);
|
|
|
|
ib = ((double)(hb)) * 255.0F / (double)(HIST_B_ELEMS-1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
ir = ir / LRAT;
|
|
|
|
ig = (ig / ARAT) + LOWA;
|
|
|
|
ib = (ib / BRAT) + LOWB;
|
|
|
|
|
|
|
|
/* fprintf(stderr, "%0.1f,%0.1f,%0.1f ", ir,ig,ib); */
|
|
|
|
|
|
|
|
space_to_rgb(ir, ig, ib,
|
|
|
|
&sr, &sg, &sb);
|
|
|
|
|
|
|
|
*r = RINT(CLAMP(sr, 0.0F, 255.0F));
|
|
|
|
*g = RINT(CLAMP(sg, 0.0F, 255.0F));
|
|
|
|
*b = RINT(CLAMP(sb, 0.0F, 255.0F));
|
|
|
|
|
|
|
|
/* fprintf(stderr, "%d,%d,%d ", *r, *g, *b); */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
struct _Color
|
|
|
|
{
|
|
|
|
int red;
|
|
|
|
int green;
|
|
|
|
int blue;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct _QuantizeObj
|
|
|
|
{
|
1999-08-29 07:40:35 +08:00
|
|
|
Pass1_Func first_pass; /* first pass over image data creates colormap */
|
2002-02-11 02:33:16 +08:00
|
|
|
Pass2i_Func second_pass_init; /* Initialize data which persists over invocations */
|
1999-08-29 07:40:35 +08:00
|
|
|
Pass2_Func second_pass; /* second pass maps from image data to colormap */
|
|
|
|
Cleanup_Func delete_func; /* function to clean up data associated with private */
|
|
|
|
|
|
|
|
int desired_number_of_colors; /* Number of colors we will allow */
|
|
|
|
int actual_number_of_colors; /* Number of colors actually needed */
|
|
|
|
Color cmap[256]; /* colormap created by quantization */
|
2002-02-11 02:33:16 +08:00
|
|
|
Color clin[256]; /* .. converted back to linear space */
|
2000-10-05 07:41:47 +08:00
|
|
|
gulong index_used_count[256]; /* how many times an index was used */
|
|
|
|
CFHistogram histogram; /* holds the histogram */
|
1999-08-29 07:40:35 +08:00
|
|
|
|
|
|
|
int want_alpha_dither;
|
1999-09-02 04:41:10 +08:00
|
|
|
int error_freedom; /* 0=much bleed, 1=controlled bleed */
|
1997-11-25 06:05:25 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
/* The bounds of the box (inclusive); expressed as histogram indexes */
|
|
|
|
int Rmin, Rmax;
|
1999-02-27 05:30:11 +08:00
|
|
|
int Rhalferror;
|
1997-11-25 06:05:25 +08:00
|
|
|
int Gmin, Gmax;
|
1999-02-27 05:30:11 +08:00
|
|
|
int Ghalferror;
|
1997-11-25 06:05:25 +08:00
|
|
|
int Bmin, Bmax;
|
1999-02-27 05:30:11 +08:00
|
|
|
int Bhalferror;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* The volume (actually 2-norm) of the box */
|
|
|
|
int volume;
|
|
|
|
|
|
|
|
/* The number of nonzero histogram cells within this box */
|
|
|
|
long colorcount;
|
1999-02-27 05:30:11 +08:00
|
|
|
|
|
|
|
/* The sum of the weighted error within this box */
|
|
|
|
guint64 error;
|
|
|
|
/* The sum of the unweighted error within this box */
|
|
|
|
guint64 rerror;
|
|
|
|
guint64 gerror;
|
|
|
|
guint64 berror;
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
} box, *boxptr;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
long ncolors;
|
|
|
|
long dither;
|
|
|
|
} Options;
|
|
|
|
|
2001-04-19 01:57:10 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
|
2000-10-05 07:41:47 +08:00
|
|
|
static void zero_histogram_gray (CFHistogram);
|
|
|
|
static void zero_histogram_rgb (CFHistogram);
|
2001-01-29 07:25:25 +08:00
|
|
|
static void generate_histogram_gray (CFHistogram, GimpLayer *, int alpha_dither);
|
|
|
|
static void generate_histogram_rgb (CFHistogram, GimpLayer *, int col_limit, int alpha_dither);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-09-01 05:26:34 +08:00
|
|
|
static QuantizeObj* initialize_median_cut (int, int, ConvertDitherType,
|
|
|
|
ConvertPaletteType, int);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-02-27 05:30:11 +08:00
|
|
|
static void
|
2002-02-11 02:33:16 +08:00
|
|
|
compute_color_lin8 (QuantizeObj *quantobj,
|
|
|
|
CFHistogram histogram,
|
|
|
|
boxptr boxp,
|
|
|
|
const int icolor);
|
|
|
|
|
1999-02-27 05:30:11 +08:00
|
|
|
|
2001-04-19 01:57:10 +08:00
|
|
|
static guchar found_cols[MAXNUMCOLORS][3];
|
|
|
|
static gint num_found_cols;
|
|
|
|
static gboolean needs_quantize;
|
2000-01-29 00:47:57 +08:00
|
|
|
|
2001-04-19 01:57:10 +08:00
|
|
|
static GimpPalette *theCustomPalette = NULL;
|
2000-01-29 00:47:57 +08:00
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
|
|
|
|
/**********************************************************/
|
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
signed long used_count;
|
|
|
|
unsigned char initial_index;
|
|
|
|
} palentryStruct;
|
|
|
|
|
|
|
|
static int
|
|
|
|
mapping_compare (const void *a, const void *b)
|
|
|
|
{
|
|
|
|
palentryStruct *m1 = (palentryStruct*)a;
|
|
|
|
palentryStruct *m2 = (palentryStruct*)b;
|
|
|
|
|
|
|
|
return (m2->used_count - m1->used_count);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* FWIW, the make_remap_table() and mapping_compare() function source
|
|
|
|
and palentryStruct may be re-used under the XFree86-style license.
|
|
|
|
<adam@gimp.org> */
|
|
|
|
static void
|
|
|
|
make_remap_table (const unsigned char old_palette[],
|
|
|
|
unsigned char new_palette[],
|
|
|
|
const unsigned long index_used_count[],
|
|
|
|
unsigned char remap_table[],
|
|
|
|
int* num_entries)
|
|
|
|
{
|
|
|
|
int i,j,k;
|
|
|
|
unsigned char temppal[256 * 3];
|
|
|
|
unsigned long tempuse[256];
|
|
|
|
unsigned long transmap[256];
|
|
|
|
palentryStruct *palentries;
|
|
|
|
int used = 0;
|
|
|
|
|
|
|
|
memset(temppal, 0, 256 * 3);
|
|
|
|
memset(tempuse, 0, 256 * sizeof(unsigned long));
|
|
|
|
memset(transmap, 255, 256 * sizeof(unsigned long));
|
|
|
|
|
|
|
|
/* 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_malloc(used * sizeof(palentryStruct));
|
|
|
|
for (i = 0; i < used; i++)
|
|
|
|
{
|
|
|
|
palentries[i].initial_index = i;
|
|
|
|
palentries[i].used_count = tempuse[i];
|
|
|
|
}
|
|
|
|
qsort(palentries, used, sizeof(palentryStruct),
|
|
|
|
&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;
|
1999-09-15 04:02:23 +08:00
|
|
|
break;
|
1999-08-29 07:40:35 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
2001-01-29 07:25:25 +08:00
|
|
|
remap_indexed_layer (GimpLayer *layer,
|
1999-10-27 02:27:27 +08:00
|
|
|
unsigned char *remap_table,
|
|
|
|
int num_entries)
|
1999-08-29 07:40:35 +08:00
|
|
|
{
|
|
|
|
PixelRegion srcPR, destPR;
|
|
|
|
void* pr;
|
|
|
|
int has_alpha;
|
|
|
|
int pixels;
|
|
|
|
unsigned char* src;
|
|
|
|
unsigned char* dest;
|
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)) ? 1 : 0;
|
1999-08-29 07:40:35 +08:00
|
|
|
pixel_region_init (&srcPR,
|
|
|
|
GIMP_DRAWABLE(layer)->tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height,
|
|
|
|
FALSE);
|
|
|
|
pixel_region_init (&destPR,
|
|
|
|
GIMP_DRAWABLE(layer)->tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height,
|
|
|
|
TRUE);
|
|
|
|
|
|
|
|
for (pr = pixel_regions_register (2, &srcPR, &destPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
|
|
|
{
|
|
|
|
src = srcPR.data;
|
|
|
|
dest = destPR.data;
|
|
|
|
pixels = srcPR.h * srcPR.w;
|
|
|
|
|
|
|
|
while (pixels--)
|
|
|
|
{
|
|
|
|
if ((!has_alpha) || (has_alpha && src[ALPHA_I_PIX]))
|
|
|
|
{
|
|
|
|
dest[INDEXED_PIX] = remap_table[src[INDEXED_PIX]];
|
|
|
|
}
|
|
|
|
|
|
|
|
src += srcPR.bytes;
|
|
|
|
dest += destPR.bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
void
|
2001-04-19 01:57:10 +08:00
|
|
|
gimp_image_convert (GimpImage *gimage,
|
|
|
|
GimpImageBaseType new_type,
|
|
|
|
/* The following three params used only for
|
2002-02-11 02:33:16 +08:00
|
|
|
* new_type == GIMP_INDEXED
|
2001-04-19 01:57:10 +08:00
|
|
|
*/
|
|
|
|
gint num_cols,
|
|
|
|
ConvertDitherType dither,
|
|
|
|
gint alpha_dither,
|
|
|
|
gint remdups,
|
|
|
|
ConvertPaletteType palette_type,
|
|
|
|
GimpPalette *custom_palette)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
QuantizeObj *quantobj;
|
2001-03-08 10:01:52 +08:00
|
|
|
GimpLayer *layer;
|
|
|
|
GimpLayer *floating_layer;
|
|
|
|
GimpImageBaseType old_type;
|
|
|
|
GList *list;
|
|
|
|
GimpImageType new_layer_type;
|
2002-02-11 02:33:16 +08:00
|
|
|
TileManager *new_tiles;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
g_return_if_fail (gimage != NULL);
|
2001-04-19 01:57:10 +08:00
|
|
|
g_return_if_fail (GIMP_IS_IMAGE (gimage));
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
quantobj = NULL;
|
|
|
|
|
2001-04-19 01:57:10 +08:00
|
|
|
theCustomPalette = custom_palette;
|
|
|
|
|
removed the gimp_busy boolean, check whether user_installation is needed
2001-07-10 Michael Natterer <mitch@gimp.org>
* app/app_procs.[ch]: removed the gimp_busy boolean, check whether
user_installation is needed here, not in user_install.c, parse
gtkrc an friends only if(!no_interface), create the Gimp object
before parsing gimp's rc files an pas it to the parse functions,
many other cleanups.
* app/appenums.h: added MessageHandlerType and StackTraceMode.
* app/appenv.h: removed MessageHandlerType, declare all global
variables from main.c (no more hidden global stuff please).
* app/errors.[ch]: added the fatal message func here (from main.c),
removed the StackTraceMode enum.
* app/gimprc.[ch]: renamed functions to gimprc_*(), pass a Gimp
pointer to some functions.
* app/gimpunit.c
* app/unitrc.h: ok, this is ugly: renamed all functions to
_gimp_unit_*() and made them public. The unit list is part
of the Gimp object now, so pass a Gimp* to all functions.
* app/libgimp_glue.[ch]: added EEKy wrappers for all gimp_unit_*()
functions which are used by widgets.
* app/main.c: cleaned up the global variables, removed the fatal
message handler, call app_init() directly, not via the
user_install stuff, misc. cleanups.
* app/user_install.[ch]: removed the check if user_installation is
needed (done by app_procs.c now).
* app/core/gimp.[ch]: added the user_unit list and the "busy"
boolean. Moved gimp_[set|unset]_busy() here. Added
gimp_initialize() which is called after unitrc and gimprc are
parsed.
* app/batch.c
* app/colormaps.c
* app/devices.c
* app/disp_callbacks.c
* app/gdisplay_ops.c
* app/gimphelp.c
* app/module_db.c
* app/nav_window.c
* app/plug_in.c
* app/core/gimpcontext.c
* app/core/gimpdatafiles.c
* app/core/gimpimage-convert.c
* app/core/gimpimage-duplicate.c
* app/core/gimpimage.c
* app/core/gimpparasite.c
* app/core/gimpparasitelist.h
* app/gui/file-open-dialog.c
* app/gui/gui.[ch]
* app/gui/info-dialog.c
* app/gui/info-window.c
* app/gui/preferences-dialog.c
* app/gui/session.c
* app/gui/tips-dialog.c
* app/gui/toolbox.c
* app/tools/gimpblendtool.c
* app/tools/gimpbucketfilltool.c
* app/tools/gimpcolorpickertool.c
* app/tools/gimpfuzzyselecttool.c
* app/tools/gimptransformtool.c
* app/tools/tool_manager.c
* app/widgets/gimpcolorpanel.c
* app/widgets/gimpcursor.c
* app/xcf/xcf-load.c
* app/xcf/xcf-save.c
* app/xcf/xcf.c
* tools/pdbgen/Makefile.am
* tools/pdbgen/app.pl
* tools/pdbgen/enums.pl
* tools/pdbgen/pdb/image.pdb
* tools/pdbgen/pdb/message.pdb
* tools/pdbgen/pdb/unit.pdb
* app/pdb/image_cmds.c
* app/pdb/message_cmds.c
* app/pdb/unit_cmds.c: changed accordingly, minor cleanups.
2001-07-11 03:16:16 +08:00
|
|
|
gimp_set_busy (gimage->gimp);
|
1999-01-11 07:36:29 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Get the floating layer if one exists */
|
2000-12-29 23:22:01 +08:00
|
|
|
floating_layer = gimp_image_floating_sel (gimage);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
undo_push_group_start (gimage, GIMAGE_MOD_UNDO);
|
|
|
|
|
|
|
|
/* Relax the floating selection */
|
|
|
|
if (floating_layer)
|
|
|
|
floating_sel_relax (floating_layer, TRUE);
|
|
|
|
|
|
|
|
/* Push the image size to the stack */
|
|
|
|
undo_push_gimage_mod (gimage);
|
|
|
|
|
|
|
|
/* Set the new base type */
|
|
|
|
old_type = gimage->base_type;
|
|
|
|
gimage->base_type = new_type;
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/* initialize the colour conversion routines */
|
|
|
|
init_conversions();
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Convert to indexed? Build histogram if necessary. */
|
2001-12-11 23:58:07 +08:00
|
|
|
if (new_type == GIMP_INDEXED)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-29 07:40:35 +08:00
|
|
|
int i;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/* fprintf(stderr, " TO INDEXED(%d) ", num_cols); */
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* don't dither if the input is grayscale and we are simply mapping every color */
|
2002-02-11 02:33:16 +08:00
|
|
|
if (old_type == GIMP_GRAY && num_cols == 256 && palette_type == MAKE_PALETTE)
|
1999-09-02 04:41:10 +08:00
|
|
|
dither = NO_DITHER;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-30 00:54:39 +08:00
|
|
|
quantobj = initialize_median_cut (old_type, num_cols, dither,
|
1999-08-29 07:40:35 +08:00
|
|
|
palette_type, alpha_dither);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
if (palette_type == MAKE_PALETTE)
|
|
|
|
{
|
2001-12-11 23:58:07 +08:00
|
|
|
if (old_type == GIMP_GRAY)
|
1997-11-25 06:05:25 +08:00
|
|
|
zero_histogram_gray (quantobj->histogram);
|
|
|
|
else
|
|
|
|
zero_histogram_rgb (quantobj->histogram);
|
|
|
|
|
|
|
|
/* To begin, assume that there are fewer colours in
|
|
|
|
* the image than the user actually asked for. In that
|
1999-08-29 07:40:35 +08:00
|
|
|
* case, we don't need to quantize or colour-dither.
|
1997-11-25 06:05:25 +08:00
|
|
|
*/
|
|
|
|
needs_quantize = FALSE;
|
|
|
|
num_found_cols = 0;
|
|
|
|
|
|
|
|
/* Build the histogram */
|
2001-02-19 21:06:09 +08:00
|
|
|
for (list = GIMP_LIST (gimage->layers)->list;
|
|
|
|
list;
|
|
|
|
list = g_list_next (list))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-01-29 07:25:25 +08:00
|
|
|
layer = (GimpLayer *) list->data;
|
2001-02-19 21:06:09 +08:00
|
|
|
|
2001-12-11 23:58:07 +08:00
|
|
|
if (old_type == GIMP_GRAY)
|
1999-08-29 07:40:35 +08:00
|
|
|
generate_histogram_gray (quantobj->histogram, layer, alpha_dither);
|
1997-11-25 06:05:25 +08:00
|
|
|
else
|
1999-08-29 07:40:35 +08:00
|
|
|
generate_histogram_rgb (quantobj->histogram, layer, num_cols, alpha_dither);
|
1997-12-08 08:24:32 +08:00
|
|
|
/*
|
|
|
|
* Note: generate_histogram_rgb may set needs_quantize if
|
|
|
|
* the image contains more colours than the limit specified
|
|
|
|
* by the user.
|
|
|
|
*/
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-12-08 08:24:32 +08:00
|
|
|
if (
|
2002-02-11 02:33:16 +08:00
|
|
|
(old_type == GIMP_RGB) &&
|
|
|
|
(!needs_quantize) &&
|
|
|
|
(palette_type == MAKE_PALETTE)
|
|
|
|
)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1997-12-08 08:24:32 +08:00
|
|
|
/* If this is an RGB image, and the user wanted a custom-built
|
|
|
|
* generated palette, and this image has no more colours than
|
|
|
|
* the user asked for, we don't need the first pass (quantization).
|
|
|
|
*
|
|
|
|
* There's also no point in dithering, since there's no error to
|
|
|
|
* spread. So we destroy the old quantobj and make a new one
|
|
|
|
* with the remapping function set to a special LUT-based
|
|
|
|
* no-dither remapper.
|
|
|
|
*/
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
quantobj->delete_func (quantobj);
|
|
|
|
quantobj = initialize_median_cut (old_type, num_cols,
|
1999-09-02 04:41:10 +08:00
|
|
|
NODESTRUCT_DITHER, palette_type,
|
1999-08-29 07:40:35 +08:00
|
|
|
alpha_dither);
|
1997-11-25 06:05:25 +08:00
|
|
|
/* 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);
|
|
|
|
}
|
1999-08-29 07:40:35 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
/* Initialise data which must persist across indexed layer iterations */
|
|
|
|
switch (new_type)
|
|
|
|
{
|
2001-12-11 23:58:07 +08:00
|
|
|
case GIMP_INDEXED:
|
1999-08-29 07:40:35 +08:00
|
|
|
if (quantobj->second_pass_init)
|
|
|
|
(* quantobj->second_pass_init) (quantobj);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert all layers */
|
2001-02-19 21:06:09 +08:00
|
|
|
for (list = GIMP_LIST (gimage->layers)->list;
|
|
|
|
list;
|
|
|
|
list = g_list_next (list))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-01-29 07:25:25 +08:00
|
|
|
layer = (GimpLayer *) list->data;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-12-13 07:48:18 +08:00
|
|
|
new_layer_type = GIMP_IMAGE_TYPE_FROM_BASE_TYPE (new_type);
|
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
if (gimp_drawable_has_alpha (GIMP_DRAWABLE (layer)))
|
2001-12-13 07:48:18 +08:00
|
|
|
new_layer_type = GIMP_IMAGE_TYPE_WITH_ALPHA (new_layer_type);
|
2002-02-11 02:33:16 +08:00
|
|
|
|
2001-12-13 07:48:18 +08:00
|
|
|
new_tiles = tile_manager_new (GIMP_DRAWABLE (layer)->width,
|
|
|
|
GIMP_DRAWABLE (layer)->height,
|
|
|
|
GIMP_IMAGE_TYPE_BYTES (new_layer_type));
|
2002-02-11 02:33:16 +08:00
|
|
|
#if 0
|
|
|
|
has_alpha = gimp_layer_has_alpha (layer);
|
|
|
|
switch (new_type)
|
|
|
|
{
|
|
|
|
case GIMP_RGB:
|
|
|
|
new_layer_type = (has_alpha) ? RGBA_GIMAGE : RGB_GIMAGE;
|
|
|
|
new_layer_bytes = (has_alpha) ? 4 : 3;
|
|
|
|
break;
|
|
|
|
case GIMP_GRAY:
|
|
|
|
new_layer_type = (has_alpha) ? GRAYA_GIMAGE : GRAY_GIMAGE;
|
|
|
|
new_layer_bytes = (has_alpha) ? 2 : 1;
|
|
|
|
break;
|
|
|
|
case GIMP_INDEXED:
|
|
|
|
new_layer_type = (has_alpha) ? INDEXEDA_GIMAGE : INDEXED_GIMAGE;
|
|
|
|
new_layer_bytes = (has_alpha) ? 2 : 1;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
new_tiles = tile_manager_new (GIMP_DRAWABLE(layer)->width,
|
|
|
|
GIMP_DRAWABLE(layer)->height,
|
|
|
|
new_layer_bytes);
|
|
|
|
#endif
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
switch (new_type)
|
|
|
|
{
|
2001-12-11 23:58:07 +08:00
|
|
|
case GIMP_RGB:
|
2001-12-13 07:48:18 +08:00
|
|
|
gimp_drawable_convert_rgb (GIMP_DRAWABLE (layer),
|
2002-02-11 02:33:16 +08:00
|
|
|
new_tiles,
|
|
|
|
old_type);
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
2001-12-11 23:58:07 +08:00
|
|
|
case GIMP_GRAY:
|
2001-12-13 07:48:18 +08:00
|
|
|
gimp_drawable_convert_grayscale (GIMP_DRAWABLE (layer),
|
2002-02-11 02:33:16 +08:00
|
|
|
new_tiles,
|
|
|
|
old_type);
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
2001-12-11 23:58:07 +08:00
|
|
|
case GIMP_INDEXED:
|
2001-12-13 07:48:18 +08:00
|
|
|
(* quantobj->second_pass) (quantobj,
|
2002-02-11 02:33:16 +08:00
|
|
|
layer,
|
|
|
|
new_tiles);
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/* Push the layer onto the undo stack */
|
1997-11-25 06:05:25 +08:00
|
|
|
undo_push_layer_mod (gimage, layer);
|
|
|
|
|
2000-12-14 02:53:35 +08:00
|
|
|
GIMP_DRAWABLE (layer)->tiles = new_tiles;
|
|
|
|
GIMP_DRAWABLE (layer)->type = new_layer_type;
|
2001-12-13 07:48:18 +08:00
|
|
|
GIMP_DRAWABLE (layer)->bytes = GIMP_IMAGE_TYPE_BYTES (new_layer_type);
|
2000-12-14 02:53:35 +08:00
|
|
|
GIMP_DRAWABLE (layer)->has_alpha = GIMP_IMAGE_TYPE_HAS_ALPHA (new_layer_type);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
/* colourmap stuff */
|
2001-12-11 23:58:07 +08:00
|
|
|
if (new_type == GIMP_INDEXED)
|
1999-08-29 07:40:35 +08:00
|
|
|
{
|
|
|
|
if (gimage->cmap)
|
|
|
|
g_free (gimage->cmap);
|
|
|
|
gimage->cmap = (unsigned char *) g_malloc (COLORMAP_SIZE);
|
|
|
|
|
|
|
|
if (remdups &&
|
|
|
|
((palette_type == WEB_PALETTE) || (palette_type == CUSTOM_PALETTE)))
|
|
|
|
{
|
|
|
|
int i,j;
|
|
|
|
unsigned char old_palette [256 * 3];
|
|
|
|
unsigned char new_palette [256 * 3];
|
|
|
|
unsigned char remap_table [256];
|
|
|
|
int 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;
|
|
|
|
|
|
|
|
#if 1
|
|
|
|
/* Generate a remapping table */
|
|
|
|
make_remap_table (old_palette, new_palette,
|
|
|
|
quantobj->index_used_count, remap_table, &num_entries);
|
|
|
|
|
|
|
|
/* Convert all layers */
|
2001-02-19 21:06:09 +08:00
|
|
|
for (list = GIMP_LIST (gimage->layers)->list;
|
|
|
|
list;
|
|
|
|
list = g_list_next (list))
|
1999-08-29 07:40:35 +08:00
|
|
|
{
|
2001-01-29 07:25:25 +08:00
|
|
|
layer = (GimpLayer *) list->data;
|
1999-08-29 07:40:35 +08:00
|
|
|
|
|
|
|
remap_indexed_layer (layer, remap_table, num_entries);
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
memcpy(new_palette, old_palette, 256*3);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < num_entries; i++)
|
|
|
|
{
|
|
|
|
gimage->cmap[j] = new_palette[j]; j++;
|
|
|
|
gimage->cmap[j] = new_palette[j]; j++;
|
|
|
|
gimage->cmap[j] = new_palette[j]; j++;
|
|
|
|
}
|
|
|
|
gimage->num_cols = num_entries;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
int i,j;
|
|
|
|
|
|
|
|
for (i = 0, j = 0; i < quantobj->actual_number_of_colors; i++)
|
|
|
|
{
|
|
|
|
gimage->cmap[j++] = quantobj->cmap[i].red;
|
|
|
|
gimage->cmap[j++] = quantobj->cmap[i].green;
|
|
|
|
gimage->cmap[j++] = quantobj->cmap[i].blue;
|
|
|
|
}
|
|
|
|
gimage->num_cols = quantobj->actual_number_of_colors;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-12-18 10:34:43 +08:00
|
|
|
/* Delete the quantizer object, if there is one */
|
|
|
|
if (quantobj)
|
|
|
|
quantobj->delete_func (quantobj);
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Make sure the projection is up to date */
|
2001-11-28 11:08:03 +08:00
|
|
|
gimp_image_projection_allocate (gimage);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* Rigor the floating selection */
|
|
|
|
if (floating_layer)
|
|
|
|
floating_sel_rigor (floating_layer, TRUE);
|
|
|
|
|
|
|
|
undo_push_group_end (gimage);
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
#if 0 /* gone in cvs */
|
|
|
|
/* shrink wrap and update all views */
|
2001-11-01 05:18:57 +08:00
|
|
|
gimp_image_invalidate_layer_previews (gimage);
|
|
|
|
gimp_viewable_invalidate_preview (GIMP_VIEWABLE (gimage));
|
2002-02-11 02:33:16 +08:00
|
|
|
gdisplays_update_title (gimage);
|
|
|
|
gdisplays_update_full (gimage);
|
|
|
|
|
|
|
|
gimp_image_colormap_changed (gimage, -1);
|
|
|
|
#endif
|
|
|
|
|
2001-05-07 00:14:34 +08:00
|
|
|
gimp_image_mode_changed (gimage);
|
|
|
|
|
removed the gimp_busy boolean, check whether user_installation is needed
2001-07-10 Michael Natterer <mitch@gimp.org>
* app/app_procs.[ch]: removed the gimp_busy boolean, check whether
user_installation is needed here, not in user_install.c, parse
gtkrc an friends only if(!no_interface), create the Gimp object
before parsing gimp's rc files an pas it to the parse functions,
many other cleanups.
* app/appenums.h: added MessageHandlerType and StackTraceMode.
* app/appenv.h: removed MessageHandlerType, declare all global
variables from main.c (no more hidden global stuff please).
* app/errors.[ch]: added the fatal message func here (from main.c),
removed the StackTraceMode enum.
* app/gimprc.[ch]: renamed functions to gimprc_*(), pass a Gimp
pointer to some functions.
* app/gimpunit.c
* app/unitrc.h: ok, this is ugly: renamed all functions to
_gimp_unit_*() and made them public. The unit list is part
of the Gimp object now, so pass a Gimp* to all functions.
* app/libgimp_glue.[ch]: added EEKy wrappers for all gimp_unit_*()
functions which are used by widgets.
* app/main.c: cleaned up the global variables, removed the fatal
message handler, call app_init() directly, not via the
user_install stuff, misc. cleanups.
* app/user_install.[ch]: removed the check if user_installation is
needed (done by app_procs.c now).
* app/core/gimp.[ch]: added the user_unit list and the "busy"
boolean. Moved gimp_[set|unset]_busy() here. Added
gimp_initialize() which is called after unitrc and gimprc are
parsed.
* app/batch.c
* app/colormaps.c
* app/devices.c
* app/disp_callbacks.c
* app/gdisplay_ops.c
* app/gimphelp.c
* app/module_db.c
* app/nav_window.c
* app/plug_in.c
* app/core/gimpcontext.c
* app/core/gimpdatafiles.c
* app/core/gimpimage-convert.c
* app/core/gimpimage-duplicate.c
* app/core/gimpimage.c
* app/core/gimpparasite.c
* app/core/gimpparasitelist.h
* app/gui/file-open-dialog.c
* app/gui/gui.[ch]
* app/gui/info-dialog.c
* app/gui/info-window.c
* app/gui/preferences-dialog.c
* app/gui/session.c
* app/gui/tips-dialog.c
* app/gui/toolbox.c
* app/tools/gimpblendtool.c
* app/tools/gimpbucketfilltool.c
* app/tools/gimpcolorpickertool.c
* app/tools/gimpfuzzyselecttool.c
* app/tools/gimptransformtool.c
* app/tools/tool_manager.c
* app/widgets/gimpcolorpanel.c
* app/widgets/gimpcursor.c
* app/xcf/xcf-load.c
* app/xcf/xcf-save.c
* app/xcf/xcf.c
* tools/pdbgen/Makefile.am
* tools/pdbgen/app.pl
* tools/pdbgen/enums.pl
* tools/pdbgen/pdb/image.pdb
* tools/pdbgen/pdb/message.pdb
* tools/pdbgen/pdb/unit.pdb
* app/pdb/image_cmds.c
* app/pdb/message_cmds.c
* app/pdb/unit_cmds.c: changed accordingly, minor cleanups.
2001-07-11 03:16:16 +08:00
|
|
|
gimp_unset_busy (gimage->gimp);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
|
2001-12-13 07:48:18 +08:00
|
|
|
void
|
|
|
|
gimp_drawable_convert_rgb (GimpDrawable *drawable,
|
|
|
|
TileManager *new_tiles,
|
|
|
|
GimpImageBaseType old_base_type)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-12-13 07:48:18 +08:00
|
|
|
PixelRegion srcPR, destPR;
|
|
|
|
gint row, col;
|
|
|
|
gint offset;
|
|
|
|
gint has_alpha;
|
|
|
|
guchar *src, *s;
|
|
|
|
guchar *dest, *d;
|
|
|
|
guchar *cmap;
|
|
|
|
gpointer pr;
|
|
|
|
|
|
|
|
g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
|
|
|
|
g_return_if_fail (new_tiles != NULL);
|
|
|
|
|
|
|
|
has_alpha = gimp_drawable_has_alpha (drawable);
|
|
|
|
|
|
|
|
pixel_region_init (&srcPR, drawable->tiles,
|
|
|
|
0, 0,
|
|
|
|
drawable->width,
|
|
|
|
drawable->height,
|
|
|
|
FALSE);
|
|
|
|
pixel_region_init (&destPR, new_tiles,
|
|
|
|
0, 0,
|
|
|
|
drawable->width,
|
|
|
|
drawable->height,
|
|
|
|
TRUE);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
|
2001-12-13 07:48:18 +08:00
|
|
|
for (pr = pixel_regions_register (2, &srcPR, &destPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
src = srcPR.data;
|
|
|
|
dest = destPR.data;
|
|
|
|
|
2001-12-13 07:48:18 +08:00
|
|
|
switch (old_base_type)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-12-11 23:58:07 +08:00
|
|
|
case GIMP_GRAY:
|
1997-11-25 06:05:25 +08:00
|
|
|
for (row = 0; row < srcPR.h; row++)
|
|
|
|
{
|
|
|
|
s = src;
|
|
|
|
d = dest;
|
|
|
|
for (col = 0; col < srcPR.w; col++)
|
|
|
|
{
|
|
|
|
d[RED_PIX] = *s;
|
|
|
|
d[GREEN_PIX] = *s;
|
|
|
|
d[BLUE_PIX] = *s;
|
|
|
|
|
|
|
|
d += 3;
|
|
|
|
s++;
|
|
|
|
if (has_alpha)
|
|
|
|
*d++ = *s++;
|
|
|
|
}
|
|
|
|
|
|
|
|
src += srcPR.rowstride;
|
|
|
|
dest += destPR.rowstride;
|
|
|
|
}
|
|
|
|
break;
|
2001-12-11 23:58:07 +08:00
|
|
|
case GIMP_INDEXED:
|
2001-12-13 07:48:18 +08:00
|
|
|
cmap = gimp_drawable_cmap (drawable);
|
1997-11-25 06:05:25 +08:00
|
|
|
for (row = 0; row < srcPR.h; row++)
|
|
|
|
{
|
|
|
|
s = src;
|
|
|
|
d = dest;
|
|
|
|
for (col = 0; col < srcPR.w; col++)
|
|
|
|
{
|
|
|
|
offset = *s++ * 3;
|
|
|
|
d[RED_PIX] = cmap[offset + 0];
|
|
|
|
d[GREEN_PIX] = cmap[offset + 1];
|
|
|
|
d[BLUE_PIX] = cmap[offset + 2];
|
|
|
|
|
|
|
|
d += 3;
|
|
|
|
if (has_alpha)
|
|
|
|
*d++ = *s++;
|
|
|
|
}
|
|
|
|
|
|
|
|
src += srcPR.rowstride;
|
|
|
|
dest += destPR.rowstride;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
|
|
|
|
|
2001-12-13 07:48:18 +08:00
|
|
|
void
|
|
|
|
gimp_drawable_convert_grayscale (GimpDrawable *drawable,
|
|
|
|
TileManager *new_tiles,
|
|
|
|
GimpImageBaseType old_base_type)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-12-13 07:48:18 +08:00
|
|
|
PixelRegion srcPR, destPR;
|
|
|
|
gint row, col;
|
|
|
|
gint offset, val;
|
|
|
|
gboolean has_alpha;
|
|
|
|
guchar *src, *s;
|
|
|
|
guchar *dest, *d;
|
|
|
|
guchar *cmap;
|
|
|
|
gpointer pr;
|
|
|
|
|
|
|
|
g_return_if_fail (GIMP_IS_DRAWABLE (drawable));
|
|
|
|
g_return_if_fail (new_tiles != NULL);
|
|
|
|
|
|
|
|
has_alpha = gimp_drawable_has_alpha (drawable);
|
|
|
|
|
|
|
|
pixel_region_init (&srcPR, drawable->tiles,
|
|
|
|
0, 0,
|
|
|
|
drawable->width,
|
|
|
|
drawable->height,
|
|
|
|
FALSE);
|
|
|
|
pixel_region_init (&destPR, new_tiles,
|
|
|
|
0, 0,
|
|
|
|
drawable->width,
|
|
|
|
drawable->height,
|
|
|
|
TRUE);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-12-13 07:48:18 +08:00
|
|
|
for (pr = pixel_regions_register (2, &srcPR, &destPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
src = srcPR.data;
|
|
|
|
dest = destPR.data;
|
|
|
|
|
2001-12-13 07:48:18 +08:00
|
|
|
switch (old_base_type)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-12-11 23:58:07 +08:00
|
|
|
case GIMP_RGB:
|
1997-11-25 06:05:25 +08:00
|
|
|
for (row = 0; row < srcPR.h; row++)
|
|
|
|
{
|
|
|
|
s = src;
|
|
|
|
d = dest;
|
|
|
|
for (col = 0; col < srcPR.w; col++)
|
|
|
|
{
|
|
|
|
val = INTENSITY (s[RED_PIX], s[GREEN_PIX], s[BLUE_PIX]);
|
2001-12-13 07:48:18 +08:00
|
|
|
*d++ = (guchar) val;
|
1997-11-25 06:05:25 +08:00
|
|
|
s += 3;
|
|
|
|
if (has_alpha)
|
|
|
|
*d++ = *s++;
|
|
|
|
}
|
|
|
|
|
|
|
|
src += srcPR.rowstride;
|
|
|
|
dest += destPR.rowstride;
|
|
|
|
}
|
|
|
|
break;
|
2001-12-11 23:58:07 +08:00
|
|
|
case GIMP_INDEXED:
|
2001-12-13 07:48:18 +08:00
|
|
|
cmap = gimp_drawable_cmap (drawable);
|
1997-11-25 06:05:25 +08:00
|
|
|
for (row = 0; row < srcPR.h; row++)
|
|
|
|
{
|
|
|
|
s = src;
|
|
|
|
d = dest;
|
|
|
|
for (col = 0; col < srcPR.w; col++)
|
|
|
|
{
|
|
|
|
offset = *s++ * 3;
|
|
|
|
val = INTENSITY (cmap[offset+0], cmap[offset+1], cmap[offset+2]);
|
2001-12-13 07:48:18 +08:00
|
|
|
*d++ = (guchar) val;
|
1997-11-25 06:05:25 +08:00
|
|
|
if (has_alpha)
|
|
|
|
*d++ = *s++;
|
|
|
|
}
|
|
|
|
|
|
|
|
src += srcPR.rowstride;
|
|
|
|
dest += destPR.rowstride;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Indexed color conversion machinery
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
2000-10-05 07:41:47 +08:00
|
|
|
zero_histogram_gray (CFHistogram histogram)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 256; i++)
|
|
|
|
histogram[i] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2000-10-05 07:41:47 +08:00
|
|
|
zero_histogram_rgb (CFHistogram histogram)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-03-26 02:41:54 +08:00
|
|
|
memset(histogram, 0,
|
|
|
|
HIST_R_ELEMS * HIST_G_ELEMS * HIST_B_ELEMS * sizeof(ColorFreq));
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2000-10-05 07:41:47 +08:00
|
|
|
generate_histogram_gray (CFHistogram histogram,
|
2001-01-29 07:25:25 +08:00
|
|
|
GimpLayer *layer,
|
2000-10-05 07:41:47 +08:00
|
|
|
int alpha_dither)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
PixelRegion srcPR;
|
|
|
|
unsigned char *data;
|
|
|
|
int size;
|
|
|
|
void *pr;
|
|
|
|
gboolean has_alpha;
|
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-01-22 15:02:57 +08:00
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
for (pr = pixel_regions_register (1, &srcPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
|
|
|
{
|
|
|
|
data = srcPR.data;
|
|
|
|
size = srcPR.w * srcPR.h;
|
|
|
|
while (size--)
|
|
|
|
{
|
|
|
|
histogram[*data] ++;
|
|
|
|
data += srcPR.bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2000-10-05 07:41:47 +08:00
|
|
|
generate_histogram_rgb (CFHistogram histogram,
|
2001-01-29 07:25:25 +08:00
|
|
|
GimpLayer *layer,
|
2000-10-05 07:41:47 +08:00
|
|
|
int col_limit,
|
|
|
|
int alpha_dither)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
PixelRegion srcPR;
|
|
|
|
unsigned char *data;
|
|
|
|
int size;
|
|
|
|
void *pr;
|
1999-08-29 07:40:35 +08:00
|
|
|
ColorFreq *colfreq;
|
1997-11-25 06:05:25 +08:00
|
|
|
gboolean has_alpha;
|
|
|
|
int nfc_iter;
|
1999-08-29 07:40:35 +08:00
|
|
|
int row, col, coledge;
|
|
|
|
int offsetx, offsety;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-01-15 05:11:52 +08:00
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE(layer), &offsetx, &offsety);
|
1999-08-29 07:40:35 +08:00
|
|
|
|
1998-05-28 17:03:57 +08:00
|
|
|
/* g_print ("col_limit = %d, nfc = %d\n", col_limit, num_found_cols);*/
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1998-03-24 20:33:54 +08:00
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width,
|
|
|
|
GIMP_DRAWABLE(layer)->height,
|
|
|
|
FALSE);
|
1997-11-25 06:05:25 +08:00
|
|
|
for (pr = pixel_regions_register (1, &srcPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
|
|
|
{
|
|
|
|
data = srcPR.data;
|
|
|
|
size = srcPR.w * srcPR.h;
|
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
/*fprintf(stderr, " [%d,%d - %d,%d]", srcPR.x, srcPR.y, offsetx, offsety);*/
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
if (needs_quantize)
|
|
|
|
{
|
1999-08-29 07:40:35 +08:00
|
|
|
if (alpha_dither)
|
|
|
|
{
|
|
|
|
/* if alpha-dithering, we need to be deterministic w.r.t. offsets */
|
|
|
|
col = srcPR.x + offsetx;
|
|
|
|
coledge = col + srcPR.w;
|
|
|
|
row = srcPR.y + offsety;
|
|
|
|
|
|
|
|
while (size--)
|
|
|
|
{
|
|
|
|
if (
|
|
|
|
(
|
|
|
|
has_alpha && (
|
|
|
|
(data[ALPHA_PIX] << 6) >
|
|
|
|
(255 * DM[col&DM_WIDTHMASK][row&DM_HEIGHTMASK])
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|| (!has_alpha))
|
|
|
|
{
|
2001-03-26 02:41:54 +08:00
|
|
|
colfreq = HIST_RGB(histogram,
|
2002-02-11 02:33:16 +08:00
|
|
|
data[RED_PIX],
|
|
|
|
data[GREEN_PIX],
|
|
|
|
data[BLUE_PIX]);
|
1999-08-29 07:40:35 +08:00
|
|
|
(*colfreq)++;
|
|
|
|
}
|
|
|
|
|
|
|
|
col++;
|
|
|
|
if (col == coledge)
|
|
|
|
{
|
|
|
|
col = srcPR.x + offsetx;
|
|
|
|
row++;
|
|
|
|
}
|
|
|
|
|
|
|
|
data += srcPR.bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-29 07:40:35 +08:00
|
|
|
while (size--)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-08-29 07:40:35 +08:00
|
|
|
if (
|
|
|
|
(
|
|
|
|
has_alpha && (
|
|
|
|
(data[ALPHA_PIX] > 127)
|
|
|
|
)
|
|
|
|
)
|
|
|
|
|| (!has_alpha))
|
|
|
|
{
|
2001-03-26 02:41:54 +08:00
|
|
|
colfreq = HIST_RGB(histogram,
|
2002-02-11 02:33:16 +08:00
|
|
|
data[RED_PIX],
|
|
|
|
data[GREEN_PIX],
|
|
|
|
data[BLUE_PIX]);
|
1999-08-29 07:40:35 +08:00
|
|
|
(*colfreq)++;
|
|
|
|
}
|
|
|
|
data += srcPR.bytes;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
1999-08-29 07:40:35 +08:00
|
|
|
/* if alpha-dithering, we need to be deterministic w.r.t. offsets */
|
|
|
|
col = srcPR.x + offsetx;
|
|
|
|
coledge = col + srcPR.w;
|
|
|
|
row = srcPR.y + offsety;
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
while (size--)
|
|
|
|
{
|
1999-08-29 07:40:35 +08:00
|
|
|
if ((has_alpha && (alpha_dither ?
|
|
|
|
((data[ALPHA_PIX] << 6) > (255 * DM[col&DM_WIDTHMASK][row&DM_HEIGHTMASK])) :
|
|
|
|
(data[ALPHA_PIX] > 127))) || (!has_alpha))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-03-26 02:41:54 +08:00
|
|
|
colfreq = HIST_RGB(histogram,
|
2002-02-11 02:33:16 +08:00
|
|
|
data[RED_PIX],
|
|
|
|
data[GREEN_PIX],
|
|
|
|
data[BLUE_PIX]);
|
1999-08-29 07:40:35 +08:00
|
|
|
(*colfreq)++;
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
if (!needs_quantize)
|
|
|
|
{
|
|
|
|
for (nfc_iter = 0;
|
|
|
|
nfc_iter < num_found_cols;
|
|
|
|
nfc_iter++)
|
|
|
|
{
|
|
|
|
if (
|
|
|
|
(data[RED_PIX] == found_cols[nfc_iter][0])
|
|
|
|
&&
|
|
|
|
(data[GREEN_PIX] == found_cols[nfc_iter][1])
|
|
|
|
&&
|
|
|
|
(data[BLUE_PIX] == found_cols[nfc_iter][2])
|
|
|
|
)
|
|
|
|
goto already_found;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Colour was not in the table of
|
|
|
|
* existing colours
|
|
|
|
*/
|
1999-08-29 07:40:35 +08:00
|
|
|
|
1998-03-24 20:33:54 +08:00
|
|
|
num_found_cols++;
|
1999-08-29 07:40:35 +08:00
|
|
|
|
1998-03-13 23:30:07 +08:00
|
|
|
if (num_found_cols > col_limit)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
/* There are more colours in the image
|
|
|
|
* than were allowed. We switch to plain
|
|
|
|
* histogram calculation with a view to
|
|
|
|
* quantizing at a later stage.
|
|
|
|
*/
|
|
|
|
needs_quantize = TRUE;
|
2001-11-16 20:23:01 +08:00
|
|
|
/* g_print ("\nmax colours exceeded - needs quantize.\n");*/
|
1997-11-25 06:05:25 +08:00
|
|
|
goto already_found;
|
|
|
|
}
|
1998-03-24 20:33:54 +08:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Remember the new colour we just found.
|
|
|
|
*/
|
|
|
|
found_cols[num_found_cols-1][0] = data[RED_PIX];
|
|
|
|
found_cols[num_found_cols-1][1] = data[GREEN_PIX];
|
|
|
|
found_cols[num_found_cols-1][2] = data[BLUE_PIX];
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
already_found:
|
1999-08-29 07:40:35 +08:00
|
|
|
|
|
|
|
col++;
|
|
|
|
if (col == coledge)
|
|
|
|
{
|
|
|
|
col = srcPR.x + offsetx;
|
|
|
|
row++;
|
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
data += srcPR.bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1999-08-29 07:40:35 +08:00
|
|
|
|
1998-05-28 17:03:57 +08:00
|
|
|
/* g_print ("O: col_limit = %d, nfc = %d\n", col_limit, num_found_cols);*/
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static boxptr
|
2002-02-11 02:33:16 +08:00
|
|
|
find_split_candidate (const boxptr boxlist,
|
|
|
|
const int numboxes,
|
|
|
|
axisType *which_axis,
|
|
|
|
const int desired_colors)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
boxptr boxp;
|
|
|
|
int i;
|
2002-02-11 02:33:16 +08:00
|
|
|
etype maxc = 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
boxptr which = NULL;
|
2002-02-11 02:33:16 +08:00
|
|
|
double Lbias;
|
|
|
|
|
|
|
|
*which_axis = AXIS_UNDEF;
|
|
|
|
|
|
|
|
/* we only perform the initial L-split bias /at all/ if the final
|
|
|
|
number of desired colours 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 colours */
|
|
|
|
Lbias = (numboxes > BIAS_NUMBER) ? 1.0F : ((double)(BIAS_NUMBER+1) -
|
|
|
|
((double)numboxes)) /
|
|
|
|
((double)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;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++)
|
|
|
|
{
|
1999-02-27 05:30:11 +08:00
|
|
|
if (boxp->volume > 0)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
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);
|
|
|
|
|
|
|
|
if (Lbias * rpe > maxc &&
|
|
|
|
boxp->Rmin < boxp->Rmax)
|
1999-02-27 05:30:11 +08:00
|
|
|
{
|
|
|
|
which = boxp;
|
2002-02-11 02:33:16 +08:00
|
|
|
maxc = Lbias * rpe;
|
|
|
|
*which_axis = AXIS_RED;
|
1999-02-27 05:30:11 +08:00
|
|
|
}
|
2002-02-11 02:33:16 +08:00
|
|
|
|
|
|
|
if (gpe > maxc &&
|
|
|
|
boxp->Gmin < boxp->Gmax)
|
1999-02-27 05:30:11 +08:00
|
|
|
{
|
|
|
|
which = boxp;
|
2002-02-11 02:33:16 +08:00
|
|
|
maxc = gpe;
|
|
|
|
*which_axis = AXIS_GREEN;
|
1999-02-27 05:30:11 +08:00
|
|
|
}
|
2002-02-11 02:33:16 +08:00
|
|
|
|
|
|
|
if (bpe > maxc &&
|
|
|
|
boxp->Bmin < boxp->Bmax)
|
1999-02-27 05:30:11 +08:00
|
|
|
{
|
|
|
|
which = boxp;
|
2002-02-11 02:33:16 +08:00
|
|
|
maxc = bpe;
|
|
|
|
*which_axis = AXIS_BLUE;
|
1999-02-27 05:30:11 +08:00
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/* fprintf(stderr, " %f,%p ", maxc, which); */
|
|
|
|
/* fprintf(stderr, " %llu ", maxc); */
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
return which;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-02-27 05:30:11 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static boxptr
|
2002-02-11 02:33:16 +08:00
|
|
|
find_biggest_volume (const boxptr boxlist,
|
|
|
|
const int numboxes)
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Find the splittable box with the largest (scaled) volume */
|
|
|
|
/* Returns NULL if no splittable boxes remain */
|
|
|
|
{
|
|
|
|
boxptr boxp;
|
|
|
|
int i;
|
|
|
|
int maxv = 0;
|
|
|
|
boxptr which = NULL;
|
|
|
|
|
|
|
|
for (i = 0, boxp = boxlist; i < numboxes; i++, boxp++)
|
|
|
|
{
|
|
|
|
if (boxp->volume > maxv)
|
|
|
|
{
|
|
|
|
which = boxp;
|
|
|
|
maxv = boxp->volume;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return which;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
2002-02-11 02:33:16 +08:00
|
|
|
update_box_gray (const CFHistogram histogram,
|
|
|
|
boxptr boxp)
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Shrink the min/max bounds of a box to enclose only nonzero elements, */
|
|
|
|
/* and recompute its volume and population */
|
|
|
|
{
|
|
|
|
int i, min, max, dist;
|
2001-03-26 02:41:54 +08:00
|
|
|
ColorFreq ccount;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void
|
2002-02-11 02:33:16 +08:00
|
|
|
update_box_rgb (const CFHistogram histogram,
|
|
|
|
boxptr boxp,
|
|
|
|
const int cells_remaining)
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Shrink the min/max bounds of a box to enclose only nonzero elements, */
|
1999-02-27 05:30:11 +08:00
|
|
|
/* and recompute its volume, population and error */
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
int R,G,B;
|
|
|
|
int Rmin,Rmax,Gmin,Gmax,Bmin,Bmax;
|
|
|
|
int dist0,dist1,dist2;
|
2001-03-26 02:41:54 +08:00
|
|
|
ColorFreq ccount;
|
2002-02-11 02:33:16 +08:00
|
|
|
/*
|
1999-02-27 05:30:11 +08:00
|
|
|
guint64 tempRerror;
|
|
|
|
guint64 tempGerror;
|
|
|
|
guint64 tempBerror;
|
2002-02-11 02:33:16 +08:00
|
|
|
*/
|
1999-02-27 05:30:11 +08:00
|
|
|
QuantizeObj dummyqo;
|
|
|
|
box dummybox;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/* fprintf(stderr, "U"); */
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
Rmin = boxp->Rmin; Rmax = boxp->Rmax;
|
|
|
|
Gmin = boxp->Gmin; Gmax = boxp->Gmax;
|
|
|
|
Bmin = boxp->Bmin; Bmax = boxp->Bmax;
|
|
|
|
|
|
|
|
if (Rmax > Rmin)
|
|
|
|
for (R = Rmin; R <= Rmax; R++)
|
|
|
|
for (G = Gmin; G <= Gmax; G++)
|
|
|
|
{
|
|
|
|
for (B = Bmin; B <= Bmax; B++)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
if (*HIST_LIN(histogram, R, G, B) != 0)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
|
|
|
boxp->Rmin = Rmin = R;
|
|
|
|
goto have_Rmin;
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
have_Rmin:
|
|
|
|
if (Rmax > Rmin)
|
|
|
|
for (R = Rmax; R >= Rmin; R--)
|
|
|
|
for (G = Gmin; G <= Gmax; G++)
|
|
|
|
{
|
|
|
|
for (B = Bmin; B <= Bmax; B++)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
if (*HIST_LIN(histogram, R, G, B) != 0)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
|
|
|
boxp->Rmax = Rmax = R;
|
|
|
|
goto have_Rmax;
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
have_Rmax:
|
|
|
|
if (Gmax > Gmin)
|
|
|
|
for (G = Gmin; G <= Gmax; G++)
|
|
|
|
for (R = Rmin; R <= Rmax; R++)
|
|
|
|
{
|
|
|
|
for (B = Bmin; B <= Bmax; B++)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
if (*HIST_LIN(histogram, R, G, B) != 0)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
|
|
|
boxp->Gmin = Gmin = G;
|
|
|
|
goto have_Gmin;
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
have_Gmin:
|
|
|
|
if (Gmax > Gmin)
|
|
|
|
for (G = Gmax; G >= Gmin; G--)
|
|
|
|
for (R = Rmin; R <= Rmax; R++)
|
|
|
|
{
|
|
|
|
for (B = Bmin; B <= Bmax; B++)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
if (*HIST_LIN(histogram, R, G, B) != 0)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
|
|
|
boxp->Gmax = Gmax = G;
|
|
|
|
goto have_Gmax;
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
have_Gmax:
|
|
|
|
if (Bmax > Bmin)
|
|
|
|
for (B = Bmin; B <= Bmax; B++)
|
|
|
|
for (R = Rmin; R <= Rmax; R++)
|
|
|
|
{
|
2001-03-26 02:41:54 +08:00
|
|
|
for (G = Gmin; G <= Gmax; G++)
|
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
if (*HIST_LIN(histogram, R, G, B) != 0)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
|
|
|
boxp->Bmin = Bmin = B;
|
|
|
|
goto have_Bmin;
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
have_Bmin:
|
|
|
|
if (Bmax > Bmin)
|
|
|
|
for (B = Bmax; B >= Bmin; B--)
|
|
|
|
for (R = Rmin; R <= Rmax; R++)
|
|
|
|
{
|
2001-03-26 02:41:54 +08:00
|
|
|
for (G = Gmin; G <= Gmax; G++)
|
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
if (*HIST_LIN(histogram, R, G, B) != 0)
|
2001-03-26 02:41:54 +08:00
|
|
|
{
|
|
|
|
boxp->Bmax = Bmax = B;
|
|
|
|
goto have_Bmax;
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
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
|
2002-02-11 02:33:16 +08:00
|
|
|
* a box is splittable iff norm > 0. (ADM: note: this isn't true.)
|
1997-11-25 06:05:25 +08:00
|
|
|
* 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.
|
|
|
|
*/
|
2002-02-11 02:33:16 +08:00
|
|
|
dist0 = ((1 + Rmax - Rmin) << R_SHIFT) * R_SCALE;
|
|
|
|
dist1 = ((1 + Gmax - Gmin) << G_SHIFT) * G_SCALE;
|
|
|
|
dist2 = ((1 + Bmax - Bmin) << B_SHIFT) * B_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
boxp->volume = dist0*dist0 + dist1*dist1 + dist2*dist2;
|
2002-02-11 02:33:16 +08:00
|
|
|
/* boxp->volume = dist0 * dist1 * dist2; */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
compute_color_lin8(&dummyqo, histogram, boxp, 0);
|
1999-02-27 05:30:11 +08:00
|
|
|
|
|
|
|
/*printf("(%d %d %d)\n", dummyqo.cmap[0].red,dummyqo.cmap[0].green,dummyqo.cmap[0].blue);
|
|
|
|
fflush(stdout);*/
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Now scan remaining volume of box and compute population */
|
|
|
|
ccount = 0;
|
1999-02-27 05:30:11 +08:00
|
|
|
boxp->error = 0;
|
|
|
|
boxp->rerror = 0;
|
|
|
|
boxp->gerror = 0;
|
|
|
|
boxp->berror = 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
for (R = Rmin; R <= Rmax; R++)
|
2002-02-11 02:33:16 +08:00
|
|
|
{
|
|
|
|
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;
|
2001-03-26 02:41:54 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
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;
|
2001-03-26 02:41:54 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
boxp->rerror += freq_here * (re) * (re);
|
|
|
|
boxp->gerror += freq_here * (ge) * (ge);
|
|
|
|
boxp->berror += freq_here * (be) * (be);
|
|
|
|
|
|
|
|
ccount += freq_here;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
#if 0
|
|
|
|
fg d;flg fd;kg fld;gflkfld
|
1999-02-27 05:30:11 +08:00
|
|
|
/* Scan again, taking note of halfway error point for red axis */
|
|
|
|
tempRerror = 0;
|
|
|
|
boxp->Rhalferror = Rmin;
|
2002-02-11 02:33:16 +08:00
|
|
|
#warning r<=?
|
|
|
|
for (R = Rmin; R <= Rmax; R++)
|
1999-02-27 05:30:11 +08:00
|
|
|
{
|
|
|
|
for (G = Gmin; G <= Gmax; G++)
|
|
|
|
{
|
2001-03-26 02:41:54 +08:00
|
|
|
for (B = Bmin; B <= Bmax; B++)
|
|
|
|
{
|
|
|
|
ColorFreq freq_here;
|
2002-02-11 02:33:16 +08:00
|
|
|
freq_here = *HIST_LIN(histogram, R, G, B);
|
2001-03-26 02:41:54 +08:00
|
|
|
if (freq_here != 0)
|
|
|
|
{
|
|
|
|
int re;
|
2002-02-11 02:33:16 +08:00
|
|
|
int idist;
|
|
|
|
double dist;
|
|
|
|
|
|
|
|
dummybox.Rmin = dummybox.Rmax = R;
|
|
|
|
dummybox.Gmin = dummybox.Gmax = G;
|
2001-03-26 02:41:54 +08:00
|
|
|
dummybox.Bmin = dummybox.Bmax = B;
|
2002-02-11 02:33:16 +08:00
|
|
|
compute_color_lin8(&dummyqo, histogram, &dummybox, 1);
|
|
|
|
|
2001-03-26 02:41:54 +08:00
|
|
|
re = dummyqo.cmap[0].red - dummyqo.cmap[1].red;
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
tempRerror += freq_here * (re) * (re);
|
2001-03-26 02:41:54 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
if (tempRerror*2 >= boxp->rerror)
|
2001-03-26 02:41:54 +08:00
|
|
|
goto green_axisscan;
|
|
|
|
else
|
|
|
|
boxp->Rhalferror = R;
|
|
|
|
}
|
|
|
|
}
|
1999-02-27 05:30:11 +08:00
|
|
|
}
|
|
|
|
}
|
2002-02-11 02:33:16 +08:00
|
|
|
fprintf(stderr, " D:");
|
1999-02-27 05:30:11 +08:00
|
|
|
green_axisscan:
|
2002-02-11 02:33:16 +08:00
|
|
|
|
|
|
|
fprintf(stderr, "<%d: %llu/%llu> ", R, tempRerror, boxp->rerror);
|
1999-02-27 05:30:11 +08:00
|
|
|
/* Scan again, taking note of halfway error point for green axis */
|
|
|
|
tempGerror = 0;
|
|
|
|
boxp->Ghalferror = Gmin;
|
2002-02-11 02:33:16 +08:00
|
|
|
#warning G<=?
|
|
|
|
for (G = Gmin; G <= Gmax; G++)
|
1999-02-27 05:30:11 +08:00
|
|
|
{
|
|
|
|
for (R = Rmin; R <= Rmax; R++)
|
|
|
|
{
|
2001-03-26 02:41:54 +08:00
|
|
|
for (B = Bmin; B <= Bmax; B++)
|
|
|
|
{
|
|
|
|
ColorFreq freq_here;
|
2002-02-11 02:33:16 +08:00
|
|
|
freq_here = *HIST_LIN(histogram, R, G, B);
|
2001-03-26 02:41:54 +08:00
|
|
|
if (freq_here != 0)
|
|
|
|
{
|
|
|
|
int ge;
|
2002-02-11 02:33:16 +08:00
|
|
|
dummybox.Rmin = dummybox.Rmax = R;
|
|
|
|
dummybox.Gmin = dummybox.Gmax = G;
|
2001-03-26 02:41:54 +08:00
|
|
|
dummybox.Bmin = dummybox.Bmax = B;
|
2002-02-11 02:33:16 +08:00
|
|
|
compute_color_lin8(&dummyqo, histogram, &dummybox, 1);
|
2001-03-26 02:41:54 +08:00
|
|
|
|
|
|
|
ge = dummyqo.cmap[0].green - dummyqo.cmap[1].green;
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
tempGerror += freq_here * (ge) * (ge);
|
2001-03-26 02:41:54 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
if (tempGerror*2 >= boxp->gerror)
|
2001-03-26 02:41:54 +08:00
|
|
|
goto blue_axisscan;
|
|
|
|
else
|
|
|
|
boxp->Ghalferror = G;
|
|
|
|
}
|
|
|
|
}
|
1999-02-27 05:30:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
blue_axisscan:
|
|
|
|
/* Scan again, taking note of halfway error point for blue axis */
|
|
|
|
tempBerror = 0;
|
|
|
|
boxp->Bhalferror = Bmin;
|
2002-02-11 02:33:16 +08:00
|
|
|
#warning B<=?
|
|
|
|
for (B = Bmin; B <= Bmax; B++)
|
1999-02-27 05:30:11 +08:00
|
|
|
{
|
|
|
|
for (R = Rmin; R <= Rmax; R++)
|
|
|
|
{
|
|
|
|
for (G = Gmin; G <= Gmax; G++)
|
|
|
|
{
|
2001-03-26 02:41:54 +08:00
|
|
|
ColorFreq freq_here;
|
2002-02-11 02:33:16 +08:00
|
|
|
freq_here = *HIST_LIN(histogram, R, G, B);
|
2001-03-26 02:41:54 +08:00
|
|
|
if (freq_here != 0)
|
1999-02-27 05:30:11 +08:00
|
|
|
{
|
|
|
|
int be;
|
2002-02-11 02:33:16 +08:00
|
|
|
dummybox.Rmin = dummybox.Rmax = R;
|
1999-02-27 05:30:11 +08:00
|
|
|
dummybox.Gmin = dummybox.Gmax = G;
|
2002-02-11 02:33:16 +08:00
|
|
|
dummybox.Bmin = dummybox.Bmax = B;
|
|
|
|
compute_color_lin8(&dummyqo, histogram, &dummybox, 1);
|
1999-02-27 05:30:11 +08:00
|
|
|
|
|
|
|
be = dummyqo.cmap[0].blue - dummyqo.cmap[1].blue;
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
tempBerror += freq_here * (be) * (be);
|
1999-02-27 05:30:11 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
if (tempBerror*2 >= boxp->berror)
|
1999-02-27 05:30:11 +08:00
|
|
|
goto finished_axesscan;
|
|
|
|
else
|
|
|
|
boxp->Bhalferror = B;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
finished_axesscan:
|
2002-02-11 02:33:16 +08:00
|
|
|
#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, longest_ax2=AXIS_UNDEF;
|
|
|
|
int longest_length=0, longest_length2=0;
|
|
|
|
int 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_ax2 = longest_ax;
|
|
|
|
longest_length = dist0;
|
|
|
|
longest_ax = AXIS_RED;
|
|
|
|
}
|
|
|
|
else if ( dist0 >= longest_length2)
|
|
|
|
{
|
|
|
|
longest_length2 = dist0;
|
|
|
|
longest_ax2 = AXIS_RED;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( dist1 >= longest_length)
|
|
|
|
{
|
|
|
|
longest_length2 = longest_length;
|
|
|
|
longest_ax2 = longest_ax;
|
|
|
|
longest_length = dist1;
|
|
|
|
longest_ax = AXIS_GREEN;
|
|
|
|
}
|
|
|
|
else if ( dist1 >= longest_length2)
|
|
|
|
{
|
|
|
|
longest_length2 = dist1;
|
|
|
|
longest_ax2 = AXIS_GREEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( dist2 >= longest_length)
|
|
|
|
{
|
|
|
|
longest_length2 = longest_length;
|
|
|
|
longest_ax2 = longest_ax;
|
|
|
|
longest_length = dist2;
|
|
|
|
longest_ax = AXIS_BLUE;
|
|
|
|
}
|
|
|
|
else if ( dist2 >= longest_length2)
|
|
|
|
{
|
|
|
|
longest_length2 = dist2;
|
|
|
|
longest_ax2 = AXIS_BLUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
*/
|
1999-02-27 05:30:11 +08:00
|
|
|
|
|
|
|
/*
|
2002-02-11 02:33:16 +08:00
|
|
|
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;
|
1999-02-27 05:30:11 +08:00
|
|
|
*/
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
|
|
|
|
#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);*/
|
|
|
|
|
1999-02-27 05:30:11 +08:00
|
|
|
/*boxp->error = (sqrt((double)(boxp->error/ccount)));*/
|
|
|
|
/* boxp->rerror = (sqrt((double)((boxp->rerror)/ccount)));
|
|
|
|
boxp->gerror = (sqrt((double)((boxp->gerror)/ccount)));
|
|
|
|
boxp->berror = (sqrt((double)((boxp->berror)/ccount)));*/
|
|
|
|
/*printf(":%lld / %ld: ", boxp->error, ccount);
|
|
|
|
printf("(%d-%d-%d)(%d-%d-%d)(%d-%d-%d)\n",
|
|
|
|
Rmin, boxp->Rhalferror, Rmax,
|
|
|
|
Gmin, boxp->Ghalferror, Gmax,
|
|
|
|
Bmin, boxp->Bhalferror, Bmax
|
|
|
|
);
|
|
|
|
fflush(stdout);*/
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
boxp->colorcount = ccount;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
2000-10-05 07:41:47 +08:00
|
|
|
median_cut_gray (CFHistogram histogram,
|
|
|
|
boxptr boxlist,
|
|
|
|
int numboxes,
|
|
|
|
int desired_colors)
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Repeatedly select and split the largest box until we have enough boxes */
|
|
|
|
{
|
|
|
|
int lb;
|
|
|
|
boxptr b1, b2;
|
|
|
|
|
|
|
|
while (numboxes < desired_colors)
|
|
|
|
{
|
|
|
|
/* Select box to split.
|
|
|
|
* Current algorithm: by population for first half, then by volume.
|
|
|
|
*/
|
2002-02-11 02:33:16 +08:00
|
|
|
|
|
|
|
b1 = find_biggest_volume (boxlist, numboxes);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
if (b1 == NULL) /* no splittable boxes left! */
|
|
|
|
break;
|
|
|
|
|
|
|
|
b2 = boxlist + numboxes; /* where new box will go */
|
|
|
|
/* Copy the color bounds to the new box. */
|
|
|
|
b2->Rmax = b1->Rmax;
|
|
|
|
b2->Rmin = b1->Rmin;
|
|
|
|
|
|
|
|
/* Current algorithm: split at halfway point.
|
|
|
|
* (Since the box has been shrunk to minimum volume,
|
|
|
|
* any split will produce two nonempty subboxes.)
|
|
|
|
* Note that lb value is max for lower box, so must be < old max.
|
|
|
|
*/
|
|
|
|
lb = (b1->Rmax + b1->Rmin) / 2;
|
|
|
|
b1->Rmax = lb;
|
|
|
|
b2->Rmin = lb + 1;
|
|
|
|
|
|
|
|
/* Update stats for boxes */
|
|
|
|
update_box_gray (histogram, b1);
|
|
|
|
update_box_gray (histogram, b2);
|
|
|
|
numboxes++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return numboxes;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2000-10-05 07:41:47 +08:00
|
|
|
median_cut_rgb (CFHistogram histogram,
|
|
|
|
boxptr boxlist,
|
|
|
|
int numboxes,
|
|
|
|
int desired_colors)
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Repeatedly select and split the largest box until we have enough boxes */
|
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
int lb;
|
1997-11-25 06:05:25 +08:00
|
|
|
boxptr b1,b2;
|
2002-02-11 02:33:16 +08:00
|
|
|
axisType which_axis;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
while (numboxes < desired_colors) {
|
2002-02-11 02:33:16 +08:00
|
|
|
|
|
|
|
b1 = find_split_candidate (boxlist, numboxes, &which_axis, desired_colors);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
if (b1 == NULL) /* no splittable boxes left! */
|
|
|
|
break;
|
|
|
|
b2 = boxlist + numboxes; /* where new box will go */
|
|
|
|
/* Copy the color bounds to the new box. */
|
|
|
|
b2->Rmax = b1->Rmax; b2->Gmax = b1->Gmax; b2->Bmax = b1->Bmax;
|
|
|
|
b2->Rmin = b1->Rmin; b2->Gmin = b1->Gmin; b2->Bmin = b1->Bmin;
|
2002-02-11 02:33:16 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* Choose split point along selected axis, and update box bounds.
|
|
|
|
* Note that lb value is max for lower box, so must be < old max.
|
|
|
|
*/
|
2002-02-11 02:33:16 +08:00
|
|
|
switch (which_axis)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
case AXIS_RED:
|
1999-02-27 05:30:11 +08:00
|
|
|
lb = b1->Rhalferror;/* *0 + (b1->Rmax + b1->Rmin) / 2; */
|
1997-11-25 06:05:25 +08:00
|
|
|
b1->Rmax = lb;
|
|
|
|
b2->Rmin = lb+1;
|
1999-02-27 05:30:11 +08:00
|
|
|
g_assert(b1->Rmax >= b1->Rmin);
|
|
|
|
g_assert(b2->Rmax >= b2->Rmin);
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
2002-02-11 02:33:16 +08:00
|
|
|
case AXIS_GREEN:
|
1999-02-27 05:30:11 +08:00
|
|
|
lb = b1->Ghalferror;/* *0 + (b1->Gmax + b1->Gmin) / 2; */
|
1997-11-25 06:05:25 +08:00
|
|
|
b1->Gmax = lb;
|
|
|
|
b2->Gmin = lb+1;
|
1999-02-27 05:30:11 +08:00
|
|
|
g_assert(b1->Gmax >= b1->Gmin);
|
|
|
|
g_assert(b2->Gmax >= b2->Gmin);
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
2002-02-11 02:33:16 +08:00
|
|
|
case AXIS_BLUE:
|
1999-02-27 05:30:11 +08:00
|
|
|
lb = b1->Bhalferror;/* *0 + (b1->Bmax + b1->Bmin) / 2; */
|
1997-11-25 06:05:25 +08:00
|
|
|
b1->Bmax = lb;
|
|
|
|
b2->Bmin = lb+1;
|
1999-02-27 05:30:11 +08:00
|
|
|
g_assert(b1->Bmax >= b1->Bmin);
|
|
|
|
g_assert(b2->Bmax >= b2->Bmin);
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
2002-02-11 02:33:16 +08:00
|
|
|
default:
|
|
|
|
g_error("Uh-oh.");
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
/* Update stats for boxes */
|
|
|
|
numboxes++;
|
2002-02-11 02:33:16 +08:00
|
|
|
update_box_rgb (histogram, b1, desired_colors - numboxes);
|
|
|
|
update_box_rgb (histogram, b2, desired_colors - numboxes);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
return numboxes;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
compute_color_gray (QuantizeObj *quantobj,
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram,
|
1997-11-25 06:05:25 +08:00
|
|
|
boxptr boxp,
|
|
|
|
int icolor)
|
|
|
|
/* Compute representative color for a box, put it in colormap[icolor] */
|
|
|
|
{
|
|
|
|
int i, min, max;
|
|
|
|
long count;
|
|
|
|
long total;
|
|
|
|
long gtotal;
|
|
|
|
|
|
|
|
min = boxp->Rmin;
|
|
|
|
max = boxp->Rmax;
|
|
|
|
|
|
|
|
total = 0;
|
|
|
|
gtotal = 0;
|
|
|
|
|
|
|
|
for (i = min; i <= max; i++)
|
|
|
|
{
|
|
|
|
count = histogram[i];
|
|
|
|
if (count != 0)
|
|
|
|
{
|
|
|
|
total += count;
|
|
|
|
gtotal += i * count;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1998-04-13 22:26:46 +08:00
|
|
|
if (total != 0)
|
|
|
|
{
|
|
|
|
quantobj->cmap[icolor].red = (gtotal + (total >> 1)) / total;
|
|
|
|
quantobj->cmap[icolor].green = quantobj->cmap[icolor].red;
|
|
|
|
quantobj->cmap[icolor].blue = quantobj->cmap[icolor].red;
|
|
|
|
}
|
|
|
|
else /* The only situation where total==0 is if the image was null or
|
|
|
|
* all-transparent. In that case we just put a dummy value in
|
|
|
|
* the colourmap.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
quantobj->cmap[icolor].red =
|
|
|
|
quantobj->cmap[icolor].green =
|
|
|
|
quantobj->cmap[icolor].blue = 0;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void
|
|
|
|
compute_color_rgb (QuantizeObj *quantobj,
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram,
|
1997-11-25 06:05:25 +08:00
|
|
|
boxptr boxp,
|
|
|
|
int icolor)
|
|
|
|
/* Compute representative color for a box, put it in colormap[icolor] */
|
|
|
|
{
|
|
|
|
/* Current algorithm: mean weighted by pixels (not colors) */
|
|
|
|
/* Note it is important to get the rounding correct! */
|
|
|
|
int R, G, B;
|
|
|
|
int Rmin, Rmax;
|
|
|
|
int Gmin, Gmax;
|
|
|
|
int Bmin, Bmax;
|
2001-03-26 02:41:54 +08:00
|
|
|
ColorFreq total = 0;
|
|
|
|
ColorFreq Rtotal = 0;
|
|
|
|
ColorFreq Gtotal = 0;
|
|
|
|
ColorFreq Btotal = 0;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
Rmin = boxp->Rmin; Rmax = boxp->Rmax;
|
|
|
|
Gmin = boxp->Gmin; Gmax = boxp->Gmax;
|
|
|
|
Bmin = boxp->Bmin; Bmax = boxp->Bmax;
|
|
|
|
|
|
|
|
for (R = Rmin; R <= Rmax; R++)
|
|
|
|
for (G = Gmin; G <= Gmax; G++)
|
|
|
|
{
|
|
|
|
for (B = Bmin; B <= Bmax; B++)
|
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
ColorFreq this_freq = *HIST_LIN(histogram, R, G, B);
|
2001-03-26 02:41:54 +08:00
|
|
|
if (this_freq != 0)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2001-03-26 02:41:54 +08:00
|
|
|
total += this_freq;
|
2002-02-11 02:33:16 +08:00
|
|
|
Rtotal += R * this_freq;
|
|
|
|
Gtotal += G * this_freq;
|
|
|
|
Btotal += B * this_freq;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
if (total > 0)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
unsigned char 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;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
else /* The only situation where total==0 is if the image was null or
|
|
|
|
* all-transparent. In that case we just put a dummy value in
|
|
|
|
* the colourmap.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
quantobj->cmap[icolor].red =
|
|
|
|
quantobj->cmap[icolor].green =
|
|
|
|
quantobj->cmap[icolor].blue = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
static void
|
|
|
|
compute_color_lin8 (QuantizeObj *quantobj,
|
|
|
|
CFHistogram histogram,
|
|
|
|
boxptr boxp,
|
|
|
|
const int icolor)
|
|
|
|
/* Compute representative color for a box, put it in colormap[icolor] */
|
|
|
|
{
|
|
|
|
/* Current algorithm: mean weighted by pixels (not colors) */
|
|
|
|
/* Note it is important to get the rounding correct! */
|
|
|
|
int R, G, B;
|
|
|
|
int Rmin, Rmax;
|
|
|
|
int Gmin, Gmax;
|
|
|
|
int 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 colourmap.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
g_warning("eep.");
|
|
|
|
quantobj->cmap[icolor].red = 0;
|
|
|
|
quantobj->cmap[icolor].green = 128;
|
|
|
|
quantobj->cmap[icolor].blue = 128;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void
|
|
|
|
select_colors_gray (QuantizeObj *quantobj,
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram)
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Master routine for color selection */
|
|
|
|
{
|
|
|
|
boxptr boxlist;
|
|
|
|
int numboxes;
|
|
|
|
int desired = quantobj->desired_number_of_colors;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Allocate workspace for box list */
|
|
|
|
boxlist = (boxptr) g_malloc ( desired * sizeof(box) );
|
|
|
|
|
|
|
|
/* Initialize one box containing whole space */
|
|
|
|
numboxes = 1;
|
|
|
|
boxlist[0].Rmin = 0;
|
|
|
|
boxlist[0].Rmax = 255;
|
|
|
|
/* Shrink it to actually-used volume and set its statistics */
|
|
|
|
update_box_gray (histogram, boxlist);
|
|
|
|
/* Perform median-cut to produce final box list */
|
|
|
|
numboxes = median_cut_gray (histogram, boxlist, numboxes, desired);
|
|
|
|
|
|
|
|
quantobj->actual_number_of_colors = numboxes;
|
|
|
|
/* Compute the representative color for each box, fill colormap */
|
|
|
|
for (i = 0; i < numboxes; i++)
|
|
|
|
compute_color_gray (quantobj, histogram, boxlist + i, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
select_colors_rgb (QuantizeObj *quantobj,
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram)
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Master routine for color selection */
|
|
|
|
{
|
|
|
|
boxptr boxlist;
|
|
|
|
int numboxes;
|
|
|
|
int desired = quantobj->desired_number_of_colors;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/* Allocate workspace for box list */
|
|
|
|
boxlist = (boxptr) g_malloc ( desired * sizeof(box) );
|
|
|
|
|
|
|
|
/* Initialize one box containing whole space */
|
|
|
|
numboxes = 1;
|
|
|
|
boxlist[0].Rmin = 0;
|
2002-02-11 02:33:16 +08:00
|
|
|
boxlist[0].Rmax = HIST_R_ELEMS - 1;
|
1997-11-25 06:05:25 +08:00
|
|
|
boxlist[0].Gmin = 0;
|
2002-02-11 02:33:16 +08:00
|
|
|
boxlist[0].Gmax = HIST_G_ELEMS - 1;
|
1997-11-25 06:05:25 +08:00
|
|
|
boxlist[0].Bmin = 0;
|
2002-02-11 02:33:16 +08:00
|
|
|
boxlist[0].Bmax = HIST_B_ELEMS - 1;
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Shrink it to actually-used volume and set its statistics */
|
2002-02-11 02:33:16 +08:00
|
|
|
update_box_rgb (histogram, &boxlist[0], quantobj->desired_number_of_colors);
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Perform median-cut to produce final box list */
|
|
|
|
numboxes = median_cut_rgb (histogram, boxlist, numboxes, desired);
|
|
|
|
|
|
|
|
quantobj->actual_number_of_colors = numboxes;
|
|
|
|
/* Compute the representative color for each box, fill colormap */
|
|
|
|
for (i = 0; i < numboxes; i++)
|
2002-02-11 02:33:16 +08:00
|
|
|
{
|
|
|
|
compute_color_rgb (quantobj, histogram, &boxlist[i], i);
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 */
|
2002-02-11 02:33:16 +08:00
|
|
|
/*#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
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
#define BOX_R_ELEMS (1<<BOX_R_LOG) /* # of hist cells in update box */
|
|
|
|
#define BOX_G_ELEMS (1<<BOX_G_LOG)
|
|
|
|
#define BOX_B_ELEMS (1<<BOX_B_LOG)
|
|
|
|
|
|
|
|
#define BOX_R_SHIFT (R_SHIFT + BOX_R_LOG)
|
|
|
|
#define BOX_G_SHIFT (G_SHIFT + BOX_G_LOG)
|
|
|
|
#define BOX_B_SHIFT (B_SHIFT + BOX_B_LOG)
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The next three routines implement inverse colormap filling. They could
|
|
|
|
* all be folded into one big routine, but splitting them up this way saves
|
|
|
|
* some stack space (the mindist[] and bestdist[] arrays need not coexist)
|
|
|
|
* and may allow some compilers to produce better code by registerizing more
|
|
|
|
* inner-loop variables.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int
|
|
|
|
find_nearby_colors (QuantizeObj *quantobj,
|
|
|
|
int minR,
|
|
|
|
int minG,
|
|
|
|
int minB,
|
|
|
|
int colorlist[])
|
|
|
|
/* Locate the colormap entries close enough to an update box to be candidates
|
|
|
|
* for the nearest entry to some cell(s) in the update box. The update box
|
|
|
|
* is specified by the center coordinates of its first cell. The number of
|
|
|
|
* candidate colormap entries is returned, and their colormap indexes are
|
|
|
|
* placed in colorlist[].
|
|
|
|
* This routine uses Heckbert's "locally sorted search" criterion to select
|
|
|
|
* the colors that need further consideration.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int numcolors = quantobj->actual_number_of_colors;
|
|
|
|
int maxR, maxG, maxB;
|
|
|
|
int centerR, centerG, centerB;
|
|
|
|
int i, x, ncolors;
|
|
|
|
int minmaxdist, min_dist, max_dist, tdist;
|
|
|
|
int mindist[MAXNUMCOLORS]; /* min distance to colormap entry i */
|
|
|
|
|
|
|
|
/* Compute true coordinates of update box's upper corner and center.
|
|
|
|
* Actually we compute the coordinates of the center of the upper-corner
|
|
|
|
* histogram cell, which are the upper bounds of the volume we care about.
|
|
|
|
* Note that since ">>" rounds down, the "center" values may be closer to
|
|
|
|
* min than to max; hence comparisons to them must be "<=", not "<".
|
|
|
|
*/
|
|
|
|
maxR = minR + ((1 << BOX_R_SHIFT) - (1 << R_SHIFT));
|
2002-02-11 02:33:16 +08:00
|
|
|
centerR = (minR + maxR + 1) >> 1;
|
1997-11-25 06:05:25 +08:00
|
|
|
maxG = minG + ((1 << BOX_G_SHIFT) - (1 << G_SHIFT));
|
2002-02-11 02:33:16 +08:00
|
|
|
centerG = (minG + maxG + 1) >> 1;
|
1997-11-25 06:05:25 +08:00
|
|
|
maxB = minB + ((1 << BOX_B_SHIFT) - (1 << B_SHIFT));
|
2002-02-11 02:33:16 +08:00
|
|
|
centerB = (minB + maxB + 1) >> 1;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* 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. */
|
2002-02-11 02:33:16 +08:00
|
|
|
x = quantobj->clin[i].red;
|
1997-11-25 06:05:25 +08:00
|
|
|
if (x < minR) {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - minR) * R_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
min_dist = tdist*tdist;
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - maxR) * R_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist = tdist*tdist;
|
|
|
|
} else if (x > maxR) {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - maxR) * R_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
min_dist = tdist*tdist;
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - minR) * R_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist = tdist*tdist;
|
|
|
|
} else {
|
|
|
|
/* within cell range so no contribution to min_dist */
|
|
|
|
min_dist = 0;
|
|
|
|
if (x <= centerR) {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - maxR) * R_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist = tdist*tdist;
|
|
|
|
} else {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - minR) * R_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist = tdist*tdist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
x = quantobj->clin[i].green;
|
1997-11-25 06:05:25 +08:00
|
|
|
if (x < minG) {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - minG) * G_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
min_dist += tdist*tdist;
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - maxG) * G_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist += tdist*tdist;
|
|
|
|
} else if (x > maxG) {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - maxG) * G_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
min_dist += tdist*tdist;
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - minG) * G_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist += tdist*tdist;
|
|
|
|
} else {
|
|
|
|
/* within cell range so no contribution to min_dist */
|
|
|
|
if (x <= centerG) {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - maxG) * G_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist += tdist*tdist;
|
|
|
|
} else {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - minG) * G_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist += tdist*tdist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
x = quantobj->clin[i].blue;
|
1997-11-25 06:05:25 +08:00
|
|
|
if (x < minB) {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - minB) * B_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
min_dist += tdist*tdist;
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - maxB) * B_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist += tdist*tdist;
|
|
|
|
} else if (x > maxB) {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - maxB) * B_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
min_dist += tdist*tdist;
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - minB) * B_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist += tdist*tdist;
|
|
|
|
} else {
|
|
|
|
/* within cell range so no contribution to min_dist */
|
|
|
|
if (x <= centerB) {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - maxB) * B_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist += tdist*tdist;
|
|
|
|
} else {
|
2000-03-26 07:44:38 +08:00
|
|
|
tdist = (x - minB) * B_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
max_dist += tdist*tdist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mindist[i] = min_dist; /* save away the results */
|
|
|
|
if (max_dist < minmaxdist)
|
|
|
|
minmaxdist = max_dist;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Now we know that no cell in the update box is more than minmaxdist
|
|
|
|
* away from some colormap entry. Therefore, only colors that are
|
|
|
|
* within minmaxdist of some part of the box need be considered.
|
|
|
|
*/
|
|
|
|
ncolors = 0;
|
|
|
|
for (i = 0; i < numcolors; i++) {
|
|
|
|
if (mindist[i] <= minmaxdist)
|
|
|
|
colorlist[ncolors++] = i;
|
|
|
|
}
|
|
|
|
return ncolors;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
find_best_colors (QuantizeObj *quantobj,
|
|
|
|
int minR,
|
|
|
|
int minG,
|
|
|
|
int minB,
|
|
|
|
int numcolors,
|
|
|
|
int colorlist[],
|
|
|
|
int bestcolor[])
|
|
|
|
/* Find the closest colormap entry for each cell in the update box,
|
|
|
|
* given the list of candidate colors prepared by find_nearby_colors.
|
|
|
|
* Return the indexes of the closest entries in the bestcolor[] array.
|
|
|
|
* This routine uses Thomas' incremental distance calculation method to
|
|
|
|
* find the distance from a colormap entry to successive cells in the box.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
int iR, iG, iB;
|
|
|
|
int i, icolor;
|
|
|
|
int * bptr; /* pointer into bestdist[] array */
|
|
|
|
int * cptr; /* pointer into bestcolor[] array */
|
|
|
|
int dist0, dist1; /* initial distance values */
|
|
|
|
int dist2; /* current distance in inner loop */
|
|
|
|
int xx0, xx1; /* distance increments */
|
|
|
|
int xx2;
|
|
|
|
int inR, inG, inB; /* initial values for increments */
|
|
|
|
|
|
|
|
/* This array holds the distance to the nearest-so-far color for each cell */
|
|
|
|
int bestdist[BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS];
|
|
|
|
|
|
|
|
/* Initialize best-distance for each cell of the update box */
|
|
|
|
bptr = bestdist;
|
|
|
|
for (i = BOX_R_ELEMS*BOX_G_ELEMS*BOX_B_ELEMS-1; i >= 0; i--)
|
|
|
|
*bptr++ = 0x7FFFFFFFL;
|
|
|
|
|
|
|
|
/* For each color selected by find_nearby_colors,
|
|
|
|
* compute its distance to the center of each cell in the box.
|
|
|
|
* If that's less than best-so-far, update best distance and color number.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* Nominal steps between cell centers ("x" in Thomas article) */
|
2000-03-26 07:44:38 +08:00
|
|
|
#define STEP_R ((1 << R_SHIFT) * R_SCALE)
|
|
|
|
#define STEP_G ((1 << G_SHIFT) * G_SCALE)
|
|
|
|
#define STEP_B ((1 << B_SHIFT) * B_SCALE)
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
for (i = 0; i < numcolors; i++) {
|
|
|
|
icolor = colorlist[i];
|
|
|
|
/* Compute (square of) distance from minR/G/B to this color */
|
2002-02-11 02:33:16 +08:00
|
|
|
inR = (minR - quantobj->clin[icolor].red) * R_SCALE;
|
1997-11-25 06:05:25 +08:00
|
|
|
dist0 = inR*inR;
|
2002-02-11 02:33:16 +08:00
|
|
|
/* 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;
|
|
|
|
} */
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Form the initial difference increments */
|
2000-03-26 07:44:38 +08:00
|
|
|
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;
|
1997-11-25 06:05:25 +08:00
|
|
|
/* 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;
|
2000-03-26 07:44:38 +08:00
|
|
|
xx2 += 2 * STEP_B * STEP_B;
|
1997-11-25 06:05:25 +08:00
|
|
|
bptr++;
|
|
|
|
cptr++;
|
|
|
|
}
|
|
|
|
dist1 += xx1;
|
2000-03-26 07:44:38 +08:00
|
|
|
xx1 += 2 * STEP_G * STEP_G;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
dist0 += xx0;
|
2000-03-26 07:44:38 +08:00
|
|
|
xx0 += 2 * STEP_R * STEP_R;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
fill_inverse_cmap_gray (QuantizeObj *quantobj,
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram,
|
1997-11-25 06:05:25 +08:00
|
|
|
int pixel)
|
|
|
|
/* Fill the inverse-colormap entries in the update box that contains */
|
|
|
|
/* histogram cell R/G/B. (Only that one cell MUST be filled, but */
|
|
|
|
/* we can fill as many others as we wish.) */
|
|
|
|
{
|
|
|
|
Color *cmap;
|
|
|
|
long dist;
|
|
|
|
long mindist;
|
|
|
|
int mindisti;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
cmap = quantobj->cmap;
|
|
|
|
|
|
|
|
mindist = 65536;
|
|
|
|
mindisti = -1;
|
|
|
|
|
|
|
|
for (i = 0; i < quantobj->actual_number_of_colors; i++)
|
|
|
|
{
|
|
|
|
dist = pixel - cmap[i].red;
|
|
|
|
dist *= dist;
|
|
|
|
|
|
|
|
if (dist < mindist)
|
|
|
|
{
|
|
|
|
mindist = dist;
|
|
|
|
mindisti = i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i >= 0)
|
|
|
|
histogram[pixel] = mindisti + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
fill_inverse_cmap_rgb (QuantizeObj *quantobj,
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram,
|
1997-11-25 06:05:25 +08:00
|
|
|
int R,
|
|
|
|
int G,
|
|
|
|
int B)
|
|
|
|
/* Fill the inverse-colormap entries in the update box that contains */
|
|
|
|
/* histogram cell R/G/B. (Only that one cell MUST be filled, but */
|
|
|
|
/* we can fill as many others as we wish.) */
|
|
|
|
{
|
|
|
|
int minR, minG, minB; /* lower left corner of update box */
|
|
|
|
int iR, iG, iB;
|
|
|
|
int * cptr; /* pointer into bestcolor[] array */
|
|
|
|
/* This array lists the candidate colormap indexes. */
|
|
|
|
int colorlist[MAXNUMCOLORS];
|
|
|
|
int numcolors; /* number of candidate colors */
|
|
|
|
/* This array holds the actually closest colormap index for each cell. */
|
|
|
|
int bestcolor[BOX_R_ELEMS * BOX_G_ELEMS * BOX_B_ELEMS];
|
|
|
|
|
|
|
|
/* Convert cell coordinates to update box id */
|
|
|
|
R >>= BOX_R_LOG;
|
|
|
|
G >>= BOX_G_LOG;
|
|
|
|
B >>= BOX_B_LOG;
|
|
|
|
|
|
|
|
/* Compute true coordinates of update box's origin corner.
|
|
|
|
* Actually we compute the coordinates of the center of the corner
|
|
|
|
* histogram cell, which are the lower bounds of the volume we care about.
|
|
|
|
*/
|
|
|
|
minR = (R << BOX_R_SHIFT) + ((1 << R_SHIFT) >> 1);
|
|
|
|
minG = (G << BOX_G_SHIFT) + ((1 << G_SHIFT) >> 1);
|
|
|
|
minB = (B << BOX_B_SHIFT) + ((1 << B_SHIFT) >> 1);
|
|
|
|
|
|
|
|
/* Determine which colormap entries are close enough to be candidates
|
|
|
|
* for the nearest entry to some cell in the update box.
|
|
|
|
*/
|
|
|
|
numcolors = find_nearby_colors (quantobj, minR, minG, minB, colorlist);
|
|
|
|
|
|
|
|
/* Determine the actually nearest colors. */
|
|
|
|
find_best_colors (quantobj, minR, minG, minB, numcolors, colorlist,
|
|
|
|
bestcolor);
|
|
|
|
|
|
|
|
/* Save the best color numbers (plus 1) in the main cache array */
|
|
|
|
R <<= BOX_R_LOG; /* convert id back to base cell indexes */
|
|
|
|
G <<= BOX_G_LOG;
|
|
|
|
B <<= BOX_B_LOG;
|
|
|
|
cptr = bestcolor;
|
|
|
|
for (iR = 0; iR < BOX_R_ELEMS; iR++) {
|
|
|
|
for (iG = 0; iG < BOX_G_ELEMS; iG++) {
|
|
|
|
for (iB = 0; iB < BOX_B_ELEMS; iB++) {
|
2002-02-11 02:33:16 +08:00
|
|
|
*HIST_LIN(histogram, R+iR, G+iG, B+iB) = (*cptr++) + 1;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* This is pass 1 */
|
|
|
|
|
|
|
|
static void
|
|
|
|
median_cut_pass1_gray (QuantizeObj *quantobj)
|
|
|
|
{
|
|
|
|
select_colors_gray (quantobj, quantobj->histogram);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
median_cut_pass1_rgb (QuantizeObj *quantobj)
|
|
|
|
{
|
|
|
|
select_colors_rgb (quantobj, quantobj->histogram);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
monopal_pass1 (QuantizeObj *quantobj)
|
|
|
|
{
|
|
|
|
quantobj -> actual_number_of_colors = 2;
|
|
|
|
quantobj -> cmap[0].red = 0;
|
|
|
|
quantobj -> cmap[0].green = 0;
|
|
|
|
quantobj -> cmap[0].blue = 0;
|
|
|
|
quantobj -> cmap[1].red = 255;
|
|
|
|
quantobj -> cmap[1].green = 255;
|
|
|
|
quantobj -> cmap[1].blue = 255;
|
|
|
|
}
|
|
|
|
static void
|
|
|
|
webpal_pass1 (QuantizeObj *quantobj)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
quantobj -> actual_number_of_colors = 216;
|
|
|
|
for (i=0;i<216;i++)
|
|
|
|
{
|
|
|
|
quantobj->cmap[i].red = webpal[i*3];
|
|
|
|
quantobj->cmap[i].green = webpal[i*3 +1];
|
|
|
|
quantobj->cmap[i].blue = webpal[i*3 +2];
|
|
|
|
}
|
|
|
|
}
|
1997-12-14 19:36:53 +08:00
|
|
|
static void
|
|
|
|
custompal_pass1 (QuantizeObj *quantobj)
|
|
|
|
{
|
2001-01-22 03:53:56 +08:00
|
|
|
gint i;
|
|
|
|
GList *list;
|
|
|
|
GimpPaletteEntry *entry;
|
|
|
|
guchar r, g, b;
|
1997-12-14 19:36:53 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/* fprintf(stderr, "custompal_pass1: using (theCustomPalette %s) from (file %s)\n",
|
|
|
|
theCustomPalette->name, theCustomPalette->filename); */
|
1997-12-14 19:36:53 +08:00
|
|
|
|
2001-01-22 03:53:56 +08:00
|
|
|
for (i = 0, list = theCustomPalette->colors;
|
2002-02-11 02:33:16 +08:00
|
|
|
list;
|
2001-01-22 03:53:56 +08:00
|
|
|
i++, list = g_list_next (list))
|
1997-12-14 19:36:53 +08:00
|
|
|
{
|
2001-01-22 03:53:56 +08:00
|
|
|
entry = (GimpPaletteEntry *) list->data;
|
2001-01-21 21:41:07 +08:00
|
|
|
|
|
|
|
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;
|
1997-12-14 19:36:53 +08:00
|
|
|
}
|
|
|
|
quantobj -> actual_number_of_colors = i;
|
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/*
|
|
|
|
* Map some rows of pixels to the output colormapped representation.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
median_cut_pass2_no_dither_gray (QuantizeObj *quantobj,
|
2001-01-29 07:25:25 +08:00
|
|
|
GimpLayer *layer,
|
1997-11-25 06:05:25 +08:00
|
|
|
TileManager *new_tiles)
|
|
|
|
{
|
|
|
|
PixelRegion srcPR, destPR;
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram = quantobj->histogram;
|
1997-11-25 06:05:25 +08:00
|
|
|
ColorFreq * cachep;
|
|
|
|
unsigned char *src, *dest;
|
|
|
|
int row, col;
|
|
|
|
int pixel;
|
|
|
|
int has_alpha;
|
1999-08-30 00:54:39 +08:00
|
|
|
unsigned long* index_used_count = quantobj->index_used_count;
|
1999-08-29 07:40:35 +08:00
|
|
|
int alpha_dither = quantobj->want_alpha_dither;
|
|
|
|
int offsetx, offsety;
|
1997-11-25 06:05:25 +08:00
|
|
|
void *pr;
|
|
|
|
|
2001-01-15 05:11:52 +08:00
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE(layer), &offsetx, &offsety);
|
1999-08-29 07:40:35 +08:00
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
|
|
|
|
pixel_region_init (&destPR, new_tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, TRUE);
|
|
|
|
for (pr = pixel_regions_register (2, &srcPR, &destPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
1999-08-30 00:54:39 +08:00
|
|
|
{
|
|
|
|
src = srcPR.data;
|
|
|
|
dest = destPR.data;
|
|
|
|
for (row = 0; row < srcPR.h; row++)
|
|
|
|
{
|
|
|
|
for (col = 0; col < srcPR.w; col++)
|
|
|
|
{
|
|
|
|
/* get pixel value and index into the cache */
|
|
|
|
pixel = src[GRAY_PIX];
|
|
|
|
cachep = &histogram[pixel];
|
|
|
|
/* If we have not seen this color before, find nearest colormap entry */
|
|
|
|
/* and update the cache */
|
|
|
|
if (*cachep == 0)
|
|
|
|
fill_inverse_cmap_gray (quantobj, histogram, pixel);
|
|
|
|
|
|
|
|
if (has_alpha)
|
|
|
|
{
|
|
|
|
if ((dest[ALPHA_I_PIX] =
|
|
|
|
(
|
|
|
|
(alpha_dither ?
|
|
|
|
((src[ALPHA_G_PIX] << 6) > (255 * DM[(col+offsetx+srcPR.x)&DM_WIDTHMASK][(row+offsety+srcPR.y)&DM_HEIGHTMASK])) :
|
|
|
|
(src[ALPHA_G_PIX] > 127)
|
|
|
|
) ? 255 : 0)))
|
|
|
|
index_used_count[dest[INDEXED_PIX] = *cachep - 1]++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Now emit the colormap index for this cell */
|
|
|
|
index_used_count[dest[INDEXED_PIX] = *cachep - 1]++;
|
|
|
|
}
|
|
|
|
|
|
|
|
src += srcPR.bytes;
|
|
|
|
dest += destPR.bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
median_cut_pass2_fixed_dither_gray (QuantizeObj *quantobj,
|
2001-01-29 07:25:25 +08:00
|
|
|
GimpLayer *layer,
|
1999-08-30 00:54:39 +08:00
|
|
|
TileManager *new_tiles)
|
|
|
|
{
|
|
|
|
PixelRegion srcPR, destPR;
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram = quantobj->histogram;
|
1999-08-30 00:54:39 +08:00
|
|
|
ColorFreq* cachep;
|
|
|
|
Color* color;
|
|
|
|
unsigned char *src, *dest;
|
|
|
|
int row, col;
|
|
|
|
int pixel;
|
|
|
|
int re, R;
|
|
|
|
unsigned long* index_used_count = quantobj->index_used_count;
|
|
|
|
int has_alpha;
|
|
|
|
int alpha_dither = quantobj->want_alpha_dither;
|
|
|
|
int offsetx, offsety;
|
|
|
|
void *pr;
|
|
|
|
|
2001-01-15 05:11:52 +08:00
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE(layer), &offsetx, &offsety);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
|
|
|
|
pixel_region_init (&destPR, new_tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, TRUE);
|
|
|
|
for (pr = pixel_regions_register (2, &srcPR, &destPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
src = srcPR.data;
|
|
|
|
dest = destPR.data;
|
|
|
|
for (row = 0; row < srcPR.h; row++)
|
|
|
|
{
|
|
|
|
for (col = 0; col < srcPR.w; col++)
|
|
|
|
{
|
1999-08-30 00:54:39 +08:00
|
|
|
int dmval =
|
|
|
|
DM[(col+offsetx+srcPR.x)&DM_WIDTHMASK]
|
|
|
|
[(row+offsety+srcPR.y)&DM_HEIGHTMASK];
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* get pixel value and index into the cache */
|
|
|
|
pixel = src[GRAY_PIX];
|
|
|
|
cachep = &histogram[pixel];
|
|
|
|
/* If we have not seen this color before, find nearest colormap entry */
|
|
|
|
/* and update the cache */
|
|
|
|
if (*cachep == 0)
|
|
|
|
fill_inverse_cmap_gray (quantobj, histogram, pixel);
|
1999-08-30 00:54:39 +08:00
|
|
|
|
|
|
|
color = &quantobj->cmap[*cachep - 1];
|
|
|
|
re = src[GRAY_PIX] - color->red;
|
|
|
|
re = (re * dmval * 2) / 63;
|
|
|
|
R = (CLAMP0255(color->red + re));
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
if (has_alpha)
|
1999-08-30 00:54:39 +08:00
|
|
|
{
|
|
|
|
if ((dest[ALPHA_I_PIX] =
|
|
|
|
((alpha_dither ?
|
|
|
|
((src[ALPHA_G_PIX] << 6) > (255 * dmval)) :
|
|
|
|
(src[ALPHA_G_PIX] > 127)
|
|
|
|
) ? 255 : 0)))
|
|
|
|
index_used_count[dest[INDEXED_PIX] = *cachep - 1]++;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Now emit the colormap index for this cell, barfbarf */
|
|
|
|
index_used_count[dest[INDEXED_PIX] = *cachep - 1]++;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
src += srcPR.bytes;
|
|
|
|
dest += destPR.bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
median_cut_pass2_no_dither_rgb (QuantizeObj *quantobj,
|
2001-01-29 07:25:25 +08:00
|
|
|
GimpLayer *layer,
|
1997-11-25 06:05:25 +08:00
|
|
|
TileManager *new_tiles)
|
|
|
|
{
|
|
|
|
PixelRegion srcPR, destPR;
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram = quantobj->histogram;
|
1997-11-25 06:05:25 +08:00
|
|
|
ColorFreq * cachep;
|
|
|
|
unsigned char *src, *dest;
|
|
|
|
int R, G, B;
|
|
|
|
int row, col;
|
|
|
|
int has_alpha;
|
|
|
|
void *pr;
|
|
|
|
int red_pix = RED_PIX;
|
|
|
|
int green_pix = GREEN_PIX;
|
|
|
|
int blue_pix = BLUE_PIX;
|
|
|
|
int alpha_pix = ALPHA_PIX;
|
1999-08-29 07:40:35 +08:00
|
|
|
int alpha_dither = quantobj->want_alpha_dither;
|
|
|
|
int offsetx, offsety;
|
|
|
|
unsigned long* index_used_count = quantobj->index_used_count;
|
|
|
|
|
2001-01-15 05:11:52 +08:00
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE(layer), &offsetx, &offsety);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* In the case of web/mono palettes, we actually force
|
|
|
|
* grayscale drawables through the rgb pass2 functions
|
|
|
|
*/
|
2001-01-15 05:11:52 +08:00
|
|
|
if (gimp_drawable_is_gray (GIMP_DRAWABLE(layer)))
|
1997-11-25 06:05:25 +08:00
|
|
|
red_pix = green_pix = blue_pix = GRAY_PIX;
|
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
|
|
|
|
|
1998-01-22 15:02:57 +08:00
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
|
|
|
|
pixel_region_init (&destPR, new_tiles, 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, TRUE);
|
2002-02-11 02:33:16 +08:00
|
|
|
for (pr = pixel_regions_register (2, &srcPR, &destPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
src = srcPR.data;
|
|
|
|
dest = destPR.data;
|
|
|
|
for (row = 0; row < srcPR.h; row++)
|
|
|
|
{
|
|
|
|
for (col = 0; col < srcPR.w; col++)
|
|
|
|
{
|
1999-08-29 07:40:35 +08:00
|
|
|
if (has_alpha)
|
|
|
|
{
|
|
|
|
if ((dest[ALPHA_I_PIX] =
|
|
|
|
(alpha_dither ?
|
|
|
|
((src[alpha_pix] << 6) > (255 * DM[(col+offsetx+srcPR.x)&DM_WIDTHMASK][(row+offsety+srcPR.y)&DM_HEIGHTMASK])) :
|
|
|
|
(src[alpha_pix] > 127)
|
|
|
|
) ? 255 : 0)
|
|
|
|
== 0)
|
|
|
|
{
|
|
|
|
goto next_pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
/* get pixel value and index into the cache */
|
2002-02-11 02:33:16 +08:00
|
|
|
rgb_to_lin(src[red_pix], src[green_pix], src[blue_pix],
|
|
|
|
&R, &G, &B);
|
|
|
|
cachep = HIST_LIN(histogram,R,G,B);
|
1999-08-29 07:40:35 +08:00
|
|
|
/* If we have not seen this color before, find nearest
|
|
|
|
colormap entry and update the cache */
|
1997-11-25 06:05:25 +08:00
|
|
|
if (*cachep == 0)
|
|
|
|
fill_inverse_cmap_rgb (quantobj, histogram, R, G, B);
|
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
/* Now emit the colormap index for this cell, barfbarf */
|
|
|
|
index_used_count[dest[INDEXED_PIX] = *cachep - 1]++;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
next_pixel:
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
src += srcPR.bytes;
|
|
|
|
dest += destPR.bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-08-30 00:54:39 +08:00
|
|
|
static void
|
|
|
|
median_cut_pass2_fixed_dither_rgb (QuantizeObj *quantobj,
|
2001-01-29 07:25:25 +08:00
|
|
|
GimpLayer *layer,
|
1999-08-30 00:54:39 +08:00
|
|
|
TileManager *new_tiles)
|
|
|
|
{
|
|
|
|
PixelRegion srcPR, destPR;
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram = quantobj->histogram;
|
1999-08-30 00:54:39 +08:00
|
|
|
ColorFreq* cachep;
|
|
|
|
Color* color;
|
|
|
|
unsigned char *src, *dest;
|
|
|
|
int R, G, B;
|
|
|
|
int row, col;
|
|
|
|
int has_alpha;
|
|
|
|
int re, ge, be;
|
|
|
|
void* pr;
|
|
|
|
int red_pix = RED_PIX;
|
|
|
|
int green_pix = GREEN_PIX;
|
|
|
|
int blue_pix = BLUE_PIX;
|
|
|
|
int alpha_pix = ALPHA_PIX;
|
|
|
|
int alpha_dither = quantobj->want_alpha_dither;
|
|
|
|
int offsetx, offsety;
|
|
|
|
unsigned long* index_used_count = quantobj->index_used_count;
|
|
|
|
|
2001-01-15 05:11:52 +08:00
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE(layer), &offsetx, &offsety);
|
1999-08-30 00:54:39 +08:00
|
|
|
|
|
|
|
/* In the case of web/mono palettes, we actually force
|
|
|
|
* grayscale drawables through the rgb pass2 functions
|
|
|
|
*/
|
2001-01-15 05:11:52 +08:00
|
|
|
if (gimp_drawable_is_gray (GIMP_DRAWABLE (layer)))
|
1999-08-30 00:54:39 +08:00
|
|
|
red_pix = green_pix = blue_pix = GRAY_PIX;
|
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
|
|
|
|
|
1999-08-30 00:54:39 +08:00
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
|
|
|
|
pixel_region_init (&destPR, new_tiles, 0, 0, GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, TRUE);
|
|
|
|
for (pr = pixel_regions_register (2, &srcPR, &destPR); pr != NULL; pr = pixel_regions_process (pr))
|
|
|
|
{
|
|
|
|
src = srcPR.data;
|
|
|
|
dest = destPR.data;
|
|
|
|
for (row = 0; row < srcPR.h; row++)
|
|
|
|
{
|
|
|
|
for (col = 0; col < srcPR.w; col++)
|
|
|
|
{
|
|
|
|
int dmval =
|
|
|
|
DM[(col+offsetx+srcPR.x)&DM_WIDTHMASK]
|
|
|
|
[(row+offsety+srcPR.y)&DM_HEIGHTMASK];
|
|
|
|
|
|
|
|
if (has_alpha)
|
|
|
|
{
|
|
|
|
if ((dest[ALPHA_I_PIX] =
|
|
|
|
(alpha_dither ?
|
|
|
|
((src[alpha_pix] << 6) > (255 * dmval)) :
|
|
|
|
(src[alpha_pix] > 127)
|
|
|
|
) ? 255 : 0)
|
|
|
|
== 0)
|
|
|
|
{
|
|
|
|
goto next_pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* get pixel value and index into the cache */
|
2002-02-11 02:33:16 +08:00
|
|
|
rgb_to_lin(src[red_pix], src[green_pix], src[blue_pix],
|
|
|
|
&R, &G, &B);
|
|
|
|
cachep = HIST_LIN(histogram,R,G,B);
|
1999-08-30 00:54:39 +08:00
|
|
|
/* 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);
|
|
|
|
|
|
|
|
/* Get the error and modulate it between 0x and 2x according
|
|
|
|
to the fixed dither matrix, then add it back to the 0x colour
|
|
|
|
and look up the new histogram entry. To do better fixed
|
|
|
|
dithering, I believe that we need to be able to find the
|
|
|
|
closest colour match on the 'other side' of the desired colour,
|
|
|
|
which is not information which we have cheap access to. */
|
|
|
|
color = &quantobj->cmap[*cachep - 1];
|
|
|
|
re = src[red_pix] - color->red;
|
|
|
|
ge = src[green_pix] - color->green;
|
|
|
|
be = src[blue_pix] - color->blue;
|
|
|
|
|
2000-03-26 07:44:38 +08:00
|
|
|
re = (re * dmval * 2) / 63;
|
|
|
|
ge = (ge * dmval * 2) / 63;
|
|
|
|
be = (be * dmval * 2) / 63;
|
1999-08-30 00:54:39 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
rgb_to_lin((CLAMP0255(color->red + re)),
|
|
|
|
(CLAMP0255(color->green + ge)),
|
|
|
|
(CLAMP0255(color->blue + be)),
|
|
|
|
&R, &G, &B);
|
|
|
|
cachep = HIST_LIN(histogram,R,G,B);
|
1999-08-30 00:54:39 +08:00
|
|
|
/* 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_PIX] = *cachep - 1]++;
|
|
|
|
|
|
|
|
next_pixel:
|
|
|
|
|
|
|
|
src += srcPR.bytes;
|
|
|
|
dest += destPR.bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void
|
|
|
|
median_cut_pass2_nodestruct_dither_rgb (QuantizeObj *quantobj,
|
2001-01-29 07:25:25 +08:00
|
|
|
GimpLayer *layer,
|
1997-11-25 06:05:25 +08:00
|
|
|
TileManager *new_tiles)
|
|
|
|
{
|
|
|
|
PixelRegion srcPR, destPR;
|
|
|
|
unsigned char *src, *dest;
|
|
|
|
int row, col;
|
|
|
|
int has_alpha;
|
1999-08-29 07:40:35 +08:00
|
|
|
int alpha_dither = quantobj->want_alpha_dither;
|
1997-11-25 06:05:25 +08:00
|
|
|
void *pr;
|
|
|
|
int red_pix = RED_PIX;
|
|
|
|
int green_pix = GREEN_PIX;
|
|
|
|
int blue_pix = BLUE_PIX;
|
|
|
|
int alpha_pix = ALPHA_PIX;
|
|
|
|
int i;
|
|
|
|
int lastindex = 0;
|
|
|
|
int lastred = -1;
|
|
|
|
int lastgreen = -1;
|
|
|
|
int lastblue = -1;
|
1999-08-29 07:40:35 +08:00
|
|
|
int offsetx, offsety;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-01-15 05:11:52 +08:00
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE(layer), &offsetx, &offsety);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
|
|
|
|
pixel_region_init (&destPR, new_tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, TRUE);
|
|
|
|
for (pr = pixel_regions_register (2, &srcPR, &destPR);
|
|
|
|
pr != NULL;
|
|
|
|
pr = pixel_regions_process (pr))
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
src = srcPR.data;
|
|
|
|
dest = destPR.data;
|
|
|
|
for (row = 0; row < srcPR.h; row++)
|
|
|
|
{
|
|
|
|
for (col = 0; col < srcPR.w; col++)
|
|
|
|
{
|
1999-08-29 07:40:35 +08:00
|
|
|
if ((has_alpha && (alpha_dither ?
|
|
|
|
((src[alpha_pix] << 6) > (255 * DM[(col+srcPR.x+offsetx)&DM_WIDTHMASK][(row+srcPR.y+offsety)&DM_HEIGHTMASK])) :
|
|
|
|
(src[alpha_pix] > 127)))
|
1997-11-25 06:05:25 +08:00
|
|
|
|| !has_alpha)
|
|
|
|
{
|
|
|
|
if ((lastred == src[red_pix]) &&
|
|
|
|
(lastgreen == src[green_pix]) &&
|
|
|
|
(lastblue == src[blue_pix]))
|
|
|
|
{
|
|
|
|
/* same pixel colour as last time */
|
|
|
|
dest[INDEXED_PIX] = lastindex;
|
|
|
|
if (has_alpha)
|
|
|
|
dest[ALPHA_I_PIX] = 255;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = 0 ;
|
|
|
|
i < quantobj->actual_number_of_colors;
|
|
|
|
i++)
|
|
|
|
{
|
|
|
|
if (
|
|
|
|
(quantobj->cmap[i].green == src[green_pix]) &&
|
2002-02-11 02:33:16 +08:00
|
|
|
(quantobj->cmap[i].red == src[red_pix]) &&
|
1997-11-25 06:05:25 +08:00
|
|
|
(quantobj->cmap[i].blue == src[blue_pix])
|
|
|
|
)
|
|
|
|
{
|
|
|
|
lastred = src[red_pix];
|
|
|
|
lastgreen = src[green_pix];
|
|
|
|
lastblue = src[blue_pix];
|
|
|
|
lastindex = i;
|
|
|
|
goto got_colour;
|
|
|
|
}
|
|
|
|
}
|
2002-02-11 02:33:16 +08:00
|
|
|
g_error ("Non-existant colour was expected to "
|
|
|
|
"be in non-destructive colourmap.");
|
1997-11-25 06:05:25 +08:00
|
|
|
got_colour:
|
|
|
|
dest[INDEXED_PIX] = lastindex;
|
|
|
|
if (has_alpha)
|
|
|
|
dest[ALPHA_I_PIX] = 255;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{ /* have alpha, and transparent */
|
|
|
|
dest[ALPHA_I_PIX] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
src += srcPR.bytes;
|
|
|
|
dest += destPR.bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the error-limiting transfer function (lookup table).
|
|
|
|
* The raw F-S error computation can potentially compute error values of up to
|
|
|
|
* +- MAXJSAMPLE. But we want the maximum correction applied to a pixel to be
|
|
|
|
* much less, otherwise obviously wrong pixels will be created. (Typical
|
|
|
|
* effects include weird fringes at color-area boundaries, isolated bright
|
|
|
|
* pixels in a dark area, etc.) The standard advice for avoiding this problem
|
|
|
|
* is to ensure that the "corners" of the color cube are allocated as output
|
|
|
|
* colors; then repeated errors in the same direction cannot cause cascading
|
|
|
|
* error buildup. However, that only prevents the error from getting
|
|
|
|
* completely out of hand; Aaron Giles reports that error limiting improves
|
|
|
|
* the results even with corner colors allocated.
|
|
|
|
* A simple clamping of the error values to about +- MAXJSAMPLE/8 works pretty
|
|
|
|
* well, but the smoother transfer function used below is even better. Thanks
|
|
|
|
* to Aaron Giles for this idea.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static int *
|
1999-09-02 04:41:10 +08:00
|
|
|
init_error_limit (const int error_freedom)
|
1997-11-25 06:05:25 +08:00
|
|
|
/* Allocate and fill in the error_limiter table */
|
|
|
|
{
|
|
|
|
int *table;
|
|
|
|
int in, out;
|
|
|
|
|
1999-02-27 05:30:11 +08:00
|
|
|
/* #define STEPSIZE 16 */
|
1999-09-02 04:41:10 +08:00
|
|
|
/* #define STEPSIZE 200 */
|
1999-02-27 05:30:11 +08:00
|
|
|
|
1999-09-02 04:41:10 +08:00
|
|
|
table = g_malloc (sizeof (int) * (255 * 2 + 1));
|
|
|
|
table += 255; /* so we can index -255 ... +255 */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-09-02 04:41:10 +08:00
|
|
|
if (error_freedom == 0)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-09-02 04:41:10 +08:00
|
|
|
/* Coarse function, much bleeding. */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-09-02 04:41:10 +08:00
|
|
|
const int STEPSIZE = 190;
|
|
|
|
|
|
|
|
for (in = 0; in < STEPSIZE; in++)
|
|
|
|
{
|
|
|
|
table[in] = in;
|
|
|
|
table[-in] = -in;
|
|
|
|
}
|
|
|
|
for (; in <= 255; in++)
|
|
|
|
{
|
|
|
|
table[in] = STEPSIZE;
|
|
|
|
table[-in] = -STEPSIZE;
|
|
|
|
}
|
|
|
|
return (table);
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
1999-09-02 04:41:10 +08:00
|
|
|
else
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
1999-09-02 04:41:10 +08:00
|
|
|
/* Smooth function, bleeding more constrained */
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-09-02 04:41:10 +08:00
|
|
|
const int STEPSIZE = 24;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-09-02 04:41:10 +08:00
|
|
|
/* 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;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Map some rows of pixels to the output colormapped representation.
|
|
|
|
* Perform floyd-steinberg dithering.
|
|
|
|
*/
|
|
|
|
|
|
|
|
static void
|
|
|
|
median_cut_pass2_fs_dither_gray (QuantizeObj *quantobj,
|
2001-01-29 07:25:25 +08:00
|
|
|
GimpLayer *layer,
|
1997-11-25 06:05:25 +08:00
|
|
|
TileManager *new_tiles)
|
|
|
|
{
|
|
|
|
PixelRegion srcPR, destPR;
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram = quantobj->histogram;
|
1997-11-25 06:05:25 +08:00
|
|
|
ColorFreq *cachep;
|
|
|
|
Color *color;
|
|
|
|
int *error_limiter;
|
1999-09-02 04:41:10 +08:00
|
|
|
const short *fs_err1, *fs_err2;
|
|
|
|
const short *fs_err3, *fs_err4;
|
|
|
|
const short *range_limiter;
|
1997-11-25 06:05:25 +08:00
|
|
|
int src_bytes, dest_bytes;
|
|
|
|
unsigned char *src, *dest;
|
|
|
|
unsigned char *src_buf, *dest_buf;
|
|
|
|
int *next_row, *prev_row;
|
|
|
|
int *nr, *pr;
|
|
|
|
int *tmp;
|
|
|
|
int pixel;
|
|
|
|
int pixele;
|
|
|
|
int row, col;
|
|
|
|
int index;
|
|
|
|
int step_dest, step_src;
|
|
|
|
int odd_row;
|
|
|
|
int has_alpha;
|
1999-08-29 07:40:35 +08:00
|
|
|
int offsetx, offsety;
|
|
|
|
int alpha_dither = quantobj->want_alpha_dither;
|
1997-11-25 06:05:25 +08:00
|
|
|
int width, height;
|
1999-08-30 00:54:39 +08:00
|
|
|
unsigned long* index_used_count = quantobj->index_used_count;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2001-01-15 05:11:52 +08:00
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE(layer), &offsetx, &offsety);
|
1999-08-29 07:40:35 +08:00
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
|
|
|
|
pixel_region_init (&destPR, new_tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, TRUE);
|
1998-01-22 15:02:57 +08:00
|
|
|
src_bytes = GIMP_DRAWABLE(layer)->bytes;
|
2001-01-23 21:01:48 +08:00
|
|
|
dest_bytes = tile_manager_bpp (new_tiles);
|
1998-01-22 15:02:57 +08:00
|
|
|
width = GIMP_DRAWABLE(layer)->width;
|
|
|
|
height = GIMP_DRAWABLE(layer)->height;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-09-02 04:41:10 +08:00
|
|
|
error_limiter = init_error_limit (quantobj->error_freedom);
|
1997-11-25 06:05:25 +08:00
|
|
|
range_limiter = range_array + 256;
|
|
|
|
|
|
|
|
src_buf = g_malloc (width * src_bytes);
|
|
|
|
dest_buf = g_malloc (width * dest_bytes);
|
|
|
|
next_row = g_malloc (sizeof (int) * (width + 2));
|
|
|
|
prev_row = g_malloc (sizeof (int) * (width + 2));
|
|
|
|
|
|
|
|
memset (prev_row, 0, (width + 2) * sizeof (int));
|
|
|
|
|
|
|
|
fs_err1 = floyd_steinberg_error1 + 511;
|
|
|
|
fs_err2 = floyd_steinberg_error2 + 511;
|
|
|
|
fs_err3 = floyd_steinberg_error3 + 511;
|
|
|
|
fs_err4 = floyd_steinberg_error4 + 511;
|
|
|
|
|
|
|
|
odd_row = 0;
|
|
|
|
|
|
|
|
for (row = 0; row < height; row++)
|
|
|
|
{
|
|
|
|
pixel_region_get_row (&srcPR, 0, row, width, src_buf, 1);
|
|
|
|
|
|
|
|
src = src_buf;
|
|
|
|
dest = dest_buf;
|
|
|
|
|
|
|
|
nr = next_row;
|
|
|
|
pr = prev_row + 1;
|
|
|
|
|
|
|
|
if (odd_row)
|
|
|
|
{
|
|
|
|
step_dest = -dest_bytes;
|
|
|
|
step_src = -src_bytes;
|
|
|
|
|
|
|
|
src += (width * src_bytes) - src_bytes;
|
|
|
|
dest += (width * dest_bytes) - dest_bytes;
|
|
|
|
|
|
|
|
nr += width + 1;
|
|
|
|
pr += width;
|
|
|
|
|
|
|
|
*(nr - 1) = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
step_dest = dest_bytes;
|
|
|
|
step_src = src_bytes;
|
|
|
|
|
|
|
|
*(nr + 1) = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*nr = 0;
|
|
|
|
|
|
|
|
for (col = 0; col < width; col++)
|
|
|
|
{
|
|
|
|
pixel = range_limiter[src[GRAY_PIX] + error_limiter[*pr]];
|
|
|
|
|
|
|
|
cachep = &histogram[pixel];
|
|
|
|
/* If we have not seen this color before, find nearest colormap entry */
|
|
|
|
/* and update the cache */
|
|
|
|
if (*cachep == 0)
|
|
|
|
fill_inverse_cmap_gray (quantobj, histogram, pixel);
|
|
|
|
|
|
|
|
if (has_alpha)
|
1999-08-30 00:54:39 +08:00
|
|
|
{
|
|
|
|
if (odd_row)
|
|
|
|
{
|
|
|
|
if ((dest[ALPHA_I_PIX] =
|
|
|
|
(alpha_dither ?
|
|
|
|
((src[ALPHA_G_PIX] << 6) > (255 * DM[((width-col)+offsetx-1)&DM_WIDTHMASK][(row+offsety)&DM_HEIGHTMASK])) :
|
|
|
|
(src[ALPHA_G_PIX] > 127)
|
|
|
|
) ? 255 : 0)
|
|
|
|
== 0)
|
|
|
|
{
|
|
|
|
pr--;
|
|
|
|
nr--;
|
|
|
|
*(nr - 1) = 0;
|
|
|
|
goto next_pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((dest[ALPHA_I_PIX] =
|
|
|
|
(alpha_dither ?
|
|
|
|
((src[ALPHA_G_PIX] << 6) > (255 * DM[(col+offsetx)&DM_WIDTHMASK][(row+offsety)&DM_HEIGHTMASK])) :
|
|
|
|
(src[ALPHA_G_PIX] > 127)
|
|
|
|
) ? 255 : 0)
|
|
|
|
== 0)
|
|
|
|
{
|
|
|
|
pr++;
|
|
|
|
nr++;
|
|
|
|
*(nr + 1) = 0;
|
|
|
|
goto next_pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
index = *cachep - 1;
|
|
|
|
index_used_count[dest[INDEXED_PIX] = index]++;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
1999-08-30 00:54:39 +08:00
|
|
|
next_pixel:
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
dest += step_dest;
|
|
|
|
src += step_src;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = next_row;
|
|
|
|
next_row = prev_row;
|
|
|
|
prev_row = tmp;
|
|
|
|
|
|
|
|
odd_row = !odd_row;
|
|
|
|
|
|
|
|
pixel_region_set_row (&destPR, 0, row, width, dest_buf);
|
|
|
|
}
|
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
g_free (error_limiter - 255); /* good lord. */
|
1997-11-25 06:05:25 +08:00
|
|
|
g_free (next_row);
|
|
|
|
g_free (prev_row);
|
|
|
|
g_free (src_buf);
|
|
|
|
g_free (dest_buf);
|
|
|
|
}
|
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
static void
|
|
|
|
median_cut_pass2_rgb_init (QuantizeObj *quantobj)
|
|
|
|
{
|
2002-02-11 02:33:16 +08:00
|
|
|
int i;
|
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
zero_histogram_rgb (quantobj->histogram);
|
|
|
|
|
|
|
|
/* Mark all indices as currently unused */
|
|
|
|
memset (quantobj->index_used_count, 0, 256 * sizeof(unsigned long));
|
2002-02-11 02:33:16 +08:00
|
|
|
|
|
|
|
/* Make a version of our discovered colourmap 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);
|
|
|
|
}
|
1999-08-29 07:40:35 +08:00
|
|
|
}
|
|
|
|
|
1999-08-30 00:54:39 +08:00
|
|
|
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(unsigned long));
|
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
static void
|
|
|
|
median_cut_pass2_fs_dither_rgb (QuantizeObj *quantobj,
|
2001-01-29 07:25:25 +08:00
|
|
|
GimpLayer *layer,
|
1997-11-25 06:05:25 +08:00
|
|
|
TileManager *new_tiles)
|
|
|
|
{
|
|
|
|
PixelRegion srcPR, destPR;
|
2000-10-05 07:41:47 +08:00
|
|
|
CFHistogram histogram = quantobj->histogram;
|
1997-11-25 06:05:25 +08:00
|
|
|
ColorFreq *cachep;
|
|
|
|
Color *color;
|
|
|
|
int *error_limiter;
|
1999-09-02 04:41:10 +08:00
|
|
|
const short *fs_err1, *fs_err2;
|
|
|
|
const short *fs_err3, *fs_err4;
|
|
|
|
const short *range_limiter;
|
1997-11-25 06:05:25 +08:00
|
|
|
int src_bytes, dest_bytes;
|
|
|
|
unsigned char *src, *dest;
|
|
|
|
unsigned char *src_buf, *dest_buf;
|
|
|
|
int *red_n_row, *red_p_row;
|
|
|
|
int *grn_n_row, *grn_p_row;
|
|
|
|
int *blu_n_row, *blu_p_row;
|
|
|
|
int *rnr, *rpr;
|
|
|
|
int *gnr, *gpr;
|
|
|
|
int *bnr, *bpr;
|
|
|
|
int *tmp;
|
|
|
|
int re, ge, be;
|
|
|
|
int row, col;
|
|
|
|
int index;
|
|
|
|
int step_dest, step_src;
|
|
|
|
int odd_row;
|
|
|
|
int has_alpha;
|
|
|
|
int width, height;
|
|
|
|
int red_pix = RED_PIX;
|
|
|
|
int green_pix = GREEN_PIX;
|
|
|
|
int blue_pix = BLUE_PIX;
|
|
|
|
int alpha_pix = ALPHA_PIX;
|
1999-08-29 07:40:35 +08:00
|
|
|
int offsetx, offsety;
|
|
|
|
int alpha_dither = quantobj->want_alpha_dither;
|
|
|
|
unsigned long *index_used_count = quantobj->index_used_count;
|
2002-02-11 02:33:16 +08:00
|
|
|
int global_rmax=0, global_rmin=G_MAXINT,
|
|
|
|
global_gmax=0, global_gmin=G_MAXINT,
|
|
|
|
global_bmax=0, global_bmin=G_MAXINT;
|
1999-08-29 07:40:35 +08:00
|
|
|
|
2001-01-15 05:11:52 +08:00
|
|
|
gimp_drawable_offsets (GIMP_DRAWABLE(layer), &offsetx, &offsety);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
/* In the case of web/mono palettes, we actually force
|
|
|
|
* grayscale drawables through the rgb pass2 functions
|
|
|
|
*/
|
2001-01-15 05:11:52 +08:00
|
|
|
if (gimp_drawable_is_gray (GIMP_DRAWABLE(layer)))
|
1997-11-25 06:05:25 +08:00
|
|
|
red_pix = green_pix = blue_pix = GRAY_PIX;
|
|
|
|
|
2002-02-01 00:47:20 +08:00
|
|
|
has_alpha = gimp_drawable_has_alpha (GIMP_DRAWABLE (layer));
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
pixel_region_init (&srcPR, GIMP_DRAWABLE(layer)->tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, FALSE);
|
|
|
|
pixel_region_init (&destPR, new_tiles, 0, 0,
|
|
|
|
GIMP_DRAWABLE(layer)->width, GIMP_DRAWABLE(layer)->height, TRUE);
|
1998-01-22 15:02:57 +08:00
|
|
|
src_bytes = GIMP_DRAWABLE(layer)->bytes;
|
2001-01-23 21:01:48 +08:00
|
|
|
dest_bytes = tile_manager_bpp (new_tiles);
|
1998-01-22 15:02:57 +08:00
|
|
|
width = GIMP_DRAWABLE(layer)->width;
|
|
|
|
height = GIMP_DRAWABLE(layer)->height;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
1999-09-02 04:41:10 +08:00
|
|
|
error_limiter = init_error_limit (quantobj->error_freedom);
|
1997-11-25 06:05:25 +08:00
|
|
|
range_limiter = range_array + 256;
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/* find the bounding box of the palette colours --
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
src_buf = g_malloc (width * src_bytes);
|
|
|
|
dest_buf = g_malloc (width * dest_bytes);
|
|
|
|
red_n_row = g_malloc (sizeof (int) * (width + 2));
|
|
|
|
red_p_row = g_malloc (sizeof (int) * (width + 2));
|
|
|
|
grn_n_row = g_malloc (sizeof (int) * (width + 2));
|
|
|
|
grn_p_row = g_malloc (sizeof (int) * (width + 2));
|
|
|
|
blu_n_row = g_malloc (sizeof (int) * (width + 2));
|
|
|
|
blu_p_row = g_malloc (sizeof (int) * (width + 2));
|
|
|
|
|
|
|
|
memset (red_p_row, 0, (width + 2) * sizeof (int));
|
|
|
|
memset (grn_p_row, 0, (width + 2) * sizeof (int));
|
|
|
|
memset (blu_p_row, 0, (width + 2) * sizeof (int));
|
|
|
|
|
|
|
|
fs_err1 = floyd_steinberg_error1 + 511;
|
|
|
|
fs_err2 = floyd_steinberg_error2 + 511;
|
|
|
|
fs_err3 = floyd_steinberg_error3 + 511;
|
|
|
|
fs_err4 = floyd_steinberg_error4 + 511;
|
|
|
|
|
|
|
|
odd_row = 0;
|
|
|
|
|
|
|
|
for (row = 0; row < height; row++)
|
|
|
|
{
|
|
|
|
pixel_region_get_row (&srcPR, 0, row, width, src_buf, 1);
|
|
|
|
|
|
|
|
src = src_buf;
|
|
|
|
dest = dest_buf;
|
|
|
|
|
|
|
|
rnr = red_n_row;
|
|
|
|
gnr = grn_n_row;
|
|
|
|
bnr = blu_n_row;
|
|
|
|
rpr = red_p_row + 1;
|
|
|
|
gpr = grn_p_row + 1;
|
|
|
|
bpr = blu_p_row + 1;
|
|
|
|
|
|
|
|
if (odd_row)
|
|
|
|
{
|
|
|
|
step_dest = -dest_bytes;
|
|
|
|
step_src = -src_bytes;
|
|
|
|
|
|
|
|
src += (width * src_bytes) - src_bytes;
|
|
|
|
dest += (width * dest_bytes) - dest_bytes;
|
|
|
|
|
|
|
|
rnr += width + 1;
|
|
|
|
gnr += width + 1;
|
|
|
|
bnr += width + 1;
|
|
|
|
rpr += width;
|
|
|
|
gpr += width;
|
|
|
|
bpr += width;
|
|
|
|
|
|
|
|
*(rnr - 1) = *(gnr - 1) = *(bnr - 1) = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
step_dest = dest_bytes;
|
|
|
|
step_src = src_bytes;
|
|
|
|
|
|
|
|
*(rnr + 1) = *(gnr + 1) = *(bnr + 1) = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
*rnr = *gnr = *bnr = 0;
|
|
|
|
|
|
|
|
for (col = 0; col < width; col++)
|
|
|
|
{
|
1999-08-29 07:40:35 +08:00
|
|
|
if (has_alpha)
|
|
|
|
{
|
|
|
|
if (odd_row)
|
|
|
|
{
|
|
|
|
/* I get goosebumps over this expression. */
|
|
|
|
if ((dest[ALPHA_I_PIX] =
|
|
|
|
(alpha_dither ?
|
|
|
|
((src[alpha_pix] << 6) > (255 * DM[((width-col)+offsetx-1)&DM_WIDTHMASK][(row+offsety)&DM_HEIGHTMASK])) :
|
|
|
|
(src[alpha_pix] > 127)
|
|
|
|
) ? 255 : 0)
|
|
|
|
== 0)
|
|
|
|
{
|
|
|
|
rpr--; gpr--; bpr--;
|
|
|
|
rnr--; gnr--; bnr--;
|
|
|
|
*(rnr - 1) = *(gnr - 1) = *(bnr - 1) = 0;
|
|
|
|
goto next_pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ((dest[ALPHA_I_PIX] =
|
|
|
|
(alpha_dither ?
|
|
|
|
((src[alpha_pix] << 6) > (255 * DM[(col+offsetx)&DM_WIDTHMASK][(row+offsety)&DM_HEIGHTMASK])) :
|
|
|
|
(src[alpha_pix] > 127)
|
|
|
|
) ? 255 : 0)
|
|
|
|
== 0)
|
|
|
|
{
|
|
|
|
rpr++; gpr++; bpr++;
|
|
|
|
rnr++; gnr++; bnr++;
|
|
|
|
*(rnr + 1) = *(gnr + 1) = *(bnr + 1) = 0;
|
|
|
|
goto next_pixel;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
#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;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
// 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);
|
1997-11-25 06:05:25 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
/*
|
|
|
|
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));
|
1999-08-29 07:40:35 +08:00
|
|
|
/* If we have not seen this color before, find nearest
|
|
|
|
colormap entry and update the cache */
|
1997-11-25 06:05:25 +08:00
|
|
|
if (*cachep == 0)
|
2002-02-11 02:33:16 +08:00
|
|
|
fill_inverse_cmap_rgb (quantobj, histogram,
|
|
|
|
RSDF(re),
|
|
|
|
GSDF(ge),
|
|
|
|
BSDF(be));
|
1999-08-29 07:40:35 +08:00
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
index = *cachep - 1;
|
1999-08-29 07:40:35 +08:00
|
|
|
index_used_count[index]++;
|
1997-11-25 06:05:25 +08:00
|
|
|
dest[INDEXED_PIX] = index;
|
2002-02-11 02:33:16 +08:00
|
|
|
|
|
|
|
/*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;
|
1999-08-29 07:40:35 +08:00
|
|
|
|
2002-02-11 02:33:16 +08:00
|
|
|
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
|
|
|
|
{
|
|
|
|
/* colour 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;
|
|
|
|
}
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
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];
|
|
|
|
}
|
|
|
|
|
1999-08-29 07:40:35 +08:00
|
|
|
next_pixel:
|
|
|
|
|
1997-11-25 06:05:25 +08:00
|
|
|
dest += step_dest;
|
|
|
|
src += step_src;
|
|
|
|
}
|
|
|
|
|
|
|
|
tmp = red_n_row;
|
|
|
|
red_n_row = red_p_row;
|
|
|
|
red_p_row = tmp;
|
|
|
|
|
|
|
|
tmp = grn_n_row;
|
|
|
|
grn_n_row = grn_p_row;
|
|
|
|
grn_p_row = tmp;
|
|
|
|
|
|
|
|
tmp = blu_n_row;
|
|
|
|
blu_n_row = blu_p_row;
|
|
|
|
blu_p_row = tmp;
|
|
|
|
|
|
|
|
odd_row = !odd_row;
|
|
|
|
|
|
|
|
pixel_region_set_row (&destPR, 0, row, width, dest_buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (error_limiter - 255);
|
|
|
|
g_free (red_n_row);
|
|
|
|
g_free (red_p_row);
|
|
|
|
g_free (grn_n_row);
|
|
|
|
g_free (grn_p_row);
|
|
|
|
g_free (blu_n_row);
|
|
|
|
g_free (blu_p_row);
|
|
|
|
g_free (src_buf);
|
|
|
|
g_free (dest_buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
|
|
|
delete_median_cut (QuantizeObj *quantobj)
|
|
|
|
{
|
|
|
|
g_free (quantobj->histogram);
|
|
|
|
g_free (quantobj);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**************************************************************/
|
|
|
|
|
|
|
|
|
|
|
|
static QuantizeObj*
|
|
|
|
initialize_median_cut (int type,
|
|
|
|
int num_colors,
|
1999-09-01 05:26:34 +08:00
|
|
|
ConvertDitherType dither_type,
|
|
|
|
ConvertPaletteType palette_type,
|
1999-08-29 07:40:35 +08:00
|
|
|
int want_alpha_dither)
|
1997-11-25 06:05:25 +08:00
|
|
|
{
|
|
|
|
QuantizeObj * quantobj;
|
|
|
|
|
|
|
|
/* Initialize the data structures */
|
|
|
|
quantobj = g_malloc (sizeof (QuantizeObj));
|
|
|
|
|
2001-12-11 23:58:07 +08:00
|
|
|
if (type == GIMP_GRAY && palette_type == MAKE_PALETTE)
|
1997-11-25 06:05:25 +08:00
|
|
|
quantobj->histogram = g_malloc (sizeof (ColorFreq) * 256);
|
|
|
|
else
|
|
|
|
quantobj->histogram = g_malloc (sizeof (ColorFreq) *
|
|
|
|
HIST_R_ELEMS *
|
|
|
|
HIST_G_ELEMS *
|
|
|
|
HIST_B_ELEMS);
|
|
|
|
|
|
|
|
quantobj->desired_number_of_colors = num_colors;
|
1999-08-29 07:40:35 +08:00
|
|
|
quantobj->want_alpha_dither = want_alpha_dither;
|
1997-11-25 06:05:25 +08:00
|
|
|
|
|
|
|
switch (type)
|
|
|
|
{
|
2001-12-11 23:58:07 +08:00
|
|
|
case GIMP_GRAY:
|
1997-11-25 06:05:25 +08:00
|
|
|
switch (palette_type)
|
|
|
|
{
|
|
|
|
case MAKE_PALETTE:
|
|
|
|
quantobj->first_pass = median_cut_pass1_gray;
|
|
|
|
break;
|
|
|
|
case WEB_PALETTE:
|
|
|
|
quantobj->first_pass = webpal_pass1;
|
|
|
|
break;
|
1997-12-14 19:36:53 +08:00
|
|
|
case CUSTOM_PALETTE:
|
|
|
|
quantobj->first_pass = custompal_pass1;
|
|
|
|
needs_quantize=TRUE;
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
case MONO_PALETTE:
|
|
|
|
default:
|
|
|
|
quantobj->first_pass = monopal_pass1;
|
|
|
|
}
|
|
|
|
if (palette_type == WEB_PALETTE ||
|
1997-12-14 19:36:53 +08:00
|
|
|
palette_type == MONO_PALETTE || palette_type == CUSTOM_PALETTE)
|
1997-11-25 06:05:25 +08:00
|
|
|
switch (dither_type)
|
|
|
|
{
|
1999-09-02 04:41:10 +08:00
|
|
|
case NODESTRUCT_DITHER:
|
|
|
|
default:
|
|
|
|
g_warning("Uh-oh, bad dither type, W1");
|
|
|
|
case NO_DITHER:
|
1999-08-29 07:40:35 +08:00
|
|
|
quantobj->second_pass_init = median_cut_pass2_rgb_init;
|
1997-11-25 06:05:25 +08:00
|
|
|
quantobj->second_pass = median_cut_pass2_no_dither_rgb;
|
|
|
|
break;
|
1999-09-02 04:41:10 +08:00
|
|
|
case 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 FSLOWBLEED_DITHER:
|
|
|
|
quantobj->error_freedom = 1;
|
1999-08-29 07:40:35 +08:00
|
|
|
quantobj->second_pass_init = median_cut_pass2_rgb_init;
|
1997-11-25 06:05:25 +08:00
|
|
|
quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
|
|
|
|
break;
|
1999-09-02 04:41:10 +08:00
|
|
|
case FIXED_DITHER:
|
1999-08-30 00:54:39 +08:00
|
|
|
quantobj->second_pass_init = median_cut_pass2_rgb_init;
|
|
|
|
quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
switch (dither_type)
|
|
|
|
{
|
1999-09-02 04:41:10 +08:00
|
|
|
case NODESTRUCT_DITHER:
|
|
|
|
default:
|
|
|
|
g_warning("Uh-oh, bad dither type, W2");
|
|
|
|
case NO_DITHER:
|
1999-08-30 00:54:39 +08:00
|
|
|
quantobj->second_pass_init = median_cut_pass2_gray_init;
|
1997-11-25 06:05:25 +08:00
|
|
|
quantobj->second_pass = median_cut_pass2_no_dither_gray;
|
|
|
|
break;
|
1999-09-02 04:41:10 +08:00
|
|
|
case FS_DITHER:
|
|
|
|
quantobj->error_freedom = 0;
|
1999-08-30 00:54:39 +08:00
|
|
|
quantobj->second_pass_init = median_cut_pass2_gray_init;
|
1997-11-25 06:05:25 +08:00
|
|
|
quantobj->second_pass = median_cut_pass2_fs_dither_gray;
|
|
|
|
break;
|
1999-09-02 04:41:10 +08:00
|
|
|
case FSLOWBLEED_DITHER:
|
|
|
|
quantobj->error_freedom = 1;
|
|
|
|
quantobj->second_pass_init = median_cut_pass2_gray_init;
|
|
|
|
quantobj->second_pass = median_cut_pass2_fs_dither_gray;
|
|
|
|
break;
|
|
|
|
case FIXED_DITHER:
|
1999-08-30 00:54:39 +08:00
|
|
|
quantobj->second_pass_init = median_cut_pass2_gray_init;
|
|
|
|
quantobj->second_pass = median_cut_pass2_fixed_dither_gray;
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
break;
|
2001-12-11 23:58:07 +08:00
|
|
|
case GIMP_RGB:
|
1997-11-25 06:05:25 +08:00
|
|
|
switch (palette_type)
|
|
|
|
{
|
|
|
|
case MAKE_PALETTE:
|
|
|
|
quantobj->first_pass = median_cut_pass1_rgb;
|
|
|
|
break;
|
|
|
|
case WEB_PALETTE:
|
|
|
|
quantobj->first_pass = webpal_pass1;
|
1997-12-14 19:36:53 +08:00
|
|
|
needs_quantize=TRUE;
|
|
|
|
break;
|
|
|
|
case CUSTOM_PALETTE:
|
|
|
|
quantobj->first_pass = custompal_pass1;
|
|
|
|
needs_quantize=TRUE;
|
1997-11-25 06:05:25 +08:00
|
|
|
break;
|
|
|
|
case MONO_PALETTE:
|
|
|
|
default:
|
|
|
|
quantobj->first_pass = monopal_pass1;
|
|
|
|
}
|
|
|
|
switch (dither_type)
|
|
|
|
{
|
1999-09-02 04:41:10 +08:00
|
|
|
case NO_DITHER:
|
1999-08-29 07:40:35 +08:00
|
|
|
quantobj->second_pass_init = median_cut_pass2_rgb_init;
|
1997-11-25 06:05:25 +08:00
|
|
|
quantobj->second_pass = median_cut_pass2_no_dither_rgb;
|
|
|
|
break;
|
1999-09-02 04:41:10 +08:00
|
|
|
case 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 FSLOWBLEED_DITHER:
|
|
|
|
quantobj->error_freedom = 1;
|
1999-08-29 07:40:35 +08:00
|
|
|
quantobj->second_pass_init = median_cut_pass2_rgb_init;
|
1997-11-25 06:05:25 +08:00
|
|
|
quantobj->second_pass = median_cut_pass2_fs_dither_rgb;
|
|
|
|
break;
|
1999-09-02 04:41:10 +08:00
|
|
|
case NODESTRUCT_DITHER:
|
1999-08-29 07:40:35 +08:00
|
|
|
quantobj->second_pass_init = NULL;
|
1997-11-25 06:05:25 +08:00
|
|
|
quantobj->second_pass = median_cut_pass2_nodestruct_dither_rgb;
|
|
|
|
break;
|
1999-09-02 04:41:10 +08:00
|
|
|
case FIXED_DITHER:
|
1999-08-30 00:54:39 +08:00
|
|
|
quantobj->second_pass_init = median_cut_pass2_rgb_init;
|
|
|
|
quantobj->second_pass = median_cut_pass2_fixed_dither_rgb;
|
|
|
|
break;
|
1997-11-25 06:05:25 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
quantobj->delete_func = delete_median_cut;
|
|
|
|
|
|
|
|
return quantobj;
|
|
|
|
}
|