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