cdxl.c 8.84 KB
Newer Older
Paul B Mahol's avatar
Paul B Mahol committed
1 2 3 4
/*
 * CDXL video decoder
 * Copyright (c) 2011-2012 Paul B Mahol
 *
5
 * This file is part of FFmpeg.
Paul B Mahol's avatar
Paul B Mahol committed
6
 *
7
 * FFmpeg is free software; you can redistribute it and/or
Paul B Mahol's avatar
Paul B Mahol committed
8 9 10 11
 * 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.
 *
12
 * FFmpeg is distributed in the hope that it will be useful,
Paul B Mahol's avatar
Paul B Mahol committed
13 14 15 16 17
 * 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
18
 * License along with FFmpeg; if not, write to the Free Software
Paul B Mahol's avatar
Paul B Mahol committed
19 20 21
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

Paul B Mahol's avatar
Paul B Mahol committed
22 23 24 25 26 27
/**
 * @file
 * Commodore CDXL video decoder
 * @author Paul B Mahol
 */

28 29
#define UNCHECKED_BITSTREAM_READER 1

Paul B Mahol's avatar
Paul B Mahol committed
30 31 32 33
#include "libavutil/intreadwrite.h"
#include "libavutil/imgutils.h"
#include "avcodec.h"
#include "get_bits.h"
34
#include "internal.h"
Paul B Mahol's avatar
Paul B Mahol committed
35

36
#define BIT_PLANAR   0x00
37 38
#define CHUNKY       0x20
#define BYTE_PLANAR  0x40
39 40 41
#define BIT_LINE     0x80
#define BYTE_LINE    0xC0

42
typedef struct CDXLVideoContext {
Paul B Mahol's avatar
Paul B Mahol committed
43 44
    AVCodecContext *avctx;
    int            bpp;
45 46
    int            format;
    int            padded_bits;
Paul B Mahol's avatar
Paul B Mahol committed
47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
    const uint8_t  *palette;
    int            palette_size;
    const uint8_t  *video;
    int            video_size;
    uint8_t        *new_video;
    int            new_video_size;
} CDXLVideoContext;

static av_cold int cdxl_decode_init(AVCodecContext *avctx)
{
    CDXLVideoContext *c = avctx->priv_data;

    c->new_video_size = 0;
    c->avctx          = avctx;

    return 0;
}

static void import_palette(CDXLVideoContext *c, uint32_t *new_palette)
{
    int i;

    for (i = 0; i < c->palette_size / 2; i++) {
        unsigned rgb = AV_RB16(&c->palette[i * 2]);
        unsigned r   = ((rgb >> 8) & 0xF) * 0x11;
        unsigned g   = ((rgb >> 4) & 0xF) * 0x11;
        unsigned b   =  (rgb       & 0xF) * 0x11;
74
        AV_WN32(&new_palette[i], (0xFFU << 24) | (r << 16) | (g << 8) | b);
Paul B Mahol's avatar
Paul B Mahol committed
75 76 77
    }
}

78
static void bitplanar2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
Paul B Mahol's avatar
Paul B Mahol committed
79 80 81 82
{
    GetBitContext gb;
    int x, y, plane;

83 84
    if (init_get_bits8(&gb, c->video, c->video_size) < 0)
        return;
85 86 87
    for (plane = 0; plane < c->bpp; plane++) {
        for (y = 0; y < c->avctx->height; y++) {
            for (x = 0; x < c->avctx->width; x++)
Paul B Mahol's avatar
Paul B Mahol committed
88
                out[linesize * y + x] |= get_bits1(&gb) << plane;
89 90 91 92 93 94 95 96 97 98
            skip_bits(&gb, c->padded_bits);
        }
    }
}

static void bitline2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
{
    GetBitContext  gb;
    int x, y, plane;

99 100
    if (init_get_bits8(&gb, c->video, c->video_size) < 0)
        return;
101 102 103 104 105
    for (y = 0; y < c->avctx->height; y++) {
        for (plane = 0; plane < c->bpp; plane++) {
            for (x = 0; x < c->avctx->width; x++)
                out[linesize * y + x] |= get_bits1(&gb) << plane;
            skip_bits(&gb, c->padded_bits);
106 107
        }
    }
Paul B Mahol's avatar
Paul B Mahol committed
108 109
}

