static void rpza_decode_stream(RpzaContext *s) { int width = s->avctx->width; int stride = s->frame.linesize[0] / 2; int row_inc = stride - 4; int stream_ptr = 0; int chunk_size; unsigned char opcode; int n_blocks; unsigned short colorA = 0, colorB; unsigned short color4[4]; unsigned char index, idx; unsigned short ta, tb; unsigned short *pixels = (unsigned short *)s->frame.data[0]; int row_ptr = 0; int pixel_ptr = 0; int block_ptr; int pixel_x, pixel_y; int total_blocks; /* First byte is always 0xe1. Warn if it's different */ if (s->buf[stream_ptr] != 0xe1) av_log(s->avctx, AV_LOG_ERROR, "First chunk byte is 0x%02x instead of 0xe1\n", s->buf[stream_ptr]); /* Get chunk size, ingnoring first byte */ chunk_size = AV_RB32(&s->buf[stream_ptr]) & 0x00FFFFFF; stream_ptr += 4; /* If length mismatch use size from MOV file and try to decode anyway */ if (chunk_size != s->size) av_log(s->avctx, AV_LOG_ERROR, "MOV chunk size != encoded chunk size; using MOV chunk size\n"); chunk_size = s->size; /* Number of 4x4 blocks in frame. */ total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4); /* Process chunk data */ while (stream_ptr < chunk_size) { opcode = s->buf[stream_ptr++]; /* Get opcode */ n_blocks = (opcode & 0x1f) + 1; /* Extract block counter from opcode */ /* If opcode MSbit is 0, we need more data to decide what to do */ if ((opcode & 0x80) == 0) { colorA = (opcode << 8) | (s->buf[stream_ptr++]); opcode = 0; if ((s->buf[stream_ptr] & 0x80) != 0) { /* Must behave as opcode 110xxxxx, using colorA computed * above. Use fake opcode 0x20 to enter switch block at * the right place */ opcode = 0x20; n_blocks = 1; } } switch (opcode & 0xe0) { /* Skip blocks */ case 0x80: while (n_blocks--) { ADVANCE_BLOCK(); } break; /* Fill blocks with one color */ case 0xa0: colorA = AV_RB16 (&s->buf[stream_ptr]); stream_ptr += 2; 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] = colorA; block_ptr++; } block_ptr += row_inc; } ADVANCE_BLOCK(); } break; /* Fill blocks with 4 colors */ case 0xc0: colorA = AV_RB16 (&s->buf[stream_ptr]); stream_ptr += 2; case 0x20: colorB = AV_RB16 (&s->buf[stream_ptr]); stream_ptr += 2; /* sort out the colors */ color4[0] = colorB; color4[1] = 0; color4[2] = 0; color4[3] = colorA; /* red components */ ta = (colorA >> 10) & 0x1F; tb = (colorB >> 10) & 0x1F; color4[1] |= ((11 * ta + 21 * tb) >> 5) << 10; color4[2] |= ((21 * ta + 11 * tb) >> 5) << 10; /* green components */ ta = (colorA >> 5) & 0x1F; tb = (colorB >> 5) & 0x1F; color4[1] |= ((11 * ta + 21 * tb) >> 5) << 5; color4[2] |= ((21 * ta + 11 * tb) >> 5) << 5; /* blue components */ ta = colorA & 0x1F; tb = colorB & 0x1F; color4[1] |= ((11 * ta + 21 * tb) >> 5); color4[2] |= ((21 * ta + 11 * tb) >> 5); while (n_blocks--) { block_ptr = row_ptr + pixel_ptr; for (pixel_y = 0; pixel_y < 4; pixel_y++) { index = s->buf[stream_ptr++]; for (pixel_x = 0; pixel_x < 4; pixel_x++){ idx = (index >> (2 * (3 - pixel_x))) & 0x03; pixels[block_ptr] = color4[idx]; block_ptr++; } block_ptr += row_inc; } ADVANCE_BLOCK(); } break; /* Fill block with 16 colors */ case 0x00: if (s->size - stream_ptr < 16) block_ptr = row_ptr + pixel_ptr; for (pixel_y = 0; pixel_y < 4; pixel_y++) { for (pixel_x = 0; pixel_x < 4; pixel_x++){ /* We already have color of upper left pixel */ if ((pixel_y != 0) || (pixel_x !=0)) { colorA = AV_RB16 (&s->buf[stream_ptr]); stream_ptr += 2; } pixels[block_ptr] = colorA; block_ptr++; } block_ptr += row_inc; } ADVANCE_BLOCK(); break; /* Unknown opcode */ default: av_log(s->avctx, AV_LOG_ERROR, "Unknown opcode %d in rpza chunk." " Skip remaining %d bytes of chunk data.\n", opcode, chunk_size - stream_ptr); } /* Opcode switch */ } }