[CVE patch] CVE-2016-3977 in giflib version 5.1.2
[platform/upstream/giflib.git] / util / gif2rgb.c
1 /*****************************************************************************
2
3 gif2rgb - convert GIF to 24-bit RGB pixel triples or vice-versa
4
5 *****************************************************************************/
6
7 /***************************************************************************
8
9 Toshio Kuratomi had written this in a comment about the rgb2gif code:
10
11   Besides fixing bugs, what's really needed is for someone to work out how to
12   calculate a colormap for writing GIFs from rgb sources.  Right now, an rgb
13   source that has only two colors (b/w) is being converted into an 8 bit GIF....
14   Which is horrendously wasteful without compression.
15
16 I (ESR) took this off the main to-do list in 2012 because I don't think
17 the GIFLIB project actually needs to be in the converters-and-tools business.
18 Plenty of hackers do that; our job is to supply stable library capability
19 with our utilities mainly interesting as test tools.
20
21 ***************************************************************************/
22
23 #include <stdlib.h>
24 #include <stdio.h>
25 #include <ctype.h>
26 #include <string.h>
27 #include <stdbool.h>
28 #include <fcntl.h>
29
30 #ifdef _WIN32
31 #include <io.h>
32 #endif /* _WIN32 */
33
34 #include "gif_lib.h"
35 #include "getarg.h"
36
37 #define PROGRAM_NAME    "gif2rgb"
38
39 __attribute__((__section__(".tizen.build-id")))
40 static char
41     VersionStr[] =
42         PROGRAM_NAME
43         VERSION_COOKIE
44         "       Gershon Elber,  "
45         __DATE__ ",   " __TIME__ "\n"
46         "(C) Copyright 1989 Gershon Elber.\n";
47 static char
48     *CtrlStr =
49         PROGRAM_NAME
50         " v%- c%-#Colors!d s%-Width|Height!d!d 1%- o%-OutFileName!s h%- GifFile!*s";
51
52 static void LoadRGB(char *FileName,
53                     int OneFileFlag,
54                     GifByteType **RedBuffer,
55                     GifByteType **GreenBuffer,
56                     GifByteType **BlueBuffer,
57                     int Width, int Height);
58 static void SaveGif(GifByteType *OutputBuffer,
59                     int Width, int Height, 
60                     int ExpColorMapSize, ColorMapObject *OutputColorMap);
61
62 /******************************************************************************
63  Load RGB file into internal frame buffer.
64 ******************************************************************************/
65 static void LoadRGB(char *FileName,
66                     int OneFileFlag,
67                     GifByteType **RedBuffer,
68                     GifByteType **GreenBuffer,
69                     GifByteType **BlueBuffer,
70                     int Width, int Height)
71 {
72     int i;
73     unsigned long Size;
74     GifByteType *RedP, *GreenP, *BlueP;
75     FILE *rgbfp[3];
76
77     Size = ((long) Width) * Height * sizeof(GifByteType);
78
79     if ((*RedBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL ||
80         (*GreenBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL ||
81         (*BlueBuffer = (GifByteType *) malloc((unsigned int) Size)) == NULL)
82         GIF_EXIT("Failed to allocate memory required, aborted.");
83
84     RedP = *RedBuffer;
85     GreenP = *GreenBuffer;
86     BlueP = *BlueBuffer;
87
88     if (FileName != NULL) {
89         if (OneFileFlag) {
90             if ((rgbfp[0] = fopen(FileName, "rb")) == NULL)
91                 GIF_EXIT("Can't open input file name.");
92         }
93         else {
94             static char *Postfixes[] = { ".R", ".G", ".B" };
95             char OneFileName[80];
96
97             for (i = 0; i < 3; i++) {
98                 strncpy(OneFileName, FileName, sizeof(OneFileName)-1);
99                 /* cppcheck-suppress uninitstring */
100                 strncat(OneFileName, Postfixes[i], 
101                         sizeof(OneFileName) - 1 - strlen(OneFileName));
102
103                 if ((rgbfp[i] = fopen(OneFileName, "rb")) == NULL)
104                     GIF_EXIT("Can't open input file name.");
105             }
106         }
107     }
108     else {
109         OneFileFlag = true;
110
111 #ifdef _WIN32
112         _setmode(0, O_BINARY);
113 #endif /* _WIN32 */
114
115         rgbfp[0] = stdin;
116     }
117
118     GifQprintf("\n%s: RGB image:     ", PROGRAM_NAME);
119
120     if (OneFileFlag) {
121         GifByteType *Buffer, *BufferP;
122
123         if ((Buffer = (GifByteType *) malloc(Width * 3)) == NULL)
124             GIF_EXIT("Failed to allocate memory required, aborted.");
125
126         for (i = 0; i < Height; i++) {
127             int j;
128             GifQprintf("\b\b\b\b%-4d", i);
129             if (fread(Buffer, Width * 3, 1, rgbfp[0]) != 1)
130                 GIF_EXIT("Input file(s) terminated prematurly.");
131             for (j = 0, BufferP = Buffer; j < Width; j++) {
132                 *RedP++ = *BufferP++;
133                 *GreenP++ = *BufferP++;
134                 *BlueP++ = *BufferP++;
135             }
136         }
137
138         free((char *) Buffer);
139         fclose(rgbfp[0]);
140     }
141     else {
142         for (i = 0; i < Height; i++) {
143             GifQprintf("\b\b\b\b%-4d", i);
144             if (fread(RedP, Width, 1, rgbfp[0]) != 1 ||
145                 fread(GreenP, Width, 1, rgbfp[1]) != 1 ||
146                 fread(BlueP, Width, 1, rgbfp[2]) != 1)
147                 GIF_EXIT("Input file(s) terminated prematurly.");
148             RedP += Width;
149             GreenP += Width;
150             BlueP += Width;
151         }
152
153         fclose(rgbfp[0]);
154         // cppcheck-suppress useClosedFile
155         fclose(rgbfp[1]);
156         // cppcheck-suppress useClosedFile
157         fclose(rgbfp[2]);
158     }
159 }
160
161 /******************************************************************************
162  Save the GIF resulting image.
163 ******************************************************************************/
164 static void SaveGif(GifByteType *OutputBuffer,
165                     int Width, int Height,
166                     int ExpColorMapSize, ColorMapObject *OutputColorMap)
167 {
168     int i, Error;
169     GifFileType *GifFile;
170     GifByteType *Ptr = OutputBuffer;
171
172     /* Open stdout for the output file: */
173     if ((GifFile = EGifOpenFileHandle(1, &Error)) == NULL) {
174         PrintGifError(Error);
175         exit(EXIT_FAILURE);
176     }
177
178     if (EGifPutScreenDesc(GifFile,
179                           Width, Height, ExpColorMapSize, 0,
180                           OutputColorMap) == GIF_ERROR ||
181         EGifPutImageDesc(GifFile,
182                          0, 0, Width, Height, false, NULL) ==
183                                                                      GIF_ERROR)
184         PrintGifError(Error);
185         if (GifFile != NULL) {
186             EGifCloseFile(GifFile, NULL);
187         }
188         exit(EXIT_FAILURE);
189
190     GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]:     ",
191                PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top,
192                GifFile->Image.Width, GifFile->Image.Height);
193
194     for (i = 0; i < Height; i++) {
195         if (EGifPutLine(GifFile, Ptr, Width) == GIF_ERROR)
196         {
197             if (GifFile != NULL) {
198                 EGifCloseFile(GifFile, NULL);
199             }
200             exit(EXIT_FAILURE);
201         }
202         GifQprintf("\b\b\b\b%-4d", Height - i - 1);
203
204         Ptr += Width;
205     }
206
207     if (EGifCloseFile(GifFile, &Error) == GIF_ERROR)
208         PrintGifError(Error);
209         if (GifFile != NULL) {
210             EGifCloseFile(GifFile, NULL);
211         }
212         exit(EXIT_FAILURE);
213 }
214
215 /******************************************************************************
216  Close output file (if open), and exit.
217 ******************************************************************************/
218 static void RGB2GIF(bool OneFileFlag, int NumFiles, char *FileName,
219                     int ExpNumOfColors, int Width, int Height)
220 {
221     int ColorMapSize;
222
223     GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL,
224         *OutputBuffer = NULL;
225     ColorMapObject *OutputColorMap = NULL;
226
227     ColorMapSize = 1 << ExpNumOfColors;
228
229     if (NumFiles == 1) {
230         LoadRGB(FileName, OneFileFlag,
231                 &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height);
232     }
233     else {
234         LoadRGB(NULL, OneFileFlag,
235                 &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height);
236     }
237
238     if ((OutputColorMap = GifMakeMapObject(ColorMapSize, NULL)) == NULL ||
239         (OutputBuffer = (GifByteType *) malloc(Width * Height *
240                                             sizeof(GifByteType))) == NULL)
241         GIF_EXIT("Failed to allocate memory required, aborted.");
242
243     if (GifQuantizeBuffer(Width, Height, &ColorMapSize,
244                        RedBuffer, GreenBuffer, BlueBuffer,
245                        OutputBuffer, OutputColorMap->Colors) == GIF_ERROR)
246         exit(EXIT_FAILURE);
247     free((char *) RedBuffer);
248     free((char *) GreenBuffer);
249     free((char *) BlueBuffer);
250
251     SaveGif(OutputBuffer, Width, Height, ExpNumOfColors, OutputColorMap);
252 }
253
254 /******************************************************************************
255  The real screen dumping routine.
256 ******************************************************************************/
257 static void DumpScreen2RGB(char *FileName, int OneFileFlag,
258                            ColorMapObject *ColorMap,
259                            GifRowType *ScreenBuffer,
260                            int ScreenWidth, int ScreenHeight)
261 {
262     int i, j;
263     GifRowType GifRow;
264     GifColorType *ColorMapEntry;
265     FILE *rgbfp[3];
266
267     if (FileName != NULL) {
268         if (OneFileFlag) {
269             if ((rgbfp[0] = fopen(FileName, "wb")) == NULL)
270             GIF_EXIT("Can't open input file name.");
271         } else {
272             static char *Postfixes[] = { ".R", ".G", ".B" };
273             char OneFileName[80];
274
275             for (i = 0; i < 3; i++) {
276                 strncpy(OneFileName, FileName, sizeof(OneFileName)-1);
277                 /* cppcheck-suppress uninitstring */
278                 strncat(OneFileName, Postfixes[i], 
279                         sizeof(OneFileName) - 1 - strlen(OneFileName));
280     
281                 if ((rgbfp[i] = fopen(OneFileName, "wb")) == NULL) {
282                     GIF_EXIT("Can't open input file name.");
283                 }
284             }
285         }
286     } else {
287         OneFileFlag = true;
288
289 #ifdef _WIN32
290         _setmode(1, O_BINARY);
291 #endif /* _WIN32 */
292         
293         rgbfp[0] = stdout;
294     }
295
296     if (OneFileFlag) {
297         unsigned char *Buffer, *BufferP;
298
299         if ((Buffer = (unsigned char *) malloc(ScreenWidth * 3)) == NULL)
300             GIF_EXIT("Failed to allocate memory required, aborted.");
301         for (i = 0; i < ScreenHeight; i++) {
302             GifRow = ScreenBuffer[i];
303             GifQprintf("\b\b\b\b%-4d", ScreenHeight - i);
304             for (j = 0, BufferP = Buffer; j < ScreenWidth; j++) {
305                 ColorMapEntry = &ColorMap->Colors[GifRow[j]];
306                 *BufferP++ = ColorMapEntry->Red;
307                 *BufferP++ = ColorMapEntry->Green;
308                 *BufferP++ = ColorMapEntry->Blue;
309             }
310             if (fwrite(Buffer, ScreenWidth * 3, 1, rgbfp[0]) != 1)
311                 GIF_EXIT("Write to file(s) failed.");
312         }
313
314         free((char *) Buffer);
315         fclose(rgbfp[0]);
316     } else {
317         unsigned char *Buffers[3];
318
319         if ((Buffers[0] = (unsigned char *) malloc(ScreenWidth)) == NULL ||
320             (Buffers[1] = (unsigned char *) malloc(ScreenWidth)) == NULL ||
321             (Buffers[2] = (unsigned char *) malloc(ScreenWidth)) == NULL)
322             GIF_EXIT("Failed to allocate memory required, aborted.");
323
324         for (i = 0; i < ScreenHeight; i++) {
325             GifRow = ScreenBuffer[i];
326             GifQprintf("\b\b\b\b%-4d", ScreenHeight - i);
327             for (j = 0; j < ScreenWidth; j++) {
328                 ColorMapEntry = &ColorMap->Colors[GifRow[j]];
329                 Buffers[0][j] = ColorMapEntry->Red;
330                 Buffers[1][j] = ColorMapEntry->Green;
331                 Buffers[2][j] = ColorMapEntry->Blue;
332             }
333             if (fwrite(Buffers[0], ScreenWidth, 1, rgbfp[0]) != 1 ||
334                 fwrite(Buffers[1], ScreenWidth, 1, rgbfp[1]) != 1 ||
335                 fwrite(Buffers[2], ScreenWidth, 1, rgbfp[2]) != 1)
336                 GIF_EXIT("Write to file(s) failed.");
337         }
338
339         free((char *) Buffers[0]);
340         free((char *) Buffers[1]);
341         free((char *) Buffers[2]);
342         fclose(rgbfp[0]);
343         // cppcheck-suppress useClosedFile
344         fclose(rgbfp[1]);
345         // cppcheck-suppress useClosedFile
346         fclose(rgbfp[2]);
347     }
348 }
349
350 static void GIF2RGB(int NumFiles, char *FileName, 
351                     bool OneFileFlag, 
352                     char *OutFileName)
353 {
354     int i, j, Size, Row, Col, Width, Height, ExtCode, Count;
355     GifRecordType RecordType;
356     GifByteType *Extension;
357     GifRowType *ScreenBuffer;
358     GifFileType *GifFile;
359     int
360         InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
361         InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
362     int ImageNum = 0;
363     ColorMapObject *ColorMap;
364     int Error;
365
366     if (NumFiles == 1) {
367         int Error;
368         if ((GifFile = DGifOpenFileName(FileName, &Error)) == NULL) {
369             PrintGifError(Error);
370             exit(EXIT_FAILURE);
371         }
372     }
373     else {
374         int Error;
375         /* Use stdin instead: */
376         if ((GifFile = DGifOpenFileHandle(0, &Error)) == NULL) {
377             PrintGifError(Error);
378             exit(EXIT_FAILURE);
379         }
380     }
381
382     /* 
383      * Allocate the screen as vector of column of rows. Note this
384      * screen is device independent - it's the screen defined by the
385      * GIF file parameters.
386      */
387     if ((ScreenBuffer = (GifRowType *)
388         malloc(GifFile->SHeight * sizeof(GifRowType))) == NULL)
389             GIF_EXIT("Failed to allocate memory required, aborted.");
390
391     Size = GifFile->SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/
392     if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */
393         GIF_EXIT("Failed to allocate memory required, aborted.");
394
395     for (i = 0; i < GifFile->SWidth; i++)  /* Set its color to BackGround. */
396         ScreenBuffer[0][i] = GifFile->SBackGroundColor;
397     for (i = 1; i < GifFile->SHeight; i++) {
398         /* Allocate the other rows, and set their color to background too: */
399         if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL)
400             GIF_EXIT("Failed to allocate memory required, aborted.");
401
402         memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
403     }
404
405     /* Scan the content of the GIF file and load the image(s) in: */
406     do {
407         if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
408             PrintGifError(GifFile->Error);
409             exit(EXIT_FAILURE);
410         }
411         switch (RecordType) {
412             case IMAGE_DESC_RECORD_TYPE:
413                 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
414                     PrintGifError(GifFile->Error);
415                     exit(EXIT_FAILURE);
416                 }
417                 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
418                 Col = GifFile->Image.Left;
419                 Width = GifFile->Image.Width;
420                 Height = GifFile->Image.Height;
421                 GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]:     ",
422                     PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height);
423                 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
424                    GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
425                     fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
426                     exit(EXIT_FAILURE);
427                 }
428                 if (GifFile->Image.Interlace) {
429                     /* Need to perform 4 passes on the images: */
430                     for (Count = i = 0; i < 4; i++)
431                         for (j = Row + InterlacedOffset[i]; j < Row + Height;
432                                                  j += InterlacedJumps[i]) {
433                             GifQprintf("\b\b\b\b%-4d", Count++);
434                             if (DGifGetLine(GifFile, &ScreenBuffer[j][Col],
435                                 Width) == GIF_ERROR) {
436                                 PrintGifError(GifFile->Error);
437                                 exit(EXIT_FAILURE);
438                             }
439                         }
440                 }
441                 else {
442                     for (i = 0; i < Height; i++) {
443                         GifQprintf("\b\b\b\b%-4d", i);
444                         if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col],
445                                 Width) == GIF_ERROR) {
446                             PrintGifError(GifFile->Error);
447                             exit(EXIT_FAILURE);
448                         }
449                     }
450                 }
451                 break;
452             case EXTENSION_RECORD_TYPE:
453                 /* Skip any extension blocks in file: */
454                 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
455                     PrintGifError(GifFile->Error);
456                     exit(EXIT_FAILURE);
457                 }
458                 while (Extension != NULL) {
459                     if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
460                         PrintGifError(GifFile->Error);
461                         exit(EXIT_FAILURE);
462                     }
463                 }
464                 break;
465             case TERMINATE_RECORD_TYPE:
466                 break;
467             default:                /* Should be trapped by DGifGetRecordType. */
468                 break;
469         }
470     } while (RecordType != TERMINATE_RECORD_TYPE);
471
472     /* Lets dump it - set the global variables required and do it: */
473     ColorMap = (GifFile->Image.ColorMap
474                 ? GifFile->Image.ColorMap
475                 : GifFile->SColorMap);
476     if (ColorMap == NULL) {
477         fprintf(stderr, "Gif Image does not have a colormap\n");
478         exit(EXIT_FAILURE);
479     }
480
481     /* check that the background color isn't garbage (SF bug #87) */
482     if (GifFile->SBackGroundColor < 0 || GifFile->SBackGroundColor >= ColorMap->ColorCount) {
483         fprintf(stderr, "Background color out of range for colormap\n");
484         exit(EXIT_FAILURE);
485     }
486
487     DumpScreen2RGB(OutFileName, OneFileFlag,
488                    ColorMap,
489                    ScreenBuffer, 
490                    GifFile->SWidth, GifFile->SHeight);
491
492     (void)free(ScreenBuffer);
493
494     if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) {
495         PrintGifError(Error);
496         exit(EXIT_FAILURE);
497     }
498
499 }
500
501 /******************************************************************************
502 * Interpret the command line and scan the given GIF file.
503 ******************************************************************************/
504 int main(int argc, char **argv)
505 {
506     bool Error, OutFileFlag = false, ColorFlag = false, SizeFlag = false;
507     int NumFiles, Width = 0, Height = 0, ExpNumOfColors = 8;
508     char *OutFileName,
509         **FileName = NULL;
510     static bool
511         OneFileFlag = false,
512         HelpFlag = false;
513
514     if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint,
515                 &ColorFlag, &ExpNumOfColors, &SizeFlag, &Width, &Height, 
516                 &OneFileFlag, &OutFileFlag, &OutFileName,
517                 &HelpFlag, &NumFiles, &FileName)) != false ||
518                 (NumFiles > 1 && !HelpFlag)) {
519         if (Error)
520             GAPrintErrMsg(Error);
521         else if (NumFiles > 1)
522             GIF_MESSAGE("Error in command line parsing - one input file please.");
523         GAPrintHowTo(CtrlStr);
524         exit(EXIT_FAILURE);
525     }
526
527     if (HelpFlag) {
528         (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
529         GAPrintHowTo(CtrlStr);
530         exit(EXIT_SUCCESS);
531     }
532     if (!OutFileFlag) OutFileName = NULL;
533
534     if (SizeFlag && Width > 0 && Height > 0)
535         RGB2GIF(OneFileFlag, NumFiles, *FileName, 
536                 ExpNumOfColors, Width, Height);
537     else
538         GIF2RGB(NumFiles, *FileName, OneFileFlag, OutFileName);
539
540     return 0;
541 }
542
543 /* end */