static int CacheFrame()

in Extended/libwebp/src/mux/anim_encode.c [1196:1295]


static int CacheFrame(WebPAnimEncoder* const enc,
                      const WebPConfig* const config) {
  int ok = 0;
  int frame_skipped = 0;
  WebPEncodingError error_code = VP8_ENC_OK;
  const size_t position = enc->count_;
  EncodedFrame* const encoded_frame = GetFrame(enc, position);

  ++enc->count_;

  if (enc->is_first_frame_) {  // Add this as a key-frame.
    error_code = SetFrame(enc, config, 1, encoded_frame, &frame_skipped);
    if (error_code != VP8_ENC_OK) goto End;
    assert(frame_skipped == 0);  // First frame can't be skipped, even if empty.
    assert(position == 0 && enc->count_ == 1);
    encoded_frame->is_key_frame_ = 1;
    enc->flush_count_ = 0;
    enc->count_since_key_frame_ = 0;
    enc->prev_candidate_undecided_ = 0;
  } else {
    ++enc->count_since_key_frame_;
    if (enc->count_since_key_frame_ <= enc->options_.kmin) {
      // Add this as a frame rectangle.
      error_code = SetFrame(enc, config, 0, encoded_frame, &frame_skipped);
      if (error_code != VP8_ENC_OK) goto End;
      if (frame_skipped) goto Skip;
      encoded_frame->is_key_frame_ = 0;
      enc->flush_count_ = enc->count_ - 1;
      enc->prev_candidate_undecided_ = 0;
    } else {
      int64_t curr_delta;
      FrameRectangle prev_rect_key, prev_rect_sub;

      // Add this as a frame rectangle to enc.
      error_code = SetFrame(enc, config, 0, encoded_frame, &frame_skipped);
      if (error_code != VP8_ENC_OK) goto End;
      if (frame_skipped) goto Skip;
      prev_rect_sub = enc->prev_rect_;


      // Add this as a key-frame to enc, too.
      error_code = SetFrame(enc, config, 1, encoded_frame, &frame_skipped);
      if (error_code != VP8_ENC_OK) goto End;
      assert(frame_skipped == 0);  // Key-frame cannot be an empty rectangle.
      prev_rect_key = enc->prev_rect_;

      // Analyze size difference of the two variants.
      curr_delta = KeyFramePenalty(encoded_frame);
      if (curr_delta <= enc->best_delta_) {  // Pick this as the key-frame.
        if (enc->keyframe_ != KEYFRAME_NONE) {
          EncodedFrame* const old_keyframe = GetFrame(enc, enc->keyframe_);
          assert(old_keyframe->is_key_frame_);
          old_keyframe->is_key_frame_ = 0;
        }
        encoded_frame->is_key_frame_ = 1;
        enc->prev_candidate_undecided_ = 1;
        enc->keyframe_ = (int)position;
        enc->best_delta_ = curr_delta;
        enc->flush_count_ = enc->count_ - 1;  // We can flush previous frames.
      } else {
        encoded_frame->is_key_frame_ = 0;
        enc->prev_candidate_undecided_ = 0;
      }
      // Note: We need '>=' below because when kmin and kmax are both zero,
      // count_since_key_frame will always be > kmax.
      if (enc->count_since_key_frame_ >= enc->options_.kmax) {
        enc->flush_count_ = enc->count_ - 1;
        enc->count_since_key_frame_ = 0;
        enc->keyframe_ = KEYFRAME_NONE;
        enc->best_delta_ = DELTA_INFINITY;
      }
      if (!enc->prev_candidate_undecided_) {
        enc->prev_rect_ =
            encoded_frame->is_key_frame_ ? prev_rect_key : prev_rect_sub;
      }
    }
  }

  // Update previous to previous and previous canvases for next call.
  WebPCopyPixels(enc->curr_canvas_, &enc->prev_canvas_);
  enc->is_first_frame_ = 0;

 Skip:
  ok = 1;
  ++enc->in_frame_count_;

 End:
  if (!ok || frame_skipped) {
    FrameRelease(encoded_frame);
    // We reset some counters, as the frame addition failed/was skipped.
    --enc->count_;
    if (!enc->is_first_frame_) --enc->count_since_key_frame_;
    if (!ok) {
      MarkError2(enc, "ERROR adding frame. WebPEncodingError", error_code);
    }
  }
  enc->curr_canvas_->error_code = error_code;   // report error_code
  assert(ok || error_code != VP8_ENC_OK);
  return ok;
}