Imported Upstream version 5.1.9 into tizen
[platform/upstream/giflib.git] / 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                 strncat(OneFileName, Postfixes[i], 
100                         sizeof(OneFileName) - 1 - strlen(OneFileName));
101
102                 if ((rgbfp[i] = fopen(OneFileName, "rb")) == NULL)
103                     GIF_EXIT("Can't open input file name.");
104             }
105         }
106     }
107     else {
108         OneFileFlag = true;
109
110 #ifdef _WIN32
111         _setmode(0, O_BINARY);
112 #endif /* _WIN32 */
113
114         rgbfp[0] = stdin;
115     }
116
117     GifQprintf("\n%s: RGB image:     ", PROGRAM_NAME);
118
119     if (OneFileFlag) {
120         GifByteType *Buffer, *BufferP;
121
122         if ((Buffer = (GifByteType *) malloc(Width * 3)) == NULL)
123             GIF_EXIT("Failed to allocate memory required, aborted.");
124
125         for (i = 0; i < Height; i++) {
126             int j;
127             GifQprintf("\b\b\b\b%-4d", i);
128             if (fread(Buffer, Width * 3, 1, rgbfp[0]) != 1)
129                 GIF_EXIT("Input file(s) terminated prematurly.");
130             for (j = 0, BufferP = Buffer; j < Width; j++) {
131                 *RedP++ = *BufferP++;
132                 *GreenP++ = *BufferP++;
133                 *BlueP++ = *BufferP++;
134             }
135         }
136
137         free((char *) Buffer);
138         fclose(rgbfp[0]);
139     }
140     else {
141         for (i = 0; i < Height; i++) {
142             GifQprintf("\b\b\b\b%-4d", i);
143             if (fread(RedP, Width, 1, rgbfp[0]) != 1 ||
144                 fread(GreenP, Width, 1, rgbfp[1]) != 1 ||
145                 fread(BlueP, Width, 1, rgbfp[2]) != 1)
146                 GIF_EXIT("Input file(s) terminated prematurly.");
147             RedP += Width;
148             GreenP += Width;
149             BlueP += Width;
150         }
151
152         fclose(rgbfp[0]);
153         fclose(rgbfp[1]);
154         fclose(rgbfp[2]);
155     }
156 }
157
158 /******************************************************************************
159  Save the GIF resulting image.
160 ******************************************************************************/
161 static void SaveGif(GifByteType *OutputBuffer,
162                     int Width, int Height,
163                     int ExpColorMapSize, ColorMapObject *OutputColorMap)
164 {
165     int i, Error;
166     GifFileType *GifFile;
167     GifByteType *Ptr = OutputBuffer;
168
169     /* Open stdout for the output file: */
170     if ((GifFile = EGifOpenFileHandle(1, &Error)) == NULL) {
171         PrintGifError(Error);
172         exit(EXIT_FAILURE);
173     }
174
175     if (EGifPutScreenDesc(GifFile,
176                           Width, Height, ExpColorMapSize, 0,
177                           OutputColorMap) == GIF_ERROR ||
178         EGifPutImageDesc(GifFile,
179                          0, 0, Width, Height, false, NULL) == GIF_ERROR) {
180         PrintGifError(Error);
181         exit(EXIT_FAILURE);
182     }
183
184     GifQprintf("\n%s: Image 1 at (%d, %d) [%dx%d]:     ",
185                PROGRAM_NAME, GifFile->Image.Left, GifFile->Image.Top,
186                GifFile->Image.Width, GifFile->Image.Height);
187
188     for (i = 0; i < Height; i++) {
189         if (EGifPutLine(GifFile, Ptr, Width) == GIF_ERROR)
190             exit(EXIT_FAILURE);
191         GifQprintf("\b\b\b\b%-4d", Height - i - 1);
192
193         Ptr += Width;
194     }
195
196     if (EGifCloseFile(GifFile, &Error) == GIF_ERROR) {
197         PrintGifError(Error);
198         exit(EXIT_FAILURE);
199     }
200 }
201
202 /******************************************************************************
203  Close output file (if open), and exit.
204 ******************************************************************************/
205 static void RGB2GIF(bool OneFileFlag, int NumFiles, char *FileName,
206                     int ExpNumOfColors, int Width, int Height)
207 {
208     int ColorMapSize;
209
210     GifByteType *RedBuffer = NULL, *GreenBuffer = NULL, *BlueBuffer = NULL,
211         *OutputBuffer = NULL;
212     ColorMapObject *OutputColorMap = NULL;
213
214     ColorMapSize = 1 << ExpNumOfColors;
215
216     if (NumFiles == 1) {
217         LoadRGB(FileName, OneFileFlag,
218                 &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height);
219     }
220     else {
221         LoadRGB(NULL, OneFileFlag,
222                 &RedBuffer, &GreenBuffer, &BlueBuffer, Width, Height);
223     }
224
225     if ((OutputColorMap = GifMakeMapObject(ColorMapSize, NULL)) == NULL ||
226         (OutputBuffer = (GifByteType *) malloc(Width * Height *
227                                             sizeof(GifByteType))) == NULL)
228         GIF_EXIT("Failed to allocate memory required, aborted.");
229
230     if (GifQuantizeBuffer(Width, Height, &ColorMapSize,
231                        RedBuffer, GreenBuffer, BlueBuffer,
232                        OutputBuffer, OutputColorMap->Colors) == GIF_ERROR)
233         exit(EXIT_FAILURE);
234     free((char *) RedBuffer);
235     free((char *) GreenBuffer);
236     free((char *) BlueBuffer);
237
238     SaveGif(OutputBuffer, Width, Height, ExpNumOfColors, OutputColorMap);
239 }
240
241 /******************************************************************************
242  The real screen dumping routine.
243 ******************************************************************************/
244 static void DumpScreen2RGB(char *FileName, int OneFileFlag,
245                            ColorMapObject *ColorMap,
246                            GifRowType *ScreenBuffer,
247                            int ScreenWidth, int ScreenHeight)
248 {
249     int i, j;
250     GifRowType GifRow;
251     GifColorType *ColorMapEntry;
252     FILE *rgbfp[3];
253
254     if (FileName != NULL) {
255         if (OneFileFlag) {
256             if ((rgbfp[0] = fopen(FileName, "wb")) == NULL)
257             GIF_EXIT("Can't open input file name.");
258         } else {
259             static char *Postfixes[] = { ".R", ".G", ".B" };
260             char OneFileName[80];
261
262             for (i = 0; i < 3; i++) {
263                 strncpy(OneFileName, FileName, sizeof(OneFileName)-1);
264                 strncat(OneFileName, Postfixes[i], 
265                         sizeof(OneFileName) - 1 - strlen(OneFileName));
266     
267                 if ((rgbfp[i] = fopen(OneFileName, "wb")) == NULL) {
268                     GIF_EXIT("Can't open input file name.");
269                 }
270             }
271         }
272     } else {
273         OneFileFlag = true;
274
275 #ifdef _WIN32
276         _setmode(1, O_BINARY);
277 #endif /* _WIN32 */
278         
279         rgbfp[0] = stdout;
280     }
281
282     if (ColorMap == NULL) {
283         fprintf(stderr, "Color map pointer is NULL.\n");
284         exit(EXIT_FAILURE);
285     }
286
287     if (OneFileFlag) {
288         unsigned char *Buffer, *BufferP;
289
290         if ((Buffer = (unsigned char *) malloc(ScreenWidth * 3)) == NULL)
291             GIF_EXIT("Failed to allocate memory required, aborted.");
292         for (i = 0; i < ScreenHeight; i++) {
293             GifRow = ScreenBuffer[i];
294             GifQprintf("\b\b\b\b%-4d", ScreenHeight - i);
295             for (j = 0, BufferP = Buffer; j < ScreenWidth; j++) {
296                 ColorMapEntry = &ColorMap->Colors[GifRow[j]];
297                 *BufferP++ = ColorMapEntry->Red;
298                 *BufferP++ = ColorMapEntry->Green;
299                 *BufferP++ = ColorMapEntry->Blue;
300             }
301             if (fwrite(Buffer, ScreenWidth * 3, 1, rgbfp[0]) != 1)
302                 GIF_EXIT("Write to file(s) failed.");
303         }
304
305         free((char *) Buffer);
306         fclose(rgbfp[0]);
307     } else {
308         unsigned char *Buffers[3];
309
310         if ((Buffers[0] = (unsigned char *) malloc(ScreenWidth)) == NULL ||
311             (Buffers[1] = (unsigned char *) malloc(ScreenWidth)) == NULL ||
312             (Buffers[2] = (unsigned char *) malloc(ScreenWidth)) == NULL)
313             GIF_EXIT("Failed to allocate memory required, aborted.");
314
315         for (i = 0; i < ScreenHeight; i++) {
316             GifRow = ScreenBuffer[i];
317             GifQprintf("\b\b\b\b%-4d", ScreenHeight - i);
318             for (j = 0; j < ScreenWidth; j++) {
319                 ColorMapEntry = &ColorMap->Colors[GifRow[j]];
320                 Buffers[0][j] = ColorMapEntry->Red;
321                 Buffers[1][j] = ColorMapEntry->Green;
322                 Buffers[2][j] = ColorMapEntry->Blue;
323             }
324             if (fwrite(Buffers[0], ScreenWidth, 1, rgbfp[0]) != 1 ||
325                 fwrite(Buffers[1], ScreenWidth, 1, rgbfp[1]) != 1 ||
326                 fwrite(Buffers[2], ScreenWidth, 1, rgbfp[2]) != 1)
327                 GIF_EXIT("Write to file(s) failed.");
328         }
329
330         free((char *) Buffers[0]);
331         free((char *) Buffers[1]);
332         free((char *) Buffers[2]);
333         fclose(rgbfp[0]);
334         fclose(rgbfp[1]);
335         fclose(rgbfp[2]);
336     }
337 }
338
339 static void GIF2RGB(int NumFiles, char *FileName, 
340                     bool OneFileFlag, 
341                     char *OutFileName)
342 {
343     int i, j, Size, Row, Col, Width, Height, ExtCode, Count;
344     GifRecordType RecordType;
345     GifByteType *Extension;
346     GifRowType *ScreenBuffer;
347     GifFileType *GifFile;
348     int
349         InterlacedOffset[] = { 0, 4, 2, 1 }, /* The way Interlaced image should. */
350         InterlacedJumps[] = { 8, 8, 4, 2 };    /* be read - offsets and jumps... */
351     int ImageNum = 0;
352     ColorMapObject *ColorMap;
353     int Error;
354
355     if (NumFiles == 1) {
356         int Error;
357         if ((GifFile = DGifOpenFileName(FileName, &Error)) == NULL) {
358             PrintGifError(Error);
359             exit(EXIT_FAILURE);
360         }
361     }
362     else {
363         int Error;
364         /* Use stdin instead: */
365         if ((GifFile = DGifOpenFileHandle(0, &Error)) == NULL) {
366             PrintGifError(Error);
367             exit(EXIT_FAILURE);
368         }
369     }
370
371     if (GifFile->SHeight == 0 || GifFile->SWidth == 0) {
372         fprintf(stderr, "Image of width or height 0\n");
373         exit(EXIT_FAILURE);
374     }
375
376     /* 
377      * Allocate the screen as vector of column of rows. Note this
378      * screen is device independent - it's the screen defined by the
379      * GIF file parameters.
380      */
381     if ((ScreenBuffer = (GifRowType *)
382         malloc(GifFile->SHeight * sizeof(GifRowType))) == NULL)
383             GIF_EXIT("Failed to allocate memory required, aborted.");
384
385     Size = GifFile->SWidth * sizeof(GifPixelType);/* Size in bytes one row.*/
386     if ((ScreenBuffer[0] = (GifRowType) malloc(Size)) == NULL) /* First row. */
387         GIF_EXIT("Failed to allocate memory required, aborted.");
388
389     for (i = 0; i < GifFile->SWidth; i++)  /* Set its color to BackGround. */
390         ScreenBuffer[0][i] = GifFile->SBackGroundColor;
391     for (i = 1; i < GifFile->SHeight; i++) {
392         /* Allocate the other rows, and set their color to background too: */
393         if ((ScreenBuffer[i] = (GifRowType) malloc(Size)) == NULL)
394             GIF_EXIT("Failed to allocate memory required, aborted.");
395
396         memcpy(ScreenBuffer[i], ScreenBuffer[0], Size);
397     }
398
399     /* Scan the content of the GIF file and load the image(s) in: */
400     do {
401         if (DGifGetRecordType(GifFile, &RecordType) == GIF_ERROR) {
402             PrintGifError(GifFile->Error);
403             exit(EXIT_FAILURE);
404         }
405         switch (RecordType) {
406             case IMAGE_DESC_RECORD_TYPE:
407                 if (DGifGetImageDesc(GifFile) == GIF_ERROR) {
408                     PrintGifError(GifFile->Error);
409                     exit(EXIT_FAILURE);
410                 }
411                 Row = GifFile->Image.Top; /* Image Position relative to Screen. */
412                 Col = GifFile->Image.Left;
413                 Width = GifFile->Image.Width;
414                 Height = GifFile->Image.Height;
415                 GifQprintf("\n%s: Image %d at (%d, %d) [%dx%d]:     ",
416                     PROGRAM_NAME, ++ImageNum, Col, Row, Width, Height);
417                 if (GifFile->Image.Left + GifFile->Image.Width > GifFile->SWidth ||
418                    GifFile->Image.Top + GifFile->Image.Height > GifFile->SHeight) {
419                     fprintf(stderr, "Image %d is not confined to screen dimension, aborted.\n",ImageNum);
420                     exit(EXIT_FAILURE);
421                 }
422                 if (GifFile->Image.Interlace) {
423                     /* Need to perform 4 passes on the images: */
424                     for (Count = i = 0; i < 4; i++)
425                         for (j = Row + InterlacedOffset[i]; j < Row + Height;
426                                                  j += InterlacedJumps[i]) {
427                             GifQprintf("\b\b\b\b%-4d", Count++);
428                             if (DGifGetLine(GifFile, &ScreenBuffer[j][Col],
429                                 Width) == GIF_ERROR) {
430                                 PrintGifError(GifFile->Error);
431                                 exit(EXIT_FAILURE);
432                             }
433                         }
434                 }
435                 else {
436                     for (i = 0; i < Height; i++) {
437                         GifQprintf("\b\b\b\b%-4d", i);
438                         if (DGifGetLine(GifFile, &ScreenBuffer[Row++][Col],
439                                 Width) == GIF_ERROR) {
440                             PrintGifError(GifFile->Error);
441                             exit(EXIT_FAILURE);
442                         }
443                     }
444                 }
445                 break;
446             case EXTENSION_RECORD_TYPE:
447                 /* Skip any extension blocks in file: */
448                 if (DGifGetExtension(GifFile, &ExtCode, &Extension) == GIF_ERROR) {
449                     PrintGifError(GifFile->Error);
450                     exit(EXIT_FAILURE);
451                 }
452                 while (Extension != NULL) {
453                     if (DGifGetExtensionNext(GifFile, &Extension) == GIF_ERROR) {
454                         PrintGifError(GifFile->Error);
455                         exit(EXIT_FAILURE);
456                     }
457                 }
458                 break;
459             case TERMINATE_RECORD_TYPE:
460                 break;
461             default:                /* Should be trapped by DGifGetRecordType. */
462                 break;
463         }
464     } while (RecordType != TERMINATE_RECORD_TYPE);
465     
466     /* Lets dump it - set the global variables required and do it: */
467     ColorMap = (GifFile->Image.ColorMap
468                 ? GifFile->Image.ColorMap
469                 : GifFile->SColorMap);
470     if (ColorMap == NULL) {
471         fprintf(stderr, "Gif Image does not have a colormap\n");
472         exit(EXIT_FAILURE);
473     }
474
475     /* check that the background color isn't garbage (SF bug #87) */
476     if (GifFile->SBackGroundColor < 0 || GifFile->SBackGroundColor >= ColorMap->ColorCount) {
477         fprintf(stderr, "Background color out of range for colormap\n");
478         exit(EXIT_FAILURE);
479     }
480
481     DumpScreen2RGB(OutFileName, OneFileFlag,
482                    ColorMap,
483                    ScreenBuffer, 
484                    GifFile->SWidth, GifFile->SHeight);
485
486     (void)free(ScreenBuffer);
487
488     if (DGifCloseFile(GifFile, &Error) == GIF_ERROR) {
489         PrintGifError(Error);
490         exit(EXIT_FAILURE);
491     }
492
493 }
494
495 /******************************************************************************
496 * Interpret the command line and scan the given GIF file.
497 ******************************************************************************/
498 int main(int argc, char **argv)
499 {
500     bool Error, OutFileFlag = false, ColorFlag = false, SizeFlag = false;
501     int NumFiles, Width = 0, Height = 0, ExpNumOfColors = 8;
502     char *OutFileName,
503         **FileName = NULL;
504     static bool
505         OneFileFlag = false,
506         HelpFlag = false;
507
508     if ((Error = GAGetArgs(argc, argv, CtrlStr, &GifNoisyPrint,
509                 &ColorFlag, &ExpNumOfColors, &SizeFlag, &Width, &Height, 
510                 &OneFileFlag, &OutFileFlag, &OutFileName,
511                 &HelpFlag, &NumFiles, &FileName)) != false ||
512                 (NumFiles > 1 && !HelpFlag)) {
513         if (Error)
514             GAPrintErrMsg(Error);
515         else if (NumFiles > 1)
516             GIF_MESSAGE("Error in command line parsing - one input file please.");
517         GAPrintHowTo(CtrlStr);
518         exit(EXIT_FAILURE);
519     }
520
521     if (HelpFlag) {
522         (void)fprintf(stderr, VersionStr, GIFLIB_MAJOR, GIFLIB_MINOR);
523         GAPrintHowTo(CtrlStr);
524         exit(EXIT_SUCCESS);
525     }
526     if (!OutFileFlag) OutFileName = NULL;
527
528     if (SizeFlag && Width > 0 && Height > 0)
529         RGB2GIF(OneFileFlag, NumFiles, *FileName, 
530                 ExpNumOfColors, Width, Height);
531     else
532         GIF2RGB(NumFiles, *FileName, OneFileFlag, OutFileName);
533
534     return 0;
535 }
536
537 /* end */