diff options
author | Mike Melanson | 2003-11-10 03:14:04 +0000 |
---|---|---|
committer | Mike Melanson | 2003-11-10 03:14:04 +0000 |
commit | 11e29a414359de4cde7c44bdce62cdf2b4363ab2 (patch) | |
tree | 94397922c0e25d97ec7cd2ddc62f9d980da22a19 /libavcodec/smc.c | |
parent | aa39a62205739a171787744784a20482b13ccbf7 (diff) |
added Apple Graphics (SMC) decoder
Originally committed as revision 2488 to svn://svn.ffmpeg.org/ffmpeg/trunk
Diffstat (limited to 'libavcodec/smc.c')
-rw-r--r-- | libavcodec/smc.c | 510 |
1 files changed, 510 insertions, 0 deletions
diff --git a/libavcodec/smc.c b/libavcodec/smc.c new file mode 100644 index 0000000000..a6864882e6 --- /dev/null +++ b/libavcodec/smc.c @@ -0,0 +1,510 @@ +/* + * Quicktime Graphics (SMC) Video Decoder + * Copyright (C) 2003 the ffmpeg project + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/** + * @file smc.c + * QT SMC Video Decoder by Mike Melanson (melanson@pcisys.net) + * For more information about the SMC format, visit: + * http://www.pcisys.net/~melanson/codecs/ + * + * The SMC decoder outputs PAL8 colorspace data. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "common.h" +#include "avcodec.h" +#include "dsputil.h" + +#define CPAIR 2 +#define CQUAD 4 +#define COCTET 8 + +#define COLORS_PER_TABLE 256 + +typedef struct SmcContext { + + AVCodecContext *avctx; + DSPContext dsp; + AVFrame frame; + AVFrame prev_frame; + + unsigned char *buf; + int size; + + /* SMC color tables */ + unsigned char color_pairs[COLORS_PER_TABLE * CPAIR]; + unsigned char color_quads[COLORS_PER_TABLE * CQUAD]; + unsigned char color_octets[COLORS_PER_TABLE * COCTET]; + +} SmcContext; + +#define BE_16(x) ((((uint8_t*)(x))[0] << 8) | ((uint8_t*)(x))[1]) +#define BE_32(x) ((((uint8_t*)(x))[0] << 24) | \ + (((uint8_t*)(x))[1] << 16) | \ + (((uint8_t*)(x))[2] << 8) | \ + ((uint8_t*)(x))[3]) +#define GET_BLOCK_COUNT() \ + (opcode & 0x10) ? (1 + s->buf[stream_ptr++]) : 1 + (opcode & 0x0F); + +#define ADVANCE_BLOCK() \ +{ \ + pixel_ptr += 4; \ + if (pixel_ptr >= width) \ + { \ + pixel_ptr = 0; \ + row_ptr += stride * 4; \ + } \ + total_blocks--; \ + if (total_blocks < 0) \ + { \ + printf("warning: block counter just went negative (this should not happen)\n"); \ + return; \ + } \ +} + +static void smc_decode_stream(SmcContext *s) +{ + int width = s->avctx->width; + int height = s->avctx->height; + int stride = s->frame.linesize[0]; + int i; + int stream_ptr = 0; + int chunk_size; + unsigned char opcode; + int n_blocks; + unsigned int color_flags; + unsigned int color_flags_a; + unsigned int color_flags_b; + unsigned int flag_mask; + + unsigned char *pixels = s->frame.data[0]; + unsigned char *prev_pixels = s->prev_frame.data[0]; + + int image_size = height * s->frame.linesize[0]; + int row_ptr = 0; + int pixel_ptr = 0; + int pixel_x, pixel_y; + int row_inc = stride - 4; + int block_ptr; + int prev_block_ptr; + int prev_block_ptr1, prev_block_ptr2; + int prev_block_flag; + int total_blocks; + int color_table_index; /* indexes to color pair, quad, or octet tables */ + int pixel; + + int color_pair_index = 0; + int color_quad_index = 0; + int color_octet_index = 0; + + /* make the palette available */ + memcpy(s->frame.data[1], s->avctx->palctrl->palette, AVPALETTE_SIZE); + if (s->avctx->palctrl->palette_changed) { + s->frame.palette_has_changed = 1; + s->avctx->palctrl->palette_changed = 0; + } + + chunk_size = BE_32(&s->buf[stream_ptr]) & 0x00FFFFFF; + stream_ptr += 4; + if (chunk_size != s->size) + printf("warning: MOV chunk size != encoded chunk size (%d != %d); using MOV chunk size\n", + chunk_size, s->size); + + chunk_size = s->size; + total_blocks = (s->avctx->width * s->avctx->height) / (4 * 4); + + /* traverse through the blocks */ + while (total_blocks) { + /* sanity checks */ + /* make sure stream ptr hasn't gone out of bounds */ + if (stream_ptr > chunk_size) { + printf("SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)\n", + stream_ptr, chunk_size); + return; + } + /* make sure the row pointer hasn't gone wild */ + if (row_ptr >= image_size) { + printf("SMC decoder just went out of bounds (row ptr = %d, height = %d)\n", + row_ptr, image_size); + return; + } + + opcode = s->buf[stream_ptr++]; + switch (opcode & 0xF0) { + /* skip n blocks */ + case 0x00: + case 0x10: + n_blocks = GET_BLOCK_COUNT(); + while (n_blocks--) { + block_ptr = row_ptr + pixel_ptr; + for (pixel_y = 0; pixel_y < 4; pixel_y++) { + for (pixel_x = 0; pixel_x < 4; pixel_x++) { + pixels[block_ptr] = prev_pixels[block_ptr]; + block_ptr++; + } + block_ptr += row_inc; + } + ADVANCE_BLOCK(); + } + break; + + /* repeat last block n times */ + case 0x20: + case 0x30: + n_blocks = GET_BLOCK_COUNT(); + + /* sanity check */ + if ((row_ptr == 0) && (pixel_ptr == 0)) { + printf("encountered repeat block opcode (%02X) but no blocks rendered yet\n", + opcode & 0xF0); + break; + } + + /* figure out where the previous block started */ + if (pixel_ptr == 0) + prev_block_ptr1 = + (row_ptr - s->avctx->width * 4) + s->avctx->width - 4; + else + prev_block_ptr1 = row_ptr + pixel_ptr - 4; + + while (n_blocks--) { + block_ptr = row_ptr + pixel_ptr; + prev_block_ptr = prev_block_ptr1; + for (pixel_y = 0; pixel_y < 4; pixel_y++) { + for (pixel_x = 0; pixel_x < 4; pixel_x++) { + pixels[block_ptr++] = pixels[prev_block_ptr++]; + } + block_ptr += row_inc; + prev_block_ptr += row_inc; + } + ADVANCE_BLOCK(); + } + break; + + /* repeat previous pair of blocks n times */ + case 0x40: + case 0x50: + n_blocks = GET_BLOCK_COUNT(); + n_blocks *= 2; + + /* sanity check */ + if ((row_ptr == 0) && (pixel_ptr < 2 * 4)) { + printf("encountered repeat block opcode (%02X) but not enough blocks rendered yet\n", + opcode & 0xF0); + break; + } + + /* figure out where the previous 2 blocks started */ + if (pixel_ptr == 0) + prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + + s->avctx->width - 4 * 2; + else if (pixel_ptr == 4) + prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + row_inc; + else + prev_block_ptr1 = row_ptr + pixel_ptr - 4 * 2; + + if (pixel_ptr == 0) + prev_block_ptr2 = (row_ptr - s->avctx->width * 4) + row_inc; + else + prev_block_ptr2 = row_ptr + pixel_ptr - 4; + + prev_block_flag = 0; + while (n_blocks--) { + block_ptr = row_ptr + pixel_ptr; + if (prev_block_flag) + prev_block_ptr = prev_block_ptr2; + else + prev_block_ptr = prev_block_ptr1; + prev_block_flag = !prev_block_flag; + + for (pixel_y = 0; pixel_y < 4; pixel_y++) { + for (pixel_x = 0; pixel_x < 4; pixel_x++) { + pixels[block_ptr++] = pixels[prev_block_ptr++]; + } + block_ptr += row_inc; + prev_block_ptr += row_inc; + } + ADVANCE_BLOCK(); + } + break; + + /* 1-color block encoding */ + case 0x60: + case 0x70: + n_blocks = GET_BLOCK_COUNT(); + pixel = s->buf[stream_ptr++]; + + while (n_blocks--) { + block_ptr = row_ptr + pixel_ptr; + for (pixel_y = 0; pixel_y < 4; pixel_y++) { + for (pixel_x = 0; pixel_x < 4; pixel_x++) { + pixels[block_ptr++] = pixel; + } + block_ptr += row_inc; + } + ADVANCE_BLOCK(); + } + break; + + /* 2-color block encoding */ + case 0x80: + case 0x90: + n_blocks = (opcode & 0x0F) + 1; + + /* figure out which color pair to use to paint the 2-color block */ + if ((opcode & 0xF0) == 0x80) { + /* fetch the next 2 colors from bytestream and store in next + * available entry in the color pair table */ + for (i = 0; i < CPAIR; i++) { + pixel = s->buf[stream_ptr++]; + color_table_index = CPAIR * color_pair_index + i; + s->color_pairs[color_table_index] = pixel; + } + /* this is the base index to use for this block */ + color_table_index = CPAIR * color_pair_index; + color_pair_index++; + /* wraparound */ + if (color_pair_index == COLORS_PER_TABLE) + color_pair_index = 0; + } else + color_table_index = CPAIR * s->buf[stream_ptr++]; + + while (n_blocks--) { + color_flags = BE_16(&s->buf[stream_ptr]); + stream_ptr += 2; + flag_mask = 0x8000; + block_ptr = row_ptr + pixel_ptr; + for (pixel_y = 0; pixel_y < 4; pixel_y++) { + for (pixel_x = 0; pixel_x < 4; pixel_x++) { + if (color_flags & flag_mask) + pixel = color_table_index + 1; + else + pixel = color_table_index; + flag_mask >>= 1; + pixels[block_ptr++] = s->color_pairs[pixel]; + } + block_ptr += row_inc; + } + ADVANCE_BLOCK(); + } + break; + + /* 4-color block encoding */ + case 0xA0: + case 0xB0: + n_blocks = (opcode & 0x0F) + 1; + + /* figure out which color quad to use to paint the 4-color block */ + if ((opcode & 0xF0) == 0xA0) { + /* fetch the next 4 colors from bytestream and store in next + * available entry in the color quad table */ + for (i = 0; i < CQUAD; i++) { + pixel = s->buf[stream_ptr++]; + color_table_index = CQUAD * color_quad_index + i; + s->color_quads[color_table_index] = pixel; + } + /* this is the base index to use for this block */ + color_table_index = CQUAD * color_quad_index; + color_quad_index++; + /* wraparound */ + if (color_quad_index == COLORS_PER_TABLE) + color_quad_index = 0; + } else + color_table_index = CQUAD * s->buf[stream_ptr++]; + + while (n_blocks--) { + color_flags = BE_32(&s->buf[stream_ptr]); + stream_ptr += 4; + /* flag mask actually acts as a bit shift count here */ + flag_mask = 30; + block_ptr = row_ptr + pixel_ptr; + for (pixel_y = 0; pixel_y < 4; pixel_y++) { + for (pixel_x = 0; pixel_x < 4; pixel_x++) { + pixel = color_table_index + + ((color_flags >> flag_mask) & 0x03); + flag_mask -= 2; + pixels[block_ptr++] = s->color_quads[pixel]; + } + block_ptr += row_inc; + } + ADVANCE_BLOCK(); + } + break; + + /* 8-color block encoding */ + case 0xC0: + case 0xD0: + n_blocks = (opcode & 0x0F) + 1; + + /* figure out which color octet to use to paint the 8-color block */ + if ((opcode & 0xF0) == 0xC0) { + /* fetch the next 8 colors from bytestream and store in next + * available entry in the color octet table */ + for (i = 0; i < COCTET; i++) { + pixel = s->buf[stream_ptr++]; + color_table_index = COCTET * color_octet_index + i; + s->color_octets[color_table_index] = pixel; + } + /* this is the base index to use for this block */ + color_table_index = COCTET * color_octet_index; + color_octet_index++; + /* wraparound */ + if (color_octet_index == COLORS_PER_TABLE) + color_octet_index = 0; + } else + color_table_index = COCTET * s->buf[stream_ptr++]; + + while (n_blocks--) { + /* + For this input of 6 hex bytes: + 01 23 45 67 89 AB + Mangle it to this output: + flags_a = xx012456, flags_b = xx89A37B + */ + /* build the color flags */ + color_flags_a = color_flags_b = 0; + color_flags_a = + (s->buf[stream_ptr + 0] << 16) | + ((s->buf[stream_ptr + 1] & 0xF0) << 8) | + ((s->buf[stream_ptr + 2] & 0xF0) << 4) | + ((s->buf[stream_ptr + 2] & 0x0F) << 4) | + ((s->buf[stream_ptr + 3] & 0xF0) >> 4); + color_flags_b = + (s->buf[stream_ptr + 4] << 16) | + ((s->buf[stream_ptr + 5] & 0xF0) << 8) | + ((s->buf[stream_ptr + 1] & 0x0F) << 8) | + ((s->buf[stream_ptr + 3] & 0x0F) << 4) | + (s->buf[stream_ptr + 5] & 0x0F); + stream_ptr += 6; + + color_flags = color_flags_a; + /* flag mask actually acts as a bit shift count here */ + flag_mask = 21; + block_ptr = row_ptr + pixel_ptr; + for (pixel_y = 0; pixel_y < 4; pixel_y++) { + /* reload flags at third row (iteration pixel_y == 2) */ + if (pixel_y == 2) { + color_flags = color_flags_b; + flag_mask = 21; + } + for (pixel_x = 0; pixel_x < 4; pixel_x++) { + pixel = color_table_index + + ((color_flags >> flag_mask) & 0x07); + flag_mask -= 3; + pixels[block_ptr++] = s->color_octets[pixel]; + } + block_ptr += row_inc; + } + ADVANCE_BLOCK(); + } + break; + + /* 16-color block encoding (every pixel is a different color) */ + case 0xE0: + n_blocks = (opcode & 0x0F) + 1; + + while (n_blocks--) { + block_ptr = row_ptr + pixel_ptr; + for (pixel_y = 0; pixel_y < 4; pixel_y++) { + for (pixel_x = 0; pixel_x < 4; pixel_x++) { + pixels[block_ptr++] = s->buf[stream_ptr++]; + } + block_ptr += row_inc; + } + ADVANCE_BLOCK(); + } + break; + + case 0xF0: + printf("0xF0 opcode seen in SMC chunk (xine developers would like to know)\n"); + break; + } + } +} + +static int smc_decode_init(AVCodecContext *avctx) +{ + SmcContext *s = (SmcContext *)avctx->priv_data; + + s->avctx = avctx; + avctx->pix_fmt = PIX_FMT_PAL8; + avctx->has_b_frames = 0; + dsputil_init(&s->dsp, avctx); + + s->frame.data[0] = s->prev_frame.data[0] = NULL; + + return 0; +} + +static int smc_decode_frame(AVCodecContext *avctx, + void *data, int *data_size, + uint8_t *buf, int buf_size) +{ + SmcContext *s = (SmcContext *)avctx->priv_data; + + s->buf = buf; + s->size = buf_size; + + s->frame.reference = 1; + if (avctx->get_buffer(avctx, &s->frame)) { + printf (" smc Video: get_buffer() failed\n"); + return -1; + } + + smc_decode_stream(s); + + if (s->prev_frame.data[0]) + avctx->release_buffer(avctx, &s->prev_frame); + + /* shuffle frames */ + s->prev_frame = s->frame; + + *data_size = sizeof(AVFrame); + *(AVFrame*)data = s->frame; + + /* always report that the buffer was completely consumed */ + return buf_size; +} + +static int smc_decode_end(AVCodecContext *avctx) +{ + SmcContext *s = (SmcContext *)avctx->priv_data; + + if (s->prev_frame.data[0]) + avctx->release_buffer(avctx, &s->prev_frame); + + return 0; +} + +AVCodec smc_decoder = { + "smc", + CODEC_TYPE_VIDEO, + CODEC_ID_SMC, + sizeof(SmcContext), + smc_decode_init, + NULL, + smc_decode_end, + smc_decode_frame, + CODEC_CAP_DR1, +}; |