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