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