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