#include "PNG.h" #include "BitStreamReader.h" #include "DeflateReader.h" static inline uint32_t GetUInt32BE(const uint8_t *ptr) { return (ptr[0]<<24)|(ptr[1]<<16)|(ptr[2]<<8)|ptr[3]; } static inline int Abs(int val) { return val<0?-val:val; } #define MakeID(c1,c2,c3,c4) (((c1)<<24)|((c2)<<16)|((c3)<<8)|(c4)) void InitializePNGLoader(PNGLoader *self,const void *bytes,size_t length) { self->bytes=bytes; self->length=length; self->width=0; self->height=0; self->bitdepth=0; self->colourtype=0; self->bytesperrow=0; self->bytesperpixel=0; self->palettestart=NULL; self->palettecount=0; self->transparencystart=NULL; self->transparencycount=0; self->idatstart=NULL; self->idatsize=0; } bool LoadPNGHeader(PNGLoader *self) { const uint8_t *ptr=self->bytes; const uint8_t *end=self->bytes+self->length; if(ptr+8>end) return false; if(*ptr++!=137) return false; if(*ptr++!=80) return false; if(*ptr++!=78) return false; if(*ptr++!=71) return false; if(*ptr++!=13) return false; if(*ptr++!=10) return false; if(*ptr++!=26) return false; if(*ptr++!=10) return false; while(ptr+12end) return false; ptr+=8; switch(type) { case MakeID('I','H','D','R'): if(size<13) return false; if(ptr[10]!=0) return false; // Compression type if(ptr[11]!=0) return false; // Filter method if(ptr[12]!=0) return false; // Interlace method self->width=GetUInt32BE(&ptr[0]); self->height=GetUInt32BE(&ptr[4]); self->bitdepth=ptr[8]; self->colourtype=ptr[9]; switch(self->colourtype) { case 0: // Grayscale case 3: // Palette if(self->bitdepth!=1&&self->bitdepth!=2&& self->bitdepth!=4&&self->bitdepth!=8) return false; self->bytesperrow=(self->bitdepth*self->width+7)/8; self->bytesperpixel=1; break; case 2: // RGB if(self->bitdepth!=8) return false; self->bytesperrow=3*self->width; self->bytesperpixel=3; break; case 4: // Gray+alpha if(self->bitdepth!=8) return false; self->bytesperrow=2*self->width; self->bytesperpixel=2; break; case 6: // RGB+alpha if(self->bitdepth!=8) return false; self->bytesperrow=4*self->width; self->bytesperpixel=4; break; default: return false; } break; case MakeID('P','L','T','E'): self->palettestart=ptr; self->palettecount=size/3; break; case MakeID('t','R','N','S'): self->transparencystart=ptr; self->transparencycount=size; break; case MakeID('I','D','A','T'): self->idatstart=ptr; self->idatsize=size; return true; break; } ptr=next; } return false; } static void InputCallback(DeflateReader *reader,void *context) { PNGLoader *self=context; const uint8_t *end=self->bytes+self->length; const uint8_t *ptr=self->idatstart+self->idatsize+4; if(ptr+12>end) return; uint32_t size=GetUInt32BE(&ptr[0]); uint32_t type=GetUInt32BE(&ptr[4]); if(ptr+12+size>end) return; if(type==MakeID('I','D','A','T')) { self->idatstart=ptr+8; self->idatsize=size; ProvideDeflateInputBuffer(reader,self->idatstart,self->idatsize); } } bool LoadPNGImageData(PNGLoader *self,void *pixels,int bytesperrow,PNGPixelFunction *pixelfunc) { uint8_t bytes[TemporaryBytesForPNGLoader(self)]; return LoadPNGImageDataWithTemporaryStorage(self,pixels,bytesperrow,pixelfunc,bytes); /* void *bytes=malloc(TemporaryBytesForPNGLoader(self)); bool res=LoadPNGImageDataWithTemporaryStorage(self,pixels,bytesperrow,pixelfunc,bytes); free(bytes); return res;*/ } bool LoadPNGImageDataWithTemporaryStorage(PNGLoader *self,void *pixels,int bytesperrow,PNGPixelFunction *pixelfunc,void *temporarystorage) { DeflateReader *reader=temporarystorage; uint8_t *buf1=(uint8_t *)temporarystorage+sizeof(DeflateReader); uint8_t *buf2=buf1+self->bytesperrow+self->bytesperpixel; // TODO: Check first two bytes for zlib header. InitializeDeflateReader(reader); ProvideDeflateInputBuffer(reader,self->idatstart+2,self->idatsize-2); SetDeflateInputCallback(reader,InputCallback,self); memset(buf1,0,self->bytesperrow+self->bytesperpixel); memset(buf2,0,self->bytesperpixel); uint8_t *lastline=buf1+self->bytesperpixel; uint8_t *currline=buf2+self->bytesperpixel; for(int y=0;yheight;y++) { uint32_t linestart=y*(self->bytesperrow+1); uint32_t lineend=(y+1)*(self->bytesperrow+1); // Advance deflate stream until we can see all data, the copy data from window. if(!ReadDeflateStreamToPosition(reader,lineend)) return false; uint8_t filter=ByteFromDeflateWindow(reader,linestart); if(filter>4) return false; CopyBytesFromDeflateWindow(reader,currline,linestart+1,self->bytesperrow); // Undo filtering. for(int i=0;ibytesperrow;i++) switch(filter) { case 0: break; // No filter. case 1: { int left=currline[i-self->bytesperpixel]; currline[i]+=left; } break; case 2: { int above=lastline[i]; currline[i]+=above; } break; case 3: { int left=currline[i-self->bytesperpixel]; int above=lastline[i]; currline[i]+=(left+above)/2; } break; case 4: { int left=currline[i-self->bytesperpixel]; int above=lastline[i]; int aboveleft=lastline[i-self->bytesperpixel]; int p=left+above-aboveleft; int pa=Abs(p-left); int pb=Abs(p-above); int pc=Abs(p-aboveleft); if(pa<=pb && pa<=pc) currline[i]+=left; else if(pb<=pc) currline[i]+=above; else currline[i]+=aboveleft; } break; } // Unpack pixels. void *outputline=(uint8_t *)pixels+bytesperrow*y; for(int x=0;xwidth;x++) switch(self->colourtype) { case 0: // Grayscale { uint8_t gray; switch(self->bitdepth) { case 1: gray=((currline[x>>3]>>(x&7))&0x01)*0xff; break; case 2: gray=((currline[x>>2]>>(x&3))&0x03)*0x55; break; case 4: gray=((currline[x>>1]>>(x&1))&0x0f)*0x11; break; case 8: gray=currline[x]; break; } outputline=pixelfunc(outputline,gray,gray,gray,0xff,x,y); } break; case 2: // RGB outputline=pixelfunc(outputline,currline[3*x+0],currline[3*x+1],currline[3*x+2],0xff,x,y); break; case 3: // Palette { uint8_t index; switch(self->bitdepth) { case 1: index=(currline[x>>3]>>(x&7))&0x01; break; case 2: index=(currline[x>>2]>>(x&3))&0x03; break; case 4: index=(currline[x>>1]>>(x&1))&0x0f; break; case 8: index=currline[x]; break; } if(index>=self->palettecount) return false; uint8_t r=self->palettestart[3*index+0]; uint8_t g=self->palettestart[3*index+1]; uint8_t b=self->palettestart[3*index+2]; uint8_t a=0xff; if(indextransparencycount) a=self->transparencystart[index]; outputline=pixelfunc(outputline,r,g,b,a,x,y); } break; case 4: // Gray+alpha outputline=pixelfunc(outputline,currline[2*x+0],currline[2*x+0],currline[2*x+0],currline[2*x+1],x,y); break; case 6: // RGB+alpha outputline=pixelfunc(outputline,currline[4*x+0],currline[4*x+1],currline[4*x+2],currline[4*x+3],x,y); break; } // Swap line buffers. uint8_t *tmp=lastline; lastline=currline; currline=tmp; } return true; } size_t TemporaryBytesForPNGLoader(PNGLoader *self) { return sizeof(DeflateReader)+(self->bytesperrow+self->bytesperpixel)*2; }