diff options
| author | Tim Redfern <tim@eclectronics.org> | 2013-08-26 15:10:18 +0100 |
|---|---|---|
| committer | Tim Redfern <tim@eclectronics.org> | 2013-08-26 15:10:18 +0100 |
| commit | 150c9823e71a161e97003849cf8b2f55b21520bd (patch) | |
| tree | 3559c840cf403d1386708b2591d58f928c7b160d /ffmpeg1/libavcodec/smc.c | |
| parent | b4b1e2630c95d5e6014463f7608d59dc2322a3b8 (diff) | |
adding ffmpeg specific version
Diffstat (limited to 'ffmpeg1/libavcodec/smc.c')
| -rw-r--r-- | ffmpeg1/libavcodec/smc.c | 474 |
1 files changed, 474 insertions, 0 deletions
diff --git a/ffmpeg1/libavcodec/smc.c b/ffmpeg1/libavcodec/smc.c new file mode 100644 index 0000000..2c7ab9a --- /dev/null +++ b/ffmpeg1/libavcodec/smc.c @@ -0,0 +1,474 @@ +/* + * Quicktime Graphics (SMC) Video Decoder + * Copyright (C) 2003 the ffmpeg project + * + * This file is part of FFmpeg. + * + * FFmpeg 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.1 of the License, or (at your option) any later version. + * + * FFmpeg 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 FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * 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 "libavutil/intreadwrite.h" +#include "avcodec.h" +#include "bytestream.h" +#include "internal.h" + +#define CPAIR 2 +#define CQUAD 4 +#define COCTET 8 + +#define COLORS_PER_TABLE 256 + +typedef struct SmcContext { + + AVCodecContext *avctx; + AVFrame frame; + + GetByteContext gb; + + /* 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]; + + uint32_t pal[256]; +} SmcContext; + +#define GET_BLOCK_COUNT() \ + (opcode & 0x10) ? (1 + bytestream2_get_byte(&s->gb)) : 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) \ + { \ + av_log(s->avctx, AV_LOG_INFO, "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 chunk_size; + int buf_size = bytestream2_size(&s->gb); + 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]; + + 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->pal, AVPALETTE_SIZE); + + bytestream2_skip(&s->gb, 1); + chunk_size = bytestream2_get_be24(&s->gb); + if (chunk_size != buf_size) + av_log(s->avctx, AV_LOG_INFO, "warning: MOV chunk size != encoded chunk size (%d != %d); using MOV chunk size\n", + chunk_size, buf_size); + + chunk_size = buf_size; + total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4); + + /* traverse through the blocks */ + while (total_blocks) { + /* sanity checks */ + /* make sure the row pointer hasn't gone wild */ + if (row_ptr >= image_size) { + av_log(s->avctx, AV_LOG_INFO, "SMC decoder just went out of bounds (row ptr = %d, height = %d)\n", + row_ptr, image_size); + return; + } + + opcode = bytestream2_get_byte(&s->gb); + switch (opcode & 0xF0) { + /* skip n blocks */ + case 0x00: + case 0x10: + n_blocks = GET_BLOCK_COUNT(); + while (n_blocks--) { + 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)) { + av_log(s->avctx, AV_LOG_INFO, "encountered repeat block opcode (%02X) but no blocks rendered yet\n", + opcode & 0xF0); + return; + } + + /* 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)) { + av_log(s->avctx, AV_LOG_INFO, "encountered repeat block opcode (%02X) but not enough blocks rendered yet\n", + opcode & 0xF0); + return; + } + + /* 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 = bytestream2_get_byte(&s->gb); + + 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 = bytestream2_get_byte(&s->gb); + 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 * bytestream2_get_byte(&s->gb); + + while (n_blocks--) { + color_flags = bytestream2_get_be16(&s->gb); + 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 = bytestream2_get_byte(&s->gb); + 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 * bytestream2_get_byte(&s->gb); + + while (n_blocks--) { + color_flags = bytestream2_get_be32(&s->gb); + /* 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 = bytestream2_get_byte(&s->gb); + 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 * bytestream2_get_byte(&s->gb); + + 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 */ + int val1 = bytestream2_get_be16(&s->gb); + int val2 = bytestream2_get_be16(&s->gb); + int val3 = bytestream2_get_be16(&s->gb); + color_flags_a = ((val1 & 0xFFF0) << 8) | (val2 >> 4); + color_flags_b = ((val3 & 0xFFF0) << 8) | + ((val1 & 0x0F) << 8) | ((val2 & 0x0F) << 4) | (val3 & 0x0F); + + 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++] = bytestream2_get_byte(&s->gb); + } + block_ptr += row_inc; + } + ADVANCE_BLOCK(); + } + break; + + case 0xF0: + avpriv_request_sample(s->avctx, "0xF0 opcode"); + break; + } + } + + return; +} + +static av_cold int smc_decode_init(AVCodecContext *avctx) +{ + SmcContext *s = avctx->priv_data; + + s->avctx = avctx; + avctx->pix_fmt = AV_PIX_FMT_PAL8; + + avcodec_get_frame_defaults(&s->frame); + + return 0; +} + +static int smc_decode_frame(AVCodecContext *avctx, + void *data, int *got_frame, + AVPacket *avpkt) +{ + const uint8_t *buf = avpkt->data; + int buf_size = avpkt->size; + SmcContext *s = avctx->priv_data; + const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, NULL); + int ret; + + bytestream2_init(&s->gb, buf, buf_size); + + if ((ret = ff_reget_buffer(avctx, &s->frame)) < 0) + return ret; + + if (pal) { + s->frame.palette_has_changed = 1; + memcpy(s->pal, pal, AVPALETTE_SIZE); + } + + smc_decode_stream(s); + + *got_frame = 1; + if ((ret = av_frame_ref(data, &s->frame)) < 0) + return ret; + + /* always report that the buffer was completely consumed */ + return buf_size; +} + +static av_cold int smc_decode_end(AVCodecContext *avctx) +{ + SmcContext *s = avctx->priv_data; + + av_frame_unref(&s->frame); + + return 0; +} + +AVCodec ff_smc_decoder = { + .name = "smc", + .type = AVMEDIA_TYPE_VIDEO, + .id = AV_CODEC_ID_SMC, + .priv_data_size = sizeof(SmcContext), + .init = smc_decode_init, + .close = smc_decode_end, + .decode = smc_decode_frame, + .capabilities = CODEC_CAP_DR1, + .long_name = NULL_IF_CONFIG_SMALL("QuickTime Graphics (SMC)"), +}; |
