Introduction
I decided to write a free and open source GIF static library/DLL, both for educational purposes and for my real needs to include some simple and animated GIFs in my applications.
Documentation
The Cover Sheet for the GIF89a Specification by CompuServe Incorporated is a comprehensive piece of information, and has been included in the attached source files.
Implementation
Based on the GIF89a Specification, the "brain" of the AniGIF custom control is the LoadGIF
procedure in the AniGIF.asm file from which I will present the most important parts:
Check if it is a valid GIF file, get its width and height, get packed fields, check whether there is a global color map, get the color resolution, the sort flag, the size of the global color table, and the background color index.
MOV EDI,lpRawData
MOV EAX,EDI
ADD EAX,dwRawDataSize
SUB EAX,2
MOV dwRawDataEnd,EAX
.If DWORD PTR [EDI]!='8FIG' || (WORD PTR [EDI+4]!='a7' && WORD PTR [EDI+4]!='a9')
Invoke MessageBox,hCtrl,Offset szErrorNotAValidSignature,Offset szControlName,MB_OK
JMP Done
.EndIf
ADD EDI,GIFSIGNATURELENGTH
XOR EAX,EAX
MOV AX,WORD PTR [EDI]
MOV [EBX].GIFDATA.Screen.right,EAX
ADD EDI,SCREENWIDTHLENGTH
MOV AX,WORD PTR [EDI]
MOV [EBX].GIFDATA.Screen.bottom,EAX
ADD EDI,SCREENHEIGHTLENGTH
MOV AL,BYTE PTR [EDI]
MOV DL,AL
AND DL,BIT8
SHR DL,7
MOV [EBX].GIFDATA.GlobalColorTableFlag,AL
MOV DL,AL
AND DL,(BIT5 OR BIT6 OR BIT7)
SHR DL,4
INC DL
MOV [EBX].GIFDATA.ColorResolution,DL
MOV DL,AL
AND DL,BIT4
.If DL
MOV [EBX].GIFDATA.GSortFlag,TRUE
.EndIf
MOV DL,AL
AND DL,(BIT1 OR BIT2 OR BIT3)
INC EDI
MOV AL,BYTE PTR [EDI]
MOV [EBX].GIFDATA.BackgroundColorIndex,AL
The next important thing to do is to keep moving forward in the file until we hit the Image Descriptor Block. Immediately following this block is the actual image data. We will copy from the start of the GIF until the end of the image data.
.While BYTE PTR [EDI] != IMAGESEPARATOR && EDI<dwRawDataEnd
.If BYTE PTR [EDI] == EXTENSIONINTRODUCER
MOV AL,BYTE PTR[EDI+1]
.If AL == APPLICATIONEXTENSION
ADD EDI,14
XOR EAX,EAX
MOV AL,BYTE PTR [EDI]
ADD EDI,EAX
.While BYTE PTR [EDI+1]!=0
MOV AL,BYTE PTR [EDI+1]
ADD EDI,EAX
INC EDI
.EndW
.ElseIf AL==COMMENTEXTENSION
ADD EDI,2
XOR EAX,EAX
MOV AL,BYTE PTR [EDI]
ADD EDI,EAX
.While BYTE PTR [EDI+1]!=0
MOV AL,BYTE PTR [EDI+1]
ADD EDI,EAX
INC EDI
.EndW
INC EDI
.ElseIf AL==GRAPHICCONTROLEXTENSION
MOV AL,BYTE PTR [EDI+2+1]
MOV DL,AL
AND DL,BIT5
.If DL
MOV [ESI].FRAME.DisposalMethod,BIT3
.EndIf
MOV DL,AL
AND DL,BIT4
.If DL
OR [ESI].FRAME.DisposalMethod,BIT2
.EndIf
MOV DL,AL
AND DL,BIT3
.If DL
OR [ESI].FRAME.DisposalMethod,BIT1
.EndIf
MOV DL,AL
AND DL,BIT2
.If DL
MOV [ESI].FRAME.UserInputFlag,TRUE
.EndIf
MOV DL,AL
AND DL,BIT1
.If DL
MOV [ESI].FRAME.TransparentColorFlag,TRUE
.EndIf
MOV AX,WORD PTR [EDI+2+2]
MOV [ESI].FRAME.DelayTime,AX
MOV AL,BYTE PTR [EDI+2+4]
MOV [ESI].FRAME.TransparentColorIndex,AL
ADD EDI,GRAPHICCONTROLEXTENSIONLENGTH
.ElseIf AL==PLAINTEXTEXTENSION
XOR EAX,EAX
MOV AL,BYTE PTR [EDI+14]
ADD EDI,EAX
.While BYTE PTR [EDI+1]!=0
MOV AL,BYTE PTR [EDI+1]
ADD EDI,EAX
INC EDI
.EndW
.EndIf
.EndIf
.If EDI>=dwRawDataEnd
JMP EndParse
.EndIf
INC EDI
.EndW
At this stage, we are able to create the individual GIF frames and store them in memory so that we will be using them in sequence to display the animation:
Invoke CoInitialize, NULL
Invoke GetDC, NULL
MOV compDC, EAX
Invoke CreateCompatibleDC, compDC
MOV tempDC, EAX
MOV rc.left,0
MOV rc.top,0
ADD dwRawDataSize,2
MOV ESI,lpFrames
MOV ECX,[EBX].GIFDATA.NumberOfFrames
MOV EAX,SizeOf FRAME
MUL ECX
MOV EDI,EAX
ADD EDI,ESI
.While ESI<EDI
Invoke HeapAlloc,hHeap,HEAP_ZERO_MEMORY,dwRawDataSize
MOV EBX,EAX
PUSH EBX
MOV ECX,lpFrames
MOV EDX,[ECX].FRAME.GifStart
SUB EDX,lpRawData
MOV dwBytesWritten,EDX
Invoke RtlMoveMemory,EBX,lpRawData,EDX
ADD EBX,dwBytesWritten
MOV EDX,[ESI].FRAME.GifStart
MOV ECX,[ESI].FRAME.GifEnd
SUB ECX,EDX
ADD dwBytesWritten,ECX
PUSH ECX
Invoke RtlMoveMemory,EBX,EDX,ECX
POP ECX
ADD EBX,ECX
ADD dwBytesWritten,1
Invoke RtlMoveMemory,EBX,Offset szZero,1
ADD EBX,1
ADD dwBytesWritten,2
Invoke RtlMoveMemory,EBX,Offset szDummyControlBlock,2
ADD EBX,2
ADD dwBytesWritten,1
Invoke RtlMoveMemory,EBX,Offset szTrailerBlock,1
POP EBX
Invoke CoTaskMemAlloc, dwBytesWritten
MOV pGlobal, EAX
Invoke RtlMoveMemory,pGlobal,EBX,dwBytesWritten
Invoke HeapFree,hHeap,0,EBX
Invoke CreateStreamOnHGlobal, pGlobal, TRUE, ADDR pStream
Invoke OleLoadPicture, pStream, NULL,TRUE, ADDR IID_IPicture, ADDR pPicture
LEA EAX, hmWidth
PUSH EAX
MOV EAX, pPicture
PUSH EAX
MOV EAX, [EAX]
CALL [EAX].IPicture.get_Width
LEA EAX, hmHeight
PUSH EAX
MOV EAX, pPicture
PUSH EAX
MOV EAX, [EAX]
CALL [EAX].IPicture.get_Height
Invoke GetDeviceCaps, compDC, LOGPIXELSX
Invoke MulDiv, hmWidth, EAX, HIMETRIC_INCH
MOV rc.right,EAX
Invoke GetDeviceCaps, compDC, LOGPIXELSY
Invoke MulDiv, hmHeight, EAX, HIMETRIC_INCH
MOV rc.bottom,EAX
XOR EAX, EAX
SUB EAX, hmHeight
MOV neghmHeight, EAX
Invoke CreateCompatibleBitmap, compDC, rc.right, rc.bottom
MOV [ESI].FRAME.hBitMap,EAX
Invoke SelectObject, tempDC, [ESI].FRAME.hBitMap
MOV OldBitmap, EAX
.If [ESI].FRAME.TransparentColorFlag
Invoke CreateSolidBrush,[ESI].FRAME.TransparentColor
PUSH EAX
Invoke FillRect,tempDC,ADDR rc,EAX
POP EAX
Invoke DeleteObject,EAX
.Else
Invoke GetWindowLong,hCtrl,0
Invoke CreateSolidBrush,[EAX].GIFDATA.BkColor
PUSH EAX
Invoke FillRect,tempDC,ADDR rc,EAX
CALL DeleteObject
.EndIf
PUSH NULL
PUSH neghmHeight
PUSH hmWidth
PUSH hmHeight
PUSH 0
PUSH rc.bottom
PUSH rc.right
PUSH 0
PUSH 0
PUSH tempDC
MOV EAX, pPicture
PUSH EAX
MOV EAX, [EAX]
CALL [EAX].IPicture.Render
Invoke SelectObject, tempDC, OldBitmap
MOV EAX, pStream
PUSH EAX
MOV EAX, [EAX]
CALL [EAX].IPicture.Release
MOV EAX, pPicture
PUSH EAX
MOV EAX, [EAX]
CALL [EAX].IPicture.Release
ADD ESI,SizeOf FRAME
.EndW
Invoke DeleteDC, tempDC
Invoke ReleaseDC,NULL,compDC
Invoke CoUninitialize
Final Note
We've managed to decode simple, animated GIFs by parsing the files from start to end while storing all the necessary information in memory. The rendering on the screen is the easy part, and can be studied from the WM_PAINT
handler of the AniGIFControlProc
. Feel free to ask any questions or make any suggestions you think will improve this project.
You can find the latest version from the WinAsm Studio site.
Edit
Swagler has implemented a Graphic Viewer in C using the AniGIF control. You can get it here.