]> git.sesse.net Git - ffmpeg/blob - libavcodec/cdxl.c
Merge commit '7e5bde93a1e7641e1622814dafac0be3f413d79b'
[ffmpeg] / libavcodec / cdxl.c
1 /*
2  * CDXL video decoder
3  * Copyright (c) 2011-2012 Paul B Mahol
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  * Commodore CDXL video decoder
25  * @author Paul B Mahol
26  */
27
28 #define UNCHECKED_BITSTREAM_READER 1
29
30 #include "libavutil/intreadwrite.h"
31 #include "libavutil/imgutils.h"
32 #include "avcodec.h"
33 #include "bytestream.h"
34 #include "get_bits.h"
35 #include "internal.h"
36
37 #define BIT_PLANAR   0x00
38 #define CHUNKY       0x20
39 #define BYTE_PLANAR  0x40
40 #define BIT_LINE     0x80
41 #define BYTE_LINE    0xC0
42
43 typedef struct CDXLVideoContext {
44     AVCodecContext *avctx;
45     int            bpp;
46     int            format;
47     int            padded_bits;
48     const uint8_t  *palette;
49     int            palette_size;
50     const uint8_t  *video;
51     int            video_size;
52     uint8_t        *new_video;
53     int            new_video_size;
54 } CDXLVideoContext;
55
56 static av_cold int cdxl_decode_init(AVCodecContext *avctx)
57 {
58     CDXLVideoContext *c = avctx->priv_data;
59
60     c->new_video_size = 0;
61     c->avctx          = avctx;
62
63     return 0;
64 }
65
66 static void import_palette(CDXLVideoContext *c, uint32_t *new_palette)
67 {
68     int i;
69
70     for (i = 0; i < c->palette_size / 2; i++) {
71         unsigned rgb = AV_RB16(&c->palette[i * 2]);
72         unsigned r   = ((rgb >> 8) & 0xF) * 0x11;
73         unsigned g   = ((rgb >> 4) & 0xF) * 0x11;
74         unsigned b   =  (rgb       & 0xF) * 0x11;
75         AV_WN32(&new_palette[i], (0xFFU << 24) | (r << 16) | (g << 8) | b);
76     }
77 }
78
79 static void bitplanar2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
80 {
81     GetBitContext gb;
82     int x, y, plane;
83
84     if (init_get_bits8(&gb, c->video, c->video_size) < 0)
85         return;
86     for (plane = 0; plane < c->bpp; plane++) {
87         for (y = 0; y < c->avctx->height; y++) {
88             for (x = 0; x < c->avctx->width; x++)
89                 out[linesize * y + x] |= get_bits1(&gb) << plane;
90             skip_bits(&gb, c->padded_bits);
91         }
92     }
93 }
94
95 static void bitline2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
96 {
97     GetBitContext  gb;
98     int x, y, plane;
99
100     if (init_get_bits8(&gb, c->video, c->video_size) < 0)
101         return;
102     for (y = 0; y < c->avctx->height; y++) {
103         for (plane = 0; plane < c->bpp; plane++) {
104             for (x = 0; x < c->avctx->width; x++)
105                 out[linesize * y + x] |= get_bits1(&gb) << plane;
106             skip_bits(&gb, c->padded_bits);
107         }
108     }
109 }
110
111 static void chunky2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
112 {
113     GetByteContext gb;
114     int y;
115
116     bytestream2_init(&gb, c->video, c->video_size);
117     for (y = 0; y < c->avctx->height; y++) {
118         bytestream2_get_buffer(&gb, out + linesize * y, c->avctx->width * 3);
119     }
120 }
121
122 static void import_format(CDXLVideoContext *c, int linesize, uint8_t *out)
123 {
124     memset(out, 0, linesize * c->avctx->height);
125
126     switch (c->format) {
127     case BIT_PLANAR:
128         bitplanar2chunky(c, linesize, out);
129         break;
130     case BIT_LINE:
131         bitline2chunky(c, linesize, out);
132         break;
133     case CHUNKY:
134         chunky2chunky(c, linesize, out);
135         break;
136     }
137 }
138
139 static void cdxl_decode_rgb(CDXLVideoContext *c, AVFrame *frame)
140 {
141     uint32_t *new_palette = (uint32_t *)frame->data[1];
142
143     memset(frame->data[1], 0, AVPALETTE_SIZE);
144     import_palette(c, new_palette);
145     import_format(c, frame->linesize[0], frame->data[0]);
146 }
147
148 static void cdxl_decode_raw(CDXLVideoContext *c, AVFrame *frame)
149 {
150     import_format(c, frame->linesize[0], frame->data[0]);
151 }
152
153 static void cdxl_decode_ham6(CDXLVideoContext *c, AVFrame *frame)
154 {
155     AVCodecContext *avctx = c->avctx;
156     uint32_t new_palette[16], r, g, b;
157     uint8_t *ptr, *out, index, op;
158     int x, y;
159
160     ptr = c->new_video;
161     out = frame->data[0];
162
163     import_palette(c, new_palette);
164     import_format(c, avctx->width, c->new_video);
165
166     for (y = 0; y < avctx->height; y++) {
167         r = new_palette[0] & 0xFF0000;
168         g = new_palette[0] & 0xFF00;
169         b = new_palette[0] & 0xFF;
170         for (x = 0; x < avctx->width; x++) {
171             index  = *ptr++;
172             op     = index >> 4;
173             index &= 15;
174             switch (op) {
175             case 0:
176                 r = new_palette[index] & 0xFF0000;
177                 g = new_palette[index] & 0xFF00;
178                 b = new_palette[index] & 0xFF;
179                 break;
180             case 1:
181                 b = index * 0x11;
182                 break;
183             case 2:
184                 r = index * 0x11 << 16;
185                 break;
186             case 3:
187                 g = index * 0x11 << 8;
188                 break;
189             }
190             AV_WL24(out + x * 3, r | g | b);
191         }
192         out += frame->linesize[0];
193     }
194 }
195
196 static void cdxl_decode_ham8(CDXLVideoContext *c, AVFrame *frame)
197 {
198     AVCodecContext *avctx = c->avctx;
199     uint32_t new_palette[64], r, g, b;
200     uint8_t *ptr, *out, index, op;
201     int x, y;
202
203     ptr = c->new_video;
204     out = frame->data[0];
205
206     import_palette(c, new_palette);
207     import_format(c, avctx->width, c->new_video);
208
209     for (y = 0; y < avctx->height; y++) {
210         r = new_palette[0] & 0xFF0000;
211         g = new_palette[0] & 0xFF00;
212         b = new_palette[0] & 0xFF;
213         for (x = 0; x < avctx->width; x++) {
214             index  = *ptr++;
215             op     = index >> 6;
216             index &= 63;
217             switch (op) {
218             case 0:
219                 r = new_palette[index] & 0xFF0000;
220                 g = new_palette[index] & 0xFF00;
221                 b = new_palette[index] & 0xFF;
222                 break;
223             case 1:
224                 b = (index <<  2) | (b & 3);
225                 break;
226             case 2:
227                 r = (index << 18) | (r & (3 << 16));
228                 break;
229             case 3:
230                 g = (index << 10) | (g & (3 << 8));
231                 break;
232             }
233             AV_WL24(out + x * 3, r | g | b);
234         }
235         out += frame->linesize[0];
236     }
237 }
238
239 static int cdxl_decode_frame(AVCodecContext *avctx, void *data,
240                              int *got_frame, AVPacket *pkt)
241 {
242     CDXLVideoContext *c = avctx->priv_data;
243     AVFrame * const p = data;
244     int ret, w, h, encoding, aligned_width, buf_size = pkt->size;
245     const uint8_t *buf = pkt->data;
246
247     if (buf_size < 32)
248         return AVERROR_INVALIDDATA;
249     encoding        = buf[1] & 7;
250     c->format       = buf[1] & 0xE0;
251     w               = AV_RB16(&buf[14]);
252     h               = AV_RB16(&buf[16]);
253     c->bpp          = buf[19];
254     c->palette_size = AV_RB16(&buf[20]);
255     c->palette      = buf + 32;
256     c->video        = c->palette + c->palette_size;
257     c->video_size   = buf_size - c->palette_size - 32;
258
259     if (c->palette_size > 512)
260         return AVERROR_INVALIDDATA;
261     if (buf_size < c->palette_size + 32)
262         return AVERROR_INVALIDDATA;
263     if (c->bpp < 1)
264         return AVERROR_INVALIDDATA;
265     if (c->format != BIT_PLANAR && c->format != BIT_LINE && c->format != CHUNKY) {
266         avpriv_request_sample(avctx, "Pixel format 0x%0x", c->format);
267         return AVERROR_PATCHWELCOME;
268     }
269
270     if ((ret = ff_set_dimensions(avctx, w, h)) < 0)
271         return ret;
272
273     if (c->format == CHUNKY)
274         aligned_width = avctx->width;
275     else
276         aligned_width = FFALIGN(c->avctx->width, 16);
277     c->padded_bits  = aligned_width - c->avctx->width;
278     if (c->video_size < aligned_width * avctx->height * (int64_t)c->bpp / 8)
279         return AVERROR_INVALIDDATA;
280     if (!encoding && c->palette_size && c->bpp <= 8 && c->format != CHUNKY) {
281         avctx->pix_fmt = AV_PIX_FMT_PAL8;
282     } else if (encoding == 1 && (c->bpp == 6 || c->bpp == 8) && c->format != CHUNKY) {
283         if (c->palette_size != (1 << (c->bpp - 1)))
284             return AVERROR_INVALIDDATA;
285         avctx->pix_fmt = AV_PIX_FMT_BGR24;
286     } else if (!encoding && c->bpp == 24 && c->format == CHUNKY &&
287                !c->palette_size) {
288         avctx->pix_fmt = AV_PIX_FMT_RGB24;
289     } else {
290         avpriv_request_sample(avctx, "Encoding %d, bpp %d and format 0x%x",
291                               encoding, c->bpp, c->format);
292         return AVERROR_PATCHWELCOME;
293     }
294
295     if ((ret = ff_get_buffer(avctx, p, 0)) < 0)
296         return ret;
297     p->pict_type = AV_PICTURE_TYPE_I;
298
299     if (encoding) {
300         av_fast_padded_malloc(&c->new_video, &c->new_video_size,
301                               h * w + AV_INPUT_BUFFER_PADDING_SIZE);
302         if (!c->new_video)
303             return AVERROR(ENOMEM);
304         if (c->bpp == 8)
305             cdxl_decode_ham8(c, p);
306         else
307             cdxl_decode_ham6(c, p);
308     } else if (avctx->pix_fmt == AV_PIX_FMT_PAL8) {
309         cdxl_decode_rgb(c, p);
310     } else {
311         cdxl_decode_raw(c, p);
312     }
313     *got_frame = 1;
314
315     return buf_size;
316 }
317
318 static av_cold int cdxl_decode_end(AVCodecContext *avctx)
319 {
320     CDXLVideoContext *c = avctx->priv_data;
321
322     av_freep(&c->new_video);
323
324     return 0;
325 }
326
327 AVCodec ff_cdxl_decoder = {
328     .name           = "cdxl",
329     .long_name      = NULL_IF_CONFIG_SMALL("Commodore CDXL video"),
330     .type           = AVMEDIA_TYPE_VIDEO,
331     .id             = AV_CODEC_ID_CDXL,
332     .priv_data_size = sizeof(CDXLVideoContext),
333     .init           = cdxl_decode_init,
334     .close          = cdxl_decode_end,
335     .decode         = cdxl_decode_frame,
336     .capabilities   = AV_CODEC_CAP_DR1,
337 };