cdxl.c
Go to the documentation of this file.
1 /*
2  * CDXL video decoder
3  * Copyright (c) 2011-2012 Paul B Mahol
4  *
5  * This file is part of Libav.
6  *
7  * Libav 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  * Libav 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 Libav; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20  */
21 
22 #include "libavutil/intreadwrite.h"
23 #include "libavutil/imgutils.h"
24 #include "avcodec.h"
25 #include "get_bits.h"
26 #include "internal.h"
27 
28 #define BIT_PLANAR 0x00
29 #define BYTE_PLANAR 0x20
30 #define CHUNKY 0x40
31 #define BIT_LINE 0x80
32 #define BYTE_LINE 0xC0
33 
34 typedef struct {
37  int bpp;
38  int format;
40  const uint8_t *palette;
42  const uint8_t *video;
47 
49 {
50  CDXLVideoContext *c = avctx->priv_data;
51 
53  c->new_video_size = 0;
54  c->avctx = avctx;
55 
56  return 0;
57 }
58 
59 static void import_palette(CDXLVideoContext *c, uint32_t *new_palette)
60 {
61  int i;
62 
63  for (i = 0; i < c->palette_size / 2; i++) {
64  unsigned rgb = AV_RB16(&c->palette[i * 2]);
65  unsigned r = ((rgb >> 8) & 0xF) * 0x11;
66  unsigned g = ((rgb >> 4) & 0xF) * 0x11;
67  unsigned b = (rgb & 0xF) * 0x11;
68  AV_WN32(&new_palette[i], (r << 16) | (g << 8) | b);
69  }
70 }
71 
72 static void bitplanar2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
73 {
74  GetBitContext gb;
75  int x, y, plane;
76 
77  init_get_bits(&gb, c->video, c->video_size * 8);
78  for (plane = 0; plane < c->bpp; plane++) {
79  for (y = 0; y < c->avctx->height; y++) {
80  for (x = 0; x < c->avctx->width; x++)
81  out[linesize * y + x] |= get_bits1(&gb) << plane;
82  skip_bits(&gb, c->padded_bits);
83  }
84  }
85 }
86 
87 static void bitline2chunky(CDXLVideoContext *c, int linesize, uint8_t *out)
88 {
89  GetBitContext gb;
90  int x, y, plane;
91 
92  init_get_bits(&gb, c->video, c->video_size * 8);
93  for (y = 0; y < c->avctx->height; y++) {
94  for (plane = 0; plane < c->bpp; plane++) {
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 
102 static void import_format(CDXLVideoContext *c, int linesize, uint8_t *out)
103 {
104  memset(out, 0, linesize * c->avctx->height);
105 
106  switch (c->format) {
107  case BIT_PLANAR:
108  bitplanar2chunky(c, linesize, out);
109  break;
110  case BIT_LINE:
111  bitline2chunky(c, linesize, out);
112  break;
113  }
114 }
115 
117 {
118  uint32_t *new_palette = (uint32_t *)c->frame.data[1];
119 
120  import_palette(c, new_palette);
121  import_format(c, c->frame.linesize[0], c->frame.data[0]);
122 }
123 
125 {
126  AVCodecContext *avctx = c->avctx;
127  uint32_t new_palette[16], r, g, b;
128  uint8_t *ptr, *out, index, op;
129  int x, y;
130 
131  ptr = c->new_video;
132  out = c->frame.data[0];
133 
134  import_palette(c, new_palette);
135  import_format(c, avctx->width, c->new_video);
136 
137  for (y = 0; y < avctx->height; y++) {
138  r = new_palette[0] & 0xFF0000;
139  g = new_palette[0] & 0xFF00;
140  b = new_palette[0] & 0xFF;
141  for (x = 0; x < avctx->width; x++) {
142  index = *ptr++;
143  op = index >> 4;
144  index &= 15;
145  switch (op) {
146  case 0:
147  r = new_palette[index] & 0xFF0000;
148  g = new_palette[index] & 0xFF00;
149  b = new_palette[index] & 0xFF;
150  break;
151  case 1:
152  b = index * 0x11;
153  break;
154  case 2:
155  r = index * 0x11 << 16;
156  break;
157  case 3:
158  g = index * 0x11 << 8;
159  break;
160  }
161  AV_WL24(out + x * 3, r | g | b);
162  }
163  out += c->frame.linesize[0];
164  }
165 }
166 
168 {
169  AVCodecContext *avctx = c->avctx;
170  uint32_t new_palette[64], r, g, b;
171  uint8_t *ptr, *out, index, op;
172  int x, y;
173 
174  ptr = c->new_video;
175  out = c->frame.data[0];
176 
177  import_palette(c, new_palette);
178  import_format(c, avctx->width, c->new_video);
179 
180  for (y = 0; y < avctx->height; y++) {
181  r = new_palette[0] & 0xFF0000;
182  g = new_palette[0] & 0xFF00;
183  b = new_palette[0] & 0xFF;
184  for (x = 0; x < avctx->width; x++) {
185  index = *ptr++;
186  op = index >> 6;
187  index &= 63;
188  switch (op) {
189  case 0:
190  r = new_palette[index] & 0xFF0000;
191  g = new_palette[index] & 0xFF00;
192  b = new_palette[index] & 0xFF;
193  break;
194  case 1:
195  b = (index << 2) | (b & 3);
196  break;
197  case 2:
198  r = (index << 18) | (r & (3 << 16));
199  break;
200  case 3:
201  g = (index << 10) | (g & (3 << 8));
202  break;
203  }
204  AV_WL24(out + x * 3, r | g | b);
205  }
206  out += c->frame.linesize[0];
207  }
208 }
209 
210 static int cdxl_decode_frame(AVCodecContext *avctx, void *data,
211  int *got_frame, AVPacket *pkt)
212 {
213  CDXLVideoContext *c = avctx->priv_data;
214  AVFrame * const p = &c->frame;
215  int ret, w, h, encoding, aligned_width, buf_size = pkt->size;
216  const uint8_t *buf = pkt->data;
217 
218  if (buf_size < 32)
219  return AVERROR_INVALIDDATA;
220  encoding = buf[1] & 7;
221  c->format = buf[1] & 0xE0;
222  w = AV_RB16(&buf[14]);
223  h = AV_RB16(&buf[16]);
224  c->bpp = buf[19];
225  c->palette_size = AV_RB16(&buf[20]);
226  c->palette = buf + 32;
227  c->video = c->palette + c->palette_size;
228  c->video_size = buf_size - c->palette_size - 32;
229 
230  if (c->palette_size > 512)
231  return AVERROR_INVALIDDATA;
232  if (buf_size < c->palette_size + 32)
233  return AVERROR_INVALIDDATA;
234  if (c->bpp < 1)
235  return AVERROR_INVALIDDATA;
236  if (c->format != BIT_PLANAR && c->format != BIT_LINE) {
237  av_log_ask_for_sample(avctx, "unsupported pixel format: 0x%0x\n", c->format);
238  return AVERROR_PATCHWELCOME;
239  }
240 
241  if ((ret = av_image_check_size(w, h, 0, avctx)) < 0)
242  return ret;
243  if (w != avctx->width || h != avctx->height)
244  avcodec_set_dimensions(avctx, w, h);
245 
246  aligned_width = FFALIGN(c->avctx->width, 16);
247  c->padded_bits = aligned_width - c->avctx->width;
248  if (c->video_size < aligned_width * avctx->height * c->bpp / 8)
249  return AVERROR_INVALIDDATA;
250  if (!encoding && c->palette_size && c->bpp <= 8) {
251  avctx->pix_fmt = AV_PIX_FMT_PAL8;
252  } else if (encoding == 1 && (c->bpp == 6 || c->bpp == 8)) {
253  if (c->palette_size != (1 << (c->bpp - 1)))
254  return AVERROR_INVALIDDATA;
255  avctx->pix_fmt = AV_PIX_FMT_BGR24;
256  } else {
257  av_log_ask_for_sample(avctx, "unsupported encoding %d and bpp %d\n",
258  encoding, c->bpp);
259  return AVERROR_PATCHWELCOME;
260  }
261 
262  if (p->data[0])
263  avctx->release_buffer(avctx, p);
264 
265  p->reference = 0;
266  if ((ret = ff_get_buffer(avctx, p)) < 0) {
267  av_log(avctx, AV_LOG_ERROR, "get_buffer() failed\n");
268  return ret;
269  }
271 
272  if (encoding) {
275  if (!c->new_video)
276  return AVERROR(ENOMEM);
277  if (c->bpp == 8)
278  cdxl_decode_ham8(c);
279  else
280  cdxl_decode_ham6(c);
281  } else {
282  cdxl_decode_rgb(c);
283  }
284  *got_frame = 1;
285  *(AVFrame*)data = c->frame;
286 
287  return buf_size;
288 }
289 
291 {
292  CDXLVideoContext *c = avctx->priv_data;
293 
294  av_free(c->new_video);
295  if (c->frame.data[0])
296  avctx->release_buffer(avctx, &c->frame);
297 
298  return 0;
299 }
300 
302  .name = "cdxl",
303  .type = AVMEDIA_TYPE_VIDEO,
304  .id = AV_CODEC_ID_CDXL,
305  .priv_data_size = sizeof(CDXLVideoContext),
309  .capabilities = CODEC_CAP_DR1,
310  .long_name = NULL_IF_CONFIG_SMALL("Commodore CDXL video"),
311 };