int WebPInitDecoderConfigInternal(WebPDecoderConfig* config, int version) { if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { return 0; // version mismatch } if (config == NULL) { return 0; } memset(config, 0, sizeof(*config)); DefaultFeatures(&config->input); WebPInitDecBuffer(&config->output); return 1; }
WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, WebPDemuxState* state, int version) { const ChunkParser* parser; int partial; ParseStatus status = PARSE_ERROR; MemBuffer mem; WebPDemuxer* dmux; if (state != NULL) *state = WEBP_DEMUX_PARSE_ERROR; if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL; if (data == NULL || data->bytes == NULL || data->size == 0) return NULL; if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL; status = ReadHeader(&mem); if (status != PARSE_OK) { if (state != NULL) { *state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER : WEBP_DEMUX_PARSE_ERROR; } return NULL; } partial = (mem.buf_size_ < mem.riff_end_); if (!allow_partial && partial) return NULL; dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux)); if (dmux == NULL) return NULL; InitDemux(dmux, &mem); status = PARSE_ERROR; for (parser = kMasterChunks; parser->parse != NULL; ++parser) { if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) { status = parser->parse(dmux); if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE; if (status == PARSE_NEED_MORE_DATA && !partial) status = PARSE_ERROR; if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR; if (status == PARSE_ERROR) dmux->state_ = WEBP_DEMUX_PARSE_ERROR; break; } } if (state != NULL) *state = dmux->state_; if (status == PARSE_ERROR) { WebPDemuxDelete(dmux); return NULL; } return dmux; }
VP8StatusCode WebPGetFeaturesInternal(const uint8_t* data, size_t data_size, WebPBitstreamFeatures* features, int version) { VP8StatusCode status; if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) { return VP8_STATUS_INVALID_PARAM; // version mismatch } if (features == NULL) { return VP8_STATUS_INVALID_PARAM; } status = GetFeatures(data, data_size, features); if (status == VP8_STATUS_NOT_ENOUGH_DATA) { return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error. } return status; }
int WebPConfigInitInternal(WebPConfig* config, WebPPreset preset, float quality, int version) { if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) { return 0; // caller/system version mismatch! } if (config == NULL) return 0; config->quality = quality; config->target_size = 0; config->target_PSNR = 0.; config->method = 0; // 4; config->sns_strength = 50; config->filter_strength = 60; // mid-filtering config->filter_sharpness = 0; config->filter_type = 1; // default: strong (so U/V is filtered too) config->partitions = 0; config->segments = 4; config->pass = 1; config->show_compressed = 0; config->preprocessing = 0; config->autofilter = 0; config->partition_limit = 0; config->alpha_compression = 1; config->alpha_filtering = 1; config->alpha_quality = 100; config->lossless = 0; config->image_hint = WEBP_HINT_DEFAULT; config->emulate_jpeg_size = 0; config->thread_level = 0; config->low_memory = 0; // TODO(skal): tune. switch (preset) { case WEBP_PRESET_PICTURE: config->sns_strength = 80; config->filter_sharpness = 4; config->filter_strength = 35; config->preprocessing &= ~2; // no dithering break; case WEBP_PRESET_PHOTO: config->sns_strength = 80; config->filter_sharpness = 3; config->filter_strength = 30; config->preprocessing |= 2; break; case WEBP_PRESET_DRAWING: config->sns_strength = 25; config->filter_sharpness = 6; config->filter_strength = 10; break; case WEBP_PRESET_ICON: config->sns_strength = 0; config->filter_strength = 0; // disable filtering to retain sharpness config->preprocessing &= ~2; // no dithering break; case WEBP_PRESET_TEXT: config->sns_strength = 0; config->filter_strength = 0; // disable filtering to retain sharpness config->preprocessing &= ~2; // no dithering config->segments = 2; break; case WEBP_PRESET_DEFAULT: default: break; } return WebPValidateConfig(config); }
WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, int version) { size_t riff_size; uint32_t tag; const uint8_t* end; WebPMux* mux = NULL; WebPMuxImage* wpi = NULL; const uint8_t* data; size_t size; WebPChunk chunk; ChunkInit(&chunk); // Sanity checks. if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) { return NULL; // version mismatch } if (bitstream == NULL) return NULL; data = bitstream->bytes; size = bitstream->size; if (data == NULL) return NULL; if (size < RIFF_HEADER_SIZE) return NULL; if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') || GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) { return NULL; } mux = WebPMuxNew(); if (mux == NULL) return NULL; if (size < RIFF_HEADER_SIZE + TAG_SIZE) goto Err; tag = GetLE32(data + RIFF_HEADER_SIZE); if (tag != kChunks[IDX_VP8].tag && tag != kChunks[IDX_VP8L].tag && tag != kChunks[IDX_VP8X].tag) { goto Err; // First chunk should be VP8, VP8L or VP8X. } riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE)); if (riff_size > MAX_CHUNK_PAYLOAD || riff_size > size) { goto Err; } else { if (riff_size < size) { // Redundant data after last chunk. size = riff_size; // To make sure we don't read any data beyond mux_size. } } end = data + size; data += RIFF_HEADER_SIZE; size -= RIFF_HEADER_SIZE; wpi = (WebPMuxImage*)WebPSafeMalloc(1ULL, sizeof(*wpi)); if (wpi == NULL) goto Err; MuxImageInit(wpi); // Loop over chunks. while (data != end) { size_t data_size; WebPChunkId id; WebPChunk** chunk_list; if (ChunkVerifyAndAssign(&chunk, data, size, riff_size, copy_data) != WEBP_MUX_OK) { goto Err; } data_size = ChunkDiskSize(&chunk); id = ChunkGetIdFromTag(chunk.tag_); switch (id) { case WEBP_CHUNK_ALPHA: if (wpi->alpha_ != NULL) goto Err; // Consecutive ALPH chunks. if (ChunkSetNth(&chunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Err; wpi->is_partial_ = 1; // Waiting for a VP8 chunk. break; case WEBP_CHUNK_IMAGE: if (ChunkSetNth(&chunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Err; if (!MuxImageFinalize(wpi)) goto Err; wpi->is_partial_ = 0; // wpi is completely filled. PushImage: // Add this to mux->images_ list. if (MuxImagePush(wpi, &mux->images_) != WEBP_MUX_OK) goto Err; MuxImageInit(wpi); // Reset for reading next image. break; case WEBP_CHUNK_ANMF: if (wpi->is_partial_) goto Err; // Previous wpi is still incomplete. if (!MuxImageParse(&chunk, copy_data, wpi)) goto Err; ChunkRelease(&chunk); goto PushImage; break; default: // A non-image chunk. if (wpi->is_partial_) goto Err; // Encountered a non-image chunk before // getting all chunks of an image. chunk_list = MuxGetChunkListFromId(mux, id); // List to add this chunk. if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err; if (id == WEBP_CHUNK_VP8X) { // grab global specs mux->canvas_width_ = GetLE24(data + 12) + 1; mux->canvas_height_ = GetLE24(data + 15) + 1; } break; } data += data_size; size -= data_size; ChunkInit(&chunk); } // Validate mux if complete. if (MuxValidate(mux) != WEBP_MUX_OK) goto Err; MuxImageDelete(wpi); return mux; // All OK; Err: // Something bad happened. ChunkRelease(&chunk); MuxImageDelete(wpi); WebPMuxDelete(mux); return NULL; }
WebPAnimEncoder* WebPAnimEncoderNewInternal( int width, int height, const WebPAnimEncoderOptions* enc_options, int abi_version) { WebPAnimEncoder* enc; if (WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_MUX_ABI_VERSION)) { return NULL; } if (width <= 0 || height <= 0 || (width * (uint64_t)height) >= MAX_IMAGE_AREA) { return NULL; } enc = (WebPAnimEncoder*)WebPSafeCalloc(1, sizeof(*enc)); if (enc == NULL) return NULL; // sanity inits, so we can call WebPAnimEncoderDelete(): enc->encoded_frames_ = NULL; enc->mux_ = NULL; // Dimensions and options. *(int*)&enc->canvas_width_ = width; *(int*)&enc->canvas_height_ = height; if (enc_options != NULL) { *(WebPAnimEncoderOptions*)&enc->options_ = *enc_options; SanitizeEncoderOptions((WebPAnimEncoderOptions*)&enc->options_); } else { DefaultEncoderOptions((WebPAnimEncoderOptions*)&enc->options_); } // Canvas buffers. if (!WebPPictureInit(&enc->curr_canvas_copy_) || !WebPPictureInit(&enc->prev_canvas_) || !WebPPictureInit(&enc->prev_canvas_disposed_)) { return NULL; } enc->curr_canvas_copy_.width = width; enc->curr_canvas_copy_.height = height; enc->curr_canvas_copy_.use_argb = 1; if (!WebPPictureAlloc(&enc->curr_canvas_copy_) || !WebPPictureCopy(&enc->curr_canvas_copy_, &enc->prev_canvas_) || !WebPPictureCopy(&enc->curr_canvas_copy_, &enc->prev_canvas_disposed_)) { goto Err; } WebPUtilClearPic(&enc->prev_canvas_, NULL); enc->curr_canvas_copy_modified_ = 1; // Encoded frames. ResetCounters(enc); // Note: one extra storage is for the previous frame. enc->size_ = enc->options_.kmax - enc->options_.kmin + 1; // We need space for at least 2 frames. But when kmin, kmax are both zero, // enc->size_ will be 1. So we handle that special case below. if (enc->size_ < 2) enc->size_ = 2; enc->encoded_frames_ = (EncodedFrame*)WebPSafeCalloc(enc->size_, sizeof(*enc->encoded_frames_)); if (enc->encoded_frames_ == NULL) goto Err; enc->mux_ = WebPMuxNew(); if (enc->mux_ == NULL) goto Err; enc->count_since_key_frame_ = 0; enc->first_timestamp_ = 0; enc->prev_timestamp_ = 0; enc->prev_candidate_undecided_ = 0; enc->is_first_frame_ = 1; enc->got_null_frame_ = 0; return enc; // All OK. Err: WebPAnimEncoderDelete(enc); return NULL; }