annotate ffmpeg/libavcodec/ansi.c @ 13:844d341cf643 tip

Back up before ISMIR
author Yading Song <yading.song@eecs.qmul.ac.uk>
date Thu, 31 Oct 2013 13:17:06 +0000
parents 6840f77b83aa
children
rev   line source
yading@10 1 /*
yading@10 2 * ASCII/ANSI art decoder
yading@10 3 * Copyright (c) 2010 Peter Ross <pross@xvid.org>
yading@10 4 *
yading@10 5 * This file is part of FFmpeg.
yading@10 6 *
yading@10 7 * FFmpeg is free software; you can redistribute it and/or
yading@10 8 * modify it under the terms of the GNU Lesser General Public
yading@10 9 * License as published by the Free Software Foundation; either
yading@10 10 * version 2.1 of the License, or (at your option) any later version.
yading@10 11 *
yading@10 12 * FFmpeg is distributed in the hope that it will be useful,
yading@10 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
yading@10 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
yading@10 15 * Lesser General Public License for more details.
yading@10 16 *
yading@10 17 * You should have received a copy of the GNU Lesser General Public
yading@10 18 * License along with FFmpeg; if not, write to the Free Software
yading@10 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
yading@10 20 */
yading@10 21
yading@10 22 /**
yading@10 23 * @file
yading@10 24 * ASCII/ANSI art decoder
yading@10 25 */
yading@10 26
yading@10 27 #include "libavutil/common.h"
yading@10 28 #include "libavutil/frame.h"
yading@10 29 #include "libavutil/lfg.h"
yading@10 30 #include "libavutil/xga_font_data.h"
yading@10 31 #include "avcodec.h"
yading@10 32 #include "cga_data.h"
yading@10 33 #include "internal.h"
yading@10 34
yading@10 35 #define ATTR_BOLD 0x01 /**< Bold/Bright-foreground (mode 1) */
yading@10 36 #define ATTR_FAINT 0x02 /**< Faint (mode 2) */
yading@10 37 #define ATTR_UNDERLINE 0x08 /**< Underline (mode 4) */
yading@10 38 #define ATTR_BLINK 0x10 /**< Blink/Bright-background (mode 5) */
yading@10 39 #define ATTR_REVERSE 0x40 /**< Reverse (mode 7) */
yading@10 40 #define ATTR_CONCEALED 0x80 /**< Concealed (mode 8) */
yading@10 41
yading@10 42 #define DEFAULT_FG_COLOR 7 /**< CGA color index */
yading@10 43 #define DEFAULT_BG_COLOR 0
yading@10 44 #define DEFAULT_SCREEN_MODE 3 /**< 80x25 */
yading@10 45
yading@10 46 #define FONT_WIDTH 8 /**< Font width */
yading@10 47
yading@10 48 /** map ansi color index to cga palette index */
yading@10 49 static const uint8_t ansi_to_cga[16] = {
yading@10 50 0, 4, 2, 6, 1, 5, 3, 7, 8, 12, 10, 14, 9, 13, 11, 15
yading@10 51 };
yading@10 52
yading@10 53 typedef struct {
yading@10 54 AVFrame *frame;
yading@10 55 int x; /**< x cursor position (pixels) */
yading@10 56 int y; /**< y cursor position (pixels) */
yading@10 57 int sx; /**< saved x cursor position (pixels) */
yading@10 58 int sy; /**< saved y cursor position (pixels) */
yading@10 59 const uint8_t* font; /**< font */
yading@10 60 int font_height; /**< font height */
yading@10 61 int attributes; /**< attribute flags */
yading@10 62 int fg; /**< foreground color */
yading@10 63 int bg; /**< background color */
yading@10 64 int first_frame;
yading@10 65
yading@10 66 /* ansi parser state machine */
yading@10 67 enum {
yading@10 68 STATE_NORMAL = 0,
yading@10 69 STATE_ESCAPE,
yading@10 70 STATE_CODE,
yading@10 71 STATE_MUSIC_PREAMBLE
yading@10 72 } state;
yading@10 73 #define MAX_NB_ARGS 4
yading@10 74 int args[MAX_NB_ARGS];
yading@10 75 int nb_args; /**< number of arguments (may exceed MAX_NB_ARGS) */
yading@10 76 } AnsiContext;
yading@10 77
yading@10 78 static av_cold int decode_init(AVCodecContext *avctx)
yading@10 79 {
yading@10 80 AnsiContext *s = avctx->priv_data;
yading@10 81 avctx->pix_fmt = AV_PIX_FMT_PAL8;
yading@10 82
yading@10 83 s->frame = av_frame_alloc();
yading@10 84 if (!s->frame)
yading@10 85 return AVERROR(ENOMEM);
yading@10 86
yading@10 87 /* defaults */
yading@10 88 s->font = avpriv_vga16_font;
yading@10 89 s->font_height = 16;
yading@10 90 s->fg = DEFAULT_FG_COLOR;
yading@10 91 s->bg = DEFAULT_BG_COLOR;
yading@10 92
yading@10 93 if (!avctx->width || !avctx->height)
yading@10 94 avcodec_set_dimensions(avctx, 80<<3, 25<<4);
yading@10 95
yading@10 96 return 0;
yading@10 97 }
yading@10 98
yading@10 99 static void set_palette(uint32_t *pal)
yading@10 100 {
yading@10 101 int r, g, b;
yading@10 102 memcpy(pal, ff_cga_palette, 16 * 4);
yading@10 103 pal += 16;
yading@10 104 #define COLOR(x) ((x) * 40 + 55)
yading@10 105 for (r = 0; r < 6; r++)
yading@10 106 for (g = 0; g < 6; g++)
yading@10 107 for (b = 0; b < 6; b++)
yading@10 108 *pal++ = 0xFF000000 | (COLOR(r) << 16) | (COLOR(g) << 8) | COLOR(b);
yading@10 109 #define GRAY(x) ((x) * 10 + 8)
yading@10 110 for (g = 0; g < 24; g++)
yading@10 111 *pal++ = 0xFF000000 | (GRAY(g) << 16) | (GRAY(g) << 8) | GRAY(g);
yading@10 112 }
yading@10 113
yading@10 114 static void hscroll(AVCodecContext *avctx)
yading@10 115 {
yading@10 116 AnsiContext *s = avctx->priv_data;
yading@10 117 int i;
yading@10 118
yading@10 119 if (s->y < avctx->height - s->font_height) {
yading@10 120 s->y += s->font_height;
yading@10 121 return;
yading@10 122 }
yading@10 123
yading@10 124 i = 0;
yading@10 125 for (; i < avctx->height - s->font_height; i++)
yading@10 126 memcpy(s->frame->data[0] + i * s->frame->linesize[0],
yading@10 127 s->frame->data[0] + (i + s->font_height) * s->frame->linesize[0],
yading@10 128 avctx->width);
yading@10 129 for (; i < avctx->height; i++)
yading@10 130 memset(s->frame->data[0] + i * s->frame->linesize[0],
yading@10 131 DEFAULT_BG_COLOR, avctx->width);
yading@10 132 }
yading@10 133
yading@10 134 static void erase_line(AVCodecContext * avctx, int xoffset, int xlength)
yading@10 135 {
yading@10 136 AnsiContext *s = avctx->priv_data;
yading@10 137 int i;
yading@10 138 for (i = 0; i < s->font_height; i++)
yading@10 139 memset(s->frame->data[0] + (s->y + i)*s->frame->linesize[0] + xoffset,
yading@10 140 DEFAULT_BG_COLOR, xlength);
yading@10 141 }
yading@10 142
yading@10 143 static void erase_screen(AVCodecContext *avctx)
yading@10 144 {
yading@10 145 AnsiContext *s = avctx->priv_data;
yading@10 146 int i;
yading@10 147 for (i = 0; i < avctx->height; i++)
yading@10 148 memset(s->frame->data[0] + i * s->frame->linesize[0], DEFAULT_BG_COLOR, avctx->width);
yading@10 149 s->x = s->y = 0;
yading@10 150 }
yading@10 151
yading@10 152 /**
yading@10 153 * Draw character to screen
yading@10 154 */
yading@10 155 static void draw_char(AVCodecContext *avctx, int c)
yading@10 156 {
yading@10 157 AnsiContext *s = avctx->priv_data;
yading@10 158 int fg = s->fg;
yading@10 159 int bg = s->bg;
yading@10 160
yading@10 161 if ((s->attributes & ATTR_BOLD))
yading@10 162 fg += 8;
yading@10 163 if ((s->attributes & ATTR_BLINK))
yading@10 164 bg += 8;
yading@10 165 if ((s->attributes & ATTR_REVERSE))
yading@10 166 FFSWAP(int, fg, bg);
yading@10 167 if ((s->attributes & ATTR_CONCEALED))
yading@10 168 fg = bg;
yading@10 169 ff_draw_pc_font(s->frame->data[0] + s->y * s->frame->linesize[0] + s->x,
yading@10 170 s->frame->linesize[0], s->font, s->font_height, c, fg, bg);
yading@10 171 s->x += FONT_WIDTH;
yading@10 172 if (s->x >= avctx->width) {
yading@10 173 s->x = 0;
yading@10 174 hscroll(avctx);
yading@10 175 }
yading@10 176 }
yading@10 177
yading@10 178 /**
yading@10 179 * Execute ANSI escape code
yading@10 180 * @return 0 on success, negative on error
yading@10 181 */
yading@10 182 static int execute_code(AVCodecContext * avctx, int c)
yading@10 183 {
yading@10 184 AnsiContext *s = avctx->priv_data;
yading@10 185 int ret, i, width, height;
yading@10 186 switch(c) {
yading@10 187 case 'A': //Cursor Up
yading@10 188 s->y = FFMAX(s->y - (s->nb_args > 0 ? s->args[0]*s->font_height : s->font_height), 0);
yading@10 189 break;
yading@10 190 case 'B': //Cursor Down
yading@10 191 s->y = FFMIN(s->y + (s->nb_args > 0 ? s->args[0]*s->font_height : s->font_height), avctx->height - s->font_height);
yading@10 192 break;
yading@10 193 case 'C': //Cursor Right
yading@10 194 s->x = FFMIN(s->x + (s->nb_args > 0 ? s->args[0]*FONT_WIDTH : FONT_WIDTH), avctx->width - FONT_WIDTH);
yading@10 195 break;
yading@10 196 case 'D': //Cursor Left
yading@10 197 s->x = FFMAX(s->x - (s->nb_args > 0 ? s->args[0]*FONT_WIDTH : FONT_WIDTH), 0);
yading@10 198 break;
yading@10 199 case 'H': //Cursor Position
yading@10 200 case 'f': //Horizontal and Vertical Position
yading@10 201 s->y = s->nb_args > 0 ? av_clip((s->args[0] - 1)*s->font_height, 0, avctx->height - s->font_height) : 0;
yading@10 202 s->x = s->nb_args > 1 ? av_clip((s->args[1] - 1)*FONT_WIDTH, 0, avctx->width - FONT_WIDTH) : 0;
yading@10 203 break;
yading@10 204 case 'h': //set creen mode
yading@10 205 case 'l': //reset screen mode
yading@10 206 if (s->nb_args < 2)
yading@10 207 s->args[0] = DEFAULT_SCREEN_MODE;
yading@10 208 width = avctx->width;
yading@10 209 height = avctx->height;
yading@10 210 switch(s->args[0]) {
yading@10 211 case 0: case 1: case 4: case 5: case 13: case 19: //320x200 (25 rows)
yading@10 212 s->font = avpriv_cga_font;
yading@10 213 s->font_height = 8;
yading@10 214 width = 40<<3;
yading@10 215 height = 25<<3;
yading@10 216 break;
yading@10 217 case 2: case 3: //640x400 (25 rows)
yading@10 218 s->font = avpriv_vga16_font;
yading@10 219 s->font_height = 16;
yading@10 220 width = 80<<3;
yading@10 221 height = 25<<4;
yading@10 222 break;
yading@10 223 case 6: case 14: //640x200 (25 rows)
yading@10 224 s->font = avpriv_cga_font;
yading@10 225 s->font_height = 8;
yading@10 226 width = 80<<3;
yading@10 227 height = 25<<3;
yading@10 228 break;
yading@10 229 case 7: //set line wrapping
yading@10 230 break;
yading@10 231 case 15: case 16: //640x350 (43 rows)
yading@10 232 s->font = avpriv_cga_font;
yading@10 233 s->font_height = 8;
yading@10 234 width = 80<<3;
yading@10 235 height = 43<<3;
yading@10 236 break;
yading@10 237 case 17: case 18: //640x480 (60 rows)
yading@10 238 s->font = avpriv_cga_font;
yading@10 239 s->font_height = 8;
yading@10 240 width = 80<<3;
yading@10 241 height = 60<<4;
yading@10 242 break;
yading@10 243 default:
yading@10 244 avpriv_request_sample(avctx, "Unsupported screen mode");
yading@10 245 }
yading@10 246 if (width != avctx->width || height != avctx->height) {
yading@10 247 av_frame_unref(s->frame);
yading@10 248 avcodec_set_dimensions(avctx, width, height);
yading@10 249 if ((ret = ff_get_buffer(avctx, s->frame,
yading@10 250 AV_GET_BUFFER_FLAG_REF)) < 0)
yading@10 251 return ret;
yading@10 252 s->frame->pict_type = AV_PICTURE_TYPE_I;
yading@10 253 s->frame->palette_has_changed = 1;
yading@10 254 set_palette((uint32_t *)s->frame->data[1]);
yading@10 255 erase_screen(avctx);
yading@10 256 } else if (c == 'l') {
yading@10 257 erase_screen(avctx);
yading@10 258 }
yading@10 259 break;
yading@10 260 case 'J': //Erase in Page
yading@10 261 switch (s->args[0]) {
yading@10 262 case 0:
yading@10 263 erase_line(avctx, s->x, avctx->width - s->x);
yading@10 264 if (s->y < avctx->height - s->font_height)
yading@10 265 memset(s->frame->data[0] + (s->y + s->font_height)*s->frame->linesize[0],
yading@10 266 DEFAULT_BG_COLOR, (avctx->height - s->y - s->font_height)*s->frame->linesize[0]);
yading@10 267 break;
yading@10 268 case 1:
yading@10 269 erase_line(avctx, 0, s->x);
yading@10 270 if (s->y > 0)
yading@10 271 memset(s->frame->data[0], DEFAULT_BG_COLOR, s->y * s->frame->linesize[0]);
yading@10 272 break;
yading@10 273 case 2:
yading@10 274 erase_screen(avctx);
yading@10 275 }
yading@10 276 break;
yading@10 277 case 'K': //Erase in Line
yading@10 278 switch(s->args[0]) {
yading@10 279 case 0:
yading@10 280 erase_line(avctx, s->x, avctx->width - s->x);
yading@10 281 break;
yading@10 282 case 1:
yading@10 283 erase_line(avctx, 0, s->x);
yading@10 284 break;
yading@10 285 case 2:
yading@10 286 erase_line(avctx, 0, avctx->width);
yading@10 287 }
yading@10 288 break;
yading@10 289 case 'm': //Select Graphics Rendition
yading@10 290 if (s->nb_args == 0) {
yading@10 291 s->nb_args = 1;
yading@10 292 s->args[0] = 0;
yading@10 293 }
yading@10 294 for (i = 0; i < FFMIN(s->nb_args, MAX_NB_ARGS); i++) {
yading@10 295 int m = s->args[i];
yading@10 296 if (m == 0) {
yading@10 297 s->attributes = 0;
yading@10 298 s->fg = DEFAULT_FG_COLOR;
yading@10 299 s->bg = DEFAULT_BG_COLOR;
yading@10 300 } else if (m == 1 || m == 2 || m == 4 || m == 5 || m == 7 || m == 8) {
yading@10 301 s->attributes |= 1 << (m - 1);
yading@10 302 } else if (m >= 30 && m <= 37) {
yading@10 303 s->fg = ansi_to_cga[m - 30];
yading@10 304 } else if (m == 38 && i + 2 < FFMIN(s->nb_args, MAX_NB_ARGS) && s->args[i + 1] == 5 && s->args[i + 2] < 256) {
yading@10 305 int index = s->args[i + 2];
yading@10 306 s->fg = index < 16 ? ansi_to_cga[index] : index;
yading@10 307 i += 2;
yading@10 308 } else if (m == 39) {
yading@10 309 s->fg = ansi_to_cga[DEFAULT_FG_COLOR];
yading@10 310 } else if (m >= 40 && m <= 47) {
yading@10 311 s->bg = ansi_to_cga[m - 40];
yading@10 312 } else if (m == 48 && i + 2 < FFMIN(s->nb_args, MAX_NB_ARGS) && s->args[i + 1] == 5 && s->args[i + 2] < 256) {
yading@10 313 int index = s->args[i + 2];
yading@10 314 s->bg = index < 16 ? ansi_to_cga[index] : index;
yading@10 315 i += 2;
yading@10 316 } else if (m == 49) {
yading@10 317 s->fg = ansi_to_cga[DEFAULT_BG_COLOR];
yading@10 318 } else {
yading@10 319 avpriv_request_sample(avctx, "Unsupported rendition parameter");
yading@10 320 }
yading@10 321 }
yading@10 322 break;
yading@10 323 case 'n': //Device Status Report
yading@10 324 case 'R': //report current line and column
yading@10 325 /* ignore */
yading@10 326 break;
yading@10 327 case 's': //Save Cursor Position
yading@10 328 s->sx = s->x;
yading@10 329 s->sy = s->y;
yading@10 330 break;
yading@10 331 case 'u': //Restore Cursor Position
yading@10 332 s->x = av_clip(s->sx, 0, avctx->width - FONT_WIDTH);
yading@10 333 s->y = av_clip(s->sy, 0, avctx->height - s->font_height);
yading@10 334 break;
yading@10 335 default:
yading@10 336 avpriv_request_sample(avctx, "Unknown escape code");
yading@10 337 break;
yading@10 338 }
yading@10 339 return 0;
yading@10 340 }
yading@10 341
yading@10 342 static int decode_frame(AVCodecContext *avctx,
yading@10 343 void *data, int *got_frame,
yading@10 344 AVPacket *avpkt)
yading@10 345 {
yading@10 346 AnsiContext *s = avctx->priv_data;
yading@10 347 uint8_t *buf = avpkt->data;
yading@10 348 int buf_size = avpkt->size;
yading@10 349 const uint8_t *buf_end = buf+buf_size;
yading@10 350 int ret, i, count;
yading@10 351
yading@10 352 if ((ret = ff_reget_buffer(avctx, s->frame)) < 0)
yading@10 353 return ret;
yading@10 354 if (!avctx->frame_number) {
yading@10 355 for (i=0; i<avctx->height; i++)
yading@10 356 memset(s->frame->data[0]+ i*s->frame->linesize[0], 0, avctx->width);
yading@10 357 memset(s->frame->data[1], 0, AVPALETTE_SIZE);
yading@10 358 }
yading@10 359
yading@10 360 s->frame->pict_type = AV_PICTURE_TYPE_I;
yading@10 361 s->frame->palette_has_changed = 1;
yading@10 362 set_palette((uint32_t *)s->frame->data[1]);
yading@10 363 if (!s->first_frame) {
yading@10 364 erase_screen(avctx);
yading@10 365 s->first_frame = 1;
yading@10 366 }
yading@10 367
yading@10 368 while(buf < buf_end) {
yading@10 369 switch(s->state) {
yading@10 370 case STATE_NORMAL:
yading@10 371 switch (buf[0]) {
yading@10 372 case 0x00: //NUL
yading@10 373 case 0x07: //BEL
yading@10 374 case 0x1A: //SUB
yading@10 375 /* ignore */
yading@10 376 break;
yading@10 377 case 0x08: //BS
yading@10 378 s->x = FFMAX(s->x - 1, 0);
yading@10 379 break;
yading@10 380 case 0x09: //HT
yading@10 381 i = s->x / FONT_WIDTH;
yading@10 382 count = ((i + 8) & ~7) - i;
yading@10 383 for (i = 0; i < count; i++)
yading@10 384 draw_char(avctx, ' ');
yading@10 385 break;
yading@10 386 case 0x0A: //LF
yading@10 387 hscroll(avctx);
yading@10 388 case 0x0D: //CR
yading@10 389 s->x = 0;
yading@10 390 break;
yading@10 391 case 0x0C: //FF
yading@10 392 erase_screen(avctx);
yading@10 393 break;
yading@10 394 case 0x1B: //ESC
yading@10 395 s->state = STATE_ESCAPE;
yading@10 396 break;
yading@10 397 default:
yading@10 398 draw_char(avctx, buf[0]);
yading@10 399 }
yading@10 400 break;
yading@10 401 case STATE_ESCAPE:
yading@10 402 if (buf[0] == '[') {
yading@10 403 s->state = STATE_CODE;
yading@10 404 s->nb_args = 0;
yading@10 405 s->args[0] = -1;
yading@10 406 } else {
yading@10 407 s->state = STATE_NORMAL;
yading@10 408 draw_char(avctx, 0x1B);
yading@10 409 continue;
yading@10 410 }
yading@10 411 break;
yading@10 412 case STATE_CODE:
yading@10 413 switch(buf[0]) {
yading@10 414 case '0': case '1': case '2': case '3': case '4':
yading@10 415 case '5': case '6': case '7': case '8': case '9':
yading@10 416 if (s->nb_args < MAX_NB_ARGS)
yading@10 417 s->args[s->nb_args] = FFMAX(s->args[s->nb_args], 0) * 10 + buf[0] - '0';
yading@10 418 break;
yading@10 419 case ';':
yading@10 420 s->nb_args++;
yading@10 421 if (s->nb_args < MAX_NB_ARGS)
yading@10 422 s->args[s->nb_args] = 0;
yading@10 423 break;
yading@10 424 case 'M':
yading@10 425 s->state = STATE_MUSIC_PREAMBLE;
yading@10 426 break;
yading@10 427 case '=': case '?':
yading@10 428 /* ignore */
yading@10 429 break;
yading@10 430 default:
yading@10 431 if (s->nb_args > MAX_NB_ARGS)
yading@10 432 av_log(avctx, AV_LOG_WARNING, "args overflow (%i)\n", s->nb_args);
yading@10 433 if (s->nb_args < MAX_NB_ARGS && s->args[s->nb_args] >= 0)
yading@10 434 s->nb_args++;
yading@10 435 if ((ret = execute_code(avctx, buf[0])) < 0)
yading@10 436 return ret;
yading@10 437 s->state = STATE_NORMAL;
yading@10 438 }
yading@10 439 break;
yading@10 440 case STATE_MUSIC_PREAMBLE:
yading@10 441 if (buf[0] == 0x0E || buf[0] == 0x1B)
yading@10 442 s->state = STATE_NORMAL;
yading@10 443 /* ignore music data */
yading@10 444 break;
yading@10 445 }
yading@10 446 buf++;
yading@10 447 }
yading@10 448
yading@10 449 *got_frame = 1;
yading@10 450 if ((ret = av_frame_ref(data, s->frame)) < 0)
yading@10 451 return ret;
yading@10 452 return buf_size;
yading@10 453 }
yading@10 454
yading@10 455 static av_cold int decode_close(AVCodecContext *avctx)
yading@10 456 {
yading@10 457 AnsiContext *s = avctx->priv_data;
yading@10 458
yading@10 459 av_frame_free(&s->frame);
yading@10 460 return 0;
yading@10 461 }
yading@10 462
yading@10 463 AVCodec ff_ansi_decoder = {
yading@10 464 .name = "ansi",
yading@10 465 .type = AVMEDIA_TYPE_VIDEO,
yading@10 466 .id = AV_CODEC_ID_ANSI,
yading@10 467 .priv_data_size = sizeof(AnsiContext),
yading@10 468 .init = decode_init,
yading@10 469 .close = decode_close,
yading@10 470 .decode = decode_frame,
yading@10 471 .capabilities = CODEC_CAP_DR1,
yading@10 472 .long_name = NULL_IF_CONFIG_SMALL("ASCII/ANSI art"),
yading@10 473 };