summaryrefslogtreecommitdiffstats
path: root/xcf-general.c
diff options
context:
space:
mode:
authorHenning Makholm <henning@makholm.net>2006-01-27 18:00:00 (EST)
committer Julien Jorge <julien.jorge@stuff-o-matic.com>2013-01-10 16:00:40 (EST)
commit7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717 (patch)
tree98d2772f50aaddb02ac94492d2b8b151aa3e9465 /xcf-general.c
downloadxcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.zip
xcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.tar.gz
xcftools-7b7cd6da61b1fcc0f2a3ecce2cb9e6c42782c717.tar.bz2
Import of release 0.7
Diffstat (limited to 'xcf-general.c')
-rw-r--r--xcf-general.c273
1 files changed, 273 insertions, 0 deletions
diff --git a/xcf-general.c b/xcf-general.c
new file mode 100644
index 0000000..c1d7da9
--- /dev/null
+++ b/xcf-general.c
@@ -0,0 +1,273 @@
+/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 ;
+ *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) ;
+
+ switch(type) {
+ case PROP_COLORMAP: minlength = 4+3*xcfL(ptr+8); 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);
+ 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);
+ }
+}
+