1 /*
2 * ASCII/ANSI art decoder
3 * Copyright (c) 2010 Peter Ross <pross@xvid.org>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
11 *
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22 /**
23 * @file
24 * ASCII/ANSI art decoder
25 */
26
34
35 #define ATTR_BOLD 0x01 /**< Bold/Bright-foreground (mode 1) */
36 #define ATTR_FAINT 0x02 /**< Faint (mode 2) */
37 #define ATTR_ITALICS 0x04 /**< Italics (mode 3) */
38 #define ATTR_UNDERLINE 0x08 /**< Underline (mode 4) */
39 #define ATTR_BLINK 0x10 /**< Blink/Bright-background (mode 5) */
40 #define ATTR_REVERSE 0x40 /**< Reverse (mode 7) */
41 #define ATTR_CONCEALED 0x80 /**< Concealed (mode 8) */
42
43 #define DEFAULT_FG_COLOR 7 /**< CGA color index */
44 #define DEFAULT_BG_COLOR 0
45 #define DEFAULT_SCREEN_MODE 3 /**< 80x25 */
46
47 #define FONT_WIDTH 8 /**< Font width */
48
49 /** map ansi color index to cga palette index */
51 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15
52 };
53
56 int x;
/**< x cursor position (pixels) */
57 int y;
/**< y cursor position (pixels) */
58 int sx;
/**< saved x cursor position (pixels) */
59 int sy;
/**< saved y cursor position (pixels) */
60 const uint8_t*
font;
/**< font */
63 int fg;
/**< foreground color */
64 int bg;
/**< background color */
66
67 /* ansi parser state machine */
68 enum {
76 int nb_args;
/**< number of arguments (may exceed MAX_NB_ARGS) */
78
80 {
83
84 /* defaults */
89
97 }
98
102
103 return 0;
104 }
105
107 {
110 pal += 16;
111 #define COLOR(x) ((x) * 40 + 55)
112 for (
r = 0;
r < 6;
r++)
113 for (
g = 0;
g < 6;
g++)
114 for (
b = 0;
b < 6;
b++)
116 #define GRAY(x) ((x) * 10 + 8)
117 for (
g = 0;
g < 24;
g++)
119 }
120
122 {
125
126 if (
s->y <= avctx->
height - 2*
s->font_height) {
127 s->y +=
s->font_height;
128 return;
129 }
130
132 for (;
i < avctx->
height -
s->font_height;
i++)
133 memcpy(
s->frame->data[0] +
i *
s->frame->linesize[0],
134 s->frame->data[0] + (
i +
s->font_height) *
s->frame->linesize[0],
137 memset(
s->frame->data[0] +
i *
s->frame->linesize[0],
139 }
140
142 {
145 for (
i = 0;
i <
s->font_height;
i++)
146 memset(
s->frame->data[0] + (
s->y +
i)*
s->frame->linesize[0] + xoffset,
148 }
149
151 {
157 }
158
159 /**
160 * Draw character to screen
161 */
163 {
167
169 fg += 8;
171 bg += 8;
175 fg = bg;
177 s->frame->linesize[0],
s->font,
s->font_height,
c, fg, bg);
182 }
183 }
184
185 /**
186 * Execute ANSI escape code
187 * @return 0 on success, negative on error
188 */
190 {
195
197 case 'A': //Cursor Up
198 s->y =
FFMAX(
s->y - (
s->nb_args > 0 ?
s->args[0]*
s->font_height :
s->font_height), 0);
199 break;
200 case 'B': //Cursor Down
201 s->y =
FFMIN(
s->y + (
s->nb_args > 0 ?
s->args[0]*
s->font_height :
s->font_height), avctx->
height -
s->font_height);
202 break;
203 case 'C': //Cursor Right
205 break;
206 case 'D': //Cursor Left
208 break;
209 case 'H': //Cursor Position
210 case 'f': //Horizontal and Vertical Position
211 s->y =
s->nb_args > 0 ?
av_clip((
s->args[0] - 1)*
s->font_height, 0, avctx->
height -
s->font_height) : 0;
213 break;
214 case 'h': //set screen mode
215 case 'l': //reset screen mode
219 case 0: case 1: case 4: case 5: case 13: case 19: //320x200 (25 rows)
224 break;
225 case 2: case 3: //640x400 (25 rows)
230 break;
231 case 6: case 14: //640x200 (25 rows)
236 break;
237 case 7: //set line wrapping
238 break;
239 case 15: case 16: //640x350 (43 rows)
244 break;
245 case 17: case 18: //640x480 (60 rows)
250 break;
251 default:
253 }
265 s->frame->palette_has_changed = 1;
268 }
else if (
c ==
'l') {
270 }
271 break;
272 case 'J': //Erase in Page
273 switch (
s->args[0]) {
274 case 0:
276 if (
s->y < avctx->
height -
s->font_height)
277 memset(
s->frame->data[0] + (
s->y +
s->font_height)*
s->frame->linesize[0],
279 break;
280 case 1:
284 break;
285 case 2:
287 }
288 break;
289 case 'K': //Erase in Line
291 case 0:
293 break;
294 case 1:
296 break;
297 case 2:
299 }
300 break;
301 case 'm': //Select Graphics Rendition
302 if (
s->nb_args == 0) {
305 }
308 if (m == 0) {
312 } else if (m == 1 || m == 2 || m == 3 || m == 4 || m == 5 || m == 7 || m == 8) {
313 s->attributes |= 1 << (m - 1);
314 } else if (m >= 30 && m <= 37) {
316 }
else if (m == 38 &&
i + 2 <
FFMIN(
s->nb_args,
MAX_NB_ARGS) &&
s->args[
i + 1] == 5 &&
s->args[
i + 2] < 256) {
320 } else if (m == 39) {
322 } else if (m >= 40 && m <= 47) {
324 }
else if (m == 48 &&
i + 2 <
FFMIN(
s->nb_args,
MAX_NB_ARGS) &&
s->args[
i + 1] == 5 &&
s->args[
i + 2] < 256) {
328 } else if (m == 49) {
330 } else {
332 }
333 }
334 break;
335 case 'n': //Device Status Report
336 case 'R': //report current line and column
337 /* ignore */
338 break;
339 case 's': //Save Cursor Position
342 break;
343 case 'u': //Restore Cursor Position
346 break;
347 default:
349 break;
350 }
353 return 0;
354 }
355
357 void *
data,
int *got_frame,
359 {
361 uint8_t *buf = avpkt->
data;
362 int buf_size = avpkt->
size;
363 const uint8_t *buf_end = buf+buf_size;
365
370 memset(
s->frame->data[0]+
i*
s->frame->linesize[0], 0, avctx->
width);
372 }
373
375 s->frame->palette_has_changed = 1;
377 if (!
s->first_frame) {
380 }
381
382 while(buf < buf_end) {
384 case STATE_NORMAL:
385 switch (buf[0]) {
386 case 0x00: //NUL
387 case 0x07: //BEL
388 case 0x1A: //SUB
389 /* ignore */
390 break;
391 case 0x08: //BS
393 break;
394 case 0x09: //HT
396 count = ((
i + 8) & ~7) -
i;
397 for (
i = 0;
i < count;
i++)
399 break;
400 case 0x0A: //LF
402 case 0x0D: //CR
404 break;
405 case 0x0C: //FF
407 break;
408 case 0x1B: //ESC
409 s->state = STATE_ESCAPE;
410 break;
411 default:
413 }
414 break;
415 case STATE_ESCAPE:
416 if (buf[0] == '[') {
417 s->state = STATE_CODE;
420 } else {
421 s->state = STATE_NORMAL;
423 continue;
424 }
425 break;
426 case STATE_CODE:
427 switch(buf[0]) {
428 case '0': case '1': case '2': case '3': case '4':
429 case '5': case '6': case '7': case '8': case '9':
431 s->args[
s->nb_args] =
FFMAX(
s->args[
s->nb_args], 0) * 10 + buf[0] -
'0';
432 break;
433 case ';':
437 s->args[
s->nb_args] = 0;
438 break;
439 case 'M':
440 s->state = STATE_MUSIC_PREAMBLE;
441 break;
442 case '=': case '?':
443 /* ignore */
444 break;
445 default:
452 s->state = STATE_NORMAL;
453 }
454 break;
455 case STATE_MUSIC_PREAMBLE:
456 if (buf[0] == 0x0E || buf[0] == 0x1B)
457 s->state = STATE_NORMAL;
458 /* ignore music data */
459 break;
460 }
461 buf++;
462 }
463
464 *got_frame = 1;
467 return buf_size;
468 }
469
471 {
473
475 return 0;
476 }
477
479 { "max_pixels", "640*480" },
481 };
482
495 };