summaryrefslogtreecommitdiffstats
path: root/qtrpza.c
blob: 024bca5b42ce3a030a8c2ad8fd955b8392187e7a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
/*
 *
 * Apple Video (rpza) QuickTime Decoder for Mplayer
 * (c) 2002 Roberto Togni
 *
 * Fourcc: rpza, azpr
 *
 * Some code comes from qtsmc.c by Mike Melanson
 *
 * A description of the decoding algorithm can be found here:
 *   http://www.pcisys.net/~melanson/codecs/
 */

#include "config.h"
#include "bswap.h"
#include "mp_msg.h"

#define BE_16(x) (be2me_16(*(unsigned short *)(x)))
#define BE_32(x) (be2me_32(*(unsigned int *)(x)))


#define ADVANCE_BLOCK() \
{ \
	pixel_ptr += block_x_inc; \
	if (pixel_ptr >= (width * bytes_per_pixel)) \
	{ \
		pixel_ptr = 0; \
		row_ptr += block_y_inc * 4; \
	} \
	total_blocks--; \
	if (total_blocks < 0) \
	{ \
		mp_msg(MSGT_DECVIDEO, MSGL_WARN, "block counter just went negative (this should not happen)\n"); \
		return; \
	} \
}

#define PAINT_CURRENT_PIXEL(r, g, b, color) \
{ \
	if (bytes_per_pixel == 2) { \
		(*(unsigned short*)(&decoded[block_ptr])) = color & 0x7fff; \
		block_ptr += 2; \
	} else { \
		decoded[block_ptr++] = (b); \
		decoded[block_ptr++] = (g); \
		decoded[block_ptr++] = (r); \
		if (bytes_per_pixel == 4) /* 32bpp */ \
			block_ptr++; \
	} \
}

#define COLOR_FIX(col_out, col_in) (col_out) = ((col_in) << 3) | ((col_in) >> 2)

#define COLOR_TO_RGB(r, g, b, color) \
{ \
	if (bytes_per_pixel != 2) { \
		unsigned short tmp; \
		tmp = (color >> 10) & 0x1f; \
		COLOR_FIX (r, tmp); \
		tmp = (color >> 5) & 0x1f; \
		COLOR_FIX (g, tmp); \
		tmp = color & 0x1f; \
		COLOR_FIX (b, tmp); \
	} \
}

#define COLORAB_TO_RGB4(rgb4, color4, colorA, colorB) \
{ \
	unsigned short ta, tb, tt; \
	if (bytes_per_pixel != 2) { \
		ta = (colorA >> 10) & 0x1f; \
		tb = (colorB >> 10) & 0x1f; \
		COLOR_FIX (rgb4[3][0], ta); \
		COLOR_FIX (rgb4[0][0], tb); \
		tt = (11 * ta + 21 * tb) >> 5; \
		COLOR_FIX (rgb4[1][0], tt); \
		tt = (21 * ta + 11 * tb) >> 5; \
		COLOR_FIX (rgb4[2][0], tt); \
		ta = (colorA >> 5) & 0x1f; \
		tb = (colorB >> 5) & 0x1f; \
		COLOR_FIX (rgb4[3][1], ta); \
		COLOR_FIX (rgb4[0][1], tb); \
		tt = (11 * ta + 21 * tb) >> 5; \
		COLOR_FIX (rgb4[1][1], tt); \
		tt = (21 * ta + 11 * tb) >> 5; \
		COLOR_FIX (rgb4[2][1], tt); \
		ta = colorA	& 0x1f; \
		tb = colorB	& 0x1f; \
		COLOR_FIX (rgb4[3][2], ta); \
		COLOR_FIX (rgb4[0][2], tb); \
		tt = (11 * ta + 21 * tb) >> 5; \
		COLOR_FIX (rgb4[1][2], tt); \
		tt = (21 * ta + 11 * tb) >> 5; \
		COLOR_FIX (rgb4[2][2], tt); \
	} else { \
		color4[3] = colorA; \
		color4[0] = colorB; \
		ta = (colorA >> 10) & 0x1f; \
		tb = (colorB >> 10) & 0x1f; \
		color4[1] = ((11 * ta + 21 * tb) << 5) & 0x7c00; \
		color4[2] = ((21 * ta + 11 * tb) << 5) & 0x7c00; \
		ta = (colorA >> 5) & 0x1f; \
		tb = (colorB >> 5) & 0x1f; \
		color4[1] |= (11 * ta + 21 * tb) & 0x3e0; \
		color4[2] |= (21 * ta + 11 * tb) & 0x3e0; \
		ta = colorA	& 0x1f; \
		tb = colorB	& 0x1f; \
		color4[1] |= (11 * ta + 21 * tb) >> 5; \
		color4[2] |= (21 * ta + 11 * tb) >> 5; \
	} \
}



