// Assemble a single image WebP bitstream from 'wpi'. static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi, WebPData* const bitstream) { uint8_t* dst; // Allocate data. const int need_vp8x = (wpi->alpha_ != NULL); const size_t vp8x_size = need_vp8x ? CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE : 0; const size_t alpha_size = need_vp8x ? ChunkDiskSize(wpi->alpha_) : 0; // Note: No need to output ANMF/FRGM chunk for a single image. const size_t size = RIFF_HEADER_SIZE + vp8x_size + alpha_size + ChunkDiskSize(wpi->img_); uint8_t* const data = (uint8_t*)WebPSafeMalloc(1ULL, size); if (data == NULL) return WEBP_MUX_MEMORY_ERROR; // Main RIFF header. dst = MuxEmitRiffHeader(data, size); if (need_vp8x) { dst = EmitVP8XChunk(dst, wpi->width_, wpi->height_, ALPHA_FLAG); // VP8X. dst = ChunkListEmit(wpi->alpha_, dst); // ALPH. } // Bitstream. dst = ChunkListEmit(wpi->img_, dst); assert(dst == data + size); // Output. bitstream->bytes = data; bitstream->size = size; return WEBP_MUX_OK; }
// Create data for frame/fragment given image data, offsets and duration. static WebPMuxError CreateFrameFragmentData( int width, int height, const WebPMuxFrameInfo* const info, int is_frame, WebPData* const frame_frgm) { uint8_t* frame_frgm_bytes; const size_t frame_frgm_size = kChunks[is_frame ? IDX_ANMF : IDX_FRGM].size; assert(width > 0 && height > 0 && info->duration >= 0); assert(info->dispose_method == (info->dispose_method & 1)); // Note: assertion on upper bounds is done in PutLE24(). frame_frgm_bytes = (uint8_t*)WebPSafeMalloc(1ULL, frame_frgm_size); if (frame_frgm_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; PutLE24(frame_frgm_bytes + 0, info->x_offset / 2); PutLE24(frame_frgm_bytes + 3, info->y_offset / 2); if (is_frame) { PutLE24(frame_frgm_bytes + 6, width - 1); PutLE24(frame_frgm_bytes + 9, height - 1); PutLE24(frame_frgm_bytes + 12, info->duration); frame_frgm_bytes[15] = (info->blend_method == WEBP_MUX_NO_BLEND ? 2 : 0) | (info->dispose_method == WEBP_MUX_DISPOSE_BACKGROUND ? 1 : 0); } frame_frgm->bytes = frame_frgm_bytes; frame_frgm->size = frame_frgm_size; return WEBP_MUX_OK; }
static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { const int w = buffer->width; const int h = buffer->height; const WEBP_CSP_MODE mode = buffer->colorspace; if (w <= 0 || h <= 0 || !IsValidColorspace(mode)) { return VP8_STATUS_INVALID_PARAM; } if (!buffer->is_external_memory && buffer->private_memory == NULL) { uint8_t* output; int uv_stride = 0, a_stride = 0; uint64_t uv_size = 0, a_size = 0, total_size; // We need memory and it hasn't been allocated yet. // => initialize output buffer, now that dimensions are known. const int stride = w * kModeBpp[mode]; const uint64_t size = (uint64_t)stride * h; if (!WebPIsRGBMode(mode)) { uv_stride = (w + 1) / 2; uv_size = (uint64_t)uv_stride * ((h + 1) / 2); if (mode == MODE_YUVA) { a_stride = w; a_size = (uint64_t)a_stride * h; } } total_size = size + 2 * uv_size + a_size; // Security/sanity checks output = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*output)); if (output == NULL) { return VP8_STATUS_OUT_OF_MEMORY; } buffer->private_memory = output; if (!WebPIsRGBMode(mode)) { // YUVA initialization WebPYUVABuffer* const buf = &buffer->u.YUVA; buf->y = output; buf->y_stride = stride; buf->y_size = (size_t)size; buf->u = output + size; buf->u_stride = uv_stride; buf->u_size = (size_t)uv_size; buf->v = output + size + uv_size; buf->v_stride = uv_stride; buf->v_size = (size_t)uv_size; if (mode == MODE_YUVA) { buf->a = output + size + 2 * uv_size; } buf->a_size = (size_t)a_size; buf->a_stride = a_stride; } else { // RGBA initialization WebPRGBABuffer* const buf = &buffer->u.RGBA; buf->rgba = output; buf->stride = stride; buf->size = (size_t)size; } } return CheckDecBuffer(buffer); }
int HuffmanTreeBuildImplicit(HuffmanTree* const tree, const int* const code_lengths, int code_lengths_size) { int symbol; int num_symbols = 0; int root_symbol = 0; assert(tree != NULL); assert(code_lengths != NULL); // Find out number of symbols and the root symbol. for (symbol = 0; symbol < code_lengths_size; ++symbol) { if (code_lengths[symbol] > 0) { // Note: code length = 0 indicates non-existent symbol. ++num_symbols; root_symbol = symbol; } } // Initialize the tree. Will fail for num_symbols = 0 if (!TreeInit(tree, num_symbols)) return 0; // Build tree. if (num_symbols == 1) { // Trivial case. const int max_symbol = code_lengths_size; if (root_symbol < 0 || root_symbol >= max_symbol) { HuffmanTreeRelease(tree); return 0; } return TreeAddSymbol(tree, root_symbol, 0, 0); } else { // Normal case. int ok = 0; // Get Huffman codes from the code lengths. int* const codes = (int*)WebPSafeMalloc((uint64_t)code_lengths_size, sizeof(*codes)); if (codes == NULL) goto End; if (!HuffmanCodeLengthsToCodes(code_lengths, code_lengths_size, codes)) { goto End; } // Add symbols one-by-one. for (symbol = 0; symbol < code_lengths_size; ++symbol) { if (code_lengths[symbol] > 0) { if (!TreeAddSymbol(tree, symbol, codes[symbol], code_lengths[symbol])) { goto End; } } } ok = 1; End: free(codes); ok = ok && IsFull(tree); if (!ok) HuffmanTreeRelease(tree); return ok; } }
static int CustomSetup(VP8Io* io) { WebPDecParams* const p = (WebPDecParams*)io->opaque; const WEBP_CSP_MODE colorspace = p->output->colorspace; const int is_rgb = WebPIsRGBMode(colorspace); const int is_alpha = WebPIsAlphaMode(colorspace); p->memory = NULL; p->emit = NULL; p->emit_alpha = NULL; p->emit_alpha_row = NULL; if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) { return 0; } if (is_alpha && WebPIsPremultipliedMode(colorspace)) { WebPInitUpsamplers(); } if (io->use_scaling) { #if !defined(WEBP_REDUCE_SIZE) const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p); if (!ok) { return 0; // memory error } #else return 0; // rescaling support not compiled #endif } else { if (is_rgb) { WebPInitSamplers(); p->emit = EmitSampledRGB; // default if (io->fancy_upsampling) { #ifdef FANCY_UPSAMPLING const int uv_width = (io->mb_w + 1) >> 1; p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width)); if (p->memory == NULL) { return 0; // memory error. } p->tmp_y = (uint8_t*)p->memory; p->tmp_u = p->tmp_y + io->mb_w; p->tmp_v = p->tmp_u + uv_width; p->emit = EmitFancyRGB; WebPInitUpsamplers(); #endif } } else { p->emit = EmitYUV; } if (is_alpha) { // need transparency output p->emit_alpha = (colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ? EmitAlphaRGBA4444 : is_rgb ? EmitAlphaRGB : EmitAlphaYUV; if (is_rgb) { WebPInitAlphaProcessing(); } } }
HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups) { HTreeGroup* const htree_groups = (HTreeGroup*)WebPSafeMalloc(num_htree_groups, sizeof(*htree_groups)); if (htree_groups == NULL) { return NULL; } assert(num_htree_groups <= MAX_HTREE_GROUPS); return htree_groups; }
WebPMux* WebPNewInternal(int version) { if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) { return NULL; } else { WebPMux* const mux = (WebPMux*)WebPSafeMalloc(1ULL, sizeof(WebPMux)); if (mux != NULL) MuxInit(mux); return mux; } }
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; }
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, rescaler_size; rescaler_t* work; WebPRescaler* scalers; const int num_rescalers = has_alpha ? 4 : 3; tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work); if (has_alpha) { tmp_size += work_size * sizeof(*work); } rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST; p->memory = WebPSafeMalloc(1ULL, tmp_size + rescaler_size); if (p->memory == NULL) { return 0; // memory error } work = (rescaler_t*)p->memory; scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + tmp_size); p->scaler_y = &scalers[0]; p->scaler_u = &scalers[1]; p->scaler_v = &scalers[2]; p->scaler_a = has_alpha ? &scalers[3] : NULL; WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h, buf->y, out_width, out_height, buf->y_stride, 1, work); WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height, buf->u, uv_out_width, uv_out_height, buf->u_stride, 1, 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, 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, work + work_size + 2 * uv_work_size); p->emit_alpha = EmitRescaledAlphaYUV; WebPInitAlphaProcessing(); } return 1; }
int VP8LHashChainInit(VP8LHashChain* const p, int size) { assert(p->size_ == 0); assert(p->offset_length_ == NULL); assert(size > 0); p->offset_length_ = (uint32_t*)WebPSafeMalloc(size, sizeof(*p->offset_length_)); if (p->offset_length_ == NULL) return 0; p->size_ = size; return 1; }
static int TreeInit(HuffmanTree* const tree, int num_leaves) { assert(tree != NULL); if (num_leaves == 0) return 0; // We allocate maximum possible nodes in the tree at once. // Note that a Huffman tree is a full binary tree; and in a full binary tree // with L leaves, the total number of nodes N = 2 * L - 1. tree->max_nodes_ = 2 * num_leaves - 1; tree->root_ = (HuffmanTreeNode*)WebPSafeMalloc((uint64_t)tree->max_nodes_, sizeof(*tree->root_)); if (tree->root_ == NULL) return 0; TreeNodeInit(tree->root_); // Initialize root. tree->num_nodes_ = 1; return 1; }
static int TBufferNewPage(VP8TBuffer* const b) { VP8Tokens* page = NULL; if (!b->error_) { const size_t size = sizeof(*page) + b->page_size_ * sizeof(token_t); page = (VP8Tokens*)WebPSafeMalloc(1ULL, size); } if (page == NULL) { b->error_ = 1; return 0; } page->next_ = NULL; *b->last_page_ = page; b->last_page_ = &page->next_; b->left_ = b->page_size_; b->tokens_ = (token_t*)TOKEN_DATA(page); return 1; }
int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref, int type, float result[5]) { VP8DistoStats stats[5]; int w, h; memset(stats, 0, sizeof(stats)); VP8SSIMDspInit(); if (src == NULL || ref == NULL || src->width != ref->width || src->height != ref->height || src->use_argb != ref->use_argb || result == NULL) { return 0; } w = src->width; h = src->height; if (src->use_argb == 1) { if (src->argb == NULL || ref->argb == NULL) { return 0; } else { int i, j, c; uint8_t* tmp1, *tmp2; uint8_t* const tmp_plane = (uint8_t*)WebPSafeMalloc(2ULL * w * h, sizeof(*tmp_plane)); if (tmp_plane == NULL) return 0; tmp1 = tmp_plane; tmp2 = tmp_plane + w * h; for (c = 0; c < 4; ++c) { for (j = 0; j < h; ++j) { for (i = 0; i < w; ++i) { tmp1[j * w + i] = src->argb[i + j * src->argb_stride] >> (c * 8); tmp2[j * w + i] = ref->argb[i + j * ref->argb_stride] >> (c * 8); } } if (type >= 2) { AccumulateLSIM(tmp1, w, tmp2, w, w, h, &stats[c]); } else { VP8SSIMAccumulatePlane(tmp1, w, tmp2, w, w, h, &stats[c]); } } free(tmp_plane); } } else {
static int CustomSetup(VP8Io* io) { WebPDecParams* const p = (WebPDecParams*)io->opaque; const WEBP_CSP_MODE colorspace = p->output->colorspace; const int is_rgb = WebPIsRGBMode(colorspace); const int is_alpha = WebPIsAlphaMode(colorspace); p->memory = NULL; p->emit = NULL; p->emit_alpha = NULL; p->emit_alpha_row = NULL; if (!WebPIoInitFromOptions(p->options, io, is_alpha ? MODE_YUV : MODE_YUVA)) { return 0; } if (is_alpha && WebPIsPremultipliedMode(colorspace)) { WebPInitUpsamplers(); } if (io->use_scaling) { const int ok = is_rgb ? InitRGBRescaler(io, p) : InitYUVRescaler(io, p); if (!ok) { return 0; // memory error } } else { if (is_rgb) { p->emit = EmitSampledRGB; // default if (io->fancy_upsampling) { #ifdef FANCY_UPSAMPLING const int uv_width = (io->mb_w + 1) >> 1; p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width)); if (p->memory == NULL) { return 0; // memory error. } p->tmp_y = (uint8_t*)p->memory; p->tmp_u = p->tmp_y + io->mb_w; p->tmp_v = p->tmp_u + uv_width; p->emit = EmitFancyRGB; WebPInitUpsamplers(); #endif } else { WebPInitSamplers(); } } else {
// Initialize all params. static int InitParams(uint8_t* const data, int width, int height, int stride, int radius, SmoothParams* const p) { const int R = 2 * radius + 1; // total size of the kernel const size_t size_scratch_m = (R + 1) * width * sizeof(*p->start_); const size_t size_m = width * sizeof(*p->average_); const size_t size_lut = (1 + 2 * LUT_SIZE) * sizeof(*p->correction_); const size_t total_size = size_scratch_m + size_m + size_lut; uint8_t* mem = (uint8_t*)WebPSafeMalloc(1U, total_size); if (mem == NULL) return 0; p->mem_ = (void*)mem; p->start_ = (uint16_t*)mem; p->cur_ = p->start_; p->end_ = p->start_ + R * width; p->top_ = p->end_ - width; memset(p->top_, 0, width * sizeof(*p->top_)); mem += size_scratch_m; p->average_ = (uint16_t*)mem; mem += size_m; p->width_ = width; p->height_ = height; p->stride_ = stride; p->src_ = data; p->dst_ = data; p->radius_ = radius; p->scale_ = (1 << (FIX + LFIX)) / (R * R); // normalization constant p->row_ = -radius; // analyze the input distribution so we can best-fit the threshold CountLevels(p); // correction table p->correction_ = ((int16_t*)mem) + LUT_SIZE; InitCorrectionLUT(p->correction_, p->min_level_dist_); return 1; }
// Create a new block, either from the free list or allocated static PixOrCopyBlock* BackwardRefsNewBlock(VP8LBackwardRefs* const refs) { PixOrCopyBlock* b = refs->free_blocks_; if (b == NULL) { // allocate new memory chunk const size_t total_size = sizeof(*b) + refs->block_size_ * sizeof(*b->start_); b = (PixOrCopyBlock*)WebPSafeMalloc(1ULL, total_size); if (b == NULL) { refs->error_ |= 1; return NULL; } b->start_ = (PixOrCopy*)((uint8_t*)b + sizeof(*b)); // not always aligned } else { // recycle from free-list refs->free_blocks_ = b->next_; } *refs->tail_ = b; refs->tail_ = &b->next_; refs->last_block_ = b; b->next_ = NULL; b->size_ = 0; return b; }
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 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); }
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((uint64_t)w * h, sizeof(*tmp)); assert((uint64_t)(w * h) == (uint64_t)w * h); 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_; cnt[mb[-w - 1].segment_]++; cnt[mb[-w + 0].segment_]++; cnt[mb[-w + 1].segment_]++; cnt[mb[ - 1].segment_]++; cnt[mb[ + 1].segment_]++; cnt[mb[ w - 1].segment_]++; cnt[mb[ w + 0].segment_]++; cnt[mb[ w + 1].segment_]++; for (n = 0; n < NUM_MB_SEGMENTS; ++n) { if (cnt[n] >= majority_cnt_3_x_3_grid) { majority_seg = n; } } 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]; } } free(tmp); }
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); }
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; }
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; }
static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, WebPPicture* const picture) { const int use_filter = (config->filter_strength > 0) || (config->autofilter > 0); const int mb_w = (picture->width + 15) >> 4; const int mb_h = (picture->height + 15) >> 4; const int preds_w = 4 * mb_w + 1; const int preds_h = 4 * mb_h + 1; const size_t preds_size = preds_w * preds_h * sizeof(uint8_t); const int top_stride = mb_w * 16; const size_t nz_size = (mb_w + 1) * sizeof(uint32_t); const size_t cache_size = (3 * YUV_SIZE + PRED_SIZE) * sizeof(uint8_t); const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo); const size_t samples_size = (2 * top_stride + // top-luma/u/v 16 + 16 + 16 + 8 + 1 + // left y/u/v 2 * ALIGN_CST) // align all * sizeof(uint8_t); const size_t lf_stats_size = config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0; VP8Encoder* enc; uint8_t* mem; const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct + ALIGN_CST // cache alignment + cache_size // working caches + info_size // modes info + preds_size // prediction modes + samples_size // top/left samples + nz_size // coeff context bits + lf_stats_size; // autofilter stats #ifdef PRINT_MEMORY_INFO printf("===================================\n"); printf("Memory used:\n" " encoder: %ld\n" " block cache: %ld\n" " info: %ld\n" " preds: %ld\n" " top samples: %ld\n" " non-zero: %ld\n" " lf-stats: %ld\n" " total: %ld\n", sizeof(VP8Encoder) + ALIGN_CST, cache_size, info_size, preds_size, samples_size, nz_size, lf_stats_size, size); printf("Transcient object sizes:\n" " VP8EncIterator: %ld\n" " VP8ModeScore: %ld\n" " VP8SegmentInfo: %ld\n" " VP8Proba: %ld\n" " LFStats: %ld\n", sizeof(VP8EncIterator), sizeof(VP8ModeScore), sizeof(VP8SegmentInfo), sizeof(VP8Proba), sizeof(LFStats)); printf("Picture size (yuv): %ld\n", mb_w * mb_h * 384 * sizeof(uint8_t)); printf("===================================\n"); #endif mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem)); if (mem == NULL) { WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); return NULL; } enc = (VP8Encoder*)mem; mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc)); memset(enc, 0, sizeof(*enc)); enc->num_parts_ = 1 << config->partitions; enc->mb_w_ = mb_w; enc->mb_h_ = mb_h; enc->preds_w_ = preds_w; enc->yuv_in_ = (uint8_t*)mem; mem += YUV_SIZE; enc->yuv_out_ = (uint8_t*)mem; mem += YUV_SIZE; enc->yuv_out2_ = (uint8_t*)mem; mem += YUV_SIZE; enc->yuv_p_ = (uint8_t*)mem; mem += PRED_SIZE; enc->mb_info_ = (VP8MBInfo*)mem; mem += info_size; enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_; mem += preds_w * preds_h * sizeof(uint8_t); enc->nz_ = 1 + (uint32_t*)mem; mem += nz_size; enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL; mem += lf_stats_size; // top samples (all 16-aligned) mem = (uint8_t*)DO_ALIGN(mem); enc->y_top_ = (uint8_t*)mem; enc->uv_top_ = enc->y_top_ + top_stride; mem += 2 * top_stride; mem = (uint8_t*)DO_ALIGN(mem + 1); enc->y_left_ = (uint8_t*)mem; mem += 16 + 16; enc->u_left_ = (uint8_t*)mem; mem += 16; enc->v_left_ = (uint8_t*)mem; mem += 8; enc->config_ = config; enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2; enc->pic_ = picture; enc->percent_ = 0; MapConfigToTools(enc); VP8EncDspInit(); VP8DefaultProbas(enc); ResetSegmentHeader(enc); ResetFilterHeader(enc); ResetBoundaryPredictions(enc); VP8EncInitAlpha(enc); #ifdef WEBP_EXPERIMENTAL_FEATURES VP8EncInitLayer(enc); #endif VP8TBufferInit(&enc->tokens_); return enc; }
// Create an optimal Huffman tree. // // (data,length): population counts. // tree_limit: maximum bit depth (inclusive) of the codes. // bit_depths[]: how many bits are used for the symbol. // // Returns 0 when an error has occurred. // // The catch here is that the tree cannot be arbitrarily deep // // count_limit is the value that is to be faked as the minimum value // and this minimum value is raised until the tree matches the // maximum length requirement. // // This algorithm is not of excellent performance for very long data blocks, // especially when population counts are longer than 2**tree_limit, but // we are not planning to use this with extremely long blocks. // // See http://en.wikipedia.org/wiki/Huffman_coding static int GenerateOptimalTree(const int* const histogram, int histogram_size, int tree_depth_limit, uint8_t* const bit_depths) { int count_min; HuffmanTree* tree_pool; HuffmanTree* tree; int tree_size_orig = 0; int i; for (i = 0; i < histogram_size; ++i) { if (histogram[i] != 0) { ++tree_size_orig; } } if (tree_size_orig == 0) { // pretty optimal already! return 1; } // 3 * tree_size is enough to cover all the nodes representing a // population and all the inserted nodes combining two existing nodes. // The tree pool needs 2 * (tree_size_orig - 1) entities, and the // tree needs exactly tree_size_orig entities. tree = (HuffmanTree*)WebPSafeMalloc(3ULL * tree_size_orig, sizeof(*tree)); if (tree == NULL) return 0; tree_pool = tree + tree_size_orig; // For block sizes with less than 64k symbols we never need to do a // second iteration of this loop. // If we actually start running inside this loop a lot, we would perhaps // be better off with the Katajainen algorithm. assert(tree_size_orig <= (1 << (tree_depth_limit - 1))); for (count_min = 1; ; count_min *= 2) { int tree_size = tree_size_orig; // We need to pack the Huffman tree in tree_depth_limit bits. // So, we try by faking histogram entries to be at least 'count_min'. int idx = 0; int j; for (j = 0; j < histogram_size; ++j) { if (histogram[j] != 0) { const int count = (histogram[j] < count_min) ? count_min : histogram[j]; tree[idx].total_count_ = count; tree[idx].value_ = j; tree[idx].pool_index_left_ = -1; tree[idx].pool_index_right_ = -1; ++idx; } } // Build the Huffman tree. qsort(tree, tree_size, sizeof(*tree), CompareHuffmanTrees); if (tree_size > 1) { // Normal case. int tree_pool_size = 0; while (tree_size > 1) { // Finish when we have only one root. int count; tree_pool[tree_pool_size++] = tree[tree_size - 1]; tree_pool[tree_pool_size++] = tree[tree_size - 2]; count = tree_pool[tree_pool_size - 1].total_count_ + tree_pool[tree_pool_size - 2].total_count_; tree_size -= 2; { // Search for the insertion point. int k; for (k = 0; k < tree_size; ++k) { if (tree[k].total_count_ <= count) { break; } } memmove(tree + (k + 1), tree + k, (tree_size - k) * sizeof(*tree)); tree[k].total_count_ = count; tree[k].value_ = -1; tree[k].pool_index_left_ = tree_pool_size - 1; tree[k].pool_index_right_ = tree_pool_size - 2; tree_size = tree_size + 1; } } SetBitDepths(&tree[0], tree_pool, bit_depths, 0); } else if (tree_size == 1) { // Trivial case: only one element. bit_depths[tree[0].value_] = 1; } { // Test if this Huffman tree satisfies our 'tree_depth_limit' criteria. int max_depth = bit_depths[0]; for (j = 1; j < histogram_size; ++j) { if (max_depth < bit_depths[j]) { max_depth = bit_depths[j]; } } if (max_depth <= tree_depth_limit) { break; } } } free(tree); return 1; }
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; }
static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, WebPPicture* const picture) { const int use_filter = (config->filter_strength > 0) || (config->autofilter > 0); const int mb_w = (picture->width + 15) >> 4; const int mb_h = (picture->height + 15) >> 4; const int preds_w = 4 * mb_w + 1; const int preds_h = 4 * mb_h + 1; const size_t preds_size = preds_w * preds_h * sizeof(uint8_t); const int top_stride = mb_w * 16; const size_t nz_size = (mb_w + 1) * sizeof(uint32_t) + ALIGN_CST; const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo); const size_t samples_size = 2 * top_stride * sizeof(uint8_t) // top-luma/u/v + ALIGN_CST; // align all const size_t lf_stats_size = config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0; VP8Encoder* enc; uint8_t* mem; const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct + ALIGN_CST // cache alignment + info_size // modes info + preds_size // prediction modes + samples_size // top/left samples + nz_size // coeff context bits + lf_stats_size; // autofilter stats #ifdef PRINT_MEMORY_INFO printf("===================================\n"); printf("Memory used:\n" " encoder: %ld\n" " info: %ld\n" " preds: %ld\n" " top samples: %ld\n" " non-zero: %ld\n" " lf-stats: %ld\n" " total: %ld\n", sizeof(VP8Encoder) + ALIGN_CST, info_size, preds_size, samples_size, nz_size, lf_stats_size, size); printf("Transient object sizes:\n" " VP8EncIterator: %ld\n" " VP8ModeScore: %ld\n" " VP8SegmentInfo: %ld\n" " VP8Proba: %ld\n" " LFStats: %ld\n", sizeof(VP8EncIterator), sizeof(VP8ModeScore), sizeof(VP8SegmentInfo), sizeof(VP8Proba), sizeof(LFStats)); printf("Picture size (yuv): %ld\n", mb_w * mb_h * 384 * sizeof(uint8_t)); printf("===================================\n"); #endif mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem)); if (mem == NULL) { WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); return NULL; } enc = (VP8Encoder*)mem; mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc)); memset(enc, 0, sizeof(*enc)); enc->num_parts_ = 1 << config->partitions; enc->mb_w_ = mb_w; enc->mb_h_ = mb_h; enc->preds_w_ = preds_w; enc->mb_info_ = (VP8MBInfo*)mem; mem += info_size; enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_; mem += preds_w * preds_h * sizeof(uint8_t); enc->nz_ = 1 + (uint32_t*)DO_ALIGN(mem); mem += nz_size; enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL; mem += lf_stats_size; // top samples (all 16-aligned) mem = (uint8_t*)DO_ALIGN(mem); enc->y_top_ = (uint8_t*)mem; enc->uv_top_ = enc->y_top_ + top_stride; mem += 2 * top_stride; assert(mem <= (uint8_t*)enc + size); enc->config_ = config; enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2; enc->pic_ = picture; enc->percent_ = 0; MapConfigToTools(enc); VP8EncDspInit(); VP8DefaultProbas(enc); ResetSegmentHeader(enc); ResetFilterHeader(enc); ResetBoundaryPredictions(enc); VP8GetResidualCostInit(); VP8SetResidualCoeffsInit(); VP8EncInitAlpha(enc); // lower quality means smaller output -> we modulate a little the page // size based on quality. This is just a crude 1rst-order prediction. { const float scale = 1.f + config->quality * 5.f / 100.f; // in [1,6] VP8TBufferInit(&enc->tokens_, (int)(mb_w * mb_h * 4 * scale)); } return enc; }
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 rescaler_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, rescaler_size; WebPRescaler* scalers; const int num_rescalers = has_alpha ? 4 : 3; 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); rescaler_size = num_rescalers * sizeof(*p->scaler_y) + WEBP_ALIGN_CST; p->memory = WebPSafeMalloc(1ULL, total_size + rescaler_size); if (p->memory == NULL) { return 0; // memory error } work = (rescaler_t*)p->memory; tmp = (uint8_t*)(work + tmp_size1); scalers = (WebPRescaler*)WEBP_ALIGN((const uint8_t*)work + total_size); p->scaler_y = &scalers[0]; p->scaler_u = &scalers[1]; p->scaler_v = &scalers[2]; p->scaler_a = has_alpha ? &scalers[3] : NULL; WebPRescalerInit(p->scaler_y, io->mb_w, io->mb_h, tmp + 0 * out_width, out_width, out_height, 0, 1, work + 0 * work_size); WebPRescalerInit(p->scaler_u, uv_in_width, uv_in_height, tmp + 1 * out_width, out_width, out_height, 0, 1, work + 1 * work_size); WebPRescalerInit(p->scaler_v, uv_in_width, uv_in_height, tmp + 2 * out_width, out_width, out_height, 0, 1, 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, 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; }