Coin Logo Coin3D is Free Software,
published under the BSD 3-clause license.
https://bitbucket.org/Coin3D/
http://www.kongsberg.com/kogt/
simage_gif.c
Go to the documentation of this file.
1 /*
2  * Copyright (c) Kongsberg Oil & Gas Technologies
3  *
4  * Permission to use, copy, modify, and distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif /* HAVE_CONFIG_H */
25 
26 #ifdef HAVE_GIFLIB
27 
28 #include <simage_gif.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <stdio.h>
32 #include <gif_lib.h>
33 #include <gif_util.h>
34 
35 #if GIFLIB_MAJOR > 5 || GIFLIB_MAJOR == 5 && GIFLIB_MINOR >= 1
36 #define DGifCloseFile(gif) DGifCloseFile(gif, NULL)
37 #define EGifCloseFile(gif) EGifCloseFile(gif, NULL)
38 #define DGifOpenFileName(gif) DGifOpenFileName(gif, NULL)
39 #define EGifOpenFileName(gif, exist) EGifOpenFileName(gif, exist, NULL)
40 //see https://sourceforge.net/p/giflib/mailman/message/29367749/
41 #define QuantizeBuffer GifQuantizeBuffer
42 #define MakeMapObject GifMakeMapObject
43 #define FreeMapObject GifFreeMapObject
44 #endif
45 
46 #ifndef FALSE
47 #define FALSE false
48 #endif
49 
50 enum {
52  ERR_OPEN,
53  ERR_READ,
54  ERR_WRITE,
55  ERR_MEM
56 };
57 
58 static int giferror = ERR_NO_ERROR;
59 
60 int
61 simage_gif_error(char * buffer, int buflen)
62 {
63  switch (giferror) {
64  case ERR_OPEN:
65  strncpy(buffer, "GIF loader: Error opening file", buflen);
66  break;
67  case ERR_READ:
68  strncpy(buffer, "GIF loader: Error reading file", buflen);
69  break;
70  case ERR_WRITE:
71  strncpy(buffer, "GIF loader: Error writing file", buflen);
72  break;
73  case ERR_MEM:
74  strncpy(buffer, "GIF loader: Out of memory error", buflen);
75  break;
76  }
77  return giferror;
78 }
79 
80 int
81 simage_gif_identify(const char *filename,
82  const unsigned char *header,
83  int headerlen)
84 {
85  return (headerlen >= 3)
86  && header[0] == 'G'
87  && header[1] == 'I'
88  && header[2] == 'F';
89 }
90 
91 static void
92 decode_row(GifFileType * giffile,
93  unsigned char * buffer,
94  unsigned char * rowdata,
95  int x, int y, int len,
96  int transparent)
97 {
98  GifColorType * cmentry;
99  ColorMapObject * colormap;
100  int colormapsize;
101  unsigned char col;
102  unsigned char * ptr;
103 
104  y = giffile->SHeight - (y+1);
105  ptr = buffer + (giffile->SWidth * y + x) * 4;
106 
107  colormap = (giffile->Image.ColorMap
108  ? giffile->Image.ColorMap
109  : giffile->SColorMap);
110  colormapsize = colormap ? colormap->ColorCount : 255;
111 
112  while (len--) {
113  col = *rowdata++;
114  if (col >= colormapsize) col = 0; /* just in case */
115  cmentry = colormap ? &colormap->Colors[col] : NULL;
116  if (cmentry) {
117  *ptr++ = cmentry->Red;
118  *ptr++ = cmentry->Green;
119  *ptr++ = cmentry->Blue;
120  }
121  else {
122  *ptr++ = col;
123  *ptr++ = col;
124  *ptr++ = col;
125  }
126  *ptr++ = (col == transparent ? 0x00 : 0xff);
127  }
128 }
129 
130 unsigned char *
131 simage_gif_load(const char *filename,
132  int *width_ret,
133  int *height_ret,
134  int *numComponents_ret)
135 {
136  int i, j, n, row, col, width, height, extcode;
137  unsigned char * rowdata;
138  unsigned char * buffer, * ptr;
139  unsigned char bg;
140  int transparent;
141  GifRecordType recordtype;
142  GifByteType * extension;
143  GifFileType * giffile;
144  GifColorType * bgcol;
145 
146  /* The way an interlaced image should be read - offsets and jumps */
147  int interlacedoffset[] = { 0, 4, 2, 1 };
148  int interlacedjumps[] = { 8, 8, 4, 2 };
149 
150  giffile = DGifOpenFileName(filename);
151  if (!giffile) {
152  giferror = ERR_OPEN;
153  return NULL;
154  }
155 
156  transparent = -1; /* no transparent color by default */
157 
158  n = giffile->SHeight * giffile->SWidth;
159  buffer = (unsigned char*) malloc(n * 4);
160  if (!buffer) {
161  giferror = ERR_MEM;
162  return NULL;
163  }
164  rowdata = (unsigned char*) malloc(giffile->SWidth);
165  if (!rowdata) {
166  giferror = ERR_MEM;
167  free(buffer);
168  return NULL;
169  }
170 
171  bg = giffile->SBackGroundColor;
172  if (giffile->SColorMap && bg < giffile->SColorMap->ColorCount) {
173  bgcol = &giffile->SColorMap->Colors[bg];
174  }
175  else bgcol = NULL;
176  ptr = buffer;
177  for (i = 0; i < n; i++) {
178  if (bgcol) {
179  *ptr++ = bgcol->Red;
180  *ptr++ = bgcol->Green;
181  *ptr++ = bgcol->Blue;
182  *ptr++ = 0xff;
183  }
184  else {
185  *ptr++ = 0x00;
186  *ptr++ = 0x00;
187  *ptr++ = 0x00;
188  *ptr++ = 0xff;
189  }
190  }
191 
192  /* Scan the content of the GIF file and load the image(s) in: */
193  do {
194  if (DGifGetRecordType(giffile, &recordtype) == GIF_ERROR) {
195  giferror = ERR_READ;
196  free(buffer);
197  free(rowdata);
198  return NULL;
199  }
200  switch (recordtype) {
201  case IMAGE_DESC_RECORD_TYPE:
202  if (DGifGetImageDesc(giffile) == GIF_ERROR) {
203  giferror = ERR_READ;
204  free(buffer);
205  free(rowdata);
206  return NULL;
207  }
208  row = giffile->Image.Top; /* subimage position in composite image */
209  col = giffile->Image.Left;
210  width = giffile->Image.Width;
211  height = giffile->Image.Height;
212  if (giffile->Image.Left + giffile->Image.Width > giffile->SWidth ||
213  giffile->Image.Top + giffile->Image.Height > giffile->SHeight) {
214  /* image is not confined to screen dimension */
215  giferror = ERR_READ;
216  free(buffer);
217  free(rowdata);
218  return NULL;
219  }
220  if (giffile->Image.Interlace) {
221  /* Need to perform 4 passes on the images: */
222  for (i = 0; i < 4; i++) {
223  for (j = row + interlacedoffset[i]; j < row + height;
224  j += interlacedjumps[i]) {
225  if (DGifGetLine(giffile, rowdata, width) == GIF_ERROR) {
226  giferror = ERR_READ;
227  free(buffer);
228  free(rowdata);
229  return NULL;
230  }
231  else decode_row(giffile, buffer, rowdata, col, j, width, transparent);
232  }
233  }
234  }
235  else {
236  for (i = 0; i < height; i++, row++) {
237  if (DGifGetLine(giffile, rowdata, width) == GIF_ERROR) {
238  giferror = ERR_READ;
239  free(buffer);
240  free(rowdata);
241  return NULL;
242  }
243  else decode_row(giffile, buffer, rowdata, col, row, width, transparent);
244  }
245  }
246  break;
247  case EXTENSION_RECORD_TYPE:
248  /* Skip any extension blocks in file: */
249  if (DGifGetExtension(giffile, &extcode, &extension) == GIF_ERROR) {
250  giferror = ERR_READ;
251  free(buffer);
252  free(rowdata);
253  return NULL;
254  }
255  /* transparent test from the gimp gif-plugin. Open Source rulez! */
256  else if (extcode == 0xf9) {
257  if (extension[0] >= 4 && extension[1] & 0x1) transparent = extension[4];
258  else transparent = -1;
259  }
260  while (extension != NULL) {
261  if (DGifGetExtensionNext(giffile, &extension) == GIF_ERROR) {
262  giferror = ERR_READ;
263  free(buffer);
264  free(rowdata);
265  return NULL;
266  }
267  }
268  break;
269  case TERMINATE_RECORD_TYPE:
270  break;
271  default: /* Should be trapped by DGifGetRecordType. */
272  break;
273  }
274  }
275  while (recordtype != TERMINATE_RECORD_TYPE);
276 
277  free(rowdata);
278  *width_ret = giffile->SWidth;
279  *height_ret = giffile->SHeight;
280  *numComponents_ret = 4;
281  DGifCloseFile(giffile);
282  return buffer;
283 }
284 
285 int
286 simage_gif_save(const char * filename,
287  const unsigned char * bytes,
288  int width,
289  int height,
290  int numcomponents)
291 {
292  const unsigned char * bytes_ptr = bytes;
293  int i, colormapsize = 256;
294  int bufsize = width * height;
295  ColorMapObject * cmapobj;
296  GifByteType * outbuf = NULL, * outbuf_ptr = NULL;
297  GifByteType * rgbbuf = NULL, * rgbbuf_ptr = NULL;
298  GifFileType * giffile = NULL;
299 
300  /* allocate memory for the channels of the rgb buffer */
301  if (!(rgbbuf = (GifByteType*)malloc(bufsize*3))) {
302  giferror = ERR_MEM;
303  return 0;
304  }
305 
306  rgbbuf_ptr = rgbbuf;
307 
308  /* FIXME: ignoring alpha channel. should find a way to set the
309  background value appropriately. 20060108 tamer. */
310  switch (numcomponents) {
311  case 3:
312  case 4:
313  /* split up the rgb values into their respective channels */
314  for (i=0; i < bufsize; i++) {
315  rgbbuf_ptr[0] = *bytes_ptr++; /* red */
316  rgbbuf_ptr[bufsize] = *bytes_ptr++; /* green */
317  rgbbuf_ptr[bufsize*2] = *bytes_ptr++; /* blue */
318  rgbbuf_ptr += 1;
319  if (numcomponents == 4) { bytes_ptr++; }
320  }
321  break;
322 
323  case 1:
324  case 2:
325  /* split up the grayscale values into their respective rgb channels */
326  for (i=0; i < bufsize; i++) {
327  rgbbuf_ptr[0] = rgbbuf_ptr[bufsize] = rgbbuf_ptr[bufsize*2] = *bytes_ptr++;
328  rgbbuf_ptr += 1;
329  if (numcomponents == 2) { bytes_ptr++; }
330  }
331  break;
332 
333  default:
334  giferror = ERR_WRITE;
335  free(rgbbuf);
336  return 0;
337  }
338 
339  if (!(outbuf = (GifByteType*)malloc(bufsize))) {
340  giferror = ERR_MEM;
341  free(rgbbuf);
342  return 0;
343  }
344 
345  if (!(cmapobj = MakeMapObject(colormapsize, NULL))) {
346  giferror = ERR_MEM;
347  free(rgbbuf);
348  free(outbuf);
349  return 0;
350  }
351 
352  if (QuantizeBuffer(width, height, &colormapsize,
353  rgbbuf, &rgbbuf[bufsize], &rgbbuf[bufsize*2],
354  outbuf, cmapobj->Colors) == GIF_ERROR) {
355  giferror = ERR_MEM;
356  free(rgbbuf);
357  free(outbuf);
358  FreeMapObject(cmapobj);
359  return 0;
360  }
361 
362  /* open gif file and overwrite any existing file */
363  if (!(giffile = EGifOpenFileName(filename, FALSE))) {
364  giferror = ERR_OPEN;
365  free(rgbbuf);
366  free(outbuf);
367  FreeMapObject(cmapobj);
368  return 0;
369  }
370 
371  if (EGifPutScreenDesc(giffile, width, height, 8,
372  0, cmapobj) == GIF_ERROR ||
373  EGifPutImageDesc(giffile, 0, 0, width, height,
374  FALSE, NULL) == GIF_ERROR) {
375  giferror = ERR_WRITE;
376  free(rgbbuf);
377  free(outbuf);
378  EGifCloseFile(giffile);
379  FreeMapObject(cmapobj);
380  return 0;
381  }
382 
383  outbuf_ptr = outbuf + bufsize;
384  for (i = height; i > 0; i--) {
385  outbuf_ptr -= width;
386  if (EGifPutLine(giffile, outbuf_ptr, width) == GIF_ERROR) {
387  giferror = ERR_WRITE;
388  free(rgbbuf);
389  free(outbuf);
390  EGifCloseFile(giffile);
391  FreeMapObject(cmapobj);
392  return 0;
393  }
394  }
395 
396  if (EGifPutComment(giffile, "Image saved using simage.") == GIF_ERROR ||
397  EGifCloseFile(giffile) == GIF_ERROR) {
398  giferror = ERR_WRITE;
399  free(rgbbuf);
400  free(outbuf);
401  EGifCloseFile(giffile);
402  FreeMapObject(cmapobj);
403  return 0;
404  }
405 
406  free(rgbbuf);
407  free(outbuf);
408  FreeMapObject(cmapobj);
409 
410  return 1;
411 }
412 
413 #endif /* HAVE_GIFLIB */
char * filename
Definition: stream.c:38
#define ERR_WRITE
int simage_gif_save(const char *filename, const unsigned char *bytes, int width, int height, int numcomponents)
int simage_gif_identify(const char *filename, const unsigned char *header, int headerlen)
int simage_gif_error(char *buffer, int bufferlen)
#define ERR_OPEN
unsigned char * simage_gif_load(const char *filename, int *width, int *height, int *numComponents)
#define ERR_MEM
#define ERR_NO_ERROR