Mercurial > libavcodec.hg
annotate ansi.c @ 12483:0159a19bfff7 libavcodec
aacdec: Rework channel mapping compatibility hacks.
For a PCE based configuration map the channels solely based on tags.
For an indexed configuration map the channels solely based on position.
This works with all known exotic samples including al17, elem_id0, bad_concat,
and lfe_is_sce.
author | alexc |
---|---|
date | Fri, 10 Sep 2010 18:01:48 +0000 |
parents | 601fbb943758 |
children | 34beb0af8204 |
rev | line source |
---|---|
12186 | 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 | |
12448
601fbb943758
Use quotes instead of angle brackets for local #includes.
diego
parents:
12283
diff
changeset
|
27 #include "libavutil/lfg.h" |
12186 | 28 #include "avcodec.h" |
29 #include "cga_data.h" | |
30 | |
12283 | 31 #define ATTR_BOLD 0x01 /**< Bold/Bright-foreground (mode 1) */ |
32 #define ATTR_FAINT 0x02 /**< Faint (mode 2) */ | |
33 #define ATTR_UNDERLINE 0x08 /**< Underline (mode 4) */ | |
34 #define ATTR_BLINK 0x10 /**< Blink/Bright-background (mode 5) */ | |
35 #define ATTR_REVERSE 0x40 /**< Reverse (mode 7) */ | |
36 #define ATTR_CONCEALED 0x80 /**< Concealed (mode 8) */ | |
12186 | 37 |
12283 | 38 #define DEFAULT_FG_COLOR 7 /**< CGA color index */ |
12186 | 39 #define DEFAULT_BG_COLOR 0 |
12283 | 40 #define DEFAULT_SCREEN_MODE 3 /**< 80x25 */ |
12186 | 41 |
12283 | 42 #define FONT_WIDTH 8 /**< Font width */ |
12186 | 43 |
44 /** map ansi color index to cga palette index */ | |
45 static const uint8_t ansi_to_cga[16] = { | |
46 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15 | |
47 }; | |
48 | |
49 typedef struct { | |
50 AVFrame frame; | |
12283 | 51 int x; /**< x cursor position (pixels) */ |
52 int y; /**< y cursor position (pixels) */ | |
53 int sx; /**< saved x cursor position (pixels) */ | |
54 int sy; /**< saved y cursor position (pixels) */ | |
55 const uint8_t* font; /**< font */ | |
56 int font_height; /**< font height */ | |
57 int attributes; /**< attribute flags */ | |
58 int fg; /**< foreground color */ | |
59 int bg; /**< background color */ | |
12186 | 60 |
61 /* ansi parser state machine */ | |
62 enum { | |
63 STATE_NORMAL = 0, | |
64 STATE_ESCAPE, | |
65 STATE_CODE, | |
66 STATE_MUSIC_PREAMBLE | |
67 } state; | |
68 #define MAX_NB_ARGS 4 | |
69 int args[MAX_NB_ARGS]; | |
12283 | 70 int nb_args; /**< number of arguments (may exceed MAX_NB_ARGS) */ |
12186 | 71 } AnsiContext; |
72 | |
73 static av_cold int decode_init(AVCodecContext *avctx) | |
74 { | |
75 AnsiContext *s = avctx->priv_data; | |
76 avctx->pix_fmt = PIX_FMT_PAL8; | |
77 | |
78 /* defaults */ | |
79 s->font = ff_vga16_font; | |
80 s->font_height = 16; | |
81 s->fg = DEFAULT_FG_COLOR; | |
82 s->bg = DEFAULT_BG_COLOR; | |
83 | |
84 if (!avctx->width || !avctx->height) | |
85 avcodec_set_dimensions(avctx, 80<<3, 25<<4); | |
86 | |
87 return 0; | |
88 } | |
89 | |
90 static void hscroll(AVCodecContext *avctx) | |
91 { | |
92 AnsiContext *s = avctx->priv_data; | |
93 int i; | |
94 | |
95 if (s->y < avctx->height - s->font_height) { | |
96 s->y += s->font_height; | |
97 return; | |
98 } | |
99 | |
100 i = 0; | |
101 for (; i < avctx->height - s->font_height; i++) | |
102 memcpy(s->frame.data[0] + i * s->frame.linesize[0], | |
103 s->frame.data[0] + (i + s->font_height) * s->frame.linesize[0], | |
104 avctx->width); | |
105 for (; i < avctx->height; i++) | |
106 memset(s->frame.data[0] + i * s->frame.linesize[0], | |
107 DEFAULT_BG_COLOR, avctx->width); | |
108 } | |
109 | |
110 static void erase_line(AVCodecContext * avctx, int xoffset, int xlength) | |
111 { | |
112 AnsiContext *s = avctx->priv_data; | |
113 int i; | |
114 for (i = 0; i < s->font_height; i++) | |
115 memset(s->frame.data[0] + (s->y + i)*s->frame.linesize[0] + xoffset, | |
116 DEFAULT_BG_COLOR, xlength); | |
117 } | |
118 | |
119 static void erase_screen(AVCodecContext *avctx) | |
120 { | |
121 AnsiContext *s = avctx->priv_data; | |
122 int i; | |
123 for (i = 0; i < avctx->height; i++) | |
124 memset(s->frame.data[0] + i * s->frame.linesize[0], DEFAULT_BG_COLOR, avctx->width); | |
125 s->x = s->y = 0; | |
126 } | |
127 | |
128 /** | |
129 * Draw character to screen | |
130 */ | |
131 static void draw_char(AVCodecContext *avctx, int c) | |
132 { | |
133 AnsiContext *s = avctx->priv_data; | |
134 int fg = s->fg; | |
135 int bg = s->bg; | |
136 | |
137 if ((s->attributes & ATTR_BOLD)) | |
138 fg += 8; | |
139 if ((s->attributes & ATTR_BLINK)) | |
140 bg += 8; | |
141 if ((s->attributes & ATTR_REVERSE)) | |
142 FFSWAP(int, fg, bg); | |
143 if ((s->attributes & ATTR_CONCEALED)) | |
144 fg = bg; | |
145 ff_draw_pc_font(s->frame.data[0] + s->y * s->frame.linesize[0] + s->x, | |
146 s->frame.linesize[0], s->font, s->font_height, c, fg, bg); | |
147 s->x += FONT_WIDTH; | |
148 if (s->x >= avctx->width) { | |
149 s->x = 0; | |
150 hscroll(avctx); | |
151 } | |
152 } | |
153 | |
154 /** | |
155 * Execute ANSI escape code | |
156 * @param <0 error | |
157 */ | |
158 static int execute_code(AVCodecContext * avctx, int c) | |
159 { | |
160 AnsiContext *s = avctx->priv_data; | |
161 int ret, i, width, height; | |
162 switch(c) { | |
163 case 'A': //Cursor Up | |
164 s->y = FFMAX(s->y - (s->nb_args > 0 ? s->args[0]*s->font_height : s->font_height), 0); | |
165 break; | |
166 case 'B': //Cursor Down | |
167 s->y = FFMIN(s->y + (s->nb_args > 0 ? s->args[0]*s->font_height : s->font_height), avctx->height - s->font_height); | |
168 break; | |
169 case 'C': //Cursor Right | |
170 s->x = FFMIN(s->x + (s->nb_args > 0 ? s->args[0]*FONT_WIDTH : FONT_WIDTH), avctx->width - FONT_WIDTH); | |
171 break; | |
172 case 'D': //Cursor Left | |
173 s->x = FFMAX(s->x - (s->nb_args > 0 ? s->args[0]*FONT_WIDTH : FONT_WIDTH), 0); | |
174 break; | |
175 case 'H': //Cursor Position | |
176 case 'f': //Horizontal and Vertical Position | |
177 s->y = s->nb_args > 0 ? av_clip((s->args[0] - 1)*s->font_height, 0, avctx->height - s->font_height) : 0; | |
178 s->x = s->nb_args > 1 ? av_clip((s->args[1] - 1)*FONT_WIDTH, 0, avctx->width - FONT_WIDTH) : 0; | |
179 break; | |
180 case 'h': //set creen mode | |
181 case 'l': //reset screen mode | |
182 if (s->nb_args < 2) | |
183 s->args[0] = DEFAULT_SCREEN_MODE; | |
184 switch(s->args[0]) { | |
185 case 0: case 1: case 4: case 5: case 13: case 19: //320x200 (25 rows) | |
186 s->font = ff_cga_font; | |
187 s->font_height = 8; | |
188 width = 40<<3; | |
189 height = 25<<3; | |
190 break; | |
191 case 2: case 3: //640x400 (25 rows) | |
192 s->font = ff_vga16_font; | |
193 s->font_height = 16; | |
194 width = 80<<3; | |
195 height = 25<<4; | |
196 break; | |
197 case 6: case 14: //640x200 (25 rows) | |
198 s->font = ff_cga_font; | |
199 s->font_height = 8; | |
200 width = 80<<3; | |
201 height = 25<<3; | |
202 break; | |
203 case 7: //set line wrapping | |
204 break; | |
205 case 15: case 16: //640x350 (43 rows) | |
206 s->font = ff_cga_font; | |
207 s->font_height = 8; | |
208 width = 80<<3; | |
209 height = 43<<3; | |
210 break; | |
211 case 17: case 18: //640x480 (60 rows) | |
212 s->font = ff_cga_font; | |
213 s->font_height = 8; | |
214 width = 80<<3; | |
215 height = 60<<4; | |
216 break; | |
217 default: | |
218 av_log_ask_for_sample(avctx, "unsupported screen mode\n"); | |
219 } | |
220 if (width != avctx->width || height != avctx->height) { | |
221 if (s->frame.data[0]) | |
222 avctx->release_buffer(avctx, &s->frame); | |
223 avcodec_set_dimensions(avctx, width, height); | |
224 ret = avctx->get_buffer(avctx, &s->frame); | |
225 if (ret < 0) { | |
226 av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); | |
227 return ret; | |
228 } | |
229 s->frame.pict_type = FF_I_TYPE; | |
230 s->frame.palette_has_changed = 1; | |
231 memcpy(s->frame.data[1], ff_cga_palette, 16 * 4); | |
232 erase_screen(avctx); | |
233 } else if (c == 'l') { | |
234 erase_screen(avctx); | |
235 } | |
236 break; | |
237 case 'J': //Erase in Page | |
238 switch (s->args[0]) { | |
239 case 0: | |
240 erase_line(avctx, s->x, avctx->width - s->x); | |
241 if (s->y < avctx->height - s->font_height) | |
242 memset(s->frame.data[0] + (s->y + s->font_height)*s->frame.linesize[0], | |
243 DEFAULT_BG_COLOR, (avctx->height - s->y - s->font_height)*s->frame.linesize[0]); | |
244 break; | |
245 case 1: | |
246 erase_line(avctx, 0, s->x); | |
247 if (s->y > 0) | |
248 memset(s->frame.data[0], DEFAULT_BG_COLOR, s->y * s->frame.linesize[0]); | |
249 break; | |
250 case 2: | |
251 erase_screen(avctx); | |
252 } | |
253 break; | |
254 case 'K': //Erase in Line | |
255 switch(s->args[0]) { | |
256 case 0: | |
257 erase_line(avctx, s->x, avctx->width - s->x); | |
258 break; | |
259 case 1: | |
260 erase_line(avctx, 0, s->x); | |
261 break; | |
262 case 2: | |
263 erase_line(avctx, 0, avctx->width); | |
264 } | |
265 break; | |
266 case 'm': //Select Graphics Rendition | |
267 if (s->nb_args == 0) { | |
268 s->nb_args = 1; | |
269 s->args[0] = 0; | |
270 } | |
271 for (i = 0; i < FFMIN(s->nb_args, MAX_NB_ARGS); i++) { | |
272 int m = s->args[i]; | |
273 if (m == 0) { | |
274 s->attributes = 0; | |
275 s->fg = DEFAULT_FG_COLOR; | |
276 s->bg = DEFAULT_BG_COLOR; | |
277 } else if (m == 1 || m == 2 || m == 4 || m == 5 || m == 7 || m == 8) { | |
278 s->attributes |= 1 << (m - 1); | |
279 } else if (m >= 30 && m <= 38) { | |
280 s->fg = ansi_to_cga[m - 30]; | |
281 } else if (m == 39) { | |
282 s->fg = ansi_to_cga[DEFAULT_FG_COLOR]; | |
283 } else if (m >= 40 && m <= 47) { | |
284 s->bg = ansi_to_cga[m - 40]; | |
285 } else if (m == 49) { | |
286 s->fg = ansi_to_cga[DEFAULT_BG_COLOR]; | |
287 } else { | |
288 av_log_ask_for_sample(avctx, "unsupported rendition parameter\n"); | |
289 } | |
290 } | |
291 break; | |
292 case 'n': //Device Status Report | |
293 case 'R': //report current line and column | |
294 /* ignore */ | |
295 break; | |
296 case 's': //Save Cursor Position | |
297 s->sx = s->x; | |
298 s->sy = s->y; | |
299 break; | |
300 case 'u': //Restore Cursor Position | |
301 s->x = av_clip(s->sx, 0, avctx->width - FONT_WIDTH); | |
302 s->y = av_clip(s->sy, 0, avctx->height - s->font_height); | |
303 break; | |
304 default: | |
305 av_log_ask_for_sample(avctx, "unsupported escape code\n"); | |
306 break; | |
307 } | |
308 return 0; | |
309 } | |
310 | |
311 static int decode_frame(AVCodecContext *avctx, | |
312 void *data, int *data_size, | |
313 AVPacket *avpkt) | |
314 { | |
315 AnsiContext *s = avctx->priv_data; | |
316 uint8_t *buf = avpkt->data; | |
317 int buf_size = avpkt->size; | |
318 const uint8_t *buf_end = buf+buf_size; | |
319 int ret, i, count; | |
320 | |
321 ret = avctx->reget_buffer(avctx, &s->frame); | |
322 if (ret < 0){ | |
323 av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n"); | |
324 return ret; | |
325 } | |
326 s->frame.pict_type = FF_I_TYPE; | |
327 s->frame.palette_has_changed = 1; | |
328 memcpy(s->frame.data[1], ff_cga_palette, 16 * 4); | |
329 | |
330 while(buf < buf_end) { | |
331 switch(s->state) { | |
332 case STATE_NORMAL: | |
333 switch (buf[0]) { | |
334 case 0x00: //NUL | |
335 case 0x07: //BEL | |
336 case 0x1A: //SUB | |
337 /* ignore */ | |
338 break; | |
339 case 0x08: //BS | |
340 s->x = FFMAX(s->x - 1, 0); | |
341 break; | |
342 case 0x09: //HT | |
343 i = s->x / FONT_WIDTH; | |
344 count = ((i + 8) & ~7) - i; | |
345 for (i = 0; i < count; i++) | |
346 draw_char(avctx, ' '); | |
347 break; | |
348 case 0x0A: //LF | |
349 hscroll(avctx); | |
350 case 0x0D: //CR | |
351 s->x = 0; | |
352 break; | |
353 case 0x0C: //FF | |
354 erase_screen(avctx); | |
355 break; | |
356 case 0x1B: //ESC | |
357 s->state = STATE_ESCAPE; | |
358 break; | |
359 default: | |
360 draw_char(avctx, buf[0]); | |
361 } | |
362 break; | |
363 case STATE_ESCAPE: | |
364 if (buf[0] == '[') { | |
365 s->state = STATE_CODE; | |
366 s->nb_args = 0; | |
367 s->args[0] = 0; | |
368 } else { | |
369 s->state = STATE_NORMAL; | |
370 draw_char(avctx, 0x1B); | |
371 return -1; | |
372 continue; | |
373 } | |
374 break; | |
375 case STATE_CODE: | |
376 switch(buf[0]) { | |
377 case '0': case '1': case '2': case '3': case '4': | |
378 case '5': case '6': case '7': case '8': case '9': | |
379 if (s->nb_args < MAX_NB_ARGS) | |
380 s->args[s->nb_args] = s->args[s->nb_args] * 10 + buf[0] - '0'; | |
381 break; | |
382 case ';': | |
383 s->nb_args++; | |
384 if (s->nb_args < MAX_NB_ARGS) | |
385 s->args[s->nb_args] = 0; | |
386 break; | |
387 case 'M': | |
388 s->state = STATE_MUSIC_PREAMBLE; | |
389 break; | |
390 case '=': case '?': | |
391 /* ignore */ | |
392 break; | |
393 default: | |
394 if (s->nb_args > MAX_NB_ARGS) | |
395 av_log(avctx, AV_LOG_WARNING, "args overflow (%i)\n", s->nb_args); | |
396 if (s->nb_args < MAX_NB_ARGS && s->args[s->nb_args]) | |
397 s->nb_args++; | |
398 if (execute_code(avctx, buf[0]) < 0) | |
399 return -1; | |
400 s->state = STATE_NORMAL; | |
401 } | |
402 break; | |
403 case STATE_MUSIC_PREAMBLE: | |
404 if (buf[0] == 0x0E || buf[0] == 0x1B) | |
405 s->state = STATE_NORMAL; | |
406 /* ignore music data */ | |
407 break; | |
408 } | |
409 buf++; | |
410 } | |
411 | |
412 *data_size = sizeof(AVFrame); | |
413 *(AVFrame*)data = s->frame; | |
414 return buf_size; | |
415 } | |
416 | |
417 static av_cold int decode_close(AVCodecContext *avctx) | |
418 { | |
419 AnsiContext *s = avctx->priv_data; | |
420 if (s->frame.data[0]) | |
421 avctx->release_buffer(avctx, &s->frame); | |
422 return 0; | |
423 } | |
424 | |
425 AVCodec ansi_decoder = { | |
426 .name = "ansi", | |
427 .type = CODEC_TYPE_VIDEO, | |
428 .id = CODEC_ID_ANSI, | |
429 .priv_data_size = sizeof(AnsiContext), | |
430 .init = decode_init, | |
431 .close = decode_close, | |
432 .decode = decode_frame, | |
433 .capabilities = CODEC_CAP_DR1, | |
434 .long_name = NULL_IF_CONFIG_SMALL("ASCII/ANSI art"), | |
435 }; |