/***************************************************************************** * * This program is free software ; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Id: png.c 313 2005-10-29 15:15:47Z picard $ * * The Core Pocket Media Player * Copyright (c) 2004-2005 Gabor Kovacs * ****************************************************************************/ #include "../common/common.h" #include "../common/zlib/zlib.h" #include "png.h" typedef struct png { codec Codec; buffer Buffer; bool_t ErrorShowed; const uint8_t* Ptr; } png; static int UpdateInput(png* p) { p->ErrorShowed = 0; BufferClear(&p->Buffer); if (p->Codec.In.Format.Type == PACKET_VIDEO) { PacketFormatCopy(&p->Codec.Out.Format,&p->Codec.In.Format); p->Codec.Out.Format.Format.Video.Pixel.Flags = PF_RGB; p->Codec.Out.Format.Format.Video.Pixel.BitCount = 24; p->Codec.Out.Format.Format.Video.Pixel.BitMask[0] = 0xFF; p->Codec.Out.Format.Format.Video.Pixel.BitMask[1] = 0xFF00; p->Codec.Out.Format.Format.Video.Pixel.BitMask[2] = 0xFF0000; DefaultPitch(&p->Codec.Out.Format.Format.Video); } return ERR_NONE; } static NOINLINE int NotSupported(png* p) { if (!p->ErrorShowed) { p->ErrorShowed = 1; ShowError(p->Codec.Node.Class,PNG_ID,PNG_NOT_SUPPORTED); } return ERR_NOT_SUPPORTED; } static NOINLINE int Read32(png* p) { int v = LOAD32BE(p->Ptr); p->Ptr += 4; return v; } static int Predictor(int Left,int Up,int Corner) { int i = Left+Up-Corner; int a = abs(i-Left); int b = abs(i-Up); int c = abs(i-Corner); return (a<=b && a<=c)?Left:((b<=c)?Up:Corner); } static void Filter(int Mode,const uint8_t* Src,const uint8_t* DstLast,uint8_t* Dst,int Ch,int BytesPerRow) { const uint8_t* DstPrev; const uint8_t* DstLastPrev; int i; DstLastPrev = DstLast; DstPrev = Dst; if (!DstLast) { if (Mode==2) Mode=0; if (Mode==4) Mode=1; } switch (Mode) { case 1: // subtraction filter for (i=0;i0;--BytesPerRow) *(Dst++) = (uint8_t)(*(Src++) + *(DstPrev++)); break; case 2: // up filter for (;BytesPerRow>0;--BytesPerRow) *(Dst++) = (uint8_t)(*(Src++) + *(DstLast++)); break; case 3: // average filter if (DstLast) { for (i=0;i>1)); for (BytesPerRow-=Ch;BytesPerRow>0;--BytesPerRow) *(Dst++) = (uint8_t)(*(Src++) + ((*(DstPrev++)+*(DstLast++))>>1)); } else { for (i=0;i0;--BytesPerRow) *(Dst++) = (uint8_t)(*(Src++) + (*(DstPrev++)>>1)); } break ; case 4: // paeth prediction for (i=0;i0;--BytesPerRow) *(Dst++) = (uint8_t)(*(Src++) + Predictor(*(DstPrev++),*(DstLast++),*(DstLastPrev++))); break; default: // copy memmove(Dst,Src,BytesPerRow); break; } } static int Process(png* p, const packet* Packet, const flowstate* State) { static const uint8_t Magic[8] = {0x89,0x50,0x4E,0x47,0x0D,0x0A,0x1A,0x0A}; rgb Pal[256]; const uint8_t* PtrEnd; uint8_t* LastRow = NULL; int Ch = 0; video Video; int BytesPerRow=0,Left=0,Row=0; z_stream Inflate; if (!Packet) return ERR_NEED_MORE_DATA; p->Ptr = Packet->Data[0]; PtrEnd = p->Ptr + Packet->Length; if (Packet->Length<16 || memcmp(p->Ptr,Magic,8)!=0) return ERR_INVALID_DATA; p->Ptr += 8; memset(&Video,0,sizeof(Video)); BufferDrop(&p->Buffer); while (p->Ptr+8<=PtrEnd) { int i; int Len = Read32(p); int Id = Read32(p); const uint8_t* Next = p->Ptr+Len+4; //dword CRC if (Next > PtrEnd) break; switch (Id) { case FOURCCBE('I','H','D','R'): if (Len<13) return ERR_INVALID_DATA; Video.Width = Read32(p); Video.Height = Read32(p); Video.Pixel.BitCount = p->Ptr[0]; Video.Aspect = ASPECT_ONE; //todo if (p->Ptr[2] != 0 || // compression p->Ptr[3] != 0 || // filter mode p->Ptr[4] != 0) // interlaced return NotSupported(p); if (Video.Pixel.BitCount != 8) return NotSupported(p); if (p->Ptr[1] & 1) { if (p->Ptr[1] & 4) return NotSupported(p); //todo Video.Pixel.Flags |= PF_PALETTE; Ch = 1; } else if (p->Ptr[1] & 2) { Video.Pixel.Flags |= PF_RGB; Video.Pixel.BitMask[0] = 0xFF; Video.Pixel.BitMask[1] = 0xFF00; Video.Pixel.BitMask[2] = 0xFF0000; Ch = 3; if (p->Ptr[1] & 4) ++Ch; // alpha } else { if (p->Ptr[1] & 4) return NotSupported(p); //todo Video.Pixel.Flags |= PF_PALETTE|PF_GRAYSCALE; Ch = 1; } for (i=0;i<256;++i) Pal[i].v = CRGB(i,i,i); BytesPerRow = Ch * ((Video.Width * Video.Pixel.BitCount)>>3)+1; Left = BytesPerRow; Row = Video.Height; LastRow = NULL; Video.Pixel.BitCount *= Ch; DefaultPitch(&Video); if (!BufferAlloc(&p->Buffer,Video.Pitch*Video.Height,4096)) return ERR_OUT_OF_MEMORY; memset(&Inflate,0,sizeof(Inflate)); if (inflateInit2(&Inflate, MAX_WBITS) != Z_OK) return ERR_OUT_OF_MEMORY; break; case FOURCCBE('P','L','T','E'): for (i=0;i<256 && Len>=3;++i,Len-=3,p->Ptr+=3) { Pal[i].c.r = p->Ptr[0]; Pal[i].c.g = p->Ptr[1]; Pal[i].c.b = p->Ptr[2]; } break; case FOURCCBE('I','D','A','T'): if (!BytesPerRow || Row<=0) break; if (!EqVideo(&Video,&p->Codec.Out.Format.Format.Video)) { if ((Video.Pixel.Flags & PF_PALETTE) && PacketFormatExtra(&p->Codec.Out.Format,sizeof(Pal))) { memcpy(p->Codec.Out.Format.Extra,Pal,sizeof(Pal)); Video.Pixel.Palette = p->Codec.Out.Format.Extra; } p->Codec.In.Format.Format.Video.Width = Video.Width; p->Codec.In.Format.Format.Video.Height = Video.Height; p->Codec.Out.Format.Format.Video = Video; ConnectionUpdate(&p->Codec.Node,CODEC_OUTPUT,p->Codec.Out.Pin.Node,p->Codec.Out.Pin.No); } Inflate.next_in = (Bytef*)p->Ptr; Inflate.avail_in = Len; Inflate.next_out = p->Buffer.Data + p->Buffer.WritePos; Inflate.avail_out = Left; do { if (Inflate.avail_out<=0) { uint8_t* CurrRow = p->Buffer.Data + p->Buffer.WritePos - BytesPerRow; Filter(CurrRow[0],CurrRow+1,LastRow,CurrRow,Ch,BytesPerRow-1); LastRow = CurrRow; p->Buffer.WritePos += Video.Pitch - BytesPerRow; Inflate.next_out += Video.Pitch - BytesPerRow; Inflate.avail_out = Left = BytesPerRow; if (--Row<=0) break; } if (Inflate.avail_in<=0) break; i = inflate(&Inflate, Z_SYNC_FLUSH); p->Buffer.WritePos += Left - Inflate.avail_out; Left = Inflate.avail_out; if (i==Z_STREAM_END) Inflate.avail_in = 0; } while (i>=0); break; case FOURCCBE('I','E','N','D'): Next = PtrEnd; break; } p->Ptr = Next; } if (BytesPerRow) inflateEnd(&Inflate); if (p->Buffer.WritePos<=0) return ERR_INVALID_DATA; p->Codec.Packet.RefTime = Packet->RefTime; p->Codec.Packet.Length = p->Buffer.WritePos; p->Codec.Packet.Data[0] = p->Buffer.Data; return ERR_NONE; } static int Resend(png* p) { int Result = ERR_INVALID_DATA; if (p->Buffer.WritePos) { packet Packet; flowstate State; State.CurrTime = TIME_RESEND; State.DropLevel = 0; memset(&Packet,0,sizeof(Packet)); Packet.RefTime = TIME_UNKNOWN; Packet.Length = p->Buffer.WritePos; Packet.Data[0] = p->Buffer.Data; Result = p->Codec.Out.Process(p->Codec.Out.Pin.Node,&Packet,&State); } return Result; } static int Flush(png* p) { BufferDrop(&p->Buffer); return ERR_NONE; } static int Create(png* p) { p->Codec.Process = (packetprocess)Process; p->Codec.UpdateInput = (nodefunc)UpdateInput; p->Codec.ReSend = (nodefunc)Resend; p->Codec.Flush = (nodefunc)Flush; return ERR_NONE; } static const nodedef PNG = { sizeof(png), PNG_ID, CODEC_CLASS, PRI_DEFAULT, (nodecreate)Create, NULL, }; //------------------------------------------------------------------------------------------- static const nodedef PNGFile = { 0, // parent size PNG_FILE_ID, RAWIMAGE_CLASS, PRI_DEFAULT, }; //--------------------------------------------------------------------------------------------- void PNG_Init() { NodeRegisterClass(&PNG); NodeRegisterClass(&PNGFile); } void PNG_Done() { NodeUnRegisterClass(PNG_ID); NodeUnRegisterClass(PNG_FILE_ID); }