gps/GPSResources/tcpmpVerOld/camera/png.c

399 lines
9.2 KiB
C
Executable File

/*****************************************************************************
*
* 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;i<Ch;++i)
*(Dst++) = *(Src++);
for (BytesPerRow-=Ch;BytesPerRow>0;--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<Ch;++i)
*(Dst++) = (uint8_t)(*(Src++) + (*(DstLast++)>>1));
for (BytesPerRow-=Ch;BytesPerRow>0;--BytesPerRow)
*(Dst++) = (uint8_t)(*(Src++) + ((*(DstPrev++)+*(DstLast++))>>1));
}
else
{
for (i=0;i<Ch;++i)
*(Dst++) = *(Src++);
for (BytesPerRow-=Ch;BytesPerRow>0;--BytesPerRow)
*(Dst++) = (uint8_t)(*(Src++) + (*(DstPrev++)>>1));
}
break
;
case 4: // paeth prediction
for (i=0;i<Ch;++i)
*(Dst++) = (uint8_t)(*(Src++) + Predictor(0,*(DstLast++),0));
for (BytesPerRow-=Ch;BytesPerRow>0;--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);
}