/***************************************************************************** * "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 #include #endif /* _MSDOS__ */ #include #include #include #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); }