gnome-xcf-thumbnailer r2 - trunk



Author: hadess
Date: Thu May  1 14:32:48 2008
New Revision: 2
URL: http://svn.gnome.org/viewvc/gnome-xcf-thumbnailer?rev=2&view=rev

Log:
2008-05-01  Bastien Nocera  <hadess hadess net>

	* *: First commit!



Added:
   trunk/AUTHORS
   trunk/COPYING
   trunk/ChangeLog
   trunk/MAINTAINERS
   trunk/Makefile.am
   trunk/NEWS
   trunk/README
   trunk/autogen.sh   (contents, props changed)
   trunk/configure.ac
   trunk/enums.c
   trunk/enums.h
   trunk/flatspec.c
   trunk/flatten.c
   trunk/flatten.h
   trunk/gnome-xcf-thumbnailer.c
   trunk/gnome-xcf-thumbnailer.schemas.in
   trunk/io-unix.c
   trunk/options.i
   trunk/palette.c
   trunk/palette.h
   trunk/pixels.c
   trunk/pixels.h
   trunk/scaletab.c
   trunk/table.c
   trunk/utils.c
   trunk/xcf-general.c
   trunk/xcf2png.c
   trunk/xcf2png.oi
   trunk/xcftools.h

Added: trunk/AUTHORS
==============================================================================
--- (empty file)
+++ trunk/AUTHORS	Thu May  1 14:32:48 2008
@@ -0,0 +1,2 @@
+Bastien Nocera <hadess hadess net>
+Henning Makholm <henning makholm net>

Added: trunk/COPYING
==============================================================================
--- (empty file)
+++ trunk/COPYING	Thu May  1 14:32:48 2008
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.

Added: trunk/MAINTAINERS
==============================================================================
--- (empty file)
+++ trunk/MAINTAINERS	Thu May  1 14:32:48 2008
@@ -0,0 +1,4 @@
+Bastien Nocera
+Email: hadess hadess net
+Userid: hadess
+

Added: trunk/Makefile.am
==============================================================================
--- (empty file)
+++ trunk/Makefile.am	Thu May  1 14:32:48 2008
@@ -0,0 +1,45 @@
+
+bin_PROGRAMS = gnome-xcf-thumbnailer
+
+gnome_xcf_thumbnailer_SOURCES =					\
+	gnome-xcf-thumbnailer.c					\
+	enums.c enums.h						\
+	flatspec.c						\
+	flatten.c flatten.h					\
+	io-unix.c						\
+	palette.c palette.h					\
+	pixels.c pixels.h					\
+	scaletab.c						\
+	table.c							\
+	utils.c							\
+	xcf-general.c						\
+	xcftools.h						\
+	options.i xcf2png.oi
+
+gnome_xcf_thumbnailer_CPPFLAGS = $(LIBPNG_CFLAGS) $(GIO_CFLAGS) -DOPTSTRING=\"h\"
+
+gnome_xcf_thumbnailer_LDADD = $(LIBPNG_LIBS) $(GIO_LIBS) -lpopt
+
+
+schemasdir = $(GCONF_SCHEMA_FILE_DIR)
+schemas_in_files = gnome-xcf-thumbnailer.schemas.in
+schemas_DATA = gnome-xcf-thumbnailer.schemas
+
+# Rule to make the service file with bindir expanded
+$(schemas_DATA): $(schemas_in_files) Makefile
+	@$(SED) -e "s|\ bindir\@|$(bindir)|" $< > $@
+
+if GCONF_SCHEMAS_INSTALL
+install-data-local:
+	if test -z "$(DESTDIR)" ; then \
+          for p in $(schemas_DATA) ; do \
+            GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) $(GCONFTOOL) --makefile-install-rule $$p ; \
+          done \
+        fi
+else
+install-data-local:
+endif
+
+EXTRA_DIST = gnome-xcf-thumbnailer.schemas.in AUTHORS COPYING README xcf2png.c
+
+CLEANFILES = gnome-xcf-thumbnailer.schemas

Added: trunk/NEWS
==============================================================================
--- (empty file)
+++ trunk/NEWS	Thu May  1 14:32:48 2008
@@ -0,0 +1,5 @@
+1.0
+---
+
+* First release
+

Added: trunk/README
==============================================================================
--- (empty file)
+++ trunk/README	Thu May  1 14:32:48 2008
@@ -0,0 +1,8 @@
+Thumbnailer for GIMP's own format, XCF files
+
+License and copyright:
+All the *.c and *.h files except gnome-xcf-thumbnailer.c are from
+xcftools 1.0.4 by Henning Makholm <henning makholm net>
+http://henning.makholm.net/software
+
+

