April 29, 2024, 10:45:59 AM

News:

Own IWBasic 2.x ? -----> Get your free upgrade to 3.x now.........


Animated gif control

Started by sapero, July 23, 2006, 02:23:11 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

sapero

July 23, 2006, 02:23:11 PM Last Edit: July 25, 2006, 09:41:54 AM by sapero
This is a "rip" from ibasic forum, converted to more usable class.
Original project title: Animated gif to DirectX sprite converter.

Please update your includes, important change for this project: IStream::Seek - first argument should be pushed by value
IStream has been used as replacement for file storage (saving single gif frames).
Every frame is stored in image object (IPicture).

bugs:
- Frame timing is supported, but null time is not.
- Gif specific disposal method is not yet implemented (see what happends with opr.gif)

example:
class CGifDemo : CWindow
{

declare virtual OnClose()
{
Destroy();
}
}


global sub main()
{
CGifDemo win;
win.Create(100,100,550,100,AWS_OVERLAPPEDWINDOW|AWS_VISIBLE,0,"",null);


CGifControl gif1,gif2,gif3,gif4;

gif1.Create(0,0,0,0,AWS_VISIBLE|AWS_CHILD, 0,"",win);
gif1.LoadImage(GetStartPath()+"zmeczony.gif");
// resize gif window to gif size
gif1.SetSize(0,0,gif1.m_width, gif1.m_height);

gif2.Create(0,0,0,0,AWS_VISIBLE|AWS_CHILD, 0,"",win);
gif2.LoadImage(GetStartPath()+"download.gif");
gif2.SetSize(gif1.m_width+2,0,gif2.m_width, gif2.m_height);

gif3.Create(0,0,0,0,AWS_VISIBLE|AWS_CHILD, 0,"",win);
gif3.m_bkgndcolor = 0x808080;
gif3.LoadImage(GetStartPath()+"dancegirl.gif");
gif3.SetSize(gif1.m_width+gif2.m_width+4,0,gif3.m_width, gif3.m_height);

gif4.Create(0,0,0,0,AWS_VISIBLE|AWS_CHILD, 0,"",win);
gif4.LoadImage(GetStartPath()+"opr.gif");
// resize gif window to gif size
gif4.SetSize(0,gif1.m_height+2,gif4.m_width, gif4.m_height);

while (win.IsValid()) {wait();}
return;
}


the class:
/////////////////////////////////////////////////////////////////
// structure used as array for storing gif frames for animating
/////////////////////////////////////////////////////////////////

struct GIF_FRAME,1 // single array entry
{
IPicture *pic;
unsigned word left, top;
unsigned word wDelay; // * 10ms
unsigned byte disposalmethod, transparencyenabled;
}

struct GIF_FRAMES // container for GIF_FRAME array
{
GIF_FRAME frame[1];
}
#define GIFCTL_TIMER_ANIMATE 1

/////////////////////////////////////////////////////////////////
// temporary structure used to store gif frames data
// while scanning image (CGifControl::scangiffile),
// and to create GIF_FRAME array (CGifControl::CreateSprites)
/////////////////////////////////////////////////////////////////

struct GIF_DATASTOR
{
GIF_DATASTOR *next;
unsigned intÂÃ,  imagestartbyte;
unsigned intÂÃ,  imageendbyte;
unsigned byte disposalmethod;
unsigned byte transparencyenabled;
unsigned word imageleft;
unsigned word imagetop;
unsigned word wDelay;
}

/////////////////////////////////////////////////////////////////
// class based on "Animated gif to DirectX sprite converter.iba"
/////////////////////////////////////////////////////////////////

class CGifControl : CWindow
{
// public
declare LoadImage(string path),BOOL;

int m_bkgndcolor; // default background color
int m_numberofframes, m_currentframe;
unsigned word m_width, m_height;

// private
declare CGifControl();
declare _CGifControl();

GIF_FRAMESÂÃ,  ÂÃ, *m_lpframes;ÂÃ,  ÂÃ, // for animating
GIF_DATASTOR *m_lpDataStor; // for loading new gif

// drawing without flickering
HDCÂÃ,  ÂÃ,  ÂÃ, m_hdc; // cache dc for drawing
HBITMAP m_hbm; // original bitmap from m_hdc
declare CleanupGdi();
declare RenderFrame(int frame);
declare virtual OnTimer(int nIDEvent);
declare virtual OnPaint();

unsigned intÂÃ,  ÂÃ, m_canvascolor; // currently unused
unsigned byte *m_gifmem;
unsigned intÂÃ,  ÂÃ, m_gifmem_len;
unsigned intÂÃ,  ÂÃ, m_lastbyteininitialblocks;

declare ScanGifFile();ÂÃ,  ÂÃ,  // cretes GIF_DATASTOR list.
declare CreateSprites();ÂÃ,  // converts GIF_DATASTOR to GIF_FRAMES array

declare DestroyGifdataStor();
declare DestroyFrames();
declare virtual OnDestroy();
}



