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