in Extended/libwebp/src/enc/vp8l_enc.c [1516:1675]
static int EncodeStreamHook(void* input, void* data2) {
StreamEncodeContext* const params = (StreamEncodeContext*)input;
const WebPConfig* const config = params->config_;
const WebPPicture* const picture = params->picture_;
VP8LBitWriter* const bw = params->bw_;
VP8LEncoder* const enc = params->enc_;
const int use_cache = params->use_cache_;
const CrunchConfig* const crunch_configs = params->crunch_configs_;
const int num_crunch_configs = params->num_crunch_configs_;
const int red_and_blue_always_zero = params->red_and_blue_always_zero_;
#if !defined(WEBP_DISABLE_STATS)
WebPAuxStats* const stats = params->stats_;
#endif
WebPEncodingError err = VP8_ENC_OK;
const int quality = (int)config->quality;
const int low_effort = (config->method == 0);
#if (WEBP_NEAR_LOSSLESS == 1)
const int width = picture->width;
#endif
const int height = picture->height;
const size_t byte_position = VP8LBitWriterNumBytes(bw);
#if (WEBP_NEAR_LOSSLESS == 1)
int use_near_lossless = 0;
#endif
int hdr_size = 0;
int data_size = 0;
int use_delta_palette = 0;
int idx;
size_t best_size = 0;
VP8LBitWriter bw_init = *bw, bw_best;
(void)data2;
if (!VP8LBitWriterInit(&bw_best, 0) ||
(num_crunch_configs > 1 && !VP8LBitWriterClone(bw, &bw_best))) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
for (idx = 0; idx < num_crunch_configs; ++idx) {
const int entropy_idx = crunch_configs[idx].entropy_idx_;
enc->use_palette_ = (entropy_idx == kPalette);
enc->use_subtract_green_ =
(entropy_idx == kSubGreen) || (entropy_idx == kSpatialSubGreen);
enc->use_predict_ =
(entropy_idx == kSpatial) || (entropy_idx == kSpatialSubGreen);
if (low_effort) {
enc->use_cross_color_ = 0;
} else {
enc->use_cross_color_ = red_and_blue_always_zero ? 0 : enc->use_predict_;
}
// Reset any parameter in the encoder that is set in the previous iteration.
enc->cache_bits_ = 0;
VP8LBackwardRefsClear(&enc->refs_[0]);
VP8LBackwardRefsClear(&enc->refs_[1]);
#if (WEBP_NEAR_LOSSLESS == 1)
// Apply near-lossless preprocessing.
use_near_lossless = (config->near_lossless < 100) && !enc->use_palette_ &&
!enc->use_predict_;
if (use_near_lossless) {
err = AllocateTransformBuffer(enc, width, height);
if (err != VP8_ENC_OK) goto Error;
if ((enc->argb_content_ != kEncoderNearLossless) &&
!VP8ApplyNearLossless(picture, config->near_lossless, enc->argb_)) {
err = VP8_ENC_ERROR_OUT_OF_MEMORY;
goto Error;
}
enc->argb_content_ = kEncoderNearLossless;
} else {
enc->argb_content_ = kEncoderNone;
}
#else
enc->argb_content_ = kEncoderNone;
#endif
// Encode palette
if (enc->use_palette_) {
err = EncodePalette(bw, low_effort, enc);
if (err != VP8_ENC_OK) goto Error;
err = MapImageFromPalette(enc, use_delta_palette);
if (err != VP8_ENC_OK) goto Error;
// If using a color cache, do not have it bigger than the number of
// colors.
if (use_cache && enc->palette_size_ < (1 << MAX_COLOR_CACHE_BITS)) {
enc->cache_bits_ = BitsLog2Floor(enc->palette_size_) + 1;
}
}
if (!use_delta_palette) {
// In case image is not packed.
if (enc->argb_content_ != kEncoderNearLossless &&
enc->argb_content_ != kEncoderPalette) {
err = MakeInputImageCopy(enc);
if (err != VP8_ENC_OK) goto Error;
}
// -----------------------------------------------------------------------
// Apply transforms and write transform data.
if (enc->use_subtract_green_) {
ApplySubtractGreen(enc, enc->current_width_, height, bw);
}
if (enc->use_predict_) {
err = ApplyPredictFilter(enc, enc->current_width_, height, quality,
low_effort, enc->use_subtract_green_, bw);
if (err != VP8_ENC_OK) goto Error;
}
if (enc->use_cross_color_) {
err = ApplyCrossColorFilter(enc, enc->current_width_, height, quality,
low_effort, bw);
if (err != VP8_ENC_OK) goto Error;
}
}
VP8LPutBits(bw, !TRANSFORM_PRESENT, 1); // No more transforms.
// -------------------------------------------------------------------------
// Encode and write the transformed image.
err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_,
enc->current_width_, height, quality, low_effort,
use_cache, &crunch_configs[idx],
&enc->cache_bits_, enc->histo_bits_,
byte_position, &hdr_size, &data_size);
if (err != VP8_ENC_OK) goto Error;
// If we are better than what we already have.
if (idx == 0 || VP8LBitWriterNumBytes(bw) < best_size) {
best_size = VP8LBitWriterNumBytes(bw);
// Store the BitWriter.
VP8LBitWriterSwap(bw, &bw_best);
#if !defined(WEBP_DISABLE_STATS)
// Update the stats.
if (stats != NULL) {
stats->lossless_features = 0;
if (enc->use_predict_) stats->lossless_features |= 1;
if (enc->use_cross_color_) stats->lossless_features |= 2;
if (enc->use_subtract_green_) stats->lossless_features |= 4;
if (enc->use_palette_) stats->lossless_features |= 8;
stats->histogram_bits = enc->histo_bits_;
stats->transform_bits = enc->transform_bits_;
stats->cache_bits = enc->cache_bits_;
stats->palette_size = enc->palette_size_;
stats->lossless_size = (int)(best_size - byte_position);
stats->lossless_hdr_size = hdr_size;
stats->lossless_data_size = data_size;
}
#endif
}
// Reset the bit writer for the following iteration if any.
if (num_crunch_configs > 1) VP8LBitWriterReset(&bw_init, bw);
}
VP8LBitWriterSwap(&bw_best, bw);
Error:
VP8LBitWriterWipeOut(&bw_best);
params->err_ = err;
// The hook should return false in case of error.
return (err == VP8_ENC_OK);
}