/*
 * rpza frame decoder
 *
 * Input values:
 *
 *	*encoded: buffer of encoded data (chunk)
 *	encoded_size: length of encoded buffer
 *	*decoded: buffer where decoded data is written (image buffer)
 *	width: width of decoded frame in pixels
 *	height: height of decoded frame in pixels
 *	bytes_per_pixel: bytes/pixel in output image (color depth)
 *
 */

void qt_decode_rpza(char *encoded, int encoded_size, char *decoded, int width,
										int height, int bytes_per_pixel)
{

	int i;
	int stream_ptr = 0;
	int chunk_size;
	unsigned char opcode;
	int n_blocks;
	unsigned short colorA, colorB;
	unsigned char r, g, b;
	unsigned char rgb4[4][3];
	unsigned short color4[4];
	unsigned char index, idx;

	int row_ptr = 0;
	int pixel_ptr = 0;
	int pixel_x, pixel_y;
	int row_inc = bytes_per_pixel * (width - 4);
	int max_height = row_inc * height;
	int block_x_inc = bytes_per_pixel * 4;
	int block_y_inc = bytes_per_pixel * width;
	int block_ptr;
	int total_blocks;

	
	/* First byte is always 0xe1. Warn if it's different */
	if ((unsigned char)encoded[stream_ptr] != 0xe1)
		mp_msg(MSGT_DECVIDEO, MSGL_WARN,
					 "First chunk byte is 0x%02x instead of 0x1e\n",
					 (unsigned char)encoded[stream_ptr]);

	/* Get chunk size, ingnoring first byte */
	chunk_size = BE_32(&encoded[stream_ptr]) & 0x00FFFFFF;
	stream_ptr += 4;

	/* If length mismatch use size from MOV file and try to decode anyway */
	if (chunk_size != encoded_size)
		mp_msg(MSGT_DECVIDEO, MSGL_WARN, "MOV chunk size != encoded chunk size; using MOV chunk size\n");

	chunk_size = encoded_size;

	/* Number of 4x4 blocks in frame. */
	total_blocks = (width * height) / (4 * 4);

	/* Process chunk data */
	while (stream_ptr < chunk_size) {
		opcode = encoded[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) | ((unsigned char)encoded[stream_ptr++]);
			opcode = 0;
			if ((encoded[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 = BE_16 (&encoded[stream_ptr]);
				stream_ptr += 2;
				COLOR_TO_RGB (r, g, b, colorA);
				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++){
							PAINT_CURRENT_PIXEL(r, g, b, colorA);
						}
						block_ptr += row_inc;
					}
					ADVANCE_BLOCK();
				}
				break;

			/* Fill blocks with 4 colors */
			case 0xc0:
				colorA = BE_16 (&encoded[stream_ptr]);
				stream_ptr += 2;
			case 0x20:
				colorB = BE_16 (&encoded[stream_ptr]);
				stream_ptr += 2;
				COLORAB_TO_RGB4 (rgb4, color4, colorA, colorB);
				while (n_blocks--) {
					block_ptr = row_ptr + pixel_ptr;
					for (pixel_y = 0; pixel_y < 4; pixel_y++) {
						index = encoded[stream_ptr++];
						for (pixel_x = 0; pixel_x < 4; pixel_x++){
							idx = (index >> (2 * (3 - pixel_x))) & 0x03;
							PAINT_CURRENT_PIXEL(rgb4[idx][0], rgb4[idx][1], rgb4[idx][2], color4[idx]);
						}
						block_ptr += row_inc;
					}
					ADVANCE_BLOCK();
				}
				break;
				
			/* Fill block with 16 colors */
			case 0x00:
				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 = BE_16 (&encoded[stream_ptr]);
							stream_ptr += 2;
						}
						COLOR_TO_RGB (r, g, b, colorA);
						PAINT_CURRENT_PIXEL(r, g, b, colorA);
					}
					block_ptr += row_inc;
				}
				ADVANCE_BLOCK();
				break;
				
			/* Unknown opcode */
			default:
				mp_msg(MSGT_DECVIDEO, MSGL_HINT, "Unknown opcode %d in rpza chunk."
							 " Skip remaining %lu bytes of chunk data.\n", opcode,
							 chunk_size - stream_ptr);
				return;
		} /* Opcode switch */

	}
}