xref: /third_party/ffmpeg/libavcodec/pictordec.c (revision cabdff1a)
1/*
2 * Pictor/PC Paint decoder
3 * Copyright (c) 2010 Peter Ross <pross@xvid.org>
4 *
5 * This file is part of FFmpeg.
6 *
7 * FFmpeg 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 * FFmpeg 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 FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 */
21
22/**
23 * @file
24 * Pictor/PC Paint decoder
25 */
26
27#include "libavutil/imgutils.h"
28#include "avcodec.h"
29#include "bytestream.h"
30#include "cga_data.h"
31#include "codec_internal.h"
32#include "internal.h"
33
34typedef struct PicContext {
35    int width, height;
36    int nb_planes;
37    GetByteContext g;
38} PicContext;
39
40static void picmemset_8bpp(PicContext *s, AVFrame *frame, int value, int run,
41                           int *x, int *y)
42{
43    while (run > 0) {
44        uint8_t *d = frame->data[0] + *y * frame->linesize[0];
45        if (*x + run >= s->width) {
46            int n = s->width - *x;
47            memset(d + *x, value, n);
48            run -= n;
49            *x = 0;
50            *y -= 1;
51            if (*y < 0)
52                break;
53        } else {
54            memset(d + *x, value, run);
55            *x += run;
56            break;
57        }
58    }
59}
60
61static void picmemset(PicContext *s, AVFrame *frame, unsigned value, int run,
62                      int *x, int *y, int *plane, int bits_per_plane)
63{
64    uint8_t *d;
65    int shift = *plane * bits_per_plane;
66    unsigned mask  = ((1U << bits_per_plane) - 1) << shift;
67    int xl = *x;
68    int yl = *y;
69    int planel = *plane;
70    int pixels_per_value = 8/bits_per_plane;
71    value   <<= shift;
72
73    d = frame->data[0] + yl * frame->linesize[0];
74    while (run > 0) {
75        int j;
76        for (j = 8-bits_per_plane; j >= 0; j -= bits_per_plane) {
77            d[xl] |= (value >> j) & mask;
78            xl += 1;
79            while (xl == s->width) {
80                yl -= 1;
81                xl = 0;
82                if (yl < 0) {
83                   yl = s->height - 1;
84                   planel += 1;
85                   if (planel >= s->nb_planes)
86                       goto end;
87                   value <<= bits_per_plane;
88                   mask  <<= bits_per_plane;
89                }
90                d = frame->data[0] + yl * frame->linesize[0];
91                if (s->nb_planes == 1 &&
92                    run*pixels_per_value >= s->width &&
93                    pixels_per_value < (s->width / pixels_per_value * pixels_per_value)
94                    ) {
95                    for (; xl < pixels_per_value; xl ++) {
96                        j = (j < bits_per_plane ? 8 : j) - bits_per_plane;
97                        d[xl] |= (value >> j) & mask;
98                    }
99                    av_memcpy_backptr(d+xl, pixels_per_value, s->width - xl);
100                    run -= s->width / pixels_per_value;
101                    xl = s->width / pixels_per_value * pixels_per_value;
102                }
103            }
104        }
105        run--;
106    }
107end:
108    *x = xl;
109    *y = yl;
110    *plane = planel;
111}
112
113static const uint8_t cga_mode45_index[6][4] = {
114    [0] = { 0, 3,  5,   7 }, // mode4, palette#1, low intensity
115    [1] = { 0, 2,  4,   6 }, // mode4, palette#2, low intensity
116    [2] = { 0, 3,  4,   7 }, // mode5, low intensity
117    [3] = { 0, 11, 13, 15 }, // mode4, palette#1, high intensity
118    [4] = { 0, 10, 12, 14 }, // mode4, palette#2, high intensity
119    [5] = { 0, 11, 12, 15 }, // mode5, high intensity
120};
121
122static int decode_frame(AVCodecContext *avctx, AVFrame *frame,
123                        int *got_frame, AVPacket *avpkt)
124{
125    PicContext *s = avctx->priv_data;
126    uint32_t *palette;
127    int bits_per_plane, bpp, etype, esize, npal, pos_after_pal;
128    int i, x, y, plane, tmp, ret, val;
129
130    bytestream2_init(&s->g, avpkt->data, avpkt->size);
131
132    if (bytestream2_get_bytes_left(&s->g) < 11)
133        return AVERROR_INVALIDDATA;
134
135    if (bytestream2_get_le16u(&s->g) != 0x1234)
136        return AVERROR_INVALIDDATA;
137
138    s->width       = bytestream2_get_le16u(&s->g);
139    s->height      = bytestream2_get_le16u(&s->g);
140    bytestream2_skip(&s->g, 4);
141    tmp            = bytestream2_get_byteu(&s->g);
142    bits_per_plane = tmp & 0xF;
143    s->nb_planes   = (tmp >> 4) + 1;
144    bpp            = bits_per_plane * s->nb_planes;
145    if (bits_per_plane > 8 || bpp < 1 || bpp > 32) {
146        avpriv_request_sample(avctx, "Unsupported bit depth");
147        return AVERROR_PATCHWELCOME;
148    }
149
150    if (bytestream2_peek_byte(&s->g) == 0xFF || bpp == 1 || bpp == 4 || bpp == 8) {
151        bytestream2_skip(&s->g, 2);
152        etype = bytestream2_get_le16(&s->g);
153        esize = bytestream2_get_le16(&s->g);
154        if (bytestream2_get_bytes_left(&s->g) < esize)
155            return AVERROR_INVALIDDATA;
156    } else {
157        etype = -1;
158        esize = 0;
159    }
160
161    avctx->pix_fmt = AV_PIX_FMT_PAL8;
162
163    if (av_image_check_size(s->width, s->height, 0, avctx) < 0)
164        return -1;
165    if (s->width != avctx->width || s->height != avctx->height) {
166        ret = ff_set_dimensions(avctx, s->width, s->height);
167        if (ret < 0)
168            return ret;
169    }
170
171    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
172        return ret;
173    memset(frame->data[0], 0, s->height * frame->linesize[0]);
174    frame->pict_type           = AV_PICTURE_TYPE_I;
175    frame->palette_has_changed = 1;
176
177    pos_after_pal = bytestream2_tell(&s->g) + esize;
178    palette = (uint32_t*)frame->data[1];
179    if (etype == 1 && esize > 1 && bytestream2_peek_byte(&s->g) < 6) {
180        int idx = bytestream2_get_byte(&s->g);
181        npal = 4;
182        for (i = 0; i < npal; i++)
183            palette[i] = ff_cga_palette[ cga_mode45_index[idx][i] ];
184    } else if (etype == 2) {
185        npal = FFMIN(esize, 16);
186        for (i = 0; i < npal; i++) {
187            int pal_idx = bytestream2_get_byte(&s->g);
188            palette[i]  = ff_cga_palette[FFMIN(pal_idx, 15)];
189        }
190    } else if (etype == 3) {
191        npal = FFMIN(esize, 16);
192        for (i = 0; i < npal; i++) {
193            int pal_idx = bytestream2_get_byte(&s->g);
194            palette[i]  = ff_ega_palette[FFMIN(pal_idx, 63)];
195        }
196    } else if (etype == 4 || etype == 5) {
197        npal = FFMIN(esize / 3, 256);
198        for (i = 0; i < npal; i++) {
199            palette[i] = bytestream2_get_be24(&s->g) << 2;
200            palette[i] |= 0xFFU << 24 | palette[i] >> 6 & 0x30303;
201        }
202    } else {
203        if (bpp == 1) {
204            npal = 2;
205            palette[0] = 0xFF000000;
206            palette[1] = 0xFFFFFFFF;
207        } else if (bpp == 2) {
208            npal = 4;
209            for (i = 0; i < npal; i++)
210                palette[i] = ff_cga_palette[ cga_mode45_index[0][i] ];
211        } else {
212            npal = 16;
213            memcpy(palette, ff_cga_palette, npal * 4);
214        }
215    }
216    // fill remaining palette entries
217    memset(palette + npal, 0, AVPALETTE_SIZE - npal * 4);
218    // skip remaining palette bytes
219    bytestream2_seek(&s->g, pos_after_pal, SEEK_SET);
220
221    val = 0;
222    y = s->height - 1;
223    if (bytestream2_get_le16(&s->g)) {
224        x = 0;
225        plane = 0;
226        while (bytestream2_get_bytes_left(&s->g) >= 6) {
227            int stop_size, marker, t1, t2;
228
229            t1        = bytestream2_get_bytes_left(&s->g);
230            t2        = bytestream2_get_le16(&s->g);
231            stop_size = t1 - FFMIN(t1, t2);
232            // ignore uncompressed block size
233            bytestream2_skip(&s->g, 2);
234            marker    = bytestream2_get_byte(&s->g);
235
236            while (plane < s->nb_planes &&
237                   bytestream2_get_bytes_left(&s->g) > stop_size) {
238                int run = 1;
239                val = bytestream2_get_byte(&s->g);
240                if (val == marker) {
241                    run = bytestream2_get_byte(&s->g);
242                    if (run == 0)
243                        run = bytestream2_get_le16(&s->g);
244                    val = bytestream2_get_byte(&s->g);
245                }
246
247                if (bits_per_plane == 8) {
248                    picmemset_8bpp(s, frame, val, run, &x, &y);
249                    if (y < 0)
250                        goto finish;
251                } else {
252                    picmemset(s, frame, val, run, &x, &y, &plane, bits_per_plane);
253                }
254            }
255        }
256
257        if (s->nb_planes - plane > 1)
258            return AVERROR_INVALIDDATA;
259
260        if (plane < s->nb_planes && x < avctx->width) {
261            int run = (y + 1) * avctx->width - x;
262            if (bits_per_plane == 8)
263                picmemset_8bpp(s, frame, val, run, &x, &y);
264            else
265                picmemset(s, frame, val, run / (8 / bits_per_plane), &x, &y, &plane, bits_per_plane);
266        }
267    } else {
268        while (y >= 0 && bytestream2_get_bytes_left(&s->g) > 0) {
269            memcpy(frame->data[0] + y * frame->linesize[0], s->g.buffer, FFMIN(avctx->width, bytestream2_get_bytes_left(&s->g)));
270            bytestream2_skip(&s->g, avctx->width);
271            y--;
272        }
273    }
274finish:
275
276    *got_frame      = 1;
277    return avpkt->size;
278}
279
280const FFCodec ff_pictor_decoder = {
281    .p.name         = "pictor",
282    .p.long_name    = NULL_IF_CONFIG_SMALL("Pictor/PC Paint"),
283    .p.type         = AVMEDIA_TYPE_VIDEO,
284    .p.id           = AV_CODEC_ID_PICTOR,
285    .p.capabilities = AV_CODEC_CAP_DR1,
286    .priv_data_size = sizeof(PicContext),
287    FF_CODEC_DECODE_CB(decode_frame),
288};
289