Mercurial > libavcodec.hg
annotate smc.c @ 3990:746a60ba3177 libavcodec
enable CMOV_IS_FAST as its faster or equal speed on every cpu (duron, athlon, PM, P3) from which ive seen benchmarks, it might be slower on P4 but noone has posted benchmarks ...
author | michael |
---|---|
date | Wed, 11 Oct 2006 12:23:40 +0000 |
parents | c8c591fe26f8 |
children | 05e932ddaaa9 |
rev | line source |
---|---|
1610 | 1 /* |
2 * Quicktime Graphics (SMC) Video Decoder | |
3 * Copyright (C) 2003 the ffmpeg project | |
4 * | |
3947
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3036
diff
changeset
|
5 * This file is part of FFmpeg. |
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3036
diff
changeset
|
6 * |
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3036
diff
changeset
|
7 * FFmpeg is free software; you can redistribute it and/or |
1610 | 8 * modify it under the terms of the GNU Lesser General Public |
9 * License as published by the Free Software Foundation; either | |
3947
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3036
diff
changeset
|
10 * version 2.1 of the License, or (at your option) any later version. |
1610 | 11 * |
3947
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3036
diff
changeset
|
12 * FFmpeg is distributed in the hope that it will be useful, |
1610 | 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 * Lesser General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU Lesser General Public | |
3947
c8c591fe26f8
Change license headers to say 'FFmpeg' instead of 'this program/this library'
diego
parents:
3036
diff
changeset
|
18 * License along with FFmpeg; if not, write to the Free Software |
3036
0b546eab515d
Update licensing information: The FSF changed postal address.
diego
parents:
2979
diff
changeset
|
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
1610 | 20 * |
21 */ | |
22 | |
23 /** | |
24 * @file smc.c | |
25 * QT SMC Video Decoder by Mike Melanson (melanson@pcisys.net) | |
26 * For more information about the SMC format, visit: | |
27 * http://www.pcisys.net/~melanson/codecs/ | |
28 * | |
29 * The SMC decoder outputs PAL8 colorspace data. | |
30 */ | |
31 | |
32 #include <stdio.h> | |
33 #include <stdlib.h> | |
34 #include <string.h> | |
35 #include <unistd.h> | |
36 | |
37 #include "common.h" | |
38 #include "avcodec.h" | |
39 #include "dsputil.h" | |
40 | |
41 #define CPAIR 2 | |
42 #define CQUAD 4 | |
43 #define COCTET 8 | |
44 | |
45 #define COLORS_PER_TABLE 256 | |
46 | |
47 typedef struct SmcContext { | |
48 | |
49 AVCodecContext *avctx; | |
50 DSPContext dsp; | |
51 AVFrame frame; | |
52 | |
53 unsigned char *buf; | |
54 int size; | |
55 | |
56 /* SMC color tables */ | |
57 unsigned char color_pairs[COLORS_PER_TABLE * CPAIR]; | |
58 unsigned char color_quads[COLORS_PER_TABLE * CQUAD]; | |
59 unsigned char color_octets[COLORS_PER_TABLE * COCTET]; | |
60 | |
61 } SmcContext; | |
62 | |
63 #define GET_BLOCK_COUNT() \ | |
64 (opcode & 0x10) ? (1 + s->buf[stream_ptr++]) : 1 + (opcode & 0x0F); | |
65 | |
66 #define ADVANCE_BLOCK() \ | |
67 { \ | |
68 pixel_ptr += 4; \ | |
69 if (pixel_ptr >= width) \ | |
70 { \ | |
71 pixel_ptr = 0; \ | |
72 row_ptr += stride * 4; \ | |
73 } \ | |
74 total_blocks--; \ | |
75 if (total_blocks < 0) \ | |
76 { \ | |
1927 | 77 av_log(s->avctx, AV_LOG_INFO, "warning: block counter just went negative (this should not happen)\n"); \ |
1610 | 78 return; \ |
79 } \ | |
80 } | |
81 | |
82 static void smc_decode_stream(SmcContext *s) | |
83 { | |
84 int width = s->avctx->width; | |
85 int height = s->avctx->height; | |
86 int stride = s->frame.linesize[0]; | |
87 int i; | |
88 int stream_ptr = 0; | |
89 int chunk_size; | |
90 unsigned char opcode; | |
91 int n_blocks; | |
92 unsigned int color_flags; | |
93 unsigned int color_flags_a; | |
94 unsigned int color_flags_b; | |
95 unsigned int flag_mask; | |
96 | |
97 unsigned char *pixels = s->frame.data[0]; | |
98 | |
99 int image_size = height * s->frame.linesize[0]; | |
100 int row_ptr = 0; | |
101 int pixel_ptr = 0; | |
102 int pixel_x, pixel_y; | |
103 int row_inc = stride - 4; | |
104 int block_ptr; | |
105 int prev_block_ptr; | |
106 int prev_block_ptr1, prev_block_ptr2; | |
107 int prev_block_flag; | |
108 int total_blocks; | |
109 int color_table_index; /* indexes to color pair, quad, or octet tables */ | |
110 int pixel; | |
111 | |
112 int color_pair_index = 0; | |
113 int color_quad_index = 0; | |
114 int color_octet_index = 0; | |
115 | |
116 /* make the palette available */ | |
117 memcpy(s->frame.data[1], s->avctx->palctrl->palette, AVPALETTE_SIZE); | |
118 if (s->avctx->palctrl->palette_changed) { | |
119 s->frame.palette_has_changed = 1; | |
120 s->avctx->palctrl->palette_changed = 0; | |
121 } | |
122 | |
123 chunk_size = BE_32(&s->buf[stream_ptr]) & 0x00FFFFFF; | |
124 stream_ptr += 4; | |
125 if (chunk_size != s->size) | |
1927 | 126 av_log(s->avctx, AV_LOG_INFO, "warning: MOV chunk size != encoded chunk size (%d != %d); using MOV chunk size\n", |
1610 | 127 chunk_size, s->size); |
128 | |
129 chunk_size = s->size; | |
2103 | 130 total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4); |
1610 | 131 |
132 /* traverse through the blocks */ | |
133 while (total_blocks) { | |
134 /* sanity checks */ | |
135 /* make sure stream ptr hasn't gone out of bounds */ | |
136 if (stream_ptr > chunk_size) { | |
1927 | 137 av_log(s->avctx, AV_LOG_INFO, "SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)\n", |
1610 | 138 stream_ptr, chunk_size); |
139 return; | |
140 } | |
141 /* make sure the row pointer hasn't gone wild */ | |
142 if (row_ptr >= image_size) { | |
1927 | 143 av_log(s->avctx, AV_LOG_INFO, "SMC decoder just went out of bounds (row ptr = %d, height = %d)\n", |
1610 | 144 row_ptr, image_size); |
145 return; | |
146 } | |
147 | |
148 opcode = s->buf[stream_ptr++]; | |
149 switch (opcode & 0xF0) { | |
150 /* skip n blocks */ | |
151 case 0x00: | |
152 case 0x10: | |
153 n_blocks = GET_BLOCK_COUNT(); | |
154 while (n_blocks--) { | |
155 ADVANCE_BLOCK(); | |
156 } | |
157 break; | |
158 | |
159 /* repeat last block n times */ | |
160 case 0x20: | |
161 case 0x30: | |
162 n_blocks = GET_BLOCK_COUNT(); | |
163 | |
164 /* sanity check */ | |
165 if ((row_ptr == 0) && (pixel_ptr == 0)) { | |
1927 | 166 av_log(s->avctx, AV_LOG_INFO, "encountered repeat block opcode (%02X) but no blocks rendered yet\n", |
1610 | 167 opcode & 0xF0); |
168 break; | |
169 } | |
170 | |
171 /* figure out where the previous block started */ | |
172 if (pixel_ptr == 0) | |
2967 | 173 prev_block_ptr1 = |
1610 | 174 (row_ptr - s->avctx->width * 4) + s->avctx->width - 4; |
175 else | |
176 prev_block_ptr1 = row_ptr + pixel_ptr - 4; | |
177 | |
178 while (n_blocks--) { | |
179 block_ptr = row_ptr + pixel_ptr; | |
180 prev_block_ptr = prev_block_ptr1; | |
181 for (pixel_y = 0; pixel_y < 4; pixel_y++) { | |
182 for (pixel_x = 0; pixel_x < 4; pixel_x++) { | |
183 pixels[block_ptr++] = pixels[prev_block_ptr++]; | |
184 } | |
185 block_ptr += row_inc; | |
186 prev_block_ptr += row_inc; | |
187 } | |
188 ADVANCE_BLOCK(); | |
189 } | |
190 break; | |
191 | |
192 /* repeat previous pair of blocks n times */ | |
193 case 0x40: | |
194 case 0x50: | |
195 n_blocks = GET_BLOCK_COUNT(); | |
196 n_blocks *= 2; | |
197 | |
198 /* sanity check */ | |
199 if ((row_ptr == 0) && (pixel_ptr < 2 * 4)) { | |
2979 | 200 av_log(s->avctx, AV_LOG_INFO, "encountered repeat block opcode (%02X) but not enough blocks rendered yet\n", |
1610 | 201 opcode & 0xF0); |
202 break; | |
203 } | |
204 | |
205 /* figure out where the previous 2 blocks started */ | |
206 if (pixel_ptr == 0) | |
2967 | 207 prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + |
1610 | 208 s->avctx->width - 4 * 2; |
209 else if (pixel_ptr == 4) | |
210 prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + row_inc; | |
211 else | |
212 prev_block_ptr1 = row_ptr + pixel_ptr - 4 * 2; | |
213 | |
214 if (pixel_ptr == 0) | |
215 prev_block_ptr2 = (row_ptr - s->avctx->width * 4) + row_inc; | |
216 else | |
217 prev_block_ptr2 = row_ptr + pixel_ptr - 4; | |
218 | |
219 prev_block_flag = 0; | |
220 while (n_blocks--) { | |
221 block_ptr = row_ptr + pixel_ptr; | |
222 if (prev_block_flag) | |
223 prev_block_ptr = prev_block_ptr2; | |
224 else | |
225 prev_block_ptr = prev_block_ptr1; | |
226 prev_block_flag = !prev_block_flag; | |
227 | |
228 for (pixel_y = 0; pixel_y < 4; pixel_y++) { | |
229 for (pixel_x = 0; pixel_x < 4; pixel_x++) { | |
230 pixels[block_ptr++] = pixels[prev_block_ptr++]; | |
231 } | |
232 block_ptr += row_inc; | |
233 prev_block_ptr += row_inc; | |
234 } | |
235 ADVANCE_BLOCK(); | |
236 } | |
237 break; | |
238 | |
239 /* 1-color block encoding */ | |
240 case 0x60: | |
241 case 0x70: | |
242 n_blocks = GET_BLOCK_COUNT(); | |
243 pixel = s->buf[stream_ptr++]; | |
244 | |
245 while (n_blocks--) { | |
246 block_ptr = row_ptr + pixel_ptr; | |
247 for (pixel_y = 0; pixel_y < 4; pixel_y++) { | |
248 for (pixel_x = 0; pixel_x < 4; pixel_x++) { | |
249 pixels[block_ptr++] = pixel; | |
250 } | |
251 block_ptr += row_inc; | |
252 } | |
253 ADVANCE_BLOCK(); | |
254 } | |
255 break; | |
256 | |
257 /* 2-color block encoding */ | |
258 case 0x80: | |
259 case 0x90: | |
260 n_blocks = (opcode & 0x0F) + 1; | |
261 | |
262 /* figure out which color pair to use to paint the 2-color block */ | |
263 if ((opcode & 0xF0) == 0x80) { | |
264 /* fetch the next 2 colors from bytestream and store in next | |
265 * available entry in the color pair table */ | |
266 for (i = 0; i < CPAIR; i++) { | |
267 pixel = s->buf[stream_ptr++]; | |
268 color_table_index = CPAIR * color_pair_index + i; | |
269 s->color_pairs[color_table_index] = pixel; | |
270 } | |
271 /* this is the base index to use for this block */ | |
272 color_table_index = CPAIR * color_pair_index; | |
273 color_pair_index++; | |
274 /* wraparound */ | |
275 if (color_pair_index == COLORS_PER_TABLE) | |
276 color_pair_index = 0; | |
277 } else | |
278 color_table_index = CPAIR * s->buf[stream_ptr++]; | |
279 | |
280 while (n_blocks--) { | |
281 color_flags = BE_16(&s->buf[stream_ptr]); | |
282 stream_ptr += 2; | |
283 flag_mask = 0x8000; | |
284 block_ptr = row_ptr + pixel_ptr; | |
285 for (pixel_y = 0; pixel_y < 4; pixel_y++) { | |
286 for (pixel_x = 0; pixel_x < 4; pixel_x++) { | |
287 if (color_flags & flag_mask) | |
288 pixel = color_table_index + 1; | |
289 else | |
290 pixel = color_table_index; | |
291 flag_mask >>= 1; | |
292 pixels[block_ptr++] = s->color_pairs[pixel]; | |
293 } | |
294 block_ptr += row_inc; | |
295 } | |
296 ADVANCE_BLOCK(); | |
297 } | |
298 break; | |
299 | |
300 /* 4-color block encoding */ | |
301 case 0xA0: | |
302 case 0xB0: | |
303 n_blocks = (opcode & 0x0F) + 1; | |
304 | |
305 /* figure out which color quad to use to paint the 4-color block */ | |
306 if ((opcode & 0xF0) == 0xA0) { | |
307 /* fetch the next 4 colors from bytestream and store in next | |
308 * available entry in the color quad table */ | |
309 for (i = 0; i < CQUAD; i++) { | |
310 pixel = s->buf[stream_ptr++]; | |
311 color_table_index = CQUAD * color_quad_index + i; | |
312 s->color_quads[color_table_index] = pixel; | |
313 } | |
314 /* this is the base index to use for this block */ | |
315 color_table_index = CQUAD * color_quad_index; | |
316 color_quad_index++; | |
317 /* wraparound */ | |
318 if (color_quad_index == COLORS_PER_TABLE) | |
319 color_quad_index = 0; | |
320 } else | |
321 color_table_index = CQUAD * s->buf[stream_ptr++]; | |
322 | |
323 while (n_blocks--) { | |
324 color_flags = BE_32(&s->buf[stream_ptr]); | |
325 stream_ptr += 4; | |
326 /* flag mask actually acts as a bit shift count here */ | |
327 flag_mask = 30; | |
328 block_ptr = row_ptr + pixel_ptr; | |
329 for (pixel_y = 0; pixel_y < 4; pixel_y++) { | |
330 for (pixel_x = 0; pixel_x < 4; pixel_x++) { | |
2967 | 331 pixel = color_table_index + |
1610 | 332 ((color_flags >> flag_mask) & 0x03); |
333 flag_mask -= 2; | |
334 pixels[block_ptr++] = s->color_quads[pixel]; | |
335 } | |
336 block_ptr += row_inc; | |
337 } | |
338 ADVANCE_BLOCK(); | |
339 } | |
340 break; | |
341 | |
342 /* 8-color block encoding */ | |
343 case 0xC0: | |
344 case 0xD0: | |
345 n_blocks = (opcode & 0x0F) + 1; | |
346 | |
347 /* figure out which color octet to use to paint the 8-color block */ | |
348 if ((opcode & 0xF0) == 0xC0) { | |
349 /* fetch the next 8 colors from bytestream and store in next | |
350 * available entry in the color octet table */ | |
351 for (i = 0; i < COCTET; i++) { | |
352 pixel = s->buf[stream_ptr++]; | |
353 color_table_index = COCTET * color_octet_index + i; | |
354 s->color_octets[color_table_index] = pixel; | |
355 } | |
356 /* this is the base index to use for this block */ | |
357 color_table_index = COCTET * color_octet_index; | |
358 color_octet_index++; | |
359 /* wraparound */ | |
360 if (color_octet_index == COLORS_PER_TABLE) | |
361 color_octet_index = 0; | |
362 } else | |
363 color_table_index = COCTET * s->buf[stream_ptr++]; | |
364 | |
365 while (n_blocks--) { | |
366 /* | |
367 For this input of 6 hex bytes: | |
368 01 23 45 67 89 AB | |
369 Mangle it to this output: | |
370 flags_a = xx012456, flags_b = xx89A37B | |
371 */ | |
372 /* build the color flags */ | |
373 color_flags_a = color_flags_b = 0; | |
374 color_flags_a = | |
375 (s->buf[stream_ptr + 0] << 16) | | |
376 ((s->buf[stream_ptr + 1] & 0xF0) << 8) | | |
377 ((s->buf[stream_ptr + 2] & 0xF0) << 4) | | |
378 ((s->buf[stream_ptr + 2] & 0x0F) << 4) | | |
379 ((s->buf[stream_ptr + 3] & 0xF0) >> 4); | |
380 color_flags_b = | |
381 (s->buf[stream_ptr + 4] << 16) | | |
382 ((s->buf[stream_ptr + 5] & 0xF0) << 8) | | |
383 ((s->buf[stream_ptr + 1] & 0x0F) << 8) | | |
384 ((s->buf[stream_ptr + 3] & 0x0F) << 4) | | |
385 (s->buf[stream_ptr + 5] & 0x0F); | |
386 stream_ptr += 6; | |
387 | |
388 color_flags = color_flags_a; | |
389 /* flag mask actually acts as a bit shift count here */ | |
390 flag_mask = 21; | |
391 block_ptr = row_ptr + pixel_ptr; | |
392 for (pixel_y = 0; pixel_y < 4; pixel_y++) { | |
393 /* reload flags at third row (iteration pixel_y == 2) */ | |
394 if (pixel_y == 2) { | |
395 color_flags = color_flags_b; | |
396 flag_mask = 21; | |
397 } | |
398 for (pixel_x = 0; pixel_x < 4; pixel_x++) { | |
2967 | 399 pixel = color_table_index + |
1610 | 400 ((color_flags >> flag_mask) & 0x07); |
401 flag_mask -= 3; | |
402 pixels[block_ptr++] = s->color_octets[pixel]; | |
403 } | |
404 block_ptr += row_inc; | |
405 } | |
406 ADVANCE_BLOCK(); | |
407 } | |
408 break; | |
409 | |
410 /* 16-color block encoding (every pixel is a different color) */ | |
411 case 0xE0: | |
412 n_blocks = (opcode & 0x0F) + 1; | |
413 | |
414 while (n_blocks--) { | |
415 block_ptr = row_ptr + pixel_ptr; | |
416 for (pixel_y = 0; pixel_y < 4; pixel_y++) { | |
417 for (pixel_x = 0; pixel_x < 4; pixel_x++) { | |
418 pixels[block_ptr++] = s->buf[stream_ptr++]; | |
419 } | |
420 block_ptr += row_inc; | |
421 } | |
422 ADVANCE_BLOCK(); | |
423 } | |
424 break; | |
425 | |
426 case 0xF0: | |
1927 | 427 av_log(s->avctx, AV_LOG_INFO, "0xF0 opcode seen in SMC chunk (contact the developers)\n"); |
1610 | 428 break; |
429 } | |
430 } | |
431 } | |
432 | |
433 static int smc_decode_init(AVCodecContext *avctx) | |
434 { | |
435 SmcContext *s = (SmcContext *)avctx->priv_data; | |
436 | |
437 s->avctx = avctx; | |
438 avctx->pix_fmt = PIX_FMT_PAL8; | |
439 avctx->has_b_frames = 0; | |
440 dsputil_init(&s->dsp, avctx); | |
441 | |
1630 | 442 s->frame.data[0] = NULL; |
1610 | 443 |
444 return 0; | |
445 } | |
446 | |
447 static int smc_decode_frame(AVCodecContext *avctx, | |
448 void *data, int *data_size, | |
449 uint8_t *buf, int buf_size) | |
450 { | |
451 SmcContext *s = (SmcContext *)avctx->priv_data; | |
452 | |
453 s->buf = buf; | |
454 s->size = buf_size; | |
455 | |
456 s->frame.reference = 1; | |
2967 | 457 s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE | |
1769 | 458 FF_BUFFER_HINTS_REUSABLE | FF_BUFFER_HINTS_READABLE; |
1630 | 459 if (avctx->reget_buffer(avctx, &s->frame)) { |
1927 | 460 av_log(s->avctx, AV_LOG_ERROR, "reget_buffer() failed\n"); |
1610 | 461 return -1; |
462 } | |
463 | |
464 smc_decode_stream(s); | |
465 | |
466 *data_size = sizeof(AVFrame); | |
467 *(AVFrame*)data = s->frame; | |
468 | |
469 /* always report that the buffer was completely consumed */ | |
470 return buf_size; | |
471 } | |
472 | |
473 static int smc_decode_end(AVCodecContext *avctx) | |
474 { | |
475 SmcContext *s = (SmcContext *)avctx->priv_data; | |
476 | |
1630 | 477 if (s->frame.data[0]) |
478 avctx->release_buffer(avctx, &s->frame); | |
1610 | 479 |
480 return 0; | |
481 } | |
482 | |
483 AVCodec smc_decoder = { | |
484 "smc", | |
485 CODEC_TYPE_VIDEO, | |
486 CODEC_ID_SMC, | |
487 sizeof(SmcContext), | |
488 smc_decode_init, | |
489 NULL, | |
490 smc_decode_end, | |
491 smc_decode_frame, | |
492 CODEC_CAP_DR1, | |
493 }; |