Skip to content

Commit c89e0b6

Browse files
committed
added gif2txt (with ANSI escape codes)
1 parent 4557142 commit c89e0b6

File tree

3 files changed

+342
-1
lines changed

3 files changed

+342
-1
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
*.o
22
SDLaffgif
33
gif2tga
4+
gif2txt
45
tmp/
56
*.d

Makefile

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ ifeq ($(OS), Darwin)
1414
LDLIBS += -framework Cocoa
1515
endif
1616

17-
EXECUTABLES=gif2tga SDLaffgif
17+
EXECUTABLES=gif2tga SDLaffgif gif2txt
1818

1919
SRCS = $(wildcard *.c)
2020
OBJS = $(patsubst %.c,%.o,$(SRCS))
@@ -59,6 +59,8 @@ SDLaffgif: SDLaffgif.o ngiflibSDL.o ngiflib.o
5959

6060
gif2tga: gif2tga.o ngiflib.o
6161

62+
gif2txt: gif2txt.o ngiflib.o
63+
6264
%.o: %.c %.d
6365

6466
%.d: %.c

gif2txt.c

+338
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,338 @@
1+
#include <stdio.h>
2+
3+
#include "ngiflib.h"
4+
5+
/* #define ANSI_COLOR_LUT */
6+
7+
enum ansi_color_mode {
8+
BASIC, /* 8 colors */
9+
VGACOLORS, /* 216 colors */
10+
TRUECOLORS /* 24 bits colors */
11+
};
12+
13+
/**
14+
* Output an unicode character in utf-8 encoding
15+
*/
16+
int fputc_utf8(int c, FILE *stream) {
17+
if (c < 0x80) {
18+
return fputc(c, stream);
19+
}
20+
if (c < 0x800) {
21+
if (fputc(0xc0 | (c >> 6), stream) == EOF)
22+
return EOF;
23+
if (fputc(0x80 | (c & 0x7f), stream) == EOF)
24+
return EOF;
25+
} else if (c < 0x10000) {
26+
if (fputc(0xe0 | (c >> 12), stream) == EOF)
27+
return EOF;
28+
if (fputc(0x80 | ((c >> 6) & 0x3f), stream) == EOF)
29+
return EOF;
30+
if (fputc(0x80 | (c & 0x3f), stream) == EOF)
31+
return EOF;
32+
} else {
33+
if (fputc(0xf0 | (c >> 18), stream) == EOF)
34+
return EOF;
35+
if (fputc(0x80 | ((c >> 12) & 0x3f), stream) == EOF)
36+
return EOF;
37+
if (fputc(0x80 | ((c >> 6) & 0x3f), stream) == EOF)
38+
return EOF;
39+
if (fputc(0x80 | (c & 0x3f), stream) == EOF)
40+
return EOF;
41+
}
42+
return c;
43+
}
44+
45+
/**
46+
* Convert from RGB 24 bits to 16 ANSI colors.
47+
* https://en.wikipedia.org/wiki/ANSI_escape_code#3-bit_and_4-bit
48+
*/
49+
unsigned int rgb24toansi(u32 c) {
50+
#ifdef ANSI_COLOR_LUT
51+
unsigned int r = (((c >> 16) & 0xff) * 3 + 1) >> 8;
52+
unsigned int g = (((c >> 8) & 0xff) * 3 + 1) >> 8;
53+
unsigned int b = ((c & 0xff) * 3 + 1) >> 8;
54+
static const unsigned char ansicolors[] = {
55+
/* 0 - G = 0, B = 0 */
56+
0 /* Black */, 1 /* Red */, 61 /* Bright Red */,
57+
/* 3 - G = 1, B = 0 */
58+
2 /* Green */, 3 /* Yellow */, 61 /* Bright Red */,
59+
/* 6 - G = 2, B = 0 */
60+
62 /* Bright Green */, 62 /* Bright Green */, 63 /* Bright Yellow */,
61+
/* 9 - G = 0, B = 1 */
62+
4 /* Blue */, 5 /* Magenta */, 61 /* Bright Red */,
63+
/* 12 - G = 1, B = 1 */
64+
6 /* Cyan */, 7 /* White */, 61 /* Bright Red */,
65+
/* 15 - G = 2, B = 1 */
66+
62 /* Bright Green */, 62 /* Bright Green */, 63 /* Bright Yellow */,
67+
/* 18 - G = 0, B = 2 */
68+
64 /* Bright Blue */, 64 /* Bright Blue */, 65 /* Bright Magenta */,
69+
/* 21 - G = 1, B = 2 */
70+
64 /* Bright Blue */, 64 /* Bright Blue */, 65 /* Bright Magenta */,
71+
/* 24 - G = 2, B = 2 */
72+
66 /* Bright Cyan */, 66 /* Bright Cyan */, 67 /* Bright White */
73+
};
74+
return (unsigned int)ansicolors[r + g * 3 + b * 9];
75+
#else
76+
static const u32 xterm_cols[] = {
77+
0x000000, /* 0 - Black */
78+
0xcd0000, /* 1 - Red */
79+
0x00cd00, /* 2 - Green */
80+
0xcdcd00, /* 3 - Yellow */
81+
0x0000ee, /* 4 - Blue */
82+
0xcd00cd, /* 5 - Magenta */
83+
0x00cdcd, /* 6 - Cyan */
84+
0xe5e5e5, /* 7 - White */
85+
0x7f7f7f, /* 60 - Gray */
86+
0xff0000, /* 61 - Bright Red */
87+
0x00ff00, /* 62 - Bright Green */
88+
0xffff00, /* 63 - Bright Yellow */
89+
0x5c5cff, /* 64 - Bright Blue */
90+
0xff00ff, /* 65 - Bright Magenta */
91+
0x00ffff, /* 66 - Bright Cyan */
92+
0xffffff /* 67 - Bright White */
93+
};
94+
int i, best = 0;
95+
u32 mindist = 0xffffffff;
96+
int r, g, b;
97+
b = c & 0xff;
98+
c >>= 8;
99+
g = c & 0xff;
100+
c >>= 8;
101+
r = c & 0xff;
102+
for (i = 0; i < (int)(sizeof(xterm_cols)/sizeof(u32)); i++) {
103+
int dr, dg, db;
104+
u32 dist; /* squared */
105+
dr = (int)(xterm_cols[i] >> 16) - r;
106+
dg = (int)((xterm_cols[i] >> 8) & 0xff) - g;
107+
db = (int)(xterm_cols[i] & 0xff) - b;
108+
dist = dr * dr + dg * dg + db * db;
109+
if (dist < mindist) {
110+
mindist = dist;
111+
best = i;
112+
}
113+
}
114+
return (unsigned)((best < 8) ? best : best + 52);
115+
#endif
116+
}
117+
118+
/*
119+
* Convert RGB 24 bits to 6x6x6 VGA color cube (16-231)
120+
* and VGA grayscale (232-255)
121+
* https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit
122+
*/
123+
unsigned int rgb24tovga216(u32 c) {
124+
#if VGA_NOGRAYSCALE
125+
unsigned int r = (((c >> 16) & 0xff) * 6 + 3) >> 8;
126+
unsigned int g = (((c >> 8) & 0xff) * 6 + 3) >> 8;
127+
unsigned int b = ((c & 0xff) * 6 + 3) >> 8;
128+
#else
129+
unsigned int r = (((c >> 16) & 0xff) * 24 + 12) >> 8;
130+
unsigned int g = (((c >> 8) & 0xff) * 24 + 12) >> 8;
131+
unsigned int b = ((c & 0xff) * 24 + 12) >> 8;
132+
if ((((r ^ g) | (g ^ b)) & ~3) == 0)
133+
return 232 + r; /* 24 grayscale colors */
134+
r >>= 2;
135+
g >>= 2;
136+
b >>= 2;
137+
#endif
138+
return 16 + (r * 6 + g) * 6 + b;
139+
}
140+
141+
/**
142+
* Set foreground color.
143+
* ANSI codes :
144+
* ESC[30m to ESC[37m set foreground color (90 to 97 for bright color)
145+
* ESC[38;5;<n>m set 8 bits foreground color
146+
* ESC[38;2;<r>,<g>,<b>m set 24 bits foreground color
147+
*/
148+
static u32 setfg(u32 fg, enum ansi_color_mode m) {
149+
switch (m) {
150+
case BASIC:
151+
printf("\033[%um", 30 + fg);
152+
break;
153+
case VGACOLORS:
154+
printf("\033[38;5;%um", fg);
155+
break;
156+
case TRUECOLORS:
157+
printf("\033[38;2;%u;%u;%um", (fg >> 16) & 0xff, (fg >> 8) & 0xff, fg & 0xff);
158+
}
159+
return fg;
160+
}
161+
162+
/**
163+
* Set background color.
164+
* ANSI codes :
165+
* ESC[40m to ESC[47m set background color (100 to 107 for bright color)
166+
* ESC[48;5;<n>m set 8 bits background color
167+
* ESC[48;2;<r>,<g>,<b>m set 24 bits background color
168+
*/
169+
static u32 setbg(u32 bg, enum ansi_color_mode m) {
170+
switch (m) {
171+
case BASIC:
172+
printf("\033[%um", 40 + bg);
173+
break;
174+
case VGACOLORS:
175+
printf("\033[48;5;%um", bg);
176+
break;
177+
case TRUECOLORS:
178+
printf("\033[48;2;%u;%u;%um", (bg >> 16) & 0xff, (bg >> 8) & 0xff, bg & 0xff);
179+
}
180+
return bg;
181+
}
182+
183+
/**
184+
* Output text corresponding to two lines using ANSI escape sequences :
185+
* https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
186+
*
187+
* https://en.wikipedia.org/wiki/Block_Elements
188+
*/
189+
void twolines(const u32 *p, u16 width, int nosecondline, enum ansi_color_mode m) {
190+
#define NOCOL 0xffffffff
191+
int x = (int)width;
192+
u32 fg = NOCOL;
193+
u32 bg = NOCOL;
194+
while (--x >= 0) {
195+
u32 upper, lower;
196+
switch (m) {
197+
case BASIC:
198+
upper = rgb24toansi(p[0]);
199+
lower = nosecondline ? NOCOL : rgb24toansi(p[width]);
200+
break;
201+
case VGACOLORS:
202+
upper = rgb24tovga216(p[0]);
203+
lower = nosecondline ? NOCOL : rgb24tovga216(p[width]);
204+
break;
205+
case TRUECOLORS:
206+
upper = p[0];
207+
lower = nosecondline ? NOCOL : p[width];
208+
}
209+
p++;
210+
if (upper == lower) {
211+
/* both pixels have same color */
212+
if (upper == fg) {
213+
fputc_utf8(0x2588, stdout); /* U+2588 Full block */
214+
} else if(upper == bg) {
215+
fputc(' ', stdout); /* U+0020 space */
216+
} else {
217+
/* Looking for the color that will be useful first, and change the other one */
218+
u32 tochange = fg; /* default */
219+
int x2 = x;
220+
const u32* p2 = p;
221+
while(--x2 >= 0) {
222+
u32 upper2, lower2;
223+
switch (m) {
224+
case BASIC:
225+
upper2 = rgb24toansi(p2[0]);
226+
lower2 = nosecondline ? NOCOL : rgb24toansi(p2[width]);
227+
break;
228+
case VGACOLORS:
229+
upper2 = rgb24tovga216(p2[0]);
230+
lower2 = nosecondline ? NOCOL : rgb24tovga216(p2[width]);
231+
break;
232+
case TRUECOLORS:
233+
upper2 = p2[0];
234+
lower2 = nosecondline ? NOCOL : p2[width];
235+
}
236+
if (bg == upper2 || bg == lower2) {
237+
break;
238+
} else if (fg == upper2 || fg == lower2) {
239+
tochange = bg;
240+
break;
241+
}
242+
p2++;
243+
}
244+
if (tochange == fg) {
245+
fg = setfg(upper, m);
246+
fputc_utf8(0x2588, stdout); /* U+2588 Full block */
247+
} else {
248+
bg = setbg(upper, m);
249+
fputc(' ', stdout); /* U+0020 space */
250+
}
251+
}
252+
} else if (upper == bg || lower == fg) {
253+
/* upper pixel is background, lower is foreground */
254+
if (lower != fg) {
255+
fg = setfg(lower, m);
256+
} else if (upper != bg) {
257+
bg = setbg(upper, m);
258+
}
259+
fputc_utf8(0x2584, stdout); /* U+2584 : lower half block */
260+
} else {
261+
/* upper pixel is foreground, lower is background */
262+
if (upper != fg) {
263+
fg = setfg(upper, m);
264+
}
265+
if (lower != bg) {
266+
bg = setbg(lower, m);
267+
}
268+
fputc_utf8(0x2580, stdout); /* U+2580 : upper half block */
269+
}
270+
}
271+
puts("\033[0m"); /* reset colors */
272+
#undef NOCOL
273+
}
274+
275+
int main(int argc, char **argv) {
276+
struct ngiflib_gif *gif;
277+
FILE *fgif;
278+
const char *input_file = NULL;
279+
enum ansi_color_mode mode = BASIC;
280+
int i;
281+
282+
for (i = 1; i < argc; i++) {
283+
if (i == argc - 1) {
284+
/* last argument => filename */
285+
input_file = argv[i];
286+
} else if(strcmp(argv[i], "--tc") == 0) {
287+
mode = TRUECOLORS;
288+
} else if(strcmp(argv[i], "--vga") == 0) {
289+
mode = VGACOLORS;
290+
} else if(strcmp(argv[i], "--ansi") == 0) {
291+
mode = BASIC;
292+
} else {
293+
fprintf(stderr, "Unrecognized argument : \"%s\"\n", argv[i]);
294+
/* TODO : usage */
295+
}
296+
}
297+
298+
if (input_file == NULL) {
299+
fprintf(stderr, "Missing argument\n");
300+
return 1;
301+
}
302+
303+
gif = (struct ngiflib_gif *)ngiflib_malloc(sizeof(struct ngiflib_gif));
304+
#ifdef EXTRA_MALLOC_CHECK
305+
if(gif == NULL) {
306+
printf("Cannot allocate %ld bytes of memory.\n", sizeof(struct ngiflib_gif));
307+
return 4;
308+
}
309+
#endif /* EXTRA_MALLOC_CHECK */
310+
ngiflib_memset(gif, 0, sizeof(struct ngiflib_gif));
311+
312+
fgif = fopen(input_file, "rb");
313+
if (fgif == NULL) {
314+
fprintf(stderr, "Cannot open file \"%s\"\n", input_file);
315+
return 1;
316+
}
317+
#ifdef NGIFLIB_NO_FILE
318+
/* TODO */
319+
#else /* NGIFLIB_NO_FILE */
320+
gif->input.file = fgif;
321+
gif->mode = NGIFLIB_MODE_FROM_FILE;
322+
#endif /* NGIFLIB_NO_FILE */
323+
gif->mode |= NGIFLIB_MODE_TRUE_COLOR;
324+
#if defined(DEBUG)
325+
gif->log = stderr;
326+
#endif
327+
328+
while (LoadGif(gif) == 1) {
329+
int y;
330+
331+
for (y = 0; y < gif->height; y += 2) {
332+
twolines(gif->frbuff.p32 + (y * gif->width), gif->width, y == gif->height - 1, mode);
333+
}
334+
}
335+
fclose(fgif);
336+
GifDestroy(gif);
337+
return 0;
338+
}

0 commit comments

Comments
 (0)