cdxl.c 8.8 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
 */

22 23
#define UNCHECKED_BITSTREAM_READER 1

Paul B Mahol's avatar
Paul B Mahol committed
24 25 26 27
#include "libavutil/intreadwrite.h"
#include "libavutil/imgutils.h"
#include "avcodec.h"
#include "get_bits.h"
28
#include "internal.h"
Paul B Mahol's avatar
Paul B Mahol committed
29

30
#define BIT_PLANAR   0x00
31 32
#define CHUNKY       0x20
#define BYTE_PLANAR  0x40
33 34 35
#define BIT_LINE     0x80
#define BYTE_LINE    0xC0

Paul B Mahol's avatar
Paul B Mahol committed
36 37 38
typedef struct {
    AVCodecContext *avctx;
    int            bpp;
39 40
    int            format;
    int            padded_bits;
Paul B Mahol's avatar
Paul B Mahol committed
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
    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;
68
        AV_WN32(&new_palette[i], (0xFFU << 24) | (r << 16) | (g << 8) | b);
Paul B Mahol's avatar
Paul B Mahol committed
69 70 71
    }
}

72
static void bitplanar2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
Paul B Mahol's avatar
Paul B Mahol committed
73 74 75 76 77
{
    GetBitContext gb;
    int x, y, plane;

    init_get_bits(&gb, c->video, c->video_size * 8);
78 79 80
    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
81
                out[linesize * y + x] |= get_bits1(&gb) << plane;
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
            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);
98 99
        }
    }
Paul B Mahol's avatar
Paul B Mahol committed
100 101
}

102 103 104 105 106 107 108 109 110 111 112 113 114 115
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;
    }
}

116
static void cdxl_decode_rgb(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
117
{
118
    uint32_t *new_palette = (uint32_t *)frame->data[1];
Paul B Mahol's avatar
Paul B Mahol committed
119

120
    memset(frame->data[1], 0, AVPALETTE_SIZE);
Paul B Mahol's avatar
Paul B Mahol committed
121
    import_palette(c, new_palette);
122
    import_format(c, frame->linesize[0], frame->data[0]);
Paul B Mahol's avatar
Paul B Mahol committed
123 124
}

125
static void cdxl_decode_ham6(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
126 127 128 129 130 131 132
{
    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;
133
    out = frame->data[0];
Paul B Mahol's avatar
Paul B Mahol committed
134 135

    import_palette(c, new_palette);
136
    import_format(c, avctx->width, c->new_video);
Paul B Mahol's avatar
Paul B Mahol committed
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161

    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;
            }
162
            AV_WL24(out + x * 3, r | g | b);
Paul B Mahol's avatar
Paul B Mahol committed
163
        }
164
        out += frame->linesize[0];
Paul B Mahol's avatar
Paul B Mahol committed
165 166 167
    }
}

168
static void cdxl_decode_ham8(CDXLVideoContext *c, AVFrame *frame)
Paul B Mahol's avatar
Paul B Mahol committed
169 170 171 172 173 174 175
{
    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;
176
    out = frame->data[0];
Paul B Mahol's avatar
Paul B Mahol committed
177 178

    import_palette(c, new_palette);
179
    import_format(c, avctx->width, c->new_video);
Paul B Mahol's avatar
Paul B Mahol committed
180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204

    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;
            }
205
            AV_WL24(out + x * 3, r | g | b);
Paul B Mahol's avatar
Paul B Mahol committed
206
        }
207
        out += frame->linesize[0];
Paul B Mahol's avatar
Paul B Mahol committed
208 209 210 211
    }
}

static int cdxl_decode_frame(AVCodecContext *avctx, void *data,
212
                             int *got_frame, AVPacket *pkt)
Paul B Mahol's avatar
Paul B Mahol committed
213 214
{
    CDXLVideoContext *c = avctx->priv_data;
215
    AVFrame * const p = data;
216
    int ret, w, h, encoding, aligned_width, buf_size = pkt->size;
Paul B Mahol's avatar
Paul B Mahol committed
217 218 219 220 221
    const uint8_t *buf = pkt->data;

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

    if ((ret = av_image_check_size(w, h, 0, avctx)) < 0)
        return ret;
    if (w != avctx->width || h != avctx->height)
        avcodec_set_dimensions(avctx, w, h);

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

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

    return buf_size;
}

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

    av_free(c->new_video);

    return 0;
}

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