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