CGifControl::CGifControl()
{
m_bkgndcolor = 0xFFFFFF;
m_gifmem = null;
m_lpframes = null;
m_numberofframes = 0;
m_hdc = 0;
}



CGifControl::_CGifControl()
{
DestroyFrames();
CleanupGdi();
}



CGifControl::OnDestroy()
{
_CGifControl();
}



CGifControl::CleanupGdi()
{
if (m_hdc)
{
DeleteObject(SelectObject(m_hdc, m_hbm));
DeleteDC(m_hdc);
m_hdc = 0;
}
}



CGifControl::DestroyFrames()
{
if (m_gifmem)
{
StopTimer(GIFCTL_TIMER_ANIMATE);
delete m_gifmem;
}
if (m_lpframes)
{
for (int frame=0; frame<m_numberofframes; frame++)
{
IPicture *pic = m_lpframes->frame[frame].pic;
if (pic) pic->Release();
}
delete m_lpframes;
}
m_numberofframes = 0;
}



CGifControl::DestroyGifdataStor()
{
GIF_DATASTOR *lpDataStor = m_lpDataStor;

while (lpDataStor)
{
pointer pNext = lpDataStor->next;
delete lpDataStor;
lpDataStor = pNext;
}
}


/*
sub ::todo disposalmethod 3
*/
CGifControl::OnTimer(int nIDEvent),int
{
if (nIDEvent == GIFCTL_TIMER_ANIMATE && m_numberofframes )
{
// int prevframe = m_currentframe;

m_currentframe++;
if (m_currentframe < 0) m_currentframe = 0;
if (m_currentframe >= m_numberofframes) m_currentframe = 0;

if (m_lpframes)
{
StopTimer(GIFCTL_TIMER_ANIMATE);
if IsWindowVisible(m_hwnd)
{
GIF_FRAME *pFrame = m_lpframes->frame[m_currentframe];

if (pFrame->disposalmethod == 2)
{
RECT rc; ::GetClientRect(m_hwnd, &rc);
HBRUSH hbr = CreateSolidBrush(m_bkgndcolor);

FillRect(m_hdc, &rc, hbr);
DeleteObject(hbr);
}
RenderFrame(m_currentframe);
}
StartTimer(pFrame->wDelay * 10, GIFCTL_TIMER_ANIMATE);
}
}
}


CGifControl::RenderFrame(int frame)
{
if (m_lpframes && frame>=0 && frame<m_numberofframes)
{
GIF_FRAME *pFrame = m_lpframes->frame[m_currentframe];
OLE_XSIZE_HIMETRIC ole_w, ole_h;
#define OLEMUL 26.457142857142857142857142857143

// is frame.picture valid?
if (pFrame->pic)
{
pFrame->pic->get_Width(&ole_w);
pFrame->pic->get_Height(&ole_h);
// draw the image on memory dc
pFrame->pic->Render(m_hdc, pFrame->left, pFrame->top, ole_w/OLEMUL, ole_h/OLEMUL,0, ole_h, ole_w, -ole_h, null);
}
// then copy memory dc onto window
OnPaint();
}
}


CGifControl::OnPaint()
{
HDC dc = GetDC(m_hwnd);
BitBlt(dc, 0,0,m_width, m_height, m_hdc,0,0,SRCCOPY);
ReleaseDC(m_hwnd, dc);
}





