xref: /third_party/ffmpeg/libavcodec/ansi.c (revision cabdff1a)
1cabdff1aSopenharmony_ci/*
2cabdff1aSopenharmony_ci * ASCII/ANSI art decoder
3cabdff1aSopenharmony_ci * Copyright (c) 2010 Peter Ross <pross@xvid.org>
4cabdff1aSopenharmony_ci *
5cabdff1aSopenharmony_ci * This file is part of FFmpeg.
6cabdff1aSopenharmony_ci *
7cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or
8cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public
9cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either
10cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version.
11cabdff1aSopenharmony_ci *
12cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful,
13cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
14cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15cabdff1aSopenharmony_ci * Lesser General Public License for more details.
16cabdff1aSopenharmony_ci *
17cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public
18cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software
19cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20cabdff1aSopenharmony_ci */
21cabdff1aSopenharmony_ci
22cabdff1aSopenharmony_ci/**
23cabdff1aSopenharmony_ci * @file
24cabdff1aSopenharmony_ci * ASCII/ANSI art decoder
25cabdff1aSopenharmony_ci */
26cabdff1aSopenharmony_ci
27cabdff1aSopenharmony_ci#include "libavutil/common.h"
28cabdff1aSopenharmony_ci#include "libavutil/frame.h"
29cabdff1aSopenharmony_ci#include "libavutil/lfg.h"
30cabdff1aSopenharmony_ci#include "libavutil/xga_font_data.h"
31cabdff1aSopenharmony_ci#include "avcodec.h"
32cabdff1aSopenharmony_ci#include "cga_data.h"
33cabdff1aSopenharmony_ci#include "codec_internal.h"
34cabdff1aSopenharmony_ci#include "internal.h"
35cabdff1aSopenharmony_ci
36cabdff1aSopenharmony_ci#define ATTR_BOLD         0x01  /**< Bold/Bright-foreground (mode 1) */
37cabdff1aSopenharmony_ci#define ATTR_FAINT        0x02  /**< Faint (mode 2) */
38cabdff1aSopenharmony_ci#define ATTR_ITALICS      0x04  /**< Italics (mode 3) */
39cabdff1aSopenharmony_ci#define ATTR_UNDERLINE    0x08  /**< Underline (mode 4) */
40cabdff1aSopenharmony_ci#define ATTR_BLINK        0x10  /**< Blink/Bright-background (mode 5) */
41cabdff1aSopenharmony_ci#define ATTR_REVERSE      0x40  /**< Reverse (mode 7) */
42cabdff1aSopenharmony_ci#define ATTR_CONCEALED    0x80  /**< Concealed (mode 8) */
43cabdff1aSopenharmony_ci
44cabdff1aSopenharmony_ci#define DEFAULT_FG_COLOR     7  /**< CGA color index */
45cabdff1aSopenharmony_ci#define DEFAULT_BG_COLOR     0
46cabdff1aSopenharmony_ci#define DEFAULT_SCREEN_MODE  3  /**< 80x25 */
47cabdff1aSopenharmony_ci
48cabdff1aSopenharmony_ci#define FONT_WIDTH           8  /**< Font width */
49cabdff1aSopenharmony_ci
50cabdff1aSopenharmony_ci/** map ansi color index to cga palette index */
51cabdff1aSopenharmony_cistatic const uint8_t ansi_to_cga[16] = {
52cabdff1aSopenharmony_ci    0,  4,  2,  6,  1,  5,  3, 7, 8, 12, 10, 14,  9, 13, 11, 15
53cabdff1aSopenharmony_ci};
54cabdff1aSopenharmony_ci
55cabdff1aSopenharmony_citypedef struct AnsiContext {
56cabdff1aSopenharmony_ci    AVFrame *frame;
57cabdff1aSopenharmony_ci    int x;                /**< x cursor position (pixels) */
58cabdff1aSopenharmony_ci    int y;                /**< y cursor position (pixels) */
59cabdff1aSopenharmony_ci    int sx;               /**< saved x cursor position (pixels) */
60cabdff1aSopenharmony_ci    int sy;               /**< saved y cursor position (pixels) */
61cabdff1aSopenharmony_ci    const uint8_t* font;  /**< font */
62cabdff1aSopenharmony_ci    int font_height;      /**< font height */
63cabdff1aSopenharmony_ci    int attributes;       /**< attribute flags */
64cabdff1aSopenharmony_ci    int fg;               /**< foreground color */
65cabdff1aSopenharmony_ci    int bg;               /**< background color */
66cabdff1aSopenharmony_ci    int first_frame;
67cabdff1aSopenharmony_ci
68cabdff1aSopenharmony_ci    /* ansi parser state machine */
69cabdff1aSopenharmony_ci    enum {
70cabdff1aSopenharmony_ci        STATE_NORMAL = 0,
71cabdff1aSopenharmony_ci        STATE_ESCAPE,
72cabdff1aSopenharmony_ci        STATE_CODE,
73cabdff1aSopenharmony_ci        STATE_MUSIC_PREAMBLE
74cabdff1aSopenharmony_ci    } state;
75cabdff1aSopenharmony_ci#define MAX_NB_ARGS 4
76cabdff1aSopenharmony_ci    int args[MAX_NB_ARGS];
77cabdff1aSopenharmony_ci    int nb_args;          /**< number of arguments (may exceed MAX_NB_ARGS) */
78cabdff1aSopenharmony_ci} AnsiContext;
79cabdff1aSopenharmony_ci
80cabdff1aSopenharmony_cistatic av_cold int decode_init(AVCodecContext *avctx)
81cabdff1aSopenharmony_ci{
82cabdff1aSopenharmony_ci    AnsiContext *s = avctx->priv_data;
83cabdff1aSopenharmony_ci    avctx->pix_fmt = AV_PIX_FMT_PAL8;
84cabdff1aSopenharmony_ci
85cabdff1aSopenharmony_ci    /* defaults */
86cabdff1aSopenharmony_ci    s->font        = avpriv_vga16_font;
87cabdff1aSopenharmony_ci    s->font_height = 16;
88cabdff1aSopenharmony_ci    s->fg          = DEFAULT_FG_COLOR;
89cabdff1aSopenharmony_ci    s->bg          = DEFAULT_BG_COLOR;
90cabdff1aSopenharmony_ci
91cabdff1aSopenharmony_ci    if (!avctx->width || !avctx->height) {
92cabdff1aSopenharmony_ci        int ret = ff_set_dimensions(avctx, 80 << 3, 25 << 4);
93cabdff1aSopenharmony_ci        if (ret < 0)
94cabdff1aSopenharmony_ci            return ret;
95cabdff1aSopenharmony_ci    } else if (avctx->width % FONT_WIDTH || avctx->height % s->font_height) {
96cabdff1aSopenharmony_ci        av_log(avctx, AV_LOG_ERROR, "Invalid dimensions %d %d\n", avctx->width, avctx->height);
97cabdff1aSopenharmony_ci        return AVERROR(EINVAL);
98cabdff1aSopenharmony_ci    }
99cabdff1aSopenharmony_ci
100cabdff1aSopenharmony_ci    s->frame = av_frame_alloc();
101cabdff1aSopenharmony_ci    if (!s->frame)
102cabdff1aSopenharmony_ci        return AVERROR(ENOMEM);
103cabdff1aSopenharmony_ci
104cabdff1aSopenharmony_ci    return 0;
105cabdff1aSopenharmony_ci}
106cabdff1aSopenharmony_ci
107cabdff1aSopenharmony_cistatic void set_palette(uint32_t *pal)
108cabdff1aSopenharmony_ci{
109cabdff1aSopenharmony_ci    int r, g, b;
110cabdff1aSopenharmony_ci    memcpy(pal, ff_cga_palette, 16 * 4);
111cabdff1aSopenharmony_ci    pal += 16;
112cabdff1aSopenharmony_ci#define COLOR(x) ((x) * 40 + 55)
113cabdff1aSopenharmony_ci    for (r = 0; r < 6; r++)
114cabdff1aSopenharmony_ci        for (g = 0; g < 6; g++)
115cabdff1aSopenharmony_ci            for (b = 0; b < 6; b++)
116cabdff1aSopenharmony_ci                *pal++ = 0xFF000000 | (COLOR(r) << 16) | (COLOR(g) << 8) | COLOR(b);
117cabdff1aSopenharmony_ci#define GRAY(x) ((x) * 10 + 8)
118cabdff1aSopenharmony_ci    for (g = 0; g < 24; g++)
119cabdff1aSopenharmony_ci        *pal++ = 0xFF000000 | (GRAY(g) << 16) | (GRAY(g) << 8) | GRAY(g);
120cabdff1aSopenharmony_ci}
121cabdff1aSopenharmony_ci
122cabdff1aSopenharmony_cistatic void hscroll(AVCodecContext *avctx)
123cabdff1aSopenharmony_ci{
124cabdff1aSopenharmony_ci    AnsiContext *s = avctx->priv_data;
125cabdff1aSopenharmony_ci    int i;
126cabdff1aSopenharmony_ci
127cabdff1aSopenharmony_ci    if (s->y <= avctx->height - 2*s->font_height) {
128cabdff1aSopenharmony_ci        s->y += s->font_height;
129cabdff1aSopenharmony_ci        return;
130cabdff1aSopenharmony_ci    }
131cabdff1aSopenharmony_ci
132cabdff1aSopenharmony_ci    i = 0;
133cabdff1aSopenharmony_ci    for (; i < avctx->height - s->font_height; i++)
134cabdff1aSopenharmony_ci        memcpy(s->frame->data[0] + i * s->frame->linesize[0],
135cabdff1aSopenharmony_ci               s->frame->data[0] + (i + s->font_height) * s->frame->linesize[0],
136cabdff1aSopenharmony_ci               avctx->width);
137cabdff1aSopenharmony_ci    for (; i < avctx->height; i++)
138cabdff1aSopenharmony_ci        memset(s->frame->data[0] + i * s->frame->linesize[0],
139cabdff1aSopenharmony_ci            DEFAULT_BG_COLOR, avctx->width);
140cabdff1aSopenharmony_ci}
141cabdff1aSopenharmony_ci
142cabdff1aSopenharmony_cistatic void erase_line(AVCodecContext * avctx, int xoffset, int xlength)
143cabdff1aSopenharmony_ci{
144cabdff1aSopenharmony_ci    AnsiContext *s = avctx->priv_data;
145cabdff1aSopenharmony_ci    int i;
146cabdff1aSopenharmony_ci    for (i = 0; i < s->font_height; i++)
147cabdff1aSopenharmony_ci        memset(s->frame->data[0] + (s->y + i)*s->frame->linesize[0] + xoffset,
148cabdff1aSopenharmony_ci            DEFAULT_BG_COLOR, xlength);
149cabdff1aSopenharmony_ci}
150cabdff1aSopenharmony_ci
151cabdff1aSopenharmony_cistatic void erase_screen(AVCodecContext *avctx)
152cabdff1aSopenharmony_ci{
153cabdff1aSopenharmony_ci    AnsiContext *s = avctx->priv_data;
154cabdff1aSopenharmony_ci    int i;
155cabdff1aSopenharmony_ci    for (i = 0; i < avctx->height; i++)
156cabdff1aSopenharmony_ci        memset(s->frame->data[0] + i * s->frame->linesize[0], DEFAULT_BG_COLOR, avctx->width);
157cabdff1aSopenharmony_ci    s->x = s->y = 0;
158cabdff1aSopenharmony_ci}
159cabdff1aSopenharmony_ci
160cabdff1aSopenharmony_ci/**
161cabdff1aSopenharmony_ci * Draw character to screen
162cabdff1aSopenharmony_ci */
163cabdff1aSopenharmony_cistatic void draw_char(AVCodecContext *avctx, int c)
164cabdff1aSopenharmony_ci{
165cabdff1aSopenharmony_ci    AnsiContext *s = avctx->priv_data;
166cabdff1aSopenharmony_ci    int fg = s->fg;
167cabdff1aSopenharmony_ci    int bg = s->bg;
168cabdff1aSopenharmony_ci
169cabdff1aSopenharmony_ci    if ((s->attributes & ATTR_BOLD))
170cabdff1aSopenharmony_ci        fg += 8;
171cabdff1aSopenharmony_ci    if ((s->attributes & ATTR_BLINK))
172cabdff1aSopenharmony_ci        bg += 8;
173cabdff1aSopenharmony_ci    if ((s->attributes & ATTR_REVERSE))
174cabdff1aSopenharmony_ci        FFSWAP(int, fg, bg);
175cabdff1aSopenharmony_ci    if ((s->attributes & ATTR_CONCEALED))
176cabdff1aSopenharmony_ci        fg = bg;
177cabdff1aSopenharmony_ci    ff_draw_pc_font(s->frame->data[0] + s->y * s->frame->linesize[0] + s->x,
178cabdff1aSopenharmony_ci                    s->frame->linesize[0], s->font, s->font_height, c, fg, bg);
179cabdff1aSopenharmony_ci    s->x += FONT_WIDTH;
180cabdff1aSopenharmony_ci    if (s->x > avctx->width - FONT_WIDTH) {
181cabdff1aSopenharmony_ci        s->x = 0;
182cabdff1aSopenharmony_ci        hscroll(avctx);
183cabdff1aSopenharmony_ci    }
184cabdff1aSopenharmony_ci}
185cabdff1aSopenharmony_ci
186cabdff1aSopenharmony_ci/**
187cabdff1aSopenharmony_ci * Execute ANSI escape code
188cabdff1aSopenharmony_ci * @return 0 on success, negative on error
189cabdff1aSopenharmony_ci */
190cabdff1aSopenharmony_cistatic int execute_code(AVCodecContext * avctx, int c)
191cabdff1aSopenharmony_ci{
192cabdff1aSopenharmony_ci    AnsiContext *s = avctx->priv_data;
193cabdff1aSopenharmony_ci    int ret, i;
194cabdff1aSopenharmony_ci    int width  = avctx->width;
195cabdff1aSopenharmony_ci    int height = avctx->height;
196cabdff1aSopenharmony_ci
197cabdff1aSopenharmony_ci    switch(c) {
198cabdff1aSopenharmony_ci    case 'A': //Cursor Up
199cabdff1aSopenharmony_ci        s->y = FFMAX(s->y - (s->nb_args > 0 ? s->args[0]*s->font_height : s->font_height), 0);
200cabdff1aSopenharmony_ci        break;
201cabdff1aSopenharmony_ci    case 'B': //Cursor Down
202cabdff1aSopenharmony_ci        s->y = FFMIN(s->y + (s->nb_args > 0 ? s->args[0]*s->font_height : s->font_height), avctx->height - s->font_height);
203cabdff1aSopenharmony_ci        break;
204cabdff1aSopenharmony_ci    case 'C': //Cursor Right
205cabdff1aSopenharmony_ci        s->x = FFMIN(s->x + (s->nb_args > 0 ? s->args[0]*FONT_WIDTH : FONT_WIDTH), avctx->width  - FONT_WIDTH);
206cabdff1aSopenharmony_ci        break;
207cabdff1aSopenharmony_ci    case 'D': //Cursor Left
208cabdff1aSopenharmony_ci        s->x = FFMAX(s->x - (s->nb_args > 0 ? s->args[0]*FONT_WIDTH : FONT_WIDTH), 0);
209cabdff1aSopenharmony_ci        break;
210cabdff1aSopenharmony_ci    case 'H': //Cursor Position
211cabdff1aSopenharmony_ci    case 'f': //Horizontal and Vertical Position
212cabdff1aSopenharmony_ci        s->y = s->nb_args > 0 ? av_clip((s->args[0] - 1)*s->font_height, 0, avctx->height - s->font_height) : 0;
213cabdff1aSopenharmony_ci        s->x = s->nb_args > 1 ? av_clip((s->args[1] - 1)*FONT_WIDTH,     0, avctx->width  - FONT_WIDTH) : 0;
214cabdff1aSopenharmony_ci        break;
215cabdff1aSopenharmony_ci    case 'h': //set screen mode
216cabdff1aSopenharmony_ci    case 'l': //reset screen mode
217cabdff1aSopenharmony_ci        if (s->nb_args < 2)
218cabdff1aSopenharmony_ci            s->args[0] = DEFAULT_SCREEN_MODE;
219cabdff1aSopenharmony_ci        switch(s->args[0]) {
220cabdff1aSopenharmony_ci        case 0: case 1: case 4: case 5: case 13: case 19: //320x200 (25 rows)
221cabdff1aSopenharmony_ci            s->font = avpriv_cga_font;
222cabdff1aSopenharmony_ci            s->font_height = 8;
223cabdff1aSopenharmony_ci            width  = 40<<3;
224cabdff1aSopenharmony_ci            height = 25<<3;
225cabdff1aSopenharmony_ci            break;
226cabdff1aSopenharmony_ci        case 2: case 3: //640x400 (25 rows)
227cabdff1aSopenharmony_ci            s->font = avpriv_vga16_font;
228cabdff1aSopenharmony_ci            s->font_height = 16;
229cabdff1aSopenharmony_ci            width  = 80<<3;
230cabdff1aSopenharmony_ci            height = 25<<4;
231cabdff1aSopenharmony_ci            break;
232cabdff1aSopenharmony_ci        case 6: case 14: //640x200 (25 rows)
233cabdff1aSopenharmony_ci            s->font = avpriv_cga_font;
234cabdff1aSopenharmony_ci            s->font_height = 8;
235cabdff1aSopenharmony_ci            width  = 80<<3;
236cabdff1aSopenharmony_ci            height = 25<<3;
237cabdff1aSopenharmony_ci            break;
238cabdff1aSopenharmony_ci        case 7: //set line wrapping
239cabdff1aSopenharmony_ci            break;
240cabdff1aSopenharmony_ci        case 15: case 16: //640x350 (43 rows)
241cabdff1aSopenharmony_ci            s->font = avpriv_cga_font;
242cabdff1aSopenharmony_ci            s->font_height = 8;
243cabdff1aSopenharmony_ci            width  = 80<<3;
244cabdff1aSopenharmony_ci            height = 43<<3;
245cabdff1aSopenharmony_ci            break;
246cabdff1aSopenharmony_ci        case 17: case 18: //640x480 (60 rows)
247cabdff1aSopenharmony_ci            s->font = avpriv_cga_font;
248cabdff1aSopenharmony_ci            s->font_height = 8;
249cabdff1aSopenharmony_ci            width  = 80<<3;
250cabdff1aSopenharmony_ci            height = 60<<4;
251cabdff1aSopenharmony_ci            break;
252cabdff1aSopenharmony_ci        default:
253cabdff1aSopenharmony_ci            avpriv_request_sample(avctx, "Unsupported screen mode");
254cabdff1aSopenharmony_ci        }
255cabdff1aSopenharmony_ci        s->x = av_clip(s->x, 0, width  - FONT_WIDTH);
256cabdff1aSopenharmony_ci        s->y = av_clip(s->y, 0, height - s->font_height);
257cabdff1aSopenharmony_ci        if (width != avctx->width || height != avctx->height) {
258cabdff1aSopenharmony_ci            av_frame_unref(s->frame);
259cabdff1aSopenharmony_ci            ret = ff_set_dimensions(avctx, width, height);
260cabdff1aSopenharmony_ci            if (ret < 0)
261cabdff1aSopenharmony_ci                return ret;
262cabdff1aSopenharmony_ci            if ((ret = ff_get_buffer(avctx, s->frame,
263cabdff1aSopenharmony_ci                                     AV_GET_BUFFER_FLAG_REF)) < 0)
264cabdff1aSopenharmony_ci                return ret;
265cabdff1aSopenharmony_ci            s->frame->pict_type           = AV_PICTURE_TYPE_I;
266cabdff1aSopenharmony_ci            s->frame->palette_has_changed = 1;
267cabdff1aSopenharmony_ci            set_palette((uint32_t *)s->frame->data[1]);
268cabdff1aSopenharmony_ci            erase_screen(avctx);
269cabdff1aSopenharmony_ci        } else if (c == 'l') {
270cabdff1aSopenharmony_ci            erase_screen(avctx);
271cabdff1aSopenharmony_ci        }
272cabdff1aSopenharmony_ci        break;
273cabdff1aSopenharmony_ci    case 'J': //Erase in Page
274cabdff1aSopenharmony_ci        switch (s->args[0]) {
275cabdff1aSopenharmony_ci        case 0:
276cabdff1aSopenharmony_ci            erase_line(avctx, s->x, avctx->width - s->x);
277cabdff1aSopenharmony_ci            if (s->y < avctx->height - s->font_height)
278cabdff1aSopenharmony_ci                memset(s->frame->data[0] + (s->y + s->font_height)*s->frame->linesize[0],
279cabdff1aSopenharmony_ci                    DEFAULT_BG_COLOR, (avctx->height - s->y - s->font_height)*s->frame->linesize[0]);
280cabdff1aSopenharmony_ci            break;
281cabdff1aSopenharmony_ci        case 1:
282cabdff1aSopenharmony_ci            erase_line(avctx, 0, s->x);
283cabdff1aSopenharmony_ci            if (s->y > 0)
284cabdff1aSopenharmony_ci                memset(s->frame->data[0], DEFAULT_BG_COLOR, s->y * s->frame->linesize[0]);
285cabdff1aSopenharmony_ci            break;
286cabdff1aSopenharmony_ci        case 2:
287cabdff1aSopenharmony_ci            erase_screen(avctx);
288cabdff1aSopenharmony_ci        }
289cabdff1aSopenharmony_ci        break;
290cabdff1aSopenharmony_ci    case 'K': //Erase in Line
291cabdff1aSopenharmony_ci        switch(s->args[0]) {
292cabdff1aSopenharmony_ci        case 0:
293cabdff1aSopenharmony_ci            erase_line(avctx, s->x, avctx->width - s->x);
294cabdff1aSopenharmony_ci            break;
295cabdff1aSopenharmony_ci        case 1:
296cabdff1aSopenharmony_ci            erase_line(avctx, 0, s->x);
297cabdff1aSopenharmony_ci            break;
298cabdff1aSopenharmony_ci        case 2:
299cabdff1aSopenharmony_ci            erase_line(avctx, 0, avctx->width);
300cabdff1aSopenharmony_ci        }
301cabdff1aSopenharmony_ci        break;
302cabdff1aSopenharmony_ci    case 'm': //Select Graphics Rendition
303cabdff1aSopenharmony_ci        if (s->nb_args == 0) {
304cabdff1aSopenharmony_ci            s->nb_args = 1;
305cabdff1aSopenharmony_ci            s->args[0] = 0;
306cabdff1aSopenharmony_ci        }
307cabdff1aSopenharmony_ci        for (i = 0; i < FFMIN(s->nb_args, MAX_NB_ARGS); i++) {
308cabdff1aSopenharmony_ci            int m = s->args[i];
309cabdff1aSopenharmony_ci            if (m == 0) {
310cabdff1aSopenharmony_ci                s->attributes = 0;
311cabdff1aSopenharmony_ci                s->fg = DEFAULT_FG_COLOR;
312cabdff1aSopenharmony_ci                s->bg = DEFAULT_BG_COLOR;
313cabdff1aSopenharmony_ci            } else if (m == 1 || m == 2 || m == 3 || m == 4 || m == 5 || m == 7 || m == 8) {
314cabdff1aSopenharmony_ci                s->attributes |= 1 << (m - 1);
315cabdff1aSopenharmony_ci            } else if (m >= 30 && m <= 37) {
316cabdff1aSopenharmony_ci                s->fg = ansi_to_cga[m - 30];
317cabdff1aSopenharmony_ci            } else if (m == 38 && i + 2 < FFMIN(s->nb_args, MAX_NB_ARGS) && s->args[i + 1] == 5 && s->args[i + 2] < 256) {
318cabdff1aSopenharmony_ci                int index = s->args[i + 2];
319cabdff1aSopenharmony_ci                s->fg = index < 16 ? ansi_to_cga[index] : index;
320cabdff1aSopenharmony_ci                i += 2;
321cabdff1aSopenharmony_ci            } else if (m == 39) {
322cabdff1aSopenharmony_ci                s->fg = ansi_to_cga[DEFAULT_FG_COLOR];
323cabdff1aSopenharmony_ci            } else if (m >= 40 && m <= 47) {
324cabdff1aSopenharmony_ci                s->bg = ansi_to_cga[m - 40];
325cabdff1aSopenharmony_ci            } else if (m == 48 && i + 2 < FFMIN(s->nb_args, MAX_NB_ARGS) && s->args[i + 1] == 5 && s->args[i + 2] < 256) {
326cabdff1aSopenharmony_ci                int index = s->args[i + 2];
327cabdff1aSopenharmony_ci                s->bg = index < 16 ? ansi_to_cga[index] : index;
328cabdff1aSopenharmony_ci                i += 2;
329cabdff1aSopenharmony_ci            } else if (m == 49) {
330cabdff1aSopenharmony_ci                s->fg = ansi_to_cga[DEFAULT_BG_COLOR];
331cabdff1aSopenharmony_ci            } else {
332cabdff1aSopenharmony_ci                avpriv_request_sample(avctx, "Unsupported rendition parameter");
333cabdff1aSopenharmony_ci            }
334cabdff1aSopenharmony_ci        }
335cabdff1aSopenharmony_ci        break;
336cabdff1aSopenharmony_ci    case 'n': //Device Status Report
337cabdff1aSopenharmony_ci    case 'R': //report current line and column
338cabdff1aSopenharmony_ci        /* ignore */
339cabdff1aSopenharmony_ci        break;
340cabdff1aSopenharmony_ci    case 's': //Save Cursor Position
341cabdff1aSopenharmony_ci        s->sx = s->x;
342cabdff1aSopenharmony_ci        s->sy = s->y;
343cabdff1aSopenharmony_ci        break;
344cabdff1aSopenharmony_ci    case 'u': //Restore Cursor Position
345cabdff1aSopenharmony_ci        s->x = av_clip(s->sx, 0, avctx->width  - FONT_WIDTH);
346cabdff1aSopenharmony_ci        s->y = av_clip(s->sy, 0, avctx->height - s->font_height);
347cabdff1aSopenharmony_ci        break;
348cabdff1aSopenharmony_ci    default:
349cabdff1aSopenharmony_ci        avpriv_request_sample(avctx, "Unknown escape code");
350cabdff1aSopenharmony_ci        break;
351cabdff1aSopenharmony_ci    }
352cabdff1aSopenharmony_ci    s->x = av_clip(s->x, 0, avctx->width  - FONT_WIDTH);
353cabdff1aSopenharmony_ci    s->y = av_clip(s->y, 0, avctx->height - s->font_height);
354cabdff1aSopenharmony_ci    return 0;
355cabdff1aSopenharmony_ci}
356cabdff1aSopenharmony_ci
357cabdff1aSopenharmony_cistatic int decode_frame(AVCodecContext *avctx, AVFrame *rframe,
358cabdff1aSopenharmony_ci                        int *got_frame, AVPacket *avpkt)
359cabdff1aSopenharmony_ci{
360cabdff1aSopenharmony_ci    AnsiContext *s = avctx->priv_data;
361cabdff1aSopenharmony_ci    const uint8_t *buf = avpkt->data;
362cabdff1aSopenharmony_ci    int buf_size = avpkt->size;
363cabdff1aSopenharmony_ci    const uint8_t *buf_end   = buf+buf_size;
364cabdff1aSopenharmony_ci    int ret, i, count;
365cabdff1aSopenharmony_ci
366cabdff1aSopenharmony_ci    if ((ret = ff_reget_buffer(avctx, s->frame, 0)) < 0)
367cabdff1aSopenharmony_ci        return ret;
368cabdff1aSopenharmony_ci    if (!avctx->frame_number) {
369cabdff1aSopenharmony_ci        for (i=0; i<avctx->height; i++)
370cabdff1aSopenharmony_ci            memset(s->frame->data[0]+ i*s->frame->linesize[0], 0, avctx->width);
371cabdff1aSopenharmony_ci        memset(s->frame->data[1], 0, AVPALETTE_SIZE);
372cabdff1aSopenharmony_ci    }
373cabdff1aSopenharmony_ci
374cabdff1aSopenharmony_ci    s->frame->pict_type           = AV_PICTURE_TYPE_I;
375cabdff1aSopenharmony_ci    s->frame->palette_has_changed = 1;
376cabdff1aSopenharmony_ci    set_palette((uint32_t *)s->frame->data[1]);
377cabdff1aSopenharmony_ci    if (!s->first_frame) {
378cabdff1aSopenharmony_ci        erase_screen(avctx);
379cabdff1aSopenharmony_ci        s->first_frame = 1;
380cabdff1aSopenharmony_ci    }
381cabdff1aSopenharmony_ci
382cabdff1aSopenharmony_ci    while(buf < buf_end) {
383cabdff1aSopenharmony_ci        switch(s->state) {
384cabdff1aSopenharmony_ci        case STATE_NORMAL:
385cabdff1aSopenharmony_ci            switch (buf[0]) {
386cabdff1aSopenharmony_ci            case 0x00: //NUL
387cabdff1aSopenharmony_ci            case 0x07: //BEL
388cabdff1aSopenharmony_ci            case 0x1A: //SUB
389cabdff1aSopenharmony_ci                /* ignore */
390cabdff1aSopenharmony_ci                break;
391cabdff1aSopenharmony_ci            case 0x08: //BS
392cabdff1aSopenharmony_ci                s->x = FFMAX(s->x - 1, 0);
393cabdff1aSopenharmony_ci                break;
394cabdff1aSopenharmony_ci            case 0x09: //HT
395cabdff1aSopenharmony_ci                i = s->x / FONT_WIDTH;
396cabdff1aSopenharmony_ci                count = ((i + 8) & ~7) - i;
397cabdff1aSopenharmony_ci                for (i = 0; i < count; i++)
398cabdff1aSopenharmony_ci                    draw_char(avctx, ' ');
399cabdff1aSopenharmony_ci                break;
400cabdff1aSopenharmony_ci            case 0x0A: //LF
401cabdff1aSopenharmony_ci                hscroll(avctx);
402cabdff1aSopenharmony_ci            case 0x0D: //CR
403cabdff1aSopenharmony_ci                s->x = 0;
404cabdff1aSopenharmony_ci                break;
405cabdff1aSopenharmony_ci            case 0x0C: //FF
406cabdff1aSopenharmony_ci                erase_screen(avctx);
407cabdff1aSopenharmony_ci                break;
408cabdff1aSopenharmony_ci            case 0x1B: //ESC
409cabdff1aSopenharmony_ci                s->state = STATE_ESCAPE;
410cabdff1aSopenharmony_ci                break;
411cabdff1aSopenharmony_ci            default:
412cabdff1aSopenharmony_ci                draw_char(avctx, buf[0]);
413cabdff1aSopenharmony_ci            }
414cabdff1aSopenharmony_ci            break;
415cabdff1aSopenharmony_ci        case STATE_ESCAPE:
416cabdff1aSopenharmony_ci            if (buf[0] == '[') {
417cabdff1aSopenharmony_ci                s->state   = STATE_CODE;
418cabdff1aSopenharmony_ci                s->nb_args = 0;
419cabdff1aSopenharmony_ci                s->args[0] = -1;
420cabdff1aSopenharmony_ci            } else {
421cabdff1aSopenharmony_ci                s->state = STATE_NORMAL;
422cabdff1aSopenharmony_ci                draw_char(avctx, 0x1B);
423cabdff1aSopenharmony_ci                continue;
424cabdff1aSopenharmony_ci            }
425cabdff1aSopenharmony_ci            break;
426cabdff1aSopenharmony_ci        case STATE_CODE:
427cabdff1aSopenharmony_ci            switch(buf[0]) {
428cabdff1aSopenharmony_ci            case '0': case '1': case '2': case '3': case '4':
429cabdff1aSopenharmony_ci            case '5': case '6': case '7': case '8': case '9':
430cabdff1aSopenharmony_ci                if (s->nb_args < MAX_NB_ARGS && s->args[s->nb_args] < 6553)
431cabdff1aSopenharmony_ci                    s->args[s->nb_args] = FFMAX(s->args[s->nb_args], 0) * 10 + buf[0] - '0';
432cabdff1aSopenharmony_ci                break;
433cabdff1aSopenharmony_ci            case ';':
434cabdff1aSopenharmony_ci                if (s->nb_args < MAX_NB_ARGS)
435cabdff1aSopenharmony_ci                    s->nb_args++;
436cabdff1aSopenharmony_ci                if (s->nb_args < MAX_NB_ARGS)
437cabdff1aSopenharmony_ci                    s->args[s->nb_args] = 0;
438cabdff1aSopenharmony_ci                break;
439cabdff1aSopenharmony_ci            case 'M':
440cabdff1aSopenharmony_ci                s->state = STATE_MUSIC_PREAMBLE;
441cabdff1aSopenharmony_ci                break;
442cabdff1aSopenharmony_ci            case '=': case '?':
443cabdff1aSopenharmony_ci                /* ignore */
444cabdff1aSopenharmony_ci                break;
445cabdff1aSopenharmony_ci            default:
446cabdff1aSopenharmony_ci                if (s->nb_args > MAX_NB_ARGS)
447cabdff1aSopenharmony_ci                    av_log(avctx, AV_LOG_WARNING, "args overflow (%i)\n", s->nb_args);
448cabdff1aSopenharmony_ci                if (s->nb_args < MAX_NB_ARGS && s->args[s->nb_args] >= 0)
449cabdff1aSopenharmony_ci                    s->nb_args++;
450cabdff1aSopenharmony_ci                if ((ret = execute_code(avctx, buf[0])) < 0)
451cabdff1aSopenharmony_ci                    return ret;
452cabdff1aSopenharmony_ci                s->state = STATE_NORMAL;
453cabdff1aSopenharmony_ci            }
454cabdff1aSopenharmony_ci            break;
455cabdff1aSopenharmony_ci        case STATE_MUSIC_PREAMBLE:
456cabdff1aSopenharmony_ci            if (buf[0] == 0x0E || buf[0] == 0x1B)
457cabdff1aSopenharmony_ci                s->state = STATE_NORMAL;
458cabdff1aSopenharmony_ci            /* ignore music data */
459cabdff1aSopenharmony_ci            break;
460cabdff1aSopenharmony_ci        }
461cabdff1aSopenharmony_ci        buf++;
462cabdff1aSopenharmony_ci    }
463cabdff1aSopenharmony_ci
464cabdff1aSopenharmony_ci    *got_frame = 1;
465cabdff1aSopenharmony_ci    if ((ret = av_frame_ref(rframe, s->frame)) < 0)
466cabdff1aSopenharmony_ci        return ret;
467cabdff1aSopenharmony_ci    return buf_size;
468cabdff1aSopenharmony_ci}
469cabdff1aSopenharmony_ci
470cabdff1aSopenharmony_cistatic av_cold int decode_close(AVCodecContext *avctx)
471cabdff1aSopenharmony_ci{
472cabdff1aSopenharmony_ci    AnsiContext *s = avctx->priv_data;
473cabdff1aSopenharmony_ci
474cabdff1aSopenharmony_ci    av_frame_free(&s->frame);
475cabdff1aSopenharmony_ci    return 0;
476cabdff1aSopenharmony_ci}
477cabdff1aSopenharmony_ci
478cabdff1aSopenharmony_cistatic const FFCodecDefault ansi_defaults[] = {
479cabdff1aSopenharmony_ci    { "max_pixels", "640*480" },
480cabdff1aSopenharmony_ci    { NULL },
481cabdff1aSopenharmony_ci};
482cabdff1aSopenharmony_ci
483cabdff1aSopenharmony_ciconst FFCodec ff_ansi_decoder = {
484cabdff1aSopenharmony_ci    .p.name         = "ansi",
485cabdff1aSopenharmony_ci    .p.long_name    = NULL_IF_CONFIG_SMALL("ASCII/ANSI art"),
486cabdff1aSopenharmony_ci    .p.type         = AVMEDIA_TYPE_VIDEO,
487cabdff1aSopenharmony_ci    .p.id           = AV_CODEC_ID_ANSI,
488cabdff1aSopenharmony_ci    .priv_data_size = sizeof(AnsiContext),
489cabdff1aSopenharmony_ci    .init           = decode_init,
490cabdff1aSopenharmony_ci    .close          = decode_close,
491cabdff1aSopenharmony_ci    FF_CODEC_DECODE_CB(decode_frame),
492cabdff1aSopenharmony_ci    .p.capabilities = AV_CODEC_CAP_DR1,
493cabdff1aSopenharmony_ci    .caps_internal  = FF_CODEC_CAP_INIT_THREADSAFE,
494cabdff1aSopenharmony_ci    .defaults       = ansi_defaults,
495cabdff1aSopenharmony_ci};
496