00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00027 #include "libavutil/common.h"
00028 #include "libavutil/lfg.h"
00029 #include "libavutil/xga_font_data.h"
00030 #include "avcodec.h"
00031 #include "cga_data.h"
00032 #include "internal.h"
00033
00034 #define ATTR_BOLD 0x01
00035 #define ATTR_FAINT 0x02
00036 #define ATTR_UNDERLINE 0x08
00037 #define ATTR_BLINK 0x10
00038 #define ATTR_REVERSE 0x40
00039 #define ATTR_CONCEALED 0x80
00041 #define DEFAULT_FG_COLOR 7
00042 #define DEFAULT_BG_COLOR 0
00043 #define DEFAULT_SCREEN_MODE 3
00045 #define FONT_WIDTH 8
00048 static const uint8_t ansi_to_cga[16] = {
00049 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15
00050 };
00051
00052 typedef struct {
00053 AVFrame frame;
00054 int x;
00055 int y;
00056 int sx;
00057 int sy;
00058 const uint8_t* font;
00059 int font_height;
00060 int attributes;
00061 int fg;
00062 int bg;
00063 int first_frame;
00064
00065
00066 enum {
00067 STATE_NORMAL = 0,
00068 STATE_ESCAPE,
00069 STATE_CODE,
00070 STATE_MUSIC_PREAMBLE
00071 } state;
00072 #define MAX_NB_ARGS 4
00073 int args[MAX_NB_ARGS];
00074 int nb_args;
00075 } AnsiContext;
00076
00077 static av_cold int decode_init(AVCodecContext *avctx)
00078 {
00079 AnsiContext *s = avctx->priv_data;
00080 avctx->pix_fmt = AV_PIX_FMT_PAL8;
00081
00082
00083 s->font = avpriv_vga16_font;
00084 s->font_height = 16;
00085 s->fg = DEFAULT_FG_COLOR;
00086 s->bg = DEFAULT_BG_COLOR;
00087
00088 avcodec_get_frame_defaults(&s->frame);
00089 if (!avctx->width || !avctx->height)
00090 avcodec_set_dimensions(avctx, 80<<3, 25<<4);
00091
00092 return 0;
00093 }
00094
00095 static void set_palette(uint32_t *pal)
00096 {
00097 int r, g, b;
00098 memcpy(pal, ff_cga_palette, 16 * 4);
00099 pal += 16;
00100 #define COLOR(x) ((x) * 40 + 55)
00101 for (r = 0; r < 6; r++)
00102 for (g = 0; g < 6; g++)
00103 for (b = 0; b < 6; b++)
00104 *pal++ = 0xFF000000 | (COLOR(r) << 16) | (COLOR(g) << 8) | COLOR(b);
00105 #define GRAY(x) ((x) * 10 + 8)
00106 for (g = 0; g < 24; g++)
00107 *pal++ = 0xFF000000 | (GRAY(g) << 16) | (GRAY(g) << 8) | GRAY(g);
00108 }
00109
00110 static void hscroll(AVCodecContext *avctx)
00111 {
00112 AnsiContext *s = avctx->priv_data;
00113 int i;
00114
00115 if (s->y < avctx->height - s->font_height) {
00116 s->y += s->font_height;
00117 return;
00118 }
00119
00120 i = 0;
00121 for (; i < avctx->height - s->font_height; i++)
00122 memcpy(s->frame.data[0] + i * s->frame.linesize[0],
00123 s->frame.data[0] + (i + s->font_height) * s->frame.linesize[0],
00124 avctx->width);
00125 for (; i < avctx->height; i++)
00126 memset(s->frame.data[0] + i * s->frame.linesize[0],
00127 DEFAULT_BG_COLOR, avctx->width);
00128 }
00129
00130 static void erase_line(AVCodecContext * avctx, int xoffset, int xlength)
00131 {
00132 AnsiContext *s = avctx->priv_data;
00133 int i;
00134 for (i = 0; i < s->font_height; i++)
00135 memset(s->frame.data[0] + (s->y + i)*s->frame.linesize[0] + xoffset,
00136 DEFAULT_BG_COLOR, xlength);
00137 }
00138
00139 static void erase_screen(AVCodecContext *avctx)
00140 {
00141 AnsiContext *s = avctx->priv_data;
00142 int i;
00143 for (i = 0; i < avctx->height; i++)
00144 memset(s->frame.data[0] + i * s->frame.linesize[0], DEFAULT_BG_COLOR, avctx->width);
00145 s->x = s->y = 0;
00146 }
00147
00151 static void draw_char(AVCodecContext *avctx, int c)
00152 {
00153 AnsiContext *s = avctx->priv_data;
00154 int fg = s->fg;
00155 int bg = s->bg;
00156
00157 if ((s->attributes & ATTR_BOLD))
00158 fg += 8;
00159 if ((s->attributes & ATTR_BLINK))
00160 bg += 8;
00161 if ((s->attributes & ATTR_REVERSE))
00162 FFSWAP(int, fg, bg);
00163 if ((s->attributes & ATTR_CONCEALED))
00164 fg = bg;
00165 ff_draw_pc_font(s->frame.data[0] + s->y * s->frame.linesize[0] + s->x,
00166 s->frame.linesize[0], s->font, s->font_height, c, fg, bg);
00167 s->x += FONT_WIDTH;
00168 if (s->x >= avctx->width) {
00169 s->x = 0;
00170 hscroll(avctx);
00171 }
00172 }
00173
00178 static int execute_code(AVCodecContext * avctx, int c)
00179 {
00180 AnsiContext *s = avctx->priv_data;
00181 int ret, i, width, height;
00182 switch(c) {
00183 case 'A':
00184 s->y = FFMAX(s->y - (s->nb_args > 0 ? s->args[0]*s->font_height : s->font_height), 0);
00185 break;
00186 case 'B':
00187 s->y = FFMIN(s->y + (s->nb_args > 0 ? s->args[0]*s->font_height : s->font_height), avctx->height - s->font_height);
00188 break;
00189 case 'C':
00190 s->x = FFMIN(s->x + (s->nb_args > 0 ? s->args[0]*FONT_WIDTH : FONT_WIDTH), avctx->width - FONT_WIDTH);
00191 break;
00192 case 'D':
00193 s->x = FFMAX(s->x - (s->nb_args > 0 ? s->args[0]*FONT_WIDTH : FONT_WIDTH), 0);
00194 break;
00195 case 'H':
00196 case 'f':
00197 s->y = s->nb_args > 0 ? av_clip((s->args[0] - 1)*s->font_height, 0, avctx->height - s->font_height) : 0;
00198 s->x = s->nb_args > 1 ? av_clip((s->args[1] - 1)*FONT_WIDTH, 0, avctx->width - FONT_WIDTH) : 0;
00199 break;
00200 case 'h':
00201 case 'l':
00202 if (s->nb_args < 2)
00203 s->args[0] = DEFAULT_SCREEN_MODE;
00204 width = avctx->width;
00205 height = avctx->height;
00206 switch(s->args[0]) {
00207 case 0: case 1: case 4: case 5: case 13: case 19:
00208 s->font = avpriv_cga_font;
00209 s->font_height = 8;
00210 width = 40<<3;
00211 height = 25<<3;
00212 break;
00213 case 2: case 3:
00214 s->font = avpriv_vga16_font;
00215 s->font_height = 16;
00216 width = 80<<3;
00217 height = 25<<4;
00218 break;
00219 case 6: case 14:
00220 s->font = avpriv_cga_font;
00221 s->font_height = 8;
00222 width = 80<<3;
00223 height = 25<<3;
00224 break;
00225 case 7:
00226 break;
00227 case 15: case 16:
00228 s->font = avpriv_cga_font;
00229 s->font_height = 8;
00230 width = 80<<3;
00231 height = 43<<3;
00232 break;
00233 case 17: case 18:
00234 s->font = avpriv_cga_font;
00235 s->font_height = 8;
00236 width = 80<<3;
00237 height = 60<<4;
00238 break;
00239 default:
00240 av_log_ask_for_sample(avctx, "unsupported screen mode\n");
00241 }
00242 if (width != avctx->width || height != avctx->height) {
00243 if (s->frame.data[0])
00244 avctx->release_buffer(avctx, &s->frame);
00245 avcodec_set_dimensions(avctx, width, height);
00246 ret = ff_get_buffer(avctx, &s->frame);
00247 if (ret < 0) {
00248 av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
00249 return ret;
00250 }
00251 s->frame.pict_type = AV_PICTURE_TYPE_I;
00252 s->frame.palette_has_changed = 1;
00253 set_palette((uint32_t *)s->frame.data[1]);
00254 erase_screen(avctx);
00255 } else if (c == 'l') {
00256 erase_screen(avctx);
00257 }
00258 break;
00259 case 'J':
00260 switch (s->args[0]) {
00261 case 0:
00262 erase_line(avctx, s->x, avctx->width - s->x);
00263 if (s->y < avctx->height - s->font_height)
00264 memset(s->frame.data[0] + (s->y + s->font_height)*s->frame.linesize[0],
00265 DEFAULT_BG_COLOR, (avctx->height - s->y - s->font_height)*s->frame.linesize[0]);
00266 break;
00267 case 1:
00268 erase_line(avctx, 0, s->x);
00269 if (s->y > 0)
00270 memset(s->frame.data[0], DEFAULT_BG_COLOR, s->y * s->frame.linesize[0]);
00271 break;
00272 case 2:
00273 erase_screen(avctx);
00274 }
00275 break;
00276 case 'K':
00277 switch(s->args[0]) {
00278 case 0:
00279 erase_line(avctx, s->x, avctx->width - s->x);
00280 break;
00281 case 1:
00282 erase_line(avctx, 0, s->x);
00283 break;
00284 case 2:
00285 erase_line(avctx, 0, avctx->width);
00286 }
00287 break;
00288 case 'm':
00289 if (s->nb_args == 0) {
00290 s->nb_args = 1;
00291 s->args[0] = 0;
00292 }
00293 for (i = 0; i < FFMIN(s->nb_args, MAX_NB_ARGS); i++) {
00294 int m = s->args[i];
00295 if (m == 0) {
00296 s->attributes = 0;
00297 s->fg = DEFAULT_FG_COLOR;
00298 s->bg = DEFAULT_BG_COLOR;
00299 } else if (m == 1 || m == 2 || m == 4 || m == 5 || m == 7 || m == 8) {
00300 s->attributes |= 1 << (m - 1);
00301 } else if (m >= 30 && m <= 37) {
00302 s->fg = ansi_to_cga[m - 30];
00303 } else if (m == 38 && i + 2 < FFMIN(s->nb_args, MAX_NB_ARGS) && s->args[i + 1] == 5 && s->args[i + 2] < 256) {
00304 int index = s->args[i + 2];
00305 s->fg = index < 16 ? ansi_to_cga[index] : index;
00306 i += 2;
00307 } else if (m == 39) {
00308 s->fg = ansi_to_cga[DEFAULT_FG_COLOR];
00309 } else if (m >= 40 && m <= 47) {
00310 s->bg = ansi_to_cga[m - 40];
00311 } else if (m == 48 && i + 2 < FFMIN(s->nb_args, MAX_NB_ARGS) && s->args[i + 1] == 5 && s->args[i + 2] < 256) {
00312 int index = s->args[i + 2];
00313 s->bg = index < 16 ? ansi_to_cga[index] : index;
00314 i += 2;
00315 } else if (m == 49) {
00316 s->fg = ansi_to_cga[DEFAULT_BG_COLOR];
00317 } else {
00318 av_log_ask_for_sample(avctx, "unsupported rendition parameter\n");
00319 }
00320 }
00321 break;
00322 case 'n':
00323 case 'R':
00324
00325 break;
00326 case 's':
00327 s->sx = s->x;
00328 s->sy = s->y;
00329 break;
00330 case 'u':
00331 s->x = av_clip(s->sx, 0, avctx->width - FONT_WIDTH);
00332 s->y = av_clip(s->sy, 0, avctx->height - s->font_height);
00333 break;
00334 default:
00335 av_log_ask_for_sample(avctx, "unsupported escape code\n");
00336 break;
00337 }
00338 return 0;
00339 }
00340
00341 static int decode_frame(AVCodecContext *avctx,
00342 void *data, int *got_frame,
00343 AVPacket *avpkt)
00344 {
00345 AnsiContext *s = avctx->priv_data;
00346 uint8_t *buf = avpkt->data;
00347 int buf_size = avpkt->size;
00348 const uint8_t *buf_end = buf+buf_size;
00349 int ret, i, count;
00350
00351 ret = avctx->reget_buffer(avctx, &s->frame);
00352 if (ret < 0){
00353 av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
00354 return ret;
00355 }
00356 s->frame.pict_type = AV_PICTURE_TYPE_I;
00357 s->frame.palette_has_changed = 1;
00358 set_palette((uint32_t *)s->frame.data[1]);
00359 if (!s->first_frame) {
00360 erase_screen(avctx);
00361 s->first_frame = 1;
00362 }
00363
00364 while(buf < buf_end) {
00365 switch(s->state) {
00366 case STATE_NORMAL:
00367 switch (buf[0]) {
00368 case 0x00:
00369 case 0x07:
00370 case 0x1A:
00371
00372 break;
00373 case 0x08:
00374 s->x = FFMAX(s->x - 1, 0);
00375 break;
00376 case 0x09:
00377 i = s->x / FONT_WIDTH;
00378 count = ((i + 8) & ~7) - i;
00379 for (i = 0; i < count; i++)
00380 draw_char(avctx, ' ');
00381 break;
00382 case 0x0A:
00383 hscroll(avctx);
00384 case 0x0D:
00385 s->x = 0;
00386 break;
00387 case 0x0C:
00388 erase_screen(avctx);
00389 break;
00390 case 0x1B:
00391 s->state = STATE_ESCAPE;
00392 break;
00393 default:
00394 draw_char(avctx, buf[0]);
00395 }
00396 break;
00397 case STATE_ESCAPE:
00398 if (buf[0] == '[') {
00399 s->state = STATE_CODE;
00400 s->nb_args = 0;
00401 s->args[0] = -1;
00402 } else {
00403 s->state = STATE_NORMAL;
00404 draw_char(avctx, 0x1B);
00405 continue;
00406 }
00407 break;
00408 case STATE_CODE:
00409 switch(buf[0]) {
00410 case '0': case '1': case '2': case '3': case '4':
00411 case '5': case '6': case '7': case '8': case '9':
00412 if (s->nb_args < MAX_NB_ARGS)
00413 s->args[s->nb_args] = FFMAX(s->args[s->nb_args], 0) * 10 + buf[0] - '0';
00414 break;
00415 case ';':
00416 s->nb_args++;
00417 if (s->nb_args < MAX_NB_ARGS)
00418 s->args[s->nb_args] = 0;
00419 break;
00420 case 'M':
00421 s->state = STATE_MUSIC_PREAMBLE;
00422 break;
00423 case '=': case '?':
00424
00425 break;
00426 default:
00427 if (s->nb_args > MAX_NB_ARGS)
00428 av_log(avctx, AV_LOG_WARNING, "args overflow (%i)\n", s->nb_args);
00429 if (s->nb_args < MAX_NB_ARGS && s->args[s->nb_args] >= 0)
00430 s->nb_args++;
00431 if (execute_code(avctx, buf[0]) < 0)
00432 return -1;
00433 s->state = STATE_NORMAL;
00434 }
00435 break;
00436 case STATE_MUSIC_PREAMBLE:
00437 if (buf[0] == 0x0E || buf[0] == 0x1B)
00438 s->state = STATE_NORMAL;
00439
00440 break;
00441 }
00442 buf++;
00443 }
00444
00445 *got_frame = 1;
00446 *(AVFrame*)data = s->frame;
00447 return buf_size;
00448 }
00449
00450 static av_cold int decode_close(AVCodecContext *avctx)
00451 {
00452 AnsiContext *s = avctx->priv_data;
00453 if (s->frame.data[0])
00454 avctx->release_buffer(avctx, &s->frame);
00455 return 0;
00456 }
00457
00458 AVCodec ff_ansi_decoder = {
00459 .name = "ansi",
00460 .type = AVMEDIA_TYPE_VIDEO,
00461 .id = AV_CODEC_ID_ANSI,
00462 .priv_data_size = sizeof(AnsiContext),
00463 .init = decode_init,
00464 .close = decode_close,
00465 .decode = decode_frame,
00466 .capabilities = CODEC_CAP_DR1,
00467 .long_name = NULL_IF_CONFIG_SMALL("ASCII/ANSI art"),
00468 };