pictordec.c 8.7 KB
Newer Older
Peter Ross's avatar
Peter Ross committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
/*
 * Pictor/PC Paint decoder
 * Copyright (c) 2010 Peter Ross <pross@xvid.org>
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
23
 * @file
Peter Ross's avatar
Peter Ross committed
24 25 26
 * Pictor/PC Paint decoder
 */

27
#include "libavutil/imgutils.h"
Peter Ross's avatar
Peter Ross committed
28 29 30
#include "avcodec.h"
#include "bytestream.h"
#include "cga_data.h"
31
#include "internal.h"
Peter Ross's avatar
Peter Ross committed
32 33 34 35

typedef struct PicContext {
    int width, height;
    int nb_planes;
36
    GetByteContext g;
Peter Ross's avatar
Peter Ross committed
37 38
} PicContext;

39 40
static void picmemset_8bpp(PicContext *s, AVFrame *frame, int value, int run,
                           int *x, int *y)
Peter Ross's avatar
Peter Ross committed
41 42
{
    while (run > 0) {
43
        uint8_t *d = frame->data[0] + *y * frame->linesize[0];
Peter Ross's avatar
Peter Ross committed
44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
        if (*x + run >= s->width) {
            int n = s->width - *x;
            memset(d + *x, value, n);
            run -= n;
            *x = 0;
            *y -= 1;
            if (*y < 0)
                break;
        } else {
            memset(d + *x, value, run);
            *x += run;
            break;
        }
    }
}

60
static void picmemset(PicContext *s, AVFrame *frame, int value, int run,
61
                      int *x, int *y, int *plane, int bits_per_plane)
Peter Ross's avatar
Peter Ross committed
62 63 64 65 66 67 68 69 70
{
    uint8_t *d;
    int shift = *plane * bits_per_plane;
    int mask  = ((1 << bits_per_plane) - 1) << shift;
    value   <<= shift;

    while (run > 0) {
        int j;
        for (j = 8-bits_per_plane; j >= 0; j -= bits_per_plane) {
71
            d = frame->data[0] + *y * frame->linesize[0];
Peter Ross's avatar
Peter Ross committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
            d[*x] |= (value >> j) & mask;
            *x += 1;
            if (*x == s->width) {
                *y -= 1;
                *x = 0;
                if (*y < 0) {
                   *y = s->height - 1;
                   *plane += 1;
                   value <<= bits_per_plane;
                   mask  <<= bits_per_plane;
                   if (*plane >= s->nb_planes)
                       break;
                }
            }
        }
        run--;
    }
}

static const uint8_t cga_mode45_index[6][4] = {
    [0] = { 0, 3,  5,   7 }, // mode4, palette#1, low intensity
    [1] = { 0, 2,  4,   6 }, // mode4, palette#2, low intensity
    [2] = { 0, 3,  4,   7 }, // mode5, low intensity
    [3] = { 0, 11, 13, 15 }, // mode4, palette#1, high intensity
    [4] = { 0, 10, 12, 14 }, // mode4, palette#2, high intensity
    [5] = { 0, 11, 12, 15 }, // mode5, high intensity
};

