FFmpeg
1.2.12
Main Page
Related Pages
Modules
Data Structures
Files
Examples
File List
Globals
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
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
35
#define ATTR_FAINT 0x02
36
#define ATTR_UNDERLINE 0x08
37
#define ATTR_BLINK 0x10
38
#define ATTR_REVERSE 0x40
39
#define ATTR_CONCEALED 0x80
41
#define DEFAULT_FG_COLOR 7
42
#define DEFAULT_BG_COLOR 0
43
#define DEFAULT_SCREEN_MODE 3
45
#define FONT_WIDTH 8
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
;
55
int
y
;
56
int
sx
;
57
int
sy
;
58
const
uint8_t
*
font
;
59
int
font_height
;
60
int
attributes
;
61
int
fg
;
62
int
bg
;
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
;
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
- 2*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
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
-
FONT_WIDTH
) {
169
s->
x
= 0;
170
hscroll
(avctx);
171
}
172
}
173
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
s->
x
= av_clip(s->
x
, 0, width - FONT_WIDTH);
243
s->
y
= av_clip(s->
y
, 0, height - s->
font_height
);
244
if
(width != avctx->
width
|| height != avctx->
height
) {
245
if
(s->
frame
.
data
[0])
246
avctx->
release_buffer
(avctx, &s->
frame
);
247
avcodec_set_dimensions
(avctx, width, height);
248
ret =
ff_get_buffer
(avctx, &s->
frame
);
249
if
(ret < 0) {
250
av_log
(avctx,
AV_LOG_ERROR
,
"get_buffer() failed\n"
);
251
return
ret;
252
}
253
s->
frame
.
pict_type
=
AV_PICTURE_TYPE_I
;
254
s->
frame
.
palette_has_changed
= 1;
255
set_palette
((uint32_t *)s->
frame
.
data
[1]);
256
erase_screen
(avctx);
257
}
else
if
(c ==
'l'
) {
258
erase_screen
(avctx);
259
}
260
break
;
261
case
'J'
:
//Erase in Page
262
switch
(s->
args
[0]) {
263
case
0:
264
erase_line
(avctx, s->
x
, avctx->
width
- s->
x
);
265
if
(s->
y
< avctx->
height
- s->
font_height
)
266
memset(s->
frame
.
data
[0] + (s->
y
+ s->
font_height
)*s->
frame
.
linesize
[0],
267
DEFAULT_BG_COLOR
, (avctx->
height
- s->
y
- s->
font_height
)*s->
frame
.
linesize
[0]);
268
break
;
269
case
1:
270
erase_line
(avctx, 0, s->
x
);
271
if
(s->
y
> 0)
272
memset(s->
frame
.
data
[0],
DEFAULT_BG_COLOR
, s->
y
* s->
frame
.
linesize
[0]);
273
break
;
274
case
2:
275
erase_screen
(avctx);
276
}
277
break
;
278
case
'K'
:
//Erase in Line
279
switch
(s->
args
[0]) {
280
case
0:
281
erase_line
(avctx, s->
x
, avctx->
width
- s->
x
);
282
break
;
283
case
1:
284
erase_line
(avctx, 0, s->
x
);
285
break
;
286
case
2:
287
erase_line
(avctx, 0, avctx->
width
);
288
}
289
break
;
290
case
'm'
:
//Select Graphics Rendition
291
if
(s->
nb_args
== 0) {
292
s->
nb_args
= 1;
293
s->
args
[0] = 0;
294
}
295
for
(i = 0; i <
FFMIN
(s->
nb_args
,
MAX_NB_ARGS
); i++) {
296
int
m
= s->
args
[i];
297
if
(m == 0) {
298
s->
attributes
= 0;
299
s->
fg
=
DEFAULT_FG_COLOR
;
300
s->
bg
=
DEFAULT_BG_COLOR
;
301
}
else
if
(m == 1 || m == 2 || m == 4 || m == 5 || m == 7 || m == 8) {
302
s->
attributes
|= 1 << (m - 1);
303
}
else
if
(m >= 30 && m <= 37) {
304
s->
fg
=
ansi_to_cga
[m - 30];
305
}
else
if
(m == 38 && i + 2 <
FFMIN
(s->
nb_args
,
MAX_NB_ARGS
) && s->
args
[i + 1] == 5 && s->
args
[i + 2] < 256) {
306
int
index
= s->
args
[i + 2];
307
s->
fg
= index < 16 ?
ansi_to_cga
[
index
] :
index
;
308
i += 2;
309
}
else
if
(m == 39) {
310
s->
fg
=
ansi_to_cga
[
DEFAULT_FG_COLOR
];
311
}
else
if
(m >= 40 && m <= 47) {
312
s->
bg
=
ansi_to_cga
[m - 40];
313
}
else
if
(m == 48 && i + 2 <
FFMIN
(s->
nb_args
,
MAX_NB_ARGS
) && s->
args
[i + 1] == 5 && s->
args
[i + 2] < 256) {
314
int
index
= s->
args
[i + 2];
315
s->
bg
= index < 16 ?
ansi_to_cga
[
index
] :
index
;
316
i += 2;
317
}
else
if
(m == 49) {
318
s->
fg
=
ansi_to_cga
[
DEFAULT_BG_COLOR
];
319
}
else
{
320
av_log_ask_for_sample
(avctx,
"unsupported rendition parameter\n"
);
321
}
322
}
323
break
;
324
case
'n'
:
//Device Status Report
325
case
'R'
:
//report current line and column
326
/* ignore */
327
break
;
328
case
's'
:
//Save Cursor Position
329
s->
sx
= s->
x
;
330
s->
sy
= s->
y
;
331
break
;
332
case
'u'
:
//Restore Cursor Position
333
s->
x
= av_clip(s->
sx
, 0, avctx->
width
- FONT_WIDTH);
334
s->
y
= av_clip(s->
sy
, 0, avctx->
height
- s->
font_height
);
335
break
;
336
default
:
337
av_log_ask_for_sample
(avctx,
"unsupported escape code\n"
);
338
break
;
339
}
340
s->
x
= av_clip(s->
x
, 0, avctx->
width
-
FONT_WIDTH
);
341
s->
y
= av_clip(s->
y
, 0, avctx->
height
- s->
font_height
);
342
return
0;
343
}
344
345
static
int
decode_frame
(
AVCodecContext
*avctx,
346
void
*
data
,
int
*got_frame,
347
AVPacket
*avpkt)
348
{
349
AnsiContext
*s = avctx->
priv_data
;
350
uint8_t
*buf = avpkt->
data
;
351
int
buf_size = avpkt->
size
;
352
const
uint8_t
*buf_end = buf+buf_size;
353
int
ret, i, count;
354
355
ret = avctx->
reget_buffer
(avctx, &s->
frame
);
356
if
(ret < 0){
357
av_log
(avctx,
AV_LOG_ERROR
,
"get_buffer() failed\n"
);
358
return
ret;
359
}
360
if
(!avctx->
frame_number
) {
361
for
(i=0; i<avctx->
height
; i++)
362
memset(s->
frame
.
data
[0]+ i*s->
frame
.
linesize
[0], 0, avctx->
width
);
363
memset(s->
frame
.
data
[1], 0,
AVPALETTE_SIZE
);
364
}
365
366
s->
frame
.
pict_type
=
AV_PICTURE_TYPE_I
;
367
s->
frame
.
palette_has_changed
= 1;
368
set_palette
((uint32_t *)s->
frame
.
data
[1]);
369
if
(!s->
first_frame
) {
370
erase_screen
(avctx);
371
s->
first_frame
= 1;
372
}
373
374
while
(buf < buf_end) {
375
switch
(s->
state
) {
376
case
STATE_NORMAL:
377
switch
(buf[0]) {
378
case
0x00:
//NUL
379
case
0x07:
//BEL
380
case
0x1A:
//SUB
381
/* ignore */
382
break
;
383
case
0x08:
//BS
384
s->
x
=
FFMAX
(s->
x
- 1, 0);
385
break
;
386
case
0x09:
//HT
387
i = s->
x
/
FONT_WIDTH
;
388
count = ((i + 8) & ~7) - i;
389
for
(i = 0; i < count; i++)
390
draw_char
(avctx,
' '
);
391
break
;
392
case
0x0A:
//LF
393
hscroll
(avctx);
394
case
0x0D:
//CR
395
s->
x
= 0;
396
break
;
397
case
0x0C:
//FF
398
erase_screen
(avctx);
399
break
;
400
case
0x1B:
//ESC
401
s->
state
= STATE_ESCAPE;
402
break
;
403
default
:
404
draw_char
(avctx, buf[0]);
405
}
406
break
;
407
case
STATE_ESCAPE:
408
if
(buf[0] ==
'['
) {
409
s->
state
= STATE_CODE;
410
s->
nb_args
= 0;
411
s->
args
[0] = -1;
412
}
else
{
413
s->
state
= STATE_NORMAL;
414
draw_char
(avctx, 0x1B);
415
continue
;
416
}
417
break
;
418
case
STATE_CODE:
419
switch
(buf[0]) {
420
case
'0'
:
case
'1'
:
case
'2'
:
case
'3'
:
case
'4'
:
421
case
'5'
:
case
'6'
:
case
'7'
:
case
'8'
:
case
'9'
:
422
if
(s->
nb_args
<
MAX_NB_ARGS
&& s->
args
[s->
nb_args
] < 6553)
423
s->
args
[s->
nb_args
] =
FFMAX
(s->
args
[s->
nb_args
], 0) * 10 + buf[0] -
'0'
;
424
break
;
425
case
';'
:
426
s->
nb_args
++;
427
if
(s->
nb_args
<
MAX_NB_ARGS
)
428
s->
args
[s->
nb_args
] = 0;
429
break
;
430
case
'M'
:
431
s->
state
= STATE_MUSIC_PREAMBLE;
432
break
;
433
case
'='
:
case
'?'
:
434
/* ignore */
435
break
;
436
default
:
437
if
(s->
nb_args
>
MAX_NB_ARGS
)
438
av_log
(avctx,
AV_LOG_WARNING
,
"args overflow (%i)\n"
, s->
nb_args
);
439
if
(s->
nb_args
<
MAX_NB_ARGS
&& s->
args
[s->
nb_args
] >= 0)
440
s->
nb_args
++;
441
if
((ret =
execute_code
(avctx, buf[0])) < 0)
442
return
ret;
443
s->
state
= STATE_NORMAL;
444
}
445
break
;
446
case
STATE_MUSIC_PREAMBLE:
447
if
(buf[0] == 0x0E || buf[0] == 0x1B)
448
s->
state
= STATE_NORMAL;
449
/* ignore music data */
450
break
;
451
}
452
buf++;
453
}
454
455
*got_frame = 1;
456
*(
AVFrame
*)data = s->
frame
;
457
return
buf_size;
458
}
459
460
static
av_cold
int
decode_close
(
AVCodecContext
*avctx)
461
{
462
AnsiContext
*s = avctx->
priv_data
;
463
if
(s->
frame
.
data
[0])
464
avctx->
release_buffer
(avctx, &s->
frame
);
465
return
0;
466
}
467
468
AVCodec
ff_ansi_decoder
= {
469
.
name
=
"ansi"
,
470
.type =
AVMEDIA_TYPE_VIDEO
,
471
.id =
AV_CODEC_ID_ANSI
,
472
.priv_data_size =
sizeof
(
AnsiContext
),
473
.
init
=
decode_init
,
474
.
close
=
decode_close
,
475
.
decode
=
decode_frame
,
476
.capabilities =
CODEC_CAP_DR1
,
477
.long_name =
NULL_IF_CONFIG_SMALL
(
"ASCII/ANSI art"
),
478
};
Generated on Thu Feb 12 2015 17:56:52 for FFmpeg by
1.8.1.2