implement reading GIFs
Stefan Schuermans

Stefan Schuermans commited on 2016-12-18 18:52:03
Showing 11 changed files, with 269 additions and 9 deletions.

... ...
@@ -377,13 +377,18 @@ int main(int argCnt, char **args)
377 377
 # define MNGEXT ", *.mng"
378 378
 #else
379 379
 # define MNGEXT
380
+#endif
381
+#ifdef BLINKENLIB_CFG_GIF
382
+# define GIFEXT ", *.gif"
383
+#else
384
+# define GIFEXT
380 385
 #endif
381 386
     printf("syntax: %s <parameter> [...]\n\n"
382 387
            "parameters:\n"
383 388
            "  -i <file>\n"
384
-           "     read movie from file (*.blm, *.bmm, *.bml, *.bbm"MNGEXT")\n"
389
+           "     read movie from file (*.blm, *.bmm, *.bml, *.bbm" MNGEXT GIFEXT ")\n"
385 390
            "  -a <file>\n"
386
-           "     append movie from file (*.blm, *.bmm, *.bml, *.bbm"MNGEXT")\n"
391
+           "     append movie from file (*.blm, *.bmm, *.bml, *.bbm" MNGEXT GIFEXT ")\n"
387 392
            "  -l <number>\n"
388 393
            "     loop video a number of times\n"
389 394
            "  -f\n"
... ...
@@ -429,7 +434,7 @@ int main(int argCnt, char **args)
429 434
            "  -Md2\n"
430 435
            "     mirror movie diagonally (/)\n"
431 436
            "  -o <file>\n"
432
-           "     write movie to file (*.blm, *.bmm, *.bml, *.bbm)\n\n"
437
+           "     write movie to file (*.blm, *.bmm, *.bml, *.bbm" MNGEXT ")\n\n"
433 438
            "test_modes: black, white, gradient, dots, lines, trans\n\n"
434 439
            "old syntax: %s <input-file> [<output-file>]\n\n",
435 440
            args[0], args[0]);
