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