1 /*****************************************************************************
3 gif2rgb - convert GIF to 24-bit RGB pixel triples or vice-versa
5 SPDX-License-Identifier: MIT
7 *****************************************************************************/
9 /***************************************************************************
11 Toshio Kuratomi had written this in a comment about the rgb2gif code:
13 Besides fixing bugs, what's really needed is for someone to work out how to
14 calculate a colormap for writing GIFs from rgb sources. Right now, an rgb
15 source that has only two colors (b/w) is being converted into an 8 bit GIF....
16 Which is horrendously wasteful without compression.
18 I (ESR) took this off the main to-do list in 2012 because I don't think
19 the GIFLIB project actually needs to be in the converters-and-tools business.
20 Plenty of hackers do that; our job is to supply stable library capability
21 with our utilities mainly interesting as test tools.
23 ***************************************************************************/
39 #define PROGRAM_NAME "gif2rgb"
41 __attribute__((__section__(".tizen.build-id")))
47 __DATE__ ", " __TIME__ "\n"
48 "(C) Copyright 1989 Gershon Elber.\n";
52 " v%- c%-#Colors!d s%-Width|Height!d!d 1%- o%-OutFileName!s h%- GifFile!*s";
54 static void LoadRGB(char *FileName,
56 GifByteType **RedBuffer,
57 GifByteType **GreenBuffer,
58 GifByteType **BlueBuffer,
59 int Width, int Height);
60 static void SaveGif(GifByteType *OutputBuffer,
61 int Width, int Height,
62 int ExpColorMapSize, ColorMapObject *OutputColorMap);
64 /******************************************************************************
65 Load RGB file into internal frame buffer.
66 ******************************************************************************/
67 static void LoadRGB(char *FileName,
69 GifByteType **RedBuffer,
70 GifByteType **GreenBuffer,
71 GifByteType **BlueBuffer,
72 int Width, int Height)
76 GifByteType *RedP, *GreenP, *BlueP;
79 Size = ((long) Width) * Height * sizeof(GifByteType);
81 if ((*RedBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL ||
82 (*GreenBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL ||
83 (*BlueBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL)
84 GIF_EXIT("Failed to allocate memory required, aborted.");
87 GreenP = *GreenBuffer;
90 if (FileName != NULL) {
92 if ((rgbfp[0] = fopen(FileName, "rb")) == NULL)
93 GIF_EXIT("Can't open input file name.");
96 static char *Postfixes[] = { ".R", ".G", ".B" };
99 for (i = 0; i < 3; i++) {
100 strncpy(OneFileName, FileName, sizeof(OneFileName)-1);
101 strncat(OneFileName, Postfixes[i],
102 sizeof(OneFileName) - 1 - strlen(OneFileName));
104 if ((rgbfp[i] = fopen(OneFileName, "rb")) == NULL)
105 GIF_EXIT("Can't open input file name.");
113 _setmode(0, O_BINARY);
119 GifQprintf("\n%s: RGB image: ", PROGRAM_NAME);
122 GifByteType *Buffer, *BufferP;
124 if ((Buffer = (GifByteType *) malloc(Width * 3)) == NULL)
125 GIF_EXIT("Failed to allocate memory required, aborted.");
127 for (i = 0; i < Height; i++) {
129 GifQprintf("\b\b\b\b%-4d", i);
130 if (fread(Buffer, Width * 3, 1, rgbfp[0]) != 1)
131 GIF_EXIT("Input file(s) terminated prematurly.");
132 for (j = 0, BufferP = Buffer; j < Width; j++) {
133 *RedP++ = *BufferP++;
134 *GreenP++ = *BufferP++;
135 *BlueP++ = *BufferP++;
139 free((char *) Buffer);
143 for (i = 0; i < Height; i++) {
144 GifQprintf("\b\b\b\b%-4d", i);
145 if (fread(RedP, Width, 1, rgbfp[0]) != 1 ||
146 fread(GreenP, Width, 1, rgbfp[1]) != 1 ||
147 fread(BlueP, Width, 1, rgbfp[2]) != 1)
148 GIF_EXIT("Input file(s) terminated prematurly.");
160 /******************************************************************************
161 Save the GIF resulting image.
162 ******************************************************************************/
163 static void SaveGif(GifByteType *OutputBuffer,
164 int Width, int Height,
165 int ExpColorMapSize, ColorMapObject *OutputColorMap)
168 GifFileType *GifFile;
169 GifByteType *Ptr = OutputBuffer;
171 /* Open stdout for the output file: */
172 if ((GifFile = EGifOpenFileHandle(1, &Error)) == NULL) {
173 PrintGifError(Error);
177 if (EGifPutScreenDesc(GifFile,
178 Width, Height, ExpColorMapSize, 0,
179 OutputColorMap) == GIF_ERROR ||
180 EGifPutImageDesc(GifFile,
181 0, 0, Width, Height, false, NULL) == GIF_ERROR) {
182 PrintGifError(Error);
186 GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]: ",
187 PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top,
188 GifFile->Image.Width, GifFile->Image.Height);
190 for (i = 0; i < Height; i++) {
191 if (EGifPutLine(GifFile, Ptr, Width) == GIF_ERROR)
193 GifQprintf("\b\b\b\b%-4d", Height - i - 1);
198 if (EGifCloseFile(GifFile, &Error) == GIF_ERROR) {
199 PrintGifError(Error);
204 /******************************************************************************
205 Close output file (if open), and exit.
206 ******************************************************************************/
207 static void RGB2GIF(bool OneFileFlag, int NumFiles, char *FileName,
208 int ExpNumOfColors, int Width, int Height)
212 GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL,
213 *OutputBuffer = NULL;
214 ColorMapObject *OutputColorMap = NULL;
216 ColorMapSize = 1 << ExpNumOfColors;
219 LoadRGB(FileName, OneFileFlag,
220 &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height);
223 LoadRGB(NULL, OneFileFlag,
224 &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height);
227 if ((OutputColorMap = GifMakeMapObject(ColorMapSize, NULL)) == NULL ||
228 (OutputBuffer = (GifByteType *) malloc(Width * Height *
229 sizeof(GifByteType))) == NULL)
230 GIF_EXIT("Failed to allocate memory required, aborted.");
232 if (GifQuantizeBuffer(Width, Height, &ColorMapSize,
233 RedBuffer, GreenBuffer, BlueBuffer,
234 OutputBuffer, OutputColorMap->Colors) == GIF_ERROR)
236 free((char *) RedBuffer);
237 free((char *) GreenBuffer);
238 free((char *) BlueBuffer);
240 SaveGif(OutputBuffer, Width, Height, ExpNumOfColors, OutputColorMap);
243 /******************************************************************************
244 The real screen dumping routine.
245 ******************************************************************************/
246 static void DumpScreen2RGB(char *FileName, int OneFileFlag,
247 ColorMapObject *ColorMap,
248 GifRowType *ScreenBuffer,
249 int ScreenWidth, int ScreenHeight)
253 GifColorType *ColorMapEntry;
256 if (FileName != NULL) {
258 if ((rgbfp[0] = fopen(FileName, "wb")) == NULL)
259 GIF_EXIT("Can't open input file name.");
261 static char *Postfixes[] = { ".R", ".G", ".B" };
262 char OneFileName[80];
264 for (i = 0; i < 3; i++) {
265 strncpy(OneFileName, FileName, sizeof(OneFileName)-1);
266 strncat(OneFileName, Postfixes[i],
267 sizeof(OneFileName) - 1 - strlen(OneFileName));
269 if ((rgbfp[i] = fopen(OneFileName, "wb")) == NULL) {
270 GIF_EXIT("Can't open input file name.");
278 _setmode(1, O_BINARY);
284 if (ColorMap == NULL) {
285 fprintf(stderr, "Color map pointer is NULL.\n");
290 unsigned char *Buffer, *BufferP;
292 if ((Buffer = (unsigned char *) malloc(ScreenWidth * 3)) == NULL)
293 GIF_EXIT("Failed to allocate memory required, aborted.");
294 for (i = 0; i < ScreenHeight; i++) {
295 GifRow = ScreenBuffer[i];
296 GifQprintf("\b\b\b\b%-4d", ScreenHeight - i);
297 for (j = 0, BufferP = Buffer; j < ScreenWidth; j++) {
298 ColorMapEntry = &ColorMap->Colors[GifRow[j]];
299 *BufferP++ = ColorMapEntry->Red;
300 *BufferP++ = ColorMapEntry->Green;
301 *BufferP++ = ColorMapEntry->Blue;
303 if (fwrite(Buffer, ScreenWidth * 3, 1, rgbfp[0]) != 1)
304 GIF_EXIT("Write to file(s) failed.");
307 free((char *) Buffer);
310 unsigned char *Buffers[3];
312 if ((Buffers[0] = (unsigned char *) malloc(ScreenWidth)) == NULL ||
313 (Buffers[1] = (unsigned char *) malloc(ScreenWidth)) == NULL ||
314 (Buffers[2] = (unsigned char *) malloc(ScreenWidth)) == NULL)
315 GIF_EXIT("Failed to allocate memory required, aborted.");
317 for (i = 0; i < ScreenHeight; i++) {
318 GifRow = ScreenBuffer[i];
319 GifQprintf("\b\b\b\b%-4d", ScreenHeight - i);
320 for (j = 0; j < ScreenWidth; j++) {
321 ColorMapEntry = &ColorMap->Colors[GifRow[j]];
322 Buffers[0][j] = ColorMapEntry->Red;
323 Buffers[1][j] = ColorMapEntry->Green;
324 Buffers[2][j] = ColorMapEntry->Blue;
326 if (fwrite(Buffers[0], ScreenWidth, 1, rgbfp[0]) != 1 ||
327 fwrite(Buffers[1], ScreenWidth, 1, rgbfp[1]) != 1 ||
328 fwrite(Buffers[2], ScreenWidth, 1, rgbfp[2]) != 1)
329 GIF_EXIT("Write to file(s) failed.");
332 free((char *) Buffers[0]);
333 free((char *) Buffers[1]);
334 free((char *) Buffers[2]);
341 static void GIF2RGB(int NumFiles, char *FileName,
345 int i, j, Size, Row, Col, Width, Height, ExtCode, Count;
346 GifRecordType RecordType;
347 GifByteType *Extension;
348 GifRowType *ScreenBuffer;
349 GifFileType *GifFile;
351 InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
352 InterlacedJumps[] = { 8, 8, 4, 2 }; /* be read - offsets and jumps... */
354 ColorMapObject *ColorMap;
359 if ((GifFile = DGifOpenFileName(FileName, &Error)) == NULL) {
360 PrintGifError(Error);
366 /* Use stdin instead: */
367 if ((GifFile = DGifOpenFileHandle(0, &Error)) == NULL) {
368 PrintGifError(Error);
373 if (GifFile->SHeight == 0 || GifFile->SWidth == 0) {
374 fprintf(stderr, "Image of width or height 0\n");
379 * Allocate the screen as vector of column of rows. Note this
380 * screen is device independent - it's the screen defined by the
381 * GIF file parameters.
383 if ((ScreenBuffer = (GifRowType *)
384 malloc(GifFile->SHeight * sizeof(GifRowType))) == NULL)
385 GIF_EXIT("Failed to allocate memory required, aborted.");
387 Size = GifFile->SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/
388 if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */
389 GIF_EXIT("Failed to allocate memory required, aborted.");
391 for (i = 0; i < GifFile->SWidth; i++) /* Set its color to BackGround. */
392 ScreenBuffer[0][i] = GifFile->SBackGroundColor;
393 for (i = 1; i < GifFile->SHeight; i++) {
394 /* Allocate the other rows, and set their color to background too: */
395 if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL)
396 GIF_EXIT("Failed to allocate memory required, aborted.");
398 memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
401 /* Scan the content of the GIF file and load the image(s) in: */
403 if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
404 PrintGifError(GifFile->Error);
407 switch (RecordType) {
408 case IMAGE_DESC_RECORD_TYPE:
409 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
410 PrintGifError(GifFile->Error);
413 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
414 Col = GifFile->Image.Left;
415 Width = GifFile->Image.Width;
416 Height = GifFile->Image.Height;
417 GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]: ",
418 PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height);
419 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
420 GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
421 fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
424 if (GifFile->Image.Interlace) {
425 /* Need to perform 4 passes on the images: */
426 for (Count = i = 0; i < 4; i++)
427 for (j = Row + InterlacedOffset[i]; j < Row + Height;
428 j += InterlacedJumps[i]) {
429 GifQprintf("\b\b\b\b%-4d", Count++);
430 if (DGifGetLine(GifFile, &ScreenBuffer[j][Col],
431 Width) == GIF_ERROR) {
432 PrintGifError(GifFile->Error);
438 for (i = 0; i < Height; i++) {
439 GifQprintf("\b\b\b\b%-4d", i);
440 if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col],
441 Width) == GIF_ERROR) {
442 PrintGifError(GifFile->Error);
448 case EXTENSION_RECORD_TYPE:
449 /* Skip any extension blocks in file: */
450 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
451 PrintGifError(GifFile->Error);
454 while (Extension != NULL) {
455 if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
456 PrintGifError(GifFile->Error);
461 case TERMINATE_RECORD_TYPE:
463 default: /* Should be trapped by DGifGetRecordType. */
466 } while (RecordType != TERMINATE_RECORD_TYPE);
468 /* Lets dump it - set the global variables required and do it: */
469 ColorMap = (GifFile->Image.ColorMap
470 ? GifFile->Image.ColorMap
471 : GifFile->SColorMap);
472 if (ColorMap == NULL) {
473 fprintf(stderr, "Gif Image does not have a colormap\n");
477 /* check that the background color isn't garbage (SF bug #87) */
478 if (GifFile->SBackGroundColor < 0 || GifFile->SBackGroundColor >= ColorMap->ColorCount) {
479 fprintf(stderr, "Background color out of range for colormap\n");
483 DumpScreen2RGB(OutFileName, OneFileFlag,
486 GifFile->SWidth, GifFile->SHeight);
488 (void)free(ScreenBuffer);
490 if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) {
491 PrintGifError(Error);
497 /******************************************************************************
498 * Interpret the command line and scan the given GIF file.
499 ******************************************************************************/
500 int main(int argc, char **argv)
502 bool Error, OutFileFlag = false, ColorFlag = false, SizeFlag = false;
503 int NumFiles, Width = 0, Height = 0, ExpNumOfColors = 8;
510 if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint,
511 &ColorFlag, &ExpNumOfColors, &SizeFlag, &Width, &Height,
512 &OneFileFlag, &OutFileFlag, &OutFileName,
513 &HelpFlag, &NumFiles, &FileName)) != false ||
514 (NumFiles > 1 && !HelpFlag)) {
516 GAPrintErrMsg(Error);
517 else if (NumFiles > 1)
518 GIF_MESSAGE("Error in command line parsing - one input file please.");
519 GAPrintHowTo(CtrlStr);
524 (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
525 GAPrintHowTo(CtrlStr);
528 if (!OutFileFlag) OutFileName = NULL;
530 if (SizeFlag && Width > 0 && Height > 0)
531 RGB2GIF(OneFileFlag, NumFiles, *FileName,
532 ExpNumOfColors, Width, Height);
534 GIF2RGB(NumFiles, *FileName, OneFileFlag, OutFileName);