apngdis-2.5.orig/0000755000000000000000000000000012152301521012432 5ustar rootrootapngdis-2.5.orig/apngdis.c0000644000000000000000000010643112026006752014240 0ustar rootroot/* APNG Disassembler 2.5 * * Deconstructs APNG files into individual frames. * * http://apngdis.sourceforge.net * * Copyright (c) 2010-2012 Max Stepin * maxst at users.sourceforge.net * * zlib license * ------------ * * This software is provided 'as-is', without any express or implied * warranty. In no event will the authors be held liable for any damages * arising from the use of this software. * * Permission is granted to anyone to use this software for any purpose, * including commercial applications, and to alter it and redistribute it * freely, subject to the following restrictions: * * 1. The origin of this software must not be misrepresented; you must not * claim that you wrote the original software. If you use this software * in a product, an acknowledgment in the product documentation would be * appreciated but is not required. * 2. Altered source versions must be plainly marked as such, and must not be * misrepresented as being the original software. * 3. This notice may not be removed or altered from any source distribution. * */ #include #include #include #include "zlib.h" #if defined(_MSC_VER) && _MSC_VER >= 1300 #define swap16(data) _byteswap_ushort(data) #define swap32(data) _byteswap_ulong(data) #elif defined(__linux__) #include #define swap16(data) bswap_16(data) #define swap32(data) bswap_32(data) #elif defined(__FreeBSD__) #include #define swap16(data) bswap16(data) #define swap32(data) bswap32(data) #elif defined(__APPLE__) #include #define swap16(data) OSSwapInt16(data) #define swap32(data) OSSwapInt32(data) #else unsigned short swap16(unsigned short data) {return((data & 0xFF) << 8) | ((data >> 8) & 0xFF);} unsigned int swap32(unsigned int data) {return((data & 0xFF) << 24) | ((data & 0xFF00) << 8) | ((data >> 8) & 0xFF00) | ((data >> 24) & 0xFF);} #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 short trns1, trns2, trns3; int trns_idx; int read32(unsigned int *val, FILE * f1) { unsigned int res; if (fread(&res, 1, 4, f1) != 4) return 1; *val = swap32(res); return 0; } int read16(unsigned short *val, FILE * f1) { unsigned short res; if (fread(&res, 1, 2, f1) != 2) return 1; *val = swap16(res); return 0; } 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 (trnssize && 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 (trnssize && 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 (trnssize && 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>3] & mask1[i&7]) >> shift1[i&7]; break; case 2: col = (sp[i>>2] & mask2[i&3]) >> shift2[i&3]; break; case 4: col = (sp[i>>1] & mask4[i&1]) >> shift4[i&1]; break; default: col = sp[i]; } r = pal[col][0]; g = pal[col][1]; b = pal[col][2]; a = trns[col]; if (bop == PNG_BLEND_OP_SOURCE) { *dp1++ = col; *dp2++ = (a << 24) + (b << 16) + (g << 8) + r; } else /* PNG_BLEND_OP_OVER */ { if (a == 255) { *dp1++ = col; *dp2++ = (a << 24) + (b << 16) + (g << 8) + r; } else if (a != 0) { if ((a2 = (*dp2)>>24) != 0) { keep_original = 0; u = a*255; v = (255-a)*a2; al = 255*255-(255-a)*(255-a2); r2 = ((*dp2)&255); g2 = (((*dp2)>>8)&255); b2 = (((*dp2)>>16)&255); r = (r*u + r2*v)/al; g = (g*u + g2*v)/al; b = (b*u + b2*v)/al; a = al/255; } *dp1++ = col; *dp2++ = (a << 24) + (b << 16) + (g << 8) + r; } 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) != 0) { u = a*255; v = (255-a)*a2; al = 255*255-(255-a)*(255-a2); r2 = ((*dp)&255); g2 = (((*dp)>>8)&255); b2 = (((*dp)>>16)&255); r = (r*u + r2*v)/al; g = (g*u + g2*v)/al; b = (b*u + b2*v)/al; a = al/255; } *dp++ = (a << 24) + (b << 16) + (g << 8) + r; } else dp++; } } src += srcbytes; dst += dstbytes; } } int LoadAPNG(char * szIn, unsigned int *pWidth, unsigned int *pHeight, unsigned int *pChannels, unsigned char *pColType, unsigned int *pFirst, unsigned int *pLast, unsigned char **ppOut1, unsigned char **ppOut2, unsigned short **ppDelays) { unsigned char sig[8]; unsigned int i, j; unsigned int len, chunk, seq, crc; unsigned int w, h, w0, h0, x0, y0; unsigned char depth, coltype, compr, filter, interl; unsigned char pixeldepth, bpp; unsigned int rowbytes, frames, loops, first_visible, cur_frame, channels; unsigned int outrow1, outrow2, outimg1, outimg2; unsigned short d1, d2; unsigned char c, dop, bop; int imagesize, zbuf_size, zsize; z_stream zstream; 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 short * pDelays; FILE * f1; int res = 1; if ((f1 = fopen(szIn, "rb")) == 0) { printf("Error: can't open '%s'\n", szIn); return res; } printf("Reading '%s'...\n", szIn); frames = 0; loops = 0; first_visible = 0; cur_frame = 0; zsize = 0; x0 = 0; y0 = 0; bop = PNG_BLEND_OP_SOURCE; palsize = 0; trnssize = 0; trns_idx = -1; memset(trns, 255, 256); zstream.zalloc = Z_NULL; zstream.zfree = Z_NULL; zstream.opaque = Z_NULL; inflateInit(&zstream); do { if (fread(sig, 1, 8, f1) != 8) { printf("Error: can't read the sig\n"); break; } if (memcmp(sig, png_sign, 8) != 0) { printf("Error: wrong PNG sig\n"); break; } if (read32(&len, f1)) break; if (read32(&chunk, f1)) break; if (len != 13 || chunk != 0x49484452) /* IHDR */ { printf("Error: missing IHDR\n"); break; } if (read32(&w, f1)) break; if (read32(&h, f1)) break; w0 = w; h0 = h; if (fread(&depth, 1, 1, f1) != 1) break; if (fread(&coltype, 1, 1, f1) != 1) break; if (fread(&compr, 1, 1, f1) != 1) break; if (fread(&filter, 1, 1, f1) != 1) break; if (fread(&interl, 1, 1, f1) != 1) break; if (read32(&crc, f1)) break; 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((frames+1)*outimg1); pOut2 = (unsigned char *)malloc((frames+1)*outimg2); pDelays = (unsigned short *)malloc((frames+1)*4); pTemp = (unsigned char *)malloc(imagesize); pData = (unsigned char *)malloc(zbuf_size); if (!pOut1 || !pOut2 || !pDelays || !pTemp || !pData) { printf("Error: not enough memory\n"); break; } pImg1 = pOut1; pImg2 = pOut2; memset(pOut1, 0, outimg1); memset(pOut2, 0, outimg2); pDelays[0] = 0; pDelays[1] = 0; while ( !feof(f1) ) { if (read32(&len, f1)) break; if (read32(&chunk, f1)) break; 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; } } } } pImg1 += outimg1; pImg2 += outimg2; if (read32(&seq, f1)) break; if (read32(&w0, f1)) break; if (read32(&h0, f1)) break; if (read32(&x0, f1)) break; if (read32(&y0, f1)) break; if (read16(&d1, f1)) break; if (read16(&d2, f1)) break; if (fread(&dop, 1, 1, f1) != 1) break; if (fread(&bop, 1, 1, f1) != 1) break; if (read32(&crc, f1)) break; if (cur_frame == 0) { bop = PNG_BLEND_OP_SOURCE; if (dop == PNG_DISPOSE_OP_PREVIOUS) dop = PNG_DISPOSE_OP_BACKGROUND; } if (coltype<=3 && trnssize==0) bop = PNG_BLEND_OP_SOURCE; rowbytes = ROWBYTES(pixeldepth, w0); cur_frame++; pDelays[cur_frame*2] = d1; pDelays[cur_frame*2+1] = d2; } else if (chunk == 0x49444154) /* IDAT */ { if (fread(pData + zsize, 1, len, f1) != len) break; zsize += len; if (read32(&crc, f1)) break; } else if (chunk == 0x66644154) /* fdAT */ { if (read32(&seq, f1)) break; len -= 4; if (fread(pData + zsize, 1, len, f1) != len) break; zsize += len; if (read32(&crc, f1)) break; } 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; } if (cur_frame == frames) res = 0; 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); if (read32(&crc, f1)) break; } } *pWidth = w; *pHeight = h; *pChannels = channels; *pColType = coltype; *pFirst = first_visible; *pLast = cur_frame; *ppOut1 = pOut1; *ppOut2 = pOut2; *ppDelays = pDelays; free(pData); free(pTemp); } while (0); inflateEnd(&zstream); fclose(f1); return res; } void write_chunk(FILE * f, const char * name, unsigned char * data, unsigned int length) { unsigned int crc = crc32(0, Z_NULL, 0); unsigned int len = swap32(length); fwrite(&len, 1, 4, f); fwrite(name, 1, 4, f); crc = crc32(crc, (const Bytef *)name, 4); if (data != NULL && length > 0) { fwrite(data, 1, length, f); crc = crc32(crc, data, length); } crc = swap32(crc); fwrite(&crc, 1, 4, f); } void write_IDATs(FILE * f, unsigned char * data, unsigned int length, unsigned int idat_size) { unsigned int z_cmf = data[0]; if ((z_cmf & 0x0f) == 8 && (z_cmf & 0xf0) <= 0x70) { if (length >= 2) { unsigned int z_cinfo = z_cmf >> 4; unsigned int half_z_window_size = 1 << (z_cinfo + 7); while (idat_size <= half_z_window_size && half_z_window_size >= 256) { z_cinfo--; half_z_window_size >>= 1; } z_cmf = (z_cmf & 0x0f) | (z_cinfo << 4); if (data[0] != (unsigned char)z_cmf) { data[0] = (unsigned char)z_cmf; data[1] &= 0xe0; data[1] += (unsigned char)(0x1f - ((z_cmf << 8) + data[1]) % 0x1f); } } } while (length > 0) { unsigned int ds = length; if (ds > PNG_ZBUF_SIZE) ds = PNG_ZBUF_SIZE; write_chunk(f, "IDAT", data, ds); data += ds; length -= ds; } } void SavePNG(char * szPath, unsigned char * pdata, unsigned short * delays, unsigned int w, unsigned int h, unsigned int first, unsigned int last, unsigned int bpp, unsigned char coltype) { struct IHDR { unsigned int mWidth; unsigned int mHeight; unsigned char mDepth; unsigned char mColorType; unsigned char mCompression; unsigned char mFilterMethod; unsigned char mInterlaceMethod; } ihdr = { swap32(w), swap32(h), 8, coltype, 0, 0, 0 }; char szOut[256]; z_stream zstream1; z_stream zstream2; FILE * f; unsigned int i, j, n, len; unsigned int rowbytes = w * bpp; unsigned int idat_size = (rowbytes + 1) * h; unsigned int zbuf_size = idat_size + ((idat_size + 7) >> 3) + ((idat_size + 63) >> 6) + 11; unsigned char * row_buf = (unsigned char *)malloc(rowbytes + 1); unsigned char * sub_row = (unsigned char *)malloc(rowbytes + 1); unsigned char * up_row = (unsigned char *)malloc(rowbytes + 1); unsigned char * avg_row = (unsigned char *)malloc(rowbytes + 1); unsigned char * paeth_row = (unsigned char *)malloc(rowbytes + 1); unsigned char * zbuf1 = (unsigned char *)malloc(zbuf_size); unsigned char * zbuf2 = (unsigned char *)malloc(zbuf_size); if (!row_buf || !sub_row || !up_row || !avg_row || !paeth_row || !zbuf1 || !zbuf2) return; row_buf[0] = 0; sub_row[0] = 1; up_row[0] = 2; avg_row[0] = 3; paeth_row[0] = 4; zstream1.data_type = Z_BINARY; zstream1.zalloc = Z_NULL; zstream1.zfree = Z_NULL; zstream1.opaque = Z_NULL; deflateInit2(&zstream1, Z_BEST_COMPRESSION, 8, 15, 8, Z_DEFAULT_STRATEGY); zstream2.data_type = Z_BINARY; zstream2.zalloc = Z_NULL; zstream2.zfree = Z_NULL; zstream2.opaque = Z_NULL; deflateInit2(&zstream2, Z_BEST_COMPRESSION, 8, 15, 8, Z_FILTERED); len = sprintf(szOut, "%d", last); for (n=first; n<=last; n++) { printf("extracting frame %d of %d\n", n, last); if (n > 0) { sprintf(szOut, "%s%.*d.txt", szPath, len, n); if ((f = fopen(szOut, "wt")) != 0) { fprintf(f, "delay=%d/%d\n", delays[n*2], delays[n*2+1]); fclose(f); } } sprintf(szOut, "%s%.*d.png", szPath, len, n); if ((f = fopen(szOut, "wb")) != 0) { int a, b, c, pa, pb, pc, p, v; unsigned char * prev; unsigned char * row; fwrite(png_sign, 1, 8, f); write_chunk(f, "IHDR", (unsigned char *)(&ihdr), 13); if (palsize > 0) write_chunk(f, "PLTE", (unsigned char *)(&pal), palsize*3); if (trnssize > 0) write_chunk(f, "tRNS", trns, trnssize); zstream1.next_out = zbuf1; zstream1.avail_out = zbuf_size; zstream2.next_out = zbuf2; zstream2.avail_out = zbuf_size; prev = NULL; row = pdata + n*h*rowbytes; for (j=0; j> 1; out = row_buf+1; for (i=0; i mins) break; } if (sum < mins) { mins = sum; best_row = sub_row; } if (prev) { sum = 0; out = up_row+1; for (i=0; i mins) break; } if (sum < mins) { mins = sum; best_row = up_row; } sum = 0; out = avg_row+1; for (i=0; i mins) break; } if (sum < mins) { mins = sum; best_row = avg_row; } sum = 0; out = paeth_row+1; for (i=0; i mins) break; } if (sum < mins) { best_row = paeth_row; } } zstream1.next_in = row_buf; zstream1.avail_in = rowbytes + 1; deflate(&zstream1, Z_NO_FLUSH); zstream2.next_in = best_row; zstream2.avail_in = rowbytes + 1; deflate(&zstream2, Z_NO_FLUSH); prev = row; row += rowbytes; } deflate(&zstream1, Z_FINISH); deflate(&zstream2, Z_FINISH); if (zstream1.total_out <= zstream2.total_out) write_IDATs(f, zbuf1, zstream1.total_out, idat_size); else write_IDATs(f, zbuf2, zstream2.total_out, idat_size); deflateReset(&zstream1); zstream1.data_type = Z_BINARY; deflateReset(&zstream2); zstream2.data_type = Z_BINARY; write_chunk(f, "IEND", 0, 0); fclose(f); } else printf("Error: can't open the file '%s'\n", szOut); } deflateEnd(&zstream1); deflateEnd(&zstream2); free(zbuf1); free(zbuf2); free(row_buf); free(sub_row); free(up_row); free(avg_row); free(paeth_row); } int main(int argc, char** argv) { char * szIn; char * szImg; char szOut[256]; unsigned int w, h, first, last, channels, i, j; unsigned char coltype; unsigned char * pOut1 = NULL; unsigned char * pOut2 = NULL; unsigned short * pDelays = NULL; printf("\nAPNG Disassembler 2.5\n\n"); if (argc > 1) szIn = argv[1]; else { printf("Usage: apngdis apng.png [name]\n"); return 1; } if (LoadAPNG(szIn, &w, &h, &channels, &coltype, &first, &last, &pOut1, &pOut2, &pDelays) != 0) { printf("Error: can't load '%s'\n", szIn); return 1; } strcpy(szOut, szIn); for (i=j=0; szOut[i]!=0; i++) { if (szOut[i] == '\\' || szOut[i] == '/' || szOut[i] == ':') j = i+1; } szOut[j] = 0; if (argc > 2) { szImg = argv[2]; for (i=j=0; szImg[i]!=0; i++) { if (szImg[i] == '\\' || szImg[i] == '/' || szImg[i] == ':') j = i+1; if (szImg[i] == '.') szImg[i] = 0; } strcat(szOut, szImg+j); } else strcat(szOut, "apngframe"); if (coltype == 6) SavePNG(szOut, pOut2, pDelays, w, h, first, last, 4, 6); else if (coltype == 4) SavePNG(szOut, pOut1, pDelays, w, h, first, last, channels, coltype); else if (keep_original) SavePNG(szOut, pOut1, pDelays, w, h, first, last, channels, coltype); else SavePNG(szOut, pOut2, pDelays, w, h, first, last, 4, 6); free(pOut1); free(pOut2); free(pDelays); printf("all done\n"); return 0; } apngdis-2.5.orig/Makefile0000644000000000000000000000010012025613746014077 0ustar rootrootall: gcc -std=c99 -Wall -pedantic -O2 -o apngdis apngdis.c -lz apngdis-2.5.orig/readme.txt0000644000000000000000000000440012026003402014423 0ustar rootroot APNG Disassembler 2.5 Deconstructs APNG files into individual frames. http://apngdis.sourceforge.net/ Copyright (c) 2010-2012 Max Stepin maxst@users.sourceforge.net License: zlib license -------------------------------- Changes in version 2.5: - Fixed loading of 1,2,4-bit per pixel paletted PNGs. -------------------------------- Usage: apngdis apng.png [name] -------------------------------- Old readme.txt: Thank you for you interest in APNG Disassembler. By making this program, I hoped to achieve two goals: First is to let you edit existing APNG files by deconstructing them into individual frames. After editing PNG frames in Photoshop/GIMP/etc you can put them back into APNG using APNG Assembler ( http://apngasm.sourceforge.net/ ) Second is to provide relatively simple reference source code for proper APNG rendering. That means correctly handling all blend/dispose combinations, in all color modes/depths. You could use this source code to make your application interface fancier, or add support for 800th format in your image viewer. But before you do that, let me explain how to choose the right version: version 2.1 - Saves frames in RGBA mode. - Stores only current frame in memory. version 2.2 - Saves frames in RGBA mode. - Stores all frames in memory. version 2.3 - Saves frames in original color mode. - Stores all frames in memory. Saving frames in original color mode is useful if you plan to assemble them back using APNG Assembler, but it makes the source code for v.2.3 more complex. If you only need to display APNG frames, check out versions 2.1 or 2.2 instead. After decoding you'll get 32 bpp memory block in RGBA format, and sending it to the screen is quite simple. You should choose between versions 2.1 and 2.2 based on your memory requirements: keeping all decoded frames in one big memory block like v.2.2 does is certainly simpler, but if you can't do that, use v.2.1, as it keeps in memory just the current frame. Time delay between frames is d1/d2 seconds. APNG Disassembler doesn't use libpng, it only needs zlib. Compiles under Windows (tested with MSVC) and Linux (tested with GCC): gcc -O2 -lz apngdis.c -o apngdis M.