Libav 0.7.1
libavcodec/smc.c
Go to the documentation of this file.
00001 /*
00002  * Quicktime Graphics (SMC) Video Decoder
00003  * Copyright (C) 2003 the ffmpeg project
00004  *
00005  * This file is part of Libav.
00006  *
00007  * Libav is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU Lesser General Public
00009  * License as published by the Free Software Foundation; either
00010  * version 2.1 of the License, or (at your option) any later version.
00011  *
00012  * Libav is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00015  * Lesser General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU Lesser General Public
00018  * License along with Libav; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
00020  */
00021 
00031 #include <stdio.h>
00032 #include <stdlib.h>
00033 #include <string.h>
00034 
00035 #include "libavutil/intreadwrite.h"
00036 #include "avcodec.h"
00037 
00038 #define CPAIR 2
00039 #define CQUAD 4
00040 #define COCTET 8
00041 
00042 #define COLORS_PER_TABLE 256
00043 
00044 typedef struct SmcContext {
00045 
00046     AVCodecContext *avctx;
00047     AVFrame frame;
00048 
00049     const unsigned char *buf;
00050     int size;
00051 
00052     /* SMC color tables */
00053     unsigned char color_pairs[COLORS_PER_TABLE * CPAIR];
00054     unsigned char color_quads[COLORS_PER_TABLE * CQUAD];
00055     unsigned char color_octets[COLORS_PER_TABLE * COCTET];
00056 
00057     uint32_t pal[256];
00058 } SmcContext;
00059 
00060 #define GET_BLOCK_COUNT() \
00061   (opcode & 0x10) ? (1 + s->buf[stream_ptr++]) : 1 + (opcode & 0x0F);
00062 
00063 #define ADVANCE_BLOCK() \
00064 { \
00065     pixel_ptr += 4; \
00066     if (pixel_ptr >= width) \
00067     { \
00068         pixel_ptr = 0; \
00069         row_ptr += stride * 4; \
00070     } \
00071     total_blocks--; \
00072     if (total_blocks < 0) \
00073     { \
00074         av_log(s->avctx, AV_LOG_INFO, "warning: block counter just went negative (this should not happen)\n"); \
00075         return; \
00076     } \
00077 }
00078 
00079 static void smc_decode_stream(SmcContext *s)
00080 {
00081     int width = s->avctx->width;
00082     int height = s->avctx->height;
00083     int stride = s->frame.linesize[0];
00084     int i;
00085     int stream_ptr = 0;
00086     int chunk_size;
00087     unsigned char opcode;
00088     int n_blocks;
00089     unsigned int color_flags;
00090     unsigned int color_flags_a;
00091     unsigned int color_flags_b;
00092     unsigned int flag_mask;
00093 
00094     unsigned char *pixels = s->frame.data[0];
00095 
00096     int image_size = height * s->frame.linesize[0];
00097     int row_ptr = 0;
00098     int pixel_ptr = 0;
00099     int pixel_x, pixel_y;
00100     int row_inc = stride - 4;
00101     int block_ptr;
00102     int prev_block_ptr;
00103     int prev_block_ptr1, prev_block_ptr2;
00104     int prev_block_flag;
00105     int total_blocks;
00106     int color_table_index;  /* indexes to color pair, quad, or octet tables */
00107     int pixel;
00108 
00109     int color_pair_index = 0;
00110     int color_quad_index = 0;
00111     int color_octet_index = 0;
00112 
00113     /* make the palette available */
00114     memcpy(s->frame.data[1], s->pal, AVPALETTE_SIZE);
00115 
00116     chunk_size = AV_RB32(&s->buf[stream_ptr]) & 0x00FFFFFF;
00117     stream_ptr += 4;
00118     if (chunk_size != s->size)
00119         av_log(s->avctx, AV_LOG_INFO, "warning: MOV chunk size != encoded chunk size (%d != %d); using MOV chunk size\n",
00120             chunk_size, s->size);
00121 
00122     chunk_size = s->size;
00123     total_blocks = ((s->avctx->width + 3) / 4) * ((s->avctx->height + 3) / 4);
00124 
00125     /* traverse through the blocks */
00126     while (total_blocks) {
00127         /* sanity checks */
00128         /* make sure stream ptr hasn't gone out of bounds */
00129         if (stream_ptr > chunk_size) {
00130             av_log(s->avctx, AV_LOG_INFO, "SMC decoder just went out of bounds (stream ptr = %d, chunk size = %d)\n",
00131                 stream_ptr, chunk_size);
00132             return;
00133         }
00134         /* make sure the row pointer hasn't gone wild */
00135         if (row_ptr >= image_size) {
00136             av_log(s->avctx, AV_LOG_INFO, "SMC decoder just went out of bounds (row ptr = %d, height = %d)\n",
00137                 row_ptr, image_size);
00138             return;
00139         }
00140 
00141         opcode = s->buf[stream_ptr++];
00142         switch (opcode & 0xF0) {
00143         /* skip n blocks */
00144         case 0x00:
00145         case 0x10:
00146             n_blocks = GET_BLOCK_COUNT();
00147             while (n_blocks--) {
00148                 ADVANCE_BLOCK();
00149             }
00150             break;
00151 
00152         /* repeat last block n times */
00153         case 0x20:
00154         case 0x30:
00155             n_blocks = GET_BLOCK_COUNT();
00156 
00157             /* sanity check */
00158             if ((row_ptr == 0) && (pixel_ptr == 0)) {
00159                 av_log(s->avctx, AV_LOG_INFO, "encountered repeat block opcode (%02X) but no blocks rendered yet\n",
00160                     opcode & 0xF0);
00161                 break;
00162             }
00163 
00164             /* figure out where the previous block started */
00165             if (pixel_ptr == 0)
00166                 prev_block_ptr1 =
00167                     (row_ptr - s->avctx->width * 4) + s->avctx->width - 4;
00168             else
00169                 prev_block_ptr1 = row_ptr + pixel_ptr - 4;
00170 
00171             while (n_blocks--) {
00172                 block_ptr = row_ptr + pixel_ptr;
00173                 prev_block_ptr = prev_block_ptr1;
00174                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
00175                     for (pixel_x = 0; pixel_x < 4; pixel_x++) {
00176                         pixels[block_ptr++] = pixels[prev_block_ptr++];
00177                     }
00178                     block_ptr += row_inc;
00179                     prev_block_ptr += row_inc;
00180                 }
00181                 ADVANCE_BLOCK();
00182             }
00183             break;
00184 
00185         /* repeat previous pair of blocks n times */
00186         case 0x40:
00187         case 0x50:
00188             n_blocks = GET_BLOCK_COUNT();
00189             n_blocks *= 2;
00190 
00191             /* sanity check */
00192             if ((row_ptr == 0) && (pixel_ptr < 2 * 4)) {
00193                 av_log(s->avctx, AV_LOG_INFO, "encountered repeat block opcode (%02X) but not enough blocks rendered yet\n",
00194                     opcode & 0xF0);
00195                 break;
00196             }
00197 
00198             /* figure out where the previous 2 blocks started */
00199             if (pixel_ptr == 0)
00200                 prev_block_ptr1 = (row_ptr - s->avctx->width * 4) +
00201                     s->avctx->width - 4 * 2;
00202             else if (pixel_ptr == 4)
00203                 prev_block_ptr1 = (row_ptr - s->avctx->width * 4) + row_inc;
00204             else
00205                 prev_block_ptr1 = row_ptr + pixel_ptr - 4 * 2;
00206 
00207             if (pixel_ptr == 0)
00208                 prev_block_ptr2 = (row_ptr - s->avctx->width * 4) + row_inc;
00209             else
00210                 prev_block_ptr2 = row_ptr + pixel_ptr - 4;
00211 
00212             prev_block_flag = 0;
00213             while (n_blocks--) {
00214                 block_ptr = row_ptr + pixel_ptr;
00215                 if (prev_block_flag)
00216                     prev_block_ptr = prev_block_ptr2;
00217                 else
00218                     prev_block_ptr = prev_block_ptr1;
00219                 prev_block_flag = !prev_block_flag;
00220 
00221                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
00222                     for (pixel_x = 0; pixel_x < 4; pixel_x++) {
00223                         pixels[block_ptr++] = pixels[prev_block_ptr++];
00224                     }
00225                     block_ptr += row_inc;
00226                     prev_block_ptr += row_inc;
00227                 }
00228                 ADVANCE_BLOCK();
00229             }
00230             break;
00231 
00232         /* 1-color block encoding */
00233         case 0x60:
00234         case 0x70:
00235             n_blocks = GET_BLOCK_COUNT();
00236             pixel = s->buf[stream_ptr++];
00237 
00238             while (n_blocks--) {
00239                 block_ptr = row_ptr + pixel_ptr;
00240                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
00241                     for (pixel_x = 0; pixel_x < 4; pixel_x++) {
00242                         pixels[block_ptr++] = pixel;
00243                     }
00244                     block_ptr += row_inc;
00245                 }
00246                 ADVANCE_BLOCK();
00247             }
00248             break;
00249 
00250         /* 2-color block encoding */
00251         case 0x80:
00252         case 0x90:
00253             n_blocks = (opcode & 0x0F) + 1;
00254 
00255             /* figure out which color pair to use to paint the 2-color block */
00256             if ((opcode & 0xF0) == 0x80) {
00257                 /* fetch the next 2 colors from bytestream and store in next
00258                  * available entry in the color pair table */
00259                 for (i = 0; i < CPAIR; i++) {
00260                     pixel = s->buf[stream_ptr++];
00261                     color_table_index = CPAIR * color_pair_index + i;
00262                     s->color_pairs[color_table_index] = pixel;
00263                 }
00264                 /* this is the base index to use for this block */
00265                 color_table_index = CPAIR * color_pair_index;
00266                 color_pair_index++;
00267                 /* wraparound */
00268                 if (color_pair_index == COLORS_PER_TABLE)
00269                     color_pair_index = 0;
00270             } else
00271                 color_table_index = CPAIR * s->buf[stream_ptr++];
00272 
00273             while (n_blocks--) {
00274                 color_flags = AV_RB16(&s->buf[stream_ptr]);
00275                 stream_ptr += 2;
00276                 flag_mask = 0x8000;
00277                 block_ptr = row_ptr + pixel_ptr;
00278                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
00279                     for (pixel_x = 0; pixel_x < 4; pixel_x++) {
00280                         if (color_flags & flag_mask)
00281                             pixel = color_table_index + 1;
00282                         else
00283                             pixel = color_table_index;
00284                         flag_mask >>= 1;
00285                         pixels[block_ptr++] = s->color_pairs[pixel];
00286                     }
00287                     block_ptr += row_inc;
00288                 }
00289                 ADVANCE_BLOCK();
00290             }
00291             break;
00292 
00293         /* 4-color block encoding */
00294         case 0xA0:
00295         case 0xB0:
00296             n_blocks = (opcode & 0x0F) + 1;
00297 
00298             /* figure out which color quad to use to paint the 4-color block */
00299             if ((opcode & 0xF0) == 0xA0) {
00300                 /* fetch the next 4 colors from bytestream and store in next
00301                  * available entry in the color quad table */
00302                 for (i = 0; i < CQUAD; i++) {
00303                     pixel = s->buf[stream_ptr++];
00304                     color_table_index = CQUAD * color_quad_index + i;
00305                     s->color_quads[color_table_index] = pixel;
00306                 }
00307                 /* this is the base index to use for this block */
00308                 color_table_index = CQUAD * color_quad_index;
00309                 color_quad_index++;
00310                 /* wraparound */
00311                 if (color_quad_index == COLORS_PER_TABLE)
00312                     color_quad_index = 0;
00313             } else
00314                 color_table_index = CQUAD * s->buf[stream_ptr++];
00315 
00316             while (n_blocks--) {
00317                 color_flags = AV_RB32(&s->buf[stream_ptr]);
00318                 stream_ptr += 4;
00319                 /* flag mask actually acts as a bit shift count here */
00320                 flag_mask = 30;
00321                 block_ptr = row_ptr + pixel_ptr;
00322                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
00323                     for (pixel_x = 0; pixel_x < 4; pixel_x++) {
00324                         pixel = color_table_index +
00325                             ((color_flags >> flag_mask) & 0x03);
00326                         flag_mask -= 2;
00327                         pixels[block_ptr++] = s->color_quads[pixel];
00328                     }
00329                     block_ptr += row_inc;
00330                 }
00331                 ADVANCE_BLOCK();
00332             }
00333             break;
00334 
00335         /* 8-color block encoding */
00336         case 0xC0:
00337         case 0xD0:
00338             n_blocks = (opcode & 0x0F) + 1;
00339 
00340             /* figure out which color octet to use to paint the 8-color block */
00341             if ((opcode & 0xF0) == 0xC0) {
00342                 /* fetch the next 8 colors from bytestream and store in next
00343                  * available entry in the color octet table */
00344                 for (i = 0; i < COCTET; i++) {
00345                     pixel = s->buf[stream_ptr++];
00346                     color_table_index = COCTET * color_octet_index + i;
00347                     s->color_octets[color_table_index] = pixel;
00348                 }
00349                 /* this is the base index to use for this block */
00350                 color_table_index = COCTET * color_octet_index;
00351                 color_octet_index++;
00352                 /* wraparound */
00353                 if (color_octet_index == COLORS_PER_TABLE)
00354                     color_octet_index = 0;
00355             } else
00356                 color_table_index = COCTET * s->buf[stream_ptr++];
00357 
00358             while (n_blocks--) {
00359                 /*
00360                   For this input of 6 hex bytes:
00361                     01 23 45 67 89 AB
00362                   Mangle it to this output:
00363                     flags_a = xx012456, flags_b = xx89A37B
00364                 */
00365                 /* build the color flags */
00366                 color_flags_a =
00367                     ((AV_RB16(s->buf + stream_ptr    ) & 0xFFF0) << 8) |
00368                      (AV_RB16(s->buf + stream_ptr + 2) >> 4);
00369                 color_flags_b =
00370                     ((AV_RB16(s->buf + stream_ptr + 4) & 0xFFF0) << 8) |
00371                     ((s->buf[stream_ptr + 1] & 0x0F) << 8) |
00372                     ((s->buf[stream_ptr + 3] & 0x0F) << 4) |
00373                     (s->buf[stream_ptr + 5] & 0x0F);
00374                 stream_ptr += 6;
00375 
00376                 color_flags = color_flags_a;
00377                 /* flag mask actually acts as a bit shift count here */
00378                 flag_mask = 21;
00379                 block_ptr = row_ptr + pixel_ptr;
00380                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
00381                     /* reload flags at third row (iteration pixel_y == 2) */
00382                     if (pixel_y == 2) {
00383                         color_flags = color_flags_b;
00384                         flag_mask = 21;
00385                     }
00386                     for (pixel_x = 0; pixel_x < 4; pixel_x++) {
00387                         pixel = color_table_index +
00388                             ((color_flags >> flag_mask) & 0x07);
00389                         flag_mask -= 3;
00390                         pixels[block_ptr++] = s->color_octets[pixel];
00391                     }
00392                     block_ptr += row_inc;
00393                 }
00394                 ADVANCE_BLOCK();
00395             }
00396             break;
00397 
00398         /* 16-color block encoding (every pixel is a different color) */
00399         case 0xE0:
00400             n_blocks = (opcode & 0x0F) + 1;
00401 
00402             while (n_blocks--) {
00403                 block_ptr = row_ptr + pixel_ptr;
00404                 for (pixel_y = 0; pixel_y < 4; pixel_y++) {
00405                     for (pixel_x = 0; pixel_x < 4; pixel_x++) {
00406                         pixels[block_ptr++] = s->buf[stream_ptr++];
00407                     }
00408                     block_ptr += row_inc;
00409                 }
00410                 ADVANCE_BLOCK();
00411             }
00412             break;
00413 
00414         case 0xF0:
00415             av_log(s->avctx, AV_LOG_INFO, "0xF0 opcode seen in SMC chunk (contact the developers)\n");
00416             break;
00417         }
00418     }
00419 }
00420 
00421 static av_cold int smc_decode_init(AVCodecContext *avctx)
00422 {
00423     SmcContext *s = avctx->priv_data;
00424 
00425     s->avctx = avctx;
00426     avctx->pix_fmt = PIX_FMT_PAL8;
00427 
00428     s->frame.data[0] = NULL;
00429 
00430     return 0;
00431 }
00432 
00433 static int smc_decode_frame(AVCodecContext *avctx,
00434                              void *data, int *data_size,
00435                              AVPacket *avpkt)
00436 {
00437     const uint8_t *buf = avpkt->data;
00438     int buf_size = avpkt->size;
00439     SmcContext *s = avctx->priv_data;
00440     const uint8_t *pal = av_packet_get_side_data(avpkt, AV_PKT_DATA_PALETTE, NULL);
00441 
00442     s->buf = buf;
00443     s->size = buf_size;
00444 
00445     s->frame.reference = 1;
00446     s->frame.buffer_hints = FF_BUFFER_HINTS_VALID | FF_BUFFER_HINTS_PRESERVE |
00447                             FF_BUFFER_HINTS_REUSABLE | FF_BUFFER_HINTS_READABLE;
00448     if (avctx->reget_buffer(avctx, &s->frame)) {
00449         av_log(s->avctx, AV_LOG_ERROR, "reget_buffer() failed\n");
00450         return -1;
00451     }
00452 
00453     if (pal) {
00454         s->frame.palette_has_changed = 1;
00455         memcpy(s->pal, pal, AVPALETTE_SIZE);
00456     }
00457 
00458     smc_decode_stream(s);
00459 
00460     *data_size = sizeof(AVFrame);
00461     *(AVFrame*)data = s->frame;
00462 
00463     /* always report that the buffer was completely consumed */
00464     return buf_size;
00465 }
00466 
00467 static av_cold int smc_decode_end(AVCodecContext *avctx)
00468 {
00469     SmcContext *s = avctx->priv_data;
00470 
00471     if (s->frame.data[0])
00472         avctx->release_buffer(avctx, &s->frame);
00473 
00474     return 0;
00475 }
00476 
00477 AVCodec ff_smc_decoder = {
00478     "smc",
00479     AVMEDIA_TYPE_VIDEO,
00480     CODEC_ID_SMC,
00481     sizeof(SmcContext),
00482     smc_decode_init,
00483     NULL,
00484     smc_decode_end,
00485     smc_decode_frame,
00486     CODEC_CAP_DR1,
00487     .long_name = NULL_IF_CONFIG_SMALL("QuickTime Graphics (SMC)"),
00488 };