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