Add armoring option
[platform/upstream/giflib.git] / gif2rgb.c
1 /*****************************************************************************
2
3 gif2rgb - convert GIF to 24-bit RGB pixel triples or vice-versa
4
5 SPDX-License-Identifier: MIT
6
7 *****************************************************************************/
8
9 /***************************************************************************
10
11 Toshio Kuratomi had written this in a comment about the rgb2gif code:
12
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.
17
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.
22
23 ***************************************************************************/
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 #include <string.h>
29 #include <stdbool.h>
30 #include <fcntl.h>
31
32 #ifdef _WIN32
33 #include <io.h>
34 #endif /* _WIN32 */
35
36 #include "gif_lib.h"
37 #include "getarg.h"
38
39 #define PROGRAM_NAME    "gif2rgb"
40
41 __attribute__((__section__(".tizen.build-id")))
42 static char
43     VersionStr[] =
44         PROGRAM_NAME
45         VERSION_COOKIE
46         "       Gershon Elber,  "
47         __DATE__ ",   " __TIME__ "\n"
48         "(C) Copyright 1989 Gershon Elber.\n";
49 static char
50     *CtrlStr =
51         PROGRAM_NAME
52         " v%- c%-#Colors!d s%-Width|Height!d!d 1%- o%-OutFileName!s h%- GifFile!*s";
53
54 static void LoadRGB(char *FileName,
55                     int OneFileFlag,
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);
63
64 /******************************************************************************
65  Load RGB file into internal frame buffer.
66 ******************************************************************************/
67 static void LoadRGB(char *FileName,
68                     int OneFileFlag,
69                     GifByteType **RedBuffer,
70                     GifByteType **GreenBuffer,
71                     GifByteType **BlueBuffer,
72                     int Width, int Height)
73 {
74     int i;
75     unsigned long Size;
76     GifByteType *RedP, *GreenP, *BlueP;
77     FILE *rgbfp[3];
78
79     Size = ((long) Width) * Height * sizeof(GifByteType);
80
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.");
85
86     RedP = *RedBuffer;
87     GreenP = *GreenBuffer;
88     BlueP = *BlueBuffer;
89
90     if (FileName != NULL) {
91         if (OneFileFlag) {
92             if ((rgbfp[0] = fopen(FileName, "rb")) == NULL)
93                 GIF_EXIT("Can't open input file name.");
94         }
95         else {
96             static char *Postfixes[] = { ".R", ".G", ".B" };
97             char OneFileName[80];
98
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));
103
104                 if ((rgbfp[i] = fopen(OneFileName, "rb")) == NULL)
105                     GIF_EXIT("Can't open input file name.");
106             }
107         }
108     }
109     else {
110         OneFileFlag = true;
111
112 #ifdef _WIN32
113         _setmode(0, O_BINARY);
114 #endif /* _WIN32 */
115
116         rgbfp[0] = stdin;
117     }
118
119     GifQprintf("\n%s: RGB image:     ", PROGRAM_NAME);
120
121     if (OneFileFlag) {
122         GifByteType *Buffer, *BufferP;
123
124         if ((Buffer = (GifByteType *) malloc(Width * 3)) == NULL)
125             GIF_EXIT("Failed to allocate memory required, aborted.");
126
127         for (i = 0; i < Height; i++) {
128             int j;
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++;
136             }
137         }
138
139         free((char *) Buffer);
140         fclose(rgbfp[0]);
141     }
142     else {
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.");
149             RedP += Width;
150             GreenP += Width;
151             BlueP += Width;
152         }
153
154         fclose(rgbfp[0]);
155         fclose(rgbfp[1]);
156         fclose(rgbfp[2]);
157     }
158 }
159
160 /******************************************************************************
161  Save the GIF resulting image.
162 ******************************************************************************/
163 static void SaveGif(GifByteType *OutputBuffer,
164                     int Width, int Height,
165                     int ExpColorMapSize, ColorMapObject *OutputColorMap)
166 {
167     int i, Error;
168     GifFileType *GifFile;
169     GifByteType *Ptr = OutputBuffer;
170
171     /* Open stdout for the output file: */
172     if ((GifFile = EGifOpenFileHandle(1, &Error)) == NULL) {
173         PrintGifError(Error);
174         exit(EXIT_FAILURE);
175     }
176
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);
183         exit(EXIT_FAILURE);
184     }
185
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);
189
190     for (i = 0; i < Height; i++) {
191         if (EGifPutLine(GifFile, Ptr, Width) == GIF_ERROR)
192             exit(EXIT_FAILURE);
193         GifQprintf("\b\b\b\b%-4d", Height - i - 1);
194
195         Ptr += Width;
196     }
197
198     if (EGifCloseFile(GifFile, &Error) == GIF_ERROR) {
199         PrintGifError(Error);
200         exit(EXIT_FAILURE);
201     }
202 }
203
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)
209 {
210     int ColorMapSize;
211
212     GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL,
213         *OutputBuffer = NULL;
214     ColorMapObject *OutputColorMap = NULL;
215
216     ColorMapSize = 1 << ExpNumOfColors;
217
218     if (NumFiles == 1) {
219         LoadRGB(FileName, OneFileFlag,
220                 &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height);
221     }
222     else {
223         LoadRGB(NULL, OneFileFlag,
224                 &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height);
225     }
226
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.");
231
232     if (GifQuantizeBuffer(Width, Height, &ColorMapSize,
233                        RedBuffer, GreenBuffer, BlueBuffer,
234                        OutputBuffer, OutputColorMap->Colors) == GIF_ERROR)
235         exit(EXIT_FAILURE);
236     free((char *) RedBuffer);
237     free((char *) GreenBuffer);
238     free((char *) BlueBuffer);
239
240     SaveGif(OutputBuffer, Width, Height, ExpNumOfColors, OutputColorMap);
241 }
242
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)
250 {
251     int i, j;
252     GifRowType GifRow;
253     GifColorType *ColorMapEntry;
254     FILE *rgbfp[3];
255
256     if (FileName != NULL) {
257         if (OneFileFlag) {
258             if ((rgbfp[0] = fopen(FileName, "wb")) == NULL)
259             GIF_EXIT("Can't open input file name.");
260         } else {
261             static char *Postfixes[] = { ".R", ".G", ".B" };
262             char OneFileName[80];
263
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));
268     
269                 if ((rgbfp[i] = fopen(OneFileName, "wb")) == NULL) {
270                     GIF_EXIT("Can't open input file name.");
271                 }
272             }
273         }
274     } else {
275         OneFileFlag = true;
276
277 #ifdef _WIN32
278         _setmode(1, O_BINARY);
279 #endif /* _WIN32 */
280         
281         rgbfp[0] = stdout;
282     }
283
284     if (ColorMap == NULL) {
285         fprintf(stderr, "Color map pointer is NULL.\n");
286         exit(EXIT_FAILURE);
287     }
288
289     if (OneFileFlag) {
290         unsigned char *Buffer, *BufferP;
291
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;
302             }
303             if (fwrite(Buffer, ScreenWidth * 3, 1, rgbfp[0]) != 1)
304                 GIF_EXIT("Write to file(s) failed.");
305         }
306
307         free((char *) Buffer);
308         fclose(rgbfp[0]);
309     } else {
310         unsigned char *Buffers[3];
311
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.");
316
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;
325             }
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.");
330         }
331
332         free((char *) Buffers[0]);
333         free((char *) Buffers[1]);
334         free((char *) Buffers[2]);
335         fclose(rgbfp[0]);
336         fclose(rgbfp[1]);
337         fclose(rgbfp[2]);
338     }
339 }
340
341 static void GIF2RGB(int NumFiles, char *FileName, 
342                     bool OneFileFlag, 
343                     char *OutFileName)
344 {
345     int i, j, Size, Row, Col, Width, Height, ExtCode, Count;
346     GifRecordType RecordType;
347     GifByteType *Extension;
348     GifRowType *ScreenBuffer;
349     GifFileType *GifFile;
350     int
351         InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
352         InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
353     int ImageNum = 0;
354     ColorMapObject *ColorMap;
355     int Error;
356
357     if (NumFiles == 1) {
358         int Error;
359         if ((GifFile = DGifOpenFileName(FileName, &Error)) == NULL) {
360             PrintGifError(Error);
361             exit(EXIT_FAILURE);
362         }
363     }
364     else {
365         int Error;
366         /* Use stdin instead: */
367         if ((GifFile = DGifOpenFileHandle(0, &Error)) == NULL) {
368             PrintGifError(Error);
369             exit(EXIT_FAILURE);
370         }
371     }
372
373     if (GifFile->SHeight == 0 || GifFile->SWidth == 0) {
374         fprintf(stderr, "Image of width or height 0\n");
375         exit(EXIT_FAILURE);
376     }
377
378     /* 
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.
382      */
383     if ((ScreenBuffer = (GifRowType *)
384         malloc(GifFile->SHeight * sizeof(GifRowType))) == NULL)
385             GIF_EXIT("Failed to allocate memory required, aborted.");
386
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.");
390
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.");
397
398         memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
399     }
400
401     /* Scan the content of the GIF file and load the image(s) in: */
402     do {
403         if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
404             PrintGifError(GifFile->Error);
405             exit(EXIT_FAILURE);
406         }
407         switch (RecordType) {
408             case IMAGE_DESC_RECORD_TYPE:
409                 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
410                     PrintGifError(GifFile->Error);
411                     exit(EXIT_FAILURE);
412                 }
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);
422                     exit(EXIT_FAILURE);
423                 }
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);
433                                 exit(EXIT_FAILURE);
434                             }
435                         }
436                 }
437                 else {
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);
443                             exit(EXIT_FAILURE);
444                         }
445                     }
446                 }
447                 break;
448             case EXTENSION_RECORD_TYPE:
449                 /* Skip any extension blocks in file: */
450                 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
451                     PrintGifError(GifFile->Error);
452                     exit(EXIT_FAILURE);
453                 }
454                 while (Extension != NULL) {
455                     if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
456                         PrintGifError(GifFile->Error);
457                         exit(EXIT_FAILURE);
458                     }
459                 }
460                 break;
461             case TERMINATE_RECORD_TYPE:
462                 break;
463             default:                /* Should be trapped by DGifGetRecordType. */
464                 break;
465         }
466     } while (RecordType != TERMINATE_RECORD_TYPE);
467     
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");
474         exit(EXIT_FAILURE);
475     }
476
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");
480         exit(EXIT_FAILURE);
481     }
482
483     DumpScreen2RGB(OutFileName, OneFileFlag,
484                    ColorMap,
485                    ScreenBuffer, 
486                    GifFile->SWidth, GifFile->SHeight);
487
488     (void)free(ScreenBuffer);
489
490     if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) {
491         PrintGifError(Error);
492         exit(EXIT_FAILURE);
493     }
494
495 }
496
497 /******************************************************************************
498 * Interpret the command line and scan the given GIF file.
499 ******************************************************************************/
500 int main(int argc, char **argv)
501 {
502     bool Error, OutFileFlag = false, ColorFlag = false, SizeFlag = false;
503     int NumFiles, Width = 0, Height = 0, ExpNumOfColors = 8;
504     char *OutFileName,
505         **FileName = NULL;
506     static bool
507         OneFileFlag = false,
508         HelpFlag = false;
509
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)) {
515         if (Error)
516             GAPrintErrMsg(Error);
517         else if (NumFiles > 1)
518             GIF_MESSAGE("Error in command line parsing - one input file please.");
519         GAPrintHowTo(CtrlStr);
520         exit(EXIT_FAILURE);
521     }
522
523     if (HelpFlag) {
524         (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
525         GAPrintHowTo(CtrlStr);
526         exit(EXIT_SUCCESS);
527     }
528     if (!OutFileFlag) OutFileName = NULL;
529
530     if (SizeFlag && Width > 0 && Height > 0)
531         RGB2GIF(OneFileFlag, NumFiles, *FileName, 
532                 ExpNumOfColors, Width, Height);
533     else
534         GIF2RGB(NumFiles, *FileName, OneFileFlag, OutFileName);
535
536     return 0;
537 }
538
539 /* end */