diff --git a/src/modules/Image/encodings/png.c b/src/modules/Image/encodings/png.c index f9849164f160a2a2ddf57f1f7c3ad87143d68c68..2577a64e9d687d950c6461f9ac6b50dbd56298ad 100644 --- a/src/modules/Image/encodings/png.c +++ b/src/modules/Image/encodings/png.c @@ -1,5 +1,5 @@ #include "global.h" -RCSID("$Id: png.c,v 1.6 1998/04/03 03:49:50 mirar Exp $"); +RCSID("$Id: png.c,v 1.7 1998/04/05 21:10:43 mirar Exp $"); #include "config.h" @@ -343,9 +343,11 @@ static void image_png___decode(INT32 args) */ static struct pike_string *_png_unfilter(unsigned char *data, - INT32 len,int xsize, + INT32 len, + int xsize,int ysize, int filter,int type, - int bpp,int interlace) + int bpp, + unsigned char **pos) { struct pike_string *ps; unsigned char *d; @@ -372,9 +374,23 @@ static struct pike_string *_png_unfilter(unsigned char *data, for (;;) { - if (!len) return end_shared_string(ps); + if (!len || !ysize--) + { + if (pos) *pos=s; + return end_shared_string(ps); + } x=xsize; + fprintf(stderr, + "%05d 0x%08lx %02x %02x %02x > %02x < %02x %02x %02x " + "len=%d xsize=%d sbb=%d next=0x%08lx d=0x%lx nd=0x%lx\n", + ysize+1,(unsigned long)s, + s[-3],s[-2],s[-1],s[0],s[1],s[2],s[3], + len,xsize,sbb, + (unsigned long)s+xsize+1, + (unsigned long)d, + (unsigned long)d+xsize); + switch (*(s++)) { case 0: /* no filter */ @@ -457,219 +473,37 @@ static struct pike_string *_png_unfilter(unsigned char *data, } } -static void image_png__decode(INT32 args) +static int _png_write_rgb(rgb_group *w1, + rgb_group *wa1, + int type,int bpp, + unsigned char *s, + unsigned long len, + unsigned long width, + int n, + struct neo_colortable *ct) { - struct array *a; - struct mapping *m; - struct neo_colortable *ct=NULL; - rgb_group *d1,*da1,*w1,*wa1,*t1; - struct pike_string *fs; - unsigned char *s; - struct image *img; + /* returns 1 if interlace, 0 if not */ + /* w1, wa1 will be freed upon error */ static rgb_group white={255,255,255}; static rgb_group grey4[4]={{0,0,0},{85,85,85},{170,170,170},{255,255,255}}; static rgb_group black={0,0,0}; - int n=0,i,mz,x,y; - struct ihdr - { - INT32 width,height; - int bpp; /* bit depth, 1, 2, 4, 8 or 16 */ - int type; /* 0, 2,3,4 or 6 */ - int compression; /* 0 */ - int filter; - int interlace; - } ihdr={-1,-1,-1,0,-1,-1,-1}; - - if (args<1) - error("Image.PNG.__decode: too few arguments\n"); - - pop_n_elems(args-1); - - if (sp[-1].type==T_STRING) - { - push_int(1); /* no care crc */ - image_png___decode(2); - if (sp[-1].type!=T_ARRAY) - error("Image.PNG._decode: Not PNG data\n"); - } - else if (sp[-1].type!=T_ARRAY) - error("Image.PNG._decode: Illegal argument\n"); - - (a=sp[-1].u.array)->refs++; - - pop_n_elems(1); - - m=allocate_mapping(10); - push_mapping(m); - - for (i=0; i<a->size; i++) - { - struct array *b; - unsigned char *data; - unsigned long len; - - if (a->item[i].type!=T_ARRAY || - (b=a->item[i].u.array)->size!=3 || - b->item[0].type!=T_STRING || - b->item[1].type!=T_STRING || - b->item[0].u.string->len!=4) - error("Image.PNG._decode: Illegal stuff in array index %d\n",i); - - data=(unsigned char*)b->item[1].u.string->str; - len=(unsigned long)b->item[1].u.string->len; - - switch (int_from_32bit((unsigned char*)b->item[0].u.string->str)) - { - /* ------ major chunks ------------ */ - case 0x49484452: /* IHDR */ - /* header info */ - if (b->item[1].u.string->len!=13) - error("Image.PNG._decode: illegal header (IHDR chunk)\n"); - - ihdr.width=int_from_32bit(data+0); - ihdr.height=int_from_32bit(data+4); - ihdr.bpp=data[8]; - ihdr.type=data[9]; - ihdr.compression=data[10]; - ihdr.filter=data[11]; - ihdr.interlace=data[12]; - break; - - case 0x504c5445: /* PLTE */ - /* palette info, 3×n bytes */ - - push_string(b->item[1].u.string); - b->item[1].u.string->refs++; - push_object(clone_object(image_colortable_program,1)); - - if (ihdr.type==3) - { - ct=(struct neo_colortable*) - get_storage(sp[-1].u.object,image_colortable_program); - push_string(param_palette); - param_palette->refs++; - mapping_insert(m,sp-1,sp-2); - } - else - { - push_string(param_spalette); - param_spalette->refs++; - mapping_insert(m,sp-1,sp-2); - } - pop_n_elems(2); - break; - - case 0x49444154: /* IDAT */ - /* compressed image data. push, n++ */ - if (ihdr.compression!=0) - free_mapping(m); - - push_string(b->item[1].u.string); - b->item[1].u.string->refs++; - n++; - if (n>32) { f_add(n); n=1; } - break; - - case 0x49454e44: /* IEND */ - /* end of file */ - break; - - /* ------ minor chunks ------------ */ - - case 0x6348524d: /* cHRM */ - break; - - case 0x67414d41: /* gAMA */ - break; - - case 0x73424954: /* sBIT */ - break; - - case 0x624b4744: /* bKGD */ - break; - - case 0x68495354: /* hIST */ - break; - - case 0x74524e53: /* tRNS */ - break; - - case 0x70485973: /* pHYs */ - break; - - case 0x74494d45: /* tIME */ - break; - - case 0x7a455874: /* tEXt */ - break; - - case 0x7a545874: /* zTXt */ - break; - } - } - - /* on stack: mapping n×string */ - - /* IDAT stuff on stack, now */ - f_add(n); - - if (ihdr.type==-1) - { - error("Image.PNG._decode: missing header (IHDR chunk)\n"); - } - if (ihdr.type==3 && !ct) - { - error("Image.PNG._decode: missing palette (PLTE chunk)\n"); - } - - if (ihdr.compression==0) - { - png_decompress(ihdr.compression); - if (sp[-1].type!=T_STRING) - error("Image.PNG._decode: got wierd stuff from decompression\n"); - } - else - error("Image.PNG._decode: illegal compression type 0x%02x\n", - ihdr.compression); - - fs=sp[-1].u.string; - push_int(-1); - mapping_insert(m,sp-2,sp-1); + rgb_group *d1=w1; + rgb_group *da1=wa1; - /* not thread-safe */ - fs=_png_unfilter((unsigned char*)fs->str,fs->len, - ihdr.width,ihdr.filter,ihdr.type,ihdr.bpp, - ihdr.interlace); - push_string(fs); - push_int(-1); - mapping_insert(m,sp-2,sp-1); - pop_n_elems(4); - - s=(unsigned char*)fs->str; - - w1=d1=malloc(sizeof(rgb_group)*ihdr.width*ihdr.height); - if (!d1) - error("Image.PNG._decode: Out of memory\n"); - - wa1=da1=malloc(sizeof(rgb_group)*ihdr.width*ihdr.height); - if (!da1) - { - free(d1); - error("Image.PNG._decode: Out of memory\n"); - } + unsigned long x,mz; /* write stuff to d1 */ - n=ihdr.width*ihdr.height; - switch (ihdr.type) + + switch (type) { case 0: /* 1,2,4,8 or 16 bit greyscale */ - switch (ihdr.bpp) + switch (bpp) { case 1: - if (n>fs->len*8) n=fs->len*8; - x=ihdr.width; + if (n>len*8) n=len*8; + x=width; while (n) { if (x) x--,*(d1++)=((*s)&128)?white:black; @@ -683,12 +517,12 @@ static void image_png__decode(INT32 args) if (n<8) break; n-=8; s++; - if (!x) x=ihdr.width; + if (!x) x=width; } break; case 2: - if (n>fs->len*4) n=fs->len*4; - x=ihdr.width; + if (n>len*4) n=len*4; + x=width; while (n) { if (x) x--,*(d1++)=grey4[((*s)>>6)&3]; @@ -698,12 +532,12 @@ static void image_png__decode(INT32 args) if (n<4) break; n-=4; s++; - if (!x) x=ihdr.width; + if (!x) x=width; } break; case 4: - if (n>fs->len*2) n=fs->len*2; - x=ihdr.width; + if (n>len*2) n=len*2; + x=width; while (n) { int q; @@ -720,11 +554,11 @@ static void image_png__decode(INT32 args) if (n<2) break; n-=2; s++; - if (!x) x=ihdr.width; + if (!x) x=width; } break; case 8: - if (n>fs->len) n=fs->len; + if (n>len) n=len; while (n) { d1->r=d1->g=d1->b=*(s++); d1++; @@ -732,7 +566,7 @@ static void image_png__decode(INT32 args) } break; case 16: - if (n>fs->len/2) n=fs->len/2; + if (n>len/2) n=len/2; while (n) { d1->r=d1->g=d1->b=*(s++); d1++; s++; @@ -742,17 +576,15 @@ static void image_png__decode(INT32 args) default: free(wa1); free(w1); error("Image.PNG->_decode: Unsupported color type/bit depth %d (grey)/%d bit.\n", - ihdr.type,ihdr.bpp); + type,bpp); } - free(wa1); /* no alpha channel */ - wa1=NULL; - break; + return 0; /* no alpha channel */ case 2: /* 8 or 16 bit r,g,b */ - switch (ihdr.bpp) + switch (bpp) { case 8: - if (n>fs->len/3) n=fs->len/3; + if (n>len/3) n=len/3; while (n) { d1->r=*(s++); @@ -763,7 +595,7 @@ static void image_png__decode(INT32 args) } break; case 16: - if (n>fs->len/6) n=fs->len/6; + if (n>len/6) n=len/6; while (n) { d1->r=*(s++); @@ -777,25 +609,31 @@ static void image_png__decode(INT32 args) default: free(wa1); free(w1); error("Image.PNG->_decode: Unsupported color type/bit depth %d (rgb)/%d bit.\n", - ihdr.type,ihdr.bpp); + type,bpp); } - free(wa1); /* no alpha channel */ - wa1=NULL; - break; + return 0; /* no alpha channel */ case 3: /* 1,2,4,8 bit palette index */ if (!ct) + { + free(w1); + free(wa1); error("Image.PNG->decode: No palette (PLTE entry), but color type (3) needs one\n"); + } if (ct->type!=NCT_FLAT) + { + free(w1); + free(wa1); error("Image.PNG->decode: Internal error (created palette isn't flat)\n"); + } mz=ct->u.flat.numentries; #define CUTPLTE(X,Z) (((X)>=(Z))?0:(X)) - switch (ihdr.bpp) + switch (bpp) { case 1: - if (n>fs->len*8) n=fs->len*8; - x=ihdr.width; + if (n>len*8) n=len*8; + x=width; while (n) { if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>7)&1,mz)].color; @@ -809,12 +647,12 @@ static void image_png__decode(INT32 args) s++; if (n<8) break; n-=8; - if (!x) x=ihdr.width; + if (!x) x=width; } break; case 2: - if (n>fs->len*4) n=fs->len*4; - x=ihdr.width; + if (n>len*4) n=len*4; + x=width; while (n) { if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>6)&3,mz)].color; @@ -824,12 +662,12 @@ static void image_png__decode(INT32 args) s++; if (n<4) break; n-=4; - if (!x) x=ihdr.width; + if (!x) x=width; } break; case 4: - if (n>fs->len*2) n=fs->len*2; - x=ihdr.width; + if (n>len*2) n=len*2; + x=width; while (n) { if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>4)&15,mz)].color; @@ -837,11 +675,11 @@ static void image_png__decode(INT32 args) s++; if (n<2) break; n--; - if (!x) x=ihdr.width; + if (!x) x=width; } break; case 8: - if (n>fs->len) n=fs->len; + if (n>len) n=len; while (n) { *(d1++)=ct->u.flat.entries[CUTPLTE(*s,mz)].color; @@ -852,17 +690,15 @@ static void image_png__decode(INT32 args) default: error("Image.PNG->_decode: Unsupported color type/bit depth %d (palette)/%d bit.\n", - ihdr.type,ihdr.bpp); + type,bpp); } - free(wa1); /* no alpha channel */ - wa1=NULL; - break; + return 0; /* no alpha channel */ case 4: /* 8 or 16 bit grey,a */ - switch (ihdr.bpp) + switch (bpp) { case 8: - if (n>fs->len/3) n=fs->len/3; + if (n>len/3) n=len/3; while (n) { d1->r=d1->g=d1->b=*(s++); @@ -873,7 +709,7 @@ static void image_png__decode(INT32 args) } break; case 16: - if (n>fs->len/6) n=fs->len/6; + if (n>len/6) n=len/6; while (n) { d1->r=d1->g=d1->b=*(s++); @@ -888,15 +724,15 @@ static void image_png__decode(INT32 args) default: free(wa1); free(w1); error("Image.PNG->_decode: Unsupported color type/bit depth %d (grey+a)/%d bit.\n", - ihdr.type,ihdr.bpp); + type,bpp); } - break; + return 1; /* alpha channel */ case 6: /* 8 or 16 bit r,g,b,a */ - switch (ihdr.bpp) + switch (bpp) { case 8: - if (n>fs->len/3) n=fs->len/3; + if (n>len/3) n=len/3; while (n) { d1->r=*(s++); @@ -909,7 +745,7 @@ static void image_png__decode(INT32 args) } break; case 16: - if (n>fs->len/6) n=fs->len/6; + if (n>len/6) n=len/6; while (n) { d1->r=*(s++); @@ -926,13 +762,220 @@ static void image_png__decode(INT32 args) default: free(wa1); free(w1); error("Image.PNG->_decode: Unsupported color type/bit depth %d(rgba)/%d bit.\n", - ihdr.type,ihdr.bpp); + type,bpp); } - break; + return 1; /* alpha channel */ default: free(wa1); free(w1); error("Image.PNG->_decode: Unknown color type %d (bit depth %d).\n", - ihdr.type,ihdr.bpp); + type,bpp); + } +} + +struct png_interlace +{ + int y0,yd,x0,xd; +}; + +static struct png_interlace adam7[8]= +{ {0,8,0,8}, + {0,8,4,8}, + {4,8,0,4}, + {0,4,2,4}, + {2,4,0,2}, + {0,2,1,2}, + {1,2,0,1} }; + +static void image_png__decode(INT32 args) +{ + struct array *a; + struct mapping *m; + struct neo_colortable *ct=NULL; + rgb_group *d1,*da1,*w1,*wa1,*t1; + struct pike_string *fs; + unsigned char *s,*s0; + struct image *img; + + int n=0,i,x,y; + struct ihdr + { + INT32 width,height; + int bpp; /* bit depth, 1, 2, 4, 8 or 16 */ + int type; /* 0, 2,3,4 or 6 */ + int compression; /* 0 */ + int filter; + int interlace; + } ihdr={-1,-1,-1,0,-1,-1,-1}; + + if (args<1) + error("Image.PNG.__decode: too few arguments\n"); + + pop_n_elems(args-1); + + if (sp[-1].type==T_STRING) + { + push_int(1); /* no care crc */ + image_png___decode(2); + if (sp[-1].type!=T_ARRAY) + error("Image.PNG._decode: Not PNG data\n"); + } + else if (sp[-1].type!=T_ARRAY) + error("Image.PNG._decode: Illegal argument\n"); + + (a=sp[-1].u.array)->refs++; + + pop_n_elems(1); + + m=allocate_mapping(10); + push_mapping(m); + + for (i=0; i<a->size; i++) + { + struct array *b; + unsigned char *data; + unsigned long len; + + if (a->item[i].type!=T_ARRAY || + (b=a->item[i].u.array)->size!=3 || + b->item[0].type!=T_STRING || + b->item[1].type!=T_STRING || + b->item[0].u.string->len!=4) + error("Image.PNG._decode: Illegal stuff in array index %d\n",i); + + data=(unsigned char*)b->item[1].u.string->str; + len=(unsigned long)b->item[1].u.string->len; + + switch (int_from_32bit((unsigned char*)b->item[0].u.string->str)) + { + /* ------ major chunks ------------ */ + case 0x49484452: /* IHDR */ + /* header info */ + if (b->item[1].u.string->len!=13) + error("Image.PNG._decode: illegal header (IHDR chunk)\n"); + + ihdr.width=int_from_32bit(data+0); + ihdr.height=int_from_32bit(data+4); + ihdr.bpp=data[8]; + ihdr.type=data[9]; + ihdr.compression=data[10]; + ihdr.filter=data[11]; + ihdr.interlace=data[12]; + break; + + case 0x504c5445: /* PLTE */ + /* palette info, 3×n bytes */ + + push_string(b->item[1].u.string); + b->item[1].u.string->refs++; + push_object(clone_object(image_colortable_program,1)); + + if (ihdr.type==3) + { + ct=(struct neo_colortable*) + get_storage(sp[-1].u.object,image_colortable_program); + push_string(param_palette); + param_palette->refs++; + mapping_insert(m,sp-1,sp-2); + } + else + { + push_string(param_spalette); + param_spalette->refs++; + mapping_insert(m,sp-1,sp-2); + } + pop_n_elems(2); + break; + + case 0x49444154: /* IDAT */ + /* compressed image data. push, n++ */ + if (ihdr.compression!=0) + free_mapping(m); + + push_string(b->item[1].u.string); + b->item[1].u.string->refs++; + n++; + if (n>32) { f_add(n); n=1; } + break; + + case 0x49454e44: /* IEND */ + /* end of file */ + break; + + /* ------ minor chunks ------------ */ + + case 0x6348524d: /* cHRM */ + break; + + case 0x67414d41: /* gAMA */ + break; + + case 0x73424954: /* sBIT */ + break; + + case 0x624b4744: /* bKGD */ + break; + + case 0x68495354: /* hIST */ + break; + + case 0x74524e53: /* tRNS */ + break; + + case 0x70485973: /* pHYs */ + break; + + case 0x74494d45: /* tIME */ + break; + + case 0x7a455874: /* tEXt */ + break; + + case 0x7a545874: /* zTXt */ + break; + } + } + + /* on stack: mapping n×string */ + + /* IDAT stuff on stack, now */ + f_add(n); + + if (ihdr.type==-1) + { + error("Image.PNG._decode: missing header (IHDR chunk)\n"); + } + if (ihdr.type==3 && !ct) + { + error("Image.PNG._decode: missing palette (PLTE chunk)\n"); + } + + if (ihdr.compression==0) + { + png_decompress(ihdr.compression); + if (sp[-1].type!=T_STRING) + error("Image.PNG._decode: got wierd stuff from decompression\n"); + } + else + error("Image.PNG._decode: illegal compression type 0x%02x\n", + ihdr.compression); + + fs=sp[-1].u.string; + push_int(-1); + mapping_insert(m,sp-2,sp-1); + + pop_n_elems(2); + + s=(unsigned char*)fs->str; + + w1=d1=malloc(sizeof(rgb_group)*ihdr.width*ihdr.height); + if (!d1) + error("Image.PNG._decode: Out of memory\n"); + + wa1=da1=malloc(sizeof(rgb_group)*ihdr.width*ihdr.height); + if (!da1) + { + free(d1); + error("Image.PNG._decode: Out of memory\n"); } /* --- interlace decoding --- */ @@ -940,32 +983,73 @@ static void image_png__decode(INT32 args) switch (ihdr.interlace) { case 0: /* none */ + fs=_png_unfilter((unsigned char*)fs->str,fs->len, + ihdr.width,ihdr.height, + ihdr.filter,ihdr.type,ihdr.bpp, + NULL); + push_string(fs); + if (!_png_write_rgb(w1,wa1, + ihdr.type,ihdr.bpp,fs->str, + fs->len, + ihdr.width, + ihdr.width*ihdr.height, + ct)) + { + free(wa1); + wa1=NULL; + } + pop_stack(); break; case 1: /* adam7 */ + + /* need arena */ t1=malloc(sizeof(rgb_group)*ihdr.width*ihdr.height); if (!t1) { if (wa1) free(wa1); free(w1); error("Image.PNG->_decode: out of memory (close one)\n"); } - d1=w1; - for (y=0;y<ihdr.height;y+=8) for (x=0;x<ihdr.width;x+=8) - t1[x+y*ihdr.width]=*(d1++); - for (y=0;y<ihdr.height;y+=8) for (x=4;x<ihdr.width;x+=8) - t1[x+y*ihdr.width]=*(d1++); - for (y=4;y<ihdr.height;y+=8) for (x=0;x<ihdr.width;x+=4) - t1[x+y*ihdr.width]=*(d1++); - for (y=0;y<ihdr.height;y+=8) for (x=2;x<ihdr.width;x+=4) - t1[x+y*ihdr.width]=*(d1++); - for (y=2;y<ihdr.height;y+=4) for (x=0;x<ihdr.width;x+=2) - t1[x+y*ihdr.width]=*(d1++); - for (y=0;y<ihdr.height;y+=2) for (x=1;x<ihdr.width;x+=2) - t1[x+y*ihdr.width]=*(d1++); - for (y=1;y<ihdr.height;y+=2) for (x=0;x<ihdr.width;x++) - t1[x+y*ihdr.width]=*(d1++); + + /* loop over adam7 interlace's + and write them to the arena */ + + s0=(unsigned char*)fs->str; + for (i=0; i<7; i++) + { + struct pike_string *ds; + + ds=_png_unfilter(s0,fs->len-(s0-(unsigned char*)fs->str), + (ihdr.width+adam7[i].xd-1-adam7[i].x0)/ + adam7[i].xd, + (ihdr.height+adam7[i].yd-1-adam7[i].y0)/ + adam7[i].yd, + ihdr.filter,ihdr.type,ihdr.bpp, + &s0); + + push_string(ds); + if (!_png_write_rgb(w1,wa1,ihdr.type,ihdr.bpp,ds->str,ds->len, + (ihdr.width+adam7[i].xd-1-adam7[i].x0)/ + adam7[i].xd, + (ihdr.width+adam7[i].xd-1-adam7[i].x0)/ + adam7[i].xd* + (ihdr.height+adam7[i].yd-1-adam7[i].y0)/ + adam7[i].yd, + ct)) + { + if (wa1) free(wa1); + wa1=NULL; + } + d1=w1; + for (y=adam7[i].y0;y<ihdr.height;y+=adam7[i].yd) + for (x=adam7[i].x0;x<ihdr.width;x+=adam7[i].xd) + t1[x+y*ihdr.width]=*(d1++); + + pop_stack(); + } free(w1); + if (wa1) free(wa1); w1=t1; break; @@ -973,6 +1057,8 @@ static void image_png__decode(INT32 args) free(w1); if (wa1) free(wa1); error("Image.PNG._decode: Unknown interlace type\n"); } + + /* --- done, store in mapping --- */