... ...
@@ -0,0 +1,188 @@
1
+/* BlinkenLib
2
+   Copyright 2004-2016 Stefan Schuermans <stefan@schuermans.info>
3
+   Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
4
+   a blinkenarea.org project */
5
+
6
+#include <gif_lib.h>
7
+
8
+#include <BlinkenLib/config.h>
9
+#include <BlinkenLib/BlinkenConstants.h>
10
+#include <BlinkenLib/BlinkenFrame.h>
11
+#include <BlinkenLib/BlinkenMovie.h>
12
+#include <BlinkenLib/BlinkenGif.h>
13
+
14
+// get color from GIF color map
15
+static void BlinkenGifGetColor(ColorMapObject *pMap, int idx, int transparent,
16
+                               unsigned char *r, unsigned char *g,
17
+                               unsigned char *b, unsigned char *t)
18
+{
19
+  if (transparent >= 0 && idx == transparent) {
20
+    *t = 1;
21
+  } else {
22
+    *t = 0;
23
+  }
24
+  if (pMap != NULL) {
25
+    if (idx >= 0 && idx < pMap->ColorCount) {
26
+      *r = pMap->Colors[idx].Red;
27
+      *g = pMap->Colors[idx].Green;
28
+      *b = pMap->Colors[idx].Blue;
29
+    } else {
30
+      *r = 0;
31
+      *g = 0;
32
+      *b = 0;
33
+      *t = 1;
34
+    }
35
+  } else {
36
+    *r = 0;
37
+    *g = 0;
38
+    *b = 0;
39
+    *t = 1;
40
+  }
41
+}
42
+
43
+// set pixel to GIF color
44
+static void BlinkenGifSetPixel(ColorMapObject *pMap, int idx, int transparent,
45
+                               stBlinkenFrame *pFrame, int y, int x)
46
+{
47
+  unsigned char r, g, b, t;
48
+
49
+  if (pFrame != NULL) {
50
+    BlinkenGifGetColor(pMap, idx, transparent, &r, &g, &b, &t);
51
+    if (! t) {
52
+      BlinkenFrameSetPixel(pFrame, y, x, 0, r);
53
+      BlinkenFrameSetPixel(pFrame, y, x, 1, g);
54
+      BlinkenFrameSetPixel(pFrame, y, x, 2, b);
55
+    }
56
+  }
57
+}
58
+
59
+// load a GIF file as BlinkenMovie
60
+stBlinkenMovie *BlinkenGifLoad(const char *pFilename)
61
+{
62
+  GifFileType *gif;
63
+  int height, width, frameCnt, frameIdx, i, y, x,
64
+      bg, disposal, delay, transp, idx;
65
+  ColorMapObject *pGlobalMap, *pMap;
66
+  SavedImage *pImg;
67
+  GifImageDesc *pDesc;
68
+  ExtensionBlock *pEx;
69
+  stBlinkenMovie *pMovie;
70
+  stBlinkenFrame *pBack, *pFrame;
71
+
72
+  if (pFilename == NULL)
73
+    return NULL;
74
+
75
+  // open GIF file for decoding
76
+  gif = DGifOpenFileName(pFilename);
77
+  if (gif == NULL) {
78
+    return NULL;
79
+  }
80
+
81
+  // read GIF file
82
+  if (DGifSlurp(gif) != GIF_OK) {
83
+    DGifCloseFile(gif);
84
+    return NULL;
85
+  }
86
+  height = gif->SHeight;
87
+  width = gif->SWidth;
88
+  frameCnt = gif->ImageCount;
89
+  pGlobalMap = gif->SColorMap;
90
+  bg = gif->SBackGroundColor;
91
+
92
+  // create movie
93
+  pMovie = BlinkenMovieNew(height, width, 3, 255);
94
+  if (! pMovie) {
95
+    DGifCloseFile(gif);
96
+    return NULL;
97
+  }
98
+
99
+  // create background frame and set it to background color
100
+  pBack = BlinkenFrameNew(height, width, 3, 255, 1);
101
+  if (! pBack) {
102
+    BlinkenMovieFree(pMovie);
103
+    DGifCloseFile(gif);
104
+    return NULL;
105
+  }
106
+  for (y = 0; y < height; ++y) {
107
+    for (x = 0; x < width; ++x) {
108
+      BlinkenGifSetPixel(pGlobalMap, bg, -1, pBack, y, x);
109
+    }
110
+  }
111
+
112
+  // process all frames
113
+  for (frameIdx = 0; frameIdx < frameCnt; ++frameIdx) {
114
+    pImg = &gif->SavedImages[frameIdx];
115
+    pDesc = &pImg->ImageDesc;
116
+
117
+    // get local color map
118
+    pMap = pDesc->ColorMap != NULL ? pDesc->ColorMap : pGlobalMap;
119
+
120
+    // get disposal mode, delay and transparent color
121
+    disposal = 0;
122
+    delay = 100;
123
+    transp = -1;
124
+    for (i = 0; i < pImg->ExtensionBlockCount; ++i) {
125
+      pEx = &pImg->ExtensionBlocks[i];
126
+      if (pEx->Function == GRAPHICS_EXT_FUNC_CODE && pEx->ByteCount == 4) {
127
+        disposal = pEx->Bytes[0] >> 2 & 0x07;
128
+        delay = (unsigned int)(unsigned char)pEx->Bytes[1] |
129
+                (unsigned int)(unsigned char)pEx->Bytes[2] << 8;
130
+        transp = pEx->Bytes[0] & 0x01 ? (int)(unsigned char)pEx->Bytes[3] : -1;
131
+      }
132
+    }
133
+
134
+    // draw new frame based on background frame, maybe update background frame
135
+    if (delay > 0) {
136
+      pFrame = BlinkenFrameClone(pBack);
137
+      BlinkenFrameSetDuration(pFrame, delay * 10); // GIF delay is in 10ms
138
+    } else {
139
+      // frames with no duration -> only effect on background
140
+      pFrame = NULL;
141
+    }
142
+    // FIXME: pDesc->Interlace > 0
143
+    for (y = pDesc->Top, i = 0; y < pDesc->Top + pDesc->Height; ++y) {
144
+      for (x = pDesc->Left; x < pDesc->Left + pDesc->Width; ++x, ++i) {
145
+        idx = pImg->RasterBits[i];
146
+        switch (disposal) {
147
+          // undefined
148
+          case 0:
149
+          case 4:
150
+          case 5:
151
+          case 6:
152
+          case 7:
153
+          // do not dispose -> draw to frame and update background
154
+          case 1:
155
+            BlinkenGifSetPixel(pMap, idx, transp, pFrame, y, x);
156
+            BlinkenGifSetPixel(pMap, idx, transp, pBack, y, x);
157
+            break;
158
+          // restore background -> draw to frame and reset background
159
+          case 2:
160
+            BlinkenGifSetPixel(pMap, idx, transp, pFrame, y, x);
161
+            BlinkenGifSetPixel(pMap, bg, transp, pBack, y, x);
162
+            break;
163
+          // restore previous -> draw to frame only
164
+          case 3:
165
+            BlinkenGifSetPixel(pMap, idx, transp, pFrame, y, x);
166
+            break;
167
+        }
168
+      }
169
+    }
170
+
171
+    // append frame to movie
172
+    if (pFrame) {
173
+      if (BlinkenMovieAppendFrame(pMovie, pFrame) != 0) {
174
+        BlinkenFrameFree(pFrame);
175
+      }
176
+    }
177
+
178
+  } // for frameIdx
179
+
180
+  // delete background frame
181
+  BlinkenFrameFree(pBack);
182
+
183
+  // close GIF file
184
+  DGifCloseFile(gif);
185
+
186
+  return pMovie;
187
+}
188
+
... ...
@@ -0,0 +1,27 @@
1
+/* BlinkenLib
2
+   Copyright 2004-2016 Stefan Schuermans <stefan@schuermans.info>
3
+   Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
4
+   a blinkenarea.org project */
5
+
6
+#ifndef INC_BlinkenLib_BlinkenGif
7
+#define INC_BlinkenLib_BlinkenGif
8
+
9
+#include <BlinkenLib/config.h>
10
+#include <BlinkenLib/BlinkenFrame.h>
11
+#include <BlinkenLib/BlinkenMovie.h>
12
+
13
+#ifndef BLINKENLIB_CFG_GIF
14
+#error This file is to be used for GIF support only.
15
+#endif // #ifndef BLINKENLIB_CFG_GIF
16
+
17
+#ifdef __cplusplus
18
+extern "C" {
19
+#endif
20
+
21
+stBlinkenMovie *BlinkenGifLoad(const char *pFilename);
22
+
23
+#ifdef __cplusplus
24
+} // extern "C"
25
+#endif
26
+
27
+#endif // #ifndef INC_BlinkenLib_BlinkenGif
... ...
@@ -24,6 +24,9 @@
24 24
 #ifdef BLINKENLIB_CFG_MNG
