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.
38 typedef struct XanContext
{
40 AVCodecContext
*avctx
;
42 AVFrame current_frame
;
44 const unsigned char *buf
;
48 unsigned char *buffer1
;
50 unsigned char *buffer2
;
57 static av_cold
int xan_decode_init(AVCodecContext
*avctx
)
59 XanContext
*s
= avctx
->priv_data
;
64 if ((avctx
->codec
->id
== CODEC_ID_XAN_WC3
) &&
65 (s
->avctx
->palctrl
== NULL
)) {
66 av_log(avctx
, AV_LOG_ERROR
, " WC3 Xan video: palette expected.\n");
70 avctx
->pix_fmt
= PIX_FMT_PAL8
;
72 if(avcodec_check_dimensions(avctx
, avctx
->width
, avctx
->height
))
75 s
->buffer1_size
= avctx
->width
* avctx
->height
;
76 s
->buffer1
= av_malloc(s
->buffer1_size
);
77 s
->buffer2_size
= avctx
->width
* avctx
->height
;
78 s
->buffer2
= av_malloc(s
->buffer2_size
);
79 if (!s
->buffer1
|| !s
->buffer2
)
85 /* This function is used in lieu of memcpy(). This decoder cannot use
86 * memcpy because the memory locations often overlap and
87 * memcpy doesn't like that; it's not uncommon, for example, for
88 * dest = src+1, to turn byte A into pattern AAAAAAAA.
89 * This was originally repz movsb in Intel x86 ASM. */
90 static inline void bytecopy(unsigned char *dest
, const unsigned char *src
, int count
)
94 for (i
= 0; i
< count
; i
++)
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
;
106 unsigned char *dest_end
= dest
+ dest_len
;
108 unsigned char bits
= *ptr
++;
110 while ( val
!= 0x16 ) {
111 if ( (1 << counter
) & bits
)
112 val
= src
[byte
+ val
- 0x17];
114 val
= src
[val
- 0x17];
117 if (dest
+ 1 > dest_end
)
123 if (counter
++ == 7) {
132 static void xan_unpack(unsigned char *dest
, const unsigned char *src
, int dest_len
)
134 unsigned char opcode
;
137 int byte1
, byte2
, byte3
;
138 unsigned char *dest_end
= dest
+ dest_len
;
143 if ( (opcode
& 0x80) == 0 ) {
148 if (dest
+ size
> dest_end
)
150 bytecopy(dest
, src
, size
); dest
+= size
; src
+= size
;
152 size
= ((opcode
& 0x1c) >> 2) + 3;
153 if (dest
+ size
> dest_end
)
155 bytecopy (dest
, dest
- (((opcode
& 0x60) << 3) + offset
+ 1), size
);
158 } else if ( (opcode
& 0x40) == 0 ) {
164 if (dest
+ size
> dest_end
)
166 bytecopy (dest
, src
, size
); dest
+= size
; src
+= size
;
168 size
= (opcode
& 0x3f) + 4;
169 if (dest
+ size
> dest_end
)
171 bytecopy (dest
, dest
- (((byte1
& 0x3f) << 8) + byte2
+ 1), size
);
174 } else if ( (opcode
& 0x20) == 0 ) {
181 if (dest
+ size
> dest_end
)
183 bytecopy (dest
, src
, size
); dest
+= size
; src
+= size
;
185 size
= byte3
+ 5 + ((opcode
& 0xc) << 6);
186 if (dest
+ size
> dest_end
)
189 dest
- ((((opcode
& 0x10) >> 4) << 0x10) + 1 + (byte1
<< 8) + byte2
),
193 size
= ((opcode
& 0x1f) << 2) + 4;
198 if (dest
+ size
> dest_end
)
200 bytecopy (dest
, src
, size
); dest
+= size
; src
+= size
;
205 bytecopy(dest
, src
, size
); dest
+= size
; src
+= size
;
208 static inline void xan_wc3_output_pixel_run(XanContext
*s
,
209 const unsigned char *pixel_buffer
, int x
, int y
, int pixel_count
)
215 int width
= s
->avctx
->width
;
216 unsigned char *palette_plane
;
218 palette_plane
= s
->current_frame
.data
[0];
219 stride
= s
->current_frame
.linesize
[0];
220 line_inc
= stride
- width
;
221 index
= y
* stride
+ x
;
223 while((pixel_count
--) && (index
< s
->frame_size
)) {
225 /* don't do a memcpy() here; keyframes generally copy an entire
226 * frame of data and the stride needs to be accounted for */
227 palette_plane
[index
++] = *pixel_buffer
++;
230 if (current_x
>= width
) {
237 static inline void xan_wc3_copy_pixel_run(XanContext
*s
,
238 int x
, int y
, int pixel_count
, int motion_x
, int motion_y
)
242 int curframe_index
, prevframe_index
;
243 int curframe_x
, prevframe_x
;
244 int width
= s
->avctx
->width
;
245 unsigned char *palette_plane
, *prev_palette_plane
;
247 palette_plane
= s
->current_frame
.data
[0];
248 prev_palette_plane
= s
->last_frame
.data
[0];
249 stride
= s
->current_frame
.linesize
[0];
250 line_inc
= stride
- width
;
251 curframe_index
= y
* stride
+ x
;
253 prevframe_index
= (y
+ motion_y
) * stride
+ x
+ motion_x
;
254 prevframe_x
= x
+ motion_x
;
255 while((pixel_count
--) && (curframe_index
< s
->frame_size
)) {
257 palette_plane
[curframe_index
++] =
258 prev_palette_plane
[prevframe_index
++];
261 if (curframe_x
>= width
) {
262 curframe_index
+= line_inc
;
267 if (prevframe_x
>= width
) {
268 prevframe_index
+= line_inc
;
274 static void xan_wc3_decode_frame(XanContext
*s
) {
276 int width
= s
->avctx
->width
;
277 int height
= s
->avctx
->height
;
278 int total_pixels
= width
* height
;
279 unsigned char opcode
;
280 unsigned char flag
= 0;
282 int motion_x
, motion_y
;
285 unsigned char *opcode_buffer
= s
->buffer1
;
286 int opcode_buffer_size
= s
->buffer1_size
;
287 const unsigned char *imagedata_buffer
= s
->buffer2
;
289 /* pointers to segments inside the compressed chunk */
290 const unsigned char *huffman_segment
;
291 const unsigned char *size_segment
;
292 const unsigned char *vector_segment
;
293 const unsigned char *imagedata_segment
;
295 huffman_segment
= s
->buf
+ AV_RL16(&s
->buf
[0]);
296 size_segment
= s
->buf
+ AV_RL16(&s
->buf
[2]);
297 vector_segment
= s
->buf
+ AV_RL16(&s
->buf
[4]);
298 imagedata_segment
= s
->buf
+ AV_RL16(&s
->buf
[6]);
300 xan_huffman_decode(opcode_buffer
, huffman_segment
, opcode_buffer_size
);
302 if (imagedata_segment
[0] == 2)
303 xan_unpack(s
->buffer2
, &imagedata_segment
[1], s
->buffer2_size
);
305 imagedata_buffer
= &imagedata_segment
[1];
307 /* use the decoded data segments to build the frame */
309 while (total_pixels
) {
311 opcode
= *opcode_buffer
++;
338 size
+= (opcode
- 10);
343 size
= *size_segment
++;
348 size
= AV_RB16(&size_segment
[0]);
354 size
= AV_RB24(size_segment
);
362 /* run of (size) pixels is unchanged from last frame */
363 xan_wc3_copy_pixel_run(s
, x
, y
, size
, 0, 0);
365 /* output a run of pixels from imagedata_buffer */
366 xan_wc3_output_pixel_run(s
, imagedata_buffer
, x
, y
, size
);
367 imagedata_buffer
+= size
;
370 /* run-based motion compensation from last frame */
371 motion_x
= (*vector_segment
>> 4) & 0xF;
372 motion_y
= *vector_segment
& 0xF;
377 motion_x
|= 0xFFFFFFF0;
379 motion_y
|= 0xFFFFFFF0;
381 /* copy a run of pixels from the previous frame */
382 xan_wc3_copy_pixel_run(s
, x
, y
, size
, motion_x
, motion_y
);
387 /* coordinate accounting */
388 total_pixels
-= size
;
390 if (x
+ size
>= width
) {
402 static void xan_wc4_decode_frame(XanContext
*s
) {
405 static int xan_decode_frame(AVCodecContext
*avctx
,
406 void *data
, int *data_size
,
407 const uint8_t *buf
, int buf_size
)
409 XanContext
*s
= avctx
->priv_data
;
410 AVPaletteControl
*palette_control
= avctx
->palctrl
;
412 if (avctx
->get_buffer(avctx
, &s
->current_frame
)) {
413 av_log(s
->avctx
, AV_LOG_ERROR
, " Xan Video: get_buffer() failed\n");
416 s
->current_frame
.reference
= 3;
419 s
->frame_size
= s
->current_frame
.linesize
[0] * s
->avctx
->height
;
421 palette_control
->palette_changed
= 0;
422 memcpy(s
->current_frame
.data
[1], palette_control
->palette
,
424 s
->current_frame
.palette_has_changed
= 1;
429 if (avctx
->codec
->id
== CODEC_ID_XAN_WC3
)
430 xan_wc3_decode_frame(s
);
431 else if (avctx
->codec
->id
== CODEC_ID_XAN_WC4
)
432 xan_wc4_decode_frame(s
);
434 /* release the last frame if it is allocated */
435 if (s
->last_frame
.data
[0])
436 avctx
->release_buffer(avctx
, &s
->last_frame
);
438 *data_size
= sizeof(AVFrame
);
439 *(AVFrame
*)data
= s
->current_frame
;
442 FFSWAP(AVFrame
, s
->current_frame
, s
->last_frame
);
444 /* always report that the buffer was completely consumed */
448 static av_cold
int xan_decode_end(AVCodecContext
*avctx
)
450 XanContext
*s
= avctx
->priv_data
;
452 /* release the frames */
453 if (s
->last_frame
.data
[0])
454 avctx
->release_buffer(avctx
, &s
->last_frame
);
455 if (s
->current_frame
.data
[0])
456 avctx
->release_buffer(avctx
, &s
->current_frame
);
464 AVCodec xan_wc3_decoder
= {
474 .long_name
= NULL_IF_CONFIG_SMALL("Wing Commander III / Xan"),
478 AVCodec xan_wc4_decoder = {