Metropoli BBS
VIEWER: gifcomb.c MODE: TEXT (LATIN1)
/*****************************************************************************
*   "Gif-Lib" - Yet another gif library.				     *
*									     *
* Written by:  Gershon Elber			IBM PC 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:								     *
* -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.				     *
*****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <alloc.h>
#include <string.h>
#include "gif_lib.h"
#include "getarg.h"

#define PROGRAM_NAME	"GifComb"
#define VERSION		"รก Version 1.0, "

extern unsigned int
    _stklen = 16384;			      /* Increase default stack size */

static char
    *VersionStr =
	PROGRAM_NAME
	"	IBMPC "
	VERSION
	"	Gershon Elber,	"
	__DATE__ ",   " __TIME__ "\n"
	"(C) Copyright 1989 Gershon Elber, Non commercial use only.\n";
static char
    *CtrlStr =
	PROGRAM_NAME
	" m%-MaskGIFFile!s h%- GifFile!*s";
static char
    *ProgramName;

static int ReadUntilImage(GifFileType *GifFile);
static int UnionColorMap(GifColorType *ColorIn1, int ColorIn1Size,
			 GifColorType *ColorIn2, int ColorIn2Size,
			 GifColorType **ColorUnionPtr, int *ColorUnionSize,
			 PixelType 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, ColorIn2Size,
	MaskFlag = FALSE, HelpFlag = FALSE;
    char **FileName = NULL, *MaskFileName;
    PixelType ColorTransIn2[256];
    RowType LineIn1, LineIn2, LineMask, LineOut;
    GifColorType *ColorIn1, *ColorIn2, *ColorUnion;
    GifFileType *GifFileIn1 = NULL, *GifFileIn2 = NULL, *GifMaskFile = NULL,
	*GifFileOut = NULL;

    if (strlen(ProgramName = argv[0]) == 0)		    /* DOS 3.x only! */
	ProgramName = PROGRAM_NAME;	  /* Do something reasonable for 2.x */

    if ((Error = GAGetArgs(argc, argv, CtrlStr,
		&MaskFlag, &MaskFileName,
		&HelpFlag, &NumFiles, &FileName)) != FALSE ||
		(NumFiles != 2 && !HelpFlag)) {
	if (Error) GAPrintErrMsg(Error);
	else
	if (NumFiles != 2)
	    MESSAGE("Error in command line parsing - two GIF file please\n");
	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) == ERROR ||
	ReadUntilImage(GifFileIn2) == ERROR ||
	(MaskFlag && ReadUntilImage(GifMaskFile) == ERROR))
	QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);

    if (GifFileIn1 -> IWidth != GifFileIn2 -> IWidth ||
	GifFileIn2 -> IHeight != GifFileIn2 -> IHeight ||
	(MaskFlag && (GifFileIn1 -> IWidth != GifMaskFile -> IWidth ||
		      GifFileIn1 -> IHeight != GifMaskFile -> IHeight)))
	EXIT("Given GIF files have different image dimensions\n");

    /* Open stdout for the output file: */
    if ((GifFileOut = EGifOpenFileHandle(1)) == NULL)
	QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);

    Size = sizeof(PixelType) * GifFileIn1 -> IWidth;
    if ((LineIn1 = (RowType) malloc(Size)) == NULL ||
	(LineIn2 = (RowType) malloc(Size)) == NULL ||
	(MaskFlag && (LineMask = (RowType) malloc(Size)) == NULL) ||
	(LineOut = (RowType) malloc(Size)) == NULL)
	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 EXIT("Neither Screen nor Image color map exists - GIF file 1\n");

    if (GifFileIn2 -> IColorMap) {
	ColorIn2 = GifFileIn2 -> IColorMap;
	ColorIn2Size = 1 << GifFileIn2 -> IBitsPerPixel;
    }
    else
    if (GifFileIn2 -> SColorMap) {
	ColorIn2 = GifFileIn2 -> SColorMap;
	ColorIn2Size = 1 << GifFileIn2 -> SBitsPerPixel;
    }
    else EXIT("Neither Screen nor Image color map exists - GIF file 2\n");

    /* 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) == ERROR)
	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) == 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) == 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:    */
    fprintf(stderr, "\n%s: Image 1 at (%d, %d) [%dx%d]:     ",
	ProgramName, GifFileOut -> ILeft, GifFileOut -> ITop,
				GifFileOut -> IWidth, GifFileOut -> IHeight);
    for (i=0; i<GifFileIn1 -> IHeight; i++) {
	if (DGifGetLine(GifFileIn1, LineIn1, GifFileIn1 -> IWidth) == ERROR ||
	    DGifGetLine(GifFileIn2, LineIn2, GifFileIn2 -> IWidth) == ERROR ||
	    (MaskFlag &&
	     DGifGetLine(GifMaskFile, LineMask, GifMaskFile -> IWidth)
								== 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)
								== ERROR)
	QuitGifError(GifFileIn1, GifFileIn2, GifMaskFile, GifFileOut);
	fprintf(stderr, "\b\b\b\b%-4d", i);
    }

    if (DGifCloseFile(GifFileIn1) == ERROR ||
	DGifCloseFile(GifFileIn2) == ERROR ||
	EGifCloseFile(GifFileOut) == ERROR ||
	(MaskFlag && DGifCloseFile(GifMaskFile) == 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;
    ByteType *Extension;

    /* Scan the content of the GIF file, until image descriptor is detected: */
    do {
	if (DGifGetRecordType(GifFile, &RecordType) == ERROR)
	    return 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) == ERROR)
		    return ERROR;

		while (Extension != NULL)
		    if (DGifGetExtensionNext(GifFile, &Extension) == ERROR)
			return ERROR;
		break;
	    case TERMINATE_RECORD_TYPE:
		break;
	    default:		     /* Should be traps by DGifGetRecordType */
		break;
	}
    }
    while (RecordType != TERMINATE_RECORD_TYPE);

    return 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, ERROR is returned, 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,
			 PixelType 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 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 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);
}
[ RETURN TO DIRECTORY ]