2 * Wing Commander/Xan Video Decoder
3 * Copyright (C) 2003 the ffmpeg project
5 * This file is part of FFmpeg.
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.
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.
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
24 * Xan video decoder for Wing Commander III computer game
25 * by Mario Brito (mbrito@student.dei.uc.pt)
26 * and Mike Melanson (melanson@pcisys.net)
28 * The xan_wc3 decoder outputs PAL8 data.
35 #include "libavutil/intreadwrite.h"
37 #include "bytestream.h"
38 #define ALT_BITSTREAM_READER_LE
40 // for av_memcpy_backptr
41 #include "libavutil/lzo.h"
43 #define RUNTIME_GAMMA 0
45 #define VGA__TAG MKTAG('V', 'G', 'A', ' ')
46 #define PALT_TAG MKTAG('P', 'A', 'L', 'T')
47 #define SHOT_TAG MKTAG('S', 'H', 'O', 'T')
48 #define PALETTE_COUNT 256
49 #define PALETTE_SIZE (PALETTE_COUNT * 3)
50 #define PALETTES_MAX 256
52 typedef struct XanContext {
54 AVCodecContext *avctx;
56 AVFrame current_frame;
58 const unsigned char *buf;
62 unsigned char *buffer1;
64 unsigned char *buffer2;
75 static av_cold int xan_decode_init(AVCodecContext *avctx)
77 XanContext *s = avctx->priv_data;
82 avctx->pix_fmt = PIX_FMT_PAL8;
84 s->buffer1_size = avctx->width * avctx->height;
85 s->buffer1 = av_malloc(s->buffer1_size);
87 return AVERROR(ENOMEM);
88 s->buffer2_size = avctx->width * avctx->height;
89 s->buffer2 = av_malloc(s->buffer2_size + 130);
91 av_freep(&s->buffer1);
92 return AVERROR(ENOMEM);
98 static int xan_huffman_decode(unsigned char *dest, const unsigned char *src,
101 unsigned char byte = *src++;
102 unsigned char ival = byte + 0x16;
103 const unsigned char * ptr = src + byte*2;
104 unsigned char val = ival;
105 unsigned char *dest_end = dest + dest_len;
108 init_get_bits(&gb, ptr, 0); // FIXME: no src size available
110 while ( val != 0x16 ) {
111 val = src[val - 0x17 + get_bits1(&gb) * byte];
114 if (dest >= dest_end)
125 * unpack simple compression
127 * @param dest destination buffer of dest_len, must be padded with at least 130 bytes
129 static void xan_unpack(unsigned char *dest, const unsigned char *src, int dest_len)
131 unsigned char opcode;
133 unsigned char *dest_end = dest + dest_len;
135 while (dest < dest_end) {
140 if ( (opcode & 0x80) == 0 ) {
144 back = ((opcode & 0x60) << 3) + *src++ + 1;
145 size2 = ((opcode & 0x1c) >> 2) + 3;
147 } else if ( (opcode & 0x40) == 0 ) {
151 back = (bytestream_get_be16(&src) & 0x3fff) + 1;
152 size2 = (opcode & 0x3f) + 4;
158 back = ((opcode & 0x10) << 12) + bytestream_get_be16(&src) + 1;
159 size2 = ((opcode & 0x0c) << 6) + *src++ + 5;
160 if (size + size2 > dest_end - dest)
163 memcpy(dest, src, size); dest += size; src += size;
164 av_memcpy_backptr(dest, back, size2);
167 int finish = opcode >= 0xfc;
168 size = finish ? opcode & 3 : ((opcode & 0x1f) << 2) + 4;
170 memcpy(dest, src, size); dest += size; src += size;
177 static inline void xan_wc3_output_pixel_run(XanContext *s,
178 const unsigned char *pixel_buffer, int x, int y, int pixel_count)
184 int width = s->avctx->width;
185 unsigned char *palette_plane;
187 palette_plane = s->current_frame.data[0];
188 stride = s->current_frame.linesize[0];
189 line_inc = stride - width;
190 index = y * stride + x;
192 while(pixel_count && (index < s->frame_size)) {
193 int count = FFMIN(pixel_count, width - current_x);
194 memcpy(palette_plane + index, pixel_buffer, count);
195 pixel_count -= count;
197 pixel_buffer += count;
200 if (current_x >= width) {
207 static inline void xan_wc3_copy_pixel_run(XanContext *s,
208 int x, int y, int pixel_count, int motion_x, int motion_y)
212 int curframe_index, prevframe_index;
213 int curframe_x, prevframe_x;
214 int width = s->avctx->width;
215 unsigned char *palette_plane, *prev_palette_plane;
217 palette_plane = s->current_frame.data[0];
218 prev_palette_plane = s->last_frame.data[0];
219 stride = s->current_frame.linesize[0];
220 line_inc = stride - width;
221 curframe_index = y * stride + x;
223 prevframe_index = (y + motion_y) * stride + x + motion_x;
224 prevframe_x = x + motion_x;
225 while(pixel_count && (curframe_index < s->frame_size)) {
226 int count = FFMIN3(pixel_count, width - curframe_x, width - prevframe_x);
228 memcpy(palette_plane + curframe_index, prev_palette_plane + prevframe_index, count);
229 pixel_count -= count;
230 curframe_index += count;
231 prevframe_index += count;
233 prevframe_x += count;
235 if (curframe_x >= width) {
236 curframe_index += line_inc;
240 if (prevframe_x >= width) {
241 prevframe_index += line_inc;
247 static void xan_wc3_decode_frame(XanContext *s) {
249 int width = s->avctx->width;
250 int height = s->avctx->height;
251 int total_pixels = width * height;
252 unsigned char opcode;
253 unsigned char flag = 0;
255 int motion_x, motion_y;
258 unsigned char *opcode_buffer = s->buffer1;
259 int opcode_buffer_size = s->buffer1_size;
260 const unsigned char *imagedata_buffer = s->buffer2;
262 /* pointers to segments inside the compressed chunk */
263 const unsigned char *huffman_segment;
264 const unsigned char *size_segment;
265 const unsigned char *vector_segment;
266 const unsigned char *imagedata_segment;
268 huffman_segment = s->buf + AV_RL16(&s->buf[0]);
269 size_segment = s->buf + AV_RL16(&s->buf[2]);
270 vector_segment = s->buf + AV_RL16(&s->buf[4]);
271 imagedata_segment = s->buf + AV_RL16(&s->buf[6]);
273 xan_huffman_decode(opcode_buffer, huffman_segment, opcode_buffer_size);
275 if (imagedata_segment[0] == 2)
276 xan_unpack(s->buffer2, &imagedata_segment[1], s->buffer2_size);
278 imagedata_buffer = &imagedata_segment[1];
280 /* use the decoded data segments to build the frame */
282 while (total_pixels) {
284 opcode = *opcode_buffer++;
311 size += (opcode - 10);
316 size = *size_segment++;
321 size = AV_RB16(&size_segment[0]);
327 size = AV_RB24(size_segment);
335 /* run of (size) pixels is unchanged from last frame */
336 xan_wc3_copy_pixel_run(s, x, y, size, 0, 0);
338 /* output a run of pixels from imagedata_buffer */
339 xan_wc3_output_pixel_run(s, imagedata_buffer, x, y, size);
340 imagedata_buffer += size;
343 /* run-based motion compensation from last frame */
344 motion_x = sign_extend(*vector_segment >> 4, 4);
345 motion_y = sign_extend(*vector_segment & 0xF, 4);
348 /* copy a run of pixels from the previous frame */
349 xan_wc3_copy_pixel_run(s, x, y, size, motion_x, motion_y);
354 /* coordinate accounting */
355 total_pixels -= size;
356 y += (x + size) / width;
357 x = (x + size) % width;
362 static inline unsigned mul(unsigned a, unsigned b)
364 return (a * b) >> 16;
367 static inline unsigned pow4(unsigned a)
369 unsigned square = mul(a, a);
370 return mul(square, square);
373 static inline unsigned pow5(unsigned a)
375 return mul(pow4(a), a);
378 static uint8_t gamma_corr(uint8_t in) {
379 unsigned lo, hi = 0xff40, target;
381 in = (in << 2) | (in >> 6);
382 /* equivalent float code:
385 return round(pow(in / 256.0, 0.8) * 256);
387 lo = target = in << 8;
389 unsigned mid = (lo + hi) >> 1;
390 unsigned pow = pow5(mid);
391 if (pow > target) hi = mid;
394 return (pow4((lo + hi) >> 1) + 0x80) >> 8;
398 * This is a gamma correction that xan3 applies to all palette entries.
400 * There is a peculiarity, namely that the values are clamped to 253 -
401 * it seems likely that this table was calculated by a buggy fixed-point
402 * implementation, the one above under RUNTIME_GAMMA behaves like this for
404 * The exponent value of 0.8 can be explained by this as well, since 0.8 = 4/5
405 * and thus pow(x, 0.8) is still easy to calculate.
406 * Also, the input values are first rotated to the left by 2.
408 static const uint8_t gamma_lookup[256] = {
409 0x00, 0x09, 0x10, 0x16, 0x1C, 0x21, 0x27, 0x2C,
410 0x31, 0x35, 0x3A, 0x3F, 0x43, 0x48, 0x4C, 0x50,
411 0x54, 0x59, 0x5D, 0x61, 0x65, 0x69, 0x6D, 0x71,
412 0x75, 0x79, 0x7D, 0x80, 0x84, 0x88, 0x8C, 0x8F,
413 0x93, 0x97, 0x9A, 0x9E, 0xA2, 0xA5, 0xA9, 0xAC,
414 0xB0, 0xB3, 0xB7, 0xBA, 0xBE, 0xC1, 0xC5, 0xC8,
415 0xCB, 0xCF, 0xD2, 0xD5, 0xD9, 0xDC, 0xDF, 0xE3,
416 0xE6, 0xE9, 0xED, 0xF0, 0xF3, 0xF6, 0xFA, 0xFD,
417 0x03, 0x0B, 0x12, 0x18, 0x1D, 0x23, 0x28, 0x2D,
418 0x32, 0x36, 0x3B, 0x40, 0x44, 0x49, 0x4D, 0x51,
419 0x56, 0x5A, 0x5E, 0x62, 0x66, 0x6A, 0x6E, 0x72,
420 0x76, 0x7A, 0x7D, 0x81, 0x85, 0x89, 0x8D, 0x90,
421 0x94, 0x98, 0x9B, 0x9F, 0xA2, 0xA6, 0xAA, 0xAD,
422 0xB1, 0xB4, 0xB8, 0xBB, 0xBF, 0xC2, 0xC5, 0xC9,
423 0xCC, 0xD0, 0xD3, 0xD6, 0xDA, 0xDD, 0xE0, 0xE4,
424 0xE7, 0xEA, 0xED, 0xF1, 0xF4, 0xF7, 0xFA, 0xFD,
425 0x05, 0x0D, 0x13, 0x19, 0x1F, 0x24, 0x29, 0x2E,
426 0x33, 0x38, 0x3C, 0x41, 0x45, 0x4A, 0x4E, 0x52,
427 0x57, 0x5B, 0x5F, 0x63, 0x67, 0x6B, 0x6F, 0x73,
428 0x77, 0x7B, 0x7E, 0x82, 0x86, 0x8A, 0x8D, 0x91,
429 0x95, 0x99, 0x9C, 0xA0, 0xA3, 0xA7, 0xAA, 0xAE,
430 0xB2, 0xB5, 0xB9, 0xBC, 0xBF, 0xC3, 0xC6, 0xCA,
431 0xCD, 0xD0, 0xD4, 0xD7, 0xDA, 0xDE, 0xE1, 0xE4,
432 0xE8, 0xEB, 0xEE, 0xF1, 0xF5, 0xF8, 0xFB, 0xFD,
433 0x07, 0x0E, 0x15, 0x1A, 0x20, 0x25, 0x2A, 0x2F,
434 0x34, 0x39, 0x3D, 0x42, 0x46, 0x4B, 0x4F, 0x53,
435 0x58, 0x5C, 0x60, 0x64, 0x68, 0x6C, 0x70, 0x74,
436 0x78, 0x7C, 0x7F, 0x83, 0x87, 0x8B, 0x8E, 0x92,
437 0x96, 0x99, 0x9D, 0xA1, 0xA4, 0xA8, 0xAB, 0xAF,
438 0xB2, 0xB6, 0xB9, 0xBD, 0xC0, 0xC4, 0xC7, 0xCB,
439 0xCE, 0xD1, 0xD5, 0xD8, 0xDB, 0xDF, 0xE2, 0xE5,
440 0xE9, 0xEC, 0xEF, 0xF2, 0xF6, 0xF9, 0xFC, 0xFD
444 static int xan_decode_frame(AVCodecContext *avctx,
445 void *data, int *data_size,
448 const uint8_t *buf = avpkt->data;
449 int ret, buf_size = avpkt->size;
450 XanContext *s = avctx->priv_data;
452 if (avctx->codec->id == CODEC_ID_XAN_WC3) {
453 const uint8_t *buf_end = buf + buf_size;
455 while (buf_end - buf > 8 && tag != VGA__TAG) {
460 tag = bytestream_get_le32(&buf);
461 size = bytestream_get_be32(&buf);
462 size = FFMIN(size, buf_end - buf);
465 if (size < PALETTE_SIZE)
466 return AVERROR_INVALIDDATA;
467 if (s->palettes_count >= PALETTES_MAX)
468 return AVERROR_INVALIDDATA;
469 tmpptr = av_realloc(s->palettes, (s->palettes_count + 1) * AVPALETTE_SIZE);
471 return AVERROR(ENOMEM);
472 s->palettes = tmpptr;
473 tmpptr += s->palettes_count * AVPALETTE_COUNT;
474 for (i = 0; i < PALETTE_COUNT; i++) {
476 int r = gamma_corr(*buf++);
477 int g = gamma_corr(*buf++);
478 int b = gamma_corr(*buf++);
480 int r = gamma_lookup[*buf++];
481 int g = gamma_lookup[*buf++];
482 int b = gamma_lookup[*buf++];
484 *tmpptr++ = (r << 16) | (g << 8) | b;
490 return AVERROR_INVALIDDATA;
491 new_pal = bytestream_get_le32(&buf);
492 if (new_pal < s->palettes_count) {
493 s->cur_palette = new_pal;
495 av_log(avctx, AV_LOG_ERROR, "Invalid palette selected\n");
504 buf_size = buf_end - buf;
506 if ((ret = avctx->get_buffer(avctx, &s->current_frame))) {
507 av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
510 s->current_frame.reference = 3;
513 s->frame_size = s->current_frame.linesize[0] * s->avctx->height;
515 memcpy(s->current_frame.data[1], s->palettes + s->cur_palette * AVPALETTE_COUNT, AVPALETTE_SIZE);
520 xan_wc3_decode_frame(s);
522 /* release the last frame if it is allocated */
523 if (s->last_frame.data[0])
524 avctx->release_buffer(avctx, &s->last_frame);
526 *data_size = sizeof(AVFrame);
527 *(AVFrame*)data = s->current_frame;
530 FFSWAP(AVFrame, s->current_frame, s->last_frame);
532 /* always report that the buffer was completely consumed */
536 static av_cold int xan_decode_end(AVCodecContext *avctx)
538 XanContext *s = avctx->priv_data;
540 /* release the frames */
541 if (s->last_frame.data[0])
542 avctx->release_buffer(avctx, &s->last_frame);
543 if (s->current_frame.data[0])
544 avctx->release_buffer(avctx, &s->current_frame);
546 av_freep(&s->buffer1);
547 av_freep(&s->buffer2);
548 av_freep(&s->palettes);
553 AVCodec ff_xan_wc3_decoder = {
563 .long_name = NULL_IF_CONFIG_SMALL("Wing Commander III / Xan"),