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; } }
void WebPAnimEncoderDelete(WebPAnimEncoder* enc) { if (enc != NULL) {; WebPPictureFree(&enc->curr_canvas_copy_); WebPPictureFree(&enc->prev_canvas_); WebPPictureFree(&enc->prev_canvas_disposed_); if (enc->encoded_frames_ != NULL) { size_t i; for (i = 0; i < enc->size_; ++i) { FrameRelease(&enc->encoded_frames_[i]); } WebPSafeFree(enc->encoded_frames_); } WebPMuxDelete(enc->mux_); WebPSafeFree(enc); } }
void VP8LHashChainClear(VP8LHashChain* const p) { assert(p != NULL); WebPSafeFree(p->offset_length_); p->size_ = 0; p->offset_length_ = NULL; }
// Parse a 'FRGM' chunk and any image bearing chunks that immediately follow. // 'fragment_chunk_size' is the previously validated, padded chunk size. static ParseStatus ParseFragment(WebPDemuxer* const dmux, uint32_t fragment_chunk_size) { const int frame_num = 1; // All fragments belong to the 1st (and only) frame. const int is_fragmented = !!(dmux->feature_flags_ & FRAGMENTS_FLAG); const uint32_t frgm_payload_size = fragment_chunk_size - FRGM_CHUNK_SIZE; int added_fragment = 0; MemBuffer* const mem = &dmux->mem_; Frame* frame; ParseStatus status = NewFrame(mem, FRGM_CHUNK_SIZE, fragment_chunk_size, &frame); if (status != PARSE_OK) return status; frame->is_fragment_ = 1; frame->x_offset_ = 2 * ReadLE24s(mem); frame->y_offset_ = 2 * ReadLE24s(mem); // Store a fragment only if the 'fragments' flag is set and there is some // data available. status = StoreFrame(frame_num, frgm_payload_size, mem, frame); if (status != PARSE_ERROR && is_fragmented && frame->frame_num_ > 0) { added_fragment = AddFrame(dmux, frame); if (!added_fragment) { status = PARSE_ERROR; } else { dmux->num_frames_ = 1; } } if (!added_fragment) WebPSafeFree(frame); return status; }
void WebPDemuxDelete(WebPDemuxer* dmux) { Chunk* c; Frame* f; if (dmux == NULL) return; for (f = dmux->frames_; f != NULL;) { Frame* const cur_frame = f; f = f->next_; WebPSafeFree(cur_frame); } for (c = dmux->chunks_; c != NULL;) { Chunk* const cur_chunk = c; c = c->next_; WebPSafeFree(cur_chunk); } WebPSafeFree(dmux); }
void WebPFreeDecBuffer(WebPDecBuffer* buffer) { if (buffer != NULL) { if (!buffer->is_external_memory) { WebPSafeFree(buffer->private_memory); } buffer->private_memory = NULL; } }
void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs) { assert(refs != NULL); VP8LClearBackwardRefs(refs); while (refs->free_blocks_ != NULL) { PixOrCopyBlock* const next = refs->free_blocks_->next_; WebPSafeFree(refs->free_blocks_); refs->free_blocks_ = next; } }
static int DeleteVP8Encoder(VP8Encoder* enc) { int ok = 1; if (enc != NULL) { ok = VP8EncDeleteAlpha(enc); VP8TBufferClear(&enc->tokens_); WebPSafeFree(enc); } return ok; }
WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) { size_t size = 0; uint8_t* data = NULL; uint8_t* dst = NULL; WebPMuxError err; if (assembled_data == NULL) { return WEBP_MUX_INVALID_ARGUMENT; } // Clean up returned data, in case something goes wrong. memset(assembled_data, 0, sizeof(*assembled_data)); if (mux == NULL) { return WEBP_MUX_INVALID_ARGUMENT; } // Finalize mux. err = MuxCleanup(mux); if (err != WEBP_MUX_OK) return err; err = CreateVP8XChunk(mux); if (err != WEBP_MUX_OK) return err; // Allocate data. size = ChunkListDiskSize(mux->vp8x_) + ChunkListDiskSize(mux->iccp_) + ChunkListDiskSize(mux->anim_) + ImageListDiskSize(mux->images_) + ChunkListDiskSize(mux->exif_) + ChunkListDiskSize(mux->xmp_) + ChunkListDiskSize(mux->unknown_) + RIFF_HEADER_SIZE; data = (uint8_t*)WebPSafeMalloc(1ULL, size); if (data == NULL) return WEBP_MUX_MEMORY_ERROR; // Emit header & chunks. dst = MuxEmitRiffHeader(data, size); dst = ChunkListEmit(mux->vp8x_, dst); dst = ChunkListEmit(mux->iccp_, dst); dst = ChunkListEmit(mux->anim_, dst); dst = ImageListEmit(mux->images_, dst); dst = ChunkListEmit(mux->exif_, dst); dst = ChunkListEmit(mux->xmp_, dst); dst = ChunkListEmit(mux->unknown_, dst); assert(dst == data + size); // Validate mux. err = MuxValidate(mux); if (err != WEBP_MUX_OK) { WebPSafeFree(data); data = NULL; size = 0; } // Finalize data. assembled_data->bytes = data; assembled_data->size = size; return err; }
void VP8TBufferClear(VP8TBuffer* const b) { if (b != NULL) { VP8Tokens* p = b->pages_; while (p != NULL) { VP8Tokens* const next = p->next_; WebPSafeFree(p); p = next; } VP8TBufferInit(b, b->page_size_); } }
// Parse a 'ANMF' chunk and any image bearing chunks that immediately follow. // 'frame_chunk_size' is the previously validated, padded chunk size. static ParseStatus ParseAnimationFrame( WebPDemuxer* const dmux, uint32_t frame_chunk_size) { const int is_animation = !!(dmux->feature_flags_ & ANIMATION_FLAG); const uint32_t anmf_payload_size = frame_chunk_size - ANMF_CHUNK_SIZE; int added_frame = 0; int bits; MemBuffer* const mem = &dmux->mem_; Frame* frame; ParseStatus status = NewFrame(mem, ANMF_CHUNK_SIZE, frame_chunk_size, &frame); if (status != PARSE_OK) return status; frame->x_offset_ = 2 * ReadLE24s(mem); frame->y_offset_ = 2 * ReadLE24s(mem); frame->width_ = 1 + ReadLE24s(mem); frame->height_ = 1 + ReadLE24s(mem); frame->duration_ = ReadLE24s(mem); bits = ReadByte(mem); frame->dispose_method_ = (bits & 1) ? WEBP_MUX_DISPOSE_BACKGROUND : WEBP_MUX_DISPOSE_NONE; frame->blend_method_ = (bits & 2) ? WEBP_MUX_NO_BLEND : WEBP_MUX_BLEND; if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) { WebPSafeFree(frame); return PARSE_ERROR; } // Store a frame only if the animation flag is set there is some data for // this frame is available. status = StoreFrame(dmux->num_frames_ + 1, anmf_payload_size, mem, frame); if (status != PARSE_ERROR && is_animation && frame->frame_num_ > 0) { added_frame = AddFrame(dmux, frame); if (added_frame) { ++dmux->num_frames_; } else { status = PARSE_ERROR; } } if (!added_frame) WebPSafeFree(frame); return status; }
static void End(WebPWorker* const worker) { if (worker->status_ >= OK) { #ifdef WEBP_USE_THREAD ChangeState(worker, NOT_OK); pthread_join(worker->impl_->thread_, NULL); pthread_mutex_destroy(&worker->impl_->mutex_); pthread_cond_destroy(&worker->impl_->condition_); #else worker->status_ = NOT_OK; #endif } WebPSafeFree(worker->impl_); worker->impl_ = NULL; assert(worker->status_ == NOT_OK); }
static void End(WebPWorker* const worker) { #ifdef WEBP_USE_THREAD if (worker->impl_ != NULL) { WebPWorkerImpl* const impl = (WebPWorkerImpl*)worker->impl_; ChangeState(worker, NOT_OK); pthread_join(impl->thread_, NULL); pthread_mutex_destroy(&impl->mutex_); pthread_cond_destroy(&impl->condition_); WebPSafeFree(impl); worker->impl_ = NULL; } #else worker->status_ = NOT_OK; assert(worker->impl_ == NULL); #endif assert(worker->status_ == NOT_OK); }
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 void SmoothSegmentMap(VP8Encoder* const enc) { int n, x, y; const int w = enc->mb_w_; const int h = enc->mb_h_; const int majority_cnt_3_x_3_grid = 5; uint8_t* const tmp = (uint8_t*)WebPSafeMalloc(w * h, sizeof(*tmp)); assert((uint64_t)(w * h) == (uint64_t)w * h); // no overflow, as per spec if (tmp == NULL) return; for (y = 1; y < h - 1; ++y) { for (x = 1; x < w - 1; ++x) { int cnt[NUM_MB_SEGMENTS] = { 0 }; const VP8MBInfo* const mb = &enc->mb_info_[x + w * y]; int majority_seg = mb->segment_; // Check the 8 neighbouring segment values. cnt[mb[-w - 1].segment_]++; // top-left cnt[mb[-w + 0].segment_]++; // top cnt[mb[-w + 1].segment_]++; // top-right cnt[mb[ - 1].segment_]++; // left cnt[mb[ + 1].segment_]++; // right cnt[mb[ w - 1].segment_]++; // bottom-left cnt[mb[ w + 0].segment_]++; // bottom cnt[mb[ w + 1].segment_]++; // bottom-right for (n = 0; n < NUM_MB_SEGMENTS; ++n) { if (cnt[n] >= majority_cnt_3_x_3_grid) { majority_seg = n; break; } } tmp[x + y * w] = majority_seg; } } for (y = 1; y < h - 1; ++y) { for (x = 1; x < w - 1; ++x) { VP8MBInfo* const mb = &enc->mb_info_[x + w * y]; mb->segment_ = tmp[x + y * w]; } } WebPSafeFree(tmp); }
int WebPPlaneDistortion(const uint8_t* src, size_t src_stride, const uint8_t* ref, size_t ref_stride, int width, int height, size_t x_step, int type, float* distortion, float* result) { uint8_t* allocated = NULL; const AccumulateFunc metric = (type == 0) ? AccumulateSSE : (type == 1) ? AccumulateSSIM : AccumulateLSIM; if (src == NULL || ref == NULL || src_stride < x_step * width || ref_stride < x_step * width || result == NULL || distortion == NULL) { return 0; } VP8SSIMDspInit(); if (x_step != 1) { // extract a packed plane if needed int x, y; uint8_t* tmp1; uint8_t* tmp2; allocated = (uint8_t*)WebPSafeMalloc(2ULL * width * height, sizeof(*allocated)); if (allocated == NULL) return 0; tmp1 = allocated; tmp2 = tmp1 + (size_t)width * height; for (y = 0; y < height; ++y) { for (x = 0; x < width; ++x) { tmp1[x + y * width] = src[x * x_step + y * src_stride]; tmp2[x + y * width] = ref[x * x_step + y * ref_stride]; } } src = tmp1; ref = tmp2; } *distortion = (float)metric(src, width, ref, width, width, height); WebPSafeFree(allocated); *result = (type == 1) ? (float)GetLogSSIM(*distortion, (double)width * height) : (float)GetPSNR(*distortion, (double)width * height); return 1; }
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; }
void VP8LColorCacheClear(VP8LColorCache* const cc) { if (cc != NULL) { WebPSafeFree(cc->colors_); cc->colors_ = NULL; } }
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits, const int code_lengths[], int code_lengths_size) { HuffmanCode* table = root_table; // next available space in table int total_size = 1 << root_bits; // total size root table + 2nd level table int* sorted = NULL; // symbols sorted by code length int len; // current code length int symbol; // symbol index in original or sorted table // number of codes of each length: int count[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 }; // offsets in sorted table for each length: int offset[MAX_ALLOWED_CODE_LENGTH + 1]; assert(code_lengths_size != 0); assert(code_lengths != NULL); assert(root_table != NULL); assert(root_bits > 0); // Build histogram of code lengths. for (symbol = 0; symbol < code_lengths_size; ++symbol) { if (code_lengths[symbol] > MAX_ALLOWED_CODE_LENGTH) { return 0; } ++count[code_lengths[symbol]]; } // Error, all code lengths are zeros. if (count[0] == code_lengths_size) { return 0; } // Generate offsets into sorted symbol table by code length. offset[1] = 0; for (len = 1; len < MAX_ALLOWED_CODE_LENGTH; ++len) { if (count[len] > (1 << len)) { return 0; } offset[len + 1] = offset[len] + count[len]; } sorted = (int*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted)); if (sorted == NULL) { return 0; } // Sort symbols by length, by symbol order within each length. for (symbol = 0; symbol < code_lengths_size; ++symbol) { const int symbol_code_length = code_lengths[symbol]; if (code_lengths[symbol] > 0) { sorted[offset[symbol_code_length]++] = symbol; } } // Special case code with only one value. if (offset[MAX_ALLOWED_CODE_LENGTH] == 1) { HuffmanCode code; code.bits = 0; code.value = (uint16_t)sorted[0]; ReplicateValue(table, 1, total_size, code); WebPSafeFree(sorted); return total_size; } { int step; // step size to replicate values in current table uint32_t low = -1; // low bits for current root entry uint32_t mask = total_size - 1; // mask for low bits uint32_t key = 0; // reversed prefix code int num_nodes = 1; // number of Huffman tree nodes int num_open = 1; // number of open branches in current tree level int table_bits = root_bits; // key length of current table int table_size = 1 << table_bits; // size of current table symbol = 0; // Fill in root table. for (len = 1, step = 2; len <= root_bits; ++len, step <<= 1) { num_open <<= 1; num_nodes += num_open; num_open -= count[len]; if (num_open < 0) { WebPSafeFree(sorted); return 0; } for (; count[len] > 0; --count[len]) { HuffmanCode code; code.bits = (uint8_t)len; code.value = (uint16_t)sorted[symbol++]; ReplicateValue(&table[key], step, table_size, code); key = GetNextKey(key, len); } } // Fill in 2nd level tables and add pointers to root table. for (len = root_bits + 1, step = 2; len <= MAX_ALLOWED_CODE_LENGTH; ++len, step <<= 1) { num_open <<= 1; num_nodes += num_open; num_open -= count[len]; if (num_open < 0) { WebPSafeFree(sorted); return 0; } for (; count[len] > 0; --count[len]) { HuffmanCode code; if ((key & mask) != low) { table += table_size; table_bits = NextTableBitSize(count, len, root_bits); table_size = 1 << table_bits; total_size += table_size; low = key & mask; root_table[low].bits = (uint8_t)(table_bits + root_bits); root_table[low].value = (uint16_t)((table - root_table) - low); } code.bits = (uint8_t)(len - root_bits); code.value = (uint16_t)sorted[symbol++]; ReplicateValue(&table[key >> root_bits], step, table_size, code); key = GetNextKey(key, len); } } // Check if tree is full. if (num_nodes != 2 * offset[MAX_ALLOWED_CODE_LENGTH] - 1) { WebPSafeFree(sorted); return 0; } } WebPSafeFree(sorted); return total_size; }
void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups) { if (htree_groups != NULL) { WebPSafeFree(htree_groups); } }
int VP8LHashChainFill(VP8LHashChain* const p, int quality, const uint32_t* const argb, int xsize, int ysize, int low_effort) { const int size = xsize * ysize; const int iter_max = GetMaxItersForQuality(quality); const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize); int pos; int argb_comp; uint32_t base_position; int32_t* hash_to_first_index; // Temporarily use the p->offset_length_ as a hash chain. int32_t* chain = (int32_t*)p->offset_length_; assert(size > 0); assert(p->size_ != 0); assert(p->offset_length_ != NULL); if (size <= 2) { p->offset_length_[0] = p->offset_length_[size - 1] = 0; return 1; } hash_to_first_index = (int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index)); if (hash_to_first_index == NULL) return 0; // Set the int32_t array to -1. memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index)); // Fill the chain linking pixels with the same hash. argb_comp = (argb[0] == argb[1]); for (pos = 0; pos < size - 2;) { uint32_t hash_code; const int argb_comp_next = (argb[pos + 1] == argb[pos + 2]); if (argb_comp && argb_comp_next) { // Consecutive pixels with the same color will share the same hash. // We therefore use a different hash: the color and its repetition // length. uint32_t tmp[2]; uint32_t len = 1; tmp[0] = argb[pos]; // Figure out how far the pixels are the same. // The last pixel has a different 64 bit hash, as its next pixel does // not have the same color, so we just need to get to the last pixel equal // to its follower. while (pos + (int)len + 2 < size && argb[pos + len + 2] == argb[pos]) { ++len; } if (len > MAX_LENGTH) { // Skip the pixels that match for distance=1 and length>MAX_LENGTH // because they are linked to their predecessor and we automatically // check that in the main for loop below. Skipping means setting no // predecessor in the chain, hence -1. memset(chain + pos, 0xff, (len - MAX_LENGTH) * sizeof(*chain)); pos += len - MAX_LENGTH; len = MAX_LENGTH; } // Process the rest of the hash chain. while (len) { tmp[1] = len--; hash_code = GetPixPairHash64(tmp); chain[pos] = hash_to_first_index[hash_code]; hash_to_first_index[hash_code] = pos++; } argb_comp = 0; } else { // Just move one pixel forward. hash_code = GetPixPairHash64(argb + pos); chain[pos] = hash_to_first_index[hash_code]; hash_to_first_index[hash_code] = pos++; argb_comp = argb_comp_next; } } // Process the penultimate pixel. chain[pos] = hash_to_first_index[GetPixPairHash64(argb + pos)]; WebPSafeFree(hash_to_first_index); // Find the best match interval at each pixel, defined by an offset to the // pixel and a length. The right-most pixel cannot match anything to the right // (hence a best length of 0) and the left-most pixel nothing to the left // (hence an offset of 0). assert(size > 2); p->offset_length_[0] = p->offset_length_[size - 1] = 0; for (base_position = size - 2; base_position > 0;) { const int max_len = MaxFindCopyLength(size - 1 - base_position); const uint32_t* const argb_start = argb + base_position; int iter = iter_max; int best_length = 0; uint32_t best_distance = 0; uint32_t best_argb; const int min_pos = (base_position > window_size) ? base_position - window_size : 0; const int length_max = (max_len < 256) ? max_len : 256; uint32_t max_base_position; pos = chain[base_position]; if (!low_effort) { int curr_length; // Heuristic: use the comparison with the above line as an initialization. if (base_position >= (uint32_t)xsize) { curr_length = FindMatchLength(argb_start - xsize, argb_start, best_length, max_len); if (curr_length > best_length) { best_length = curr_length; best_distance = xsize; } --iter; } // Heuristic: compare to the previous pixel. curr_length = FindMatchLength(argb_start - 1, argb_start, best_length, max_len); if (curr_length > best_length) { best_length = curr_length; best_distance = 1; } --iter; // Skip the for loop if we already have the maximum. if (best_length == MAX_LENGTH) pos = min_pos - 1; } best_argb = argb_start[best_length]; for (; pos >= min_pos && --iter; pos = chain[pos]) { int curr_length; assert(base_position > (uint32_t)pos); if (argb[pos + best_length] != best_argb) continue; curr_length = VP8LVectorMismatch(argb + pos, argb_start, max_len); if (best_length < curr_length) { best_length = curr_length; best_distance = base_position - pos; best_argb = argb_start[best_length]; // Stop if we have reached a good enough length. if (best_length >= length_max) break; } } // We have the best match but in case the two intervals continue matching // to the left, we have the best matches for the left-extended pixels. max_base_position = base_position; while (1) { assert(best_length <= MAX_LENGTH); assert(best_distance <= WINDOW_SIZE); p->offset_length_[base_position] = (best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length; --base_position; // Stop if we don't have a match or if we are out of bounds. if (best_distance == 0 || base_position == 0) break; // Stop if we cannot extend the matching intervals to the left. if (base_position < best_distance || argb[base_position - best_distance] != argb[base_position]) { break; } // Stop if we are matching at its limit because there could be a closer // matching interval with the same maximum length. Then again, if the // matching interval is as close as possible (best_distance == 1), we will // never find anything better so let's continue. if (best_length == MAX_LENGTH && best_distance != 1 && base_position + MAX_LENGTH < max_base_position) { break; } if (best_length < MAX_LENGTH) { ++best_length; max_base_position = base_position; } } } return 1; }
void WebPMuxDelete(WebPMux* mux) { if (mux != NULL) { MuxRelease(mux); WebPSafeFree(mux); } }
static int BackwardReferencesLz77Box(int xsize, int ysize, const uint32_t* const argb, int cache_bits, const VP8LHashChain* const hash_chain_best, VP8LHashChain* hash_chain, VP8LBackwardRefs* const refs) { int i; const int pix_count = xsize * ysize; uint16_t* counts; int window_offsets[WINDOW_OFFSETS_SIZE_MAX] = {0}; int window_offsets_new[WINDOW_OFFSETS_SIZE_MAX] = {0}; int window_offsets_size = 0; int window_offsets_new_size = 0; uint16_t* const counts_ini = (uint16_t*)WebPSafeMalloc(xsize * ysize, sizeof(*counts_ini)); int best_offset_prev = -1, best_length_prev = -1; if (counts_ini == NULL) return 0; // counts[i] counts how many times a pixel is repeated starting at position i. i = pix_count - 2; counts = counts_ini + i; counts[1] = 1; for (; i >= 0; --i, --counts) { if (argb[i] == argb[i + 1]) { // Max out the counts to MAX_LENGTH. counts[0] = counts[1] + (counts[1] != MAX_LENGTH); } else { counts[0] = 1; } } // Figure out the window offsets around a pixel. They are stored in a // spiraling order around the pixel as defined by VP8LDistanceToPlaneCode. { int x, y; for (y = 0; y <= 6; ++y) { for (x = -6; x <= 6; ++x) { const int offset = y * xsize + x; int plane_code; // Ignore offsets that bring us after the pixel. if (offset <= 0) continue; plane_code = VP8LDistanceToPlaneCode(xsize, offset) - 1; if (plane_code >= WINDOW_OFFSETS_SIZE_MAX) continue; window_offsets[plane_code] = offset; } } // For narrow images, not all plane codes are reached, so remove those. for (i = 0; i < WINDOW_OFFSETS_SIZE_MAX; ++i) { if (window_offsets[i] == 0) continue; window_offsets[window_offsets_size++] = window_offsets[i]; } // Given a pixel P, find the offsets that reach pixels unreachable from P-1 // with any of the offsets in window_offsets[]. for (i = 0; i < window_offsets_size; ++i) { int j; int is_reachable = 0; for (j = 0; j < window_offsets_size && !is_reachable; ++j) { is_reachable |= (window_offsets[i] == window_offsets[j] + 1); } if (!is_reachable) { window_offsets_new[window_offsets_new_size] = window_offsets[i]; ++window_offsets_new_size; } } } hash_chain->offset_length_[0] = 0; for (i = 1; i < pix_count; ++i) { int ind; int best_length = VP8LHashChainFindLength(hash_chain_best, i); int best_offset; int do_compute = 1; if (best_length >= MAX_LENGTH) { // Do not recompute the best match if we already have a maximal one in the // window. best_offset = VP8LHashChainFindOffset(hash_chain_best, i); for (ind = 0; ind < window_offsets_size; ++ind) { if (best_offset == window_offsets[ind]) { do_compute = 0; break; } } } if (do_compute) { // Figure out if we should use the offset/length from the previous pixel // as an initial guess and therefore only inspect the offsets in // window_offsets_new[]. const int use_prev = (best_length_prev > 1) && (best_length_prev < MAX_LENGTH); const int num_ind = use_prev ? window_offsets_new_size : window_offsets_size; best_length = use_prev ? best_length_prev - 1 : 0; best_offset = use_prev ? best_offset_prev : 0; // Find the longest match in a window around the pixel. for (ind = 0; ind < num_ind; ++ind) { int curr_length = 0; int j = i; int j_offset = use_prev ? i - window_offsets_new[ind] : i - window_offsets[ind]; if (j_offset < 0 || argb[j_offset] != argb[i]) continue; // The longest match is the sum of how many times each pixel is // repeated. do { const int counts_j_offset = counts_ini[j_offset]; const int counts_j = counts_ini[j]; if (counts_j_offset != counts_j) { curr_length += (counts_j_offset < counts_j) ? counts_j_offset : counts_j; break; } // The same color is repeated counts_pos times at j_offset and j. curr_length += counts_j_offset; j_offset += counts_j_offset; j += counts_j_offset; } while (curr_length <= MAX_LENGTH && j < pix_count && argb[j_offset] == argb[j]); if (best_length < curr_length) { best_offset = use_prev ? window_offsets_new[ind] : window_offsets[ind]; if (curr_length >= MAX_LENGTH) { best_length = MAX_LENGTH; break; } else { best_length = curr_length; } } } } assert(i + best_length <= pix_count); assert(best_length <= MAX_LENGTH); if (best_length <= MIN_LENGTH) { hash_chain->offset_length_[i] = 0; best_offset_prev = 0; best_length_prev = 0; } else { hash_chain->offset_length_[i] = (best_offset << MAX_LENGTH_BITS) | (uint32_t)best_length; best_offset_prev = best_offset; best_length_prev = best_length; } } hash_chain->offset_length_[0] = 0; WebPSafeFree(counts_ini); return BackwardReferencesLz77(xsize, ysize, argb, cache_bits, hash_chain, refs); }
static void CleanupParams(SmoothParams* const p) { WebPSafeFree(p->mem_); }