/* based on APNG Disassembler 2.3 * * Copyright (c) 2009 Max Stepin * maxst at users.sourceforge.net * * GNU LGPL information * -------------------- * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ #include #include #include #include #include "apngloader.h" // hack to create a portable fmemopen FILE *fmemopen (void *buf, size_t size, const char *opentype) { FILE *f; f = tmpfile(); fwrite(buf, 1, size, f); rewind(f); return f; } #if defined(_MSC_VER) && _MSC_VER >= 1300 #define swap16(data) _byteswap_ushort(data) #define swap32(data) _byteswap_ulong(data) #elif __linux__ #include #define swap16(data) bswap_16(data) #define swap32(data) bswap_32(data) #else #define swap16(data) (((data >> 8) & 255) | ((data & 255) << 8)) #define swap32(data) ((swap16(data) << 16) | swap16(data >> 16)) #endif #define PNG_ZBUF_SIZE 32768 #define PNG_DISPOSE_OP_NONE 0x00 #define PNG_DISPOSE_OP_BACKGROUND 0x01 #define PNG_DISPOSE_OP_PREVIOUS 0x02 #define PNG_BLEND_OP_SOURCE 0x00 #define PNG_BLEND_OP_OVER 0x01 #define notabc(c) ((c) < 65 || (c) > 122 || ((c) > 90 && (c) < 97)) #define ROWBYTES(pixel_bits, width) \ ((pixel_bits) >= 8 ? \ ((width) * (((unsigned int)(pixel_bits)) >> 3)) : \ (( ((width) * ((unsigned int)(pixel_bits))) + 7) >> 3) ) unsigned char png_sign[8] = {137, 80, 78, 71, 13, 10, 26, 10}; int mask4[2]={240,15}; int shift4[2]={4,0}; int mask2[4]={192,48,12,3}; int shift2[4]={6,4,2,0}; int mask1[8]={128,64,32,16,8,4,2,1}; int shift1[8]={7,6,5,4,3,2,1,0}; unsigned int keep_original = 1; unsigned char pal[256][3]; unsigned char trns[256]; unsigned int palsize, trnssize; unsigned int hasTRNS; unsigned short trns1, trns2, trns3; unsigned int read32(FILE * f1) { unsigned char a, b, c, d; fread(&a, 1, 1, f1); fread(&b, 1, 1, f1); fread(&c, 1, 1, f1); fread(&d, 1, 1, f1); return ((unsigned int)a<<24)+((unsigned int)b<<16)+((unsigned int)c<<8)+(unsigned int)d; } unsigned short read16(FILE * f1) { unsigned char a, b; fread(&a, 1, 1, f1); fread(&b, 1, 1, f1); return ((unsigned short)a<<8)+(unsigned short)b; } unsigned short readshort(unsigned char * p) { return ((unsigned short)(*p)<<8)+(unsigned short)(*(p+1)); } void read_sub_row(unsigned char * row, unsigned int rowbytes, unsigned int bpp) { unsigned int i; for (i=bpp; i>1; for (i=bpp; i>1; } else { for (i=bpp; i>1; } } void read_paeth_row(unsigned char * row, unsigned char * prev_row, unsigned int rowbytes, unsigned int bpp) { unsigned int i; int a, b, c, pa, pb, pc, p; if (prev_row) { for (i=0; i>1] & mask4[i&1]) >> shift4[i&1]; a = 0xFF; if (hasTRNS && g==trns1) a = 0; *dp1++ = g*0x11; *dp2++ = (a<<24) + g*0x111111; } break; case 2: for (i=0; i>2] & mask2[i&3]) >> shift2[i&3]; a = 0xFF; if (hasTRNS && g==trns1) a = 0; *dp1++ = g*0x55; *dp2++ = (a<<24) + g*0x555555; } break; case 1: for (i=0; i>3] & mask1[i&7]) >> shift1[i&7]; a = 0xFF; if (hasTRNS && g==trns1) a = 0; *dp1++ = g*0xFF; *dp2++ = (a<<24) + g*0xFFFFFF; } break; } } else /* PNG_BLEND_OP_OVER */ { switch (depth) { case 16: for (i=0; i>1] & mask4[i&1]) >> shift4[i&1]; if (g != trns1) { *dp1 = g*0x11; *dp2 = 0xFF000000+g*0x111111; } } break; case 2: for (i=0; i>2] & mask2[i&3]) >> shift2[i&3]; if (g != trns1) { *dp1 = g*0x55; *dp2 = 0xFF000000+g*0x555555; } } break; case 1: for (i=0; i>3] & mask1[i&7]) >> shift1[i&7]; if (g != trns1) { *dp1 = g*0xFF; *dp2 = 0xFF000000+g*0xFFFFFF; } } break; } } src += srcbytes; dst1 += dstbytes1; dst2 += dstbytes2; } } void compose2(unsigned char * dst1, unsigned int dstbytes1, unsigned char * dst2, unsigned int dstbytes2, unsigned char * src, unsigned int srcbytes, unsigned int w, unsigned int h, unsigned int bop, unsigned char depth) { unsigned int i, j; unsigned int r, g, b, a; unsigned char * sp; unsigned char * dp1; unsigned int * dp2; for (j=0; j>1] & mask4[i&1]) >> shift4[i&1]; break; case 2: col = (sp[i>>2] & mask2[i&3]) >> shift2[i&3]; break; case 1: col = (sp[i>>3] & mask1[i&7]) >> shift1[i&7]; break; } b = pal[col][0]; g = pal[col][1]; r = pal[col][2]; a = trns[col]; if (bop == PNG_BLEND_OP_SOURCE) { *dp1++ = col; *dp2++ = (a << 24) + (r << 16) + (g << 8) + b; } else /* PNG_BLEND_OP_OVER */ { if (a == 255) { *dp1++ = col; *dp2++ = (a << 24) + (r << 16) + (g << 8) + b; } else if (a != 0) { if ((a2 = (*dp2)>>24)) { keep_original = 0; u = a*255; v = (255-a)*a2; al = 255*255-(255-a)*(255-a2); b2 = ((*dp2)&255); g2 = (((*dp2)>>8)&255); r2 = (((*dp2)>>16)&255); b = (b*u + b2*v)/al; g = (g*u + g2*v)/al; r = (r*u + r2*v)/al; a = al/255; } *dp1++ = col; *dp2++ = (a << 24) + (r << 16) + (g << 8) + b; } else { dp1++; dp2++; } } } src += srcbytes; dst1 += dstbytes1; dst2 += dstbytes2; } } void compose4(unsigned char * dst, unsigned int dstbytes, unsigned char * src, unsigned int srcbytes, unsigned int w, unsigned int h, unsigned int bop, unsigned char depth) { unsigned int i, j, step; unsigned int g, a, g2, a2; int u, v, al; unsigned char * sp; unsigned char * dp; step = (depth+7)/8; for (j=0; j>24)) { u = a*255; v = (255-a)*a2; al = 255*255-(255-a)*(255-a2); b2 = ((*dp)&255); g2 = (((*dp)>>8)&255); r2 = (((*dp)>>16)&255); b = (b*u + b2*v)/al; g = (g*u + g2*v)/al; r = (r*u + r2*v)/al; a = al/255; } *dp++ = (a << 24) + (r << 16) + (g << 8) + b; } else dp++; } } src += srcbytes; dst += dstbytes; } } int load_apng(unsigned char *filedata, unsigned int filesize, struct apng_data *apng) { int res; unsigned int i, j; unsigned int rowbytes; int imagesize, zbuf_size, zsize, trns_idx; unsigned int len, chunk, crc; unsigned int w, h, seq, w0, h0, x0, y0; unsigned int frames, loops, first_frame, cur_frame; unsigned int outrow1, outrow2, outimg1, outimg2; unsigned short d1, d2; unsigned char c, dop, bop; unsigned char channels, depth, pixeldepth, bpp; unsigned char coltype, compr, filter, interl; z_stream zstream; memset(apng, 0, sizeof(struct apng_data)); FILE * f1 = fmemopen(filedata, filesize, "rb"); if(!f1) return -1; for (i=0; i<256; i++) { pal[i][0] = i; pal[i][1] = i; pal[i][2] = i; trns[i] = 255; } zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; inflateInit(&zstream); frames = 1; first_frame = 0; cur_frame = 0; zsize = 0; hasTRNS = 0; trns_idx = -1; x0 = 0; y0 = 0; loops = 0; bop = PNG_BLEND_OP_SOURCE; unsigned char sig[8]; unsigned char * pOut1; unsigned char * pOut2; unsigned char * pTemp; unsigned char * pData; unsigned char * pImg1; unsigned char * pImg2; unsigned char * pDst1; unsigned char * pDst2; unsigned int * frames_delay; if ((res = fread(sig, 1, 8, f1)) == 8) { if (memcmp(sig, png_sign, 8) == 0) { len = read32(f1); chunk = read32(f1); if ((len == 13) && (chunk == 0x49484452)) /* IHDR */ { w = w0 = read32(f1); h = h0 = read32(f1); fread(&depth, 1, 1, f1); fread(&coltype, 1, 1, f1); fread(&compr, 1, 1, f1); fread(&filter, 1, 1, f1); fread(&interl, 1, 1, f1); crc = read32(f1); channels = 1; if (coltype == 2) channels = 3; else if (coltype == 4) channels = 2; else if (coltype == 6) channels = 4; pixeldepth = depth*channels; bpp = (pixeldepth + 7) >> 3; rowbytes = ROWBYTES(pixeldepth, w); imagesize = (rowbytes + 1) * h; zbuf_size = imagesize + ((imagesize + 7) >> 3) + ((imagesize + 63) >> 6) + 11; /* * We'll render into 2 output buffers, first in original coltype, * second in RGBA. * * It's better to try to keep the original coltype, but if dispose/blend * operations will make it impossible, then we'll save RGBA version instead. */ outrow1 = w*channels; /* output coltype = input coltype */ outrow2 = w*4; /* output coltype = RGBA */ outimg1 = h*outrow1; outimg2 = h*outrow2; pOut1=(unsigned char *)malloc(outimg1); pOut2=(unsigned char *)malloc(outimg2); pTemp=(unsigned char *)malloc(imagesize); pData=(unsigned char *)malloc(zbuf_size); pImg1=pOut1; pImg2=pOut2; frames_delay = NULL; /* apng decoding - begin */ memset(pOut1, 0, outimg1); memset(pOut2, 0, outimg2); while ( !feof(f1) ) { len = read32(f1); chunk = read32(f1); if (chunk == 0x504C5445) /* PLTE */ { unsigned int col; for (i=0; i= 0) memset(pDst1, trns_idx, w0); else keep_original = 0; break; case 4: memset(pDst1, 0, w0*2); break; case 6: memset(pDst2, 0, w0*4); break; } pDst1 += outrow1; pDst2 += outrow2; } } } } seq = read32(f1); w0 = read32(f1); h0 = read32(f1); x0 = read32(f1); y0 = read32(f1); d1 = read16(f1); d2 = read16(f1); fread(&dop, 1, 1, f1); fread(&bop, 1, 1, f1); crc = read32(f1); if(d2 == 0) d2 = 100; frames_delay[cur_frame] = (d1 * 1000)/d2; if (cur_frame == 0) { bop = PNG_BLEND_OP_SOURCE; if (dop == PNG_DISPOSE_OP_PREVIOUS) dop = PNG_DISPOSE_OP_BACKGROUND; } if (!(coltype & 4) && !(hasTRNS)) bop = PNG_BLEND_OP_SOURCE; rowbytes = ROWBYTES(pixeldepth, w0); cur_frame++; pImg1 += outimg1; pImg2 += outimg2; } else if (chunk == 0x49444154) /* IDAT */ { fread(pData + zsize, 1, len, f1); zsize += len; crc = read32(f1); } else if (chunk == 0x66644154) /* fdAT */ { seq = read32(f1); len -= 4; fread(pData + zsize, 1, len, f1); zsize += len; crc = read32(f1); } else if (chunk == 0x49454E44) /* IEND */ { pDst1 = pImg1 + y0*outrow1 + x0*channels; pDst2 = pImg2 + y0*outrow2 + x0*4; unpack(zstream, pTemp, imagesize, pData, zsize, h0, rowbytes, bpp); switch (coltype) { case 0: compose0(pDst1, outrow1, pDst2, outrow2, pTemp, rowbytes+1, w0, h0, bop, depth); break; case 2: compose2(pDst1, outrow1, pDst2, outrow2, pTemp, rowbytes+1, w0, h0, bop, depth); break; case 3: compose3(pDst1, outrow1, pDst2, outrow2, pTemp, rowbytes+1, w0, h0, bop, depth); break; case 4: compose4(pDst1, outrow1, pTemp, rowbytes+1, w0, h0, bop, depth); break; case 6: compose6( pDst2, outrow2, pTemp, rowbytes+1, w0, h0, bop, depth); break; } break; } else { c = (unsigned char)(chunk>>24); if (notabc(c)) break; c = (unsigned char)((chunk>>16) & 0xFF); if (notabc(c)) break; c = (unsigned char)((chunk>>8) & 0xFF); if (notabc(c)) break; c = (unsigned char)(chunk & 0xFF); if (notabc(c)) break; fseek( f1, len, SEEK_CUR ); crc = read32(f1); } } /* apng decoding - end */ if (coltype == 0) { switch (depth) { case 4: trns[1] *= 0x11; break; case 2: trns[1] *= 0x55; break; case 1: trns[1] *= 0xFF; break; } } inflateEnd(&zstream); apng->bpp = channels; apng->coltype = coltype; apng->last_frame = cur_frame; apng->first_frame = first_frame; apng->height = h; apng->width = w; apng->num_frames = frames; apng->num_plays = loops; apng->frames_delay = frames_delay; apng->pdata = pOut2; apng->bpp = 4; apng->coltype = 6; if(pData) free(pData); if(pTemp) free(pTemp); if(pOut1) free(pOut1); } else return -1; } else return -1; } else return -1; return 0; } void free_apng(struct apng_data *apng) { if(apng->pdata) free(apng->pdata); if(apng->frames_delay) free(apng->frames_delay); }