CGifControl::CreateSprites()
{
// create m_numberofframes pictures
m_lpframes = new(GIF_FRAME, m_numberofframes);

if (!m_gifmem || !m_numberofframes || !m_lpDataStor || !m_lpframes)
{
// error!
if (m_gifmem) delete m_gifmem;
if (m_lpframes) delete m_lpframes;
m_numberofframes = 0;
DestroyGifdataStor();
return;
}

IStream *gifframeStream;
GIF_FRAMEÂÃ,  ÂÃ,  *pFrame = m_lpframes->frame;
GIF_DATASTOR *lpDataStor = m_lpDataStor;

for (int frameNr=0; frameNr<m_numberofframes; frameNr++)
{
if !CreateStreamOnHGlobal(0, true, &gifframeStream)
{
// copy frame data from temporary structure
pFrame->leftÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  = lpDataStor->imageleft;
pFrame->topÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ, = lpDataStor->imagetop;
pFrame->disposalmethodÂÃ,  ÂÃ,  ÂÃ,  = lpDataStor->disposalmethod;
pFrame->transparencyenabled = lpDataStor->transparencyenabled;
pFrame->wDelayÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  ÂÃ,  = lpDataStor->wDelay;

// change 10 to global delay
if (!pFrame->wDelay) pFrame->wDelay = 10;

// Copy initial blocks to file
gifframeStream->write(m_gifmem, m_lastbyteininitialblocks, null);

// Copy image data to file
int imgDataLen = lpDataStor->imageendbyte - lpDataStor->imagestartbyte + 1;
gifframeStream->write(m_gifmem + lpDataStor->imagestartbyte - 1, imgDataLen, null);

// Add end of file byte
byte onebyte = 0x3B;
gifframeStream->write(&onebyte, 1, null);

int StreamSize = m_lastbyteininitialblocks + imgDataLen + 1;

// set stream seek pointer to zero and create picture object from stream
LARGE_INTEGER liPos; // same as int64 with int union
liPos.QuadPart = 0;
gifframeStream->seek(liPos, STREAM_SEEK_SET, &liPos);

if OleLoadPicture(gifframeStream, StreamSize, false, _IID_IPicture, &pFrame->pic)
pFrame->pic = null;

gifframeStream->Release();
}
pFrame += sizeof(GIF_FRAME);
lpDataStor = lpDataStor->next;
if (!lpDataStor) break;
}
DestroyGifdataStor();
}






CGifControl::ScanGifFile()
{
unsigned int l_canvascolornumber, l_blocklength; // l - local
BOOL l_foundgraphicextnblock = false, l_finished = false;

unsigned int l_blockstartbyte=14;
unsigned int l_sizeofcolortable = 0;

m_widthÂÃ,  = *(unsigned word)(m_gifmem+6);
m_height = *(unsigned word)(m_gifmem+8);
unsigned byte l_onebyte = *(unsigned byte)(m_gifmem+10);

if (l_onebyte>127)
{
l_sizeofcolortable=3*2^(1+(l_onebyte&7));
//Find number of canvas colors
l_canvascolornumber = *(unsigned byte)(m_gifmem+11)&255;
//Find canvas color
int record = l_blockstartbyte+3*l_canvascolornumber;
m_canvascolor = *(int)(m_gifmem-1+record) & 0xFFFFFF;
}
l_blockstartbyte += l_sizeofcolortable;
// Store number of last byte in initial blocks
m_lastbyteininitialblocks = l_blockstartbyte-1;

GIF_DATASTOR *lpDataStor = m_lpDataStor;

do {
l_onebyte = *(unsigned byte)(m_gifmem-1+l_blockstartbyte);
switch (l_onebyte)
{
case 0x21:
// extension block
l_onebyte = *(unsigned byte)(m_gifmem+l_blockstartbyte);
switch (l_onebyte)
{
case 0xFF:
// application extension
l_blockstartbyte += (*(unsigned byte)(m_gifmem+l_blockstartbyte+1)+3);

//Read through the sub chunks
do { // Read the length of the sub chunk
l_onebyte = *(unsigned byte)(m_gifmem-1+l_blockstartbyte);
l_blockstartbyte += (*(unsigned byte)(m_gifmem-1+l_blockstartbyte)+1);
//Keep reading till find a sub chunk with length zero
} until (l_onebyte==0 || l_blockstartbyte>m_gifmem_len);

case 0xF9: // BLOCK_CONTROLEXT
lpDataStor->wDelay = *(unsigned word)(m_gifmem+l_blockstartbyte+3);
// graphic control extension
l_foundgraphicextnblock = true;
lpDataStor->imagestartbyte = l_blockstartbyte;
//Read number of bytes left in block
l_blocklength = *(unsigned byte)(m_gifmem+l_blockstartbyte+1)+4;
//Read packed byte
l_onebyte = *(unsigned byte)(m_gifmem+l_blockstartbyte+2);
lpDataStor->disposalmethod = (l_onebyte&28)>>2;
lpDataStor->transparencyenabled = l_onebyte&1;
//Skip to beginning of next block
l_blockstartbyte += l_blocklength;

case 0x1:
// plain text extension
l_blockstartbyte += (*(unsigned byte)(m_gifmem+l_blockstartbyte+1)+3);
//Read sub blocks
do { //Read the length of the sub chunk
l_onebyte = *(unsigned byte)(m_gifmem-1+l_blockstartbyte);
l_blockstartbyte += (l_onebyte+1);
//Keep reading till find a sub chunk with length zero
} until (l_onebyte==0 || l_blockstartbyte>m_gifmem_len);

l_foundgraphicextnblock = false;

case 0xFE:
// comment extension
l_blockstartbyte=l_blockstartbyte+2;
//Read sub blocks
do { //Read the length of the sub chunk
l_onebyte = *(unsigned byte)(m_gifmem-1+l_blockstartbyte);
l_blockstartbyte += (l_onebyte+1);
//Keep reading till find a sub chunk with length zero
} until (l_onebyte==0 || l_blockstartbyte>m_gifmem_len);

default:
l_finished = true;
}

// case 0x3B:
// trailer, BLOCK_TRAILER

case 0x2C:
// image data
//Read left and top position of image
lpDataStor->imageleft = *(unsigned word)(m_gifmem+l_blockstartbyte);
lpDataStor->imagetop = *(unsigned word)(m_gifmem+l_blockstartbyte+2);
//Read last byte in image descriptor block
l_onebyte = *(unsigned byte)(m_gifmem+l_blockstartbyte+8);
l_blockstartbyte += 10;
if (l_onebyte>127) {
//Local color table follows this block.Skip it
l_sizeofcolortable = 3*2^(1+(l_onebyte&7));
l_blockstartbyte += (l_sizeofcolortable);
}
l_blockstartbyte++; //skip image compression data byte
//Read image sub blocks
do { //Read the length of the sub chunk
l_onebyte = *(unsigned byte)(m_gifmem-1+l_blockstartbyte);
l_blockstartbyte += (l_onebyte+1);
//Keep reading till find a sub chunk with length zero
} until (l_onebyte==0 || l_blockstartbyte>m_gifmem_len);

lpDataStor->imageendbyte = l_blockstartbyte-1;
if (l_foundgraphicextnblock)
{
m_numberofframes++;
lpDataStor->next = new(GIF_DATASTOR, 1);
lpDataStor = lpDataStor->next;
if (!lpDataStor) l_finished = true; // out of memory
}
l_foundgraphicextnblock = false;

default:
l_finished = true;
}
} until l_finished;
}



