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