static int decode_frame(AVCodecContext *avctx,
101
                        void *data, int *got_frame,
Peter Ross's avatar
Peter Ross committed
102 103 104
                        AVPacket *avpkt)
{
    PicContext *s = avctx->priv_data;
105
    AVFrame *frame = data;
Peter Ross's avatar
Peter Ross committed
106
    uint32_t *palette;
107
    int bits_per_plane, bpp, etype, esize, npal, pos_after_pal;
108
    int i, x, y, plane, tmp, ret, val;
Peter Ross's avatar
Peter Ross committed
109

110 111 112
    bytestream2_init(&s->g, avpkt->data, avpkt->size);

    if (bytestream2_get_bytes_left(&s->g) < 11)
Peter Ross's avatar
Peter Ross committed
113 114
        return AVERROR_INVALIDDATA;

115
    if (bytestream2_get_le16u(&s->g) != 0x1234)
Peter Ross's avatar
Peter Ross committed
116
        return AVERROR_INVALIDDATA;
117 118 119 120 121 122 123 124

    s->width       = bytestream2_get_le16u(&s->g);
    s->height      = bytestream2_get_le16u(&s->g);
    bytestream2_skip(&s->g, 4);
    tmp            = bytestream2_get_byteu(&s->g);
    bits_per_plane = tmp & 0xF;
    s->nb_planes   = (tmp >> 4) + 1;
    bpp            = bits_per_plane * s->nb_planes;
Peter Ross's avatar
Peter Ross committed
125
    if (bits_per_plane > 8 || bpp < 1 || bpp > 32) {
126
        avpriv_request_sample(avctx, "Unsupported bit depth");
127
        return AVERROR_PATCHWELCOME;
Peter Ross's avatar
Peter Ross committed
128 129
    }

130
    if (bytestream2_peek_byte(&s->g) == 0xFF || bpp == 1 || bpp == 4 || bpp == 8) {
131 132 133 134
        bytestream2_skip(&s->g, 2);
        etype = bytestream2_get_le16(&s->g);
        esize = bytestream2_get_le16(&s->g);
        if (bytestream2_get_bytes_left(&s->g) < esize)
Peter Ross's avatar
Peter Ross committed
135 136 137 138 139 140
            return AVERROR_INVALIDDATA;
    } else {
        etype = -1;
        esize = 0;
    }

141
    avctx->pix_fmt = AV_PIX_FMT_PAL8;
Peter Ross's avatar
Peter Ross committed
142

143 144
    if (av_image_check_size(s->width, s->height, 0, avctx) < 0)
        return -1;
Peter Ross's avatar
Peter Ross committed
145
    if (s->width != avctx->width && s->height != avctx->height) {
146 147 148
        ret = ff_set_dimensions(avctx, s->width, s->height);
        if (ret < 0)
            return ret;
Peter Ross's avatar
Peter Ross committed
149 150
    }

151
    if ((ret = ff_get_buffer(avctx, frame, 0)) < 0)
152 153 154 155
        return ret;
    memset(frame->data[0], 0, s->height * frame->linesize[0]);
    frame->pict_type           = AV_PICTURE_TYPE_I;
    frame->palette_has_changed = 1;
Peter Ross's avatar
Peter Ross committed
156

157
    pos_after_pal = bytestream2_tell(&s->g) + esize;
158
    palette = (uint32_t*)frame->data[1];
159 160
    if (etype == 1 && esize > 1 && bytestream2_peek_byte(&s->g) < 6) {
        int idx = bytestream2_get_byte(&s->g);
Peter Ross's avatar
Peter Ross committed
161 162 163 164 165
        npal = 4;
        for (i = 0; i < npal; i++)
            palette[i] = ff_cga_palette[ cga_mode45_index[idx][i] ];
    } else if (etype == 2) {
        npal = FFMIN(esize, 16);
166 167
        for (i = 0; i < npal; i++) {
            int pal_idx = bytestream2_get_byte(&s->g);
168
            palette[i]  = ff_cga_palette[FFMIN(pal_idx, 15)];
169
        }
Peter Ross's avatar
Peter Ross committed
170 171
    } else if (etype == 3) {
        npal = FFMIN(esize, 16);
172 173 174 175
        for (i = 0; i < npal; i++) {
            int pal_idx = bytestream2_get_byte(&s->g);
            palette[i]  = ff_ega_palette[FFMIN(pal_idx, 63)];
        }
Peter Ross's avatar
Peter Ross committed
176 177
    } else if (etype == 4 || etype == 5) {
        npal = FFMIN(esize / 3, 256);
178
        for (i = 0; i < npal; i++) {
179
            palette[i] = bytestream2_get_be24(&s->g) << 2;
180
            palette[i] |= 0xFFU << 24 | palette[i] >> 6 & 0x30303;
181
        }
Peter Ross's avatar
Peter Ross committed
182 183 184
    } else {
        if (bpp == 1) {
            npal = 2;
185 186
            palette[0] = 0xFF000000;
            palette[1] = 0xFFFFFFFF;
Peter Ross's avatar
Peter Ross committed
187 188 189 190 191 192 193 194 195 196 197
        } else if (bpp == 2) {
            npal = 4;
            for (i = 0; i < npal; i++)
                palette[i] = ff_cga_palette[ cga_mode45_index[0][i] ];
        } else {
            npal = 16;
            memcpy(palette, ff_cga_palette, npal * 4);
        }
    }
    // fill remaining palette entries
    memset(palette + npal, 0, AVPALETTE_SIZE - npal * 4);
198 199
    // skip remaining palette bytes
    bytestream2_seek(&s->g, pos_after_pal, SEEK_SET);
Peter Ross's avatar
Peter Ross committed
200

201
    val = 0;
Peter Ross's avatar
Peter Ross committed
202
    y = s->height - 1;
203
    if (bytestream2_get_le16(&s->g)) {
204 205
        x = 0;
        plane = 0;
206
        while (bytestream2_get_bytes_left(&s->g) >= 6) {
207 208 209 210 211 212 213 214
            int stop_size, marker, t1, t2;

            t1        = bytestream2_get_bytes_left(&s->g);
            t2        = bytestream2_get_le16(&s->g);
            stop_size = t1 - FFMIN(t1, t2);
            // ignore uncompressed block size
            bytestream2_skip(&s->g, 2);
            marker    = bytestream2_get_byte(&s->g);
Peter Ross's avatar
Peter Ross committed
215

216
            while (plane < s->nb_planes &&
217
                   bytestream2_get_bytes_left(&s->g) > stop_size) {
Peter Ross's avatar
Peter Ross committed
218
                int run = 1;
219
                val = bytestream2_get_byte(&s->g);
Peter Ross's avatar
Peter Ross committed
220
                if (val == marker) {
221
                    run = bytestream2_get_byte(&s->g);
Peter Ross's avatar
Peter Ross committed
222
                    if (run == 0)
223 224
                        run = bytestream2_get_le16(&s->g);
                    val = bytestream2_get_byte(&s->g);
Peter Ross's avatar
Peter Ross committed
225
                }
226
                if (!bytestream2_get_bytes_left(&s->g))
Peter Ross's avatar
Peter Ross committed
227 228 229
                    break;

                if (bits_per_plane == 8) {
230
                    picmemset_8bpp(s, frame, val, run, &x, &y);
Peter Ross's avatar
Peter Ross committed
231
                    if (y < 0)
232
                        goto finish;
Peter Ross's avatar
Peter Ross committed
233
                } else {
234
                    picmemset(s, frame, val, run, &x, &y, &plane, bits_per_plane);
Peter Ross's avatar
Peter Ross committed
235 236 237
                }
            }
        }
238

239
        if (x < avctx->width) {
240 241
            int run = (y + 1) * avctx->width - x;
            if (bits_per_plane == 8)
242
                picmemset_8bpp(s, frame, val, run, &x, &y);
243
            else
244
                picmemset(s, frame, val, run / (8 / bits_per_plane), &x, &y, &plane, bits_per_plane);
245
        }
Peter Ross's avatar
Peter Ross committed
246
    } else {
247
        while (y >= 0 && bytestream2_get_bytes_left(&s->g) > 0) {
248
            memcpy(frame->data[0] + y * frame->linesize[0], s->g.buffer, FFMIN(avctx->width, bytestream2_get_bytes_left(&s->g)));
249
            bytestream2_skip(&s->g, avctx->width);
250 251
            y--;
        }
Peter Ross's avatar
Peter Ross committed
252
    }
253
finish:
Peter Ross's avatar
Peter Ross committed
254

255
    *got_frame      = 1;
256
    return avpkt->size;
Peter Ross's avatar
Peter Ross committed
257 258
}

259
AVCodec ff_pictor_decoder = {
260
    .name           = "pictor",
261
    .long_name      = NULL_IF_CONFIG_SMALL("Pictor/PC Paint"),
262
    .type           = AVMEDIA_TYPE_VIDEO,
263
    .id             = AV_CODEC_ID_PICTOR,
264 265 266
    .priv_data_size = sizeof(PicContext),
    .decode         = decode_frame,
    .capabilities   = CODEC_CAP_DR1,
Peter Ross's avatar
Peter Ross committed
267
};