From a4e18e1c1ff447fc8be1907daa98ea2abdce8397 Mon Sep 17 00:00:00 2001 From: "Mirar (Pontus Hagland)" <pike@sort.mirar.org> Date: Wed, 1 Apr 1998 07:37:25 +0200 Subject: [PATCH] png support added, finally (a lot is missing, but anyway) Rev: src/modules/Image/encodings/Makefile.in:1.15 Rev: src/modules/Image/encodings/png.c:1.3 Rev: src/modules/Image/image.c:1.94 --- src/modules/Image/encodings/Makefile.in | 4 +- src/modules/Image/encodings/png.c | 1191 ++++++++++++++++++++++- src/modules/Image/image.c | 18 +- 3 files changed, 1160 insertions(+), 53 deletions(-) diff --git a/src/modules/Image/encodings/Makefile.in b/src/modules/Image/encodings/Makefile.in index 347a2b9a5f..c59333403e 100644 --- a/src/modules/Image/encodings/Makefile.in +++ b/src/modules/Image/encodings/Makefile.in @@ -1,7 +1,7 @@ -# $Id: Makefile.in,v 1.14 1998/03/28 14:47:49 grubba Exp $ +# $Id: Makefile.in,v 1.15 1998/04/01 05:37:24 mirar Exp $ SRCDIR=@srcdir@ VPATH=@srcdir@:@srcdir@/../../..:../../.. -OBJS = gif.o gif_lzw.o pnm.o x.o xwd.o +OBJS = gif.o gif_lzw.o pnm.o x.o xwd.o png.o @SET_MAKE@ diff --git a/src/modules/Image/encodings/png.c b/src/modules/Image/encodings/png.c index afdca70d52..a15f51a9db 100644 --- a/src/modules/Image/encodings/png.c +++ b/src/modules/Image/encodings/png.c @@ -1,29 +1,8 @@ -/* $Id: png.c,v 1.2 1998/01/25 08:27:14 hubbe Exp $ */ - -/* -**! module Image -**! note -**! $Id: png.c,v 1.2 1998/01/25 08:27:14 hubbe Exp $ -**! submodule PNG -**! -**! This submodule keep the PNG encode/decode capabilities -**! of the <ref>Image</ref> module. -**! -**! PNG is a rather new lossless image storage format, -**! usable for most images. -**! -**! Simple encoding: -**! <ref>encode</ref> -**! -**! see also: Image, Image.image, Image.colortable -*/ +#include "global.h" +RCSID("$Id: png.c,v 1.3 1998/04/01 05:37:25 mirar Exp $"); -#include <math.h> -#include <ctype.h> +#include "config.h" -#include "stralloc.h" -#include "global.h" -RCSID("$Id: png.c,v 1.2 1998/01/25 08:27:14 hubbe Exp $"); #include "pike_macros.h" #include "object.h" #include "constants.h" @@ -31,34 +10,1166 @@ RCSID("$Id: png.c,v 1.2 1998/01/25 08:27:14 hubbe Exp $"); #include "svalue.h" #include "threads.h" #include "array.h" +#include "mapping.h" #include "error.h" -#include "threads.h" +#include "stralloc.h" +#include "dynamic_buffer.h" + +#include "image.h" +#include "colortable.h" + +extern struct program *image_colortable_program; +extern struct program *image_program; + +static struct program *gz_inflate=NULL; +static struct program *gz_deflate=NULL; +static struct svalue gz_crc32; + +static struct pike_string *param_palette; +static struct pike_string *param_spalette; +static struct pike_string *param_image; +static struct pike_string *param_alpha; +static struct pike_string *param_type; +static struct pike_string *param_bpp; + + +void f_add(INT32 args); +void f_aggregate(INT32 args); + +/* +**! module Image +**! submodule PNG +**! +**! note +**! This module uses <tt>zlib</tt>. +*/ + +static INLINE void push_nbo_32bit(unsigned long x) +{ + char buf[4]; + buf[0]=(char)(x>>24); + buf[1]=(char)(x>>16); + buf[2]=(char)(x>>8); + buf[3]=(char)(x); + push_string(make_shared_binary_string(buf,4)); +} + +static INLINE unsigned long int_from_32bit(unsigned char *data) +{ + return (data[0]<<24)|(data[1]<<16)|(data[2]<<8)|(data[3]); +} + +static INLINE INT32 call_gz_crc32(INT32 args) +{ + INT32 z; + apply_svalue(&gz_crc32,args); + if (sp[-1].type!=T_INT) + error("Image.PNG: internal error (not integer from Gz.crc32)\n"); + z=sp[-1].u.integer; + pop_stack(); + return z; +} + +static INLINE void add_crc_string() +{ + push_svalue(sp-1); + push_nbo_32bit(call_gz_crc32(1)); +} + +static INLINE INT32 my_crc32(INT32 init,unsigned char *data,INT32 len) +{ + push_string(make_shared_binary_string((char*)data,len)); + push_int(init); + return call_gz_crc32(2); +} + +static void push_png_chunk(char *type, /* 4 bytes */ + struct pike_string *data) /* (freed) or on stack */ +{ + /* + * 0: 4 bytes of length of data block (=n) + * 4: 4 bytes of chunk type + * 8: n bytes of data + * 8+n: 4 bytes of CRC + */ + + if (!data) { data=sp[-1].u.string; sp--; } + + push_nbo_32bit(data->len); + push_string(make_shared_binary_string(type,4)); + push_string(data); + f_add(2); + add_crc_string(); + f_add(3); +} + +static void png_decompress(int style) +{ + struct object *o; + + if (style) + error("internal error: illegal decompression style %d\n",style); + + o=clone_object(gz_inflate,0); + apply(o,"inflate",1); + free_object(o); +} + +static void png_compress(int style) +{ + struct object *o; + + if (style) + error("internal error: illegal decompression style %d\n",style); + + push_int(8); + o=clone_object(gz_deflate,1); + apply(o,"deflate",1); + free_object(o); +} + +/* +49 48 44 52 +00 00 01 e0 00 00 01 68 08 06 00 00 00 8f 37 28 20 +-> 00 00 00 04 +*/ +/* +**! method string _chunk(string type,string data) +**! Encodes a PNG chunk. +**! +**! note +**! Please read about the PNG file format. +*/ + +static void image_png__chunk(INT32 args) +{ + struct pike_string *a,*b; + + if (args!=2 || + sp[-args].type!=T_STRING || + sp[1-args].type!=T_STRING) + error("Image.PNG.chunk: Illegal argument(s)\n"); + + a=sp[-args].u.string; + if (a->len!=4) + error("Image.PNG.chunk: Type string not 4 characters\n"); + b=sp[1-args].u.string; + pop_n_elems(args-2); + sp-=2; + push_png_chunk(a->str,b); + free_string(a); +} + + +/* +**! method array __decode(string data) +**! method array __decode(string data, int dontcheckcrc) +**! Splits a PNG file into chunks. +**! +**! Result is an array of arrays, +**! <tt>({ ({ string chunk_type, string data, int crc_ok }), +**! ({ string chunk_type, string data, int crc_ok }) ... })</tt> +**! +**! <tt>chunk_type</tt> is the type of the chunk, like +**! <tt>"IHDR"</tt> or <tt>"IDAT"</tt>. +**! +**! <tt>data</tt> is the actual chunk data. +**! +**! <tt>crcok</tt> is set to 1 if the checksum is ok and +**! <tt>dontcheckcrc</tt> parameter isn't set. +**! +**! Returns 0 if it isn't a PNG file. +**! +**! note +**! Please read about the PNG file format. +*/ -void image_png__module_value(INT32 args) +static void image_png___decode(INT32 args) { + int nocrc=0; + unsigned char *data; + unsigned long len; + struct pike_string *str; + int n=0; + + if (args<1) + error("Image.PNG.__decode: too few arguments\n"); + if (sp[-args].type!=T_STRING) + error("Image.PNG.__decode: illegal argument 1\n"); + + if (args==2 && + sp[1-args].type!=T_INT || + sp[1-args].u.integer!=0) + nocrc=1; + + (str=sp[-args].u.string)->refs++; + data=(unsigned char*)str->str; + len=str->len; + pop_n_elems(args); - push_text("_Image_dot_PNG"); - SAFE_APPLY_MASTER("resolv",1); - if (sp[-1].type==T_INT) - error("Image.PNG: Can't load submodule\n"); + + if (len<8 || + data[0]!=137 || + data[1]!='P' || + data[2]!='N' || + data[3]!='G' || + data[4]!=13 || + data[5]!=10 || + data[6]!=26 || + data[7]!=10) + { + free_string(str); + push_int(0); + return; + } + + len-=8; data+=8; + + while (len>8) + { + unsigned long x; + x=int_from_32bit(data); + push_string(make_shared_binary_string((char*)data+4,4)); + len-=8; + data+=8; + if (x>len) + { + push_string(make_shared_binary_string((char*)data,len)); + push_int(0); + f_aggregate(3); + n++; + break; + } + push_string(make_shared_binary_string((char*)data,x)); + if (!nocrc && x+4<=len) + push_int( my_crc32(my_crc32(0,NULL,0),data-4,x+4) == + int_from_32bit(data+x) ); + else + push_int(0); + if (x+4>len) break; + f_aggregate(3); + n++; + len-=x+4; + data+=x+4; + } + + free_string(str); + f_aggregate(n); } -void init_image_png(void) +/* +**! method array _decode(string|array data) +**! Decode a PNG image file. +**! +**! Result is a mapping, +**! <pre> +**! ([ +**! "image": object image, +**! +**! ... options ... +**! ]) +**! </pre> +**! +**! <tt>image</tt> is the stored image. +**! +**! Valid entries in <tt>options</tt> is the same +**! as given to <ref>encode</ref>: +**! +**! <pre> +**! basic options: +**! +**! "alpha": object alpha, - alpha channel +**! +**! "palette": object colortable, - image palette +**! (if non-truecolor) +**! +**! advanced options: +**! +**! "background": array(int) color, - suggested background color +**! "background_index": int index, - what index in colortable +**! +**! "chroma": ({ float white_point_x, +**! float white_point_y, +**! float red_x, +**! float red_y, - CIE x,y chromaticities +**! float green_x, +**! float green_y, +**! float blue_x, +**! float blue_y }) +**! +**! "gamma": float gamma, - gamma +**! +**! "spalette": object colortable, - suggested palette, +**! for truecolor images +**! "histogram": array(int) hist, - histogram for the image, +**! corresponds to palette index +**! +**! "physical": ({ int unit, - physical pixel dimension +**! int x,y }) unit 0 means pixels/meter +**! +**! "sbit": array(int) sbits - significant bits +**! +**! "text": array(array(string)) text - text information, +**! ({ ({ keyword, data }), ... }) +**! +**! Standard keywords: +**! +**! Title Short (one line) title or caption for image +**! Author Name of image's creator +**! Description Description of image (possibly long) +**! Copyright Copyright notice +**! Creation Time Time of original image creation +**! Software Software used to create the image +**! Disclaimer Legal disclaimer +**! Warning Warning of nature of content +**! Source Device used to create the image +**! Comment Miscellaneous comment +**! +**! "time": ({ int year, month, day, - time of last modification +**! hour, minute, second }) +**! +**! wizard options: +**! "compression": int method - compression method (0) +**! +**! </pre> +**! +**! note +**! Please read about the PNG file format. +**! This function ignores any checksum errors in the file. +**! A PNG of higher color resolution then the Image module +**! supports (8 bit) will have a lose that information in +**! the conversion. +**! It throws if the image data is erranous. +*/ + +static struct pike_string *_png_unfilter(unsigned char *data, + INT32 len,int xsize, + int filter,int type, + int bpp,int interlace) { - struct pike_string *s; - struct program *p; - start_new_program(); + struct pike_string *ps; + unsigned char *d; + unsigned char *s; + int x; + int sbb; + + switch (type) + { + case 2: x=3; break; + case 4: x=2; break; + case 6: x=4; break; + default: x=1; + } + + bpp*=x; /* multipy for units/pixel (rgba=4, grey=1, etc) */ + xsize=(xsize*bpp+7)>>3; /* xsize in bytes */ + + ps=begin_shared_string(len-((len+xsize)/(xsize+1))); + d=(unsigned char*)ps->str; + s=data; + + sbb=(bpp+7)>>3; /* rounding up */ + + for (;;) + { + if (!len) return end_shared_string(ps); + x=xsize; + + switch (*(s++)) + { + case 0: /* no filter */ + while (x-- && --len) + *(d++)=*(s++); + if (len) len--; + break; + case 1: /* sub left */ + while (x-- && --len) + if (x+sbb<xsize) + *d=s[0]+d[-sbb],d++,s++; + else + *(d++)=*(s++); + if (len) len--; + break; + case 2: /* sub up */ + if (d - (unsigned char*)ps->str >= xsize) + while (x-- && --len) + *d=s[0]+d[-xsize],d++,s++; + else + while (x-- && --len) + *(d++)=*(s++); + if (len) len--; + break; + case 3: /* average */ + while (x-- && --len) + { + int a,b; + + if (x+sbb<xsize) a=d[-sbb]; else a=0; + if (d - (unsigned char*)ps->str >= xsize) b=d[-xsize]; else b=0; + a=(a+b)>>1; + + *d=*s+a; + + d++; + s++; + } + if (len) len--; + + break; + case 4: /* paeth */ + while (x-- && --len) + { + int a,b,c,p,pa,pb,pc; + + if (x+sbb<xsize) + { + a=d[-sbb]; + if (d - (unsigned char*)ps->str >= xsize) + { + b=d[-xsize]; + c=d[-xsize-sbb]; + } + else b=c=0; + + p=a+b-c; + pa=abs(p-a); + pb=abs(p-b); + pc=abs(p-c); + if (pa<=pb && pa<=pc) p=a; + else if (pb<=pc) p=b; + else p=c; + + *d=(unsigned char)(p+*s); + } + else if (d - (unsigned char*)ps->str >= xsize) + *d=(unsigned char)(d[-xsize]+*s); /* de facto */ + else *d=*s; + + d++; + s++; + } + if (len) len--; + + break; + default: + error("Image.PNG._decode: unsupported filter %d\n",s[-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; + struct image *img; + + 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"); + } - add_function("_module_value",image_png__module_value, - "function(:object)",0); + 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); + + /* 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"); + } - push_object(clone_object(p=end_program(),0)); - free_program(p); - add_constant(s=make_shared_string("PNG"),sp-1,0); - free_string(s); + /* write stuff to d1 */ + n=ihdr.width*ihdr.height; + switch (ihdr.type) + { + case 0: /* 1,2,4,8 or 16 bit greyscale */ + switch (ihdr.bpp) + { + case 1: + if (n>fs->len*8) n=fs->len*8; + x=ihdr.width; + while (n) + { + if (x) x--,*(d1++)=((*s)&128)?white:black; + if (x) x--,*(d1++)=((*s)&64)?white:black; + if (x) x--,*(d1++)=((*s)&32)?white:black; + if (x) x--,*(d1++)=((*s)&16)?white:black; + if (x) x--,*(d1++)=((*s)&8)?white:black; + if (x) x--,*(d1++)=((*s)&4)?white:black; + if (x) x--,*(d1++)=((*s)&2)?white:black; + if (x) x--,*(d1++)=((*s)&1)?white:black; + if (n<8) break; + n-=8; + s++; + if (!x) x=ihdr.width; + } + break; + case 2: + if (n>fs->len*4) n=fs->len*4; + x=ihdr.width; + while (n) + { + if (x) x--,*(d1++)=grey4[((*s)>>6)&3]; + if (x) x--,*(d1++)=grey4[((*s)>>4)&3]; + if (x) x--,*(d1++)=grey4[((*s)>>2)&3]; + if (x) x--,*(d1++)=grey4[(*s)&3]; + if (n<4) break; + n-=4; + s++; + if (!x) x=ihdr.width; + } + break; + case 4: + if (n>fs->len*2) n=fs->len*2; + x=ihdr.width; + while (n) + { + int q; + if (x) + { + x--,q=(((*s)>>4)&15)|((*s)&240); + d1->r=d1->g=d1->b=q; d1++; + } + if (x) + { + x--,q=((*s)&15)|((*s)<<4); + d1->r=d1->g=d1->b=q; d1++; + } + if (n<2) break; + n-=2; + s++; + if (!x) x=ihdr.width; + } + break; + case 8: + if (n>fs->len) n=fs->len; + while (n) + { + d1->r=d1->g=d1->b=*(s++); d1++; + n--; + } + break; + case 16: + if (n>fs->len/2) n=fs->len/2; + while (n) + { + d1->r=d1->g=d1->b=*(s++); d1++; s++; + n--; + } + break; + default: + free(wa1); free(w1); + error("Image.PNG->_decode: Unsupported color type/bit depth %d (grey)/%d bit.\n", + ihdr.type,ihdr.bpp); + } + free(wa1); /* no alpha channel */ + wa1=NULL; + break; + + case 2: /* 8 or 16 bit r,g,b */ + switch (ihdr.bpp) + { + case 8: + if (n>fs->len/3) n=fs->len/3; + while (n) + { + d1->r=*(s++); + d1->g=*(s++); + d1->b=*(s++); + d1++; + n--; + } + break; + case 16: + if (n>fs->len/6) n=fs->len/6; + while (n) + { + d1->r=*(s++); + d1->g=*(s++); + d1->b=*(s++); + d1++; + s++; + n--; + } + break; + default: + free(wa1); free(w1); + error("Image.PNG->_decode: Unsupported color type/bit depth %d (rgb)/%d bit.\n", + ihdr.type,ihdr.bpp); + } + free(wa1); /* no alpha channel */ + wa1=NULL; + break; + + case 3: /* 1,2,4,8 bit palette index */ + if (!ct) + error("Image.PNG->decode: No palette (PLTE entry), but color type (3) needs one\n"); + if (ct->type!=NCT_FLAT) + 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) + { + case 1: + if (n>fs->len*8) n=fs->len*8; + x=ihdr.width; + while (n) + { + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>7)&1,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>6)&1,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>5)&1,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>4)&1,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>3)&1,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>2)&1,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>1)&1,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE((*s)&1,mz)].color; + s++; + if (n<8) break; + n-=8; + if (!x) x=ihdr.width; + } + break; + case 2: + if (n>fs->len*4) n=fs->len*4; + x=ihdr.width; + while (n) + { + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>6)&3,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>4)&3,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>2)&3,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE((*s)&3,mz)].color; + s++; + if (n<4) break; + n-=4; + if (!x) x=ihdr.width; + } + break; + case 4: + if (n>fs->len*2) n=fs->len*2; + x=ihdr.width; + while (n) + { + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE(((*s)>>4)&15,mz)].color; + if (x) x--,*(d1++)=ct->u.flat.entries[CUTPLTE((*s)&15,mz)].color; + s++; + if (n<2) break; + n--; + if (!x) x=ihdr.width; + } + break; + case 8: + if (n>fs->len) n=fs->len; + while (n) + { + *(d1++)=ct->u.flat.entries[CUTPLTE(*s,mz)].color; + s++; + n--; + } + break; + + default: + error("Image.PNG->_decode: Unsupported color type/bit depth %d (palette)/%d bit.\n", + ihdr.type,ihdr.bpp); + } + free(wa1); /* no alpha channel */ + wa1=NULL; + break; + + case 4: /* 8 or 16 bit grey,a */ + switch (ihdr.bpp) + { + case 8: + if (n>fs->len/3) n=fs->len/3; + while (n) + { + d1->r=d1->g=d1->b=*(s++); + da1->r=da1->g=da1->b=*(s++); + d1++; + da1++; + n--; + } + break; + case 16: + if (n>fs->len/6) n=fs->len/6; + while (n) + { + d1->r=d1->g=d1->b=*(s++); + d1++; + s++; + da1->r=da1->g=da1->b=*(s++); + s++; + da1++; + n--; + } + break; + default: + free(wa1); free(w1); + error("Image.PNG->_decode: Unsupported color type/bit depth %d (grey+a)/%d bit.\n", + ihdr.type,ihdr.bpp); + } + break; + + case 6: /* 8 or 16 bit r,g,b,a */ + switch (ihdr.bpp) + { + case 8: + if (n>fs->len/3) n=fs->len/3; + while (n) + { + d1->r=*(s++); + d1->g=*(s++); + d1->b=*(s++); + da1->r=da1->g=da1->b=*(s++); + d1++; + da1++; + n--; + } + break; + case 16: + if (n>fs->len/6) n=fs->len/6; + while (n) + { + d1->r=*(s++); + d1->g=*(s++); + d1->b=*(s++); + d1++; + s++; + da1->r=da1->g=da1->b=*(s++); + s++; + da1++; + n--; + } + break; + default: + free(wa1); free(w1); + error("Image.PNG->_decode: Unsupported color type/bit depth %d(rgba)/%d bit.\n", + ihdr.type,ihdr.bpp); + } + break; + default: + free(wa1); free(w1); + error("Image.PNG->_decode: Unknown color type %d (bit depth %d).\n", + ihdr.type,ihdr.bpp); + } + + /* --- interlace decoding --- */ + + switch (ihdr.interlace) + { + case 0: /* none */ + break; + + case 1: /* adam7 */ + 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++); + + free(w1); + w1=t1; + + break; + default: + free(w1); if (wa1) free(wa1); + error("Image.PNG._decode: Unknown interlace type\n"); + } + + + /* --- done, store in mapping --- */ + + push_string(param_image); + param_image->refs++; + push_object(clone_object(image_program,0)); + img=(struct image*)get_storage(sp[-1].u.object,image_program); + if (img->img) free(img->img); /* protect from memleak */ + img->xsize=ihdr.width; + img->ysize=ihdr.height; + img->img=w1; + mapping_insert(m,sp-2,sp-1); + pop_n_elems(2); + + if (wa1) + { + push_string(param_alpha); + param_image->refs++; + push_object(clone_object(image_program,0)); + img=(struct image*)get_storage(sp[-1].u.object,image_program); + if (img->img) free(img->img); /* protect from memleak */ + img->xsize=ihdr.width; + img->ysize=ihdr.height; + img->img=wa1; + mapping_insert(m,sp-2,sp-1); + pop_n_elems(2); + } + + push_string(param_type); param_type->refs++; + push_int(ihdr.type); + mapping_insert(m,sp-2,sp-1); + pop_n_elems(2); + push_string(param_bpp); param_bpp->refs++; + push_int(ihdr.bpp); + mapping_insert(m,sp-2,sp-1); + pop_n_elems(2); + + push_int(-1); + map_delete(m,sp-1); pop_stack(); } + +/* +**! method string encode(object image) +**! method string encode(object image, mapping options) +**! Encodes a PNG image. +**! +**! The <tt>options</tt> argument may be a mapping +**! containing zero or more encoding options: +**! +**! <pre> +**! normal options: +**! "quality":0..100 +**! Set quality of result. Default is 75. +**! "optimize":0|1 +**! Optimize Huffman table. Default is on (1) for +**! images smaller than 50kpixels. +**! "progressive":0|1 +**! Make a progressive JPEG. Default is off. +**! +**! advanced options: +**! "smooth":1..100 +**! Smooth input. Value is strength. +**! "method":JPEG.IFAST|JPEG.ISLOW|JPEG.FLOAT|JPEG.DEFAULT|JPEG.FASTEST +**! DCT method to use. +**! DEFAULT and FASTEST is from the jpeg library, +**! probably ISLOW and IFAST respective. +**! +**! wizard options: +**! "baseline":0|1 +**! Force baseline output. Useful for quality<20. +**! </pre> +**! +**! note +**! Please read some about JPEG files. A quality +**! setting of 100 does not mean the result is +**! lossless. +*/ + +static void image_png_encode(INT32 args) +{ +} + +/* +**! method object decode(string data) +**! method object decode(string data, mapping options) +**! Decodes a PNG image. +**! +**! The <tt>options</tt> argument may be a mapping +**! containing zero or more encoding options: +**! +**! <pre> +**! </pre> +**! +**! note +**! Throws upon error in data. +*/ + +void f_index(INT32 args); + +static void image_png_decode(INT32 args) +{ + if (!args) + error("Image.PNG.decode: missing argument(s)\n"); + + image_png__decode(args); + push_string(make_shared_string("image")); + f_index(2); +} + +/*** module init & exit & stuff *****************************************/ + void exit_image_png(void) { + free_string(param_palette); + free_string(param_spalette); + free_string(param_image); + free_string(param_alpha); + free_string(param_bpp); + free_string(param_type); +} + +void init_image_png(void) +{ + start_new_program(); + + push_string(make_shared_string("Gz")); + push_int(0); + SAFE_APPLY_MASTER("resolv",2); + if (sp[-1].type==T_OBJECT) + { + push_string(make_shared_string("deflate")); + f_index(2); + gz_deflate=program_from_svalue(sp-1); + } + pop_n_elems(1); + + push_string(make_shared_string("Gz")); + push_int(0); + SAFE_APPLY_MASTER("resolv",2); + if (sp[-1].type==T_OBJECT) + { + push_string(make_shared_string("inflate")); + f_index(2); + gz_inflate=program_from_svalue(sp-1); + } + pop_n_elems(1); + + push_string(make_shared_string("Gz")); + push_int(0); + SAFE_APPLY_MASTER("resolv",2); + if (sp[-1].type==T_OBJECT) + { + push_string(make_shared_string("crc32")); + f_index(2); + gz_crc32=sp[-1]; + sp--; + } + else gz_crc32.type=T_INT; + pop_n_elems(1); + + if (gz_deflate && gz_inflate && + gz_crc32.type!=T_INT) + { + add_function("_chunk",image_png__chunk, + "function(string,string:string)", + OPT_TRY_OPTIMIZE); + add_function("__decode",image_png___decode, + "function(string:array)", + OPT_TRY_OPTIMIZE); + + if (gz_deflate) + { + add_function("_decode",image_png__decode, + "function(array|string,void|mapping(string:int):object)",0); + add_function("decode",image_png_decode, + "function(string,void|mapping(string:int):object)",0); + } + add_function("encode",image_png_encode, + "function(object,void|mapping(string:int):string)", + OPT_TRY_OPTIMIZE); + } + + param_palette=make_shared_string("palette"); + param_spalette=make_shared_string("spalette"); + param_image=make_shared_string("image"); + param_alpha=make_shared_string("alpha"); + param_bpp=make_shared_string("bpp"); + param_type=make_shared_string("type"); + + push_object(clone_object(end_program(),0)); + { + struct pike_string *s=make_shared_string("PNG"); + add_constant(s,sp-1,0); + free_string(s); + } + pop_stack(); } diff --git a/src/modules/Image/image.c b/src/modules/Image/image.c index 283bd6fa2f..f500652ef0 100644 --- a/src/modules/Image/image.c +++ b/src/modules/Image/image.c @@ -1,9 +1,9 @@ -/* $Id: image.c,v 1.93 1998/03/25 22:26:28 hedda Exp $ */ +/* $Id: image.c,v 1.94 1998/04/01 05:37:21 mirar Exp $ */ /* **! module Image **! note -**! $Id: image.c,v 1.93 1998/03/25 22:26:28 hedda Exp $ +**! $Id: image.c,v 1.94 1998/04/01 05:37:21 mirar Exp $ **! class image **! **! The main object of the <ref>Image</ref> module, this object @@ -82,7 +82,7 @@ #include "stralloc.h" #include "global.h" -RCSID("$Id: image.c,v 1.93 1998/03/25 22:26:28 hedda Exp $"); +RCSID("$Id: image.c,v 1.94 1998/04/01 05:37:21 mirar Exp $"); #include "pike_macros.h" #include "object.h" #include "constants.h" @@ -3130,6 +3130,8 @@ extern void init_image_pnm(void); extern void exit_image_pnm(void); extern void init_image_xwd(void); extern void exit_image_xwd(void); +extern void init_image_png(void); +extern void exit_image_png(void); extern void init_image_x(void); extern void exit_image_x(void); @@ -3161,14 +3163,6 @@ static void image_index_magic(INT32 args) SAFE_APPLY_MASTER("resolv",2); return; } - else if (sp[-1].u.string==magic_PNG) - { - pop_stack(); - push_string(make_shared_string("_Image_PNG")); - push_int(0); - SAFE_APPLY_MASTER("resolv",2); - return; - } push_object(THISOBJ); THISOBJ->refs++; tmp=sp[-1], sp[-1]=sp[-2], sp[-2]=tmp; f_arrow(2); @@ -3410,6 +3404,7 @@ void pike_module_init(void) init_image_gif(); init_image_pnm(); init_image_xwd(); + init_image_png(); init_image_x(); } @@ -3426,6 +3421,7 @@ void pike_module_exit(void) exit_image_gif(); exit_image_pnm(); exit_image_xwd(); + exit_image_png(); exit_image_x(); free_string(magic_PNG); -- GitLab