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