|
|
|
@@ -34,6 +34,7 @@ |
|
|
|
|
|
|
|
#include "libavutil/intreadwrite.h" |
|
|
|
#include "avcodec.h" |
|
|
|
#include "bytestream.h" |
|
|
|
|
|
|
|
#define CPAIR 2 |
|
|
|
#define CQUAD 4 |
|
|
|
@@ -46,8 +47,7 @@ typedef struct SmcContext { |
|
|
|
AVCodecContext *avctx; |
|
|
|
AVFrame frame; |
|
|
|
|
|
|
|
const unsigned char *buf; |
|
|
|
int size; |
|
|
|
GetByteContext gb; |
|
|
|
|
|
|
|
/* SMC color tables */ |
|
|
|
unsigned char color_pairs[COLORS_PER_TABLE * CPAIR]; |
|
|
|
@@ -58,7 +58,7 @@ typedef struct SmcContext { |
|
|
|
} SmcContext; |
|
|
|
|
|
|
|
#define GET_BLOCK_COUNT() \ |
|
|
|
(opcode & 0x10) ? (1 + s->buf[stream_ptr++]) : 1 + (opcode & 0x0F); |
|
|
|
(opcode & 0x10) ? (1 + bytestream2_get_byte(&s->gb)) : 1 + (opcode & 0x0F); |
|
|
|
|
|
|
|
#define ADVANCE_BLOCK() \ |
|
|
|
{ \ |
|
|
|
@@ -82,8 +82,8 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
int height = s->avctx->height; |
|
|
|
int stride = s->frame.linesize[0]; |
|
|
|
int i; |
|
|
|
int stream_ptr = 0; |
|
|
|
int chunk_size; |
|
|
|
int buf_size = (int) (s->gb.buffer_end - s->gb.buffer_start); |
|
|
|
unsigned char opcode; |
|
|
|
int n_blocks; |
|
|
|
unsigned int color_flags; |
|
|
|
@@ -113,24 +113,18 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
/* make the palette available */ |
|
|
|
memcpy(s->frame.data[1], s->pal, AVPALETTE_SIZE); |
|
|
|
|
|
|
|
chunk_size = AV_RB32(&s->buf[stream_ptr]) & 0x00FFFFFF; |
|
|
|
stream_ptr += 4; |
|
|
|
if (chunk_size != s->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, s->size); |
|
|
|
chunk_size, buf_size); |
|
|
|
|
|
|
|
chunk_size = s->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 stream ptr hasn't gone out of bounds */ |
|
|
|
if (stream_ptr > chunk_size) { |
|
|
|
av_log(s->avctx, AV_LOG_INFO, "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) { |
|
|
|
av_log(s->avctx, AV_LOG_INFO, "SMC decoder just went out of bounds (row ptr = %d, height = %d)\n", |
|
|
|
@@ -138,7 +132,7 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
opcode = s->buf[stream_ptr++]; |
|
|
|
opcode = bytestream2_get_byte(&s->gb); |
|
|
|
switch (opcode & 0xF0) { |
|
|
|
/* skip n blocks */ |
|
|
|
case 0x00: |
|
|
|
@@ -158,7 +152,7 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
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); |
|
|
|
break; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
/* figure out where the previous block started */ |
|
|
|
@@ -192,7 +186,7 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
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); |
|
|
|
break; |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
/* figure out where the previous 2 blocks started */ |
|
|
|
@@ -233,7 +227,7 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
case 0x60: |
|
|
|
case 0x70: |
|
|
|
n_blocks = GET_BLOCK_COUNT(); |
|
|
|
pixel = s->buf[stream_ptr++]; |
|
|
|
pixel = bytestream2_get_byte(&s->gb); |
|
|
|
|
|
|
|
while (n_blocks--) { |
|
|
|
block_ptr = row_ptr + pixel_ptr; |
|
|
|
@@ -257,7 +251,7 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
/* 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++]; |
|
|
|
pixel = bytestream2_get_byte(&s->gb); |
|
|
|
color_table_index = CPAIR * color_pair_index + i; |
|
|
|
s->color_pairs[color_table_index] = pixel; |
|
|
|
} |
|
|
|
@@ -268,11 +262,10 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
if (color_pair_index == COLORS_PER_TABLE) |
|
|
|
color_pair_index = 0; |
|
|
|
} else |
|
|
|
color_table_index = CPAIR * s->buf[stream_ptr++]; |
|
|
|
color_table_index = CPAIR * bytestream2_get_byte(&s->gb); |
|
|
|
|
|
|
|
while (n_blocks--) { |
|
|
|
color_flags = AV_RB16(&s->buf[stream_ptr]); |
|
|
|
stream_ptr += 2; |
|
|
|
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++) { |
|
|
|
@@ -300,7 +293,7 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
/* 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++]; |
|
|
|
pixel = bytestream2_get_byte(&s->gb); |
|
|
|
color_table_index = CQUAD * color_quad_index + i; |
|
|
|
s->color_quads[color_table_index] = pixel; |
|
|
|
} |
|
|
|
@@ -311,11 +304,10 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
if (color_quad_index == COLORS_PER_TABLE) |
|
|
|
color_quad_index = 0; |
|
|
|
} else |
|
|
|
color_table_index = CQUAD * s->buf[stream_ptr++]; |
|
|
|
color_table_index = CQUAD * bytestream2_get_byte(&s->gb); |
|
|
|
|
|
|
|
while (n_blocks--) { |
|
|
|
color_flags = AV_RB32(&s->buf[stream_ptr]); |
|
|
|
stream_ptr += 4; |
|
|
|
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; |
|
|
|
@@ -342,7 +334,7 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
/* 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++]; |
|
|
|
pixel = bytestream2_get_byte(&s->gb); |
|
|
|
color_table_index = COCTET * color_octet_index + i; |
|
|
|
s->color_octets[color_table_index] = pixel; |
|
|
|
} |
|
|
|
@@ -353,7 +345,7 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
if (color_octet_index == COLORS_PER_TABLE) |
|
|
|
color_octet_index = 0; |
|
|
|
} else |
|
|
|
color_table_index = COCTET * s->buf[stream_ptr++]; |
|
|
|
color_table_index = COCTET * bytestream2_get_byte(&s->gb); |
|
|
|
|
|
|
|
while (n_blocks--) { |
|
|
|
/* |
|
|
|
@@ -363,15 +355,12 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
flags_a = xx012456, flags_b = xx89A37B |
|
|
|
*/ |
|
|
|
/* build the color flags */ |
|
|
|
color_flags_a = |
|
|
|
((AV_RB16(s->buf + stream_ptr ) & 0xFFF0) << 8) | |
|
|
|
(AV_RB16(s->buf + stream_ptr + 2) >> 4); |
|
|
|
color_flags_b = |
|
|
|
((AV_RB16(s->buf + stream_ptr + 4) & 0xFFF0) << 8) | |
|
|
|
((s->buf[stream_ptr + 1] & 0x0F) << 8) | |
|
|
|
((s->buf[stream_ptr + 3] & 0x0F) << 4) | |
|
|
|
(s->buf[stream_ptr + 5] & 0x0F); |
|
|
|
stream_ptr += 6; |
|
|
|
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 */ |
|
|
|
@@ -403,7 +392,7 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
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++]; |
|
|
|
pixels[block_ptr++] = bytestream2_get_byte(&s->gb); |
|
|
|
} |
|
|
|
block_ptr += row_inc; |
|
|
|
} |
|
|
|
@@ -412,10 +401,12 @@ static void smc_decode_stream(SmcContext *s) |
|
|
|
break; |
|
|
|
|
|
|
|
case 0xF0: |
|
|
|
av_log(s->avctx, AV_LOG_INFO, "0xF0 opcode seen in SMC chunk (contact the developers)\n"); |
|
|
|
av_log_missing_feature(s->avctx, "0xF0 opcode", 1); |
|
|
|
break; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
static av_cold int smc_decode_init(AVCodecContext *avctx) |
|
|
|
@@ -439,8 +430,7 @@ static int smc_decode_frame(AVCodecContext *avctx, |
|
|
|
SmcContext *s = avctx->priv_data; |
|
|
|
const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, NULL); |
|
|
|
|
|
|
|
s->buf = buf; |
|
|
|
s->size = buf_size; |
|
|
|
bytestream2_init(&s->gb, buf, buf_size); |
|
|
|
|
|
|
|
s->frame.reference = 1; |
|
|
|
s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | |
|
|
|
|