add oversampler

This commit is contained in:
2024-05-24 13:28:31 +02:00
parent e4a4a661a0
commit 989dba5a6b
484 changed files with 313937 additions and 0 deletions

View File

@@ -0,0 +1,491 @@
/*
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 <stdio.h>
#include "../wdltypes.h"
#include "../filewrite.h"
extern "C" {
#include "../giflib/gif_lib.h"
//int _GifError;
};
struct liceGifWriteRec
{
GifFileType *f;
WDL_FileWrite *fh;
ColorMapObject *cmap;
GifPixelType *linebuf;
LICE_IBitmap *prevframe; // used when multiframe, transalpha<0
void *last_octree;
LICE_pixel last_palette[256];
unsigned char from15to8bit[32][32][32];//r,g,b
int transalpha;
int w,h;
bool append;
bool dither;
bool has_had_frame;
bool has_global_cmap;
bool has_from15to8bit; // set when last_octree has been generated into from15to8bit
};
static inline GifPixelType QuantPixel(LICE_pixel p, liceGifWriteRec *wr)
{
return wr->from15to8bit[LICE_GETR(p)>>3][LICE_GETG(p)>>3][LICE_GETB(p)>>3];
}
static int generate_palette_from_octree(void *ww, void *octree, int numcolors)
{
liceGifWriteRec *wr = (liceGifWriteRec *)ww;
if (!octree||!ww||numcolors>256) return 0;
ColorMapObject *cmap = wr->cmap;
int palette_sz=0;
// store palette
{
LICE_pixel* palette=wr->last_palette;
palette_sz = LICE_ExtractOctreePalette(octree, palette);
int i;
for (i = 0; i < palette_sz; ++i)
{
cmap->Colors[i].Red = LICE_GETR(palette[i]);
cmap->Colors[i].Green = LICE_GETG(palette[i]);
cmap->Colors[i].Blue = LICE_GETB(palette[i]);
}
for (i = palette_sz; i < numcolors; ++i)
{
cmap->Colors[i].Red = cmap->Colors[i].Green = cmap->Colors[i].Blue = 0;
}
}
wr->has_from15to8bit = false;
wr->has_global_cmap=true;
return palette_sz;
}
static void generate15to8(void *ww, void *octree)
{
liceGifWriteRec *wr = (liceGifWriteRec *)ww;
if (!octree||!ww) return;
// map palette to 16 bit
unsigned char r,g,b;
for(r=0;r<32;r++)
{
unsigned char cr = r<<3;
for (g=0;g<32;g++)
{
unsigned char cg = g<<3;
for (b=0;b<32;b++)
{
unsigned char cb = b<<3;
LICE_pixel col = LICE_RGBA(cr,cg,cb,0);
wr->from15to8bit[r][g][b] = LICE_FindInOctree(octree, col);
}
}
}
wr->has_from15to8bit=true;
}
int LICE_SetGIFColorMapFromOctree(void *ww, void *octree, int numcolors)
{
const int rv = generate_palette_from_octree(ww,octree,numcolors);
generate15to8(ww,octree);
return rv;
}
unsigned int LICE_WriteGIFGetSize(void *handle)
{
if (handle)
{
liceGifWriteRec *wr = (liceGifWriteRec*)handle;
if (wr->fh) return (unsigned int) ((WDL_FileWrite *)wr->fh)->GetPosition();
}
return 0;
}
bool LICE_WriteGIFFrame(void *handle, LICE_IBitmap *frame, int xpos, int ypos, bool perImageColorMap, int frame_delay, int nreps)
{
liceGifWriteRec *wr = (liceGifWriteRec*)handle;
if (!wr) return false;
bool isFirst=false;
if (!wr->has_had_frame)
{
wr->has_had_frame=true;
isFirst=true;
if (!perImageColorMap && !wr->has_global_cmap)
{
const int ccnt = 256 - (wr->transalpha?1:0);
void* octree = wr->last_octree;
if (!octree) wr->last_octree = octree = LICE_CreateOctree(ccnt);
else LICE_ResetOctree(octree,ccnt);
if (octree)
{
LICE_BuildOctree(octree, frame);
// sets has_global_cmap
int pcnt = generate_palette_from_octree(wr, octree, ccnt);
if (pcnt < 256 && wr->transalpha) pcnt++;
int nb = 1;
while (nb < 8 && (1<<nb) < pcnt) nb++;
wr->cmap->ColorCount = 1<<nb;
wr->cmap->BitsPerPixel=nb;
}
}
if (!wr->append) EGifPutScreenDesc(wr->f,wr->w,wr->h,8,0,wr->has_global_cmap ? wr->cmap : 0);
}
int usew=frame->getWidth(), useh=frame->getHeight();
if (xpos+usew > wr->w) usew = wr->w-xpos;
if (ypos+useh > wr->h) useh = wr->h-ypos;
if (usew<1||useh<1) return false;
int pixcnt=usew*useh;
const int trans_chan_mask = wr->transalpha&0xff; // -1 means 0xff by default, user can change this accordingly
const LICE_pixel trans_mask = LICE_RGBA(trans_chan_mask,trans_chan_mask,trans_chan_mask,0);
const bool advanced_trans_stats = !!(wr->transalpha&0x100);
if (perImageColorMap && !wr->has_global_cmap)
{
const int ccnt = 256 - (wr->transalpha?1:0);
void* octree = wr->last_octree;
if (!octree) wr->last_octree = octree = LICE_CreateOctree(ccnt);
else LICE_ResetOctree(octree,ccnt);
if (octree)
{
if ((!isFirst || frame_delay) && wr->transalpha<0 && wr->prevframe)
{
LICE_SubBitmap tmpprev(wr->prevframe, xpos, ypos, usew, useh);
int pc=LICE_BuildOctreeForDiff(octree,frame,&tmpprev,trans_mask);
if (!advanced_trans_stats) pixcnt = pc;
}
else if (wr->transalpha>0)
pixcnt=LICE_BuildOctreeForAlpha(octree, frame,wr->transalpha&0xff);
else
LICE_BuildOctree(octree, frame);
// sets has_global_cmap (clear below)
int pcnt = generate_palette_from_octree(wr, octree, ccnt);
wr->has_global_cmap=false;
if (pcnt < 256 && wr->transalpha) pcnt++;
int nb = 1;
while (nb < 8 && (1<<nb) < pcnt) nb++;
wr->cmap->ColorCount = 1<<nb;
wr->cmap->BitsPerPixel=nb;
}
}
if (!wr->has_from15to8bit && pixcnt > 40000 && wr->last_octree)
{
generate15to8(wr,wr->last_octree);
}
const unsigned char transparent_pix = wr->cmap->ColorCount-1;
unsigned char gce[4] = { 0, };
if (wr->transalpha)
{
gce[0] |= 1;
gce[3] = transparent_pix;
}
int a = frame_delay/10;
if(a<2&&frame_delay)a=2; // https://www.biphelps.com/blog/The-Fastest-GIF-Does-Not-Exist
else if (a>60000) a=60000;
gce[1]=(a)&255;
gce[2]=(a)>>8;
if (isFirst && frame_delay && nreps!=1 && !wr->append)
{
int nr = nreps > 1 && nreps <= 65536 ? nreps-1 : 0;
unsigned char ext[]={0xB, 'N','E','T','S','C','A','P','E','2','.','0',3,1,(unsigned char) (nr&0xff), (unsigned char) ((nr>>8)&0xff)};
EGifPutExtension(wr->f,0xFF, sizeof(ext),ext);
}
if (gce[0]||gce[1]||gce[2])
EGifPutExtension(wr->f, 0xF9, sizeof(gce), gce);
EGifPutImageDesc(wr->f, xpos, ypos, usew,useh, 0, wr->has_global_cmap ? NULL : wr->cmap);
GifPixelType *linebuf = wr->linebuf;
int y;
void *use_octree = wr->has_from15to8bit ? NULL : wr->last_octree;
if ((!isFirst || frame_delay) && wr->transalpha<0)
{
bool ignFr=false;
if (!wr->prevframe)
{
ignFr=true;
wr->prevframe = new WDL_NEW LICE_MemBitmap(wr->w,wr->h);
LICE_Clear(wr->prevframe,0);
}
LICE_SubBitmap tmp(wr->prevframe,xpos,ypos,usew,useh);
LICE_pixel last_pixel_rgb=0;
GifPixelType last_pixel_idx=transparent_pix;
int pix_stats[256];
if (advanced_trans_stats) memset(pix_stats,0,sizeof(pix_stats));
pix_stats[transparent_pix] = -8;
for(y=0;y<useh;y++)
{
int rdy=y,rdy2=y;
if (frame->isFlipped()) rdy = frame->getHeight()-1-y;
if (tmp.isFlipped()) rdy2 = tmp.getHeight()-1-y;
const LICE_pixel *in = frame->getBits() + rdy*frame->getRowSpan();
const LICE_pixel *in2 = tmp.getBits() + rdy2*tmp.getRowSpan();
int x;
if (advanced_trans_stats)
{
if (use_octree) for(x=0;x<usew;x++)
{
const LICE_pixel p = in[x]&trans_mask;
if (last_pixel_idx == transparent_pix || last_pixel_rgb!=p)
{
if (ignFr || p != (in2[x]&trans_mask)) last_pixel_idx = LICE_FindInOctree(use_octree,p);
else
{
const GifPixelType np = LICE_FindInOctree(use_octree,p);
if (p != (wr->last_palette[np]&trans_mask) || pix_stats[transparent_pix] > pix_stats[np])
last_pixel_idx = transparent_pix;
else
last_pixel_idx = np;
}
}
linebuf[x] = last_pixel_idx;
pix_stats[last_pixel_idx]++;
last_pixel_rgb = p;
}
else for(x=0;x<usew;x++)
{
const LICE_pixel p = in[x]&trans_mask;
if (last_pixel_idx == transparent_pix || last_pixel_rgb!=p)
{
if (ignFr || p != (in2[x]&trans_mask)) last_pixel_idx = QuantPixel(p,wr);
else
{
const GifPixelType np = QuantPixel(p,wr);
if (p != (wr->last_palette[np]&trans_mask) || pix_stats[transparent_pix] > pix_stats[np])
last_pixel_idx = transparent_pix;
else
last_pixel_idx = np;
}
}
linebuf[x] = last_pixel_idx;
pix_stats[last_pixel_idx]++;
last_pixel_rgb = p;
}
}
else
{
// optimize solids by reusing the same value if previous rgb was the same, also avoid switching between
// from color to transparent if the color hasn't changed
if (use_octree) for(x=0;x<usew;x++)
{
const LICE_pixel p = in[x]&trans_mask;
if (last_pixel_idx == transparent_pix || last_pixel_rgb!=p)
{
if (ignFr || p != (in2[x]&trans_mask)) last_pixel_idx = LICE_FindInOctree(use_octree,last_pixel_rgb = p);
else last_pixel_idx = transparent_pix;
}
linebuf[x] = last_pixel_idx;
}
else for(x=0;x<usew;x++)
{
const LICE_pixel p = in[x]&trans_mask;
if (last_pixel_idx == transparent_pix || last_pixel_rgb!=p)
{
if (ignFr || p != (in2[x]&trans_mask)) last_pixel_idx = QuantPixel(last_pixel_rgb = p,wr);
else last_pixel_idx = transparent_pix;
}
linebuf[x] = last_pixel_idx;
}
}
EGifPutLine(wr->f, linebuf, usew);
}
LICE_Blit(&tmp,frame,0,0,0,0,usew,useh,1.0f,LICE_BLIT_MODE_COPY);
}
else if (wr->transalpha>0)
{
const unsigned int al = wr->transalpha&0xff;
for(y=0;y<useh;y++)
{
int rdy=y;
if (frame->isFlipped()) rdy = frame->getHeight()-1-y;
const LICE_pixel *in = frame->getBits() + rdy*frame->getRowSpan();
int x;
if (use_octree) for(x=0;x<usew;x++)
{
const LICE_pixel p = in[x];
if (LICE_GETA(p)<al) linebuf[x]=transparent_pix;
else linebuf[x] = LICE_FindInOctree(use_octree,p);
}
else for(x=0;x<usew;x++)
{
const LICE_pixel p = in[x];
if (LICE_GETA(p)<al) linebuf[x]=transparent_pix;
else linebuf[x] = QuantPixel(p,wr);
}
EGifPutLine(wr->f, linebuf, usew);
}
}
else for(y=0;y<useh;y++)
{
int rdy=y;
if (frame->isFlipped()) rdy = frame->getHeight()-1-y;
const LICE_pixel *in = frame->getBits() + rdy*frame->getRowSpan();
int x;
if (use_octree) for(x=0;x<usew;x++) linebuf[x] = LICE_FindInOctree(use_octree,in[x]);
else for(x=0;x<usew;x++) linebuf[x] = QuantPixel(in[x],wr);
EGifPutLine(wr->f, linebuf, usew);
}
return true;
}
static int writefunc_fh(GifFileType *fh, const GifByteType *buf, int sz)
{
return ((WDL_FileWrite *)fh->UserData)->Write(buf,sz);
}
void *LICE_WriteGIFBeginNoFrame(const char *filename, int w, int h, int transparent_alpha, bool dither, bool is_append)
{
WDL_FileWrite *fp = new WDL_FileWrite(filename,1,65536,16,16,is_append);
if (!fp->IsOpen())
{
delete fp;
return NULL;
}
EGifSetGifVersion("89a");
GifFileType *f = EGifOpen(fp,writefunc_fh);
if (!f)
{
delete fp;
return NULL;
}
liceGifWriteRec *wr = (liceGifWriteRec*)calloc(sizeof(liceGifWriteRec),1);
wr->f = f;
wr->fh = fp;
wr->append = is_append;
wr->dither = dither;
wr->w=w;
wr->h=h;
wr->cmap = (ColorMapObject*)calloc(sizeof(ColorMapObject)+256*sizeof(GifColorType),1);
wr->cmap->Colors = (GifColorType*)(wr->cmap+1);
wr->cmap->ColorCount=256;
wr->cmap->BitsPerPixel=8;
wr->has_had_frame=false;
wr->has_global_cmap=false;
wr->has_from15to8bit=false;
wr->last_octree=NULL;
wr->linebuf = (GifPixelType*)malloc(wr->w*sizeof(GifPixelType));
wr->transalpha = transparent_alpha;
return wr;
}
void *LICE_WriteGIFBegin(const char *filename, LICE_IBitmap *firstframe, int transparent_alpha, int frame_delay, bool dither, int nreps)
{
if (!firstframe) return NULL;
void *wr=LICE_WriteGIFBeginNoFrame(filename,firstframe->getWidth(),firstframe->getHeight(),transparent_alpha,dither);
if (wr) LICE_WriteGIFFrame(wr,firstframe,0,0,true,frame_delay,nreps);
return wr;
}
bool LICE_WriteGIFEnd(void *handle)
{
liceGifWriteRec *wr = (liceGifWriteRec*)handle;
if (!wr) return false;
int ret = EGifCloseFile(wr->f);
free(wr->linebuf);
free(wr->cmap);
if (wr->last_octree) LICE_DestroyOctree(wr->last_octree);
delete wr->prevframe;
delete wr->fh;
free(wr);
return ret!=GIF_ERROR;
}
bool LICE_WriteGIF(const char *filename, LICE_IBitmap *bmp, int transparent_alpha, bool dither)
{
// todo: alpha?
if (!bmp) return false;
int has_transparent = 0;
if (transparent_alpha>0)
{
int y=bmp->getHeight();
LICE_pixel *p = bmp->getBits();
int w = bmp->getWidth();
while (y--&&!has_transparent)
{
int x=w;
while(x--)
{
if (LICE_GETA(*p) < (unsigned int)transparent_alpha)
{
has_transparent=1;
break;
}
p++;
}
p+=bmp->getRowSpan()-w;
}
}
void *wr=LICE_WriteGIFBeginNoFrame(filename,bmp->getWidth(),bmp->getHeight(),has_transparent?transparent_alpha:0,dither);
if (!wr) return false;
LICE_WriteGIFFrame(wr,bmp,0,0,false,0);
return LICE_WriteGIFEnd(wr);
}