323 lines
12 KiB
C
323 lines
12 KiB
C
/*****************************************************************************
|
|
* "Gif-Lib" - Yet another gif library. *
|
|
* *
|
|
* Written by: Gershon Elber Ver 0.1, Jul. 1989 *
|
|
******************************************************************************
|
|
* Program to combine 2 GIF images into single one, using optional mask GIF *
|
|
* file. Result colormap will be the union of the two images colormaps. *
|
|
* Both images should have exactly the same size, although they may be mapped *
|
|
* differently on screen. Only First GIF screen descriptor info. is used. *
|
|
* Options: *
|
|
* -q : quite printing mode. *
|
|
* -m mask : optional boolean image, defines where second GIF should be used. *
|
|
* -h : on line help. *
|
|
******************************************************************************
|
|
* History: *
|
|
* 12 Jul 89 - Version 1.0 by Gershon Elber. *
|
|
*****************************************************************************/
|
|
|
|
#ifdef __MSDOS__
|
|
#include <stdlib.h>
|
|
#include <alloc.h>
|
|
#endif /* _MSDOS__ */
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include "gif_lib.h"
|
|
#include "getarg.h"
|
|
|
|
#define PROGRAM_NAME "GifComb"
|
|
|
|
#ifdef __MSDOS__
|
|
extern unsigned int
|
|
_stklen = 16384; /* Increase default stack size. */
|
|
#endif /* __MSDOS__ */
|
|
|
|
#ifdef SYSV
|
|
static char *VersionStr =
|
|
"Gif library module,\t\tGershon Elber\n\
|
|
(C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
|
|
static char
|
|
*CtrlStr = "GifComb q%- m%-MaskGIFFile!s h%- GifFile!*s";
|
|
#else
|
|
static char
|
|
*VersionStr =
|
|
PROGRAM_NAME
|
|
GIF_LIB_VERSION
|
|
" Gershon Elber, "
|
|
__DATE__ ", " __TIME__ "\n"
|
|
"(C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
|
|
static char
|
|
*CtrlStr =
|
|
PROGRAM_NAME
|
|
" q%- m%-MaskGIFFile!s h%- GifFile!*s";
|
|
#endif /* SYSV */
|
|
|
|
static int ReadUntilImage(GifFileType *GifFile);
|
|
static int UnionColorMap(GifColorType *ColorIn1, int ColorIn1Size,
|
|
GifColorType *ColorIn2, int ColorIn2Size,
|
|
GifColorType **ColorUnionPtr, int *ColorUnionSize,
|
|
GifPixelType ColorTransIn2[]);
|
|
static void QuitGifError(GifFileType *GifFileIn1, GifFileType *GifFileIn2,
|
|
GifFileType *GifMaskFile, GifFileType *GifFileOut);
|
|
|
|
/******************************************************************************
|
|
* Interpret the command line and scan the given GIF file. *
|
|
******************************************************************************/
|
|
void main(int argc, char **argv)
|
|
{
|
|
int i, j, Error, NumFiles, Size, ColorUnionSize,
|
|
ColorIn1Size = 0, ColorIn2Size = 0,
|
|
MaskFlag = FALSE, HelpFlag = FALSE;
|
|
char **FileName = NULL, *MaskFileName;
|
|
GifPixelType ColorTransIn2[256];
|
|
GifRowType LineIn1 = NULL, LineIn2 = NULL, LineMask = NULL, LineOut = NULL;
|
|
GifColorType *ColorIn1 = NULL, *ColorIn2 = NULL, *ColorUnion;
|
|
GifFileType *GifFileIn1 = NULL, *GifFileIn2 = NULL, *GifMaskFile = NULL,
|
|
*GifFileOut = NULL;
|
|
|
|
if ((Error = GAGetArgs(argc, argv, CtrlStr,
|
|
&GifQuitePrint, &MaskFlag, &MaskFileName,
|
|
&HelpFlag, &NumFiles, &FileName)) != FALSE ||
|
|
(NumFiles != 2 && !HelpFlag)) {
|
|
if (Error)
|
|
GAPrintErrMsg(Error);
|
|
else if (NumFiles != 2)
|
|
GIF_MESSAGE("Error in command line parsing - two GIF file please.");
|
|
GAPrintHowTo(CtrlStr);
|
|
exit(1);
|
|
}
|
|
|
|
if (HelpFlag) {
|
|
fprintf(stderr, VersionStr);
|
|
GAPrintHowTo(CtrlStr);
|
|
exit(0);
|
|
}
|
|
|
|
/* Open all input files (two GIF to combine, and optional mask): */
|
|
if ((GifFileIn1 = DGifOpenFileName(FileName[0])) == NULL ||
|
|
(GifFileIn2 = DGifOpenFileName(FileName[1])) == NULL ||
|
|
(MaskFlag && (GifMaskFile = DGifOpenFileName(MaskFileName)) == NULL))
|
|
QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
|
|
|
|
if (ReadUntilImage(GifFileIn1) == GIF_ERROR ||
|
|
ReadUntilImage(GifFileIn2) == GIF_ERROR ||
|
|
(MaskFlag && ReadUntilImage(GifMaskFile) == GIF_ERROR))
|
|
QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
|
|
|
|
if (GifFileIn1 -> IWidth != GifFileIn2 -> IWidth ||
|
|
GifFileIn2 -> IHeight != GifFileIn2 -> IHeight ||
|
|
(MaskFlag && (GifFileIn1 -> IWidth != GifMaskFile -> IWidth ||
|
|
GifFileIn1 -> IHeight != GifMaskFile -> IHeight)))
|
|
GIF_EXIT("Given GIF files have different image dimensions.");
|
|
|
|
/* Open stdout for the output file: */
|
|
if ((GifFileOut = EGifOpenFileHandle(1)) == NULL)
|
|
QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
|
|
|
|
Size = sizeof(GifPixelType) * GifFileIn1 -> IWidth;
|
|
if ((LineIn1 = (GifRowType) malloc(Size)) == NULL ||
|
|
(LineIn2 = (GifRowType) malloc(Size)) == NULL ||
|
|
(MaskFlag && (LineMask = (GifRowType) malloc(Size)) == NULL) ||
|
|
(LineOut = (GifRowType) malloc(Size)) == NULL)
|
|
GIF_EXIT("Failed to allocate memory required, aborted.");
|
|
|
|
if (GifFileIn1 -> IColorMap) {
|
|
ColorIn1 = GifFileIn1 -> IColorMap;
|
|
ColorIn1Size = 1 << GifFileIn1 -> IBitsPerPixel;
|
|
}
|
|
else if (GifFileIn1 -> SColorMap) {
|
|
ColorIn1 = GifFileIn1 -> SColorMap;
|
|
ColorIn1Size = 1 << GifFileIn1 -> SBitsPerPixel;
|
|
}
|
|
else
|
|
GIF_EXIT("Neither Screen nor Image color map exists - GIF file 1.");
|
|
|
|
if (GifFileIn2 -> IColorMap) {
|
|
ColorIn2 = GifFileIn2 -> IColorMap;
|
|
ColorIn2Size = 1 << GifFileIn2 -> IBitsPerPixel;
|
|
}
|
|
else if (GifFileIn2 -> SColorMap) {
|
|
ColorIn2 = GifFileIn2 -> SColorMap;
|
|
ColorIn2Size = 1 << GifFileIn2 -> SBitsPerPixel;
|
|
}
|
|
else
|
|
GIF_EXIT("Neither Screen nor Image color map exists - GIF file 2.");
|
|
|
|
/* Create union of the two given color maps. ColorIn1 will be copied as */
|
|
/* is while ColorIn2 will be mapped using ColorTransIn2 table. */
|
|
/* ColorUnion is allocated by the procedure itself. */
|
|
if (UnionColorMap(ColorIn1, ColorIn1Size, ColorIn2, ColorIn2Size,
|
|
&ColorUnion, &ColorUnionSize, ColorTransIn2) == GIF_ERROR)
|
|
GIF_EXIT("Unioned color map is two big (>256 colors).");
|
|
|
|
/* Dump out new image and screen descriptors: */
|
|
if (EGifPutScreenDesc(GifFileOut,
|
|
GifFileIn1 -> SWidth, GifFileIn1 -> SHeight,
|
|
ColorUnionSize, GifFileIn1 -> SBackGroundColor,
|
|
ColorUnionSize, ColorUnion) == GIF_ERROR)
|
|
QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
|
|
free((char *) ColorUnion); /* We dont need this any more... */
|
|
|
|
if (EGifPutImageDesc(GifFileOut,
|
|
GifFileIn1 -> ILeft, GifFileIn1 -> ITop,
|
|
GifFileIn1 -> IWidth, GifFileIn1 -> IHeight,
|
|
GifFileIn1 -> IInterlace, GifFileIn1 -> IBitsPerPixel, NULL) == GIF_ERROR)
|
|
QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
|
|
|
|
|
|
/* Time to do it: read 2 scan lines from 2 files (and optionally from */
|
|
/* the mask file, merge them and them result out. Do it Height times: */
|
|
GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ",
|
|
PROGRAM_NAME, GifFileOut -> ILeft, GifFileOut -> ITop,
|
|
GifFileOut -> IWidth, GifFileOut -> IHeight);
|
|
for (i = 0; i < GifFileIn1 -> IHeight; i++) {
|
|
if (DGifGetLine(GifFileIn1, LineIn1, GifFileIn1 -> IWidth) == GIF_ERROR ||
|
|
DGifGetLine(GifFileIn2, LineIn2, GifFileIn2 -> IWidth) == GIF_ERROR ||
|
|
(MaskFlag &&
|
|
DGifGetLine(GifMaskFile, LineMask, GifMaskFile -> IWidth)
|
|
== GIF_ERROR))
|
|
QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
|
|
if (MaskFlag) {
|
|
/* Every time Mask has non background color, use LineIn1 pixel, */
|
|
/* otherwise use LineIn2 pixel instead. */
|
|
for (j = 0; j < GifFileIn1 -> IWidth; j++) {
|
|
if (LineMask[j] != GifMaskFile -> SBackGroundColor)
|
|
LineOut[j] = LineIn1[j];
|
|
else
|
|
LineOut[j] = ColorTransIn2[LineIn2[j]];
|
|
}
|
|
}
|
|
else {
|
|
/* Every time Color of Image 1 is equal to background - take it */
|
|
/* From Image 2 instead of the background. */
|
|
for (j = 0; j < GifFileIn1 -> IWidth; j++) {
|
|
if (LineIn1[j] != GifFileIn1 -> SBackGroundColor)
|
|
LineOut[j] = LineIn1[j];
|
|
else
|
|
LineOut[j] = ColorTransIn2[LineIn2[j]];
|
|
}
|
|
}
|
|
if (EGifPutLine(GifFileOut, LineOut, GifFileOut -> IWidth)
|
|
== GIF_ERROR)
|
|
QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
|
|
GifQprintf("\b\b\b\b%-4d", i);
|
|
}
|
|
|
|
if (DGifCloseFile(GifFileIn1) == GIF_ERROR ||
|
|
DGifCloseFile(GifFileIn2) == GIF_ERROR ||
|
|
EGifCloseFile(GifFileOut) == GIF_ERROR ||
|
|
(MaskFlag && DGifCloseFile(GifMaskFile) == GIF_ERROR))
|
|
QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Read until first image in GIF file is detected and read its descriptor. *
|
|
******************************************************************************/
|
|
static int ReadUntilImage(GifFileType *GifFile)
|
|
{
|
|
int ExtCode;
|
|
GifRecordType RecordType;
|
|
GifByteType *Extension;
|
|
|
|
/* Scan the content of the GIF file, until image descriptor is detected: */
|
|
do {
|
|
if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR)
|
|
return GIF_ERROR;
|
|
|
|
switch (RecordType) {
|
|
case IMAGE_DESC_RECORD_TYPE:
|
|
return DGifGetImageDesc(GifFile);
|
|
case EXTENSION_RECORD_TYPE:
|
|
/* Skip any extension blocks in file: */
|
|
if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR)
|
|
return GIF_ERROR;
|
|
|
|
while (Extension != NULL)
|
|
if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR)
|
|
return GIF_ERROR;
|
|
break;
|
|
case TERMINATE_RECORD_TYPE:
|
|
break;
|
|
default: /* Should be traps by DGifGetRecordType. */
|
|
break;
|
|
}
|
|
}
|
|
while (RecordType != TERMINATE_RECORD_TYPE);
|
|
|
|
return GIF_ERROR; /* We should be here - no image was found! */
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Create union of the two given color maps and return it. If result can not *
|
|
* fit into 256 colors, GIF_ERROR is returned, GIF_OK otherwise. *
|
|
* ColorIn1 is copied as it to ColorUnion, while colors from ColorIn2 are *
|
|
* copied iff they dont exists before. ColorTransIn2 is used to map old *
|
|
* ColorIn2 into ColorUnion color map table. *
|
|
******************************************************************************/
|
|
static int UnionColorMap(GifColorType *ColorIn1, int ColorIn1Size,
|
|
GifColorType *ColorIn2, int ColorIn2Size,
|
|
GifColorType **ColorUnionPtr, int *ColorUnionSize,
|
|
GifPixelType ColorTransIn2[])
|
|
{
|
|
int i, j, CrntSlot;
|
|
GifColorType *ColorUnion;
|
|
|
|
/* Allocate table which will hold result for sure: */
|
|
*ColorUnionPtr = ColorUnion = (GifColorType *) malloc(sizeof(GifColorType)
|
|
* (ColorIn1Size > ColorIn2Size ? ColorIn1Size : ColorIn2Size) * 2);
|
|
|
|
/* Copy ColorIn1 to ColorUnionSize; */
|
|
for (i = 0; i < ColorIn1Size; i++) ColorUnion[i] = ColorIn1[i];
|
|
CrntSlot = ColorIn1Size; /* Current Empty slot. */
|
|
|
|
/* Copy ColorIn2 to ColorUnionSize (use old colors if exists): */
|
|
for (i = 0; i < ColorIn2Size && CrntSlot<=256; i++) {
|
|
/* Let see if this color already exists: */
|
|
for (j = 0; j < ColorIn1Size; j++) {
|
|
/* If memcmp does not exists for you, use the following: */
|
|
/*
|
|
if (ColorIn1[j].Red == ColorIn2[i].Red &&
|
|
ColorIn1[j].Green == ColorIn2[i].Green &&
|
|
ColorIn1[j].Blue == ColorIn2[i].Blue) break;
|
|
*/
|
|
if (memcmp(&ColorIn1[j], &ColorIn2[i], 3) == 0) break;
|
|
}
|
|
if (j < ColorIn1Size) {
|
|
/* We found this color aleardy exists in ColorIn1: */
|
|
ColorTransIn2[i] = j;
|
|
}
|
|
else {
|
|
/* Its new - copy it to a new slot: */
|
|
ColorUnion[CrntSlot] = ColorIn2[i];
|
|
ColorTransIn2[i] = CrntSlot++;
|
|
}
|
|
}
|
|
|
|
if (CrntSlot > 256) return GIF_ERROR;
|
|
|
|
/* Complete the color map to a power of two: */
|
|
for (i = 1; i <= 8; i++) if ((1 << i) >= CrntSlot) break;
|
|
for (j = CrntSlot; j < (1 << i); j++)
|
|
ColorUnion[j].Red = ColorUnion[j].Green = ColorUnion[j].Blue = 0;
|
|
|
|
*ColorUnionSize = i;
|
|
|
|
return GIF_OK;
|
|
}
|
|
|
|
/******************************************************************************
|
|
* Close both input and output file (if open), and exit. *
|
|
******************************************************************************/
|
|
static void QuitGifError(GifFileType *GifFileIn1, GifFileType *GifFileIn2,
|
|
GifFileType *GifMaskFile, GifFileType *GifFileOut)
|
|
{
|
|
PrintGifError();
|
|
if (GifFileIn1 != NULL) DGifCloseFile(GifFileIn1);
|
|
if (GifFileIn2 != NULL) DGifCloseFile(GifFileIn2);
|
|
if (GifMaskFile != NULL) DGifCloseFile(GifMaskFile);
|
|
if (GifFileOut != NULL) EGifCloseFile(GifFileOut);
|
|
exit(1);
|
|
}
|