Home | History | Annotate | Download | only in demux
      1 // Copyright 2015 Google Inc. All Rights Reserved.
      2 //
      3 // Use of this source code is governed by a BSD-style license
      4 // that can be found in the COPYING file in the root of the source
      5 // tree. An additional intellectual property rights grant can be found
      6 // in the file PATENTS. All contributing project authors may
      7 // be found in the AUTHORS file in the root of the source tree.
      8 // -----------------------------------------------------------------------------
      9 //
     10 //  AnimDecoder implementation.
     11 //
     12 
     13 #ifdef HAVE_CONFIG_H
     14 #include "../webp/config.h"
     15 #endif
     16 
     17 #include <assert.h>
     18 #include <string.h>
     19 
     20 #include "../utils/utils.h"
     21 #include "../webp/decode.h"
     22 #include "../webp/demux.h"
     23 
     24 #define NUM_CHANNELS 4
     25 
     26 typedef void (*BlendRowFunc)(uint32_t* const, const uint32_t* const, int);
     27 static void BlendPixelRowNonPremult(uint32_t* const src,
     28                                     const uint32_t* const dst, int num_pixels);
     29 static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
     30                                  int num_pixels);
     31 
     32 struct WebPAnimDecoder {
     33   WebPDemuxer* demux_;             // Demuxer created from given WebP bitstream.
     34   WebPDecoderConfig config_;       // Decoder config.
     35   // Note: we use a pointer to a function blending multiple pixels at a time to
     36   // allow possible inlining of per-pixel blending function.
     37   BlendRowFunc blend_func_;        // Pointer to the chose blend row function.
     38   WebPAnimInfo info_;              // Global info about the animation.
     39   uint8_t* curr_frame_;            // Current canvas (not disposed).
     40   uint8_t* prev_frame_disposed_;   // Previous canvas (properly disposed).
     41   int prev_frame_timestamp_;       // Previous frame timestamp (milliseconds).
     42   WebPIterator prev_iter_;         // Iterator object for previous frame.
     43   int prev_frame_was_keyframe_;    // True if previous frame was a keyframe.
     44   int next_frame_;                 // Index of the next frame to be decoded
     45                                    // (starting from 1).
     46 };
     47 
     48 static void DefaultDecoderOptions(WebPAnimDecoderOptions* const dec_options) {
     49   dec_options->color_mode = MODE_RGBA;
     50   dec_options->use_threads = 0;
     51 }
     52 
     53 int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions* dec_options,
     54                                        int abi_version) {
     55   if (dec_options == NULL ||
     56       WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) {
     57     return 0;
     58   }
     59   DefaultDecoderOptions(dec_options);
     60   return 1;
     61 }
     62 
     63 static int ApplyDecoderOptions(const WebPAnimDecoderOptions* const dec_options,
     64                                WebPAnimDecoder* const dec) {
     65   WEBP_CSP_MODE mode;
     66   WebPDecoderConfig* config = &dec->config_;
     67   assert(dec_options != NULL);
     68 
     69   mode = dec_options->color_mode;
     70   if (mode != MODE_RGBA && mode != MODE_BGRA &&
     71       mode != MODE_rgbA && mode != MODE_bgrA) {
     72     return 0;
     73   }
     74   dec->blend_func_ = (mode == MODE_RGBA || mode == MODE_BGRA)
     75                          ? &BlendPixelRowNonPremult
     76                          : &BlendPixelRowPremult;
     77   WebPInitDecoderConfig(config);
     78   config->output.colorspace = mode;
     79   config->output.is_external_memory = 1;
     80   config->options.use_threads = dec_options->use_threads;
     81   // Note: config->output.u.RGBA is set at the time of decoding each frame.
     82   return 1;
     83 }
     84 
     85 WebPAnimDecoder* WebPAnimDecoderNewInternal(
     86     const WebPData* webp_data, const WebPAnimDecoderOptions* dec_options,
     87     int abi_version) {
     88   WebPAnimDecoderOptions options;
     89   WebPAnimDecoder* dec = NULL;
     90   if (webp_data == NULL ||
     91       WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) {
     92     return NULL;
     93   }
     94 
     95   // Note: calloc() so that the pointer members are initialized to NULL.
     96   dec = (WebPAnimDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
     97   if (dec == NULL) goto Error;
     98 
     99   if (dec_options != NULL) {
    100     options = *dec_options;
    101   } else {
    102     DefaultDecoderOptions(&options);
    103   }
    104   if (!ApplyDecoderOptions(&options, dec)) goto Error;
    105 
    106   dec->demux_ = WebPDemux(webp_data);
    107   if (dec->demux_ == NULL) goto Error;
    108 
    109   dec->info_.canvas_width = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_WIDTH);
    110   dec->info_.canvas_height = WebPDemuxGetI(dec->demux_, WEBP_FF_CANVAS_HEIGHT);
    111   dec->info_.loop_count = WebPDemuxGetI(dec->demux_, WEBP_FF_LOOP_COUNT);
    112   dec->info_.bgcolor = WebPDemuxGetI(dec->demux_, WEBP_FF_BACKGROUND_COLOR);
    113   dec->info_.frame_count = WebPDemuxGetI(dec->demux_, WEBP_FF_FRAME_COUNT);
    114 
    115   // Note: calloc() because we fill frame with zeroes as well.
    116   dec->curr_frame_ = (uint8_t*)WebPSafeCalloc(
    117       dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
    118   if (dec->curr_frame_ == NULL) goto Error;
    119   dec->prev_frame_disposed_ = (uint8_t*)WebPSafeCalloc(
    120       dec->info_.canvas_width * NUM_CHANNELS, dec->info_.canvas_height);
    121   if (dec->prev_frame_disposed_ == NULL) goto Error;
    122 
    123   WebPAnimDecoderReset(dec);
    124   return dec;
    125 
    126  Error:
    127   WebPAnimDecoderDelete(dec);
    128   return NULL;
    129 }
    130 
    131 int WebPAnimDecoderGetInfo(const WebPAnimDecoder* dec, WebPAnimInfo* info) {
    132   if (dec == NULL || info == NULL) return 0;
    133   *info = dec->info_;
    134   return 1;
    135 }
    136 
    137 // Returns true if the frame covers the full canvas.
    138 static int IsFullFrame(int width, int height, int canvas_width,
    139                        int canvas_height) {
    140   return (width == canvas_width && height == canvas_height);
    141 }
    142 
    143 // Clear the canvas to transparent.
    144 static int ZeroFillCanvas(uint8_t* buf, uint32_t canvas_width,
    145                           uint32_t canvas_height) {
    146   const uint64_t size =
    147       (uint64_t)canvas_width * canvas_height * NUM_CHANNELS * sizeof(*buf);
    148   if (size != (size_t)size) return 0;
    149   memset(buf, 0, (size_t)size);
    150   return 1;
    151 }
    152 
    153 // Clear given frame rectangle to transparent.
    154 static void ZeroFillFrameRect(uint8_t* buf, int buf_stride, int x_offset,
    155                               int y_offset, int width, int height) {
    156   int j;
    157   assert(width * NUM_CHANNELS <= buf_stride);
    158   buf += y_offset * buf_stride + x_offset * NUM_CHANNELS;
    159   for (j = 0; j < height; ++j) {
    160     memset(buf, 0, width * NUM_CHANNELS);
    161     buf += buf_stride;
    162   }
    163 }
    164 
    165 // Copy width * height pixels from 'src' to 'dst'.
    166 static int CopyCanvas(const uint8_t* src, uint8_t* dst,
    167                       uint32_t width, uint32_t height) {
    168   const uint64_t size = (uint64_t)width * height * NUM_CHANNELS;
    169   if (size != (size_t)size) return 0;
    170   assert(src != NULL && dst != NULL);
    171   memcpy(dst, src, (size_t)size);
    172   return 1;
    173 }
    174 
    175 // Returns true if the current frame is a key-frame.
    176 static int IsKeyFrame(const WebPIterator* const curr,
    177                       const WebPIterator* const prev,
    178                       int prev_frame_was_key_frame,
    179                       int canvas_width, int canvas_height) {
    180   if (curr->frame_num == 1) {
    181     return 1;
    182   } else if ((!curr->has_alpha || curr->blend_method == WEBP_MUX_NO_BLEND) &&
    183              IsFullFrame(curr->width, curr->height,
    184                          canvas_width, canvas_height)) {
    185     return 1;
    186   } else {
    187     return (prev->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) &&
    188            (IsFullFrame(prev->width, prev->height, canvas_width,
    189                         canvas_height) ||
    190             prev_frame_was_key_frame);
    191   }
    192 }
    193 
    194 
    195 // Blend a single channel of 'src' over 'dst', given their alpha channel values.
    196 // 'src' and 'dst' are assumed to be NOT pre-multiplied by alpha.
    197 static uint8_t BlendChannelNonPremult(uint32_t src, uint8_t src_a,
    198                                       uint32_t dst, uint8_t dst_a,
    199                                       uint32_t scale, int shift) {
    200   const uint8_t src_channel = (src >> shift) & 0xff;
    201   const uint8_t dst_channel = (dst >> shift) & 0xff;
    202   const uint32_t blend_unscaled = src_channel * src_a + dst_channel * dst_a;
    203   assert(blend_unscaled < (1ULL << 32) / scale);
    204   return (blend_unscaled * scale) >> 24;
    205 }
    206 
    207 // Blend 'src' over 'dst' assuming they are NOT pre-multiplied by alpha.
    208 static uint32_t BlendPixelNonPremult(uint32_t src, uint32_t dst) {
    209   const uint8_t src_a = (src >> 24) & 0xff;
    210 
    211   if (src_a == 0) {
    212     return dst;
    213   } else {
    214     const uint8_t dst_a = (dst >> 24) & 0xff;
    215     // This is the approximate integer arithmetic for the actual formula:
    216     // dst_factor_a = (dst_a * (255 - src_a)) / 255.
    217     const uint8_t dst_factor_a = (dst_a * (256 - src_a)) >> 8;
    218     const uint8_t blend_a = src_a + dst_factor_a;
    219     const uint32_t scale = (1UL << 24) / blend_a;
    220 
    221     const uint8_t blend_r =
    222         BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 0);
    223     const uint8_t blend_g =
    224         BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 8);
    225     const uint8_t blend_b =
    226         BlendChannelNonPremult(src, src_a, dst, dst_factor_a, scale, 16);
    227     assert(src_a + dst_factor_a < 256);
    228 
    229     return (blend_r << 0) |
    230            (blend_g << 8) |
    231            (blend_b << 16) |
    232            ((uint32_t)blend_a << 24);
    233   }
    234 }
    235 
    236 // Blend 'num_pixels' in 'src' over 'dst' assuming they are NOT pre-multiplied
    237 // by alpha.
    238 static void BlendPixelRowNonPremult(uint32_t* const src,
    239                                     const uint32_t* const dst, int num_pixels) {
    240   int i;
    241   for (i = 0; i < num_pixels; ++i) {
    242     const uint8_t src_alpha = (src[i] >> 24) & 0xff;
    243     if (src_alpha != 0xff) {
    244       src[i] = BlendPixelNonPremult(src[i], dst[i]);
    245     }
    246   }
    247 }
    248 
    249 // Individually multiply each channel in 'pix' by 'scale'.
    250 static WEBP_INLINE uint32_t ChannelwiseMultiply(uint32_t pix, uint32_t scale) {
    251   uint32_t mask = 0x00FF00FF;
    252   uint32_t rb = ((pix & mask) * scale) >> 8;
    253   uint32_t ag = ((pix >> 8) & mask) * scale;
    254   return (rb & mask) | (ag & ~mask);
    255 }
    256 
    257 // Blend 'src' over 'dst' assuming they are pre-multiplied by alpha.
    258 static uint32_t BlendPixelPremult(uint32_t src, uint32_t dst) {
    259   const uint8_t src_a = (src >> 24) & 0xff;
    260   return src + ChannelwiseMultiply(dst, 256 - src_a);
    261 }
    262 
    263 // Blend 'num_pixels' in 'src' over 'dst' assuming they are pre-multiplied by
    264 // alpha.
    265 static void BlendPixelRowPremult(uint32_t* const src, const uint32_t* const dst,
    266                                  int num_pixels) {
    267   int i;
    268   for (i = 0; i < num_pixels; ++i) {
    269     const uint8_t src_alpha = (src[i] >> 24) & 0xff;
    270     if (src_alpha != 0xff) {
    271       src[i] = BlendPixelPremult(src[i], dst[i]);
    272     }
    273   }
    274 }
    275 
    276 // Returns two ranges (<left, width> pairs) at row 'canvas_y', that belong to
    277 // 'src' but not 'dst'. A point range is empty if the corresponding width is 0.
    278 static void FindBlendRangeAtRow(const WebPIterator* const src,
    279                                 const WebPIterator* const dst, int canvas_y,
    280                                 int* const left1, int* const width1,
    281                                 int* const left2, int* const width2) {
    282   const int src_max_x = src->x_offset + src->width;
    283   const int dst_max_x = dst->x_offset + dst->width;
    284   const int dst_max_y = dst->y_offset + dst->height;
    285   assert(canvas_y >= src->y_offset && canvas_y < (src->y_offset + src->height));
    286   *left1 = -1;
    287   *width1 = 0;
    288   *left2 = -1;
    289   *width2 = 0;
    290 
    291   if (canvas_y < dst->y_offset || canvas_y >= dst_max_y ||
    292       src->x_offset >= dst_max_x || src_max_x <= dst->x_offset) {
    293     *left1 = src->x_offset;
    294     *width1 = src->width;
    295     return;
    296   }
    297 
    298   if (src->x_offset < dst->x_offset) {
    299     *left1 = src->x_offset;
    300     *width1 = dst->x_offset - src->x_offset;
    301   }
    302 
    303   if (src_max_x > dst_max_x) {
    304     *left2 = dst_max_x;
    305     *width2 = src_max_x - dst_max_x;
    306   }
    307 }
    308 
    309 int WebPAnimDecoderGetNext(WebPAnimDecoder* dec,
    310                            uint8_t** buf_ptr, int* timestamp_ptr) {
    311   WebPIterator iter;
    312   uint32_t width;
    313   uint32_t height;
    314   int is_key_frame;
    315   int timestamp;
    316   BlendRowFunc blend_row;
    317 
    318   if (dec == NULL || buf_ptr == NULL || timestamp_ptr == NULL) return 0;
    319   if (!WebPAnimDecoderHasMoreFrames(dec)) return 0;
    320 
    321   width = dec->info_.canvas_width;
    322   height = dec->info_.canvas_height;
    323   blend_row = dec->blend_func_;
    324 
    325   // Get compressed frame.
    326   if (!WebPDemuxGetFrame(dec->demux_, dec->next_frame_, &iter)) {
    327     return 0;
    328   }
    329   timestamp = dec->prev_frame_timestamp_ + iter.duration;
    330 
    331   // Initialize.
    332   is_key_frame = IsKeyFrame(&iter, &dec->prev_iter_,
    333                             dec->prev_frame_was_keyframe_, width, height);
    334   if (is_key_frame) {
    335     if (!ZeroFillCanvas(dec->curr_frame_, width, height)) {
    336       goto Error;
    337     }
    338   } else {
    339     if (!CopyCanvas(dec->prev_frame_disposed_, dec->curr_frame_,
    340                     width, height)) {
    341       goto Error;
    342     }
    343   }
    344 
    345   // Decode.
    346   {
    347     const uint8_t* in = iter.fragment.bytes;
    348     const size_t in_size = iter.fragment.size;
    349     const size_t out_offset =
    350         (iter.y_offset * width + iter.x_offset) * NUM_CHANNELS;
    351     WebPDecoderConfig* const config = &dec->config_;
    352     WebPRGBABuffer* const buf = &config->output.u.RGBA;
    353     buf->stride = NUM_CHANNELS * width;
    354     buf->size = buf->stride * iter.height;
    355     buf->rgba = dec->curr_frame_ + out_offset;
    356 
    357     if (WebPDecode(in, in_size, config) != VP8_STATUS_OK) {
    358       goto Error;
    359     }
    360   }
    361 
    362   // During the decoding of current frame, we may have set some pixels to be
    363   // transparent (i.e. alpha < 255). However, the value of each of these
    364   // pixels should have been determined by blending it against the value of
    365   // that pixel in the previous frame if blending method of is WEBP_MUX_BLEND.
    366   if (iter.frame_num > 1 && iter.blend_method == WEBP_MUX_BLEND &&
    367       !is_key_frame) {
    368     if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_NONE) {
    369       int y;
    370       // Blend transparent pixels with pixels in previous canvas.
    371       for (y = 0; y < iter.height; ++y) {
    372         const size_t offset =
    373             (iter.y_offset + y) * width + iter.x_offset;
    374         blend_row((uint32_t*)dec->curr_frame_ + offset,
    375                   (uint32_t*)dec->prev_frame_disposed_ + offset, iter.width);
    376       }
    377     } else {
    378       int y;
    379       assert(dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND);
    380       // We need to blend a transparent pixel with its value just after
    381       // initialization. That is, blend it with:
    382       // * Fully transparent pixel if it belongs to prevRect <-- No-op.
    383       // * The pixel in the previous canvas otherwise <-- Need alpha-blending.
    384       for (y = 0; y < iter.height; ++y) {
    385         const int canvas_y = iter.y_offset + y;
    386         int left1, width1, left2, width2;
    387         FindBlendRangeAtRow(&iter, &dec->prev_iter_, canvas_y, &left1, &width1,
    388                             &left2, &width2);
    389         if (width1 > 0) {
    390           const size_t offset1 = canvas_y * width + left1;
    391           blend_row((uint32_t*)dec->curr_frame_ + offset1,
    392                     (uint32_t*)dec->prev_frame_disposed_ + offset1, width1);
    393         }
    394         if (width2 > 0) {
    395           const size_t offset2 = canvas_y * width + left2;
    396           blend_row((uint32_t*)dec->curr_frame_ + offset2,
    397                     (uint32_t*)dec->prev_frame_disposed_ + offset2, width2);
    398         }
    399       }
    400     }
    401   }
    402 
    403   // Update info of the previous frame and dispose it for the next iteration.
    404   dec->prev_frame_timestamp_ = timestamp;
    405   WebPDemuxReleaseIterator(&dec->prev_iter_);
    406   dec->prev_iter_ = iter;
    407   dec->prev_frame_was_keyframe_ = is_key_frame;
    408   CopyCanvas(dec->curr_frame_, dec->prev_frame_disposed_, width, height);
    409   if (dec->prev_iter_.dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
    410     ZeroFillFrameRect(dec->prev_frame_disposed_, width * NUM_CHANNELS,
    411                       dec->prev_iter_.x_offset, dec->prev_iter_.y_offset,
    412                       dec->prev_iter_.width, dec->prev_iter_.height);
    413   }
    414   ++dec->next_frame_;
    415 
    416   // All OK, fill in the values.
    417   *buf_ptr = dec->curr_frame_;
    418   *timestamp_ptr = timestamp;
    419   return 1;
    420 
    421  Error:
    422   WebPDemuxReleaseIterator(&iter);
    423   return 0;
    424 }
    425 
    426 int WebPAnimDecoderHasMoreFrames(const WebPAnimDecoder* dec) {
    427   if (dec == NULL) return 0;
    428   return (dec->next_frame_ <= (int)dec->info_.frame_count);
    429 }
    430 
    431 void WebPAnimDecoderReset(WebPAnimDecoder* dec) {
    432   if (dec != NULL) {
    433     dec->prev_frame_timestamp_ = 0;
    434     WebPDemuxReleaseIterator(&dec->prev_iter_);
    435     memset(&dec->prev_iter_, 0, sizeof(dec->prev_iter_));
    436     dec->prev_frame_was_keyframe_ = 0;
    437     dec->next_frame_ = 1;
    438   }
    439 }
    440 
    441 const WebPDemuxer* WebPAnimDecoderGetDemuxer(const WebPAnimDecoder* dec) {
    442   if (dec == NULL) return NULL;
    443   return dec->demux_;
    444 }
    445 
    446 void WebPAnimDecoderDelete(WebPAnimDecoder* dec) {
    447   if (dec != NULL) {
    448     WebPDemuxReleaseIterator(&dec->prev_iter_);
    449     WebPDemuxDelete(dec->demux_);
    450     WebPSafeFree(dec->curr_frame_);
    451     WebPSafeFree(dec->prev_frame_disposed_);
    452     WebPSafeFree(dec);
    453   }
    454 }
    455