1048 lines
57 KiB
C
1048 lines
57 KiB
C
/**********************************************************************************************************************************/
|
|
/* Module : SCR2GIF.C */
|
|
/* Executable : SCR2GIF.EXE */
|
|
/* Version type : Standalone program */
|
|
/* Last changed : 25-05-1998 20:30 */
|
|
/* Update count : 2 */
|
|
/* OS type : Generic */
|
|
/* Description : Spectrum .SCR to compressed GIF87a convertor. */
|
|
/* Copyrights : GIF87a functions based on a source from Sverre H. Huseby, Bjoelsengt. 17, N-0468 Oslo, Norway 26-09-1992 */
|
|
/* Implementation taken from Workbench! v2.71.3, copyright (C) 1995-1998 ThunderWare Research Center */
|
|
/* GIF89a extensions added 25-05-1998, "NETSCAPE v2.0" compliant (web-based default) LOOPing */
|
|
/* */
|
|
/* The Graphics Interchange Format(c) is the Copyright property of CompuServe Incorporated. */
|
|
/* GIF(sm) is a Service Mark property of CompuServe Incorporated. */
|
|
/* */
|
|
/* Copyright (C) 1998 by M. van der Heide of ThunderWare Research Center */
|
|
/**********************************************************************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <malloc.h>
|
|
#include <errno.h>
|
|
#include <unistd.h>
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Add some variables types */
|
|
/**********************************************************************************************************************************/
|
|
|
|
typedef char bool;
|
|
typedef unsigned char byte;
|
|
typedef unsigned short word; /* (Must be 16-bit) */
|
|
typedef unsigned long dword; /* (Must be 32-bit) */
|
|
|
|
#ifndef TRUE
|
|
#define TRUE (bool)1
|
|
#define FALSE (bool)0
|
|
#endif
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Define the GIF return codes */
|
|
/**********************************************************************************************************************************/
|
|
|
|
#define GIF_OK 0x00 /* No errors */
|
|
#define GIF_ERRCREATE 0x01 /* Error creating GIF file */
|
|
#define GIF_ERRWRITE 0x02 /* Error writing to GIF file */
|
|
#define GIF_OUTMEM 0x03 /* Cannot allocate resources */
|
|
#define GIF_OUTMEM2 0x04 /* Cannot allocate resources */
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Define the static variables */
|
|
/**********************************************************************************************************************************/
|
|
|
|
static FILE *_OutFile; /* File to write to */
|
|
static char _GIFErrorMessage[50];
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Define the information to write a bit-file */
|
|
/**********************************************************************************************************************************/
|
|
|
|
static byte _Buffer[256];
|
|
static int _Index; /* Current byte in buffer */
|
|
static int _BitsLeft; /* Bits left to fill in current byte. These are right-justified */
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Define the information to maintain an LZW-string table */
|
|
/**********************************************************************************************************************************/
|
|
|
|
#define RES_CODES 2
|
|
|
|
#define HASH_FREE 0xFFFF
|
|
#define NEXT_FIRST 0xFFFF
|
|
|
|
#define MAXBITS 12
|
|
#define MAXSTR (1 << MAXBITS)
|
|
|
|
#define HASHSIZE 9973
|
|
#define HASHSTEP 2039
|
|
|
|
#define HASH(Index, Lastbyte) (((Lastbyte << 8) ^ Index) % HASHSIZE)
|
|
|
|
static byte *StrChr = NULL;
|
|
static word *StrNxt = NULL;
|
|
static word *StrHsh = NULL;
|
|
static word NumStrings;
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Define the information on GIF images */
|
|
/**********************************************************************************************************************************/
|
|
|
|
typedef struct
|
|
{
|
|
word LocalScreenWidth;
|
|
word LocalScreenHeight;
|
|
byte GlobalColourTableSize : 3;
|
|
byte SortFlag : 1;
|
|
byte ColourResolution : 3;
|
|
byte GlobalColourTableFlag : 1;
|
|
byte BackgroundColourIndex;
|
|
byte PixelAspectRatio;
|
|
} _ScreenDescriptor;
|
|
|
|
typedef struct
|
|
{
|
|
byte Separator;
|
|
word LeftPosition;
|
|
word TopPosition;
|
|
word Width;
|
|
word Height;
|
|
byte LocalColourTableSize : 3;
|
|
byte Reserved : 2;
|
|
byte SortFlag : 1;
|
|
byte InterlaceFlag : 1;
|
|
byte LocalColourTableFlag : 1;
|
|
} _ImageDescriptor;
|
|
|
|
typedef struct
|
|
{
|
|
byte ExtensionIntroducer;
|
|
byte GraphicControlLabel;
|
|
byte BlockSize;
|
|
byte TransparantColorFlag : 1;
|
|
byte UserInputFlag : 1;
|
|
byte DisposalMethod : 3;
|
|
byte Reserved : 3;
|
|
word DelayTime; /* BIG endian! */
|
|
byte TransparantColorIndex;
|
|
byte BlockTerminator;
|
|
} _GraphicControlExtension;
|
|
|
|
static byte _ApplicationExtensionHeader[19] =
|
|
{
|
|
0x21, /* GIF Extension Code */
|
|
0xFF, /* Application Extension Label */
|
|
0x0B, /* Length of Application Block */
|
|
'N', 'E', 'T', 'S', 'C', 'A', 'P', 'E', /* Application Name */
|
|
'2', '.', '0', /* Application ID */
|
|
0x03, /* Length of Data Sub-Block */
|
|
0x01, /* Sub-Block ID */
|
|
0x00, /* Loop Count higher value (big endian) */
|
|
0x00, /* Loop Count lower value (0 = infinite) */
|
|
0x00 /* Data Sub-Block Terminator */
|
|
};
|
|
|
|
static short _BitsPrPrimColour; /* Bits per primary colour */
|
|
static short _NumColours; /* Number of colours in colour table */
|
|
static byte *_ColourTable = NULL;
|
|
static word _ScreenHeight;
|
|
static word _ScreenWidth;
|
|
static word _ImageHeight;
|
|
static word _ImageWidth;
|
|
static word _ImageLeft;
|
|
static word _ImageTop;
|
|
static word _RelPixX;
|
|
static word _RelPixY;
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Define the static functions */
|
|
/**********************************************************************************************************************************/
|
|
|
|
static byte _Create (char *FileName);
|
|
static byte _Write (void *Buf, word Length);
|
|
static byte _WriteByte (byte B);
|
|
static byte _WriteWord (word W);
|
|
static void _Close (void);
|
|
|
|
static void _InitBitFile (void);
|
|
static int _ResetOutBitFile (void);
|
|
static int _WriteBits (short Bits, short NumBits);
|
|
|
|
static void _FreeStrtab (void);
|
|
static byte _AllocStrtab (void);
|
|
static word _AddCharString (word Index, byte B);
|
|
static word _FindCharString (word Index, byte B);
|
|
static void _ClearStrtab (short CodeSize);
|
|
|
|
static byte _LZW_Compress (short CodeSize, short (*GetPixelFunction)(short PixX, short PixY));
|
|
|
|
static short _BitsNeeded (word N);
|
|
static short (*_GetPixel) (short PixX, short PixY);
|
|
static short _InputByte (void);
|
|
static byte _WriteScreenDescriptor (_ScreenDescriptor *Sd);
|
|
static byte _WriteImageDescriptor (_ImageDescriptor *Id);
|
|
static byte _WriteGraphicControlExtension (_GraphicControlExtension *Ext);
|
|
|
|
static byte _GIFCreate (char *FileName, short Width, short Height, short NumColours, short ColourRes,
|
|
bool GIF89a);
|
|
static void _GIFSetColour (byte ColourNum, byte Red, byte Green, byte Blue);
|
|
static byte _GIFWriteGlobalColorTable (bool GIF89a);
|
|
static byte _GIFCompressImage (short StartX, short StartY, int Width, int Height,
|
|
short (*GetPixelFunction)(short PixX, short PixY), bool GIF89a, word PictureDelayTime);
|
|
static byte _GIFClose (void);
|
|
static char *_GIFstrerror (byte ErrorCode);
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> START OF GENERIC LIBRARY FUNCTIONS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
|
|
/**********************************************************************************************************************************/
|
|
|
|
static byte _Create (char *FileName)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `FileName' points to the name of the file to be created. */
|
|
/* Post : Creates a new file and enables referencing using the global variable _OutFile. */
|
|
/* The return value is GIF_ERRCREATE if the file could not be created, GIF_OK otherwise. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
return (((_OutFile = fopen (FileName, "wb")) == NULL) ? GIF_ERRCREATE : GIF_OK);
|
|
}
|
|
|
|
static byte _Write (void *Buf, word Length)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `Buf' points to the buffer to be written, `Length' holds the length. */
|
|
/* Post : The buffer has been written to the _OutFile. */
|
|
/* The return value is GIF_ERRWRITE if a write error occured, GIF_OK otherwise. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
return ((fwrite (Buf, 1, Length, _OutFile) < Length) ? GIF_ERRWRITE : GIF_OK);
|
|
}
|
|
|
|
static byte _WriteByte (byte B)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `B' holds the byte to be written. */
|
|
/* Post : The byte has been written. The return value is GIF_ERRWRITE if an error occured, GIF_OK otherwise. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
return ((putc (B, _OutFile) == EOF) ? GIF_ERRWRITE : GIF_OK);
|
|
}
|
|
|
|
static byte _WriteWord (word W)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `W' holds the word to be written. */
|
|
/* Post : The word has been written BIG-endian. The return value is GIF_ERRWRITE if an error occured, GIF_OK otherwise. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
if (putc (W & 0xFF, _OutFile) == EOF)
|
|
return (GIF_ERRWRITE);
|
|
|
|
return ((putc ((W >> 8), _OutFile) == EOF) ? GIF_ERRWRITE : GIF_OK);
|
|
}
|
|
|
|
static void _Close (void)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : None. */
|
|
/* Post : Closes the _OutFile. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
fclose (_OutFile);
|
|
}
|
|
|
|
static void _InitBitFile (void)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : None. */
|
|
/* Post : Initiate when using a bitfile. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
_Buffer[_Index = 0] = 0;
|
|
_BitsLeft = 8;
|
|
}
|
|
|
|
static int _ResetOutBitFile (void)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : None. */
|
|
/* Post : Tidy up after using a bitfile. Returns 0 if no errors occured, -1 otherwise. */
|
|
/* Import: _WriteByte, _Write. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
byte NumBytes;
|
|
|
|
NumBytes = _Index + (_BitsLeft == 8 ? 0 : 1); /* Find out how much is in the buffer */
|
|
if (NumBytes) /* Write whatever is in the buffer to the file */
|
|
{
|
|
if (_WriteByte (NumBytes) != GIF_OK)
|
|
return (-1);
|
|
if (_Write (_Buffer, NumBytes) != GIF_OK)
|
|
return (-1);
|
|
_Buffer[_Index = 0] = 0;
|
|
_BitsLeft = 8;
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static int _WriteBits (short Bits, short NumBits)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : 'Bits' holds the (right justified) bits to be written, `NumBits' holds the number of bits to be written. */
|
|
/* Post : The given number of bits are written to the _OutFile. If an error occured, -1 is returned. */
|
|
/* Import: _WriteByte, _Write. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
int register BitsWritten = 0;
|
|
byte register NumBytes = 255;
|
|
|
|
do /* If the buffer is full, write it */
|
|
{
|
|
if ((_Index == 254 && !_BitsLeft) || _Index > 254)
|
|
{
|
|
if (_WriteByte (NumBytes) != GIF_OK)
|
|
return (-1);
|
|
if (_Write (_Buffer, NumBytes) != GIF_OK)
|
|
return (-1);
|
|
_Buffer[_Index = 0] = 0;
|
|
_BitsLeft = 8;
|
|
}
|
|
|
|
if (NumBits <= _BitsLeft) /* Now take care of the two special cases */
|
|
{
|
|
_Buffer[_Index] |= (Bits & ((1 << NumBits) - 1)) << (8 - _BitsLeft);
|
|
BitsWritten += NumBits;
|
|
_BitsLeft -= NumBits;
|
|
NumBits = 0;
|
|
}
|
|
else
|
|
{
|
|
_Buffer[_Index] |= (Bits & ((1 << _BitsLeft) - 1)) << (8 - _BitsLeft);
|
|
BitsWritten += _BitsLeft;
|
|
Bits >>= _BitsLeft;
|
|
NumBits -= _BitsLeft;
|
|
_Buffer[++ _Index] = 0;
|
|
_BitsLeft = 8;
|
|
}
|
|
} while (NumBits);
|
|
return (BitsWritten);
|
|
}
|
|
|
|
static void _FreeStrtab (void)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : None. */
|
|
/* Post : Free arrays used in string table routines. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
if (StrHsh)
|
|
{
|
|
free (StrHsh);
|
|
StrHsh = NULL;
|
|
}
|
|
if (StrNxt)
|
|
{
|
|
free (StrNxt);
|
|
StrNxt = NULL;
|
|
}
|
|
if (StrChr)
|
|
{
|
|
free (StrChr);
|
|
StrChr = NULL;
|
|
}
|
|
}
|
|
|
|
static byte _AllocStrtab (void)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : None. */
|
|
/* Post : Allocate arrays used in string table routines. Returns GIF_OUTMEM or GIF_OK. */
|
|
/* Import: _FreeStrtab. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
_FreeStrtab (); /* Just in case ... */
|
|
|
|
if ((StrNxt = (word *)malloc (MAXSTR * 2)) == NULL)
|
|
{
|
|
_FreeStrtab ();
|
|
return (GIF_OUTMEM2);
|
|
}
|
|
if ((StrChr = (byte *)malloc (MAXSTR)) == NULL)
|
|
{
|
|
_FreeStrtab ();
|
|
return (GIF_OUTMEM2);
|
|
}
|
|
if ((StrHsh = (word *)malloc (HASHSIZE * 2)) == NULL)
|
|
{
|
|
_FreeStrtab ();
|
|
return (GIF_OUTMEM2);
|
|
}
|
|
return (GIF_OK);
|
|
}
|
|
|
|
static word _AddCharString (word Index, byte B)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `Index' holds the index to the first part of the string, or 0xFFFF is only 1 byte is wanted, `B' holds the last byte */
|
|
/* in the new string. */
|
|
/* Post : Add a string consisting of the string of Index plus the byte B. The return value is 0xFFFF if room is not available. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
word register HshIdx;
|
|
|
|
if (NumStrings >= MAXSTR) /* Check if there is more room */
|
|
return 0xFFFF;
|
|
HshIdx = HASH (Index, B); /* Search the string table until a free position is found */
|
|
while (StrHsh[HshIdx] != 0xFFFF)
|
|
HshIdx = (HshIdx + HASHSTEP) % HASHSIZE;
|
|
StrHsh[HshIdx] = NumStrings; /* Insert new string */
|
|
StrChr[NumStrings] = B;
|
|
StrNxt[NumStrings] = (Index != 0xFFFF ? Index : NEXT_FIRST);
|
|
return (NumStrings ++);
|
|
}
|
|
|
|
static word _FindCharString (word Index, byte B)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `Index' holds the index to the first part of the string, or 0xFFFF is only 1 byte is wanted, `B' holds the last byte */
|
|
/* in the new string. */
|
|
/* Post : Find index of string consisting of the string of index plus the byte B. The return value is 0xFFFF if not found. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
word register HshIdx;
|
|
word register NxtIdx;
|
|
|
|
if (Index == 0xFFFF) /* Check if Index is 0xFFFF. In that case we need only return B, */
|
|
return (B); /* since all one-character strings has their bytevalue as their index */
|
|
HshIdx = HASH (Index, B); /* Search the string table until the string is found, or */
|
|
while ((NxtIdx = StrHsh[HshIdx]) != 0xFFFF) /* we find HASH_FREE. In that case the string does not exist. */
|
|
{
|
|
if (StrNxt[NxtIdx] == Index && StrChr[NxtIdx] == B)
|
|
return (NxtIdx);
|
|
HshIdx = (HshIdx + HASHSTEP) % HASHSIZE;
|
|
}
|
|
return (0xFFFF); /* No match is found */
|
|
}
|
|
|
|
static void _ClearStrtab (short CodeSize)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `CodeSize' holds the number of bits to encode one pixel. */
|
|
/* Post : Mark the entire table as free, enter the 2**CodeSize one-byte strings and reserve the RES_CODES reserved codes. */
|
|
/* Import: _AddCharString. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
int register Q;
|
|
int register W;
|
|
word register *Wp;
|
|
|
|
NumStrings = 0; /* No strings currently in the table */
|
|
Wp = StrHsh; /* Mark entire hashtable as free */
|
|
for (Q = 0 ; Q < HASHSIZE ; Q ++)
|
|
*Wp ++ = HASH_FREE;
|
|
W = (1 << CodeSize) + RES_CODES; /* Insert 2**CodeSize one-character strings, and reserved codes */
|
|
for (Q = 0 ; Q < W ; Q ++)
|
|
_AddCharString (0xFFFF, (byte)Q);
|
|
}
|
|
|
|
static byte _LZW_Compress (short CodeSize, short (*GetPixelFunction)(short PixX, short PixY))
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `CodeSize' holds the number of bits needed to represent one pixelvalue. */
|
|
/* `GetPixelFunction' points to a callback function used to fetch the color value of each pixel of the image. */
|
|
/* Post : Perform LZW compression as specified in the GIF87a standard. The return value is one of GIF_OK or GIF_OUTMEM. */
|
|
/* Import: _InitBitFile, _ClearStrtab, _WriteBits, _FindCharString, _AddCharString, _ResetOutBitFile, _FreeStrtab. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
word register Index;
|
|
short register C;
|
|
int ClearCode;
|
|
int EndOfInfo;
|
|
int NumBits;
|
|
int Limit;
|
|
int ErrCode;
|
|
word Prefix = 0xFFFF;
|
|
|
|
_GetPixel = GetPixelFunction;
|
|
_InitBitFile (); /* Set up the given _OutFile */
|
|
ClearCode = 1 << CodeSize; /* Set up variables and tables */
|
|
EndOfInfo = ClearCode + 1;
|
|
NumBits = CodeSize + 1;
|
|
Limit = (1 << NumBits) - 1;
|
|
if ((ErrCode = _AllocStrtab ()) != GIF_OK)
|
|
return (ErrCode);
|
|
_ClearStrtab (CodeSize);
|
|
_WriteBits (ClearCode, NumBits); /* First send a code telling the unpacker to clear the stringtable */
|
|
while ((C = _InputByte ()) != -1)
|
|
{ /* Now perform the packing */
|
|
if ((Index = _FindCharString (Prefix, (byte)C)) != 0xFFFF) /* Check if the prefix + the new character is a string that */
|
|
{ /* exists in the table */
|
|
Prefix = Index; /* The string exists in the table. Make this string the new prefix */
|
|
}
|
|
else
|
|
{ /* The string does not exist in the table */
|
|
_WriteBits (Prefix, NumBits); /* First write code of the old prefix to the file */
|
|
if (_AddCharString (Prefix, (byte)C) > Limit) /* Add the new string (the prefix + the new character) to the stringtable */
|
|
{
|
|
if (++ NumBits > 12)
|
|
{
|
|
_WriteBits (ClearCode, NumBits - 1);
|
|
_ClearStrtab (CodeSize);
|
|
NumBits = CodeSize + 1;
|
|
}
|
|
Limit = (1 << NumBits) - 1;
|
|
}
|
|
Prefix = C; /* Set prefix to a string containing only the character read. Since all possible one-character */
|
|
} /* strings exist in the table, there's no need to check if it is found */
|
|
}
|
|
if (Prefix != 0xFFFF) /* End of info is reached. Write last prefix */
|
|
_WriteBits (Prefix, NumBits);
|
|
_WriteBits (EndOfInfo, NumBits); /* Write end of info-mark */
|
|
_ResetOutBitFile (); /* Flush the buffer */
|
|
_FreeStrtab (); /* Tidy up */
|
|
return (GIF_OK);
|
|
}
|
|
|
|
static short _BitsNeeded (word N)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `N' holds the number of numbers to store (0 to (n - 1)). */
|
|
/* Post : Calculates the number of bits needed to store numbers between 0 and (n - 1). This number is returned. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
short Ret = 1;
|
|
|
|
if (!N --)
|
|
return (0);
|
|
while (N >>= 1)
|
|
Ret ++;
|
|
return (Ret);
|
|
}
|
|
|
|
static short _InputByte (void)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : None. */
|
|
/* Post : Get next pixel from the image. Called by the _LZW_Compress function. Returns next pixelvalue or -1 at the end. */
|
|
/* Import: _GetPixel. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
short Ret;
|
|
|
|
if (_RelPixY >= _ImageHeight)
|
|
return (-1);
|
|
Ret = _GetPixel (_ImageLeft + _RelPixX, _ImageTop + _RelPixY);
|
|
if (++ _RelPixX >= _ImageWidth)
|
|
{
|
|
_RelPixX = 0;
|
|
_RelPixY ++;
|
|
}
|
|
return (Ret);
|
|
}
|
|
|
|
static byte _WriteScreenDescriptor (_ScreenDescriptor *Sd)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `Sd' points to the screen descriptor to output. */
|
|
/* Post : Output a screen descriptor to the current GIF-file. Returns GIF_ERRWRITE if an error occured. */
|
|
/* Import: _WriteWord, _WriteByte. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
byte Tmp;
|
|
|
|
if (_WriteWord (Sd->LocalScreenWidth) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteWord (Sd->LocalScreenHeight) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
Tmp = (Sd->GlobalColourTableFlag << 7) | (Sd->ColourResolution << 4) | (Sd->SortFlag << 3) | Sd->GlobalColourTableSize;
|
|
if (_WriteByte (Tmp) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteByte (Sd->BackgroundColourIndex) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteByte (Sd->PixelAspectRatio) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
return (GIF_OK);
|
|
}
|
|
|
|
static byte _WriteImageDescriptor (_ImageDescriptor *Id)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `Id' points to the image descriptor to output. */
|
|
/* Post : Output an image descriptor to the current GIF-file. Returns GIF_ERRWRITE if an error occured. */
|
|
/* Import: _WriteWord, _WriteByte. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
byte Tmp;
|
|
|
|
if (_WriteByte (Id->Separator) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteWord (Id->LeftPosition) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteWord (Id->TopPosition) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteWord (Id->Width) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteWord (Id->Height) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
Tmp = (Id->LocalColourTableFlag << 7) | (Id->InterlaceFlag << 6) | (Id->SortFlag << 5) | (Id->Reserved << 3) |
|
|
Id->LocalColourTableSize;
|
|
if (_WriteByte (Tmp) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
return (GIF_OK);
|
|
}
|
|
|
|
static byte _WriteGraphicControlExtension (_GraphicControlExtension *Ext)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `Ext' points to the graphic control extension block to output. */
|
|
/* Post : Output a graphic control extension block to the current GIF-file. Returns GIF_ERRWRITE if an error occured. */
|
|
/* Import: _WriteWord, _WriteByte. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
byte Tmp;
|
|
|
|
if (_WriteByte (Ext->ExtensionIntroducer) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteByte (Ext->GraphicControlLabel) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteByte (Ext->BlockSize) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
Tmp = (Ext->Reserved << 5) | (Ext->DisposalMethod << 2) | (Ext->UserInputFlag << 1) | Ext->TransparantColorFlag;
|
|
if (_WriteByte (Tmp) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteWord (Ext->DelayTime) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteByte (Ext->TransparantColorIndex) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_WriteByte (Ext->BlockTerminator) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
return (GIF_OK);
|
|
}
|
|
|
|
static byte _GIFCreate (char *FileName, short Width, short Height, short NumColours, short ColourRes, bool GIF89a)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `FileName' points to the filename to create, `Width', `Height' hold the dimensions on the screen, `NumColours' holds */
|
|
/* the number of colours in the colourmaps, `ColourRes' holds the colour resolution - number of bits for each primary */
|
|
/* colour. `GIF89a' is TRUE is the GIF89a header should be written rather than the GIF87a header. */
|
|
/* Post : Create a GIF-file and write the GIF and screen header. The return value is one of: */
|
|
/* GIF_OK : Ok; */
|
|
/* GIF_ERRCREATE : Could not create file; */
|
|
/* GIF_ERRWRITE : Error writing to the file; */
|
|
/* GIF_OUTMEM : Could not allocate the colour table. */
|
|
/* Import: _Create, _Write, _BitsNeeded. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
int register Q;
|
|
int TabSize;
|
|
byte *Bp;
|
|
_ScreenDescriptor SD;
|
|
|
|
_NumColours = NumColours ? (1 << _BitsNeeded (NumColours)) : 0; /* Initiate variables for new GIF-file */
|
|
_BitsPrPrimColour = ColourRes;
|
|
_ScreenHeight = Height;
|
|
_ScreenWidth = Width;
|
|
if (_Create (FileName) != GIF_OK) /* Create file specified */
|
|
return (GIF_ERRCREATE);
|
|
if (GIF89a)
|
|
{
|
|
if ((_Write ("GIF89a", 6)) != GIF_OK) /* Write GIF signature */
|
|
return (GIF_ERRWRITE);
|
|
}
|
|
else if ((_Write ("GIF87a", 6)) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
SD.LocalScreenWidth = Width; /* Initiate and write screen descriptor */
|
|
SD.LocalScreenHeight = Height;
|
|
if (_NumColours)
|
|
{
|
|
SD.GlobalColourTableSize = _BitsNeeded (_NumColours) - 1;
|
|
SD.GlobalColourTableFlag = 1;
|
|
}
|
|
else
|
|
{
|
|
SD.GlobalColourTableSize = 0;
|
|
SD.GlobalColourTableFlag = 0;
|
|
}
|
|
SD.SortFlag = 0;
|
|
SD.ColourResolution = ColourRes - 1;
|
|
SD.BackgroundColourIndex = 0;
|
|
SD.PixelAspectRatio = 0;
|
|
if (_WriteScreenDescriptor (&SD) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (_ColourTable) /* Allocate colour table */
|
|
{
|
|
free (_ColourTable);
|
|
_ColourTable = NULL;
|
|
}
|
|
if (_NumColours)
|
|
{
|
|
TabSize = _NumColours * 3;
|
|
if ((_ColourTable = (byte *)malloc (TabSize)) == NULL)
|
|
return (GIF_OUTMEM);
|
|
else
|
|
{
|
|
Bp = _ColourTable;
|
|
for (Q = 0 ; Q < TabSize ; Q ++)
|
|
*Bp ++ = 0;
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
static void _GIFSetColour (byte ColourNum, byte Red, byte Green, byte Blue)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `ColourNum' holds the the colour index to set in range [0, _NumColours - 1], `Red', `Green', `Blue' hold the */
|
|
/* components. */
|
|
/* Post : Set red, green and blue component of one of the colours. They are all in the range [0, (1 << _BitsPrPrimColour) -1]. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
dword MaxColour;
|
|
byte *P;
|
|
|
|
MaxColour = (1L << _BitsPrPrimColour) - 1L;
|
|
P = _ColourTable + ColourNum * 3;
|
|
*P ++ = (byte)((Red * 255L) / MaxColour);
|
|
*P ++ = (byte)((Green * 255L) / MaxColour);
|
|
*P ++ = (byte)((Blue * 255L) / MaxColour);
|
|
}
|
|
|
|
static byte _GIFWriteGlobalColorTable (bool GIF89a)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `GIF89a' is TRUE if animated GIFs are written. Should be used after each successive _GETSetColor call. */
|
|
/* Post : Write the global color table to the GIF file. If animated GIFs are written, insert a LOOP control block as well. */
|
|
/* Import: _Write. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
if (_NumColours) /* Write global colour table if any */
|
|
if ((_Write (_ColourTable, _NumColours * 3)) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
if (GIF89a)
|
|
if ((_Write (&_ApplicationExtensionHeader, 19)) != GIF_OK) /* Write Netscape Loop Header */
|
|
return (GIF_ERRWRITE);
|
|
return (0);
|
|
}
|
|
|
|
static byte _GIFCompressImage (short StartX, short StartY, int Width, int Height, short (*GetPixelFunction)(short PixX, short PixY),
|
|
bool GIF89a, word PictureDelayTime)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `StartX', `StartY' holds the screen-relative pixel coordinates of the image, `Width', `Height' the dimensions. */
|
|
/* `GetPixelFunction' points to a callback function used to fetch the color value of each pixel of the image. */
|
|
/* `GIF89a' is TRUE is animated GIFs are written, in which case `PictureDelayTime' holds the delay time to write this */
|
|
/* image in 1/100'th second resolution. */
|
|
/* Post : Compress an image into the GIF-file previously created using _GIFCreate. All colour values should have been specified */
|
|
/* before this function is called. The pixels are retrieved using a user defined callback function. This function should */
|
|
/* accept two parameters, X and Y, specifying which pixel to retrieve. The pixel values sent to this function are as */
|
|
/* follows: X = [Left, Left + Width - 1], Y = [Top, Top + Height - 1]. The function should return the pixel value for the */
|
|
/* point given, in the interval [0, _NumColours - 1]. The return value is GIF_OK, GIF_OUTMEM or GIF_ERRWRITE. */
|
|
/* Import: _Write, _WriteImageDescriptor, _BitsNeeded, _LZW_Compress, _WriteByte. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
int CodeSize;
|
|
int ErrCode;
|
|
_ImageDescriptor ID;
|
|
_GraphicControlExtension Ext;
|
|
|
|
if (Width < 0)
|
|
{
|
|
Width = _ScreenWidth;
|
|
StartX = 0;
|
|
}
|
|
if (Height < 0)
|
|
{
|
|
Height = _ScreenHeight;
|
|
StartY = 0;
|
|
}
|
|
if (StartX < 0)
|
|
StartX = 0;
|
|
if (StartY < 0)
|
|
StartY = 0;
|
|
if (GIF89a)
|
|
{
|
|
Ext.ExtensionIntroducer = 0x21; /* Initiate and write graphic control extension */
|
|
Ext.GraphicControlLabel = 0xF9;
|
|
Ext.BlockSize = 4;
|
|
Ext.DisposalMethod = 0; /* (No disposal specified) */
|
|
Ext.UserInputFlag = 0; /* (No user input expected) */
|
|
Ext.TransparantColorFlag = 0; /* (No transparant colour) */
|
|
Ext.DelayTime = PictureDelayTime;
|
|
Ext.TransparantColorIndex = 0;
|
|
Ext.BlockTerminator = 0;
|
|
if (_WriteGraphicControlExtension (&Ext) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
}
|
|
ID.Separator = 0x2C; /* Initiate and write image descriptor */
|
|
ID.LeftPosition = _ImageLeft = StartX;
|
|
ID.TopPosition = _ImageTop = StartY;
|
|
ID.Width = _ImageWidth = Width;
|
|
ID.Height = _ImageHeight = Height;
|
|
ID.LocalColourTableSize = 0;
|
|
ID.Reserved = 0;
|
|
ID.SortFlag = 0;
|
|
ID.InterlaceFlag = 0;
|
|
ID.LocalColourTableFlag = 0;
|
|
if (_WriteImageDescriptor (&ID) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
CodeSize = _BitsNeeded (_NumColours); /* Write code size */
|
|
if (CodeSize == 1)
|
|
CodeSize ++;
|
|
if (_WriteByte ((byte)CodeSize) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
_RelPixX = _RelPixY = 0; /* Perform compression */
|
|
if ((ErrCode = _LZW_Compress (CodeSize, GetPixelFunction)) != GIF_OK)
|
|
return (ErrCode);
|
|
if (_WriteByte (0) != GIF_OK) /* Write terminating 0-byte */
|
|
return (GIF_ERRWRITE);
|
|
return (GIF_OK);
|
|
}
|
|
|
|
static byte _GIFClose (void)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : None. */
|
|
/* Post : Close the GIF-file. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
_ImageDescriptor ID;
|
|
|
|
ID.Separator = ';'; /* Initiate and write ending image descriptor */
|
|
if (_WriteImageDescriptor (&ID) != GIF_OK)
|
|
return (GIF_ERRWRITE);
|
|
_Close (); /* Close file */
|
|
_FreeStrtab (); /* Just in case ... */
|
|
if (_ColourTable) /* Release colour table */
|
|
{
|
|
free (_ColourTable);
|
|
_ColourTable = NULL;
|
|
}
|
|
return (GIF_OK);
|
|
}
|
|
|
|
static char *_GIFstrerror (byte ErrorCode)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `ErrorCode' holds the error code to be returned. */
|
|
/* Post : A string describing the error is returned. */
|
|
/* Import: None. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
switch (ErrorCode)
|
|
{
|
|
case GIF_OK : strcpy (_GIFErrorMessage, "(Call succesfull)"); break;
|
|
case GIF_ERRCREATE : strcpy (_GIFErrorMessage, "Error creating file"); break;
|
|
case GIF_ERRWRITE : strcpy (_GIFErrorMessage, "Error writing to file"); break;
|
|
case GIF_OUTMEM : strcpy (_GIFErrorMessage, "Out of memory"); break;
|
|
case GIF_OUTMEM2 : strcpy (_GIFErrorMessage, "Out of memory!"); break;
|
|
default : sprintf (_GIFErrorMessage, "Undefined error %02X", ErrorCode);
|
|
}
|
|
return (_GIFErrorMessage);
|
|
}
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> START OF MAIN PROGRAM <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
|
|
/**********************************************************************************************************************************/
|
|
|
|
dword SpecPalette[16] = { 0x00000000, 0x002F0000, 0x0000002F, 0x002F002F, 0x00002F00, 0x002F2F00, 0x00002F2F, 0x002A2A2A,
|
|
0x00000000, 0x003F0000, 0x0000003F, 0x003F003F, 0x00003F00, 0x003F3F00, 0x00003F3F, 0x003F3F3F };
|
|
|
|
byte ScreenBuffer[6912];
|
|
byte PreparedScreenData[49152U]; /* (Make room for 256*192 pixels) */
|
|
bool Use89aForFLASH = FALSE;
|
|
bool ScreenContainsFLASH;
|
|
bool ConvertTo89a;
|
|
|
|
short MyGetPixel (short PixX, short PixY)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : (`PixX', `PixY') are the coordinates of the requested pixel. */
|
|
/* (This is the callback function from _LZX_Compress) */
|
|
/* Post : The appropriate pixel colour value has been returned. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
return ((short)PreparedScreenData[PixY * 256 + PixX]);
|
|
}
|
|
|
|
byte SpectrumScreenToGIF (bool FLASHState)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : `FLASHState' is TRUE if animated GIFs are written and this is the FLASH 1 image. */
|
|
/* Post : The buffered SCR image has been inserted into the open GIF file. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
byte PaletteData;
|
|
byte PaletteINK;
|
|
byte PalettePAPER;
|
|
byte PixelData;
|
|
byte PixelMask;
|
|
byte ScreenBlockIndex;
|
|
word ScreenBlockCount;
|
|
word register PreparedDataIndex;
|
|
word register ScreenDataIndex;
|
|
word register PaletteIndex;
|
|
short register CntY; /* Vertical scanline counter */
|
|
short register CntX; /* Horizontal byte counter */
|
|
|
|
PaletteIndex = 6144; /* Start of the attributes area */
|
|
PreparedDataIndex = 0;
|
|
for (ScreenBlockCount = 0 ; ScreenBlockCount < 6144 ; ScreenBlockCount += 2048) /* A Spectrum screen is composed of 3 blocks */
|
|
for (CntY = 0 ; CntY < 256 ; CntY += 32) /* 8 character lines in each block */
|
|
{
|
|
ScreenDataIndex = ScreenBlockCount + CntY;
|
|
for (ScreenBlockIndex = 0 ; ScreenBlockIndex < 8 ; ScreenBlockIndex ++) /* Each character has 8 scan lines */
|
|
{
|
|
for (CntX = 0 ; CntX < 32 ; CntX ++) /* And each scanline contains 32 bytes */
|
|
{
|
|
PaletteData = *(ScreenBuffer + PaletteIndex + CntX);
|
|
if ((PaletteData & 0x80) && FLASHState) /* FLASH 1 state ? */
|
|
{
|
|
PaletteINK = (PaletteData & 0x78) >> 3;
|
|
PalettePAPER = (PaletteData & 0x07) | ((PaletteData & 0x40) >> 3);
|
|
}
|
|
else /* (No FLASH or first state) */
|
|
{
|
|
PaletteINK = (PaletteData & 0x07) | ((PaletteData & 0x40) >> 3);
|
|
PalettePAPER = (PaletteData & 0x78) >> 3;
|
|
}
|
|
PixelData = *(ScreenBuffer + ScreenDataIndex + CntX);
|
|
for (PixelMask = 128 ; PixelMask ; PixelMask >>= 1) /* 8 bits in a byte (left to right) */
|
|
PreparedScreenData[PreparedDataIndex ++] = (PixelData & PixelMask ? PaletteINK : PalettePAPER);
|
|
}
|
|
ScreenDataIndex += 256; /* One spectrum scan line down is 256 bytes */
|
|
}
|
|
PaletteIndex += 32; /* Palette is `correctly' addressed */
|
|
}
|
|
|
|
/* All set? Here goes! */
|
|
/* If a FLASHING screen is written, the inter-image pause is 32/100 = 16/50's of a second, just like a real Speccy :-) */
|
|
|
|
return (_GIFCompressImage (0, 0, 256, 192, &MyGetPixel, ConvertTo89a, 32));
|
|
}
|
|
|
|
int main (int argc, char **argv)
|
|
|
|
/**********************************************************************************************************************************/
|
|
/* Pre : None. */
|
|
/* Post : All SCR files specified on the command line have been converted to (animated) GIF files. */
|
|
/**********************************************************************************************************************************/
|
|
|
|
{
|
|
FILE *InputFile;
|
|
char InputFileName[256];
|
|
char OutputFileName[256];
|
|
int FileIndex = 0;
|
|
word BIndex;
|
|
byte register ColourCount;
|
|
byte ErrCode;
|
|
dword *PP; /* Palette Pointer */
|
|
|
|
printf ("\nSCR2GIF v1.0 - Copyright (C) 1998 M. van der Heide, ThunderWare Research Center\n\n");
|
|
if (argc < 2) /* Moron check */
|
|
{
|
|
fprintf (stderr, "Usage: scr2gif [-f] filename[.scr] ...\n");
|
|
fprintf (stderr, " -f = Convert FLASHing screens to animated GIF\n");
|
|
exit (1);
|
|
}
|
|
if (!strcmp (argv[1], "-f") || !strcmp (argv[1], "-F")) /* Want animated GIFs for FLASHing screens ? */
|
|
{
|
|
if (argc < 3) /* (Moron check) */
|
|
{
|
|
fprintf (stderr, "Usage: scr2gif [-f] filename[.scr] ...\n");
|
|
fprintf (stderr, " -f = Convert FLASHing screens to animated GIF\n");
|
|
exit (1);
|
|
}
|
|
Use89aForFLASH = TRUE;
|
|
FileIndex = 1;
|
|
}
|
|
while (++ FileIndex < argc) /* Do all SCR files specified on the command line */
|
|
{
|
|
strcpy (InputFileName, argv[FileIndex]);
|
|
if ((InputFile = fopen (InputFileName, "rb")) == NULL) /* Try to open the SCR file */
|
|
{
|
|
strcat (InputFileName, ".scr");
|
|
if ((InputFile = fopen (InputFileName, "rb")) == NULL)
|
|
{
|
|
strcpy (InputFileName + strlen (InputFileName) - 3, "SCR");
|
|
if ((InputFile = fopen (InputFileName, "rb")) == NULL)
|
|
{ printf ("Cannot find any file named %s, %s.scr or %s.SCR, so there!\n",
|
|
argv[FileIndex], argv[FileIndex], argv[FileIndex]); exit (1); }
|
|
}
|
|
}
|
|
strcpy (OutputFileName, InputFileName);
|
|
if (!strcmp (OutputFileName + strlen (OutputFileName) - 4, ".scr"))
|
|
strcpy (OutputFileName + strlen (OutputFileName) - 3, "gif"); /* Determine output file name */
|
|
else if (!strcmp (OutputFileName + strlen (OutputFileName) - 4, ".SCR"))
|
|
strcpy (OutputFileName + strlen (OutputFileName) - 3, "GIF"); /* (Maintain case) */
|
|
else
|
|
strcat (OutputFileName, ".gif");
|
|
printf ("Converting %s to %s ... ", InputFileName, OutputFileName);
|
|
fflush (stdout);
|
|
if (fread (ScreenBuffer, 1, 6912, InputFile) != 6912)
|
|
{ printf ("\nSorry, %s doesn't look like a SCR file to me!\n", InputFileName); fclose (InputFile); exit (1); }
|
|
fclose (InputFile);
|
|
ScreenContainsFLASH = FALSE;
|
|
if (Use89aForFLASH)
|
|
for (BIndex = 6144 ; BIndex < 6912 && !ScreenContainsFLASH ; BIndex ++) /* Check if there's FLASH */
|
|
if (*(ScreenBuffer + BIndex) & 0x80)
|
|
ScreenContainsFLASH = TRUE;
|
|
ConvertTo89a = (bool)(Use89aForFLASH && ScreenContainsFLASH); /* No need for animation is there's no FLASH! */
|
|
printf (ConvertTo89a ? "animated ... " : "single image ... ");
|
|
fflush (stdout);
|
|
if ((ErrCode = _GIFCreate (OutputFileName, 256, 192, 16, 6, ConvertTo89a)) != GIF_OK)
|
|
{ printf ("\nCannot create GIF file (%s)\n", _GIFstrerror (ErrCode)); exit (1); }
|
|
PP = SpecPalette;
|
|
for (ColourCount = 0 ; ColourCount < 16 ; ColourCount ++, PP ++)
|
|
_GIFSetColour (ColourCount, (byte)(*PP & 0x000000FF), (byte)((*PP & 0x0000FF00) >> 8), (byte)((*PP & 0x00FF0000) >> 16));
|
|
_GIFWriteGlobalColorTable (ConvertTo89a);
|
|
if ((ErrCode = SpectrumScreenToGIF (FALSE)) != GIF_OK)
|
|
{ printf ("\nCannot compress GIF image (%s)\n", _GIFstrerror (ErrCode)); _GIFClose (); unlink (OutputFileName); exit (1); }
|
|
if (ConvertTo89a)
|
|
if ((ErrCode = SpectrumScreenToGIF (TRUE)) != GIF_OK)
|
|
{ printf ("\nCannot compress GIF image (%s)\n", _GIFstrerror (ErrCode)); _GIFClose (); unlink (OutputFileName); exit (1); }
|
|
if ((ErrCode = _GIFClose ()) != GIF_OK)
|
|
{ printf ("\nCannot close GIF file (%s)\n", _GIFstrerror (ErrCode)); unlink (OutputFileName); exit (1); }
|
|
puts ("done");
|
|
}
|
|
return (0);
|
|
}
|