static int png_read_header(FILE*fi, struct png_header*header) { char id[4]; int len; int ok=0; unsigned char head[8] = {137,80,78,71,13,10,26,10}; unsigned char head2[8]; unsigned char*data; fread(head2,8,1,fi); if(strncmp((const char*)head,(const char*)head2,4)) return 0; // not a png file while(png_read_chunk(&id, &len, &data, fi)) { //printf("Chunk: %c%c%c%c (len:%d)\n", id[0],id[1],id[2],id[3], len); if(!strncmp(id, "IHDR", 4)) { char a,b,c,f,i; if(len < 8) exit(1); header->width = data[0]<<24|data[1]<<16|data[2]<<8|data[3]; header->height = data[4]<<24|data[5]<<16|data[6]<<8|data[7]; a = data[8]; // should be 8 b = data[9]; // should be 3(indexed) or 2(rgb) c = data[10]; // compression mode (0) f = data[11]; // filter mode (0) i = data[12]; // interlace mode (0) if(b!=0 && b!=4 && b!=2 && b!=3 && b!=6) { fprintf(stderr, "Image mode %d not supported!\n", b); return 0; } if(a!=8 && (b==2 || b==6)) { printf("Bpp %d in mode %d not supported!\n", a); return 0; } if(c!=0) { printf("Compression mode %d not supported!\n", c); return 0; } if(f!=0) { printf("Filter mode %d not supported!\n", f); return 0; } if(i!=0) { printf("Interlace mode %d not supported!\n", i); return 0; } //printf("%dx%d bpp:%d mode:%d comp:%d filter:%d interlace:%d\n",header->width, header->height, a,b,c,f,i); header->bpp = a; header->mode = b; ok = 1; } free(data); } return ok; }
static bool png_parse_ihdr_fio(FILE **fd, struct png_chunk *chunk, struct png_ihdr *ihdr) { if (!png_read_chunk(fd, chunk)) return false; if (chunk->size != 13) return false; ihdr->width = dword_be(chunk->data + 0); ihdr->height = dword_be(chunk->data + 4); ihdr->depth = chunk->data[8]; ihdr->color_type = chunk->data[9]; ihdr->compression = chunk->data[10]; ihdr->filter = chunk->data[11]; ihdr->interlace = chunk->data[12]; if (ihdr->width == 0 || ihdr->height == 0) return false; return true; }
static bool png_parse_ihdr(FILE *file, struct png_chunk *chunk, struct png_ihdr *ihdr) { unsigned i; bool ret = true; if (!png_read_chunk(file, chunk)) return false; if (chunk->size != 13) GOTO_END_ERROR(); ihdr->width = dword_be(chunk->data + 0); ihdr->height = dword_be(chunk->data + 4); ihdr->depth = chunk->data[8]; ihdr->color_type = chunk->data[9]; ihdr->compression = chunk->data[10]; ihdr->filter = chunk->data[11]; ihdr->interlace = chunk->data[12]; if (ihdr->width == 0 || ihdr->height == 0) GOTO_END_ERROR(); if (ihdr->color_type == 2 || ihdr->color_type == 4 || ihdr->color_type == 6) { if (ihdr->depth != 8 && ihdr->depth != 16) GOTO_END_ERROR(); } else if (ihdr->color_type == 0) { static const unsigned valid_bpp[] = { 1, 2, 4, 8, 16 }; bool correct_bpp = false; for (i = 0; i < ARRAY_SIZE(valid_bpp); i++) { if (valid_bpp[i] == ihdr->depth) { correct_bpp = true; break; } } if (!correct_bpp) GOTO_END_ERROR(); } else if (ihdr->color_type == 3) { static const unsigned valid_bpp[] = { 1, 2, 4, 8 }; bool correct_bpp = false; for (i = 0; i < ARRAY_SIZE(valid_bpp); i++) { if (valid_bpp[i] == ihdr->depth) { correct_bpp = true; break; } } if (!correct_bpp) GOTO_END_ERROR(); } else GOTO_END_ERROR(); #ifdef RPNG_TEST fprintf(stderr, "IHDR: (%u x %u), bpc = %u, palette = %s, color = %s, alpha = %s, adam7 = %s.\n", ihdr->width, ihdr->height, ihdr->depth, ihdr->color_type == 3 ? "yes" : "no", ihdr->color_type & 2 ? "yes" : "no", ihdr->color_type & 4 ? "yes" : "no", ihdr->interlace == 1 ? "yes" : "no"); #endif if (ihdr->compression != 0) GOTO_END_ERROR(); //if (ihdr->interlace != 0) // No Adam7 supported. // GOTO_END_ERROR(); end: png_free_chunk(chunk); return ret; }
EXPORT int getPNG(const char*sname, int*destwidth, int*destheight, unsigned char**destdata) { char tagid[4]; int len; unsigned char*data; unsigned char*imagedata; unsigned char*zimagedata=0; unsigned long int imagedatalen; unsigned long int zimagedatalen=0; unsigned char*palette = 0; int palettelen = 0; unsigned char*alphapalette = 0; int alphapalettelen = 0; struct png_header header; int bypp; unsigned char*data2 = 0; unsigned char alphacolor[3]; int hasalphacolor=0; FILE *fi; unsigned char *scanline; if ((fi = fopen(sname, "rb")) == NULL) { printf("Couldn't open %s\n", sname); return 0; } if(!png_read_header(fi, &header)) { fclose(fi); return 0; } if(header.mode == 3 || header.mode == 0) bypp = 1; else if(header.mode == 4) bypp = 2; else if(header.mode == 2) bypp = 3; else if(header.mode == 6) bypp = 4; else { printf("ERROR: mode:%d\n", header.mode); return 0; } imagedatalen = bypp * header.width * header.height + 65536; imagedata = (unsigned char*)malloc(imagedatalen); fseek(fi,8,SEEK_SET); while(!feof(fi)) { if(!png_read_chunk(&tagid, &len, &data, fi)) break; if(!strncmp(tagid, "IEND", 4)) { break; } if(!strncmp(tagid, "PLTE", 4)) { palette = data; palettelen = len/3; data = 0; //don't free data //printf("%d colors in palette\n", palettelen); } if(!strncmp(tagid, "tRNS", 4)) { if(header.mode == 3) { alphapalette = data; alphapalettelen = len; data = 0; //don't free data //printf("found %d alpha colors\n", alphapalettelen); } else if(header.mode == 0 || header.mode == 2) { int t; if(header.mode == 2) { alphacolor[0] = data[1]; alphacolor[1] = data[3]; alphacolor[2] = data[5]; } else { alphacolor[0] = alphacolor[1] = alphacolor[2] = data[1]; } hasalphacolor = 1; } } if(!strncmp(tagid, "IDAT", 4)) { if(!zimagedata) { zimagedatalen = len; zimagedata = (unsigned char*)malloc(len); memcpy(zimagedata,data,len); } else { zimagedata = (unsigned char*)realloc(zimagedata, zimagedatalen+len); memcpy(&zimagedata[zimagedatalen], data, len); zimagedatalen += len; } } if(!strncmp(tagid, "tEXt", 4)) { /*int t; printf("Image Text: "); for(t=0;t<len;t++) { if(data[t]>=32 && data[t]<128) printf("%c", data[t]); else printf("?"); } printf("\n");*/ } if(data) { free(data); data=0; } } if(!zimagedata || uncompress(imagedata, &imagedatalen, zimagedata, zimagedatalen) != Z_OK) { printf("Couldn't uncompress %s!\n", sname); if(zimagedata) free(zimagedata); return 0; } free(zimagedata); fclose(fi); *destwidth = header.width; *destheight = header.height; data2 = (unsigned char*)malloc(header.width*header.height*4); if(header.mode == 4) { int i,s=0; int x,y; int pos=0; unsigned char* old= (unsigned char*)malloc(header.width*2); memset(old, 0, header.width*2); *destdata = data2; for(y=0; y<header.height; y++) { int mode = imagedata[pos++]; //filter mode unsigned char*src; unsigned char*dest; int x; dest = &data2[(y*header.width)*4]; if(header.bpp == 8) { /* one byte per pixel */ src = &imagedata[pos]; pos+=header.width*2; } else { /* not implemented yet */ fprintf(stderr, "ERROR: mode=4 bpp:%d\n", header.bpp); free(data2); return 0; } applyfilter2(mode, src, old, dest, header.width); memcpy(old, dest, header.width*2); for(x=header.width-1; x>=0; x--) { unsigned char gray = dest[x*2+0]; unsigned char alpha = dest[x*2+1]; dest[x*4+0] = alpha; dest[x*4+1] = gray; dest[x*4+2] = gray; dest[x*4+3] = gray; } } free(old); free(imagedata); } else if(header.mode == 6 || header.mode == 2) { int i,s=0; int x,y; int pos=0; *destdata = data2; unsigned char* firstline = malloc(header.width*4); memset(firstline,0,header.width*4); for(y=0; y<header.height; y++) { int mode = imagedata[pos++]; //filter mode unsigned char*src; unsigned char*dest; unsigned char*old; dest = &data2[(y*header.width)*4]; if(header.bpp == 8) { /* one byte per pixel */ src = &imagedata[pos]; pos+=header.width*(header.mode==6?4:3); } else { /* not implemented yet */ fprintf(stderr, "ERROR: bpp:%d\n", header.bpp); free(data2); return 0; } if(!y) { old = firstline; } else { old = &data2[(y-1)*header.width*4]; } if(header.mode == 6) { applyfilter4(mode, src, old, dest, header.width); } else { // header.mode = 2 applyfilter3(mode, src, old, dest, header.width); /* replace alpha color */ if(hasalphacolor) { int x; for(x=0; x<header.width; x++) { if(dest[x*4+1] == alphacolor[0] && dest[x*4+2] == alphacolor[1] && dest[x*4+3] == alphacolor[2]) { *(u32*)&dest[x*4] = 0; } } } } } free(firstline); free(imagedata); } else if(header.mode == 0 || header.mode == 3) { COL*rgba = 0; unsigned char*tmpline = (unsigned char*)malloc(header.width+1); unsigned char*destline = (unsigned char*)malloc(header.width+1); int i,x,y; int pos=0; *destdata = data2; if(header.mode == 0) { // grayscale palette int mult = (0x1ff>>header.bpp); palettelen = 1<<header.bpp; rgba = (COL*)malloc(palettelen*sizeof(COL)); for(i=0; i<palettelen; i++) { rgba[i].a = 255; rgba[i].r = i*mult; rgba[i].g = i*mult; rgba[i].b = i*mult; if(hasalphacolor) { if(rgba[i].r == alphacolor[0]) rgba[i].a = 0; } } } else {