Added: trunk/autogen.sh
==============================================================================
--- (empty file)
+++ trunk/autogen.sh	Thu May  1 14:32:48 2008
@@ -0,0 +1,20 @@
+#!/bin/sh
+# Run this to generate all the initial makefiles, etc.
+
+srcdir=`dirname $0`
+test -z "$srcdir" && srcdir=.
+
+PKG_NAME="gnome-nds-thumbnailer"
+
+(test -f $srcdir/configure.ac) || {
+    echo -n "**Error**: Directory "\`$srcdir\'" does not look like the"
+    echo " top-level $PKG_NAME directory"
+    exit 1
+}
+
+which gnome-autogen.sh || {
+	echo "You need to install gnome-common from the GNOME SVN"
+	exit 1
+}
+
+REQUIRED_PKG_CONFIG_VERSION=0.17.1 REQUIRED_AUTOMAKE_VERSION=1.9 USE_GNOME2_MACROS=1 . gnome-autogen.sh

Added: trunk/configure.ac
==============================================================================
--- (empty file)
+++ trunk/configure.ac	Thu May  1 14:32:48 2008
@@ -0,0 +1,31 @@
+AC_PREREQ(2.59)
+
+AC_INIT([gnome-xcf-thumbnailer], [1.0], [hadess hadess net])
+AM_INIT_AUTOMAKE(1.9 dist-bzip2 no-dist-gzip check-news)
+AC_CONFIG_HEADERS([config.h])
+
+AC_PROG_CC
+AC_PROG_MAKE_SET
+AC_PROG_INSTALL
+AC_PROG_SED
+AM_PROG_CC_C_O
+AC_FUNC_MMAP
+
+AC_CHECK_HEADERS(inttypes.h getopt.h)
+
+PKG_CHECK_MODULES(LIBPNG, libpng, [], [AC_MSG_FAILURE([You need libpng's development package installed])])
+AC_SUBST(LIBPNG_CFLAGS)
+AC_SUBST(LIBPNG_LIBS)
+
+PKG_CHECK_MODULES(GIO, gio-2.0, [], [AC_MSG_FAILURE([You need glib 2.16 or newer installed])])
+
+AC_PATH_PROG(GCONFTOOL, gconftool-2, no)
+
+if test x$GCONFTOOL = "xno"; then
+  AC_MSG_FAILURE([gconftool-2 is needed])
+fi
+
+AM_GCONF_SOURCE_2
+
+AC_CONFIG_FILES([Makefile])
+AC_OUTPUT

Added: trunk/enums.c
==============================================================================
--- (empty file)
+++ trunk/enums.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,116 @@
+/* Autogenerated from enums.h */
+#include "enums.h"
+#define N_
+#include <stdio.h>
+const char*
+showGimpLayerModeEffects(GimpLayerModeEffects x)
+{
+  static char buf[35];
+  switch(x) {
+    case GIMP_NORMAL_MODE: return N_("Normal");
+    case GIMP_DISSOLVE_MODE: return N_("Dissolve");
+    case GIMP_BEHIND_MODE: return N_("Behind");
+    case GIMP_MULTIPLY_MODE: return N_("Multiply");
+    case GIMP_SCREEN_MODE: return N_("Screen");
+    case GIMP_OVERLAY_MODE: return N_("Overlay");
+    case GIMP_DIFFERENCE_MODE: return N_("Difference");
+    case GIMP_ADDITION_MODE: return N_("Addition");
+    case GIMP_SUBTRACT_MODE: return N_("Subtract");
+    case GIMP_DARKEN_ONLY_MODE: return N_("DarkenOnly");
+    case GIMP_LIGHTEN_ONLY_MODE: return N_("LightenOnly");
+    case GIMP_HUE_MODE: return N_("Hue");
+    case GIMP_SATURATION_MODE: return N_("Saturation");
+    case GIMP_COLOR_MODE: return N_("Color");
+    case GIMP_VALUE_MODE: return N_("Value");
+    case GIMP_DIVIDE_MODE: return N_("Divide");
+    case GIMP_DODGE_MODE: return N_("Dodge");
+    case GIMP_BURN_MODE: return N_("Burn");
+    case GIMP_HARDLIGHT_MODE: return N_("Hardlight");
+    case GIMP_SOFTLIGHT_MODE: return N_("Softlight");
+    case GIMP_GRAIN_EXTRACT_MODE: return N_("GrainExtract");
+    case GIMP_GRAIN_MERGE_MODE: return N_("GrainMerge");
+    case GIMP_COLOR_ERASE_MODE: return N_("ColorErase");
+    case GIMP_ERASE_MODE: return N_("Erase");
+    case GIMP_REPLACE_MODE: return N_("Replace");
+    case GIMP_ANTI_ERASE_MODE: return N_("AntiErase");
+    case GIMP_NORMAL_NOPARTIAL_MODE: return N_("NormalNopartial");
+    default: sprintf(buf,"(GimpLayerModeEffects:%d)",(int)x);
+             return buf;
+  }
+}
+const char*
+showGimpImageBaseType(GimpImageBaseType x)
+{
+  static char buf[32];
+  switch(x) {
+    case GIMP_RGB: return N_("RGB color");
+    case GIMP_GRAY: return N_("Grayscale");
+    case GIMP_INDEXED: return N_("Indexed color");
+    default: sprintf(buf,"(GimpImageBaseType:%d)",(int)x);
+             return buf;
+  }
+}
+const char*
+showGimpImageType(GimpImageType x)
+{
+  static char buf[28];
+  switch(x) {
+    case GIMP_RGB_IMAGE: return N_("RGB");
+    case GIMP_RGBA_IMAGE: return N_("RGB-alpha");
+    case GIMP_GRAY_IMAGE: return N_("Grayscale");
+    case GIMP_GRAYA_IMAGE: return N_("Grayscale-alpha");
+    case GIMP_INDEXED_IMAGE: return N_("Indexed");
+    case GIMP_INDEXEDA_IMAGE: return N_("Indexed-alpha");
+    default: sprintf(buf,"(GimpImageType:%d)",(int)x);
+             return buf;
+  }
+}
+const char*
+showPropType(PropType x)
+{
+  static char buf[23];
+  switch(x) {
+    case PROP_END: return ("End");
+    case PROP_COLORMAP: return ("Colormap");
+    case PROP_ACTIVE_LAYER: return ("ActiveLayer");
+    case PROP_ACTIVE_CHANNEL: return ("ActiveChannel");
+    case PROP_SELECTION: return ("Selection");
+    case PROP_FLOATING_SELECTION: return ("FloatingSelection");
+    case PROP_OPACITY: return ("Opacity");
+    case PROP_MODE: return ("Mode");
+    case PROP_VISIBLE: return ("Visible");
+    case PROP_LINKED: return ("Linked");
+    case PROP_PRESERVE_TRANSPARENCY: return ("PreserveTransparency");
+    case PROP_APPLY_MASK: return ("ApplyMask");
+    case PROP_EDIT_MASK: return ("EditMask");
+    case PROP_SHOW_MASK: return ("ShowMask");
+    case PROP_SHOW_MASKED: return ("ShowMasked");
+    case PROP_OFFSETS: return ("Offsets");
+    case PROP_COLOR: return ("Color");
+    case PROP_COMPRESSION: return ("Compression");
+    case PROP_GUIDES: return ("Guides");
+    case PROP_RESOLUTION: return ("Resolution");
+    case PROP_TATTOO: return ("Tattoo");
+    case PROP_PARASITES: return ("Parasites");
+    case PROP_UNIT: return ("Unit");
+    case PROP_PATHS: return ("Paths");
+    case PROP_USER_UNIT: return ("UserUnit");
+    case PROP_VECTORS: return ("Vectors");
+    case PROP_TEXT_LAYER_FLAGS: return ("TextLayerFlags");
+    default: sprintf(buf,"(PropType:%d)",(int)x);
+             return buf;
+  }
+}
+const char*
+showXcfCompressionType(XcfCompressionType x)
+{
+  static char buf[33];
+  switch(x) {
+    case COMPRESS_NONE: return N_("None");
+    case COMPRESS_RLE: return N_("RLE");
+    case COMPRESS_ZLIB: return N_("Zlib");
+    case COMPRESS_FRACTAL: return N_("Fractal");
+    default: sprintf(buf,"(XcfCompressionType:%d)",(int)x);
+             return buf;
+  }
+}

Added: trunk/enums.h
==============================================================================
--- (empty file)
+++ trunk/enums.h	Thu May  1 14:32:48 2008
@@ -0,0 +1,98 @@
+/* Extracted from
+ *   gimp/base-enums.h
+ *   gimp/gimpbaseenums.h
+ *   gimp/xcf-private.h
+ * by mkenumsh.pl
+ */
+typedef enum
+{
+  GIMP_NORMAL_MODE,
+  GIMP_DISSOLVE_MODE,
+  GIMP_BEHIND_MODE,
+  GIMP_MULTIPLY_MODE,
+  GIMP_SCREEN_MODE,
+  GIMP_OVERLAY_MODE,
+  GIMP_DIFFERENCE_MODE,
+  GIMP_ADDITION_MODE,
+  GIMP_SUBTRACT_MODE,
+  GIMP_DARKEN_ONLY_MODE,
+  GIMP_LIGHTEN_ONLY_MODE,
+  GIMP_HUE_MODE,
+  GIMP_SATURATION_MODE,
+  GIMP_COLOR_MODE,
+  GIMP_VALUE_MODE,
+  GIMP_DIVIDE_MODE,
+  GIMP_DODGE_MODE,
+  GIMP_BURN_MODE,
+  GIMP_HARDLIGHT_MODE,
+  GIMP_SOFTLIGHT_MODE,
+  GIMP_GRAIN_EXTRACT_MODE,
+  GIMP_GRAIN_MERGE_MODE,
+  GIMP_COLOR_ERASE_MODE,
+  GIMP_ERASE_MODE,           /*< pdb-skip, skip >*/
+  GIMP_REPLACE_MODE,         /*< pdb-skip, skip >*/
+  GIMP_ANTI_ERASE_MODE       /*< pdb-skip, skip >*/
+  ,GIMP_NORMAL_NOPARTIAL_MODE=-1
+} GimpLayerModeEffects;
+const char *showGimpLayerModeEffects(GimpLayerModeEffects);
+#define GimpLayerModeEffects_LAST GIMP_ANTI_ERASE_MODE
+typedef enum
+{
+  GIMP_RGB,     /*< desc="RGB color"     >*/
+  GIMP_GRAY,    /*< desc="Grayscale"     >*/
+  GIMP_INDEXED  /*< desc="Indexed color" >*/
+} GimpImageBaseType;
+const char *showGimpImageBaseType(GimpImageBaseType);
+#define GimpImageBaseType_LAST GIMP_INDEXED
+typedef enum
+{
+  GIMP_RGB_IMAGE,      /*< desc="RGB"             >*/
+  GIMP_RGBA_IMAGE,     /*< desc="RGB-alpha"       >*/
+  GIMP_GRAY_IMAGE,     /*< desc="Grayscale"       >*/
+  GIMP_GRAYA_IMAGE,    /*< desc="Grayscale-alpha" >*/
+  GIMP_INDEXED_IMAGE,  /*< desc="Indexed"         >*/
+  GIMP_INDEXEDA_IMAGE  /*< desc="Indexed-alpha"   >*/
+} GimpImageType;
+const char *showGimpImageType(GimpImageType);
+#define GimpImageType_LAST GIMP_INDEXEDA_IMAGE
+typedef enum
+{
+  PROP_END                   =  0,
+  PROP_COLORMAP              =  1,
+  PROP_ACTIVE_LAYER          =  2,
+  PROP_ACTIVE_CHANNEL        =  3,
+  PROP_SELECTION             =  4,
+  PROP_FLOATING_SELECTION    =  5,
+  PROP_OPACITY               =  6,
+  PROP_MODE                  =  7,
+  PROP_VISIBLE               =  8,
+  PROP_LINKED                =  9,
+  PROP_PRESERVE_TRANSPARENCY = 10,
+  PROP_APPLY_MASK            = 11,
+  PROP_EDIT_MASK             = 12,
+  PROP_SHOW_MASK             = 13,
+  PROP_SHOW_MASKED           = 14,
+  PROP_OFFSETS               = 15,
+  PROP_COLOR                 = 16,
+  PROP_COMPRESSION           = 17,
+  PROP_GUIDES                = 18,
+  PROP_RESOLUTION            = 19,
+  PROP_TATTOO                = 20,
+  PROP_PARASITES             = 21,
+  PROP_UNIT                  = 22,
+  PROP_PATHS                 = 23,
+  PROP_USER_UNIT             = 24,
+  PROP_VECTORS               = 25,
+  PROP_TEXT_LAYER_FLAGS      = 26
+} PropType;
+const char *showPropType(PropType);
+#define PropType_LAST PROP_TEXT_LAYER_FLAGS
+typedef enum
+{
+  COMPRESS_NONE              =  0,
+  COMPRESS_RLE               =  1,
+  COMPRESS_ZLIB              =  2,  /* unused */
+  COMPRESS_FRACTAL           =  3   /* unused */
+} XcfCompressionType;
+const char *showXcfCompressionType(XcfCompressionType);
+#define XcfCompressionType_LAST COMPRESS_FRACTAL

Added: trunk/flatspec.c
==============================================================================
--- (empty file)
+++ trunk/flatspec.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,370 @@
+/* Flattening selections function for xcftools
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "xcftools.h"
+#include "flatten.h"
+#include <string.h>
+#include <stdlib.h>
+
+void
+init_flatspec(struct FlattenSpec *spec)
+{
+  spec->window_mode = USE_CANVAS ;
+  spec->default_pixel = PERHAPS_ALPHA_CHANNEL ;
+  spec->numLayers = 0 ;
+  spec->layers = NULL ;
+  spec->transmap_filename = NULL ;
+  spec->output_filename = "-" ;
+  spec->out_color_mode = COLOR_BY_CONTENTS ;
+  spec->partial_transparency_mode = ALLOW_PARTIAL_TRANSPARENCY ;
+  spec->process_in_memory = 0 ;
+  spec->gimpish_indexed = 1 ;
+}
+
+void
+add_layer_request(struct FlattenSpec *spec, const char *layer)
+{
+  spec->layers = realloc(spec->layers,
+                         sizeof(struct xcfLayer) * (1+spec->numLayers));
+  if( spec->layers == NULL )
+    FatalUnexpected(_("Out of memory"));
+  spec->layers[spec->numLayers].name = layer ;
+  spec->layers[spec->numLayers].mode = (GimpLayerModeEffects)-1 ;
+  spec->layers[spec->numLayers].opacity = 9999 ;
+  spec->layers[spec->numLayers].hasMask = -1 ;
+  spec->numLayers++ ;
+}
+
+struct xcfLayer *
+lastlayerspec(struct FlattenSpec *spec,const char *option)
+{
+  if( spec->numLayers == 0 )
+    FatalGeneric(20,_("The %s option must follow a layer name on the "
+                      "command line"),option);
+  return spec->layers + (spec->numLayers-1) ;
+}
+
+static int
+typeHasTransparency(GimpImageType type)
+{
+  switch( type ) {
+  case GIMP_RGB_IMAGE:
+  case GIMP_GRAY_IMAGE:
+  case GIMP_INDEXED_IMAGE:
+    return 0 ;
+  case GIMP_RGBA_IMAGE:
+  case GIMP_GRAYA_IMAGE:
+  case GIMP_INDEXEDA_IMAGE:
+    return 1 ;
+  }
+  return 1 ;
+}
+
+static enum out_color_mode
+color_by_layers(struct FlattenSpec *spec)
+{
+  int colormap_is_colored = 0 ;
+  enum out_color_mode grayish = COLOR_MONO ;
+  int i ;
+
+  if( spec->default_pixel == CHECKERED_BACKGROUND )
+    grayish = COLOR_GRAY ;
+  else if( degrayPixel(spec->default_pixel) < 0 )
+    return COLOR_RGB ;
+  for( i=0; i<colormapLength; i++ ) {
+    if( colormap[i] == NEWALPHA(0,0) || colormap[i] == NEWALPHA(-1,0) )
+      continue ;
+    if( degrayPixel(colormap[i]) == -1 ) {
+      colormap_is_colored = 1 ;
+      break ;
+    } else {
+      grayish = COLOR_GRAY ;
+    }
+  }
+  for( i=0; i<spec->numLayers; i++ )
+    switch( spec->layers[i].type ) {
+    case GIMP_RGB_IMAGE:
+    case GIMP_RGBA_IMAGE:
+      return COLOR_RGB ;
+    case GIMP_GRAY_IMAGE:
+    case GIMP_GRAYA_IMAGE:
+      grayish = COLOR_GRAY ;
+      break ;
+    case GIMP_INDEXED_IMAGE:
+    case GIMP_INDEXEDA_IMAGE:
+      if( colormap_is_colored ) return COLOR_RGB ;
+      break ;
+    }
+  return grayish ;
+}
+
+void
+complete_flatspec(struct FlattenSpec *spec, guesser guess_callback)
+{
+  unsigned i ;
+  int anyPartial ;
+
+  /* Find the layers to convert.
+   */
+  if( spec->numLayers == 0 ) {
+    spec->layers = XCF.layers ;
+    spec->numLayers = XCF.numLayers ;
+  } else {
+    for( i=0; i<spec->numLayers; i++ ) {
+      GimpLayerModeEffects mode ;
+      int opacity, hasMask ;
+      unsigned j ;
+
+      for( j=0; ; j++ ) {
+        if( j == XCF.numLayers )
+          FatalGeneric(22,_("The image has no layer called '%s'"),
+                       spec->layers[i].name);
+        if( strcmp(spec->layers[i].name,XCF.layers[j].name) == 0 )
+          break ;
+      }
+      mode = spec->layers[i].mode == (GimpLayerModeEffects)-1 ?
+        XCF.layers[j].mode : spec->layers[i].mode ;
+      opacity = spec->layers[i].opacity == 9999 ?
+        XCF.layers[j].opacity : spec->layers[i].opacity ;
+      hasMask = spec->layers[i].hasMask == -1 ?
+        XCF.layers[j].hasMask : spec->layers[i].hasMask ;
+      if( hasMask && !XCF.layers[j].hasMask &&
+          XCF.layers[j].mask.hierarchy == 0 )
+        FatalGeneric(22,_("Layer '%s' has no layer mask to enable"),
+                     spec->layers[i].name);
+      spec->layers[i] = XCF.layers[j] ;
+      spec->layers[i].mode = mode ;
+      spec->layers[i].opacity = opacity ;
+      spec->layers[i].hasMask = hasMask ;
+      spec->layers[i].isVisible = 1 ;
+    }
+  }
+
+  /* Force the mode of the lowest visible layer to be Normal or Dissolve.
+   * That may not be logical, but the Gimp does it
+   */
+  for( i=0; i < spec->numLayers; i++ ) {
+    if( spec->layers[i].isVisible ) {
+      if( spec->layers[i].mode != GIMP_DISSOLVE_MODE )
+        spec->layers[i].mode = GIMP_NORMAL_MODE ;
+      break ;
+    }
+  }
+
+  /* Mimic the Gimp's behavior on indexed layers */
+  if( XCF.type == GIMP_INDEXED && spec->gimpish_indexed ) {
+    for( i=0; i<spec->numLayers; i++ )
+      if( spec->layers[i].mode != GIMP_DISSOLVE_MODE )
+        spec->layers[i].mode = GIMP_NORMAL_NOPARTIAL_MODE ;
+  } else
+    spec->gimpish_indexed = 0 ;
+
+  /* compute dimensions of the window */
+  if( spec->window_mode == AUTOCROP ) {
+    int first = 1 ;
+    for( i=0; i<spec->numLayers; i++ )
+      if( spec->layers[i].isVisible ) {
+        computeDimensions(&spec->layers[i].dim) ;
+        if( first ) {
+          spec->dim = spec->layers[i].dim ;
+          first = 0 ;
+        } else {
+          if( spec->dim.c.l < spec->layers[i].dim.c.l )
+            spec->dim.c.l = spec->layers[i].dim.c.l ;
+          if( spec->dim.c.r > spec->layers[i].dim.c.r )
+            spec->dim.c.r = spec->layers[i].dim.c.r ;
+          if( spec->dim.c.t < spec->layers[i].dim.c.t )
+            spec->dim.c.t = spec->layers[i].dim.c.t ;
+          if( spec->dim.c.b > spec->layers[i].dim.c.b )
+            spec->dim.c.b = spec->layers[i].dim.c.b ;
+        }
+      }
+    if( first ) {
+      spec->window_mode = USE_CANVAS ;
+    } else {
+      spec->dim.width = spec->dim.c.r - spec->dim.c.l ;
+      spec->dim.height = spec->dim.c.b - spec->dim.c.t ;
+    }
+  }
+  if( spec->window_mode != AUTOCROP ) {
+    if( (spec->window_mode & MANUAL_OFFSET) == 0 )
+      spec->dim.c.t = spec->dim.c.l = 0 ;
+    if( (spec->window_mode & MANUAL_CROP) == 0 ) {      
+      spec->dim.height = XCF.height ;
+      spec->dim.width = XCF.width ;
+    }
+  }
+  computeDimensions(&spec->dim);
+  
+  /* Turn off layers that we don't hit at all */
+  for( i=0; i<spec->numLayers; i++ )
+    if( spec->layers[i].isVisible &&
+        disjointRects(spec->dim.c,spec->layers[i].dim.c) )
+      spec->layers[i].isVisible = 0 ;
+  
+  /* See if there is a completely covering layer somewhere in the stack */
+  /* Also check if partial transparency is possible */
+  anyPartial = 0 ;
+  for( i=spec->numLayers; i-- ; ) {
+    if( !spec->layers[i].isVisible )
+      continue ;
+    if( typeHasTransparency(spec->layers[i].type) ) {
+      if( spec->layers[i].mode == GIMP_NORMAL_MODE )
+        anyPartial = 1;
+    } else if( isSubrect(spec->dim.c,spec->layers[i].dim.c) &&
+             (spec->layers[i].mode == GIMP_NORMAL_MODE ||
+              spec->layers[i].mode == GIMP_NORMAL_NOPARTIAL_MODE ||
+              spec->layers[i].mode == GIMP_DISSOLVE_MODE) ) {
+      /* This layer fills out the entire image.
+       * Turn off anly lower layers, and note that we cannot have
+       * transparency at all.
+       */
+      while(i) spec->layers[--i].isVisible = 0 ;
+      if( spec->default_pixel != FORCE_ALPHA_CHANNEL )
+        spec->default_pixel = NEWALPHA(colormap[0],255);
+      anyPartial = 0 ;
+      break ;
+    }
+  }
+  if( spec->partial_transparency_mode == ALLOW_PARTIAL_TRANSPARENCY &&
+      (!anyPartial || ALPHA(spec->default_pixel) >= 128) )
+    spec->partial_transparency_mode = PARTIAL_TRANSPARENCY_IMPOSSIBLE ;
+
+  /* Initialize layers and print overview if we're verbose */
+  for( i=spec->numLayers; i--; )
+    if( spec->layers[i].isVisible ) {
+      initLayer(&spec->layers[i]) ;
+      if( verboseFlag ) {
+        fprintf(stderr,"%dx%d%+d%+d %s %s",
+                spec->layers[i].dim.width, spec->layers[i].dim.height,
+                spec->layers[i].dim.c.l - spec->dim.c.l,
+                spec->layers[i].dim.c.t - spec->dim.c.t,
+                _(showGimpImageType(spec->layers[i].type)),
+                _(showGimpLayerModeEffects(spec->layers[i].mode)));
+        if( spec->layers[i].opacity < 255 )
+          fprintf(stderr,"/%02d%%",spec->layers[i].opacity * 100 / 255);
+        if( XCF.layers[i].hasMask )
+          fprintf(stderr,_("/mask"));
+        fprintf(stderr," %s\n",spec->layers[i].name);
+      }
+    }
+  
+  /* Resolve color mode unless we wait until we have the entire image */
+  if( spec->out_color_mode == COLOR_BY_CONTENTS &&
+      !spec->process_in_memory ) {
+    if( guess_callback )
+      spec->out_color_mode = guess_callback(spec,NULL);
+    if( spec->out_color_mode == COLOR_BY_CONTENTS )
+      spec->out_color_mode = color_by_layers(spec) ;
+  }
+}
+
+void
+analyse_colormode(struct FlattenSpec *spec,rgba **allPixels,
+                  guesser guess_callback)
+{
+  unsigned x,y ;
+  int status ;
+  /* 8 - looking for any transparency
+   * 4 - looking for partially transparent pixels
+   * 2 - looking for pixels other than black and white
+   * 1 - looking for colored pixels
+   */
+  int known_absent = 0 ;
+  int assume_present = 0 ;
+
+  if( spec->out_color_mode == COLOR_BY_CONTENTS && guess_callback )
+    spec->out_color_mode = guess_callback(spec,allPixels) ;
+
+  if( spec->out_color_mode == COLOR_RGB     ) assume_present |= 3 ;
+  if( spec->out_color_mode == COLOR_INDEXED ) assume_present |= 3 ;
+  if( spec->out_color_mode == COLOR_GRAY    ) assume_present |= 2 ;
+  switch( color_by_layers(spec) ) {
+  case COLOR_GRAY: known_absent |= 1 ; break ;
+  case COLOR_MONO: known_absent |= 3 ; break ;
+  default: break ;
+  }
+  if( spec->partial_transparency_mode == DISSOLVE_PARTIAL_TRANSPARENCY ||
+      spec->partial_transparency_mode == PARTIAL_TRANSPARENCY_IMPOSSIBLE )
+    known_absent |= 4 ;
+  if( ALPHA(spec->default_pixel) >= 128 )               known_absent |= 12 ;
+  else if( spec->default_pixel == FORCE_ALPHA_CHANNEL ) assume_present |= 8 ;
+
+  status = 15 - (known_absent | assume_present) ;
+  
+  for( y=0; status && y<spec->dim.height; y++ ) {
+    rgba *row = allPixels[y] ;
+    if( (status & 3) != 0 ) {
+      /* We're still interested in color */
+      for( x=0; status && x<spec->dim.width; x++ ) {
+        if( NULLALPHA(row[x]) )
+          status &= ~8 ;
+        else {
+          rgba full = row[x] | (255 << ALPHA_SHIFT) ;
+          if( !FULLALPHA(row[x]) ) status &= ~12 ;
+          if( full == NEWALPHA(0,255) || full == NEWALPHA(-1,255) )
+            /* Black or white */ ;
+          else if( degrayPixel(row[x]) != -1 )
+            status &= ~2 ; /* gray */
+          else
+            status &= ~3 ; /* color */
+        }
+      }
+    } else {
+      /* Not interested in color */
+      for( x=0; status && x<spec->dim.width; x++ ) {
+        if( NULLALPHA(row[x]) )
+          status &= ~8 ;
+        else if( !FULLALPHA(row[x]) )
+          status &= ~12 ;
+      }
+    }
+  }
+
+  status |= known_absent ;
+  
+  switch( spec->out_color_mode ) {
+  case COLOR_INDEXED: /* The caller takes responsibility */
+  case COLOR_RGB: /* Everything is fine. */
+    break ;
+  case COLOR_GRAY:
+    if( (status & 1) == 0 )
+      FatalGeneric(103,
+                   _("Grayscale output selected, but colored pixel(s) found"));
+    break ;
+  case COLOR_MONO:
+    if( (status & 2) == 0 )
+      FatalGeneric(103,_("Monochrome output selected, but not all pixels "
+                         "are black or white"));
+    break ;
+  case COLOR_BY_FILENAME: /* Should not happen ... */
+  case COLOR_BY_CONTENTS:
+    if( (status & 1) == 0 )
+      spec->out_color_mode = COLOR_RGB ;
+    else if( (status & 2) == 0 )
+      spec->out_color_mode = COLOR_GRAY ;
+    else
+      spec->out_color_mode = COLOR_MONO ;
+    break ;
+  }
+
+  if( (status & 12) == 12 ) /* No transparency found */
+    spec->default_pixel = NEWALPHA(colormap[0],255);
+  else if( (status & 12) == 4 )
+    spec->partial_transparency_mode = PARTIAL_TRANSPARENCY_IMPOSSIBLE ;
+}

Added: trunk/flatten.c
==============================================================================
--- (empty file)
+++ trunk/flatten.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,689 @@
+/* Flattning functions for xcftools
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "xcftools.h"
+#include "flatten.h"
+#include "pixels.h"
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+static rgba __ATTRIBUTE__((noinline,const))
+composite_one(rgba bot,rgba top)
+{
+  unsigned tfrac, alpha ;
+
+  tfrac = ALPHA(top) ;
+  alpha = 255 ;
+  if( !FULLALPHA(bot) ) {
+    alpha = 255 ^ scaletable[255-ALPHA(bot)][255-ALPHA(top)] ;
+    /* This peculiar combination of ^ and - makes the GCC code
+     * generator for i386 particularly happy.
+     */
+    tfrac = (256*ALPHA(top) - 1) / alpha ;
+    /* Tfrac is the fraction of the coposited pixel's covered area
+     * that comes from the top pixel.
+     * For mathematical accuracy we ought to scale by 255 and
+     * subtract alpha/2, but this is faster, and never misses the
+     * true value by more than one 1/255. This effect is completely
+     * overshadowed by the linear interpolation in the first place.
+     * (I.e. gamma is ignored when combining intensities).
+     *   [In any case, complete fairness is not possible: if the
+     *    bottom pixel had alpha=170 and the top has alpha=102,
+     *    each should contribute equally to the color of the
+     *    resulting alpha=204 pixel, which is not possible in general]
+     * Subtracting one helps the topfrac never be 256, which would
+     * be bad.
+     * On the other hand it means that we would get tfrac=-1 if the
+     * top pixel is completely transparent, and we get a division
+     * by zero if _both_ pixels are fully transparent. These cases
+     * must be handled by all callers.
+     *    More snooping in the Gimp sources reveal that it uses
+     *    floating-point for its equivalent of tfrac when the
+     *    bottom layer has an alpha channel. (alphify() macro
+     *    in paint-funcs.c). What gives?
+     */
+  }
+  return (alpha << ALPHA_SHIFT)
+    + ((uint32_t)scaletable[  tfrac  ][255&(top>>RED_SHIFT  )] << RED_SHIFT   )
+    + ((uint32_t)scaletable[  tfrac  ][255&(top>>GREEN_SHIFT)] << GREEN_SHIFT )
+    + ((uint32_t)scaletable[  tfrac  ][255&(top>>BLUE_SHIFT )] << BLUE_SHIFT  )
+    + ((uint32_t)scaletable[255^tfrac][255&(bot>>RED_SHIFT  )] << RED_SHIFT   )
+    + ((uint32_t)scaletable[255^tfrac][255&(bot>>GREEN_SHIFT)] << GREEN_SHIFT )
+    + ((uint32_t)scaletable[255^tfrac][255&(bot>>BLUE_SHIFT )] << BLUE_SHIFT  )
+    ;
+}
+
+/* merge_normal() takes ownership of bot.
+ * merge_normal() will share ownership of top.
+ * Return: may be shared.
+ */
+static struct Tile * __ATTRIBUTE__((noinline))
+merge_normal(struct Tile *bot, struct Tile *top)
+{
+  unsigned i ;
+  assertTileCompatibility(bot,top);
+
+  /* See if there is an easy winner */
+  if( (bot->summary & TILESUMMARY_ALLNULL) ||
+      (top->summary & TILESUMMARY_ALLFULL) ) {
+    freeTile(bot);
+    return top ;
+  }
+  if( top->summary & TILESUMMARY_ALLNULL ) {
+    freeTile(top);
+    return bot ;
+  }
+  
+  /* Try hard to make top win */
+  for( i=0; ; i++ ) {
+    if( i == top->count ) {
+      freeTile(bot);
+      return top ;
+    }
+    if( !(NULLALPHA(bot->pixels[i]) || FULLALPHA(top->pixels[i])) )
+      break ;
+  }
+
+  INIT_SCALETABLE_IF( !(top->summary & TILESUMMARY_CRISP) );
+  
+  /* Otherwise bot wins, but is forever changed ... */
+  if( (top->summary & TILESUMMARY_ALLNULL) == 0 ) {
+    unsigned i ;
+    invalidateSummary(bot,0);
+    for( i=0 ; i < top->count ; i++ ) {
+      if( !NULLALPHA(top->pixels[i]) ) {
+        if( FULLALPHA(top->pixels[i]) || NULLALPHA(bot->pixels[i]) )
+          bot->pixels[i] = top->pixels[i] ;
+        else
+          bot->pixels[i] = composite_one(bot->pixels[i],top->pixels[i]);
+      }
+    }
+  }
+  freeTile(top);
+  return bot ;
+}
+
+#define exotic_combinator static inline unsigned __ATTRIBUTE__((const))
+
+
+
+exotic_combinator
+ucombine_ADDITION(uint8_t bot,uint8_t top)
+{
+  return bot+top > 255 ? 255 : bot+top ;
+}
+
+exotic_combinator
+ucombine_SUBTRACT(uint8_t bot,uint8_t top)
+{
+  return top>bot ? 0 : bot-top ;
+}
+
+exotic_combinator
+ucombine_LIGHTEN_ONLY(uint8_t bot,uint8_t top)
+{
+  return top > bot ? top : bot ;
+}
+
+exotic_combinator
+ucombine_DARKEN_ONLY(uint8_t bot,uint8_t top)
+{
+  return top < bot ? top : bot ;
+}
+
+exotic_combinator
+ucombine_DIFFERENCE(uint8_t bot,uint8_t top)
+{
+  return top > bot ? top-bot : bot-top ;
+}
+
+exotic_combinator
+ucombine_MULTIPLY(uint8_t bot,uint8_t top)
+{
+  return scaletable[bot][top] ;
+}
+
+exotic_combinator
+ucombine_DIVIDE(uint8_t bot,uint8_t top)
+{
+  int result = (int)bot*256 / (1+top) ;
+  return result >= 256 ? 255 : result ;
+}
+
+exotic_combinator
+ucombine_SCREEN(uint8_t bot,uint8_t top)
+{
+  /* An inverted version of "multiply" */
+  return 255 ^ scaletable[255-bot][255-top] ;
+}
+
+exotic_combinator
+ucombine_OVERLAY(uint8_t bot,uint8_t top)
+{
+  return scaletable[bot][bot] +
+    2*scaletable[top][scaletable[bot][255-bot]] ;
+  /* This strange formula is equivalent to
+   *   (1-top)*(bot^2) + top*(1-(1-top)^2)
+   * that is, the top value is used to interpolate between
+   * the self-multiply and the self-screen of the bottom.
+   */
+  /* Note: This is exactly what the "Soft light" effect also
+   * does, though with different code in the Gimp.
+   */
+}
+
+exotic_combinator
+ucombine_DODGE(uint8_t bot,uint8_t top)
+{
+  return ucombine_DIVIDE(bot,255-top);
+}
+
+exotic_combinator
+ucombine_BURN(uint8_t bot,uint8_t top)
+{
+  return 255 - ucombine_DIVIDE(255-bot,top);
+}
+
+exotic_combinator
+ucombine_HARDLIGHT(uint8_t bot,uint8_t top)
+{
+  if( top >= 128 )
+    return 255 ^ scaletable[255-bot][2*(255-top)] ;
+  else
+    return scaletable[bot][2*top];
+  /* The code that implements "hardlight" in Gimp 2.2.10 has some
+   * rounding errors, but this is undoubtedly what is meant.
+   */
+}
+
+exotic_combinator
+ucombine_GRAIN_EXTRACT(uint8_t bot,uint8_t top)
+{
+  int temp = (int)bot - (int)top + 128 ;
+  return temp < 0 ? 0 : temp >= 256 ? 255 : temp ;
+}
+
+exotic_combinator
+ucombine_GRAIN_MERGE(uint8_t bot,uint8_t top)
+{
+  int temp = (int)bot + (int)top - 128 ;
+  return temp < 0 ? 0 : temp >= 256 ? 255 : temp ;
+}
+
+struct HSV {
+  enum { HUE_RED_GREEN_BLUE,HUE_RED_BLUE_GREEN,HUE_BLUE_RED_GREEN,
+         HUE_BLUE_GREEN_RED,HUE_GREEN_BLUE_RED,HUE_GREEN_RED_BLUE } hue;
+  unsigned ch1, ch2, ch3 ;
+};
+
+static void
+RGBtoHSV(rgba rgb,struct HSV *hsv)
+{
+  unsigned RED = (uint8_t)(rgb >> RED_SHIFT);
+  unsigned GREEN = (uint8_t)(rgb >> GREEN_SHIFT);
+  unsigned BLUE = (uint8_t)(rgb >> BLUE_SHIFT) ;
+  #define HEXTANT(b,m,t) hsv->ch1 = b, hsv->ch2 = m, hsv->ch3 = t, \
+                         hsv->hue = HUE_ ## b ## _ ## m ## _ ## t
+  if( GREEN <= RED )
+    if( BLUE <= RED )
+      if( GREEN <= BLUE )
+        HEXTANT(GREEN,BLUE,RED);
+      else
+        HEXTANT(BLUE,GREEN,RED);
+    else
+      HEXTANT(GREEN,RED,BLUE);
+  else if( BLUE <= RED )
+    HEXTANT(BLUE,RED,GREEN);
+  else if( BLUE <= GREEN )
+    HEXTANT(RED,BLUE,GREEN);
+  else
+    HEXTANT(RED,GREEN,BLUE);
+  #undef HEXTANT
+}
+
+/* merge_exotic() destructively updates bot.
+ * merge_exotic() reads but does not free top.
+ */
+static void __ATTRIBUTE__((noinline))
+merge_exotic(struct Tile *bot, const struct Tile *top,
+             GimpLayerModeEffects mode)
+{
+  unsigned i ;
+  assertTileCompatibility(bot,top);
+  if( (bot->summary & TILESUMMARY_ALLNULL) != 0 ) return ;
+  if( (top->summary & TILESUMMARY_ALLNULL) != 0 ) return ;
+  assert( bot->refcount == 1 );
+  /* The transparency status of bot never changes */
+
+  INIT_SCALETABLE_IF(1);
+  
+  for( i=0; i < top->count ; i++ ) {
+    uint32_t RED, GREEN, BLUE ;
+    if( NULLALPHA(bot->pixels[i]) || NULLALPHA(top->pixels[i]) )
+      continue ;
+#define UNIFORM(mode) case GIMP_ ## mode ## _MODE: \
+      RED   = ucombine_ ## mode (bot->pixels[i]>>RED_SHIFT  ,  \
+                                 top->pixels[i]>>RED_SHIFT  ); \
+      GREEN = ucombine_ ## mode (bot->pixels[i]>>GREEN_SHIFT,  \
+                                 top->pixels[i]>>GREEN_SHIFT); \
+      BLUE  = ucombine_ ## mode (bot->pixels[i]>>BLUE_SHIFT ,  \
+                                 top->pixels[i]>>BLUE_SHIFT ); \
+      break ;
+    switch( mode ) {
+    case GIMP_NORMAL_MODE:
+    case GIMP_DISSOLVE_MODE:
+      FatalUnexpected("Normal and Dissolve mode can't happen here!");
+      UNIFORM(ADDITION);
+      UNIFORM(SUBTRACT);
+      UNIFORM(LIGHTEN_ONLY);
+      UNIFORM(DARKEN_ONLY);
+      UNIFORM(DIFFERENCE);
+      UNIFORM(MULTIPLY);
+      UNIFORM(DIVIDE);
+      UNIFORM(SCREEN);
+    case GIMP_SOFTLIGHT_MODE: /* A synonym for "overlay"! */
+      UNIFORM(OVERLAY);
+      UNIFORM(DODGE);
+      UNIFORM(BURN);
+      UNIFORM(HARDLIGHT);
+      UNIFORM(GRAIN_EXTRACT);
+      UNIFORM(GRAIN_MERGE);
+    case GIMP_HUE_MODE:
+    case GIMP_SATURATION_MODE:
+    case GIMP_VALUE_MODE:
+    case GIMP_COLOR_MODE:
+      {
+        static struct HSV hsvTop, hsvBot ;
+        RGBtoHSV(top->pixels[i],&hsvTop);
+        if( mode == GIMP_HUE_MODE && hsvTop.ch1 == hsvTop.ch3 )
+          continue ;
+        RGBtoHSV(bot->pixels[i],&hsvBot);
+        if( mode == GIMP_VALUE_MODE ) {
+          if( hsvBot.ch3 ) {
+            hsvBot.ch1 = (hsvBot.ch1*hsvTop.ch3 + hsvBot.ch3/2) / hsvBot.ch3;
+            hsvBot.ch2 = (hsvBot.ch2*hsvTop.ch3 + hsvBot.ch3/2) / hsvBot.ch3;
+            hsvBot.ch3 = hsvTop.ch3 ;
+          } else {
+            hsvBot.ch1 = hsvBot.ch2 = hsvBot.ch3 = hsvTop.ch3 ;
+          }
+        } else {
+          unsigned mfNum, mfDenom ;
+          if( mode == GIMP_HUE_MODE || mode == GIMP_COLOR_MODE ) {
+            mfNum   = hsvTop.ch2-hsvTop.ch1 ;
+            mfDenom = hsvTop.ch3-hsvTop.ch1 ;
+            hsvBot.hue = hsvTop.hue ;
+          } else {
+            mfNum   = hsvBot.ch2-hsvBot.ch1 ;
+            mfDenom = hsvBot.ch3-hsvBot.ch1 ;
+          }
+          if( mode == GIMP_SATURATION_MODE ) {
+            if( hsvTop.ch3 == 0 )
+              hsvBot.ch1 = hsvBot.ch3 ; /* Black has no saturation */
+            else
+              hsvBot.ch1 = (hsvTop.ch1*hsvBot.ch3 + hsvTop.ch3/2) / hsvTop.ch3;
+          } else if( mode == GIMP_COLOR_MODE ) {
+            /* GIMP_COLOR_MODE works in HSL space instead of HSV. We must
+             * transfer H and S, keeping the L = ch1+ch3 of the bottom pixel,
+             * but the S we transfer works differently from the S in HSV.
+             */
+            unsigned L = hsvTop.ch1 + hsvTop.ch3 ;
+            unsigned sNum = hsvTop.ch3 - hsvTop.ch1 ;
+            unsigned sDenom = L < 256 ? L : 510-L ;
+            if( sDenom == 0 ) sDenom = 1 ; /* sNum will be 0 */
+            L = hsvBot.ch1 + hsvBot.ch3 ;
+            if( L < 256 ) {
+              /* Ideally we want to compute L/2 * (1-sNum/sDenom)
+               * But shuffle this a bit so we can use integer arithmetic.
+               * The "-1" in the rounding prevents us from ending up with
+               * ch1 > ch3.
+               */
+              hsvBot.ch1 = (L*(sDenom-sNum)+sDenom-1)/(2*sDenom);
+              hsvBot.ch3 = L - hsvBot.ch1 ;
+            } else {
+              /* Here our goal is 255 - (510-L)/2 * (1-sNum/sDenom) */
+              hsvBot.ch3 = 255 - ((510-L)*(sDenom-sNum)+sDenom-1)/(2*sDenom);
+              hsvBot.ch1 = L - hsvBot.ch3 ;
+            }
+            assert(hsvBot.ch3 <= 255);
+            assert(hsvBot.ch3 >= hsvBot.ch1);
+          }
+          if( mfDenom == 0 )
+            hsvBot.ch2 = hsvBot.ch1 ;
+          else
+            hsvBot.ch2 = hsvBot.ch1 +
+              (mfNum*(hsvBot.ch3-hsvBot.ch1) + mfDenom/2) / mfDenom ;
+        }
+        switch( hsvBot.hue ) {
+          #define HEXTANT(b,m,t) case HUE_ ## b ## _ ## m ## _ ## t : \
+               b = hsvBot.ch1; m = hsvBot.ch2; t = hsvBot.ch3; break;
+          HEXTANT(RED,GREEN,BLUE);
+          HEXTANT(RED,BLUE,GREEN);
+          HEXTANT(BLUE,RED,GREEN);
+          HEXTANT(BLUE,GREEN,RED);
+          HEXTANT(GREEN,BLUE,RED);
+          HEXTANT(GREEN,RED,BLUE);
+          #undef HEXTANT
+          }
+        break ;
+      }
+    default:
+      FatalUnsupportedXCF(_("'%s' layer mode"),
+                          _(showGimpLayerModeEffects(mode)));
+    }
+    if( FULLALPHA(bot->pixels[i] & top->pixels[i]) )
+      bot->pixels[i] = (bot->pixels[i] & (255 << ALPHA_SHIFT)) +
+        (RED << RED_SHIFT) +
+        (GREEN << GREEN_SHIFT) +
+        (BLUE << BLUE_SHIFT) ;
+    else {
+      rgba bp = bot->pixels[i] ;
+      /* In a sane world, the alpha of the top pixel would simply be
+       * used to interpolate linearly between the bottom pixel's base
+       * color and the effect-computed color.
+       * But no! What the Gimp actually does is empirically
+       * described by the following (which borrows code from
+       * composite_one() that makes no theoretical sense here):
+       */
+      unsigned tfrac = ALPHA(top->pixels[i]) ;
+      if( !FULLALPHA(bp) ) {
+        unsigned pseudotop = (tfrac < ALPHA(bp) ? tfrac : ALPHA(bp));
+        unsigned alpha = 255 ^ scaletable[255-ALPHA(bp)][255-pseudotop] ;
+        tfrac = (256*pseudotop - 1) / alpha ;
+      }
+      bot->pixels[i] = (bp & (255 << ALPHA_SHIFT)) +
+        ((rgba)scaletable[  tfrac  ][  RED                ] << RED_SHIFT  ) +
+        ((rgba)scaletable[  tfrac  ][  GREEN              ] << GREEN_SHIFT) +
+        ((rgba)scaletable[  tfrac  ][  BLUE               ] << BLUE_SHIFT ) +
+        ((rgba)scaletable[255^tfrac][255&(bp>>RED_SHIFT  )] << RED_SHIFT  ) +
+        ((rgba)scaletable[255^tfrac][255&(bp>>GREEN_SHIFT)] << GREEN_SHIFT) +
+        ((rgba)scaletable[255^tfrac][255&(bp>>BLUE_SHIFT )] << BLUE_SHIFT ) ;
+    }
+  }
+  return ;
+}      
+     
+static void
+dissolveTile(struct Tile *tile)
+{
+  unsigned i ;
+  summary_t summary ;
+  assert( tile->refcount == 1 );
+  if( (tile->summary & TILESUMMARY_CRISP) )
+    return ;
+  summary = TILESUMMARY_UPTODATE + TILESUMMARY_ALLNULL
+    + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ;
+  for( i = 0 ; i < tile->count ; i++ ) {
+    if( FULLALPHA(tile->pixels[i]) )
+      summary &= ~TILESUMMARY_ALLNULL ;
+    else if ( NULLALPHA(tile->pixels[i]) )
+      summary &= ~TILESUMMARY_ALLFULL ;
+    else if( ALPHA(tile->pixels[i]) > rand() % 0xFF ) {
+      tile->pixels[i] |= 255 << ALPHA_SHIFT ;
+      summary &= ~TILESUMMARY_ALLNULL ;
+    } else {
+      tile->pixels[i] = 0 ;
+      summary &= ~TILESUMMARY_ALLFULL ;
+    }
+  }
+  tile->summary = summary ;
+}
+     
+static void
+roundAlpha(struct Tile *tile)
+{
+  unsigned i ;
+  summary_t summary ;
+  assert( tile->refcount == 1 );
+  if( (tile->summary & TILESUMMARY_CRISP) )
+    return ;
+  summary = TILESUMMARY_UPTODATE + TILESUMMARY_ALLNULL
+    + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ;
+  for( i = 0 ; i < tile->count ; i++ ) {
+    if( ALPHA(tile->pixels[i]) >= 128 ) {
+      tile->pixels[i] |= 255 << ALPHA_SHIFT ;
+      summary &= ~TILESUMMARY_ALLNULL ;
+    } else {
+      tile->pixels[i] = 0 ;
+      summary &= ~TILESUMMARY_ALLFULL ;
+    }
+  }
+  tile->summary = summary ;
+}
+
+/* flattenTopdown() shares ownership of top.
+ * The return value may be a shared tile.
+ */
+static struct Tile *
+flattenTopdown(struct FlattenSpec *spec, struct Tile *top,
+               unsigned nlayers, const struct rect *where)
+{
+  struct Tile *tile;
+
+  while( nlayers-- ) {
+    if( tileSummary(top) & TILESUMMARY_ALLFULL )
+      return top ;
+    if( !spec->layers[nlayers].isVisible )
+      continue ;
+    
+    tile = getLayerTile(&spec->layers[nlayers],where);
+    
+    if( tile->summary & TILESUMMARY_ALLNULL )
+      continue ; /* Simulate a tail call */
+
+    switch( spec->layers[nlayers].mode ) {
+    case GIMP_NORMAL_NOPARTIAL_MODE:
+      roundAlpha(tile) ;
+      /* fall through */
+      if(0) {
+      case GIMP_DISSOLVE_MODE:
+        dissolveTile(tile);
+        /* fall through */
+      }
+    case GIMP_NORMAL_MODE:
+      top = merge_normal(tile,top);
+      break ;
+    default:
+      {
+        struct Tile *below, *above ;
+        unsigned i ;
+        if( !(top->summary & TILESUMMARY_ALLNULL) ) {
+          rgba tile_or = 0 ;
+          invalidateSummary(tile,0);
+          for( i=0; i<top->count; i++ )
+            if( FULLALPHA(top->pixels[i]) )
+              tile->pixels[i] = 0 ;
+            else
+              tile_or |= tile->pixels[i] ;
+          /* If the tile only has pixels that will be covered by 'top' anyway,
+           * forget it anyway.
+           */
+          if( ALPHA(tile_or) == 0 ) {
+            freeTile(tile);
+            break ; /* from the switch, which will continue the while */
+          }
+        }
+        /* Create a dummy top for the layers below this */
+        if( top->summary & TILESUMMARY_CRISP ) {
+          above = forkTile(top);
+        } else {
+          summary_t summary = TILESUMMARY_ALLNULL ;
+          above = newTile(*where);
+          for( i=0; i<top->count; i++ )
+            if( FULLALPHA(top->pixels[i]) ) {
+              above->pixels[i] = -1 ;
+              summary = 0 ;
+            } else
+              above->pixels[i] = 0 ;
+          above->summary = TILESUMMARY_UPTODATE + TILESUMMARY_CRISP + summary;
+        }
+        below = flattenTopdown(spec, above, nlayers, where);
+        if( below->refcount > 1 ) {
+          assert( below == top );
+          /* This can only happen if 'below' is a copy of 'top'
+           * THROUGH 'above', which in turn means that none of all
+           * this is visible after all. So just free it and return 'top'.
+           */
+          freeTile(below);
+          return top ;
+        }
+        merge_exotic(below,tile,spec->layers[nlayers].mode);
+        freeTile(tile);
+        top = merge_normal(below,top);
+        return top ;
+      }
+    }
+  }
+  return top ;
+}
+
+static void
+addBackground(struct FlattenSpec *spec, struct Tile *tile, unsigned ncols)
+{
+  unsigned i ;
+
+  if( tileSummary(tile) & TILESUMMARY_ALLFULL )
+    return ;
+
+  switch( spec->partial_transparency_mode ) {
+  case FORBID_PARTIAL_TRANSPARENCY:
+    if( !(tileSummary(tile) & TILESUMMARY_CRISP) )
+      FatalGeneric(102,_("Flattened image has partially transparent pixels"));
+    break ;
+  case DISSOLVE_PARTIAL_TRANSPARENCY:
+    dissolveTile(tile);
+    break ;
+  case ALLOW_PARTIAL_TRANSPARENCY:
+  case PARTIAL_TRANSPARENCY_IMPOSSIBLE:
+    break ;
+  }
+
+  if( spec->default_pixel == CHECKERED_BACKGROUND ) {
+    INIT_SCALETABLE_IF( !(tile->summary & TILESUMMARY_CRISP ) );
+    for( i=0; i<tile->count; i++ )
+      if( !FULLALPHA(tile->pixels[i]) ) {
+        rgba fillwith = ((i/ncols)^(i%ncols))&8 ? 0x66 : 0x99 ;
+        fillwith = graytable[fillwith] + (255 << ALPHA_SHIFT) ;
+        if( NULLALPHA(tile->pixels[i]) )
+          tile->pixels[i] = fillwith ;
+        else
+          tile->pixels[i] = composite_one(fillwith,tile->pixels[i]);
+      }
+    tile->summary = TILESUMMARY_UPTODATE +
+      TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ;
+    return ;
+  }
+  if( !FULLALPHA(spec->default_pixel) )  return ;
+  if( tileSummary(tile) & TILESUMMARY_ALLNULL ) {
+    fillTile(tile,spec->default_pixel);
+  } else {
+    INIT_SCALETABLE_IF( !(tile->summary & TILESUMMARY_CRISP) );
+    for( i=0; i<tile->count; i++ )
+      if( NULLALPHA(tile->pixels[i]) )
+        tile->pixels[i] = spec->default_pixel ;
+      else if( FULLALPHA(tile->pixels[i]) )
+        ;
+      else
+        tile->pixels[i] = composite_one(spec->default_pixel,tile->pixels[i]);
+    
+    tile->summary = TILESUMMARY_UPTODATE +
+      TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ;
+  }
+}
+
+void
+flattenIncrementally(struct FlattenSpec *spec,lineCallback callback)
+{
+  rgba *rows[TILE_HEIGHT] ;
+  unsigned i, y, nrows, ncols ;
+  struct rect where ;
+  struct Tile *tile ;
+  static struct Tile toptile ;
+
+  toptile.count = TILE_HEIGHT * TILE_WIDTH ;
+  fillTile(&toptile,0);
+
+  for( where.t = spec->dim.c.t; where.t < spec->dim.c.b; where.t=where.b ) {
+    where.b = (where.t+TILE_HEIGHT) - where.t % TILE_HEIGHT ;
+    if( where.b > spec->dim.c.b ) where.b = spec->dim.c.b ;
+    nrows = where.b - where.t ;
+    for( y = 0; y < nrows ; y++ )
+      rows[y] = xcfmalloc(4*(spec->dim.c.r-spec->dim.c.l));
+
+    for( where.l = spec->dim.c.l; where.l < spec->dim.c.r; where.l=where.r ) {
+      where.r = (where.l+TILE_WIDTH) - where.l % TILE_WIDTH ;
+      if( where.r > spec->dim.c.r ) where.r = spec->dim.c.r ;
+      ncols = where.r - where.l ;
+
+      toptile.count = ncols * nrows ;
+      toptile.refcount = 2 ; /* For bug checking */
+      assert( toptile.summary == TILESUMMARY_UPTODATE +
+              TILESUMMARY_ALLNULL + TILESUMMARY_CRISP );
+      tile = flattenTopdown(spec,&toptile,spec->numLayers,&where) ;
+      toptile.refcount-- ; /* addBackground may change destructively */
+      addBackground(spec,tile,ncols);
+
+      for( i = 0 ; i < tile->count ; i++ )
+        if( NULLALPHA(tile->pixels[i]) )
+          tile->pixels[i] = 0 ;
+      for( y = 0 ; y < nrows ; y++ )
+        memcpy(rows[y] + (where.l - spec->dim.c.l),
+               tile->pixels + y * ncols, ncols*4);
+      
+      if( tile == &toptile ) {
+        fillTile(&toptile,0);
+      } else {
+        freeTile(tile);
+      }
+    }
+    for( y = 0 ; y < nrows ; y++ )
+      callback(spec->dim.width,rows[y]);
+  }
+}
+
+static rgba **collectPointer ;
+
+static void
+collector(unsigned num,rgba *row)
+{
+  *collectPointer++ = row ;
+}
+
+rgba **
+flattenAll(struct FlattenSpec *spec)
+{
+  rgba **rows = xcfmalloc(spec->dim.height * sizeof(rgba*));
+  if( verboseFlag )
+    fprintf(stderr,_("Flattening image ..."));
+  collectPointer = rows ;
+  flattenIncrementally(spec,collector);
+  if( verboseFlag )
+    fprintf(stderr,"\n");
+  return rows ;
+}
+
+void
+shipoutWithCallback(struct FlattenSpec *spec, rgba **pixels,
+                    lineCallback callback)
+{
+  unsigned i ;
+  for( i = 0; i < spec->dim.height; i++ ) {
+    callback(spec->dim.width,pixels[i]);
+  }
+  xcffree(pixels);
+}

Added: trunk/flatten.h
==============================================================================
--- (empty file)
+++ trunk/flatten.h	Thu May  1 14:32:48 2008
@@ -0,0 +1,77 @@
+/* Flattning functions for xcftools
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef FLATTEN_H
+#define FLATTEN_H
+
+#include "xcftools.h"
+#include "pixels.h"
+
+#define PERHAPS_ALPHA_CHANNEL (NEWALPHA(0,1))
+#define FORCE_ALPHA_CHANNEL (NEWALPHA(0,2))
+#define CHECKERED_BACKGROUND (NEWALPHA(0,200))
+struct FlattenSpec {
+  struct tileDimensions dim ;
+  rgba default_pixel ;
+  int numLayers ;
+  struct xcfLayer *layers ;
+
+  const char * transmap_filename ;
+  const char * output_filename ;
+  enum out_color_mode {
+    COLOR_BY_FILENAME,
+    COLOR_BY_CONTENTS,
+    COLOR_INDEXED,
+    COLOR_RGB,
+    COLOR_GRAY,
+    COLOR_MONO
+  } out_color_mode ;
+  enum { ALLOW_PARTIAL_TRANSPARENCY,
+         DISSOLVE_PARTIAL_TRANSPARENCY,
+         FORBID_PARTIAL_TRANSPARENCY,
+         PARTIAL_TRANSPARENCY_IMPOSSIBLE
+  } partial_transparency_mode ;
+  enum { USE_CANVAS = 0,
+         MANUAL_OFFSET = 1,
+         MANUAL_CROP = 2,
+         AUTOCROP = 4 } window_mode ;
+  int process_in_memory ;
+  int gimpish_indexed ;
+};
+
+/* From flatspec.c */
+
+void init_flatspec(struct FlattenSpec *);
+
+void add_layer_request(struct FlattenSpec *,const char *name);
+struct xcfLayer *lastlayerspec(struct FlattenSpec *,const char *option);
+
+typedef enum out_color_mode (*guesser) (struct FlattenSpec *,rgba **);
+
+/* Call this after processing options, and after opening the XCF file */
+void complete_flatspec(struct FlattenSpec *,guesser);
+void analyse_colormode(struct FlattenSpec *,rgba **allPixels,guesser);
+
+/* From flatten.c */
+
+typedef void (*lineCallback)(unsigned num,rgba *pixels);
+void flattenIncrementally(struct FlattenSpec *,lineCallback);
+rgba **flattenAll(struct FlattenSpec*);
+void shipoutWithCallback(struct FlattenSpec *,rgba **pixels,lineCallback);
+
+#endif /* FLATTEN_H */

Added: trunk/gnome-xcf-thumbnailer.c
==============================================================================
--- (empty file)
+++ trunk/gnome-xcf-thumbnailer.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,129 @@
+/* 
+ * Copyright (C) 2008 Bastien Nocera <hadess hadess net>
+ *
+ * Authors: Bastien Nocera <hadess hadess net>
+ * Henning Makholm <henning makholm net>
+ *
+ * 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
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+/* Make sure the xcf2png.c's main doesn't get used */
+#define main(x, y) foobar(x, y)
+#define opt_usage(f) {}
+#include "xcf2png.c"
+#undef main
+
+#include <string.h>
+#include <gio/gio.h>
+
+static int output_size = 64;
+static gboolean g_fatal_warnings = FALSE;
+static char **filenames = NULL;
+
+static const GOptionEntry entries[] = {
+	{ "size", 's', 0, G_OPTION_ARG_INT, &output_size, "Size of the thumbnail in pixels", NULL },
+	{"g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, "Make all warnings fatal", NULL},
+ 	{ G_OPTION_REMAINING, '\0', 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, "[FILE...]" },
+	{ NULL }
+};
+
+int main (int argc, char **argv)
+{
+#if 0
+	GMappedFile *map;
+	guint32 offset;
+	gchar *base;
+	gchar *tile_data;
+	guint16 *palette_data;
+#endif
+//	GdkPixbuf *pixbuf, *scaled;
+	GError *error = NULL;
+	GOptionContext *context;
+	char *input, *output;
+
+	/* Options parsing */
+	context = g_option_context_new ("Thumbnail Nintendo DS ROMs");
+	g_option_context_add_main_entries (context, entries, NULL);
+	g_type_init ();
+
+	if (g_option_context_parse (context, &argc, &argv, &error) == FALSE) {
+		g_warning ("Couldn't parse command-line options: %s", error->message);
+		g_error_free (error);
+		return 1;
+	}
+
+	/* Set fatal warnings if required */
+	if (g_fatal_warnings) {
+		GLogLevelFlags fatal_mask;
+
+		fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+		fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
+		g_log_set_always_fatal (fatal_mask);
+	}
+
+	if (filenames == NULL || g_strv_length (filenames) != 2) {
+		g_print ("Expects an input and an output file\n");
+		return 1;
+	}
+	input = filenames[0];
+	output = filenames[1];
+
+	init_flatspec(&flatspec) ;
+	flatspec.output_filename = output;
+	read_or_mmap_xcf(input,NULL);
+	getBasicXcfInfo() ;
+	initColormap();
+
+	complete_flatspec(&flatspec,guessIndexed);
+	if( flatspec.process_in_memory ) {
+		rgba **allPixels = flattenAll(&flatspec);
+
+		analyse_colormode(&flatspec,allPixels,guessIndexed);
+
+		/* See if we can do alpha compaction.
+		*/
+		if( flatspec.partial_transparency_mode != ALLOW_PARTIAL_TRANSPARENCY &&
+		    !FULLALPHA(flatspec.default_pixel) &&
+		    flatspec.out_color_mode != COLOR_INDEXED ) {
+			rgba unused = findUnusedColor(allPixels,
+						      flatspec.dim.width,
+						      flatspec.dim.height);
+			if( unused && (flatspec.out_color_mode == COLOR_RGB ||
+				       degrayPixel(unused) >= 0) ) {
+				unsigned x,y ;
+				unused = NEWALPHA(unused,0) ;
+				for( y=0; y<flatspec.dim.height; y++)
+					for( x=0; x<flatspec.dim.width; x++)
+						if( allPixels[y][x] == 0 )
+							allPixels[y][x] = unused ;
+				flatspec.default_pixel = unused ;
+			}
+		}
+		shipoutWithCallback(&flatspec,allPixels,selectCallback());
+	} else {
+		flattenIncrementally(&flatspec,selectCallback());
+	}
+	if( libpng ) {
+		png_write_end(libpng,libpng2);
+		png_destroy_write_struct(&libpng,&libpng2);
+	}
+	closeout(outfile,output);
+
+	return 0;
+}
+

Added: trunk/gnome-xcf-thumbnailer.schemas.in
==============================================================================
--- (empty file)
+++ trunk/gnome-xcf-thumbnailer.schemas.in	Thu May  1 14:32:48 2008
@@ -0,0 +1,53 @@
+<gconfschemafile>
+  <schemalist>
+
+    <schema>
+      <applyto>/desktop/gnome/thumbnailers/image x-compressed-xcf/command</applyto>
+      <key>/schemas/desktop/gnome/thumbnailers/image x-compressed-xcf/command</key>
+      <owner>gnome-xcf-thumbnailer</owner>
+      <type>string</type>
+      <default>@bindir@/gnome-xcf-thumbnailer %i %o</default>
+      <locale name="C">
+        <short></short>
+        <long></long>
+      </locale>
+    </schema>
+
+    <schema>
+      <applyto>/desktop/gnome/thumbnailers/image x-compressed-xcf/enable</applyto>
+      <key>/schemas/desktop/gnome/thumbnailers/image x-compressed-xcf/enable</key>
+      <owner>gnome-xcf-thumbnailer</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+        <short></short>
+        <long></long>
+      </locale>
+    </schema>
+
+    <schema>
+      <applyto>/desktop/gnome/thumbnailers/image x-xcf/command</applyto>
+      <key>/schemas/desktop/gnome/thumbnailers/image x-xcf/command</key>
+      <owner>gnome-xcf-thumbnailer</owner>
+      <type>string</type>
+      <default>@bindir@/gnome-xcf-thumbnailer %i %o</default>
+      <locale name="C">
+        <short></short>
+        <long></long>
+      </locale>
+    </schema>
+
+    <schema>
+      <applyto>/desktop/gnome/thumbnailers/image x-xcf/enable</applyto>
+      <key>/schemas/desktop/gnome/thumbnailers/image x-xcf/enable</key>
+      <owner>gnome-xcf-thumbnailer</owner>
+      <type>bool</type>
+      <default>true</default>
+      <locale name="C">
+        <short></short>
+        <long></long>
+      </locale>
+    </schema>
+
+  </schemalist>
+</gconfschemafile>

Added: trunk/io-unix.c
==============================================================================
--- (empty file)
+++ trunk/io-unix.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,176 @@
+/* OS-specific IO functions for xcftools
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "xcftools.h"
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#if HAVE_MMAP
+#include <sys/mman.h>
+#endif
+
+static FILE *xcfstream = 0 ;
+
+void
+free_or_close_xcf(void)
+{
+  if( xcf_file ) {
+    if( xcfstream ) {
+      munmap(xcf_file,xcf_length) ;
+      fclose(xcfstream);
+      xcf_file = 0 ;
+      xcfstream = 0 ;
+    } else {
+      free(xcf_file) ;
+      xcf_file = 0 ;
+    }
+  }
+}
+
+void
+read_or_mmap_xcf(const char *filename,const char *unzipper)
+{
+  struct stat statbuf ;
+  
+  free_or_close_xcf() ;
+
+  if( strcmp(filename,"-") != 0 ) {
+    if( access(filename,R_OK) != 0 )
+      FatalGeneric(21,"!%s",filename);
+  }
+
+  if( !unzipper ) {
+    const char *pc ;
+    pc = filename + strlen(filename) ;
+    if( pc-filename > 2 && strcmp(pc-2,"gz") == 0 )
+      unzipper = "zcat" ;
+    else if ( pc-filename > 3 && strcmp(pc-3,"bz2") == 0 )
+      unzipper = "bzcat" ;
+    else
+      unzipper = "" ;
+  } else if( strcmp(unzipper,"cat") == 0 )
+    unzipper = "" ;
+  
+  if( *unzipper ) {
+    int pid, status, outfd ;
+#if HAVE_MMAP
+    xcfstream = tmpfile() ;
+    if( !xcfstream )
+      FatalUnexpected(_("!Cannot create temporary unzipped file"));
+    outfd = fileno(xcfstream) ;
+#else
+    int fh[2] ;
+    if( pipe(fh) < 0 )
+      FatalUnexpected("!Cannot create pipe for %s",unzipper);
+    xcfstream = fdopen(fh[1],"rb") ;
+    if( !xcfstream )
+      FatalUnexpected("!Cannot fdopen() unzipper pipe");
+    outfd = fh[0] ;
+#endif
+    if( (pid = fork()) == 0 ) {
+      /* We're the child */
+      if( dup2(outfd,1) < 0 ) {
+        perror("Cannot dup2 in unzip process");
+        exit(127) ;
+      }
+      fclose(xcfstream) ;
+      execlp(unzipper,unzipper,filename,NULL) ;
+      fprintf(stderr,_("Cannot execute "));
+      perror(unzipper);
+      exit(126) ;
+    }
+#if HAVE_MMAP
+    while( wait(&status) != pid )
+      ;
+    if( WIFEXITED(status) ) {
+      status = WEXITSTATUS(status) ;
+      if( status > 0 ) {
+        fclose(xcfstream) ;
+        xcfstream = 0 ;
+        FatalGeneric(status,NULL);
+      }
+    } else {
+      fclose(xcfstream) ;
+      xcfstream = 0 ;
+      FatalGeneric(126,_("%s terminated abnormally"),unzipper);
+    }
+#else
+    close(fh[0]) ;
+#endif
+  } else if( strcmp(filename,"-") == 0 ) {
+    xcfstream = fdopen(dup(0),"rb") ;
+    if( !xcfstream )
+      FatalUnexpected("!Cannot dup stdin for input") ;
+  } else {
+    xcfstream = fopen(filename,"rb") ;
+    if( !xcfstream )
+      FatalGeneric(21,_("!Cannot open %s"),filename);
+  }
+  /* OK, now we have an open stream ... */
+  if( fstat(fileno(xcfstream),&statbuf) == 0 &&
+      (statbuf.st_mode & S_IFMT) == S_IFREG ) {
+    xcf_length = statbuf.st_size ;
+#if HAVE_MMAP
+    xcf_file = mmap(0,xcf_length,PROT_READ,MAP_SHARED,fileno(xcfstream),0);
+    if( xcf_file != (void*)-1 )
+      return ;
+    if( errno != ENODEV ) {
+      int saved = errno ;
+      fclose(xcfstream) ;
+      xcf_file = 0 ;
+      errno = saved ;
+      FatalUnexpected("!Could not mmap input");
+    }
+#endif
+    xcf_file = malloc(xcf_length);
+    if( xcf_file == 0 )
+      FatalUnexpected(_("Out of memory for xcf data"));
+    if( fread(xcf_file,1,xcf_length,xcfstream) != xcf_length ) {
+      if( feof(xcfstream) )
+        FatalUnexpected(_("XCF file shrunk while reading it"));
+      else
+        FatalUnexpected(_("!Could not read xcf data"));
+    }
+    fclose(xcfstream) ;
+    xcfstream = 0 ;
+  } else {
+    size_t blocksize = 0x80000 ; /* 512 KB */
+    xcf_length = 0 ;
+    xcf_file = 0 ;
+    while(1) {
+      xcf_file = realloc(xcf_file,blocksize) ;
+      if( xcf_file == 0 )
+        FatalUnexpected(_("Out of memory for xcf data"));
+      size_t actual = fread(xcf_file+xcf_length,1,blocksize-xcf_length,
+                            xcfstream) ;
+      xcf_length += actual ;
+      if( feof(xcfstream) )
+        break ;
+      if( xcf_length < blocksize ) {
+        FatalUnexpected(_("!Could not read xcf data")) ;
+      }
+      blocksize += (blocksize >> 1) & ~(size_t)0x3FFF ; /* 16 KB granularity */
+    }
+    fclose(xcfstream) ;
+    xcfstream = 0 ;
+  }
+}

Added: trunk/options.i
==============================================================================

Added: trunk/palette.c
==============================================================================
--- (empty file)
+++ trunk/palette.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,110 @@
+/* Palette-manipulation functions functions for xcftools
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "palette.h"
+#include <assert.h>
+
+#define HASH_SIZE 1711
+/* If I remember correctly, this size hash will be able to handle
+ * either of
+ *  a) the Netscape cube with intensities 0x00, 0x33, 0x66, 0x99, 0xCC, xFF
+ *  b) the EGA cube with intensities 0x00, 0x55, 0xAA, 0xFF
+ *  c) the "CGA cube" with intensites 0x00, 0x80, 0xFF
+ *  d) a full 256-step grayscale
+ * without collisions. It will also have a minimal number of collisions
+ * (4) on a full 16-to-a-side cube with intensities
+ * 0x00, 0x11, 0x22, ..., 0xDD, 0xEE, 0xFF.
+ */
+
+unsigned paletteSize ;
+rgba palette[MAX_PALETTE] ;
+static int masterhash[HASH_SIZE];
+static int bucketlinks[MAX_PALETTE];
+    
+void
+init_palette_hash(void)
+{
+  unsigned i ;
+  for( i=0; i<HASH_SIZE; i++ )
+    masterhash[i] = -1 ;
+  for( i=0; i<MAX_PALETTE; i++ )
+    bucketlinks[i] = -1 ;
+  paletteSize = 0 ;
+}
+
+inline int
+lookup_or_intern(rgba color) {
+  int *target = &masterhash[color % HASH_SIZE];
+  while( *target >= 0 ) {
+    if( palette[*target] == color )
+      return *target ;
+    target = &bucketlinks[*target] ;
+  }
+#if 0
+  fprintf(stderr,"Palette[%u] = %08x (%u --> %d)\n",paletteSize,color,
+          color % HASH_SIZE, *target);
+#endif
+  if( paletteSize >= MAX_PALETTE )
+    return -1 ;
+  *target = paletteSize ;
+  palette[paletteSize] = color ;
+  return paletteSize++ ;
+}
+
+static inline void
+unpalettify_row(rgba *row,unsigned ncols)
+{
+  index_t *newrow = (index_t*) row ;
+  unsigned i ;
+  for( i=ncols; i--; ) {
+    row[i] = palette[newrow[i]] ;
+  }
+}
+
+int
+palettify_row(rgba *row,unsigned ncols)
+{
+  index_t *newrow = (index_t*)row ;
+  assert(sizeof(index_t) <= sizeof(rgba));
+  unsigned i ;
+  for( i=0; i<ncols; i++ ) {
+    int j = lookup_or_intern(row[i]) ;
+    if( j < 0 ) {
+      unpalettify_row(row,i);
+      return 0 ;
+    }
+    newrow[i] = j ;
+  }
+  return 1 ;
+}
+    
+int
+palettify_rows (rgba *rows[],unsigned ncols,unsigned nlines)
+{
+  unsigned i ;
+  for( i=0; i<nlines; i++ ) {
+    if( !palettify_row(rows[i],ncols) ) {
+      while( i-- )
+        unpalettify_row(rows[i],ncols);
+      return 0 ;
+    }
+  }
+  return 1 ;
+}
+        
+  

Added: trunk/palette.h
==============================================================================
--- (empty file)
+++ trunk/palette.h	Thu May  1 14:32:48 2008
@@ -0,0 +1,52 @@
+/* Palette-manipulation functions functions for xcftools
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef PALETTE_H
+#define PALETTE_H
+
+#include "xcftools.h"
+#include "pixels.h"
+
+#define MAX_PALETTE 256
+extern rgba palette[MAX_PALETTE] ;
+extern unsigned paletteSize ;
+
+typedef uint8_t index_t ;
+
+void init_palette_hash(void);
+
+/* lookup_or_intern() returns a negative number if there is no room
+ * for the color in the palette.
+ */
+int lookup_or_intern(rgba color);
+
+/* palettify_row will convert a row of 'rgba' values into a packed row
+ * of 'uint8_t' indces. If it succeeds without running out of colormap
+ * entries, it returns nonzero. On the other hand if it does run out
+ * of colormap entries it returns zero _and_ undoes the conversions
+ * already done, so that the row is still a full row of 'rgba' values
+ * afterwards.
+ */
+int palettify_row(rgba *row,unsigned ncols);
+
+/* palettify_rows is like palettify_rows, but works on several
+ * rows at a time.
+ */
+int palettify_rows(rgba *rows[],unsigned ncols,unsigned nlines);
+
+#endif /* PALETTE_H */

Added: trunk/pixels.c
==============================================================================
--- (empty file)
+++ trunk/pixels.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,487 @@
+/*  Pixel and tile functions for xcftools
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#define DEBUG
+#include "xcftools.h"
+#include "pixels.h"
+#include <assert.h>
+#include <string.h>
+
+rgba colormap[256] ;
+unsigned colormapLength=0 ;
+
+int
+degrayPixel(rgba pixel)
+{
+  if( ((pixel >> RED_SHIFT) & 255) == ((pixel >> GREEN_SHIFT) & 255) &&
+      ((pixel >> RED_SHIFT) & 255) == ((pixel >> BLUE_SHIFT) & 255) )
+    return (pixel >> RED_SHIFT) & 255 ;
+  return -1 ;
+}
+
+/* ****************************************************************** */
+
+typedef const struct _convertParams {
+  int bpp ;
+  int shift[4] ;
+  uint32_t base_pixel ;
+  const rgba *lookup ;
+} convertParams ;
+#define RGB_SHIFT RED_SHIFT, GREEN_SHIFT, BLUE_SHIFT
+#define OPAQUE (255 << ALPHA_SHIFT)
+static convertParams convertRGB       = { 3, {RGB_SHIFT}, OPAQUE, 0 };
+static convertParams convertRGBA      = { 4, {RGB_SHIFT, ALPHA_SHIFT}, 0,0 };
+static convertParams convertGRAY      = { 1, {-1}, OPAQUE, graytable };
+static convertParams convertGRAYA     = { 2, {-1,ALPHA_SHIFT}, 0, graytable };
+static convertParams convertINDEXED   = { 1, {-1}, OPAQUE, colormap };
+static convertParams convertINDEXEDA  = { 2, {-1,ALPHA_SHIFT}, 0, colormap };
+
+static convertParams convertColormap  = { 3, {RGB_SHIFT}, 0, 0 };
+static convertParams convertChannel   = { 1, {ALPHA_SHIFT}, 0, 0 };
+
+/* ****************************************************************** */
+
+static inline int
+tileDirectoryOneLevel(struct tileDimensions *dim,uint32_t ptr)
+{
+  if( ptr == 0 )
+    return 0 ;
+  if( xcfL(ptr  ) != dim->c.r - dim->c.l ||
+      xcfL(ptr+4) != dim->c.b - dim->c.t )
+    FatalBadXCF("Drawable size mismatch at %" PRIX32, ptr);
+  return ptr += 8 ;
+}
+
+static void
+initTileDirectory(struct tileDimensions *dim,struct xcfTiles *tiles,
+                  const char *type)
+{
+  uint32_t ptr ;
+  uint32_t data ;
+
+  ptr = tiles->hierarchy ;
+  tiles->hierarchy = 0 ;
+  if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return ;
+  if( tiles->params == &convertChannel ) {
+    /* A layer mask is a channel.
+     * Skip a name and a property list.
+     */
+    xcfString(ptr,&ptr);
+    while( xcfNextprop(&ptr,&data) != PROP_END )
+      ;
+    ptr = xcfOffset(ptr,4*4);
+    if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return ;
+  }
+  /* The XCF format has a dummy "hierarchy" level which was
+   * once meant to mean something, but never happened. It contains
+   * the bpp value and a list of "level" pointers; but only the
+   * first level actually contains data.
+   */
+  data = xcfL(ptr) ;
+  if( xcfL(ptr) != tiles->params->bpp )
+    FatalBadXCF("%"PRIu32" bytes per pixel for %s drawable",xcfL(ptr),type);
+  ptr = xcfOffset(ptr+4,3*4) ;
+  if( (ptr = tileDirectoryOneLevel(dim,ptr)) == 0 ) return ;
+
+  xcfCheckspace(ptr,dim->ntiles*4+4,"Tile directory at %" PRIX32,ptr);
+  if( xcfL(ptr + dim->ntiles*4) != 0 )
+    FatalBadXCF("Wrong sized tile directory at %" PRIX32,ptr);
+#define REUSE_RAW_DATA tiles->tileptrs = (uint32_t*)(xcf_file + ptr)
+#if defined(WORDS_BIGENDIAN) && defined(CAN_DO_UNALIGNED_WORDS)
+  REUSE_RAW_DATA;
+#else
+# if defined(WORDS_BIGENDIAN)
+  if( (ptr&3) == 0 ) REUSE_RAW_DATA; else
+# endif
+    {
+      unsigned i ;
+      tiles->tileptrs = xcfmalloc(dim->ntiles * sizeof(uint32_t)) ;
+      for( i = 0 ; i < dim->ntiles ; i++ )
+        tiles->tileptrs[i] = xcfL(ptr+i*4);
+    }
+#endif
+}
+
+void
+initLayer(struct xcfLayer *layer) {
+  if( layer->dim.ntiles == 0 ||
+      (layer->pixels.hierarchy == 0 && layer->mask.hierarchy == 0) )
+    return ;
+  switch(layer->type) {
+#define DEF(X) case GIMP_##X##_IMAGE: layer->pixels.params = &convert##X; break
+    DEF(RGB);
+    DEF(RGBA);
+    DEF(GRAY);
+    DEF(GRAYA);
+    DEF(INDEXED);
+    DEF(INDEXEDA);
+  default:
+    FatalUnsupportedXCF(_("Layer type %s"),_(showGimpImageType(layer->type)));
+  }
+  initTileDirectory(&layer->dim,&layer->pixels,
+                    _(showGimpImageType(layer->type)));
+  layer->mask.params = &convertChannel ;
+  initTileDirectory(&layer->dim,&layer->mask,"layer mask");
+}
+static void copyStraightPixels(rgba *dest,unsigned npixels,
+                               uint32_t ptr,convertParams *params);
+void
+initColormap(void) {
+  uint32_t ncolors ;
+  if( XCF.colormapptr == 0 ) {
+    colormapLength = 0 ;
+    return ;
+  }
+  ncolors = xcfL(XCF.colormapptr) ;
+  if( ncolors > 256 )
+    FatalUnsupportedXCF(_("Color map has more than 256 entries"));
+  copyStraightPixels(colormap,ncolors,XCF.colormapptr+4,&convertColormap);
+  colormapLength = ncolors ;
+#ifdef xDEBUG
+  {
+    unsigned j ;
+    fprintf(stderr,"Colormap decoding OK\n");
+    for( j = 0 ; j < ncolors ; j++ ) {
+      if( j % 8 == 0 ) fprintf(stderr,"\n");
+      fprintf(stderr," %08x",colormap[j]);
+    }
+    fprintf(stderr,"\n");
+  }
+#endif
+}
+
+/* ****************************************************************** */
+
+struct Tile *
+newTile(struct rect r)
+{
+  unsigned npixels = (unsigned)(r.b-r.t) * (unsigned)(r.r-r.l) ;
+  struct Tile *data
+    = xcfmalloc(sizeof(struct Tile) -
+                sizeof(rgba)*(TILE_HEIGHT*TILE_WIDTH - npixels)) ;
+  data->count = npixels ;
+  data->refcount = 1 ;
+  data->summary = 0 ;
+  return data ;
+}
+
+struct Tile *
+forkTile(struct Tile* tile)
+{
+  if( ++tile->refcount <= 0 )
+    FatalUnsupportedXCF(_("Unbelievably many layers?\n"
+                          "More likely to be a bug in %s"),progname);
+  return tile ;
+}
+
+void
+freeTile(struct Tile* tile)
+{
+  if( --tile->refcount == 0 )
+    xcffree(tile) ;
+}
+
+summary_t
+tileSummary(struct Tile *tile)
+{
+  unsigned i ;
+  summary_t summary ;
+  if( (tile->summary & TILESUMMARY_UPTODATE) != 0 )
+    return tile->summary ;
+  summary = TILESUMMARY_ALLNULL + TILESUMMARY_ALLFULL + TILESUMMARY_CRISP ;
+  for( i=0; summary && i<tile->count; i++ ) {
+    if( FULLALPHA(tile->pixels[i]) )
+      summary &= ~TILESUMMARY_ALLNULL ;
+    else if( NULLALPHA(tile->pixels[i]) )
+      summary &= ~TILESUMMARY_ALLFULL ;
+    else
+      summary = 0 ;
+  }
+  summary += TILESUMMARY_UPTODATE ;
+  tile->summary = summary ;
+  return summary ;
+}
+  
+void
+fillTile(struct Tile *tile,rgba data)
+{
+  unsigned i ;
+  for( i = 0 ; i < tile->count ; i++ )
+    tile->pixels[i] = data ;
+  if( FULLALPHA(data) )
+    tile->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLFULL+TILESUMMARY_CRISP;
+  else if (NULLALPHA(data) )
+    tile->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLNULL+TILESUMMARY_CRISP;
+  else
+    tile->summary = TILESUMMARY_UPTODATE ;
+}
+
+/* ****************************************************************** */
+
+static void
+copyStraightPixels(rgba *dest,unsigned npixels,
+                   uint32_t ptr,convertParams *params)
+{
+  unsigned bpp = params->bpp;
+  const rgba *lookup = params->lookup;
+  rgba base_pixel = params->base_pixel ;
+  uint8_t *bp = xcf_file + ptr ;
+  xcfCheckspace(ptr,bpp*npixels,
+                "pixel array (%u x %d bpp) at %"PRIX32,npixels,bpp,ptr);
+  while( npixels-- ) {
+    rgba pixel = base_pixel ;
+    unsigned i ;
+    for( i = 0 ; i < bpp ; i++ ) {
+      if( params->shift[i] < 0 ) {
+        pixel += lookup[*bp++] ;
+      } else {
+        pixel += *bp++ << params->shift[i] ;
+      }
+    }
+    *dest++ = pixel ;
+  }
+}
+
+static inline void
+copyRLEpixels(rgba *dest,unsigned npixels,uint32_t ptr,convertParams *params)
+{
+  unsigned i,j ;
+  rgba base_pixel = params->base_pixel ;
+
+#ifdef xDEBUG
+  fprintf(stderr,"RLE stream at %x, want %u x %u pixels, base %x\n",
+          ptr,params->bpp,npixels,base_pixel);
+#endif
+
+  
+  /* This algorithm depends on the indexed byte always being the first one */
+  if( params->shift[0] < -1 )
+    base_pixel = 0 ;
+  for( j = npixels ; j-- ; )
+    dest[j] = base_pixel ;
+
+  for( i = 0 ; i < params->bpp ; i++ ) {
+    int shift = params->shift[i] ;
+    if( shift < 0 )
+      shift = 0 ;
+    for( j = 0 ; j < npixels ; ) {
+      int countspec ;
+      unsigned count ;
+      xcfCheckspace(ptr,2,"RLE data stream");
+      countspec = (int8_t) xcf_file[ptr++] ;
+      count = countspec >= 0 ? countspec+1 : -countspec ;
+      if( count == 128 ) {
+        xcfCheckspace(ptr,3,"RLE long count");
+        count = xcf_file[ptr++] << 8 ;
+        count += xcf_file[ptr++] ;
+      }
+      if( j + count > npixels )
+        FatalBadXCF("Overlong RLE run at %"PRIX32" (plane %u, %u left)",
+                    ptr,i,npixels-j);
+      if( countspec >= 0 ) {
+        rgba data = (uint32_t) xcf_file[ptr++] << shift ;
+        while( count-- )
+          dest[j++] += data ;
+      } else {
+        while( count-- )
+          dest[j++] += (uint32_t) xcf_file[ptr++] << shift ;
+      }
+    }
+    if( i == 0 && params->shift[0] < 0 ) {
+      const rgba *lookup = params->lookup ;
+      base_pixel = params->base_pixel ;
+      for( j = npixels ; j-- ; ) {
+        dest[j] = lookup[dest[j]-base_pixel] + base_pixel ;
+      }
+    }
+  }
+#ifdef xDEBUG
+  fprintf(stderr,"RLE decoding OK at %"PRIX32"\n",ptr);
+  /*
+  for( j = 0 ; j < npixels ; j++ ) {
+    if( j % 8 == 0 ) fprintf(stderr,"\n");
+    fprintf(stderr," %8x",dest[j]);
+  }
+  fprintf(stderr,"\n");
+  */
+#endif
+}
+
+static inline void
+copyTilePixels(struct Tile *dest, uint32_t ptr,convertParams *params)
+{
+  if( FULLALPHA(params->base_pixel) )
+    dest->summary = TILESUMMARY_UPTODATE+TILESUMMARY_ALLFULL+TILESUMMARY_CRISP;
+  else
+    dest->summary = 0 ;
+  switch( XCF.compression ) {
+  case COMPRESS_NONE:
+    copyStraightPixels(dest->pixels,dest->count,ptr,params);
+    break ;
+  case COMPRESS_RLE:
+    copyRLEpixels(dest->pixels,dest->count,ptr,params);
+    break ;
+  default:
+    FatalUnsupportedXCF(_("%s compression"),
+                        _(showXcfCompressionType(XCF.compression)));
+  }
+}
+
+static struct Tile *
+getMaskOrLayerTile(struct tileDimensions *dim, struct xcfTiles *tiles,
+                   struct rect want)
+{
+  struct Tile *tile = newTile(want);
+
+  assert( want.l < want.r && want.t < want.b );
+  if( tiles->tileptrs == 0 ) {
+    fillTile(tile,0);
+    return tile ;
+  }
+  
+#ifdef xDEBUG
+  fprintf(stderr,"getMaskOrLayer: (%d-%d),(%d-%d)\n",left,right,top,bottom);
+#endif
+  
+  if( isSubrect(want,dim->c) &&
+      (want.l - dim->c.l) % TILE_WIDTH == 0 &&
+      (want.t - dim->c.t) % TILE_HEIGHT == 0 ) {
+    unsigned tx = (want.l - dim->c.l) / TILE_WIDTH ;
+    unsigned ty = (want.t - dim->c.t) / TILE_WIDTH ;
+    if( want.r == TILEXn(*dim,tx+1) && want.b == TILEYn(*dim,ty+1) ) {
+      /* The common case? An entire single tile from the layer */
+      copyTilePixels(tile,tiles->tileptrs[tx + ty*dim->tilesx],tiles->params);
+      return tile ;
+    }
+  }
+
+  /* OK, we must construct the wanted tile as a jigsaw */
+  {
+    unsigned width = want.r-want.l ;
+    rgba *pixvert = tile->pixels ;
+    rgba *pixhoriz ;
+    unsigned y, ty, l0, l1, lstart, lnum ;
+    unsigned x, tx, c0, c1, cstart, cnum ;
+    
+    if( !isSubrect(want,dim->c) ) {
+      if( want.l < dim->c.l ) pixvert += (dim->c.l - want.l),
+                                want.l = dim->c.l ;
+      if( want.r > dim->c.r ) want.r = dim->c.r ;
+      if( want.t < dim->c.t ) pixvert += (dim->c.t - want.t) * width,
+                                want.t = dim->c.t ;
+      if( want.b > dim->c.b ) want.b = dim->c.b ;
+      fillTile(tile,0);
+    } else {
+      tile->summary = -1 ; /* I.e. whatever the jigsaw pieces say */
+    }
+
+#ifdef xDEBUG
+    fprintf(stderr,"jig0 (%d-%d),(%d-%d)\n",left,right,top,bottom);
+#endif
+
+    for( y=want.t, ty=(want.t-dim->c.t)/TILE_HEIGHT, l0=TILEYn(*dim,ty);
+         y<want.b;
+         pixvert += lnum*width, ty++, y=l0=l1 ) {
+      l1 = TILEYn(*dim,ty+1) ;
+      lstart = y - l0 ;
+      lnum = (l1 > want.b ? want.b : l1) - y ;
+      
+      pixhoriz = pixvert ;
+      for( x=want.l, tx=(want.l-dim->c.l)/TILE_WIDTH, c0=TILEXn(*dim,tx);
+           x<want.r;
+           pixhoriz += cnum, tx++, x=c0=c1 ) {
+        c1 = TILEXn(*dim,tx+1);
+        cstart = x - c0 ;
+        cnum = (c1 > want.r ? want.r : c1) - x ;
+
+        {
+          static struct Tile tmptile ;
+          unsigned dwidth = c1-c0 ;
+          unsigned i, j ;
+          tmptile.count = (c1-c0)*(l1-l0) ;
+#ifdef xDEBUG
+          fprintf(stderr,"jig ty=%u(%u-%u-%u)(%u+%u) tx=%u(%u-%u-%u)(%u+%u)\n",
+                  ty,l0,y,l1,lstart,lnum,
+                  tx,c0,x,c1,cstart,cnum);
+#endif
+          copyTilePixels(&tmptile,
+                         tiles->tileptrs[tx+ty*dim->tilesx],tiles->params);
+          for(i=0; i<lnum; i++)
+            for(j=0; j<cnum; j++)
+              pixhoriz[i*width+j]
+                = tmptile.pixels[(i+lstart)*dwidth+(j+cstart)];
+          tile->summary &= tmptile.summary ;
+        }
+      }
+    }
+  }
+  return tile ;
+}
+
+void
+applyMask(struct Tile *tile, struct Tile *mask)
+{
+  unsigned i ;
+  assertTileCompatibility(tile,mask);
+  assert( tile->count == mask->count );
+  INIT_SCALETABLE_IF(1);
+  invalidateSummary(tile,0);
+  for( i=0; i < tile->count ;i++ )
+    tile->pixels[i] = NEWALPHA(tile->pixels[i],
+                               scaletable[mask->pixels[i]>>ALPHA_SHIFT]
+                                         [ALPHA(tile->pixels[i])]);
+  freeTile(mask);
+}
+
+struct Tile *
+getLayerTile(struct xcfLayer *layer,const struct rect *where)
+{
+  struct Tile *data ;
+
+#ifdef xDEBUG
+  fprintf(stderr,"getLayerTile(%s): (%d-%d),(%d-%d)\n",
+          layer->name,where->l,where->r,where->t,where->b);
+#endif
+
+  if( disjointRects(*where,layer->dim.c) ||
+      layer->opacity == 0 ) {
+    data = newTile(*where);
+    fillTile(data,0);
+    return data ;
+  }
+  
+  data = getMaskOrLayerTile(&layer->dim,&layer->pixels,*where);
+  if( (data->summary & TILESUMMARY_ALLNULL) != 0 )
+    return data ;
+  if( layer->hasMask ) {
+    struct Tile *mask = getMaskOrLayerTile(&layer->dim,&layer->mask,*where);
+    applyMask(data,mask);
+  }
+  if( layer->opacity < 255 ) {
+    const uint8_t *ourtable ;
+    int i ;
+    invalidateSummary(data,~(TILESUMMARY_CRISP | TILESUMMARY_ALLFULL));
+    INIT_SCALETABLE_IF(1);
+    ourtable = scaletable[layer->opacity] ;
+    for( i=0; i < data->count; i++ )
+      data->pixels[i]
+        = NEWALPHA(data->pixels[i],ourtable[ALPHA(data->pixels[i])]) ;
+  }
+  return data ;
+}
+  

Added: trunk/pixels.h
==============================================================================
--- (empty file)
+++ trunk/pixels.h	Thu May  1 14:32:48 2008
@@ -0,0 +1,128 @@
+/* Pixel and tile functions for xcftools
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef PIXELS_H
+#define PIXELS_H
+
+#include "xcftools.h"
+
+/* MACROS FOR INTERNAL PIXEL ORDERING HERE */
+/*=========================================*/
+/* In principle the internal representation of pixels may change.
+ * - this was supposed to allow an optimization where a layer could
+ * be represented as a pointer into the mmapped xcf file, if
+ * alignment, bpp, and endianness agreed (the point was that the
+ * pixel representation had to agree with the endianness).
+ *
+ * However, it turns out that the current Gimp _always_ saves images
+ * with RLE encoding of tiles, so such an effort would be in vain.
+ *
+ * Just for modularity, nevertheless try to isolate knowledge of
+ * the RGBA-to-machine-word packing in this section of the
+ * header file. Define new macros if necessary.
+ *
+ * Given that we don't have to agree with the uncompressed
+ * RLE format, we choose to have the alpha in the _least_
+ * significant byte on all archs - it is tested and used more
+ * often than the visible channels.
+ */
+typedef uint32_t rgba ;
+
+#define ALPHA_SHIFT 0
+#define RED_SHIFT 8
+#define GREEN_SHIFT 16
+#define BLUE_SHIFT 24
+
+#define ALPHA(rgba) ((uint8_t)(rgba))
+#define FULLALPHA(rgba) ((uint8_t)(rgba) == 255)
+#define NULLALPHA(rgba) ((uint8_t)(rgba) == 0)
+#define NEWALPHA(rgb,a) (((rgba)(rgb) & 0xFFFFFF00) + (a))
+
+#ifdef PRECOMPUTED_SCALETABLE
+extern const uint8_t scaletable[256][256] ;
+#define INIT_SCALETABLE_IF(foo) ((void)0)
+#else
+extern uint8_t scaletable[256][256] ;
+extern int ok_scaletable ;
+void mk_scaletable(void);
+#define INIT_SCALETABLE_IF(foo) \
+             (ok_scaletable || !(foo) || (mk_scaletable(),0) )
+#endif
+
+extern const rgba graytable[256] ;
+extern rgba colormap[256] ;
+extern unsigned colormapLength ;
+void initLayer(struct xcfLayer *);
+void initColormap();
+
+int degrayPixel(rgba); /* returns -1 for non-gray pixels */
+
+/* ******************************************************* */
+
+#define TILEXn(dim,tx) \
+    ((tx)==(dim).tilesx ? (dim).c.r : (dim).c.l + ((tx)*TILE_WIDTH))
+#define TILEYn(dim,ty) \
+    ((ty)==(dim).tilesy ? (dim).c.b : (dim).c.t + ((ty)*TILE_HEIGHT))
+
+#if __i386__
+/* This is probably the only common architecture where small constants
+ * are more efficient for byte operations.
+ */
+typedef int8_t summary_t ;
+typedef short int refcount_t ;
+#else
+typedef int summary_t ;
+typedef int refcount_t ;
+#endif
+
+#define TILESUMMARY_UPTODATE 8
+#define TILESUMMARY_ALLNULL 4
+#define TILESUMMARY_ALLFULL 2
+#define TILESUMMARY_CRISP   1 /* everyting either null or full */
+struct Tile {
+  refcount_t refcount ;
+  summary_t summary ; /* a combination of TIMESUMMARY_FOO constatns */
+  unsigned count ;
+  rgba pixels[TILE_WIDTH * TILE_HEIGHT];
+};
+/* Actually, the Tile structures that get allocated many not have
+ * room for that many pixels. We subtract the space for those we don't
+ * use - which is Not Legal C, but ought to be portable.
+ *  OTOH, one can also use a static struct Tile for temporary storage.
+ */
+
+
+#define assertTileCompatibility(t1,t2) assert((t1)->count==(t2)->count)
+
+struct Tile *newTile(struct rect);
+struct Tile *forkTile(struct Tile*);
+void freeTile(struct Tile*);
+#define invalidateSummary(tile,mask) \
+  do{ assert((tile)->refcount==1); (tile)->summary &= mask; } while(0)
+summary_t __ATTRIBUTE__((pure)) tileSummary(struct Tile *);
+
+void fillTile(struct Tile*,rgba);
+
+/* applyMask() destructively changes tile,
+ * applyMask() gets ownership of mask
+ */
+void applyMask(struct Tile *tile, struct Tile *mask);
+
+struct Tile *getLayerTile(struct xcfLayer *,const struct rect *);
+
+#endif /* FLATTEN_H */

Added: trunk/scaletab.c
==============================================================================
--- (empty file)
+++ trunk/scaletab.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,42 @@
+/* Run-time scaletable computation for Xcftools
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "pixels.h"
+#ifndef PRECOMPUTED_SCALETABLE
+
+uint8_t scaletable[256][256] ;
+int ok_scaletable = 0 ;
+
+void
+mk_scaletable(void)
+{
+  unsigned p, q, r ;
+  if( ok_scaletable ) return ;
+  for( p = 0 ; p < 128 ; p++ )
+    for( q = 0 ; q <= p ; q++  ) {
+      r = (p*q+127)/255 ;
+      scaletable[p][q] = scaletable[q][p] = r ;
+      scaletable[255-p][q] = scaletable[q][255-p] = q-r ;
+      scaletable[p][255-q] = scaletable[255-q][p] = p-r ;
+      scaletable[255-p][255-q] = scaletable[255-q][255-p] = (255-q)-(p-r) ;
+    }
+  ok_scaletable = 1 ;
+}
+
+#endif
+    

Added: trunk/table.c
==============================================================================
--- (empty file)
+++ trunk/table.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,263 @@
+/* Autogenerated by mktablec.pl */
+#include "pixels.h"
+#ifdef PRECOMPUTED_SCALETABLE
+#error PRECOMPUTED_SCALETABLE was not defined at generation time
+#endif
+const rgba graytable[256] = {
+   (rgba)0 << RED_SHIFT | (rgba)0 << GREEN_SHIFT | (rgba)0 << BLUE_SHIFT,
+   (rgba)1 << RED_SHIFT | (rgba)1 << GREEN_SHIFT | (rgba)1 << BLUE_SHIFT,
+   (rgba)2 << RED_SHIFT | (rgba)2 << GREEN_SHIFT | (rgba)2 << BLUE_SHIFT,
+   (rgba)3 << RED_SHIFT | (rgba)3 << GREEN_SHIFT | (rgba)3 << BLUE_SHIFT,
+   (rgba)4 << RED_SHIFT | (rgba)4 << GREEN_SHIFT | (rgba)4 << BLUE_SHIFT,
+   (rgba)5 << RED_SHIFT | (rgba)5 << GREEN_SHIFT | (rgba)5 << BLUE_SHIFT,
+   (rgba)6 << RED_SHIFT | (rgba)6 << GREEN_SHIFT | (rgba)6 << BLUE_SHIFT,
+   (rgba)7 << RED_SHIFT | (rgba)7 << GREEN_SHIFT | (rgba)7 << BLUE_SHIFT,
+   (rgba)8 << RED_SHIFT | (rgba)8 << GREEN_SHIFT | (rgba)8 << BLUE_SHIFT,
+   (rgba)9 << RED_SHIFT | (rgba)9 << GREEN_SHIFT | (rgba)9 << BLUE_SHIFT,
+   (rgba)10 << RED_SHIFT | (rgba)10 << GREEN_SHIFT | (rgba)10 << BLUE_SHIFT,
+   (rgba)11 << RED_SHIFT | (rgba)11 << GREEN_SHIFT | (rgba)11 << BLUE_SHIFT,
+   (rgba)12 << RED_SHIFT | (rgba)12 << GREEN_SHIFT | (rgba)12 << BLUE_SHIFT,
+   (rgba)13 << RED_SHIFT | (rgba)13 << GREEN_SHIFT | (rgba)13 << BLUE_SHIFT,
+   (rgba)14 << RED_SHIFT | (rgba)14 << GREEN_SHIFT | (rgba)14 << BLUE_SHIFT,
+   (rgba)15 << RED_SHIFT | (rgba)15 << GREEN_SHIFT | (rgba)15 << BLUE_SHIFT,
+   (rgba)16 << RED_SHIFT | (rgba)16 << GREEN_SHIFT | (rgba)16 << BLUE_SHIFT,
+   (rgba)17 << RED_SHIFT | (rgba)17 << GREEN_SHIFT | (rgba)17 << BLUE_SHIFT,
+   (rgba)18 << RED_SHIFT | (rgba)18 << GREEN_SHIFT | (rgba)18 << BLUE_SHIFT,
+   (rgba)19 << RED_SHIFT | (rgba)19 << GREEN_SHIFT | (rgba)19 << BLUE_SHIFT,
+   (rgba)20 << RED_SHIFT | (rgba)20 << GREEN_SHIFT | (rgba)20 << BLUE_SHIFT,
+   (rgba)21 << RED_SHIFT | (rgba)21 << GREEN_SHIFT | (rgba)21 << BLUE_SHIFT,
+   (rgba)22 << RED_SHIFT | (rgba)22 << GREEN_SHIFT | (rgba)22 << BLUE_SHIFT,
+   (rgba)23 << RED_SHIFT | (rgba)23 << GREEN_SHIFT | (rgba)23 << BLUE_SHIFT,
+   (rgba)24 << RED_SHIFT | (rgba)24 << GREEN_SHIFT | (rgba)24 << BLUE_SHIFT,
+   (rgba)25 << RED_SHIFT | (rgba)25 << GREEN_SHIFT | (rgba)25 << BLUE_SHIFT,
+   (rgba)26 << RED_SHIFT | (rgba)26 << GREEN_SHIFT | (rgba)26 << BLUE_SHIFT,
+   (rgba)27 << RED_SHIFT | (rgba)27 << GREEN_SHIFT | (rgba)27 << BLUE_SHIFT,
+   (rgba)28 << RED_SHIFT | (rgba)28 << GREEN_SHIFT | (rgba)28 << BLUE_SHIFT,
+   (rgba)29 << RED_SHIFT | (rgba)29 << GREEN_SHIFT | (rgba)29 << BLUE_SHIFT,
+   (rgba)30 << RED_SHIFT | (rgba)30 << GREEN_SHIFT | (rgba)30 << BLUE_SHIFT,
+   (rgba)31 << RED_SHIFT | (rgba)31 << GREEN_SHIFT | (rgba)31 << BLUE_SHIFT,
+   (rgba)32 << RED_SHIFT | (rgba)32 << GREEN_SHIFT | (rgba)32 << BLUE_SHIFT,
+   (rgba)33 << RED_SHIFT | (rgba)33 << GREEN_SHIFT | (rgba)33 << BLUE_SHIFT,
+   (rgba)34 << RED_SHIFT | (rgba)34 << GREEN_SHIFT | (rgba)34 << BLUE_SHIFT,
+   (rgba)35 << RED_SHIFT | (rgba)35 << GREEN_SHIFT | (rgba)35 << BLUE_SHIFT,
+   (rgba)36 << RED_SHIFT | (rgba)36 << GREEN_SHIFT | (rgba)36 << BLUE_SHIFT,
+   (rgba)37 << RED_SHIFT | (rgba)37 << GREEN_SHIFT | (rgba)37 << BLUE_SHIFT,
+   (rgba)38 << RED_SHIFT | (rgba)38 << GREEN_SHIFT | (rgba)38 << BLUE_SHIFT,
+   (rgba)39 << RED_SHIFT | (rgba)39 << GREEN_SHIFT | (rgba)39 << BLUE_SHIFT,
+   (rgba)40 << RED_SHIFT | (rgba)40 << GREEN_SHIFT | (rgba)40 << BLUE_SHIFT,
+   (rgba)41 << RED_SHIFT | (rgba)41 << GREEN_SHIFT | (rgba)41 << BLUE_SHIFT,
+   (rgba)42 << RED_SHIFT | (rgba)42 << GREEN_SHIFT | (rgba)42 << BLUE_SHIFT,
+   (rgba)43 << RED_SHIFT | (rgba)43 << GREEN_SHIFT | (rgba)43 << BLUE_SHIFT,
+   (rgba)44 << RED_SHIFT | (rgba)44 << GREEN_SHIFT | (rgba)44 << BLUE_SHIFT,
+   (rgba)45 << RED_SHIFT | (rgba)45 << GREEN_SHIFT | (rgba)45 << BLUE_SHIFT,
+   (rgba)46 << RED_SHIFT | (rgba)46 << GREEN_SHIFT | (rgba)46 << BLUE_SHIFT,
+   (rgba)47 << RED_SHIFT | (rgba)47 << GREEN_SHIFT | (rgba)47 << BLUE_SHIFT,
+   (rgba)48 << RED_SHIFT | (rgba)48 << GREEN_SHIFT | (rgba)48 << BLUE_SHIFT,
+   (rgba)49 << RED_SHIFT | (rgba)49 << GREEN_SHIFT | (rgba)49 << BLUE_SHIFT,
+   (rgba)50 << RED_SHIFT | (rgba)50 << GREEN_SHIFT | (rgba)50 << BLUE_SHIFT,
+   (rgba)51 << RED_SHIFT | (rgba)51 << GREEN_SHIFT | (rgba)51 << BLUE_SHIFT,
+   (rgba)52 << RED_SHIFT | (rgba)52 << GREEN_SHIFT | (rgba)52 << BLUE_SHIFT,
+   (rgba)53 << RED_SHIFT | (rgba)53 << GREEN_SHIFT | (rgba)53 << BLUE_SHIFT,
+   (rgba)54 << RED_SHIFT | (rgba)54 << GREEN_SHIFT | (rgba)54 << BLUE_SHIFT,
+   (rgba)55 << RED_SHIFT | (rgba)55 << GREEN_SHIFT | (rgba)55 << BLUE_SHIFT,
+   (rgba)56 << RED_SHIFT | (rgba)56 << GREEN_SHIFT | (rgba)56 << BLUE_SHIFT,
+   (rgba)57 << RED_SHIFT | (rgba)57 << GREEN_SHIFT | (rgba)57 << BLUE_SHIFT,
+   (rgba)58 << RED_SHIFT | (rgba)58 << GREEN_SHIFT | (rgba)58 << BLUE_SHIFT,
+   (rgba)59 << RED_SHIFT | (rgba)59 << GREEN_SHIFT | (rgba)59 << BLUE_SHIFT,
+   (rgba)60 << RED_SHIFT | (rgba)60 << GREEN_SHIFT | (rgba)60 << BLUE_SHIFT,
+   (rgba)61 << RED_SHIFT | (rgba)61 << GREEN_SHIFT | (rgba)61 << BLUE_SHIFT,
+   (rgba)62 << RED_SHIFT | (rgba)62 << GREEN_SHIFT | (rgba)62 << BLUE_SHIFT,
+   (rgba)63 << RED_SHIFT | (rgba)63 << GREEN_SHIFT | (rgba)63 << BLUE_SHIFT,
+   (rgba)64 << RED_SHIFT | (rgba)64 << GREEN_SHIFT | (rgba)64 << BLUE_SHIFT,
+   (rgba)65 << RED_SHIFT | (rgba)65 << GREEN_SHIFT | (rgba)65 << BLUE_SHIFT,
+   (rgba)66 << RED_SHIFT | (rgba)66 << GREEN_SHIFT | (rgba)66 << BLUE_SHIFT,
+   (rgba)67 << RED_SHIFT | (rgba)67 << GREEN_SHIFT | (rgba)67 << BLUE_SHIFT,
+   (rgba)68 << RED_SHIFT | (rgba)68 << GREEN_SHIFT | (rgba)68 << BLUE_SHIFT,
+   (rgba)69 << RED_SHIFT | (rgba)69 << GREEN_SHIFT | (rgba)69 << BLUE_SHIFT,
+   (rgba)70 << RED_SHIFT | (rgba)70 << GREEN_SHIFT | (rgba)70 << BLUE_SHIFT,
+   (rgba)71 << RED_SHIFT | (rgba)71 << GREEN_SHIFT | (rgba)71 << BLUE_SHIFT,
+   (rgba)72 << RED_SHIFT | (rgba)72 << GREEN_SHIFT | (rgba)72 << BLUE_SHIFT,
+   (rgba)73 << RED_SHIFT | (rgba)73 << GREEN_SHIFT | (rgba)73 << BLUE_SHIFT,
+   (rgba)74 << RED_SHIFT | (rgba)74 << GREEN_SHIFT | (rgba)74 << BLUE_SHIFT,
+   (rgba)75 << RED_SHIFT | (rgba)75 << GREEN_SHIFT | (rgba)75 << BLUE_SHIFT,
+   (rgba)76 << RED_SHIFT | (rgba)76 << GREEN_SHIFT | (rgba)76 << BLUE_SHIFT,
+   (rgba)77 << RED_SHIFT | (rgba)77 << GREEN_SHIFT | (rgba)77 << BLUE_SHIFT,
+   (rgba)78 << RED_SHIFT | (rgba)78 << GREEN_SHIFT | (rgba)78 << BLUE_SHIFT,
+   (rgba)79 << RED_SHIFT | (rgba)79 << GREEN_SHIFT | (rgba)79 << BLUE_SHIFT,
+   (rgba)80 << RED_SHIFT | (rgba)80 << GREEN_SHIFT | (rgba)80 << BLUE_SHIFT,
+   (rgba)81 << RED_SHIFT | (rgba)81 << GREEN_SHIFT | (rgba)81 << BLUE_SHIFT,
+   (rgba)82 << RED_SHIFT | (rgba)82 << GREEN_SHIFT | (rgba)82 << BLUE_SHIFT,
+   (rgba)83 << RED_SHIFT | (rgba)83 << GREEN_SHIFT | (rgba)83 << BLUE_SHIFT,
+   (rgba)84 << RED_SHIFT | (rgba)84 << GREEN_SHIFT | (rgba)84 << BLUE_SHIFT,
+   (rgba)85 << RED_SHIFT | (rgba)85 << GREEN_SHIFT | (rgba)85 << BLUE_SHIFT,
+   (rgba)86 << RED_SHIFT | (rgba)86 << GREEN_SHIFT | (rgba)86 << BLUE_SHIFT,
+   (rgba)87 << RED_SHIFT | (rgba)87 << GREEN_SHIFT | (rgba)87 << BLUE_SHIFT,
+   (rgba)88 << RED_SHIFT | (rgba)88 << GREEN_SHIFT | (rgba)88 << BLUE_SHIFT,
+   (rgba)89 << RED_SHIFT | (rgba)89 << GREEN_SHIFT | (rgba)89 << BLUE_SHIFT,
+   (rgba)90 << RED_SHIFT | (rgba)90 << GREEN_SHIFT | (rgba)90 << BLUE_SHIFT,
+   (rgba)91 << RED_SHIFT | (rgba)91 << GREEN_SHIFT | (rgba)91 << BLUE_SHIFT,
+   (rgba)92 << RED_SHIFT | (rgba)92 << GREEN_SHIFT | (rgba)92 << BLUE_SHIFT,
+   (rgba)93 << RED_SHIFT | (rgba)93 << GREEN_SHIFT | (rgba)93 << BLUE_SHIFT,
+   (rgba)94 << RED_SHIFT | (rgba)94 << GREEN_SHIFT | (rgba)94 << BLUE_SHIFT,
+   (rgba)95 << RED_SHIFT | (rgba)95 << GREEN_SHIFT | (rgba)95 << BLUE_SHIFT,
+   (rgba)96 << RED_SHIFT | (rgba)96 << GREEN_SHIFT | (rgba)96 << BLUE_SHIFT,
+   (rgba)97 << RED_SHIFT | (rgba)97 << GREEN_SHIFT | (rgba)97 << BLUE_SHIFT,
+   (rgba)98 << RED_SHIFT | (rgba)98 << GREEN_SHIFT | (rgba)98 << BLUE_SHIFT,
+   (rgba)99 << RED_SHIFT | (rgba)99 << GREEN_SHIFT | (rgba)99 << BLUE_SHIFT,
+   (rgba)100 << RED_SHIFT | (rgba)100 << GREEN_SHIFT | (rgba)100 << BLUE_SHIFT,
+   (rgba)101 << RED_SHIFT | (rgba)101 << GREEN_SHIFT | (rgba)101 << BLUE_SHIFT,
+   (rgba)102 << RED_SHIFT | (rgba)102 << GREEN_SHIFT | (rgba)102 << BLUE_SHIFT,
+   (rgba)103 << RED_SHIFT | (rgba)103 << GREEN_SHIFT | (rgba)103 << BLUE_SHIFT,
+   (rgba)104 << RED_SHIFT | (rgba)104 << GREEN_SHIFT | (rgba)104 << BLUE_SHIFT,
+   (rgba)105 << RED_SHIFT | (rgba)105 << GREEN_SHIFT | (rgba)105 << BLUE_SHIFT,
+   (rgba)106 << RED_SHIFT | (rgba)106 << GREEN_SHIFT | (rgba)106 << BLUE_SHIFT,
+   (rgba)107 << RED_SHIFT | (rgba)107 << GREEN_SHIFT | (rgba)107 << BLUE_SHIFT,
+   (rgba)108 << RED_SHIFT | (rgba)108 << GREEN_SHIFT | (rgba)108 << BLUE_SHIFT,
+   (rgba)109 << RED_SHIFT | (rgba)109 << GREEN_SHIFT | (rgba)109 << BLUE_SHIFT,
+   (rgba)110 << RED_SHIFT | (rgba)110 << GREEN_SHIFT | (rgba)110 << BLUE_SHIFT,
+   (rgba)111 << RED_SHIFT | (rgba)111 << GREEN_SHIFT | (rgba)111 << BLUE_SHIFT,
+   (rgba)112 << RED_SHIFT | (rgba)112 << GREEN_SHIFT | (rgba)112 << BLUE_SHIFT,
+   (rgba)113 << RED_SHIFT | (rgba)113 << GREEN_SHIFT | (rgba)113 << BLUE_SHIFT,
+   (rgba)114 << RED_SHIFT | (rgba)114 << GREEN_SHIFT | (rgba)114 << BLUE_SHIFT,
+   (rgba)115 << RED_SHIFT | (rgba)115 << GREEN_SHIFT | (rgba)115 << BLUE_SHIFT,
+   (rgba)116 << RED_SHIFT | (rgba)116 << GREEN_SHIFT | (rgba)116 << BLUE_SHIFT,
+   (rgba)117 << RED_SHIFT | (rgba)117 << GREEN_SHIFT | (rgba)117 << BLUE_SHIFT,
+   (rgba)118 << RED_SHIFT | (rgba)118 << GREEN_SHIFT | (rgba)118 << BLUE_SHIFT,
+   (rgba)119 << RED_SHIFT | (rgba)119 << GREEN_SHIFT | (rgba)119 << BLUE_SHIFT,
+   (rgba)120 << RED_SHIFT | (rgba)120 << GREEN_SHIFT | (rgba)120 << BLUE_SHIFT,
+   (rgba)121 << RED_SHIFT | (rgba)121 << GREEN_SHIFT | (rgba)121 << BLUE_SHIFT,
+   (rgba)122 << RED_SHIFT | (rgba)122 << GREEN_SHIFT | (rgba)122 << BLUE_SHIFT,
+   (rgba)123 << RED_SHIFT | (rgba)123 << GREEN_SHIFT | (rgba)123 << BLUE_SHIFT,
+   (rgba)124 << RED_SHIFT | (rgba)124 << GREEN_SHIFT | (rgba)124 << BLUE_SHIFT,
+   (rgba)125 << RED_SHIFT | (rgba)125 << GREEN_SHIFT | (rgba)125 << BLUE_SHIFT,
+   (rgba)126 << RED_SHIFT | (rgba)126 << GREEN_SHIFT | (rgba)126 << BLUE_SHIFT,
+   (rgba)127 << RED_SHIFT | (rgba)127 << GREEN_SHIFT | (rgba)127 << BLUE_SHIFT,
+   (rgba)128 << RED_SHIFT | (rgba)128 << GREEN_SHIFT | (rgba)128 << BLUE_SHIFT,
+   (rgba)129 << RED_SHIFT | (rgba)129 << GREEN_SHIFT | (rgba)129 << BLUE_SHIFT,
+   (rgba)130 << RED_SHIFT | (rgba)130 << GREEN_SHIFT | (rgba)130 << BLUE_SHIFT,
+   (rgba)131 << RED_SHIFT | (rgba)131 << GREEN_SHIFT | (rgba)131 << BLUE_SHIFT,
+   (rgba)132 << RED_SHIFT | (rgba)132 << GREEN_SHIFT | (rgba)132 << BLUE_SHIFT,
+   (rgba)133 << RED_SHIFT | (rgba)133 << GREEN_SHIFT | (rgba)133 << BLUE_SHIFT,
+   (rgba)134 << RED_SHIFT | (rgba)134 << GREEN_SHIFT | (rgba)134 << BLUE_SHIFT,
+   (rgba)135 << RED_SHIFT | (rgba)135 << GREEN_SHIFT | (rgba)135 << BLUE_SHIFT,
+   (rgba)136 << RED_SHIFT | (rgba)136 << GREEN_SHIFT | (rgba)136 << BLUE_SHIFT,
+   (rgba)137 << RED_SHIFT | (rgba)137 << GREEN_SHIFT | (rgba)137 << BLUE_SHIFT,
+   (rgba)138 << RED_SHIFT | (rgba)138 << GREEN_SHIFT | (rgba)138 << BLUE_SHIFT,
+   (rgba)139 << RED_SHIFT | (rgba)139 << GREEN_SHIFT | (rgba)139 << BLUE_SHIFT,
+   (rgba)140 << RED_SHIFT | (rgba)140 << GREEN_SHIFT | (rgba)140 << BLUE_SHIFT,
+   (rgba)141 << RED_SHIFT | (rgba)141 << GREEN_SHIFT | (rgba)141 << BLUE_SHIFT,
+   (rgba)142 << RED_SHIFT | (rgba)142 << GREEN_SHIFT | (rgba)142 << BLUE_SHIFT,
+   (rgba)143 << RED_SHIFT | (rgba)143 << GREEN_SHIFT | (rgba)143 << BLUE_SHIFT,
+   (rgba)144 << RED_SHIFT | (rgba)144 << GREEN_SHIFT | (rgba)144 << BLUE_SHIFT,
+   (rgba)145 << RED_SHIFT | (rgba)145 << GREEN_SHIFT | (rgba)145 << BLUE_SHIFT,
+   (rgba)146 << RED_SHIFT | (rgba)146 << GREEN_SHIFT | (rgba)146 << BLUE_SHIFT,
+   (rgba)147 << RED_SHIFT | (rgba)147 << GREEN_SHIFT | (rgba)147 << BLUE_SHIFT,
+   (rgba)148 << RED_SHIFT | (rgba)148 << GREEN_SHIFT | (rgba)148 << BLUE_SHIFT,
+   (rgba)149 << RED_SHIFT | (rgba)149 << GREEN_SHIFT | (rgba)149 << BLUE_SHIFT,
+   (rgba)150 << RED_SHIFT | (rgba)150 << GREEN_SHIFT | (rgba)150 << BLUE_SHIFT,
+   (rgba)151 << RED_SHIFT | (rgba)151 << GREEN_SHIFT | (rgba)151 << BLUE_SHIFT,
+   (rgba)152 << RED_SHIFT | (rgba)152 << GREEN_SHIFT | (rgba)152 << BLUE_SHIFT,
+   (rgba)153 << RED_SHIFT | (rgba)153 << GREEN_SHIFT | (rgba)153 << BLUE_SHIFT,
+   (rgba)154 << RED_SHIFT | (rgba)154 << GREEN_SHIFT | (rgba)154 << BLUE_SHIFT,
+   (rgba)155 << RED_SHIFT | (rgba)155 << GREEN_SHIFT | (rgba)155 << BLUE_SHIFT,
+   (rgba)156 << RED_SHIFT | (rgba)156 << GREEN_SHIFT | (rgba)156 << BLUE_SHIFT,
+   (rgba)157 << RED_SHIFT | (rgba)157 << GREEN_SHIFT | (rgba)157 << BLUE_SHIFT,
+   (rgba)158 << RED_SHIFT | (rgba)158 << GREEN_SHIFT | (rgba)158 << BLUE_SHIFT,
+   (rgba)159 << RED_SHIFT | (rgba)159 << GREEN_SHIFT | (rgba)159 << BLUE_SHIFT,
+   (rgba)160 << RED_SHIFT | (rgba)160 << GREEN_SHIFT | (rgba)160 << BLUE_SHIFT,
+   (rgba)161 << RED_SHIFT | (rgba)161 << GREEN_SHIFT | (rgba)161 << BLUE_SHIFT,
+   (rgba)162 << RED_SHIFT | (rgba)162 << GREEN_SHIFT | (rgba)162 << BLUE_SHIFT,
+   (rgba)163 << RED_SHIFT | (rgba)163 << GREEN_SHIFT | (rgba)163 << BLUE_SHIFT,
+   (rgba)164 << RED_SHIFT | (rgba)164 << GREEN_SHIFT | (rgba)164 << BLUE_SHIFT,
+   (rgba)165 << RED_SHIFT | (rgba)165 << GREEN_SHIFT | (rgba)165 << BLUE_SHIFT,
+   (rgba)166 << RED_SHIFT | (rgba)166 << GREEN_SHIFT | (rgba)166 << BLUE_SHIFT,
+   (rgba)167 << RED_SHIFT | (rgba)167 << GREEN_SHIFT | (rgba)167 << BLUE_SHIFT,
+   (rgba)168 << RED_SHIFT | (rgba)168 << GREEN_SHIFT | (rgba)168 << BLUE_SHIFT,
+   (rgba)169 << RED_SHIFT | (rgba)169 << GREEN_SHIFT | (rgba)169 << BLUE_SHIFT,
+   (rgba)170 << RED_SHIFT | (rgba)170 << GREEN_SHIFT | (rgba)170 << BLUE_SHIFT,
+   (rgba)171 << RED_SHIFT | (rgba)171 << GREEN_SHIFT | (rgba)171 << BLUE_SHIFT,
+   (rgba)172 << RED_SHIFT | (rgba)172 << GREEN_SHIFT | (rgba)172 << BLUE_SHIFT,
+   (rgba)173 << RED_SHIFT | (rgba)173 << GREEN_SHIFT | (rgba)173 << BLUE_SHIFT,
+   (rgba)174 << RED_SHIFT | (rgba)174 << GREEN_SHIFT | (rgba)174 << BLUE_SHIFT,
+   (rgba)175 << RED_SHIFT | (rgba)175 << GREEN_SHIFT | (rgba)175 << BLUE_SHIFT,
+   (rgba)176 << RED_SHIFT | (rgba)176 << GREEN_SHIFT | (rgba)176 << BLUE_SHIFT,
+   (rgba)177 << RED_SHIFT | (rgba)177 << GREEN_SHIFT | (rgba)177 << BLUE_SHIFT,
+   (rgba)178 << RED_SHIFT | (rgba)178 << GREEN_SHIFT | (rgba)178 << BLUE_SHIFT,
+   (rgba)179 << RED_SHIFT | (rgba)179 << GREEN_SHIFT | (rgba)179 << BLUE_SHIFT,
+   (rgba)180 << RED_SHIFT | (rgba)180 << GREEN_SHIFT | (rgba)180 << BLUE_SHIFT,
+   (rgba)181 << RED_SHIFT | (rgba)181 << GREEN_SHIFT | (rgba)181 << BLUE_SHIFT,
+   (rgba)182 << RED_SHIFT | (rgba)182 << GREEN_SHIFT | (rgba)182 << BLUE_SHIFT,
+   (rgba)183 << RED_SHIFT | (rgba)183 << GREEN_SHIFT | (rgba)183 << BLUE_SHIFT,
+   (rgba)184 << RED_SHIFT | (rgba)184 << GREEN_SHIFT | (rgba)184 << BLUE_SHIFT,
+   (rgba)185 << RED_SHIFT | (rgba)185 << GREEN_SHIFT | (rgba)185 << BLUE_SHIFT,
+   (rgba)186 << RED_SHIFT | (rgba)186 << GREEN_SHIFT | (rgba)186 << BLUE_SHIFT,
+   (rgba)187 << RED_SHIFT | (rgba)187 << GREEN_SHIFT | (rgba)187 << BLUE_SHIFT,
+   (rgba)188 << RED_SHIFT | (rgba)188 << GREEN_SHIFT | (rgba)188 << BLUE_SHIFT,
+   (rgba)189 << RED_SHIFT | (rgba)189 << GREEN_SHIFT | (rgba)189 << BLUE_SHIFT,
+   (rgba)190 << RED_SHIFT | (rgba)190 << GREEN_SHIFT | (rgba)190 << BLUE_SHIFT,
+   (rgba)191 << RED_SHIFT | (rgba)191 << GREEN_SHIFT | (rgba)191 << BLUE_SHIFT,
+   (rgba)192 << RED_SHIFT | (rgba)192 << GREEN_SHIFT | (rgba)192 << BLUE_SHIFT,
+   (rgba)193 << RED_SHIFT | (rgba)193 << GREEN_SHIFT | (rgba)193 << BLUE_SHIFT,
+   (rgba)194 << RED_SHIFT | (rgba)194 << GREEN_SHIFT | (rgba)194 << BLUE_SHIFT,
+   (rgba)195 << RED_SHIFT | (rgba)195 << GREEN_SHIFT | (rgba)195 << BLUE_SHIFT,
+   (rgba)196 << RED_SHIFT | (rgba)196 << GREEN_SHIFT | (rgba)196 << BLUE_SHIFT,
+   (rgba)197 << RED_SHIFT | (rgba)197 << GREEN_SHIFT | (rgba)197 << BLUE_SHIFT,
+   (rgba)198 << RED_SHIFT | (rgba)198 << GREEN_SHIFT | (rgba)198 << BLUE_SHIFT,
+   (rgba)199 << RED_SHIFT | (rgba)199 << GREEN_SHIFT | (rgba)199 << BLUE_SHIFT,
+   (rgba)200 << RED_SHIFT | (rgba)200 << GREEN_SHIFT | (rgba)200 << BLUE_SHIFT,
+   (rgba)201 << RED_SHIFT | (rgba)201 << GREEN_SHIFT | (rgba)201 << BLUE_SHIFT,
+   (rgba)202 << RED_SHIFT | (rgba)202 << GREEN_SHIFT | (rgba)202 << BLUE_SHIFT,
+   (rgba)203 << RED_SHIFT | (rgba)203 << GREEN_SHIFT | (rgba)203 << BLUE_SHIFT,
+   (rgba)204 << RED_SHIFT | (rgba)204 << GREEN_SHIFT | (rgba)204 << BLUE_SHIFT,
+   (rgba)205 << RED_SHIFT | (rgba)205 << GREEN_SHIFT | (rgba)205 << BLUE_SHIFT,
+   (rgba)206 << RED_SHIFT | (rgba)206 << GREEN_SHIFT | (rgba)206 << BLUE_SHIFT,
+   (rgba)207 << RED_SHIFT | (rgba)207 << GREEN_SHIFT | (rgba)207 << BLUE_SHIFT,
+   (rgba)208 << RED_SHIFT | (rgba)208 << GREEN_SHIFT | (rgba)208 << BLUE_SHIFT,
+   (rgba)209 << RED_SHIFT | (rgba)209 << GREEN_SHIFT | (rgba)209 << BLUE_SHIFT,
+   (rgba)210 << RED_SHIFT | (rgba)210 << GREEN_SHIFT | (rgba)210 << BLUE_SHIFT,
+   (rgba)211 << RED_SHIFT | (rgba)211 << GREEN_SHIFT | (rgba)211 << BLUE_SHIFT,
+   (rgba)212 << RED_SHIFT | (rgba)212 << GREEN_SHIFT | (rgba)212 << BLUE_SHIFT,
+   (rgba)213 << RED_SHIFT | (rgba)213 << GREEN_SHIFT | (rgba)213 << BLUE_SHIFT,
+   (rgba)214 << RED_SHIFT | (rgba)214 << GREEN_SHIFT | (rgba)214 << BLUE_SHIFT,
+   (rgba)215 << RED_SHIFT | (rgba)215 << GREEN_SHIFT | (rgba)215 << BLUE_SHIFT,
+   (rgba)216 << RED_SHIFT | (rgba)216 << GREEN_SHIFT | (rgba)216 << BLUE_SHIFT,
+   (rgba)217 << RED_SHIFT | (rgba)217 << GREEN_SHIFT | (rgba)217 << BLUE_SHIFT,
+   (rgba)218 << RED_SHIFT | (rgba)218 << GREEN_SHIFT | (rgba)218 << BLUE_SHIFT,
+   (rgba)219 << RED_SHIFT | (rgba)219 << GREEN_SHIFT | (rgba)219 << BLUE_SHIFT,
+   (rgba)220 << RED_SHIFT | (rgba)220 << GREEN_SHIFT | (rgba)220 << BLUE_SHIFT,
+   (rgba)221 << RED_SHIFT | (rgba)221 << GREEN_SHIFT | (rgba)221 << BLUE_SHIFT,
+   (rgba)222 << RED_SHIFT | (rgba)222 << GREEN_SHIFT | (rgba)222 << BLUE_SHIFT,
+   (rgba)223 << RED_SHIFT | (rgba)223 << GREEN_SHIFT | (rgba)223 << BLUE_SHIFT,
+   (rgba)224 << RED_SHIFT | (rgba)224 << GREEN_SHIFT | (rgba)224 << BLUE_SHIFT,
+   (rgba)225 << RED_SHIFT | (rgba)225 << GREEN_SHIFT | (rgba)225 << BLUE_SHIFT,
+   (rgba)226 << RED_SHIFT | (rgba)226 << GREEN_SHIFT | (rgba)226 << BLUE_SHIFT,
+   (rgba)227 << RED_SHIFT | (rgba)227 << GREEN_SHIFT | (rgba)227 << BLUE_SHIFT,
+   (rgba)228 << RED_SHIFT | (rgba)228 << GREEN_SHIFT | (rgba)228 << BLUE_SHIFT,
+   (rgba)229 << RED_SHIFT | (rgba)229 << GREEN_SHIFT | (rgba)229 << BLUE_SHIFT,
+   (rgba)230 << RED_SHIFT | (rgba)230 << GREEN_SHIFT | (rgba)230 << BLUE_SHIFT,
+   (rgba)231 << RED_SHIFT | (rgba)231 << GREEN_SHIFT | (rgba)231 << BLUE_SHIFT,
+   (rgba)232 << RED_SHIFT | (rgba)232 << GREEN_SHIFT | (rgba)232 << BLUE_SHIFT,
+   (rgba)233 << RED_SHIFT | (rgba)233 << GREEN_SHIFT | (rgba)233 << BLUE_SHIFT,
+   (rgba)234 << RED_SHIFT | (rgba)234 << GREEN_SHIFT | (rgba)234 << BLUE_SHIFT,
+   (rgba)235 << RED_SHIFT | (rgba)235 << GREEN_SHIFT | (rgba)235 << BLUE_SHIFT,
+   (rgba)236 << RED_SHIFT | (rgba)236 << GREEN_SHIFT | (rgba)236 << BLUE_SHIFT,
+   (rgba)237 << RED_SHIFT | (rgba)237 << GREEN_SHIFT | (rgba)237 << BLUE_SHIFT,
+   (rgba)238 << RED_SHIFT | (rgba)238 << GREEN_SHIFT | (rgba)238 << BLUE_SHIFT,
+   (rgba)239 << RED_SHIFT | (rgba)239 << GREEN_SHIFT | (rgba)239 << BLUE_SHIFT,
+   (rgba)240 << RED_SHIFT | (rgba)240 << GREEN_SHIFT | (rgba)240 << BLUE_SHIFT,
+   (rgba)241 << RED_SHIFT | (rgba)241 << GREEN_SHIFT | (rgba)241 << BLUE_SHIFT,
+   (rgba)242 << RED_SHIFT | (rgba)242 << GREEN_SHIFT | (rgba)242 << BLUE_SHIFT,
+   (rgba)243 << RED_SHIFT | (rgba)243 << GREEN_SHIFT | (rgba)243 << BLUE_SHIFT,
+   (rgba)244 << RED_SHIFT | (rgba)244 << GREEN_SHIFT | (rgba)244 << BLUE_SHIFT,
+   (rgba)245 << RED_SHIFT | (rgba)245 << GREEN_SHIFT | (rgba)245 << BLUE_SHIFT,
+   (rgba)246 << RED_SHIFT | (rgba)246 << GREEN_SHIFT | (rgba)246 << BLUE_SHIFT,
+   (rgba)247 << RED_SHIFT | (rgba)247 << GREEN_SHIFT | (rgba)247 << BLUE_SHIFT,
+   (rgba)248 << RED_SHIFT | (rgba)248 << GREEN_SHIFT | (rgba)248 << BLUE_SHIFT,
+   (rgba)249 << RED_SHIFT | (rgba)249 << GREEN_SHIFT | (rgba)249 << BLUE_SHIFT,
+   (rgba)250 << RED_SHIFT | (rgba)250 << GREEN_SHIFT | (rgba)250 << BLUE_SHIFT,
+   (rgba)251 << RED_SHIFT | (rgba)251 << GREEN_SHIFT | (rgba)251 << BLUE_SHIFT,
+   (rgba)252 << RED_SHIFT | (rgba)252 << GREEN_SHIFT | (rgba)252 << BLUE_SHIFT,
+   (rgba)253 << RED_SHIFT | (rgba)253 << GREEN_SHIFT | (rgba)253 << BLUE_SHIFT,
+   (rgba)254 << RED_SHIFT | (rgba)254 << GREEN_SHIFT | (rgba)254 << BLUE_SHIFT,
+   (rgba)255 << RED_SHIFT | (rgba)255 << GREEN_SHIFT | (rgba)255 << BLUE_SHIFT,
+};

Added: trunk/utils.c
==============================================================================
--- (empty file)
+++ trunk/utils.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,164 @@
+/* Generic support functions for Xcftools
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "xcftools.h"
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <errno.h>
+
+const char *progname = "$0" ;
+int verboseFlag = 0 ;
+
+
+static void  __ATTRIBUTE__((noreturn))
+vFatalGeneric(int status,const char *format,va_list args)
+{
+  if( format ) {
+    if( *format == '!' ) {
+      vfprintf(stderr,format+1,args);
+      fprintf(stderr,": %s\n",strerror(errno));
+    } else {
+      vfprintf(stderr,format,args);
+      fputc('\n',stderr);
+    }
+  }
+  exit(status);
+}
+
+void
+FatalGeneric(int status,const char* format,...)
+{
+  va_list v; va_start(v,format);
+  if( format ) fprintf(stderr,"%s: ",progname);
+  vFatalGeneric(status,format,v);
+}
+
+void
+FatalUnexpected(const char* format,...)
+{
+  va_list v; va_start(v,format);
+  fprintf(stderr,"%s: ",progname);
+  vFatalGeneric(127,format,v) ;
+}
+
+void
+FatalBadXCF(const char* format,...)
+{
+  va_list v; va_start(v,format);
+  fprintf(stderr,"%s: %s:\n ",progname,_("Corrupted or malformed XCF file"));
+  vFatalGeneric(125,format,v) ;
+}
+
+void
+xcfCheckspace(uint32_t addr,int spaceafter,const char *format,...)
+{
+  if( xcf_length < spaceafter || addr > xcf_length - spaceafter ) {
+    va_list v; va_start(v,format);
+    fprintf(stderr,"%s: %s\n ",progname,_("Corrupted or truncated XCF file"));
+    fprintf(stderr,"(0x%" PRIXPTR " bytes): ",(uintptr_t)xcf_length);
+    vFatalGeneric(125,format,v) ;
+  }
+}
+
+
+void
+FatalUnsupportedXCF(const char* format,...)
+{
+  va_list v; va_start(v,format);
+  fprintf(stderr,"%s: %s\n ",progname,
+          _("The image contains features not understood by this program:"));
+  vFatalGeneric(123,format,v) ;
+}
+
+void
+gpl_blurb(void)
+{
+  fprintf(stderr,PACKAGE_STRING "\n");
+  fprintf(stderr,
+          _("This program is free software; you can modify and distribute it\n"
+            "under the terms of the GNU General Public License, version 2.\n"
+            "There is no warranty for %s.\n\n"),
+          PACKAGE_NAME);
+  fprintf(stderr,
+          _("Type \"%s -h\" to get an option summary.\n"),progname);
+  exit(1) ;
+}
+
+/* ******************************************************* */
+
+void *
+xcfmalloc(size_t size)
+{
+  void *ptr = malloc(size);
+  if( !ptr )
+    FatalUnexpected(_("Out of memory"));
+  return ptr ;
+}
+
+void
+xcffree(void *block)
+{
+  if( xcf_file &&
+      (uint8_t*)block >= xcf_file &&
+      (uint8_t*)block < xcf_file + xcf_length )
+    ;
+  else
+    free(block);
+}
+
+/* ******************************************************* */
+
+FILE *
+openout(const char *name)
+{
+  FILE *newfile ;
+  if( strcmp(name,"-") == 0 )
+    return stdout ;
+  newfile = fopen(name,"wb") ;
+  if( newfile == NULL )
+    FatalUnexpected(_("!Cannot create file %s"),name);
+  return newfile ;
+}
+
+void
+closeout(FILE *f,const char *name)
+{
+  if( f == NULL )
+    return ;
+  if( fflush(f) == 0 ) {
+    errno = 0 ;
+    if( !ferror(f) ) {
+      if( fclose(f) == 0 )
+        return ;
+    } else if( errno == 0 ) {
+      /* Attempt to coax a valid errno out of the standard library,
+       * following an idea by Bruno Haible
+       * http://lists.gnu.org/archive/html/bug-gnulib/2003-09/msg00157.html
+       */
+      if( fputc('\0', f) != EOF &&
+          fflush(f) == 0 )
+        errno = EIO ; /* Argh, everything succeds. Just call it an I/O error */
+    }
+  }
+  FatalUnexpected(_("!Error writing file %s"),name);
+}
+
+        
+      
+    

Added: trunk/xcf-general.c
==============================================================================
--- (empty file)
+++ trunk/xcf-general.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,287 @@
+/* Generic functions for reading XCF files
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "xcftools.h"
+#include <string.h>
+#include <errno.h>
+#ifdef HAVE_ICONV
+# include <iconv.h>
+#elif !defined(ICONV_CONST)
+# define ICONV_CONST const
+#endif
+
+uint8_t *xcf_file = 0 ;
+size_t xcf_length ;
+int use_utf8 = 0 ;
+
+uint32_t
+xcfOffset(uint32_t addr,int spaceafter)
+{
+  uint32_t apparent ;
+  xcfCheckspace(addr,4,"(xcfOffset)");
+  apparent = xcfL(addr);
+  xcfCheckspace(apparent,spaceafter,
+                "Too large offset (%" PRIX32 ") at position %" PRIX32,
+                apparent,addr);
+  return apparent ;
+}
+
+int
+xcfNextprop(uint32_t *master,uint32_t *body)
+{
+  uint32_t ptr, length, total, minlength ;
+  PropType type ;
+  ptr = *master ;
+  xcfCheckspace(ptr,8,"(property header)");
+  type = xcfL(ptr);
+  length = xcfL(ptr+4);
+  *body = ptr+8 ;
+
+  switch(type) {
+  case PROP_COLORMAP:
+    {
+      uint32_t ncolors ;
+      xcfCheckspace(ptr+8,4,"(colormap length)");
+      ncolors = xcfL(ptr+8) ;
+      if( ncolors > 256 )
+        FatalBadXCF("Colormap has %" PRIu32 " entries",ncolors);
+      /* Surprise! Some older verion of the Gimp computed the wrong length
+       * word, and the _reader_ always just reads three bytes per color
+       * and ignores the length tag! Duplicate this so we too can read
+       * the buggy XCF files.
+       */
+      length = minlength = 4+3*ncolors;
+      break;
+    }
+  case PROP_COMPRESSION: minlength = 1; break;
+  case PROP_OPACITY:     minlength = 4; break;
+  case PROP_APPLY_MASK:  minlength = 4; break;
+  case PROP_OFFSETS:     minlength = 8; break;
+  case PROP_MODE:        minlength = 4; break;
+  default:               minlength = 0; break;
+  }
+  if( length < minlength )
+    FatalBadXCF("Short %s property at %" PRIX32 " (%" PRIu32 "<%" PRIu32 ")",
+                showPropType(type),ptr,length,minlength);
+  *master = ptr+8+length ;
+  total = 8 + length + (type != PROP_END ? 8 : 0) ;
+  if( total < length ) /* Check overwrap */
+    FatalBadXCF("Overlong property at %" PRIX32, ptr);
+  xcfCheckspace(ptr,total,"Overlong property at %" PRIX32,ptr) ;
+  return type ;
+}
+
+const char*
+xcfString(uint32_t ptr,uint32_t *after)
+{
+  uint32_t length ;
+  unsigned i ;
+  ICONV_CONST char *utf8master ;
+  
+  xcfCheckspace(ptr,4,"(string length)");
+  length = xcfL(ptr) ;
+  ptr += 4 ;
+  xcfCheckspace(ptr,length,"(string)");
+  utf8master = (ICONV_CONST char*)(xcf_file+ptr) ;
+  if( after ) *after = ptr + length ;
+  if( length == 0 || utf8master[length-1] != 0 )
+    FatalBadXCF("String at %" PRIX32 " not zero-terminated",ptr-4);
+  length-- ;
+
+  if( use_utf8 ) return utf8master ;
+
+  /* We assume that the local character set includes ASCII...
+   * Check if conversion is needed at all
+   */
+  for( i=0 ; ; i++ ) {
+    if( i == length )
+      return utf8master ; /* Only ASCII after all */
+    if( utf8master[i] == 0 )
+      FatalBadXCF("String at %" PRIX32 " has embedded zeroes",ptr-4);
+    if( (int8_t) utf8master[i] < 0 )
+      break ;
+  }
+#ifdef HAVE_ICONV
+  {
+    size_t targetsize = length+1 ;
+    int sloppy_translation = 0 ;
+    iconv_t cd = iconv_open("//TRANSLIT","UTF-8");
+    if( cd == (iconv_t) -1 ) {
+      cd = iconv_open("","UTF-8");
+      sloppy_translation = 1 ;
+    }
+    if( cd == (iconv_t) -1 )
+      iconv_close(cd) ; /* Give up; perhaps iconv doesn't know UTF-8 */
+    else
+      while(1) {
+        char *buffer = xcfmalloc(targetsize) ;
+        ICONV_CONST char *inbuf = utf8master ;
+        char *outbuf = buffer ;
+        size_t incount = length ;
+        size_t outcount = targetsize ;
+        while(1) { /* Loop for systems without //ICONV support */
+          size_t result = iconv(cd,&inbuf,&incount,&outbuf,&outcount) ;
+          if( result == (size_t)-1 && errno == EILSEQ &&
+              sloppy_translation && outcount > 0 ) {
+            *outbuf++ = '?' ;
+            outcount-- ;
+            while( (int8_t)*inbuf < 0 ) inbuf++, incount-- ;
+            continue ;
+          }
+          if( result != (size_t)-1 ) {
+            if( outcount == 0 )
+              errno = E2BIG ;
+            else {
+              *outbuf = 0 ;
+              iconv_close(cd) ;
+              return buffer ;
+            }
+          }
+          break ;
+        }
+        if( errno == EILSEQ || errno == EINVAL )
+          FatalBadXCF("Bad UTF-8 encoding '%s' at %" PRIXPTR,
+                      inbuf,(uintptr_t)((inbuf-utf8master)+ptr));
+        if( errno == E2BIG ) {
+          targetsize += 1+incount ;
+          xcffree(buffer) ;
+          continue ;
+        }
+        FatalUnexpected("!iconv on layer name at %"PRIX32,ptr);
+      }
+  }
+#endif
+  {
+    static int warned = 0 ;
+    if( !warned ) {
+      fprintf(stderr,_("Warning: one or more layer names could not be\n"
+                       "         translated to the local character set.\n"));
+      warned = 1 ;
+    }
+  }
+  return utf8master ;
+}
+
+/* ****************************************************************** */
+
+void
+computeDimensions(struct tileDimensions *d)
+{
+  d->c.r = d->c.l + d->width ;
+  d->c.b = d->c.t + d->height ;
+  d->tilesx = (d->width+TILE_WIDTH-1)/TILE_WIDTH ;
+  d->tilesy = (d->height+TILE_HEIGHT-1)/TILE_HEIGHT ;
+  d->ntiles = d->tilesx * d->tilesy ;
+}
+
+struct xcfImage XCF ;
+
+void
+getBasicXcfInfo(void)
+{
+  uint32_t ptr, data, layerfile ;
+  PropType type ;
+  int i ;
+  
+  xcfCheckspace(0,14+7*4,"(very short)");
+  if( strcmp((char*)xcf_file,"gimp xcf file") == 0 )
+    XCF.version = 0 ;
+  else if( xcf_file[13] == 0 &&
+          sscanf((char*)xcf_file,"gimp xcf v%d",&XCF.version) == 1 )
+    ;
+  else
+    FatalBadXCF(_("Not an XCF file at all (magic not recognized)"));
+
+  if( XCF.version < 0 || XCF.version > 2 ) {
+    fprintf(stderr,
+            _("Warning: XCF version %d not supported (trying anyway...)\n"),
+            XCF.version);
+  }
+  
+  XCF.compression = COMPRESS_NONE ;
+  XCF.colormapptr = 0 ;
+  
+  ptr = 14 ;
+  XCF.width    = xcfL(ptr); ptr += 4 ;
+  XCF.height   = xcfL(ptr); ptr += 4 ;
+  XCF.type     = xcfL(ptr); ptr += 4 ;
+  while( (type = xcfNextprop(&ptr,&data)) != PROP_END ) {
+    switch(type) {
+    case PROP_COLORMAP:
+      XCF.colormapptr = data ;
+      break ;
+    case PROP_COMPRESSION:
+      XCF.compression = xcf_file[data] ;
+      break ;
+    default:
+      /* Ignore unknown properties */
+      break ;
+    }
+  }
+
+  layerfile = ptr ;
+  for( XCF.numLayers = 0 ; xcfOffset(ptr,8*4) ; XCF.numLayers++, ptr+=4  )
+    ;
+  XCF.layers = xcfmalloc(XCF.numLayers * sizeof(struct xcfLayer)) ;
+  for( i = 0 ; i < XCF.numLayers ; i++ ) {
+    struct xcfLayer *L = XCF.layers + i ;
+    ptr = xcfL(layerfile+4*(XCF.numLayers-1-i)) ;
+    L->mode = GIMP_NORMAL_MODE ;
+    L->opacity = 255 ;
+    L->isVisible = 1 ;
+    L->hasMask = 0 ;
+    L->dim.width = xcfL(ptr); ptr+=4 ;
+    L->dim.height = xcfL(ptr); ptr+=4 ;
+    L->type = xcfL(ptr); ptr+=4 ;
+    L->name = xcfString(ptr,&ptr);
+    L->propptr = ptr ;
+    while( (type = xcfNextprop(&ptr,&data)) != PROP_END ) {
+      switch(type) {
+      case PROP_OPACITY:
+        L->opacity = xcfL(data);
+        if( L->opacity > 255 )
+          L->opacity = 255 ;
+        break ;
+      case PROP_VISIBLE:
+        L->isVisible = xcfL(data) != 0 ;
+        break ;
+      case PROP_APPLY_MASK:
+        L->hasMask = xcfL(data) != 0 ;
+        break ;
+      case PROP_OFFSETS:
+        L->dim.c.l = (int32_t)(xcfL(data  )) ;
+        L->dim.c.t = (int32_t)(xcfL(data+4)) ;
+        break ;
+      case PROP_MODE:
+        L->mode = xcfL(data);
+        break ;
+      default:
+        /* Ignore unknown properties */
+        break ;
+      }
+    }
+    xcfCheckspace(ptr,8,"(end of layer %s)",L->name);
+    L->pixels.tileptrs = 0 ;
+    L->pixels.hierarchy = xcfOffset(ptr  ,4*4);
+    L->mask.tileptrs = 0 ;
+    L->mask.hierarchy   = xcfOffset(ptr+4,4*4);
+
+    computeDimensions(&L->dim);
+  }
+}
+

Added: trunk/xcf2png.c
==============================================================================
--- (empty file)
+++ trunk/xcf2png.c	Thu May  1 14:32:48 2008
@@ -0,0 +1,427 @@
+/* Convert xcf files to png
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "xcftools.h"
+#include "flatten.h"
+#include "palette.h"
+#include <png.h>
+#include <stdlib.h>
+#include <string.h>
+#include <locale.h>
+#include <ctype.h>
+#if HAVE_GETOPT_H
+#include <getopt.h>
+#else
+#include <unistd.h>
+#endif
+#ifndef HAVE_GETOPT_LONG
+#define getopt_long(argc,argv,optstring,l1,l2) getopt(argc,argv,optstring)
+#endif
+
+#include "xcf2png.oi"
+
+static void
+usage(FILE *where)
+{
+  fprintf(where,_("Usage: %s [options] filename.xcf[.gz] [layers]\n"),
+          progname) ;
+  fprintf(where,_("Options:\n"));
+  opt_usage(where);
+  if( where == stderr ) {
+    exit(1);
+  }
+}
+
+static struct FlattenSpec flatspec ;
+
+static FILE *outfile = NULL ;
+static png_structp libpng = NULL ;
+static png_infop libpng2 = NULL ;
+
+static void
+my_error_callback(png_structp png_ptr, png_const_charp errormsg)
+{
+  FatalUnexpected(_("Libpng error '%s'"),errormsg);
+}
+
+  
+static void
+init_output(void)
+{
+  int bit_depth ;
+  int color_type ;
+  int invert_mono = 0 ;
+  png_colorp pngpalette = NULL ;
+  png_bytep ptrans = NULL ;
+  
+  outfile = openout(flatspec.output_filename);
+  libpng = png_create_write_struct(PNG_LIBPNG_VER_STRING,
+                                   png_voidp_NULL,
+                                   my_error_callback,
+                                   png_error_ptr_NULL);
+  if( !libpng )
+    FatalUnexpected(_("Couldn't initialize libpng library"));
+  
+  libpng2 = png_create_info_struct(libpng);
+  if( !libpng2 )
+    FatalUnexpected("Couldn't create PNG info structure");
+
+  png_init_io(libpng,outfile);
+  
+  bit_depth = 8;
+  switch( flatspec.out_color_mode ) {
+  case COLOR_GRAY:
+    if( flatspec.default_pixel == PERHAPS_ALPHA_CHANNEL ||
+        flatspec.default_pixel == FORCE_ALPHA_CHANNEL )
+      color_type = PNG_COLOR_TYPE_GRAY_ALPHA ;
+    else
+      color_type = PNG_COLOR_TYPE_GRAY ;
+    break ;
+  case COLOR_RGB:
+    if( flatspec.default_pixel == PERHAPS_ALPHA_CHANNEL ||
+        flatspec.default_pixel == FORCE_ALPHA_CHANNEL )
+      color_type = PNG_COLOR_TYPE_RGB_ALPHA ;
+    else
+      color_type = PNG_COLOR_TYPE_RGB ;
+    break ;
+  case COLOR_INDEXED:
+    if( paletteSize == 2 &&
+        palette[0] == NEWALPHA(0,255) &&
+        palette[1] == NEWALPHA(-1,255) ) {
+      color_type = PNG_COLOR_TYPE_GRAY ;
+      bit_depth = 1 ;
+    } else if( paletteSize == 2 &&
+               palette[0] == NEWALPHA(-1,255) &&
+               palette[1] == NEWALPHA(0,255) ) {
+      color_type = PNG_COLOR_TYPE_GRAY ;
+      bit_depth = 1 ;
+      invert_mono = 1 ;
+    } else {
+      unsigned i ;
+      int need_trans = flatspec.default_pixel == FORCE_ALPHA_CHANNEL ;
+      color_type = PNG_COLOR_TYPE_PALETTE ;
+      pngpalette = xcfmalloc(paletteSize*sizeof(png_color)) ;
+      ptrans = xcfmalloc(paletteSize);
+      for(i = 0; i<paletteSize; i++ ) {
+        pngpalette[i].red = 255 & (palette[i] >> RED_SHIFT);
+        pngpalette[i].green = 255 & (palette[i] >> GREEN_SHIFT);
+        pngpalette[i].blue = 255 & (palette[i] >> BLUE_SHIFT);
+        if( (ptrans[i] = ALPHA(palette[i])) != 255 )
+          need_trans = 1 ;
+      }
+      if( !need_trans ) {
+        xcffree(ptrans);
+        ptrans = NULL ;
+      }
+      if( paletteSize <= 2 )
+        bit_depth = 1 ;
+      else if( paletteSize <= 4 )
+        bit_depth = 2 ;
+      else if( paletteSize <= 16 )
+        bit_depth = 4 ;
+      else
+        bit_depth = 8;
+    }
+    break ;
+  default:
+    FatalUnexpected("This can't happen (unknown out_color_mode)");
+  }
+
+  if( verboseFlag ) {
+    fprintf(stderr,"Writing PNG: %s%s%s%s, %d bits",
+            color_type & PNG_COLOR_MASK_COLOR ? _("color") : _("grayscale"),
+            color_type & PNG_COLOR_MASK_PALETTE ? _("+palette") : "",
+            color_type & PNG_COLOR_MASK_ALPHA ? _("+alpha") : "",
+            ptrans || NULLALPHA(flatspec.default_pixel)
+            ? _("+transparency") : "",
+            bit_depth);
+    if( pngpalette )
+      fprintf(stderr,_(" (%d colors)"),paletteSize);
+    fprintf(stderr,"\n");
+  }
+  
+  png_set_IHDR(libpng,libpng2,flatspec.dim.width,flatspec.dim.height,
+               bit_depth, color_type,
+               PNG_INTERLACE_NONE,
+               PNG_COMPRESSION_TYPE_DEFAULT,
+               PNG_FILTER_TYPE_DEFAULT);
+
+  if( invert_mono )
+    png_set_invert_mono(libpng);
+  
+  if( pngpalette )
+    png_set_PLTE(libpng,libpng2,pngpalette,paletteSize);
+  if( ptrans )
+    png_set_tRNS(libpng,libpng2,ptrans,paletteSize,NULL);
+  else if ( !pngpalette &&
+            NULLALPHA(flatspec.default_pixel) ) {
+    static png_color_16 trans ;
+    trans.gray =
+      trans.red = 255 & (flatspec.default_pixel >> RED_SHIFT) ;
+    trans.green = 255 & (flatspec.default_pixel >> GREEN_SHIFT) ;
+    trans.blue = 255 & (flatspec.default_pixel >> BLUE_SHIFT) ;
+    png_set_tRNS(libpng,libpng2,NULL,0,&trans);
+  }
+
+  /* png_set_text here */
+
+  png_write_info(libpng,libpng2);
+
+  if( bit_depth < 8 )
+    png_set_packing(libpng);
+
+  switch( color_type ) {
+  case PNG_COLOR_TYPE_RGB:
+  case PNG_COLOR_TYPE_RGBA:
+#if (BLUE_SHIFT < RED_SHIFT) == !defined(WORDS_BIGENDIAN)
+    png_set_bgr(libpng);
+#endif
+    if( color_type == PNG_COLOR_TYPE_RGB )
+#if (ALPHA_SHIFT < RED_SHIFT) == !defined(WORDS_BIGENDIAN)
+      png_set_filler(libpng,0,PNG_FILLER_BEFORE);
+    else
+      png_set_swap_alpha(libpng);
+#else
+    png_set_filler(libpng,0,PNG_FILLER_AFTER);
+#endif
+    break ;
+  case PNG_COLOR_TYPE_GRAY:
+    png_set_filler(libpng,0,PNG_FILLER_AFTER);
+    break ;
+  case PNG_COLOR_TYPE_GRAY_ALPHA:
+  case PNG_COLOR_TYPE_PALETTE:
+    break ;
+  default:
+    FatalUnexpected("This can't happen (unexpected png color_type)");
+  }
+}
+
+
+static void
+raw_callback(unsigned num, rgba *pixels) {
+  if( libpng == NULL ) {
+    init_output() ;
+  }
+  png_write_row(libpng,(png_bytep)pixels);
+  xcffree(pixels);
+}
+
+static void
+graying_callback(unsigned num, rgba *pixels) {
+  png_bytep fillptr = (uint8_t *)pixels ;
+  unsigned i ;
+  for( i = 0 ; i < num ; i++ ) {
+    rgba pixel = pixels[i] ;
+    int g = degrayPixel(pixel) ;
+    if( g == -1 )
+      FatalGeneric(103,
+                   _("Grayscale output selected, but colored pixel(s) found"));
+    *fillptr++ = g ;
+    *fillptr++ = ALPHA(pixel) ;
+  }
+  raw_callback(num,pixels);
+}
+
+static void
+optimistic_palette_callback(unsigned num,rgba *pixels) {
+  unsigned prev_size = paletteSize ;
+  if( !palettify_row(pixels,num)  || paletteSize != prev_size )
+    FatalUnexpected("Oops! Somehow the precomputed palette does not suffice "
+                    "after all...");
+  raw_callback(num,pixels);
+}
+
+static enum out_color_mode
+guessIndexed(struct FlattenSpec *spec,rgba *allPixels[])
+{
+  if( allPixels == NULL ) {
+    if (spec->gimpish_indexed && colormapLength ) {
+      unsigned i ;
+      init_palette_hash();
+      for( i=0; i<colormapLength; i++ )
+        lookup_or_intern(NEWALPHA(colormap[i],255));
+      if( lookup_or_intern( FULLALPHA(spec->default_pixel) ?
+                            spec->default_pixel : 0 ) >= 0 )
+        return COLOR_INDEXED ;
+    }
+  } else {
+    init_palette_hash() ;
+    if( palettify_rows(allPixels,spec->dim.width,spec->dim.height) ) {
+      /* Might grayscale sometimes be preferred? No, that is what
+       * -g is for! */
+      return COLOR_INDEXED ;
+    }
+  }
+  return COLOR_BY_CONTENTS ;
+}
+
+static lineCallback
+selectCallback(void)
+{
+  switch( flatspec.out_color_mode ) {
+  default:
+  case COLOR_RGB: return &raw_callback ;
+  case COLOR_GRAY: return &graying_callback ;
+  case COLOR_INDEXED:
+    if( flatspec.process_in_memory )
+      return &raw_callback ;
+    else
+      return &optimistic_palette_callback ;
+  }
+}
+
+/* findUnusedColor() will prefer to find a gray pixel */
+static rgba
+findUnusedColor(rgba *pixels[],unsigned width,unsigned height)
+{
+  size_t freqtab[256] ;
+  unsigned x,y ;
+  unsigned i,j ;
+  rgba sofar ;
+  
+  for( i=0; i<256; i++ )
+    freqtab[i] = 0 ;
+  for( y=0; y<height; y++ )
+    for( x=0; x<width; x++ )
+      if( pixels[y][x] )
+        freqtab[255 & (pixels[y][x] >> RED_SHIFT)] ++ ;
+  j = 0 ;
+  for( i=0; i<256; i++ ) {
+    if( freqtab[i] == 0 ) {
+      return ((rgba)i << RED_SHIFT) +
+        ((rgba)i << GREEN_SHIFT) +
+        ((rgba)i << BLUE_SHIFT) +
+        ((rgba)255 << ALPHA_SHIFT) ;
+    }
+    if( freqtab[i] < freqtab[j] ) {
+      j = i ;
+    }
+  }
+  sofar = ((rgba)255<<ALPHA_SHIFT) + ((rgba)j << RED_SHIFT) ;
+
+  for( i=0; i<256; i++ )
+    freqtab[i] = 0 ;
+  for( y=0; y<height; y++ )
+    for( x=0; x<width; x++ )
+      if( ((((rgba)255 << ALPHA_SHIFT) + ((rgba)255 << RED_SHIFT))
+           & pixels[y][x]) == sofar )
+        freqtab[255 & (pixels[y][x] >> GREEN_SHIFT)] ++ ;
+  j = 0 ;
+  for( i=0; i<256; i++ ) {
+    if( freqtab[i] == 0 ) {
+      return sofar + ((rgba)i << GREEN_SHIFT);
+    }
+    if( freqtab[i] < freqtab[j] ) {
+      j = i ;
+    }
+  }
+  sofar += (rgba)j << GREEN_SHIFT ;
+
+  for( i=0; i<256; i++ )
+    freqtab[i] = 0 ;
+  for( y=0; y<height; y++ )
+    for( x=0; x<width; x++ )
+      if( ((((rgba)255 << ALPHA_SHIFT) +
+           ((rgba)255 << RED_SHIFT) +
+            ((rgba)255 << GREEN_SHIFT))
+           & pixels[y][x]) == sofar )
+        freqtab[255 & (pixels[y][x] >> BLUE_SHIFT)] ++ ;
+  for( i=0; i<256; i++ ) {
+    if( freqtab[i] == 0 ) {
+      return sofar + ((rgba)i << BLUE_SHIFT);
+    }
+  }
+
+  return 0 ;
+}
+
+int
+main(int argc,char **argv)
+{
+  int option ;
+  const char *unzipper = NULL ;
+  const char *infile = NULL ;
+
+  setlocale(LC_ALL,"");
+  progname = argv[0] ;
+  nls_init();
+
+  if( argc <= 1 ) gpl_blurb() ;
+  
+  init_flatspec(&flatspec) ;
+  while( (option=getopt_long(argc,argv,"-"OPTSTRING,longopts,NULL)) >= 0 )
+    switch(option) {
+      #define OPTION(char,long,desc,man) case char:
+      #include "options.i"
+    case 1:
+      if( infile ) 
+        add_layer_request(&flatspec,optarg);
+      else
+        infile = optarg ;
+      break ;
+    case '?':
+      usage(stderr);
+    default:
+      FatalUnexpected("Getopt(_long) unexpectedly returned '%c'",option);
+    }
+  if( infile == NULL ) {
+    usage(stderr);
+  }
+  
+  read_or_mmap_xcf(infile,unzipper);
+  getBasicXcfInfo() ;
+  initColormap();
+ 
+  complete_flatspec(&flatspec,guessIndexed);
+  if( flatspec.process_in_memory ) {
+    rgba **allPixels = flattenAll(&flatspec);
+     
+    analyse_colormode(&flatspec,allPixels,guessIndexed);
+
+    /* See if we can do alpha compaction.
+     */
+    if( flatspec.partial_transparency_mode != ALLOW_PARTIAL_TRANSPARENCY &&
+        !FULLALPHA(flatspec.default_pixel) &&
+        flatspec.out_color_mode != COLOR_INDEXED ) {
+      rgba unused = findUnusedColor(allPixels,
+                                    flatspec.dim.width,
+                                    flatspec.dim.height);
+      if( unused && (flatspec.out_color_mode == COLOR_RGB ||
+                     degrayPixel(unused) >= 0) ) {
+        unsigned x,y ;
+        unused = NEWALPHA(unused,0) ;
+        for( y=0; y<flatspec.dim.height; y++)
+          for( x=0; x<flatspec.dim.width; x++)
+            if( allPixels[y][x] == 0 )
+              allPixels[y][x] = unused ;
+        flatspec.default_pixel = unused ;
+      }
+    }
+    shipoutWithCallback(&flatspec,allPixels,selectCallback());
+  } else {
+    flattenIncrementally(&flatspec,selectCallback());
+  }
+  if( libpng ) {
+    png_write_end(libpng,libpng2);
+    png_destroy_write_struct(&libpng,&libpng2);
+  }
+  if( outfile ) {
+    closeout(outfile,flatspec.output_filename);
+  }
+  return 0 ;
+}

Added: trunk/xcf2png.oi
==============================================================================

Added: trunk/xcftools.h
==============================================================================
--- (empty file)
+++ trunk/xcftools.h	Thu May  1 14:32:48 2008
@@ -0,0 +1,189 @@
+/* Generic functions and macros for reading XCF files
+ *
+ * Copyright (C) 2006  Henning Makholm
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * 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
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef XCFTOOLS_H
+#define XCFTOOLS_H
+
+#include "config.h"
+#include "enums.h"
+#include <stddef.h>
+#include <stdio.h>
+
+#if defined(HAVE_GETTEXT) && defined(ENABLE_NLS)
+#include <libintl.h>
+#define _(s) gettext(s)
+void nls_init(void);
+#else
+#define _(s) (s)
+#define nls_init() (void)0
+#endif
+#define N_(s) (s)
+
+#if HAVE_INTTYPES_H
+# define __STDC_FORMAT_MACROS
+# include <inttypes.h>
+#else
+/* These legacy fall-backs will probably work on every system
+ * that does not supply a inttypes.h ... */
+typedef unsigned char     uint8_t ;
+typedef unsigned long int uint32_t, uintptr_t ;
+typedef signed char       int8_t ;
+typedef signed long int   int32_t ;
+# define PRIX32 "lX"
+# define PRIu32 "lu"
+# define PRIXPTR "lX"
+#endif
+
+#if __GNUC__
+# define __ATTRIBUTE__ __attribute__
+#else
+# define __ATTRIBUTE__(x)
+#endif
+
+#if HAVE_NETINET_IN_H
+# include <netinet/in.h>
+#elif HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#elif WORDS_BIGENDIAN
+# define ntohl(x) (x)
+#else
+static inline uint32_t ntohl(uint32_t a) {
+  return (a << 24) + ((a & 0xFF00) << 8) + ((a >> 8) & 0xFF00) + (a >> 24) ;
+}
+#endif
+
+#ifndef HAVE_STRCASECMP
+#define strcasecmp strcmp
+#endif
+
+/* Read a single word value from the XCF file */
+
+/* Use + instead of | because that allows LEA instructions */
+#define xcfBE(a) ( ((uint32_t)xcf_file[(a)  ] << 24) + \
+                   ((uint32_t)xcf_file[(a)+1] << 16) + \
+                   ((uint32_t)xcf_file[(a)+2] << 8 ) + \
+                   ((uint32_t)xcf_file[(a)+3]      ) )
+#define xcfLE(a) ( ((uint32_t)xcf_file[(a)  ]      ) + \
+                   ((uint32_t)xcf_file[(a)+1] << 8 ) + \
+                   ((uint32_t)xcf_file[(a)+2] << 16) + \
+                   ((uint32_t)xcf_file[(a)+3] << 24) )
+
+#if CAN_DO_UNALIGNED_WORDS
+# define xcfL(a) ntohl(*(uint32_t *)(xcf_file + (a)))
+#else
+# define xcfL(a) ((a) & 3 ? xcfBE(a) : ntohl(*(uint32_t *)(xcf_file + (a))))
+#endif
+
+/* ****************************************************************** */
+
+/* The following are exported from am OS-specific source file;
+ * io-unix.c on unixish systems.
+ */
+void read_or_mmap_xcf(const char* filename, const char *unzipper);
+void free_or_close_xcf(void);
+
+/* ****************************************************************** */
+/* utils.c */
+
+extern const char *progname ;
+extern int verboseFlag ;
+
+void *xcfmalloc(size_t size);
+void xcffree(void*);
+
+void FatalGeneric(int status,const char* format,...)
+     __ATTRIBUTE__((format(printf,2,3),noreturn)) ;
+void FatalUnexpected(const char* format,...)
+     __ATTRIBUTE__((format(printf,1,2),noreturn)) ;
+void FatalBadXCF(const char* format,...)
+     __ATTRIBUTE__((format(printf,1,2),noreturn)) ;
+void FatalUnsupportedXCF(const char* format,...)
+     __ATTRIBUTE__((format(printf,1,2),noreturn)) ;
+
+void gpl_blurb(void) __ATTRIBUTE__((noreturn));
+     
+FILE* openout(const char*);
+void closeout(FILE *,const char*);
+
+struct rect {
+  int t, b, l, r ;
+};
+
+#define isSubrect(A,B) \
+  ((A).l >= (B).l && (A).r <= (B).r && (A).t >= (B).t && (A).b <= (B).b)
+#define disjointRects(A,B) \
+  ((A).l >= (B).r || (A).r <= (B).l || (A).t >= (B).b || (A).b <= (B).t)
+
+/* ****************************************************************** */
+/* xcf-general.c */
+
+extern uint8_t *xcf_file ;
+extern size_t xcf_length ;
+extern int use_utf8 ;
+
+void xcfCheckspace(uint32_t addr,int spaceafter, const char *format,...)
+     __ATTRIBUTE__((format(printf,3,4)));
+uint32_t xcfOffset(uint32_t addr,int spaceafter);
+
+int xcfNextprop(uint32_t *master,uint32_t *body);
+const char* xcfString(uint32_t ptr,uint32_t *after);
+
+/* These are hardcoded in the Gimp sources: */
+#define TILE_WIDTH 64
+#define TILE_HEIGHT 64
+
+struct tileDimensions {
+  struct rect c ;
+  unsigned width, height ;
+  unsigned tilesx, tilesy ;
+  unsigned ntiles ;
+};
+/* computeDimensions assumes that width, height, c.l, and c.t are set */
+void computeDimensions(struct tileDimensions *);
+
+struct xcfTiles {
+  const struct _convertParams *params ;
+  uint32_t *tileptrs ;
+  uint32_t hierarchy ;
+};
+
+struct xcfLayer {
+  struct tileDimensions dim ;
+  const char *name ;
+  GimpLayerModeEffects mode ;
+  GimpImageType type ;
+  unsigned int opacity ;
+  int isVisible, hasMask ;
+  uint32_t propptr ;
+  struct xcfTiles pixels ;
+  struct xcfTiles mask ;
+}; 
+
+extern struct xcfImage {
+  int version ;
+  unsigned width, height ;
+  GimpImageBaseType type ;
+  XcfCompressionType compression ;
+  int numLayers ;
+  struct xcfLayer *layers ;
+  uint32_t colormapptr ;
+} XCF ;
+
+void getBasicXcfInfo(void);
+
+#endif /* XCFTOOLS_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]