static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem, WebPDemuxer** demuxer) { WebPBitstreamFeatures features; const VP8StatusCode status = WebPGetFeatures(mem->buf_, mem->buf_size_, &features); *demuxer = NULL; if (status != VP8_STATUS_OK) { return (status == VP8_STATUS_NOT_ENOUGH_DATA) ? PARSE_NEED_MORE_DATA : PARSE_ERROR; } { WebPDemuxer* const dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux)); Frame* const frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame)); if (dmux == NULL || frame == NULL) goto Error; InitDemux(dmux, mem); SetFrameInfo(0, mem->buf_size_, 1 /*frame_num*/, 1 /*complete*/, &features, frame); if (!AddFrame(dmux, frame)) goto Error; dmux->state_ = WEBP_DEMUX_DONE; dmux->canvas_width_ = frame->width_; dmux->canvas_height_ = frame->height_; dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0; dmux->num_frames_ = 1; assert(IsValidSimpleFormat(dmux)); *demuxer = dmux; return PARSE_OK; Error: WebPSafeFree(dmux); WebPSafeFree(frame); return PARSE_ERROR; } }
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 parsing of the webp file header fails attempt to handle a raw // VP8/VP8L frame. Note 'allow_partial' is ignored in this case. if (status == PARSE_ERROR) { status = CreateRawImageDemuxer(&mem, &dmux); if (status == PARSE_OK) { if (state != NULL) *state = WEBP_DEMUX_DONE; return dmux; } } 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*)WebPSafeCalloc(1ULL, 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; }
// Creates a new Frame if 'actual_size' is within bounds and 'mem' contains // enough data ('min_size') to parse the payload. // Returns PARSE_OK on success with *frame pointing to the new Frame. // Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise. static ParseStatus NewFrame(const MemBuffer* const mem, uint32_t min_size, uint32_t actual_size, Frame** frame) { if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; if (actual_size < min_size) return PARSE_ERROR; if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; *frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(**frame)); return (*frame == NULL) ? PARSE_ERROR : PARSE_OK; }
// General chunk storage, starting with the header at 'start_offset', allowing // the user to request the payload via a fourcc string. 'size' includes the // header and the unpadded payload size. // Returns true on success, false otherwise. static int StoreChunk(WebPDemuxer* const dmux, size_t start_offset, uint32_t size) { Chunk* const chunk = (Chunk*)WebPSafeCalloc(1ULL, sizeof(*chunk)); if (chunk == NULL) return 0; chunk->data_.offset_ = start_offset; chunk->data_.size_ = size; AddChunk(dmux, chunk); return 1; }
int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) { const int hash_size = 1 << hash_bits; assert(cc != NULL); assert(hash_bits > 0); cc->colors_ = (uint32_t*)WebPSafeCalloc((uint64_t)hash_size, sizeof(*cc->colors_)); if (cc->colors_ == NULL) return 0; cc->hash_shift_ = 32 - hash_bits; return 1; }
static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) { const int has_alpha = WebPIsAlphaMode(p->output->colorspace); const int out_width = io->scaled_width; const int out_height = io->scaled_height; const int uv_in_width = (io->mb_w + 1) >> 1; const int uv_in_height = (io->mb_h + 1) >> 1; const size_t work_size = 2 * out_width; // scratch memory for one rescaler int32_t* work; // rescalers work area uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion size_t tmp_size1, tmp_size2, total_size; tmp_size1 = 3 * work_size; tmp_size2 = 3 * out_width; if (has_alpha) { tmp_size1 += work_size; tmp_size2 += out_width; } total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp); p->memory = WebPSafeCalloc(1ULL, total_size); if (p->memory == NULL) { return 0; // memory error } work = (int32_t*)p->memory; tmp = (uint8_t*)(work + tmp_size1); WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h, tmp + 0 * out_width, out_width, out_height, 0, 1, io->mb_w, out_width, io->mb_h, out_height, work + 0 * work_size); WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height, tmp + 1 * out_width, out_width, out_height, 0, 1, io->mb_w, 2 * out_width, io->mb_h, 2 * out_height, work + 1 * work_size); WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height, tmp + 2 * out_width, out_width, out_height, 0, 1, io->mb_w, 2 * out_width, io->mb_h, 2 * out_height, work + 2 * work_size); p->emit = EmitRescaledRGB; WebPInitYUV444Converters(); if (has_alpha) { WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h, tmp + 3 * out_width, out_width, out_height, 0, 1, io->mb_w, out_width, io->mb_h, out_height, work + 3 * work_size); p->emit_alpha = EmitRescaledAlphaRGB; if (p->output->colorspace == MODE_RGBA_4444 || p->output->colorspace == MODE_rgbA_4444) { p->emit_alpha_row = ExportAlphaRGBA4444; } else { p->emit_alpha_row = ExportAlpha; } WebPInitAlphaProcessing(); } return 1; }
static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) { const int has_alpha = WebPIsAlphaMode(p->output->colorspace); const WebPYUVABuffer* const buf = &p->output->u.YUVA; const int out_width = io->scaled_width; const int out_height = io->scaled_height; const int uv_out_width = (out_width + 1) >> 1; const int uv_out_height = (out_height + 1) >> 1; const int uv_in_width = (io->mb_w + 1) >> 1; const int uv_in_height = (io->mb_h + 1) >> 1; const size_t work_size = 2 * out_width; // scratch memory for luma rescaler const size_t uv_work_size = 2 * uv_out_width; // and for each u/v ones size_t tmp_size; int32_t* work; tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work); if (has_alpha) { tmp_size += work_size * sizeof(*work); } p->memory = WebPSafeCalloc(1ULL, tmp_size); if (p->memory == NULL) { return 0; // memory error } work = (int32_t*)p->memory; WebPRescalerInit(&p->scaler_y, io->mb_w, io->mb_h, buf->y, out_width, out_height, buf->y_stride, 1, io->mb_w, out_width, io->mb_h, out_height, work); WebPRescalerInit(&p->scaler_u, uv_in_width, uv_in_height, buf->u, uv_out_width, uv_out_height, buf->u_stride, 1, uv_in_width, uv_out_width, uv_in_height, uv_out_height, work + work_size); WebPRescalerInit(&p->scaler_v, uv_in_width, uv_in_height, buf->v, uv_out_width, uv_out_height, buf->v_stride, 1, uv_in_width, uv_out_width, uv_in_height, uv_out_height, work + work_size + uv_work_size); p->emit = EmitRescaledYUV; if (has_alpha) { WebPRescalerInit(&p->scaler_a, io->mb_w, io->mb_h, buf->a, out_width, out_height, buf->a_stride, 1, io->mb_w, out_width, io->mb_h, out_height, work + work_size + 2 * uv_work_size); p->emit_alpha = EmitRescaledAlphaYUV; WebPInitAlphaProcessing(); } return 1; }
static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { const size_t min_size = CHUNK_HEADER_SIZE; MemBuffer* const mem = &dmux->mem_; Frame* frame; ParseStatus status; int image_added = 0; if (dmux->frames_ != NULL) return PARSE_ERROR; if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame)); if (frame == NULL) return PARSE_ERROR; // For the single image case we allow parsing of a partial frame, so no // minimum size is imposed here. status = StoreFrame(1, 0, &dmux->mem_, frame); if (status != PARSE_ERROR) { const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG); // Clear any alpha when the alpha flag is missing. if (!has_alpha && frame->img_components_[1].size_ > 0) { frame->img_components_[1].offset_ = 0; frame->img_components_[1].size_ = 0; frame->has_alpha_ = 0; } // Use the frame width/height as the canvas values for non-vp8x files. // Also, set ALPHA_FLAG if this is a lossless image with alpha. if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) { dmux->state_ = WEBP_DEMUX_PARSED_HEADER; dmux->canvas_width_ = frame->width_; dmux->canvas_height_ = frame->height_; dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0; } if (!AddFrame(dmux, frame)) { status = PARSE_ERROR; // last frame was left incomplete } else { image_added = 1; dmux->num_frames_ = 1; } } if (!image_added) WebPSafeFree(frame); return status; }
static int Reset(WebPWorker* const worker) { int ok = 1; worker->had_error = 0; if (worker->status_ < OK) { #ifdef WEBP_USE_THREAD WebPWorkerImpl* const impl = (WebPWorkerImpl*)WebPSafeCalloc(1, sizeof(WebPWorkerImpl)); worker->impl_ = (void*)impl; if (worker->impl_ == NULL) { return 0; } if (pthread_mutex_init(&impl->mutex_, NULL)) { goto Error; } if (pthread_cond_init(&impl->condition_, NULL)) { pthread_mutex_destroy(&impl->mutex_); goto Error; } pthread_mutex_lock(&impl->mutex_); ok = !pthread_create(&impl->thread_, NULL, ThreadLoop, worker); if (ok) worker->status_ = OK; pthread_mutex_unlock(&impl->mutex_); if (!ok) { pthread_mutex_destroy(&impl->mutex_); pthread_cond_destroy(&impl->condition_); Error: WebPSafeFree(impl); worker->impl_ = NULL; return 0; } #else worker->status_ = OK; #endif } else if (worker->status_ > OK) { ok = Sync(worker); } assert(!ok || (worker->status_ == OK)); return ok; }
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; }