github.com/cellofellow/gopkg@v0.0.0-20140722061823-eec0544a62ad/image/webp/libwebp/examples/gif2webp_util.c (about)

     1  // Copyright 2013 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  //  Helper structs and methods for gif2webp tool.
    11  //
    12  
    13  #include <assert.h>
    14  #include <stdio.h>
    15  
    16  #include "webp/encode.h"
    17  #include "./gif2webp_util.h"
    18  
    19  #define DELTA_INFINITY      1ULL << 32
    20  #define KEYFRAME_NONE       -1
    21  
    22  //------------------------------------------------------------------------------
    23  // Helper utilities.
    24  
    25  static void ClearRectangle(WebPPicture* const picture,
    26                             int left, int top, int width, int height) {
    27    int j;
    28    for (j = top; j < top + height; ++j) {
    29      uint32_t* const dst = picture->argb + j * picture->argb_stride;
    30      int i;
    31      for (i = left; i < left + width; ++i) {
    32        dst[i] = WEBP_UTIL_TRANSPARENT_COLOR;
    33      }
    34    }
    35  }
    36  
    37  void WebPUtilClearPic(WebPPicture* const picture,
    38                        const WebPFrameRect* const rect) {
    39    if (rect != NULL) {
    40      ClearRectangle(picture, rect->x_offset, rect->y_offset,
    41                     rect->width, rect->height);
    42    } else {
    43      ClearRectangle(picture, 0, 0, picture->width, picture->height);
    44    }
    45  }
    46  
    47  // TODO: Also used in picture.c. Move to a common location?
    48  // Copy width x height pixels from 'src' to 'dst' honoring the strides.
    49  static void CopyPlane(const uint8_t* src, int src_stride,
    50                        uint8_t* dst, int dst_stride, int width, int height) {
    51    while (height-- > 0) {
    52      memcpy(dst, src, width);
    53      src += src_stride;
    54      dst += dst_stride;
    55    }
    56  }
    57  
    58  // Copy pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are assumed
    59  // to be already allocated.
    60  static void CopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
    61    assert(src->width == dst->width && src->height == dst->height);
    62    CopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
    63              4 * dst->argb_stride, 4 * src->width, src->height);
    64  }
    65  
    66  // Given 'src' picture and its frame rectangle 'rect', blend it into 'dst'.
    67  static void BlendPixels(const WebPPicture* const src,
    68                          const WebPFrameRect* const rect,
    69                          WebPPicture* const dst) {
    70    int j;
    71    assert(src->width == dst->width && src->height == dst->height);
    72    for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
    73      int i;
    74      for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
    75        const uint32_t src_pixel = src->argb[j * src->argb_stride + i];
    76        const int src_alpha = src_pixel >> 24;
    77        if (src_alpha != 0) {
    78          dst->argb[j * dst->argb_stride + i] = src_pixel;
    79        }
    80      }
    81    }
    82  }
    83  
    84  // Replace transparent pixels within 'dst_rect' of 'dst' by those in the 'src'.
    85  static void ReduceTransparency(const WebPPicture* const src,
    86                                 const WebPFrameRect* const rect,
    87                                 WebPPicture* const dst) {
    88    int i, j;
    89    assert(src != NULL && dst != NULL && rect != NULL);
    90    assert(src->width == dst->width && src->height == dst->height);
    91    for (j = rect->y_offset; j < rect->y_offset + rect->height; ++j) {
    92      for (i = rect->x_offset; i < rect->x_offset + rect->width; ++i) {
    93        const uint32_t src_pixel = src->argb[j * src->argb_stride + i];
    94        const int src_alpha = src_pixel >> 24;
    95        const uint32_t dst_pixel = dst->argb[j * dst->argb_stride + i];
    96        const int dst_alpha = dst_pixel >> 24;
    97        if (dst_alpha == 0 && src_alpha == 0xff) {
    98          dst->argb[j * dst->argb_stride + i] = src_pixel;
    99        }
   100      }
   101    }
   102  }
   103  
   104  // Replace similar blocks of pixels by a 'see-through' transparent block
   105  // with uniform average color.
   106  static void FlattenSimilarBlocks(const WebPPicture* const src,
   107                                   const WebPFrameRect* const rect,
   108                                   WebPPicture* const dst) {
   109    int i, j;
   110    const int block_size = 8;
   111    const int y_start = (rect->y_offset + block_size) & ~(block_size - 1);
   112    const int y_end = (rect->y_offset + rect->height) & ~(block_size - 1);
   113    const int x_start = (rect->x_offset + block_size) & ~(block_size - 1);
   114    const int x_end = (rect->x_offset + rect->width) & ~(block_size - 1);
   115    assert(src != NULL && dst != NULL && rect != NULL);
   116    assert(src->width == dst->width && src->height == dst->height);
   117    assert((block_size & (block_size - 1)) == 0);  // must be a power of 2
   118    // Iterate over each block and count similar pixels.
   119    for (j = y_start; j < y_end; j += block_size) {
   120      for (i = x_start; i < x_end; i += block_size) {
   121        int cnt = 0;
   122        int avg_r = 0, avg_g = 0, avg_b = 0;
   123        int x, y;
   124        const uint32_t* const psrc = src->argb + j * src->argb_stride + i;
   125        uint32_t* const pdst = dst->argb + j * dst->argb_stride + i;
   126        for (y = 0; y < block_size; ++y) {
   127          for (x = 0; x < block_size; ++x) {
   128            const uint32_t src_pixel = psrc[x + y * src->argb_stride];
   129            const int alpha = src_pixel >> 24;
   130            if (alpha == 0xff &&
   131                src_pixel == pdst[x + y * dst->argb_stride]) {
   132                ++cnt;
   133                avg_r += (src_pixel >> 16) & 0xff;
   134                avg_g += (src_pixel >>  8) & 0xff;
   135                avg_b += (src_pixel >>  0) & 0xff;
   136            }
   137          }
   138        }
   139        // If we have a fully similar block, we replace it with an
   140        // average transparent block. This compresses better in lossy mode.
   141        if (cnt == block_size * block_size) {
   142          const uint32_t color = (0x00          << 24) |
   143                                 ((avg_r / cnt) << 16) |
   144                                 ((avg_g / cnt) <<  8) |
   145                                 ((avg_b / cnt) <<  0);
   146          for (y = 0; y < block_size; ++y) {
   147            for (x = 0; x < block_size; ++x) {
   148              pdst[x + y * dst->argb_stride] = color;
   149            }
   150          }
   151        }
   152      }
   153    }
   154  }
   155  
   156  //------------------------------------------------------------------------------
   157  // Key frame related utilities.
   158  
   159  // Returns true if 'curr' frame with frame rectangle 'curr_rect' is a key frame,
   160  // that is, it can be decoded independently of 'prev' canvas.
   161  static int IsKeyFrame(const WebPPicture* const curr,
   162                        const WebPFrameRect* const curr_rect,
   163                        const WebPPicture* const prev) {
   164    int i, j;
   165    int is_key_frame = 1;
   166  
   167    // If previous canvas (with previous frame disposed) is all transparent,
   168    // current frame is a key frame.
   169    for (i = 0; i < prev->width; ++i) {
   170      for (j = 0; j < prev->height; ++j) {
   171        const uint32_t prev_alpha = (prev->argb[j * prev->argb_stride + i]) >> 24;
   172        if (prev_alpha != 0) {
   173          is_key_frame = 0;
   174          break;
   175        }
   176      }
   177      if (!is_key_frame) break;
   178    }
   179    if (is_key_frame) return 1;
   180  
   181    // If current frame covers the whole canvas and does not contain any
   182    // transparent pixels that depend on previous canvas, then current frame is
   183    // a key frame.
   184    if (curr_rect->width == curr->width && curr_rect->height == curr->height) {
   185      assert(curr_rect->x_offset == 0 && curr_rect->y_offset == 0);
   186      is_key_frame = 1;
   187      for (j = 0; j < prev->height; ++j) {
   188        for (i = 0; i < prev->width; ++i) {
   189          const uint32_t prev_alpha =
   190              (prev->argb[j * prev->argb_stride + i]) >> 24;
   191          const uint32_t curr_alpha =
   192              (curr->argb[j * curr->argb_stride + i]) >> 24;
   193          if (curr_alpha != 0xff && prev_alpha != 0) {
   194            is_key_frame = 0;
   195            break;
   196          }
   197        }
   198        if (!is_key_frame) break;
   199      }
   200      if (is_key_frame) return 1;
   201    }
   202  
   203    return 0;
   204  }
   205  
   206  // Given 'prev' frame and current frame rectangle 'rect', convert 'curr' frame
   207  // to a key frame.
   208  static void ConvertToKeyFrame(const WebPPicture* const prev,
   209                                WebPFrameRect* const rect,
   210                                WebPPicture* const curr) {
   211    int j;
   212    assert(curr->width == prev->width && curr->height == prev->height);
   213  
   214    // Replace transparent pixels of current canvas with those from previous
   215    // canvas (with previous frame disposed).
   216    for (j = 0; j < curr->height; ++j) {
   217      int i;
   218      for (i = 0; i < curr->width; ++i) {
   219        uint32_t* const curr_pixel = curr->argb + j * curr->argb_stride + i;
   220        const int curr_alpha = *curr_pixel >> 24;
   221        if (curr_alpha == 0) {
   222          *curr_pixel = prev->argb[j * prev->argb_stride + i];
   223        }
   224      }
   225    }
   226  
   227    // Frame rectangle now covers the whole canvas.
   228    rect->x_offset = 0;
   229    rect->y_offset = 0;
   230    rect->width = curr->width;
   231    rect->height = curr->height;
   232  }
   233  
   234  //------------------------------------------------------------------------------
   235  // Encoded frame.
   236  
   237  // Used to store two candidates of encoded data for an animation frame. One of
   238  // the two will be chosen later.
   239  typedef struct {
   240    WebPMuxFrameInfo sub_frame;  // Encoded frame rectangle.
   241    WebPMuxFrameInfo key_frame;  // Encoded frame if it was converted to keyframe.
   242  } EncodedFrame;
   243  
   244  // Release the data contained by 'encoded_frame'.
   245  static void FrameRelease(EncodedFrame* const encoded_frame) {
   246    if (encoded_frame != NULL) {
   247      WebPDataClear(&encoded_frame->sub_frame.bitstream);
   248      WebPDataClear(&encoded_frame->key_frame.bitstream);
   249      memset(encoded_frame, 0, sizeof(*encoded_frame));
   250    }
   251  }
   252  
   253  //------------------------------------------------------------------------------
   254  // Frame cache.
   255  
   256  // Used to store encoded frames that haven't been output yet.
   257  struct WebPFrameCache {
   258    EncodedFrame* encoded_frames;  // Array of encoded frames.
   259    size_t size;               // Number of allocated data elements.
   260    size_t start;              // Start index.
   261    size_t count;              // Number of valid data elements.
   262    int flush_count;           // If >0, ‘flush_count’ frames starting from
   263                               // 'start' are ready to be added to mux.
   264    int64_t best_delta;        // min(canvas size - frame size) over the frames.
   265                               // Can be negative in certain cases due to
   266                               // transparent pixels in a frame.
   267    int keyframe;              // Index of selected keyframe relative to 'start'.
   268  
   269    size_t kmin;                   // Min distance between key frames.
   270    size_t kmax;                   // Max distance between key frames.
   271    size_t count_since_key_frame;  // Frames seen since the last key frame.
   272    int allow_mixed;           // If true, each frame can be lossy or lossless.
   273    WebPPicture prev_canvas;   // Previous canvas (properly disposed).
   274    WebPPicture curr_canvas;   // Current canvas (temporary buffer).
   275    int is_first_frame;        // True if no frames have been added to the cache
   276                               // since WebPFrameCacheNew().
   277  };
   278  
   279  // Reset the counters in the cache struct. Doesn't touch 'cache->encoded_frames'
   280  // and 'cache->size'.
   281  static void CacheReset(WebPFrameCache* const cache) {
   282    cache->start = 0;
   283    cache->count = 0;
   284    cache->flush_count = 0;
   285    cache->best_delta = DELTA_INFINITY;
   286    cache->keyframe = KEYFRAME_NONE;
   287  }
   288  
   289  WebPFrameCache* WebPFrameCacheNew(int width, int height,
   290                                    size_t kmin, size_t kmax, int allow_mixed) {
   291    WebPFrameCache* cache = (WebPFrameCache*)malloc(sizeof(*cache));
   292    if (cache == NULL) return NULL;
   293    CacheReset(cache);
   294    // sanity init, so we can call WebPFrameCacheDelete():
   295    cache->encoded_frames = NULL;
   296  
   297    cache->is_first_frame = 1;
   298  
   299    // Picture buffers.
   300    if (!WebPPictureInit(&cache->prev_canvas) ||
   301        !WebPPictureInit(&cache->curr_canvas)) {
   302      return NULL;
   303    }
   304    cache->prev_canvas.width = width;
   305    cache->prev_canvas.height = height;
   306    cache->prev_canvas.use_argb = 1;
   307    if (!WebPPictureAlloc(&cache->prev_canvas) ||
   308        !WebPPictureCopy(&cache->prev_canvas, &cache->curr_canvas)) {
   309      goto Err;
   310    }
   311    WebPUtilClearPic(&cache->prev_canvas, NULL);
   312  
   313    // Cache data.
   314    cache->allow_mixed = allow_mixed;
   315    cache->kmin = kmin;
   316    cache->kmax = kmax;
   317    cache->count_since_key_frame = 0;
   318    assert(kmax > kmin);
   319    cache->size = kmax - kmin;
   320    cache->encoded_frames =
   321        (EncodedFrame*)calloc(cache->size, sizeof(*cache->encoded_frames));
   322    if (cache->encoded_frames == NULL) goto Err;
   323  
   324    return cache;  // All OK.
   325  
   326   Err:
   327    WebPFrameCacheDelete(cache);
   328    return NULL;
   329  }
   330  
   331  void WebPFrameCacheDelete(WebPFrameCache* const cache) {
   332    if (cache != NULL) {
   333      if (cache->encoded_frames != NULL) {
   334        size_t i;
   335        for (i = 0; i < cache->size; ++i) {
   336          FrameRelease(&cache->encoded_frames[i]);
   337        }
   338        free(cache->encoded_frames);
   339      }
   340      WebPPictureFree(&cache->prev_canvas);
   341      WebPPictureFree(&cache->curr_canvas);
   342      free(cache);
   343    }
   344  }
   345  
   346  static int EncodeFrame(const WebPConfig* const config, WebPPicture* const pic,
   347                         WebPMemoryWriter* const memory) {
   348    pic->use_argb = 1;
   349    pic->writer = WebPMemoryWrite;
   350    pic->custom_ptr = memory;
   351    if (!WebPEncode(config, pic)) {
   352      return 0;
   353    }
   354    return 1;
   355  }
   356  
   357  static void GetEncodedData(const WebPMemoryWriter* const memory,
   358                             WebPData* const encoded_data) {
   359    encoded_data->bytes = memory->mem;
   360    encoded_data->size  = memory->size;
   361  }
   362  
   363  #define MIN_COLORS_LOSSY     31  // Don't try lossy below this threshold.
   364  #define MAX_COLORS_LOSSLESS 194  // Don't try lossless above this threshold.
   365  #define MAX_COLOR_COUNT     256  // Power of 2 greater than MAX_COLORS_LOSSLESS.
   366  #define HASH_SIZE (MAX_COLOR_COUNT * 4)
   367  #define HASH_RIGHT_SHIFT     22  // 32 - log2(HASH_SIZE).
   368  
   369  // TODO(urvang): Also used in enc/vp8l.c. Move to utils.
   370  // If the number of colors in the 'pic' is at least MAX_COLOR_COUNT, return
   371  // MAX_COLOR_COUNT. Otherwise, return the exact number of colors in the 'pic'.
   372  static int GetColorCount(const WebPPicture* const pic) {
   373    int x, y;
   374    int num_colors = 0;
   375    uint8_t in_use[HASH_SIZE] = { 0 };
   376    uint32_t colors[HASH_SIZE];
   377    static const uint32_t kHashMul = 0x1e35a7bd;
   378    const uint32_t* argb = pic->argb;
   379    const int width = pic->width;
   380    const int height = pic->height;
   381    uint32_t last_pix = ~argb[0];   // so we're sure that last_pix != argb[0]
   382  
   383    for (y = 0; y < height; ++y) {
   384      for (x = 0; x < width; ++x) {
   385        int key;
   386        if (argb[x] == last_pix) {
   387          continue;
   388        }
   389        last_pix = argb[x];
   390        key = (kHashMul * last_pix) >> HASH_RIGHT_SHIFT;
   391        while (1) {
   392          if (!in_use[key]) {
   393            colors[key] = last_pix;
   394            in_use[key] = 1;
   395            ++num_colors;
   396            if (num_colors >= MAX_COLOR_COUNT) {
   397              return MAX_COLOR_COUNT;  // Exact count not needed.
   398            }
   399            break;
   400          } else if (colors[key] == last_pix) {
   401            break;  // The color is already there.
   402          } else {
   403            // Some other color sits here, so do linear conflict resolution.
   404            ++key;
   405            key &= (HASH_SIZE - 1);  // Key mask.
   406          }
   407        }
   408      }
   409      argb += pic->argb_stride;
   410    }
   411    return num_colors;
   412  }
   413  
   414  #undef MAX_COLOR_COUNT
   415  #undef HASH_SIZE
   416  #undef HASH_RIGHT_SHIFT
   417  
   418  static int SetFrame(const WebPConfig* const config, int allow_mixed,
   419                      int is_key_frame, const WebPPicture* const prev_canvas,
   420                      WebPPicture* const frame, const WebPFrameRect* const rect,
   421                      const WebPMuxFrameInfo* const info,
   422                      WebPPicture* const sub_frame, EncodedFrame* encoded_frame) {
   423    int try_lossless;
   424    int try_lossy;
   425    int try_both;
   426    WebPMemoryWriter mem1, mem2;
   427    WebPData* encoded_data;
   428    WebPMuxFrameInfo* const dst =
   429        is_key_frame ? &encoded_frame->key_frame : &encoded_frame->sub_frame;
   430    *dst = *info;
   431    encoded_data = &dst->bitstream;
   432    WebPMemoryWriterInit(&mem1);
   433    WebPMemoryWriterInit(&mem2);
   434  
   435    if (!allow_mixed) {
   436      try_lossless = config->lossless;
   437      try_lossy = !try_lossless;
   438    } else {  // Use a heuristic for trying lossless and/or lossy compression.
   439      const int num_colors = GetColorCount(sub_frame);
   440      try_lossless = (num_colors < MAX_COLORS_LOSSLESS);
   441      try_lossy = (num_colors >= MIN_COLORS_LOSSY);
   442    }
   443    try_both = try_lossless && try_lossy;
   444  
   445    if (try_lossless) {
   446      WebPConfig config_ll = *config;
   447      config_ll.lossless = 1;
   448      if (!EncodeFrame(&config_ll, sub_frame, &mem1)) {
   449        goto Err;
   450      }
   451    }
   452  
   453    if (try_lossy) {
   454      WebPConfig config_lossy = *config;
   455      config_lossy.lossless = 0;
   456      if (!is_key_frame) {
   457        // For lossy compression of a frame, it's better to replace transparent
   458        // pixels of 'curr' with actual RGB values, whenever possible.
   459        ReduceTransparency(prev_canvas, rect, frame);
   460        // TODO(later): Investigate if this helps lossless compression as well.
   461        FlattenSimilarBlocks(prev_canvas, rect, frame);
   462      }
   463      if (!EncodeFrame(&config_lossy, sub_frame, &mem2)) {
   464        goto Err;
   465      }
   466    }
   467  
   468    if (try_both) {  // Pick the encoding with smallest size.
   469      // TODO(later): Perhaps a rough SSIM/PSNR produced by the encoder should
   470      // also be a criteria, in addition to sizes.
   471      if (mem1.size <= mem2.size) {
   472        free(mem2.mem);
   473        GetEncodedData(&mem1, encoded_data);
   474      } else {
   475        free(mem1.mem);
   476        GetEncodedData(&mem2, encoded_data);
   477      }
   478    } else {
   479      GetEncodedData(try_lossless ? &mem1 : &mem2, encoded_data);
   480    }
   481    return 1;
   482  
   483   Err:
   484    free(mem1.mem);
   485    free(mem2.mem);
   486    return 0;
   487  }
   488  
   489  #undef MIN_COLORS_LOSSY
   490  #undef MAX_COLORS_LOSSLESS
   491  
   492  // Returns cached frame at given 'position' index.
   493  static EncodedFrame* CacheGetFrame(const WebPFrameCache* const cache,
   494                                     size_t position) {
   495    assert(cache->start + position < cache->size);
   496    return &cache->encoded_frames[cache->start + position];
   497  }
   498  
   499  // Calculate the penalty incurred if we encode given frame as a key frame
   500  // instead of a sub-frame.
   501  static int64_t KeyFramePenalty(const EncodedFrame* const encoded_frame) {
   502    return ((int64_t)encoded_frame->key_frame.bitstream.size -
   503            encoded_frame->sub_frame.bitstream.size);
   504  }
   505  
   506  static void DisposeFrame(WebPMuxAnimDispose dispose_method,
   507                           const WebPFrameRect* const gif_rect,
   508                           WebPPicture* const frame, WebPPicture* const canvas) {
   509    if (dispose_method == WEBP_MUX_DISPOSE_BACKGROUND) {
   510      WebPUtilClearPic(frame, NULL);
   511      WebPUtilClearPic(canvas, gif_rect);
   512    }
   513  }
   514  
   515  int WebPFrameCacheAddFrame(WebPFrameCache* const cache,
   516                             const WebPConfig* const config,
   517                             const WebPFrameRect* const orig_rect,
   518                             WebPPicture* const frame,
   519                             WebPMuxFrameInfo* const info) {
   520    int ok = 0;
   521    WebPFrameRect rect = *orig_rect;
   522    WebPPicture sub_image;  // View extracted from 'frame' with rectangle 'rect'.
   523    WebPPicture* const prev_canvas = &cache->prev_canvas;
   524    const size_t position = cache->count;
   525    const int allow_mixed = cache->allow_mixed;
   526    EncodedFrame* const encoded_frame = CacheGetFrame(cache, position);
   527    assert(position < cache->size);
   528  
   529    // Snap to even offsets (and adjust dimensions if needed).
   530    rect.width += (rect.x_offset & 1);
   531    rect.height += (rect.y_offset & 1);
   532    rect.x_offset &= ~1;
   533    rect.y_offset &= ~1;
   534  
   535    if (!WebPPictureView(frame, rect.x_offset, rect.y_offset,
   536                         rect.width, rect.height, &sub_image)) {
   537      return 0;
   538    }
   539    info->x_offset = rect.x_offset;
   540    info->y_offset = rect.y_offset;
   541  
   542    ++cache->count;
   543  
   544    if (cache->is_first_frame || IsKeyFrame(frame, &rect, prev_canvas)) {
   545      // Add this as a key frame.
   546      if (!SetFrame(config, allow_mixed, 1, NULL, NULL, NULL, info, &sub_image,
   547                    encoded_frame)) {
   548        goto End;
   549      }
   550      cache->keyframe = position;
   551      cache->flush_count = cache->count;
   552      cache->count_since_key_frame = 0;
   553      // Update prev_canvas by simply copying from 'curr'.
   554      CopyPixels(frame, prev_canvas);
   555    } else {
   556      ++cache->count_since_key_frame;
   557      if (cache->count_since_key_frame <= cache->kmin) {
   558        // Add this as a frame rectangle.
   559        if (!SetFrame(config, allow_mixed, 0, prev_canvas, frame, &rect, info,
   560                      &sub_image, encoded_frame)) {
   561          goto End;
   562        }
   563        cache->flush_count = cache->count;
   564        // Update prev_canvas by blending 'curr' into it.
   565        BlendPixels(frame, orig_rect, prev_canvas);
   566      } else {
   567        WebPPicture full_image;
   568        WebPMuxFrameInfo full_image_info;
   569        int frame_added;
   570        int64_t curr_delta;
   571  
   572        // Add frame rectangle to cache.
   573        if (!SetFrame(config, allow_mixed, 0, prev_canvas, frame, &rect, info,
   574                      &sub_image, encoded_frame)) {
   575          goto End;
   576        }
   577  
   578        // Convert to a key frame.
   579        CopyPixels(frame, &cache->curr_canvas);
   580        ConvertToKeyFrame(prev_canvas, &rect, &cache->curr_canvas);
   581        if (!WebPPictureView(&cache->curr_canvas, rect.x_offset, rect.y_offset,
   582                             rect.width, rect.height, &full_image)) {
   583          goto End;
   584        }
   585        full_image_info = *info;
   586        full_image_info.x_offset = rect.x_offset;
   587        full_image_info.y_offset = rect.y_offset;
   588  
   589        // Add key frame to cache, too.
   590        frame_added = SetFrame(config, allow_mixed, 1, NULL, NULL, NULL,
   591                               &full_image_info, &full_image, encoded_frame);
   592        WebPPictureFree(&full_image);
   593        if (!frame_added) goto End;
   594  
   595        // Analyze size difference of the two variants.
   596        curr_delta = KeyFramePenalty(encoded_frame);
   597        if (curr_delta <= cache->best_delta) {  // Pick this as keyframe.
   598          cache->keyframe = position;
   599          cache->best_delta = curr_delta;
   600          cache->flush_count = cache->count - 1;  // We can flush previous frames.
   601        }
   602        if (cache->count_since_key_frame == cache->kmax) {
   603          cache->flush_count = cache->count;
   604          cache->count_since_key_frame = 0;
   605        }
   606  
   607        // Update prev_canvas by simply copying from 'curr_canvas'.
   608        CopyPixels(&cache->curr_canvas, prev_canvas);
   609      }
   610    }
   611  
   612    DisposeFrame(info->dispose_method, orig_rect, frame, prev_canvas);
   613  
   614    cache->is_first_frame = 0;
   615    ok = 1;
   616  
   617   End:
   618    WebPPictureFree(&sub_image);
   619    if (!ok) {
   620      FrameRelease(encoded_frame);
   621      --cache->count;  // We reset the count, as the frame addition failed.
   622    }
   623    return ok;
   624  }
   625  
   626  WebPMuxError WebPFrameCacheFlush(WebPFrameCache* const cache, int verbose,
   627                                   WebPMux* const mux) {
   628    while (cache->flush_count > 0) {
   629      WebPMuxFrameInfo* info;
   630      WebPMuxError err;
   631      EncodedFrame* const curr = CacheGetFrame(cache, 0);
   632      // Pick frame or full canvas.
   633      if (cache->keyframe == 0) {
   634        info = &curr->key_frame;
   635        info->blend_method = WEBP_MUX_NO_BLEND;
   636        cache->keyframe = KEYFRAME_NONE;
   637        cache->best_delta = DELTA_INFINITY;
   638      } else {
   639        info = &curr->sub_frame;
   640        info->blend_method = WEBP_MUX_BLEND;
   641      }
   642      // Add to mux.
   643      err = WebPMuxPushFrame(mux, info, 1);
   644      if (err != WEBP_MUX_OK) return err;
   645      if (verbose) {
   646        printf("Added frame. offset:%d,%d duration:%d dispose:%d blend:%d\n",
   647               info->x_offset, info->y_offset, info->duration,
   648               info->dispose_method, info->blend_method);
   649      }
   650      FrameRelease(curr);
   651      ++cache->start;
   652      --cache->flush_count;
   653      --cache->count;
   654      if (cache->keyframe != KEYFRAME_NONE) --cache->keyframe;
   655    }
   656  
   657    if (cache->count == 0) CacheReset(cache);
   658    return WEBP_MUX_OK;
   659  }
   660  
   661  WebPMuxError WebPFrameCacheFlushAll(WebPFrameCache* const cache, int verbose,
   662                                      WebPMux* const mux) {
   663    cache->flush_count = cache->count;  // Force flushing of all frames.
   664    return WebPFrameCacheFlush(cache, verbose, mux);
   665  }
   666  
   667  //------------------------------------------------------------------------------