110 111 112 113 114 115 116 117 118 119 120 121 122 123
static void import_format(CDXLVideoContext *c, int linesize, uint8_t *out)
{
    memset(out, 0, linesize * c->avctx->height);

    switch (c->format) {
    case BIT_PLANAR:
        bitplanar2chunky(c, linesize, out);
        break;
    case BIT_LINE:
        bitline2chunky(c, linesize, out);
        break;
    }
}

124
static void cdxl_decode_rgb(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
125
{
126
    uint32_t *new_palette = (uint32_t *)frame->data[1];
Paul B Mahol's avatar
Paul B Mahol committed
127

128
    memset(frame->data[1], 0, AVPALETTE_SIZE);
Paul B Mahol's avatar
Paul B Mahol committed
129
    import_palette(c, new_palette);
130
    import_format(c, frame->linesize[0], frame->data[0]);
Paul B Mahol's avatar
Paul B Mahol committed
131 132
}

133
static void cdxl_decode_ham6(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
134 135 136 137 138 139 140
{
    AVCodecContext *avctx = c->avctx;
    uint32_t new_palette[16], r, g, b;
    uint8_t *ptr, *out, index, op;
    int x, y;

    ptr = c->new_video;
141
    out = frame->data[0];
Paul B Mahol's avatar
Paul B Mahol committed
142 143

    import_palette(c, new_palette);
144
    import_format(c, avctx->width, c->new_video);
Paul B Mahol's avatar
Paul B Mahol committed
145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169

    for (y = 0; y < avctx->height; y++) {
        r = new_palette[0] & 0xFF0000;
        g = new_palette[0] & 0xFF00;
        b = new_palette[0] & 0xFF;
        for (x = 0; x < avctx->width; x++) {
            index  = *ptr++;
            op     = index >> 4;
            index &= 15;
            switch (op) {
            case 0:
                r = new_palette[index] & 0xFF0000;
                g = new_palette[index] & 0xFF00;
                b = new_palette[index] & 0xFF;
                break;
            case 1:
                b = index * 0x11;
                break;
            case 2:
                r = index * 0x11 << 16;
                break;
            case 3:
                g = index * 0x11 << 8;
                break;
            }
170
            AV_WL24(out + x * 3, r | g | b);
Paul B Mahol's avatar
Paul B Mahol committed
171
        }
172
        out += frame->linesize[0];
Paul B Mahol's avatar
Paul B Mahol committed
173 174 175
    }
}

176
static void cdxl_decode_ham8(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
177 178 179 180 181 182 183
{
    AVCodecContext *avctx = c->avctx;
    uint32_t new_palette[64], r, g, b;
    uint8_t *ptr, *out, index, op;
    int x, y;

    ptr = c->new_video;
184
    out = frame->data[0];
Paul B Mahol's avatar
Paul B Mahol committed
185 186

    import_palette(c, new_palette);
187
    import_format(c, avctx->width, c->new_video);
Paul B Mahol's avatar
Paul B Mahol committed
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212

    for (y = 0; y < avctx->height; y++) {
        r = new_palette[0] & 0xFF0000;
        g = new_palette[0] & 0xFF00;
        b = new_palette[0] & 0xFF;
        for (x = 0; x < avctx->width; x++) {
            index  = *ptr++;
            op     = index >> 6;
            index &= 63;
            switch (op) {
            case 0:
                r = new_palette[index] & 0xFF0000;
                g = new_palette[index] & 0xFF00;
                b = new_palette[index] & 0xFF;
                break;
            case 1:
                b = (index <<  2) | (b & 3);
                break;
            case 2:
                r = (index << 18) | (r & (3 << 16));
                break;
            case 3:
                g = (index << 10) | (g & (3 << 8));
                break;
            }
213
            AV_WL24(out + x * 3, r | g | b);
Paul B Mahol's avatar
Paul B Mahol committed
214
        }
215
        out += frame->linesize[0];
Paul B Mahol's avatar
Paul B Mahol committed
216 217 218 219
    }
}

static int cdxl_decode_frame(AVCodecContext *avctx, void *data,
220
                             int *got_frame, AVPacket *pkt)
Paul B Mahol's avatar
Paul B Mahol committed
221 222
{
    CDXLVideoContext *c = avctx->priv_data;
223
    AVFrame * const p = data;
224
    int ret, w, h, encoding, aligned_width, buf_size = pkt->size;
Paul B Mahol's avatar
Paul B Mahol committed
225 226 227 228 229
    const uint8_t *buf = pkt->data;

    if (buf_size < 32)
        return AVERROR_INVALIDDATA;
    encoding        = buf[1] & 7;
230
    c->format       = buf[1] & 0xE0;
Paul B Mahol's avatar
Paul B Mahol committed
231 232 233 234 235 236 237 238 239 240 241 242 243 244
    w               = AV_RB16(&buf[14]);
    h               = AV_RB16(&buf[16]);
    c->bpp          = buf[19];
    c->palette_size = AV_RB16(&buf[20]);
    c->palette      = buf + 32;
    c->video        = c->palette + c->palette_size;
    c->video_size   = buf_size - c->palette_size - 32;

    if (c->palette_size > 512)
        return AVERROR_INVALIDDATA;
    if (buf_size < c->palette_size + 32)
        return AVERROR_INVALIDDATA;
    if (c->bpp < 1)
        return AVERROR_INVALIDDATA;
245
    if (c->format != BIT_PLANAR && c->format != BIT_LINE) {
246
        avpriv_request_sample(avctx, "Pixel format 0x%0x", c->format);
Paul B Mahol's avatar
Paul B Mahol committed
247 248 249
        return AVERROR_PATCHWELCOME;
    }

250
    if ((ret = ff_set_dimensions(avctx, w, h)) < 0)
Paul B Mahol's avatar
Paul B Mahol committed
251 252
        return ret;

253 254 255
    aligned_width = FFALIGN(c->avctx->width, 16);
    c->padded_bits  = aligned_width - c->avctx->width;
    if (c->video_size < aligned_width * avctx->height * c->bpp / 8)
256
        return AVERROR_INVALIDDATA;
257
    if (!encoding && c->palette_size && c->bpp <= 8) {
258
        avctx->pix_fmt = AV_PIX_FMT_PAL8;
Paul B Mahol's avatar
Paul B Mahol committed
259 260 261
    } else if (encoding == 1 && (c->bpp == 6 || c->bpp == 8)) {
        if (c->palette_size != (1 << (c->bpp - 1)))
            return AVERROR_INVALIDDATA;
262
        avctx->pix_fmt = AV_PIX_FMT_BGR24;
Paul B Mahol's avatar
Paul B Mahol committed
263
    } else {
264
        avpriv_request_sample(avctx, "Encoding %d and bpp %d",
Paul B Mahol's avatar
Paul B Mahol committed
265 266 267 268
                              encoding, c->bpp);
        return AVERROR_PATCHWELCOME;
    }

269
    if ((ret = ff_get_buffer(avctx, p, 0)) < 0)
Paul B Mahol's avatar
Paul B Mahol committed
270 271 272 273 274 275 276 277 278
        return ret;
    p->pict_type = AV_PICTURE_TYPE_I;

    if (encoding) {
        av_fast_padded_malloc(&c->new_video, &c->new_video_size,
                              h * w + FF_INPUT_BUFFER_PADDING_SIZE);
        if (!c->new_video)
            return AVERROR(ENOMEM);
        if (c->bpp == 8)
279
            cdxl_decode_ham8(c, p);
Paul B Mahol's avatar
Paul B Mahol committed
280
        else
281
            cdxl_decode_ham6(c, p);
Paul B Mahol's avatar
Paul B Mahol committed
282
    } else {
283
        cdxl_decode_rgb(c, p);
Paul B Mahol's avatar
Paul B Mahol committed
284
    }
285
    *got_frame = 1;
Paul B Mahol's avatar
Paul B Mahol committed
286 287 288 289 290 291 292 293

    return buf_size;
}

static av_cold int cdxl_decode_end(AVCodecContext *avctx)
{
    CDXLVideoContext *c = avctx->priv_data;

294
    av_freep(&c->new_video);
Paul B Mahol's avatar
Paul B Mahol committed
295 296 297 298 299 300

    return 0;
}

AVCodec ff_cdxl_decoder = {
    .name           = "cdxl",
301
    .long_name      = NULL_IF_CONFIG_SMALL("Commodore CDXL video"),
Paul B Mahol's avatar
Paul B Mahol committed
302
    .type           = AVMEDIA_TYPE_VIDEO,
303
    .id             = AV_CODEC_ID_CDXL,
Paul B Mahol's avatar
Paul B Mahol committed
304 305 306 307 308 309
    .priv_data_size = sizeof(CDXLVideoContext),
    .init           = cdxl_decode_init,
    .close          = cdxl_decode_end,
    .decode         = cdxl_decode_frame,
    .capabilities   = CODEC_CAP_DR1,
};