25 25
 #include <BlinkenLib/BlinkenMng.h>
26 26
 #endif // #ifdef BLINKENLIB_CFG_MNG
27
+#ifdef BLINKENLIB_CFG_GIF
28
+#include <BlinkenLib/BlinkenGif.h>
29
+#endif // #ifdef BLINKENLIB_CFG_GIF
27 30
 
28 31
 struct sBlinkenMovie {
29 32
   int height;
... ...
@@ -1494,6 +1497,15 @@ stBlinkenMovie *BlinkenMovieLoadMng(const char *pFilename)
1494 1497
 
1495 1498
 #endif // #ifdef BLINKENLIB_CFG_MNG
1496 1499
 
1500
+#ifdef BLINKENLIB_CFG_GIF
1501
+
1502
+stBlinkenMovie *BlinkenMovieLoadGif(const char *pFilename)
1503
+{
1504
+  return BlinkenGifLoad(pFilename);
1505
+}
1506
+
1507
+#endif // #ifdef BLINKENLIB_CFG_GIF
1508
+
1497 1509
 stBlinkenMovie *BlinkenMovieLoad(const char *pFilename)
1498 1510
 {
1499 1511
   int len;
... ...
@@ -1514,6 +1526,10 @@ stBlinkenMovie *BlinkenMovieLoad(const char *pFilename)
1514 1526
   if (len > 4 && strcmp(pFilename + len - 4, ".mng") == 0)
1515 1527
     return BlinkenMovieLoadMng(pFilename);
1516 1528
 #endif // #ifdef BLINKENLIB_CFG_MNG
1529
+#ifdef BLINKENLIB_CFG_GIF
1530
+  if (len > 4 && strcmp(pFilename + len - 4, ".gif") == 0)
1531
+    return BlinkenMovieLoadGif(pFilename);
1532
+#endif // #ifdef BLINKENLIB_CFG_GIF
1517 1533
   return NULL;
1518 1534
 }
1519 1535
 
... ...
@@ -1,5 +1,5 @@
1 1
 # BlinkenLib
2
-# Copyright 2004-2014 Stefan Schuermans <stefan@schuermans.info>
2
+# Copyright 2004-2016 Stefan Schuermans <stefan@schuermans.info>
3 3
 # Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
4 4
 # a blinkenarea.org project
5 5
 
... ...
@@ -29,7 +29,14 @@ BLINKEN_MNG_O:=BlinkenMng.o
29 29
 LMNG:=-lmng -lz
30 30
 endif
31 31
 
32
-LIB_OBJS=BlinkenColorizer.o BlinkenFrame.o BlinkenMovie.o $(BLINKEN_MNG_O) \
32
+ifeq ($(BLINKENLIB_CFG_GIF),1)
33
+BLINKEN_GIF_H:=BlinkenGif.h
34
+BLINKEN_GIF_O:=BlinkenGif.o
35
+LGIF:=-lgif
36
+endif
37
+
38
+LIB_OBJS:=BlinkenColorizer.o BlinkenFrame.o BlinkenMovie.o \
39
+          $(BLINKEN_MNG_O) $(BLINKEN_GIF_O) \
33 40
           BlinkenProto.o Tools.o
34 41
 
35 42
 .phony: all clean
... ...
@@ -48,7 +55,7 @@ BlinkenProto.o: BlinkenProto.c BlinkenProto.h BlinkenProtoIntern.h
48 55
 BlinkenFrame.o: BlinkenFrame.c BlinkenConstants.h BlinkenColorizer.h BlinkenFrame.h BlinkenProto.h BlinkenProtoIntern.h Tools.h
49 56
 	$(CC) $(CFLAGS) -c -o $@ $<
50 57
 
51
-BlinkenMovie.o: BlinkenMovie.c BlinkenConstants.h BlinkenColorizer.h BlinkenFrame.h BlinkenProto.h BlinkenMovie.h $(BLINKEN_MNG_H) Tools.h config.h
58
+BlinkenMovie.o: BlinkenMovie.c BlinkenConstants.h BlinkenColorizer.h BlinkenFrame.h BlinkenProto.h BlinkenMovie.h $(BLINKEN_MNG_H) $(BLINKEN_GIF_H) Tools.h config.h
52 59
 	$(CC) $(CFLAGS) -c -o $@ $<
53 60
 
54 61
 BlinkenMng.o: BlinkenMng.c BlinkenConstants.h BlinkenColorizer.h BlinkenFrame.h BlinkenProto.h BlinkenMovie.h Tools.h config.h
... ...
@@ -62,7 +69,7 @@ libBlinkenLib.a: $(LIB_OBJS)
62 69
 	$(RANLIB) $@
63 70
 
64 71
 libBlinkenLib.$(SHLIBEXT).$(VERSION): $(LIB_OBJS)
65
-	$(CC) -shared $(SONAMEOPT) $(LIB_LFLAGS) -o $@ $+ $(LMNG)
72
+	$(CC) -shared $(SONAMEOPT) $(LIB_LFLAGS) -o $@ $+ $(LMNG) $(LGIF)
66 73
 
67 74
 libBlinkenLib.$(SHLIBEXT).$(VERSION_MAJOR): libBlinkenLib.$(SHLIBEXT).$(VERSION)
68 75
 	rm -f $@
... ...
@@ -1,5 +1,5 @@
1 1
 # BlinkenLib
2
-# Copyright 2004-2014 Stefan Schuermans <stefan@schuermans.info>
2
+# Copyright 2004-2016 Stefan Schuermans <stefan@schuermans.info>
3 3
 # Copyleft GNU public license - http://www.gnu.org/copyleft/gpl.html
4 4
 # a blinkenarea.org project
5 5
 
... ...
@@ -20,7 +20,12 @@ mng.cfg:
20 20
 	(($(CC) $(INCDIR) $(LIBDIR) -o mng mng.c -lmng && ./mng && echo "1") || echo "0") >>mng.cfg
21 21
 	rm -f mng
22 22
 
23
-config.cfg: osx.cfg mng.cfg
23
+gif.cfg:
24
+	echo "GIF=" | tr -d '\n' >gif.cfg
25
+	(($(CC) $(INCDIR) $(LIBDIR) -o gif gif.c -lgif && ./gif && echo "1") || echo "0") >>gif.cfg
26
+	rm -f gif
27
+
28
+config.cfg: osx.cfg mng.cfg gif.cfg
24 29
 	cat $+ >config.cfg
25 30
 
26 31
 config.mk: config.cfg
... ...
@@ -0,0 +1,12 @@
1
+#include <gif_lib.h>
2
+
3
+int main()
4
+{
5
+  GifFileType *gif;
6
+
7
+  gif = DGifOpenFileName("x.gif");
8
+  if (gif)
9
+    DGifCloseFile(gif);
10
+
11
+  return 0;
12
+}