CGifControl::LoadImage(string path),BOOL
{
m_currentframe = -1;
m_numberofframes = 0;

DestroyFrames();
CleanupGdi();

m_lpDataStor = new(GIF_DATASTOR, 1);
if (!m_lpDataStor)
return false;

HANDLE hFile = OpenFile(path, MODE_READ);
if (hFile == INVALID_HANDLE_VALUE)
return false;

m_gifmem_len = GetFilelength(hFile);
m_gifmem = new(byte, m_gifmem_len);

if (m_gifmem)
read(hFile, *(byte)m_gifmem, m_gifmem_len);

CloseFile(hFile);

if (m_gifmem)
{
// check gif signature
if ((strncmp(m_gifmem, "GIF87a", 6)!=0) && (strncmp(m_gifmem, "GIF89a", 6)!=0))
{
delete m_gifmem;
return false;
}

ScanGifFile();
CreateSprites();

// create cache dc
HDC dc = GetDC(m_hwnd);
m_hdc = CreateCompatibleDC(dc);
m_hbm = SelectObject(m_hdc, CreateCompatibleBitmap(dc, m_width, m_height));
ReleaseDC(m_hwnd, dc);
RECT rc = GetClientRect();
HBRUSH hbr = CreateSolidBrush(m_bkgndcolor);
FillRect(m_hdc, &rc, hbr);
DeleteObject(hbr);

OnTimer(GIFCTL_TIMER_ANIMATE);
return true;
}
return false;
}

kryton9

Downloaded  CGifControl.zip
Unzipped it into its own folder outside of Aurora
Loaded up the source file in Aurora
and compiled as a window

And it worked great!!

Thanks, nice example!!

sapero

July 25, 2006, 09:51:51 AM #2 Last Edit: July 25, 2006, 09:54:18 AM by sapero
Uploaded new version - fixed some small leaks and added converted cpp PictureExWnd class
from article "Add GIF-animation to your MFC and ATL projects with the help of CPictureEx and CPictureExWnd"
based on two projects - the first animates gifs using thread and event (original version),
the second project uses only timer, which needs less memory than thread, and loads images from resource.

The source includes extra made small include file with all nested api declarations ;)
The dancegirl.gif plays much faster with thread version ;D

kryton9

This one worked fine too :)

The girl was still dancing about the same speed on mine, nothing wrong with, just a young healthy dancer :)

I liked the pacman like object that eats the progress bar, I can see him being invisible and right when you think the
download process is done by watching the progress bar he appears and eats it back down to like 5% :)