cdxl.c 8.78 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

Paul B Mahol's avatar
Paul B Mahol committed
42 43 44
typedef struct {
    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 83
{
    GetBitContext gb;
    int x, y, plane;

    init_get_bits(&gb, c->video, c->video_size * 8);
84 85 86
    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
87
                out[linesize * y + x] |= get_bits1(&gb) << plane;
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
            skip_bits(&gb, c->padded_bits);
        }
    }
}

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

    init_get_bits(&gb, c->video, c->video_size * 8);
    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);
104 105
        }
    }
Paul B Mahol's avatar
Paul B Mahol committed
106 107
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121
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;
    }
}

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

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

131
static void cdxl_decode_ham6(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
132 133 134 135 136 137 138
{
    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;
139
    out = frame->data[0];
Paul B Mahol's avatar
Paul B Mahol committed
140 141

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

    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;
            }
168
            AV_WL24(out + x * 3, r | g | b);
Paul B Mahol's avatar
Paul B Mahol committed
169
        }
170
        out += frame->linesize[0];
Paul B Mahol's avatar
Paul B Mahol committed
171 172 173
    }
}

174
static void cdxl_decode_ham8(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
175 176 177 178 179 180 181
{
    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;
182
    out = frame->data[0];
Paul B Mahol's avatar
Paul B Mahol committed
183 184

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

    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;
            }
211
            AV_WL24(out + x * 3, r | g | b);
Paul B Mahol's avatar
Paul B Mahol committed
212
        }
213
        out += frame->linesize[0];
Paul B Mahol's avatar
Paul B Mahol committed
214 215 216 217
    }
}

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

    if (buf_size < 32)
        return AVERROR_INVALIDDATA;
    encoding        = buf[1] & 7;
228
    c->format       = buf[1] & 0xE0;
Paul B Mahol's avatar
Paul B Mahol committed
229 230 231 232 233 234 235 236 237 238 239 240 241 242
    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;
243
    if (c->format != BIT_PLANAR && c->format != BIT_LINE) {
244
        avpriv_request_sample(avctx, "Pixel format 0x%0x", c->format);
Paul B Mahol's avatar
Paul B Mahol committed
245 246 247
        return AVERROR_PATCHWELCOME;
    }

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

251 252 253
    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)
254
        return AVERROR_INVALIDDATA;
255
    if (!encoding && c->palette_size && c->bpp <= 8) {
256
        avctx->pix_fmt = AV_PIX_FMT_PAL8;
Paul B Mahol's avatar
Paul B Mahol committed
257 258 259
    } else if (encoding == 1 && (c->bpp == 6 || c->bpp == 8)) {
        if (c->palette_size != (1 << (c->bpp - 1)))
            return AVERROR_INVALIDDATA;
260
        avctx->pix_fmt = AV_PIX_FMT_BGR24;
Paul B Mahol's avatar
Paul B Mahol committed
261
    } else {
262
        avpriv_request_sample(avctx, "Encoding %d and bpp %d",
Paul B Mahol's avatar
Paul B Mahol committed
263 264 265 266
                              encoding, c->bpp);
        return AVERROR_PATCHWELCOME;
    }

267
    if ((ret = ff_get_buffer(avctx, p, 0)) < 0)
Paul B Mahol's avatar
Paul B Mahol committed
268 269 270 271 272 273 274 275 276
        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)
277
            cdxl_decode_ham8(c, p);
Paul B Mahol's avatar
Paul B Mahol committed
278
        else
279
            cdxl_decode_ham6(c, p);
Paul B Mahol's avatar
Paul B Mahol committed
280
    } else {
281
        cdxl_decode_rgb(c, p);
Paul B Mahol's avatar
Paul B Mahol committed
282
    }
283
    *got_frame = 1;
Paul B Mahol's avatar
Paul B Mahol committed
284 285 286 287 288 289 290 291

    return buf_size;
}

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

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

    return 0;
}

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