You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
8.2 KiB
288 lines
8.2 KiB
/* 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);
|
|
}
|
|
}
|
|
|