go-libwebp

Experimental translation from libwebp to Go source.
Log | Files | Refs | README | LICENSE

anim_decode.c (17495B)


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