mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-07-11 22:03:01 -06:00
Draw, plugin/gif: Animated GIF support (thanks Koldo!)
git-svn-id: svn://ultimatepp.org/upp/trunk@2490 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
parent
f1b3c62fd2
commit
fa96c2b294
4 changed files with 155 additions and 22 deletions
|
|
@ -31,6 +31,16 @@ int Raster::GetPageCount()
|
|||
return 1;
|
||||
}
|
||||
|
||||
int Raster::GetPageAspect(int page)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Raster::GetPageDelay(int page)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Raster::Line::Pick(pick_ Line& b)
|
||||
{
|
||||
data = b.data;
|
||||
|
|
|
|||
|
|
@ -118,6 +118,8 @@ public:
|
|||
virtual void SeekPage(int page);
|
||||
virtual int GetActivePage() const;
|
||||
virtual int GetPageCount();
|
||||
virtual int GetPageAspect(int n);
|
||||
virtual int GetPageDelay(int n);
|
||||
|
||||
virtual bool Create();
|
||||
virtual Size GetSize() = 0;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,12 @@ public:
|
|||
virtual const RasterFormat *GetFormat();
|
||||
virtual int GetPaletteCount();
|
||||
virtual RGBA *GetPalette();
|
||||
|
||||
virtual int GetPageCount();
|
||||
virtual int GetActivePage() const;
|
||||
virtual void SeekPage(int n);
|
||||
virtual int GetPageAspect(int n);
|
||||
virtual int GetPageDelay(int n);
|
||||
|
||||
private:
|
||||
bool Init();
|
||||
|
|
|
|||
|
|
@ -84,6 +84,8 @@ struct GifLocalInfo // subimage info
|
|||
int64 loc_fpos; // file address of this structure
|
||||
int64 lct_fpos; // file address of LCT
|
||||
int64 dat_fpos; // data position (1st byte after LCT)
|
||||
|
||||
int delay; // delay time
|
||||
};
|
||||
|
||||
bool GifLocalInfo::Load(Stream& stream)
|
||||
|
|
@ -92,6 +94,7 @@ bool GifLocalInfo::Load(Stream& stream)
|
|||
int c1;
|
||||
|
||||
transparent = -1;
|
||||
delay = 0;
|
||||
|
||||
while((c1 = stream.Get()) != 0x2C)
|
||||
if(c1 == 0x00)
|
||||
|
|
@ -102,7 +105,7 @@ bool GifLocalInfo::Load(Stream& stream)
|
|||
int64 next = stream.GetPos() + len;
|
||||
if(code == 0xF9) { // read transparent color index
|
||||
byte flags = stream.Get(); // packed fields
|
||||
stream.Get16le(); // delay time
|
||||
delay = stream.Get16le(); // delay time in 1/100 sec
|
||||
transparent = stream.Get();
|
||||
if(!(flags & 1))
|
||||
transparent = -1;
|
||||
|
|
@ -829,6 +832,8 @@ public:
|
|||
|
||||
bool Create();
|
||||
Raster::Line GetLine(int line);
|
||||
bool SeekPage(int page);
|
||||
void GetPagesData();
|
||||
const RasterFormat *GetFormat() const { return &format; }
|
||||
int GetPaletteCount() { return 256; }
|
||||
RGBA *GetPalette() { return palette; }
|
||||
|
|
@ -836,13 +841,21 @@ public:
|
|||
public:
|
||||
Size size;
|
||||
Raster::Info info;
|
||||
|
||||
struct Page {
|
||||
int64 beginPos;
|
||||
int delay;
|
||||
};
|
||||
Array<Page> pages;
|
||||
int page_index;
|
||||
int aspect; // aspect ratio * 1000, 0 = undefined
|
||||
|
||||
private:
|
||||
void ClearTable();
|
||||
bool LoadSubimage(int& transparent_index);
|
||||
bool LoadSubimage(int64 &beginPos, int &delay);
|
||||
bool GotoSubimage(int& transparent_index);
|
||||
bool FetchDataBlock();
|
||||
bool Fetch(byte *&cptr, const byte *cend);
|
||||
|
||||
|
||||
private:
|
||||
GIFRaster& owner;
|
||||
Stream& stream;
|
||||
|
|
@ -851,7 +864,7 @@ private:
|
|||
GifGlobalInfo ggi;
|
||||
Vector<byte> temp;
|
||||
bool first_row;
|
||||
|
||||
|
||||
static const int MAX_BITS = 12;
|
||||
static const int MAX_COUNT = 1 << MAX_BITS;
|
||||
static const int HASH_COUNT = 5331;
|
||||
|
|
@ -883,6 +896,7 @@ private:
|
|||
GIFRaster::Data::Data(GIFRaster& owner, Stream& stream)
|
||||
: owner(owner), stream(stream)
|
||||
{
|
||||
page_index = 0;
|
||||
}
|
||||
|
||||
GIFRaster::Data::~Data()
|
||||
|
|
@ -1071,13 +1085,14 @@ bool GIFRaster::Data::Fetch(byte *&cptr, const byte *cend)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool GIFRaster::Data::LoadSubimage(int& transparent_index)
|
||||
bool GIFRaster::Data::GotoSubimage(int& transparent_index)
|
||||
{
|
||||
// TIMING("GifEncoder::LoadSubimage");
|
||||
stream.Seek(pages[page_index].beginPos);
|
||||
|
||||
GifLocalInfo l;
|
||||
if(!l.Load(stream))
|
||||
return false;
|
||||
|
||||
|
||||
if(l.transparent >= 0)
|
||||
transparent_index = l.transparent;
|
||||
|
||||
|
|
@ -1095,6 +1110,62 @@ bool GIFRaster::Data::LoadSubimage(int& transparent_index)
|
|||
old_byte = 0;
|
||||
ClearTable();
|
||||
|
||||
int cache = max(size.cx, 1000);
|
||||
Buffer<byte> data(cache + MAX_COUNT);
|
||||
bool more = true;
|
||||
byte *data_ptr = data, *data_end = data;
|
||||
int pass = 0;
|
||||
for(int y = 0; y < size.cy;) {
|
||||
if(more && data_ptr + size.cx > data_end) { // fetch more data
|
||||
if(data_end > data_ptr)
|
||||
Copy((byte *)data, data_ptr, data_end);
|
||||
data_end -= (data_ptr - data);
|
||||
data_ptr = data;
|
||||
more = Fetch(data_end, data + cache);
|
||||
}
|
||||
int avail = min(size.cx, (int)intptr_t(data_end - data_ptr));
|
||||
if(avail == 0)
|
||||
break;
|
||||
byte *wrt = &temp[y * size.cx];
|
||||
memcpy(wrt, data_ptr, avail);
|
||||
data_ptr += avail;
|
||||
if(l.interlace)
|
||||
switch(pass) {
|
||||
case 0: if((y += 8) < size.cy) break; y = 4 - 8; pass++;
|
||||
case 1: if((y += 8) < size.cy) break; y = 2 - 4; pass++;
|
||||
case 2: if((y += 4) < size.cy) break; y = 1 - 2; pass++;
|
||||
case 3: y += 2; break;
|
||||
}
|
||||
else
|
||||
y++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool GIFRaster::Data::LoadSubimage(int64 &beginPos, int &delay)
|
||||
{
|
||||
beginPos = stream.GetPos();
|
||||
delay = 0;
|
||||
GifLocalInfo l;
|
||||
if(!l.Load(stream))
|
||||
return false;
|
||||
|
||||
delay = l.delay;
|
||||
|
||||
if(l.x + l.width > size.cx || l.y + l.height > size.cy)
|
||||
return false; // out of bitmap rectangle
|
||||
|
||||
// ignore LCT
|
||||
stream.Seek(l.dat_fpos);
|
||||
|
||||
if((start_bpp = stream.Get()) <= 0 || start_bpp > 12)
|
||||
return false;
|
||||
|
||||
data_ptr = data_end = data_block; // empty data block
|
||||
old_shift = 8;
|
||||
old_byte = 0;
|
||||
ClearTable();
|
||||
|
||||
int cache = max(size.cx, 1000);
|
||||
Buffer<byte> data(cache + MAX_COUNT);
|
||||
bool more = true;
|
||||
|
|
@ -1153,28 +1224,47 @@ bool GIFRaster::Data::Create()
|
|||
palette[i].a = 255;
|
||||
}
|
||||
format.Set8();
|
||||
GetPagesData();
|
||||
|
||||
SeekPage(1);
|
||||
return true;
|
||||
}
|
||||
|
||||
Raster::Line GIFRaster::Data::GetLine(int line)
|
||||
{
|
||||
if(first_row) {
|
||||
first_row = false;
|
||||
temp.SetCount(size.cx * size.cy, 0);
|
||||
|
||||
stream.Seek(ggi.loc_fpos);
|
||||
int transparent_index = -1;
|
||||
while(LoadSubimage(transparent_index))
|
||||
;
|
||||
if(stream.Term() == 0x3B)
|
||||
stream.Get();
|
||||
|
||||
if(transparent_index >= 0 && transparent_index < 256)
|
||||
palette[transparent_index] = RGBAZero();
|
||||
}
|
||||
return Raster::Line(&temp[size.cx * line], &owner, false);
|
||||
}
|
||||
|
||||
bool GIFRaster::Data::SeekPage(int npage) {
|
||||
page_index = npage;
|
||||
|
||||
stream.Seek(ggi.loc_fpos);
|
||||
int transparent_index = -1;
|
||||
if(!GotoSubimage(transparent_index))
|
||||
return false;
|
||||
|
||||
if(transparent_index >= 0 && transparent_index < 256)
|
||||
palette[transparent_index] = RGBAZero();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GIFRaster::Data::GetPagesData() {
|
||||
temp.SetCount(size.cx * size.cy, 0);
|
||||
|
||||
stream.Seek(ggi.loc_fpos);
|
||||
while(true) {
|
||||
int64 beginPos;
|
||||
int delay;
|
||||
if(!LoadSubimage(beginPos, delay)) {
|
||||
return;
|
||||
}
|
||||
Page &page = pages.Add();
|
||||
page.beginPos = beginPos;
|
||||
page.delay = delay;
|
||||
}
|
||||
}
|
||||
|
||||
GIFRaster::GIFRaster()
|
||||
{
|
||||
}
|
||||
|
|
@ -1219,6 +1309,31 @@ RGBA *GIFRaster::GetPalette()
|
|||
return data->GetPalette();
|
||||
}
|
||||
|
||||
int GIFRaster::GetPageCount()
|
||||
{
|
||||
return data->pages.GetCount();
|
||||
}
|
||||
|
||||
int GIFRaster::GetActivePage() const
|
||||
{
|
||||
return data->page_index;
|
||||
}
|
||||
|
||||
void GIFRaster::SeekPage(int n)
|
||||
{
|
||||
data->SeekPage(n);
|
||||
}
|
||||
|
||||
int GIFRaster::GetPageAspect(int n)
|
||||
{
|
||||
return data->aspect;
|
||||
}
|
||||
|
||||
int GIFRaster::GetPageDelay(int n)
|
||||
{
|
||||
return data->pages[n].delay;
|
||||
}
|
||||
|
||||
class GIFEncoder::Data {
|
||||
public:
|
||||
Data(Stream& stream, RasterFormat& format);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue