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
ff_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;
186
int
width
= avctx->
width
;
187
int
height
= avctx->
height
;
188
189
switch
(c) {
190
case
'A'
:
//Cursor Up
191
s->
y
=
FFMAX
(s->
y
- (s->
nb_args
> 0 ? s->
args
[0]*s->
font_height
: s->
font_height
), 0);
192
break
;
193
case
'B'
:
//Cursor Down
194
s->
y
=
FFMIN
(s->
y
+ (s->
nb_args
> 0 ? s->
args
[0]*s->
font_height
: s->
font_height
), avctx->
height
- s->
font_height
);
195
break
;
196
case
'C'
:
//Cursor Right
197
s->
x
=
FFMIN
(s->
x
+ (s->
nb_args
> 0 ? s->
args
[0]*
FONT_WIDTH
:
FONT_WIDTH
), avctx->
width
- FONT_WIDTH);
198
break
;
199
case
'D'
:
//Cursor Left
200
s->
x
=
FFMAX
(s->
x
- (s->
nb_args
> 0 ? s->
args
[0]*FONT_WIDTH : FONT_WIDTH), 0);
201
break
;
202
case
'H'
:
//Cursor Position
203
case
'f'
:
//Horizontal and Vertical Position
204
s->
y
= s->
nb_args
> 0 ? av_clip((s->
args
[0] - 1)*s->
font_height
, 0, avctx->
height
- s->
font_height
) : 0;
205
s->
x
= s->
nb_args
> 1 ? av_clip((s->
args
[1] - 1)*FONT_WIDTH, 0, avctx->
width
- FONT_WIDTH) : 0;
206
break
;
207
case
'h'
:
//set creen mode
208
case
'l'
:
//reset screen mode
209
if
(s->
nb_args
< 2)
210
s->
args
[0] =
DEFAULT_SCREEN_MODE
;
211
switch
(s->
args
[0]) {
212
case
0:
case
1:
case
4:
case
5:
case
13:
case
19:
//320x200 (25 rows)
213
s->
font
=
avpriv_cga_font
;
214
s->
font_height
= 8;
215
width = 40<<3;
216
height = 25<<3;
217
break
;
218
case
2:
case
3:
//640x400 (25 rows)
219
s->
font
=
avpriv_vga16_font
;
220
s->
font_height
= 16;
221
width = 80<<3;
222
height = 25<<4;
223
break
;
224
case
6:
case
14:
//640x200 (25 rows)
225
s->
font
=
avpriv_cga_font
;
226
s->
font_height
= 8;
227
width = 80<<3;
228
height = 25<<3;
229
break
;
230
case
7:
//set line wrapping
231
break
;
232
case
15:
case
16:
//640x350 (43 rows)
233
s->
font
=
avpriv_cga_font
;
234
s->
font_height
= 8;
235
width = 80<<3;
236
height = 43<<3;
237
break
;
238
case
17:
case
18:
//640x480 (60 rows)
239
s->
font
=
avpriv_cga_font
;
240
s->
font_height
= 8;
241
width = 80<<3;
242
height = 60<<4;
243
break
;
244
default
:
245
avpriv_request_sample
(avctx,
"Unsupported screen mode"
);
246
}
247
s->
x
= av_clip(s->
x
, 0, width - FONT_WIDTH);
248
s->
y
= av_clip(s->
y
, 0, height - s->
font_height
);
249
if
(width != avctx->
width
|| height != avctx->
height
) {
250
av_frame_unref
(s->
frame
);
251
ret =
ff_set_dimensions
(avctx, width, height);
252
if
(ret < 0)
253
return
ret
;
254
if
((ret =
ff_get_buffer
(avctx, s->
frame
,
255
AV_GET_BUFFER_FLAG_REF
)) < 0)
256
return
ret
;
257
s->
frame
->
pict_type
=
AV_PICTURE_TYPE_I
;
258
s->
frame
->
palette_has_changed
= 1;
259
set_palette
((uint32_t *)s->
frame
->
data
[1]);
260
erase_screen
(avctx);
261
}
else
if
(c ==
'l'
) {
262
erase_screen
(avctx);
263
}
264
break
;
265
case
'J'
:
//Erase in Page
266
switch
(s->
args
[0]) {
267
case
0:
268
erase_line
(avctx, s->
x
, avctx->
width
- s->
x
);
269
if
(s->
y
< avctx->
height
- s->
font_height
)
270
memset(s->
frame
->
data
[0] + (s->
y
+ s->
font_height
)*s->
frame
->
linesize
[0],
271
DEFAULT_BG_COLOR
, (avctx->
height
- s->
y
- s->
font_height
)*s->
frame
->
linesize
[0]);
272
break
;
273
case
1:
274
erase_line
(avctx, 0, s->
x
);
275
if
(s->
y
> 0)
276
memset(s->
frame
->
data
[0],
DEFAULT_BG_COLOR
, s->
y
* s->
frame
->
linesize
[0]);
277
break
;
278
case
2:
279
erase_screen
(avctx);
280
}
281
break
;
282
case
'K'
:
//Erase in Line
283
switch
(s->
args
[0]) {
284
case
0:
285
erase_line
(avctx, s->
x
, avctx->
width
- s->
x
);
286
break
;
287
case
1:
288
erase_line
(avctx, 0, s->
x
);
289
break
;
290
case
2:
291
erase_line
(avctx, 0, avctx->
width
);
292
}
293
break
;
294
case
'm'
:
//Select Graphics Rendition
295
if
(s->
nb_args
== 0) {
296
s->
nb_args
= 1;
297
s->
args
[0] = 0;
298
}
299
for
(i = 0; i <
FFMIN
(s->
nb_args
,
MAX_NB_ARGS
); i++) {
300
int
m
= s->
args
[i];
301
if
(m == 0) {
302
s->
attributes
= 0;
303
s->
fg
=
DEFAULT_FG_COLOR
;
304
s->
bg
=
DEFAULT_BG_COLOR
;
305
}
else
if
(m == 1 || m == 2 || m == 4 || m == 5 || m == 7 || m == 8) {
306
s->
attributes
|= 1 << (m - 1);
307
}
else
if
(m >= 30 && m <= 37) {
308
s->
fg
=
ansi_to_cga
[m - 30];
309
}
else
if
(m == 38 && i + 2 <
FFMIN
(s->
nb_args
,
MAX_NB_ARGS
) && s->
args
[i + 1] == 5 && s->
args
[i + 2] < 256) {
310
int
index
= s->
args
[i + 2];
311
s->
fg
= index < 16 ?
ansi_to_cga
[
index
] :
index
;
312
i += 2;
313
}
else
if
(m == 39) {
314
s->
fg
=
ansi_to_cga
[
DEFAULT_FG_COLOR
];
315
}
else
if
(m >= 40 && m <= 47) {
316
s->
bg
=
ansi_to_cga
[m - 40];
317
}
else
if
(m == 48 && i + 2 <
FFMIN
(s->
nb_args
,
MAX_NB_ARGS
) && s->
args
[i + 1] == 5 && s->
args
[i + 2] < 256) {
318
int
index
= s->
args
[i + 2];
319
s->
bg
= index < 16 ?
ansi_to_cga
[
index
] :
index
;
320
i += 2;
321
}
else
if
(m == 49) {
322
s->
fg
=
ansi_to_cga
[
DEFAULT_BG_COLOR
];
323
}
else
{
324
avpriv_request_sample
(avctx,
"Unsupported rendition parameter"
);
325
}
326
}
327
break
;
328
case
'n'
:
//Device Status Report
329
case
'R'
:
//report current line and column
330
/* ignore */
331
break
;
332
case
's'
:
//Save Cursor Position
333
s->
sx
= s->
x
;
334
s->
sy
= s->
y
;
335
break
;
336
case
'u'
:
//Restore Cursor Position
337
s->
x
= av_clip(s->
sx
, 0, avctx->
width
- FONT_WIDTH);
338
s->
y
= av_clip(s->
sy
, 0, avctx->
height
- s->
font_height
);
339
break
;
340
default
:
341
avpriv_request_sample
(avctx,
"Unknown escape code"
);
342
break
;
343
}
344
s->
x
= av_clip(s->
x
, 0, avctx->
width
-
FONT_WIDTH
);
345
s->
y
= av_clip(s->
y
, 0, avctx->
height
- s->
font_height
);
346
return
0;
347
}
348
349
static
int
decode_frame
(
AVCodecContext
*avctx,
350
void
*
data
,
int
*got_frame,
351
AVPacket
*avpkt)
352
{
353
AnsiContext
*
s
= avctx->
priv_data
;
354
uint8_t
*
buf
= avpkt->
data
;
355
int
buf_size = avpkt->
size
;
356
const
uint8_t
*buf_end = buf+buf_size;
357
int
ret
, i,
count
;
358
359
if
((ret =
ff_reget_buffer
(avctx, s->
frame
)) < 0)
360
return
ret
;
361
if
(!avctx->
frame_number
) {
362
for
(i=0; i<avctx->
height
; i++)
363
memset(s->
frame
->
data
[0]+ i*s->
frame
->
linesize
[0], 0, avctx->
width
);
364
memset(s->
frame
->
data
[1], 0,
AVPALETTE_SIZE
);
365
}
366
367
s->
frame
->
pict_type
=
AV_PICTURE_TYPE_I
;
368
s->
frame
->
palette_has_changed
= 1;
369
set_palette
((uint32_t *)s->
frame
->
data
[1]);
370
if
(!s->
first_frame
) {
371
erase_screen
(avctx);
372
s->
first_frame
= 1;
373
}
374
375
while
(buf < buf_end) {
376
switch
(s->
state
) {
377
case
STATE_NORMAL:
378
switch
(buf[0]) {
379
case
0x00:
//NUL
380
case
0x07:
//BEL
381
case
0x1A:
//SUB
382
/* ignore */
383
break
;
384
case
0x08:
//BS
385
s->
x
=
FFMAX
(s->
x
- 1, 0);
386
break
;
387
case
0x09:
//HT
388
i = s->
x
/
FONT_WIDTH
;
389
count = ((i + 8) & ~7) - i;
390
for
(i = 0; i <
count
; i++)
391
draw_char
(avctx,
' '
);
392
break
;
393
case
0x0A:
//LF
394
hscroll
(avctx);
395
case
0x0D:
//CR
396
s->
x
= 0;
397
break
;
398
case
0x0C:
//FF
399
erase_screen
(avctx);
400
break
;
401
case
0x1B:
//ESC
402
s->
state
= STATE_ESCAPE;
403
break
;
404
default
:
405
draw_char
(avctx, buf[0]);
406
}
407
break
;
408
case
STATE_ESCAPE:
409
if
(buf[0] ==
'['
) {
410
s->
state
= STATE_CODE;
411
s->
nb_args
= 0;
412
s->
args
[0] = -1;
413
}
else
{
414
s->
state
= STATE_NORMAL;
415
draw_char
(avctx, 0x1B);
416
continue
;
417
}
418
break
;
419
case
STATE_CODE:
420
switch
(buf[0]) {
421
case
'0'
:
case
'1'
:
case
'2'
:
case
'3'
:
case
'4'
:
422
case
'5'
:
case
'6'
:
case
'7'
:
case
'8'
:
case
'9'
:
423
if
(s->
nb_args
<
MAX_NB_ARGS
&& s->
args
[s->
nb_args
] < 6553)
424
s->
args
[s->
nb_args
] =
FFMAX
(s->
args
[s->
nb_args
], 0) * 10 + buf[0] -
'0'
;
425
break
;
426
case
';'
:
427
s->
nb_args
++;
428
if
(s->
nb_args
<
MAX_NB_ARGS
)
429
s->
args
[s->
nb_args
] = 0;
430
break
;
431
case
'M'
:
432
s->
state
= STATE_MUSIC_PREAMBLE;
433
break
;
434
case
'='
:
case
'?'
:
435
/* ignore */
436
break
;
437
default
:
438
if
(s->
nb_args
>
MAX_NB_ARGS
)
439
av_log
(avctx,
AV_LOG_WARNING
,
"args overflow (%i)\n"
, s->
nb_args
);
440
if
(s->
nb_args
<
MAX_NB_ARGS
&& s->
args
[s->
nb_args
] >= 0)
441
s->
nb_args
++;
442
if
((ret =
execute_code
(avctx, buf[0])) < 0)
443
return
ret
;
444
s->
state
= STATE_NORMAL;
445
}
446
break
;
447
case
STATE_MUSIC_PREAMBLE:
448
if
(buf[0] == 0x0E || buf[0] == 0x1B)
449
s->
state
= STATE_NORMAL;
450
/* ignore music data */
451
break
;
452
}
453
buf++;
454
}
455
456
*got_frame = 1;
457
if
((ret =
av_frame_ref
(data, s->
frame
)) < 0)
458
return
ret
;
459
return
buf_size;
460
}
461
462
static
av_cold
int
decode_close
(
AVCodecContext
*avctx)
463
{
464
AnsiContext
*
s
= avctx->
priv_data
;
465
466
av_frame_free
(&s->
frame
);
467
return
0;
468
}
469
470
AVCodec
ff_ansi_decoder
= {
471
.
name
=
"ansi"
,
472
.long_name =
NULL_IF_CONFIG_SMALL
(
"ASCII/ANSI art"
),
473
.type =
AVMEDIA_TYPE_VIDEO
,
474
.id =
AV_CODEC_ID_ANSI
,
475
.priv_data_size =
sizeof
(
AnsiContext
),
476
.
init
=
decode_init
,
477
.
close
=
decode_close
,
478
.
decode
=
decode_frame
,
479
.capabilities =
CODEC_CAP_DR1
,
480
};
Generated on Sun Mar 23 2014 23:49:51 for FFmpeg by
1.8.2