add oversampler
This commit is contained in:
462
oversampling/WDL/lice/lice_gif.cpp
Normal file
462
oversampling/WDL/lice/lice_gif.cpp
Normal file
@@ -0,0 +1,462 @@
|
||||
/*
|
||||
Cockos WDL - LICE - Lightweight Image Compositing Engine
|
||||
Copyright (C) 2007 and later, Cockos Incorporated
|
||||
File: lice_gif.cpp (GIF loading for LICE)
|
||||
See lice.h for license and other information
|
||||
*/
|
||||
|
||||
#include "lice.h"
|
||||
#include "../heapbuf.h"
|
||||
#include "../fileread.h"
|
||||
#include <stdio.h>
|
||||
|
||||
extern "C" {
|
||||
|
||||
#include "../giflib/gif_lib.h"
|
||||
int _GifError;
|
||||
};
|
||||
|
||||
static void applyGifFrameToBitmap(LICE_IBitmap *bmp, GifFileType *fp, int transparent_pix, bool clear)
|
||||
{
|
||||
const int width=fp->Image.Width,height=fp->Image.Height;
|
||||
|
||||
LICE_pixel cmap[256];
|
||||
GifPixelType _linebuf[2048];
|
||||
GifPixelType *linebuf=width > 2048 ? (GifPixelType*)malloc(width*sizeof(GifPixelType)) : _linebuf;
|
||||
int y;
|
||||
|
||||
if (bmp)
|
||||
{
|
||||
y=0;
|
||||
if (fp->Image.ColorMap && fp->Image.ColorMap->Colors) for (;y<256 && y < fp->Image.ColorMap->ColorCount;y++)
|
||||
{
|
||||
GifColorType *ct=fp->Image.ColorMap->Colors+y;
|
||||
cmap[y]=LICE_RGBA(ct->Red,ct->Green,ct->Blue,255);
|
||||
}
|
||||
|
||||
if (fp->SColorMap && fp->SColorMap->Colors) for (;y<256 && y < fp->SColorMap->ColorCount;y++)
|
||||
{
|
||||
GifColorType *ct=fp->SColorMap->Colors+y;
|
||||
cmap[y]=LICE_RGBA(ct->Red,ct->Green,ct->Blue,255);
|
||||
}
|
||||
for (;y<256;y++) cmap[y]=0;
|
||||
|
||||
if (clear)
|
||||
{
|
||||
LICE_pixel col = 0;
|
||||
|
||||
if (fp->SColorMap && fp->SColorMap->Colors && fp->SBackGroundColor >= 0 && fp->SBackGroundColor < fp->SColorMap->ColorCount)
|
||||
{
|
||||
GifColorType *ct=fp->SColorMap->Colors+fp->SBackGroundColor;
|
||||
col = LICE_RGBA(ct->Red,ct->Green,ct->Blue,0);
|
||||
}
|
||||
else if (fp->SBackGroundColor>=0 && fp->SBackGroundColor<256)
|
||||
{
|
||||
col = cmap[fp->SBackGroundColor] & LICE_RGBA(255,255,255,0);
|
||||
}
|
||||
LICE_Clear(bmp,col);
|
||||
}
|
||||
}
|
||||
|
||||
const int bmp_h = bmp ? bmp->getHeight() : 0;
|
||||
const int bmp_w = bmp ? bmp->getWidth() : 0;
|
||||
LICE_pixel *bmp_ptr = bmp ? bmp->getBits() : NULL;
|
||||
int bmp_span = bmp ? bmp->getRowSpan() : 0;
|
||||
if (bmp && bmp->isFlipped() )
|
||||
{
|
||||
bmp_ptr += (bmp_h-1)*bmp_span;
|
||||
bmp_span = -bmp_span;
|
||||
}
|
||||
const int xpos = fp->Image.Left;
|
||||
|
||||
int skip=0;
|
||||
int use_width = width;
|
||||
if (xpos < 0) skip = -xpos;
|
||||
|
||||
if (use_width > bmp_w - xpos) use_width = bmp_w - xpos;
|
||||
|
||||
int ystate = 0, ypass=0;
|
||||
for (y=0; y < height; y ++)
|
||||
{
|
||||
if (DGifGetLine(fp,linebuf,width)==GIF_ERROR) break;
|
||||
|
||||
int ypos;
|
||||
if (fp->Image.Interlace)
|
||||
{
|
||||
if (ypass == 0)
|
||||
{
|
||||
ypos = ystate++ * 8;
|
||||
if (ypos >= height) { ypass++; ystate=0; }
|
||||
}
|
||||
if (ypass == 1)
|
||||
{
|
||||
ypos = ystate++ * 8 + 4;
|
||||
if (ypos >= height) { ypass++; ystate=0; }
|
||||
}
|
||||
if (ypass == 2)
|
||||
{
|
||||
ypos = ystate++ * 4 + 2;
|
||||
if (ypos >= height) { ypass++; ystate=0; }
|
||||
}
|
||||
if (ypass==3) ypos = ystate++ * 2 + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
ypos = ystate++;
|
||||
}
|
||||
|
||||
ypos += fp->Image.Top;
|
||||
if (ypos < 0 || ypos >= bmp_h) continue;
|
||||
|
||||
LICE_pixel *p = bmp_ptr + ypos*bmp_span + xpos;
|
||||
int x;
|
||||
for (x = skip; x < use_width; x ++)
|
||||
{
|
||||
const int a = (int) linebuf[x];
|
||||
if (a != transparent_pix) p[x]=cmap[a];
|
||||
}
|
||||
}
|
||||
if (linebuf != _linebuf) free(linebuf);
|
||||
}
|
||||
|
||||
static int readfunc_fh(GifFileType *fh, GifByteType *buf, int sz)
|
||||
{
|
||||
return ((WDL_FileRead*)fh->UserData)->Read(buf,sz);
|
||||
}
|
||||
|
||||
|
||||
LICE_IBitmap *LICE_LoadGIF(const char *filename, LICE_IBitmap *bmp, int *nframes)
|
||||
{
|
||||
WDL_FileRead fpp(filename,0,65536);
|
||||
|
||||
if (nframes) *nframes=0;
|
||||
|
||||
if (!fpp.IsOpen()) return 0;
|
||||
|
||||
GifFileType *fp=DGifOpen(&fpp, readfunc_fh);
|
||||
if (!fp) return 0;
|
||||
|
||||
int transparent_pix = -1;
|
||||
|
||||
bool had_image = false;
|
||||
bool had_delay = false;
|
||||
bool need_new_frame = false;
|
||||
|
||||
const bool own_bmp = !bmp;
|
||||
GifRecordType RecordType;
|
||||
GifByteType *Extension;
|
||||
int ExtCode;
|
||||
LICE_WrapperBitmap workbm(NULL,0,0,0,false);
|
||||
WDL_HeapBuf workbuf(128*1024);
|
||||
|
||||
|
||||
do
|
||||
{
|
||||
if (DGifGetRecordType(fp, &RecordType) == GIF_ERROR) break;
|
||||
|
||||
switch (RecordType)
|
||||
{
|
||||
case IMAGE_DESC_RECORD_TYPE:
|
||||
if (DGifGetImageDesc(fp) == GIF_ERROR)
|
||||
{
|
||||
RecordType = TERMINATE_RECORD_TYPE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bmp) bmp=new WDL_NEW LICE_MemBitmap;
|
||||
|
||||
if (!had_image || (nframes && need_new_frame))
|
||||
{
|
||||
if (had_image)
|
||||
{
|
||||
// implies nframes
|
||||
const int ht = (*nframes+1) * (int)fp->SHeight;
|
||||
workbm.m_h = ht;
|
||||
workbm.m_w = (int)fp->SWidth;
|
||||
workbm.m_span = (workbm.m_w+3)&~3;
|
||||
workbm.m_buf = (LICE_pixel *) workbuf.ResizeOK(ht * sizeof(LICE_pixel) * workbm.m_span);
|
||||
if (!workbm.m_buf)
|
||||
{
|
||||
RecordType = TERMINATE_RECORD_TYPE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bmp) bmp->resize(fp->SWidth,fp->SHeight);
|
||||
|
||||
if (!bmp || bmp->getWidth() != (int)fp->SWidth || bmp->getHeight() != (int)fp->SHeight)
|
||||
{
|
||||
RecordType = TERMINATE_RECORD_TYPE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (nframes)
|
||||
{
|
||||
if (*nframes > 0)
|
||||
{
|
||||
if (*nframes == 1)
|
||||
LICE_Blit(&workbm,bmp,0,0,0,0,bmp->getWidth(),bmp->getHeight(),1.0,LICE_BLIT_MODE_COPY);
|
||||
|
||||
LICE_Blit(&workbm,&workbm,0,workbm.getHeight()-(int)fp->SHeight,0,
|
||||
workbm.getHeight()-(int)fp->SHeight*2, workbm.getWidth(), (int)fp->SHeight,1.0f,LICE_BLIT_MODE_COPY);
|
||||
}
|
||||
|
||||
*nframes += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if (nframes)
|
||||
{
|
||||
LICE_SubBitmap tmp(&workbm, 0,workbm.getHeight() - (int) fp->SHeight, workbm.getWidth(), (int)fp->SHeight);
|
||||
applyGifFrameToBitmap(&tmp,fp,transparent_pix,!had_image);
|
||||
}
|
||||
else
|
||||
applyGifFrameToBitmap(bmp,fp,transparent_pix,!had_image);
|
||||
|
||||
had_image = true;
|
||||
|
||||
need_new_frame = false;
|
||||
transparent_pix = -1;
|
||||
|
||||
if (had_delay)
|
||||
{
|
||||
if (!nframes)
|
||||
{
|
||||
RecordType = TERMINATE_RECORD_TYPE; // finish up if first frame is finished
|
||||
}
|
||||
else
|
||||
{
|
||||
need_new_frame = true;
|
||||
}
|
||||
had_delay = false;
|
||||
}
|
||||
break;
|
||||
case EXTENSION_RECORD_TYPE:
|
||||
if (DGifGetExtension(fp, &ExtCode, &Extension) == GIF_ERROR)
|
||||
{
|
||||
RecordType = TERMINATE_RECORD_TYPE;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (Extension != NULL)
|
||||
{
|
||||
if (ExtCode == 0xF9 && *Extension >= 4)
|
||||
{
|
||||
transparent_pix = -1;
|
||||
if (Extension[1]&1)
|
||||
{
|
||||
transparent_pix = Extension[4];
|
||||
}
|
||||
if (Extension[2] || Extension[3]) had_delay=true;
|
||||
}
|
||||
|
||||
if (DGifGetExtensionNext(fp, &Extension) == GIF_ERROR)
|
||||
{
|
||||
RecordType = TERMINATE_RECORD_TYPE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TERMINATE_RECORD_TYPE:
|
||||
break;
|
||||
default: /* Should be traps by DGifGetRecordType. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (RecordType != TERMINATE_RECORD_TYPE);
|
||||
|
||||
DGifCloseFile(fp);
|
||||
|
||||
if (had_image)
|
||||
{
|
||||
if (workbm.getWidth()) LICE_Copy(bmp,&workbm);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
if (own_bmp) delete bmp;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
class LICE_GIFLoader
|
||||
{
|
||||
public:
|
||||
_LICE_ImageLoader_rec rec;
|
||||
LICE_GIFLoader()
|
||||
{
|
||||
rec.loadfunc = loadfunc;
|
||||
rec.get_extlist = get_extlist;
|
||||
rec._next = LICE_ImageLoader_list;
|
||||
LICE_ImageLoader_list = &rec;
|
||||
}
|
||||
|
||||
static LICE_IBitmap *loadfunc(const char *filename, bool checkFileName, LICE_IBitmap *bmpbase)
|
||||
{
|
||||
if (checkFileName)
|
||||
{
|
||||
const char *p=filename;
|
||||
while (*p)p++;
|
||||
while (p>filename && *p != '\\' && *p != '/' && *p != '.') p--;
|
||||
if (stricmp(p,".gif")) return 0;
|
||||
}
|
||||
return LICE_LoadGIF(filename,bmpbase,NULL);
|
||||
}
|
||||
static const char *get_extlist()
|
||||
{
|
||||
return "GIF files (*.GIF)\0*.GIF\0";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
LICE_GIFLoader LICE_gifldr;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
struct lice_gif_read_ctx
|
||||
{
|
||||
WDL_FileRead *fh;
|
||||
GifFileType *gif;
|
||||
int msecpos;
|
||||
int state;
|
||||
WDL_FILEREAD_POSTYPE ipos;
|
||||
};
|
||||
|
||||
|
||||
void *LICE_GIF_LoadEx(const char *filename)
|
||||
{
|
||||
WDL_FileRead *fpp = new WDL_FileRead(filename,0,32768);
|
||||
|
||||
if (!fpp->IsOpen())
|
||||
{
|
||||
delete fpp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
GifFileType *gif=DGifOpen(fpp, readfunc_fh);
|
||||
if (!gif)
|
||||
{
|
||||
delete fpp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
lice_gif_read_ctx *ret = (lice_gif_read_ctx*)calloc(sizeof(lice_gif_read_ctx), 1);
|
||||
if (!ret)
|
||||
{
|
||||
DGifCloseFile(gif);
|
||||
delete fpp;
|
||||
return NULL;
|
||||
}
|
||||
ret->fh = fpp;
|
||||
ret->gif = gif;
|
||||
ret->msecpos = 0;
|
||||
ret->state = 0;
|
||||
ret->ipos = fpp->GetPosition();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
void LICE_GIF_Close(void *handle)
|
||||
{
|
||||
lice_gif_read_ctx *h = (lice_gif_read_ctx*)handle;
|
||||
if (h)
|
||||
{
|
||||
DGifCloseFile(h->gif);
|
||||
delete h->fh;
|
||||
free(h);
|
||||
}
|
||||
}
|
||||
void LICE_GIF_Rewind(void *handle)
|
||||
{
|
||||
lice_gif_read_ctx *h = (lice_gif_read_ctx*)handle;
|
||||
if (h)
|
||||
{
|
||||
h->state = 0;
|
||||
h->msecpos = 0;
|
||||
h->fh->SetPosition(h->ipos);
|
||||
// todo: flush giflib too
|
||||
}
|
||||
}
|
||||
unsigned int LICE_GIF_GetFilePos(void *handle)
|
||||
{
|
||||
// only used for accounting at the moment, so meh
|
||||
lice_gif_read_ctx *h = (lice_gif_read_ctx*)handle;
|
||||
if (h && h->fh)
|
||||
{
|
||||
return (unsigned int) h->fh->GetPosition();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LICE_GIF_UpdateFrame(void *handle, LICE_IBitmap *bm)
|
||||
{
|
||||
lice_gif_read_ctx *h = (lice_gif_read_ctx*)handle;
|
||||
if (!h||h->state<0) return -2;
|
||||
GifFileType *fp = h->gif;
|
||||
|
||||
if (bm)
|
||||
{
|
||||
bm->resize(fp->SWidth, fp->SHeight);
|
||||
if (bm->getWidth() != (int)fp->SWidth || bm->getHeight() != (int)fp->SHeight) return -3;
|
||||
}
|
||||
|
||||
int has_delay = 0;
|
||||
int transparent_pix = -1;
|
||||
GifRecordType RecordType;
|
||||
GifByteType *Extension;
|
||||
int ExtCode;
|
||||
do
|
||||
{
|
||||
if (DGifGetRecordType(fp, &RecordType) == GIF_ERROR) return h->state = -5;
|
||||
switch (RecordType)
|
||||
{
|
||||
case IMAGE_DESC_RECORD_TYPE:
|
||||
if (DGifGetImageDesc(fp) == GIF_ERROR) return h->state = -4;
|
||||
|
||||
applyGifFrameToBitmap(bm,fp,transparent_pix,!h->state);
|
||||
h->state += 1;
|
||||
h->msecpos += has_delay*10;
|
||||
return has_delay*10;
|
||||
|
||||
case EXTENSION_RECORD_TYPE:
|
||||
if (DGifGetExtension(fp, &ExtCode, &Extension) == GIF_ERROR)
|
||||
{
|
||||
return h->state = -9;
|
||||
}
|
||||
else
|
||||
{
|
||||
while (Extension != NULL)
|
||||
{
|
||||
if (ExtCode == 0xF9 && *Extension >= 4)
|
||||
{
|
||||
transparent_pix = -1;
|
||||
if (Extension[1]&1)
|
||||
{
|
||||
transparent_pix = Extension[4];
|
||||
}
|
||||
has_delay = ((int)Extension[3] << 8) + Extension[2];
|
||||
if (has_delay == 1) has_delay = 10; // https://www.biphelps.com/blog/The-Fastest-GIF-Does-Not-Exist
|
||||
}
|
||||
|
||||
if (DGifGetExtensionNext(fp, &Extension) == GIF_ERROR) return h->state = -8;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case TERMINATE_RECORD_TYPE:
|
||||
break;
|
||||
default: /* Should be traps by DGifGetRecordType. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (RecordType != TERMINATE_RECORD_TYPE);
|
||||
|
||||
return h->state = -10;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user