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