-Upgraded webp to a MUCH newer version. Hoping it fixes some bugs in the process. Keeping old version just in case for now.
-Added ability to convert xml and tscn scenes to binary on export, makes loading of larger scenes faster
This commit is contained in:
@ -86,6 +86,7 @@ env_base.__class__.add_source_files = methods.add_source_files
|
||||
|
||||
env_base["x86_opt_gcc"]=False
|
||||
env_base["x86_opt_vc"]=False
|
||||
env_base["armv7_opt_gcc"]=False
|
||||
|
||||
customs = ['custom.py']
|
||||
|
||||
|
||||
@ -1,63 +1,115 @@
|
||||
Import('env')
|
||||
|
||||
|
||||
webp_sources = [
|
||||
"webp/mux/muxedit.c",
|
||||
"webp/mux/muxread.c",
|
||||
"webp/mux/muxinternal.c",
|
||||
"webp/mux/demux.c",
|
||||
"webp/enc/tree.c",
|
||||
"webp/enc/analysis.c",
|
||||
"webp/enc/backward_references.c",
|
||||
"webp/enc/alpha.c",
|
||||
"webp/enc/picture.c",
|
||||
"webp/enc/frame.c",
|
||||
"webp/enc/webpenc.c",
|
||||
"webp/enc/cost.c",
|
||||
"webp/enc/filter.c",
|
||||
"webp/enc/vp8l.c",
|
||||
"webp/enc/quant.c",
|
||||
"webp/enc/histogram.c",
|
||||
"webp/enc/syntax.c",
|
||||
"webp/enc/config.c",
|
||||
"webp/enc/layer.c",
|
||||
"webp/enc/iterator.c",
|
||||
"webp/dsp/dec_sse2.c",
|
||||
"webp/dsp/upsampling_sse2.c",
|
||||
"webp/dsp/dec_neon.c",
|
||||
"webp/dsp/enc.c",
|
||||
"webp/dsp/enc_sse2.c",
|
||||
"webp/dsp/upsampling.c",
|
||||
"webp/dsp/lossless.c",
|
||||
"webp/dsp/cpu.c",
|
||||
"webp/dsp/dec.c",
|
||||
"webp/dsp/yuv.c",
|
||||
"webp/utils/bit_reader.c",
|
||||
"webp/utils/filters.c",
|
||||
"webp/utils/bit_writer.c",
|
||||
"webp/utils/thread.c",
|
||||
"webp/utils/quant_levels.c",
|
||||
"webp/utils/color_cache.c",
|
||||
"webp/utils/rescaler.c",
|
||||
"webp/utils/utils.c",
|
||||
"webp/utils/huffman.c",
|
||||
"webp/utils/huffman_encode.c",
|
||||
"webp/dec/tree.c",
|
||||
"webp/dec/alpha.c",
|
||||
"webp/dec/frame.c",
|
||||
"webp/dec/vp8l.c",
|
||||
"webp/dec/vp8.c",
|
||||
"webp/dec/quant.c",
|
||||
"webp/dec/webp.c",
|
||||
"webp/dec/buffer.c",
|
||||
"webp/dec/io.c",
|
||||
"webp/dec/layer.c",
|
||||
"webp/dec/idec.c",
|
||||
"webp/image_loader_webp.cpp"
|
||||
"webp/enc/webpenc.c",\
|
||||
"webp/enc/near_lossless.c",\
|
||||
"webp/enc/frame.c",\
|
||||
"webp/enc/alpha.c",\
|
||||
"webp/enc/picture_csp.c",\
|
||||
"webp/enc/vp8l.c",\
|
||||
"webp/enc/picture_psnr.c",\
|
||||
"webp/enc/delta_palettization.c",\
|
||||
"webp/enc/syntax.c",\
|
||||
"webp/enc/backward_references.c",\
|
||||
"webp/enc/token.c",\
|
||||
"webp/enc/analysis.c",\
|
||||
"webp/enc/iterator.c",\
|
||||
"webp/enc/picture_tools.c",\
|
||||
"webp/enc/picture_rescale.c",\
|
||||
"webp/enc/config.c",\
|
||||
"webp/enc/tree.c",\
|
||||
"webp/enc/cost.c",\
|
||||
"webp/enc/picture.c",\
|
||||
"webp/enc/quant.c",\
|
||||
"webp/enc/filter.c",\
|
||||
"webp/enc/histogram.c",\
|
||||
"webp/image_loader_webp.cpp",\
|
||||
"webp/utils/rescaler.c",\
|
||||
"webp/utils/filters.c",\
|
||||
"webp/utils/quant_levels_dec.c",\
|
||||
"webp/utils/huffman.c",\
|
||||
"webp/utils/thread.c",\
|
||||
"webp/utils/quant_levels.c",\
|
||||
"webp/utils/bit_writer.c",\
|
||||
"webp/utils/bit_reader.c",\
|
||||
"webp/utils/random.c",\
|
||||
"webp/utils/utils.c",\
|
||||
"webp/utils/huffman_encode.c",\
|
||||
"webp/utils/color_cache.c",\
|
||||
"webp/mux/muxinternal.c",\
|
||||
"webp/mux/muxread.c",\
|
||||
"webp/mux/anim_encode.c",\
|
||||
"webp/mux/muxedit.c",\
|
||||
"webp/dec/webp.c",\
|
||||
"webp/dec/frame.c",\
|
||||
"webp/dec/alpha.c",\
|
||||
"webp/dec/vp8l.c",\
|
||||
"webp/dec/io.c",\
|
||||
"webp/dec/vp8.c",\
|
||||
"webp/dec/idec.c",\
|
||||
"webp/dec/tree.c",\
|
||||
"webp/dec/buffer.c",\
|
||||
"webp/dec/quant.c",\
|
||||
"webp/demux/demux.c",\
|
||||
"webp/demux/anim_decode.c",\
|
||||
"webp/dsp/yuv.c",\
|
||||
"webp/dsp/filters_sse2.c",\
|
||||
"webp/dsp/dec_sse41.c",\
|
||||
"webp/dsp/rescaler.c",\
|
||||
"webp/dsp/lossless_sse2.c",\
|
||||
"webp/dsp/alpha_processing_sse41.c",\
|
||||
"webp/dsp/alpha_processing_sse2.c",\
|
||||
"webp/dsp/filters.c",\
|
||||
"webp/dsp/upsampling_mips_dsp_r2.c",\
|
||||
"webp/dsp/dec_neon.c",\
|
||||
"webp/dsp/enc_neon.c",\
|
||||
"webp/dsp/lossless_enc_mips32.c",\
|
||||
"webp/dsp/lossless_enc_sse2.c",\
|
||||
"webp/dsp/upsampling.c",\
|
||||
"webp/dsp/lossless_enc_neon.c",\
|
||||
"webp/dsp/alpha_processing.c",\
|
||||
"webp/dsp/cost_sse2.c",\
|
||||
"webp/dsp/dec_mips32.c",\
|
||||
"webp/dsp/enc_avx2.c",\
|
||||
"webp/dsp/rescaler_mips32.c",\
|
||||
"webp/dsp/enc.c",\
|
||||
"webp/dsp/lossless_enc_sse41.c",\
|
||||
"webp/dsp/cost_mips32.c",\
|
||||
"webp/dsp/lossless_mips_dsp_r2.c",\
|
||||
"webp/dsp/filters_mips_dsp_r2.c",\
|
||||
"webp/dsp/upsampling_neon.c",\
|
||||
"webp/dsp/alpha_processing_mips_dsp_r2.c",\
|
||||
"webp/dsp/enc_mips_dsp_r2.c",\
|
||||
"webp/dsp/lossless.c",\
|
||||
"webp/dsp/yuv_mips_dsp_r2.c",\
|
||||
"webp/dsp/cost_mips_dsp_r2.c",\
|
||||
"webp/dsp/argb.c",\
|
||||
"webp/dsp/dec_sse2.c",\
|
||||
"webp/dsp/rescaler_sse2.c",\
|
||||
"webp/dsp/enc_sse41.c",\
|
||||
"webp/dsp/argb_mips_dsp_r2.c",\
|
||||
"webp/dsp/lossless_enc_mips_dsp_r2.c",\
|
||||
"webp/dsp/dec_clip_tables.c",\
|
||||
"webp/dsp/yuv_mips32.c",\
|
||||
"webp/dsp/cpu.c",\
|
||||
"webp/dsp/dec.c",\
|
||||
"webp/dsp/argb_sse2.c",\
|
||||
"webp/dsp/lossless_neon.c",\
|
||||
"webp/dsp/lossless_enc.c",\
|
||||
"webp/dsp/enc_mips32.c",\
|
||||
"webp/dsp/cost.c",\
|
||||
"webp/dsp/rescaler_mips_dsp_r2.c",\
|
||||
"webp/dsp/dec_mips_dsp_r2.c",\
|
||||
"webp/dsp/rescaler_neon.c",\
|
||||
"webp/dsp/yuv_sse2.c",\
|
||||
"webp/dsp/enc_sse2.c",\
|
||||
"webp/dsp/upsampling_sse2.c"
|
||||
]
|
||||
|
||||
env.drivers_sources+=webp_sources
|
||||
|
||||
#env.add_source_files(env.drivers_sources, webp_sources)
|
||||
|
||||
|
||||
Export('env')
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Alpha-plane decompression.
|
||||
@ -10,131 +12,156 @@
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "./alphai.h"
|
||||
#include "./vp8i.h"
|
||||
#include "./vp8li.h"
|
||||
#include "../utils/filters.h"
|
||||
#include "../utils/quant_levels.h"
|
||||
#include "../format_constants.h"
|
||||
#include "../dsp/dsp.h"
|
||||
#include "../utils/quant_levels_dec.h"
|
||||
#include "../utils/utils.h"
|
||||
#include "../webp/format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
// ALPHDecoder object.
|
||||
|
||||
// TODO(skal): move to dsp/ ?
|
||||
static void CopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height) {
|
||||
while (height-- > 0) {
|
||||
memcpy(dst, src, width);
|
||||
src += src_stride;
|
||||
dst += dst_stride;
|
||||
ALPHDecoder* ALPHNew(void) {
|
||||
ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
||||
return dec;
|
||||
}
|
||||
|
||||
void ALPHDelete(ALPHDecoder* const dec) {
|
||||
if (dec != NULL) {
|
||||
VP8LDelete(dec->vp8l_dec_);
|
||||
dec->vp8l_dec_ = NULL;
|
||||
WebPSafeFree(dec);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Decodes the compressed data 'data' of size 'data_size' into the 'output'.
|
||||
// The 'output' buffer should be pre-allocated and must be of the same
|
||||
// dimension 'height'x'stride', as that of the image.
|
||||
//
|
||||
// Returns 1 on successfully decoding the compressed alpha and
|
||||
// 0 if either:
|
||||
// error in bit-stream header (invalid compression mode or filter), or
|
||||
// error returned by appropriate compression method.
|
||||
// Decoding.
|
||||
|
||||
static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||
int width, int height, int stride, uint8_t* output) {
|
||||
uint8_t* decoded_data = NULL;
|
||||
const size_t decoded_size = height * width;
|
||||
uint8_t* unfiltered_data = NULL;
|
||||
WEBP_FILTER_TYPE filter;
|
||||
int pre_processing;
|
||||
int rsrv;
|
||||
// Initialize alpha decoding by parsing the alpha header and decoding the image
|
||||
// header for alpha data stored using lossless compression.
|
||||
// Returns false in case of error in alpha header (data too short, invalid
|
||||
// compression method or filter, error in lossless header data etc).
|
||||
static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data,
|
||||
size_t data_size, int width, int height, uint8_t* output) {
|
||||
int ok = 0;
|
||||
int method;
|
||||
const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN;
|
||||
const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN;
|
||||
int rsrv;
|
||||
|
||||
assert(width > 0 && height > 0 && stride >= width);
|
||||
assert(width > 0 && height > 0);
|
||||
assert(data != NULL && output != NULL);
|
||||
|
||||
dec->width_ = width;
|
||||
dec->height_ = height;
|
||||
|
||||
if (data_size <= ALPHA_HEADER_LEN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
method = (data[0] >> 0) & 0x03;
|
||||
filter = (data[0] >> 2) & 0x03;
|
||||
pre_processing = (data[0] >> 4) & 0x03;
|
||||
dec->method_ = (data[0] >> 0) & 0x03;
|
||||
dec->filter_ = (data[0] >> 2) & 0x03;
|
||||
dec->pre_processing_ = (data[0] >> 4) & 0x03;
|
||||
rsrv = (data[0] >> 6) & 0x03;
|
||||
if (method < ALPHA_NO_COMPRESSION ||
|
||||
method > ALPHA_LOSSLESS_COMPRESSION ||
|
||||
filter >= WEBP_FILTER_LAST ||
|
||||
pre_processing > ALPHA_PREPROCESSED_LEVELS ||
|
||||
if (dec->method_ < ALPHA_NO_COMPRESSION ||
|
||||
dec->method_ > ALPHA_LOSSLESS_COMPRESSION ||
|
||||
dec->filter_ >= WEBP_FILTER_LAST ||
|
||||
dec->pre_processing_ > ALPHA_PREPROCESSED_LEVELS ||
|
||||
rsrv != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (method == ALPHA_NO_COMPRESSION) {
|
||||
ok = (data_size >= decoded_size);
|
||||
decoded_data = (uint8_t*)data + ALPHA_HEADER_LEN;
|
||||
if (dec->method_ == ALPHA_NO_COMPRESSION) {
|
||||
const size_t alpha_decoded_size = dec->width_ * dec->height_;
|
||||
ok = (alpha_data_size >= alpha_decoded_size);
|
||||
} else {
|
||||
decoded_data = (uint8_t*)malloc(decoded_size);
|
||||
if (decoded_data == NULL) return 0;
|
||||
ok = VP8LDecodeAlphaImageStream(width, height,
|
||||
data + ALPHA_HEADER_LEN,
|
||||
data_size - ALPHA_HEADER_LEN,
|
||||
decoded_data);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
WebPFilterFunc unfilter_func = WebPUnfilters[filter];
|
||||
if (unfilter_func != NULL) {
|
||||
unfiltered_data = (uint8_t*)malloc(decoded_size);
|
||||
if (unfiltered_data == NULL) {
|
||||
ok = 0;
|
||||
goto Error;
|
||||
}
|
||||
// TODO(vikas): Implement on-the-fly decoding & filter mechanism to decode
|
||||
// and apply filter per image-row.
|
||||
unfilter_func(decoded_data, width, height, 1, width, unfiltered_data);
|
||||
// Construct raw_data (height x stride) from alpha data (height x width).
|
||||
CopyPlane(unfiltered_data, width, output, stride, width, height);
|
||||
free(unfiltered_data);
|
||||
} else {
|
||||
// Construct raw_data (height x stride) from alpha data (height x width).
|
||||
CopyPlane(decoded_data, width, output, stride, width, height);
|
||||
}
|
||||
if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
|
||||
ok = DequantizeLevels(decoded_data, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
Error:
|
||||
if (method != ALPHA_NO_COMPRESSION) {
|
||||
free(decoded_data);
|
||||
assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION);
|
||||
ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size, output);
|
||||
}
|
||||
VP8FiltersInit();
|
||||
return ok;
|
||||
}
|
||||
|
||||
// Decodes, unfilters and dequantizes *at least* 'num_rows' rows of alpha
|
||||
// starting from row number 'row'. It assumes that rows up to (row - 1) have
|
||||
// already been decoded.
|
||||
// Returns false in case of bitstream error.
|
||||
static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) {
|
||||
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
||||
const int width = alph_dec->width_;
|
||||
const int height = alph_dec->height_;
|
||||
WebPUnfilterFunc unfilter_func = WebPUnfilters[alph_dec->filter_];
|
||||
uint8_t* const output = dec->alpha_plane_;
|
||||
if (alph_dec->method_ == ALPHA_NO_COMPRESSION) {
|
||||
const size_t offset = row * width;
|
||||
const size_t num_pixels = num_rows * width;
|
||||
assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN + offset + num_pixels);
|
||||
memcpy(dec->alpha_plane_ + offset,
|
||||
dec->alpha_data_ + ALPHA_HEADER_LEN + offset, num_pixels);
|
||||
} else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION
|
||||
assert(alph_dec->vp8l_dec_ != NULL);
|
||||
if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (unfilter_func != NULL) {
|
||||
unfilter_func(width, height, width, row, num_rows, output);
|
||||
}
|
||||
|
||||
if (row + num_rows == dec->pic_hdr_.height_) {
|
||||
dec->is_alpha_decoded_ = 1;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main entry point.
|
||||
|
||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
int row, int num_rows) {
|
||||
const int stride = dec->pic_hdr_.width_;
|
||||
const int width = dec->pic_hdr_.width_;
|
||||
const int height = dec->pic_hdr_.height_;
|
||||
|
||||
if (row < 0 || num_rows < 0 || row + num_rows > dec->pic_hdr_.height_) {
|
||||
if (row < 0 || num_rows <= 0 || row + num_rows > height) {
|
||||
return NULL; // sanity check.
|
||||
}
|
||||
|
||||
if (row == 0) {
|
||||
// Decode everything during the first call.
|
||||
if (!DecodeAlpha(dec->alpha_data_, (size_t)dec->alpha_data_size_,
|
||||
dec->pic_hdr_.width_, dec->pic_hdr_.height_, stride,
|
||||
dec->alpha_plane_)) {
|
||||
return NULL; // Error.
|
||||
// Initialize decoding.
|
||||
assert(dec->alpha_plane_ != NULL);
|
||||
dec->alph_dec_ = ALPHNew();
|
||||
if (dec->alph_dec_ == NULL) return NULL;
|
||||
if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_,
|
||||
width, height, dec->alpha_plane_)) {
|
||||
ALPHDelete(dec->alph_dec_);
|
||||
dec->alph_dec_ = NULL;
|
||||
return NULL;
|
||||
}
|
||||
// if we allowed use of alpha dithering, check whether it's needed at all
|
||||
if (dec->alph_dec_->pre_processing_ != ALPHA_PREPROCESSED_LEVELS) {
|
||||
dec->alpha_dithering_ = 0; // disable dithering
|
||||
} else {
|
||||
num_rows = height; // decode everything in one pass
|
||||
}
|
||||
}
|
||||
|
||||
// Return a pointer to the current decoded row.
|
||||
return dec->alpha_plane_ + row * stride;
|
||||
}
|
||||
if (!dec->is_alpha_decoded_) {
|
||||
int ok = 0;
|
||||
assert(dec->alph_dec_ != NULL);
|
||||
ok = ALPHDecode(dec, row, num_rows);
|
||||
if (ok && dec->alpha_dithering_ > 0) {
|
||||
ok = WebPDequantizeLevels(dec->alpha_plane_, width, height,
|
||||
dec->alpha_dithering_);
|
||||
}
|
||||
if (!ok || dec->is_alpha_decoded_) {
|
||||
ALPHDelete(dec->alph_dec_);
|
||||
dec->alph_dec_ = NULL;
|
||||
}
|
||||
if (!ok) return NULL; // Error.
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
// Return a pointer to the current decoded row.
|
||||
return dec->alpha_plane_ + row * width;
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Everything about WebPDecBuffer
|
||||
@ -15,10 +17,6 @@
|
||||
#include "./webpi.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecBuffer
|
||||
|
||||
@ -35,6 +33,11 @@ static int IsValidColorspace(int webp_csp_mode) {
|
||||
return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
|
||||
}
|
||||
|
||||
// strictly speaking, the very last (or first, if flipped) row
|
||||
// doesn't require padding.
|
||||
#define MIN_BUFFER_SIZE(WIDTH, HEIGHT, STRIDE) \
|
||||
(uint64_t)(STRIDE) * ((HEIGHT) - 1) + (WIDTH)
|
||||
|
||||
static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
int ok = 1;
|
||||
const WEBP_CSP_MODE mode = buffer->colorspace;
|
||||
@ -44,33 +47,41 @@ static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
ok = 0;
|
||||
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
||||
const WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
const uint64_t y_size = (uint64_t)buf->y_stride * height;
|
||||
const uint64_t u_size = (uint64_t)buf->u_stride * ((height + 1) / 2);
|
||||
const uint64_t v_size = (uint64_t)buf->v_stride * ((height + 1) / 2);
|
||||
const uint64_t a_size = (uint64_t)buf->a_stride * height;
|
||||
const int uv_width = (width + 1) / 2;
|
||||
const int uv_height = (height + 1) / 2;
|
||||
const int y_stride = abs(buf->y_stride);
|
||||
const int u_stride = abs(buf->u_stride);
|
||||
const int v_stride = abs(buf->v_stride);
|
||||
const int a_stride = abs(buf->a_stride);
|
||||
const uint64_t y_size = MIN_BUFFER_SIZE(width, height, y_stride);
|
||||
const uint64_t u_size = MIN_BUFFER_SIZE(uv_width, uv_height, u_stride);
|
||||
const uint64_t v_size = MIN_BUFFER_SIZE(uv_width, uv_height, v_stride);
|
||||
const uint64_t a_size = MIN_BUFFER_SIZE(width, height, a_stride);
|
||||
ok &= (y_size <= buf->y_size);
|
||||
ok &= (u_size <= buf->u_size);
|
||||
ok &= (v_size <= buf->v_size);
|
||||
ok &= (buf->y_stride >= width);
|
||||
ok &= (buf->u_stride >= (width + 1) / 2);
|
||||
ok &= (buf->v_stride >= (width + 1) / 2);
|
||||
ok &= (y_stride >= width);
|
||||
ok &= (u_stride >= uv_width);
|
||||
ok &= (v_stride >= uv_width);
|
||||
ok &= (buf->y != NULL);
|
||||
ok &= (buf->u != NULL);
|
||||
ok &= (buf->v != NULL);
|
||||
if (mode == MODE_YUVA) {
|
||||
ok &= (buf->a_stride >= width);
|
||||
ok &= (a_stride >= width);
|
||||
ok &= (a_size <= buf->a_size);
|
||||
ok &= (buf->a != NULL);
|
||||
}
|
||||
} else { // RGB checks
|
||||
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
const uint64_t size = (uint64_t)buf->stride * height;
|
||||
const int stride = abs(buf->stride);
|
||||
const uint64_t size = MIN_BUFFER_SIZE(width, height, stride);
|
||||
ok &= (size <= buf->size);
|
||||
ok &= (buf->stride >= width * kModeBpp[mode]);
|
||||
ok &= (stride >= width * kModeBpp[mode]);
|
||||
ok &= (buf->rgba != NULL);
|
||||
}
|
||||
return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
#undef MIN_BUFFER_SIZE
|
||||
|
||||
static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||
const int w = buffer->width;
|
||||
@ -133,9 +144,35 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) {
|
||||
return CheckDecBuffer(buffer);
|
||||
}
|
||||
|
||||
VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer) {
|
||||
if (buffer == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
if (WebPIsRGBMode(buffer->colorspace)) {
|
||||
WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
buf->rgba += (buffer->height - 1) * buf->stride;
|
||||
buf->stride = -buf->stride;
|
||||
} else {
|
||||
WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
const int H = buffer->height;
|
||||
buf->y += (H - 1) * buf->y_stride;
|
||||
buf->y_stride = -buf->y_stride;
|
||||
buf->u += ((H - 1) >> 1) * buf->u_stride;
|
||||
buf->u_stride = -buf->u_stride;
|
||||
buf->v += ((H - 1) >> 1) * buf->v_stride;
|
||||
buf->v_stride = -buf->v_stride;
|
||||
if (buf->a != NULL) {
|
||||
buf->a += (H - 1) * buf->a_stride;
|
||||
buf->a_stride = -buf->a_stride;
|
||||
}
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
VP8StatusCode WebPAllocateDecBuffer(int w, int h,
|
||||
const WebPDecoderOptions* const options,
|
||||
WebPDecBuffer* const out) {
|
||||
VP8StatusCode status;
|
||||
if (out == NULL || w <= 0 || h <= 0) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
@ -152,18 +189,28 @@ VP8StatusCode WebPAllocateDecBuffer(int w, int h,
|
||||
h = ch;
|
||||
}
|
||||
if (options->use_scaling) {
|
||||
if (options->scaled_width <= 0 || options->scaled_height <= 0) {
|
||||
int scaled_width = options->scaled_width;
|
||||
int scaled_height = options->scaled_height;
|
||||
if (!WebPRescalerGetScaledDimensions(
|
||||
w, h, &scaled_width, &scaled_height)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
w = options->scaled_width;
|
||||
h = options->scaled_height;
|
||||
w = scaled_width;
|
||||
h = scaled_height;
|
||||
}
|
||||
}
|
||||
out->width = w;
|
||||
out->height = h;
|
||||
|
||||
// Then, allocate buffer for real
|
||||
return AllocateBuffer(out);
|
||||
// Then, allocate buffer for real.
|
||||
status = AllocateBuffer(out);
|
||||
if (status != VP8_STATUS_OK) return status;
|
||||
|
||||
// Use the stride trick if vertical flip is needed.
|
||||
if (options != NULL && options->flip) {
|
||||
status = WebPFlipBuffer(out);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -180,8 +227,9 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
|
||||
|
||||
void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
|
||||
if (buffer != NULL) {
|
||||
if (!buffer->is_external_memory)
|
||||
free(buffer->private_memory);
|
||||
if (!buffer->is_external_memory) {
|
||||
WebPSafeFree(buffer->private_memory);
|
||||
}
|
||||
buffer->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
@ -210,6 +258,3 @@ void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Low-level API for VP8 decoder
|
||||
@ -12,9 +14,9 @@
|
||||
#ifndef WEBP_WEBP_DECODE_VP8_H_
|
||||
#define WEBP_WEBP_DECODE_VP8_H_
|
||||
|
||||
#include "../decode.h"
|
||||
#include "../webp/decode.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -130,7 +132,8 @@ static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
|
||||
return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Start decoding a new picture. Returns true if ok.
|
||||
// Decode the VP8 frame header. Returns true if ok.
|
||||
// Note: 'io->data' must be pointing to the start of the VP8 frame header.
|
||||
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
|
||||
|
||||
// Decode a picture. Will call VP8GetHeaders() if it wasn't done already.
|
||||
@ -175,7 +178,7 @@ WEBP_EXTERN(int) VP8LGetInfo(
|
||||
const uint8_t* data, size_t data_size, // data available so far
|
||||
int* const width, int* const height, int* const has_alpha);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Frame-reconstruction function. Memory allocation.
|
||||
@ -13,11 +15,180 @@
|
||||
#include "./vp8i.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
// Main reconstruction function.
|
||||
|
||||
#define ALIGN_MASK (32 - 1)
|
||||
static const int kScan[16] = {
|
||||
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
|
||||
0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
|
||||
0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
|
||||
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
|
||||
};
|
||||
|
||||
static int CheckMode(int mb_x, int mb_y, int mode) {
|
||||
if (mode == B_DC_PRED) {
|
||||
if (mb_x == 0) {
|
||||
return (mb_y == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
|
||||
} else {
|
||||
return (mb_y == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
|
||||
}
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
static void Copy32b(uint8_t* const dst, const uint8_t* const src) {
|
||||
memcpy(dst, src, 4);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void DoTransform(uint32_t bits, const int16_t* const src,
|
||||
uint8_t* const dst) {
|
||||
switch (bits >> 30) {
|
||||
case 3:
|
||||
VP8Transform(src, dst, 0);
|
||||
break;
|
||||
case 2:
|
||||
VP8TransformAC3(src, dst);
|
||||
break;
|
||||
case 1:
|
||||
VP8TransformDC(src, dst);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void DoUVTransform(uint32_t bits, const int16_t* const src,
|
||||
uint8_t* const dst) {
|
||||
if (bits & 0xff) { // any non-zero coeff at all?
|
||||
if (bits & 0xaa) { // any non-zero AC coefficient?
|
||||
VP8TransformUV(src, dst); // note we don't use the AC3 variant for U/V
|
||||
} else {
|
||||
VP8TransformDCUV(src, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void ReconstructRow(const VP8Decoder* const dec,
|
||||
const VP8ThreadContext* ctx) {
|
||||
int j;
|
||||
int mb_x;
|
||||
const int mb_y = ctx->mb_y_;
|
||||
const int cache_id = ctx->id_;
|
||||
uint8_t* const y_dst = dec->yuv_b_ + Y_OFF;
|
||||
uint8_t* const u_dst = dec->yuv_b_ + U_OFF;
|
||||
uint8_t* const v_dst = dec->yuv_b_ + V_OFF;
|
||||
|
||||
// Initialize left-most block.
|
||||
for (j = 0; j < 16; ++j) {
|
||||
y_dst[j * BPS - 1] = 129;
|
||||
}
|
||||
for (j = 0; j < 8; ++j) {
|
||||
u_dst[j * BPS - 1] = 129;
|
||||
v_dst[j * BPS - 1] = 129;
|
||||
}
|
||||
|
||||
// Init top-left sample on left column too.
|
||||
if (mb_y > 0) {
|
||||
y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
|
||||
} else {
|
||||
// we only need to do this init once at block (0,0).
|
||||
// Afterward, it remains valid for the whole topmost row.
|
||||
memset(y_dst - BPS - 1, 127, 16 + 4 + 1);
|
||||
memset(u_dst - BPS - 1, 127, 8 + 1);
|
||||
memset(v_dst - BPS - 1, 127, 8 + 1);
|
||||
}
|
||||
|
||||
// Reconstruct one row.
|
||||
for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
|
||||
const VP8MBData* const block = ctx->mb_data_ + mb_x;
|
||||
|
||||
// Rotate in the left samples from previously decoded block. We move four
|
||||
// pixels at a time for alignment reason, and because of in-loop filter.
|
||||
if (mb_x > 0) {
|
||||
for (j = -1; j < 16; ++j) {
|
||||
Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]);
|
||||
}
|
||||
for (j = -1; j < 8; ++j) {
|
||||
Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]);
|
||||
Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]);
|
||||
}
|
||||
}
|
||||
{
|
||||
// bring top samples into the cache
|
||||
VP8TopSamples* const top_yuv = dec->yuv_t_ + mb_x;
|
||||
const int16_t* const coeffs = block->coeffs_;
|
||||
uint32_t bits = block->non_zero_y_;
|
||||
int n;
|
||||
|
||||
if (mb_y > 0) {
|
||||
memcpy(y_dst - BPS, top_yuv[0].y, 16);
|
||||
memcpy(u_dst - BPS, top_yuv[0].u, 8);
|
||||
memcpy(v_dst - BPS, top_yuv[0].v, 8);
|
||||
}
|
||||
|
||||
// predict and add residuals
|
||||
if (block->is_i4x4_) { // 4x4
|
||||
uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
|
||||
|
||||
if (mb_y > 0) {
|
||||
if (mb_x >= dec->mb_w_ - 1) { // on rightmost border
|
||||
memset(top_right, top_yuv[0].y[15], sizeof(*top_right));
|
||||
} else {
|
||||
memcpy(top_right, top_yuv[1].y, sizeof(*top_right));
|
||||
}
|
||||
}
|
||||
// replicate the top-right pixels below
|
||||
top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0];
|
||||
|
||||
// predict and add residuals for all 4x4 blocks in turn.
|
||||
for (n = 0; n < 16; ++n, bits <<= 2) {
|
||||
uint8_t* const dst = y_dst + kScan[n];
|
||||
VP8PredLuma4[block->imodes_[n]](dst);
|
||||
DoTransform(bits, coeffs + n * 16, dst);
|
||||
}
|
||||
} else { // 16x16
|
||||
const int pred_func = CheckMode(mb_x, mb_y, block->imodes_[0]);
|
||||
VP8PredLuma16[pred_func](y_dst);
|
||||
if (bits != 0) {
|
||||
for (n = 0; n < 16; ++n, bits <<= 2) {
|
||||
DoTransform(bits, coeffs + n * 16, y_dst + kScan[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// Chroma
|
||||
const uint32_t bits_uv = block->non_zero_uv_;
|
||||
const int pred_func = CheckMode(mb_x, mb_y, block->uvmode_);
|
||||
VP8PredChroma8[pred_func](u_dst);
|
||||
VP8PredChroma8[pred_func](v_dst);
|
||||
DoUVTransform(bits_uv >> 0, coeffs + 16 * 16, u_dst);
|
||||
DoUVTransform(bits_uv >> 8, coeffs + 20 * 16, v_dst);
|
||||
}
|
||||
|
||||
// stash away top samples for next block
|
||||
if (mb_y < dec->mb_h_ - 1) {
|
||||
memcpy(top_yuv[0].y, y_dst + 15 * BPS, 16);
|
||||
memcpy(top_yuv[0].u, u_dst + 7 * BPS, 8);
|
||||
memcpy(top_yuv[0].v, v_dst + 7 * BPS, 8);
|
||||
}
|
||||
}
|
||||
// Transfer reconstructed samples from yuv_b_ cache to final destination.
|
||||
{
|
||||
const int y_offset = cache_id * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const y_out = dec->cache_y_ + mb_x * 16 + y_offset;
|
||||
uint8_t* const u_out = dec->cache_u_ + mb_x * 8 + uv_offset;
|
||||
uint8_t* const v_out = dec->cache_v_ + mb_x * 8 + uv_offset;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
memcpy(y_out + j * dec->cache_y_stride_, y_dst + j * BPS, 16);
|
||||
}
|
||||
for (j = 0; j < 8; ++j) {
|
||||
memcpy(u_out + j * dec->cache_uv_stride_, u_dst + j * BPS, 8);
|
||||
memcpy(v_out + j * dec->cache_uv_stride_, v_dst + j * BPS, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Filtering
|
||||
@ -29,25 +200,18 @@ extern "C" {
|
||||
// U/V, so it's 8 samples total (because of the 2x upsampling).
|
||||
static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
|
||||
|
||||
static WEBP_INLINE int hev_thresh_from_level(int level, int keyframe) {
|
||||
if (keyframe) {
|
||||
return (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
||||
} else {
|
||||
return (level >= 40) ? 3 : (level >= 20) ? 2 : (level >= 15) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int cache_id = ctx->id_;
|
||||
const int y_bps = dec->cache_y_stride_;
|
||||
VP8FInfo* const f_info = ctx->f_info_ + mb_x;
|
||||
uint8_t* const y_dst = dec->cache_y_ + ctx->id_ * 16 * y_bps + mb_x * 16;
|
||||
const int level = f_info->f_level_;
|
||||
const VP8FInfo* const f_info = ctx->f_info_ + mb_x;
|
||||
uint8_t* const y_dst = dec->cache_y_ + cache_id * 16 * y_bps + mb_x * 16;
|
||||
const int ilevel = f_info->f_ilevel_;
|
||||
const int limit = 2 * level + ilevel;
|
||||
if (level == 0) {
|
||||
const int limit = f_info->f_limit_;
|
||||
if (limit == 0) {
|
||||
return;
|
||||
}
|
||||
assert(limit >= 3);
|
||||
if (dec->filter_type_ == 1) { // simple
|
||||
if (mb_x > 0) {
|
||||
VP8SimpleHFilter16(y_dst, y_bps, limit + 4);
|
||||
@ -63,10 +227,9 @@ static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
|
||||
}
|
||||
} else { // complex
|
||||
const int uv_bps = dec->cache_uv_stride_;
|
||||
uint8_t* const u_dst = dec->cache_u_ + ctx->id_ * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + ctx->id_ * 8 * uv_bps + mb_x * 8;
|
||||
const int hev_thresh =
|
||||
hev_thresh_from_level(level, dec->frm_hdr_.key_frame_);
|
||||
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
const int hev_thresh = f_info->hev_thresh_;
|
||||
if (mb_x > 0) {
|
||||
VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
|
||||
VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
|
||||
@ -97,53 +260,138 @@ static void FilterRow(const VP8Decoder* const dec) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Precompute the filtering strength for each segment and each i4x4/i16x16 mode.
|
||||
|
||||
void VP8StoreBlock(VP8Decoder* const dec) {
|
||||
static void PrecomputeFilterStrengths(VP8Decoder* const dec) {
|
||||
if (dec->filter_type_ > 0) {
|
||||
VP8FInfo* const info = dec->f_info_ + dec->mb_x_;
|
||||
const int skip = dec->mb_info_[dec->mb_x_].skip_;
|
||||
int level = dec->filter_levels_[dec->segment_];
|
||||
if (dec->filter_hdr_.use_lf_delta_) {
|
||||
// TODO(skal): only CURRENT is handled for now.
|
||||
level += dec->filter_hdr_.ref_lf_delta_[0];
|
||||
if (dec->is_i4x4_) {
|
||||
level += dec->filter_hdr_.mode_lf_delta_[0];
|
||||
}
|
||||
}
|
||||
level = (level < 0) ? 0 : (level > 63) ? 63 : level;
|
||||
info->f_level_ = level;
|
||||
|
||||
if (dec->filter_hdr_.sharpness_ > 0) {
|
||||
if (dec->filter_hdr_.sharpness_ > 4) {
|
||||
level >>= 2;
|
||||
int s;
|
||||
const VP8FilterHeader* const hdr = &dec->filter_hdr_;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
int i4x4;
|
||||
// First, compute the initial level
|
||||
int base_level;
|
||||
if (dec->segment_hdr_.use_segment_) {
|
||||
base_level = dec->segment_hdr_.filter_strength_[s];
|
||||
if (!dec->segment_hdr_.absolute_delta_) {
|
||||
base_level += hdr->level_;
|
||||
}
|
||||
} else {
|
||||
level >>= 1;
|
||||
base_level = hdr->level_;
|
||||
}
|
||||
if (level > 9 - dec->filter_hdr_.sharpness_) {
|
||||
level = 9 - dec->filter_hdr_.sharpness_;
|
||||
for (i4x4 = 0; i4x4 <= 1; ++i4x4) {
|
||||
VP8FInfo* const info = &dec->fstrengths_[s][i4x4];
|
||||
int level = base_level;
|
||||
if (hdr->use_lf_delta_) {
|
||||
level += hdr->ref_lf_delta_[0];
|
||||
if (i4x4) {
|
||||
level += hdr->mode_lf_delta_[0];
|
||||
}
|
||||
}
|
||||
level = (level < 0) ? 0 : (level > 63) ? 63 : level;
|
||||
if (level > 0) {
|
||||
int ilevel = level;
|
||||
if (hdr->sharpness_ > 0) {
|
||||
if (hdr->sharpness_ > 4) {
|
||||
ilevel >>= 2;
|
||||
} else {
|
||||
ilevel >>= 1;
|
||||
}
|
||||
if (ilevel > 9 - hdr->sharpness_) {
|
||||
ilevel = 9 - hdr->sharpness_;
|
||||
}
|
||||
}
|
||||
if (ilevel < 1) ilevel = 1;
|
||||
info->f_ilevel_ = ilevel;
|
||||
info->f_limit_ = 2 * level + ilevel;
|
||||
info->hev_thresh_ = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
||||
} else {
|
||||
info->f_limit_ = 0; // no filtering
|
||||
}
|
||||
info->f_inner_ = i4x4;
|
||||
}
|
||||
}
|
||||
|
||||
info->f_ilevel_ = (level < 1) ? 1 : level;
|
||||
info->f_inner_ = (!skip || dec->is_i4x4_);
|
||||
}
|
||||
{
|
||||
// Transfer samples to row cache
|
||||
int y;
|
||||
const int y_offset = dec->cache_id_ * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = dec->cache_id_ * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const ydst = dec->cache_y_ + dec->mb_x_ * 16 + y_offset;
|
||||
uint8_t* const udst = dec->cache_u_ + dec->mb_x_ * 8 + uv_offset;
|
||||
uint8_t* const vdst = dec->cache_v_ + dec->mb_x_ * 8 + uv_offset;
|
||||
for (y = 0; y < 16; ++y) {
|
||||
memcpy(ydst + y * dec->cache_y_stride_,
|
||||
dec->yuv_b_ + Y_OFF + y * BPS, 16);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Dithering
|
||||
|
||||
#define DITHER_AMP_TAB_SIZE 12
|
||||
static const int kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = {
|
||||
// roughly, it's dqm->uv_mat_[1]
|
||||
8, 7, 6, 4, 4, 2, 2, 2, 1, 1, 1, 1
|
||||
};
|
||||
|
||||
void VP8InitDithering(const WebPDecoderOptions* const options,
|
||||
VP8Decoder* const dec) {
|
||||
assert(dec != NULL);
|
||||
if (options != NULL) {
|
||||
const int d = options->dithering_strength;
|
||||
const int max_amp = (1 << VP8_RANDOM_DITHER_FIX) - 1;
|
||||
const int f = (d < 0) ? 0 : (d > 100) ? max_amp : (d * max_amp / 100);
|
||||
if (f > 0) {
|
||||
int s;
|
||||
int all_amp = 0;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
VP8QuantMatrix* const dqm = &dec->dqm_[s];
|
||||
if (dqm->uv_quant_ < DITHER_AMP_TAB_SIZE) {
|
||||
// TODO(skal): should we specially dither more for uv_quant_ < 0?
|
||||
const int idx = (dqm->uv_quant_ < 0) ? 0 : dqm->uv_quant_;
|
||||
dqm->dither_ = (f * kQuantToDitherAmp[idx]) >> 3;
|
||||
}
|
||||
all_amp |= dqm->dither_;
|
||||
}
|
||||
if (all_amp != 0) {
|
||||
VP8InitRandom(&dec->dithering_rg_, 1.0f);
|
||||
dec->dither_ = 1;
|
||||
}
|
||||
}
|
||||
for (y = 0; y < 8; ++y) {
|
||||
memcpy(udst + y * dec->cache_uv_stride_,
|
||||
dec->yuv_b_ + U_OFF + y * BPS, 8);
|
||||
memcpy(vdst + y * dec->cache_uv_stride_,
|
||||
dec->yuv_b_ + V_OFF + y * BPS, 8);
|
||||
// potentially allow alpha dithering
|
||||
dec->alpha_dithering_ = options->alpha_dithering_strength;
|
||||
if (dec->alpha_dithering_ > 100) {
|
||||
dec->alpha_dithering_ = 100;
|
||||
} else if (dec->alpha_dithering_ < 0) {
|
||||
dec->alpha_dithering_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// minimal amp that will provide a non-zero dithering effect
|
||||
#define MIN_DITHER_AMP 4
|
||||
#define DITHER_DESCALE 4
|
||||
#define DITHER_DESCALE_ROUNDER (1 << (DITHER_DESCALE - 1))
|
||||
#define DITHER_AMP_BITS 8
|
||||
#define DITHER_AMP_CENTER (1 << DITHER_AMP_BITS)
|
||||
|
||||
static void Dither8x8(VP8Random* const rg, uint8_t* dst, int bps, int amp) {
|
||||
int i, j;
|
||||
for (j = 0; j < 8; ++j) {
|
||||
for (i = 0; i < 8; ++i) {
|
||||
// TODO: could be made faster with SSE2
|
||||
const int bits =
|
||||
VP8RandomBits2(rg, DITHER_AMP_BITS + 1, amp) - DITHER_AMP_CENTER;
|
||||
// Convert to range: [-2,2] for dither=50, [-4,4] for dither=100
|
||||
const int delta = (bits + DITHER_DESCALE_ROUNDER) >> DITHER_DESCALE;
|
||||
const int v = (int)dst[i] + delta;
|
||||
dst[i] = (v < 0) ? 0 : (v > 255) ? 255u : (uint8_t)v;
|
||||
}
|
||||
dst += bps;
|
||||
}
|
||||
}
|
||||
|
||||
static void DitherRow(VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
assert(dec->dither_);
|
||||
for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const VP8MBData* const data = ctx->mb_data_ + mb_x;
|
||||
const int cache_id = ctx->id_;
|
||||
const int uv_bps = dec->cache_uv_stride_;
|
||||
if (data->dither_ >= MIN_DITHER_AMP) {
|
||||
uint8_t* const u_dst = dec->cache_u_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + cache_id * 8 * uv_bps + mb_x * 8;
|
||||
Dither8x8(&dec->dithering_rg_, u_dst, uv_bps, data->dither_);
|
||||
Dither8x8(&dec->dithering_rg_, v_dst, uv_bps, data->dither_);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,25 +413,35 @@ void VP8StoreBlock(VP8Decoder* const dec) {
|
||||
static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int cache_id = ctx->id_;
|
||||
const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
|
||||
const int ysize = extra_y_rows * dec->cache_y_stride_;
|
||||
const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_;
|
||||
const int y_offset = ctx->id_ * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = ctx->id_ * 8 * dec->cache_uv_stride_;
|
||||
const int y_offset = cache_id * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = cache_id * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const ydst = dec->cache_y_ - ysize + y_offset;
|
||||
uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset;
|
||||
uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset;
|
||||
const int first_row = (ctx->mb_y_ == 0);
|
||||
const int last_row = (ctx->mb_y_ >= dec->br_mb_y_ - 1);
|
||||
int y_start = MACROBLOCK_VPOS(ctx->mb_y_);
|
||||
int y_end = MACROBLOCK_VPOS(ctx->mb_y_ + 1);
|
||||
const int mb_y = ctx->mb_y_;
|
||||
const int is_first_row = (mb_y == 0);
|
||||
const int is_last_row = (mb_y >= dec->br_mb_y_ - 1);
|
||||
|
||||
if (dec->mt_method_ == 2) {
|
||||
ReconstructRow(dec, ctx);
|
||||
}
|
||||
|
||||
if (ctx->filter_row_) {
|
||||
FilterRow(dec);
|
||||
}
|
||||
|
||||
if (io->put) {
|
||||
if (!first_row) {
|
||||
if (dec->dither_) {
|
||||
DitherRow(dec);
|
||||
}
|
||||
|
||||
if (io->put != NULL) {
|
||||
int y_start = MACROBLOCK_VPOS(mb_y);
|
||||
int y_end = MACROBLOCK_VPOS(mb_y + 1);
|
||||
if (!is_first_row) {
|
||||
y_start -= extra_y_rows;
|
||||
io->y = ydst;
|
||||
io->u = udst;
|
||||
@ -194,7 +452,7 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
io->v = dec->cache_v_ + uv_offset;
|
||||
}
|
||||
|
||||
if (!last_row) {
|
||||
if (!is_last_row) {
|
||||
y_end -= extra_y_rows;
|
||||
}
|
||||
if (y_end > io->crop_bottom) {
|
||||
@ -202,11 +460,8 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
}
|
||||
io->a = NULL;
|
||||
if (dec->alpha_data_ != NULL && y_start < y_end) {
|
||||
// TODO(skal): several things to correct here:
|
||||
// * testing presence of alpha with dec->alpha_data_ is not a good idea
|
||||
// * we're actually decompressing the full plane only once. It should be
|
||||
// more obvious from signature.
|
||||
// * we could free alpha_data_ right after this call, but we don't own.
|
||||
// TODO(skal): testing presence of alpha with dec->alpha_data_ is not a
|
||||
// good idea.
|
||||
io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start);
|
||||
if (io->a == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
@ -238,8 +493,8 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
}
|
||||
}
|
||||
// rotate top samples if needed
|
||||
if (ctx->id_ + 1 == dec->num_caches_) {
|
||||
if (!last_row) {
|
||||
if (cache_id + 1 == dec->num_caches_) {
|
||||
if (!is_last_row) {
|
||||
memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize);
|
||||
memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize);
|
||||
memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize);
|
||||
@ -256,27 +511,40 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
if (!dec->use_threads_) {
|
||||
const int filter_row =
|
||||
(dec->filter_type_ > 0) &&
|
||||
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
|
||||
if (dec->mt_method_ == 0) {
|
||||
// ctx->id_ and ctx->f_info_ are already set
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = dec->filter_row_;
|
||||
ctx->filter_row_ = filter_row;
|
||||
ReconstructRow(dec, ctx);
|
||||
ok = FinishRow(dec, io);
|
||||
} else {
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
// Finish previous job *before* updating context
|
||||
ok &= WebPWorkerSync(worker);
|
||||
ok &= WebPGetWorkerInterface()->Sync(worker);
|
||||
assert(worker->status_ == OK);
|
||||
if (ok) { // spawn a new deblocking/output job
|
||||
ctx->io_ = *io;
|
||||
ctx->id_ = dec->cache_id_;
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = dec->filter_row_;
|
||||
if (ctx->filter_row_) { // just swap filter info
|
||||
ctx->filter_row_ = filter_row;
|
||||
if (dec->mt_method_ == 2) { // swap macroblock data
|
||||
VP8MBData* const tmp = ctx->mb_data_;
|
||||
ctx->mb_data_ = dec->mb_data_;
|
||||
dec->mb_data_ = tmp;
|
||||
} else {
|
||||
// perform reconstruction directly in main thread
|
||||
ReconstructRow(dec, ctx);
|
||||
}
|
||||
if (filter_row) { // swap filter info
|
||||
VP8FInfo* const tmp = ctx->f_info_;
|
||||
ctx->f_info_ = dec->f_info_;
|
||||
dec->f_info_ = tmp;
|
||||
}
|
||||
WebPWorkerLaunch(worker);
|
||||
// (reconstruct)+filter in parallel
|
||||
WebPGetWorkerInterface()->Launch(worker);
|
||||
if (++dec->cache_id_ == dec->num_caches_) {
|
||||
dec->cache_id_ = 0;
|
||||
}
|
||||
@ -290,8 +558,8 @@ int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
|
||||
VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// Call setup() first. This may trigger additional decoding features on 'io'.
|
||||
// Note: Afterward, we must call teardown() not matter what.
|
||||
if (io->setup && !io->setup(io)) {
|
||||
// Note: Afterward, we must call teardown() no matter what.
|
||||
if (io->setup != NULL && !io->setup(io)) {
|
||||
VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed");
|
||||
return dec->status_;
|
||||
}
|
||||
@ -304,7 +572,7 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
|
||||
// Define the area where we can skip in-loop filtering, in case of cropping.
|
||||
//
|
||||
// 'Simple' filter reads two luma samples outside of the macroblock and
|
||||
// 'Simple' filter reads two luma samples outside of the macroblock
|
||||
// and filters one. It doesn't filter the chroma samples. Hence, we can
|
||||
// avoid doing the in-loop filtering before crop_top/crop_left position.
|
||||
// For the 'Complex' filter, 3 samples are read and up to 3 are filtered.
|
||||
@ -339,16 +607,17 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
dec->br_mb_y_ = dec->mb_h_;
|
||||
}
|
||||
}
|
||||
PrecomputeFilterStrengths(dec);
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
if (dec->use_threads_) {
|
||||
ok = WebPWorkerSync(&dec->worker_);
|
||||
if (dec->mt_method_ > 0) {
|
||||
ok = WebPGetWorkerInterface()->Sync(&dec->worker_);
|
||||
}
|
||||
|
||||
if (io->teardown) {
|
||||
if (io->teardown != NULL) {
|
||||
io->teardown(io);
|
||||
}
|
||||
return ok;
|
||||
@ -384,9 +653,9 @@ int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// Initialize multi/single-thread worker
|
||||
static int InitThreadContext(VP8Decoder* const dec) {
|
||||
dec->cache_id_ = 0;
|
||||
if (dec->use_threads_) {
|
||||
if (dec->mt_method_ > 0) {
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
if (!WebPWorkerReset(worker)) {
|
||||
if (!WebPGetWorkerInterface()->Reset(worker)) {
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"thread initialization failed.");
|
||||
}
|
||||
@ -401,6 +670,28 @@ static int InitThreadContext(VP8Decoder* const dec) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
||||
const WebPHeaderStructure* const headers,
|
||||
int width, int height) {
|
||||
if (options == NULL || options->use_threads == 0) {
|
||||
return 0;
|
||||
}
|
||||
(void)headers;
|
||||
(void)width;
|
||||
(void)height;
|
||||
assert(headers == NULL || !headers->is_lossless);
|
||||
#if defined(WEBP_USE_THREAD)
|
||||
if (width < MIN_WIDTH_FOR_THREADS) return 0;
|
||||
// TODO(skal): tune the heuristic further
|
||||
#if 0
|
||||
if (height < 2 * width) return 2;
|
||||
#endif
|
||||
return 2;
|
||||
#else // !WEBP_USE_THREAD
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
#undef MT_CACHE_LINES
|
||||
#undef ST_CACHE_LINES
|
||||
|
||||
@ -412,14 +703,15 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
const int mb_w = dec->mb_w_;
|
||||
// Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise.
|
||||
const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
|
||||
const size_t top_size = (16 + 8 + 8) * mb_w;
|
||||
const size_t top_size = sizeof(VP8TopSamples) * mb_w;
|
||||
const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
|
||||
const size_t f_info_size =
|
||||
(dec->filter_type_ > 0) ?
|
||||
mb_w * (dec->use_threads_ ? 2 : 1) * sizeof(VP8FInfo)
|
||||
mb_w * (dec->mt_method_ > 0 ? 2 : 1) * sizeof(VP8FInfo)
|
||||
: 0;
|
||||
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
|
||||
const size_t coeffs_size = 384 * sizeof(*dec->coeffs_);
|
||||
const size_t mb_data_size =
|
||||
(dec->mt_method_ == 2 ? 2 : 1) * mb_w * sizeof(*dec->mb_data_);
|
||||
const size_t cache_height = (16 * num_caches
|
||||
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
|
||||
const size_t cache_size = top_size * cache_height;
|
||||
@ -428,13 +720,13 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
(uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
|
||||
const uint64_t needed = (uint64_t)intra_pred_mode_size
|
||||
+ top_size + mb_info_size + f_info_size
|
||||
+ yuv_size + coeffs_size
|
||||
+ cache_size + alpha_size + ALIGN_MASK;
|
||||
+ yuv_size + mb_data_size
|
||||
+ cache_size + alpha_size + WEBP_ALIGN_CST;
|
||||
uint8_t* mem;
|
||||
|
||||
if (needed != (size_t)needed) return 0; // check for overflow
|
||||
if (needed > dec->mem_size_) {
|
||||
free(dec->mem_);
|
||||
WebPSafeFree(dec->mem_);
|
||||
dec->mem_size_ = 0;
|
||||
dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
|
||||
if (dec->mem_ == NULL) {
|
||||
@ -449,12 +741,8 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
dec->intra_t_ = (uint8_t*)mem;
|
||||
mem += intra_pred_mode_size;
|
||||
|
||||
dec->y_t_ = (uint8_t*)mem;
|
||||
mem += 16 * mb_w;
|
||||
dec->u_t_ = (uint8_t*)mem;
|
||||
mem += 8 * mb_w;
|
||||
dec->v_t_ = (uint8_t*)mem;
|
||||
mem += 8 * mb_w;
|
||||
dec->yuv_t_ = (VP8TopSamples*)mem;
|
||||
mem += top_size;
|
||||
|
||||
dec->mb_info_ = ((VP8MB*)mem) + 1;
|
||||
mem += mb_info_size;
|
||||
@ -463,20 +751,24 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
mem += f_info_size;
|
||||
dec->thread_ctx_.id_ = 0;
|
||||
dec->thread_ctx_.f_info_ = dec->f_info_;
|
||||
if (dec->use_threads_) {
|
||||
if (dec->mt_method_ > 0) {
|
||||
// secondary cache line. The deblocking process need to make use of the
|
||||
// filtering strength from previous macroblock row, while the new ones
|
||||
// are being decoded in parallel. We'll just swap the pointers.
|
||||
dec->thread_ctx_.f_info_ += mb_w;
|
||||
}
|
||||
|
||||
mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
|
||||
assert((yuv_size & ALIGN_MASK) == 0);
|
||||
mem = (uint8_t*)WEBP_ALIGN(mem);
|
||||
assert((yuv_size & WEBP_ALIGN_CST) == 0);
|
||||
dec->yuv_b_ = (uint8_t*)mem;
|
||||
mem += yuv_size;
|
||||
|
||||
dec->coeffs_ = (int16_t*)mem;
|
||||
mem += coeffs_size;
|
||||
dec->mb_data_ = (VP8MBData*)mem;
|
||||
dec->thread_ctx_.mb_data_ = (VP8MBData*)mem;
|
||||
if (dec->mt_method_ == 2) {
|
||||
dec->thread_ctx_.mb_data_ += mb_w;
|
||||
}
|
||||
mem += mb_data_size;
|
||||
|
||||
dec->cache_y_stride_ = 16 * mb_w;
|
||||
dec->cache_uv_stride_ = 8 * mb_w;
|
||||
@ -496,9 +788,11 @@ static int AllocateMemory(VP8Decoder* const dec) {
|
||||
// alpha plane
|
||||
dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL;
|
||||
mem += alpha_size;
|
||||
assert(mem <= (uint8_t*)dec->mem_ + dec->mem_size_);
|
||||
|
||||
// note: left-info is initialized once for all.
|
||||
// note: left/top-info is initialized once for all.
|
||||
memset(dec->mb_info_ - 1, 0, mb_info_size);
|
||||
VP8InitScanline(dec); // initialize left too.
|
||||
|
||||
// initialize top
|
||||
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
|
||||
@ -517,7 +811,7 @@ static void InitIo(VP8Decoder* const dec, VP8Io* io) {
|
||||
io->a = NULL;
|
||||
}
|
||||
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io) {
|
||||
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
|
||||
if (!AllocateMemory(dec)) return 0;
|
||||
InitIo(dec, io);
|
||||
@ -526,154 +820,3 @@ int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main reconstruction function.
|
||||
|
||||
static const int kScan[16] = {
|
||||
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
|
||||
0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
|
||||
0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
|
||||
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
|
||||
};
|
||||
|
||||
static WEBP_INLINE int CheckMode(VP8Decoder* const dec, int mode) {
|
||||
if (mode == B_DC_PRED) {
|
||||
if (dec->mb_x_ == 0) {
|
||||
return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
|
||||
} else {
|
||||
return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
|
||||
}
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void Copy32b(uint8_t* dst, uint8_t* src) {
|
||||
*(uint32_t*)dst = *(uint32_t*)src;
|
||||
}
|
||||
|
||||
void VP8ReconstructBlock(VP8Decoder* const dec) {
|
||||
uint8_t* const y_dst = dec->yuv_b_ + Y_OFF;
|
||||
uint8_t* const u_dst = dec->yuv_b_ + U_OFF;
|
||||
uint8_t* const v_dst = dec->yuv_b_ + V_OFF;
|
||||
|
||||
// Rotate in the left samples from previously decoded block. We move four
|
||||
// pixels at a time for alignment reason, and because of in-loop filter.
|
||||
if (dec->mb_x_ > 0) {
|
||||
int j;
|
||||
for (j = -1; j < 16; ++j) {
|
||||
Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]);
|
||||
}
|
||||
for (j = -1; j < 8; ++j) {
|
||||
Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]);
|
||||
Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]);
|
||||
}
|
||||
} else {
|
||||
int j;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
y_dst[j * BPS - 1] = 129;
|
||||
}
|
||||
for (j = 0; j < 8; ++j) {
|
||||
u_dst[j * BPS - 1] = 129;
|
||||
v_dst[j * BPS - 1] = 129;
|
||||
}
|
||||
// Init top-left sample on left column too
|
||||
if (dec->mb_y_ > 0) {
|
||||
y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
|
||||
}
|
||||
}
|
||||
{
|
||||
// bring top samples into the cache
|
||||
uint8_t* const top_y = dec->y_t_ + dec->mb_x_ * 16;
|
||||
uint8_t* const top_u = dec->u_t_ + dec->mb_x_ * 8;
|
||||
uint8_t* const top_v = dec->v_t_ + dec->mb_x_ * 8;
|
||||
const int16_t* coeffs = dec->coeffs_;
|
||||
int n;
|
||||
|
||||
if (dec->mb_y_ > 0) {
|
||||
memcpy(y_dst - BPS, top_y, 16);
|
||||
memcpy(u_dst - BPS, top_u, 8);
|
||||
memcpy(v_dst - BPS, top_v, 8);
|
||||
} else if (dec->mb_x_ == 0) {
|
||||
// we only need to do this init once at block (0,0).
|
||||
// Afterward, it remains valid for the whole topmost row.
|
||||
memset(y_dst - BPS - 1, 127, 16 + 4 + 1);
|
||||
memset(u_dst - BPS - 1, 127, 8 + 1);
|
||||
memset(v_dst - BPS - 1, 127, 8 + 1);
|
||||
}
|
||||
|
||||
// predict and add residuals
|
||||
|
||||
if (dec->is_i4x4_) { // 4x4
|
||||
uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
|
||||
|
||||
if (dec->mb_y_ > 0) {
|
||||
if (dec->mb_x_ >= dec->mb_w_ - 1) { // on rightmost border
|
||||
top_right[0] = top_y[15] * 0x01010101u;
|
||||
} else {
|
||||
memcpy(top_right, top_y + 16, sizeof(*top_right));
|
||||
}
|
||||
}
|
||||
// replicate the top-right pixels below
|
||||
top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0];
|
||||
|
||||
// predict and add residues for all 4x4 blocks in turn.
|
||||
for (n = 0; n < 16; n++) {
|
||||
uint8_t* const dst = y_dst + kScan[n];
|
||||
VP8PredLuma4[dec->imodes_[n]](dst);
|
||||
if (dec->non_zero_ac_ & (1 << n)) {
|
||||
VP8Transform(coeffs + n * 16, dst, 0);
|
||||
} else if (dec->non_zero_ & (1 << n)) { // only DC is present
|
||||
VP8TransformDC(coeffs + n * 16, dst);
|
||||
}
|
||||
}
|
||||
} else { // 16x16
|
||||
const int pred_func = CheckMode(dec, dec->imodes_[0]);
|
||||
VP8PredLuma16[pred_func](y_dst);
|
||||
if (dec->non_zero_) {
|
||||
for (n = 0; n < 16; n++) {
|
||||
uint8_t* const dst = y_dst + kScan[n];
|
||||
if (dec->non_zero_ac_ & (1 << n)) {
|
||||
VP8Transform(coeffs + n * 16, dst, 0);
|
||||
} else if (dec->non_zero_ & (1 << n)) { // only DC is present
|
||||
VP8TransformDC(coeffs + n * 16, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// Chroma
|
||||
const int pred_func = CheckMode(dec, dec->uvmode_);
|
||||
VP8PredChroma8[pred_func](u_dst);
|
||||
VP8PredChroma8[pred_func](v_dst);
|
||||
|
||||
if (dec->non_zero_ & 0x0f0000) { // chroma-U
|
||||
const int16_t* const u_coeffs = dec->coeffs_ + 16 * 16;
|
||||
if (dec->non_zero_ac_ & 0x0f0000) {
|
||||
VP8TransformUV(u_coeffs, u_dst);
|
||||
} else {
|
||||
VP8TransformDCUV(u_coeffs, u_dst);
|
||||
}
|
||||
}
|
||||
if (dec->non_zero_ & 0xf00000) { // chroma-V
|
||||
const int16_t* const v_coeffs = dec->coeffs_ + 20 * 16;
|
||||
if (dec->non_zero_ac_ & 0xf00000) {
|
||||
VP8TransformUV(v_coeffs, v_dst);
|
||||
} else {
|
||||
VP8TransformDCUV(v_coeffs, v_dst);
|
||||
}
|
||||
}
|
||||
|
||||
// stash away top samples for next block
|
||||
if (dec->mb_y_ < dec->mb_h_ - 1) {
|
||||
memcpy(top_y, y_dst + 15 * BPS, 16);
|
||||
memcpy(top_u, u_dst + 7 * BPS, 8);
|
||||
memcpy(top_v, v_dst + 7 * BPS, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Incremental decoding
|
||||
@ -13,14 +15,11 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./alphai.h"
|
||||
#include "./webpi.h"
|
||||
#include "./vp8i.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// In append mode, buffer allocations increase as multiples of this value.
|
||||
// Needs to be a power of 2.
|
||||
#define CHUNK_SIZE 4096
|
||||
@ -29,11 +28,13 @@ extern "C" {
|
||||
//------------------------------------------------------------------------------
|
||||
// Data structures for memory and states
|
||||
|
||||
// Decoding states. State normally flows like HEADER->PARTS0->DATA->DONE.
|
||||
// Decoding states. State normally flows as:
|
||||
// WEBP_HEADER->VP8_HEADER->VP8_PARTS0->VP8_DATA->DONE for a lossy image, and
|
||||
// WEBP_HEADER->VP8L_HEADER->VP8L_DATA->DONE for a lossless image.
|
||||
// If there is any error the decoder goes into state ERROR.
|
||||
typedef enum {
|
||||
STATE_PRE_VP8, // All data before that of the first VP8 chunk.
|
||||
STATE_VP8_FRAME_HEADER, // For VP8 Frame header (within VP8 chunk).
|
||||
STATE_WEBP_HEADER, // All the data before that of the VP8/VP8L chunk.
|
||||
STATE_VP8_HEADER, // The VP8 Frame header (within the VP8 chunk).
|
||||
STATE_VP8_PARTS0,
|
||||
STATE_VP8_DATA,
|
||||
STATE_VP8L_HEADER,
|
||||
@ -71,32 +72,41 @@ struct WebPIDecoder {
|
||||
MemBuffer mem_; // input memory buffer.
|
||||
WebPDecBuffer output_; // output buffer (when no external one is supplied)
|
||||
size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
|
||||
|
||||
int last_mb_y_; // last row reached for intra-mode decoding
|
||||
};
|
||||
|
||||
// MB context to restore in case VP8DecodeMB() fails
|
||||
typedef struct {
|
||||
VP8MB left_;
|
||||
VP8MB info_;
|
||||
uint8_t intra_t_[4];
|
||||
uint8_t intra_l_[4];
|
||||
VP8BitReader br_;
|
||||
VP8BitReader token_br_;
|
||||
} MBContext;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MemBuffer: incoming data handling
|
||||
|
||||
static void RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) {
|
||||
if (br->buf_ != NULL) {
|
||||
br->buf_ += offset;
|
||||
br->buf_end_ += offset;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {
|
||||
return (mem->end_ - mem->start_);
|
||||
}
|
||||
|
||||
// Check if we need to preserve the compressed alpha data, as it may not have
|
||||
// been decoded yet.
|
||||
static int NeedCompressedAlpha(const WebPIDecoder* const idec) {
|
||||
if (idec->state_ == STATE_WEBP_HEADER) {
|
||||
// We haven't parsed the headers yet, so we don't know whether the image is
|
||||
// lossy or lossless. This also means that we haven't parsed the ALPH chunk.
|
||||
return 0;
|
||||
}
|
||||
if (idec->is_lossless_) {
|
||||
return 0; // ALPH chunk is not present for lossless images.
|
||||
} else {
|
||||
const VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
assert(dec != NULL); // Must be true as idec->state_ != STATE_WEBP_HEADER.
|
||||
return (dec->alpha_data_ != NULL) && !dec->is_alpha_decoded_;
|
||||
}
|
||||
}
|
||||
|
||||
static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* const new_base = mem->buf_ + mem->start_;
|
||||
@ -112,16 +122,36 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
|
||||
if (offset != 0) {
|
||||
int p;
|
||||
for (p = 0; p <= last_part; ++p) {
|
||||
RemapBitReader(dec->parts_ + p, offset);
|
||||
VP8RemapBitReader(dec->parts_ + p, offset);
|
||||
}
|
||||
// Remap partition #0 data pointer to new offset, but only in MAP
|
||||
// mode (in APPEND mode, partition #0 is copied into a fixed memory).
|
||||
if (mem->mode_ == MEM_MODE_MAP) {
|
||||
RemapBitReader(&dec->br_, offset);
|
||||
VP8RemapBitReader(&dec->br_, offset);
|
||||
}
|
||||
}
|
||||
{
|
||||
const uint8_t* const last_start = dec->parts_[last_part].buf_;
|
||||
assert(last_part >= 0);
|
||||
VP8BitReaderSetBuffer(&dec->parts_[last_part], last_start,
|
||||
mem->buf_ + mem->end_ - last_start);
|
||||
}
|
||||
if (NeedCompressedAlpha(idec)) {
|
||||
ALPHDecoder* const alph_dec = dec->alph_dec_;
|
||||
dec->alpha_data_ += offset;
|
||||
if (alph_dec != NULL) {
|
||||
if (alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION) {
|
||||
VP8LDecoder* const alph_vp8l_dec = alph_dec->vp8l_dec_;
|
||||
assert(alph_vp8l_dec != NULL);
|
||||
assert(dec->alpha_data_size_ >= ALPHA_HEADER_LEN);
|
||||
VP8LBitReaderSetBuffer(&alph_vp8l_dec->br_,
|
||||
dec->alpha_data_ + ALPHA_HEADER_LEN,
|
||||
dec->alpha_data_size_ - ALPHA_HEADER_LEN);
|
||||
} else { // alph_dec->method_ == ALPHA_NO_COMPRESSION
|
||||
// Nothing special to do in this case.
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(last_part >= 0);
|
||||
dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_;
|
||||
} else { // Resize lossless bitreader
|
||||
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
|
||||
VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem));
|
||||
@ -133,8 +163,12 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
|
||||
// size if required and also updates VP8BitReader's if new memory is allocated.
|
||||
static int AppendToMemBuffer(WebPIDecoder* const idec,
|
||||
const uint8_t* const data, size_t data_size) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* const old_base = mem->buf_ + mem->start_;
|
||||
const int need_compressed_alpha = NeedCompressedAlpha(idec);
|
||||
const uint8_t* const old_start = mem->buf_ + mem->start_;
|
||||
const uint8_t* const old_base =
|
||||
need_compressed_alpha ? dec->alpha_data_ : old_start;
|
||||
assert(mem->mode_ == MEM_MODE_APPEND);
|
||||
if (data_size > MAX_CHUNK_PAYLOAD) {
|
||||
// security safeguard: trying to allocate more than what the format
|
||||
@ -143,17 +177,18 @@ static int AppendToMemBuffer(WebPIDecoder* const idec,
|
||||
}
|
||||
|
||||
if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory
|
||||
const size_t current_size = MemDataSize(mem);
|
||||
const size_t new_mem_start = old_start - old_base;
|
||||
const size_t current_size = MemDataSize(mem) + new_mem_start;
|
||||
const uint64_t new_size = (uint64_t)current_size + data_size;
|
||||
const uint64_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1);
|
||||
uint8_t* const new_buf =
|
||||
(uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));
|
||||
if (new_buf == NULL) return 0;
|
||||
memcpy(new_buf, old_base, current_size);
|
||||
free(mem->buf_);
|
||||
WebPSafeFree(mem->buf_);
|
||||
mem->buf_ = new_buf;
|
||||
mem->buf_size_ = (size_t)extra_size;
|
||||
mem->start_ = 0;
|
||||
mem->start_ = new_mem_start;
|
||||
mem->end_ = current_size;
|
||||
}
|
||||
|
||||
@ -161,14 +196,15 @@ static int AppendToMemBuffer(WebPIDecoder* const idec,
|
||||
mem->end_ += data_size;
|
||||
assert(mem->end_ <= mem->buf_size_);
|
||||
|
||||
DoRemap(idec, mem->buf_ + mem->start_ - old_base);
|
||||
DoRemap(idec, mem->buf_ + mem->start_ - old_start);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int RemapMemBuffer(WebPIDecoder* const idec,
|
||||
const uint8_t* const data, size_t data_size) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* const old_base = mem->buf_ + mem->start_;
|
||||
const uint8_t* const old_buf = mem->buf_;
|
||||
const uint8_t* const old_start = old_buf + mem->start_;
|
||||
assert(mem->mode_ == MEM_MODE_MAP);
|
||||
|
||||
if (data_size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
|
||||
@ -176,7 +212,7 @@ static int RemapMemBuffer(WebPIDecoder* const idec,
|
||||
mem->buf_ = (uint8_t*)data;
|
||||
mem->end_ = mem->buf_size_ = data_size;
|
||||
|
||||
DoRemap(idec, mem->buf_ + mem->start_ - old_base);
|
||||
DoRemap(idec, mem->buf_ + mem->start_ - old_start);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -191,8 +227,8 @@ static void InitMemBuffer(MemBuffer* const mem) {
|
||||
static void ClearMemBuffer(MemBuffer* const mem) {
|
||||
assert(mem);
|
||||
if (mem->mode_ == MEM_MODE_APPEND) {
|
||||
free(mem->buf_);
|
||||
free((void*)mem->part0_buf_);
|
||||
WebPSafeFree(mem->buf_);
|
||||
WebPSafeFree((void*)mem->part0_buf_);
|
||||
}
|
||||
}
|
||||
|
||||
@ -206,35 +242,34 @@ static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
// To be called last.
|
||||
static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) {
|
||||
const WebPDecoderOptions* const options = idec->params_.options;
|
||||
WebPDecBuffer* const output = idec->params_.output;
|
||||
|
||||
idec->state_ = STATE_DONE;
|
||||
if (options != NULL && options->flip) {
|
||||
return WebPFlipBuffer(output);
|
||||
} else {
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Macroblock-decoding contexts
|
||||
|
||||
static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,
|
||||
MBContext* const context) {
|
||||
const VP8BitReader* const br = &dec->br_;
|
||||
const VP8MB* const left = dec->mb_info_ - 1;
|
||||
const VP8MB* const info = dec->mb_info_ + dec->mb_x_;
|
||||
|
||||
context->left_ = *left;
|
||||
context->info_ = *info;
|
||||
context->br_ = *br;
|
||||
context->left_ = dec->mb_info_[-1];
|
||||
context->info_ = dec->mb_info_[dec->mb_x_];
|
||||
context->token_br_ = *token_br;
|
||||
memcpy(context->intra_t_, dec->intra_t_ + 4 * dec->mb_x_, 4);
|
||||
memcpy(context->intra_l_, dec->intra_l_, 4);
|
||||
}
|
||||
|
||||
static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
|
||||
VP8BitReader* const token_br) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
VP8MB* const info = dec->mb_info_ + dec->mb_x_;
|
||||
|
||||
*left = context->left_;
|
||||
*info = context->info_;
|
||||
*br = context->br_;
|
||||
dec->mb_info_[-1] = context->left_;
|
||||
dec->mb_info_[dec->mb_x_] = context->info_;
|
||||
*token_br = context->token_br_;
|
||||
memcpy(dec->intra_t_ + 4 * dec->mb_x_, context->intra_t_, 4);
|
||||
memcpy(dec->intra_l_, context->intra_l_, 4);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -242,7 +277,7 @@ static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
|
||||
static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
VP8Io* const io = &idec->io_;
|
||||
if (io->teardown) {
|
||||
if (io->teardown != NULL) {
|
||||
io->teardown(io);
|
||||
}
|
||||
}
|
||||
@ -270,6 +305,7 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
|
||||
|
||||
headers.data = data;
|
||||
headers.data_size = curr_size;
|
||||
headers.have_all_data = 0;
|
||||
status = WebPParseHeaders(&headers);
|
||||
if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.
|
||||
@ -285,15 +321,9 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
idec->dec_ = dec;
|
||||
#ifdef WEBP_USE_THREAD
|
||||
dec->use_threads_ = (idec->params_.options != NULL) &&
|
||||
(idec->params_.options->use_threads > 0);
|
||||
#else
|
||||
dec->use_threads_ = 0;
|
||||
#endif
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
ChangeState(idec, STATE_VP8_FRAME_HEADER, headers.offset);
|
||||
ChangeState(idec, STATE_VP8_HEADER, headers.offset);
|
||||
} else {
|
||||
VP8LDecoder* const dec = VP8LNew();
|
||||
if (dec == NULL) {
|
||||
@ -308,13 +338,14 @@ static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
|
||||
static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
|
||||
const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;
|
||||
const size_t curr_size = MemDataSize(&idec->mem_);
|
||||
int width, height;
|
||||
uint32_t bits;
|
||||
|
||||
if (curr_size < VP8_FRAME_HEADER_SIZE) {
|
||||
// Not enough data bytes to extract VP8 Frame Header.
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
if (!VP8GetInfo(data, curr_size, idec->chunk_size_, NULL, NULL)) {
|
||||
if (!VP8GetInfo(data, curr_size, idec->chunk_size_, &width, &height)) {
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
|
||||
@ -328,30 +359,32 @@ static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
|
||||
}
|
||||
|
||||
// Partition #0
|
||||
static int CopyParts0Data(WebPIDecoder* const idec) {
|
||||
static VP8StatusCode CopyParts0Data(WebPIDecoder* const idec) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
const size_t psize = br->buf_end_ - br->buf_;
|
||||
const size_t part_size = br->buf_end_ - br->buf_;
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
assert(!idec->is_lossless_);
|
||||
assert(mem->part0_buf_ == NULL);
|
||||
assert(psize > 0);
|
||||
assert(psize <= mem->part0_size_); // Format limit: no need for runtime check
|
||||
// the following is a format limitation, no need for runtime check:
|
||||
assert(part_size <= mem->part0_size_);
|
||||
if (part_size == 0) { // can't have zero-size partition #0
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (mem->mode_ == MEM_MODE_APPEND) {
|
||||
// We copy and grab ownership of the partition #0 data.
|
||||
uint8_t* const part0_buf = (uint8_t*)malloc(psize);
|
||||
uint8_t* const part0_buf = (uint8_t*)WebPSafeMalloc(1ULL, part_size);
|
||||
if (part0_buf == NULL) {
|
||||
return 0;
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
memcpy(part0_buf, br->buf_, psize);
|
||||
memcpy(part0_buf, br->buf_, part_size);
|
||||
mem->part0_buf_ = part0_buf;
|
||||
br->buf_ = part0_buf;
|
||||
br->buf_end_ = part0_buf + psize;
|
||||
VP8BitReaderSetBuffer(br, part0_buf, part_size);
|
||||
} else {
|
||||
// Else: just keep pointers to the partition #0's data in dec_->br_.
|
||||
}
|
||||
mem->start_ += psize;
|
||||
return 1;
|
||||
mem->start_ += part_size;
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
|
||||
@ -381,9 +414,14 @@ static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
|
||||
if (dec->status_ != VP8_STATUS_OK) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
// This change must be done before calling VP8InitFrame()
|
||||
dec->mt_method_ = VP8GetThreadMethod(params->options, NULL,
|
||||
io->width, io->height);
|
||||
VP8InitDithering(params->options, dec);
|
||||
|
||||
if (!CopyParts0Data(idec)) {
|
||||
return IDecError(idec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
dec->status_ = CopyParts0Data(idec);
|
||||
if (dec->status_ != VP8_STATUS_OK) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
|
||||
// Finish setting up the decoding parameters. Will call io->setup().
|
||||
@ -407,50 +445,52 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
|
||||
VP8Io* const io = &idec->io_;
|
||||
|
||||
assert(dec->ready_);
|
||||
|
||||
for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
|
||||
VP8BitReader* token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
|
||||
if (dec->mb_x_ == 0) {
|
||||
VP8InitScanline(dec);
|
||||
if (idec->last_mb_y_ != dec->mb_y_) {
|
||||
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
||||
// note: normally, error shouldn't occur since we already have the whole
|
||||
// partition0 available here in DecodeRemaining(). Reaching EOF while
|
||||
// reading intra modes really means a BITSTREAM_ERROR.
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
idec->last_mb_y_ = dec->mb_y_;
|
||||
}
|
||||
for (; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
|
||||
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
|
||||
VP8BitReader* const token_br =
|
||||
&dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
|
||||
MBContext context;
|
||||
SaveContext(dec, token_br, &context);
|
||||
|
||||
if (!VP8DecodeMB(dec, token_br)) {
|
||||
RestoreContext(&context, dec, token_br);
|
||||
// We shouldn't fail when MAX_MB data was available
|
||||
if (dec->num_parts_ == 1 && MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
RestoreContext(&context, dec, token_br);
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
VP8ReconstructBlock(dec);
|
||||
// Store data and save block's filtering params
|
||||
VP8StoreBlock(dec);
|
||||
|
||||
// Release buffer only if there is only one partition
|
||||
if (dec->num_parts_ == 1) {
|
||||
idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
|
||||
assert(idec->mem_.start_ <= idec->mem_.end_);
|
||||
}
|
||||
}
|
||||
VP8InitScanline(dec); // Prepare for next scanline
|
||||
|
||||
// Reconstruct, filter and emit the row.
|
||||
if (!VP8ProcessRow(dec, io)) {
|
||||
return IDecError(idec, VP8_STATUS_USER_ABORT);
|
||||
}
|
||||
dec->mb_x_ = 0;
|
||||
}
|
||||
// Synchronize the thread and check for errors.
|
||||
if (!VP8ExitCritical(dec, io)) {
|
||||
return IDecError(idec, VP8_STATUS_USER_ABORT);
|
||||
}
|
||||
dec->ready_ = 0;
|
||||
idec->state_ = STATE_DONE;
|
||||
|
||||
return VP8_STATUS_OK;
|
||||
return FinishDecoding(idec);
|
||||
}
|
||||
|
||||
static int ErrorStatusLossless(WebPIDecoder* const idec, VP8StatusCode status) {
|
||||
static VP8StatusCode ErrorStatusLossless(WebPIDecoder* const idec,
|
||||
VP8StatusCode status) {
|
||||
if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
@ -467,9 +507,15 @@ static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
|
||||
|
||||
// Wait until there's enough data for decoding header.
|
||||
if (curr_size < (idec->chunk_size_ >> 3)) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
dec->status_ = VP8_STATUS_SUSPENDED;
|
||||
return ErrorStatusLossless(idec, dec->status_);
|
||||
}
|
||||
|
||||
if (!VP8LDecodeHeader(dec, io)) {
|
||||
if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR &&
|
||||
curr_size < idec->chunk_size_) {
|
||||
dec->status_ = VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
return ErrorStatusLossless(idec, dec->status_);
|
||||
}
|
||||
// Allocate/verify output buffer now.
|
||||
@ -488,33 +534,29 @@ static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
|
||||
const size_t curr_size = MemDataSize(&idec->mem_);
|
||||
assert(idec->is_lossless_);
|
||||
|
||||
// At present Lossless decoder can't decode image incrementally. So wait till
|
||||
// all the image data is aggregated before image can be decoded.
|
||||
if (curr_size < idec->chunk_size_) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
// Switch to incremental decoding if we don't have all the bytes available.
|
||||
dec->incremental_ = (curr_size < idec->chunk_size_);
|
||||
|
||||
if (!VP8LDecodeImage(dec)) {
|
||||
return ErrorStatusLossless(idec, dec->status_);
|
||||
}
|
||||
|
||||
idec->state_ = STATE_DONE;
|
||||
|
||||
return VP8_STATUS_OK;
|
||||
assert(dec->status_ == VP8_STATUS_OK || dec->status_ == VP8_STATUS_SUSPENDED);
|
||||
return (dec->status_ == VP8_STATUS_SUSPENDED) ? dec->status_
|
||||
: FinishDecoding(idec);
|
||||
}
|
||||
|
||||
// Main decoding loop
|
||||
static VP8StatusCode IDecode(WebPIDecoder* idec) {
|
||||
VP8StatusCode status = VP8_STATUS_SUSPENDED;
|
||||
|
||||
if (idec->state_ == STATE_PRE_VP8) {
|
||||
if (idec->state_ == STATE_WEBP_HEADER) {
|
||||
status = DecodeWebPHeaders(idec);
|
||||
} else {
|
||||
if (idec->dec_ == NULL) {
|
||||
return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
|
||||
}
|
||||
}
|
||||
if (idec->state_ == STATE_VP8_FRAME_HEADER) {
|
||||
if (idec->state_ == STATE_VP8_HEADER) {
|
||||
status = DecodeVP8FrameHeader(idec);
|
||||
}
|
||||
if (idec->state_ == STATE_VP8_PARTS0) {
|
||||
@ -536,20 +578,23 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) {
|
||||
// Public functions
|
||||
|
||||
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
|
||||
WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(*idec));
|
||||
WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec));
|
||||
if (idec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idec->state_ = STATE_PRE_VP8;
|
||||
idec->state_ = STATE_WEBP_HEADER;
|
||||
idec->chunk_size_ = 0;
|
||||
|
||||
idec->last_mb_y_ = -1;
|
||||
|
||||
InitMemBuffer(&idec->mem_);
|
||||
WebPInitDecBuffer(&idec->output_);
|
||||
VP8InitIo(&idec->io_);
|
||||
|
||||
WebPResetDecParams(&idec->params_);
|
||||
idec->params_.output = output_buffer ? output_buffer : &idec->output_;
|
||||
idec->params_.output = (output_buffer != NULL) ? output_buffer
|
||||
: &idec->output_;
|
||||
WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.
|
||||
|
||||
return idec;
|
||||
@ -581,14 +626,18 @@ void WebPIDelete(WebPIDecoder* idec) {
|
||||
if (idec == NULL) return;
|
||||
if (idec->dec_ != NULL) {
|
||||
if (!idec->is_lossless_) {
|
||||
VP8Delete(idec->dec_);
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
// Synchronize the thread, clean-up and check for errors.
|
||||
VP8ExitCritical((VP8Decoder*)idec->dec_, &idec->io_);
|
||||
}
|
||||
VP8Delete((VP8Decoder*)idec->dec_);
|
||||
} else {
|
||||
VP8LDelete(idec->dec_);
|
||||
VP8LDelete((VP8LDecoder*)idec->dec_);
|
||||
}
|
||||
}
|
||||
ClearMemBuffer(&idec->mem_);
|
||||
WebPFreeDecBuffer(&idec->output_);
|
||||
free(idec);
|
||||
WebPSafeFree(idec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -596,12 +645,22 @@ void WebPIDelete(WebPIDecoder* idec) {
|
||||
|
||||
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
|
||||
size_t output_buffer_size, int output_stride) {
|
||||
const int is_external_memory = (output_buffer != NULL);
|
||||
WebPIDecoder* idec;
|
||||
|
||||
if (mode >= MODE_YUV) return NULL;
|
||||
if (!is_external_memory) { // Overwrite parameters to sane values.
|
||||
output_buffer_size = 0;
|
||||
output_stride = 0;
|
||||
} else { // A buffer was passed. Validate the other params.
|
||||
if (output_stride == 0 || output_buffer_size == 0) {
|
||||
return NULL; // invalid parameter.
|
||||
}
|
||||
}
|
||||
idec = WebPINewDecoder(NULL);
|
||||
if (idec == NULL) return NULL;
|
||||
idec->output_.colorspace = mode;
|
||||
idec->output_.is_external_memory = 1;
|
||||
idec->output_.is_external_memory = is_external_memory;
|
||||
idec->output_.u.RGBA.rgba = output_buffer;
|
||||
idec->output_.u.RGBA.stride = output_stride;
|
||||
idec->output_.u.RGBA.size = output_buffer_size;
|
||||
@ -612,10 +671,30 @@ WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride,
|
||||
uint8_t* a, size_t a_size, int a_stride) {
|
||||
WebPIDecoder* const idec = WebPINewDecoder(NULL);
|
||||
const int is_external_memory = (luma != NULL);
|
||||
WebPIDecoder* idec;
|
||||
WEBP_CSP_MODE colorspace;
|
||||
|
||||
if (!is_external_memory) { // Overwrite parameters to sane values.
|
||||
luma_size = u_size = v_size = a_size = 0;
|
||||
luma_stride = u_stride = v_stride = a_stride = 0;
|
||||
u = v = a = NULL;
|
||||
colorspace = MODE_YUVA;
|
||||
} else { // A luma buffer was passed. Validate the other parameters.
|
||||
if (u == NULL || v == NULL) return NULL;
|
||||
if (luma_size == 0 || u_size == 0 || v_size == 0) return NULL;
|
||||
if (luma_stride == 0 || u_stride == 0 || v_stride == 0) return NULL;
|
||||
if (a != NULL) {
|
||||
if (a_size == 0 || a_stride == 0) return NULL;
|
||||
}
|
||||
colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA;
|
||||
}
|
||||
|
||||
idec = WebPINewDecoder(NULL);
|
||||
if (idec == NULL) return NULL;
|
||||
idec->output_.colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA;
|
||||
idec->output_.is_external_memory = 1;
|
||||
|
||||
idec->output_.colorspace = colorspace;
|
||||
idec->output_.is_external_memory = is_external_memory;
|
||||
idec->output_.u.YUVA.y = luma;
|
||||
idec->output_.u.YUVA.y_stride = luma_stride;
|
||||
idec->output_.u.YUVA.y_size = luma_size;
|
||||
@ -768,7 +847,7 @@ int WebPISetIOHooks(WebPIDecoder* const idec,
|
||||
VP8IoSetupHook setup,
|
||||
VP8IoTeardownHook teardown,
|
||||
void* user_data) {
|
||||
if (idec == NULL || idec->state_ > STATE_PRE_VP8) {
|
||||
if (idec == NULL || idec->state_ > STATE_WEBP_HEADER) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -779,7 +858,3 @@ int WebPISetIOHooks(WebPIDecoder* const idec,
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// functions for sample output.
|
||||
@ -15,10 +17,7 @@
|
||||
#include "./webpi.h"
|
||||
#include "../dsp/dsp.h"
|
||||
#include "../dsp/yuv.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "../utils/utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main YUV<->RGB conversion functions
|
||||
@ -46,56 +45,16 @@ static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
|
||||
// Point-sampling U/V sampler.
|
||||
static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
WebPDecBuffer* output = p->output;
|
||||
const WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
|
||||
const uint8_t* y_src = io->y;
|
||||
const uint8_t* u_src = io->u;
|
||||
const uint8_t* v_src = io->v;
|
||||
const WebPSampleLinePairFunc sample = WebPSamplers[output->colorspace];
|
||||
const int mb_w = io->mb_w;
|
||||
const int last = io->mb_h - 1;
|
||||
int j;
|
||||
for (j = 0; j < last; j += 2) {
|
||||
sample(y_src, y_src + io->y_stride, u_src, v_src,
|
||||
dst, dst + buf->stride, mb_w);
|
||||
y_src += 2 * io->y_stride;
|
||||
u_src += io->uv_stride;
|
||||
v_src += io->uv_stride;
|
||||
dst += 2 * buf->stride;
|
||||
}
|
||||
if (j == last) { // Just do the last line twice
|
||||
sample(y_src, y_src, u_src, v_src, dst, dst, mb_w);
|
||||
}
|
||||
WebPDecBuffer* const output = p->output;
|
||||
WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||
uint8_t* const dst = buf->rgba + io->mb_y * buf->stride;
|
||||
WebPSamplerProcessPlane(io->y, io->y_stride,
|
||||
io->u, io->v, io->uv_stride,
|
||||
dst, buf->stride, io->mb_w, io->mb_h,
|
||||
WebPSamplers[output->colorspace]);
|
||||
return io->mb_h;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// YUV444 -> RGB conversion
|
||||
|
||||
#if 0 // TODO(skal): this is for future rescaling.
|
||||
static int EmitRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
WebPDecBuffer* output = p->output;
|
||||
const WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
|
||||
const uint8_t* y_src = io->y;
|
||||
const uint8_t* u_src = io->u;
|
||||
const uint8_t* v_src = io->v;
|
||||
const WebPYUV444Converter convert = WebPYUV444Converters[output->colorspace];
|
||||
const int mb_w = io->mb_w;
|
||||
const int last = io->mb_h;
|
||||
int j;
|
||||
for (j = 0; j < last; ++j) {
|
||||
convert(y_src, u_src, v_src, dst, mb_w);
|
||||
y_src += io->y_stride;
|
||||
u_src += io->uv_stride;
|
||||
v_src += io->uv_stride;
|
||||
dst += buf->stride;
|
||||
}
|
||||
return io->mb_h;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Fancy upsampling
|
||||
|
||||
@ -117,7 +76,7 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
|
||||
if (y == 0) {
|
||||
// First line is special cased. We mirror the u/v samples at boundary.
|
||||
upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w);
|
||||
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, mb_w);
|
||||
} else {
|
||||
// We can finish the left-over line from previous call.
|
||||
upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
|
||||
@ -160,14 +119,16 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_lines_out) {
|
||||
const uint8_t* alpha = io->a;
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
|
||||
int j;
|
||||
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == mb_h);
|
||||
if (alpha != NULL) {
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
memcpy(dst, alpha, mb_w * sizeof(*dst));
|
||||
@ -210,7 +171,8 @@ static int GetAlphaSourceRow(const VP8Io* const io,
|
||||
return start_y;
|
||||
}
|
||||
|
||||
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_lines_out) {
|
||||
const uint8_t* alpha = io->a;
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
@ -221,21 +183,13 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
int num_rows;
|
||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
uint32_t alpha_mask = 0xff;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
const uint32_t alpha_value = alpha[i];
|
||||
dst[4 * i] = alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha += io->width;
|
||||
dst += buf->stride;
|
||||
}
|
||||
// alpha_mask is < 0xff if there's non-trivial alpha to premultiply with.
|
||||
if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) {
|
||||
uint8_t* const dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
const int has_alpha = WebPDispatchAlpha(alpha, io->width, mb_w,
|
||||
num_rows, dst, buf->stride);
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == num_rows);
|
||||
// has_alpha is true if there's non-trivial alpha to premultiply with.
|
||||
if (has_alpha && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
mb_w, num_rows, buf->stride);
|
||||
}
|
||||
@ -243,7 +197,8 @@ static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
||||
static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_lines_out) {
|
||||
const uint8_t* alpha = io->a;
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
@ -252,10 +207,13 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
||||
int num_rows;
|
||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
#ifdef WEBP_SWAP_16BIT_CSP
|
||||
uint8_t* alpha_dst = base_rgba;
|
||||
#else
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
#endif
|
||||
uint32_t alpha_mask = 0x0f;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
@ -266,6 +224,8 @@ static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
||||
alpha += io->width;
|
||||
alpha_dst += buf->stride;
|
||||
}
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == num_rows);
|
||||
if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
|
||||
}
|
||||
@ -291,15 +251,35 @@ static int Rescale(const uint8_t* src, int src_stride,
|
||||
static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int mb_h = io->mb_h;
|
||||
const int uv_mb_h = (mb_h + 1) >> 1;
|
||||
const int num_lines_out = Rescale(io->y, io->y_stride, mb_h, &p->scaler_y);
|
||||
WebPRescaler* const scaler = &p->scaler_y;
|
||||
int num_lines_out = 0;
|
||||
if (WebPIsAlphaMode(p->output->colorspace) && io->a != NULL) {
|
||||
// Before rescaling, we premultiply the luma directly into the io->y
|
||||
// internal buffer. This is OK since these samples are not used for
|
||||
// intra-prediction (the top samples are saved in cache_y_/u_/v_).
|
||||
// But we need to cast the const away, though.
|
||||
WebPMultRows((uint8_t*)io->y, io->y_stride,
|
||||
io->a, io->width, io->mb_w, mb_h, 0);
|
||||
}
|
||||
num_lines_out = Rescale(io->y, io->y_stride, mb_h, scaler);
|
||||
Rescale(io->u, io->uv_stride, uv_mb_h, &p->scaler_u);
|
||||
Rescale(io->v, io->uv_stride, uv_mb_h, &p->scaler_v);
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_lines_out) {
|
||||
if (io->a != NULL) {
|
||||
Rescale(io->a, io->width, io->mb_h, &p->scaler_a);
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
uint8_t* dst_y = buf->y + p->last_y * buf->y_stride;
|
||||
const uint8_t* src_a = buf->a + p->last_y * buf->a_stride;
|
||||
const int num_lines_out = Rescale(io->a, io->width, io->mb_h, &p->scaler_a);
|
||||
(void)expected_num_lines_out;
|
||||
assert(expected_num_lines_out == num_lines_out);
|
||||
if (num_lines_out > 0) { // unmultiply the Y
|
||||
WebPMultRows(dst_y, buf->y_stride, src_a, buf->a_stride,
|
||||
p->scaler_a.dst_width, num_lines_out, 1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -316,39 +296,34 @@ static int InitYUVRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
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;
|
||||
rescaler_t* work;
|
||||
|
||||
tmp_size = work_size + 2 * uv_work_size;
|
||||
tmp_size = (work_size + 2 * uv_work_size) * sizeof(*work);
|
||||
if (has_alpha) {
|
||||
tmp_size += work_size;
|
||||
tmp_size += work_size * sizeof(*work);
|
||||
}
|
||||
p->memory = calloc(1, tmp_size * sizeof(*work));
|
||||
p->memory = WebPSafeMalloc(1ULL, tmp_size);
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error
|
||||
}
|
||||
work = (int32_t*)p->memory;
|
||||
work = (rescaler_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;
|
||||
}
|
||||
@ -360,13 +335,13 @@ static int ExportRGB(WebPDecParams* const p, int y_pos) {
|
||||
const WebPYUV444Converter convert =
|
||||
WebPYUV444Converters[p->output->colorspace];
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
||||
uint8_t* dst = buf->rgba + y_pos * buf->stride;
|
||||
int num_lines_out = 0;
|
||||
// For RGB rescaling, because of the YUV420, current scan position
|
||||
// U/V can be +1/-1 line from the Y one. Hence the double test.
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_y) &&
|
||||
WebPRescalerHasPendingOutput(&p->scaler_u)) {
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
assert(y_pos + num_lines_out < p->output->height);
|
||||
assert(p->scaler_u.y_accum == p->scaler_v.y_accum);
|
||||
WebPRescalerExportRow(&p->scaler_y);
|
||||
WebPRescalerExportRow(&p->scaler_u);
|
||||
@ -388,65 +363,69 @@ static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int y_lines_in =
|
||||
WebPRescalerImport(&p->scaler_y, mb_h - j,
|
||||
io->y + j * io->y_stride, io->y_stride);
|
||||
const int u_lines_in =
|
||||
WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j,
|
||||
io->u + uv_j * io->uv_stride, io->uv_stride);
|
||||
const int v_lines_in =
|
||||
WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j,
|
||||
io->v + uv_j * io->uv_stride, io->uv_stride);
|
||||
(void)v_lines_in; // remove a gcc warning
|
||||
assert(u_lines_in == v_lines_in);
|
||||
j += y_lines_in;
|
||||
uv_j += u_lines_in;
|
||||
num_lines_out += ExportRGB(p, num_lines_out);
|
||||
if (WebPRescaleNeededLines(&p->scaler_u, uv_mb_h - uv_j)) {
|
||||
const int u_lines_in =
|
||||
WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j,
|
||||
io->u + uv_j * io->uv_stride, io->uv_stride);
|
||||
const int v_lines_in =
|
||||
WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j,
|
||||
io->v + uv_j * io->uv_stride, io->uv_stride);
|
||||
(void)v_lines_in; // remove a gcc warning
|
||||
assert(u_lines_in == v_lines_in);
|
||||
uv_j += u_lines_in;
|
||||
}
|
||||
num_lines_out += ExportRGB(p, p->last_y + num_lines_out);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int ExportAlpha(WebPDecParams* const p, int y_pos) {
|
||||
static int ExportAlpha(WebPDecParams* const p, int y_pos, int max_lines_out) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
||||
uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int alpha_first =
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
int num_lines_out = 0;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
uint32_t alpha_mask = 0xff;
|
||||
uint32_t non_opaque = 0;
|
||||
const int width = p->scaler_a.dst_width;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||
int i;
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a) &&
|
||||
num_lines_out < max_lines_out) {
|
||||
assert(y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(&p->scaler_a);
|
||||
for (i = 0; i < width; ++i) {
|
||||
const uint32_t alpha_value = p->scaler_a.dst[i];
|
||||
dst[4 * i] = alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
non_opaque |= WebPDispatchAlpha(p->scaler_a.dst, 0, width, 1, dst, 0);
|
||||
dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha && alpha_mask != 0xff) {
|
||||
if (is_premult_alpha && non_opaque) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
width, num_lines_out, buf->stride);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
||||
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos,
|
||||
int max_lines_out) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
||||
uint8_t* const base_rgba = buf->rgba + y_pos * buf->stride;
|
||||
#ifdef WEBP_SWAP_16BIT_CSP
|
||||
uint8_t* alpha_dst = base_rgba;
|
||||
#else
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
#endif
|
||||
int num_lines_out = 0;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int width = p->scaler_a.dst_width;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
uint32_t alpha_mask = 0x0f;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a) &&
|
||||
num_lines_out < max_lines_out) {
|
||||
int i;
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
assert(y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(&p->scaler_a);
|
||||
for (i = 0; i < width; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
@ -463,15 +442,17 @@ static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_out_lines) {
|
||||
if (io->a != NULL) {
|
||||
WebPRescaler* const scaler = &p->scaler_a;
|
||||
int j = 0;
|
||||
int pos = 0;
|
||||
while (j < io->mb_h) {
|
||||
j += WebPRescalerImport(scaler, io->mb_h - j,
|
||||
io->a + j * io->width, io->width);
|
||||
pos += p->emit_alpha_row(p, pos);
|
||||
int lines_left = expected_num_out_lines;
|
||||
const int y_end = p->last_y + lines_left;
|
||||
while (lines_left > 0) {
|
||||
const int row_offset = scaler->src_y - io->mb_y;
|
||||
WebPRescalerImport(scaler, io->mb_h + io->mb_y - scaler->src_y,
|
||||
io->a + row_offset * io->width, io->width);
|
||||
lines_left -= p->emit_alpha_row(p, y_end - lines_left, lines_left);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
@ -484,9 +465,9 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
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
|
||||
rescaler_t* work; // rescalers work area
|
||||
uint8_t* tmp; // tmp storage for scaled YUV444 samples before RGB conversion
|
||||
size_t tmp_size1, tmp_size2;
|
||||
size_t tmp_size1, tmp_size2, total_size;
|
||||
|
||||
tmp_size1 = 3 * work_size;
|
||||
tmp_size2 = 3 * out_width;
|
||||
@ -494,30 +475,28 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
tmp_size1 += work_size;
|
||||
tmp_size2 += out_width;
|
||||
}
|
||||
p->memory = calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
|
||||
total_size = tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp);
|
||||
p->memory = WebPSafeMalloc(1ULL, total_size);
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error
|
||||
}
|
||||
work = (int32_t*)p->memory;
|
||||
work = (rescaler_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 ||
|
||||
@ -526,6 +505,7 @@ static int InitRGBRescaler(const VP8Io* const io, WebPDecParams* const p) {
|
||||
} else {
|
||||
p->emit_alpha_row = ExportAlpha;
|
||||
}
|
||||
WebPInitAlphaProcessing();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -546,7 +526,9 @@ static int CustomSetup(VP8Io* io) {
|
||||
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) {
|
||||
@ -554,11 +536,12 @@ static int CustomSetup(VP8Io* io) {
|
||||
}
|
||||
} else {
|
||||
if (is_rgb) {
|
||||
WebPInitSamplers();
|
||||
p->emit = EmitSampledRGB; // default
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
if (io->fancy_upsampling) {
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
const int uv_width = (io->mb_w + 1) >> 1;
|
||||
p->memory = malloc(io->mb_w + 2 * uv_width);
|
||||
p->memory = WebPSafeMalloc(1ULL, (size_t)(io->mb_w + 2 * uv_width));
|
||||
if (p->memory == NULL) {
|
||||
return 0; // memory error.
|
||||
}
|
||||
@ -567,18 +550,20 @@ static int CustomSetup(VP8Io* io) {
|
||||
p->tmp_v = p->tmp_u + uv_width;
|
||||
p->emit = EmitFancyRGB;
|
||||
WebPInitUpsamplers();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
p->emit = EmitYUV;
|
||||
}
|
||||
if (is_alpha) { // need transparency output
|
||||
if (WebPIsPremultipliedMode(colorspace)) WebPInitPremultiply();
|
||||
p->emit_alpha =
|
||||
(colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
|
||||
EmitAlphaRGBA4444
|
||||
: is_rgb ? EmitAlphaRGB
|
||||
: EmitAlphaYUV;
|
||||
if (is_rgb) {
|
||||
WebPInitAlphaProcessing();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -601,8 +586,8 @@ static int CustomPut(const VP8Io* io) {
|
||||
return 0;
|
||||
}
|
||||
num_lines_out = p->emit(io, p);
|
||||
if (p->emit_alpha) {
|
||||
p->emit_alpha(io, p);
|
||||
if (p->emit_alpha != NULL) {
|
||||
p->emit_alpha(io, p, num_lines_out);
|
||||
}
|
||||
p->last_y += num_lines_out;
|
||||
return 1;
|
||||
@ -612,7 +597,7 @@ static int CustomPut(const VP8Io* io) {
|
||||
|
||||
static void CustomTeardown(const VP8Io* io) {
|
||||
WebPDecParams* const p = (WebPDecParams*)io->opaque;
|
||||
free(p->memory);
|
||||
WebPSafeFree(p->memory);
|
||||
p->memory = NULL;
|
||||
}
|
||||
|
||||
@ -627,7 +612,3 @@ void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Quantizer initialization
|
||||
@ -11,10 +13,6 @@
|
||||
|
||||
#include "./vp8i.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static WEBP_INLINE int clip(int v, int M) {
|
||||
return v < 0 ? 0 : v > M ? M : v;
|
||||
}
|
||||
@ -102,12 +100,11 @@ void VP8ParseQuant(VP8Decoder* const dec) {
|
||||
|
||||
m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)];
|
||||
m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)];
|
||||
|
||||
m->uv_quant_ = q + dquv_ac; // for dithering strength evaluation
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,22 +1,21 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Coding trees and probas
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "vp8i.h"
|
||||
#include "./vp8i.h"
|
||||
#include "../utils/bit_reader_inl.h"
|
||||
|
||||
#define USE_GENERIC_TREE
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef USE_GENERIC_TREE
|
||||
static const int8_t kYModesIntra4[18] = {
|
||||
-B_DC_PRED, 1,
|
||||
@ -31,61 +30,12 @@ static const int8_t kYModesIntra4[18] = {
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
|
||||
// inter prediction modes
|
||||
enum {
|
||||
LEFT4 = 0, ABOVE4 = 1, ZERO4 = 2, NEW4 = 3,
|
||||
NEARESTMV, NEARMV, ZEROMV, NEWMV, SPLITMV };
|
||||
|
||||
static const int8_t kYModesInter[8] = {
|
||||
-DC_PRED, 1,
|
||||
2, 3,
|
||||
-V_PRED, -H_PRED,
|
||||
-TM_PRED, -B_PRED
|
||||
};
|
||||
|
||||
static const int8_t kMBSplit[6] = {
|
||||
-3, 1,
|
||||
-2, 2,
|
||||
-0, -1
|
||||
};
|
||||
|
||||
static const int8_t kMVRef[8] = {
|
||||
-ZEROMV, 1,
|
||||
-NEARESTMV, 2,
|
||||
-NEARMV, 3,
|
||||
-NEWMV, -SPLITMV
|
||||
};
|
||||
|
||||
static const int8_t kMVRef4[6] = {
|
||||
-LEFT4, 1,
|
||||
-ABOVE4, 2,
|
||||
-ZERO4, -NEW4
|
||||
};
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Default probabilities
|
||||
|
||||
// Inter
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
static const uint8_t kYModeProbaInter0[4] = { 112, 86, 140, 37 };
|
||||
static const uint8_t kUVModeProbaInter0[3] = { 162, 101, 204 };
|
||||
static const uint8_t kMVProba0[2][NUM_MV_PROBAS] = {
|
||||
{ 162, 128, 225, 146, 172, 147, 214, 39,
|
||||
156, 128, 129, 132, 75, 145, 178, 206,
|
||||
239, 254, 254 },
|
||||
{ 164, 128, 204, 170, 119, 235, 140, 230,
|
||||
228, 128, 130, 130, 74, 148, 180, 203,
|
||||
236, 254, 254 }
|
||||
};
|
||||
#endif
|
||||
|
||||
// Paragraph 13.5
|
||||
static const uint8_t
|
||||
CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
|
||||
// genereated using vp8_default_coef_probs() in entropy.c:129
|
||||
{ { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
@ -326,28 +276,38 @@ static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
|
||||
|
||||
void VP8ResetProba(VP8Proba* const proba) {
|
||||
memset(proba->segments_, 255u, sizeof(proba->segments_));
|
||||
memcpy(proba->coeffs_, CoeffsProba0, sizeof(CoeffsProba0));
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
memcpy(proba->mv_, kMVProba0, sizeof(kMVProba0));
|
||||
memcpy(proba->ymode_, kYModeProbaInter0, sizeof(kYModeProbaInter0));
|
||||
memcpy(proba->uvmode_, kUVModeProbaInter0, sizeof(kUVModeProbaInter0));
|
||||
#endif
|
||||
// proba->bands_[][] is initialized later
|
||||
}
|
||||
|
||||
void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
uint8_t* const top = dec->intra_t_ + 4 * dec->mb_x_;
|
||||
static void ParseIntraMode(VP8BitReader* const br,
|
||||
VP8Decoder* const dec, int mb_x) {
|
||||
uint8_t* const top = dec->intra_t_ + 4 * mb_x;
|
||||
uint8_t* const left = dec->intra_l_;
|
||||
// Hardcoded 16x16 intra-mode decision tree.
|
||||
dec->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first
|
||||
if (!dec->is_i4x4_) {
|
||||
VP8MBData* const block = dec->mb_data_ + mb_x;
|
||||
|
||||
// Note: we don't save segment map (yet), as we don't expect
|
||||
// to decode more than 1 keyframe.
|
||||
if (dec->segment_hdr_.update_map_) {
|
||||
// Hardcoded tree parsing
|
||||
block->segment_ = !VP8GetBit(br, dec->proba_.segments_[0])
|
||||
? VP8GetBit(br, dec->proba_.segments_[1])
|
||||
: 2 + VP8GetBit(br, dec->proba_.segments_[2]);
|
||||
} else {
|
||||
block->segment_ = 0; // default for intra
|
||||
}
|
||||
if (dec->use_skip_proba_) block->skip_ = VP8GetBit(br, dec->skip_p_);
|
||||
|
||||
block->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first
|
||||
if (!block->is_i4x4_) {
|
||||
// Hardcoded 16x16 intra-mode decision tree.
|
||||
const int ymode =
|
||||
VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED)
|
||||
: (VP8GetBit(br, 163) ? V_PRED : DC_PRED);
|
||||
dec->imodes_[0] = ymode;
|
||||
memset(top, ymode, 4 * sizeof(top[0]));
|
||||
memset(left, ymode, 4 * sizeof(left[0]));
|
||||
block->imodes_[0] = ymode;
|
||||
memset(top, ymode, 4 * sizeof(*top));
|
||||
memset(left, ymode, 4 * sizeof(*left));
|
||||
} else {
|
||||
uint8_t* modes = dec->imodes_;
|
||||
uint8_t* modes = block->imodes_;
|
||||
int y;
|
||||
for (y = 0; y < 4; ++y) {
|
||||
int ymode = left[y];
|
||||
@ -356,10 +316,10 @@ void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
const uint8_t* const prob = kBModesProba[top[x]][ymode];
|
||||
#ifdef USE_GENERIC_TREE
|
||||
// Generic tree-parsing
|
||||
int i = 0;
|
||||
do {
|
||||
int i = kYModesIntra4[VP8GetBit(br, prob[0])];
|
||||
while (i > 0) {
|
||||
i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])];
|
||||
} while (i > 0);
|
||||
}
|
||||
ymode = -i;
|
||||
#else
|
||||
// Hardcoded tree parsing
|
||||
@ -374,15 +334,24 @@ void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
(!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED)));
|
||||
#endif // USE_GENERIC_TREE
|
||||
top[x] = ymode;
|
||||
*modes++ = ymode;
|
||||
}
|
||||
memcpy(modes, top, 4 * sizeof(*top));
|
||||
modes += 4;
|
||||
left[y] = ymode;
|
||||
}
|
||||
}
|
||||
// Hardcoded UVMode decision tree
|
||||
dec->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED
|
||||
: !VP8GetBit(br, 114) ? V_PRED
|
||||
: VP8GetBit(br, 183) ? TM_PRED : H_PRED;
|
||||
block->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED
|
||||
: !VP8GetBit(br, 114) ? V_PRED
|
||||
: VP8GetBit(br, 183) ? TM_PRED : H_PRED;
|
||||
}
|
||||
|
||||
int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
for (mb_x = 0; mb_x < dec->mb_w_; ++mb_x) {
|
||||
ParseIntraMode(br, dec, mb_x);
|
||||
}
|
||||
return !dec->br_.eof_;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -524,18 +493,13 @@ static const uint8_t
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
static const uint8_t MVUpdateProba[2][NUM_MV_PROBAS] = {
|
||||
{ 237, 246, 253, 253, 254, 254, 254, 254,
|
||||
254, 254, 254, 254, 254, 254, 250, 250,
|
||||
252, 254, 254 },
|
||||
{ 231, 243, 245, 253, 254, 254, 254, 254,
|
||||
254, 254, 254, 254, 254, 254, 251, 251,
|
||||
254, 254, 254 }
|
||||
};
|
||||
#endif
|
||||
|
||||
// Paragraph 9.9
|
||||
|
||||
static const int kBands[16 + 1] = {
|
||||
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
||||
0 // extra entry as sentinel
|
||||
};
|
||||
|
||||
void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
VP8Proba* const proba = &dec->proba_;
|
||||
int t, b, c, p;
|
||||
@ -543,47 +507,19 @@ void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
for (b = 0; b < NUM_BANDS; ++b) {
|
||||
for (c = 0; c < NUM_CTX; ++c) {
|
||||
for (p = 0; p < NUM_PROBAS; ++p) {
|
||||
if (VP8GetBit(br, CoeffsUpdateProba[t][b][c][p])) {
|
||||
proba->coeffs_[t][b][c][p] = VP8GetValue(br, 8);
|
||||
}
|
||||
const int v = VP8GetBit(br, CoeffsUpdateProba[t][b][c][p]) ?
|
||||
VP8GetValue(br, 8) : CoeffsProba0[t][b][c][p];
|
||||
proba->bands_[t][b].probas_[c][p] = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (b = 0; b < 16 + 1; ++b) {
|
||||
proba->bands_ptr_[t][b] = &proba->bands_[t][kBands[b]];
|
||||
}
|
||||
}
|
||||
dec->use_skip_proba_ = VP8Get(br);
|
||||
if (dec->use_skip_proba_) {
|
||||
dec->skip_p_ = VP8GetValue(br, 8);
|
||||
}
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
if (!dec->frm_hdr_.key_frame_) {
|
||||
int i;
|
||||
dec->intra_p_ = VP8GetValue(br, 8);
|
||||
dec->last_p_ = VP8GetValue(br, 8);
|
||||
dec->golden_p_ = VP8GetValue(br, 8);
|
||||
if (VP8Get(br)) { // update y-mode
|
||||
for (i = 0; i < 4; ++i) {
|
||||
proba->ymode_[i] = VP8GetValue(br, 8);
|
||||
}
|
||||
}
|
||||
if (VP8Get(br)) { // update uv-mode
|
||||
for (i = 0; i < 3; ++i) {
|
||||
proba->uvmode_[i] = VP8GetValue(br, 8);
|
||||
}
|
||||
}
|
||||
// update MV
|
||||
for (i = 0; i < 2; ++i) {
|
||||
int k;
|
||||
for (k = 0; k < NUM_MV_PROBAS; ++k) {
|
||||
if (VP8GetBit(br, MVUpdateProba[i][k])) {
|
||||
const int v = VP8GetValue(br, 7);
|
||||
proba->mv_[i][k] = v ? v << 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// main entry for the decoder
|
||||
@ -11,14 +13,12 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./alphai.h"
|
||||
#include "./vp8i.h"
|
||||
#include "./vp8li.h"
|
||||
#include "./webpi.h"
|
||||
#include "../utils/bit_reader.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "../utils/bit_reader_inl.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@ -45,10 +45,10 @@ int VP8InitIoInternal(VP8Io* const io, int version) {
|
||||
}
|
||||
|
||||
VP8Decoder* VP8New(void) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)calloc(1, sizeof(*dec));
|
||||
VP8Decoder* const dec = (VP8Decoder*)WebPSafeCalloc(1ULL, sizeof(*dec));
|
||||
if (dec != NULL) {
|
||||
SetOk(dec);
|
||||
WebPWorkerInit(&dec->worker_);
|
||||
WebPGetWorkerInterface()->Init(&dec->worker_);
|
||||
dec->ready_ = 0;
|
||||
dec->num_parts_ = 1;
|
||||
}
|
||||
@ -69,16 +69,13 @@ const char* VP8StatusMessage(VP8Decoder* const dec) {
|
||||
void VP8Delete(VP8Decoder* const dec) {
|
||||
if (dec != NULL) {
|
||||
VP8Clear(dec);
|
||||
free(dec);
|
||||
WebPSafeFree(dec);
|
||||
}
|
||||
}
|
||||
|
||||
int VP8SetError(VP8Decoder* const dec,
|
||||
VP8StatusCode error, const char* const msg) {
|
||||
// TODO This check would be unnecessary if alpha decompression was separated
|
||||
// from VP8ProcessRow/FinishRow. This avoids setting 'dec->status_' to
|
||||
// something other than VP8_STATUS_BITSTREAM_ERROR on alpha decompression
|
||||
// failure.
|
||||
// The oldest error reported takes precedence over the new one.
|
||||
if (dec->status_ == VP8_STATUS_OK) {
|
||||
dec->status_ = error;
|
||||
dec->error_msg_ = msg;
|
||||
@ -121,6 +118,9 @@ int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
|
||||
if (((bits >> 5)) >= chunk_size) { // partition_length
|
||||
return 0; // inconsistent size information.
|
||||
}
|
||||
if (w == 0 || h == 0) {
|
||||
return 0; // We don't support both width and height to be zero.
|
||||
}
|
||||
|
||||
if (width) {
|
||||
*width = w;
|
||||
@ -190,25 +190,27 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
||||
const uint8_t* sz = buf;
|
||||
const uint8_t* buf_end = buf + size;
|
||||
const uint8_t* part_start;
|
||||
int last_part;
|
||||
int p;
|
||||
size_t size_left = size;
|
||||
size_t last_part;
|
||||
size_t p;
|
||||
|
||||
dec->num_parts_ = 1 << VP8GetValue(br, 2);
|
||||
last_part = dec->num_parts_ - 1;
|
||||
part_start = buf + last_part * 3;
|
||||
if (buf_end < part_start) {
|
||||
if (size < 3 * last_part) {
|
||||
// we can't even read the sizes with sz[]! That's a failure.
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
part_start = buf + last_part * 3;
|
||||
size_left -= last_part * 3;
|
||||
for (p = 0; p < last_part; ++p) {
|
||||
const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
||||
const uint8_t* part_end = part_start + psize;
|
||||
if (part_end > buf_end) part_end = buf_end;
|
||||
VP8InitBitReader(dec->parts_ + p, part_start, part_end);
|
||||
part_start = part_end;
|
||||
size_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
||||
if (psize > size_left) psize = size_left;
|
||||
VP8InitBitReader(dec->parts_ + p, part_start, psize);
|
||||
part_start += psize;
|
||||
size_left -= psize;
|
||||
sz += 3;
|
||||
}
|
||||
VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end);
|
||||
VP8InitBitReader(dec->parts_ + last_part, part_start, size_left);
|
||||
return (part_start < buf_end) ? VP8_STATUS_OK :
|
||||
VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
|
||||
}
|
||||
@ -236,20 +238,6 @@ static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
|
||||
}
|
||||
}
|
||||
dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2;
|
||||
if (dec->filter_type_ > 0) { // precompute filter levels per segment
|
||||
if (dec->segment_hdr_.use_segment_) {
|
||||
int s;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
int strength = dec->segment_hdr_.filter_strength_[s];
|
||||
if (!dec->segment_hdr_.absolute_delta_) {
|
||||
strength += hdr->level_;
|
||||
}
|
||||
dec->filter_levels_[s] = strength;
|
||||
}
|
||||
} else {
|
||||
dec->filter_levels_[0] = hdr->level_;
|
||||
}
|
||||
}
|
||||
return !br->eof_;
|
||||
}
|
||||
|
||||
@ -261,7 +249,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
VP8PictureHeader* pic_hdr;
|
||||
VP8BitReader* br;
|
||||
VP8StatusCode status;
|
||||
WebPHeaderStructure headers;
|
||||
|
||||
if (dec == NULL) {
|
||||
return 0;
|
||||
@ -271,33 +258,8 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
||||
"null VP8Io passed to VP8GetHeaders()");
|
||||
}
|
||||
|
||||
// Process Pre-VP8 chunks.
|
||||
headers.data = io->data;
|
||||
headers.data_size = io->data_size;
|
||||
status = WebPParseHeaders(&headers);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return VP8SetError(dec, status, "Incorrect/incomplete header.");
|
||||
}
|
||||
if (headers.is_lossless) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Unexpected lossless format encountered.");
|
||||
}
|
||||
|
||||
if (dec->alpha_data_ == NULL) {
|
||||
assert(dec->alpha_data_size_ == 0);
|
||||
// We have NOT set alpha data yet. Set it now.
|
||||
// (This is to ensure that dec->alpha_data_ is NOT reset to NULL if
|
||||
// WebPParseHeaders() is called more than once, as in incremental decoding
|
||||
// case.)
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
}
|
||||
|
||||
// Process the VP8 frame header.
|
||||
buf = headers.data + headers.offset;
|
||||
buf_size = headers.data_size - headers.offset;
|
||||
assert(headers.data_size >= headers.offset); // WebPParseHeaders' guarantee
|
||||
buf = io->data;
|
||||
buf_size = io->data_size;
|
||||
if (buf_size < 4) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Truncated header.");
|
||||
@ -355,7 +317,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
|
||||
VP8ResetProba(&dec->proba_);
|
||||
ResetSegmentHeader(&dec->segment_hdr_);
|
||||
dec->segment_ = 0; // default for intra
|
||||
}
|
||||
|
||||
// Check if we have all the partition #0 available, and initialize dec->br_
|
||||
@ -366,7 +327,7 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
}
|
||||
|
||||
br = &dec->br_;
|
||||
VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_);
|
||||
VP8InitBitReader(br, buf, frm_hdr->partition_length_);
|
||||
buf += frm_hdr->partition_length_;
|
||||
buf_size -= frm_hdr->partition_length_;
|
||||
|
||||
@ -393,63 +354,14 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
|
||||
// Frame buffer marking
|
||||
if (!frm_hdr->key_frame_) {
|
||||
// Paragraph 9.7
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
dec->buffer_flags_ = VP8Get(br) << 0; // update golden
|
||||
dec->buffer_flags_ |= VP8Get(br) << 1; // update alt ref
|
||||
if (!(dec->buffer_flags_ & 1)) {
|
||||
dec->buffer_flags_ |= VP8GetValue(br, 2) << 2;
|
||||
}
|
||||
if (!(dec->buffer_flags_ & 2)) {
|
||||
dec->buffer_flags_ |= VP8GetValue(br, 2) << 4;
|
||||
}
|
||||
dec->buffer_flags_ |= VP8Get(br) << 6; // sign bias golden
|
||||
dec->buffer_flags_ |= VP8Get(br) << 7; // sign bias alt ref
|
||||
#else
|
||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
"Not a key frame.");
|
||||
#endif
|
||||
} else {
|
||||
dec->buffer_flags_ = 0x003 | 0x100;
|
||||
}
|
||||
|
||||
// Paragraph 9.8
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
dec->update_proba_ = VP8Get(br);
|
||||
if (!dec->update_proba_) { // save for later restore
|
||||
dec->proba_saved_ = dec->proba_;
|
||||
}
|
||||
dec->buffer_flags_ &= 1 << 8;
|
||||
dec->buffer_flags_ |=
|
||||
(frm_hdr->key_frame_ || VP8Get(br)) << 8; // refresh last frame
|
||||
#else
|
||||
VP8Get(br); // just ignore the value of update_proba_
|
||||
#endif
|
||||
VP8Get(br); // ignore the value of update_proba_
|
||||
|
||||
VP8ParseProba(br, dec);
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
// Extensions
|
||||
if (dec->pic_hdr_.colorspace_) {
|
||||
const size_t kTrailerSize = 8;
|
||||
const uint8_t kTrailerMarker = 0x01;
|
||||
const uint8_t* ext_buf = buf - kTrailerSize;
|
||||
size_t size;
|
||||
|
||||
if (frm_hdr->partition_length_ < kTrailerSize ||
|
||||
ext_buf[kTrailerSize - 1] != kTrailerMarker) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"RIFF: Inconsistent extra information.");
|
||||
}
|
||||
|
||||
// Layer
|
||||
size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16);
|
||||
dec->layer_data_size_ = size;
|
||||
dec->layer_data_ = NULL; // will be set later
|
||||
dec->layer_colorspace_ = ext_buf[3];
|
||||
}
|
||||
#endif
|
||||
|
||||
// sanitized state
|
||||
dec->ready_ = 1;
|
||||
return 1;
|
||||
@ -458,11 +370,6 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
//------------------------------------------------------------------------------
|
||||
// Residual decoding (Paragraph 13.2 / 13.3)
|
||||
|
||||
static const uint8_t kBands[16 + 1] = {
|
||||
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
||||
0 // extra entry as sentinel
|
||||
};
|
||||
|
||||
static const uint8_t kCat3[] = { 173, 148, 140, 0 };
|
||||
static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 };
|
||||
static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 };
|
||||
@ -473,254 +380,227 @@ static const uint8_t kZigzag[16] = {
|
||||
0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
|
||||
};
|
||||
|
||||
typedef const uint8_t (*ProbaArray)[NUM_CTX][NUM_PROBAS]; // for const-casting
|
||||
|
||||
// Returns the position of the last non-zero coeff plus one
|
||||
// (and 0 if there's no coeff at all)
|
||||
static int GetCoeffs(VP8BitReader* const br, ProbaArray prob,
|
||||
int ctx, const quant_t dq, int n, int16_t* out) {
|
||||
// n is either 0 or 1 here. kBands[n] is not necessary for extracting '*p'.
|
||||
const uint8_t* p = prob[n][ctx];
|
||||
if (!VP8GetBit(br, p[0])) { // first EOB is more a 'CBP' bit.
|
||||
return 0;
|
||||
}
|
||||
while (1) {
|
||||
++n;
|
||||
if (!VP8GetBit(br, p[1])) {
|
||||
p = prob[kBands[n]][0];
|
||||
} else { // non zero coeff
|
||||
int v, j;
|
||||
if (!VP8GetBit(br, p[2])) {
|
||||
p = prob[kBands[n]][1];
|
||||
v = 1;
|
||||
// See section 13-2: http://tools.ietf.org/html/rfc6386#section-13.2
|
||||
static int GetLargeValue(VP8BitReader* const br, const uint8_t* const p) {
|
||||
int v;
|
||||
if (!VP8GetBit(br, p[3])) {
|
||||
if (!VP8GetBit(br, p[4])) {
|
||||
v = 2;
|
||||
} else {
|
||||
v = 3 + VP8GetBit(br, p[5]);
|
||||
}
|
||||
} else {
|
||||
if (!VP8GetBit(br, p[6])) {
|
||||
if (!VP8GetBit(br, p[7])) {
|
||||
v = 5 + VP8GetBit(br, 159);
|
||||
} else {
|
||||
if (!VP8GetBit(br, p[3])) {
|
||||
if (!VP8GetBit(br, p[4])) {
|
||||
v = 2;
|
||||
} else {
|
||||
v = 3 + VP8GetBit(br, p[5]);
|
||||
}
|
||||
} else {
|
||||
if (!VP8GetBit(br, p[6])) {
|
||||
if (!VP8GetBit(br, p[7])) {
|
||||
v = 5 + VP8GetBit(br, 159);
|
||||
} else {
|
||||
v = 7 + 2 * VP8GetBit(br, 165);
|
||||
v += VP8GetBit(br, 145);
|
||||
}
|
||||
} else {
|
||||
const uint8_t* tab;
|
||||
const int bit1 = VP8GetBit(br, p[8]);
|
||||
const int bit0 = VP8GetBit(br, p[9 + bit1]);
|
||||
const int cat = 2 * bit1 + bit0;
|
||||
v = 0;
|
||||
for (tab = kCat3456[cat]; *tab; ++tab) {
|
||||
v += v + VP8GetBit(br, *tab);
|
||||
}
|
||||
v += 3 + (8 << cat);
|
||||
}
|
||||
}
|
||||
p = prob[kBands[n]][2];
|
||||
v = 7 + 2 * VP8GetBit(br, 165);
|
||||
v += VP8GetBit(br, 145);
|
||||
}
|
||||
j = kZigzag[n - 1];
|
||||
out[j] = VP8GetSigned(br, v) * dq[j > 0];
|
||||
if (n == 16 || !VP8GetBit(br, p[0])) { // EOB
|
||||
return n;
|
||||
} else {
|
||||
const uint8_t* tab;
|
||||
const int bit1 = VP8GetBit(br, p[8]);
|
||||
const int bit0 = VP8GetBit(br, p[9 + bit1]);
|
||||
const int cat = 2 * bit1 + bit0;
|
||||
v = 0;
|
||||
for (tab = kCat3456[cat]; *tab; ++tab) {
|
||||
v += v + VP8GetBit(br, *tab);
|
||||
}
|
||||
}
|
||||
if (n == 16) {
|
||||
return 16;
|
||||
v += 3 + (8 << cat);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
// Alias-safe way of converting 4bytes to 32bits.
|
||||
typedef union {
|
||||
uint8_t i8[4];
|
||||
uint32_t i32;
|
||||
} PackedNz;
|
||||
// Returns the position of the last non-zero coeff plus one
|
||||
static int GetCoeffs(VP8BitReader* const br, const VP8BandProbas* const prob[],
|
||||
int ctx, const quant_t dq, int n, int16_t* out) {
|
||||
const uint8_t* p = prob[n]->probas_[ctx];
|
||||
for (; n < 16; ++n) {
|
||||
if (!VP8GetBit(br, p[0])) {
|
||||
return n; // previous coeff was last non-zero coeff
|
||||
}
|
||||
while (!VP8GetBit(br, p[1])) { // sequence of zero coeffs
|
||||
p = prob[++n]->probas_[0];
|
||||
if (n == 16) return 16;
|
||||
}
|
||||
{ // non zero coeff
|
||||
const VP8ProbaArray* const p_ctx = &prob[n + 1]->probas_[0];
|
||||
int v;
|
||||
if (!VP8GetBit(br, p[2])) {
|
||||
v = 1;
|
||||
p = p_ctx[1];
|
||||
} else {
|
||||
v = GetLargeValue(br, p);
|
||||
p = p_ctx[2];
|
||||
}
|
||||
out[kZigzag[n]] = VP8GetSigned(br, v) * dq[n > 0];
|
||||
}
|
||||
}
|
||||
return 16;
|
||||
}
|
||||
|
||||
// Table to unpack four bits into four bytes
|
||||
static const PackedNz kUnpackTab[16] = {
|
||||
{{0, 0, 0, 0}}, {{1, 0, 0, 0}}, {{0, 1, 0, 0}}, {{1, 1, 0, 0}},
|
||||
{{0, 0, 1, 0}}, {{1, 0, 1, 0}}, {{0, 1, 1, 0}}, {{1, 1, 1, 0}},
|
||||
{{0, 0, 0, 1}}, {{1, 0, 0, 1}}, {{0, 1, 0, 1}}, {{1, 1, 0, 1}},
|
||||
{{0, 0, 1, 1}}, {{1, 0, 1, 1}}, {{0, 1, 1, 1}}, {{1, 1, 1, 1}} };
|
||||
static WEBP_INLINE uint32_t NzCodeBits(uint32_t nz_coeffs, int nz, int dc_nz) {
|
||||
nz_coeffs <<= 2;
|
||||
nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz;
|
||||
return nz_coeffs;
|
||||
}
|
||||
|
||||
// Macro to pack four LSB of four bytes into four bits.
|
||||
#if defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || \
|
||||
defined(__BIG_ENDIAN__)
|
||||
#define PACK_CST 0x08040201U
|
||||
#else
|
||||
#define PACK_CST 0x01020408U
|
||||
#endif
|
||||
#define PACK(X, S) ((((X).i32 * PACK_CST) & 0xff000000) >> (S))
|
||||
|
||||
static void ParseResiduals(VP8Decoder* const dec,
|
||||
VP8MB* const mb, VP8BitReader* const token_br) {
|
||||
int out_t_nz, out_l_nz, first;
|
||||
ProbaArray ac_prob;
|
||||
const VP8QuantMatrix* q = &dec->dqm_[dec->segment_];
|
||||
int16_t* dst = dec->coeffs_;
|
||||
static int ParseResiduals(VP8Decoder* const dec,
|
||||
VP8MB* const mb, VP8BitReader* const token_br) {
|
||||
const VP8BandProbas* (* const bands)[16 + 1] = dec->proba_.bands_ptr_;
|
||||
const VP8BandProbas* const * ac_proba;
|
||||
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
||||
const VP8QuantMatrix* const q = &dec->dqm_[block->segment_];
|
||||
int16_t* dst = block->coeffs_;
|
||||
VP8MB* const left_mb = dec->mb_info_ - 1;
|
||||
PackedNz nz_ac, nz_dc;
|
||||
PackedNz tnz, lnz;
|
||||
uint32_t non_zero_ac = 0;
|
||||
uint32_t non_zero_dc = 0;
|
||||
uint8_t tnz, lnz;
|
||||
uint32_t non_zero_y = 0;
|
||||
uint32_t non_zero_uv = 0;
|
||||
int x, y, ch;
|
||||
uint32_t out_t_nz, out_l_nz;
|
||||
int first;
|
||||
|
||||
nz_dc.i32 = nz_ac.i32 = 0;
|
||||
memset(dst, 0, 384 * sizeof(*dst));
|
||||
if (!dec->is_i4x4_) { // parse DC
|
||||
if (!block->is_i4x4_) { // parse DC
|
||||
int16_t dc[16] = { 0 };
|
||||
const int ctx = mb->dc_nz_ + left_mb->dc_nz_;
|
||||
mb->dc_nz_ = left_mb->dc_nz_ =
|
||||
(GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[1],
|
||||
ctx, q->y2_mat_, 0, dc) > 0);
|
||||
const int ctx = mb->nz_dc_ + left_mb->nz_dc_;
|
||||
const int nz = GetCoeffs(token_br, bands[1], ctx, q->y2_mat_, 0, dc);
|
||||
mb->nz_dc_ = left_mb->nz_dc_ = (nz > 0);
|
||||
if (nz > 1) { // more than just the DC -> perform the full transform
|
||||
VP8TransformWHT(dc, dst);
|
||||
} else { // only DC is non-zero -> inlined simplified transform
|
||||
int i;
|
||||
const int dc0 = (dc[0] + 3) >> 3;
|
||||
for (i = 0; i < 16 * 16; i += 16) dst[i] = dc0;
|
||||
}
|
||||
first = 1;
|
||||
ac_prob = (ProbaArray)dec->proba_.coeffs_[0];
|
||||
VP8TransformWHT(dc, dst);
|
||||
ac_proba = bands[0];
|
||||
} else {
|
||||
first = 0;
|
||||
ac_prob = (ProbaArray)dec->proba_.coeffs_[3];
|
||||
ac_proba = bands[3];
|
||||
}
|
||||
|
||||
tnz = kUnpackTab[mb->nz_ & 0xf];
|
||||
lnz = kUnpackTab[left_mb->nz_ & 0xf];
|
||||
tnz = mb->nz_ & 0x0f;
|
||||
lnz = left_mb->nz_ & 0x0f;
|
||||
for (y = 0; y < 4; ++y) {
|
||||
int l = lnz.i8[y];
|
||||
int l = lnz & 1;
|
||||
uint32_t nz_coeffs = 0;
|
||||
for (x = 0; x < 4; ++x) {
|
||||
const int ctx = l + tnz.i8[x];
|
||||
const int nz = GetCoeffs(token_br, ac_prob, ctx,
|
||||
q->y1_mat_, first, dst);
|
||||
tnz.i8[x] = l = (nz > 0);
|
||||
nz_dc.i8[x] = (dst[0] != 0);
|
||||
nz_ac.i8[x] = (nz > 1);
|
||||
const int ctx = l + (tnz & 1);
|
||||
const int nz = GetCoeffs(token_br, ac_proba, ctx, q->y1_mat_, first, dst);
|
||||
l = (nz > first);
|
||||
tnz = (tnz >> 1) | (l << 7);
|
||||
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
|
||||
dst += 16;
|
||||
}
|
||||
lnz.i8[y] = l;
|
||||
non_zero_dc |= PACK(nz_dc, 24 - y * 4);
|
||||
non_zero_ac |= PACK(nz_ac, 24 - y * 4);
|
||||
tnz >>= 4;
|
||||
lnz = (lnz >> 1) | (l << 7);
|
||||
non_zero_y = (non_zero_y << 8) | nz_coeffs;
|
||||
}
|
||||
out_t_nz = PACK(tnz, 24);
|
||||
out_l_nz = PACK(lnz, 24);
|
||||
out_t_nz = tnz;
|
||||
out_l_nz = lnz >> 4;
|
||||
|
||||
tnz = kUnpackTab[mb->nz_ >> 4];
|
||||
lnz = kUnpackTab[left_mb->nz_ >> 4];
|
||||
for (ch = 0; ch < 4; ch += 2) {
|
||||
uint32_t nz_coeffs = 0;
|
||||
tnz = mb->nz_ >> (4 + ch);
|
||||
lnz = left_mb->nz_ >> (4 + ch);
|
||||
for (y = 0; y < 2; ++y) {
|
||||
int l = lnz.i8[ch + y];
|
||||
int l = lnz & 1;
|
||||
for (x = 0; x < 2; ++x) {
|
||||
const int ctx = l + tnz.i8[ch + x];
|
||||
const int nz =
|
||||
GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[2],
|
||||
ctx, q->uv_mat_, 0, dst);
|
||||
tnz.i8[ch + x] = l = (nz > 0);
|
||||
nz_dc.i8[y * 2 + x] = (dst[0] != 0);
|
||||
nz_ac.i8[y * 2 + x] = (nz > 1);
|
||||
const int ctx = l + (tnz & 1);
|
||||
const int nz = GetCoeffs(token_br, bands[2], ctx, q->uv_mat_, 0, dst);
|
||||
l = (nz > 0);
|
||||
tnz = (tnz >> 1) | (l << 3);
|
||||
nz_coeffs = NzCodeBits(nz_coeffs, nz, dst[0] != 0);
|
||||
dst += 16;
|
||||
}
|
||||
lnz.i8[ch + y] = l;
|
||||
tnz >>= 2;
|
||||
lnz = (lnz >> 1) | (l << 5);
|
||||
}
|
||||
non_zero_dc |= PACK(nz_dc, 8 - ch * 2);
|
||||
non_zero_ac |= PACK(nz_ac, 8 - ch * 2);
|
||||
// Note: we don't really need the per-4x4 details for U/V blocks.
|
||||
non_zero_uv |= nz_coeffs << (4 * ch);
|
||||
out_t_nz |= (tnz << 4) << ch;
|
||||
out_l_nz |= (lnz & 0xf0) << ch;
|
||||
}
|
||||
out_t_nz |= PACK(tnz, 20);
|
||||
out_l_nz |= PACK(lnz, 20);
|
||||
mb->nz_ = out_t_nz;
|
||||
left_mb->nz_ = out_l_nz;
|
||||
|
||||
dec->non_zero_ac_ = non_zero_ac;
|
||||
dec->non_zero_ = non_zero_ac | non_zero_dc;
|
||||
mb->skip_ = !dec->non_zero_;
|
||||
block->non_zero_y_ = non_zero_y;
|
||||
block->non_zero_uv_ = non_zero_uv;
|
||||
|
||||
// We look at the mode-code of each block and check if some blocks have less
|
||||
// than three non-zero coeffs (code < 2). This is to avoid dithering flat and
|
||||
// empty blocks.
|
||||
block->dither_ = (non_zero_uv & 0xaaaa) ? 0 : q->dither_;
|
||||
|
||||
return !(non_zero_y | non_zero_uv); // will be used for further optimization
|
||||
}
|
||||
#undef PACK
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main loop
|
||||
|
||||
int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
VP8MB* const info = dec->mb_info_ + dec->mb_x_;
|
||||
VP8MB* const mb = dec->mb_info_ + dec->mb_x_;
|
||||
VP8MBData* const block = dec->mb_data_ + dec->mb_x_;
|
||||
int skip = dec->use_skip_proba_ ? block->skip_ : 0;
|
||||
|
||||
// Note: we don't save segment map (yet), as we don't expect
|
||||
// to decode more than 1 keyframe.
|
||||
if (dec->segment_hdr_.update_map_) {
|
||||
// Hardcoded tree parsing
|
||||
dec->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) ?
|
||||
VP8GetBit(br, dec->proba_.segments_[1]) :
|
||||
2 + VP8GetBit(br, dec->proba_.segments_[2]);
|
||||
}
|
||||
info->skip_ = dec->use_skip_proba_ ? VP8GetBit(br, dec->skip_p_) : 0;
|
||||
|
||||
VP8ParseIntraMode(br, dec);
|
||||
if (br->eof_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!info->skip_) {
|
||||
ParseResiduals(dec, info, token_br);
|
||||
if (!skip) {
|
||||
skip = ParseResiduals(dec, mb, token_br);
|
||||
} else {
|
||||
left->nz_ = info->nz_ = 0;
|
||||
if (!dec->is_i4x4_) {
|
||||
left->dc_nz_ = info->dc_nz_ = 0;
|
||||
left->nz_ = mb->nz_ = 0;
|
||||
if (!block->is_i4x4_) {
|
||||
left->nz_dc_ = mb->nz_dc_ = 0;
|
||||
}
|
||||
dec->non_zero_ = 0;
|
||||
dec->non_zero_ac_ = 0;
|
||||
block->non_zero_y_ = 0;
|
||||
block->non_zero_uv_ = 0;
|
||||
block->dither_ = 0;
|
||||
}
|
||||
|
||||
return (!token_br->eof_);
|
||||
if (dec->filter_type_ > 0) { // store filter info
|
||||
VP8FInfo* const finfo = dec->f_info_ + dec->mb_x_;
|
||||
*finfo = dec->fstrengths_[block->segment_][block->is_i4x4_];
|
||||
finfo->f_inner_ |= !skip;
|
||||
}
|
||||
|
||||
return !token_br->eof_;
|
||||
}
|
||||
|
||||
void VP8InitScanline(VP8Decoder* const dec) {
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
left->nz_ = 0;
|
||||
left->dc_nz_ = 0;
|
||||
left->nz_dc_ = 0;
|
||||
memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
|
||||
dec->filter_row_ =
|
||||
(dec->filter_type_ > 0) &&
|
||||
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
|
||||
dec->mb_x_ = 0;
|
||||
}
|
||||
|
||||
static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
|
||||
for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
|
||||
// Parse bitstream for this row.
|
||||
VP8BitReader* const token_br =
|
||||
&dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
|
||||
VP8InitScanline(dec);
|
||||
for (dec->mb_x_ = 0; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
|
||||
if (!VP8ParseIntraModeRow(&dec->br_, dec)) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Premature end-of-partition0 encountered.");
|
||||
}
|
||||
for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) {
|
||||
if (!VP8DecodeMB(dec, token_br)) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Premature end-of-file encountered.");
|
||||
}
|
||||
VP8ReconstructBlock(dec);
|
||||
|
||||
// Store data and save block's filtering params
|
||||
VP8StoreBlock(dec);
|
||||
}
|
||||
VP8InitScanline(dec); // Prepare for next scanline
|
||||
|
||||
// Reconstruct, filter and emit the row.
|
||||
if (!VP8ProcessRow(dec, io)) {
|
||||
return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted.");
|
||||
}
|
||||
}
|
||||
if (dec->use_threads_ && !WebPWorkerSync(&dec->worker_)) {
|
||||
return 0;
|
||||
if (dec->mt_method_ > 0) {
|
||||
if (!WebPGetWorkerInterface()->Sync(&dec->worker_)) return 0;
|
||||
}
|
||||
|
||||
// Finish
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
if (!dec->update_proba_) {
|
||||
dec->proba_ = dec->proba_saved_;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
if (dec->layer_data_size_ > 0) {
|
||||
if (!VP8DecodeLayer(dec)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -768,12 +648,10 @@ void VP8Clear(VP8Decoder* const dec) {
|
||||
if (dec == NULL) {
|
||||
return;
|
||||
}
|
||||
if (dec->use_threads_) {
|
||||
WebPWorkerEnd(&dec->worker_);
|
||||
}
|
||||
if (dec->mem_) {
|
||||
free(dec->mem_);
|
||||
}
|
||||
WebPGetWorkerInterface()->End(&dec->worker_);
|
||||
ALPHDelete(dec->alph_dec_);
|
||||
dec->alph_dec_ = NULL;
|
||||
WebPSafeFree(dec->mem_);
|
||||
dec->mem_ = NULL;
|
||||
dec->mem_size_ = 0;
|
||||
memset(&dec->br_, 0, sizeof(dec->br_));
|
||||
@ -782,6 +660,3 @@ void VP8Clear(VP8Decoder* const dec) {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// VP8 decoder: internal header.
|
||||
@ -13,12 +15,14 @@
|
||||
#define WEBP_DEC_VP8I_H_
|
||||
|
||||
#include <string.h> // for memcpy()
|
||||
#include "./common.h"
|
||||
#include "./vp8li.h"
|
||||
#include "../utils/bit_reader.h"
|
||||
#include "../utils/random.h"
|
||||
#include "../utils/thread.h"
|
||||
#include "../dsp/dsp.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -27,48 +31,10 @@ extern "C" {
|
||||
|
||||
// version numbers
|
||||
#define DEC_MAJ_VERSION 0
|
||||
#define DEC_MIN_VERSION 2
|
||||
#define DEC_REV_VERSION 0
|
||||
#define DEC_MIN_VERSION 4
|
||||
#define DEC_REV_VERSION 4
|
||||
|
||||
#define ONLY_KEYFRAME_CODE // to remove any code related to P-Frames
|
||||
|
||||
// intra prediction modes
|
||||
enum { B_DC_PRED = 0, // 4x4 modes
|
||||
B_TM_PRED,
|
||||
B_VE_PRED,
|
||||
B_HE_PRED,
|
||||
B_RD_PRED,
|
||||
B_VR_PRED,
|
||||
B_LD_PRED,
|
||||
B_VL_PRED,
|
||||
B_HD_PRED,
|
||||
B_HU_PRED,
|
||||
NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
|
||||
|
||||
// Luma16 or UV modes
|
||||
DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
|
||||
H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
|
||||
B_PRED = NUM_BMODES, // refined I4x4 mode
|
||||
|
||||
// special modes
|
||||
B_DC_PRED_NOTOP = 4,
|
||||
B_DC_PRED_NOLEFT = 5,
|
||||
B_DC_PRED_NOTOPLEFT = 6,
|
||||
NUM_B_DC_MODES = 7 };
|
||||
|
||||
enum { MB_FEATURE_TREE_PROBS = 3,
|
||||
NUM_MB_SEGMENTS = 4,
|
||||
NUM_REF_LF_DELTAS = 4,
|
||||
NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
|
||||
MAX_NUM_PARTITIONS = 8,
|
||||
// Probabilities
|
||||
NUM_TYPES = 4,
|
||||
NUM_BANDS = 8,
|
||||
NUM_CTX = 3,
|
||||
NUM_PROBAS = 11,
|
||||
NUM_MV_PROBAS = 19 };
|
||||
|
||||
// YUV-cache parameters.
|
||||
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
|
||||
// Constraints are: We need to store one 16x16 block of luma samples (y),
|
||||
// and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned,
|
||||
// in order to be SIMD-friendly. We also need to store the top, left and
|
||||
@ -90,14 +56,15 @@ enum { MB_FEATURE_TREE_PROBS = 3,
|
||||
// 'y' = y-samples 'u' = u-samples 'v' = u-samples
|
||||
// '|' = left sample, '-' = top sample, '+' = top-left sample
|
||||
// 't' = extra top-right sample for 4x4 modes
|
||||
// With this layout, BPS (=Bytes Per Scan-line) is one cacheline size.
|
||||
#define BPS 32 // this is the common stride used by yuv[]
|
||||
#define YUV_SIZE (BPS * 17 + BPS * 9)
|
||||
#define Y_SIZE (BPS * 17)
|
||||
#define Y_OFF (BPS * 1 + 8)
|
||||
#define U_OFF (Y_OFF + BPS * 16 + BPS)
|
||||
#define V_OFF (U_OFF + 16)
|
||||
|
||||
// minimal width under which lossy multi-threading is always disabled
|
||||
#define MIN_WIDTH_FOR_THREADS 512
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Headers
|
||||
|
||||
@ -126,15 +93,19 @@ typedef struct {
|
||||
int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments
|
||||
} VP8SegmentHeader;
|
||||
|
||||
// probas associated to one of the contexts
|
||||
typedef uint8_t VP8ProbaArray[NUM_PROBAS];
|
||||
|
||||
typedef struct { // all the probas associated to one band
|
||||
VP8ProbaArray probas_[NUM_CTX];
|
||||
} VP8BandProbas;
|
||||
|
||||
// Struct collecting all frame-persistent probabilities.
|
||||
typedef struct {
|
||||
uint8_t segments_[MB_FEATURE_TREE_PROBS];
|
||||
// Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
|
||||
uint8_t coeffs_[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
uint8_t ymode_[4], uvmode_[3];
|
||||
uint8_t mv_[2][NUM_MV_PROBAS];
|
||||
#endif
|
||||
VP8BandProbas bands_[NUM_TYPES][NUM_BANDS];
|
||||
const VP8BandProbas* bands_ptr_[NUM_TYPES][16 + 1];
|
||||
} VP8Proba;
|
||||
|
||||
// Filter parameters
|
||||
@ -151,32 +122,61 @@ typedef struct {
|
||||
// Informations about the macroblocks.
|
||||
|
||||
typedef struct { // filter specs
|
||||
unsigned int f_level_:6; // filter strength: 0..63
|
||||
unsigned int f_ilevel_:6; // inner limit: 1..63
|
||||
unsigned int f_inner_:1; // do inner filtering?
|
||||
uint8_t f_limit_; // filter limit in [3..189], or 0 if no filtering
|
||||
uint8_t f_ilevel_; // inner limit in [1..63]
|
||||
uint8_t f_inner_; // do inner filtering?
|
||||
uint8_t hev_thresh_; // high edge variance threshold in [0..2]
|
||||
} VP8FInfo;
|
||||
|
||||
typedef struct { // used for syntax-parsing
|
||||
unsigned int nz_; // non-zero AC/DC coeffs
|
||||
unsigned int dc_nz_:1; // non-zero DC coeffs
|
||||
unsigned int skip_:1; // block type
|
||||
typedef struct { // Top/Left Contexts used for syntax-parsing
|
||||
uint8_t nz_; // non-zero AC/DC coeffs (4bit for luma + 4bit for chroma)
|
||||
uint8_t nz_dc_; // non-zero DC coeff (1bit)
|
||||
} VP8MB;
|
||||
|
||||
// Dequantization matrices
|
||||
typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower).
|
||||
typedef struct {
|
||||
quant_t y1_mat_, y2_mat_, uv_mat_;
|
||||
|
||||
int uv_quant_; // U/V quantizer value
|
||||
int dither_; // dithering amplitude (0 = off, max=255)
|
||||
} VP8QuantMatrix;
|
||||
|
||||
// Data needed to reconstruct a macroblock
|
||||
typedef struct {
|
||||
int16_t coeffs_[384]; // 384 coeffs = (16+4+4) * 4*4
|
||||
uint8_t is_i4x4_; // true if intra4x4
|
||||
uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
|
||||
uint8_t uvmode_; // chroma prediction mode
|
||||
// bit-wise info about the content of each sub-4x4 blocks (in decoding order).
|
||||
// Each of the 4x4 blocks for y/u/v is associated with a 2b code according to:
|
||||
// code=0 -> no coefficient
|
||||
// code=1 -> only DC
|
||||
// code=2 -> first three coefficients are non-zero
|
||||
// code=3 -> more than three coefficients are non-zero
|
||||
// This allows to call specialized transform functions.
|
||||
uint32_t non_zero_y_;
|
||||
uint32_t non_zero_uv_;
|
||||
uint8_t dither_; // local dithering strength (deduced from non_zero_*)
|
||||
uint8_t skip_;
|
||||
uint8_t segment_;
|
||||
} VP8MBData;
|
||||
|
||||
// Persistent information needed by the parallel processing
|
||||
typedef struct {
|
||||
int id_; // cache row to process (in [0..2])
|
||||
int mb_y_; // macroblock position of the row
|
||||
int filter_row_; // true if row-filtering is needed
|
||||
VP8FInfo* f_info_; // filter strengths
|
||||
VP8Io io_; // copy of the VP8Io to pass to put()
|
||||
int id_; // cache row to process (in [0..2])
|
||||
int mb_y_; // macroblock position of the row
|
||||
int filter_row_; // true if row-filtering is needed
|
||||
VP8FInfo* f_info_; // filter strengths (swapped with dec->f_info_)
|
||||
VP8MBData* mb_data_; // reconstruction data (swapped with dec->mb_data_)
|
||||
VP8Io io_; // copy of the VP8Io to pass to put()
|
||||
} VP8ThreadContext;
|
||||
|
||||
// Saved top samples, per macroblock. Fits into a cache-line.
|
||||
typedef struct {
|
||||
uint8_t y[16], u[8], v[8];
|
||||
} VP8TopSamples;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8Decoder: the main opaque structure handed over to user
|
||||
|
||||
@ -196,7 +196,8 @@ struct VP8Decoder {
|
||||
|
||||
// Worker
|
||||
WebPWorker worker_;
|
||||
int use_threads_; // use multi-thread
|
||||
int mt_method_; // multi-thread method: 0=off, 1=[parse+recon][filter]
|
||||
// 2=[parse][recon+filter]
|
||||
int cache_id_; // current cache row
|
||||
int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3)
|
||||
VP8ThreadContext thread_ctx_; // Thread context
|
||||
@ -213,12 +214,9 @@ struct VP8Decoder {
|
||||
// per-partition boolean decoders.
|
||||
VP8BitReader parts_[MAX_NUM_PARTITIONS];
|
||||
|
||||
// buffer refresh flags
|
||||
// bit 0: refresh Gold, bit 1: refresh Alt
|
||||
// bit 2-3: copy to Gold, bit 4-5: copy to Alt
|
||||
// bit 6: Gold sign bias, bit 7: Alt sign bias
|
||||
// bit 8: refresh last frame
|
||||
uint32_t buffer_flags_;
|
||||
// Dithering strength, deduced from decoding options
|
||||
int dither_; // whether to use dithering or not
|
||||
VP8Random dithering_rg_; // random generator for dithering
|
||||
|
||||
// dequantization (one set of DC/AC dequant factor per segment)
|
||||
VP8QuantMatrix dqm_[NUM_MB_SEGMENTS];
|
||||
@ -227,24 +225,18 @@ struct VP8Decoder {
|
||||
VP8Proba proba_;
|
||||
int use_skip_proba_;
|
||||
uint8_t skip_p_;
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
uint8_t intra_p_, last_p_, golden_p_;
|
||||
VP8Proba proba_saved_;
|
||||
int update_proba_;
|
||||
#endif
|
||||
|
||||
// Boundary data cache and persistent buffers.
|
||||
uint8_t* intra_t_; // top intra modes values: 4 * mb_w_
|
||||
uint8_t intra_l_[4]; // left intra modes values
|
||||
uint8_t* y_t_; // top luma samples: 16 * mb_w_
|
||||
uint8_t* u_t_, *v_t_; // top u/v samples: 8 * mb_w_ each
|
||||
uint8_t* intra_t_; // top intra modes values: 4 * mb_w_
|
||||
uint8_t intra_l_[4]; // left intra modes values
|
||||
|
||||
VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1)
|
||||
VP8FInfo* f_info_; // filter strength info
|
||||
uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE)
|
||||
int16_t* coeffs_; // 384 coeffs = (16+8+8) * 4*4
|
||||
VP8TopSamples* yuv_t_; // top y/u/v samples
|
||||
|
||||
uint8_t* cache_y_; // macroblock row for storing unfiltered samples
|
||||
VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1)
|
||||
VP8FInfo* f_info_; // filter strength info
|
||||
uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE)
|
||||
|
||||
uint8_t* cache_y_; // macroblock row for storing unfiltered samples
|
||||
uint8_t* cache_u_;
|
||||
uint8_t* cache_v_;
|
||||
int cache_y_stride_;
|
||||
@ -256,31 +248,19 @@ struct VP8Decoder {
|
||||
|
||||
// Per macroblock non-persistent infos.
|
||||
int mb_x_, mb_y_; // current position, in macroblock units
|
||||
uint8_t is_i4x4_; // true if intra4x4
|
||||
uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
|
||||
uint8_t uvmode_; // chroma prediction mode
|
||||
uint8_t segment_; // block's segment
|
||||
|
||||
// bit-wise info about the content of each sub-4x4 blocks: there are 16 bits
|
||||
// for luma (bits #0->#15), then 4 bits for chroma-u (#16->#19) and 4 bits for
|
||||
// chroma-v (#20->#23), each corresponding to one 4x4 block in decoding order.
|
||||
// If the bit is set, the 4x4 block contains some non-zero coefficients.
|
||||
uint32_t non_zero_;
|
||||
uint32_t non_zero_ac_;
|
||||
VP8MBData* mb_data_; // parsed reconstruction data
|
||||
|
||||
// Filtering side-info
|
||||
int filter_type_; // 0=off, 1=simple, 2=complex
|
||||
int filter_row_; // per-row flag
|
||||
uint8_t filter_levels_[NUM_MB_SEGMENTS]; // precalculated per-segment
|
||||
int filter_type_; // 0=off, 1=simple, 2=complex
|
||||
VP8FInfo fstrengths_[NUM_MB_SEGMENTS][2]; // precalculated per-segment/type
|
||||
|
||||
// extensions
|
||||
const uint8_t* alpha_data_; // compressed alpha data (if present)
|
||||
// Alpha
|
||||
struct ALPHDecoder* alph_dec_; // alpha-plane decoder object
|
||||
const uint8_t* alpha_data_; // compressed alpha data (if present)
|
||||
size_t alpha_data_size_;
|
||||
uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
|
||||
|
||||
int layer_colorspace_;
|
||||
const uint8_t* layer_data_; // compressed layer data (if present)
|
||||
size_t layer_data_size_;
|
||||
int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_
|
||||
uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
|
||||
int alpha_dithering_; // derived from decoding options (0=off, 100=full).
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -293,15 +273,14 @@ int VP8SetError(VP8Decoder* const dec,
|
||||
// in tree.c
|
||||
void VP8ResetProba(VP8Proba* const proba);
|
||||
void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec);
|
||||
void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec);
|
||||
// parses one row of intra mode data in partition 0, returns !eof
|
||||
int VP8ParseIntraModeRow(VP8BitReader* const br, VP8Decoder* const dec);
|
||||
|
||||
// in quant.c
|
||||
void VP8ParseQuant(VP8Decoder* const dec);
|
||||
|
||||
// in frame.c
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io);
|
||||
// Predict a block and add residual
|
||||
void VP8ReconstructBlock(VP8Decoder* const dec);
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Call io->setup() and finish setting up scan parameters.
|
||||
// After this call returns, one must always call VP8ExitCritical() with the
|
||||
// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK
|
||||
@ -310,10 +289,16 @@ VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Must always be called in pair with VP8EnterCritical().
|
||||
// Returns false in case of error.
|
||||
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Process the last decoded row (filtering + output)
|
||||
// Return the multi-threading method to use (0=off), depending
|
||||
// on options and bitstream size. Only for lossy decoding.
|
||||
int VP8GetThreadMethod(const WebPDecoderOptions* const options,
|
||||
const WebPHeaderStructure* const headers,
|
||||
int width, int height);
|
||||
// Initialize dithering post-process if needed.
|
||||
void VP8InitDithering(const WebPDecoderOptions* const options,
|
||||
VP8Decoder* const dec);
|
||||
// Process the last decoded row (filtering + output).
|
||||
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Store a block, along with filtering params
|
||||
void VP8StoreBlock(VP8Decoder* const dec);
|
||||
// To be called at the start of a new scanline, to initialize predictors.
|
||||
void VP8InitScanline(VP8Decoder* const dec);
|
||||
// Decode one macroblock. Returns false if there is not enough data.
|
||||
@ -323,12 +308,9 @@ int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br);
|
||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
int row, int num_rows);
|
||||
|
||||
// in layer.c
|
||||
int VP8DecodeLayer(VP8Decoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Lossless decoder: internal header.
|
||||
@ -18,9 +20,8 @@
|
||||
#include "../utils/bit_reader.h"
|
||||
#include "../utils/color_cache.h"
|
||||
#include "../utils/huffman.h"
|
||||
#include "../format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -39,13 +40,10 @@ struct VP8LTransform {
|
||||
uint32_t *data_; // transform data.
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
HuffmanTree htrees_[HUFFMAN_CODES_PER_META_CODE];
|
||||
} HTreeGroup;
|
||||
|
||||
typedef struct {
|
||||
int color_cache_size_;
|
||||
VP8LColorCache color_cache_;
|
||||
VP8LColorCache saved_color_cache_; // for incremental
|
||||
|
||||
int huffman_mask_;
|
||||
int huffman_subsample_bits_;
|
||||
@ -53,24 +51,32 @@ typedef struct {
|
||||
uint32_t *huffman_image_;
|
||||
int num_htree_groups_;
|
||||
HTreeGroup *htree_groups_;
|
||||
HuffmanCode *huffman_tables_;
|
||||
} VP8LMetadata;
|
||||
|
||||
typedef struct {
|
||||
typedef struct VP8LDecoder VP8LDecoder;
|
||||
struct VP8LDecoder {
|
||||
VP8StatusCode status_;
|
||||
VP8LDecodeState action_;
|
||||
VP8LDecodeState state_;
|
||||
VP8Io *io_;
|
||||
|
||||
const WebPDecBuffer *output_; // shortcut to io->opaque->output
|
||||
|
||||
uint32_t *argb_; // Internal data: always in BGRA color mode.
|
||||
uint32_t *pixels_; // Internal data: either uint8_t* for alpha
|
||||
// or uint32_t* for BGRA.
|
||||
uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
|
||||
|
||||
VP8LBitReader br_;
|
||||
int incremental_; // if true, incremental decoding is expected
|
||||
VP8LBitReader saved_br_; // note: could be local variables too
|
||||
int saved_last_pixel_;
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
int last_row_; // last input row decoded so far.
|
||||
int last_pixel_; // last pixel decoded so far. However, it may
|
||||
// not be transformed, scaled and
|
||||
// color-converted yet.
|
||||
int last_out_row_; // last row output so far.
|
||||
|
||||
VP8LMetadata hdr_;
|
||||
@ -82,18 +88,27 @@ typedef struct {
|
||||
|
||||
uint8_t *rescaler_memory; // Working memory for rescaling work.
|
||||
WebPRescaler *rescaler; // Common rescaler for all channels.
|
||||
} VP8LDecoder;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// internal functions. Not public.
|
||||
|
||||
struct ALPHDecoder; // Defined in dec/alphai.h.
|
||||
|
||||
// in vp8l.c
|
||||
|
||||
// Decodes a raw image stream (without header) and store the alpha data
|
||||
// into *output, which must be of size width x height. Returns false in case
|
||||
// of error.
|
||||
int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data,
|
||||
size_t data_size, uint8_t* const output);
|
||||
// Decodes image header for alpha data stored using lossless compression.
|
||||
// Returns false in case of error.
|
||||
int VP8LDecodeAlphaHeader(struct ALPHDecoder* const alph_dec,
|
||||
const uint8_t* const data, size_t data_size,
|
||||
uint8_t* const output);
|
||||
|
||||
// Decodes *at least* 'last_row' rows of alpha. If some of the initial rows are
|
||||
// already decoded in previous call(s), it will resume decoding from where it
|
||||
// was paused.
|
||||
// Returns false in case of bitstream error.
|
||||
int VP8LDecodeAlphaImageStream(struct ALPHDecoder* const alph_dec,
|
||||
int last_row);
|
||||
|
||||
// Allocates and initialize a new lossless decoder instance.
|
||||
VP8LDecoder* VP8LNew(void);
|
||||
@ -114,7 +129,7 @@ void VP8LDelete(VP8LDecoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Main decoding functions for WEBP images.
|
||||
@ -14,11 +16,8 @@
|
||||
#include "./vp8i.h"
|
||||
#include "./vp8li.h"
|
||||
#include "./webpi.h"
|
||||
#include "../format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "../utils/utils.h"
|
||||
#include "../webp/mux_types.h" // ALPHA_FLAG
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RIFF layout is:
|
||||
@ -40,27 +39,20 @@ extern "C" {
|
||||
// 20..23 VP8X flags bit-map corresponding to the chunk-types present.
|
||||
// 24..26 Width of the Canvas Image.
|
||||
// 27..29 Height of the Canvas Image.
|
||||
// There can be extra chunks after the "VP8X" chunk (ICCP, TILE, FRM, VP8,
|
||||
// META ...)
|
||||
// There can be extra chunks after the "VP8X" chunk (ICCP, FRGM, ANMF, VP8,
|
||||
// VP8L, XMP, EXIF ...)
|
||||
// All sizes are in little-endian order.
|
||||
// Note: chunk data size must be padded to multiple of 2 when written.
|
||||
|
||||
static WEBP_INLINE uint32_t get_le24(const uint8_t* const data) {
|
||||
return data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) {
|
||||
return (uint32_t)get_le24(data) | (data[3] << 24);
|
||||
}
|
||||
|
||||
// Validates the RIFF container (if detected) and skips over it.
|
||||
// If a RIFF container is detected,
|
||||
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and
|
||||
// VP8_STATUS_OK otherwise.
|
||||
// If a RIFF container is detected, returns:
|
||||
// VP8_STATUS_BITSTREAM_ERROR for invalid header,
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA for truncated data if have_all_data is true,
|
||||
// and VP8_STATUS_OK otherwise.
|
||||
// In case there are not enough bytes (partial RIFF container), return 0 for
|
||||
// *riff_size. Else return the RIFF size extracted from the header.
|
||||
static VP8StatusCode ParseRIFF(const uint8_t** const data,
|
||||
size_t* const data_size,
|
||||
size_t* const data_size, int have_all_data,
|
||||
size_t* const riff_size) {
|
||||
assert(data != NULL);
|
||||
assert(data_size != NULL);
|
||||
@ -71,11 +63,17 @@ static VP8StatusCode ParseRIFF(const uint8_t** const data,
|
||||
if (memcmp(*data + 8, "WEBP", TAG_SIZE)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature.
|
||||
} else {
|
||||
const uint32_t size = get_le32(*data + TAG_SIZE);
|
||||
const uint32_t size = GetLE32(*data + TAG_SIZE);
|
||||
// Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
|
||||
if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (size > MAX_CHUNK_PAYLOAD) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream.
|
||||
}
|
||||
// We have a RIFF container. Skip it.
|
||||
*riff_size = size;
|
||||
*data += RIFF_HEADER_SIZE;
|
||||
@ -111,7 +109,7 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data,
|
||||
if (!memcmp(*data, "VP8X", TAG_SIZE)) {
|
||||
int width, height;
|
||||
uint32_t flags;
|
||||
const uint32_t chunk_size = get_le32(*data + TAG_SIZE);
|
||||
const uint32_t chunk_size = GetLE32(*data + TAG_SIZE);
|
||||
if (chunk_size != VP8X_CHUNK_SIZE) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size.
|
||||
}
|
||||
@ -120,9 +118,9 @@ static VP8StatusCode ParseVP8X(const uint8_t** const data,
|
||||
if (*data_size < vp8x_size) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
|
||||
}
|
||||
flags = get_le32(*data + 8);
|
||||
width = 1 + get_le24(*data + 12);
|
||||
height = 1 + get_le24(*data + 15);
|
||||
flags = GetLE32(*data + 8);
|
||||
width = 1 + GetLE24(*data + 12);
|
||||
height = 1 + GetLE24(*data + 15);
|
||||
if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // image is too large
|
||||
}
|
||||
@ -176,7 +174,10 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
|
||||
chunk_size = get_le32(buf + TAG_SIZE);
|
||||
chunk_size = GetLE32(buf + TAG_SIZE);
|
||||
if (chunk_size > MAX_CHUNK_PAYLOAD) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
|
||||
}
|
||||
// For odd-sized chunk-payload, there's one byte padding at the end.
|
||||
disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1;
|
||||
total_size += disk_chunk_size;
|
||||
@ -186,6 +187,15 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
|
||||
}
|
||||
|
||||
// Start of a (possibly incomplete) VP8/VP8L chunk implies that we have
|
||||
// parsed all the optional chunks.
|
||||
// Note: This check must occur before the check 'buf_size < disk_chunk_size'
|
||||
// below to allow incomplete VP8/VP8L chunks.
|
||||
if (!memcmp(buf, "VP8 ", TAG_SIZE) ||
|
||||
!memcmp(buf, "VP8L", TAG_SIZE)) {
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
if (buf_size < disk_chunk_size) { // Insufficient data.
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
@ -193,9 +203,6 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header.
|
||||
*alpha_data = buf + CHUNK_HEADER_SIZE;
|
||||
*alpha_size = chunk_size;
|
||||
} else if (!memcmp(buf, "VP8 ", TAG_SIZE) ||
|
||||
!memcmp(buf, "VP8L", TAG_SIZE)) { // A valid VP8/VP8L header.
|
||||
return VP8_STATUS_OK; // Found.
|
||||
}
|
||||
|
||||
// We have a full and valid chunk; skip it.
|
||||
@ -213,9 +220,8 @@ static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
// extracted from the VP8/VP8L chunk header.
|
||||
// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
|
||||
static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
|
||||
size_t* const data_size,
|
||||
size_t riff_size,
|
||||
size_t* const chunk_size,
|
||||
size_t* const data_size, int have_all_data,
|
||||
size_t riff_size, size_t* const chunk_size,
|
||||
int* const is_lossless) {
|
||||
const uint8_t* const data = *data_ptr;
|
||||
const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE);
|
||||
@ -234,10 +240,13 @@ static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
|
||||
|
||||
if (is_vp8 || is_vp8l) {
|
||||
// Bitstream contains VP8/VP8L header.
|
||||
const uint32_t size = get_le32(data + TAG_SIZE);
|
||||
const uint32_t size = GetLE32(data + TAG_SIZE);
|
||||
if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information.
|
||||
}
|
||||
if (have_all_data && (size > *data_size - CHUNK_HEADER_SIZE)) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA; // Truncated bitstream.
|
||||
}
|
||||
// Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
|
||||
*chunk_size = size;
|
||||
*data_ptr += CHUNK_HEADER_SIZE;
|
||||
@ -270,9 +279,19 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
int* const width,
|
||||
int* const height,
|
||||
int* const has_alpha,
|
||||
int* const has_animation,
|
||||
int* const format,
|
||||
WebPHeaderStructure* const headers) {
|
||||
int canvas_width = 0;
|
||||
int canvas_height = 0;
|
||||
int image_width = 0;
|
||||
int image_height = 0;
|
||||
int found_riff = 0;
|
||||
int found_vp8x = 0;
|
||||
int animation_present = 0;
|
||||
int fragments_present = 0;
|
||||
const int have_all_data = (headers != NULL) ? headers->have_all_data : 0;
|
||||
|
||||
VP8StatusCode status;
|
||||
WebPHeaderStructure hdrs;
|
||||
|
||||
@ -284,7 +303,7 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
hdrs.data_size = data_size;
|
||||
|
||||
// Skip over RIFF header.
|
||||
status = ParseRIFF(&data, &data_size, &hdrs.riff_size);
|
||||
status = ParseRIFF(&data, &data_size, have_all_data, &hdrs.riff_size);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong RIFF header / insufficient data.
|
||||
}
|
||||
@ -293,22 +312,35 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
// Skip over VP8X.
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags);
|
||||
status = ParseVP8X(&data, &data_size, &found_vp8x,
|
||||
&canvas_width, &canvas_height, &flags);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong VP8X / insufficient data.
|
||||
}
|
||||
animation_present = !!(flags & ANIMATION_FLAG);
|
||||
fragments_present = !!(flags & FRAGMENTS_FLAG);
|
||||
if (!found_riff && found_vp8x) {
|
||||
// Note: This restriction may be removed in the future, if it becomes
|
||||
// necessary to send VP8X chunk to the decoder.
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG_BIT);
|
||||
if (found_vp8x && headers == NULL) {
|
||||
return VP8_STATUS_OK; // Return features from VP8X header.
|
||||
if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG);
|
||||
if (has_animation != NULL) *has_animation = animation_present;
|
||||
if (format != NULL) *format = 0; // default = undefined
|
||||
|
||||
image_width = canvas_width;
|
||||
image_height = canvas_height;
|
||||
if (found_vp8x && (animation_present || fragments_present) &&
|
||||
headers == NULL) {
|
||||
status = VP8_STATUS_OK;
|
||||
goto ReturnWidthHeight; // Just return features from VP8X header.
|
||||
}
|
||||
}
|
||||
|
||||
if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
if (data_size < TAG_SIZE) {
|
||||
status = VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
goto ReturnWidthHeight;
|
||||
}
|
||||
|
||||
// Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
|
||||
if ((found_riff && found_vp8x) ||
|
||||
@ -316,43 +348,49 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
|
||||
&hdrs.alpha_data, &hdrs.alpha_data_size);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Found an invalid chunk size / insufficient data.
|
||||
goto ReturnWidthHeight; // Invalid chunk size / insufficient data.
|
||||
}
|
||||
}
|
||||
|
||||
// Skip over VP8/VP8L header.
|
||||
status = ParseVP8Header(&data, &data_size, hdrs.riff_size,
|
||||
status = ParseVP8Header(&data, &data_size, have_all_data, hdrs.riff_size,
|
||||
&hdrs.compressed_size, &hdrs.is_lossless);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong VP8/VP8L chunk-header / insufficient data.
|
||||
goto ReturnWidthHeight; // Wrong VP8/VP8L chunk-header / insufficient data.
|
||||
}
|
||||
if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
|
||||
if (format != NULL && !(animation_present || fragments_present)) {
|
||||
*format = hdrs.is_lossless ? 2 : 1;
|
||||
}
|
||||
|
||||
if (!hdrs.is_lossless) {
|
||||
if (data_size < VP8_FRAME_HEADER_SIZE) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
status = VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
goto ReturnWidthHeight;
|
||||
}
|
||||
// Validates raw VP8 data.
|
||||
if (!VP8GetInfo(data, data_size,
|
||||
(uint32_t)hdrs.compressed_size, width, height)) {
|
||||
if (!VP8GetInfo(data, data_size, (uint32_t)hdrs.compressed_size,
|
||||
&image_width, &image_height)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (data_size < VP8L_FRAME_HEADER_SIZE) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
status = VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
goto ReturnWidthHeight;
|
||||
}
|
||||
// Validates raw VP8L data.
|
||||
if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) {
|
||||
if (!VP8LGetInfo(data, data_size, &image_width, &image_height, has_alpha)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_alpha != NULL) {
|
||||
// If the data did not contain a VP8X/VP8L chunk the only definitive way
|
||||
// to set this is by looking for alpha data (from an ALPH chunk).
|
||||
*has_alpha |= (hdrs.alpha_data != NULL);
|
||||
// Validates image size coherency.
|
||||
if (found_vp8x) {
|
||||
if (canvas_width != image_width || canvas_height != image_height) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
}
|
||||
if (headers != NULL) {
|
||||
*headers = hdrs;
|
||||
@ -360,21 +398,44 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
|
||||
assert(headers->offset == headers->data_size - data_size);
|
||||
}
|
||||
return VP8_STATUS_OK; // Return features from VP8 header.
|
||||
ReturnWidthHeight:
|
||||
if (status == VP8_STATUS_OK ||
|
||||
(status == VP8_STATUS_NOT_ENOUGH_DATA && found_vp8x && headers == NULL)) {
|
||||
if (has_alpha != NULL) {
|
||||
// If the data did not contain a VP8X/VP8L chunk the only definitive way
|
||||
// to set this is by looking for alpha data (from an ALPH chunk).
|
||||
*has_alpha |= (hdrs.alpha_data != NULL);
|
||||
}
|
||||
if (width != NULL) *width = image_width;
|
||||
if (height != NULL) *height = image_height;
|
||||
return VP8_STATUS_OK;
|
||||
} else {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
|
||||
VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
|
||||
VP8StatusCode status;
|
||||
int has_animation = 0;
|
||||
assert(headers != NULL);
|
||||
// fill out headers, ignore width/height/has_alpha.
|
||||
return ParseHeadersInternal(headers->data, headers->data_size,
|
||||
NULL, NULL, NULL, headers);
|
||||
status = ParseHeadersInternal(headers->data, headers->data_size,
|
||||
NULL, NULL, NULL, &has_animation,
|
||||
NULL, headers);
|
||||
if (status == VP8_STATUS_OK || status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
// TODO(jzern): full support of animation frames will require API additions.
|
||||
if (has_animation) {
|
||||
status = VP8_STATUS_UNSUPPORTED_FEATURE;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecParams
|
||||
|
||||
void WebPResetDecParams(WebPDecParams* const params) {
|
||||
if (params) {
|
||||
if (params != NULL) {
|
||||
memset(params, 0, sizeof(*params));
|
||||
}
|
||||
}
|
||||
@ -391,6 +452,7 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
||||
|
||||
headers.data = data;
|
||||
headers.data_size = data_size;
|
||||
headers.have_all_data = 1;
|
||||
status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks.
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status;
|
||||
@ -407,11 +469,6 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
||||
if (dec == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
#ifdef WEBP_USE_THREAD
|
||||
dec->use_threads_ = params->options && (params->options->use_threads > 0);
|
||||
#else
|
||||
dec->use_threads_ = 0;
|
||||
#endif
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
|
||||
@ -423,6 +480,10 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
||||
status = WebPAllocateDecBuffer(io.width, io.height, params->options,
|
||||
params->output);
|
||||
if (status == VP8_STATUS_OK) { // Decode
|
||||
// This change must be done before calling VP8Decode()
|
||||
dec->mt_method_ = VP8GetThreadMethod(params->options, &headers,
|
||||
io.width, io.height);
|
||||
VP8InitDithering(params->options, dec);
|
||||
if (!VP8Decode(dec, &io)) {
|
||||
status = dec->status_;
|
||||
}
|
||||
@ -452,6 +513,10 @@ static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
||||
if (status != VP8_STATUS_OK) {
|
||||
WebPFreeDecBuffer(params->output);
|
||||
}
|
||||
|
||||
if (params->options != NULL && params->options->flip) {
|
||||
status = WebPFlipBuffer(params->output);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
@ -609,7 +674,6 @@ uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
|
||||
static void DefaultFeatures(WebPBitstreamFeatures* const features) {
|
||||
assert(features != NULL);
|
||||
memset(features, 0, sizeof(*features));
|
||||
features->bitstream_version = 0;
|
||||
}
|
||||
|
||||
static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
|
||||
@ -619,10 +683,11 @@ static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
|
||||
}
|
||||
DefaultFeatures(features);
|
||||
|
||||
// Only parse enough of the data to retrieve width/height/has_alpha.
|
||||
// Only parse enough of the data to retrieve the features.
|
||||
return ParseHeadersInternal(data, data_size,
|
||||
&features->width, &features->height,
|
||||
&features->has_alpha, NULL);
|
||||
&features->has_alpha, &features->has_animation,
|
||||
&features->format, NULL);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -666,19 +731,13 @@ int WebPInitDecoderConfigInternal(WebPDecoderConfig* config,
|
||||
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;
|
||||
return GetFeatures(data, data_size, features);
|
||||
}
|
||||
|
||||
VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
|
||||
@ -722,9 +781,9 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
h = options->crop_height;
|
||||
x = options->crop_left;
|
||||
y = options->crop_top;
|
||||
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 or YUV422
|
||||
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420
|
||||
x &= ~1;
|
||||
y &= ~1; // TODO(later): only for YUV420, not YUV422.
|
||||
y &= ~1;
|
||||
}
|
||||
if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
|
||||
return 0; // out of frame boundary error
|
||||
@ -740,11 +799,13 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
// Scaling
|
||||
io->use_scaling = (options != NULL) && (options->use_scaling > 0);
|
||||
if (io->use_scaling) {
|
||||
if (options->scaled_width <= 0 || options->scaled_height <= 0) {
|
||||
int scaled_width = options->scaled_width;
|
||||
int scaled_height = options->scaled_height;
|
||||
if (!WebPRescalerGetScaledDimensions(w, h, &scaled_width, &scaled_height)) {
|
||||
return 0;
|
||||
}
|
||||
io->scaled_width = options->scaled_width;
|
||||
io->scaled_height = options->scaled_height;
|
||||
io->scaled_width = scaled_width;
|
||||
io->scaled_height = scaled_height;
|
||||
}
|
||||
|
||||
// Filter
|
||||
@ -766,6 +827,3 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Internal header: WebP decoding parameters and custom IO on buffer
|
||||
@ -12,7 +14,7 @@
|
||||
#ifndef WEBP_DEC_WEBPI_H_
|
||||
#define WEBP_DEC_WEBPI_H_
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -24,7 +26,10 @@ extern "C" {
|
||||
|
||||
typedef struct WebPDecParams WebPDecParams;
|
||||
typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p);
|
||||
typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos);
|
||||
typedef int (*OutputAlphaFunc)(const VP8Io* const io, WebPDecParams* const p,
|
||||
int expected_num_out_lines);
|
||||
typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos,
|
||||
int max_out_lines);
|
||||
|
||||
struct WebPDecParams {
|
||||
WebPDecBuffer* output; // output buffer.
|
||||
@ -38,7 +43,7 @@ struct WebPDecParams {
|
||||
void* memory; // overall scratch memory for the output work.
|
||||
|
||||
OutputFunc emit; // output RGB or YUV samples
|
||||
OutputFunc emit_alpha; // output alpha channel
|
||||
OutputAlphaFunc emit_alpha; // output alpha channel
|
||||
OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values
|
||||
};
|
||||
|
||||
@ -52,6 +57,7 @@ void WebPResetDecParams(WebPDecParams* const params);
|
||||
typedef struct {
|
||||
const uint8_t* data; // input buffer
|
||||
size_t data_size; // input buffer size
|
||||
int have_all_data; // true if all data is known to be available
|
||||
size_t offset; // offset to main data chunk (VP8 or VP8L)
|
||||
const uint8_t* alpha_data; // points to alpha chunk (if present)
|
||||
size_t alpha_data_size; // alpha chunk size
|
||||
@ -61,10 +67,10 @@ typedef struct {
|
||||
} WebPHeaderStructure;
|
||||
|
||||
// Skips over all valid chunks prior to the first VP8/VP8L frame header.
|
||||
// Returns VP8_STATUS_OK on success,
|
||||
// VP8_STATUS_BITSTREAM_ERROR if an invalid header/chunk is found, and
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA if case of insufficient data.
|
||||
// In 'headers', compressed_size, offset, alpha_data, alpha_size and lossless
|
||||
// Returns: VP8_STATUS_OK, VP8_STATUS_BITSTREAM_ERROR (invalid header/chunk),
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA (partial input) or VP8_STATUS_UNSUPPORTED_FEATURE
|
||||
// in the case of non-decodable features (animation for instance).
|
||||
// In 'headers', compressed_size, offset, alpha_data, alpha_size, and lossless
|
||||
// fields are updated appropriately upon success.
|
||||
VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers);
|
||||
|
||||
@ -91,10 +97,15 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
// dimension / etc.). If *options is not NULL, also verify that the options'
|
||||
// parameters are valid and apply them to the width/height dimensions of the
|
||||
// output buffer. This takes cropping / scaling / rotation into account.
|
||||
// Also incorporates the options->flip flag to flip the buffer parameters if
|
||||
// needed.
|
||||
VP8StatusCode WebPAllocateDecBuffer(int width, int height,
|
||||
const WebPDecoderOptions* const options,
|
||||
WebPDecBuffer* const buffer);
|
||||
|
||||
// Flip buffer vertically by negating the various strides.
|
||||
VP8StatusCode WebPFlipBuffer(WebPDecBuffer* const buffer);
|
||||
|
||||
// Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the
|
||||
// memory (still held by 'src').
|
||||
void WebPCopyDecBuffer(const WebPDecBuffer* const src,
|
||||
@ -103,11 +114,9 @@ void WebPCopyDecBuffer(const WebPDecBuffer* const src,
|
||||
// Copy and transfer ownership from src to dst (beware of parameter order!)
|
||||
void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst);
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Main decoding functions for WebP images.
|
||||
@ -14,11 +16,23 @@
|
||||
|
||||
#include "./types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WEBP_DECODER_ABI_VERSION 0x0200 // MAJOR(8b) + MINOR(8b)
|
||||
#define WEBP_DECODER_ABI_VERSION 0x0207 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
// Note: forward declaring enumerations is not allowed in (strict) C and C++,
|
||||
// the types are left here for reference.
|
||||
// typedef enum VP8StatusCode VP8StatusCode;
|
||||
// typedef enum WEBP_CSP_MODE WEBP_CSP_MODE;
|
||||
typedef struct WebPRGBABuffer WebPRGBABuffer;
|
||||
typedef struct WebPYUVABuffer WebPYUVABuffer;
|
||||
typedef struct WebPDecBuffer WebPDecBuffer;
|
||||
typedef struct WebPIDecoder WebPIDecoder;
|
||||
typedef struct WebPBitstreamFeatures WebPBitstreamFeatures;
|
||||
typedef struct WebPDecoderOptions WebPDecoderOptions;
|
||||
typedef struct WebPDecoderConfig WebPDecoderConfig;
|
||||
|
||||
// Return the decoder's version number, packed in hexadecimal using 8bits for
|
||||
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||
@ -34,7 +48,7 @@ WEBP_EXTERN(int) WebPGetInfo(const uint8_t* data, size_t data_size,
|
||||
// Decodes WebP images pointed to by 'data' and returns RGBA samples, along
|
||||
// with the dimensions in *width and *height. The ordering of samples in
|
||||
// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
|
||||
// The returned pointer should be deleted calling free().
|
||||
// The returned pointer should be deleted calling WebPFree().
|
||||
// Returns NULL in case of error.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeRGBA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
@ -59,9 +73,9 @@ WEBP_EXTERN(uint8_t*) WebPDecodeBGR(const uint8_t* data, size_t data_size,
|
||||
|
||||
// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer
|
||||
// returned is the Y samples buffer. Upon return, *u and *v will point to
|
||||
// the U and V chroma data. These U and V buffers need NOT be free()'d,
|
||||
// unlike the returned Y luma one. The dimension of the U and V planes
|
||||
// are both (*width + 1) / 2 and (*height + 1)/ 2.
|
||||
// the U and V chroma data. These U and V buffers need NOT be passed to
|
||||
// WebPFree(), unlike the returned Y luma one. The dimension of the U and V
|
||||
// planes are both (*width + 1) / 2 and (*height + 1)/ 2.
|
||||
// Upon return, the Y buffer has a stride returned as '*stride', while U and V
|
||||
// have a common stride returned as '*uv_stride'.
|
||||
// Return NULL in case of error.
|
||||
@ -71,6 +85,9 @@ WEBP_EXTERN(uint8_t*) WebPDecodeYUV(const uint8_t* data, size_t data_size,
|
||||
uint8_t** u, uint8_t** v,
|
||||
int* stride, int* uv_stride);
|
||||
|
||||
// Releases memory returned by the WebPDecode*() functions above.
|
||||
WEBP_EXTERN(void) WebPFree(void* ptr);
|
||||
|
||||
// These five functions are variants of the above ones, that decode the image
|
||||
// directly into a pre-allocated buffer 'output_buffer'. The maximum storage
|
||||
// available in this buffer is indicated by 'output_buffer_size'. If this
|
||||
@ -118,20 +135,28 @@ WEBP_EXTERN(uint8_t*) WebPDecodeYUVInto(
|
||||
// Note: the naming describes the byte-ordering of packed samples in memory.
|
||||
// For instance, MODE_BGRA relates to samples ordered as B,G,R,A,B,G,R,A,...
|
||||
// Non-capital names (e.g.:MODE_Argb) relates to pre-multiplied RGB channels.
|
||||
// RGB-565 and RGBA-4444 are also endian-agnostic and byte-oriented.
|
||||
typedef enum { MODE_RGB = 0, MODE_RGBA = 1,
|
||||
MODE_BGR = 2, MODE_BGRA = 3,
|
||||
MODE_ARGB = 4, MODE_RGBA_4444 = 5,
|
||||
MODE_RGB_565 = 6,
|
||||
// RGB-premultiplied transparent modes (alpha value is preserved)
|
||||
MODE_rgbA = 7,
|
||||
MODE_bgrA = 8,
|
||||
MODE_Argb = 9,
|
||||
MODE_rgbA_4444 = 10,
|
||||
// YUV modes must come after RGB ones.
|
||||
MODE_YUV = 11, MODE_YUVA = 12, // yuv 4:2:0
|
||||
MODE_LAST = 13
|
||||
} WEBP_CSP_MODE;
|
||||
// RGBA-4444 and RGB-565 colorspaces are represented by following byte-order:
|
||||
// RGBA-4444: [r3 r2 r1 r0 g3 g2 g1 g0], [b3 b2 b1 b0 a3 a2 a1 a0], ...
|
||||
// RGB-565: [r4 r3 r2 r1 r0 g5 g4 g3], [g2 g1 g0 b4 b3 b2 b1 b0], ...
|
||||
// In the case WEBP_SWAP_16BITS_CSP is defined, the bytes are swapped for
|
||||
// these two modes:
|
||||
// RGBA-4444: [b3 b2 b1 b0 a3 a2 a1 a0], [r3 r2 r1 r0 g3 g2 g1 g0], ...
|
||||
// RGB-565: [g2 g1 g0 b4 b3 b2 b1 b0], [r4 r3 r2 r1 r0 g5 g4 g3], ...
|
||||
|
||||
typedef enum WEBP_CSP_MODE {
|
||||
MODE_RGB = 0, MODE_RGBA = 1,
|
||||
MODE_BGR = 2, MODE_BGRA = 3,
|
||||
MODE_ARGB = 4, MODE_RGBA_4444 = 5,
|
||||
MODE_RGB_565 = 6,
|
||||
// RGB-premultiplied transparent modes (alpha value is preserved)
|
||||
MODE_rgbA = 7,
|
||||
MODE_bgrA = 8,
|
||||
MODE_Argb = 9,
|
||||
MODE_rgbA_4444 = 10,
|
||||
// YUV modes must come after RGB ones.
|
||||
MODE_YUV = 11, MODE_YUVA = 12, // yuv 4:2:0
|
||||
MODE_LAST = 13
|
||||
} WEBP_CSP_MODE;
|
||||
|
||||
// Some useful macros:
|
||||
static WEBP_INLINE int WebPIsPremultipliedMode(WEBP_CSP_MODE mode) {
|
||||
@ -152,13 +177,13 @@ static WEBP_INLINE int WebPIsRGBMode(WEBP_CSP_MODE mode) {
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecBuffer: Generic structure for describing the output sample buffer.
|
||||
|
||||
typedef struct { // view as RGBA
|
||||
struct WebPRGBABuffer { // view as RGBA
|
||||
uint8_t* rgba; // pointer to RGBA samples
|
||||
int stride; // stride in bytes from one scanline to the next.
|
||||
size_t size; // total size of the *rgba buffer.
|
||||
} WebPRGBABuffer;
|
||||
};
|
||||
|
||||
typedef struct { // view as YUVA
|
||||
struct WebPYUVABuffer { // view as YUVA
|
||||
uint8_t* y, *u, *v, *a; // pointer to luma, chroma U/V, alpha samples
|
||||
int y_stride; // luma stride
|
||||
int u_stride, v_stride; // chroma strides
|
||||
@ -166,10 +191,10 @@ typedef struct { // view as YUVA
|
||||
size_t y_size; // luma plane size
|
||||
size_t u_size, v_size; // chroma planes size
|
||||
size_t a_size; // alpha-plane size
|
||||
} WebPYUVABuffer;
|
||||
};
|
||||
|
||||
// Output buffer
|
||||
typedef struct {
|
||||
struct WebPDecBuffer {
|
||||
WEBP_CSP_MODE colorspace; // Colorspace.
|
||||
int width, height; // Dimensions.
|
||||
int is_external_memory; // If true, 'internal_memory' pointer is not used.
|
||||
@ -182,7 +207,7 @@ typedef struct {
|
||||
uint8_t* private_memory; // Internally allocated memory (only when
|
||||
// is_external_memory is false). Should not be used
|
||||
// externally, but accessed via the buffer union.
|
||||
} WebPDecBuffer;
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(int) WebPInitDecBufferInternal(WebPDecBuffer*, int);
|
||||
@ -200,7 +225,7 @@ WEBP_EXTERN(void) WebPFreeDecBuffer(WebPDecBuffer* buffer);
|
||||
//------------------------------------------------------------------------------
|
||||
// Enumeration of the status codes
|
||||
|
||||
typedef enum {
|
||||
typedef enum VP8StatusCode {
|
||||
VP8_STATUS_OK = 0,
|
||||
VP8_STATUS_OUT_OF_MEMORY,
|
||||
VP8_STATUS_INVALID_PARAM,
|
||||
@ -237,13 +262,17 @@ typedef enum {
|
||||
// }
|
||||
// WebPIDelete(idec);
|
||||
|
||||
typedef struct WebPIDecoder WebPIDecoder;
|
||||
|
||||
// Creates a new incremental decoder with the supplied buffer parameter.
|
||||
// This output_buffer can be passed NULL, in which case a default output buffer
|
||||
// is used (with MODE_RGB). Otherwise, an internal reference to 'output_buffer'
|
||||
// is kept, which means that the lifespan of 'output_buffer' must be larger than
|
||||
// that of the returned WebPIDecoder object.
|
||||
// The supplied 'output_buffer' content MUST NOT be changed between calls to
|
||||
// WebPIAppend() or WebPIUpdate() unless 'output_buffer.is_external_memory' is
|
||||
// set to 1. In such a case, it is allowed to modify the pointers, size and
|
||||
// stride of output_buffer.u.RGBA or output_buffer.u.YUVA, provided they remain
|
||||
// within valid bounds.
|
||||
// All other fields of WebPDecBuffer MUST remain constant between calls.
|
||||
// Returns NULL if the allocation failed.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewDecoder(WebPDecBuffer* output_buffer);
|
||||
|
||||
@ -251,19 +280,27 @@ WEBP_EXTERN(WebPIDecoder*) WebPINewDecoder(WebPDecBuffer* output_buffer);
|
||||
// will output the RGB/A samples specified by 'csp' into a preallocated
|
||||
// buffer 'output_buffer'. The size of this buffer is at least
|
||||
// 'output_buffer_size' and the stride (distance in bytes between two scanlines)
|
||||
// is specified by 'output_stride'. Returns NULL if the allocation failed.
|
||||
// is specified by 'output_stride'.
|
||||
// Additionally, output_buffer can be passed NULL in which case the output
|
||||
// buffer will be allocated automatically when the decoding starts. The
|
||||
// colorspace 'csp' is taken into account for allocating this buffer. All other
|
||||
// parameters are ignored.
|
||||
// Returns NULL if the allocation failed, or if some parameters are invalid.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewRGB(
|
||||
WEBP_CSP_MODE csp,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
|
||||
// This function allocates and initializes an incremental-decoder object, which
|
||||
// will output the raw luma/chroma samples into a preallocated planes. The luma
|
||||
// plane is specified by its pointer 'luma', its size 'luma_size' and its stride
|
||||
// 'luma_stride'. Similarly, the chroma-u plane is specified by the 'u',
|
||||
// 'u_size' and 'u_stride' parameters, and the chroma-v plane by 'v'
|
||||
// and 'v_size'. And same for the alpha-plane. The 'a' pointer can be pass
|
||||
// NULL in case one is not interested in the transparency plane.
|
||||
// Returns NULL if the allocation failed.
|
||||
// will output the raw luma/chroma samples into a preallocated planes if
|
||||
// supplied. The luma plane is specified by its pointer 'luma', its size
|
||||
// 'luma_size' and its stride 'luma_stride'. Similarly, the chroma-u plane
|
||||
// is specified by the 'u', 'u_size' and 'u_stride' parameters, and the chroma-v
|
||||
// plane by 'v' and 'v_size'. And same for the alpha-plane. The 'a' pointer
|
||||
// can be pass NULL in case one is not interested in the transparency plane.
|
||||
// Conversely, 'luma' can be passed NULL if no preallocated planes are supplied.
|
||||
// In this case, the output buffer will be automatically allocated (using
|
||||
// MODE_YUVA) when decoding starts. All parameters are then ignored.
|
||||
// Returns NULL if the allocation failed or if a parameter is invalid.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewYUVA(
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
@ -344,7 +381,7 @@ WEBP_EXTERN(const WebPDecBuffer*) WebPIDecodedArea(
|
||||
CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
|
||||
|
||||
// C) Adjust 'config', if needed
|
||||
config.no_fancy = 1;
|
||||
config.no_fancy_upsampling = 1;
|
||||
config.output.colorspace = MODE_BGRA;
|
||||
// etc.
|
||||
|
||||
@ -365,19 +402,15 @@ WEBP_EXTERN(const WebPDecBuffer*) WebPIDecodedArea(
|
||||
*/
|
||||
|
||||
// Features gathered from the bitstream
|
||||
typedef struct {
|
||||
int width; // Width in pixels, as read from the bitstream.
|
||||
int height; // Height in pixels, as read from the bitstream.
|
||||
int has_alpha; // True if the bitstream contains an alpha channel.
|
||||
struct WebPBitstreamFeatures {
|
||||
int width; // Width in pixels, as read from the bitstream.
|
||||
int height; // Height in pixels, as read from the bitstream.
|
||||
int has_alpha; // True if the bitstream contains an alpha channel.
|
||||
int has_animation; // True if the bitstream is an animation.
|
||||
int format; // 0 = undefined (/mixed), 1 = lossy, 2 = lossless
|
||||
|
||||
// Unused for now:
|
||||
int bitstream_version; // should be 0 for now. TODO(later)
|
||||
int no_incremental_decoding; // if true, using incremental decoding is not
|
||||
// recommended.
|
||||
int rotate; // TODO(later)
|
||||
int uv_sampling; // should be 0 for now. TODO(later)
|
||||
uint32_t pad[3]; // padding for later use
|
||||
} WebPBitstreamFeatures;
|
||||
uint32_t pad[5]; // padding for later use
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(VP8StatusCode) WebPGetFeaturesInternal(
|
||||
@ -385,8 +418,9 @@ WEBP_EXTERN(VP8StatusCode) WebPGetFeaturesInternal(
|
||||
|
||||
// Retrieve features from the bitstream. The *features structure is filled
|
||||
// with information gathered from the bitstream.
|
||||
// Returns false in case of error or version mismatch.
|
||||
// In case of error, features->bitstream_status will reflect the error code.
|
||||
// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the
|
||||
// features from headers. Returns error in other cases.
|
||||
static WEBP_INLINE VP8StatusCode WebPGetFeatures(
|
||||
const uint8_t* data, size_t data_size,
|
||||
WebPBitstreamFeatures* features) {
|
||||
@ -395,7 +429,7 @@ static WEBP_INLINE VP8StatusCode WebPGetFeatures(
|
||||
}
|
||||
|
||||
// Decoding options
|
||||
typedef struct {
|
||||
struct WebPDecoderOptions {
|
||||
int bypass_filtering; // if true, skip the in-loop filtering
|
||||
int no_fancy_upsampling; // if true, use faster pointwise upsampler
|
||||
int use_cropping; // if true, cropping is applied _first_
|
||||
@ -405,19 +439,19 @@ typedef struct {
|
||||
int use_scaling; // if true, scaling is applied _afterward_
|
||||
int scaled_width, scaled_height; // final resolution
|
||||
int use_threads; // if true, use multi-threaded decoding
|
||||
int dithering_strength; // dithering strength (0=Off, 100=full)
|
||||
int flip; // flip output vertically
|
||||
int alpha_dithering_strength; // alpha dithering strength in [0..100]
|
||||
|
||||
// Unused for now:
|
||||
int force_rotation; // forced rotation (to be applied _last_)
|
||||
int no_enhancement; // if true, discard enhancement layer
|
||||
uint32_t pad[6]; // padding for later use
|
||||
} WebPDecoderOptions;
|
||||
uint32_t pad[5]; // padding for later use
|
||||
};
|
||||
|
||||
// Main object storing the configuration for advanced decoding.
|
||||
typedef struct {
|
||||
struct WebPDecoderConfig {
|
||||
WebPBitstreamFeatures input; // Immutable bitstream features (optional)
|
||||
WebPDecBuffer output; // Output buffer (can point to external mem)
|
||||
WebPDecoderOptions options; // Decoding options
|
||||
} WebPDecoderConfig;
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(int) WebPInitDecoderConfigInternal(WebPDecoderConfig*, int);
|
||||
@ -447,7 +481,7 @@ WEBP_EXTERN(WebPIDecoder*) WebPIDecode(const uint8_t* data, size_t data_size,
|
||||
WEBP_EXTERN(VP8StatusCode) WebPDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// CPU detection
|
||||
@ -11,14 +13,10 @@
|
||||
|
||||
#include "./dsp.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#if defined(WEBP_ANDROID_NEON)
|
||||
#include <cpu-features.h>
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// SSE2 detection.
|
||||
//
|
||||
@ -31,22 +29,66 @@ static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
|
||||
"cpuid\n"
|
||||
"xchg %%edi, %%ebx\n"
|
||||
: "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
|
||||
: "a"(info_type));
|
||||
: "a"(info_type), "c"(0));
|
||||
}
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
|
||||
__asm__ volatile (
|
||||
"cpuid\n"
|
||||
: "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
|
||||
: "a"(info_type));
|
||||
: "a"(info_type), "c"(0));
|
||||
}
|
||||
#elif (defined(_M_X64) || defined(_M_IX86)) && \
|
||||
defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 150030729 // >= VS2008 SP1
|
||||
#include <intrin.h>
|
||||
#define GetCPUInfo(info, type) __cpuidex(info, type, 0) // set ecx=0
|
||||
#elif defined(WEBP_MSC_SSE2)
|
||||
#define GetCPUInfo __cpuid
|
||||
#endif
|
||||
|
||||
// NaCl has no support for xgetbv or the raw opcode.
|
||||
#if !defined(__native_client__) && (defined(__i386__) || defined(__x86_64__))
|
||||
static WEBP_INLINE uint64_t xgetbv(void) {
|
||||
const uint32_t ecx = 0;
|
||||
uint32_t eax, edx;
|
||||
// Use the raw opcode for xgetbv for compatibility with older toolchains.
|
||||
__asm__ volatile (
|
||||
".byte 0x0f, 0x01, 0xd0\n"
|
||||
: "=a"(eax), "=d"(edx) : "c" (ecx));
|
||||
return ((uint64_t)edx << 32) | eax;
|
||||
}
|
||||
#elif (defined(_M_X64) || defined(_M_IX86)) && \
|
||||
defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 160040219 // >= VS2010 SP1
|
||||
#include <immintrin.h>
|
||||
#define xgetbv() _xgetbv(0)
|
||||
#elif defined(_MSC_VER) && defined(_M_IX86)
|
||||
static WEBP_INLINE uint64_t xgetbv(void) {
|
||||
uint32_t eax_, edx_;
|
||||
__asm {
|
||||
xor ecx, ecx // ecx = 0
|
||||
// Use the raw opcode for xgetbv for compatibility with older toolchains.
|
||||
__asm _emit 0x0f __asm _emit 0x01 __asm _emit 0xd0
|
||||
mov eax_, eax
|
||||
mov edx_, edx
|
||||
}
|
||||
return ((uint64_t)edx_ << 32) | eax_;
|
||||
}
|
||||
#else
|
||||
#define xgetbv() 0U // no AVX for older x64 or unrecognized toolchains.
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(WEBP_MSC_SSE2)
|
||||
static int x86CPUInfo(CPUFeature feature) {
|
||||
int max_cpuid_value;
|
||||
int cpu_info[4];
|
||||
|
||||
// get the highest feature value cpuid supports
|
||||
GetCPUInfo(cpu_info, 0);
|
||||
max_cpuid_value = cpu_info[0];
|
||||
if (max_cpuid_value < 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
GetCPUInfo(cpu_info, 1);
|
||||
if (feature == kSSE2) {
|
||||
return 0 != (cpu_info[3] & 0x04000000);
|
||||
@ -54,10 +96,26 @@ static int x86CPUInfo(CPUFeature feature) {
|
||||
if (feature == kSSE3) {
|
||||
return 0 != (cpu_info[2] & 0x00000001);
|
||||
}
|
||||
if (feature == kSSE4_1) {
|
||||
return 0 != (cpu_info[2] & 0x00080000);
|
||||
}
|
||||
if (feature == kAVX) {
|
||||
// bits 27 (OSXSAVE) & 28 (256-bit AVX)
|
||||
if ((cpu_info[2] & 0x18000000) == 0x18000000) {
|
||||
// XMM state and YMM state enabled by the OS.
|
||||
return (xgetbv() & 0x6) == 0x6;
|
||||
}
|
||||
}
|
||||
if (feature == kAVX2) {
|
||||
if (x86CPUInfo(kAVX) && max_cpuid_value >= 7) {
|
||||
GetCPUInfo(cpu_info, 7);
|
||||
return ((cpu_info[1] & 0x00000020) == 0x00000020);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
VP8CPUInfo VP8GetCPUInfo = x86CPUInfo;
|
||||
#elif defined(WEBP_ANDROID_NEON)
|
||||
#elif defined(WEBP_ANDROID_NEON) // NB: needs to be before generic NEON test.
|
||||
static int AndroidCPUInfo(CPUFeature feature) {
|
||||
const AndroidCpuFamily cpu_family = android_getCpuFamily();
|
||||
const uint64_t cpu_features = android_getCpuFeatures();
|
||||
@ -68,7 +126,7 @@ static int AndroidCPUInfo(CPUFeature feature) {
|
||||
return 0;
|
||||
}
|
||||
VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo;
|
||||
#elif defined(__ARM_NEON__)
|
||||
#elif defined(WEBP_USE_NEON)
|
||||
// define a dummy function to enable turning off NEON at runtime by setting
|
||||
// VP8DecGetCPUInfo = NULL
|
||||
static int armCPUInfo(CPUFeature feature) {
|
||||
@ -76,10 +134,17 @@ static int armCPUInfo(CPUFeature feature) {
|
||||
return 1;
|
||||
}
|
||||
VP8CPUInfo VP8GetCPUInfo = armCPUInfo;
|
||||
#elif defined(WEBP_USE_MIPS32) || defined(WEBP_USE_MIPS_DSP_R2)
|
||||
static int mipsCPUInfo(CPUFeature feature) {
|
||||
if ((feature == kMIPS32) || (feature == kMIPSdspR2)) {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
VP8CPUInfo VP8GetCPUInfo = mipsCPUInfo;
|
||||
#else
|
||||
VP8CPUInfo VP8GetCPUInfo = NULL;
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,53 +1,20 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Speed-critical decoding functions.
|
||||
// Speed-critical decoding functions, default plain-C implementations.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "./dsp.h"
|
||||
#include "../dec/vp8i.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// run-time tables (~4k)
|
||||
|
||||
static uint8_t abs0[255 + 255 + 1]; // abs(i)
|
||||
static uint8_t abs1[255 + 255 + 1]; // abs(i)>>1
|
||||
static int8_t sclip1[1020 + 1020 + 1]; // clips [-1020, 1020] to [-128, 127]
|
||||
static int8_t sclip2[112 + 112 + 1]; // clips [-112, 112] to [-16, 15]
|
||||
static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
|
||||
|
||||
// We declare this variable 'volatile' to prevent instruction reordering
|
||||
// and make sure it's set to true _last_ (so as to be thread-safe)
|
||||
static volatile int tables_ok = 0;
|
||||
|
||||
static void DspInitTables(void) {
|
||||
if (!tables_ok) {
|
||||
int i;
|
||||
for (i = -255; i <= 255; ++i) {
|
||||
abs0[255 + i] = (i < 0) ? -i : i;
|
||||
abs1[255 + i] = abs0[255 + i] >> 1;
|
||||
}
|
||||
for (i = -1020; i <= 1020; ++i) {
|
||||
sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i;
|
||||
}
|
||||
for (i = -112; i <= 112; ++i) {
|
||||
sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i;
|
||||
}
|
||||
for (i = -255; i <= 255 + 255; ++i) {
|
||||
clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
|
||||
}
|
||||
tables_ok = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t clip_8b(int v) {
|
||||
return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
|
||||
@ -59,9 +26,16 @@ static WEBP_INLINE uint8_t clip_8b(int v) {
|
||||
#define STORE(x, y, v) \
|
||||
dst[x + y * BPS] = clip_8b(dst[x + y * BPS] + ((v) >> 3))
|
||||
|
||||
static const int kC1 = 20091 + (1 << 16);
|
||||
static const int kC2 = 35468;
|
||||
#define MUL(a, b) (((a) * (b)) >> 16)
|
||||
#define STORE2(y, dc, d, c) do { \
|
||||
const int DC = (dc); \
|
||||
STORE(0, y, DC + (d)); \
|
||||
STORE(1, y, DC + (c)); \
|
||||
STORE(2, y, DC - (c)); \
|
||||
STORE(3, y, DC - (d)); \
|
||||
} while (0)
|
||||
|
||||
#define MUL1(a) ((((a) * 20091) >> 16) + (a))
|
||||
#define MUL2(a) (((a) * 35468) >> 16)
|
||||
|
||||
static void TransformOne(const int16_t* in, uint8_t* dst) {
|
||||
int C[4 * 4], *tmp;
|
||||
@ -70,8 +44,8 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
|
||||
for (i = 0; i < 4; ++i) { // vertical pass
|
||||
const int a = in[0] + in[8]; // [-4096, 4094]
|
||||
const int b = in[0] - in[8]; // [-4095, 4095]
|
||||
const int c = MUL(in[4], kC2) - MUL(in[12], kC1); // [-3783, 3783]
|
||||
const int d = MUL(in[4], kC1) + MUL(in[12], kC2); // [-3785, 3781]
|
||||
const int c = MUL2(in[4]) - MUL1(in[12]); // [-3783, 3783]
|
||||
const int d = MUL1(in[4]) + MUL2(in[12]); // [-3785, 3781]
|
||||
tmp[0] = a + d; // [-7881, 7875]
|
||||
tmp[1] = b + c; // [-7878, 7878]
|
||||
tmp[2] = b - c; // [-7878, 7878]
|
||||
@ -80,7 +54,7 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
|
||||
in++;
|
||||
}
|
||||
// Each pass is expanding the dynamic range by ~3.85 (upper bound).
|
||||
// The exact value is (2. + (kC1 + kC2) / 65536).
|
||||
// The exact value is (2. + (20091 + 35468) / 65536).
|
||||
// After the second pass, maximum interval is [-3794, 3794], assuming
|
||||
// an input in [-2048, 2047] interval. We then need to add a dst value
|
||||
// in the [0, 255] range.
|
||||
@ -91,8 +65,8 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
|
||||
const int dc = tmp[0] + 4;
|
||||
const int a = dc + tmp[8];
|
||||
const int b = dc - tmp[8];
|
||||
const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1);
|
||||
const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2);
|
||||
const int c = MUL2(tmp[4]) - MUL1(tmp[12]);
|
||||
const int d = MUL1(tmp[4]) + MUL2(tmp[12]);
|
||||
STORE(0, 0, a + d);
|
||||
STORE(1, 0, b + c);
|
||||
STORE(2, 0, b - c);
|
||||
@ -101,7 +75,22 @@ static void TransformOne(const int16_t* in, uint8_t* dst) {
|
||||
dst += BPS;
|
||||
}
|
||||
}
|
||||
#undef MUL
|
||||
|
||||
// Simplified transform when only in[0], in[1] and in[4] are non-zero
|
||||
static void TransformAC3(const int16_t* in, uint8_t* dst) {
|
||||
const int a = in[0] + 4;
|
||||
const int c4 = MUL2(in[4]);
|
||||
const int d4 = MUL1(in[4]);
|
||||
const int c1 = MUL2(in[1]);
|
||||
const int d1 = MUL1(in[1]);
|
||||
STORE2(0, a + d4, d1, c1);
|
||||
STORE2(1, a + c4, d1, c1);
|
||||
STORE2(2, a - c4, d1, c1);
|
||||
STORE2(3, a - d4, d1, c1);
|
||||
}
|
||||
#undef MUL1
|
||||
#undef MUL2
|
||||
#undef STORE2
|
||||
|
||||
static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) {
|
||||
TransformOne(in, dst);
|
||||
@ -115,7 +104,7 @@ static void TransformUV(const int16_t* in, uint8_t* dst) {
|
||||
VP8Transform(in + 2 * 16, dst + 4 * BPS, 1);
|
||||
}
|
||||
|
||||
static void TransformDC(const int16_t *in, uint8_t* dst) {
|
||||
static void TransformDC(const int16_t* in, uint8_t* dst) {
|
||||
const int DC = in[0] + 4;
|
||||
int i, j;
|
||||
for (j = 0; j < 4; ++j) {
|
||||
@ -126,10 +115,10 @@ static void TransformDC(const int16_t *in, uint8_t* dst) {
|
||||
}
|
||||
|
||||
static void TransformDCUV(const int16_t* in, uint8_t* dst) {
|
||||
if (in[0 * 16]) TransformDC(in + 0 * 16, dst);
|
||||
if (in[1 * 16]) TransformDC(in + 1 * 16, dst + 4);
|
||||
if (in[2 * 16]) TransformDC(in + 2 * 16, dst + 4 * BPS);
|
||||
if (in[3 * 16]) TransformDC(in + 3 * 16, dst + 4 * BPS + 4);
|
||||
if (in[0 * 16]) VP8TransformDC(in + 0 * 16, dst);
|
||||
if (in[1 * 16]) VP8TransformDC(in + 1 * 16, dst + 4);
|
||||
if (in[2 * 16]) VP8TransformDC(in + 2 * 16, dst + 4 * BPS);
|
||||
if (in[3 * 16]) VP8TransformDC(in + 3 * 16, dst + 4 * BPS + 4);
|
||||
}
|
||||
|
||||
#undef STORE
|
||||
@ -164,16 +153,16 @@ static void TransformWHT(const int16_t* in, int16_t* out) {
|
||||
}
|
||||
}
|
||||
|
||||
void (*VP8TransformWHT)(const int16_t* in, int16_t* out) = TransformWHT;
|
||||
void (*VP8TransformWHT)(const int16_t* in, int16_t* out);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Intra predictions
|
||||
|
||||
#define DST(x, y) dst[(x) + (y) * BPS]
|
||||
|
||||
static WEBP_INLINE void TrueMotion(uint8_t *dst, int size) {
|
||||
static WEBP_INLINE void TrueMotion(uint8_t* dst, int size) {
|
||||
const uint8_t* top = dst - BPS;
|
||||
const uint8_t* const clip0 = clip1 + 255 - top[-1];
|
||||
const uint8_t* const clip0 = VP8kclip1 - top[-1];
|
||||
int y;
|
||||
for (y = 0; y < size; ++y) {
|
||||
const uint8_t* const clip = clip0 + dst[-1];
|
||||
@ -184,21 +173,21 @@ static WEBP_INLINE void TrueMotion(uint8_t *dst, int size) {
|
||||
dst += BPS;
|
||||
}
|
||||
}
|
||||
static void TM4(uint8_t *dst) { TrueMotion(dst, 4); }
|
||||
static void TM8uv(uint8_t *dst) { TrueMotion(dst, 8); }
|
||||
static void TM16(uint8_t *dst) { TrueMotion(dst, 16); }
|
||||
static void TM4(uint8_t* dst) { TrueMotion(dst, 4); }
|
||||
static void TM8uv(uint8_t* dst) { TrueMotion(dst, 8); }
|
||||
static void TM16(uint8_t* dst) { TrueMotion(dst, 16); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 16x16
|
||||
|
||||
static void VE16(uint8_t *dst) { // vertical
|
||||
static void VE16(uint8_t* dst) { // vertical
|
||||
int j;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
memcpy(dst + j * BPS, dst - BPS, 16);
|
||||
}
|
||||
}
|
||||
|
||||
static void HE16(uint8_t *dst) { // horizontal
|
||||
static void HE16(uint8_t* dst) { // horizontal
|
||||
int j;
|
||||
for (j = 16; j > 0; --j) {
|
||||
memset(dst, dst[-1], 16);
|
||||
@ -213,7 +202,7 @@ static WEBP_INLINE void Put16(int v, uint8_t* dst) {
|
||||
}
|
||||
}
|
||||
|
||||
static void DC16(uint8_t *dst) { // DC
|
||||
static void DC16(uint8_t* dst) { // DC
|
||||
int DC = 16;
|
||||
int j;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
@ -222,7 +211,7 @@ static void DC16(uint8_t *dst) { // DC
|
||||
Put16(DC >> 5, dst);
|
||||
}
|
||||
|
||||
static void DC16NoTop(uint8_t *dst) { // DC with top samples not available
|
||||
static void DC16NoTop(uint8_t* dst) { // DC with top samples not available
|
||||
int DC = 8;
|
||||
int j;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
@ -231,7 +220,7 @@ static void DC16NoTop(uint8_t *dst) { // DC with top samples not available
|
||||
Put16(DC >> 4, dst);
|
||||
}
|
||||
|
||||
static void DC16NoLeft(uint8_t *dst) { // DC with left samples not available
|
||||
static void DC16NoLeft(uint8_t* dst) { // DC with left samples not available
|
||||
int DC = 8;
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
@ -240,17 +229,19 @@ static void DC16NoLeft(uint8_t *dst) { // DC with left samples not available
|
||||
Put16(DC >> 4, dst);
|
||||
}
|
||||
|
||||
static void DC16NoTopLeft(uint8_t *dst) { // DC with no top and left samples
|
||||
static void DC16NoTopLeft(uint8_t* dst) { // DC with no top and left samples
|
||||
Put16(0x80, dst);
|
||||
}
|
||||
|
||||
VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 4x4
|
||||
|
||||
#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
|
||||
#define AVG2(a, b) (((a) + (b) + 1) >> 1)
|
||||
|
||||
static void VE4(uint8_t *dst) { // vertical
|
||||
static void VE4(uint8_t* dst) { // vertical
|
||||
const uint8_t* top = dst - BPS;
|
||||
const uint8_t vals[4] = {
|
||||
AVG3(top[-1], top[0], top[1]),
|
||||
@ -264,7 +255,7 @@ static void VE4(uint8_t *dst) { // vertical
|
||||
}
|
||||
}
|
||||
|
||||
static void HE4(uint8_t *dst) { // horizontal
|
||||
static void HE4(uint8_t* dst) { // horizontal
|
||||
const int A = dst[-1 - BPS];
|
||||
const int B = dst[-1];
|
||||
const int C = dst[-1 + BPS];
|
||||
@ -276,7 +267,7 @@ static void HE4(uint8_t *dst) { // horizontal
|
||||
*(uint32_t*)(dst + 3 * BPS) = 0x01010101U * AVG3(D, E, E);
|
||||
}
|
||||
|
||||
static void DC4(uint8_t *dst) { // DC
|
||||
static void DC4(uint8_t* dst) { // DC
|
||||
uint32_t dc = 4;
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) dc += dst[i - BPS] + dst[-1 + i * BPS];
|
||||
@ -284,7 +275,7 @@ static void DC4(uint8_t *dst) { // DC
|
||||
for (i = 0; i < 4; ++i) memset(dst + i * BPS, dc, 4);
|
||||
}
|
||||
|
||||
static void RD4(uint8_t *dst) { // Down-right
|
||||
static void RD4(uint8_t* dst) { // Down-right
|
||||
const int I = dst[-1 + 0 * BPS];
|
||||
const int J = dst[-1 + 1 * BPS];
|
||||
const int K = dst[-1 + 2 * BPS];
|
||||
@ -295,15 +286,15 @@ static void RD4(uint8_t *dst) { // Down-right
|
||||
const int C = dst[2 - BPS];
|
||||
const int D = dst[3 - BPS];
|
||||
DST(0, 3) = AVG3(J, K, L);
|
||||
DST(0, 2) = DST(1, 3) = AVG3(I, J, K);
|
||||
DST(0, 1) = DST(1, 2) = DST(2, 3) = AVG3(X, I, J);
|
||||
DST(0, 0) = DST(1, 1) = DST(2, 2) = DST(3, 3) = AVG3(A, X, I);
|
||||
DST(1, 0) = DST(2, 1) = DST(3, 2) = AVG3(B, A, X);
|
||||
DST(2, 0) = DST(3, 1) = AVG3(C, B, A);
|
||||
DST(3, 0) = AVG3(D, C, B);
|
||||
DST(1, 3) = DST(0, 2) = AVG3(I, J, K);
|
||||
DST(2, 3) = DST(1, 2) = DST(0, 1) = AVG3(X, I, J);
|
||||
DST(3, 3) = DST(2, 2) = DST(1, 1) = DST(0, 0) = AVG3(A, X, I);
|
||||
DST(3, 2) = DST(2, 1) = DST(1, 0) = AVG3(B, A, X);
|
||||
DST(3, 1) = DST(2, 0) = AVG3(C, B, A);
|
||||
DST(3, 0) = AVG3(D, C, B);
|
||||
}
|
||||
|
||||
static void LD4(uint8_t *dst) { // Down-Left
|
||||
static void LD4(uint8_t* dst) { // Down-Left
|
||||
const int A = dst[0 - BPS];
|
||||
const int B = dst[1 - BPS];
|
||||
const int C = dst[2 - BPS];
|
||||
@ -316,12 +307,12 @@ static void LD4(uint8_t *dst) { // Down-Left
|
||||
DST(1, 0) = DST(0, 1) = AVG3(B, C, D);
|
||||
DST(2, 0) = DST(1, 1) = DST(0, 2) = AVG3(C, D, E);
|
||||
DST(3, 0) = DST(2, 1) = DST(1, 2) = DST(0, 3) = AVG3(D, E, F);
|
||||
DST(3, 1) = DST(2, 2) = DST(1, 3) = AVG3(E, F, G);
|
||||
DST(3, 2) = DST(2, 3) = AVG3(F, G, H);
|
||||
DST(3, 3) = AVG3(G, H, H);
|
||||
DST(3, 1) = DST(2, 2) = DST(1, 3) = AVG3(E, F, G);
|
||||
DST(3, 2) = DST(2, 3) = AVG3(F, G, H);
|
||||
DST(3, 3) = AVG3(G, H, H);
|
||||
}
|
||||
|
||||
static void VR4(uint8_t *dst) { // Vertical-Right
|
||||
static void VR4(uint8_t* dst) { // Vertical-Right
|
||||
const int I = dst[-1 + 0 * BPS];
|
||||
const int J = dst[-1 + 1 * BPS];
|
||||
const int K = dst[-1 + 2 * BPS];
|
||||
@ -343,7 +334,7 @@ static void VR4(uint8_t *dst) { // Vertical-Right
|
||||
DST(3, 1) = AVG3(B, C, D);
|
||||
}
|
||||
|
||||
static void VL4(uint8_t *dst) { // Vertical-Left
|
||||
static void VL4(uint8_t* dst) { // Vertical-Left
|
||||
const int A = dst[0 - BPS];
|
||||
const int B = dst[1 - BPS];
|
||||
const int C = dst[2 - BPS];
|
||||
@ -365,7 +356,7 @@ static void VL4(uint8_t *dst) { // Vertical-Left
|
||||
DST(3, 3) = AVG3(F, G, H);
|
||||
}
|
||||
|
||||
static void HU4(uint8_t *dst) { // Horizontal-Up
|
||||
static void HU4(uint8_t* dst) { // Horizontal-Up
|
||||
const int I = dst[-1 + 0 * BPS];
|
||||
const int J = dst[-1 + 1 * BPS];
|
||||
const int K = dst[-1 + 2 * BPS];
|
||||
@ -380,7 +371,7 @@ static void HU4(uint8_t *dst) { // Horizontal-Up
|
||||
DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L;
|
||||
}
|
||||
|
||||
static void HD4(uint8_t *dst) { // Horizontal-Down
|
||||
static void HD4(uint8_t* dst) { // Horizontal-Down
|
||||
const int I = dst[-1 + 0 * BPS];
|
||||
const int J = dst[-1 + 1 * BPS];
|
||||
const int K = dst[-1 + 2 * BPS];
|
||||
@ -407,17 +398,19 @@ static void HD4(uint8_t *dst) { // Horizontal-Down
|
||||
#undef AVG3
|
||||
#undef AVG2
|
||||
|
||||
VP8PredFunc VP8PredLuma4[NUM_BMODES];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Chroma
|
||||
|
||||
static void VE8uv(uint8_t *dst) { // vertical
|
||||
static void VE8uv(uint8_t* dst) { // vertical
|
||||
int j;
|
||||
for (j = 0; j < 8; ++j) {
|
||||
memcpy(dst + j * BPS, dst - BPS, 8);
|
||||
}
|
||||
}
|
||||
|
||||
static void HE8uv(uint8_t *dst) { // horizontal
|
||||
static void HE8uv(uint8_t* dst) { // horizontal
|
||||
int j;
|
||||
for (j = 0; j < 8; ++j) {
|
||||
memset(dst, dst[-1], 8);
|
||||
@ -426,60 +419,45 @@ static void HE8uv(uint8_t *dst) { // horizontal
|
||||
}
|
||||
|
||||
// helper for chroma-DC predictions
|
||||
static WEBP_INLINE void Put8x8uv(uint64_t v, uint8_t* dst) {
|
||||
static WEBP_INLINE void Put8x8uv(uint8_t value, uint8_t* dst) {
|
||||
int j;
|
||||
for (j = 0; j < 8; ++j) {
|
||||
*(uint64_t*)(dst + j * BPS) = v;
|
||||
memset(dst + j * BPS, value, 8);
|
||||
}
|
||||
}
|
||||
|
||||
static void DC8uv(uint8_t *dst) { // DC
|
||||
static void DC8uv(uint8_t* dst) { // DC
|
||||
int dc0 = 8;
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
dc0 += dst[i - BPS] + dst[-1 + i * BPS];
|
||||
}
|
||||
Put8x8uv((uint64_t)((dc0 >> 4) * 0x0101010101010101ULL), dst);
|
||||
Put8x8uv(dc0 >> 4, dst);
|
||||
}
|
||||
|
||||
static void DC8uvNoLeft(uint8_t *dst) { // DC with no left samples
|
||||
static void DC8uvNoLeft(uint8_t* dst) { // DC with no left samples
|
||||
int dc0 = 4;
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
dc0 += dst[i - BPS];
|
||||
}
|
||||
Put8x8uv((uint64_t)((dc0 >> 3) * 0x0101010101010101ULL), dst);
|
||||
Put8x8uv(dc0 >> 3, dst);
|
||||
}
|
||||
|
||||
static void DC8uvNoTop(uint8_t *dst) { // DC with no top samples
|
||||
static void DC8uvNoTop(uint8_t* dst) { // DC with no top samples
|
||||
int dc0 = 4;
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
dc0 += dst[-1 + i * BPS];
|
||||
}
|
||||
Put8x8uv((uint64_t)((dc0 >> 3) * 0x0101010101010101ULL), dst);
|
||||
Put8x8uv(dc0 >> 3, dst);
|
||||
}
|
||||
|
||||
static void DC8uvNoTopLeft(uint8_t *dst) { // DC with nothing
|
||||
Put8x8uv(0x8080808080808080ULL, dst);
|
||||
static void DC8uvNoTopLeft(uint8_t* dst) { // DC with nothing
|
||||
Put8x8uv(0x80, dst);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// default C implementations
|
||||
|
||||
const VP8PredFunc VP8PredLuma4[NUM_BMODES] = {
|
||||
DC4, TM4, VE4, HE4, RD4, VR4, LD4, VL4, HD4, HU4
|
||||
};
|
||||
|
||||
const VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES] = {
|
||||
DC16, TM16, VE16, HE16,
|
||||
DC16NoTop, DC16NoLeft, DC16NoTopLeft
|
||||
};
|
||||
|
||||
const VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES] = {
|
||||
DC8uv, TM8uv, VE8uv, HE8uv,
|
||||
DC8uvNoTop, DC8uvNoLeft, DC8uvNoTopLeft
|
||||
};
|
||||
VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Edge filtering functions
|
||||
@ -487,61 +465,62 @@ const VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES] = {
|
||||
// 4 pixels in, 2 pixels out
|
||||
static WEBP_INLINE void do_filter2(uint8_t* p, int step) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1];
|
||||
const int a1 = sclip2[112 + ((a + 4) >> 3)];
|
||||
const int a2 = sclip2[112 + ((a + 3) >> 3)];
|
||||
p[-step] = clip1[255 + p0 + a2];
|
||||
p[ 0] = clip1[255 + q0 - a1];
|
||||
const int a = 3 * (q0 - p0) + VP8ksclip1[p1 - q1]; // in [-893,892]
|
||||
const int a1 = VP8ksclip2[(a + 4) >> 3]; // in [-16,15]
|
||||
const int a2 = VP8ksclip2[(a + 3) >> 3];
|
||||
p[-step] = VP8kclip1[p0 + a2];
|
||||
p[ 0] = VP8kclip1[q0 - a1];
|
||||
}
|
||||
|
||||
// 4 pixels in, 4 pixels out
|
||||
static WEBP_INLINE void do_filter4(uint8_t* p, int step) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
const int a = 3 * (q0 - p0);
|
||||
const int a1 = sclip2[112 + ((a + 4) >> 3)];
|
||||
const int a2 = sclip2[112 + ((a + 3) >> 3)];
|
||||
const int a1 = VP8ksclip2[(a + 4) >> 3];
|
||||
const int a2 = VP8ksclip2[(a + 3) >> 3];
|
||||
const int a3 = (a1 + 1) >> 1;
|
||||
p[-2*step] = clip1[255 + p1 + a3];
|
||||
p[- step] = clip1[255 + p0 + a2];
|
||||
p[ 0] = clip1[255 + q0 - a1];
|
||||
p[ step] = clip1[255 + q1 - a3];
|
||||
p[-2*step] = VP8kclip1[p1 + a3];
|
||||
p[- step] = VP8kclip1[p0 + a2];
|
||||
p[ 0] = VP8kclip1[q0 - a1];
|
||||
p[ step] = VP8kclip1[q1 - a3];
|
||||
}
|
||||
|
||||
// 6 pixels in, 6 pixels out
|
||||
static WEBP_INLINE void do_filter6(uint8_t* p, int step) {
|
||||
const int p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
|
||||
const int q0 = p[0], q1 = p[step], q2 = p[2*step];
|
||||
const int a = sclip1[1020 + 3 * (q0 - p0) + sclip1[1020 + p1 - q1]];
|
||||
const int a = VP8ksclip1[3 * (q0 - p0) + VP8ksclip1[p1 - q1]];
|
||||
// a is in [-128,127], a1 in [-27,27], a2 in [-18,18] and a3 in [-9,9]
|
||||
const int a1 = (27 * a + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7
|
||||
const int a2 = (18 * a + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7
|
||||
const int a3 = (9 * a + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7
|
||||
p[-3*step] = clip1[255 + p2 + a3];
|
||||
p[-2*step] = clip1[255 + p1 + a2];
|
||||
p[- step] = clip1[255 + p0 + a1];
|
||||
p[ 0] = clip1[255 + q0 - a1];
|
||||
p[ step] = clip1[255 + q1 - a2];
|
||||
p[ 2*step] = clip1[255 + q2 - a3];
|
||||
p[-3*step] = VP8kclip1[p2 + a3];
|
||||
p[-2*step] = VP8kclip1[p1 + a2];
|
||||
p[- step] = VP8kclip1[p0 + a1];
|
||||
p[ 0] = VP8kclip1[q0 - a1];
|
||||
p[ step] = VP8kclip1[q1 - a2];
|
||||
p[ 2*step] = VP8kclip1[q2 - a3];
|
||||
}
|
||||
|
||||
static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh);
|
||||
return (VP8kabs0[p1 - p0] > thresh) || (VP8kabs0[q1 - q0] > thresh);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int thresh) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh;
|
||||
static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int t) {
|
||||
const int p1 = p[-2 * step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
return ((4 * VP8kabs0[p0 - q0] + VP8kabs0[p1 - q1]) <= t);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int needs_filter2(const uint8_t* p,
|
||||
int step, int t, int it) {
|
||||
const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
|
||||
const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step];
|
||||
if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t)
|
||||
return 0;
|
||||
return abs0[255 + p3 - p2] <= it && abs0[255 + p2 - p1] <= it &&
|
||||
abs0[255 + p1 - p0] <= it && abs0[255 + q3 - q2] <= it &&
|
||||
abs0[255 + q2 - q1] <= it && abs0[255 + q1 - q0] <= it;
|
||||
const int p3 = p[-4 * step], p2 = p[-3 * step], p1 = p[-2 * step];
|
||||
const int p0 = p[-step], q0 = p[0];
|
||||
const int q1 = p[step], q2 = p[2 * step], q3 = p[3 * step];
|
||||
if ((4 * VP8kabs0[p0 - q0] + VP8kabs0[p1 - q1]) > t) return 0;
|
||||
return VP8kabs0[p3 - p2] <= it && VP8kabs0[p2 - p1] <= it &&
|
||||
VP8kabs0[p1 - p0] <= it && VP8kabs0[q3 - q2] <= it &&
|
||||
VP8kabs0[q2 - q1] <= it && VP8kabs0[q1 - q0] <= it;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -549,8 +528,9 @@ static WEBP_INLINE int needs_filter2(const uint8_t* p,
|
||||
|
||||
static void SimpleVFilter16(uint8_t* p, int stride, int thresh) {
|
||||
int i;
|
||||
const int thresh2 = 2 * thresh + 1;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
if (needs_filter(p + i, stride, thresh)) {
|
||||
if (needs_filter(p + i, stride, thresh2)) {
|
||||
do_filter2(p + i, stride);
|
||||
}
|
||||
}
|
||||
@ -558,8 +538,9 @@ static void SimpleVFilter16(uint8_t* p, int stride, int thresh) {
|
||||
|
||||
static void SimpleHFilter16(uint8_t* p, int stride, int thresh) {
|
||||
int i;
|
||||
const int thresh2 = 2 * thresh + 1;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
if (needs_filter(p + i * stride, 1, thresh)) {
|
||||
if (needs_filter(p + i * stride, 1, thresh2)) {
|
||||
do_filter2(p + i * stride, 1);
|
||||
}
|
||||
}
|
||||
@ -587,8 +568,9 @@ static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) {
|
||||
static WEBP_INLINE void FilterLoop26(uint8_t* p,
|
||||
int hstride, int vstride, int size,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
const int thresh2 = 2 * thresh + 1;
|
||||
while (size-- > 0) {
|
||||
if (needs_filter2(p, hstride, thresh, ithresh)) {
|
||||
if (needs_filter2(p, hstride, thresh2, ithresh)) {
|
||||
if (hev(p, hstride, hev_thresh)) {
|
||||
do_filter2(p, hstride);
|
||||
} else {
|
||||
@ -602,8 +584,9 @@ static WEBP_INLINE void FilterLoop26(uint8_t* p,
|
||||
static WEBP_INLINE void FilterLoop24(uint8_t* p,
|
||||
int hstride, int vstride, int size,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
const int thresh2 = 2 * thresh + 1;
|
||||
while (size-- > 0) {
|
||||
if (needs_filter2(p, hstride, thresh, ithresh)) {
|
||||
if (needs_filter2(p, hstride, thresh2, ithresh)) {
|
||||
if (hev(p, hstride, hev_thresh)) {
|
||||
do_filter2(p, hstride);
|
||||
} else {
|
||||
@ -672,6 +655,7 @@ static void HFilter8i(uint8_t* u, uint8_t* v, int stride,
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
VP8DecIdct2 VP8Transform;
|
||||
VP8DecIdct VP8TransformAC3;
|
||||
VP8DecIdct VP8TransformUV;
|
||||
VP8DecIdct VP8TransformDC;
|
||||
VP8DecIdct VP8TransformDCUV;
|
||||
@ -690,15 +674,25 @@ VP8SimpleFilterFunc VP8SimpleVFilter16i;
|
||||
VP8SimpleFilterFunc VP8SimpleHFilter16i;
|
||||
|
||||
extern void VP8DspInitSSE2(void);
|
||||
extern void VP8DspInitSSE41(void);
|
||||
extern void VP8DspInitNEON(void);
|
||||
extern void VP8DspInitMIPS32(void);
|
||||
extern void VP8DspInitMIPSdspR2(void);
|
||||
|
||||
void VP8DspInit(void) {
|
||||
DspInitTables();
|
||||
static volatile VP8CPUInfo dec_last_cpuinfo_used =
|
||||
(VP8CPUInfo)&dec_last_cpuinfo_used;
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void VP8DspInit(void) {
|
||||
if (dec_last_cpuinfo_used == VP8GetCPUInfo) return;
|
||||
|
||||
VP8InitClipTables();
|
||||
|
||||
VP8TransformWHT = TransformWHT;
|
||||
VP8Transform = TransformTwo;
|
||||
VP8TransformUV = TransformUV;
|
||||
VP8TransformDC = TransformDC;
|
||||
VP8TransformDCUV = TransformDCUV;
|
||||
VP8TransformAC3 = TransformAC3;
|
||||
|
||||
VP8VFilter16 = VFilter16;
|
||||
VP8HFilter16 = HFilter16;
|
||||
@ -713,20 +707,60 @@ void VP8DspInit(void) {
|
||||
VP8SimpleVFilter16i = SimpleVFilter16i;
|
||||
VP8SimpleHFilter16i = SimpleHFilter16i;
|
||||
|
||||
VP8PredLuma4[0] = DC4;
|
||||
VP8PredLuma4[1] = TM4;
|
||||
VP8PredLuma4[2] = VE4;
|
||||
VP8PredLuma4[3] = HE4;
|
||||
VP8PredLuma4[4] = RD4;
|
||||
VP8PredLuma4[5] = VR4;
|
||||
VP8PredLuma4[6] = LD4;
|
||||
VP8PredLuma4[7] = VL4;
|
||||
VP8PredLuma4[8] = HD4;
|
||||
VP8PredLuma4[9] = HU4;
|
||||
|
||||
VP8PredLuma16[0] = DC16;
|
||||
VP8PredLuma16[1] = TM16;
|
||||
VP8PredLuma16[2] = VE16;
|
||||
VP8PredLuma16[3] = HE16;
|
||||
VP8PredLuma16[4] = DC16NoTop;
|
||||
VP8PredLuma16[5] = DC16NoLeft;
|
||||
VP8PredLuma16[6] = DC16NoTopLeft;
|
||||
|
||||
VP8PredChroma8[0] = DC8uv;
|
||||
VP8PredChroma8[1] = TM8uv;
|
||||
VP8PredChroma8[2] = VE8uv;
|
||||
VP8PredChroma8[3] = HE8uv;
|
||||
VP8PredChroma8[4] = DC8uvNoTop;
|
||||
VP8PredChroma8[5] = DC8uvNoLeft;
|
||||
VP8PredChroma8[6] = DC8uvNoTopLeft;
|
||||
|
||||
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
|
||||
if (VP8GetCPUInfo) {
|
||||
if (VP8GetCPUInfo != NULL) {
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
if (VP8GetCPUInfo(kSSE2)) {
|
||||
VP8DspInitSSE2();
|
||||
#if defined(WEBP_USE_SSE41)
|
||||
if (VP8GetCPUInfo(kSSE4_1)) {
|
||||
VP8DspInitSSE41();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#elif defined(WEBP_USE_NEON)
|
||||
#endif
|
||||
#if defined(WEBP_USE_NEON)
|
||||
if (VP8GetCPUInfo(kNEON)) {
|
||||
VP8DspInitNEON();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#if defined(WEBP_USE_MIPS32)
|
||||
if (VP8GetCPUInfo(kMIPS32)) {
|
||||
VP8DspInitMIPS32();
|
||||
}
|
||||
#endif
|
||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||
if (VP8GetCPUInfo(kMIPSdspR2)) {
|
||||
VP8DspInitMIPSdspR2();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
dec_last_cpuinfo_used = VP8GetCPUInfo;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Speed-critical functions.
|
||||
@ -12,45 +14,122 @@
|
||||
#ifndef WEBP_DSP_DSP_H_
|
||||
#define WEBP_DSP_DSP_H_
|
||||
|
||||
#include "../types.h"
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../webp/config.h"
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#include "../webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BPS 32 // this is the common stride for enc/dec
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// CPU detection
|
||||
|
||||
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
|
||||
#if defined(__GNUC__)
|
||||
# define LOCAL_GCC_VERSION ((__GNUC__ << 8) | __GNUC_MINOR__)
|
||||
# define LOCAL_GCC_PREREQ(maj, min) \
|
||||
(LOCAL_GCC_VERSION >= (((maj) << 8) | (min)))
|
||||
#else
|
||||
# define LOCAL_GCC_VERSION 0
|
||||
# define LOCAL_GCC_PREREQ(maj, min) 0
|
||||
#endif
|
||||
|
||||
#ifndef __has_builtin
|
||||
# define __has_builtin(x) 0
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER > 1310 && \
|
||||
(defined(_M_X64) || defined(_M_IX86))
|
||||
#define WEBP_MSC_SSE2 // Visual C++ SSE2 targets
|
||||
#endif
|
||||
|
||||
#if defined(__SSE2__) || defined(WEBP_MSC_SSE2)
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1500 && \
|
||||
(defined(_M_X64) || defined(_M_IX86))
|
||||
#define WEBP_MSC_SSE41 // Visual C++ SSE4.1 targets
|
||||
#endif
|
||||
|
||||
// WEBP_HAVE_* are used to indicate the presence of the instruction set in dsp
|
||||
// files without intrinsics, allowing the corresponding Init() to be called.
|
||||
// Files containing intrinsics will need to be built targeting the instruction
|
||||
// set so should succeed on one of the earlier tests.
|
||||
#if defined(__SSE2__) || defined(WEBP_MSC_SSE2) || defined(WEBP_HAVE_SSE2)
|
||||
#define WEBP_USE_SSE2
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__) && defined(__ARM_NEON__)
|
||||
#if defined(__SSE4_1__) || defined(WEBP_MSC_SSE41) || defined(WEBP_HAVE_SSE41)
|
||||
#define WEBP_USE_SSE41
|
||||
#endif
|
||||
|
||||
#if defined(__AVX2__) || defined(WEBP_HAVE_AVX2)
|
||||
#define WEBP_USE_AVX2
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__)
|
||||
#define WEBP_ANDROID_NEON // Android targets that might support NEON
|
||||
#endif
|
||||
|
||||
#if ( (defined(__ARM_NEON__) && !defined(__aarch64__)) || defined(WEBP_ANDROID_NEON)) && !defined(PSP2_ENABLED)
|
||||
// The intrinsics currently cause compiler errors with arm-nacl-gcc and the
|
||||
// inline assembly would need to be modified for use with Native Client.
|
||||
#if (defined(__ARM_NEON__) || defined(WEBP_ANDROID_NEON) || \
|
||||
defined(__aarch64__)) && !defined(__native_client__)
|
||||
#define WEBP_USE_NEON
|
||||
#endif
|
||||
|
||||
#if defined(_MSC_VER) && _MSC_VER >= 1700 && defined(_M_ARM)
|
||||
#define WEBP_USE_NEON
|
||||
#define WEBP_USE_INTRINSICS
|
||||
#endif
|
||||
|
||||
#if defined(__mips__) && !defined(__mips64) && \
|
||||
defined(__mips_isa_rev) && (__mips_isa_rev >= 1) && (__mips_isa_rev < 6)
|
||||
#define WEBP_USE_MIPS32
|
||||
#if (__mips_isa_rev >= 2)
|
||||
#define WEBP_USE_MIPS32_R2
|
||||
#if defined(__mips_dspr2) || (__mips_dsp_rev >= 2)
|
||||
#define WEBP_USE_MIPS_DSP_R2
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// This macro prevents thread_sanitizer from reporting known concurrent writes.
|
||||
#define WEBP_TSAN_IGNORE_FUNCTION
|
||||
#if defined(__has_feature)
|
||||
#if __has_feature(thread_sanitizer)
|
||||
#undef WEBP_TSAN_IGNORE_FUNCTION
|
||||
#define WEBP_TSAN_IGNORE_FUNCTION __attribute__((no_sanitize_thread))
|
||||
#endif
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
kSSE2,
|
||||
kSSE3,
|
||||
kNEON
|
||||
kSSE4_1,
|
||||
kAVX,
|
||||
kAVX2,
|
||||
kNEON,
|
||||
kMIPS32,
|
||||
kMIPSdspR2
|
||||
} CPUFeature;
|
||||
// returns true if the CPU supports the feature.
|
||||
typedef int (*VP8CPUInfo)(CPUFeature feature);
|
||||
extern VP8CPUInfo VP8GetCPUInfo;
|
||||
WEBP_EXTERN(VP8CPUInfo) VP8GetCPUInfo;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Init stub generator
|
||||
|
||||
// Defines an init function stub to ensure each module exposes a symbol,
|
||||
// avoiding a compiler warning.
|
||||
#define WEBP_DSP_INIT_STUB(func) \
|
||||
extern void func(void); \
|
||||
WEBP_TSAN_IGNORE_FUNCTION void func(void) {}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Encoding
|
||||
|
||||
int VP8GetAlpha(const int histo[]);
|
||||
|
||||
// Transforms
|
||||
// VP8Idct: Does one of two inverse transforms. If do_two is set, the transforms
|
||||
// will be done for (ref, in, dst) and (ref + 4, in + 16, dst + 4).
|
||||
@ -60,7 +139,7 @@ typedef void (*VP8Fdct)(const uint8_t* src, const uint8_t* ref, int16_t* out);
|
||||
typedef void (*VP8WHT)(const int16_t* in, int16_t* out);
|
||||
extern VP8Idct VP8ITransform;
|
||||
extern VP8Fdct VP8FTransform;
|
||||
extern VP8WHT VP8ITransformWHT;
|
||||
extern VP8Fdct VP8FTransform2; // performs two transforms at a time
|
||||
extern VP8WHT VP8FTransformWHT;
|
||||
// Predictions
|
||||
// *dst is the destination block. *top and *left can be NULL.
|
||||
@ -79,20 +158,63 @@ extern VP8WMetric VP8TDisto4x4, VP8TDisto16x16;
|
||||
|
||||
typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst);
|
||||
extern VP8BlockCopy VP8Copy4x4;
|
||||
extern VP8BlockCopy VP8Copy16x8;
|
||||
// Quantization
|
||||
struct VP8Matrix; // forward declaration
|
||||
typedef int (*VP8QuantizeBlock)(int16_t in[16], int16_t out[16],
|
||||
int n, const struct VP8Matrix* const mtx);
|
||||
const struct VP8Matrix* const mtx);
|
||||
// Same as VP8QuantizeBlock, but quantizes two consecutive blocks.
|
||||
typedef int (*VP8Quantize2Blocks)(int16_t in[32], int16_t out[32],
|
||||
const struct VP8Matrix* const mtx);
|
||||
|
||||
extern VP8QuantizeBlock VP8EncQuantizeBlock;
|
||||
extern VP8Quantize2Blocks VP8EncQuantize2Blocks;
|
||||
|
||||
// specific to 2nd transform:
|
||||
typedef int (*VP8QuantizeBlockWHT)(int16_t in[16], int16_t out[16],
|
||||
const struct VP8Matrix* const mtx);
|
||||
extern VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT;
|
||||
|
||||
// Compute susceptibility based on DCT-coeff histograms:
|
||||
// the higher, the "easier" the macroblock is to compress.
|
||||
typedef int (*VP8CHisto)(const uint8_t* ref, const uint8_t* pred,
|
||||
int start_block, int end_block);
|
||||
extern const int VP8DspScan[16 + 4 + 4];
|
||||
extern VP8CHisto VP8CollectHistogram;
|
||||
|
||||
void VP8EncDspInit(void); // must be called before using any of the above
|
||||
// Collect histogram for susceptibility calculation.
|
||||
#define MAX_COEFF_THRESH 31 // size of histogram used by CollectHistogram.
|
||||
typedef struct {
|
||||
// We only need to store max_value and last_non_zero, not the distribution.
|
||||
int max_value;
|
||||
int last_non_zero;
|
||||
} VP8Histogram;
|
||||
typedef void (*VP8CHisto)(const uint8_t* ref, const uint8_t* pred,
|
||||
int start_block, int end_block,
|
||||
VP8Histogram* const histo);
|
||||
extern VP8CHisto VP8CollectHistogram;
|
||||
// General-purpose util function to help VP8CollectHistogram().
|
||||
void VP8SetHistogramData(const int distribution[MAX_COEFF_THRESH + 1],
|
||||
VP8Histogram* const histo);
|
||||
|
||||
// must be called before using any of the above
|
||||
void VP8EncDspInit(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// cost functions (encoding)
|
||||
|
||||
extern const uint16_t VP8EntropyCost[256]; // 8bit fixed-point log(p)
|
||||
// approximate cost per level:
|
||||
extern const uint16_t VP8LevelFixedCosts[2047 /*MAX_LEVEL*/ + 1];
|
||||
extern const uint8_t VP8EncBands[16 + 1];
|
||||
|
||||
struct VP8Residual;
|
||||
typedef void (*VP8SetResidualCoeffsFunc)(const int16_t* const coeffs,
|
||||
struct VP8Residual* const res);
|
||||
extern VP8SetResidualCoeffsFunc VP8SetResidualCoeffs;
|
||||
|
||||
// Cost calculation function.
|
||||
typedef int (*VP8GetResidualCostFunc)(int ctx0,
|
||||
const struct VP8Residual* const res);
|
||||
extern VP8GetResidualCostFunc VP8GetResidualCost;
|
||||
|
||||
// must be called before anything using the above
|
||||
void VP8EncDspCostInit(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Decoding
|
||||
@ -101,17 +223,26 @@ typedef void (*VP8DecIdct)(const int16_t* coeffs, uint8_t* dst);
|
||||
// when doing two transforms, coeffs is actually int16_t[2][16].
|
||||
typedef void (*VP8DecIdct2)(const int16_t* coeffs, uint8_t* dst, int do_two);
|
||||
extern VP8DecIdct2 VP8Transform;
|
||||
extern VP8DecIdct VP8TransformAC3;
|
||||
extern VP8DecIdct VP8TransformUV;
|
||||
extern VP8DecIdct VP8TransformDC;
|
||||
extern VP8DecIdct VP8TransformDCUV;
|
||||
extern void (*VP8TransformWHT)(const int16_t* in, int16_t* out);
|
||||
extern VP8WHT VP8TransformWHT;
|
||||
|
||||
// *dst is the destination block, with stride BPS. Boundary samples are
|
||||
// assumed accessible when needed.
|
||||
typedef void (*VP8PredFunc)(uint8_t* dst);
|
||||
extern const VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */];
|
||||
extern const VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */];
|
||||
extern const VP8PredFunc VP8PredLuma4[/* NUM_BMODES */];
|
||||
extern VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */];
|
||||
extern VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */];
|
||||
extern VP8PredFunc VP8PredLuma4[/* NUM_BMODES */];
|
||||
|
||||
// clipping tables (for filtering)
|
||||
extern const int8_t* const VP8ksclip1; // clips [-1020, 1020] to [-128, 127]
|
||||
extern const int8_t* const VP8ksclip2; // clips [-112, 112] to [-16, 15]
|
||||
extern const uint8_t* const VP8kclip1; // clips [-255,511] to [0,255]
|
||||
extern const uint8_t* const VP8kabs0; // abs(x) for x in [-255,255]
|
||||
// must be called first
|
||||
void VP8InitClipTables(void);
|
||||
|
||||
// simple filter (only for luma)
|
||||
typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh);
|
||||
@ -145,6 +276,8 @@ void VP8DspInit(void);
|
||||
|
||||
#define FANCY_UPSAMPLING // undefined to remove fancy upsampling support
|
||||
|
||||
// Convert a pair of y/u/v lines together to the output rgb/a colorspace.
|
||||
// bottom_y can be NULL if only one line of output is needed (at top/bottom).
|
||||
typedef void (*WebPUpsampleLinePairFunc)(
|
||||
const uint8_t* top_y, const uint8_t* bottom_y,
|
||||
const uint8_t* top_u, const uint8_t* top_v,
|
||||
@ -156,18 +289,20 @@ typedef void (*WebPUpsampleLinePairFunc)(
|
||||
// Fancy upsampling functions to convert YUV to RGB(A) modes
|
||||
extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
|
||||
|
||||
// Initializes SSE2 version of the fancy upsamplers.
|
||||
void WebPInitUpsamplersSSE2(void);
|
||||
|
||||
#endif // FANCY_UPSAMPLING
|
||||
|
||||
// Point-sampling methods.
|
||||
typedef void (*WebPSampleLinePairFunc)(
|
||||
const uint8_t* top_y, const uint8_t* bottom_y,
|
||||
const uint8_t* u, const uint8_t* v,
|
||||
uint8_t* top_dst, uint8_t* bottom_dst, int len);
|
||||
// Per-row point-sampling methods.
|
||||
typedef void (*WebPSamplerRowFunc)(const uint8_t* y,
|
||||
const uint8_t* u, const uint8_t* v,
|
||||
uint8_t* dst, int len);
|
||||
// Generic function to apply 'WebPSamplerRowFunc' to the whole plane:
|
||||
void WebPSamplerProcessPlane(const uint8_t* y, int y_stride,
|
||||
const uint8_t* u, const uint8_t* v, int uv_stride,
|
||||
uint8_t* dst, int dst_stride,
|
||||
int width, int height, WebPSamplerRowFunc func);
|
||||
|
||||
extern const WebPSampleLinePairFunc WebPSamplers[/* MODE_LAST */];
|
||||
// Sampling functions to convert rows of YUV to RGB(A)
|
||||
extern WebPSamplerRowFunc WebPSamplers[/* MODE_LAST */];
|
||||
|
||||
// General function for converting two lines of ARGB or RGBA.
|
||||
// 'alpha_is_last' should be true if 0xff000000 is stored in memory as
|
||||
@ -179,13 +314,84 @@ typedef void (*WebPYUV444Converter)(const uint8_t* y,
|
||||
const uint8_t* u, const uint8_t* v,
|
||||
uint8_t* dst, int len);
|
||||
|
||||
extern const WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */];
|
||||
extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */];
|
||||
|
||||
// Main function to be called
|
||||
// Must be called before using the WebPUpsamplers[] (and for premultiplied
|
||||
// colorspaces like rgbA, rgbA4444, etc)
|
||||
void WebPInitUpsamplers(void);
|
||||
// Must be called before using WebPSamplers[]
|
||||
void WebPInitSamplers(void);
|
||||
// Must be called before using WebPYUV444Converters[]
|
||||
void WebPInitYUV444Converters(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Pre-multiply planes with alpha values
|
||||
// ARGB -> YUV converters
|
||||
|
||||
// Convert ARGB samples to luma Y.
|
||||
extern void (*WebPConvertARGBToY)(const uint32_t* argb, uint8_t* y, int width);
|
||||
// Convert ARGB samples to U/V with downsampling. do_store should be '1' for
|
||||
// even lines and '0' for odd ones. 'src_width' is the original width, not
|
||||
// the U/V one.
|
||||
extern void (*WebPConvertARGBToUV)(const uint32_t* argb, uint8_t* u, uint8_t* v,
|
||||
int src_width, int do_store);
|
||||
|
||||
// Convert a row of accumulated (four-values) of rgba32 toward U/V
|
||||
extern void (*WebPConvertRGBA32ToUV)(const uint16_t* rgb,
|
||||
uint8_t* u, uint8_t* v, int width);
|
||||
|
||||
// Convert RGB or BGR to Y
|
||||
extern void (*WebPConvertRGB24ToY)(const uint8_t* rgb, uint8_t* y, int width);
|
||||
extern void (*WebPConvertBGR24ToY)(const uint8_t* bgr, uint8_t* y, int width);
|
||||
|
||||
// used for plain-C fallback.
|
||||
extern void WebPConvertARGBToUV_C(const uint32_t* argb, uint8_t* u, uint8_t* v,
|
||||
int src_width, int do_store);
|
||||
extern void WebPConvertRGBA32ToUV_C(const uint16_t* rgb,
|
||||
uint8_t* u, uint8_t* v, int width);
|
||||
|
||||
// Must be called before using the above.
|
||||
void WebPInitConvertARGBToYUV(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Rescaler
|
||||
|
||||
struct WebPRescaler;
|
||||
|
||||
// Import a row of data and save its contribution in the rescaler.
|
||||
// 'channel' denotes the channel number to be imported. 'Expand' corresponds to
|
||||
// the wrk->x_expand case. Otherwise, 'Shrink' is to be used.
|
||||
typedef void (*WebPRescalerImportRowFunc)(struct WebPRescaler* const wrk,
|
||||
const uint8_t* src);
|
||||
|
||||
extern WebPRescalerImportRowFunc WebPRescalerImportRowExpand;
|
||||
extern WebPRescalerImportRowFunc WebPRescalerImportRowShrink;
|
||||
|
||||
// Export one row (starting at x_out position) from rescaler.
|
||||
// 'Expand' corresponds to the wrk->y_expand case.
|
||||
// Otherwise 'Shrink' is to be used
|
||||
typedef void (*WebPRescalerExportRowFunc)(struct WebPRescaler* const wrk);
|
||||
extern WebPRescalerExportRowFunc WebPRescalerExportRowExpand;
|
||||
extern WebPRescalerExportRowFunc WebPRescalerExportRowShrink;
|
||||
|
||||
// Plain-C implementation, as fall-back.
|
||||
extern void WebPRescalerImportRowExpandC(struct WebPRescaler* const wrk,
|
||||
const uint8_t* src);
|
||||
extern void WebPRescalerImportRowShrinkC(struct WebPRescaler* const wrk,
|
||||
const uint8_t* src);
|
||||
extern void WebPRescalerExportRowExpandC(struct WebPRescaler* const wrk);
|
||||
extern void WebPRescalerExportRowShrinkC(struct WebPRescaler* const wrk);
|
||||
|
||||
// Main entry calls:
|
||||
extern void WebPRescalerImportRow(struct WebPRescaler* const wrk,
|
||||
const uint8_t* src);
|
||||
// Export one row (starting at x_out position) from rescaler.
|
||||
extern void WebPRescalerExportRow(struct WebPRescaler* const wrk);
|
||||
|
||||
// Must be called first before using the above.
|
||||
void WebPRescalerDspInit(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Utilities for processing transparent channel.
|
||||
|
||||
// Apply alpha pre-multiply on an rgba, bgra or argb plane of size w * h.
|
||||
// alpha_first should be 0 for argb, 1 for rgba or bgra (where alpha is last).
|
||||
@ -196,14 +402,98 @@ extern void (*WebPApplyAlphaMultiply)(
|
||||
extern void (*WebPApplyAlphaMultiply4444)(
|
||||
uint8_t* rgba4444, int w, int h, int stride);
|
||||
|
||||
// To be called first before using the above.
|
||||
void WebPInitPremultiply(void);
|
||||
// Dispatch the values from alpha[] plane to the ARGB destination 'dst'.
|
||||
// Returns true if alpha[] plane has non-trivial values different from 0xff.
|
||||
extern int (*WebPDispatchAlpha)(const uint8_t* alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint8_t* dst, int dst_stride);
|
||||
|
||||
void WebPInitPremultiplySSE2(void); // should not be called directly.
|
||||
// Transfer packed 8b alpha[] values to green channel in dst[], zero'ing the
|
||||
// A/R/B values. 'dst_stride' is the stride for dst[] in uint32_t units.
|
||||
extern void (*WebPDispatchAlphaToGreen)(const uint8_t* alpha, int alpha_stride,
|
||||
int width, int height,
|
||||
uint32_t* dst, int dst_stride);
|
||||
|
||||
// Extract the alpha values from 32b values in argb[] and pack them into alpha[]
|
||||
// (this is the opposite of WebPDispatchAlpha).
|
||||
// Returns true if there's only trivial 0xff alpha values.
|
||||
extern int (*WebPExtractAlpha)(const uint8_t* argb, int argb_stride,
|
||||
int width, int height,
|
||||
uint8_t* alpha, int alpha_stride);
|
||||
|
||||
// Pre-Multiply operation transforms x into x * A / 255 (where x=Y,R,G or B).
|
||||
// Un-Multiply operation transforms x into x * 255 / A.
|
||||
|
||||
// Pre-Multiply or Un-Multiply (if 'inverse' is true) argb values in a row.
|
||||
extern void (*WebPMultARGBRow)(uint32_t* const ptr, int width, int inverse);
|
||||
|
||||
// Same a WebPMultARGBRow(), but for several rows.
|
||||
void WebPMultARGBRows(uint8_t* ptr, int stride, int width, int num_rows,
|
||||
int inverse);
|
||||
|
||||
// Same for a row of single values, with side alpha values.
|
||||
extern void (*WebPMultRow)(uint8_t* const ptr, const uint8_t* const alpha,
|
||||
int width, int inverse);
|
||||
|
||||
// Same a WebPMultRow(), but for several 'num_rows' rows.
|
||||
void WebPMultRows(uint8_t* ptr, int stride,
|
||||
const uint8_t* alpha, int alpha_stride,
|
||||
int width, int num_rows, int inverse);
|
||||
|
||||
// Plain-C versions, used as fallback by some implementations.
|
||||
void WebPMultRowC(uint8_t* const ptr, const uint8_t* const alpha,
|
||||
int width, int inverse);
|
||||
void WebPMultARGBRowC(uint32_t* const ptr, int width, int inverse);
|
||||
|
||||
// To be called first before using the above.
|
||||
void WebPInitAlphaProcessing(void);
|
||||
|
||||
// ARGB packing function: a/r/g/b input is rgba or bgra order.
|
||||
extern void (*VP8PackARGB)(const uint8_t* a, const uint8_t* r,
|
||||
const uint8_t* g, const uint8_t* b, int len,
|
||||
uint32_t* out);
|
||||
|
||||
// RGB packing function. 'step' can be 3 or 4. r/g/b input is rgb or bgr order.
|
||||
extern void (*VP8PackRGB)(const uint8_t* r, const uint8_t* g, const uint8_t* b,
|
||||
int len, int step, uint32_t* out);
|
||||
|
||||
// To be called first before using the above.
|
||||
void VP8EncDspARGBInit(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Filter functions
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
typedef enum { // Filter types.
|
||||
WEBP_FILTER_NONE = 0,
|
||||
WEBP_FILTER_HORIZONTAL,
|
||||
WEBP_FILTER_VERTICAL,
|
||||
WEBP_FILTER_GRADIENT,
|
||||
WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1, // end marker
|
||||
WEBP_FILTER_BEST, // meta-types
|
||||
WEBP_FILTER_FAST
|
||||
} WEBP_FILTER_TYPE;
|
||||
|
||||
typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
|
||||
int stride, uint8_t* out);
|
||||
typedef void (*WebPUnfilterFunc)(int width, int height, int stride,
|
||||
int row, int num_rows, uint8_t* data);
|
||||
|
||||
// Filter the given data using the given predictor.
|
||||
// 'in' corresponds to a 2-dimensional pixel array of size (stride * height)
|
||||
// in raster order.
|
||||
// 'stride' is number of bytes per scan line (with possible padding).
|
||||
// 'out' should be pre-allocated.
|
||||
extern WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
|
||||
|
||||
// In-place reconstruct the original data from the given filtered data.
|
||||
// The reconstruction will be done for 'num_rows' rows starting from 'row'
|
||||
// (assuming rows upto 'row - 1' are already reconstructed).
|
||||
extern WebPUnfilterFunc WebPUnfilters[WEBP_FILTER_LAST];
|
||||
|
||||
// To be called first before using the above.
|
||||
void VP8FiltersInit(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,47 +1,34 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Speed-critical encoding functions.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h> // for abs()
|
||||
|
||||
#include "./dsp.h"
|
||||
#include "../enc/vp8enci.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
static WEBP_INLINE uint8_t clip_8b(int v) {
|
||||
return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
|
||||
}
|
||||
|
||||
static WEBP_INLINE int clip_max(int v, int max) {
|
||||
return (v > max) ? max : v;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Compute susceptibility based on DCT-coeff histograms:
|
||||
// the higher, the "easier" the macroblock is to compress.
|
||||
|
||||
static int ClipAlpha(int alpha) {
|
||||
return alpha < 0 ? 0 : alpha > 255 ? 255 : alpha;
|
||||
}
|
||||
|
||||
int VP8GetAlpha(const int histo[MAX_COEFF_THRESH + 1]) {
|
||||
int num = 0, den = 0, val = 0;
|
||||
int k;
|
||||
int alpha;
|
||||
// note: changing this loop to avoid the numerous "k + 1" slows things down.
|
||||
for (k = 0; k < MAX_COEFF_THRESH; ++k) {
|
||||
if (histo[k + 1]) {
|
||||
val += histo[k + 1];
|
||||
num += val * (k + 1);
|
||||
den += (k + 1) * (k + 1);
|
||||
}
|
||||
}
|
||||
// we scale the value to a usable [0..255] range
|
||||
alpha = den ? 10 * num / den - 5 : 0;
|
||||
return ClipAlpha(alpha);
|
||||
}
|
||||
|
||||
const int VP8DspScan[16 + 4 + 4] = {
|
||||
// Luma
|
||||
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
|
||||
@ -53,27 +40,41 @@ const int VP8DspScan[16 + 4 + 4] = {
|
||||
8 + 0 * BPS, 12 + 0 * BPS, 8 + 4 * BPS, 12 + 4 * BPS // V
|
||||
};
|
||||
|
||||
static int CollectHistogram(const uint8_t* ref, const uint8_t* pred,
|
||||
int start_block, int end_block) {
|
||||
int histo[MAX_COEFF_THRESH + 1] = { 0 };
|
||||
int16_t out[16];
|
||||
int j, k;
|
||||
for (j = start_block; j < end_block; ++j) {
|
||||
VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out);
|
||||
|
||||
// Convert coefficients to bin (within out[]).
|
||||
for (k = 0; k < 16; ++k) {
|
||||
const int v = abs(out[k]) >> 2;
|
||||
out[k] = (v > MAX_COEFF_THRESH) ? MAX_COEFF_THRESH : v;
|
||||
}
|
||||
|
||||
// Use bin to update histogram.
|
||||
for (k = 0; k < 16; ++k) {
|
||||
histo[out[k]]++;
|
||||
// general-purpose util function
|
||||
void VP8SetHistogramData(const int distribution[MAX_COEFF_THRESH + 1],
|
||||
VP8Histogram* const histo) {
|
||||
int max_value = 0, last_non_zero = 1;
|
||||
int k;
|
||||
for (k = 0; k <= MAX_COEFF_THRESH; ++k) {
|
||||
const int value = distribution[k];
|
||||
if (value > 0) {
|
||||
if (value > max_value) max_value = value;
|
||||
last_non_zero = k;
|
||||
}
|
||||
}
|
||||
histo->max_value = max_value;
|
||||
histo->last_non_zero = last_non_zero;
|
||||
}
|
||||
|
||||
return VP8GetAlpha(histo);
|
||||
static void CollectHistogram(const uint8_t* ref, const uint8_t* pred,
|
||||
int start_block, int end_block,
|
||||
VP8Histogram* const histo) {
|
||||
int j;
|
||||
int distribution[MAX_COEFF_THRESH + 1] = { 0 };
|
||||
for (j = start_block; j < end_block; ++j) {
|
||||
int k;
|
||||
int16_t out[16];
|
||||
|
||||
VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out);
|
||||
|
||||
// Convert coefficients to bin.
|
||||
for (k = 0; k < 16; ++k) {
|
||||
const int v = abs(out[k]) >> 3; // TODO(skal): add rounding?
|
||||
const int clipped_value = clip_max(v, MAX_COEFF_THRESH);
|
||||
++distribution[clipped_value];
|
||||
}
|
||||
}
|
||||
VP8SetHistogramData(distribution, histo);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -85,19 +86,16 @@ static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
|
||||
// and make sure it's set to true _last_ (so as to be thread-safe)
|
||||
static volatile int tables_ok = 0;
|
||||
|
||||
static void InitTables(void) {
|
||||
static WEBP_TSAN_IGNORE_FUNCTION void InitTables(void) {
|
||||
if (!tables_ok) {
|
||||
int i;
|
||||
for (i = -255; i <= 255 + 255; ++i) {
|
||||
clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
|
||||
clip1[255 + i] = clip_8b(i);
|
||||
}
|
||||
tables_ok = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t clip_8b(int v) {
|
||||
return (!(v & ~0xff)) ? v : v < 0 ? 0 : 255;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Transforms (Paragraph 14.4)
|
||||
@ -154,84 +152,63 @@ static void FTransform(const uint8_t* src, const uint8_t* ref, int16_t* out) {
|
||||
int i;
|
||||
int tmp[16];
|
||||
for (i = 0; i < 4; ++i, src += BPS, ref += BPS) {
|
||||
const int d0 = src[0] - ref[0];
|
||||
const int d0 = src[0] - ref[0]; // 9bit dynamic range ([-255,255])
|
||||
const int d1 = src[1] - ref[1];
|
||||
const int d2 = src[2] - ref[2];
|
||||
const int d3 = src[3] - ref[3];
|
||||
const int a0 = (d0 + d3) << 3;
|
||||
const int a1 = (d1 + d2) << 3;
|
||||
const int a2 = (d1 - d2) << 3;
|
||||
const int a3 = (d0 - d3) << 3;
|
||||
tmp[0 + i * 4] = (a0 + a1);
|
||||
tmp[1 + i * 4] = (a2 * 2217 + a3 * 5352 + 14500) >> 12;
|
||||
tmp[2 + i * 4] = (a0 - a1);
|
||||
tmp[3 + i * 4] = (a3 * 2217 - a2 * 5352 + 7500) >> 12;
|
||||
const int a0 = (d0 + d3); // 10b [-510,510]
|
||||
const int a1 = (d1 + d2);
|
||||
const int a2 = (d1 - d2);
|
||||
const int a3 = (d0 - d3);
|
||||
tmp[0 + i * 4] = (a0 + a1) * 8; // 14b [-8160,8160]
|
||||
tmp[1 + i * 4] = (a2 * 2217 + a3 * 5352 + 1812) >> 9; // [-7536,7542]
|
||||
tmp[2 + i * 4] = (a0 - a1) * 8;
|
||||
tmp[3 + i * 4] = (a3 * 2217 - a2 * 5352 + 937) >> 9;
|
||||
}
|
||||
for (i = 0; i < 4; ++i) {
|
||||
const int a0 = (tmp[0 + i] + tmp[12 + i]);
|
||||
const int a0 = (tmp[0 + i] + tmp[12 + i]); // 15b
|
||||
const int a1 = (tmp[4 + i] + tmp[ 8 + i]);
|
||||
const int a2 = (tmp[4 + i] - tmp[ 8 + i]);
|
||||
const int a3 = (tmp[0 + i] - tmp[12 + i]);
|
||||
out[0 + i] = (a0 + a1 + 7) >> 4;
|
||||
out[0 + i] = (a0 + a1 + 7) >> 4; // 12b
|
||||
out[4 + i] = ((a2 * 2217 + a3 * 5352 + 12000) >> 16) + (a3 != 0);
|
||||
out[8 + i] = (a0 - a1 + 7) >> 4;
|
||||
out[12+ i] = ((a3 * 2217 - a2 * 5352 + 51000) >> 16);
|
||||
}
|
||||
}
|
||||
|
||||
static void ITransformWHT(const int16_t* in, int16_t* out) {
|
||||
int tmp[16];
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
const int a0 = in[0 + i] + in[12 + i];
|
||||
const int a1 = in[4 + i] + in[ 8 + i];
|
||||
const int a2 = in[4 + i] - in[ 8 + i];
|
||||
const int a3 = in[0 + i] - in[12 + i];
|
||||
tmp[0 + i] = a0 + a1;
|
||||
tmp[8 + i] = a0 - a1;
|
||||
tmp[4 + i] = a3 + a2;
|
||||
tmp[12 + i] = a3 - a2;
|
||||
}
|
||||
for (i = 0; i < 4; ++i) {
|
||||
const int dc = tmp[0 + i * 4] + 3; // w/ rounder
|
||||
const int a0 = dc + tmp[3 + i * 4];
|
||||
const int a1 = tmp[1 + i * 4] + tmp[2 + i * 4];
|
||||
const int a2 = tmp[1 + i * 4] - tmp[2 + i * 4];
|
||||
const int a3 = dc - tmp[3 + i * 4];
|
||||
out[ 0] = (a0 + a1) >> 3;
|
||||
out[16] = (a3 + a2) >> 3;
|
||||
out[32] = (a0 - a1) >> 3;
|
||||
out[48] = (a3 - a2) >> 3;
|
||||
out += 64;
|
||||
}
|
||||
static void FTransform2(const uint8_t* src, const uint8_t* ref, int16_t* out) {
|
||||
VP8FTransform(src, ref, out);
|
||||
VP8FTransform(src + 4, ref + 4, out + 16);
|
||||
}
|
||||
|
||||
static void FTransformWHT(const int16_t* in, int16_t* out) {
|
||||
int tmp[16];
|
||||
// input is 12b signed
|
||||
int32_t tmp[16];
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i, in += 64) {
|
||||
const int a0 = (in[0 * 16] + in[2 * 16]) << 2;
|
||||
const int a1 = (in[1 * 16] + in[3 * 16]) << 2;
|
||||
const int a2 = (in[1 * 16] - in[3 * 16]) << 2;
|
||||
const int a3 = (in[0 * 16] - in[2 * 16]) << 2;
|
||||
tmp[0 + i * 4] = (a0 + a1) + (a0 != 0);
|
||||
const int a0 = (in[0 * 16] + in[2 * 16]); // 13b
|
||||
const int a1 = (in[1 * 16] + in[3 * 16]);
|
||||
const int a2 = (in[1 * 16] - in[3 * 16]);
|
||||
const int a3 = (in[0 * 16] - in[2 * 16]);
|
||||
tmp[0 + i * 4] = a0 + a1; // 14b
|
||||
tmp[1 + i * 4] = a3 + a2;
|
||||
tmp[2 + i * 4] = a3 - a2;
|
||||
tmp[3 + i * 4] = a0 - a1;
|
||||
}
|
||||
for (i = 0; i < 4; ++i) {
|
||||
const int a0 = (tmp[0 + i] + tmp[8 + i]);
|
||||
const int a0 = (tmp[0 + i] + tmp[8 + i]); // 15b
|
||||
const int a1 = (tmp[4 + i] + tmp[12+ i]);
|
||||
const int a2 = (tmp[4 + i] - tmp[12+ i]);
|
||||
const int a3 = (tmp[0 + i] - tmp[8 + i]);
|
||||
const int b0 = a0 + a1;
|
||||
const int b0 = a0 + a1; // 16b
|
||||
const int b1 = a3 + a2;
|
||||
const int b2 = a3 - a2;
|
||||
const int b3 = a0 - a1;
|
||||
out[ 0 + i] = (b0 + (b0 > 0) + 3) >> 3;
|
||||
out[ 4 + i] = (b1 + (b1 > 0) + 3) >> 3;
|
||||
out[ 8 + i] = (b2 + (b2 > 0) + 3) >> 3;
|
||||
out[12 + i] = (b3 + (b3 > 0) + 3) >> 3;
|
||||
out[ 0 + i] = b0 >> 1; // 15b
|
||||
out[ 4 + i] = b1 >> 1;
|
||||
out[ 8 + i] = b2 >> 1;
|
||||
out[12 + i] = b3 >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -241,8 +218,6 @@ static void FTransformWHT(const int16_t* in, int16_t* out) {
|
||||
//------------------------------------------------------------------------------
|
||||
// Intra predictions
|
||||
|
||||
#define DST(x, y) dst[(x) + (y) * BPS]
|
||||
|
||||
static WEBP_INLINE void Fill(uint8_t* dst, int value, int size) {
|
||||
int j;
|
||||
for (j = 0; j < size; ++j) {
|
||||
@ -253,7 +228,7 @@ static WEBP_INLINE void Fill(uint8_t* dst, int value, int size) {
|
||||
static WEBP_INLINE void VerticalPred(uint8_t* dst,
|
||||
const uint8_t* top, int size) {
|
||||
int j;
|
||||
if (top) {
|
||||
if (top != NULL) {
|
||||
for (j = 0; j < size; ++j) memcpy(dst + j * BPS, top, size);
|
||||
} else {
|
||||
Fill(dst, 127, size);
|
||||
@ -262,7 +237,7 @@ static WEBP_INLINE void VerticalPred(uint8_t* dst,
|
||||
|
||||
static WEBP_INLINE void HorizontalPred(uint8_t* dst,
|
||||
const uint8_t* left, int size) {
|
||||
if (left) {
|
||||
if (left != NULL) {
|
||||
int j;
|
||||
for (j = 0; j < size; ++j) {
|
||||
memset(dst + j * BPS, left[j], size);
|
||||
@ -275,8 +250,8 @@ static WEBP_INLINE void HorizontalPred(uint8_t* dst,
|
||||
static WEBP_INLINE void TrueMotion(uint8_t* dst, const uint8_t* left,
|
||||
const uint8_t* top, int size) {
|
||||
int y;
|
||||
if (left) {
|
||||
if (top) {
|
||||
if (left != NULL) {
|
||||
if (top != NULL) {
|
||||
const uint8_t* const clip = clip1 + 255 - left[-1];
|
||||
for (y = 0; y < size; ++y) {
|
||||
const uint8_t* const clip_table = clip + left[y];
|
||||
@ -294,7 +269,7 @@ static WEBP_INLINE void TrueMotion(uint8_t* dst, const uint8_t* left,
|
||||
// is equivalent to VE prediction where you just copy the top samples.
|
||||
// Note that if top samples are not available, the default value is
|
||||
// then 129, and not 127 as in the VerticalPred case.
|
||||
if (top) {
|
||||
if (top != NULL) {
|
||||
VerticalPred(dst, top, size);
|
||||
} else {
|
||||
Fill(dst, 129, size);
|
||||
@ -307,15 +282,15 @@ static WEBP_INLINE void DCMode(uint8_t* dst, const uint8_t* left,
|
||||
int size, int round, int shift) {
|
||||
int DC = 0;
|
||||
int j;
|
||||
if (top) {
|
||||
if (top != NULL) {
|
||||
for (j = 0; j < size; ++j) DC += top[j];
|
||||
if (left) { // top and left present
|
||||
if (left != NULL) { // top and left present
|
||||
for (j = 0; j < size; ++j) DC += left[j];
|
||||
} else { // top, but no left
|
||||
DC += DC;
|
||||
}
|
||||
DC = (DC + round) >> shift;
|
||||
} else if (left) { // left but no top
|
||||
} else if (left != NULL) { // left but no top
|
||||
for (j = 0; j < size; ++j) DC += left[j];
|
||||
DC += DC;
|
||||
DC = (DC + round) >> shift;
|
||||
@ -337,8 +312,8 @@ static void IntraChromaPreds(uint8_t* dst, const uint8_t* left,
|
||||
TrueMotion(C8TM8 + dst, left, top, 8);
|
||||
// V block
|
||||
dst += 8;
|
||||
if (top) top += 8;
|
||||
if (left) left += 16;
|
||||
if (top != NULL) top += 8;
|
||||
if (left != NULL) left += 16;
|
||||
DCMode(C8DC8 + dst, left, top, 8, 8, 4);
|
||||
VerticalPred(C8VE8 + dst, top, 8);
|
||||
HorizontalPred(C8HE8 + dst, left, 8);
|
||||
@ -359,6 +334,7 @@ static void Intra16Preds(uint8_t* dst,
|
||||
//------------------------------------------------------------------------------
|
||||
// luma 4x4 prediction
|
||||
|
||||
#define DST(x, y) dst[(x) + (y) * BPS]
|
||||
#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
|
||||
#define AVG2(a, b) (((a) + (b) + 1) >> 1)
|
||||
|
||||
@ -589,30 +565,30 @@ static int TTransform(const uint8_t* in, const uint16_t* w) {
|
||||
int i;
|
||||
// horizontal pass
|
||||
for (i = 0; i < 4; ++i, in += BPS) {
|
||||
const int a0 = (in[0] + in[2]) << 2;
|
||||
const int a1 = (in[1] + in[3]) << 2;
|
||||
const int a2 = (in[1] - in[3]) << 2;
|
||||
const int a3 = (in[0] - in[2]) << 2;
|
||||
tmp[0 + i * 4] = a0 + a1 + (a0 != 0);
|
||||
const int a0 = in[0] + in[2];
|
||||
const int a1 = in[1] + in[3];
|
||||
const int a2 = in[1] - in[3];
|
||||
const int a3 = in[0] - in[2];
|
||||
tmp[0 + i * 4] = a0 + a1;
|
||||
tmp[1 + i * 4] = a3 + a2;
|
||||
tmp[2 + i * 4] = a3 - a2;
|
||||
tmp[3 + i * 4] = a0 - a1;
|
||||
}
|
||||
// vertical pass
|
||||
for (i = 0; i < 4; ++i, ++w) {
|
||||
const int a0 = (tmp[0 + i] + tmp[8 + i]);
|
||||
const int a1 = (tmp[4 + i] + tmp[12+ i]);
|
||||
const int a2 = (tmp[4 + i] - tmp[12+ i]);
|
||||
const int a3 = (tmp[0 + i] - tmp[8 + i]);
|
||||
const int a0 = tmp[0 + i] + tmp[8 + i];
|
||||
const int a1 = tmp[4 + i] + tmp[12+ i];
|
||||
const int a2 = tmp[4 + i] - tmp[12+ i];
|
||||
const int a3 = tmp[0 + i] - tmp[8 + i];
|
||||
const int b0 = a0 + a1;
|
||||
const int b1 = a3 + a2;
|
||||
const int b2 = a3 - a2;
|
||||
const int b3 = a0 - a1;
|
||||
// abs((b + (b<0) + 3) >> 3) = (abs(b) + 3) >> 3
|
||||
sum += w[ 0] * ((abs(b0) + 3) >> 3);
|
||||
sum += w[ 4] * ((abs(b1) + 3) >> 3);
|
||||
sum += w[ 8] * ((abs(b2) + 3) >> 3);
|
||||
sum += w[12] * ((abs(b3) + 3) >> 3);
|
||||
|
||||
sum += w[ 0] * abs(b0);
|
||||
sum += w[ 4] * abs(b1);
|
||||
sum += w[ 8] * abs(b2);
|
||||
sum += w[12] * abs(b3);
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
@ -621,7 +597,7 @@ static int Disto4x4(const uint8_t* const a, const uint8_t* const b,
|
||||
const uint16_t* const w) {
|
||||
const int sum1 = TTransform(a, w);
|
||||
const int sum2 = TTransform(b, w);
|
||||
return (abs(sum2 - sum1) + 8) >> 4;
|
||||
return abs(sum2 - sum1) >> 5;
|
||||
}
|
||||
|
||||
static int Disto16x16(const uint8_t* const a, const uint8_t* const b,
|
||||
@ -646,21 +622,57 @@ static const uint8_t kZigzag[16] = {
|
||||
|
||||
// Simple quantization
|
||||
static int QuantizeBlock(int16_t in[16], int16_t out[16],
|
||||
int n, const VP8Matrix* const mtx) {
|
||||
const VP8Matrix* const mtx) {
|
||||
int last = -1;
|
||||
for (; n < 16; ++n) {
|
||||
int n;
|
||||
for (n = 0; n < 16; ++n) {
|
||||
const int j = kZigzag[n];
|
||||
const int sign = (in[j] < 0);
|
||||
int coeff = (sign ? -in[j] : in[j]) + mtx->sharpen_[j];
|
||||
if (coeff > 2047) coeff = 2047;
|
||||
const uint32_t coeff = (sign ? -in[j] : in[j]) + mtx->sharpen_[j];
|
||||
if (coeff > mtx->zthresh_[j]) {
|
||||
const int Q = mtx->q_[j];
|
||||
const int iQ = mtx->iq_[j];
|
||||
const int B = mtx->bias_[j];
|
||||
out[n] = QUANTDIV(coeff, iQ, B);
|
||||
if (sign) out[n] = -out[n];
|
||||
in[j] = out[n] * Q;
|
||||
if (out[n]) last = n;
|
||||
const uint32_t Q = mtx->q_[j];
|
||||
const uint32_t iQ = mtx->iq_[j];
|
||||
const uint32_t B = mtx->bias_[j];
|
||||
int level = QUANTDIV(coeff, iQ, B);
|
||||
if (level > MAX_LEVEL) level = MAX_LEVEL;
|
||||
if (sign) level = -level;
|
||||
in[j] = level * Q;
|
||||
out[n] = level;
|
||||
if (level) last = n;
|
||||
} else {
|
||||
out[n] = 0;
|
||||
in[j] = 0;
|
||||
}
|
||||
}
|
||||
return (last >= 0);
|
||||
}
|
||||
|
||||
static int Quantize2Blocks(int16_t in[32], int16_t out[32],
|
||||
const VP8Matrix* const mtx) {
|
||||
int nz;
|
||||
nz = VP8EncQuantizeBlock(in + 0 * 16, out + 0 * 16, mtx) << 0;
|
||||
nz |= VP8EncQuantizeBlock(in + 1 * 16, out + 1 * 16, mtx) << 1;
|
||||
return nz;
|
||||
}
|
||||
|
||||
static int QuantizeBlockWHT(int16_t in[16], int16_t out[16],
|
||||
const VP8Matrix* const mtx) {
|
||||
int n, last = -1;
|
||||
for (n = 0; n < 16; ++n) {
|
||||
const int j = kZigzag[n];
|
||||
const int sign = (in[j] < 0);
|
||||
const uint32_t coeff = sign ? -in[j] : in[j];
|
||||
assert(mtx->sharpen_[j] == 0);
|
||||
if (coeff > mtx->zthresh_[j]) {
|
||||
const uint32_t Q = mtx->q_[j];
|
||||
const uint32_t iQ = mtx->iq_[j];
|
||||
const uint32_t B = mtx->bias_[j];
|
||||
int level = QUANTDIV(coeff, iQ, B);
|
||||
if (level > MAX_LEVEL) level = MAX_LEVEL;
|
||||
if (sign) level = -level;
|
||||
in[j] = level * Q;
|
||||
out[n] = level;
|
||||
if (level) last = n;
|
||||
} else {
|
||||
out[n] = 0;
|
||||
in[j] = 0;
|
||||
@ -672,16 +684,22 @@ static int QuantizeBlock(int16_t in[16], int16_t out[16],
|
||||
//------------------------------------------------------------------------------
|
||||
// Block copy
|
||||
|
||||
static WEBP_INLINE void Copy(const uint8_t* src, uint8_t* dst, int size) {
|
||||
static WEBP_INLINE void Copy(const uint8_t* src, uint8_t* dst, int w, int h) {
|
||||
int y;
|
||||
for (y = 0; y < size; ++y) {
|
||||
memcpy(dst, src, size);
|
||||
for (y = 0; y < h; ++y) {
|
||||
memcpy(dst, src, w);
|
||||
src += BPS;
|
||||
dst += BPS;
|
||||
}
|
||||
}
|
||||
|
||||
static void Copy4x4(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 4); }
|
||||
static void Copy4x4(const uint8_t* src, uint8_t* dst) {
|
||||
Copy(src, dst, 4, 4);
|
||||
}
|
||||
|
||||
static void Copy16x8(const uint8_t* src, uint8_t* dst) {
|
||||
Copy(src, dst, 16, 8);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Initialization
|
||||
@ -691,7 +709,7 @@ static void Copy4x4(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 4); }
|
||||
VP8CHisto VP8CollectHistogram;
|
||||
VP8Idct VP8ITransform;
|
||||
VP8Fdct VP8FTransform;
|
||||
VP8WHT VP8ITransformWHT;
|
||||
VP8Fdct VP8FTransform2;
|
||||
VP8WHT VP8FTransformWHT;
|
||||
VP8Intra4Preds VP8EncPredLuma4;
|
||||
VP8IntraPreds VP8EncPredLuma16;
|
||||
@ -703,18 +721,32 @@ VP8Metric VP8SSE4x4;
|
||||
VP8WMetric VP8TDisto4x4;
|
||||
VP8WMetric VP8TDisto16x16;
|
||||
VP8QuantizeBlock VP8EncQuantizeBlock;
|
||||
VP8Quantize2Blocks VP8EncQuantize2Blocks;
|
||||
VP8QuantizeBlockWHT VP8EncQuantizeBlockWHT;
|
||||
VP8BlockCopy VP8Copy4x4;
|
||||
VP8BlockCopy VP8Copy16x8;
|
||||
|
||||
extern void VP8EncDspInitSSE2(void);
|
||||
extern void VP8EncDspInitSSE41(void);
|
||||
extern void VP8EncDspInitAVX2(void);
|
||||
extern void VP8EncDspInitNEON(void);
|
||||
extern void VP8EncDspInitMIPS32(void);
|
||||
extern void VP8EncDspInitMIPSdspR2(void);
|
||||
|
||||
void VP8EncDspInit(void) {
|
||||
static volatile VP8CPUInfo enc_last_cpuinfo_used =
|
||||
(VP8CPUInfo)&enc_last_cpuinfo_used;
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void VP8EncDspInit(void) {
|
||||
if (enc_last_cpuinfo_used == VP8GetCPUInfo) return;
|
||||
|
||||
VP8DspInit(); // common inverse transforms
|
||||
InitTables();
|
||||
|
||||
// default C implementations
|
||||
VP8CollectHistogram = CollectHistogram;
|
||||
VP8ITransform = ITransform;
|
||||
VP8FTransform = FTransform;
|
||||
VP8ITransformWHT = ITransformWHT;
|
||||
VP8FTransform2 = FTransform2;
|
||||
VP8FTransformWHT = FTransformWHT;
|
||||
VP8EncPredLuma4 = Intra4Preds;
|
||||
VP8EncPredLuma16 = Intra16Preds;
|
||||
@ -726,18 +758,43 @@ void VP8EncDspInit(void) {
|
||||
VP8TDisto4x4 = Disto4x4;
|
||||
VP8TDisto16x16 = Disto16x16;
|
||||
VP8EncQuantizeBlock = QuantizeBlock;
|
||||
VP8EncQuantize2Blocks = Quantize2Blocks;
|
||||
VP8EncQuantizeBlockWHT = QuantizeBlockWHT;
|
||||
VP8Copy4x4 = Copy4x4;
|
||||
VP8Copy16x8 = Copy16x8;
|
||||
|
||||
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
|
||||
if (VP8GetCPUInfo) {
|
||||
if (VP8GetCPUInfo != NULL) {
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
if (VP8GetCPUInfo(kSSE2)) {
|
||||
VP8EncDspInitSSE2();
|
||||
#if defined(WEBP_USE_SSE41)
|
||||
if (VP8GetCPUInfo(kSSE4_1)) {
|
||||
VP8EncDspInitSSE41();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#if defined(WEBP_USE_AVX2)
|
||||
if (VP8GetCPUInfo(kAVX2)) {
|
||||
VP8EncDspInitAVX2();
|
||||
}
|
||||
#endif
|
||||
#if defined(WEBP_USE_NEON)
|
||||
if (VP8GetCPUInfo(kNEON)) {
|
||||
VP8EncDspInitNEON();
|
||||
}
|
||||
#endif
|
||||
#if defined(WEBP_USE_MIPS32)
|
||||
if (VP8GetCPUInfo(kMIPS32)) {
|
||||
VP8EncDspInitMIPS32();
|
||||
}
|
||||
#endif
|
||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||
if (VP8GetCPUInfo(kMIPSdspR2)) {
|
||||
VP8EncDspInitMIPSdspR2();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
enc_last_cpuinfo_used = VP8GetCPUInfo;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Image transforms and color space conversion methods for lossless decoder.
|
||||
@ -13,15 +15,42 @@
|
||||
#ifndef WEBP_DSP_LOSSLESS_H_
|
||||
#define WEBP_DSP_LOSSLESS_H_
|
||||
|
||||
#include "../types.h"
|
||||
#include "../decode.h"
|
||||
#include "../webp/types.h"
|
||||
#include "../webp/decode.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#include "../enc/histogram.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
#include "../enc/delta_palettization.h"
|
||||
#endif // WEBP_EXPERIMENTAL_FEATURES
|
||||
|
||||
// Not a trivial literal symbol.
|
||||
#define VP8L_NON_TRIVIAL_SYM (0xffffffff)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Image transforms.
|
||||
// Decoding
|
||||
|
||||
typedef uint32_t (*VP8LPredictorFunc)(uint32_t left, const uint32_t* const top);
|
||||
extern VP8LPredictorFunc VP8LPredictors[16];
|
||||
|
||||
typedef void (*VP8LProcessBlueAndRedFunc)(uint32_t* argb_data, int num_pixels);
|
||||
extern VP8LProcessBlueAndRedFunc VP8LAddGreenToBlueAndRed;
|
||||
|
||||
typedef struct {
|
||||
// Note: the members are uint8_t, so that any negative values are
|
||||
// automatically converted to "mod 256" values.
|
||||
uint8_t green_to_red_;
|
||||
uint8_t green_to_blue_;
|
||||
uint8_t red_to_blue_;
|
||||
} VP8LMultipliers;
|
||||
typedef void (*VP8LTransformColorFunc)(const VP8LMultipliers* const m,
|
||||
uint32_t* argb_data, int num_pixels);
|
||||
extern VP8LTransformColorFunc VP8LTransformColorInverse;
|
||||
|
||||
struct VP8LTransform; // Defined in dec/vp8li.h.
|
||||
|
||||
@ -33,23 +62,110 @@ void VP8LInverseTransform(const struct VP8LTransform* const transform,
|
||||
int row_start, int row_end,
|
||||
const uint32_t* const in, uint32_t* const out);
|
||||
|
||||
// Subtracts green from blue and red channels.
|
||||
void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs);
|
||||
|
||||
void VP8LResidualImage(int width, int height, int bits,
|
||||
uint32_t* const argb, uint32_t* const argb_scratch,
|
||||
uint32_t* const image);
|
||||
|
||||
void VP8LColorSpaceTransform(int width, int height, int bits, int step,
|
||||
uint32_t* const argb, uint32_t* image);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Color space conversion.
|
||||
typedef void (*VP8LConvertFunc)(const uint32_t* src, int num_pixels,
|
||||
uint8_t* dst);
|
||||
extern VP8LConvertFunc VP8LConvertBGRAToRGB;
|
||||
extern VP8LConvertFunc VP8LConvertBGRAToRGBA;
|
||||
extern VP8LConvertFunc VP8LConvertBGRAToRGBA4444;
|
||||
extern VP8LConvertFunc VP8LConvertBGRAToRGB565;
|
||||
extern VP8LConvertFunc VP8LConvertBGRAToBGR;
|
||||
|
||||
// Converts from BGRA to other color spaces.
|
||||
void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels,
|
||||
WEBP_CSP_MODE out_colorspace, uint8_t* const rgba);
|
||||
|
||||
// color mapping related functions.
|
||||
static WEBP_INLINE uint32_t VP8GetARGBIndex(uint32_t idx) {
|
||||
return (idx >> 8) & 0xff;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t VP8GetAlphaIndex(uint8_t idx) {
|
||||
return idx;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t VP8GetARGBValue(uint32_t val) {
|
||||
return val;
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t VP8GetAlphaValue(uint32_t val) {
|
||||
return (val >> 8) & 0xff;
|
||||
}
|
||||
|
||||
typedef void (*VP8LMapARGBFunc)(const uint32_t* src,
|
||||
const uint32_t* const color_map,
|
||||
uint32_t* dst, int y_start,
|
||||
int y_end, int width);
|
||||
typedef void (*VP8LMapAlphaFunc)(const uint8_t* src,
|
||||
const uint32_t* const color_map,
|
||||
uint8_t* dst, int y_start,
|
||||
int y_end, int width);
|
||||
|
||||
extern VP8LMapARGBFunc VP8LMapColor32b;
|
||||
extern VP8LMapAlphaFunc VP8LMapColor8b;
|
||||
|
||||
// Similar to the static method ColorIndexInverseTransform() that is part of
|
||||
// lossless.c, but used only for alpha decoding. It takes uint8_t (rather than
|
||||
// uint32_t) arguments for 'src' and 'dst'.
|
||||
void VP8LColorIndexInverseTransformAlpha(
|
||||
const struct VP8LTransform* const transform, int y_start, int y_end,
|
||||
const uint8_t* src, uint8_t* dst);
|
||||
|
||||
// Expose some C-only fallback functions
|
||||
void VP8LTransformColorInverse_C(const VP8LMultipliers* const m,
|
||||
uint32_t* data, int num_pixels);
|
||||
|
||||
void VP8LConvertBGRAToRGB_C(const uint32_t* src, int num_pixels, uint8_t* dst);
|
||||
void VP8LConvertBGRAToRGBA_C(const uint32_t* src, int num_pixels, uint8_t* dst);
|
||||
void VP8LConvertBGRAToRGBA4444_C(const uint32_t* src,
|
||||
int num_pixels, uint8_t* dst);
|
||||
void VP8LConvertBGRAToRGB565_C(const uint32_t* src,
|
||||
int num_pixels, uint8_t* dst);
|
||||
void VP8LConvertBGRAToBGR_C(const uint32_t* src, int num_pixels, uint8_t* dst);
|
||||
void VP8LAddGreenToBlueAndRed_C(uint32_t* data, int num_pixels);
|
||||
|
||||
// Must be called before calling any of the above methods.
|
||||
void VP8LDspInit(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Encoding
|
||||
|
||||
extern VP8LProcessBlueAndRedFunc VP8LSubtractGreenFromBlueAndRed;
|
||||
extern VP8LTransformColorFunc VP8LTransformColor;
|
||||
typedef void (*VP8LCollectColorBlueTransformsFunc)(
|
||||
const uint32_t* argb, int stride,
|
||||
int tile_width, int tile_height,
|
||||
int green_to_blue, int red_to_blue, int histo[]);
|
||||
extern VP8LCollectColorBlueTransformsFunc VP8LCollectColorBlueTransforms;
|
||||
|
||||
typedef void (*VP8LCollectColorRedTransformsFunc)(
|
||||
const uint32_t* argb, int stride,
|
||||
int tile_width, int tile_height,
|
||||
int green_to_red, int histo[]);
|
||||
extern VP8LCollectColorRedTransformsFunc VP8LCollectColorRedTransforms;
|
||||
|
||||
// Expose some C-only fallback functions
|
||||
void VP8LTransformColor_C(const VP8LMultipliers* const m,
|
||||
uint32_t* data, int num_pixels);
|
||||
void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels);
|
||||
void VP8LCollectColorRedTransforms_C(const uint32_t* argb, int stride,
|
||||
int tile_width, int tile_height,
|
||||
int green_to_red, int histo[]);
|
||||
void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride,
|
||||
int tile_width, int tile_height,
|
||||
int green_to_blue, int red_to_blue,
|
||||
int histo[]);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Image transforms.
|
||||
|
||||
void VP8LResidualImage(int width, int height, int bits, int low_effort,
|
||||
uint32_t* const argb, uint32_t* const argb_scratch,
|
||||
uint32_t* const image);
|
||||
|
||||
void VP8LColorSpaceTransform(int width, int height, int bits, int quality,
|
||||
uint32_t* const argb, uint32_t* image);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Misc methods.
|
||||
|
||||
@ -59,10 +175,136 @@ static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size,
|
||||
return (size + (1 << sampling_bits) - 1) >> sampling_bits;
|
||||
}
|
||||
|
||||
// Faster logarithm for integers, with the property of log2(0) == 0.
|
||||
float VP8LFastLog2(int v);
|
||||
// -----------------------------------------------------------------------------
|
||||
// Faster logarithm for integers. Small values use a look-up table.
|
||||
#define LOG_LOOKUP_IDX_MAX 256
|
||||
extern const float kLog2Table[LOG_LOOKUP_IDX_MAX];
|
||||
extern const float kSLog2Table[LOG_LOOKUP_IDX_MAX];
|
||||
typedef float (*VP8LFastLog2SlowFunc)(uint32_t v);
|
||||
|
||||
extern VP8LFastLog2SlowFunc VP8LFastLog2Slow;
|
||||
extern VP8LFastLog2SlowFunc VP8LFastSLog2Slow;
|
||||
|
||||
static WEBP_INLINE float VP8LFastLog2(uint32_t v) {
|
||||
return (v < LOG_LOOKUP_IDX_MAX) ? kLog2Table[v] : VP8LFastLog2Slow(v);
|
||||
}
|
||||
// Fast calculation of v * log2(v) for integer input.
|
||||
static WEBP_INLINE float VP8LFastSLog2(int v) { return VP8LFastLog2(v) * v; }
|
||||
static WEBP_INLINE float VP8LFastSLog2(uint32_t v) {
|
||||
return (v < LOG_LOOKUP_IDX_MAX) ? kSLog2Table[v] : VP8LFastSLog2Slow(v);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Huffman-cost related functions.
|
||||
|
||||
typedef double (*VP8LCostFunc)(const uint32_t* population, int length);
|
||||
typedef double (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y,
|
||||
int length);
|
||||
|
||||
extern VP8LCostFunc VP8LExtraCost;
|
||||
extern VP8LCostCombinedFunc VP8LExtraCostCombined;
|
||||
|
||||
typedef struct { // small struct to hold counters
|
||||
int counts[2]; // index: 0=zero steak, 1=non-zero streak
|
||||
int streaks[2][2]; // [zero/non-zero][streak<3 / streak>=3]
|
||||
} VP8LStreaks;
|
||||
|
||||
typedef VP8LStreaks (*VP8LCostCountFunc)(const uint32_t* population,
|
||||
int length);
|
||||
typedef VP8LStreaks (*VP8LCostCombinedCountFunc)(const uint32_t* X,
|
||||
const uint32_t* Y, int length);
|
||||
|
||||
extern VP8LCostCountFunc VP8LHuffmanCostCount;
|
||||
extern VP8LCostCombinedCountFunc VP8LHuffmanCostCombinedCount;
|
||||
|
||||
// Get the symbol entropy for the distribution 'population'.
|
||||
// Set 'trivial_sym', if there's only one symbol present in the distribution.
|
||||
double VP8LPopulationCost(const uint32_t* const population, int length,
|
||||
uint32_t* const trivial_sym);
|
||||
|
||||
// Get the combined symbol entropy for the distributions 'X' and 'Y'.
|
||||
double VP8LGetCombinedEntropy(const uint32_t* const X,
|
||||
const uint32_t* const Y, int length);
|
||||
|
||||
double VP8LBitsEntropy(const uint32_t* const array, int n,
|
||||
uint32_t* const trivial_symbol);
|
||||
|
||||
// Estimate how many bits the combined entropy of literals and distance
|
||||
// approximately maps to.
|
||||
double VP8LHistogramEstimateBits(const VP8LHistogram* const p);
|
||||
|
||||
// This function estimates the cost in bits excluding the bits needed to
|
||||
// represent the entropy code itself.
|
||||
double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p);
|
||||
|
||||
typedef void (*VP8LHistogramAddFunc)(const VP8LHistogram* const a,
|
||||
const VP8LHistogram* const b,
|
||||
VP8LHistogram* const out);
|
||||
extern VP8LHistogramAddFunc VP8LHistogramAdd;
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PrefixEncode()
|
||||
|
||||
static WEBP_INLINE int VP8LBitsLog2Ceiling(uint32_t n) {
|
||||
const int log_floor = BitsLog2Floor(n);
|
||||
if (n == (n & ~(n - 1))) // zero or a power of two.
|
||||
return log_floor;
|
||||
else
|
||||
return log_floor + 1;
|
||||
}
|
||||
|
||||
// Splitting of distance and length codes into prefixes and
|
||||
// extra bits. The prefixes are encoded with an entropy code
|
||||
// while the extra bits are stored just as normal bits.
|
||||
static WEBP_INLINE void VP8LPrefixEncodeBitsNoLUT(int distance, int* const code,
|
||||
int* const extra_bits) {
|
||||
const int highest_bit = BitsLog2Floor(--distance);
|
||||
const int second_highest_bit = (distance >> (highest_bit - 1)) & 1;
|
||||
*extra_bits = highest_bit - 1;
|
||||
*code = 2 * highest_bit + second_highest_bit;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8LPrefixEncodeNoLUT(int distance, int* const code,
|
||||
int* const extra_bits,
|
||||
int* const extra_bits_value) {
|
||||
const int highest_bit = BitsLog2Floor(--distance);
|
||||
const int second_highest_bit = (distance >> (highest_bit - 1)) & 1;
|
||||
*extra_bits = highest_bit - 1;
|
||||
*extra_bits_value = distance & ((1 << *extra_bits) - 1);
|
||||
*code = 2 * highest_bit + second_highest_bit;
|
||||
}
|
||||
|
||||
#define PREFIX_LOOKUP_IDX_MAX 512
|
||||
typedef struct {
|
||||
int8_t code_;
|
||||
int8_t extra_bits_;
|
||||
} VP8LPrefixCode;
|
||||
|
||||
// These tables are derived using VP8LPrefixEncodeNoLUT.
|
||||
extern const VP8LPrefixCode kPrefixEncodeCode[PREFIX_LOOKUP_IDX_MAX];
|
||||
extern const uint8_t kPrefixEncodeExtraBitsValue[PREFIX_LOOKUP_IDX_MAX];
|
||||
static WEBP_INLINE void VP8LPrefixEncodeBits(int distance, int* const code,
|
||||
int* const extra_bits) {
|
||||
if (distance < PREFIX_LOOKUP_IDX_MAX) {
|
||||
const VP8LPrefixCode prefix_code = kPrefixEncodeCode[distance];
|
||||
*code = prefix_code.code_;
|
||||
*extra_bits = prefix_code.extra_bits_;
|
||||
} else {
|
||||
VP8LPrefixEncodeBitsNoLUT(distance, code, extra_bits);
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8LPrefixEncode(int distance, int* const code,
|
||||
int* const extra_bits,
|
||||
int* const extra_bits_value) {
|
||||
if (distance < PREFIX_LOOKUP_IDX_MAX) {
|
||||
const VP8LPrefixCode prefix_code = kPrefixEncodeCode[distance];
|
||||
*code = prefix_code.code_;
|
||||
*extra_bits = prefix_code.extra_bits_;
|
||||
*extra_bits_value = kPrefixEncodeExtraBitsValue[distance];
|
||||
} else {
|
||||
VP8LPrefixEncodeNoLUT(distance, code, extra_bits, extra_bits_value);
|
||||
}
|
||||
}
|
||||
|
||||
// In-place difference of each component with mod 256.
|
||||
static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) {
|
||||
@ -73,9 +315,15 @@ static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) {
|
||||
return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu);
|
||||
}
|
||||
|
||||
void VP8LBundleColorMap(const uint8_t* const row, int width,
|
||||
int xbits, uint32_t* const dst);
|
||||
|
||||
// Must be called before calling any of the above methods.
|
||||
void VP8LEncDspInit(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// YUV to RGB upsampling functions.
|
||||
@ -12,9 +14,7 @@
|
||||
#include "./dsp.h"
|
||||
#include "./yuv.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Fancy upsampler
|
||||
@ -32,7 +32,7 @@ WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST];
|
||||
// ([3*a + b + 9*c + 3*d a + 3*b + 3*c + 9*d] [8 8]) / 16
|
||||
|
||||
// We process u and v together stashed into 32bit (16bit each).
|
||||
#define LOAD_UV(u,v) ((u) | ((v) << 16))
|
||||
#define LOAD_UV(u, v) ((u) | ((v) << 16))
|
||||
|
||||
#define UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
|
||||
static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
|
||||
@ -43,11 +43,12 @@ static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
|
||||
const int last_pixel_pair = (len - 1) >> 1; \
|
||||
uint32_t tl_uv = LOAD_UV(top_u[0], top_v[0]); /* top-left sample */ \
|
||||
uint32_t l_uv = LOAD_UV(cur_u[0], cur_v[0]); /* left-sample */ \
|
||||
if (top_y) { \
|
||||
assert(top_y != NULL); \
|
||||
{ \
|
||||
const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
|
||||
FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \
|
||||
} \
|
||||
if (bottom_y) { \
|
||||
if (bottom_y != NULL) { \
|
||||
const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
|
||||
FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \
|
||||
} \
|
||||
@ -58,7 +59,7 @@ static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
|
||||
const uint32_t avg = tl_uv + t_uv + l_uv + uv + 0x00080008u; \
|
||||
const uint32_t diag_12 = (avg + 2 * (t_uv + l_uv)) >> 3; \
|
||||
const uint32_t diag_03 = (avg + 2 * (tl_uv + uv)) >> 3; \
|
||||
if (top_y) { \
|
||||
{ \
|
||||
const uint32_t uv0 = (diag_12 + tl_uv) >> 1; \
|
||||
const uint32_t uv1 = (diag_03 + t_uv) >> 1; \
|
||||
FUNC(top_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
|
||||
@ -66,7 +67,7 @@ static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
|
||||
FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \
|
||||
top_dst + (2 * x - 0) * XSTEP); \
|
||||
} \
|
||||
if (bottom_y) { \
|
||||
if (bottom_y != NULL) { \
|
||||
const uint32_t uv0 = (diag_03 + l_uv) >> 1; \
|
||||
const uint32_t uv1 = (diag_12 + uv) >> 1; \
|
||||
FUNC(bottom_y[2 * x - 1], uv0 & 0xff, (uv0 >> 16), \
|
||||
@ -78,12 +79,12 @@ static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
|
||||
l_uv = uv; \
|
||||
} \
|
||||
if (!(len & 1)) { \
|
||||
if (top_y) { \
|
||||
{ \
|
||||
const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \
|
||||
FUNC(top_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
|
||||
top_dst + (len - 1) * XSTEP); \
|
||||
} \
|
||||
if (bottom_y) { \
|
||||
if (bottom_y != NULL) { \
|
||||
const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \
|
||||
FUNC(bottom_y[len - 1], uv0 & 0xff, (uv0 >> 16), \
|
||||
bottom_dst + (len - 1) * XSTEP); \
|
||||
@ -105,57 +106,6 @@ UPSAMPLE_FUNC(UpsampleRgb565LinePair, VP8YuvToRgb565, 2)
|
||||
|
||||
#endif // FANCY_UPSAMPLING
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// simple point-sampling
|
||||
|
||||
#define SAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
|
||||
static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
|
||||
const uint8_t* u, const uint8_t* v, \
|
||||
uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
|
||||
int i; \
|
||||
for (i = 0; i < len - 1; i += 2) { \
|
||||
FUNC(top_y[0], u[0], v[0], top_dst); \
|
||||
FUNC(top_y[1], u[0], v[0], top_dst + XSTEP); \
|
||||
FUNC(bottom_y[0], u[0], v[0], bottom_dst); \
|
||||
FUNC(bottom_y[1], u[0], v[0], bottom_dst + XSTEP); \
|
||||
top_y += 2; \
|
||||
bottom_y += 2; \
|
||||
u++; \
|
||||
v++; \
|
||||
top_dst += 2 * XSTEP; \
|
||||
bottom_dst += 2 * XSTEP; \
|
||||
} \
|
||||
if (i == len - 1) { /* last one */ \
|
||||
FUNC(top_y[0], u[0], v[0], top_dst); \
|
||||
FUNC(bottom_y[0], u[0], v[0], bottom_dst); \
|
||||
} \
|
||||
}
|
||||
|
||||
// All variants implemented.
|
||||
SAMPLE_FUNC(SampleRgbLinePair, VP8YuvToRgb, 3)
|
||||
SAMPLE_FUNC(SampleBgrLinePair, VP8YuvToBgr, 3)
|
||||
SAMPLE_FUNC(SampleRgbaLinePair, VP8YuvToRgba, 4)
|
||||
SAMPLE_FUNC(SampleBgraLinePair, VP8YuvToBgra, 4)
|
||||
SAMPLE_FUNC(SampleArgbLinePair, VP8YuvToArgb, 4)
|
||||
SAMPLE_FUNC(SampleRgba4444LinePair, VP8YuvToRgba4444, 2)
|
||||
SAMPLE_FUNC(SampleRgb565LinePair, VP8YuvToRgb565, 2)
|
||||
|
||||
#undef SAMPLE_FUNC
|
||||
|
||||
const WebPSampleLinePairFunc WebPSamplers[MODE_LAST] = {
|
||||
SampleRgbLinePair, // MODE_RGB
|
||||
SampleRgbaLinePair, // MODE_RGBA
|
||||
SampleBgrLinePair, // MODE_BGR
|
||||
SampleBgraLinePair, // MODE_BGRA
|
||||
SampleArgbLinePair, // MODE_ARGB
|
||||
SampleRgba4444LinePair, // MODE_RGBA_4444
|
||||
SampleRgb565LinePair, // MODE_RGB_565
|
||||
SampleRgbaLinePair, // MODE_rgbA
|
||||
SampleBgraLinePair, // MODE_bgrA
|
||||
SampleArgbLinePair, // MODE_Argb
|
||||
SampleRgba4444LinePair // MODE_rgbA_4444
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if !defined(FANCY_UPSAMPLING)
|
||||
@ -166,7 +116,8 @@ static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bot_y, \
|
||||
uint8_t* top_dst, uint8_t* bot_dst, int len) { \
|
||||
const int half_len = len >> 1; \
|
||||
int x; \
|
||||
if (top_dst != NULL) { \
|
||||
assert(top_dst != NULL); \
|
||||
{ \
|
||||
for (x = 0; x < half_len; ++x) { \
|
||||
FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x + 0); \
|
||||
FUNC(top_y[2 * x + 1], top_u[x], top_v[x], top_dst + 8 * x + 4); \
|
||||
@ -202,116 +153,75 @@ WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last) {
|
||||
// YUV444 converter
|
||||
|
||||
#define YUV444_FUNC(FUNC_NAME, FUNC, XSTEP) \
|
||||
static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
|
||||
uint8_t* dst, int len) { \
|
||||
extern void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
|
||||
uint8_t* dst, int len); \
|
||||
void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
|
||||
uint8_t* dst, int len) { \
|
||||
int i; \
|
||||
for (i = 0; i < len; ++i) FUNC(y[i], u[i], v[i], &dst[i * XSTEP]); \
|
||||
}
|
||||
|
||||
YUV444_FUNC(Yuv444ToRgb, VP8YuvToRgb, 3)
|
||||
YUV444_FUNC(Yuv444ToBgr, VP8YuvToBgr, 3)
|
||||
YUV444_FUNC(Yuv444ToRgba, VP8YuvToRgba, 4)
|
||||
YUV444_FUNC(Yuv444ToBgra, VP8YuvToBgra, 4)
|
||||
YUV444_FUNC(Yuv444ToArgb, VP8YuvToArgb, 4)
|
||||
YUV444_FUNC(Yuv444ToRgba4444, VP8YuvToRgba4444, 2)
|
||||
YUV444_FUNC(Yuv444ToRgb565, VP8YuvToRgb565, 2)
|
||||
YUV444_FUNC(WebPYuv444ToRgbC, VP8YuvToRgb, 3)
|
||||
YUV444_FUNC(WebPYuv444ToBgrC, VP8YuvToBgr, 3)
|
||||
YUV444_FUNC(WebPYuv444ToRgbaC, VP8YuvToRgba, 4)
|
||||
YUV444_FUNC(WebPYuv444ToBgraC, VP8YuvToBgra, 4)
|
||||
YUV444_FUNC(WebPYuv444ToArgbC, VP8YuvToArgb, 4)
|
||||
YUV444_FUNC(WebPYuv444ToRgba4444C, VP8YuvToRgba4444, 2)
|
||||
YUV444_FUNC(WebPYuv444ToRgb565C, VP8YuvToRgb565, 2)
|
||||
|
||||
#undef YUV444_FUNC
|
||||
|
||||
const WebPYUV444Converter WebPYUV444Converters[MODE_LAST] = {
|
||||
Yuv444ToRgb, // MODE_RGB
|
||||
Yuv444ToRgba, // MODE_RGBA
|
||||
Yuv444ToBgr, // MODE_BGR
|
||||
Yuv444ToBgra, // MODE_BGRA
|
||||
Yuv444ToArgb, // MODE_ARGB
|
||||
Yuv444ToRgba4444, // MODE_RGBA_4444
|
||||
Yuv444ToRgb565, // MODE_RGB_565
|
||||
Yuv444ToRgba, // MODE_rgbA
|
||||
Yuv444ToBgra, // MODE_bgrA
|
||||
Yuv444ToArgb, // MODE_Argb
|
||||
Yuv444ToRgba4444 // MODE_rgbA_4444
|
||||
};
|
||||
WebPYUV444Converter WebPYUV444Converters[MODE_LAST];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Premultiplied modes
|
||||
extern void WebPInitYUV444ConvertersMIPSdspR2(void);
|
||||
extern void WebPInitYUV444ConvertersSSE2(void);
|
||||
|
||||
// non dithered-modes
|
||||
static volatile VP8CPUInfo upsampling_last_cpuinfo_used1 =
|
||||
(VP8CPUInfo)&upsampling_last_cpuinfo_used1;
|
||||
|
||||
// (x * a * 32897) >> 23 is bit-wise equivalent to (int)(x * a / 255.)
|
||||
// for all 8bit x or a. For bit-wise equivalence to (int)(x * a / 255. + .5),
|
||||
// one can use instead: (x * a * 65793 + (1 << 23)) >> 24
|
||||
#if 1 // (int)(x * a / 255.)
|
||||
#define MULTIPLIER(a) ((a) * 32897UL)
|
||||
#define PREMULTIPLY(x, m) (((x) * (m)) >> 23)
|
||||
#else // (int)(x * a / 255. + .5)
|
||||
#define MULTIPLIER(a) ((a) * 65793UL)
|
||||
#define PREMULTIPLY(x, m) (((x) * (m) + (1UL << 23)) >> 24)
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444Converters(void) {
|
||||
if (upsampling_last_cpuinfo_used1 == VP8GetCPUInfo) return;
|
||||
|
||||
WebPYUV444Converters[MODE_RGB] = WebPYuv444ToRgbC;
|
||||
WebPYUV444Converters[MODE_RGBA] = WebPYuv444ToRgbaC;
|
||||
WebPYUV444Converters[MODE_BGR] = WebPYuv444ToBgrC;
|
||||
WebPYUV444Converters[MODE_BGRA] = WebPYuv444ToBgraC;
|
||||
WebPYUV444Converters[MODE_ARGB] = WebPYuv444ToArgbC;
|
||||
WebPYUV444Converters[MODE_RGBA_4444] = WebPYuv444ToRgba4444C;
|
||||
WebPYUV444Converters[MODE_RGB_565] = WebPYuv444ToRgb565C;
|
||||
WebPYUV444Converters[MODE_rgbA] = WebPYuv444ToRgbaC;
|
||||
WebPYUV444Converters[MODE_bgrA] = WebPYuv444ToBgraC;
|
||||
WebPYUV444Converters[MODE_Argb] = WebPYuv444ToArgbC;
|
||||
WebPYUV444Converters[MODE_rgbA_4444] = WebPYuv444ToRgba4444C;
|
||||
|
||||
if (VP8GetCPUInfo != NULL) {
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
if (VP8GetCPUInfo(kSSE2)) {
|
||||
WebPInitYUV444ConvertersSSE2();
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ApplyAlphaMultiply(uint8_t* rgba, int alpha_first,
|
||||
int w, int h, int stride) {
|
||||
while (h-- > 0) {
|
||||
uint8_t* const rgb = rgba + (alpha_first ? 1 : 0);
|
||||
const uint8_t* const alpha = rgba + (alpha_first ? 0 : 3);
|
||||
int i;
|
||||
for (i = 0; i < w; ++i) {
|
||||
const uint32_t a = alpha[4 * i];
|
||||
if (a != 0xff) {
|
||||
const uint32_t mult = MULTIPLIER(a);
|
||||
rgb[4 * i + 0] = PREMULTIPLY(rgb[4 * i + 0], mult);
|
||||
rgb[4 * i + 1] = PREMULTIPLY(rgb[4 * i + 1], mult);
|
||||
rgb[4 * i + 2] = PREMULTIPLY(rgb[4 * i + 2], mult);
|
||||
}
|
||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||
if (VP8GetCPUInfo(kMIPSdspR2)) {
|
||||
WebPInitYUV444ConvertersMIPSdspR2();
|
||||
}
|
||||
rgba += stride;
|
||||
#endif
|
||||
}
|
||||
upsampling_last_cpuinfo_used1 = VP8GetCPUInfo;
|
||||
}
|
||||
#undef MULTIPLIER
|
||||
#undef PREMULTIPLY
|
||||
|
||||
// rgbA4444
|
||||
|
||||
#define MULTIPLIER(a) ((a) * 0x1111) // 0x1111 ~= (1 << 16) / 15
|
||||
|
||||
static WEBP_INLINE uint8_t dither_hi(uint8_t x) {
|
||||
return (x & 0xf0) | (x >> 4);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t dither_lo(uint8_t x) {
|
||||
return (x & 0x0f) | (x << 4);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t multiply(uint8_t x, uint32_t m) {
|
||||
return (x * m) >> 16;
|
||||
}
|
||||
|
||||
static void ApplyAlphaMultiply4444(uint8_t* rgba4444,
|
||||
int w, int h, int stride) {
|
||||
while (h-- > 0) {
|
||||
int i;
|
||||
for (i = 0; i < w; ++i) {
|
||||
const uint8_t a = (rgba4444[2 * i + 1] & 0x0f);
|
||||
const uint32_t mult = MULTIPLIER(a);
|
||||
const uint8_t r = multiply(dither_hi(rgba4444[2 * i + 0]), mult);
|
||||
const uint8_t g = multiply(dither_lo(rgba4444[2 * i + 0]), mult);
|
||||
const uint8_t b = multiply(dither_hi(rgba4444[2 * i + 1]), mult);
|
||||
rgba4444[2 * i + 0] = (r & 0xf0) | ((g >> 4) & 0x0f);
|
||||
rgba4444[2 * i + 1] = (b & 0xf0) | a;
|
||||
}
|
||||
rgba4444 += stride;
|
||||
}
|
||||
}
|
||||
#undef MULTIPLIER
|
||||
|
||||
void (*WebPApplyAlphaMultiply)(uint8_t*, int, int, int, int)
|
||||
= ApplyAlphaMultiply;
|
||||
void (*WebPApplyAlphaMultiply4444)(uint8_t*, int, int, int)
|
||||
= ApplyAlphaMultiply4444;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main call
|
||||
// Main calls
|
||||
|
||||
extern void WebPInitUpsamplersSSE2(void);
|
||||
extern void WebPInitUpsamplersNEON(void);
|
||||
extern void WebPInitUpsamplersMIPSdspR2(void);
|
||||
|
||||
static volatile VP8CPUInfo upsampling_last_cpuinfo_used2 =
|
||||
(VP8CPUInfo)&upsampling_last_cpuinfo_used2;
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplers(void) {
|
||||
if (upsampling_last_cpuinfo_used2 == VP8GetCPUInfo) return;
|
||||
|
||||
void WebPInitUpsamplers(void) {
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair;
|
||||
WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair;
|
||||
@ -320,6 +230,10 @@ void WebPInitUpsamplers(void) {
|
||||
WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair;
|
||||
WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair;
|
||||
WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair;
|
||||
WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair;
|
||||
WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair;
|
||||
WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair;
|
||||
WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair;
|
||||
|
||||
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
|
||||
if (VP8GetCPUInfo != NULL) {
|
||||
@ -328,30 +242,19 @@ void WebPInitUpsamplers(void) {
|
||||
WebPInitUpsamplersSSE2();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif // FANCY_UPSAMPLING
|
||||
}
|
||||
|
||||
void WebPInitPremultiply(void) {
|
||||
WebPApplyAlphaMultiply = ApplyAlphaMultiply;
|
||||
WebPApplyAlphaMultiply4444 = ApplyAlphaMultiply4444;
|
||||
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair;
|
||||
WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair;
|
||||
WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair;
|
||||
WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair;
|
||||
|
||||
if (VP8GetCPUInfo != NULL) {
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
if (VP8GetCPUInfo(kSSE2)) {
|
||||
WebPInitPremultiplySSE2();
|
||||
#if defined(WEBP_USE_NEON)
|
||||
if (VP8GetCPUInfo(kNEON)) {
|
||||
WebPInitUpsamplersNEON();
|
||||
}
|
||||
#endif
|
||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||
if (VP8GetCPUInfo(kMIPSdspR2)) {
|
||||
WebPInitUpsamplersMIPSdspR2();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endif // FANCY_UPSAMPLING
|
||||
upsampling_last_cpuinfo_used2 = VP8GetCPUInfo;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// SSE2 version of YUV to RGB upsampling functions.
|
||||
@ -18,10 +20,6 @@
|
||||
#include <string.h>
|
||||
#include "./yuv.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
|
||||
// We compute (9*a + 3*b + 3*c + d + 8) / 16 as follows
|
||||
@ -49,23 +47,23 @@ extern "C" {
|
||||
(out) = _mm_sub_epi8(tmp0, tmp4); /* (k + in + 1) / 2 - lsb_correction */ \
|
||||
} while (0)
|
||||
|
||||
// pack and store two alterning pixel rows
|
||||
// pack and store two alternating pixel rows
|
||||
#define PACK_AND_STORE(a, b, da, db, out) do { \
|
||||
const __m128i ta = _mm_avg_epu8(a, da); /* (9a + 3b + 3c + d + 8) / 16 */ \
|
||||
const __m128i tb = _mm_avg_epu8(b, db); /* (3a + 9b + c + 3d + 8) / 16 */ \
|
||||
const __m128i t1 = _mm_unpacklo_epi8(ta, tb); \
|
||||
const __m128i t2 = _mm_unpackhi_epi8(ta, tb); \
|
||||
_mm_store_si128(((__m128i*)(out)) + 0, t1); \
|
||||
_mm_store_si128(((__m128i*)(out)) + 1, t2); \
|
||||
const __m128i t_a = _mm_avg_epu8(a, da); /* (9a + 3b + 3c + d + 8) / 16 */ \
|
||||
const __m128i t_b = _mm_avg_epu8(b, db); /* (3a + 9b + c + 3d + 8) / 16 */ \
|
||||
const __m128i t_1 = _mm_unpacklo_epi8(t_a, t_b); \
|
||||
const __m128i t_2 = _mm_unpackhi_epi8(t_a, t_b); \
|
||||
_mm_store_si128(((__m128i*)(out)) + 0, t_1); \
|
||||
_mm_store_si128(((__m128i*)(out)) + 1, t_2); \
|
||||
} while (0)
|
||||
|
||||
// Loads 17 pixels each from rows r1 and r2 and generates 32 pixels.
|
||||
#define UPSAMPLE_32PIXELS(r1, r2, out) { \
|
||||
const __m128i one = _mm_set1_epi8(1); \
|
||||
const __m128i a = _mm_loadu_si128((__m128i*)&(r1)[0]); \
|
||||
const __m128i b = _mm_loadu_si128((__m128i*)&(r1)[1]); \
|
||||
const __m128i c = _mm_loadu_si128((__m128i*)&(r2)[0]); \
|
||||
const __m128i d = _mm_loadu_si128((__m128i*)&(r2)[1]); \
|
||||
const __m128i a = _mm_loadu_si128((const __m128i*)&(r1)[0]); \
|
||||
const __m128i b = _mm_loadu_si128((const __m128i*)&(r1)[1]); \
|
||||
const __m128i c = _mm_loadu_si128((const __m128i*)&(r2)[0]); \
|
||||
const __m128i d = _mm_loadu_si128((const __m128i*)&(r2)[1]); \
|
||||
\
|
||||
const __m128i s = _mm_avg_epu8(a, d); /* s = (a + d + 1) / 2 */ \
|
||||
const __m128i t = _mm_avg_epu8(b, c); /* t = (b + c + 1) / 2 */ \
|
||||
@ -85,8 +83,8 @@ extern "C" {
|
||||
GET_M(ad, s, diag2); /* diag2 = (3a + b + c + 3d) / 8 */ \
|
||||
\
|
||||
/* pack the alternate pixels */ \
|
||||
PACK_AND_STORE(a, b, diag1, diag2, &(out)[0 * 32]); \
|
||||
PACK_AND_STORE(c, d, diag2, diag1, &(out)[2 * 32]); \
|
||||
PACK_AND_STORE(a, b, diag1, diag2, out + 0); /* store top */ \
|
||||
PACK_AND_STORE(c, d, diag2, diag1, out + 2 * 32); /* store bottom */ \
|
||||
}
|
||||
|
||||
// Turn the macro into a function for reducing code-size when non-critical
|
||||
@ -106,104 +104,140 @@ static void Upsample32Pixels(const uint8_t r1[], const uint8_t r2[],
|
||||
Upsample32Pixels(r1, r2, out); \
|
||||
}
|
||||
|
||||
#define CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, uv, \
|
||||
#define CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, \
|
||||
top_dst, bottom_dst, cur_x, num_pixels) { \
|
||||
int n; \
|
||||
if (top_y) { \
|
||||
for (n = 0; n < (num_pixels); ++n) { \
|
||||
FUNC(top_y[(cur_x) + n], (uv)[n], (uv)[32 + n], \
|
||||
top_dst + ((cur_x) + n) * XSTEP); \
|
||||
} \
|
||||
for (n = 0; n < (num_pixels); ++n) { \
|
||||
FUNC(top_y[(cur_x) + n], r_u[n], r_v[n], \
|
||||
top_dst + ((cur_x) + n) * XSTEP); \
|
||||
} \
|
||||
if (bottom_y) { \
|
||||
if (bottom_y != NULL) { \
|
||||
for (n = 0; n < (num_pixels); ++n) { \
|
||||
FUNC(bottom_y[(cur_x) + n], (uv)[64 + n], (uv)[64 + 32 + n], \
|
||||
FUNC(bottom_y[(cur_x) + n], r_u[64 + n], r_v[64 + n], \
|
||||
bottom_dst + ((cur_x) + n) * XSTEP); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
||||
#define CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, \
|
||||
top_dst, bottom_dst, cur_x) do { \
|
||||
FUNC##32(top_y + (cur_x), r_u, r_v, top_dst + (cur_x) * XSTEP); \
|
||||
if (bottom_y != NULL) { \
|
||||
FUNC##32(bottom_y + (cur_x), r_u + 64, r_v + 64, \
|
||||
bottom_dst + (cur_x) * XSTEP); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define SSE2_UPSAMPLE_FUNC(FUNC_NAME, FUNC, XSTEP) \
|
||||
static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bottom_y, \
|
||||
const uint8_t* top_u, const uint8_t* top_v, \
|
||||
const uint8_t* cur_u, const uint8_t* cur_v, \
|
||||
uint8_t* top_dst, uint8_t* bottom_dst, int len) { \
|
||||
int b; \
|
||||
/* 16 byte aligned array to cache reconstructed u and v */ \
|
||||
int uv_pos, pos; \
|
||||
/* 16byte-aligned array to cache reconstructed u and v */ \
|
||||
uint8_t uv_buf[4 * 32 + 15]; \
|
||||
uint8_t* const r_uv = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \
|
||||
const int uv_len = (len + 1) >> 1; \
|
||||
/* 17 pixels must be read-able for each block */ \
|
||||
const int num_blocks = (uv_len - 1) >> 4; \
|
||||
const int leftover = uv_len - num_blocks * 16; \
|
||||
const int last_pos = 1 + 32 * num_blocks; \
|
||||
uint8_t* const r_u = (uint8_t*)((uintptr_t)(uv_buf + 15) & ~15); \
|
||||
uint8_t* const r_v = r_u + 32; \
|
||||
\
|
||||
const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \
|
||||
const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \
|
||||
\
|
||||
assert(len > 0); \
|
||||
/* Treat the first pixel in regular way */ \
|
||||
if (top_y) { \
|
||||
const int u0 = (top_u[0] + u_diag) >> 1; \
|
||||
const int v0 = (top_v[0] + v_diag) >> 1; \
|
||||
FUNC(top_y[0], u0, v0, top_dst); \
|
||||
assert(top_y != NULL); \
|
||||
{ /* Treat the first pixel in regular way */ \
|
||||
const int u_diag = ((top_u[0] + cur_u[0]) >> 1) + 1; \
|
||||
const int v_diag = ((top_v[0] + cur_v[0]) >> 1) + 1; \
|
||||
const int u0_t = (top_u[0] + u_diag) >> 1; \
|
||||
const int v0_t = (top_v[0] + v_diag) >> 1; \
|
||||
FUNC(top_y[0], u0_t, v0_t, top_dst); \
|
||||
if (bottom_y != NULL) { \
|
||||
const int u0_b = (cur_u[0] + u_diag) >> 1; \
|
||||
const int v0_b = (cur_v[0] + v_diag) >> 1; \
|
||||
FUNC(bottom_y[0], u0_b, v0_b, bottom_dst); \
|
||||
} \
|
||||
} \
|
||||
if (bottom_y) { \
|
||||
const int u0 = (cur_u[0] + u_diag) >> 1; \
|
||||
const int v0 = (cur_v[0] + v_diag) >> 1; \
|
||||
FUNC(bottom_y[0], u0, v0, bottom_dst); \
|
||||
/* For UPSAMPLE_32PIXELS, 17 u/v values must be read-able for each block */ \
|
||||
for (pos = 1, uv_pos = 0; pos + 32 + 1 <= len; pos += 32, uv_pos += 16) { \
|
||||
UPSAMPLE_32PIXELS(top_u + uv_pos, cur_u + uv_pos, r_u); \
|
||||
UPSAMPLE_32PIXELS(top_v + uv_pos, cur_v + uv_pos, r_v); \
|
||||
CONVERT2RGB_32(FUNC, XSTEP, top_y, bottom_y, top_dst, bottom_dst, pos); \
|
||||
} \
|
||||
\
|
||||
for (b = 0; b < num_blocks; ++b) { \
|
||||
UPSAMPLE_32PIXELS(top_u, cur_u, r_uv + 0 * 32); \
|
||||
UPSAMPLE_32PIXELS(top_v, cur_v, r_uv + 1 * 32); \
|
||||
CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, r_uv, top_dst, bottom_dst, \
|
||||
32 * b + 1, 32) \
|
||||
top_u += 16; \
|
||||
cur_u += 16; \
|
||||
top_v += 16; \
|
||||
cur_v += 16; \
|
||||
if (len > 1) { \
|
||||
const int left_over = ((len + 1) >> 1) - (pos >> 1); \
|
||||
assert(left_over > 0); \
|
||||
UPSAMPLE_LAST_BLOCK(top_u + uv_pos, cur_u + uv_pos, left_over, r_u); \
|
||||
UPSAMPLE_LAST_BLOCK(top_v + uv_pos, cur_v + uv_pos, left_over, r_v); \
|
||||
CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, top_dst, bottom_dst, \
|
||||
pos, len - pos); \
|
||||
} \
|
||||
\
|
||||
UPSAMPLE_LAST_BLOCK(top_u, cur_u, leftover, r_uv + 0 * 32); \
|
||||
UPSAMPLE_LAST_BLOCK(top_v, cur_v, leftover, r_uv + 1 * 32); \
|
||||
CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, r_uv, top_dst, bottom_dst, \
|
||||
last_pos, len - last_pos); \
|
||||
}
|
||||
|
||||
// SSE2 variants of the fancy upsampler.
|
||||
SSE2_UPSAMPLE_FUNC(UpsampleRgbLinePairSSE2, VP8YuvToRgb, 3)
|
||||
SSE2_UPSAMPLE_FUNC(UpsampleBgrLinePairSSE2, VP8YuvToBgr, 3)
|
||||
SSE2_UPSAMPLE_FUNC(UpsampleRgbaLinePairSSE2, VP8YuvToRgba, 4)
|
||||
SSE2_UPSAMPLE_FUNC(UpsampleBgraLinePairSSE2, VP8YuvToBgra, 4)
|
||||
SSE2_UPSAMPLE_FUNC(UpsampleRgbLinePair, VP8YuvToRgb, 3)
|
||||
SSE2_UPSAMPLE_FUNC(UpsampleBgrLinePair, VP8YuvToBgr, 3)
|
||||
SSE2_UPSAMPLE_FUNC(UpsampleRgbaLinePair, VP8YuvToRgba, 4)
|
||||
SSE2_UPSAMPLE_FUNC(UpsampleBgraLinePair, VP8YuvToBgra, 4)
|
||||
|
||||
#undef GET_M
|
||||
#undef PACK_AND_STORE
|
||||
#undef UPSAMPLE_32PIXELS
|
||||
#undef UPSAMPLE_LAST_BLOCK
|
||||
#undef CONVERT2RGB
|
||||
#undef CONVERT2RGB_32
|
||||
#undef SSE2_UPSAMPLE_FUNC
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Entry point
|
||||
|
||||
extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
|
||||
|
||||
void WebPInitUpsamplersSSE2(void) {
|
||||
WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePairSSE2;
|
||||
WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePairSSE2;
|
||||
WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePairSSE2;
|
||||
WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePairSSE2;
|
||||
}
|
||||
extern void WebPInitUpsamplersSSE2(void);
|
||||
|
||||
void WebPInitPremultiplySSE2(void) {
|
||||
WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePairSSE2;
|
||||
WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePairSSE2;
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersSSE2(void) {
|
||||
VP8YUVInitSSE2();
|
||||
WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair;
|
||||
WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair;
|
||||
WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair;
|
||||
WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair;
|
||||
WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair;
|
||||
WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair;
|
||||
}
|
||||
|
||||
#endif // FANCY_UPSAMPLING
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#endif // WEBP_USE_SSE2
|
||||
extern WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */];
|
||||
extern void WebPInitYUV444ConvertersSSE2(void);
|
||||
|
||||
#define YUV444_FUNC(FUNC_NAME, CALL, XSTEP) \
|
||||
extern void WebP##FUNC_NAME##C(const uint8_t* y, const uint8_t* u, \
|
||||
const uint8_t* v, uint8_t* dst, int len); \
|
||||
static void FUNC_NAME(const uint8_t* y, const uint8_t* u, const uint8_t* v, \
|
||||
uint8_t* dst, int len) { \
|
||||
int i; \
|
||||
const int max_len = len & ~31; \
|
||||
for (i = 0; i < max_len; i += 32) CALL(y + i, u + i, v + i, dst + i * XSTEP);\
|
||||
if (i < len) { /* C-fallback */ \
|
||||
WebP##FUNC_NAME##C(y + i, u + i, v + i, dst + i * XSTEP, len - i); \
|
||||
} \
|
||||
}
|
||||
|
||||
YUV444_FUNC(Yuv444ToRgba, VP8YuvToRgba32, 4);
|
||||
YUV444_FUNC(Yuv444ToBgra, VP8YuvToBgra32, 4);
|
||||
YUV444_FUNC(Yuv444ToRgb, VP8YuvToRgb32, 3);
|
||||
YUV444_FUNC(Yuv444ToBgr, VP8YuvToBgr32, 3);
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitYUV444ConvertersSSE2(void) {
|
||||
VP8YUVInitSSE2();
|
||||
WebPYUV444Converters[MODE_RGBA] = Yuv444ToRgba;
|
||||
WebPYUV444Converters[MODE_BGRA] = Yuv444ToBgra;
|
||||
WebPYUV444Converters[MODE_RGB] = Yuv444ToRgb;
|
||||
WebPYUV444Converters[MODE_BGR] = Yuv444ToBgr;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
WEBP_DSP_INIT_STUB(WebPInitYUV444ConvertersSSE2)
|
||||
|
||||
#endif // WEBP_USE_SSE2
|
||||
|
||||
#if !(defined(FANCY_UPSAMPLING) && defined(WEBP_USE_SSE2))
|
||||
WEBP_DSP_INIT_STUB(WebPInitUpsamplersSSE2)
|
||||
#endif
|
||||
|
||||
@ -1,26 +1,19 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// YUV->RGB conversion function
|
||||
// YUV->RGB conversion functions
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "./yuv.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum { YUV_HALF = 1 << (YUV_FIX - 1) };
|
||||
|
||||
int16_t VP8kVToR[256], VP8kUToB[256];
|
||||
int32_t VP8kVToG[256], VP8kUToG[256];
|
||||
uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN];
|
||||
uint8_t VP8kClip4Bits[YUV_RANGE_MAX - YUV_RANGE_MIN];
|
||||
#if defined(WEBP_YUV_USE_TABLE)
|
||||
|
||||
static int done = 0;
|
||||
|
||||
@ -28,11 +21,17 @@ static WEBP_INLINE uint8_t clip(int v, int max_value) {
|
||||
return v < 0 ? 0 : v > max_value ? max_value : v;
|
||||
}
|
||||
|
||||
void VP8YUVInit(void) {
|
||||
int16_t VP8kVToR[256], VP8kUToB[256];
|
||||
int32_t VP8kVToG[256], VP8kUToG[256];
|
||||
uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN];
|
||||
uint8_t VP8kClip4Bits[YUV_RANGE_MAX - YUV_RANGE_MIN];
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void VP8YUVInit(void) {
|
||||
int i;
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
#ifndef USE_YUVj
|
||||
for (i = 0; i < 256; ++i) {
|
||||
VP8kVToR[i] = (89858 * (i - 128) + YUV_HALF) >> YUV_FIX;
|
||||
VP8kUToG[i] = -22014 * (i - 128) + YUV_HALF;
|
||||
@ -44,9 +43,238 @@ void VP8YUVInit(void) {
|
||||
VP8kClip[i - YUV_RANGE_MIN] = clip(k, 255);
|
||||
VP8kClip4Bits[i - YUV_RANGE_MIN] = clip((k + 8) >> 4, 15);
|
||||
}
|
||||
#else
|
||||
for (i = 0; i < 256; ++i) {
|
||||
VP8kVToR[i] = (91881 * (i - 128) + YUV_HALF) >> YUV_FIX;
|
||||
VP8kUToG[i] = -22554 * (i - 128) + YUV_HALF;
|
||||
VP8kVToG[i] = -46802 * (i - 128);
|
||||
VP8kUToB[i] = (116130 * (i - 128) + YUV_HALF) >> YUV_FIX;
|
||||
}
|
||||
for (i = YUV_RANGE_MIN; i < YUV_RANGE_MAX; ++i) {
|
||||
const int k = i;
|
||||
VP8kClip[i - YUV_RANGE_MIN] = clip(k, 255);
|
||||
VP8kClip4Bits[i - YUV_RANGE_MIN] = clip((k + 8) >> 4, 15);
|
||||
}
|
||||
#endif
|
||||
|
||||
done = 1;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
#else
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void VP8YUVInit(void) {}
|
||||
|
||||
#endif // WEBP_YUV_USE_TABLE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Plain-C version
|
||||
|
||||
#define ROW_FUNC(FUNC_NAME, FUNC, XSTEP) \
|
||||
static void FUNC_NAME(const uint8_t* y, \
|
||||
const uint8_t* u, const uint8_t* v, \
|
||||
uint8_t* dst, int len) { \
|
||||
const uint8_t* const end = dst + (len & ~1) * XSTEP; \
|
||||
while (dst != end) { \
|
||||
FUNC(y[0], u[0], v[0], dst); \
|
||||
FUNC(y[1], u[0], v[0], dst + XSTEP); \
|
||||
y += 2; \
|
||||
++u; \
|
||||
++v; \
|
||||
dst += 2 * XSTEP; \
|
||||
} \
|
||||
if (len & 1) { \
|
||||
FUNC(y[0], u[0], v[0], dst); \
|
||||
} \
|
||||
} \
|
||||
|
||||
// All variants implemented.
|
||||
ROW_FUNC(YuvToRgbRow, VP8YuvToRgb, 3)
|
||||
ROW_FUNC(YuvToBgrRow, VP8YuvToBgr, 3)
|
||||
ROW_FUNC(YuvToRgbaRow, VP8YuvToRgba, 4)
|
||||
ROW_FUNC(YuvToBgraRow, VP8YuvToBgra, 4)
|
||||
ROW_FUNC(YuvToArgbRow, VP8YuvToArgb, 4)
|
||||
ROW_FUNC(YuvToRgba4444Row, VP8YuvToRgba4444, 2)
|
||||
ROW_FUNC(YuvToRgb565Row, VP8YuvToRgb565, 2)
|
||||
|
||||
#undef ROW_FUNC
|
||||
|
||||
// Main call for processing a plane with a WebPSamplerRowFunc function:
|
||||
void WebPSamplerProcessPlane(const uint8_t* y, int y_stride,
|
||||
const uint8_t* u, const uint8_t* v, int uv_stride,
|
||||
uint8_t* dst, int dst_stride,
|
||||
int width, int height, WebPSamplerRowFunc func) {
|
||||
int j;
|
||||
for (j = 0; j < height; ++j) {
|
||||
func(y, u, v, dst, width);
|
||||
y += y_stride;
|
||||
if (j & 1) {
|
||||
u += uv_stride;
|
||||
v += uv_stride;
|
||||
}
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Main call
|
||||
|
||||
WebPSamplerRowFunc WebPSamplers[MODE_LAST];
|
||||
|
||||
extern void WebPInitSamplersSSE2(void);
|
||||
extern void WebPInitSamplersMIPS32(void);
|
||||
extern void WebPInitSamplersMIPSdspR2(void);
|
||||
|
||||
static volatile VP8CPUInfo yuv_last_cpuinfo_used =
|
||||
(VP8CPUInfo)&yuv_last_cpuinfo_used;
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplers(void) {
|
||||
if (yuv_last_cpuinfo_used == VP8GetCPUInfo) return;
|
||||
|
||||
WebPSamplers[MODE_RGB] = YuvToRgbRow;
|
||||
WebPSamplers[MODE_RGBA] = YuvToRgbaRow;
|
||||
WebPSamplers[MODE_BGR] = YuvToBgrRow;
|
||||
WebPSamplers[MODE_BGRA] = YuvToBgraRow;
|
||||
WebPSamplers[MODE_ARGB] = YuvToArgbRow;
|
||||
WebPSamplers[MODE_RGBA_4444] = YuvToRgba4444Row;
|
||||
WebPSamplers[MODE_RGB_565] = YuvToRgb565Row;
|
||||
WebPSamplers[MODE_rgbA] = YuvToRgbaRow;
|
||||
WebPSamplers[MODE_bgrA] = YuvToBgraRow;
|
||||
WebPSamplers[MODE_Argb] = YuvToArgbRow;
|
||||
WebPSamplers[MODE_rgbA_4444] = YuvToRgba4444Row;
|
||||
|
||||
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
|
||||
if (VP8GetCPUInfo != NULL) {
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
if (VP8GetCPUInfo(kSSE2)) {
|
||||
WebPInitSamplersSSE2();
|
||||
}
|
||||
#endif // WEBP_USE_SSE2
|
||||
#if defined(WEBP_USE_MIPS32)
|
||||
if (VP8GetCPUInfo(kMIPS32)) {
|
||||
WebPInitSamplersMIPS32();
|
||||
}
|
||||
#endif // WEBP_USE_MIPS32
|
||||
#if defined(WEBP_USE_MIPS_DSP_R2)
|
||||
if (VP8GetCPUInfo(kMIPSdspR2)) {
|
||||
WebPInitSamplersMIPSdspR2();
|
||||
}
|
||||
#endif // WEBP_USE_MIPS_DSP_R2
|
||||
}
|
||||
yuv_last_cpuinfo_used = VP8GetCPUInfo;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// ARGB -> YUV converters
|
||||
|
||||
static void ConvertARGBToY(const uint32_t* argb, uint8_t* y, int width) {
|
||||
int i;
|
||||
for (i = 0; i < width; ++i) {
|
||||
const uint32_t p = argb[i];
|
||||
y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff,
|
||||
YUV_HALF);
|
||||
}
|
||||
}
|
||||
|
||||
void WebPConvertARGBToUV_C(const uint32_t* argb, uint8_t* u, uint8_t* v,
|
||||
int src_width, int do_store) {
|
||||
// No rounding. Last pixel is dealt with separately.
|
||||
const int uv_width = src_width >> 1;
|
||||
int i;
|
||||
for (i = 0; i < uv_width; ++i) {
|
||||
const uint32_t v0 = argb[2 * i + 0];
|
||||
const uint32_t v1 = argb[2 * i + 1];
|
||||
// VP8RGBToU/V expects four accumulated pixels. Hence we need to
|
||||
// scale r/g/b value by a factor 2. We just shift v0/v1 one bit less.
|
||||
const int r = ((v0 >> 15) & 0x1fe) + ((v1 >> 15) & 0x1fe);
|
||||
const int g = ((v0 >> 7) & 0x1fe) + ((v1 >> 7) & 0x1fe);
|
||||
const int b = ((v0 << 1) & 0x1fe) + ((v1 << 1) & 0x1fe);
|
||||
const int tmp_u = VP8RGBToU(r, g, b, YUV_HALF << 2);
|
||||
const int tmp_v = VP8RGBToV(r, g, b, YUV_HALF << 2);
|
||||
if (do_store) {
|
||||
u[i] = tmp_u;
|
||||
v[i] = tmp_v;
|
||||
} else {
|
||||
// Approximated average-of-four. But it's an acceptable diff.
|
||||
u[i] = (u[i] + tmp_u + 1) >> 1;
|
||||
v[i] = (v[i] + tmp_v + 1) >> 1;
|
||||
}
|
||||
}
|
||||
if (src_width & 1) { // last pixel
|
||||
const uint32_t v0 = argb[2 * i + 0];
|
||||
const int r = (v0 >> 14) & 0x3fc;
|
||||
const int g = (v0 >> 6) & 0x3fc;
|
||||
const int b = (v0 << 2) & 0x3fc;
|
||||
const int tmp_u = VP8RGBToU(r, g, b, YUV_HALF << 2);
|
||||
const int tmp_v = VP8RGBToV(r, g, b, YUV_HALF << 2);
|
||||
if (do_store) {
|
||||
u[i] = tmp_u;
|
||||
v[i] = tmp_v;
|
||||
} else {
|
||||
u[i] = (u[i] + tmp_u + 1) >> 1;
|
||||
v[i] = (v[i] + tmp_v + 1) >> 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
static void ConvertRGB24ToY(const uint8_t* rgb, uint8_t* y, int width) {
|
||||
int i;
|
||||
for (i = 0; i < width; ++i, rgb += 3) {
|
||||
y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF);
|
||||
}
|
||||
}
|
||||
|
||||
static void ConvertBGR24ToY(const uint8_t* bgr, uint8_t* y, int width) {
|
||||
int i;
|
||||
for (i = 0; i < width; ++i, bgr += 3) {
|
||||
y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF);
|
||||
}
|
||||
}
|
||||
|
||||
void WebPConvertRGBA32ToUV_C(const uint16_t* rgb,
|
||||
uint8_t* u, uint8_t* v, int width) {
|
||||
int i;
|
||||
for (i = 0; i < width; i += 1, rgb += 4) {
|
||||
const int r = rgb[0], g = rgb[1], b = rgb[2];
|
||||
u[i] = VP8RGBToU(r, g, b, YUV_HALF << 2);
|
||||
v[i] = VP8RGBToV(r, g, b, YUV_HALF << 2);
|
||||
}
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
void (*WebPConvertRGB24ToY)(const uint8_t* rgb, uint8_t* y, int width);
|
||||
void (*WebPConvertBGR24ToY)(const uint8_t* bgr, uint8_t* y, int width);
|
||||
void (*WebPConvertRGBA32ToUV)(const uint16_t* rgb,
|
||||
uint8_t* u, uint8_t* v, int width);
|
||||
|
||||
void (*WebPConvertARGBToY)(const uint32_t* argb, uint8_t* y, int width);
|
||||
void (*WebPConvertARGBToUV)(const uint32_t* argb, uint8_t* u, uint8_t* v,
|
||||
int src_width, int do_store);
|
||||
|
||||
static volatile VP8CPUInfo rgba_to_yuv_last_cpuinfo_used =
|
||||
(VP8CPUInfo)&rgba_to_yuv_last_cpuinfo_used;
|
||||
|
||||
extern void WebPInitConvertARGBToYUVSSE2(void);
|
||||
|
||||
WEBP_TSAN_IGNORE_FUNCTION void WebPInitConvertARGBToYUV(void) {
|
||||
if (rgba_to_yuv_last_cpuinfo_used == VP8GetCPUInfo) return;
|
||||
|
||||
WebPConvertARGBToY = ConvertARGBToY;
|
||||
WebPConvertARGBToUV = WebPConvertARGBToUV_C;
|
||||
|
||||
WebPConvertRGB24ToY = ConvertRGB24ToY;
|
||||
WebPConvertBGR24ToY = ConvertBGR24ToY;
|
||||
|
||||
WebPConvertRGBA32ToUV = WebPConvertRGBA32ToUV_C;
|
||||
|
||||
if (VP8GetCPUInfo != NULL) {
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
if (VP8GetCPUInfo(kSSE2)) {
|
||||
WebPInitConvertARGBToYUVSSE2();
|
||||
}
|
||||
#endif // WEBP_USE_SSE2
|
||||
}
|
||||
rgba_to_yuv_last_cpuinfo_used = VP8GetCPUInfo;
|
||||
}
|
||||
|
||||
@ -1,36 +1,165 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// inline YUV<->RGB conversion function
|
||||
//
|
||||
// The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
|
||||
// More information at: http://en.wikipedia.org/wiki/YCbCr
|
||||
// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
|
||||
// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
|
||||
// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
|
||||
// We use 16bit fixed point operations for RGB->YUV conversion (YUV_FIX).
|
||||
//
|
||||
// For the Y'CbCr to RGB conversion, the BT.601 specification reads:
|
||||
// R = 1.164 * (Y-16) + 1.596 * (V-128)
|
||||
// G = 1.164 * (Y-16) - 0.813 * (V-128) - 0.391 * (U-128)
|
||||
// B = 1.164 * (Y-16) + 2.018 * (U-128)
|
||||
// where Y is in the [16,235] range, and U/V in the [16,240] range.
|
||||
// In the table-lookup version (WEBP_YUV_USE_TABLE), the common factor
|
||||
// "1.164 * (Y-16)" can be handled as an offset in the VP8kClip[] table.
|
||||
// So in this case the formulae should read:
|
||||
// R = 1.164 * [Y + 1.371 * (V-128) ] - 18.624
|
||||
// G = 1.164 * [Y - 0.698 * (V-128) - 0.336 * (U-128)] - 18.624
|
||||
// B = 1.164 * [Y + 1.733 * (U-128)] - 18.624
|
||||
// once factorized.
|
||||
// For YUV->RGB conversion, only 14bit fixed precision is used (YUV_FIX2).
|
||||
// That's the maximum possible for a convenient ARM implementation.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_DSP_YUV_H_
|
||||
#define WEBP_DSP_YUV_H_
|
||||
|
||||
#include "./dsp.h"
|
||||
#include "../dec/decode_vp8.h"
|
||||
|
||||
// Define the following to use the LUT-based code:
|
||||
// #define WEBP_YUV_USE_TABLE
|
||||
|
||||
#if defined(WEBP_EXPERIMENTAL_FEATURES)
|
||||
// Do NOT activate this feature for real compression. This is only experimental!
|
||||
// This flag is for comparison purpose against JPEG's "YUVj" natural colorspace.
|
||||
// This colorspace is close to Rec.601's Y'CbCr model with the notable
|
||||
// difference of allowing larger range for luma/chroma.
|
||||
// See http://en.wikipedia.org/wiki/YCbCr#JPEG_conversion paragraph, and its
|
||||
// difference with http://en.wikipedia.org/wiki/YCbCr#ITU-R_BT.601_conversion
|
||||
// #define USE_YUVj
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// YUV -> RGB conversion
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
enum { YUV_FIX = 16, // fixed-point precision
|
||||
YUV_RANGE_MIN = -227, // min value of r/g/b output
|
||||
YUV_RANGE_MAX = 256 + 226 // max value of r/g/b output
|
||||
enum {
|
||||
YUV_FIX = 16, // fixed-point precision for RGB->YUV
|
||||
YUV_HALF = 1 << (YUV_FIX - 1),
|
||||
YUV_MASK = (256 << YUV_FIX) - 1,
|
||||
YUV_RANGE_MIN = -227, // min value of r/g/b output
|
||||
YUV_RANGE_MAX = 256 + 226, // max value of r/g/b output
|
||||
|
||||
YUV_FIX2 = 14, // fixed-point precision for YUV->RGB
|
||||
YUV_HALF2 = 1 << (YUV_FIX2 - 1),
|
||||
YUV_MASK2 = (256 << YUV_FIX2) - 1
|
||||
};
|
||||
|
||||
// These constants are 14b fixed-point version of ITU-R BT.601 constants.
|
||||
#define kYScale 19077 // 1.164 = 255 / 219
|
||||
#define kVToR 26149 // 1.596 = 255 / 112 * 0.701
|
||||
#define kUToG 6419 // 0.391 = 255 / 112 * 0.886 * 0.114 / 0.587
|
||||
#define kVToG 13320 // 0.813 = 255 / 112 * 0.701 * 0.299 / 0.587
|
||||
#define kUToB 33050 // 2.018 = 255 / 112 * 0.886
|
||||
#define kRCst (-kYScale * 16 - kVToR * 128 + YUV_HALF2)
|
||||
#define kGCst (-kYScale * 16 + kUToG * 128 + kVToG * 128 + YUV_HALF2)
|
||||
#define kBCst (-kYScale * 16 - kUToB * 128 + YUV_HALF2)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if !defined(WEBP_YUV_USE_TABLE)
|
||||
|
||||
// slower on x86 by ~7-8%, but bit-exact with the SSE2 version
|
||||
|
||||
static WEBP_INLINE int VP8Clip8(int v) {
|
||||
return ((v & ~YUV_MASK2) == 0) ? (v >> YUV_FIX2) : (v < 0) ? 0 : 255;
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8YUVToR(int y, int v) {
|
||||
return VP8Clip8(kYScale * y + kVToR * v + kRCst);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8YUVToG(int y, int u, int v) {
|
||||
return VP8Clip8(kYScale * y - kUToG * u - kVToG * v + kGCst);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8YUVToB(int y, int u) {
|
||||
return VP8Clip8(kYScale * y + kUToB * u + kBCst);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToRgb(int y, int u, int v,
|
||||
uint8_t* const rgb) {
|
||||
rgb[0] = VP8YUVToR(y, v);
|
||||
rgb[1] = VP8YUVToG(y, u, v);
|
||||
rgb[2] = VP8YUVToB(y, u);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToBgr(int y, int u, int v,
|
||||
uint8_t* const bgr) {
|
||||
bgr[0] = VP8YUVToB(y, u);
|
||||
bgr[1] = VP8YUVToG(y, u, v);
|
||||
bgr[2] = VP8YUVToR(y, v);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToRgb565(int y, int u, int v,
|
||||
uint8_t* const rgb) {
|
||||
const int r = VP8YUVToR(y, v); // 5 usable bits
|
||||
const int g = VP8YUVToG(y, u, v); // 6 usable bits
|
||||
const int b = VP8YUVToB(y, u); // 5 usable bits
|
||||
const int rg = (r & 0xf8) | (g >> 5);
|
||||
const int gb = ((g << 3) & 0xe0) | (b >> 3);
|
||||
#ifdef WEBP_SWAP_16BIT_CSP
|
||||
rgb[0] = gb;
|
||||
rgb[1] = rg;
|
||||
#else
|
||||
rgb[0] = rg;
|
||||
rgb[1] = gb;
|
||||
#endif
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToRgba4444(int y, int u, int v,
|
||||
uint8_t* const argb) {
|
||||
const int r = VP8YUVToR(y, v); // 4 usable bits
|
||||
const int g = VP8YUVToG(y, u, v); // 4 usable bits
|
||||
const int b = VP8YUVToB(y, u); // 4 usable bits
|
||||
const int rg = (r & 0xf0) | (g >> 4);
|
||||
const int ba = (b & 0xf0) | 0x0f; // overwrite the lower 4 bits
|
||||
#ifdef WEBP_SWAP_16BIT_CSP
|
||||
argb[0] = ba;
|
||||
argb[1] = rg;
|
||||
#else
|
||||
argb[0] = rg;
|
||||
argb[1] = ba;
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// Table-based version, not totally equivalent to the SSE2 version.
|
||||
// Rounding diff is only +/-1 though.
|
||||
|
||||
extern int16_t VP8kVToR[256], VP8kUToB[256];
|
||||
extern int32_t VP8kVToG[256], VP8kUToG[256];
|
||||
extern uint8_t VP8kClip[YUV_RANGE_MAX - YUV_RANGE_MIN];
|
||||
extern uint8_t VP8kClip4Bits[YUV_RANGE_MAX - YUV_RANGE_MIN];
|
||||
|
||||
static WEBP_INLINE void VP8YuvToRgb(uint8_t y, uint8_t u, uint8_t v,
|
||||
static WEBP_INLINE void VP8YuvToRgb(int y, int u, int v,
|
||||
uint8_t* const rgb) {
|
||||
const int r_off = VP8kVToR[v];
|
||||
const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
|
||||
@ -40,35 +169,7 @@ static WEBP_INLINE void VP8YuvToRgb(uint8_t y, uint8_t u, uint8_t v,
|
||||
rgb[2] = VP8kClip[y + b_off - YUV_RANGE_MIN];
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToRgb565(uint8_t y, uint8_t u, uint8_t v,
|
||||
uint8_t* const rgb) {
|
||||
const int r_off = VP8kVToR[v];
|
||||
const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
|
||||
const int b_off = VP8kUToB[u];
|
||||
rgb[0] = ((VP8kClip[y + r_off - YUV_RANGE_MIN] & 0xf8) |
|
||||
(VP8kClip[y + g_off - YUV_RANGE_MIN] >> 5));
|
||||
rgb[1] = (((VP8kClip[y + g_off - YUV_RANGE_MIN] << 3) & 0xe0) |
|
||||
(VP8kClip[y + b_off - YUV_RANGE_MIN] >> 3));
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToArgb(uint8_t y, uint8_t u, uint8_t v,
|
||||
uint8_t* const argb) {
|
||||
argb[0] = 0xff;
|
||||
VP8YuvToRgb(y, u, v, argb + 1);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToRgba4444(uint8_t y, uint8_t u, uint8_t v,
|
||||
uint8_t* const argb) {
|
||||
const int r_off = VP8kVToR[v];
|
||||
const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
|
||||
const int b_off = VP8kUToB[u];
|
||||
// Don't update alpha (last 4 bits of argb[1])
|
||||
argb[0] = ((VP8kClip4Bits[y + r_off - YUV_RANGE_MIN] << 4) |
|
||||
VP8kClip4Bits[y + g_off - YUV_RANGE_MIN]);
|
||||
argb[1] = 0x0f | (VP8kClip4Bits[y + b_off - YUV_RANGE_MIN] << 4);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToBgr(uint8_t y, uint8_t u, uint8_t v,
|
||||
static WEBP_INLINE void VP8YuvToBgr(int y, int u, int v,
|
||||
uint8_t* const bgr) {
|
||||
const int r_off = VP8kVToR[v];
|
||||
const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
|
||||
@ -78,6 +179,52 @@ static WEBP_INLINE void VP8YuvToBgr(uint8_t y, uint8_t u, uint8_t v,
|
||||
bgr[2] = VP8kClip[y + r_off - YUV_RANGE_MIN];
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToRgb565(int y, int u, int v,
|
||||
uint8_t* const rgb) {
|
||||
const int r_off = VP8kVToR[v];
|
||||
const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
|
||||
const int b_off = VP8kUToB[u];
|
||||
const int rg = ((VP8kClip[y + r_off - YUV_RANGE_MIN] & 0xf8) |
|
||||
(VP8kClip[y + g_off - YUV_RANGE_MIN] >> 5));
|
||||
const int gb = (((VP8kClip[y + g_off - YUV_RANGE_MIN] << 3) & 0xe0) |
|
||||
(VP8kClip[y + b_off - YUV_RANGE_MIN] >> 3));
|
||||
#ifdef WEBP_SWAP_16BIT_CSP
|
||||
rgb[0] = gb;
|
||||
rgb[1] = rg;
|
||||
#else
|
||||
rgb[0] = rg;
|
||||
rgb[1] = gb;
|
||||
#endif
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToRgba4444(int y, int u, int v,
|
||||
uint8_t* const argb) {
|
||||
const int r_off = VP8kVToR[v];
|
||||
const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX;
|
||||
const int b_off = VP8kUToB[u];
|
||||
const int rg = ((VP8kClip4Bits[y + r_off - YUV_RANGE_MIN] << 4) |
|
||||
VP8kClip4Bits[y + g_off - YUV_RANGE_MIN]);
|
||||
const int ba = (VP8kClip4Bits[y + b_off - YUV_RANGE_MIN] << 4) | 0x0f;
|
||||
#ifdef WEBP_SWAP_16BIT_CSP
|
||||
argb[0] = ba;
|
||||
argb[1] = rg;
|
||||
#else
|
||||
argb[0] = rg;
|
||||
argb[1] = ba;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif // WEBP_YUV_USE_TABLE
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Alpha handling variants
|
||||
|
||||
static WEBP_INLINE void VP8YuvToArgb(uint8_t y, uint8_t u, uint8_t v,
|
||||
uint8_t* const argb) {
|
||||
argb[0] = 0xff;
|
||||
VP8YuvToRgb(y, u, v, argb + 1);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8YuvToBgra(uint8_t y, uint8_t u, uint8_t v,
|
||||
uint8_t* const bgra) {
|
||||
VP8YuvToBgr(y, u, v, bgra);
|
||||
@ -93,35 +240,79 @@ static WEBP_INLINE void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v,
|
||||
// Must be called before everything, to initialize the tables.
|
||||
void VP8YUVInit(void);
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// SSE2 extra functions (mostly for upsampling_sse2.c)
|
||||
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
|
||||
// When the following is defined, tables are initialized statically, adding ~12k
|
||||
// to the binary size. Otherwise, they are initialized at run-time (small cost).
|
||||
#define WEBP_YUV_USE_SSE2_TABLES
|
||||
|
||||
// Process 32 pixels and store the result (24b or 32b per pixel) in *dst.
|
||||
void VP8YuvToRgba32(const uint8_t* y, const uint8_t* u, const uint8_t* v,
|
||||
uint8_t* dst);
|
||||
void VP8YuvToRgb32(const uint8_t* y, const uint8_t* u, const uint8_t* v,
|
||||
uint8_t* dst);
|
||||
void VP8YuvToBgra32(const uint8_t* y, const uint8_t* u, const uint8_t* v,
|
||||
uint8_t* dst);
|
||||
void VP8YuvToBgr32(const uint8_t* y, const uint8_t* u, const uint8_t* v,
|
||||
uint8_t* dst);
|
||||
|
||||
// Must be called to initialize tables before using the functions.
|
||||
void VP8YUVInitSSE2(void);
|
||||
|
||||
#endif // WEBP_USE_SSE2
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RGB -> YUV conversion
|
||||
// The exact naming is Y'CbCr, following the ITU-R BT.601 standard.
|
||||
// More information at: http://en.wikipedia.org/wiki/YCbCr
|
||||
// Y = 0.2569 * R + 0.5044 * G + 0.0979 * B + 16
|
||||
// U = -0.1483 * R - 0.2911 * G + 0.4394 * B + 128
|
||||
// V = 0.4394 * R - 0.3679 * G - 0.0715 * B + 128
|
||||
// We use 16bit fixed point operations.
|
||||
|
||||
static WEBP_INLINE int VP8ClipUV(int v) {
|
||||
v = (v + (257 << (YUV_FIX + 2 - 1))) >> (YUV_FIX + 2);
|
||||
return ((v & ~0xff) == 0) ? v : (v < 0) ? 0 : 255;
|
||||
// Stub functions that can be called with various rounding values:
|
||||
static WEBP_INLINE int VP8ClipUV(int uv, int rounding) {
|
||||
uv = (uv + rounding + (128 << (YUV_FIX + 2))) >> (YUV_FIX + 2);
|
||||
return ((uv & ~0xff) == 0) ? uv : (uv < 0) ? 0 : 255;
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8RGBToY(int r, int g, int b) {
|
||||
const int kRound = (1 << (YUV_FIX - 1)) + (16 << YUV_FIX);
|
||||
#ifndef USE_YUVj
|
||||
|
||||
static WEBP_INLINE int VP8RGBToY(int r, int g, int b, int rounding) {
|
||||
const int luma = 16839 * r + 33059 * g + 6420 * b;
|
||||
return (luma + kRound) >> YUV_FIX; // no need to clip
|
||||
return (luma + rounding + (16 << YUV_FIX)) >> YUV_FIX; // no need to clip
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8RGBToU(int r, int g, int b) {
|
||||
return VP8ClipUV(-9719 * r - 19081 * g + 28800 * b);
|
||||
static WEBP_INLINE int VP8RGBToU(int r, int g, int b, int rounding) {
|
||||
const int u = -9719 * r - 19081 * g + 28800 * b;
|
||||
return VP8ClipUV(u, rounding);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8RGBToV(int r, int g, int b) {
|
||||
return VP8ClipUV(+28800 * r - 24116 * g - 4684 * b);
|
||||
static WEBP_INLINE int VP8RGBToV(int r, int g, int b, int rounding) {
|
||||
const int v = +28800 * r - 24116 * g - 4684 * b;
|
||||
return VP8ClipUV(v, rounding);
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#else
|
||||
|
||||
// This JPEG-YUV colorspace, only for comparison!
|
||||
// These are also 16bit precision coefficients from Rec.601, but with full
|
||||
// [0..255] output range.
|
||||
static WEBP_INLINE int VP8RGBToY(int r, int g, int b, int rounding) {
|
||||
const int luma = 19595 * r + 38470 * g + 7471 * b;
|
||||
return (luma + rounding) >> YUV_FIX; // no need to clip
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8RGBToU(int r, int g, int b, int rounding) {
|
||||
const int u = -11058 * r - 21710 * g + 32768 * b;
|
||||
return VP8ClipUV(u, rounding);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8RGBToV(int r, int g, int b, int rounding) {
|
||||
const int v = 32768 * r - 27439 * g - 5329 * b;
|
||||
return VP8ClipUV(v, rounding);
|
||||
}
|
||||
|
||||
#endif // USE_YUVj
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Alpha-plane compression.
|
||||
@ -13,13 +15,11 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./vp8enci.h"
|
||||
#include "../dsp/dsp.h"
|
||||
#include "../utils/filters.h"
|
||||
#include "../utils/quant_levels.h"
|
||||
#include "../format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "../utils/utils.h"
|
||||
#include "../webp/format_constants.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Encodes the given alpha data via specified compression method 'method'.
|
||||
@ -36,7 +36,7 @@ extern "C" {
|
||||
//
|
||||
// 'output' corresponds to the buffer containing compressed alpha data.
|
||||
// This buffer is allocated by this method and caller should call
|
||||
// free(*output) when done.
|
||||
// WebPSafeFree(*output) when done.
|
||||
// 'output_size' corresponds to size of this compressed alpha buffer.
|
||||
//
|
||||
// Returns 1 on successfully encoding the alpha and
|
||||
@ -48,12 +48,11 @@ extern "C" {
|
||||
|
||||
static int EncodeLossless(const uint8_t* const data, int width, int height,
|
||||
int effort_level, // in [0..6] range
|
||||
VP8BitWriter* const bw,
|
||||
VP8LBitWriter* const bw,
|
||||
WebPAuxStats* const stats) {
|
||||
int ok = 0;
|
||||
WebPConfig config;
|
||||
WebPPicture picture;
|
||||
VP8LBitWriter tmp_bw;
|
||||
|
||||
WebPPictureInit(&picture);
|
||||
picture.width = width;
|
||||
@ -63,53 +62,51 @@ static int EncodeLossless(const uint8_t* const data, int width, int height,
|
||||
if (!WebPPictureAlloc(&picture)) return 0;
|
||||
|
||||
// Transfer the alpha values to the green channel.
|
||||
{
|
||||
int i, j;
|
||||
uint32_t* dst = picture.argb;
|
||||
const uint8_t* src = data;
|
||||
for (j = 0; j < picture.height; ++j) {
|
||||
for (i = 0; i < picture.width; ++i) {
|
||||
dst[i] = (src[i] << 8) | 0xff000000u;
|
||||
}
|
||||
src += width;
|
||||
dst += picture.argb_stride;
|
||||
}
|
||||
}
|
||||
WebPDispatchAlphaToGreen(data, width, picture.width, picture.height,
|
||||
picture.argb, picture.argb_stride);
|
||||
|
||||
WebPConfigInit(&config);
|
||||
config.lossless = 1;
|
||||
config.method = effort_level; // impact is very small
|
||||
// Set moderate default quality setting for alpha. Higher qualities (80 and
|
||||
// above) could be very slow.
|
||||
config.quality = 10.f + 15.f * effort_level;
|
||||
if (config.quality > 100.f) config.quality = 100.f;
|
||||
// Set a low default quality for encoding alpha. Ensure that Alpha quality at
|
||||
// lower methods (3 and below) is less than the threshold for triggering
|
||||
// costly 'BackwardReferencesTraceBackwards'.
|
||||
config.quality = 8.f * effort_level;
|
||||
assert(config.quality >= 0 && config.quality <= 100.f);
|
||||
|
||||
ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3);
|
||||
ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_bw) == VP8_ENC_OK);
|
||||
ok = (VP8LEncodeStream(&config, &picture, bw) == VP8_ENC_OK);
|
||||
WebPPictureFree(&picture);
|
||||
if (ok) {
|
||||
const uint8_t* const data = VP8LBitWriterFinish(&tmp_bw);
|
||||
const size_t data_size = VP8LBitWriterNumBytes(&tmp_bw);
|
||||
VP8BitWriterAppend(bw, data, data_size);
|
||||
ok = ok && !bw->error_;
|
||||
if (!ok) {
|
||||
VP8LBitWriterWipeOut(bw);
|
||||
return 0;
|
||||
}
|
||||
VP8LBitWriterDestroy(&tmp_bw);
|
||||
return ok && !bw->error_;
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// Small struct to hold the result of a filter mode compression attempt.
|
||||
typedef struct {
|
||||
size_t score;
|
||||
VP8BitWriter bw;
|
||||
WebPAuxStats stats;
|
||||
} FilterTrial;
|
||||
|
||||
// This function always returns an initialized 'bw' object, even upon error.
|
||||
static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
|
||||
int method, int filter, int reduce_levels,
|
||||
int effort_level, // in [0..6] range
|
||||
uint8_t* const tmp_alpha,
|
||||
VP8BitWriter* const bw,
|
||||
WebPAuxStats* const stats) {
|
||||
FilterTrial* result) {
|
||||
int ok = 0;
|
||||
const uint8_t* alpha_src;
|
||||
WebPFilterFunc filter_func;
|
||||
uint8_t header;
|
||||
size_t expected_size;
|
||||
const size_t data_size = width * height;
|
||||
const uint8_t* output = NULL;
|
||||
size_t output_size = 0;
|
||||
VP8LBitWriter tmp_bw;
|
||||
|
||||
assert((uint64_t)data_size == (uint64_t)width * height); // as per spec
|
||||
assert(filter >= 0 && filter < WEBP_FILTER_LAST);
|
||||
@ -118,43 +115,163 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height,
|
||||
assert(sizeof(header) == ALPHA_HEADER_LEN);
|
||||
// TODO(skal): have a common function and #define's to validate alpha params.
|
||||
|
||||
expected_size =
|
||||
(method == ALPHA_NO_COMPRESSION) ? (ALPHA_HEADER_LEN + data_size)
|
||||
: (data_size >> 5);
|
||||
header = method | (filter << 2);
|
||||
if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
|
||||
|
||||
VP8BitWriterInit(bw, expected_size);
|
||||
VP8BitWriterAppend(bw, &header, ALPHA_HEADER_LEN);
|
||||
|
||||
filter_func = WebPFilters[filter];
|
||||
if (filter_func) {
|
||||
filter_func(data, width, height, 1, width, tmp_alpha);
|
||||
if (filter_func != NULL) {
|
||||
filter_func(data, width, height, width, tmp_alpha);
|
||||
alpha_src = tmp_alpha;
|
||||
} else {
|
||||
alpha_src = data;
|
||||
}
|
||||
|
||||
if (method == ALPHA_NO_COMPRESSION) {
|
||||
ok = VP8BitWriterAppend(bw, alpha_src, width * height);
|
||||
ok = ok && !bw->error_;
|
||||
} else {
|
||||
ok = EncodeLossless(alpha_src, width, height, effort_level, bw, stats);
|
||||
VP8BitWriterFinish(bw);
|
||||
if (method != ALPHA_NO_COMPRESSION) {
|
||||
ok = VP8LBitWriterInit(&tmp_bw, data_size >> 3);
|
||||
ok = ok && EncodeLossless(alpha_src, width, height, effort_level,
|
||||
&tmp_bw, &result->stats);
|
||||
if (ok) {
|
||||
output = VP8LBitWriterFinish(&tmp_bw);
|
||||
output_size = VP8LBitWriterNumBytes(&tmp_bw);
|
||||
if (output_size > data_size) {
|
||||
// compressed size is larger than source! Revert to uncompressed mode.
|
||||
method = ALPHA_NO_COMPRESSION;
|
||||
VP8LBitWriterWipeOut(&tmp_bw);
|
||||
}
|
||||
} else {
|
||||
VP8LBitWriterWipeOut(&tmp_bw);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (method == ALPHA_NO_COMPRESSION) {
|
||||
output = alpha_src;
|
||||
output_size = data_size;
|
||||
ok = 1;
|
||||
}
|
||||
|
||||
// Emit final result.
|
||||
header = method | (filter << 2);
|
||||
if (reduce_levels) header |= ALPHA_PREPROCESSED_LEVELS << 4;
|
||||
|
||||
VP8BitWriterInit(&result->bw, ALPHA_HEADER_LEN + output_size);
|
||||
ok = ok && VP8BitWriterAppend(&result->bw, &header, ALPHA_HEADER_LEN);
|
||||
ok = ok && VP8BitWriterAppend(&result->bw, output, output_size);
|
||||
|
||||
if (method != ALPHA_NO_COMPRESSION) {
|
||||
VP8LBitWriterWipeOut(&tmp_bw);
|
||||
}
|
||||
ok = ok && !result->bw.error_;
|
||||
result->score = VP8BitWriterSize(&result->bw);
|
||||
return ok;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
// TODO(skal): move to dsp/ ?
|
||||
static void CopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height) {
|
||||
while (height-- > 0) {
|
||||
memcpy(dst, src, width);
|
||||
src += src_stride;
|
||||
dst += dst_stride;
|
||||
static int GetNumColors(const uint8_t* data, int width, int height,
|
||||
int stride) {
|
||||
int j;
|
||||
int colors = 0;
|
||||
uint8_t color[256] = { 0 };
|
||||
|
||||
for (j = 0; j < height; ++j) {
|
||||
int i;
|
||||
const uint8_t* const p = data + j * stride;
|
||||
for (i = 0; i < width; ++i) {
|
||||
color[p[i]] = 1;
|
||||
}
|
||||
}
|
||||
for (j = 0; j < 256; ++j) {
|
||||
if (color[j] > 0) ++colors;
|
||||
}
|
||||
return colors;
|
||||
}
|
||||
|
||||
#define FILTER_TRY_NONE (1 << WEBP_FILTER_NONE)
|
||||
#define FILTER_TRY_ALL ((1 << WEBP_FILTER_LAST) - 1)
|
||||
|
||||
// Given the input 'filter' option, return an OR'd bit-set of filters to try.
|
||||
static uint32_t GetFilterMap(const uint8_t* alpha, int width, int height,
|
||||
int filter, int effort_level) {
|
||||
uint32_t bit_map = 0U;
|
||||
if (filter == WEBP_FILTER_FAST) {
|
||||
// Quick estimate of the best candidate.
|
||||
int try_filter_none = (effort_level > 3);
|
||||
const int kMinColorsForFilterNone = 16;
|
||||
const int kMaxColorsForFilterNone = 192;
|
||||
const int num_colors = GetNumColors(alpha, width, height, width);
|
||||
// For low number of colors, NONE yields better compression.
|
||||
filter = (num_colors <= kMinColorsForFilterNone)
|
||||
? WEBP_FILTER_NONE
|
||||
: WebPEstimateBestFilter(alpha, width, height, width);
|
||||
bit_map |= 1 << filter;
|
||||
// For large number of colors, try FILTER_NONE in addition to the best
|
||||
// filter as well.
|
||||
if (try_filter_none || num_colors > kMaxColorsForFilterNone) {
|
||||
bit_map |= FILTER_TRY_NONE;
|
||||
}
|
||||
} else if (filter == WEBP_FILTER_NONE) {
|
||||
bit_map = FILTER_TRY_NONE;
|
||||
} else { // WEBP_FILTER_BEST -> try all
|
||||
bit_map = FILTER_TRY_ALL;
|
||||
}
|
||||
return bit_map;
|
||||
}
|
||||
|
||||
static void InitFilterTrial(FilterTrial* const score) {
|
||||
score->score = (size_t)~0U;
|
||||
VP8BitWriterInit(&score->bw, 0);
|
||||
}
|
||||
|
||||
static int ApplyFiltersAndEncode(const uint8_t* alpha, int width, int height,
|
||||
size_t data_size, int method, int filter,
|
||||
int reduce_levels, int effort_level,
|
||||
uint8_t** const output,
|
||||
size_t* const output_size,
|
||||
WebPAuxStats* const stats) {
|
||||
int ok = 1;
|
||||
FilterTrial best;
|
||||
uint32_t try_map =
|
||||
GetFilterMap(alpha, width, height, filter, effort_level);
|
||||
InitFilterTrial(&best);
|
||||
|
||||
if (try_map != FILTER_TRY_NONE) {
|
||||
uint8_t* filtered_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size);
|
||||
if (filtered_alpha == NULL) return 0;
|
||||
|
||||
for (filter = WEBP_FILTER_NONE; ok && try_map; ++filter, try_map >>= 1) {
|
||||
if (try_map & 1) {
|
||||
FilterTrial trial;
|
||||
ok = EncodeAlphaInternal(alpha, width, height, method, filter,
|
||||
reduce_levels, effort_level, filtered_alpha,
|
||||
&trial);
|
||||
if (ok && trial.score < best.score) {
|
||||
VP8BitWriterWipeOut(&best.bw);
|
||||
best = trial;
|
||||
} else {
|
||||
VP8BitWriterWipeOut(&trial.bw);
|
||||
}
|
||||
}
|
||||
}
|
||||
WebPSafeFree(filtered_alpha);
|
||||
} else {
|
||||
ok = EncodeAlphaInternal(alpha, width, height, method, WEBP_FILTER_NONE,
|
||||
reduce_levels, effort_level, NULL, &best);
|
||||
}
|
||||
if (ok) {
|
||||
if (stats != NULL) {
|
||||
stats->lossless_features = best.stats.lossless_features;
|
||||
stats->histogram_bits = best.stats.histogram_bits;
|
||||
stats->transform_bits = best.stats.transform_bits;
|
||||
stats->cache_bits = best.stats.cache_bits;
|
||||
stats->palette_size = best.stats.palette_size;
|
||||
stats->lossless_size = best.stats.lossless_size;
|
||||
stats->lossless_hdr_size = best.stats.lossless_hdr_size;
|
||||
stats->lossless_data_size = best.stats.lossless_data_size;
|
||||
}
|
||||
*output_size = VP8BitWriterSize(&best.bw);
|
||||
*output = VP8BitWriterBuf(&best.bw);
|
||||
} else {
|
||||
VP8BitWriterWipeOut(&best.bw);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int EncodeAlpha(VP8Encoder* const enc,
|
||||
@ -187,13 +304,18 @@ static int EncodeAlpha(VP8Encoder* const enc,
|
||||
return 0;
|
||||
}
|
||||
|
||||
quant_alpha = (uint8_t*)malloc(data_size);
|
||||
if (method == ALPHA_NO_COMPRESSION) {
|
||||
// Don't filter, as filtering will make no impact on compressed size.
|
||||
filter = WEBP_FILTER_NONE;
|
||||
}
|
||||
|
||||
quant_alpha = (uint8_t*)WebPSafeMalloc(1ULL, data_size);
|
||||
if (quant_alpha == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Extract alpha data (width x height) from raw_data (stride x height).
|
||||
CopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
|
||||
WebPCopyPlane(pic->a, pic->a_stride, quant_alpha, width, width, height);
|
||||
|
||||
if (reduce_levels) { // No Quantization required for 'quality = 100'.
|
||||
// 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence
|
||||
@ -205,126 +327,99 @@ static int EncodeAlpha(VP8Encoder* const enc,
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
VP8BitWriter bw;
|
||||
int test_filter;
|
||||
uint8_t* filtered_alpha = NULL;
|
||||
|
||||
// We always test WEBP_FILTER_NONE first.
|
||||
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
||||
method, WEBP_FILTER_NONE, reduce_levels,
|
||||
effort_level, NULL, &bw, pic->stats);
|
||||
if (!ok) {
|
||||
VP8BitWriterWipeOut(&bw);
|
||||
goto End;
|
||||
VP8FiltersInit();
|
||||
ok = ApplyFiltersAndEncode(quant_alpha, width, height, data_size, method,
|
||||
filter, reduce_levels, effort_level, output,
|
||||
output_size, pic->stats);
|
||||
if (pic->stats != NULL) { // need stats?
|
||||
pic->stats->coded_size += (int)(*output_size);
|
||||
enc->sse_[3] = sse;
|
||||
}
|
||||
|
||||
if (filter == WEBP_FILTER_FAST) { // Quick estimate of a second candidate?
|
||||
filter = EstimateBestFilter(quant_alpha, width, height, width);
|
||||
}
|
||||
// Stop?
|
||||
if (filter == WEBP_FILTER_NONE) {
|
||||
goto Ok;
|
||||
}
|
||||
|
||||
filtered_alpha = (uint8_t*)malloc(data_size);
|
||||
ok = (filtered_alpha != NULL);
|
||||
if (!ok) {
|
||||
goto End;
|
||||
}
|
||||
|
||||
// Try the other mode(s).
|
||||
{
|
||||
WebPAuxStats best_stats;
|
||||
size_t best_score = VP8BitWriterSize(&bw);
|
||||
|
||||
memset(&best_stats, 0, sizeof(best_stats)); // prevent spurious warning
|
||||
if (pic->stats != NULL) best_stats = *pic->stats;
|
||||
for (test_filter = WEBP_FILTER_HORIZONTAL;
|
||||
ok && (test_filter <= WEBP_FILTER_GRADIENT);
|
||||
++test_filter) {
|
||||
VP8BitWriter tmp_bw;
|
||||
if (filter != WEBP_FILTER_BEST && test_filter != filter) {
|
||||
continue;
|
||||
}
|
||||
ok = EncodeAlphaInternal(quant_alpha, width, height,
|
||||
method, test_filter, reduce_levels,
|
||||
effort_level, filtered_alpha, &tmp_bw,
|
||||
pic->stats);
|
||||
if (ok) {
|
||||
const size_t score = VP8BitWriterSize(&tmp_bw);
|
||||
if (score < best_score) {
|
||||
// swap bitwriter objects.
|
||||
VP8BitWriter tmp = tmp_bw;
|
||||
tmp_bw = bw;
|
||||
bw = tmp;
|
||||
best_score = score;
|
||||
if (pic->stats != NULL) best_stats = *pic->stats;
|
||||
}
|
||||
} else {
|
||||
VP8BitWriterWipeOut(&bw);
|
||||
}
|
||||
VP8BitWriterWipeOut(&tmp_bw);
|
||||
}
|
||||
if (pic->stats != NULL) *pic->stats = best_stats;
|
||||
}
|
||||
Ok:
|
||||
if (ok) {
|
||||
*output_size = VP8BitWriterSize(&bw);
|
||||
*output = VP8BitWriterBuf(&bw);
|
||||
if (pic->stats != NULL) { // need stats?
|
||||
pic->stats->coded_size += (int)(*output_size);
|
||||
enc->sse_[3] = sse;
|
||||
}
|
||||
}
|
||||
free(filtered_alpha);
|
||||
}
|
||||
End:
|
||||
free(quant_alpha);
|
||||
|
||||
WebPSafeFree(quant_alpha);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main calls
|
||||
|
||||
static int CompressAlphaJob(VP8Encoder* const enc, void* dummy) {
|
||||
const WebPConfig* config = enc->config_;
|
||||
uint8_t* alpha_data = NULL;
|
||||
size_t alpha_size = 0;
|
||||
const int effort_level = config->method; // maps to [0..6]
|
||||
const WEBP_FILTER_TYPE filter =
|
||||
(config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
|
||||
(config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
|
||||
WEBP_FILTER_BEST;
|
||||
if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
|
||||
filter, effort_level, &alpha_data, &alpha_size)) {
|
||||
return 0;
|
||||
}
|
||||
if (alpha_size != (uint32_t)alpha_size) { // Sanity check.
|
||||
WebPSafeFree(alpha_data);
|
||||
return 0;
|
||||
}
|
||||
enc->alpha_data_size_ = (uint32_t)alpha_size;
|
||||
enc->alpha_data_ = alpha_data;
|
||||
(void)dummy;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VP8EncInitAlpha(VP8Encoder* const enc) {
|
||||
WebPInitAlphaProcessing();
|
||||
enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_);
|
||||
enc->alpha_data_ = NULL;
|
||||
enc->alpha_data_size_ = 0;
|
||||
if (enc->thread_level_ > 0) {
|
||||
WebPWorker* const worker = &enc->alpha_worker_;
|
||||
WebPGetWorkerInterface()->Init(worker);
|
||||
worker->data1 = enc;
|
||||
worker->data2 = NULL;
|
||||
worker->hook = (WebPWorkerHook)CompressAlphaJob;
|
||||
}
|
||||
}
|
||||
|
||||
int VP8EncStartAlpha(VP8Encoder* const enc) {
|
||||
if (enc->has_alpha_) {
|
||||
if (enc->thread_level_ > 0) {
|
||||
WebPWorker* const worker = &enc->alpha_worker_;
|
||||
// Makes sure worker is good to go.
|
||||
if (!WebPGetWorkerInterface()->Reset(worker)) {
|
||||
return 0;
|
||||
}
|
||||
WebPGetWorkerInterface()->Launch(worker);
|
||||
return 1;
|
||||
} else {
|
||||
return CompressAlphaJob(enc, NULL); // just do the job right away
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int VP8EncFinishAlpha(VP8Encoder* const enc) {
|
||||
if (enc->has_alpha_) {
|
||||
const WebPConfig* config = enc->config_;
|
||||
uint8_t* tmp_data = NULL;
|
||||
size_t tmp_size = 0;
|
||||
const int effort_level = config->method; // maps to [0..6]
|
||||
const WEBP_FILTER_TYPE filter =
|
||||
(config->alpha_filtering == 0) ? WEBP_FILTER_NONE :
|
||||
(config->alpha_filtering == 1) ? WEBP_FILTER_FAST :
|
||||
WEBP_FILTER_BEST;
|
||||
|
||||
if (!EncodeAlpha(enc, config->alpha_quality, config->alpha_compression,
|
||||
filter, effort_level, &tmp_data, &tmp_size)) {
|
||||
return 0;
|
||||
if (enc->thread_level_ > 0) {
|
||||
WebPWorker* const worker = &enc->alpha_worker_;
|
||||
if (!WebPGetWorkerInterface()->Sync(worker)) return 0; // error
|
||||
}
|
||||
if (tmp_size != (uint32_t)tmp_size) { // Sanity check.
|
||||
free(tmp_data);
|
||||
return 0;
|
||||
}
|
||||
enc->alpha_data_size_ = (uint32_t)tmp_size;
|
||||
enc->alpha_data_ = tmp_data;
|
||||
}
|
||||
return WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
|
||||
}
|
||||
|
||||
void VP8EncDeleteAlpha(VP8Encoder* const enc) {
|
||||
free(enc->alpha_data_);
|
||||
int VP8EncDeleteAlpha(VP8Encoder* const enc) {
|
||||
int ok = 1;
|
||||
if (enc->thread_level_ > 0) {
|
||||
WebPWorker* const worker = &enc->alpha_worker_;
|
||||
// finish anything left in flight
|
||||
ok = WebPGetWorkerInterface()->Sync(worker);
|
||||
// still need to end the worker, even if !ok
|
||||
WebPGetWorkerInterface()->End(worker);
|
||||
}
|
||||
WebPSafeFree(enc->alpha_data_);
|
||||
enc->alpha_data_ = NULL;
|
||||
enc->alpha_data_size_ = 0;
|
||||
enc->has_alpha_ = 0;
|
||||
return ok;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Macroblock analysis
|
||||
@ -17,16 +19,8 @@
|
||||
#include "./cost.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define MAX_ITERS_K_MEANS 6
|
||||
|
||||
static int ClipAlpha(int alpha) {
|
||||
return alpha < 0 ? 0 : alpha > 255 ? 255 : alpha;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Smooth the segment map by replacing isolated block by the majority of its
|
||||
// neighbours.
|
||||
@ -36,7 +30,7 @@ static void SmoothSegmentMap(VP8Encoder* const enc) {
|
||||
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));
|
||||
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;
|
||||
@ -57,6 +51,7 @@ static void SmoothSegmentMap(VP8Encoder* const enc) {
|
||||
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;
|
||||
@ -68,54 +63,14 @@ static void SmoothSegmentMap(VP8Encoder* const enc) {
|
||||
mb->segment_ = tmp[x + y * w];
|
||||
}
|
||||
}
|
||||
free(tmp);
|
||||
WebPSafeFree(tmp);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Finalize Segment probability based on the coding tree
|
||||
|
||||
static int GetProba(int a, int b) {
|
||||
int proba;
|
||||
const int total = a + b;
|
||||
if (total == 0) return 255; // that's the default probability.
|
||||
proba = (255 * a + total / 2) / total;
|
||||
return proba;
|
||||
}
|
||||
|
||||
static void SetSegmentProbas(VP8Encoder* const enc) {
|
||||
int p[NUM_MB_SEGMENTS] = { 0 };
|
||||
int n;
|
||||
|
||||
for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
|
||||
const VP8MBInfo* const mb = &enc->mb_info_[n];
|
||||
p[mb->segment_]++;
|
||||
}
|
||||
if (enc->pic_->stats) {
|
||||
for (n = 0; n < NUM_MB_SEGMENTS; ++n) {
|
||||
enc->pic_->stats->segment_size[n] = p[n];
|
||||
}
|
||||
}
|
||||
if (enc->segment_hdr_.num_segments_ > 1) {
|
||||
uint8_t* const probas = enc->proba_.segments_;
|
||||
probas[0] = GetProba(p[0] + p[1], p[2] + p[3]);
|
||||
probas[1] = GetProba(p[0], p[1]);
|
||||
probas[2] = GetProba(p[2], p[3]);
|
||||
|
||||
enc->segment_hdr_.update_map_ =
|
||||
(probas[0] != 255) || (probas[1] != 255) || (probas[2] != 255);
|
||||
enc->segment_hdr_.size_ =
|
||||
p[0] * (VP8BitCost(0, probas[0]) + VP8BitCost(0, probas[1])) +
|
||||
p[1] * (VP8BitCost(0, probas[0]) + VP8BitCost(1, probas[1])) +
|
||||
p[2] * (VP8BitCost(1, probas[0]) + VP8BitCost(0, probas[2])) +
|
||||
p[3] * (VP8BitCost(1, probas[0]) + VP8BitCost(1, probas[2]));
|
||||
} else {
|
||||
enc->segment_hdr_.update_map_ = 0;
|
||||
enc->segment_hdr_.size_ = 0;
|
||||
}
|
||||
}
|
||||
// set segment susceptibility alpha_ / beta_
|
||||
|
||||
static WEBP_INLINE int clip(int v, int m, int M) {
|
||||
return v < m ? m : v > M ? M : v;
|
||||
return (v < m) ? m : (v > M) ? M : v;
|
||||
}
|
||||
|
||||
static void SetSegmentAlphas(VP8Encoder* const enc,
|
||||
@ -141,29 +96,78 @@ static void SetSegmentAlphas(VP8Encoder* const enc,
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Compute susceptibility based on DCT-coeff histograms:
|
||||
// the higher, the "easier" the macroblock is to compress.
|
||||
|
||||
#define MAX_ALPHA 255 // 8b of precision for susceptibilities.
|
||||
#define ALPHA_SCALE (2 * MAX_ALPHA) // scaling factor for alpha.
|
||||
#define DEFAULT_ALPHA (-1)
|
||||
#define IS_BETTER_ALPHA(alpha, best_alpha) ((alpha) > (best_alpha))
|
||||
|
||||
static int FinalAlphaValue(int alpha) {
|
||||
alpha = MAX_ALPHA - alpha;
|
||||
return clip(alpha, 0, MAX_ALPHA);
|
||||
}
|
||||
|
||||
static int GetAlpha(const VP8Histogram* const histo) {
|
||||
// 'alpha' will later be clipped to [0..MAX_ALPHA] range, clamping outer
|
||||
// values which happen to be mostly noise. This leaves the maximum precision
|
||||
// for handling the useful small values which contribute most.
|
||||
const int max_value = histo->max_value;
|
||||
const int last_non_zero = histo->last_non_zero;
|
||||
const int alpha =
|
||||
(max_value > 1) ? ALPHA_SCALE * last_non_zero / max_value : 0;
|
||||
return alpha;
|
||||
}
|
||||
|
||||
static void InitHistogram(VP8Histogram* const histo) {
|
||||
histo->max_value = 0;
|
||||
histo->last_non_zero = 1;
|
||||
}
|
||||
|
||||
static void MergeHistograms(const VP8Histogram* const in,
|
||||
VP8Histogram* const out) {
|
||||
if (in->max_value > out->max_value) {
|
||||
out->max_value = in->max_value;
|
||||
}
|
||||
if (in->last_non_zero > out->last_non_zero) {
|
||||
out->last_non_zero = in->last_non_zero;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Simplified k-Means, to assign Nb segments based on alpha-histogram
|
||||
|
||||
static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) {
|
||||
const int nb = enc->segment_hdr_.num_segments_;
|
||||
static void AssignSegments(VP8Encoder* const enc,
|
||||
const int alphas[MAX_ALPHA + 1]) {
|
||||
// 'num_segments_' is previously validated and <= NUM_MB_SEGMENTS, but an
|
||||
// explicit check is needed to avoid spurious warning about 'n + 1' exceeding
|
||||
// array bounds of 'centers' with some compilers (noticed with gcc-4.9).
|
||||
const int nb = (enc->segment_hdr_.num_segments_ < NUM_MB_SEGMENTS) ?
|
||||
enc->segment_hdr_.num_segments_ : NUM_MB_SEGMENTS;
|
||||
int centers[NUM_MB_SEGMENTS];
|
||||
int weighted_average = 0;
|
||||
int map[256];
|
||||
int map[MAX_ALPHA + 1];
|
||||
int a, n, k;
|
||||
int min_a = 0, max_a = 255, range_a;
|
||||
int min_a = 0, max_a = MAX_ALPHA, range_a;
|
||||
// 'int' type is ok for histo, and won't overflow
|
||||
int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS];
|
||||
|
||||
assert(nb >= 1);
|
||||
assert(nb <= NUM_MB_SEGMENTS);
|
||||
|
||||
// bracket the input
|
||||
for (n = 0; n < 256 && alphas[n] == 0; ++n) {}
|
||||
for (n = 0; n <= MAX_ALPHA && alphas[n] == 0; ++n) {}
|
||||
min_a = n;
|
||||
for (n = 255; n > min_a && alphas[n] == 0; --n) {}
|
||||
for (n = MAX_ALPHA; n > min_a && alphas[n] == 0; --n) {}
|
||||
max_a = n;
|
||||
range_a = max_a - min_a;
|
||||
|
||||
// Spread initial centers evenly
|
||||
for (n = 1, k = 0; n < 2 * nb; n += 2) {
|
||||
centers[k++] = min_a + (n * range_a) / (2 * nb);
|
||||
for (k = 0, n = 1; k < nb; ++k, n += 2) {
|
||||
assert(n < 2 * nb);
|
||||
centers[k] = min_a + (n * range_a) / (2 * nb);
|
||||
}
|
||||
|
||||
for (k = 0; k < MAX_ITERS_K_MEANS; ++k) { // few iters are enough
|
||||
@ -178,7 +182,7 @@ static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) {
|
||||
n = 0; // track the nearest center for current 'a'
|
||||
for (a = min_a; a <= max_a; ++a) {
|
||||
if (alphas[a]) {
|
||||
while (n < nb - 1 && abs(a - centers[n + 1]) < abs(a - centers[n])) {
|
||||
while (n + 1 < nb && abs(a - centers[n + 1]) < abs(a - centers[n])) {
|
||||
n++;
|
||||
}
|
||||
map[a] = n;
|
||||
@ -210,7 +214,7 @@ static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) {
|
||||
VP8MBInfo* const mb = &enc->mb_info_[n];
|
||||
const int alpha = mb->alpha_;
|
||||
mb->segment_ = map[alpha];
|
||||
mb->alpha_ = centers[map[alpha]]; // just for the record.
|
||||
mb->alpha_ = centers[map[alpha]]; // for the record.
|
||||
}
|
||||
|
||||
if (nb > 1) {
|
||||
@ -218,7 +222,6 @@ static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) {
|
||||
if (smooth) SmoothSegmentMap(enc);
|
||||
}
|
||||
|
||||
SetSegmentProbas(enc); // Assign final proba
|
||||
SetSegmentAlphas(enc, centers, weighted_average); // pick some alphas.
|
||||
}
|
||||
|
||||
@ -227,24 +230,30 @@ static void AssignSegments(VP8Encoder* const enc, const int alphas[256]) {
|
||||
// susceptibility and set best modes for this macroblock.
|
||||
// Segment assignment is done later.
|
||||
|
||||
// Number of modes to inspect for alpha_ evaluation. For high-quality settings,
|
||||
// we don't need to test all the possible modes during the analysis phase.
|
||||
// Number of modes to inspect for alpha_ evaluation. We don't need to test all
|
||||
// the possible modes during the analysis phase: we risk falling into a local
|
||||
// optimum, or be subject to boundary effect
|
||||
#define MAX_INTRA16_MODE 2
|
||||
#define MAX_INTRA4_MODE 2
|
||||
#define MAX_UV_MODE 2
|
||||
|
||||
static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) {
|
||||
const int max_mode = (it->enc_->method_ >= 3) ? MAX_INTRA16_MODE : 4;
|
||||
const int max_mode = MAX_INTRA16_MODE;
|
||||
int mode;
|
||||
int best_alpha = -1;
|
||||
int best_alpha = DEFAULT_ALPHA;
|
||||
int best_mode = 0;
|
||||
|
||||
VP8MakeLuma16Preds(it);
|
||||
for (mode = 0; mode < max_mode; ++mode) {
|
||||
const int alpha = VP8CollectHistogram(it->yuv_in_ + Y_OFF,
|
||||
it->yuv_p_ + VP8I16ModeOffsets[mode],
|
||||
0, 16);
|
||||
if (alpha > best_alpha) {
|
||||
VP8Histogram histo;
|
||||
int alpha;
|
||||
|
||||
InitHistogram(&histo);
|
||||
VP8CollectHistogram(it->yuv_in_ + Y_OFF_ENC,
|
||||
it->yuv_p_ + VP8I16ModeOffsets[mode],
|
||||
0, 16, &histo);
|
||||
alpha = GetAlpha(&histo);
|
||||
if (IS_BETTER_ALPHA(alpha, best_alpha)) {
|
||||
best_alpha = alpha;
|
||||
best_mode = mode;
|
||||
}
|
||||
@ -256,46 +265,62 @@ static int MBAnalyzeBestIntra16Mode(VP8EncIterator* const it) {
|
||||
static int MBAnalyzeBestIntra4Mode(VP8EncIterator* const it,
|
||||
int best_alpha) {
|
||||
uint8_t modes[16];
|
||||
const int max_mode = (it->enc_->method_ >= 3) ? MAX_INTRA4_MODE : NUM_BMODES;
|
||||
int i4_alpha = 0;
|
||||
const int max_mode = MAX_INTRA4_MODE;
|
||||
int i4_alpha;
|
||||
VP8Histogram total_histo;
|
||||
int cur_histo = 0;
|
||||
InitHistogram(&total_histo);
|
||||
|
||||
VP8IteratorStartI4(it);
|
||||
do {
|
||||
int mode;
|
||||
int best_mode_alpha = -1;
|
||||
const uint8_t* const src = it->yuv_in_ + Y_OFF + VP8Scan[it->i4_];
|
||||
int best_mode_alpha = DEFAULT_ALPHA;
|
||||
VP8Histogram histos[2];
|
||||
const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_];
|
||||
|
||||
VP8MakeIntra4Preds(it);
|
||||
for (mode = 0; mode < max_mode; ++mode) {
|
||||
const int alpha = VP8CollectHistogram(src,
|
||||
it->yuv_p_ + VP8I4ModeOffsets[mode],
|
||||
0, 1);
|
||||
if (alpha > best_mode_alpha) {
|
||||
int alpha;
|
||||
|
||||
InitHistogram(&histos[cur_histo]);
|
||||
VP8CollectHistogram(src, it->yuv_p_ + VP8I4ModeOffsets[mode],
|
||||
0, 1, &histos[cur_histo]);
|
||||
alpha = GetAlpha(&histos[cur_histo]);
|
||||
if (IS_BETTER_ALPHA(alpha, best_mode_alpha)) {
|
||||
best_mode_alpha = alpha;
|
||||
modes[it->i4_] = mode;
|
||||
cur_histo ^= 1; // keep track of best histo so far.
|
||||
}
|
||||
}
|
||||
i4_alpha += best_mode_alpha;
|
||||
// accumulate best histogram
|
||||
MergeHistograms(&histos[cur_histo ^ 1], &total_histo);
|
||||
// Note: we reuse the original samples for predictors
|
||||
} while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF));
|
||||
} while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF_ENC));
|
||||
|
||||
if (i4_alpha > best_alpha) {
|
||||
i4_alpha = GetAlpha(&total_histo);
|
||||
if (IS_BETTER_ALPHA(i4_alpha, best_alpha)) {
|
||||
VP8SetIntra4Mode(it, modes);
|
||||
best_alpha = ClipAlpha(i4_alpha);
|
||||
best_alpha = i4_alpha;
|
||||
}
|
||||
return best_alpha;
|
||||
}
|
||||
|
||||
static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
|
||||
int best_alpha = -1;
|
||||
int best_alpha = DEFAULT_ALPHA;
|
||||
int best_mode = 0;
|
||||
const int max_mode = (it->enc_->method_ >= 3) ? MAX_UV_MODE : 4;
|
||||
const int max_mode = MAX_UV_MODE;
|
||||
int mode;
|
||||
|
||||
VP8MakeChroma8Preds(it);
|
||||
for (mode = 0; mode < max_mode; ++mode) {
|
||||
const int alpha = VP8CollectHistogram(it->yuv_in_ + U_OFF,
|
||||
it->yuv_p_ + VP8UVModeOffsets[mode],
|
||||
16, 16 + 4 + 4);
|
||||
if (alpha > best_alpha) {
|
||||
VP8Histogram histo;
|
||||
int alpha;
|
||||
InitHistogram(&histo);
|
||||
VP8CollectHistogram(it->yuv_in_ + U_OFF_ENC,
|
||||
it->yuv_p_ + VP8UVModeOffsets[mode],
|
||||
16, 16 + 4 + 4, &histo);
|
||||
alpha = GetAlpha(&histo);
|
||||
if (IS_BETTER_ALPHA(alpha, best_alpha)) {
|
||||
best_alpha = alpha;
|
||||
best_mode = mode;
|
||||
}
|
||||
@ -305,7 +330,8 @@ static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
|
||||
}
|
||||
|
||||
static void MBAnalyze(VP8EncIterator* const it,
|
||||
int alphas[256], int* const uv_alpha) {
|
||||
int alphas[MAX_ALPHA + 1],
|
||||
int* const alpha, int* const uv_alpha) {
|
||||
const VP8Encoder* const enc = it->enc_;
|
||||
int best_alpha, best_uv_alpha;
|
||||
|
||||
@ -314,7 +340,7 @@ static void MBAnalyze(VP8EncIterator* const it,
|
||||
VP8SetSegment(it, 0); // default segment, spec-wise.
|
||||
|
||||
best_alpha = MBAnalyzeBestIntra16Mode(it);
|
||||
if (enc->method_ != 3) {
|
||||
if (enc->method_ >= 5) {
|
||||
// We go and make a fast decision for intra4/intra16.
|
||||
// It's usually not a good and definitive pick, but helps seeding the stats
|
||||
// about level bit-cost.
|
||||
@ -324,10 +350,22 @@ static void MBAnalyze(VP8EncIterator* const it,
|
||||
best_uv_alpha = MBAnalyzeBestUVMode(it);
|
||||
|
||||
// Final susceptibility mix
|
||||
best_alpha = (best_alpha + best_uv_alpha + 1) / 2;
|
||||
best_alpha = (3 * best_alpha + best_uv_alpha + 2) >> 2;
|
||||
best_alpha = FinalAlphaValue(best_alpha);
|
||||
alphas[best_alpha]++;
|
||||
it->mb_->alpha_ = best_alpha; // for later remapping.
|
||||
|
||||
// Accumulate for later complexity analysis.
|
||||
*alpha += best_alpha; // mixed susceptibility (not just luma)
|
||||
*uv_alpha += best_uv_alpha;
|
||||
it->mb_->alpha_ = best_alpha; // Informative only.
|
||||
}
|
||||
|
||||
static void DefaultMBInfo(VP8MBInfo* const mb) {
|
||||
mb->type_ = 1; // I16x16
|
||||
mb->uv_mode_ = 0;
|
||||
mb->skip_ = 0; // not skipped
|
||||
mb->segment_ = 0; // default segment
|
||||
mb->alpha_ = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -340,25 +378,124 @@ static void MBAnalyze(VP8EncIterator* const it,
|
||||
// and decide intra4/intra16, but that's usually almost always a bad choice at
|
||||
// this stage.
|
||||
|
||||
int VP8EncAnalyze(VP8Encoder* const enc) {
|
||||
int ok = 1;
|
||||
int alphas[256] = { 0 };
|
||||
VP8EncIterator it;
|
||||
|
||||
VP8IteratorInit(enc, &it);
|
||||
static void ResetAllMBInfo(VP8Encoder* const enc) {
|
||||
int n;
|
||||
for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
|
||||
DefaultMBInfo(&enc->mb_info_[n]);
|
||||
}
|
||||
// Default susceptibilities.
|
||||
enc->dqm_[0].alpha_ = 0;
|
||||
enc->dqm_[0].beta_ = 0;
|
||||
// Note: we can't compute this alpha_ / uv_alpha_ -> set to default value.
|
||||
enc->alpha_ = 0;
|
||||
enc->uv_alpha_ = 0;
|
||||
do {
|
||||
VP8IteratorImport(&it);
|
||||
MBAnalyze(&it, alphas, &enc->uv_alpha_);
|
||||
ok = VP8IteratorProgress(&it, 20);
|
||||
// Let's pretend we have perfect lossless reconstruction.
|
||||
} while (ok && VP8IteratorNext(&it, it.yuv_in_));
|
||||
enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_;
|
||||
if (ok) AssignSegments(enc, alphas);
|
||||
WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
|
||||
}
|
||||
|
||||
// struct used to collect job result
|
||||
typedef struct {
|
||||
WebPWorker worker;
|
||||
int alphas[MAX_ALPHA + 1];
|
||||
int alpha, uv_alpha;
|
||||
VP8EncIterator it;
|
||||
int delta_progress;
|
||||
} SegmentJob;
|
||||
|
||||
// main work call
|
||||
static int DoSegmentsJob(SegmentJob* const job, VP8EncIterator* const it) {
|
||||
int ok = 1;
|
||||
if (!VP8IteratorIsDone(it)) {
|
||||
uint8_t tmp[32 + WEBP_ALIGN_CST];
|
||||
uint8_t* const scratch = (uint8_t*)WEBP_ALIGN(tmp);
|
||||
do {
|
||||
// Let's pretend we have perfect lossless reconstruction.
|
||||
VP8IteratorImport(it, scratch);
|
||||
MBAnalyze(it, job->alphas, &job->alpha, &job->uv_alpha);
|
||||
ok = VP8IteratorProgress(it, job->delta_progress);
|
||||
} while (ok && VP8IteratorNext(it));
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
static void MergeJobs(const SegmentJob* const src, SegmentJob* const dst) {
|
||||
int i;
|
||||
for (i = 0; i <= MAX_ALPHA; ++i) dst->alphas[i] += src->alphas[i];
|
||||
dst->alpha += src->alpha;
|
||||
dst->uv_alpha += src->uv_alpha;
|
||||
}
|
||||
|
||||
// initialize the job struct with some TODOs
|
||||
static void InitSegmentJob(VP8Encoder* const enc, SegmentJob* const job,
|
||||
int start_row, int end_row) {
|
||||
WebPGetWorkerInterface()->Init(&job->worker);
|
||||
job->worker.data1 = job;
|
||||
job->worker.data2 = &job->it;
|
||||
job->worker.hook = (WebPWorkerHook)DoSegmentsJob;
|
||||
VP8IteratorInit(enc, &job->it);
|
||||
VP8IteratorSetRow(&job->it, start_row);
|
||||
VP8IteratorSetCountDown(&job->it, (end_row - start_row) * enc->mb_w_);
|
||||
memset(job->alphas, 0, sizeof(job->alphas));
|
||||
job->alpha = 0;
|
||||
job->uv_alpha = 0;
|
||||
// only one of both jobs can record the progress, since we don't
|
||||
// expect the user's hook to be multi-thread safe
|
||||
job->delta_progress = (start_row == 0) ? 20 : 0;
|
||||
}
|
||||
|
||||
// main entry point
|
||||
int VP8EncAnalyze(VP8Encoder* const enc) {
|
||||
int ok = 1;
|
||||
const int do_segments =
|
||||
enc->config_->emulate_jpeg_size || // We need the complexity evaluation.
|
||||
(enc->segment_hdr_.num_segments_ > 1) ||
|
||||
(enc->method_ == 0); // for method 0, we need preds_[] to be filled.
|
||||
if (do_segments) {
|
||||
const int last_row = enc->mb_h_;
|
||||
// We give a little more than a half work to the main thread.
|
||||
const int split_row = (9 * last_row + 15) >> 4;
|
||||
const int total_mb = last_row * enc->mb_w_;
|
||||
#ifdef WEBP_USE_THREAD
|
||||
const int kMinSplitRow = 2; // minimal rows needed for mt to be worth it
|
||||
const int do_mt = (enc->thread_level_ > 0) && (split_row >= kMinSplitRow);
|
||||
#else
|
||||
const int do_mt = 0;
|
||||
#endif
|
||||
const WebPWorkerInterface* const worker_interface =
|
||||
WebPGetWorkerInterface();
|
||||
SegmentJob main_job;
|
||||
if (do_mt) {
|
||||
SegmentJob side_job;
|
||||
// Note the use of '&' instead of '&&' because we must call the functions
|
||||
// no matter what.
|
||||
InitSegmentJob(enc, &main_job, 0, split_row);
|
||||
InitSegmentJob(enc, &side_job, split_row, last_row);
|
||||
// we don't need to call Reset() on main_job.worker, since we're calling
|
||||
// WebPWorkerExecute() on it
|
||||
ok &= worker_interface->Reset(&side_job.worker);
|
||||
// launch the two jobs in parallel
|
||||
if (ok) {
|
||||
worker_interface->Launch(&side_job.worker);
|
||||
worker_interface->Execute(&main_job.worker);
|
||||
ok &= worker_interface->Sync(&side_job.worker);
|
||||
ok &= worker_interface->Sync(&main_job.worker);
|
||||
}
|
||||
worker_interface->End(&side_job.worker);
|
||||
if (ok) MergeJobs(&side_job, &main_job); // merge results together
|
||||
} else {
|
||||
// Even for single-thread case, we use the generic Worker tools.
|
||||
InitSegmentJob(enc, &main_job, 0, last_row);
|
||||
worker_interface->Execute(&main_job.worker);
|
||||
ok &= worker_interface->Sync(&main_job.worker);
|
||||
}
|
||||
worker_interface->End(&main_job.worker);
|
||||
if (ok) {
|
||||
enc->alpha_ = main_job.alpha / total_mb;
|
||||
enc->uv_alpha_ = main_job.uv_alpha / total_mb;
|
||||
AssignSegments(enc, main_job.alphas);
|
||||
}
|
||||
} else { // Use only one default segment.
|
||||
ResetAllMBInfo(enc);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Author: Jyrki Alakuijala (jyrki@google.com)
|
||||
@ -13,82 +15,15 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "../types.h"
|
||||
#include "../format_constants.h"
|
||||
#include "../webp/types.h"
|
||||
#include "../webp/format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// The spec allows 11, we use 9 bits to reduce memory consumption in encoding.
|
||||
// Having 9 instead of 11 only removes about 0.25 % of compression density.
|
||||
#define MAX_COLOR_CACHE_BITS 9
|
||||
|
||||
// Max ever number of codes we'll use:
|
||||
#define PIX_OR_COPY_CODES_MAX \
|
||||
(NUM_LITERAL_CODES + NUM_LENGTH_CODES + (1 << MAX_COLOR_CACHE_BITS))
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PrefixEncode()
|
||||
|
||||
// use GNU builtins where available.
|
||||
#if defined(__GNUC__) && \
|
||||
((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4)
|
||||
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
|
||||
return n == 0 ? -1 : 31 ^ __builtin_clz(n);
|
||||
}
|
||||
#elif defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_BitScanReverse)
|
||||
|
||||
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
|
||||
unsigned long first_set_bit;
|
||||
return _BitScanReverse(&first_set_bit, n) ? first_set_bit : -1;
|
||||
}
|
||||
#else
|
||||
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
|
||||
int log = 0;
|
||||
uint32_t value = n;
|
||||
int i;
|
||||
|
||||
if (value == 0) return -1;
|
||||
for (i = 4; i >= 0; --i) {
|
||||
const int shift = (1 << i);
|
||||
const uint32_t x = value >> shift;
|
||||
if (x != 0) {
|
||||
value = x;
|
||||
log += shift;
|
||||
}
|
||||
}
|
||||
return log;
|
||||
}
|
||||
#endif
|
||||
|
||||
static WEBP_INLINE int VP8LBitsLog2Ceiling(uint32_t n) {
|
||||
const int floor = BitsLog2Floor(n);
|
||||
if (n == (n & ~(n - 1))) // zero or a power of two.
|
||||
return floor;
|
||||
else
|
||||
return floor + 1;
|
||||
}
|
||||
|
||||
// Splitting of distance and length codes into prefixes and
|
||||
// extra bits. The prefixes are encoded with an entropy code
|
||||
// while the extra bits are stored just as normal bits.
|
||||
static WEBP_INLINE void PrefixEncode(int distance, int* const code,
|
||||
int* const extra_bits_count,
|
||||
int* const extra_bits_value) {
|
||||
// Collect the two most significant bits where the highest bit is 1.
|
||||
const int highest_bit = BitsLog2Floor(--distance);
|
||||
// & 0x3f is to make behavior well defined when highest_bit
|
||||
// does not exist or is the least significant bit.
|
||||
const int second_highest_bit =
|
||||
(distance >> ((highest_bit - 1) & 0x3f)) & 1;
|
||||
*extra_bits_count = (highest_bit > 0) ? (highest_bit - 1) : 0;
|
||||
*extra_bits_value = distance & ((1 << *extra_bits_count) - 1);
|
||||
*code = (highest_bit > 0) ? (2 * highest_bit + second_highest_bit)
|
||||
: (highest_bit == 0) ? 1 : 0;
|
||||
}
|
||||
// The maximum allowed limit is 11.
|
||||
#define MAX_COLOR_CACHE_BITS 10
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// PixOrCopy
|
||||
@ -173,39 +108,94 @@ static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) {
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// VP8LBackwardRefs
|
||||
// VP8LHashChain
|
||||
|
||||
#define HASH_BITS 18
|
||||
#define HASH_SIZE (1 << HASH_BITS)
|
||||
|
||||
typedef struct VP8LHashChain VP8LHashChain;
|
||||
struct VP8LHashChain {
|
||||
// Stores the most recently added position with the given hash value.
|
||||
int32_t hash_to_first_index_[HASH_SIZE];
|
||||
// chain_[pos] stores the previous position with the same hash value
|
||||
// for every pixel in the image.
|
||||
int32_t* chain_;
|
||||
// This is the maximum size of the hash_chain that can be constructed.
|
||||
// Typically this is the pixel count (width x height) for a given image.
|
||||
int size_;
|
||||
};
|
||||
|
||||
// Must be called first, to set size.
|
||||
int VP8LHashChainInit(VP8LHashChain* const p, int size);
|
||||
void VP8LHashChainClear(VP8LHashChain* const p); // release memory
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// VP8LBackwardRefs (block-based backward-references storage)
|
||||
|
||||
// maximum number of reference blocks the image will be segmented into
|
||||
#define MAX_REFS_BLOCK_PER_IMAGE 16
|
||||
|
||||
typedef struct PixOrCopyBlock PixOrCopyBlock; // forward declaration
|
||||
typedef struct VP8LBackwardRefs VP8LBackwardRefs;
|
||||
|
||||
// Container for blocks chain
|
||||
struct VP8LBackwardRefs {
|
||||
int block_size_; // common block-size
|
||||
int error_; // set to true if some memory error occurred
|
||||
PixOrCopyBlock* refs_; // list of currently used blocks
|
||||
PixOrCopyBlock** tail_; // for list recycling
|
||||
PixOrCopyBlock* free_blocks_; // free-list
|
||||
PixOrCopyBlock* last_block_; // used for adding new refs (internal)
|
||||
};
|
||||
|
||||
// Initialize the object. 'block_size' is the common block size to store
|
||||
// references (typically, width * height / MAX_REFS_BLOCK_PER_IMAGE).
|
||||
void VP8LBackwardRefsInit(VP8LBackwardRefs* const refs, int block_size);
|
||||
// Release memory for backward references.
|
||||
void VP8LBackwardRefsClear(VP8LBackwardRefs* const refs);
|
||||
// Copies the 'src' backward refs to the 'dst'. Returns 0 in case of error.
|
||||
int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src,
|
||||
VP8LBackwardRefs* const dst);
|
||||
|
||||
// Cursor for iterating on references content
|
||||
typedef struct {
|
||||
PixOrCopy* refs;
|
||||
int size; // currently used
|
||||
int max_size; // maximum capacity
|
||||
} VP8LBackwardRefs;
|
||||
// public:
|
||||
PixOrCopy* cur_pos; // current position
|
||||
// private:
|
||||
PixOrCopyBlock* cur_block_; // current block in the refs list
|
||||
const PixOrCopy* last_pos_; // sentinel for switching to next block
|
||||
} VP8LRefsCursor;
|
||||
|
||||
// Initialize the object. Must be called first. 'refs' can be NULL.
|
||||
void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs);
|
||||
|
||||
// Release memory and re-initialize the object. 'refs' can be NULL.
|
||||
void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs);
|
||||
|
||||
// Allocate 'max_size' references. Returns false in case of memory error.
|
||||
int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size);
|
||||
// Returns a cursor positioned at the beginning of the references list.
|
||||
VP8LRefsCursor VP8LRefsCursorInit(const VP8LBackwardRefs* const refs);
|
||||
// Returns true if cursor is pointing at a valid position.
|
||||
static WEBP_INLINE int VP8LRefsCursorOk(const VP8LRefsCursor* const c) {
|
||||
return (c->cur_pos != NULL);
|
||||
}
|
||||
// Move to next block of references. Internal, not to be called directly.
|
||||
void VP8LRefsCursorNextBlock(VP8LRefsCursor* const c);
|
||||
// Move to next position, or NULL. Should not be called if !VP8LRefsCursorOk().
|
||||
static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) {
|
||||
assert(c != NULL);
|
||||
assert(VP8LRefsCursorOk(c));
|
||||
if (++c->cur_pos == c->last_pos_) VP8LRefsCursorNextBlock(c);
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Main entry points
|
||||
|
||||
// Evaluates best possible backward references for specified quality.
|
||||
// Further optimize for 2D locality if use_2d_locality flag is set.
|
||||
int VP8LGetBackwardReferences(int width, int height,
|
||||
const uint32_t* const argb,
|
||||
int quality, int cache_bits, int use_2d_locality,
|
||||
VP8LBackwardRefs* const best);
|
||||
// The input cache_bits to 'VP8LGetBackwardReferences' sets the maximum cache
|
||||
// bits to use (passing 0 implies disabling the local color cache).
|
||||
// The optimal cache bits is evaluated and set for the *cache_bits parameter.
|
||||
// The return value is the pointer to the best of the two backward refs viz,
|
||||
// refs[0] or refs[1].
|
||||
VP8LBackwardRefs* VP8LGetBackwardReferences(
|
||||
int width, int height, const uint32_t* const argb, int quality,
|
||||
int low_effort, int* const cache_bits, VP8LHashChain* const hash_chain,
|
||||
VP8LBackwardRefs refs[2]);
|
||||
|
||||
// Produce an estimate for a good color cache size for the image.
|
||||
int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb,
|
||||
int xsize, int ysize,
|
||||
int* const best_cache_bits);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,19 +1,17 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Coding tools configuration
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "../encode.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "../webp/encode.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPConfig
|
||||
@ -31,9 +29,9 @@ int WebPConfigInitInternal(WebPConfig* config,
|
||||
config->target_PSNR = 0.;
|
||||
config->method = 4;
|
||||
config->sns_strength = 50;
|
||||
config->filter_strength = 20; // default: light filtering
|
||||
config->filter_strength = 60; // mid-filtering
|
||||
config->filter_sharpness = 0;
|
||||
config->filter_type = 0; // default: simple
|
||||
config->filter_type = 1; // default: strong (so U/V is filtered too)
|
||||
config->partitions = 0;
|
||||
config->segments = 4;
|
||||
config->pass = 1;
|
||||
@ -45,7 +43,15 @@ int WebPConfigInitInternal(WebPConfig* config,
|
||||
config->alpha_filtering = 1;
|
||||
config->alpha_quality = 100;
|
||||
config->lossless = 0;
|
||||
config->exact = 0;
|
||||
config->image_hint = WEBP_HINT_DEFAULT;
|
||||
config->emulate_jpeg_size = 0;
|
||||
config->thread_level = 0;
|
||||
config->low_memory = 0;
|
||||
config->near_lossless = 100;
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
config->delta_palettization = 0;
|
||||
#endif // WEBP_EXPERIMENTAL_FEATURES
|
||||
|
||||
// TODO(skal): tune.
|
||||
switch (preset) {
|
||||
@ -53,11 +59,13 @@ int WebPConfigInitInternal(WebPConfig* config,
|
||||
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;
|
||||
@ -67,10 +75,12 @@ int WebPConfigInitInternal(WebPConfig* config,
|
||||
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:
|
||||
@ -106,7 +116,7 @@ int WebPValidateConfig(const WebPConfig* config) {
|
||||
return 0;
|
||||
if (config->show_compressed < 0 || config->show_compressed > 1)
|
||||
return 0;
|
||||
if (config->preprocessing < 0 || config->preprocessing > 1)
|
||||
if (config->preprocessing < 0 || config->preprocessing > 7)
|
||||
return 0;
|
||||
if (config->partitions < 0 || config->partitions > 3)
|
||||
return 0;
|
||||
@ -120,13 +130,44 @@ int WebPValidateConfig(const WebPConfig* config) {
|
||||
return 0;
|
||||
if (config->lossless < 0 || config->lossless > 1)
|
||||
return 0;
|
||||
if (config->near_lossless < 0 || config->near_lossless > 100)
|
||||
return 0;
|
||||
if (config->image_hint >= WEBP_HINT_LAST)
|
||||
return 0;
|
||||
if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1)
|
||||
return 0;
|
||||
if (config->thread_level < 0 || config->thread_level > 1)
|
||||
return 0;
|
||||
if (config->low_memory < 0 || config->low_memory > 1)
|
||||
return 0;
|
||||
if (config->exact < 0 || config->exact > 1)
|
||||
return 0;
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
if (config->delta_palettization < 0 || config->delta_palettization > 1)
|
||||
return 0;
|
||||
#endif // WEBP_EXPERIMENTAL_FEATURES
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
#define MAX_LEVEL 9
|
||||
|
||||
// Mapping between -z level and -m / -q parameter settings.
|
||||
static const struct {
|
||||
uint8_t method_;
|
||||
uint8_t quality_;
|
||||
} kLosslessPresets[MAX_LEVEL + 1] = {
|
||||
{ 0, 0 }, { 1, 20 }, { 2, 25 }, { 3, 30 }, { 3, 50 },
|
||||
{ 4, 50 }, { 4, 75 }, { 4, 90 }, { 5, 90 }, { 6, 100 }
|
||||
};
|
||||
|
||||
int WebPConfigLosslessPreset(WebPConfig* config, int level) {
|
||||
if (config == NULL || level < 0 || level > MAX_LEVEL) return 0;
|
||||
config->lossless = 1;
|
||||
config->method = kLosslessPresets[level].method_;
|
||||
config->quality = kLosslessPresets[level].quality_;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Cost tables for level and modes
|
||||
@ -11,42 +13,6 @@
|
||||
|
||||
#include "./cost.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Boolean-cost cost table
|
||||
|
||||
const uint16_t VP8EntropyCost[256] = {
|
||||
1792, 1792, 1792, 1536, 1536, 1408, 1366, 1280, 1280, 1216,
|
||||
1178, 1152, 1110, 1076, 1061, 1024, 1024, 992, 968, 951,
|
||||
939, 911, 896, 878, 871, 854, 838, 820, 811, 794,
|
||||
786, 768, 768, 752, 740, 732, 720, 709, 704, 690,
|
||||
683, 672, 666, 655, 647, 640, 631, 622, 615, 607,
|
||||
598, 592, 586, 576, 572, 564, 559, 555, 547, 541,
|
||||
534, 528, 522, 512, 512, 504, 500, 494, 488, 483,
|
||||
477, 473, 467, 461, 458, 452, 448, 443, 438, 434,
|
||||
427, 424, 419, 415, 410, 406, 403, 399, 394, 390,
|
||||
384, 384, 377, 374, 370, 366, 362, 359, 355, 351,
|
||||
347, 342, 342, 336, 333, 330, 326, 323, 320, 316,
|
||||
312, 308, 305, 302, 299, 296, 293, 288, 287, 283,
|
||||
280, 277, 274, 272, 268, 266, 262, 256, 256, 256,
|
||||
251, 248, 245, 242, 240, 237, 234, 232, 228, 226,
|
||||
223, 221, 218, 216, 214, 211, 208, 205, 203, 201,
|
||||
198, 196, 192, 191, 188, 187, 183, 181, 179, 176,
|
||||
175, 171, 171, 168, 165, 163, 160, 159, 156, 154,
|
||||
152, 150, 148, 146, 144, 142, 139, 138, 135, 133,
|
||||
131, 128, 128, 125, 123, 121, 119, 117, 115, 113,
|
||||
111, 110, 107, 105, 103, 102, 100, 98, 96, 94,
|
||||
92, 91, 89, 86, 86, 83, 82, 80, 77, 76,
|
||||
74, 73, 71, 69, 67, 66, 64, 63, 61, 59,
|
||||
57, 55, 54, 52, 51, 49, 47, 46, 44, 43,
|
||||
41, 40, 38, 36, 35, 33, 32, 30, 29, 27,
|
||||
25, 24, 22, 21, 19, 18, 16, 15, 13, 12,
|
||||
10, 9, 7, 6, 4, 3
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Level cost tables
|
||||
|
||||
@ -73,267 +39,6 @@ const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2] = {
|
||||
{0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x153}
|
||||
};
|
||||
|
||||
// fixed costs for coding levels, deduce from the coding tree.
|
||||
// This is only the part that doesn't depend on the probability state.
|
||||
const uint16_t VP8LevelFixedCosts[2048] = {
|
||||
0, 256, 256, 256, 256, 432, 618, 630,
|
||||
731, 640, 640, 828, 901, 948, 1021, 1101,
|
||||
1174, 1221, 1294, 1042, 1085, 1115, 1158, 1202,
|
||||
1245, 1275, 1318, 1337, 1380, 1410, 1453, 1497,
|
||||
1540, 1570, 1613, 1280, 1295, 1317, 1332, 1358,
|
||||
1373, 1395, 1410, 1454, 1469, 1491, 1506, 1532,
|
||||
1547, 1569, 1584, 1601, 1616, 1638, 1653, 1679,
|
||||
1694, 1716, 1731, 1775, 1790, 1812, 1827, 1853,
|
||||
1868, 1890, 1905, 1727, 1733, 1742, 1748, 1759,
|
||||
1765, 1774, 1780, 1800, 1806, 1815, 1821, 1832,
|
||||
1838, 1847, 1853, 1878, 1884, 1893, 1899, 1910,
|
||||
1916, 1925, 1931, 1951, 1957, 1966, 1972, 1983,
|
||||
1989, 1998, 2004, 2027, 2033, 2042, 2048, 2059,
|
||||
2065, 2074, 2080, 2100, 2106, 2115, 2121, 2132,
|
||||
2138, 2147, 2153, 2178, 2184, 2193, 2199, 2210,
|
||||
2216, 2225, 2231, 2251, 2257, 2266, 2272, 2283,
|
||||
2289, 2298, 2304, 2168, 2174, 2183, 2189, 2200,
|
||||
2206, 2215, 2221, 2241, 2247, 2256, 2262, 2273,
|
||||
2279, 2288, 2294, 2319, 2325, 2334, 2340, 2351,
|
||||
2357, 2366, 2372, 2392, 2398, 2407, 2413, 2424,
|
||||
2430, 2439, 2445, 2468, 2474, 2483, 2489, 2500,
|
||||
2506, 2515, 2521, 2541, 2547, 2556, 2562, 2573,
|
||||
2579, 2588, 2594, 2619, 2625, 2634, 2640, 2651,
|
||||
2657, 2666, 2672, 2692, 2698, 2707, 2713, 2724,
|
||||
2730, 2739, 2745, 2540, 2546, 2555, 2561, 2572,
|
||||
2578, 2587, 2593, 2613, 2619, 2628, 2634, 2645,
|
||||
2651, 2660, 2666, 2691, 2697, 2706, 2712, 2723,
|
||||
2729, 2738, 2744, 2764, 2770, 2779, 2785, 2796,
|
||||
2802, 2811, 2817, 2840, 2846, 2855, 2861, 2872,
|
||||
2878, 2887, 2893, 2913, 2919, 2928, 2934, 2945,
|
||||
2951, 2960, 2966, 2991, 2997, 3006, 3012, 3023,
|
||||
3029, 3038, 3044, 3064, 3070, 3079, 3085, 3096,
|
||||
3102, 3111, 3117, 2981, 2987, 2996, 3002, 3013,
|
||||
3019, 3028, 3034, 3054, 3060, 3069, 3075, 3086,
|
||||
3092, 3101, 3107, 3132, 3138, 3147, 3153, 3164,
|
||||
3170, 3179, 3185, 3205, 3211, 3220, 3226, 3237,
|
||||
3243, 3252, 3258, 3281, 3287, 3296, 3302, 3313,
|
||||
3319, 3328, 3334, 3354, 3360, 3369, 3375, 3386,
|
||||
3392, 3401, 3407, 3432, 3438, 3447, 3453, 3464,
|
||||
3470, 3479, 3485, 3505, 3511, 3520, 3526, 3537,
|
||||
3543, 3552, 3558, 2816, 2822, 2831, 2837, 2848,
|
||||
2854, 2863, 2869, 2889, 2895, 2904, 2910, 2921,
|
||||
2927, 2936, 2942, 2967, 2973, 2982, 2988, 2999,
|
||||
3005, 3014, 3020, 3040, 3046, 3055, 3061, 3072,
|
||||
3078, 3087, 3093, 3116, 3122, 3131, 3137, 3148,
|
||||
3154, 3163, 3169, 3189, 3195, 3204, 3210, 3221,
|
||||
3227, 3236, 3242, 3267, 3273, 3282, 3288, 3299,
|
||||
3305, 3314, 3320, 3340, 3346, 3355, 3361, 3372,
|
||||
3378, 3387, 3393, 3257, 3263, 3272, 3278, 3289,
|
||||
3295, 3304, 3310, 3330, 3336, 3345, 3351, 3362,
|
||||
3368, 3377, 3383, 3408, 3414, 3423, 3429, 3440,
|
||||
3446, 3455, 3461, 3481, 3487, 3496, 3502, 3513,
|
||||
3519, 3528, 3534, 3557, 3563, 3572, 3578, 3589,
|
||||
3595, 3604, 3610, 3630, 3636, 3645, 3651, 3662,
|
||||
3668, 3677, 3683, 3708, 3714, 3723, 3729, 3740,
|
||||
3746, 3755, 3761, 3781, 3787, 3796, 3802, 3813,
|
||||
3819, 3828, 3834, 3629, 3635, 3644, 3650, 3661,
|
||||
3667, 3676, 3682, 3702, 3708, 3717, 3723, 3734,
|
||||
3740, 3749, 3755, 3780, 3786, 3795, 3801, 3812,
|
||||
3818, 3827, 3833, 3853, 3859, 3868, 3874, 3885,
|
||||
3891, 3900, 3906, 3929, 3935, 3944, 3950, 3961,
|
||||
3967, 3976, 3982, 4002, 4008, 4017, 4023, 4034,
|
||||
4040, 4049, 4055, 4080, 4086, 4095, 4101, 4112,
|
||||
4118, 4127, 4133, 4153, 4159, 4168, 4174, 4185,
|
||||
4191, 4200, 4206, 4070, 4076, 4085, 4091, 4102,
|
||||
4108, 4117, 4123, 4143, 4149, 4158, 4164, 4175,
|
||||
4181, 4190, 4196, 4221, 4227, 4236, 4242, 4253,
|
||||
4259, 4268, 4274, 4294, 4300, 4309, 4315, 4326,
|
||||
4332, 4341, 4347, 4370, 4376, 4385, 4391, 4402,
|
||||
4408, 4417, 4423, 4443, 4449, 4458, 4464, 4475,
|
||||
4481, 4490, 4496, 4521, 4527, 4536, 4542, 4553,
|
||||
4559, 4568, 4574, 4594, 4600, 4609, 4615, 4626,
|
||||
4632, 4641, 4647, 3515, 3521, 3530, 3536, 3547,
|
||||
3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620,
|
||||
3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698,
|
||||
3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771,
|
||||
3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847,
|
||||
3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920,
|
||||
3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998,
|
||||
4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071,
|
||||
4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988,
|
||||
3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061,
|
||||
4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139,
|
||||
4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212,
|
||||
4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288,
|
||||
4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361,
|
||||
4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439,
|
||||
4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512,
|
||||
4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360,
|
||||
4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433,
|
||||
4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511,
|
||||
4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584,
|
||||
4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660,
|
||||
4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733,
|
||||
4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811,
|
||||
4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884,
|
||||
4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801,
|
||||
4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874,
|
||||
4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952,
|
||||
4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025,
|
||||
5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101,
|
||||
5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174,
|
||||
5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252,
|
||||
5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325,
|
||||
5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636,
|
||||
4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709,
|
||||
4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787,
|
||||
4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860,
|
||||
4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936,
|
||||
4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009,
|
||||
5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087,
|
||||
5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160,
|
||||
5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077,
|
||||
5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150,
|
||||
5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228,
|
||||
5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301,
|
||||
5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377,
|
||||
5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450,
|
||||
5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528,
|
||||
5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601,
|
||||
5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449,
|
||||
5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522,
|
||||
5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600,
|
||||
5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673,
|
||||
5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749,
|
||||
5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822,
|
||||
5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900,
|
||||
5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973,
|
||||
5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890,
|
||||
5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963,
|
||||
5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041,
|
||||
6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114,
|
||||
6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190,
|
||||
6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263,
|
||||
6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341,
|
||||
6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414,
|
||||
6420, 6429, 6435, 3515, 3521, 3530, 3536, 3547,
|
||||
3553, 3562, 3568, 3588, 3594, 3603, 3609, 3620,
|
||||
3626, 3635, 3641, 3666, 3672, 3681, 3687, 3698,
|
||||
3704, 3713, 3719, 3739, 3745, 3754, 3760, 3771,
|
||||
3777, 3786, 3792, 3815, 3821, 3830, 3836, 3847,
|
||||
3853, 3862, 3868, 3888, 3894, 3903, 3909, 3920,
|
||||
3926, 3935, 3941, 3966, 3972, 3981, 3987, 3998,
|
||||
4004, 4013, 4019, 4039, 4045, 4054, 4060, 4071,
|
||||
4077, 4086, 4092, 3956, 3962, 3971, 3977, 3988,
|
||||
3994, 4003, 4009, 4029, 4035, 4044, 4050, 4061,
|
||||
4067, 4076, 4082, 4107, 4113, 4122, 4128, 4139,
|
||||
4145, 4154, 4160, 4180, 4186, 4195, 4201, 4212,
|
||||
4218, 4227, 4233, 4256, 4262, 4271, 4277, 4288,
|
||||
4294, 4303, 4309, 4329, 4335, 4344, 4350, 4361,
|
||||
4367, 4376, 4382, 4407, 4413, 4422, 4428, 4439,
|
||||
4445, 4454, 4460, 4480, 4486, 4495, 4501, 4512,
|
||||
4518, 4527, 4533, 4328, 4334, 4343, 4349, 4360,
|
||||
4366, 4375, 4381, 4401, 4407, 4416, 4422, 4433,
|
||||
4439, 4448, 4454, 4479, 4485, 4494, 4500, 4511,
|
||||
4517, 4526, 4532, 4552, 4558, 4567, 4573, 4584,
|
||||
4590, 4599, 4605, 4628, 4634, 4643, 4649, 4660,
|
||||
4666, 4675, 4681, 4701, 4707, 4716, 4722, 4733,
|
||||
4739, 4748, 4754, 4779, 4785, 4794, 4800, 4811,
|
||||
4817, 4826, 4832, 4852, 4858, 4867, 4873, 4884,
|
||||
4890, 4899, 4905, 4769, 4775, 4784, 4790, 4801,
|
||||
4807, 4816, 4822, 4842, 4848, 4857, 4863, 4874,
|
||||
4880, 4889, 4895, 4920, 4926, 4935, 4941, 4952,
|
||||
4958, 4967, 4973, 4993, 4999, 5008, 5014, 5025,
|
||||
5031, 5040, 5046, 5069, 5075, 5084, 5090, 5101,
|
||||
5107, 5116, 5122, 5142, 5148, 5157, 5163, 5174,
|
||||
5180, 5189, 5195, 5220, 5226, 5235, 5241, 5252,
|
||||
5258, 5267, 5273, 5293, 5299, 5308, 5314, 5325,
|
||||
5331, 5340, 5346, 4604, 4610, 4619, 4625, 4636,
|
||||
4642, 4651, 4657, 4677, 4683, 4692, 4698, 4709,
|
||||
4715, 4724, 4730, 4755, 4761, 4770, 4776, 4787,
|
||||
4793, 4802, 4808, 4828, 4834, 4843, 4849, 4860,
|
||||
4866, 4875, 4881, 4904, 4910, 4919, 4925, 4936,
|
||||
4942, 4951, 4957, 4977, 4983, 4992, 4998, 5009,
|
||||
5015, 5024, 5030, 5055, 5061, 5070, 5076, 5087,
|
||||
5093, 5102, 5108, 5128, 5134, 5143, 5149, 5160,
|
||||
5166, 5175, 5181, 5045, 5051, 5060, 5066, 5077,
|
||||
5083, 5092, 5098, 5118, 5124, 5133, 5139, 5150,
|
||||
5156, 5165, 5171, 5196, 5202, 5211, 5217, 5228,
|
||||
5234, 5243, 5249, 5269, 5275, 5284, 5290, 5301,
|
||||
5307, 5316, 5322, 5345, 5351, 5360, 5366, 5377,
|
||||
5383, 5392, 5398, 5418, 5424, 5433, 5439, 5450,
|
||||
5456, 5465, 5471, 5496, 5502, 5511, 5517, 5528,
|
||||
5534, 5543, 5549, 5569, 5575, 5584, 5590, 5601,
|
||||
5607, 5616, 5622, 5417, 5423, 5432, 5438, 5449,
|
||||
5455, 5464, 5470, 5490, 5496, 5505, 5511, 5522,
|
||||
5528, 5537, 5543, 5568, 5574, 5583, 5589, 5600,
|
||||
5606, 5615, 5621, 5641, 5647, 5656, 5662, 5673,
|
||||
5679, 5688, 5694, 5717, 5723, 5732, 5738, 5749,
|
||||
5755, 5764, 5770, 5790, 5796, 5805, 5811, 5822,
|
||||
5828, 5837, 5843, 5868, 5874, 5883, 5889, 5900,
|
||||
5906, 5915, 5921, 5941, 5947, 5956, 5962, 5973,
|
||||
5979, 5988, 5994, 5858, 5864, 5873, 5879, 5890,
|
||||
5896, 5905, 5911, 5931, 5937, 5946, 5952, 5963,
|
||||
5969, 5978, 5984, 6009, 6015, 6024, 6030, 6041,
|
||||
6047, 6056, 6062, 6082, 6088, 6097, 6103, 6114,
|
||||
6120, 6129, 6135, 6158, 6164, 6173, 6179, 6190,
|
||||
6196, 6205, 6211, 6231, 6237, 6246, 6252, 6263,
|
||||
6269, 6278, 6284, 6309, 6315, 6324, 6330, 6341,
|
||||
6347, 6356, 6362, 6382, 6388, 6397, 6403, 6414,
|
||||
6420, 6429, 6435, 5303, 5309, 5318, 5324, 5335,
|
||||
5341, 5350, 5356, 5376, 5382, 5391, 5397, 5408,
|
||||
5414, 5423, 5429, 5454, 5460, 5469, 5475, 5486,
|
||||
5492, 5501, 5507, 5527, 5533, 5542, 5548, 5559,
|
||||
5565, 5574, 5580, 5603, 5609, 5618, 5624, 5635,
|
||||
5641, 5650, 5656, 5676, 5682, 5691, 5697, 5708,
|
||||
5714, 5723, 5729, 5754, 5760, 5769, 5775, 5786,
|
||||
5792, 5801, 5807, 5827, 5833, 5842, 5848, 5859,
|
||||
5865, 5874, 5880, 5744, 5750, 5759, 5765, 5776,
|
||||
5782, 5791, 5797, 5817, 5823, 5832, 5838, 5849,
|
||||
5855, 5864, 5870, 5895, 5901, 5910, 5916, 5927,
|
||||
5933, 5942, 5948, 5968, 5974, 5983, 5989, 6000,
|
||||
6006, 6015, 6021, 6044, 6050, 6059, 6065, 6076,
|
||||
6082, 6091, 6097, 6117, 6123, 6132, 6138, 6149,
|
||||
6155, 6164, 6170, 6195, 6201, 6210, 6216, 6227,
|
||||
6233, 6242, 6248, 6268, 6274, 6283, 6289, 6300,
|
||||
6306, 6315, 6321, 6116, 6122, 6131, 6137, 6148,
|
||||
6154, 6163, 6169, 6189, 6195, 6204, 6210, 6221,
|
||||
6227, 6236, 6242, 6267, 6273, 6282, 6288, 6299,
|
||||
6305, 6314, 6320, 6340, 6346, 6355, 6361, 6372,
|
||||
6378, 6387, 6393, 6416, 6422, 6431, 6437, 6448,
|
||||
6454, 6463, 6469, 6489, 6495, 6504, 6510, 6521,
|
||||
6527, 6536, 6542, 6567, 6573, 6582, 6588, 6599,
|
||||
6605, 6614, 6620, 6640, 6646, 6655, 6661, 6672,
|
||||
6678, 6687, 6693, 6557, 6563, 6572, 6578, 6589,
|
||||
6595, 6604, 6610, 6630, 6636, 6645, 6651, 6662,
|
||||
6668, 6677, 6683, 6708, 6714, 6723, 6729, 6740,
|
||||
6746, 6755, 6761, 6781, 6787, 6796, 6802, 6813,
|
||||
6819, 6828, 6834, 6857, 6863, 6872, 6878, 6889,
|
||||
6895, 6904, 6910, 6930, 6936, 6945, 6951, 6962,
|
||||
6968, 6977, 6983, 7008, 7014, 7023, 7029, 7040,
|
||||
7046, 7055, 7061, 7081, 7087, 7096, 7102, 7113,
|
||||
7119, 7128, 7134, 6392, 6398, 6407, 6413, 6424,
|
||||
6430, 6439, 6445, 6465, 6471, 6480, 6486, 6497,
|
||||
6503, 6512, 6518, 6543, 6549, 6558, 6564, 6575,
|
||||
6581, 6590, 6596, 6616, 6622, 6631, 6637, 6648,
|
||||
6654, 6663, 6669, 6692, 6698, 6707, 6713, 6724,
|
||||
6730, 6739, 6745, 6765, 6771, 6780, 6786, 6797,
|
||||
6803, 6812, 6818, 6843, 6849, 6858, 6864, 6875,
|
||||
6881, 6890, 6896, 6916, 6922, 6931, 6937, 6948,
|
||||
6954, 6963, 6969, 6833, 6839, 6848, 6854, 6865,
|
||||
6871, 6880, 6886, 6906, 6912, 6921, 6927, 6938,
|
||||
6944, 6953, 6959, 6984, 6990, 6999, 7005, 7016,
|
||||
7022, 7031, 7037, 7057, 7063, 7072, 7078, 7089,
|
||||
7095, 7104, 7110, 7133, 7139, 7148, 7154, 7165,
|
||||
7171, 7180, 7186, 7206, 7212, 7221, 7227, 7238,
|
||||
7244, 7253, 7259, 7284, 7290, 7299, 7305, 7316,
|
||||
7322, 7331, 7337, 7357, 7363, 7372, 7378, 7389,
|
||||
7395, 7404, 7410, 7205, 7211, 7220, 7226, 7237,
|
||||
7243, 7252, 7258, 7278, 7284, 7293, 7299, 7310,
|
||||
7316, 7325, 7331, 7356, 7362, 7371, 7377, 7388,
|
||||
7394, 7403, 7409, 7429, 7435, 7444, 7450, 7461,
|
||||
7467, 7476, 7482, 7505, 7511, 7520, 7526, 7537,
|
||||
7543, 7552, 7558, 7578, 7584, 7593, 7599, 7610,
|
||||
7616, 7625, 7631, 7656, 7662, 7671, 7677, 7688,
|
||||
7694, 7703, 7709, 7729, 7735, 7744, 7750, 7761
|
||||
};
|
||||
|
||||
static int VariableLevelCost(int level, const uint8_t probas[NUM_PROBAS]) {
|
||||
int pattern = VP8LevelCodes[level - 1][0];
|
||||
int bits = VP8LevelCodes[level - 1][1];
|
||||
@ -352,19 +57,21 @@ static int VariableLevelCost(int level, const uint8_t probas[NUM_PROBAS]) {
|
||||
//------------------------------------------------------------------------------
|
||||
// Pre-calc level costs once for all
|
||||
|
||||
void VP8CalculateLevelCosts(VP8Proba* const proba) {
|
||||
void VP8CalculateLevelCosts(VP8EncProba* const proba) {
|
||||
int ctype, band, ctx;
|
||||
|
||||
if (!proba->dirty_) return; // nothing to do.
|
||||
|
||||
for (ctype = 0; ctype < NUM_TYPES; ++ctype) {
|
||||
int n;
|
||||
for (band = 0; band < NUM_BANDS; ++band) {
|
||||
for(ctx = 0; ctx < NUM_CTX; ++ctx) {
|
||||
for (ctx = 0; ctx < NUM_CTX; ++ctx) {
|
||||
const uint8_t* const p = proba->coeffs_[ctype][band][ctx];
|
||||
uint16_t* const table = proba->level_cost_[ctype][band][ctx];
|
||||
const int cost_base = VP8BitCost(1, p[1]);
|
||||
const int cost0 = (ctx > 0) ? VP8BitCost(1, p[0]) : 0;
|
||||
const int cost_base = VP8BitCost(1, p[1]) + cost0;
|
||||
int v;
|
||||
table[0] = VP8BitCost(0, p[1]);
|
||||
table[0] = VP8BitCost(0, p[1]) + cost0;
|
||||
for (v = 1; v <= MAX_VARIABLE_LEVEL; ++v) {
|
||||
table[v] = cost_base + VariableLevelCost(v, p);
|
||||
}
|
||||
@ -372,6 +79,12 @@ void VP8CalculateLevelCosts(VP8Proba* const proba) {
|
||||
// actually constant.
|
||||
}
|
||||
}
|
||||
for (n = 0; n < 16; ++n) { // replicate bands. We don't need to sentinel.
|
||||
for (ctx = 0; ctx < NUM_CTX; ++ctx) {
|
||||
proba->remapped_costs_[ctype][n][ctx] =
|
||||
proba->level_cost_[ctype][VP8EncBands[n]][ctx];
|
||||
}
|
||||
}
|
||||
}
|
||||
proba->dirty_ = 0;
|
||||
}
|
||||
@ -385,110 +98,257 @@ const uint16_t VP8FixedCostsUV[4] = { 302, 984, 439, 642 };
|
||||
// note: these values include the fixed VP8BitCost(1, 145) mode selection cost.
|
||||
const uint16_t VP8FixedCostsI16[4] = { 663, 919, 872, 919 };
|
||||
const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES] = {
|
||||
{ { 251, 1362, 1934, 2085, 2314, 2230, 1839, 1988, 2437, 2348 },
|
||||
{ 403, 680, 1507, 1519, 2060, 2005, 1992, 1914, 1924, 1733 },
|
||||
{ 353, 1121, 973, 1895, 2060, 1787, 1671, 1516, 2012, 1868 },
|
||||
{ 770, 852, 1581, 632, 1393, 1780, 1823, 1936, 1074, 1218 },
|
||||
{ 510, 1270, 1467, 1319, 847, 1279, 1792, 2094, 1080, 1353 },
|
||||
{ 488, 1322, 918, 1573, 1300, 883, 1814, 1752, 1756, 1502 },
|
||||
{ 425, 992, 1820, 1514, 1843, 2440, 937, 1771, 1924, 1129 },
|
||||
{ 363, 1248, 1257, 1970, 2194, 2385, 1569, 953, 1951, 1601 },
|
||||
{ 723, 1257, 1631, 964, 963, 1508, 1697, 1824, 671, 1418 },
|
||||
{ 635, 1038, 1573, 930, 1673, 1413, 1410, 1687, 1410, 749 } },
|
||||
{ { 451, 613, 1345, 1702, 1870, 1716, 1728, 1766, 2190, 2310 },
|
||||
{ 678, 453, 1171, 1443, 1925, 1831, 2045, 1781, 1887, 1602 },
|
||||
{ 711, 666, 674, 1718, 1910, 1493, 1775, 1193, 2325, 2325 },
|
||||
{ 883, 854, 1583, 542, 1800, 1878, 1664, 2149, 1207, 1087 },
|
||||
{ 669, 994, 1248, 1122, 949, 1179, 1376, 1729, 1070, 1244 },
|
||||
{ 715, 1026, 715, 1350, 1430, 930, 1717, 1296, 1479, 1479 },
|
||||
{ 544, 841, 1656, 1450, 2094, 3883, 1010, 1759, 2076, 809 },
|
||||
{ 610, 855, 957, 1553, 2067, 1561, 1704, 824, 2066, 1226 },
|
||||
{ 833, 960, 1416, 819, 1277, 1619, 1501, 1617, 757, 1182 },
|
||||
{ 711, 964, 1252, 879, 1441, 1828, 1508, 1636, 1594, 734 } },
|
||||
{ { 605, 764, 734, 1713, 1747, 1192, 1819, 1353, 1877, 2392 },
|
||||
{ 866, 641, 586, 1622, 2072, 1431, 1888, 1346, 2189, 1764 },
|
||||
{ 901, 851, 456, 2165, 2281, 1405, 1739, 1193, 2183, 2443 },
|
||||
{ 770, 1045, 952, 1078, 1342, 1191, 1436, 1063, 1303, 995 },
|
||||
{ 901, 1086, 727, 1170, 884, 1105, 1267, 1401, 1739, 1337 },
|
||||
{ 951, 1162, 595, 1488, 1388, 703, 1790, 1366, 2057, 1724 },
|
||||
{ 534, 986, 1273, 1987, 3273, 1485, 1024, 1399, 1583, 866 },
|
||||
{ 699, 1182, 695, 1978, 1726, 1986, 1326, 714, 1750, 1672 },
|
||||
{ 951, 1217, 1209, 920, 1062, 1441, 1548, 999, 952, 932 },
|
||||
{ 733, 1284, 784, 1256, 1557, 1098, 1257, 1357, 1414, 908 } },
|
||||
{ { 316, 1075, 1653, 1220, 2145, 2051, 1730, 2131, 1884, 1790 },
|
||||
{ 745, 516, 1404, 894, 1599, 2375, 2013, 2105, 1475, 1381 },
|
||||
{ 516, 729, 1088, 1319, 1637, 3426, 1636, 1275, 1531, 1453 },
|
||||
{ 894, 943, 2138, 468, 1704, 2259, 2069, 1763, 1266, 1158 },
|
||||
{ 605, 1025, 1235, 871, 1170, 1767, 1493, 1500, 1104, 1258 },
|
||||
{ 739, 826, 1207, 1151, 1412, 846, 1305, 2726, 1014, 1569 },
|
||||
{ 558, 825, 1820, 1398, 3344, 1556, 1218, 1550, 1228, 878 },
|
||||
{ 429, 951, 1089, 1816, 3861, 3861, 1556, 969, 1568, 1828 },
|
||||
{ 883, 961, 1752, 769, 1468, 1810, 2081, 2346, 613, 1298 },
|
||||
{ 803, 895, 1372, 641, 1303, 1708, 1686, 1700, 1306, 1033 } },
|
||||
{ { 439, 1267, 1270, 1579, 963, 1193, 1723, 1729, 1198, 1993 },
|
||||
{ 705, 725, 1029, 1153, 1176, 1103, 1821, 1567, 1259, 1574 },
|
||||
{ 723, 859, 802, 1253, 972, 1202, 1407, 1665, 1520, 1674 },
|
||||
{ 894, 960, 1254, 887, 1052, 1607, 1344, 1349, 865, 1150 },
|
||||
{ 833, 1312, 1337, 1205, 572, 1288, 1414, 1529, 1088, 1430 },
|
||||
{ 842, 1279, 1068, 1861, 862, 688, 1861, 1630, 1039, 1381 },
|
||||
{ 766, 938, 1279, 1546, 3338, 1550, 1031, 1542, 1288, 640 },
|
||||
{ 715, 1090, 835, 1609, 1100, 1100, 1603, 1019, 1102, 1617 },
|
||||
{ 894, 1813, 1500, 1188, 789, 1194, 1491, 1919, 617, 1333 },
|
||||
{ 610, 1076, 1644, 1281, 1283, 975, 1179, 1688, 1434, 889 } },
|
||||
{ { 544, 971, 1146, 1849, 1221, 740, 1857, 1621, 1683, 2430 },
|
||||
{ 723, 705, 961, 1371, 1426, 821, 2081, 2079, 1839, 1380 },
|
||||
{ 783, 857, 703, 2145, 1419, 814, 1791, 1310, 1609, 2206 },
|
||||
{ 997, 1000, 1153, 792, 1229, 1162, 1810, 1418, 942, 979 },
|
||||
{ 901, 1226, 883, 1289, 793, 715, 1904, 1649, 1319, 3108 },
|
||||
{ 979, 1478, 782, 2216, 1454, 455, 3092, 1591, 1997, 1664 },
|
||||
{ 663, 1110, 1504, 1114, 1522, 3311, 676, 1522, 1530, 1024 },
|
||||
{ 605, 1138, 1153, 1314, 1569, 1315, 1157, 804, 1574, 1320 },
|
||||
{ 770, 1216, 1218, 1227, 869, 1384, 1232, 1375, 834, 1239 },
|
||||
{ 775, 1007, 843, 1216, 1225, 1074, 2527, 1479, 1149, 975 } },
|
||||
{ { 477, 817, 1309, 1439, 1708, 1454, 1159, 1241, 1945, 1672 },
|
||||
{ 577, 796, 1112, 1271, 1618, 1458, 1087, 1345, 1831, 1265 },
|
||||
{ 663, 776, 753, 1940, 1690, 1690, 1227, 1097, 3149, 1361 },
|
||||
{ 766, 1299, 1744, 1161, 1565, 1106, 1045, 1230, 1232, 707 },
|
||||
{ 915, 1026, 1404, 1182, 1184, 851, 1428, 2425, 1043, 789 },
|
||||
{ 883, 1456, 790, 1082, 1086, 985, 1083, 1484, 1238, 1160 },
|
||||
{ 507, 1345, 2261, 1995, 1847, 3636, 653, 1761, 2287, 933 },
|
||||
{ 553, 1193, 1470, 2057, 2059, 2059, 833, 779, 2058, 1263 },
|
||||
{ 766, 1275, 1515, 1039, 957, 1554, 1286, 1540, 1289, 705 },
|
||||
{ 499, 1378, 1496, 1385, 1850, 1850, 1044, 2465, 1515, 720 } },
|
||||
{ { 553, 930, 978, 2077, 1968, 1481, 1457, 761, 1957, 2362 },
|
||||
{ 694, 864, 905, 1720, 1670, 1621, 1429, 718, 2125, 1477 },
|
||||
{ 699, 968, 658, 3190, 2024, 1479, 1865, 750, 2060, 2320 },
|
||||
{ 733, 1308, 1296, 1062, 1576, 1322, 1062, 1112, 1172, 816 },
|
||||
{ 920, 927, 1052, 939, 947, 1156, 1152, 1073, 3056, 1268 },
|
||||
{ 723, 1534, 711, 1547, 1294, 892, 1553, 928, 1815, 1561 },
|
||||
{ 663, 1366, 1583, 2111, 1712, 3501, 522, 1155, 2130, 1133 },
|
||||
{ 614, 1731, 1188, 2343, 1944, 3733, 1287, 487, 3546, 1758 },
|
||||
{ 770, 1585, 1312, 826, 884, 2673, 1185, 1006, 1195, 1195 },
|
||||
{ 758, 1333, 1273, 1023, 1621, 1162, 1351, 833, 1479, 862 } },
|
||||
{ { 376, 1193, 1446, 1149, 1545, 1577, 1870, 1789, 1175, 1823 },
|
||||
{ 803, 633, 1136, 1058, 1350, 1323, 1598, 2247, 1072, 1252 },
|
||||
{ 614, 1048, 943, 981, 1152, 1869, 1461, 1020, 1618, 1618 },
|
||||
{ 1107, 1085, 1282, 592, 1779, 1933, 1648, 2403, 691, 1246 },
|
||||
{ 851, 1309, 1223, 1243, 895, 1593, 1792, 2317, 627, 1076 },
|
||||
{ 770, 1216, 1030, 1125, 921, 981, 1629, 1131, 1049, 1646 },
|
||||
{ 626, 1469, 1456, 1081, 1489, 3278, 981, 1232, 1498, 733 },
|
||||
{ 617, 1201, 812, 1220, 1476, 1476, 1478, 970, 1228, 1488 },
|
||||
{ 1179, 1393, 1540, 999, 1243, 1503, 1916, 1925, 414, 1614 },
|
||||
{ 943, 1088, 1490, 682, 1112, 1372, 1756, 1505, 966, 966 } },
|
||||
{ { 322, 1142, 1589, 1396, 2144, 1859, 1359, 1925, 2084, 1518 },
|
||||
{ 617, 625, 1241, 1234, 2121, 1615, 1524, 1858, 1720, 1004 },
|
||||
{ 553, 851, 786, 1299, 1452, 1560, 1372, 1561, 1967, 1713 },
|
||||
{ 770, 977, 1396, 568, 1893, 1639, 1540, 2108, 1430, 1013 },
|
||||
{ 684, 1120, 1375, 982, 930, 2719, 1638, 1643, 933, 993 },
|
||||
{ 553, 1103, 996, 1356, 1361, 1005, 1507, 1761, 1184, 1268 },
|
||||
{ 419, 1247, 1537, 1554, 1817, 3606, 1026, 1666, 1829, 923 },
|
||||
{ 439, 1139, 1101, 1257, 3710, 1922, 1205, 1040, 1931, 1529 },
|
||||
{ 979, 935, 1269, 847, 1202, 1286, 1530, 1535, 827, 1036 },
|
||||
{ 516, 1378, 1569, 1110, 1798, 1798, 1198, 2199, 1543, 712 } },
|
||||
{ { 40, 1151, 1723, 1874, 2103, 2019, 1628, 1777, 2226, 2137 },
|
||||
{ 192, 469, 1296, 1308, 1849, 1794, 1781, 1703, 1713, 1522 },
|
||||
{ 142, 910, 762, 1684, 1849, 1576, 1460, 1305, 1801, 1657 },
|
||||
{ 559, 641, 1370, 421, 1182, 1569, 1612, 1725, 863, 1007 },
|
||||
{ 299, 1059, 1256, 1108, 636, 1068, 1581, 1883, 869, 1142 },
|
||||
{ 277, 1111, 707, 1362, 1089, 672, 1603, 1541, 1545, 1291 },
|
||||
{ 214, 781, 1609, 1303, 1632, 2229, 726, 1560, 1713, 918 },
|
||||
{ 152, 1037, 1046, 1759, 1983, 2174, 1358, 742, 1740, 1390 },
|
||||
{ 512, 1046, 1420, 753, 752, 1297, 1486, 1613, 460, 1207 },
|
||||
{ 424, 827, 1362, 719, 1462, 1202, 1199, 1476, 1199, 538 } },
|
||||
{ { 240, 402, 1134, 1491, 1659, 1505, 1517, 1555, 1979, 2099 },
|
||||
{ 467, 242, 960, 1232, 1714, 1620, 1834, 1570, 1676, 1391 },
|
||||
{ 500, 455, 463, 1507, 1699, 1282, 1564, 982, 2114, 2114 },
|
||||
{ 672, 643, 1372, 331, 1589, 1667, 1453, 1938, 996, 876 },
|
||||
{ 458, 783, 1037, 911, 738, 968, 1165, 1518, 859, 1033 },
|
||||
{ 504, 815, 504, 1139, 1219, 719, 1506, 1085, 1268, 1268 },
|
||||
{ 333, 630, 1445, 1239, 1883, 3672, 799, 1548, 1865, 598 },
|
||||
{ 399, 644, 746, 1342, 1856, 1350, 1493, 613, 1855, 1015 },
|
||||
{ 622, 749, 1205, 608, 1066, 1408, 1290, 1406, 546, 971 },
|
||||
{ 500, 753, 1041, 668, 1230, 1617, 1297, 1425, 1383, 523 } },
|
||||
{ { 394, 553, 523, 1502, 1536, 981, 1608, 1142, 1666, 2181 },
|
||||
{ 655, 430, 375, 1411, 1861, 1220, 1677, 1135, 1978, 1553 },
|
||||
{ 690, 640, 245, 1954, 2070, 1194, 1528, 982, 1972, 2232 },
|
||||
{ 559, 834, 741, 867, 1131, 980, 1225, 852, 1092, 784 },
|
||||
{ 690, 875, 516, 959, 673, 894, 1056, 1190, 1528, 1126 },
|
||||
{ 740, 951, 384, 1277, 1177, 492, 1579, 1155, 1846, 1513 },
|
||||
{ 323, 775, 1062, 1776, 3062, 1274, 813, 1188, 1372, 655 },
|
||||
{ 488, 971, 484, 1767, 1515, 1775, 1115, 503, 1539, 1461 },
|
||||
{ 740, 1006, 998, 709, 851, 1230, 1337, 788, 741, 721 },
|
||||
{ 522, 1073, 573, 1045, 1346, 887, 1046, 1146, 1203, 697 } },
|
||||
{ { 105, 864, 1442, 1009, 1934, 1840, 1519, 1920, 1673, 1579 },
|
||||
{ 534, 305, 1193, 683, 1388, 2164, 1802, 1894, 1264, 1170 },
|
||||
{ 305, 518, 877, 1108, 1426, 3215, 1425, 1064, 1320, 1242 },
|
||||
{ 683, 732, 1927, 257, 1493, 2048, 1858, 1552, 1055, 947 },
|
||||
{ 394, 814, 1024, 660, 959, 1556, 1282, 1289, 893, 1047 },
|
||||
{ 528, 615, 996, 940, 1201, 635, 1094, 2515, 803, 1358 },
|
||||
{ 347, 614, 1609, 1187, 3133, 1345, 1007, 1339, 1017, 667 },
|
||||
{ 218, 740, 878, 1605, 3650, 3650, 1345, 758, 1357, 1617 },
|
||||
{ 672, 750, 1541, 558, 1257, 1599, 1870, 2135, 402, 1087 },
|
||||
{ 592, 684, 1161, 430, 1092, 1497, 1475, 1489, 1095, 822 } },
|
||||
{ { 228, 1056, 1059, 1368, 752, 982, 1512, 1518, 987, 1782 },
|
||||
{ 494, 514, 818, 942, 965, 892, 1610, 1356, 1048, 1363 },
|
||||
{ 512, 648, 591, 1042, 761, 991, 1196, 1454, 1309, 1463 },
|
||||
{ 683, 749, 1043, 676, 841, 1396, 1133, 1138, 654, 939 },
|
||||
{ 622, 1101, 1126, 994, 361, 1077, 1203, 1318, 877, 1219 },
|
||||
{ 631, 1068, 857, 1650, 651, 477, 1650, 1419, 828, 1170 },
|
||||
{ 555, 727, 1068, 1335, 3127, 1339, 820, 1331, 1077, 429 },
|
||||
{ 504, 879, 624, 1398, 889, 889, 1392, 808, 891, 1406 },
|
||||
{ 683, 1602, 1289, 977, 578, 983, 1280, 1708, 406, 1122 },
|
||||
{ 399, 865, 1433, 1070, 1072, 764, 968, 1477, 1223, 678 } },
|
||||
{ { 333, 760, 935, 1638, 1010, 529, 1646, 1410, 1472, 2219 },
|
||||
{ 512, 494, 750, 1160, 1215, 610, 1870, 1868, 1628, 1169 },
|
||||
{ 572, 646, 492, 1934, 1208, 603, 1580, 1099, 1398, 1995 },
|
||||
{ 786, 789, 942, 581, 1018, 951, 1599, 1207, 731, 768 },
|
||||
{ 690, 1015, 672, 1078, 582, 504, 1693, 1438, 1108, 2897 },
|
||||
{ 768, 1267, 571, 2005, 1243, 244, 2881, 1380, 1786, 1453 },
|
||||
{ 452, 899, 1293, 903, 1311, 3100, 465, 1311, 1319, 813 },
|
||||
{ 394, 927, 942, 1103, 1358, 1104, 946, 593, 1363, 1109 },
|
||||
{ 559, 1005, 1007, 1016, 658, 1173, 1021, 1164, 623, 1028 },
|
||||
{ 564, 796, 632, 1005, 1014, 863, 2316, 1268, 938, 764 } },
|
||||
{ { 266, 606, 1098, 1228, 1497, 1243, 948, 1030, 1734, 1461 },
|
||||
{ 366, 585, 901, 1060, 1407, 1247, 876, 1134, 1620, 1054 },
|
||||
{ 452, 565, 542, 1729, 1479, 1479, 1016, 886, 2938, 1150 },
|
||||
{ 555, 1088, 1533, 950, 1354, 895, 834, 1019, 1021, 496 },
|
||||
{ 704, 815, 1193, 971, 973, 640, 1217, 2214, 832, 578 },
|
||||
{ 672, 1245, 579, 871, 875, 774, 872, 1273, 1027, 949 },
|
||||
{ 296, 1134, 2050, 1784, 1636, 3425, 442, 1550, 2076, 722 },
|
||||
{ 342, 982, 1259, 1846, 1848, 1848, 622, 568, 1847, 1052 },
|
||||
{ 555, 1064, 1304, 828, 746, 1343, 1075, 1329, 1078, 494 },
|
||||
{ 288, 1167, 1285, 1174, 1639, 1639, 833, 2254, 1304, 509 } },
|
||||
{ { 342, 719, 767, 1866, 1757, 1270, 1246, 550, 1746, 2151 },
|
||||
{ 483, 653, 694, 1509, 1459, 1410, 1218, 507, 1914, 1266 },
|
||||
{ 488, 757, 447, 2979, 1813, 1268, 1654, 539, 1849, 2109 },
|
||||
{ 522, 1097, 1085, 851, 1365, 1111, 851, 901, 961, 605 },
|
||||
{ 709, 716, 841, 728, 736, 945, 941, 862, 2845, 1057 },
|
||||
{ 512, 1323, 500, 1336, 1083, 681, 1342, 717, 1604, 1350 },
|
||||
{ 452, 1155, 1372, 1900, 1501, 3290, 311, 944, 1919, 922 },
|
||||
{ 403, 1520, 977, 2132, 1733, 3522, 1076, 276, 3335, 1547 },
|
||||
{ 559, 1374, 1101, 615, 673, 2462, 974, 795, 984, 984 },
|
||||
{ 547, 1122, 1062, 812, 1410, 951, 1140, 622, 1268, 651 } },
|
||||
{ { 165, 982, 1235, 938, 1334, 1366, 1659, 1578, 964, 1612 },
|
||||
{ 592, 422, 925, 847, 1139, 1112, 1387, 2036, 861, 1041 },
|
||||
{ 403, 837, 732, 770, 941, 1658, 1250, 809, 1407, 1407 },
|
||||
{ 896, 874, 1071, 381, 1568, 1722, 1437, 2192, 480, 1035 },
|
||||
{ 640, 1098, 1012, 1032, 684, 1382, 1581, 2106, 416, 865 },
|
||||
{ 559, 1005, 819, 914, 710, 770, 1418, 920, 838, 1435 },
|
||||
{ 415, 1258, 1245, 870, 1278, 3067, 770, 1021, 1287, 522 },
|
||||
{ 406, 990, 601, 1009, 1265, 1265, 1267, 759, 1017, 1277 },
|
||||
{ 968, 1182, 1329, 788, 1032, 1292, 1705, 1714, 203, 1403 },
|
||||
{ 732, 877, 1279, 471, 901, 1161, 1545, 1294, 755, 755 } },
|
||||
{ { 111, 931, 1378, 1185, 1933, 1648, 1148, 1714, 1873, 1307 },
|
||||
{ 406, 414, 1030, 1023, 1910, 1404, 1313, 1647, 1509, 793 },
|
||||
{ 342, 640, 575, 1088, 1241, 1349, 1161, 1350, 1756, 1502 },
|
||||
{ 559, 766, 1185, 357, 1682, 1428, 1329, 1897, 1219, 802 },
|
||||
{ 473, 909, 1164, 771, 719, 2508, 1427, 1432, 722, 782 },
|
||||
{ 342, 892, 785, 1145, 1150, 794, 1296, 1550, 973, 1057 },
|
||||
{ 208, 1036, 1326, 1343, 1606, 3395, 815, 1455, 1618, 712 },
|
||||
{ 228, 928, 890, 1046, 3499, 1711, 994, 829, 1720, 1318 },
|
||||
{ 768, 724, 1058, 636, 991, 1075, 1319, 1324, 616, 825 },
|
||||
{ 305, 1167, 1358, 899, 1587, 1587, 987, 1988, 1332, 501 } }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// helper functions for residuals struct VP8Residual.
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
void VP8InitResidual(int first, int coeff_type,
|
||||
VP8Encoder* const enc, VP8Residual* const res) {
|
||||
res->coeff_type = coeff_type;
|
||||
res->prob = enc->proba_.coeffs_[coeff_type];
|
||||
res->stats = enc->proba_.stats_[coeff_type];
|
||||
res->costs = enc->proba_.remapped_costs_[coeff_type];
|
||||
res->first = first;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Mode costs
|
||||
|
||||
int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]) {
|
||||
const int x = (it->i4_ & 3), y = (it->i4_ >> 2);
|
||||
VP8Residual res;
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
int R = 0;
|
||||
int ctx;
|
||||
|
||||
VP8InitResidual(0, 3, enc, &res);
|
||||
ctx = it->top_nz_[x] + it->left_nz_[y];
|
||||
VP8SetResidualCoeffs(levels, &res);
|
||||
R += VP8GetResidualCost(ctx, &res);
|
||||
return R;
|
||||
}
|
||||
|
||||
int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd) {
|
||||
VP8Residual res;
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
int x, y;
|
||||
int R = 0;
|
||||
|
||||
VP8IteratorNzToBytes(it); // re-import the non-zero context
|
||||
|
||||
// DC
|
||||
VP8InitResidual(0, 1, enc, &res);
|
||||
VP8SetResidualCoeffs(rd->y_dc_levels, &res);
|
||||
R += VP8GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &res);
|
||||
|
||||
// AC
|
||||
VP8InitResidual(1, 0, enc, &res);
|
||||
for (y = 0; y < 4; ++y) {
|
||||
for (x = 0; x < 4; ++x) {
|
||||
const int ctx = it->top_nz_[x] + it->left_nz_[y];
|
||||
VP8SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res);
|
||||
R += VP8GetResidualCost(ctx, &res);
|
||||
it->top_nz_[x] = it->left_nz_[y] = (res.last >= 0);
|
||||
}
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd) {
|
||||
VP8Residual res;
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
int ch, x, y;
|
||||
int R = 0;
|
||||
|
||||
VP8IteratorNzToBytes(it); // re-import the non-zero context
|
||||
|
||||
VP8InitResidual(0, 2, enc, &res);
|
||||
for (ch = 0; ch <= 2; ch += 2) {
|
||||
for (y = 0; y < 2; ++y) {
|
||||
for (x = 0; x < 2; ++x) {
|
||||
const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y];
|
||||
VP8SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res);
|
||||
R += VP8GetResidualCost(ctx, &res);
|
||||
it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = (res.last >= 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
return R;
|
||||
}
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Recording of token probabilities.
|
||||
|
||||
// Record proba context used
|
||||
static int Record(int bit, proba_t* const stats) {
|
||||
proba_t p = *stats;
|
||||
if (p >= 0xffff0000u) { // an overflow is inbound.
|
||||
p = ((p + 1u) >> 1) & 0x7fff7fffu; // -> divide the stats by 2.
|
||||
}
|
||||
// record bit count (lower 16 bits) and increment total count (upper 16 bits).
|
||||
p += 0x00010000u + bit;
|
||||
*stats = p;
|
||||
return bit;
|
||||
}
|
||||
|
||||
// We keep the table-free variant around for reference, in case.
|
||||
#define USE_LEVEL_CODE_TABLE
|
||||
|
||||
// Simulate block coding, but only record statistics.
|
||||
// Note: no need to record the fixed probas.
|
||||
int VP8RecordCoeffs(int ctx, const VP8Residual* const res) {
|
||||
int n = res->first;
|
||||
// should be stats[VP8EncBands[n]], but it's equivalent for n=0 or 1
|
||||
proba_t* s = res->stats[n][ctx];
|
||||
if (res->last < 0) {
|
||||
Record(0, s + 0);
|
||||
return 0;
|
||||
}
|
||||
while (n <= res->last) {
|
||||
int v;
|
||||
Record(1, s + 0); // order of record doesn't matter
|
||||
while ((v = res->coeffs[n++]) == 0) {
|
||||
Record(0, s + 1);
|
||||
s = res->stats[VP8EncBands[n]][0];
|
||||
}
|
||||
Record(1, s + 1);
|
||||
if (!Record(2u < (unsigned int)(v + 1), s + 2)) { // v = -1 or 1
|
||||
s = res->stats[VP8EncBands[n]][1];
|
||||
} else {
|
||||
v = abs(v);
|
||||
#if !defined(USE_LEVEL_CODE_TABLE)
|
||||
if (!Record(v > 4, s + 3)) {
|
||||
if (Record(v != 2, s + 4))
|
||||
Record(v == 4, s + 5);
|
||||
} else if (!Record(v > 10, s + 6)) {
|
||||
Record(v > 6, s + 7);
|
||||
} else if (!Record((v >= 3 + (8 << 2)), s + 8)) {
|
||||
Record((v >= 3 + (8 << 1)), s + 9);
|
||||
} else {
|
||||
Record((v >= 3 + (8 << 3)), s + 10);
|
||||
}
|
||||
#else
|
||||
if (v > MAX_VARIABLE_LEVEL) {
|
||||
v = MAX_VARIABLE_LEVEL;
|
||||
}
|
||||
|
||||
{
|
||||
const int bits = VP8LevelCodes[v - 1][1];
|
||||
int pattern = VP8LevelCodes[v - 1][0];
|
||||
int i;
|
||||
for (i = 0; (pattern >>= 1) != 0; ++i) {
|
||||
const int mask = 2 << i;
|
||||
if (pattern & 1) Record(!!(bits & mask), s + 3 + i);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
s = res->stats[VP8EncBands[n]][2];
|
||||
}
|
||||
}
|
||||
if (n < 16) Record(0, s + 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Cost tables for level and modes.
|
||||
@ -12,14 +14,32 @@
|
||||
#ifndef WEBP_ENC_COST_H_
|
||||
#define WEBP_ENC_COST_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "./vp8enci.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const uint16_t VP8LevelFixedCosts[2048]; // approximate cost per level
|
||||
extern const uint16_t VP8EntropyCost[256]; // 8bit fixed-point log(p)
|
||||
// On-the-fly info about the current set of residuals. Handy to avoid
|
||||
// passing zillions of params.
|
||||
typedef struct VP8Residual VP8Residual;
|
||||
struct VP8Residual {
|
||||
int first;
|
||||
int last;
|
||||
const int16_t* coeffs;
|
||||
|
||||
int coeff_type;
|
||||
ProbaArray* prob;
|
||||
StatsArray* stats;
|
||||
CostArrayPtr costs;
|
||||
};
|
||||
|
||||
void VP8InitResidual(int first, int coeff_type,
|
||||
VP8Encoder* const enc, VP8Residual* const res);
|
||||
|
||||
int VP8RecordCoeffs(int ctx, const VP8Residual* const res);
|
||||
|
||||
// Cost of coding one event with probability 'proba'.
|
||||
static WEBP_INLINE int VP8BitCost(int bit, uint8_t proba) {
|
||||
@ -28,7 +48,7 @@ static WEBP_INLINE int VP8BitCost(int bit, uint8_t proba) {
|
||||
|
||||
// Level cost calculations
|
||||
extern const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2];
|
||||
void VP8CalculateLevelCosts(VP8Proba* const proba);
|
||||
void VP8CalculateLevelCosts(VP8EncProba* const proba);
|
||||
static WEBP_INLINE int VP8LevelCost(const uint16_t* const table, int level) {
|
||||
return VP8LevelFixedCosts[level]
|
||||
+ table[(level > MAX_VARIABLE_LEVEL) ? MAX_VARIABLE_LEVEL : level];
|
||||
@ -41,7 +61,7 @@ extern const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,193 +1,67 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Selecting filter level
|
||||
//
|
||||
// Author: somnath@google.com (Somnath Banerjee)
|
||||
|
||||
#include <assert.h>
|
||||
#include "./vp8enci.h"
|
||||
#include "../dsp/dsp.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
// This table gives, for a given sharpness, the filtering strength to be
|
||||
// used (at least) in order to filter a given edge step delta.
|
||||
// This is constructed by brute force inspection: for all delta, we iterate
|
||||
// over all possible filtering strength / thresh until needs_filter() returns
|
||||
// true.
|
||||
#define MAX_DELTA_SIZE 64
|
||||
static const uint8_t kLevelsFromDelta[8][MAX_DELTA_SIZE] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
|
||||
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
|
||||
32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63 },
|
||||
{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 17, 18,
|
||||
20, 21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42,
|
||||
44, 45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16, 17, 19,
|
||||
20, 22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43,
|
||||
44, 46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19,
|
||||
21, 22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43,
|
||||
45, 46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20,
|
||||
21, 23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44,
|
||||
45, 47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 17, 19, 20,
|
||||
22, 23, 25, 26, 28, 29, 31, 32, 34, 35, 37, 38, 40, 41, 43, 44,
|
||||
46, 47, 49, 50, 52, 53, 55, 56, 58, 59, 61, 62, 63, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 13, 15, 16, 18, 19, 21,
|
||||
22, 24, 25, 27, 28, 30, 31, 33, 34, 36, 37, 39, 40, 42, 43, 45,
|
||||
46, 48, 49, 51, 52, 54, 55, 57, 58, 60, 61, 63, 63, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 },
|
||||
{ 0, 1, 2, 4, 5, 7, 8, 9, 11, 12, 14, 15, 17, 18, 20, 21,
|
||||
23, 24, 26, 27, 29, 30, 32, 33, 35, 36, 38, 39, 41, 42, 44, 45,
|
||||
47, 48, 50, 51, 53, 54, 56, 57, 59, 60, 62, 63, 63, 63, 63, 63,
|
||||
63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63, 63 }
|
||||
};
|
||||
|
||||
// NOTE: clip1, tables and InitTables are repeated entries of dsp.c
|
||||
static uint8_t abs0[255 + 255 + 1]; // abs(i)
|
||||
static uint8_t abs1[255 + 255 + 1]; // abs(i)>>1
|
||||
static int8_t sclip1[1020 + 1020 + 1]; // clips [-1020, 1020] to [-128, 127]
|
||||
static int8_t sclip2[112 + 112 + 1]; // clips [-112, 112] to [-16, 15]
|
||||
static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
|
||||
|
||||
static int tables_ok = 0;
|
||||
|
||||
static void InitTables(void) {
|
||||
if (!tables_ok) {
|
||||
int i;
|
||||
for (i = -255; i <= 255; ++i) {
|
||||
abs0[255 + i] = (i < 0) ? -i : i;
|
||||
abs1[255 + i] = abs0[255 + i] >> 1;
|
||||
}
|
||||
for (i = -1020; i <= 1020; ++i) {
|
||||
sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i;
|
||||
}
|
||||
for (i = -112; i <= 112; ++i) {
|
||||
sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i;
|
||||
}
|
||||
for (i = -255; i <= 255 + 255; ++i) {
|
||||
clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
|
||||
}
|
||||
tables_ok = 1;
|
||||
}
|
||||
int VP8FilterStrengthFromDelta(int sharpness, int delta) {
|
||||
const int pos = (delta < MAX_DELTA_SIZE) ? delta : MAX_DELTA_SIZE - 1;
|
||||
assert(sharpness >= 0 && sharpness <= 7);
|
||||
return kLevelsFromDelta[sharpness][pos];
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Edge filtering functions
|
||||
|
||||
// 4 pixels in, 2 pixels out
|
||||
static WEBP_INLINE void do_filter2(uint8_t* p, int step) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1];
|
||||
const int a1 = sclip2[112 + ((a + 4) >> 3)];
|
||||
const int a2 = sclip2[112 + ((a + 3) >> 3)];
|
||||
p[-step] = clip1[255 + p0 + a2];
|
||||
p[ 0] = clip1[255 + q0 - a1];
|
||||
}
|
||||
|
||||
// 4 pixels in, 4 pixels out
|
||||
static WEBP_INLINE void do_filter4(uint8_t* p, int step) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
const int a = 3 * (q0 - p0);
|
||||
const int a1 = sclip2[112 + ((a + 4) >> 3)];
|
||||
const int a2 = sclip2[112 + ((a + 3) >> 3)];
|
||||
const int a3 = (a1 + 1) >> 1;
|
||||
p[-2*step] = clip1[255 + p1 + a3];
|
||||
p[- step] = clip1[255 + p0 + a2];
|
||||
p[ 0] = clip1[255 + q0 - a1];
|
||||
p[ step] = clip1[255 + q1 - a3];
|
||||
}
|
||||
|
||||
// high edge-variance
|
||||
static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int thresh) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh;
|
||||
}
|
||||
|
||||
static WEBP_INLINE int needs_filter2(const uint8_t* p,
|
||||
int step, int t, int it) {
|
||||
const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
|
||||
const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step];
|
||||
if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t)
|
||||
return 0;
|
||||
return abs0[255 + p3 - p2] <= it && abs0[255 + p2 - p1] <= it &&
|
||||
abs0[255 + p1 - p0] <= it && abs0[255 + q3 - q2] <= it &&
|
||||
abs0[255 + q2 - q1] <= it && abs0[255 + q1 - q0] <= it;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Simple In-loop filtering (Paragraph 15.2)
|
||||
|
||||
static void SimpleVFilter16(uint8_t* p, int stride, int thresh) {
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
if (needs_filter(p + i, stride, thresh)) {
|
||||
do_filter2(p + i, stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SimpleHFilter16(uint8_t* p, int stride, int thresh) {
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
if (needs_filter(p + i * stride, 1, thresh)) {
|
||||
do_filter2(p + i * stride, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SimpleVFilter16i(uint8_t* p, int stride, int thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4 * stride;
|
||||
SimpleVFilter16(p, stride, thresh);
|
||||
}
|
||||
}
|
||||
|
||||
static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4;
|
||||
SimpleHFilter16(p, stride, thresh);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Complex In-loop filtering (Paragraph 15.3)
|
||||
|
||||
static WEBP_INLINE void FilterLoop24(uint8_t* p,
|
||||
int hstride, int vstride, int size,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
while (size-- > 0) {
|
||||
if (needs_filter2(p, hstride, thresh, ithresh)) {
|
||||
if (hev(p, hstride, hev_thresh)) {
|
||||
do_filter2(p, hstride);
|
||||
} else {
|
||||
do_filter4(p, hstride);
|
||||
}
|
||||
}
|
||||
p += vstride;
|
||||
}
|
||||
}
|
||||
|
||||
// on three inner edges
|
||||
static void VFilter16i(uint8_t* p, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4 * stride;
|
||||
FilterLoop24(p, stride, 1, 16, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
}
|
||||
|
||||
static void HFilter16i(uint8_t* p, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4;
|
||||
FilterLoop24(p, 1, stride, 16, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
}
|
||||
|
||||
static void VFilter8i(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
FilterLoop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
|
||||
FilterLoop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
|
||||
static void HFilter8i(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
FilterLoop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
|
||||
FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void (*VP8EncVFilter16i)(uint8_t*, int, int, int, int) = VFilter16i;
|
||||
void (*VP8EncHFilter16i)(uint8_t*, int, int, int, int) = HFilter16i;
|
||||
void (*VP8EncVFilter8i)(uint8_t*, uint8_t*, int, int, int, int) = VFilter8i;
|
||||
void (*VP8EncHFilter8i)(uint8_t*, uint8_t*, int, int, int, int) = HFilter8i;
|
||||
|
||||
void (*VP8EncSimpleVFilter16i)(uint8_t*, int, int) = SimpleVFilter16i;
|
||||
void (*VP8EncSimpleHFilter16i)(uint8_t*, int, int) = SimpleHFilter16i;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Paragraph 15.4: compute the inner-edge filtering strength
|
||||
|
||||
@ -211,22 +85,22 @@ static void DoFilter(const VP8EncIterator* const it, int level) {
|
||||
const int ilevel = GetILevel(enc->config_->filter_sharpness, level);
|
||||
const int limit = 2 * level + ilevel;
|
||||
|
||||
uint8_t* const y_dst = it->yuv_out2_ + Y_OFF;
|
||||
uint8_t* const u_dst = it->yuv_out2_ + U_OFF;
|
||||
uint8_t* const v_dst = it->yuv_out2_ + V_OFF;
|
||||
uint8_t* const y_dst = it->yuv_out2_ + Y_OFF_ENC;
|
||||
uint8_t* const u_dst = it->yuv_out2_ + U_OFF_ENC;
|
||||
uint8_t* const v_dst = it->yuv_out2_ + V_OFF_ENC;
|
||||
|
||||
// copy current block to yuv_out2_
|
||||
memcpy(y_dst, it->yuv_out_, YUV_SIZE * sizeof(uint8_t));
|
||||
memcpy(y_dst, it->yuv_out_, YUV_SIZE_ENC * sizeof(uint8_t));
|
||||
|
||||
if (enc->filter_hdr_.simple_ == 1) { // simple
|
||||
VP8EncSimpleHFilter16i(y_dst, BPS, limit);
|
||||
VP8EncSimpleVFilter16i(y_dst, BPS, limit);
|
||||
VP8SimpleHFilter16i(y_dst, BPS, limit);
|
||||
VP8SimpleVFilter16i(y_dst, BPS, limit);
|
||||
} else { // complex
|
||||
const int hev_thresh = (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
||||
VP8EncHFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
|
||||
VP8EncHFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
|
||||
VP8EncVFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
|
||||
VP8EncVFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
|
||||
VP8HFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
|
||||
VP8HFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
|
||||
VP8VFilter16i(y_dst, BPS, limit, ilevel, hev_thresh);
|
||||
VP8VFilter8i(u_dst, v_dst, BPS, limit, ilevel, hev_thresh);
|
||||
}
|
||||
}
|
||||
|
||||
@ -321,13 +195,16 @@ static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) {
|
||||
// compute SSIM in a 10 x 10 window
|
||||
for (x = 3; x < 13; x++) {
|
||||
for (y = 3; y < 13; y++) {
|
||||
VP8SSIMAccumulate(yuv1 + Y_OFF, BPS, yuv2 + Y_OFF, BPS, x, y, 16, 16, &s);
|
||||
VP8SSIMAccumulate(yuv1 + Y_OFF_ENC, BPS, yuv2 + Y_OFF_ENC, BPS,
|
||||
x, y, 16, 16, &s);
|
||||
}
|
||||
}
|
||||
for (x = 1; x < 7; x++) {
|
||||
for (y = 1; y < 7; y++) {
|
||||
VP8SSIMAccumulate(yuv1 + U_OFF, BPS, yuv2 + U_OFF, BPS, x, y, 8, 8, &s);
|
||||
VP8SSIMAccumulate(yuv1 + V_OFF, BPS, yuv2 + V_OFF, BPS, x, y, 8, 8, &s);
|
||||
VP8SSIMAccumulate(yuv1 + U_OFF_ENC, BPS, yuv2 + U_OFF_ENC, BPS,
|
||||
x, y, 8, 8, &s);
|
||||
VP8SSIMAccumulate(yuv1 + V_OFF_ENC, BPS, yuv2 + V_OFF_ENC, BPS,
|
||||
x, y, 8, 8, &s);
|
||||
}
|
||||
}
|
||||
return VP8SSIMGet(&s);
|
||||
@ -338,28 +215,28 @@ static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) {
|
||||
// loop filter strength
|
||||
|
||||
void VP8InitFilter(VP8EncIterator* const it) {
|
||||
int s, i;
|
||||
if (!it->lf_stats_) return;
|
||||
|
||||
InitTables();
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; s++) {
|
||||
for (i = 0; i < MAX_LF_LEVELS; i++) {
|
||||
(*it->lf_stats_)[s][i] = 0;
|
||||
if (it->lf_stats_ != NULL) {
|
||||
int s, i;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; s++) {
|
||||
for (i = 0; i < MAX_LF_LEVELS; i++) {
|
||||
(*it->lf_stats_)[s][i] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VP8StoreFilterStats(VP8EncIterator* const it) {
|
||||
int d;
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
const int s = it->mb_->segment_;
|
||||
const int level0 = it->enc_->dqm_[s].fstrength_; // TODO: ref_lf_delta[]
|
||||
const int level0 = enc->dqm_[s].fstrength_; // TODO: ref_lf_delta[]
|
||||
|
||||
// explore +/-quant range of values around level0
|
||||
const int delta_min = -it->enc_->dqm_[s].quant_;
|
||||
const int delta_max = it->enc_->dqm_[s].quant_;
|
||||
const int delta_min = -enc->dqm_[s].quant_;
|
||||
const int delta_max = enc->dqm_[s].quant_;
|
||||
const int step_size = (delta_max - delta_min >= 4) ? 4 : 1;
|
||||
|
||||
if (!it->lf_stats_) return;
|
||||
if (it->lf_stats_ == NULL) return;
|
||||
|
||||
// NOTE: Currently we are applying filter only across the sublock edges
|
||||
// There are two reasons for that.
|
||||
@ -383,27 +260,40 @@ void VP8StoreFilterStats(VP8EncIterator* const it) {
|
||||
}
|
||||
|
||||
void VP8AdjustFilterStrength(VP8EncIterator* const it) {
|
||||
int s;
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
|
||||
if (!it->lf_stats_) {
|
||||
return;
|
||||
}
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; s++) {
|
||||
int i, best_level = 0;
|
||||
// Improvement over filter level 0 should be at least 1e-5 (relatively)
|
||||
double best_v = 1.00001 * (*it->lf_stats_)[s][0];
|
||||
for (i = 1; i < MAX_LF_LEVELS; i++) {
|
||||
const double v = (*it->lf_stats_)[s][i];
|
||||
if (v > best_v) {
|
||||
best_v = v;
|
||||
best_level = i;
|
||||
if (it->lf_stats_ != NULL) {
|
||||
int s;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; s++) {
|
||||
int i, best_level = 0;
|
||||
// Improvement over filter level 0 should be at least 1e-5 (relatively)
|
||||
double best_v = 1.00001 * (*it->lf_stats_)[s][0];
|
||||
for (i = 1; i < MAX_LF_LEVELS; i++) {
|
||||
const double v = (*it->lf_stats_)[s][i];
|
||||
if (v > best_v) {
|
||||
best_v = v;
|
||||
best_level = i;
|
||||
}
|
||||
}
|
||||
enc->dqm_[s].fstrength_ = best_level;
|
||||
}
|
||||
} else if (enc->config_->filter_strength > 0) {
|
||||
int max_level = 0;
|
||||
int s;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; s++) {
|
||||
VP8SegmentInfo* const dqm = &enc->dqm_[s];
|
||||
// this '>> 3' accounts for some inverse WHT scaling
|
||||
const int delta = (dqm->max_edge_ * dqm->y2_.q_[1]) >> 3;
|
||||
const int level =
|
||||
VP8FilterStrengthFromDelta(enc->filter_hdr_.sharpness_, delta);
|
||||
if (level > dqm->fstrength_) {
|
||||
dqm->fstrength_ = level;
|
||||
}
|
||||
if (max_level < dqm->fstrength_) {
|
||||
max_level = dqm->fstrength_;
|
||||
}
|
||||
}
|
||||
enc->dqm_[s].fstrength_ = best_level;
|
||||
enc->filter_hdr_.level_ = max_level;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Author: Jyrki Alakuijala (jyrki@google.com)
|
||||
@ -12,17 +14,13 @@
|
||||
#ifndef WEBP_ENC_HISTOGRAM_H_
|
||||
#define WEBP_ENC_HISTOGRAM_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "./backward_references.h"
|
||||
#include "../format_constants.h"
|
||||
#include "../types.h"
|
||||
#include "../webp/format_constants.h"
|
||||
#include "../webp/types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -30,18 +28,23 @@ extern "C" {
|
||||
typedef struct {
|
||||
// literal_ contains green literal, palette-code and
|
||||
// copy-length-prefix histogram
|
||||
int literal_[PIX_OR_COPY_CODES_MAX];
|
||||
int red_[256];
|
||||
int blue_[256];
|
||||
int alpha_[256];
|
||||
uint32_t* literal_; // Pointer to the allocated buffer for literal.
|
||||
uint32_t red_[NUM_LITERAL_CODES];
|
||||
uint32_t blue_[NUM_LITERAL_CODES];
|
||||
uint32_t alpha_[NUM_LITERAL_CODES];
|
||||
// Backward reference prefix-code histogram.
|
||||
int distance_[NUM_DISTANCE_CODES];
|
||||
uint32_t distance_[NUM_DISTANCE_CODES];
|
||||
int palette_code_bits_;
|
||||
double bit_cost_; // cached value of VP8LHistogramEstimateBits(this)
|
||||
uint32_t trivial_symbol_; // True, if histograms for Red, Blue & Alpha
|
||||
// literal symbols are single valued.
|
||||
double bit_cost_; // cached value of bit cost.
|
||||
double literal_cost_; // Cached values of dominant entropy costs:
|
||||
double red_cost_; // literal, red & blue.
|
||||
double blue_cost_;
|
||||
} VP8LHistogram;
|
||||
|
||||
// Collection of histograms with fixed capacity, allocated as one
|
||||
// big memory chunk. Can be destroyed by simply calling 'free()'.
|
||||
// big memory chunk. Can be destroyed by calling WebPSafeFree().
|
||||
typedef struct {
|
||||
int size; // number of slots currently in use
|
||||
int max_size; // maximum capacity
|
||||
@ -57,6 +60,9 @@ void VP8LHistogramCreate(VP8LHistogram* const p,
|
||||
const VP8LBackwardRefs* const refs,
|
||||
int palette_code_bits);
|
||||
|
||||
// Return the size of the histogram for a given palette_code_bits.
|
||||
int VP8LGetHistogramSize(int palette_code_bits);
|
||||
|
||||
// Set the palette_code_bits and reset the stats.
|
||||
void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits);
|
||||
|
||||
@ -64,51 +70,40 @@ void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits);
|
||||
void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs,
|
||||
VP8LHistogram* const histo);
|
||||
|
||||
// Free the memory allocated for the histogram.
|
||||
void VP8LFreeHistogram(VP8LHistogram* const histo);
|
||||
|
||||
// Free the memory allocated for the histogram set.
|
||||
void VP8LFreeHistogramSet(VP8LHistogramSet* const histo);
|
||||
|
||||
// Allocate an array of pointer to histograms, allocated and initialized
|
||||
// using 'cache_bits'. Return NULL in case of memory error.
|
||||
VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits);
|
||||
|
||||
// Allocate and initialize histogram object with specified 'cache_bits'.
|
||||
// Returns NULL in case of memory error.
|
||||
// Special case of VP8LAllocateHistogramSet, with size equals 1.
|
||||
VP8LHistogram* VP8LAllocateHistogram(int cache_bits);
|
||||
|
||||
// Accumulate a token 'v' into a histogram.
|
||||
void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo,
|
||||
const PixOrCopy* const v);
|
||||
|
||||
// Estimate how many bits the combined entropy of literals and distance
|
||||
// approximately maps to.
|
||||
double VP8LHistogramEstimateBits(const VP8LHistogram* const p);
|
||||
|
||||
// This function estimates the cost in bits excluding the bits needed to
|
||||
// represent the entropy code itself.
|
||||
double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p);
|
||||
|
||||
static WEBP_INLINE void VP8LHistogramAdd(VP8LHistogram* const p,
|
||||
const VP8LHistogram* const a) {
|
||||
int i;
|
||||
for (i = 0; i < PIX_OR_COPY_CODES_MAX; ++i) {
|
||||
p->literal_[i] += a->literal_[i];
|
||||
}
|
||||
for (i = 0; i < NUM_DISTANCE_CODES; ++i) {
|
||||
p->distance_[i] += a->distance_[i];
|
||||
}
|
||||
for (i = 0; i < 256; ++i) {
|
||||
p->red_[i] += a->red_[i];
|
||||
p->blue_[i] += a->blue_[i];
|
||||
p->alpha_[i] += a->alpha_[i];
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8LHistogramNumCodes(const VP8LHistogram* const p) {
|
||||
return 256 + NUM_LENGTH_CODES +
|
||||
((p->palette_code_bits_ > 0) ? (1 << p->palette_code_bits_) : 0);
|
||||
static WEBP_INLINE int VP8LHistogramNumCodes(int palette_code_bits) {
|
||||
return NUM_LITERAL_CODES + NUM_LENGTH_CODES +
|
||||
((palette_code_bits > 0) ? (1 << palette_code_bits) : 0);
|
||||
}
|
||||
|
||||
// Builds the histogram image.
|
||||
int VP8LGetHistoImageSymbols(int xsize, int ysize,
|
||||
const VP8LBackwardRefs* const refs,
|
||||
int quality, int histogram_bits, int cache_bits,
|
||||
int quality, int low_effort,
|
||||
int histogram_bits, int cache_bits,
|
||||
VP8LHistogramSet* const image_in,
|
||||
VP8LHistogramSet* const tmp_histos,
|
||||
uint16_t* const histogram_symbols);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// VP8Iterator: block iterator
|
||||
@ -13,21 +15,16 @@
|
||||
|
||||
#include "./vp8enci.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8Iterator
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void InitLeft(VP8EncIterator* const it) {
|
||||
const VP8Encoder* const enc = it->enc_;
|
||||
enc->y_left_[-1] = enc->u_left_[-1] = enc->v_left_[-1] =
|
||||
it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] =
|
||||
(it->y_ > 0) ? 129 : 127;
|
||||
memset(enc->y_left_, 129, 16);
|
||||
memset(enc->u_left_, 129, 8);
|
||||
memset(enc->v_left_, 129, 8);
|
||||
memset(it->y_left_, 129, 16);
|
||||
memset(it->u_left_, 129, 8);
|
||||
memset(it->v_left_, 129, 8);
|
||||
it->left_nz_[8] = 0;
|
||||
}
|
||||
|
||||
@ -38,43 +35,60 @@ static void InitTop(VP8EncIterator* const it) {
|
||||
memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_));
|
||||
}
|
||||
|
||||
void VP8IteratorReset(VP8EncIterator* const it) {
|
||||
void VP8IteratorSetRow(VP8EncIterator* const it, int y) {
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
it->x_ = 0;
|
||||
it->y_ = 0;
|
||||
it->y_offset_ = 0;
|
||||
it->uv_offset_ = 0;
|
||||
it->mb_ = enc->mb_info_;
|
||||
it->preds_ = enc->preds_;
|
||||
it->y_ = y;
|
||||
it->bw_ = &enc->parts_[y & (enc->num_parts_ - 1)];
|
||||
it->preds_ = enc->preds_ + y * 4 * enc->preds_w_;
|
||||
it->nz_ = enc->nz_;
|
||||
it->bw_ = &enc->parts_[0];
|
||||
it->done_ = enc->mb_w_* enc->mb_h_;
|
||||
it->mb_ = enc->mb_info_ + y * enc->mb_w_;
|
||||
it->y_top_ = enc->y_top_;
|
||||
it->uv_top_ = enc->uv_top_;
|
||||
InitLeft(it);
|
||||
}
|
||||
|
||||
void VP8IteratorReset(VP8EncIterator* const it) {
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
VP8IteratorSetRow(it, 0);
|
||||
VP8IteratorSetCountDown(it, enc->mb_w_ * enc->mb_h_); // default
|
||||
InitTop(it);
|
||||
InitLeft(it);
|
||||
memset(it->bit_count_, 0, sizeof(it->bit_count_));
|
||||
it->do_trellis_ = 0;
|
||||
}
|
||||
|
||||
void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down) {
|
||||
it->count_down_ = it->count_down0_ = count_down;
|
||||
}
|
||||
|
||||
int VP8IteratorIsDone(const VP8EncIterator* const it) {
|
||||
return (it->count_down_ <= 0);
|
||||
}
|
||||
|
||||
void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it) {
|
||||
it->enc_ = enc;
|
||||
it->y_stride_ = enc->pic_->y_stride;
|
||||
it->uv_stride_ = enc->pic_->uv_stride;
|
||||
// TODO(later): for multithreading, these should be owned by 'it'.
|
||||
it->yuv_in_ = enc->yuv_in_;
|
||||
it->yuv_out_ = enc->yuv_out_;
|
||||
it->yuv_out2_ = enc->yuv_out2_;
|
||||
it->yuv_p_ = enc->yuv_p_;
|
||||
it->yuv_in_ = (uint8_t*)WEBP_ALIGN(it->yuv_mem_);
|
||||
it->yuv_out_ = it->yuv_in_ + YUV_SIZE_ENC;
|
||||
it->yuv_out2_ = it->yuv_out_ + YUV_SIZE_ENC;
|
||||
it->yuv_p_ = it->yuv_out2_ + YUV_SIZE_ENC;
|
||||
it->lf_stats_ = enc->lf_stats_;
|
||||
it->percent0_ = enc->percent_;
|
||||
it->y_left_ = (uint8_t*)WEBP_ALIGN(it->yuv_left_mem_ + 1);
|
||||
it->u_left_ = it->y_left_ + 16 + 16;
|
||||
it->v_left_ = it->u_left_ + 16;
|
||||
VP8IteratorReset(it);
|
||||
}
|
||||
|
||||
int VP8IteratorProgress(const VP8EncIterator* const it, int delta) {
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
if (delta && enc->pic_->progress_hook) {
|
||||
const int percent = (enc->mb_h_ <= 1)
|
||||
if (delta && enc->pic_->progress_hook != NULL) {
|
||||
const int done = it->count_down0_ - it->count_down_;
|
||||
const int percent = (it->count_down0_ <= 0)
|
||||
? it->percent0_
|
||||
: it->percent0_ + delta * it->y_ / (enc->mb_h_ - 1);
|
||||
: it->percent0_ + delta * done / it->count_down0_;
|
||||
return WebPReportProgress(enc->pic_, percent, &enc->percent_);
|
||||
}
|
||||
return 1;
|
||||
@ -84,6 +98,8 @@ int VP8IteratorProgress(const VP8EncIterator* const it, int delta) {
|
||||
// Import the source samples into the cache. Takes care of replicating
|
||||
// boundary pixels if necessary.
|
||||
|
||||
static WEBP_INLINE int MinSize(int a, int b) { return (a < b) ? a : b; }
|
||||
|
||||
static void ImportBlock(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int w, int h, int size) {
|
||||
int i;
|
||||
@ -101,30 +117,55 @@ static void ImportBlock(const uint8_t* src, int src_stride,
|
||||
}
|
||||
}
|
||||
|
||||
void VP8IteratorImport(const VP8EncIterator* const it) {
|
||||
static void ImportLine(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int len, int total_len) {
|
||||
int i;
|
||||
for (i = 0; i < len; ++i, src += src_stride) dst[i] = *src;
|
||||
for (; i < total_len; ++i) dst[i] = dst[len - 1];
|
||||
}
|
||||
|
||||
void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32) {
|
||||
const VP8Encoder* const enc = it->enc_;
|
||||
const int x = it->x_, y = it->y_;
|
||||
const WebPPicture* const pic = enc->pic_;
|
||||
const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16;
|
||||
const uint8_t* const ysrc = pic->y + (y * pic->y_stride + x) * 16;
|
||||
const uint8_t* const usrc = pic->u + (y * pic->uv_stride + x) * 8;
|
||||
const uint8_t* const vsrc = pic->v + (y * pic->uv_stride + x) * 8;
|
||||
uint8_t* const ydst = it->yuv_in_ + Y_OFF;
|
||||
uint8_t* const udst = it->yuv_in_ + U_OFF;
|
||||
uint8_t* const vdst = it->yuv_in_ + V_OFF;
|
||||
int w = (pic->width - x * 16);
|
||||
int h = (pic->height - y * 16);
|
||||
const int w = MinSize(pic->width - x * 16, 16);
|
||||
const int h = MinSize(pic->height - y * 16, 16);
|
||||
const int uv_w = (w + 1) >> 1;
|
||||
const int uv_h = (h + 1) >> 1;
|
||||
|
||||
if (w > 16) w = 16;
|
||||
if (h > 16) h = 16;
|
||||
ImportBlock(ysrc, pic->y_stride, it->yuv_in_ + Y_OFF_ENC, w, h, 16);
|
||||
ImportBlock(usrc, pic->uv_stride, it->yuv_in_ + U_OFF_ENC, uv_w, uv_h, 8);
|
||||
ImportBlock(vsrc, pic->uv_stride, it->yuv_in_ + V_OFF_ENC, uv_w, uv_h, 8);
|
||||
|
||||
// Luma plane
|
||||
ImportBlock(ysrc, pic->y_stride, ydst, w, h, 16);
|
||||
if (tmp_32 == NULL) return;
|
||||
|
||||
{ // U/V planes
|
||||
const int uv_w = (w + 1) >> 1;
|
||||
const int uv_h = (h + 1) >> 1;
|
||||
ImportBlock(usrc, pic->uv_stride, udst, uv_w, uv_h, 8);
|
||||
ImportBlock(vsrc, pic->uv_stride, vdst, uv_w, uv_h, 8);
|
||||
// Import source (uncompressed) samples into boundary.
|
||||
if (x == 0) {
|
||||
InitLeft(it);
|
||||
} else {
|
||||
if (y == 0) {
|
||||
it->y_left_[-1] = it->u_left_[-1] = it->v_left_[-1] = 127;
|
||||
} else {
|
||||
it->y_left_[-1] = ysrc[- 1 - pic->y_stride];
|
||||
it->u_left_[-1] = usrc[- 1 - pic->uv_stride];
|
||||
it->v_left_[-1] = vsrc[- 1 - pic->uv_stride];
|
||||
}
|
||||
ImportLine(ysrc - 1, pic->y_stride, it->y_left_, h, 16);
|
||||
ImportLine(usrc - 1, pic->uv_stride, it->u_left_, uv_h, 8);
|
||||
ImportLine(vsrc - 1, pic->uv_stride, it->v_left_, uv_h, 8);
|
||||
}
|
||||
|
||||
it->y_top_ = tmp_32 + 0;
|
||||
it->uv_top_ = tmp_32 + 16;
|
||||
if (y == 0) {
|
||||
memset(tmp_32, 127, 32 * sizeof(*tmp_32));
|
||||
} else {
|
||||
ImportLine(ysrc - pic->y_stride, 1, tmp_32, w, 16);
|
||||
ImportLine(usrc - pic->uv_stride, 1, tmp_32 + 16, uv_w, 8);
|
||||
ImportLine(vsrc - pic->uv_stride, 1, tmp_32 + 16 + 8, uv_w, 8);
|
||||
}
|
||||
}
|
||||
|
||||
@ -144,9 +185,9 @@ void VP8IteratorExport(const VP8EncIterator* const it) {
|
||||
const VP8Encoder* const enc = it->enc_;
|
||||
if (enc->config_->show_compressed) {
|
||||
const int x = it->x_, y = it->y_;
|
||||
const uint8_t* const ysrc = it->yuv_out_ + Y_OFF;
|
||||
const uint8_t* const usrc = it->yuv_out_ + U_OFF;
|
||||
const uint8_t* const vsrc = it->yuv_out_ + V_OFF;
|
||||
const uint8_t* const ysrc = it->yuv_out_ + Y_OFF_ENC;
|
||||
const uint8_t* const usrc = it->yuv_out_ + U_OFF_ENC;
|
||||
const uint8_t* const vsrc = it->yuv_out_ + V_OFF_ENC;
|
||||
const WebPPicture* const pic = enc->pic_;
|
||||
uint8_t* const ydst = pic->y + (y * pic->y_stride + x) * 16;
|
||||
uint8_t* const udst = pic->u + (y * pic->uv_stride + x) * 8;
|
||||
@ -240,48 +281,44 @@ void VP8IteratorBytesToNz(VP8EncIterator* const it) {
|
||||
#undef BIT
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Advance to the next position, doing the bookeeping.
|
||||
// Advance to the next position, doing the bookkeeping.
|
||||
|
||||
int VP8IteratorNext(VP8EncIterator* const it,
|
||||
const uint8_t* const block_to_save) {
|
||||
void VP8IteratorSaveBoundary(VP8EncIterator* const it) {
|
||||
VP8Encoder* const enc = it->enc_;
|
||||
if (block_to_save) {
|
||||
const int x = it->x_, y = it->y_;
|
||||
const uint8_t* const ysrc = block_to_save + Y_OFF;
|
||||
const uint8_t* const usrc = block_to_save + U_OFF;
|
||||
if (x < enc->mb_w_ - 1) { // left
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
enc->y_left_[i] = ysrc[15 + i * BPS];
|
||||
}
|
||||
for (i = 0; i < 8; ++i) {
|
||||
enc->u_left_[i] = usrc[7 + i * BPS];
|
||||
enc->v_left_[i] = usrc[15 + i * BPS];
|
||||
}
|
||||
// top-left (before 'top'!)
|
||||
enc->y_left_[-1] = enc->y_top_[x * 16 + 15];
|
||||
enc->u_left_[-1] = enc->uv_top_[x * 16 + 0 + 7];
|
||||
enc->v_left_[-1] = enc->uv_top_[x * 16 + 8 + 7];
|
||||
const int x = it->x_, y = it->y_;
|
||||
const uint8_t* const ysrc = it->yuv_out_ + Y_OFF_ENC;
|
||||
const uint8_t* const uvsrc = it->yuv_out_ + U_OFF_ENC;
|
||||
if (x < enc->mb_w_ - 1) { // left
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
it->y_left_[i] = ysrc[15 + i * BPS];
|
||||
}
|
||||
if (y < enc->mb_h_ - 1) { // top
|
||||
memcpy(enc->y_top_ + x * 16, ysrc + 15 * BPS, 16);
|
||||
memcpy(enc->uv_top_ + x * 16, usrc + 7 * BPS, 8 + 8);
|
||||
for (i = 0; i < 8; ++i) {
|
||||
it->u_left_[i] = uvsrc[7 + i * BPS];
|
||||
it->v_left_[i] = uvsrc[15 + i * BPS];
|
||||
}
|
||||
// top-left (before 'top'!)
|
||||
it->y_left_[-1] = it->y_top_[15];
|
||||
it->u_left_[-1] = it->uv_top_[0 + 7];
|
||||
it->v_left_[-1] = it->uv_top_[8 + 7];
|
||||
}
|
||||
if (y < enc->mb_h_ - 1) { // top
|
||||
memcpy(it->y_top_, ysrc + 15 * BPS, 16);
|
||||
memcpy(it->uv_top_, uvsrc + 7 * BPS, 8 + 8);
|
||||
}
|
||||
}
|
||||
|
||||
it->mb_++;
|
||||
int VP8IteratorNext(VP8EncIterator* const it) {
|
||||
it->preds_ += 4;
|
||||
it->nz_++;
|
||||
it->x_++;
|
||||
if (it->x_ == enc->mb_w_) {
|
||||
it->x_ = 0;
|
||||
it->y_++;
|
||||
it->bw_ = &enc->parts_[it->y_ & (enc->num_parts_ - 1)];
|
||||
it->preds_ = enc->preds_ + it->y_ * 4 * enc->preds_w_;
|
||||
it->nz_ = enc->nz_;
|
||||
InitLeft(it);
|
||||
it->mb_ += 1;
|
||||
it->nz_ += 1;
|
||||
it->y_top_ += 16;
|
||||
it->uv_top_ += 16;
|
||||
it->x_ += 1;
|
||||
if (it->x_ == it->enc_->mb_w_) {
|
||||
VP8IteratorSetRow(it, ++it->y_);
|
||||
}
|
||||
return (0 < --it->done_);
|
||||
return (0 < --it->count_down_);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -368,15 +405,15 @@ void VP8IteratorStartI4(VP8EncIterator* const it) {
|
||||
|
||||
// Import the boundary samples
|
||||
for (i = 0; i < 17; ++i) { // left
|
||||
it->i4_boundary_[i] = enc->y_left_[15 - i];
|
||||
it->i4_boundary_[i] = it->y_left_[15 - i];
|
||||
}
|
||||
for (i = 0; i < 16; ++i) { // top
|
||||
it->i4_boundary_[17 + i] = enc->y_top_[it->x_ * 16 + i];
|
||||
it->i4_boundary_[17 + i] = it->y_top_[i];
|
||||
}
|
||||
// top-right samples have a special case on the far right of the picture
|
||||
if (it->x_ < enc->mb_w_ - 1) {
|
||||
for (i = 16; i < 16 + 4; ++i) {
|
||||
it->i4_boundary_[17 + i] = enc->y_top_[it->x_ * 16 + i];
|
||||
it->i4_boundary_[17 + i] = it->y_top_[i];
|
||||
}
|
||||
} else { // else, replicate the last valid pixel four times
|
||||
for (i = 16; i < 16 + 4; ++i) {
|
||||
@ -417,6 +454,3 @@ int VP8IteratorRotateI4(VP8EncIterator* const it,
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Header syntax writing
|
||||
@ -11,35 +13,20 @@
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#include "../format_constants.h"
|
||||
#include "../utils/utils.h"
|
||||
#include "../webp/format_constants.h" // RIFF constants
|
||||
#include "../webp/mux_types.h" // ALPHA_FLAG
|
||||
#include "./vp8enci.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper functions
|
||||
|
||||
// TODO(later): Move to webp/format_constants.h?
|
||||
static void PutLE24(uint8_t* const data, uint32_t val) {
|
||||
data[0] = (val >> 0) & 0xff;
|
||||
data[1] = (val >> 8) & 0xff;
|
||||
data[2] = (val >> 16) & 0xff;
|
||||
}
|
||||
|
||||
static void PutLE32(uint8_t* const data, uint32_t val) {
|
||||
PutLE24(data, val);
|
||||
data[3] = (val >> 24) & 0xff;
|
||||
}
|
||||
|
||||
static int IsVP8XNeeded(const VP8Encoder* const enc) {
|
||||
return !!enc->has_alpha_; // Currently the only case when VP8X is needed.
|
||||
// This could change in the future.
|
||||
}
|
||||
|
||||
static int PutPaddingByte(const WebPPicture* const pic) {
|
||||
|
||||
const uint8_t pad_byte[1] = { 0 };
|
||||
return !!pic->writer(pad_byte, 1, pic);
|
||||
}
|
||||
@ -73,14 +60,14 @@ static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) {
|
||||
assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE);
|
||||
|
||||
if (enc->has_alpha_) {
|
||||
flags |= ALPHA_FLAG_BIT;
|
||||
flags |= ALPHA_FLAG;
|
||||
}
|
||||
|
||||
PutLE32(vp8x + TAG_SIZE, VP8X_CHUNK_SIZE);
|
||||
PutLE32(vp8x + CHUNK_HEADER_SIZE, flags);
|
||||
PutLE24(vp8x + CHUNK_HEADER_SIZE + 4, pic->width - 1);
|
||||
PutLE24(vp8x + CHUNK_HEADER_SIZE + 7, pic->height - 1);
|
||||
if(!pic->writer(vp8x, sizeof(vp8x), pic)) {
|
||||
if (!pic->writer(vp8x, sizeof(vp8x), pic)) {
|
||||
return VP8_ENC_ERROR_BAD_WRITE;
|
||||
}
|
||||
return VP8_ENC_OK;
|
||||
@ -199,8 +186,8 @@ static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0,
|
||||
// Segmentation header
|
||||
static void PutSegmentHeader(VP8BitWriter* const bw,
|
||||
const VP8Encoder* const enc) {
|
||||
const VP8SegmentHeader* const hdr = &enc->segment_hdr_;
|
||||
const VP8Proba* const proba = &enc->proba_;
|
||||
const VP8EncSegmentHeader* const hdr = &enc->segment_hdr_;
|
||||
const VP8EncProba* const proba = &enc->proba_;
|
||||
if (VP8PutBitUniform(bw, (hdr->num_segments_ > 1))) {
|
||||
// We always 'update' the quant and filter strength values
|
||||
const int update_data = 1;
|
||||
@ -210,16 +197,16 @@ static void PutSegmentHeader(VP8BitWriter* const bw,
|
||||
// we always use absolute values, not relative ones
|
||||
VP8PutBitUniform(bw, 1); // (segment_feature_mode = 1. Paragraph 9.3.)
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
VP8PutSignedValue(bw, enc->dqm_[s].quant_, 7);
|
||||
VP8PutSignedBits(bw, enc->dqm_[s].quant_, 7);
|
||||
}
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
VP8PutSignedValue(bw, enc->dqm_[s].fstrength_, 6);
|
||||
VP8PutSignedBits(bw, enc->dqm_[s].fstrength_, 6);
|
||||
}
|
||||
}
|
||||
if (hdr->update_map_) {
|
||||
for (s = 0; s < 3; ++s) {
|
||||
if (VP8PutBitUniform(bw, (proba->segments_[s] != 255u))) {
|
||||
VP8PutValue(bw, proba->segments_[s], 8);
|
||||
VP8PutBits(bw, proba->segments_[s], 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -228,20 +215,20 @@ static void PutSegmentHeader(VP8BitWriter* const bw,
|
||||
|
||||
// Filtering parameters header
|
||||
static void PutFilterHeader(VP8BitWriter* const bw,
|
||||
const VP8FilterHeader* const hdr) {
|
||||
const VP8EncFilterHeader* const hdr) {
|
||||
const int use_lf_delta = (hdr->i4x4_lf_delta_ != 0);
|
||||
VP8PutBitUniform(bw, hdr->simple_);
|
||||
VP8PutValue(bw, hdr->level_, 6);
|
||||
VP8PutValue(bw, hdr->sharpness_, 3);
|
||||
VP8PutBits(bw, hdr->level_, 6);
|
||||
VP8PutBits(bw, hdr->sharpness_, 3);
|
||||
if (VP8PutBitUniform(bw, use_lf_delta)) {
|
||||
// '0' is the default value for i4x4_lf_delta_ at frame #0.
|
||||
const int need_update = (hdr->i4x4_lf_delta_ != 0);
|
||||
if (VP8PutBitUniform(bw, need_update)) {
|
||||
// we don't use ref_lf_delta => emit four 0 bits
|
||||
VP8PutValue(bw, 0, 4);
|
||||
VP8PutBits(bw, 0, 4);
|
||||
// we use mode_lf_delta for i4x4
|
||||
VP8PutSignedValue(bw, hdr->i4x4_lf_delta_, 6);
|
||||
VP8PutValue(bw, 0, 3); // all others unused
|
||||
VP8PutSignedBits(bw, hdr->i4x4_lf_delta_, 6);
|
||||
VP8PutBits(bw, 0, 3); // all others unused
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -249,12 +236,12 @@ static void PutFilterHeader(VP8BitWriter* const bw,
|
||||
// Nominal quantization parameters
|
||||
static void PutQuant(VP8BitWriter* const bw,
|
||||
const VP8Encoder* const enc) {
|
||||
VP8PutValue(bw, enc->base_quant_, 7);
|
||||
VP8PutSignedValue(bw, enc->dq_y1_dc_, 4);
|
||||
VP8PutSignedValue(bw, enc->dq_y2_dc_, 4);
|
||||
VP8PutSignedValue(bw, enc->dq_y2_ac_, 4);
|
||||
VP8PutSignedValue(bw, enc->dq_uv_dc_, 4);
|
||||
VP8PutSignedValue(bw, enc->dq_uv_ac_, 4);
|
||||
VP8PutBits(bw, enc->base_quant_, 7);
|
||||
VP8PutSignedBits(bw, enc->dq_y1_dc_, 4);
|
||||
VP8PutSignedBits(bw, enc->dq_y2_dc_, 4);
|
||||
VP8PutSignedBits(bw, enc->dq_y2_ac_, 4);
|
||||
VP8PutSignedBits(bw, enc->dq_uv_dc_, 4);
|
||||
VP8PutSignedBits(bw, enc->dq_uv_ac_, 4);
|
||||
}
|
||||
|
||||
// Partition sizes
|
||||
@ -276,58 +263,23 @@ static int EmitPartitionsSize(const VP8Encoder* const enc,
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
|
||||
#define KTRAILER_SIZE 8
|
||||
|
||||
static int WriteExtensions(VP8Encoder* const enc) {
|
||||
uint8_t buffer[KTRAILER_SIZE];
|
||||
VP8BitWriter* const bw = &enc->bw_;
|
||||
WebPPicture* const pic = enc->pic_;
|
||||
|
||||
// Layer (bytes 0..3)
|
||||
PutLE24(buffer + 0, enc->layer_data_size_);
|
||||
buffer[3] = enc->pic_->colorspace & WEBP_CSP_UV_MASK;
|
||||
if (enc->layer_data_size_ > 0) {
|
||||
assert(enc->use_layer_);
|
||||
// append layer data to last partition
|
||||
if (!VP8BitWriterAppend(&enc->parts_[enc->num_parts_ - 1],
|
||||
enc->layer_data_, enc->layer_data_size_)) {
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
|
||||
}
|
||||
}
|
||||
|
||||
buffer[KTRAILER_SIZE - 1] = 0x01; // marker
|
||||
if (!VP8BitWriterAppend(bw, buffer, KTRAILER_SIZE)) {
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* WEBP_EXPERIMENTAL_FEATURES */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static size_t GeneratePartition0(VP8Encoder* const enc) {
|
||||
static int GeneratePartition0(VP8Encoder* const enc) {
|
||||
VP8BitWriter* const bw = &enc->bw_;
|
||||
const int mb_size = enc->mb_w_ * enc->mb_h_;
|
||||
uint64_t pos1, pos2, pos3;
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
const int need_extensions = enc->use_layer_;
|
||||
#endif
|
||||
|
||||
pos1 = VP8BitWriterPos(bw);
|
||||
VP8BitWriterInit(bw, mb_size * 7 / 8); // ~7 bits per macroblock
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
VP8PutBitUniform(bw, need_extensions); // extensions
|
||||
#else
|
||||
if (!VP8BitWriterInit(bw, mb_size * 7 / 8)) { // ~7 bits per macroblock
|
||||
return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
VP8PutBitUniform(bw, 0); // colorspace
|
||||
#endif
|
||||
VP8PutBitUniform(bw, 0); // clamp type
|
||||
|
||||
PutSegmentHeader(bw, enc);
|
||||
PutFilterHeader(bw, &enc->filter_hdr_);
|
||||
VP8PutValue(bw, enc->config_->partitions, 2);
|
||||
VP8PutBits(bw, enc->num_parts_ == 8 ? 3 :
|
||||
enc->num_parts_ == 4 ? 2 :
|
||||
enc->num_parts_ == 2 ? 1 : 0, 2);
|
||||
PutQuant(bw, enc);
|
||||
VP8PutBitUniform(bw, 0); // no proba update
|
||||
VP8WriteProbas(bw, &enc->proba_);
|
||||
@ -335,21 +287,17 @@ static size_t GeneratePartition0(VP8Encoder* const enc) {
|
||||
VP8CodeIntraModes(enc);
|
||||
VP8BitWriterFinish(bw);
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
if (need_extensions && !WriteExtensions(enc)) {
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
pos3 = VP8BitWriterPos(bw);
|
||||
|
||||
if (enc->pic_->stats) {
|
||||
enc->pic_->stats->header_bytes[0] = (int)((pos2 - pos1 + 7) >> 3);
|
||||
enc->pic_->stats->header_bytes[1] = (int)((pos3 - pos2 + 7) >> 3);
|
||||
enc->pic_->stats->alpha_data_size = (int)enc->alpha_data_size_;
|
||||
enc->pic_->stats->layer_data_size = (int)enc->layer_data_size_;
|
||||
}
|
||||
return !bw->error_;
|
||||
if (bw->error_) {
|
||||
return WebPEncodingSetError(enc->pic_, VP8_ENC_ERROR_OUT_OF_MEMORY);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VP8EncFreeBitWriters(VP8Encoder* const enc) {
|
||||
@ -371,7 +319,8 @@ int VP8EncWrite(VP8Encoder* const enc) {
|
||||
int p;
|
||||
|
||||
// Partition #0 with header and partition sizes
|
||||
ok = !!GeneratePartition0(enc);
|
||||
ok = GeneratePartition0(enc);
|
||||
if (!ok) return 0;
|
||||
|
||||
// Compute VP8 size
|
||||
vp8_size = VP8_FRAME_HEADER_SIZE +
|
||||
@ -432,6 +381,3 @@ int VP8EncWrite(VP8Encoder* const enc) {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,27 +1,24 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Token probabilities
|
||||
// Coding of token probabilities, intra modes and segments.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "./vp8enci.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Default probabilities
|
||||
|
||||
// Paragraph 13.5
|
||||
const uint8_t
|
||||
VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
|
||||
// genereated using vp8_default_coef_probs() in entropy.c:129
|
||||
{ { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
@ -157,7 +154,7 @@ const uint8_t
|
||||
};
|
||||
|
||||
void VP8DefaultProbas(VP8Encoder* const enc) {
|
||||
VP8Proba* const probas = &enc->proba_;
|
||||
VP8EncProba* const probas = &enc->proba_;
|
||||
probas->use_skip_proba_ = 0;
|
||||
memset(probas->segments_, 255u, sizeof(probas->segments_));
|
||||
memcpy(probas->coeffs_, VP8CoeffsProba0, sizeof(VP8CoeffsProba0));
|
||||
@ -318,7 +315,7 @@ void VP8CodeIntraModes(VP8Encoder* const enc) {
|
||||
VP8EncIterator it;
|
||||
VP8IteratorInit(enc, &it);
|
||||
do {
|
||||
const VP8MBInfo* mb = it.mb_;
|
||||
const VP8MBInfo* const mb = it.mb_;
|
||||
const uint8_t* preds = it.preds_;
|
||||
if (enc->segment_hdr_.update_map_) {
|
||||
PutSegment(bw, mb->segment_, enc->proba_.segments_);
|
||||
@ -343,7 +340,7 @@ void VP8CodeIntraModes(VP8Encoder* const enc) {
|
||||
}
|
||||
}
|
||||
PutUVMode(bw, mb->uv_mode_);
|
||||
} while (VP8IteratorNext(&it, 0));
|
||||
} while (VP8IteratorNext(&it));
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -485,7 +482,7 @@ const uint8_t
|
||||
}
|
||||
};
|
||||
|
||||
void VP8WriteProbas(VP8BitWriter* const bw, const VP8Proba* const probas) {
|
||||
void VP8WriteProbas(VP8BitWriter* const bw, const VP8EncProba* const probas) {
|
||||
int t, b, c, p;
|
||||
for (t = 0; t < NUM_TYPES; ++t) {
|
||||
for (b = 0; b < NUM_BANDS; ++b) {
|
||||
@ -494,17 +491,14 @@ void VP8WriteProbas(VP8BitWriter* const bw, const VP8Proba* const probas) {
|
||||
const uint8_t p0 = probas->coeffs_[t][b][c][p];
|
||||
const int update = (p0 != VP8CoeffsProba0[t][b][c][p]);
|
||||
if (VP8PutBit(bw, update, VP8CoeffsUpdateProba[t][b][c][p])) {
|
||||
VP8PutValue(bw, p0, 8);
|
||||
VP8PutBits(bw, p0, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (VP8PutBitUniform(bw, probas->use_skip_proba_)) {
|
||||
VP8PutValue(bw, probas->skip_proba_, 8);
|
||||
VP8PutBits(bw, probas->skip_proba_, 8);
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// WebP encoder: internal header.
|
||||
@ -13,11 +15,18 @@
|
||||
#define WEBP_ENC_VP8ENCI_H_
|
||||
|
||||
#include <string.h> // for memcpy()
|
||||
#include "../encode.h"
|
||||
#include "../dec/common.h"
|
||||
#include "../dsp/dsp.h"
|
||||
#include "../utils/bit_writer.h"
|
||||
#include "../utils/thread.h"
|
||||
#include "../utils/utils.h"
|
||||
#include "../webp/encode.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
#include "./vp8li.h"
|
||||
#endif // WEBP_EXPERIMENTAL_FEATURES
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -26,141 +35,94 @@ extern "C" {
|
||||
|
||||
// version numbers
|
||||
#define ENC_MAJ_VERSION 0
|
||||
#define ENC_MIN_VERSION 2
|
||||
#define ENC_REV_VERSION 0
|
||||
#define ENC_MIN_VERSION 4
|
||||
#define ENC_REV_VERSION 4
|
||||
|
||||
// size of histogram used by CollectHistogram.
|
||||
#define MAX_COEFF_THRESH 64
|
||||
|
||||
// intra prediction modes
|
||||
enum { B_DC_PRED = 0, // 4x4 modes
|
||||
B_TM_PRED = 1,
|
||||
B_VE_PRED = 2,
|
||||
B_HE_PRED = 3,
|
||||
B_RD_PRED = 4,
|
||||
B_VR_PRED = 5,
|
||||
B_LD_PRED = 6,
|
||||
B_VL_PRED = 7,
|
||||
B_HD_PRED = 8,
|
||||
B_HU_PRED = 9,
|
||||
NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
|
||||
|
||||
// Luma16 or UV modes
|
||||
DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
|
||||
H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED
|
||||
enum { MAX_LF_LEVELS = 64, // Maximum loop filter level
|
||||
MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost
|
||||
MAX_LEVEL = 2047 // max level (note: max codable is 2047 + 67)
|
||||
};
|
||||
|
||||
enum { NUM_MB_SEGMENTS = 4,
|
||||
MAX_NUM_PARTITIONS = 8,
|
||||
NUM_TYPES = 4, // 0: i16-AC, 1: i16-DC, 2:chroma-AC, 3:i4-AC
|
||||
NUM_BANDS = 8,
|
||||
NUM_CTX = 3,
|
||||
NUM_PROBAS = 11,
|
||||
MAX_LF_LEVELS = 64, // Maximum loop filter level
|
||||
MAX_VARIABLE_LEVEL = 67 // last (inclusive) level with variable cost
|
||||
};
|
||||
typedef enum { // Rate-distortion optimization levels
|
||||
RD_OPT_NONE = 0, // no rd-opt
|
||||
RD_OPT_BASIC = 1, // basic scoring (no trellis)
|
||||
RD_OPT_TRELLIS = 2, // perform trellis-quant on the final decision only
|
||||
RD_OPT_TRELLIS_ALL = 3 // trellis-quant for every scoring (much slower)
|
||||
} VP8RDLevel;
|
||||
|
||||
// YUV-cache parameters. Cache is 16-pixels wide.
|
||||
// The original or reconstructed samples can be accessed using VP8Scan[]
|
||||
// YUV-cache parameters. Cache is 32-bytes wide (= one cacheline).
|
||||
// The original or reconstructed samples can be accessed using VP8Scan[].
|
||||
// The predicted blocks can be accessed using offsets to yuv_p_ and
|
||||
// the arrays VP8*ModeOffsets[];
|
||||
// +----+ YUV Samples area. See VP8Scan[] for accessing the blocks.
|
||||
// Y_OFF |YYYY| <- original samples (enc->yuv_in_)
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// U_OFF |UUVV| V_OFF (=U_OFF + 8)
|
||||
// |UUVV|
|
||||
// +----+
|
||||
// Y_OFF |YYYY| <- compressed/decoded samples ('yuv_out_')
|
||||
// |YYYY| There are two buffers like this ('yuv_out_'/'yuv_out2_')
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// U_OFF |UUVV| V_OFF
|
||||
// |UUVV|
|
||||
// x2 (for yuv_out2_)
|
||||
// +----+ Prediction area ('yuv_p_', size = PRED_SIZE)
|
||||
// I16DC16 |YYYY| Intra16 predictions (16x16 block each)
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// I16TM16 |YYYY|
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// I16VE16 |YYYY|
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// I16HE16 |YYYY|
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// |YYYY|
|
||||
// +----+ Chroma U/V predictions (16x8 block each)
|
||||
// C8DC8 |UUVV|
|
||||
// |UUVV|
|
||||
// C8TM8 |UUVV|
|
||||
// |UUVV|
|
||||
// C8VE8 |UUVV|
|
||||
// |UUVV|
|
||||
// C8HE8 |UUVV|
|
||||
// |UUVV|
|
||||
// +----+ Intra 4x4 predictions (4x4 block each)
|
||||
// |YYYY| I4DC4 I4TM4 I4VE4 I4HE4
|
||||
// |YYYY| I4RD4 I4VR4 I4LD4 I4VL4
|
||||
// |YY..| I4HD4 I4HU4 I4TMP
|
||||
// +----+
|
||||
#define BPS 16 // this is the common stride
|
||||
#define Y_SIZE (BPS * 16)
|
||||
#define UV_SIZE (BPS * 8)
|
||||
#define YUV_SIZE (Y_SIZE + UV_SIZE)
|
||||
#define PRED_SIZE (6 * 16 * BPS + 12 * BPS)
|
||||
#define Y_OFF (0)
|
||||
#define U_OFF (Y_SIZE)
|
||||
#define V_OFF (U_OFF + 8)
|
||||
#define ALIGN_CST 15
|
||||
#define DO_ALIGN(PTR) ((uintptr_t)((PTR) + ALIGN_CST) & ~ALIGN_CST)
|
||||
// the arrays VP8*ModeOffsets[].
|
||||
// * YUV Samples area (yuv_in_/yuv_out_/yuv_out2_)
|
||||
// (see VP8Scan[] for accessing the blocks, along with
|
||||
// Y_OFF_ENC/U_OFF_ENC/V_OFF_ENC):
|
||||
// +----+----+
|
||||
// Y_OFF_ENC |YYYY|UUVV|
|
||||
// U_OFF_ENC |YYYY|UUVV|
|
||||
// V_OFF_ENC |YYYY|....| <- 25% wasted U/V area
|
||||
// |YYYY|....|
|
||||
// +----+----+
|
||||
// * Prediction area ('yuv_p_', size = PRED_SIZE_ENC)
|
||||
// Intra16 predictions (16x16 block each, two per row):
|
||||
// |I16DC16|I16TM16|
|
||||
// |I16VE16|I16HE16|
|
||||
// Chroma U/V predictions (16x8 block each, two per row):
|
||||
// |C8DC8|C8TM8|
|
||||
// |C8VE8|C8HE8|
|
||||
// Intra 4x4 predictions (4x4 block each)
|
||||
// |I4DC4 I4TM4 I4VE4 I4HE4|I4RD4 I4VR4 I4LD4 I4VL4|
|
||||
// |I4HD4 I4HU4 I4TMP .....|.......................| <- ~31% wasted
|
||||
#define YUV_SIZE_ENC (BPS * 16)
|
||||
#define PRED_SIZE_ENC (32 * BPS + 16 * BPS + 8 * BPS) // I16+Chroma+I4 preds
|
||||
#define Y_OFF_ENC (0)
|
||||
#define U_OFF_ENC (16)
|
||||
#define V_OFF_ENC (16 + 8)
|
||||
|
||||
extern const int VP8Scan[16 + 4 + 4]; // in quant.c
|
||||
extern const int VP8UVModeOffsets[4]; // in analyze.c
|
||||
extern const int VP8Scan[16]; // in quant.c
|
||||
extern const int VP8UVModeOffsets[4]; // in analyze.c
|
||||
extern const int VP8I16ModeOffsets[4];
|
||||
extern const int VP8I4ModeOffsets[NUM_BMODES];
|
||||
|
||||
// Layout of prediction blocks
|
||||
// intra 16x16
|
||||
#define I16DC16 (0 * 16 * BPS)
|
||||
#define I16TM16 (1 * 16 * BPS)
|
||||
#define I16VE16 (2 * 16 * BPS)
|
||||
#define I16HE16 (3 * 16 * BPS)
|
||||
#define I16TM16 (I16DC16 + 16)
|
||||
#define I16VE16 (1 * 16 * BPS)
|
||||
#define I16HE16 (I16VE16 + 16)
|
||||
// chroma 8x8, two U/V blocks side by side (hence: 16x8 each)
|
||||
#define C8DC8 (4 * 16 * BPS)
|
||||
#define C8TM8 (4 * 16 * BPS + 8 * BPS)
|
||||
#define C8VE8 (5 * 16 * BPS)
|
||||
#define C8HE8 (5 * 16 * BPS + 8 * BPS)
|
||||
#define C8DC8 (2 * 16 * BPS)
|
||||
#define C8TM8 (C8DC8 + 1 * 16)
|
||||
#define C8VE8 (2 * 16 * BPS + 8 * BPS)
|
||||
#define C8HE8 (C8VE8 + 1 * 16)
|
||||
// intra 4x4
|
||||
#define I4DC4 (6 * 16 * BPS + 0)
|
||||
#define I4TM4 (6 * 16 * BPS + 4)
|
||||
#define I4VE4 (6 * 16 * BPS + 8)
|
||||
#define I4HE4 (6 * 16 * BPS + 12)
|
||||
#define I4RD4 (6 * 16 * BPS + 4 * BPS + 0)
|
||||
#define I4VR4 (6 * 16 * BPS + 4 * BPS + 4)
|
||||
#define I4LD4 (6 * 16 * BPS + 4 * BPS + 8)
|
||||
#define I4VL4 (6 * 16 * BPS + 4 * BPS + 12)
|
||||
#define I4HD4 (6 * 16 * BPS + 8 * BPS + 0)
|
||||
#define I4HU4 (6 * 16 * BPS + 8 * BPS + 4)
|
||||
#define I4TMP (6 * 16 * BPS + 8 * BPS + 8)
|
||||
#define I4DC4 (3 * 16 * BPS + 0)
|
||||
#define I4TM4 (I4DC4 + 4)
|
||||
#define I4VE4 (I4DC4 + 8)
|
||||
#define I4HE4 (I4DC4 + 12)
|
||||
#define I4RD4 (I4DC4 + 16)
|
||||
#define I4VR4 (I4DC4 + 20)
|
||||
#define I4LD4 (I4DC4 + 24)
|
||||
#define I4VL4 (I4DC4 + 28)
|
||||
#define I4HD4 (3 * 16 * BPS + 4 * BPS)
|
||||
#define I4HU4 (I4HD4 + 4)
|
||||
#define I4TMP (I4HD4 + 8)
|
||||
|
||||
typedef int64_t score_t; // type used for scores, rate, distortion
|
||||
// Note that MAX_COST is not the maximum allowed by sizeof(score_t),
|
||||
// in order to allow overflowing computations.
|
||||
#define MAX_COST ((score_t)0x7fffffffffffffLL)
|
||||
|
||||
#define QFIX 17
|
||||
#define BIAS(b) ((b) << (QFIX - 8))
|
||||
// Fun fact: this is the _only_ line where we're actually being lossy and
|
||||
// discarding bits.
|
||||
static WEBP_INLINE int QUANTDIV(int n, int iQ, int B) {
|
||||
return (n * iQ + B) >> QFIX;
|
||||
static WEBP_INLINE int QUANTDIV(uint32_t n, uint32_t iQ, uint32_t B) {
|
||||
return (int)((n * iQ + B) >> QFIX);
|
||||
}
|
||||
extern const uint8_t VP8Zigzag[16];
|
||||
|
||||
// Uncomment the following to remove token-buffer code:
|
||||
// #define DISABLE_TOKEN_BUFFER
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Headers
|
||||
@ -169,6 +131,8 @@ typedef uint32_t proba_t; // 16b + 16b
|
||||
typedef uint8_t ProbaArray[NUM_CTX][NUM_PROBAS];
|
||||
typedef proba_t StatsArray[NUM_CTX][NUM_PROBAS];
|
||||
typedef uint16_t CostArray[NUM_CTX][MAX_VARIABLE_LEVEL + 1];
|
||||
typedef const uint16_t* (*CostArrayPtr)[NUM_CTX]; // for easy casting
|
||||
typedef const uint16_t* CostArrayMap[16][NUM_CTX];
|
||||
typedef double LFStats[NUM_MB_SEGMENTS][MAX_LF_LEVELS]; // filter stats
|
||||
|
||||
typedef struct VP8Encoder VP8Encoder;
|
||||
@ -179,19 +143,20 @@ typedef struct {
|
||||
int update_map_; // whether to update the segment map or not.
|
||||
// must be 0 if there's only 1 segment.
|
||||
int size_; // bit-cost for transmitting the segment map
|
||||
} VP8SegmentHeader;
|
||||
} VP8EncSegmentHeader;
|
||||
|
||||
// Struct collecting all frame-persistent probabilities.
|
||||
typedef struct {
|
||||
uint8_t segments_[3]; // probabilities for segment tree
|
||||
uint8_t skip_proba_; // final probability of being skipped.
|
||||
ProbaArray coeffs_[NUM_TYPES][NUM_BANDS]; // 924 bytes
|
||||
ProbaArray coeffs_[NUM_TYPES][NUM_BANDS]; // 1056 bytes
|
||||
StatsArray stats_[NUM_TYPES][NUM_BANDS]; // 4224 bytes
|
||||
CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 11.4k
|
||||
CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 13056 bytes
|
||||
CostArrayMap remapped_costs_[NUM_TYPES]; // 1536 bytes
|
||||
int dirty_; // if true, need to call VP8CalculateLevelCosts()
|
||||
int use_skip_proba_; // Note: we always use skip_proba for now.
|
||||
int nb_skip_; // number of skipped blocks
|
||||
} VP8Proba;
|
||||
} VP8EncProba;
|
||||
|
||||
// Filter parameters. Not actually used in the code (we don't perform
|
||||
// the in-loop filtering), but filled from user's config
|
||||
@ -200,7 +165,7 @@ typedef struct {
|
||||
int level_; // base filter level [0..63]
|
||||
int sharpness_; // [0..7]
|
||||
int i4x4_lf_delta_; // delta filter level for i4x4 relative to i16x16
|
||||
} VP8FilterHeader;
|
||||
} VP8EncFilterHeader;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Informations about the macroblocks.
|
||||
@ -217,8 +182,8 @@ typedef struct {
|
||||
typedef struct VP8Matrix {
|
||||
uint16_t q_[16]; // quantizer steps
|
||||
uint16_t iq_[16]; // reciprocals, fixed point.
|
||||
uint16_t bias_[16]; // rounding bias
|
||||
uint16_t zthresh_[16]; // value under which a coefficient is zeroed
|
||||
uint32_t bias_[16]; // rounding bias
|
||||
uint32_t zthresh_[16]; // value below which a coefficient is zeroed
|
||||
uint16_t sharpen_[16]; // frequency boosters for slight sharpening
|
||||
} VP8Matrix;
|
||||
|
||||
@ -229,16 +194,19 @@ typedef struct {
|
||||
int beta_; // filter-susceptibility, range [0,255].
|
||||
int quant_; // final segment quantizer.
|
||||
int fstrength_; // final in-loop filtering strength
|
||||
int max_edge_; // max edge delta (for filtering strength)
|
||||
int min_disto_; // minimum distortion required to trigger filtering record
|
||||
// reactivities
|
||||
int lambda_i16_, lambda_i4_, lambda_uv_;
|
||||
int lambda_mode_, lambda_trellis_, tlambda_;
|
||||
int lambda_trellis_i16_, lambda_trellis_i4_, lambda_trellis_uv_;
|
||||
} VP8SegmentInfo;
|
||||
|
||||
// Handy transcient struct to accumulate score and info during RD-optimization
|
||||
// Handy transient struct to accumulate score and info during RD-optimization
|
||||
// and mode evaluation.
|
||||
typedef struct {
|
||||
score_t D, SD, R, score; // Distortion, spectral distortion, rate, score.
|
||||
score_t D, SD; // Distortion, spectral distortion
|
||||
score_t H, R, score; // header bits, rate, score.
|
||||
int16_t y_dc_levels[16]; // Quantized levels for luma-DC, luma-AC, chroma.
|
||||
int16_t y_ac_levels[16][16];
|
||||
int16_t uv_levels[4 + 4][16];
|
||||
@ -252,12 +220,11 @@ typedef struct {
|
||||
// right neighbouring data (samples, predictions, contexts, ...)
|
||||
typedef struct {
|
||||
int x_, y_; // current macroblock
|
||||
int y_offset_, uv_offset_; // offset to the luma / chroma planes
|
||||
int y_stride_, uv_stride_; // respective strides
|
||||
uint8_t* yuv_in_; // borrowed from enc_ (for now)
|
||||
uint8_t* yuv_out_; // ''
|
||||
uint8_t* yuv_out2_; // ''
|
||||
uint8_t* yuv_p_; // ''
|
||||
uint8_t* yuv_in_; // input samples
|
||||
uint8_t* yuv_out_; // output samples
|
||||
uint8_t* yuv_out2_; // secondary buffer swapped with yuv_out_.
|
||||
uint8_t* yuv_p_; // scratch buffer for prediction
|
||||
VP8Encoder* enc_; // back-pointer
|
||||
VP8MBInfo* mb_; // current macroblock
|
||||
VP8BitWriter* bw_; // current bit-writer
|
||||
@ -273,24 +240,44 @@ typedef struct {
|
||||
uint64_t uv_bits_; // macroblock bit-cost for chroma
|
||||
LFStats* lf_stats_; // filter stats (borrowed from enc_)
|
||||
int do_trellis_; // if true, perform extra level optimisation
|
||||
int done_; // true when scan is finished
|
||||
int count_down_; // number of mb still to be processed
|
||||
int count_down0_; // starting counter value (for progress)
|
||||
int percent0_; // saved initial progress percent
|
||||
|
||||
uint8_t* y_left_; // left luma samples (addressable from index -1 to 15).
|
||||
uint8_t* u_left_; // left u samples (addressable from index -1 to 7)
|
||||
uint8_t* v_left_; // left v samples (addressable from index -1 to 7)
|
||||
|
||||
uint8_t* y_top_; // top luma samples at position 'x_'
|
||||
uint8_t* uv_top_; // top u/v samples at position 'x_', packed as 16 bytes
|
||||
|
||||
// memory for storing y/u/v_left_
|
||||
uint8_t yuv_left_mem_[17 + 16 + 16 + 8 + WEBP_ALIGN_CST];
|
||||
// memory for yuv_*
|
||||
uint8_t yuv_mem_[3 * YUV_SIZE_ENC + PRED_SIZE_ENC + WEBP_ALIGN_CST];
|
||||
} VP8EncIterator;
|
||||
|
||||
// in iterator.c
|
||||
// must be called first.
|
||||
// must be called first
|
||||
void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it);
|
||||
// restart a scan.
|
||||
// restart a scan
|
||||
void VP8IteratorReset(VP8EncIterator* const it);
|
||||
// import samples from source
|
||||
void VP8IteratorImport(const VP8EncIterator* const it);
|
||||
// reset iterator position to row 'y'
|
||||
void VP8IteratorSetRow(VP8EncIterator* const it, int y);
|
||||
// set count down (=number of iterations to go)
|
||||
void VP8IteratorSetCountDown(VP8EncIterator* const it, int count_down);
|
||||
// return true if iteration is finished
|
||||
int VP8IteratorIsDone(const VP8EncIterator* const it);
|
||||
// Import uncompressed samples from source.
|
||||
// If tmp_32 is not NULL, import boundary samples too.
|
||||
// tmp_32 is a 32-bytes scratch buffer that must be aligned in memory.
|
||||
void VP8IteratorImport(VP8EncIterator* const it, uint8_t* tmp_32);
|
||||
// export decimated samples
|
||||
void VP8IteratorExport(const VP8EncIterator* const it);
|
||||
// go to next macroblock. Returns !done_. If *block_to_save is non-null, will
|
||||
// save the boundary values to top_/left_ arrays. block_to_save can be
|
||||
// it->yuv_out_ or it->yuv_in_.
|
||||
int VP8IteratorNext(VP8EncIterator* const it,
|
||||
const uint8_t* const block_to_save);
|
||||
// go to next macroblock. Returns false if not finished.
|
||||
int VP8IteratorNext(VP8EncIterator* const it);
|
||||
// save the yuv_out_ boundary values to top_/left_ arrays for next iterations.
|
||||
void VP8IteratorSaveBoundary(VP8EncIterator* const it);
|
||||
// Report progression based on macroblock rows. Return 0 for user-abort request.
|
||||
int VP8IteratorProgress(const VP8EncIterator* const it,
|
||||
int final_delta_percent);
|
||||
@ -314,44 +301,43 @@ void VP8SetSegment(const VP8EncIterator* const it, int segment);
|
||||
//------------------------------------------------------------------------------
|
||||
// Paginated token buffer
|
||||
|
||||
// WIP: #define USE_TOKEN_BUFFER
|
||||
|
||||
#ifdef USE_TOKEN_BUFFER
|
||||
|
||||
#define MAX_NUM_TOKEN 2048
|
||||
|
||||
typedef struct VP8Tokens VP8Tokens;
|
||||
struct VP8Tokens {
|
||||
uint16_t tokens_[MAX_NUM_TOKEN]; // bit#15: bit, bits 0..14: slot
|
||||
int left_;
|
||||
VP8Tokens* next_;
|
||||
};
|
||||
typedef struct VP8Tokens VP8Tokens; // struct details in token.c
|
||||
|
||||
typedef struct {
|
||||
VP8Tokens* rows_;
|
||||
uint16_t* tokens_; // set to (*last_)->tokens_
|
||||
VP8Tokens** last_;
|
||||
int left_;
|
||||
int error_; // true in case of malloc error
|
||||
#if !defined(DISABLE_TOKEN_BUFFER)
|
||||
VP8Tokens* pages_; // first page
|
||||
VP8Tokens** last_page_; // last page
|
||||
uint16_t* tokens_; // set to (*last_page_)->tokens_
|
||||
int left_; // how many free tokens left before the page is full
|
||||
int page_size_; // number of tokens per page
|
||||
#endif
|
||||
int error_; // true in case of malloc error
|
||||
} VP8TBuffer;
|
||||
|
||||
void VP8TBufferInit(VP8TBuffer* const b); // initialize an empty buffer
|
||||
int VP8TBufferNewPage(VP8TBuffer* const b); // allocate a new page
|
||||
void VP8TBufferClear(VP8TBuffer* const b); // de-allocate memory
|
||||
// initialize an empty buffer
|
||||
void VP8TBufferInit(VP8TBuffer* const b, int page_size);
|
||||
void VP8TBufferClear(VP8TBuffer* const b); // de-allocate pages memory
|
||||
|
||||
int VP8EmitTokens(const VP8TBuffer* const b, VP8BitWriter* const bw,
|
||||
const uint8_t* const probas);
|
||||
#if !defined(DISABLE_TOKEN_BUFFER)
|
||||
|
||||
static WEBP_INLINE int VP8AddToken(VP8TBuffer* const b,
|
||||
int bit, int proba_idx) {
|
||||
if (b->left_ > 0 || VP8TBufferNewPage(b)) {
|
||||
const int slot = --b->left_;
|
||||
b->tokens_[slot] = (bit << 15) | proba_idx;
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
// Finalizes bitstream when probabilities are known.
|
||||
// Deletes the allocated token memory if final_pass is true.
|
||||
int VP8EmitTokens(VP8TBuffer* const b, VP8BitWriter* const bw,
|
||||
const uint8_t* const probas, int final_pass);
|
||||
|
||||
#endif // USE_TOKEN_BUFFER
|
||||
// record the coding of coefficients without knowing the probabilities yet
|
||||
int VP8RecordCoeffTokens(const int ctx, const int coeff_type,
|
||||
int first, int last,
|
||||
const int16_t* const coeffs,
|
||||
VP8TBuffer* const tokens);
|
||||
|
||||
// Estimate the final coded size given a set of 'probas'.
|
||||
size_t VP8EstimateTokenSize(VP8TBuffer* const b, const uint8_t* const probas);
|
||||
|
||||
// unused for now
|
||||
void VP8TokenToStats(const VP8TBuffer* const b, proba_t* const stats);
|
||||
|
||||
#endif // !DISABLE_TOKEN_BUFFER
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8Encoder
|
||||
@ -361,8 +347,8 @@ struct VP8Encoder {
|
||||
WebPPicture* pic_; // input / output picture
|
||||
|
||||
// headers
|
||||
VP8FilterHeader filter_hdr_; // filtering information
|
||||
VP8SegmentHeader segment_hdr_; // segment information
|
||||
VP8EncFilterHeader filter_hdr_; // filtering information
|
||||
VP8EncSegmentHeader segment_hdr_; // segment information
|
||||
|
||||
int profile_; // VP8's profile, deduced from Config.
|
||||
|
||||
@ -376,6 +362,7 @@ struct VP8Encoder {
|
||||
// per-partition boolean decoders.
|
||||
VP8BitWriter bw_; // part0
|
||||
VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions
|
||||
VP8TBuffer tokens_; // token buffer
|
||||
|
||||
int percent_; // for progress
|
||||
|
||||
@ -383,17 +370,13 @@ struct VP8Encoder {
|
||||
int has_alpha_;
|
||||
uint8_t* alpha_data_; // non-NULL if transparency is present
|
||||
uint32_t alpha_data_size_;
|
||||
|
||||
// enhancement layer
|
||||
int use_layer_;
|
||||
VP8BitWriter layer_bw_;
|
||||
uint8_t* layer_data_;
|
||||
size_t layer_data_size_;
|
||||
WebPWorker alpha_worker_;
|
||||
|
||||
// quantization info (one set of DC/AC dequant factor per segment)
|
||||
VP8SegmentInfo dqm_[NUM_MB_SEGMENTS];
|
||||
int base_quant_; // nominal quantizer value. Only used
|
||||
// for relative coding of segments' quant.
|
||||
int alpha_; // global susceptibility (<=> complexity)
|
||||
int uv_alpha_; // U/V quantization susceptibility
|
||||
// global offset of quantizers, shared by all segments
|
||||
int dq_y1_dc_;
|
||||
@ -401,34 +384,29 @@ struct VP8Encoder {
|
||||
int dq_uv_dc_, dq_uv_ac_;
|
||||
|
||||
// probabilities and statistics
|
||||
VP8Proba proba_;
|
||||
uint64_t sse_[4]; // sum of Y/U/V/A squared errors for all macroblocks
|
||||
uint64_t sse_count_; // pixel count for the sse_[] stats
|
||||
int coded_size_;
|
||||
int residual_bytes_[3][4];
|
||||
int block_count_[3];
|
||||
VP8EncProba proba_;
|
||||
uint64_t sse_[4]; // sum of Y/U/V/A squared errors for all macroblocks
|
||||
uint64_t sse_count_; // pixel count for the sse_[] stats
|
||||
int coded_size_;
|
||||
int residual_bytes_[3][4];
|
||||
int block_count_[3];
|
||||
|
||||
// quality/speed settings
|
||||
int method_; // 0=fastest, 6=best/slowest.
|
||||
int rd_opt_level_; // Deduced from method_.
|
||||
int max_i4_header_bits_; // partition #0 safeness factor
|
||||
int method_; // 0=fastest, 6=best/slowest.
|
||||
VP8RDLevel rd_opt_level_; // Deduced from method_.
|
||||
int max_i4_header_bits_; // partition #0 safeness factor
|
||||
int thread_level_; // derived from config->thread_level
|
||||
int do_search_; // derived from config->target_XXX
|
||||
int use_tokens_; // if true, use token buffer
|
||||
|
||||
// Memory
|
||||
VP8MBInfo* mb_info_; // contextual macroblock infos (mb_w_ + 1)
|
||||
uint8_t* preds_; // predictions modes: (4*mb_w+1) * (4*mb_h+1)
|
||||
uint32_t* nz_; // non-zero bit context: mb_w+1
|
||||
uint8_t* yuv_in_; // input samples
|
||||
uint8_t* yuv_out_; // output samples
|
||||
uint8_t* yuv_out2_; // secondary scratch out-buffer. swapped with yuv_out_.
|
||||
uint8_t* yuv_p_; // scratch buffer for prediction
|
||||
uint8_t *y_top_; // top luma samples.
|
||||
uint8_t *uv_top_; // top u/v samples.
|
||||
// U and V are packed into 16 pixels (8 U + 8 V)
|
||||
uint8_t *y_left_; // left luma samples (adressable from index -1 to 15).
|
||||
uint8_t *u_left_; // left u samples (adressable from index -1 to 7)
|
||||
uint8_t *v_left_; // left v samples (adressable from index -1 to 7)
|
||||
|
||||
LFStats *lf_stats_; // autofilter stats (if NULL, autofilter is off)
|
||||
uint8_t* y_top_; // top luma samples.
|
||||
uint8_t* uv_top_; // top u/v samples.
|
||||
// U and V are packed into 16 bytes (8 U + 8 V)
|
||||
LFStats* lf_stats_; // autofilter stats (if NULL, autofilter is off)
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -441,7 +419,7 @@ extern const uint8_t
|
||||
// Reset the token probabilities to their initial (default) values
|
||||
void VP8DefaultProbas(VP8Encoder* const enc);
|
||||
// Write the token probabilities
|
||||
void VP8WriteProbas(VP8BitWriter* const bw, const VP8Proba* const probas);
|
||||
void VP8WriteProbas(VP8BitWriter* const bw, const VP8EncProba* const probas);
|
||||
// Writes the partition #0 modes (that is: all intra modes)
|
||||
void VP8CodeIntraModes(VP8Encoder* const enc);
|
||||
|
||||
@ -454,7 +432,11 @@ int VP8EncWrite(VP8Encoder* const enc);
|
||||
void VP8EncFreeBitWriters(VP8Encoder* const enc);
|
||||
|
||||
// in frame.c
|
||||
extern const uint8_t VP8EncBands[16 + 1];
|
||||
extern const uint8_t VP8Cat3[];
|
||||
extern const uint8_t VP8Cat4[];
|
||||
extern const uint8_t VP8Cat5[];
|
||||
extern const uint8_t VP8Cat6[];
|
||||
|
||||
// Form all the four Intra16x16 predictions in the yuv_p_ cache
|
||||
void VP8MakeLuma16Preds(const VP8EncIterator* const it);
|
||||
// Form all the four Chroma8x8 predictions in the yuv_p_ cache
|
||||
@ -466,9 +448,9 @@ void VP8MakeIntra4Preds(const VP8EncIterator* const it);
|
||||
int VP8GetCostLuma16(VP8EncIterator* const it, const VP8ModeScore* const rd);
|
||||
int VP8GetCostLuma4(VP8EncIterator* const it, const int16_t levels[16]);
|
||||
int VP8GetCostUV(VP8EncIterator* const it, const VP8ModeScore* const rd);
|
||||
// Main stat / coding passes
|
||||
// Main coding calls
|
||||
int VP8EncLoop(VP8Encoder* const enc);
|
||||
int VP8StatLoop(VP8Encoder* const enc);
|
||||
int VP8EncTokenLoop(VP8Encoder* const enc);
|
||||
|
||||
// in webpenc.c
|
||||
// Assign an error code to a picture. Return false for convenience.
|
||||
@ -485,18 +467,14 @@ int VP8EncAnalyze(VP8Encoder* const enc);
|
||||
// Sets up segment's quantization values, base_quant_ and filter strengths.
|
||||
void VP8SetSegmentParams(VP8Encoder* const enc, float quality);
|
||||
// Pick best modes and fills the levels. Returns true if skipped.
|
||||
int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt);
|
||||
int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd,
|
||||
VP8RDLevel rd_opt);
|
||||
|
||||
// in alpha.c
|
||||
void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression
|
||||
int VP8EncStartAlpha(VP8Encoder* const enc); // start alpha coding process
|
||||
int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data
|
||||
void VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data
|
||||
|
||||
// in layer.c
|
||||
void VP8EncInitLayer(VP8Encoder* const enc); // init everything
|
||||
void VP8EncCodeLayerBlock(VP8EncIterator* it); // code one more macroblock
|
||||
int VP8EncFinishLayer(VP8Encoder* const enc); // finalize coding
|
||||
void VP8EncDeleteLayer(VP8Encoder* enc); // reclaim memory
|
||||
int VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data
|
||||
|
||||
// in filter.c
|
||||
|
||||
@ -516,9 +494,38 @@ void VP8InitFilter(VP8EncIterator* const it);
|
||||
void VP8StoreFilterStats(VP8EncIterator* const it);
|
||||
void VP8AdjustFilterStrength(VP8EncIterator* const it);
|
||||
|
||||
// returns the approximate filtering strength needed to smooth a edge
|
||||
// step of 'delta', given a sharpness parameter 'sharpness'.
|
||||
int VP8FilterStrengthFromDelta(int sharpness, int delta);
|
||||
|
||||
// misc utils for picture_*.c:
|
||||
|
||||
// Remove reference to the ARGB/YUVA buffer (doesn't free anything).
|
||||
void WebPPictureResetBuffers(WebPPicture* const picture);
|
||||
|
||||
// Allocates ARGB buffer of given dimension (previous one is always free'd).
|
||||
// Preserves the YUV(A) buffer. Returns false in case of error (invalid param,
|
||||
// out-of-memory).
|
||||
int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height);
|
||||
|
||||
// Allocates YUVA buffer of given dimension (previous one is always free'd).
|
||||
// Uses picture->csp to determine whether an alpha buffer is needed.
|
||||
// Preserves the ARGB buffer.
|
||||
// Returns false in case of error (invalid param, out-of-memory).
|
||||
int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height);
|
||||
|
||||
// in near_lossless.c
|
||||
// Near lossless preprocessing in RGB color-space.
|
||||
int VP8ApplyNearLossless(int xsize, int ysize, uint32_t* argb, int quality);
|
||||
// Near lossless adjustment for predictors.
|
||||
void VP8ApplyNearLosslessPredict(int xsize, int ysize, int pred_bits,
|
||||
const uint32_t* argb_orig,
|
||||
uint32_t* argb, uint32_t* argb_scratch,
|
||||
const uint32_t* const transform_data,
|
||||
int quality, int subtract_green);
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Lossless encoder: internal header.
|
||||
@ -12,12 +14,13 @@
|
||||
#ifndef WEBP_ENC_VP8LI_H_
|
||||
#define WEBP_ENC_VP8LI_H_
|
||||
|
||||
#include "./backward_references.h"
|
||||
#include "./histogram.h"
|
||||
#include "../utils/bit_writer.h"
|
||||
#include "../encode.h"
|
||||
#include "../format_constants.h"
|
||||
#include "../webp/encode.h"
|
||||
#include "../webp/format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -43,6 +46,12 @@ typedef struct {
|
||||
int use_palette_;
|
||||
int palette_size_;
|
||||
uint32_t palette_[MAX_PALETTE_SIZE];
|
||||
|
||||
// Some 'scratch' (potentially large) objects.
|
||||
struct VP8LBackwardRefs refs_[2]; // Backward Refs array corresponding to
|
||||
// LZ77 & RLE coding.
|
||||
VP8LHashChain hash_chain_; // HashChain data for constructing
|
||||
// backward references.
|
||||
} VP8LEncoder;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -61,7 +70,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config,
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// WebP encoder: main entry point
|
||||
@ -14,16 +16,13 @@
|
||||
#include <string.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "./cost.h"
|
||||
#include "./vp8enci.h"
|
||||
#include "./vp8li.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
// #define PRINT_MEMORY_INFO
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef PRINT_MEMORY_INFO
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
@ -34,44 +33,19 @@ int WebPGetEncoderVersion(void) {
|
||||
return (ENC_MAJ_VERSION << 16) | (ENC_MIN_VERSION << 8) | ENC_REV_VERSION;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPPicture
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int DummyWriter(const uint8_t* data, size_t data_size,
|
||||
const WebPPicture* const picture) {
|
||||
// The following are to prevent 'unused variable' error message.
|
||||
(void)data;
|
||||
(void)data_size;
|
||||
(void)picture;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int WebPPictureInitInternal(WebPPicture* picture, int version) {
|
||||
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) {
|
||||
return 0; // caller/system version mismatch!
|
||||
}
|
||||
if (picture != NULL) {
|
||||
memset(picture, 0, sizeof(*picture));
|
||||
picture->writer = DummyWriter;
|
||||
WebPEncodingSetError(picture, VP8_ENC_OK);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8Encoder
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void ResetSegmentHeader(VP8Encoder* const enc) {
|
||||
VP8SegmentHeader* const hdr = &enc->segment_hdr_;
|
||||
VP8EncSegmentHeader* const hdr = &enc->segment_hdr_;
|
||||
hdr->num_segments_ = enc->config_->segments;
|
||||
hdr->update_map_ = (hdr->num_segments_ > 1);
|
||||
hdr->size_ = 0;
|
||||
}
|
||||
|
||||
static void ResetFilterHeader(VP8Encoder* const enc) {
|
||||
VP8FilterHeader* const hdr = &enc->filter_hdr_;
|
||||
VP8EncFilterHeader* const hdr = &enc->filter_hdr_;
|
||||
hdr->simple_ = 1;
|
||||
hdr->level_ = 0;
|
||||
hdr->sharpness_ = 0;
|
||||
@ -93,56 +67,73 @@ static void ResetBoundaryPredictions(VP8Encoder* const enc) {
|
||||
enc->nz_[-1] = 0; // constant
|
||||
}
|
||||
|
||||
// Map configured quality level to coding tools used.
|
||||
//-------------+---+---+---+---+---+---+
|
||||
// Quality | 0 | 1 | 2 | 3 | 4 | 5 +
|
||||
//-------------+---+---+---+---+---+---+
|
||||
// dynamic prob| ~ | x | x | x | x | x |
|
||||
//-------------+---+---+---+---+---+---+
|
||||
// rd-opt modes| | | x | x | x | x |
|
||||
//-------------+---+---+---+---+---+---+
|
||||
// fast i4/i16 | x | x | | | | |
|
||||
//-------------+---+---+---+---+---+---+
|
||||
// rd-opt i4/16| | | x | x | x | x |
|
||||
//-------------+---+---+---+---+---+---+
|
||||
// Trellis | | x | | | x | x |
|
||||
//-------------+---+---+---+---+---+---+
|
||||
// full-SNS | | | | | | x |
|
||||
//-------------+---+---+---+---+---+---+
|
||||
// Mapping from config->method_ to coding tools used.
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
// Method | 0 | 1 | 2 | 3 |(4)| 5 | 6 |
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
// fast probe | x | | | x | | | |
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
// dynamic proba | ~ | x | x | x | x | x | x |
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
// fast mode analysis| | | | | x | x | x |
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
// basic rd-opt | | | | x | x | x | x |
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
// disto-score i4/16 | | | x | | | | |
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
// rd-opt i4/16 | | | ~ | x | x | x | x |
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
// token buffer (opt)| | | | x | x | x | x |
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
// Trellis | | | | | | x |Ful|
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
// full-SNS | | | | | x | x | x |
|
||||
//-------------------+---+---+---+---+---+---+---+
|
||||
|
||||
static void MapConfigToTools(VP8Encoder* const enc) {
|
||||
const int method = enc->config_->method;
|
||||
const int limit = 100 - enc->config_->partition_limit;
|
||||
const WebPConfig* const config = enc->config_;
|
||||
const int method = config->method;
|
||||
const int limit = 100 - config->partition_limit;
|
||||
enc->method_ = method;
|
||||
enc->rd_opt_level_ = (method >= 6) ? 3
|
||||
: (method >= 5) ? 2
|
||||
: (method >= 3) ? 1
|
||||
: 0;
|
||||
enc->rd_opt_level_ = (method >= 6) ? RD_OPT_TRELLIS_ALL
|
||||
: (method >= 5) ? RD_OPT_TRELLIS
|
||||
: (method >= 3) ? RD_OPT_BASIC
|
||||
: RD_OPT_NONE;
|
||||
enc->max_i4_header_bits_ =
|
||||
256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block
|
||||
(limit * limit) / (100 * 100); // ... modulated with a quadratic curve.
|
||||
|
||||
enc->thread_level_ = config->thread_level;
|
||||
|
||||
enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0);
|
||||
if (!config->low_memory) {
|
||||
#if !defined(DISABLE_TOKEN_BUFFER)
|
||||
enc->use_tokens_ = (enc->rd_opt_level_ >= RD_OPT_BASIC); // need rd stats
|
||||
#endif
|
||||
if (enc->use_tokens_) {
|
||||
enc->num_parts_ = 1; // doesn't work with multi-partition
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Memory scaling with dimensions:
|
||||
// memory (bytes) ~= 2.25 * w + 0.0625 * w * h
|
||||
//
|
||||
// Typical memory footprint (768x510 picture)
|
||||
// Memory used:
|
||||
// encoder: 33919
|
||||
// block cache: 2880
|
||||
// info: 3072
|
||||
// preds: 24897
|
||||
// top samples: 1623
|
||||
// non-zero: 196
|
||||
// lf-stats: 2048
|
||||
// total: 68635
|
||||
// Transcient object sizes:
|
||||
// VP8EncIterator: 352
|
||||
// VP8ModeScore: 912
|
||||
// VP8SegmentInfo: 532
|
||||
// VP8Proba: 31032
|
||||
// Typical memory footprint (614x440 picture)
|
||||
// encoder: 22111
|
||||
// info: 4368
|
||||
// preds: 17741
|
||||
// top samples: 1263
|
||||
// non-zero: 175
|
||||
// lf-stats: 0
|
||||
// total: 45658
|
||||
// Transient object sizes:
|
||||
// VP8EncIterator: 3360
|
||||
// VP8ModeScore: 872
|
||||
// VP8SegmentInfo: 732
|
||||
// VP8EncProba: 18352
|
||||
// LFStats: 2048
|
||||
// Picture size (yuv): 589824
|
||||
// Picture size (yuv): 419328
|
||||
|
||||
static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
|
||||
WebPPicture* const picture) {
|
||||
@ -154,20 +145,16 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
|
||||
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 nz_size = (mb_w + 1) * sizeof(uint32_t) + WEBP_ALIGN_CST;
|
||||
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 samples_size = 2 * top_stride * sizeof(uint8_t) // top-luma/u/v
|
||||
+ WEBP_ALIGN_CST; // align all
|
||||
const size_t lf_stats_size =
|
||||
config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0;
|
||||
config->autofilter ? sizeof(LFStats) + WEBP_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
|
||||
+ WEBP_ALIGN_CST // cache alignment
|
||||
+ info_size // modes info
|
||||
+ preds_size // prediction modes
|
||||
+ samples_size // top/left samples
|
||||
@ -178,23 +165,22 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
|
||||
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,
|
||||
sizeof(VP8Encoder) + WEBP_ALIGN_CST, info_size,
|
||||
preds_size, samples_size, nz_size, lf_stats_size, size);
|
||||
printf("Transcient object sizes:\n"
|
||||
printf("Transient object sizes:\n"
|
||||
" VP8EncIterator: %ld\n"
|
||||
" VP8ModeScore: %ld\n"
|
||||
" VP8SegmentInfo: %ld\n"
|
||||
" VP8Proba: %ld\n"
|
||||
" VP8EncProba: %ld\n"
|
||||
" LFStats: %ld\n",
|
||||
sizeof(VP8EncIterator), sizeof(VP8ModeScore),
|
||||
sizeof(VP8SegmentInfo), sizeof(VP8Proba),
|
||||
sizeof(VP8SegmentInfo), sizeof(VP8EncProba),
|
||||
sizeof(LFStats));
|
||||
printf("Picture size (yuv): %ld\n",
|
||||
mb_w * mb_h * 384 * sizeof(uint8_t));
|
||||
@ -206,41 +192,27 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
|
||||
return NULL;
|
||||
}
|
||||
enc = (VP8Encoder*)mem;
|
||||
mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc));
|
||||
mem = (uint8_t*)WEBP_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;
|
||||
enc->nz_ = 1 + (uint32_t*)WEBP_ALIGN(mem);
|
||||
mem += nz_size;
|
||||
enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL;
|
||||
enc->lf_stats_ = lf_stats_size ? (LFStats*)WEBP_ALIGN(mem) : NULL;
|
||||
mem += lf_stats_size;
|
||||
|
||||
// top samples (all 16-aligned)
|
||||
mem = (uint8_t*)DO_ALIGN(mem);
|
||||
mem = (uint8_t*)WEBP_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;
|
||||
assert(mem <= (uint8_t*)enc + size);
|
||||
|
||||
enc->config_ = config;
|
||||
enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2;
|
||||
@ -253,29 +225,32 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config,
|
||||
ResetSegmentHeader(enc);
|
||||
ResetFilterHeader(enc);
|
||||
ResetBoundaryPredictions(enc);
|
||||
|
||||
VP8EncDspCostInit();
|
||||
VP8EncInitAlpha(enc);
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
VP8EncInitLayer(enc);
|
||||
#endif
|
||||
|
||||
// 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 void DeleteVP8Encoder(VP8Encoder* enc) {
|
||||
static int DeleteVP8Encoder(VP8Encoder* enc) {
|
||||
int ok = 1;
|
||||
if (enc != NULL) {
|
||||
VP8EncDeleteAlpha(enc);
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
VP8EncDeleteLayer(enc);
|
||||
#endif
|
||||
free(enc);
|
||||
ok = VP8EncDeleteAlpha(enc);
|
||||
VP8TBufferClear(&enc->tokens_);
|
||||
WebPSafeFree(enc);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static double GetPSNR(uint64_t err, uint64_t size) {
|
||||
return err ? 10. * log10(255. * 255. * size / err) : 99.;
|
||||
return (err > 0 && size > 0) ? 10. * log10(255. * 255. * size / err) : 99.;
|
||||
}
|
||||
|
||||
static void FinalizePSNR(const VP8Encoder* const enc) {
|
||||
@ -332,7 +307,7 @@ int WebPReportProgress(const WebPPicture* const pic,
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
|
||||
int ok;
|
||||
int ok = 0;
|
||||
|
||||
if (pic == NULL)
|
||||
return 0;
|
||||
@ -346,44 +321,63 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) {
|
||||
if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION)
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION);
|
||||
|
||||
if (!config->exact) {
|
||||
WebPCleanupTransparentArea(pic);
|
||||
}
|
||||
|
||||
if (pic->stats != NULL) memset(pic->stats, 0, sizeof(*pic->stats));
|
||||
|
||||
if (!config->lossless) {
|
||||
VP8Encoder* enc = NULL;
|
||||
if (pic->y == NULL || pic->u == NULL || pic->v == NULL) {
|
||||
if (pic->argb != NULL) {
|
||||
if (!WebPPictureARGBToYUVA(pic, WEBP_YUV420)) return 0;
|
||||
if (pic->use_argb || pic->y == NULL || pic->u == NULL || pic->v == NULL) {
|
||||
// Make sure we have YUVA samples.
|
||||
if (config->preprocessing & 4) {
|
||||
if (!WebPPictureSmartARGBToYUVA(pic)) {
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
|
||||
float dithering = 0.f;
|
||||
if (config->preprocessing & 2) {
|
||||
const float x = config->quality / 100.f;
|
||||
const float x2 = x * x;
|
||||
// slowly decreasing from max dithering at low quality (q->0)
|
||||
// to 0.5 dithering amplitude at high quality (q->100)
|
||||
dithering = 1.0f + (0.5f - 1.0f) * x2 * x2;
|
||||
}
|
||||
if (!WebPPictureARGBToYUVADithered(pic, WEBP_YUV420, dithering)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enc = InitVP8Encoder(config, pic);
|
||||
if (enc == NULL) return 0; // pic->error is already set.
|
||||
// Note: each of the tasks below account for 20% in the progress report.
|
||||
ok = VP8EncAnalyze(enc)
|
||||
&& VP8StatLoop(enc)
|
||||
&& VP8EncLoop(enc)
|
||||
&& VP8EncFinishAlpha(enc)
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
&& VP8EncFinishLayer(enc)
|
||||
#endif
|
||||
&& VP8EncWrite(enc);
|
||||
ok = VP8EncAnalyze(enc);
|
||||
|
||||
// Analysis is done, proceed to actual coding.
|
||||
ok = ok && VP8EncStartAlpha(enc); // possibly done in parallel
|
||||
if (!enc->use_tokens_) {
|
||||
ok = ok && VP8EncLoop(enc);
|
||||
} else {
|
||||
ok = ok && VP8EncTokenLoop(enc);
|
||||
}
|
||||
ok = ok && VP8EncFinishAlpha(enc);
|
||||
|
||||
ok = ok && VP8EncWrite(enc);
|
||||
StoreStats(enc);
|
||||
if (!ok) {
|
||||
VP8EncFreeBitWriters(enc);
|
||||
}
|
||||
DeleteVP8Encoder(enc);
|
||||
ok &= DeleteVP8Encoder(enc); // must always be called, even if !ok
|
||||
} else {
|
||||
if (pic->argb == NULL)
|
||||
return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER);
|
||||
// Make sure we have ARGB samples.
|
||||
if (pic->argb == NULL && !WebPPictureYUVAToARGB(pic)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem.
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// WebP encoder: main interface
|
||||
@ -14,11 +16,22 @@
|
||||
|
||||
#include "./types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WEBP_ENCODER_ABI_VERSION 0x0200 // MAJOR(8b) + MINOR(8b)
|
||||
#define WEBP_ENCODER_ABI_VERSION 0x0209 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
// Note: forward declaring enumerations is not allowed in (strict) C and C++,
|
||||
// the types are left here for reference.
|
||||
// typedef enum WebPImageHint WebPImageHint;
|
||||
// typedef enum WebPEncCSP WebPEncCSP;
|
||||
// typedef enum WebPPreset WebPPreset;
|
||||
// typedef enum WebPEncodingError WebPEncodingError;
|
||||
typedef struct WebPConfig WebPConfig;
|
||||
typedef struct WebPPicture WebPPicture; // main structure for I/O
|
||||
typedef struct WebPAuxStats WebPAuxStats;
|
||||
typedef struct WebPMemoryWriter WebPMemoryWriter;
|
||||
|
||||
// Return the encoder's version number, packed in hexadecimal using 8bits for
|
||||
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||
@ -29,7 +42,7 @@ WEBP_EXTERN(int) WebPGetEncoderVersion(void);
|
||||
|
||||
// Returns the size of the compressed data (pointed to by *output), or 0 if
|
||||
// an error occurred. The compressed data must be released by the caller
|
||||
// using the call 'free(*output)'.
|
||||
// using the call 'WebPFree(*output)'.
|
||||
// These functions compress using the lossy format, and the quality_factor
|
||||
// can go from 0 (smaller output, lower quality) to 100 (best quality,
|
||||
// larger output).
|
||||
@ -62,11 +75,14 @@ WEBP_EXTERN(size_t) WebPEncodeLosslessBGRA(const uint8_t* bgra,
|
||||
int width, int height, int stride,
|
||||
uint8_t** output);
|
||||
|
||||
// Releases memory returned by the WebPEncode*() functions above.
|
||||
WEBP_EXTERN(void) WebPFree(void* ptr);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Coding parameters
|
||||
|
||||
// Image characteristics hint for the underlying encoder.
|
||||
typedef enum {
|
||||
typedef enum WebPImageHint {
|
||||
WEBP_HINT_DEFAULT = 0, // default preset.
|
||||
WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot
|
||||
WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting
|
||||
@ -74,7 +90,8 @@ typedef enum {
|
||||
WEBP_HINT_LAST
|
||||
} WebPImageHint;
|
||||
|
||||
typedef struct {
|
||||
// Compression parameters.
|
||||
struct WebPConfig {
|
||||
int lossless; // Lossless encoding (0=lossy(default), 1=lossless).
|
||||
float quality; // between 0 (smallest file) and 100 (biggest)
|
||||
int method; // quality/speed trade-off (0=fast, 6=slower-better)
|
||||
@ -103,19 +120,38 @@ typedef struct {
|
||||
|
||||
int show_compressed; // if true, export the compressed picture back.
|
||||
// In-loop filtering is not applied.
|
||||
int preprocessing; // preprocessing filter (0=none, 1=segment-smooth)
|
||||
int preprocessing; // preprocessing filter:
|
||||
// 0=none, 1=segment-smooth, 2=pseudo-random dithering
|
||||
int partitions; // log2(number of token partitions) in [0..3]. Default
|
||||
// is set to 0 for easier progressive decoding.
|
||||
int partition_limit; // quality degradation allowed to fit the 512k limit
|
||||
// on prediction modes coding (0: no degradation,
|
||||
// 100: maximum possible degradation).
|
||||
int emulate_jpeg_size; // If true, compression parameters will be remapped
|
||||
// to better match the expected output size from
|
||||
// JPEG compression. Generally, the output size will
|
||||
// be similar but the degradation will be lower.
|
||||
int thread_level; // If non-zero, try and use multi-threaded encoding.
|
||||
int low_memory; // If set, reduce memory usage (but increase CPU use).
|
||||
|
||||
uint32_t pad[8]; // padding for later use
|
||||
} WebPConfig;
|
||||
int near_lossless; // Near lossless encoding [0 = off(default) .. 100].
|
||||
// This feature is experimental.
|
||||
int exact; // if non-zero, preserve the exact RGB values under
|
||||
// transparent area. Otherwise, discard this invisible
|
||||
// RGB information for better compression. The default
|
||||
// value is 0.
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
int delta_palettization;
|
||||
uint32_t pad[2]; // padding for later use
|
||||
#else
|
||||
uint32_t pad[3]; // padding for later use
|
||||
#endif // WEBP_EXPERIMENTAL_FEATURES
|
||||
};
|
||||
|
||||
// Enumerate some predefined settings for WebPConfig, depending on the type
|
||||
// of source picture. These presets are used when calling WebPConfigPreset().
|
||||
typedef enum {
|
||||
typedef enum WebPPreset {
|
||||
WEBP_PRESET_DEFAULT = 0, // default preset.
|
||||
WEBP_PRESET_PICTURE, // digital picture, like portrait, inner shot
|
||||
WEBP_PRESET_PHOTO, // outdoor photograph, with natural lighting
|
||||
@ -146,17 +182,23 @@ static WEBP_INLINE int WebPConfigPreset(WebPConfig* config,
|
||||
WEBP_ENCODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Activate the lossless compression mode with the desired efficiency level
|
||||
// between 0 (fastest, lowest compression) and 9 (slower, best compression).
|
||||
// A good default level is '6', providing a fair tradeoff between compression
|
||||
// speed and final compressed size.
|
||||
// This function will overwrite several fields from config: 'method', 'quality'
|
||||
// and 'lossless'. Returns false in case of parameter error.
|
||||
WEBP_EXTERN(int) WebPConfigLosslessPreset(WebPConfig* config, int level);
|
||||
|
||||
// Returns true if 'config' is non-NULL and all configuration parameters are
|
||||
// within their valid ranges.
|
||||
WEBP_EXTERN(int) WebPValidateConfig(const WebPConfig* config);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Input / Output
|
||||
|
||||
typedef struct WebPPicture WebPPicture; // main structure for I/O
|
||||
|
||||
// Structure for storing auxiliary statistics (mostly for lossy encoding).
|
||||
typedef struct {
|
||||
|
||||
struct WebPAuxStats {
|
||||
int coded_size; // final size
|
||||
|
||||
float PSNR[5]; // peak-signal-to-noise ratio for Y/U/V/All/Alpha
|
||||
@ -180,9 +222,11 @@ typedef struct {
|
||||
int cache_bits; // number of bits for color cache lookup
|
||||
int palette_size; // number of color in palette, if used
|
||||
int lossless_size; // final lossless size
|
||||
int lossless_hdr_size; // lossless header (transform, huffman etc) size
|
||||
int lossless_data_size; // lossless image data size
|
||||
|
||||
uint32_t pad[4]; // padding for later use
|
||||
} WebPAuxStats;
|
||||
uint32_t pad[2]; // padding for later use
|
||||
};
|
||||
|
||||
// Signature for output function. Should return true if writing was successful.
|
||||
// data/data_size is the segment of data to write, and 'picture' is for
|
||||
@ -192,18 +236,22 @@ typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size,
|
||||
|
||||
// WebPMemoryWrite: a special WebPWriterFunction that writes to memory using
|
||||
// the following WebPMemoryWriter object (to be set as a custom_ptr).
|
||||
typedef struct {
|
||||
struct WebPMemoryWriter {
|
||||
uint8_t* mem; // final buffer (of size 'max_size', larger than 'size').
|
||||
size_t size; // final size
|
||||
size_t max_size; // total capacity
|
||||
uint32_t pad[1]; // padding for later use
|
||||
} WebPMemoryWriter;
|
||||
};
|
||||
|
||||
// The following must be called first before any use.
|
||||
WEBP_EXTERN(void) WebPMemoryWriterInit(WebPMemoryWriter* writer);
|
||||
|
||||
// The following must be called to deallocate writer->mem memory. The 'writer'
|
||||
// object itself is not deallocated.
|
||||
WEBP_EXTERN(void) WebPMemoryWriterClear(WebPMemoryWriter* writer);
|
||||
// The custom writer to be used with WebPMemoryWriter as custom_ptr. Upon
|
||||
// completion, writer.mem and writer.size will hold the coded data.
|
||||
// writer.mem must be freed by calling WebPMemoryWriterClear.
|
||||
WEBP_EXTERN(int) WebPMemoryWrite(const uint8_t* data, size_t data_size,
|
||||
const WebPPicture* picture);
|
||||
|
||||
@ -212,23 +260,17 @@ WEBP_EXTERN(int) WebPMemoryWrite(const uint8_t* data, size_t data_size,
|
||||
// everything is OK.
|
||||
typedef int (*WebPProgressHook)(int percent, const WebPPicture* picture);
|
||||
|
||||
typedef enum {
|
||||
// Color spaces.
|
||||
typedef enum WebPEncCSP {
|
||||
// chroma sampling
|
||||
WEBP_YUV420 = 0, // 4:2:0
|
||||
WEBP_YUV422 = 1, // 4:2:2
|
||||
WEBP_YUV444 = 2, // 4:4:4
|
||||
WEBP_YUV400 = 3, // grayscale
|
||||
WEBP_CSP_UV_MASK = 3, // bit-mask to get the UV sampling factors
|
||||
// alpha channel variants
|
||||
WEBP_YUV420A = 4,
|
||||
WEBP_YUV422A = 5,
|
||||
WEBP_YUV444A = 6,
|
||||
WEBP_YUV400A = 7, // grayscale + alpha
|
||||
WEBP_YUV420 = 0, // 4:2:0
|
||||
WEBP_YUV420A = 4, // alpha channel variant
|
||||
WEBP_CSP_UV_MASK = 3, // bit-mask to get the UV sampling factors
|
||||
WEBP_CSP_ALPHA_BIT = 4 // bit that is set if alpha is present
|
||||
} WebPEncCSP;
|
||||
|
||||
// Encoding error conditions.
|
||||
typedef enum {
|
||||
typedef enum WebPEncodingError {
|
||||
VP8_ENC_OK = 0,
|
||||
VP8_ENC_ERROR_OUT_OF_MEMORY, // memory error allocating objects
|
||||
VP8_ENC_ERROR_BITSTREAM_OUT_OF_MEMORY, // memory error while flushing bits
|
||||
@ -248,7 +290,6 @@ typedef enum {
|
||||
|
||||
// Main exchange structure (input samples, output bytes, statistics)
|
||||
struct WebPPicture {
|
||||
|
||||
// INPUT
|
||||
//////////////
|
||||
// Main flag for encoder selecting between ARGB or YUV input.
|
||||
@ -303,17 +344,15 @@ struct WebPPicture {
|
||||
|
||||
uint32_t pad3[3]; // padding for later use
|
||||
|
||||
// Unused for now: original samples (for non-YUV420 modes)
|
||||
uint8_t *u0, *v0;
|
||||
int uv0_stride;
|
||||
|
||||
uint32_t pad4[7]; // padding for later use
|
||||
// Unused for now
|
||||
uint8_t *pad4, *pad5;
|
||||
uint32_t pad6[8]; // padding for later use
|
||||
|
||||
// PRIVATE FIELDS
|
||||
////////////////////
|
||||
void* memory_; // row chunk of memory for yuva planes
|
||||
void* memory_argb_; // and for argb too.
|
||||
void* pad5[2]; // padding for later use
|
||||
void* pad7[2]; // padding for later use
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
@ -343,18 +382,19 @@ WEBP_EXTERN(int) WebPPictureAlloc(WebPPicture* picture);
|
||||
// preserved.
|
||||
WEBP_EXTERN(void) WebPPictureFree(WebPPicture* picture);
|
||||
|
||||
// Copy the pixels of *src into *dst, using WebPPictureAlloc. Upon return,
|
||||
// *dst will fully own the copied pixels (this is not a view).
|
||||
// Copy the pixels of *src into *dst, using WebPPictureAlloc. Upon return, *dst
|
||||
// will fully own the copied pixels (this is not a view). The 'dst' picture need
|
||||
// not be initialized as its content is overwritten.
|
||||
// Returns false in case of memory allocation error.
|
||||
WEBP_EXTERN(int) WebPPictureCopy(const WebPPicture* src, WebPPicture* dst);
|
||||
|
||||
// Compute PSNR or SSIM distortion between two pictures.
|
||||
// Result is in dB, stores in result[] in the Y/U/V/Alpha/All order.
|
||||
// Returns false in case of error (pic1 and pic2 don't have same dimension, ...)
|
||||
// Compute PSNR, SSIM or LSIM distortion metric between two pictures. Results
|
||||
// are in dB, stored in result[] in the Y/U/V/Alpha/All or B/G/R/A/All order.
|
||||
// Returns false in case of error (src and ref don't have same dimension, ...)
|
||||
// Warning: this function is rather CPU-intensive.
|
||||
WEBP_EXTERN(int) WebPPictureDistortion(
|
||||
const WebPPicture* pic1, const WebPPicture* pic2,
|
||||
int metric_type, // 0 = PSNR, 1 = SSIM
|
||||
const WebPPicture* src, const WebPPicture* ref,
|
||||
int metric_type, // 0 = PSNR, 1 = SSIM, 2 = LSIM
|
||||
float result[5]);
|
||||
|
||||
// self-crops a picture to the rectangle defined by top/left/width/height.
|
||||
@ -375,7 +415,9 @@ WEBP_EXTERN(int) WebPPictureCrop(WebPPicture* picture,
|
||||
// the top and left coordinates will be snapped to even values.
|
||||
// Picture 'src' must out-live 'dst' picture. Self-extraction of view is allowed
|
||||
// ('src' equal to 'dst') as a mean of fast-cropping (but note that doing so,
|
||||
// the original dimension will be lost).
|
||||
// the original dimension will be lost). Picture 'dst' need not be initialized
|
||||
// with WebPPictureInit() if it is different from 'src', since its content will
|
||||
// be overwritten.
|
||||
// Returns false in case of memory allocation error or invalid parameters.
|
||||
WEBP_EXTERN(int) WebPPictureView(const WebPPicture* src,
|
||||
int left, int top, int width, int height,
|
||||
@ -386,7 +428,9 @@ WEBP_EXTERN(int) WebPPictureView(const WebPPicture* src,
|
||||
WEBP_EXTERN(int) WebPPictureIsView(const WebPPicture* picture);
|
||||
|
||||
// Rescale a picture to new dimension width x height.
|
||||
// Now gamma correction is applied.
|
||||
// If either 'width' or 'height' (but not both) is 0 the corresponding
|
||||
// dimension will be calculated preserving the aspect ratio.
|
||||
// No gamma correction is applied.
|
||||
// Returns false in case of error (invalid parameter or insufficient memory).
|
||||
WEBP_EXTERN(int) WebPPictureRescale(WebPPicture* pic, int width, int height);
|
||||
|
||||
@ -413,13 +457,28 @@ WEBP_EXTERN(int) WebPPictureImportBGRA(
|
||||
WEBP_EXTERN(int) WebPPictureImportBGRX(
|
||||
WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride);
|
||||
|
||||
// Converts picture->argb data to the YUVA format specified by 'colorspace'.
|
||||
// Converts picture->argb data to the YUV420A format. The 'colorspace'
|
||||
// parameter is deprecated and should be equal to WEBP_YUV420.
|
||||
// Upon return, picture->use_argb is set to false. The presence of real
|
||||
// non-opaque transparent values is detected, and 'colorspace' will be
|
||||
// adjusted accordingly. Note that this method is lossy.
|
||||
// Returns false in case of error.
|
||||
WEBP_EXTERN(int) WebPPictureARGBToYUVA(WebPPicture* picture,
|
||||
WebPEncCSP colorspace);
|
||||
WebPEncCSP /*colorspace = WEBP_YUV420*/);
|
||||
|
||||
// Same as WebPPictureARGBToYUVA(), but the conversion is done using
|
||||
// pseudo-random dithering with a strength 'dithering' between
|
||||
// 0.0 (no dithering) and 1.0 (maximum dithering). This is useful
|
||||
// for photographic picture.
|
||||
WEBP_EXTERN(int) WebPPictureARGBToYUVADithered(
|
||||
WebPPicture* picture, WebPEncCSP colorspace, float dithering);
|
||||
|
||||
// Performs 'smart' RGBA->YUVA420 downsampling and colorspace conversion.
|
||||
// Downsampling is handled with extra care in case of color clipping. This
|
||||
// method is roughly 2x slower than WebPPictureARGBToYUVA() but produces better
|
||||
// YUV representation.
|
||||
// Returns false in case of error.
|
||||
WEBP_EXTERN(int) WebPPictureSmartARGBToYUVA(WebPPicture* picture);
|
||||
|
||||
// Converts picture->yuv to picture->argb and sets picture->use_argb to true.
|
||||
// The input format must be YUV_420 or YUV_420A.
|
||||
@ -429,9 +488,9 @@ WEBP_EXTERN(int) WebPPictureARGBToYUVA(WebPPicture* picture,
|
||||
// Returns false in case of error.
|
||||
WEBP_EXTERN(int) WebPPictureYUVAToARGB(WebPPicture* picture);
|
||||
|
||||
// Helper function: given a width x height plane of YUV(A) samples
|
||||
// (with stride 'stride'), clean-up the YUV samples under fully transparent
|
||||
// area, to help compressibility (no guarantee, though).
|
||||
// Helper function: given a width x height plane of RGBA or YUV(A) samples
|
||||
// clean-up the YUV or RGB samples under fully transparent area, to help
|
||||
// compressibility (no guarantee, though).
|
||||
WEBP_EXTERN(void) WebPCleanupTransparentArea(WebPPicture* picture);
|
||||
|
||||
// Scan the picture 'picture' for the presence of non fully opaque alpha values.
|
||||
@ -439,6 +498,11 @@ WEBP_EXTERN(void) WebPCleanupTransparentArea(WebPPicture* picture);
|
||||
// alpha plane can be ignored altogether e.g.).
|
||||
WEBP_EXTERN(int) WebPPictureHasTransparency(const WebPPicture* picture);
|
||||
|
||||
// Remove the transparency information (if present) by blending the color with
|
||||
// the background color 'background_rgb' (specified as 24bit RGB triplet).
|
||||
// After this call, all alpha values are reset to 0xff.
|
||||
WEBP_EXTERN(void) WebPBlendAlpha(WebPPicture* pic, uint32_t background_rgb);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main call
|
||||
|
||||
@ -456,7 +520,7 @@ WEBP_EXTERN(int) WebPEncode(const WebPConfig* config, WebPPicture* picture);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Internal header for constants related to WebP file format.
|
||||
@ -12,6 +14,9 @@
|
||||
#ifndef WEBP_WEBP_FORMAT_CONSTANTS_H_
|
||||
#define WEBP_WEBP_FORMAT_CONSTANTS_H_
|
||||
|
||||
// Create fourcc of the chunk from the chunk tag characters.
|
||||
#define MKFOURCC(a, b, c, d) ((a) | (b) << 8 | (c) << 16 | (uint32_t)(d) << 24)
|
||||
|
||||
// VP8 related constants.
|
||||
#define VP8_SIGNATURE 0x9d012a // Signature in VP8 data.
|
||||
#define VP8_MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition
|
||||
@ -65,23 +70,16 @@ typedef enum {
|
||||
#define CHUNK_SIZE_BYTES 4 // Size needed to store chunk's size.
|
||||
#define CHUNK_HEADER_SIZE 8 // Size of a chunk header.
|
||||
#define RIFF_HEADER_SIZE 12 // Size of the RIFF header ("RIFFnnnnWEBP").
|
||||
#define FRAME_CHUNK_SIZE 15 // Size of a FRM chunk.
|
||||
#define LOOP_CHUNK_SIZE 2 // Size of a LOOP chunk.
|
||||
#define TILE_CHUNK_SIZE 6 // Size of a TILE chunk.
|
||||
#define ANMF_CHUNK_SIZE 16 // Size of an ANMF chunk.
|
||||
#define ANIM_CHUNK_SIZE 6 // Size of an ANIM chunk.
|
||||
#define FRGM_CHUNK_SIZE 6 // Size of a FRGM chunk.
|
||||
#define VP8X_CHUNK_SIZE 10 // Size of a VP8X chunk.
|
||||
|
||||
#define TILING_FLAG_BIT 0x01 // Set if tiles are possibly used.
|
||||
#define ANIMATION_FLAG_BIT 0x02 // Set if some animation is expected
|
||||
#define ICC_FLAG_BIT 0x04 // Whether ICC is present or not.
|
||||
#define METADATA_FLAG_BIT 0x08 // Set if some META chunk is possibly present.
|
||||
#define ALPHA_FLAG_BIT 0x10 // Should be same as the ALPHA_FLAG in mux.h
|
||||
#define ROTATION_FLAG_BITS 0xe0 // all 3 bits for rotation + symmetry
|
||||
|
||||
#define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height.
|
||||
#define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height.
|
||||
#define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count
|
||||
#define MAX_DURATION (1 << 24) // maximum duration
|
||||
#define MAX_POSITION_OFFSET (1 << 24) // maximum frame/tile x/y offset
|
||||
#define MAX_CANVAS_SIZE (1 << 24) // 24-bit max for VP8X width/height.
|
||||
#define MAX_IMAGE_AREA (1ULL << 32) // 32-bit max for width x height.
|
||||
#define MAX_LOOP_COUNT (1 << 16) // maximum value for loop-count
|
||||
#define MAX_DURATION (1 << 24) // maximum duration
|
||||
#define MAX_POSITION_OFFSET (1 << 24) // maximum frame/fragment x/y offset
|
||||
|
||||
// Maximum chunk payload is such that adding the header and padding won't
|
||||
// overflow a uint32_t.
|
||||
|
||||
@ -1,60 +1,76 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// RIFF container manipulation for WEBP images.
|
||||
// RIFF container manipulation and encoding for WebP images.
|
||||
//
|
||||
// Authors: Urvang (urvang@google.com)
|
||||
// Vikas (vikasa@google.com)
|
||||
|
||||
// This API allows manipulation of WebP container images containing features
|
||||
// like Color profile, XMP metadata, Animation and Tiling.
|
||||
//
|
||||
// Code Example#1: Creating a MUX with image data, color profile and XMP
|
||||
// metadata.
|
||||
//
|
||||
// int copy_data = 0;
|
||||
// WebPMux* mux = WebPMuxNew();
|
||||
// // ... (Prepare image data).
|
||||
// WebPMuxSetImage(mux, &image, copy_data);
|
||||
// // ... (Prepare ICCP color profile data).
|
||||
// WebPMuxSetColorProfile(mux, &icc_profile, copy_data);
|
||||
// // ... (Prepare XMP metadata).
|
||||
// WebPMuxSetMetadata(mux, &xmp, copy_data);
|
||||
// // Get data from mux in WebP RIFF format.
|
||||
// WebPMuxAssemble(mux, &output_data);
|
||||
// WebPMuxDelete(mux);
|
||||
// // ... (Consume output_data; e.g. write output_data.bytes_ to file).
|
||||
// WebPDataClear(&output_data);
|
||||
//
|
||||
// Code Example#2: Get image and color profile data from a WebP file.
|
||||
//
|
||||
// int copy_data = 0;
|
||||
// // ... (Read data from file).
|
||||
// WebPMux* mux = WebPMuxCreate(&data, copy_data);
|
||||
// WebPMuxGetImage(mux, &image);
|
||||
// // ... (Consume image; e.g. call WebPDecode() to decode the data).
|
||||
// WebPMuxGetColorProfile(mux, &icc_profile);
|
||||
// // ... (Consume icc_data).
|
||||
// WebPMuxDelete(mux);
|
||||
// free(data);
|
||||
|
||||
#ifndef WEBP_WEBP_MUX_H_
|
||||
#define WEBP_WEBP_MUX_H_
|
||||
|
||||
#include "./types.h"
|
||||
#include "./mux_types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WEBP_MUX_ABI_VERSION 0x0100 // MAJOR(8b) + MINOR(8b)
|
||||
#define WEBP_MUX_ABI_VERSION 0x0106 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Mux API
|
||||
//
|
||||
// This API allows manipulation of WebP container images containing features
|
||||
// like color profile, metadata, animation and fragmented images.
|
||||
//
|
||||
// Code Example#1: Create a WebPMux object with image data, color profile and
|
||||
// XMP metadata.
|
||||
/*
|
||||
int copy_data = 0;
|
||||
WebPMux* mux = WebPMuxNew();
|
||||
// ... (Prepare image data).
|
||||
WebPMuxSetImage(mux, &image, copy_data);
|
||||
// ... (Prepare ICCP color profile data).
|
||||
WebPMuxSetChunk(mux, "ICCP", &icc_profile, copy_data);
|
||||
// ... (Prepare XMP metadata).
|
||||
WebPMuxSetChunk(mux, "XMP ", &xmp, copy_data);
|
||||
// Get data from mux in WebP RIFF format.
|
||||
WebPMuxAssemble(mux, &output_data);
|
||||
WebPMuxDelete(mux);
|
||||
// ... (Consume output_data; e.g. write output_data.bytes to file).
|
||||
WebPDataClear(&output_data);
|
||||
*/
|
||||
|
||||
// Code Example#2: Get image and color profile data from a WebP file.
|
||||
/*
|
||||
int copy_data = 0;
|
||||
// ... (Read data from file).
|
||||
WebPMux* mux = WebPMuxCreate(&data, copy_data);
|
||||
WebPMuxGetFrame(mux, 1, &image);
|
||||
// ... (Consume image; e.g. call WebPDecode() to decode the data).
|
||||
WebPMuxGetChunk(mux, "ICCP", &icc_profile);
|
||||
// ... (Consume icc_data).
|
||||
WebPMuxDelete(mux);
|
||||
free(data);
|
||||
*/
|
||||
|
||||
// Note: forward declaring enumerations is not allowed in (strict) C and C++,
|
||||
// the types are left here for reference.
|
||||
// typedef enum WebPMuxError WebPMuxError;
|
||||
// typedef enum WebPChunkId WebPChunkId;
|
||||
typedef struct WebPMux WebPMux; // main opaque object.
|
||||
typedef struct WebPMuxFrameInfo WebPMuxFrameInfo;
|
||||
typedef struct WebPMuxAnimParams WebPMuxAnimParams;
|
||||
typedef struct WebPAnimEncoderOptions WebPAnimEncoderOptions;
|
||||
|
||||
// Error codes
|
||||
typedef enum {
|
||||
typedef enum WebPMuxError {
|
||||
WEBP_MUX_OK = 1,
|
||||
WEBP_MUX_NOT_FOUND = 0,
|
||||
WEBP_MUX_INVALID_ARGUMENT = -1,
|
||||
@ -63,51 +79,26 @@ typedef enum {
|
||||
WEBP_MUX_NOT_ENOUGH_DATA = -4
|
||||
} WebPMuxError;
|
||||
|
||||
// Flag values for different features used in VP8X chunk.
|
||||
typedef enum {
|
||||
TILE_FLAG = 0x00000001,
|
||||
ANIMATION_FLAG = 0x00000002,
|
||||
ICCP_FLAG = 0x00000004,
|
||||
META_FLAG = 0x00000008,
|
||||
ALPHA_FLAG = 0x00000010
|
||||
} WebPFeatureFlags;
|
||||
|
||||
// IDs for different types of chunks.
|
||||
typedef enum {
|
||||
typedef enum WebPChunkId {
|
||||
WEBP_CHUNK_VP8X, // VP8X
|
||||
WEBP_CHUNK_ICCP, // ICCP
|
||||
WEBP_CHUNK_LOOP, // LOOP
|
||||
WEBP_CHUNK_FRAME, // FRM
|
||||
WEBP_CHUNK_TILE, // TILE
|
||||
WEBP_CHUNK_ANIM, // ANIM
|
||||
WEBP_CHUNK_ANMF, // ANMF
|
||||
WEBP_CHUNK_FRGM, // FRGM
|
||||
WEBP_CHUNK_ALPHA, // ALPH
|
||||
WEBP_CHUNK_IMAGE, // VP8/VP8L
|
||||
WEBP_CHUNK_META, // META
|
||||
WEBP_CHUNK_EXIF, // EXIF
|
||||
WEBP_CHUNK_XMP, // XMP
|
||||
WEBP_CHUNK_UNKNOWN, // Other chunks.
|
||||
WEBP_CHUNK_NIL
|
||||
} WebPChunkId;
|
||||
|
||||
typedef struct WebPMux WebPMux; // main opaque object.
|
||||
|
||||
// Data type used to describe 'raw' data, e.g., chunk data
|
||||
// (ICC profile, metadata) and WebP compressed image data.
|
||||
typedef struct {
|
||||
const uint8_t* bytes_;
|
||||
size_t size_;
|
||||
} WebPData;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Manipulation of a WebPData object.
|
||||
|
||||
// Initializes the contents of the 'webp_data' object with default values.
|
||||
WEBP_EXTERN(void) WebPDataInit(WebPData* webp_data);
|
||||
|
||||
// Clears the contents of the 'webp_data' object by calling free(). Does not
|
||||
// deallocate the object itself.
|
||||
WEBP_EXTERN(void) WebPDataClear(WebPData* webp_data);
|
||||
|
||||
// Allocates necessary storage for 'dst' and copies the contents of 'src'.
|
||||
// Returns true on success.
|
||||
WEBP_EXTERN(int) WebPDataCopy(const WebPData* src, WebPData* dst);
|
||||
// Returns the version number of the mux library, packed in hexadecimal using
|
||||
// 8bits for each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||
WEBP_EXTERN(int) WebPGetMuxVersion(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Life of a Mux object
|
||||
@ -118,6 +109,7 @@ WEBP_EXTERN(WebPMux*) WebPNewInternal(int);
|
||||
// Creates an empty mux object.
|
||||
// Returns:
|
||||
// A pointer to the newly created empty mux object.
|
||||
// Or NULL in case of memory error.
|
||||
static WEBP_INLINE WebPMux* WebPMuxNew(void) {
|
||||
return WebPNewInternal(WEBP_MUX_ABI_VERSION);
|
||||
}
|
||||
@ -136,8 +128,8 @@ WEBP_EXTERN(WebPMux*) WebPMuxCreateInternal(const WebPData*, int, int);
|
||||
// Creates a mux object from raw data given in WebP RIFF format.
|
||||
// Parameters:
|
||||
// bitstream - (in) the bitstream data in WebP RIFF format
|
||||
// copy_data - (in) value 1 indicates given data WILL copied to the mux, and
|
||||
// value 0 indicates data will NOT be copied.
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// A pointer to the mux object created from given data - on success.
|
||||
// NULL - In case of invalid data or memory error.
|
||||
@ -147,295 +139,237 @@ static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream,
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Single Image.
|
||||
// Non-image chunks.
|
||||
|
||||
// Sets the image in the mux object. Any existing images (including frame/tile)
|
||||
// will be removed.
|
||||
// Note: Only non-image related chunks should be managed through chunk APIs.
|
||||
// (Image related chunks are: "ANMF", "FRGM", "VP8 ", "VP8L" and "ALPH").
|
||||
// To add, get and delete images, use WebPMuxSetImage(), WebPMuxPushFrame(),
|
||||
// WebPMuxGetFrame() and WebPMuxDeleteFrame().
|
||||
|
||||
// Adds a chunk with id 'fourcc' and data 'chunk_data' in the mux object.
|
||||
// Any existing chunk(s) with the same id will be removed.
|
||||
// Parameters:
|
||||
// mux - (in/out) object to which the chunk is to be added
|
||||
// fourcc - (in) a character array containing the fourcc of the given chunk;
|
||||
// e.g., "ICCP", "XMP ", "EXIF" etc.
|
||||
// chunk_data - (in) the chunk data to be added
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
|
||||
// or if fourcc corresponds to an image chunk.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetChunk(
|
||||
WebPMux* mux, const char fourcc[4], const WebPData* chunk_data,
|
||||
int copy_data);
|
||||
|
||||
// Gets a reference to the data of the chunk with id 'fourcc' in the mux object.
|
||||
// The caller should NOT free the returned data.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the chunk data is to be fetched
|
||||
// fourcc - (in) a character array containing the fourcc of the chunk;
|
||||
// e.g., "ICCP", "XMP ", "EXIF" etc.
|
||||
// chunk_data - (out) returned chunk data
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux, fourcc or chunk_data is NULL
|
||||
// or if fourcc corresponds to an image chunk.
|
||||
// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given id.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetChunk(
|
||||
const WebPMux* mux, const char fourcc[4], WebPData* chunk_data);
|
||||
|
||||
// Deletes the chunk with the given 'fourcc' from the mux object.
|
||||
// Parameters:
|
||||
// mux - (in/out) object from which the chunk is to be deleted
|
||||
// fourcc - (in) a character array containing the fourcc of the chunk;
|
||||
// e.g., "ICCP", "XMP ", "EXIF" etc.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or fourcc is NULL
|
||||
// or if fourcc corresponds to an image chunk.
|
||||
// WEBP_MUX_NOT_FOUND - If mux does not contain a chunk with the given fourcc.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxDeleteChunk(
|
||||
WebPMux* mux, const char fourcc[4]);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Images.
|
||||
|
||||
// Encapsulates data about a single frame/fragment.
|
||||
struct WebPMuxFrameInfo {
|
||||
WebPData bitstream; // image data: can be a raw VP8/VP8L bitstream
|
||||
// or a single-image WebP file.
|
||||
int x_offset; // x-offset of the frame.
|
||||
int y_offset; // y-offset of the frame.
|
||||
int duration; // duration of the frame (in milliseconds).
|
||||
|
||||
WebPChunkId id; // frame type: should be one of WEBP_CHUNK_ANMF,
|
||||
// WEBP_CHUNK_FRGM or WEBP_CHUNK_IMAGE
|
||||
WebPMuxAnimDispose dispose_method; // Disposal method for the frame.
|
||||
WebPMuxAnimBlend blend_method; // Blend operation for the frame.
|
||||
uint32_t pad[1]; // padding for later use
|
||||
};
|
||||
|
||||
// Sets the (non-animated and non-fragmented) image in the mux object.
|
||||
// Note: Any existing images (including frames/fragments) will be removed.
|
||||
// Parameters:
|
||||
// mux - (in/out) object in which the image is to be set
|
||||
// bitstream - (in) can either be a raw VP8/VP8L bitstream or a single-image
|
||||
// WebP file (non-animated and non-tiled)
|
||||
// copy_data - (in) value 1 indicates given data WILL copied to the mux, and
|
||||
// value 0 indicates data will NOT be copied.
|
||||
// bitstream - (in) can be a raw VP8/VP8L bitstream or a single-image
|
||||
// WebP file (non-animated and non-fragmented)
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetImage(WebPMux* mux,
|
||||
const WebPData* bitstream,
|
||||
int copy_data);
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetImage(
|
||||
WebPMux* mux, const WebPData* bitstream, int copy_data);
|
||||
|
||||
// Gets image data from the mux object.
|
||||
// The content of 'bitstream' is allocated using malloc(), and NOT
|
||||
// owned by the 'mux' object. It MUST be deallocated by the caller by calling
|
||||
// WebPDataClear().
|
||||
// Adds a frame at the end of the mux object.
|
||||
// Notes: (1) frame.id should be one of WEBP_CHUNK_ANMF or WEBP_CHUNK_FRGM
|
||||
// (2) For setting a non-animated non-fragmented image, use
|
||||
// WebPMuxSetImage() instead.
|
||||
// (3) Type of frame being pushed must be same as the frames in mux.
|
||||
// (4) As WebP only supports even offsets, any odd offset will be snapped
|
||||
// to an even location using: offset &= ~1
|
||||
// Parameters:
|
||||
// mux - (in) object from which the image is to be fetched
|
||||
// bitstream - (out) the image data
|
||||
// mux - (in/out) object to which the frame is to be added
|
||||
// frame - (in) frame data.
|
||||
// copy_data - (in) value 1 indicates given data WILL be copied to the mux
|
||||
// object and value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if either mux or bitstream is NULL
|
||||
// OR mux contains animation/tiling.
|
||||
// WEBP_MUX_NOT_FOUND - if image is not present in mux object.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetImage(const WebPMux* mux,
|
||||
WebPData* bitstream);
|
||||
|
||||
// Deletes the image in the mux object.
|
||||
// Parameters:
|
||||
// mux - (in/out) object from which the image is to be deleted
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL
|
||||
// OR if mux contains animation/tiling.
|
||||
// WEBP_MUX_NOT_FOUND - if image is not present in mux object.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxDeleteImage(WebPMux* mux);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// XMP Metadata.
|
||||
|
||||
// Sets the XMP metadata in the mux object. Any existing metadata chunk(s) will
|
||||
// be removed.
|
||||
// Parameters:
|
||||
// mux - (in/out) object to which the XMP metadata is to be added
|
||||
// metadata - (in) the XMP metadata data to be added
|
||||
// copy_data - (in) value 1 indicates given data WILL copied to the mux, and
|
||||
// value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or metadata is NULL.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetMetadata(WebPMux* mux,
|
||||
const WebPData* metadata,
|
||||
int copy_data);
|
||||
|
||||
// Gets a reference to the XMP metadata in the mux object.
|
||||
// The caller should NOT free the returned data.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the XMP metadata is to be fetched
|
||||
// metadata - (out) XMP metadata
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if either mux or metadata is NULL.
|
||||
// WEBP_MUX_NOT_FOUND - if metadata is not present in mux object.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetMetadata(const WebPMux* mux,
|
||||
WebPData* metadata);
|
||||
|
||||
// Deletes the XMP metadata in the mux object.
|
||||
// Parameters:
|
||||
// mux - (in/out) object from which XMP metadata is to be deleted
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL
|
||||
// WEBP_MUX_NOT_FOUND - If mux does not contain metadata.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxDeleteMetadata(WebPMux* mux);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// ICC Color Profile.
|
||||
|
||||
// Sets the color profile in the mux object. Any existing color profile chunk(s)
|
||||
// will be removed.
|
||||
// Parameters:
|
||||
// mux - (in/out) object to which the color profile is to be added
|
||||
// color_profile - (in) the color profile data to be added
|
||||
// copy_data - (in) value 1 indicates given data WILL copied to the mux, and
|
||||
// value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or color_profile is NULL
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error
|
||||
// WEBP_MUX_OK - on success
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetColorProfile(WebPMux* mux,
|
||||
const WebPData* color_profile,
|
||||
int copy_data);
|
||||
|
||||
// Gets a reference to the color profile in the mux object.
|
||||
// The caller should NOT free the returned data.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the color profile data is to be fetched
|
||||
// color_profile - (out) color profile data
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if either mux or color_profile is NULL.
|
||||
// WEBP_MUX_NOT_FOUND - if color profile is not present in mux object.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetColorProfile(const WebPMux* mux,
|
||||
WebPData* color_profile);
|
||||
|
||||
// Deletes the color profile in the mux object.
|
||||
// Parameters:
|
||||
// mux - (in/out) object from which color profile is to be deleted
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL
|
||||
// WEBP_MUX_NOT_FOUND - If mux does not contain color profile.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxDeleteColorProfile(WebPMux* mux);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Animation.
|
||||
|
||||
// Adds an animation frame at the end of the mux object.
|
||||
// Note: as WebP only supports even offsets, any odd offset will be snapped to
|
||||
// an even location using: offset &= ~1
|
||||
// Parameters:
|
||||
// mux - (in/out) object to which an animation frame is to be added
|
||||
// bitstream - (in) the image data corresponding to the frame. It can either
|
||||
// be a raw VP8/VP8L bitstream or a single-image WebP file
|
||||
// (non-animated and non-tiled)
|
||||
// x_offset - (in) x-offset of the frame to be added
|
||||
// y_offset - (in) y-offset of the frame to be added
|
||||
// duration - (in) duration of the frame to be added (in milliseconds)
|
||||
// copy_data - (in) value 1 indicates given data WILL copied to the mux, and
|
||||
// value 0 indicates data will NOT be copied.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL
|
||||
// or if content of 'frame' is invalid.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxPushFrame(
|
||||
WebPMux* mux, const WebPData* bitstream,
|
||||
int x_offset, int y_offset, int duration, int copy_data);
|
||||
WebPMux* mux, const WebPMuxFrameInfo* frame, int copy_data);
|
||||
|
||||
// TODO(urvang): Create a struct as follows to reduce argument list size:
|
||||
// typedef struct {
|
||||
// WebPData bitstream;
|
||||
// int x_offset, y_offset;
|
||||
// int duration;
|
||||
// } FrameInfo;
|
||||
|
||||
// Gets the nth animation frame from the mux object.
|
||||
// The content of 'bitstream' is allocated using malloc(), and NOT
|
||||
// Gets the nth frame from the mux object.
|
||||
// The content of 'frame->bitstream' is allocated using malloc(), and NOT
|
||||
// owned by the 'mux' object. It MUST be deallocated by the caller by calling
|
||||
// WebPDataClear().
|
||||
// nth=0 has a special meaning - last position.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the info is to be fetched
|
||||
// nth - (in) index of the frame in the mux object
|
||||
// bitstream - (out) the image data
|
||||
// x_offset - (out) x-offset of the returned frame
|
||||
// y_offset - (out) y-offset of the returned frame
|
||||
// duration - (out) duration of the returned frame (in milliseconds)
|
||||
// frame - (out) data of the returned frame
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if either mux, bitstream, x_offset,
|
||||
// y_offset, or duration is NULL
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or frame is NULL.
|
||||
// WEBP_MUX_NOT_FOUND - if there are less than nth frames in the mux object.
|
||||
// WEBP_MUX_BAD_DATA - if nth frame chunk in mux is invalid.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetFrame(
|
||||
const WebPMux* mux, uint32_t nth, WebPData* bitstream,
|
||||
int* x_offset, int* y_offset, int* duration);
|
||||
const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame);
|
||||
|
||||
// Deletes an animation frame from the mux object.
|
||||
// Deletes a frame from the mux object.
|
||||
// nth=0 has a special meaning - last position.
|
||||
// Parameters:
|
||||
// mux - (in/out) object from which a frame is to be deleted
|
||||
// nth - (in) The position from which the frame is to be deleted
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL.
|
||||
// WEBP_MUX_NOT_FOUND - If there are less than nth frames in the mux object
|
||||
// before deletion.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth);
|
||||
|
||||
// Sets the animation loop count in the mux object. Any existing loop count
|
||||
// value(s) will be removed.
|
||||
// Parameters:
|
||||
// mux - (in/out) object in which loop chunk is to be set/added
|
||||
// loop_count - (in) animation loop count value.
|
||||
// Note that loop_count of zero denotes infinite loop.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetLoopCount(WebPMux* mux, int loop_count);
|
||||
|
||||
// Gets the animation loop count from the mux object.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the loop count is to be fetched
|
||||
// loop_count - (out) the loop_count value present in the LOOP chunk
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if either of mux or loop_count is NULL
|
||||
// WEBP_MUX_NOT_FOUND - if loop chunk is not present in mux object.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetLoopCount(const WebPMux* mux,
|
||||
int* loop_count);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Tiling.
|
||||
// Animation.
|
||||
|
||||
// Adds a tile at the end of the mux object.
|
||||
// Note: as WebP only supports even offsets, any odd offset will be snapped to
|
||||
// an even location using: offset &= ~1
|
||||
// Animation parameters.
|
||||
struct WebPMuxAnimParams {
|
||||
uint32_t bgcolor; // Background color of the canvas stored (in MSB order) as:
|
||||
// Bits 00 to 07: Alpha.
|
||||
// Bits 08 to 15: Red.
|
||||
// Bits 16 to 23: Green.
|
||||
// Bits 24 to 31: Blue.
|
||||
int loop_count; // Number of times to repeat the animation [0 = infinite].
|
||||
};
|
||||
|
||||
// Sets the animation parameters in the mux object. Any existing ANIM chunks
|
||||
// will be removed.
|
||||
// Parameters:
|
||||
// mux - (in/out) object to which a tile is to be added.
|
||||
// bitstream - (in) the image data corresponding to the frame. It can either
|
||||
// be a raw VP8/VP8L bitstream or a single-image WebP file
|
||||
// (non-animated and non-tiled)
|
||||
// x_offset - (in) x-offset of the tile to be added
|
||||
// y_offset - (in) y-offset of the tile to be added
|
||||
// copy_data - (in) value 1 indicates given data WILL copied to the mux, and
|
||||
// value 0 indicates data will NOT be copied.
|
||||
// mux - (in/out) object in which ANIM chunk is to be set/added
|
||||
// params - (in) animation parameters.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL or bitstream is NULL
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxPushTile(
|
||||
WebPMux* mux, const WebPData* bitstream,
|
||||
int x_offset, int y_offset, int copy_data);
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetAnimationParams(
|
||||
WebPMux* mux, const WebPMuxAnimParams* params);
|
||||
|
||||
// Gets the nth tile from the mux object.
|
||||
// The content of 'bitstream' is allocated using malloc(), and NOT
|
||||
// owned by the 'mux' object. It MUST be deallocated by the caller by calling
|
||||
// WebPDataClear().
|
||||
// nth=0 has a special meaning - last position.
|
||||
// Gets the animation parameters from the mux object.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the info is to be fetched
|
||||
// nth - (in) index of the tile in the mux object
|
||||
// bitstream - (out) the image data
|
||||
// x_offset - (out) x-offset of the returned tile
|
||||
// y_offset - (out) y-offset of the returned tile
|
||||
// mux - (in) object from which the animation parameters to be fetched
|
||||
// params - (out) animation parameters extracted from the ANIM chunk
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if either mux, bitstream, x_offset or
|
||||
// y_offset is NULL
|
||||
// WEBP_MUX_NOT_FOUND - if there are less than nth tiles in the mux object.
|
||||
// WEBP_MUX_BAD_DATA - if nth tile chunk in mux is invalid.
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or params is NULL.
|
||||
// WEBP_MUX_NOT_FOUND - if ANIM chunk is not present in mux object.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetTile(
|
||||
const WebPMux* mux, uint32_t nth, WebPData* bitstream,
|
||||
int* x_offset, int* y_offset);
|
||||
|
||||
// Deletes a tile from the mux object.
|
||||
// nth=0 has a special meaning - last position
|
||||
// Parameters:
|
||||
// mux - (in/out) object from which a tile is to be deleted
|
||||
// nth - (in) The position from which the tile is to be deleted
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL
|
||||
// WEBP_MUX_NOT_FOUND - If there are less than nth tiles in the mux object
|
||||
// before deletion.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxDeleteTile(WebPMux* mux, uint32_t nth);
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetAnimationParams(
|
||||
const WebPMux* mux, WebPMuxAnimParams* params);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Misc Utilities.
|
||||
|
||||
// Sets the canvas size for the mux object. The width and height can be
|
||||
// specified explicitly or left as zero (0, 0).
|
||||
// * When width and height are specified explicitly, then this frame bound is
|
||||
// enforced during subsequent calls to WebPMuxAssemble() and an error is
|
||||
// reported if any animated frame does not completely fit within the canvas.
|
||||
// * When unspecified (0, 0), the constructed canvas will get the frame bounds
|
||||
// from the bounding-box over all frames after calling WebPMuxAssemble().
|
||||
// Parameters:
|
||||
// mux - (in) object to which the canvas size is to be set
|
||||
// width - (in) canvas width
|
||||
// height - (in) canvas height
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux is NULL; or
|
||||
// width or height are invalid or out of bounds
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxSetCanvasSize(WebPMux* mux,
|
||||
int width, int height);
|
||||
|
||||
// Gets the canvas size from the mux object.
|
||||
// Note: This method assumes that the VP8X chunk, if present, is up-to-date.
|
||||
// That is, the mux object hasn't been modified since the last call to
|
||||
// WebPMuxAssemble() or WebPMuxCreate().
|
||||
// Parameters:
|
||||
// mux - (in) object from which the canvas size is to be fetched
|
||||
// width - (out) canvas width
|
||||
// height - (out) canvas height
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux, width or height is NULL.
|
||||
// WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetCanvasSize(const WebPMux* mux,
|
||||
int* width, int* height);
|
||||
|
||||
// Gets the feature flags from the mux object.
|
||||
// Note: This method assumes that the VP8X chunk, if present, is up-to-date.
|
||||
// That is, the mux object hasn't been modified since the last call to
|
||||
// WebPMuxAssemble() or WebPMuxCreate().
|
||||
// Parameters:
|
||||
// mux - (in) object from which the features are to be fetched
|
||||
// flags - (out) the flags specifying which features are present in the
|
||||
// mux object. This will be an OR of various flag values.
|
||||
// Enum 'WebPFeatureFlags' can be used to test individual flag values.
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or flags is NULL
|
||||
// WEBP_MUX_NOT_FOUND - if VP8X chunk is not present in mux object.
|
||||
// WEBP_MUX_BAD_DATA - if VP8X chunk in mux is invalid.
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or flags is NULL.
|
||||
// WEBP_MUX_BAD_DATA - if VP8X/VP8/VP8L chunk or canvas size is invalid.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxGetFeatures(const WebPMux* mux,
|
||||
uint32_t* flags);
|
||||
|
||||
// Gets number of chunks having tag value tag in the mux object.
|
||||
// Gets number of chunks with the given 'id' in the mux object.
|
||||
// Parameters:
|
||||
// mux - (in) object from which the info is to be fetched
|
||||
// id - (in) chunk id specifying the type of chunk
|
||||
// num_elements - (out) number of chunks with the given chunk id
|
||||
// Returns:
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if either mux, or num_elements is NULL
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux, or num_elements is NULL.
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxNumChunks(const WebPMux* mux,
|
||||
WebPChunkId id, int* num_elements);
|
||||
@ -445,159 +379,151 @@ WEBP_EXTERN(WebPMuxError) WebPMuxNumChunks(const WebPMux* mux,
|
||||
// Note: The content of 'assembled_data' will be ignored and overwritten.
|
||||
// Also, the content of 'assembled_data' is allocated using malloc(), and NOT
|
||||
// owned by the 'mux' object. It MUST be deallocated by the caller by calling
|
||||
// WebPDataClear().
|
||||
// WebPDataClear(). It's always safe to call WebPDataClear() upon return,
|
||||
// even in case of error.
|
||||
// Parameters:
|
||||
// mux - (in/out) object whose chunks are to be assembled
|
||||
// assembled_data - (out) assembled WebP data
|
||||
// Returns:
|
||||
// WEBP_MUX_BAD_DATA - if mux object is invalid.
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if either mux, output_data or output_size is
|
||||
// NULL.
|
||||
// WEBP_MUX_INVALID_ARGUMENT - if mux or assembled_data is NULL.
|
||||
// WEBP_MUX_MEMORY_ERROR - on memory allocation error.
|
||||
// WEBP_MUX_OK - on success
|
||||
// WEBP_MUX_OK - on success.
|
||||
WEBP_EXTERN(WebPMuxError) WebPMuxAssemble(WebPMux* mux,
|
||||
WebPData* assembled_data);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Demux API.
|
||||
// Enables extraction of image and extended format data from WebP files.
|
||||
// WebPAnimEncoder API
|
||||
//
|
||||
// This API allows encoding (possibly) animated WebP images.
|
||||
//
|
||||
// Code Example:
|
||||
/*
|
||||
WebPAnimEncoderOptions enc_options;
|
||||
WebPAnimEncoderOptionsInit(&enc_options);
|
||||
// Tune 'enc_options' as needed.
|
||||
WebPAnimEncoder* enc = WebPAnimEncoderNew(width, height, &enc_options);
|
||||
while(<there are more frames>) {
|
||||
WebPConfig config;
|
||||
WebPConfigInit(&config);
|
||||
// Tune 'config' as needed.
|
||||
WebPAnimEncoderAdd(enc, frame, timestamp_ms, &config);
|
||||
}
|
||||
WebPAnimEncoderAdd(enc, NULL, timestamp_ms, NULL);
|
||||
WebPAnimEncoderAssemble(enc, webp_data);
|
||||
WebPAnimEncoderDelete(enc);
|
||||
// Write the 'webp_data' to a file, or re-mux it further.
|
||||
*/
|
||||
|
||||
#define WEBP_DEMUX_ABI_VERSION 0x0100 // MAJOR(8b) + MINOR(8b)
|
||||
typedef struct WebPAnimEncoder WebPAnimEncoder; // Main opaque object.
|
||||
|
||||
typedef struct WebPDemuxer WebPDemuxer;
|
||||
// Forward declarations. Defined in encode.h.
|
||||
struct WebPPicture;
|
||||
struct WebPConfig;
|
||||
|
||||
typedef enum {
|
||||
WEBP_DEMUX_PARSING_HEADER, // Not enough data to parse full header.
|
||||
WEBP_DEMUX_PARSED_HEADER, // Header parsing complete, data may be available.
|
||||
WEBP_DEMUX_DONE // Entire file has been parsed.
|
||||
} WebPDemuxState;
|
||||
// Global options.
|
||||
struct WebPAnimEncoderOptions {
|
||||
WebPMuxAnimParams anim_params; // Animation parameters.
|
||||
int minimize_size; // If true, minimize the output size (slow). Implicitly
|
||||
// disables key-frame insertion.
|
||||
int kmin;
|
||||
int kmax; // Minimum and maximum distance between consecutive key
|
||||
// frames in the output. The library may insert some key
|
||||
// frames as needed to satisfy this criteria.
|
||||
// Note that these conditions should hold: kmax > kmin
|
||||
// and kmin >= kmax / 2 + 1. Also, if kmin == 0, then
|
||||
// key-frame insertion is disabled; and if kmax == 0,
|
||||
// then all frames will be key-frames.
|
||||
int allow_mixed; // If true, use mixed compression mode; may choose
|
||||
// either lossy and lossless for each frame.
|
||||
int verbose; // If true, print info and warning messages to stderr.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Life of a Demux object
|
||||
uint32_t padding[4]; // Padding for later use.
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(WebPDemuxer*) WebPDemuxInternal(
|
||||
const WebPData*, int, WebPDemuxState*, int);
|
||||
// Internal, version-checked, entry point.
|
||||
WEBP_EXTERN(int) WebPAnimEncoderOptionsInitInternal(
|
||||
WebPAnimEncoderOptions*, int);
|
||||
|
||||
// Parses the WebP file given by 'data'.
|
||||
// A complete WebP file must be present in 'data' for the function to succeed.
|
||||
// Returns a WebPDemuxer object on successful parse, NULL otherwise.
|
||||
static WEBP_INLINE WebPDemuxer* WebPDemux(const WebPData* data) {
|
||||
return WebPDemuxInternal(data, 0, NULL, WEBP_DEMUX_ABI_VERSION);
|
||||
// Should always be called, to initialize a fresh WebPAnimEncoderOptions
|
||||
// structure before modification. Returns false in case of version mismatch.
|
||||
// WebPAnimEncoderOptionsInit() must have succeeded before using the
|
||||
// 'enc_options' object.
|
||||
static WEBP_INLINE int WebPAnimEncoderOptionsInit(
|
||||
WebPAnimEncoderOptions* enc_options) {
|
||||
return WebPAnimEncoderOptionsInitInternal(enc_options, WEBP_MUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Parses the WebP file given by 'data'.
|
||||
// If 'state' is non-NULL it will be set to indicate the status of the demuxer.
|
||||
// Returns a WebPDemuxer object on successful parse, NULL otherwise.
|
||||
static WEBP_INLINE WebPDemuxer* WebPDemuxPartial(
|
||||
const WebPData* data, WebPDemuxState* state) {
|
||||
return WebPDemuxInternal(data, 1, state, WEBP_DEMUX_ABI_VERSION);
|
||||
// Internal, version-checked, entry point.
|
||||
WEBP_EXTERN(WebPAnimEncoder*) WebPAnimEncoderNewInternal(
|
||||
int, int, const WebPAnimEncoderOptions*, int);
|
||||
|
||||
// Creates and initializes a WebPAnimEncoder object.
|
||||
// Parameters:
|
||||
// width/height - (in) canvas width and height of the animation.
|
||||
// enc_options - (in) encoding options; can be passed NULL to pick
|
||||
// reasonable defaults.
|
||||
// Returns:
|
||||
// A pointer to the newly created WebPAnimEncoder object.
|
||||
// Or NULL in case of memory error.
|
||||
static WEBP_INLINE WebPAnimEncoder* WebPAnimEncoderNew(
|
||||
int width, int height, const WebPAnimEncoderOptions* enc_options) {
|
||||
return WebPAnimEncoderNewInternal(width, height, enc_options,
|
||||
WEBP_MUX_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Frees memory associated with 'dmux'.
|
||||
WEBP_EXTERN(void) WebPDemuxDelete(WebPDemuxer* dmux);
|
||||
// Optimize the given frame for WebP, encode it and add it to the
|
||||
// WebPAnimEncoder object.
|
||||
// The last call to 'WebPAnimEncoderAdd' should be with frame = NULL, which
|
||||
// indicates that no more frames are to be added. This call is also used to
|
||||
// determine the duration of the last frame.
|
||||
// Parameters:
|
||||
// enc - (in/out) object to which the frame is to be added.
|
||||
// frame - (in/out) frame data in ARGB or YUV(A) format. If it is in YUV(A)
|
||||
// format, it will be converted to ARGB, which incurs a small loss.
|
||||
// timestamp_ms - (in) timestamp of this frame in milliseconds.
|
||||
// Duration of a frame would be calculated as
|
||||
// "timestamp of next frame - timestamp of this frame".
|
||||
// Hence, timestamps should be in non-decreasing order.
|
||||
// config - (in) encoding options; can be passed NULL to pick
|
||||
// reasonable defaults.
|
||||
// Returns:
|
||||
// On error, returns false and frame->error_code is set appropriately.
|
||||
// Otherwise, returns true.
|
||||
WEBP_EXTERN(int) WebPAnimEncoderAdd(
|
||||
WebPAnimEncoder* enc, struct WebPPicture* frame, int timestamp_ms,
|
||||
const struct WebPConfig* config);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Data/information extraction.
|
||||
// Assemble all frames added so far into a WebP bitstream.
|
||||
// This call should be preceded by a call to 'WebPAnimEncoderAdd' with
|
||||
// frame = NULL; if not, the duration of the last frame will be internally
|
||||
// estimated.
|
||||
// Parameters:
|
||||
// enc - (in/out) object from which the frames are to be assembled.
|
||||
// webp_data - (out) generated WebP bitstream.
|
||||
// Returns:
|
||||
// True on success.
|
||||
WEBP_EXTERN(int) WebPAnimEncoderAssemble(WebPAnimEncoder* enc,
|
||||
WebPData* webp_data);
|
||||
|
||||
typedef enum {
|
||||
WEBP_FF_FORMAT_FLAGS, // Extended format flags present in the 'VP8X' chunk.
|
||||
WEBP_FF_CANVAS_WIDTH,
|
||||
WEBP_FF_CANVAS_HEIGHT,
|
||||
WEBP_FF_LOOP_COUNT
|
||||
} WebPFormatFeature;
|
||||
// Get error string corresponding to the most recent call using 'enc'. The
|
||||
// returned string is owned by 'enc' and is valid only until the next call to
|
||||
// WebPAnimEncoderAdd() or WebPAnimEncoderAssemble() or WebPAnimEncoderDelete().
|
||||
// Parameters:
|
||||
// enc - (in/out) object from which the error string is to be fetched.
|
||||
// Returns:
|
||||
// NULL if 'enc' is NULL. Otherwise, returns the error string if the last call
|
||||
// to 'enc' had an error, or an empty string if the last call was a success.
|
||||
WEBP_EXTERN(const char*) WebPAnimEncoderGetError(WebPAnimEncoder* enc);
|
||||
|
||||
// Get the 'feature' value from the 'dmux'.
|
||||
// NOTE: values are only valid if WebPDemux() was used or WebPDemuxPartial()
|
||||
// returned a state > WEBP_DEMUX_PARSING_HEADER.
|
||||
WEBP_EXTERN(uint32_t) WebPDemuxGetI(
|
||||
const WebPDemuxer* dmux, WebPFormatFeature feature);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Frame iteration.
|
||||
|
||||
typedef struct {
|
||||
int frame_num_;
|
||||
int num_frames_;
|
||||
int tile_num_;
|
||||
int num_tiles_;
|
||||
int x_offset_, y_offset_; // offset relative to the canvas.
|
||||
int width_, height_; // dimensions of this frame or tile.
|
||||
int duration_; // display duration in milliseconds.
|
||||
int complete_; // true if 'tile_' contains a full frame. partial images may
|
||||
// still be decoded with the WebP incremental decoder.
|
||||
WebPData tile_; // The frame or tile given by 'frame_num_' and 'tile_num_'.
|
||||
|
||||
uint32_t pad[4]; // padding for later use
|
||||
void* private_;
|
||||
} WebPIterator;
|
||||
|
||||
// Retrieves frame 'frame_number' from 'dmux'.
|
||||
// 'iter->tile_' points to the first tile on return from this function.
|
||||
// Individual tiles may be extracted using WebPDemuxSetTile().
|
||||
// Setting 'frame_number' equal to 0 will return the last frame of the image.
|
||||
// Returns false if 'dmux' is NULL or frame 'frame_number' is not present.
|
||||
// Call WebPDemuxReleaseIterator() when use of the iterator is complete.
|
||||
// NOTE: 'dmux' must persist for the lifetime of 'iter'.
|
||||
WEBP_EXTERN(int) WebPDemuxGetFrame(
|
||||
const WebPDemuxer* dmux, int frame_number, WebPIterator* iter);
|
||||
|
||||
// Sets 'iter->tile_' to point to the next ('iter->frame_num_' + 1) or previous
|
||||
// ('iter->frame_num_' - 1) frame. These functions do not loop.
|
||||
// Returns true on success, false otherwise.
|
||||
WEBP_EXTERN(int) WebPDemuxNextFrame(WebPIterator* iter);
|
||||
WEBP_EXTERN(int) WebPDemuxPrevFrame(WebPIterator* iter);
|
||||
|
||||
// Sets 'iter->tile_' to reflect tile number 'tile_number'.
|
||||
// Returns true if tile 'tile_number' is present, false otherwise.
|
||||
WEBP_EXTERN(int) WebPDemuxSelectTile(WebPIterator* iter, int tile_number);
|
||||
|
||||
// Releases any memory associated with 'iter'.
|
||||
// Must be called before destroying the associated WebPDemuxer with
|
||||
// WebPDemuxDelete().
|
||||
WEBP_EXTERN(void) WebPDemuxReleaseIterator(WebPIterator* iter);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Chunk iteration.
|
||||
|
||||
typedef struct {
|
||||
// The current and total number of chunks with the fourcc given to
|
||||
// WebPDemuxGetChunk().
|
||||
int chunk_num_;
|
||||
int num_chunks_;
|
||||
WebPData chunk_; // The payload of the chunk.
|
||||
|
||||
uint32_t pad[6]; // padding for later use
|
||||
void* private_;
|
||||
} WebPChunkIterator;
|
||||
|
||||
// Retrieves the 'chunk_number' instance of the chunk with id 'fourcc' from
|
||||
// 'dmux'.
|
||||
// 'fourcc' is a character array containing the fourcc of the chunk to return,
|
||||
// e.g., "ICCP", "META", "EXIF", etc.
|
||||
// Setting 'chunk_number' equal to 0 will return the last chunk in a set.
|
||||
// Returns true if the chunk is found, false otherwise. Image related chunk
|
||||
// payloads are accessed through WebPDemuxGetFrame() and related functions.
|
||||
// Call WebPDemuxReleaseChunkIterator() when use of the iterator is complete.
|
||||
// NOTE: 'dmux' must persist for the lifetime of the iterator.
|
||||
WEBP_EXTERN(int) WebPDemuxGetChunk(const WebPDemuxer* dmux,
|
||||
const char fourcc[4], int chunk_number,
|
||||
WebPChunkIterator* iter);
|
||||
|
||||
// Sets 'iter->chunk_' to point to the next ('iter->chunk_num_' + 1) or previous
|
||||
// ('iter->chunk_num_' - 1) chunk. These functions do not loop.
|
||||
// Returns true on success, false otherwise.
|
||||
WEBP_EXTERN(int) WebPDemuxNextChunk(WebPChunkIterator* iter);
|
||||
WEBP_EXTERN(int) WebPDemuxPrevChunk(WebPChunkIterator* iter);
|
||||
|
||||
// Releases any memory associated with 'iter'.
|
||||
// Must be called before destroying the associated WebPDemuxer with
|
||||
// WebPDemuxDelete().
|
||||
WEBP_EXTERN(void) WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter);
|
||||
// Deletes the WebPAnimEncoder object.
|
||||
// Parameters:
|
||||
// enc - (in/out) object to be deleted
|
||||
WEBP_EXTERN(void) WebPAnimEncoderDelete(WebPAnimEncoder* enc);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Internal header for mux library.
|
||||
@ -15,34 +17,41 @@
|
||||
#include <stdlib.h>
|
||||
#include "../dec/vp8i.h"
|
||||
#include "../dec/vp8li.h"
|
||||
#include "../format_constants.h"
|
||||
#include "../mux.h"
|
||||
#include "../webp/mux.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Defines and constants.
|
||||
|
||||
#define MUX_MAJ_VERSION 0
|
||||
#define MUX_MIN_VERSION 2
|
||||
#define MUX_REV_VERSION 2
|
||||
|
||||
// Chunk object.
|
||||
typedef struct WebPChunk WebPChunk;
|
||||
struct WebPChunk {
|
||||
uint32_t tag_;
|
||||
int owner_; // True if *data_ memory is owned internally.
|
||||
// VP8X, Loop, and other internally created chunks
|
||||
// like frame/tile are always owned.
|
||||
// VP8X, ANIM, and other internally created chunks
|
||||
// like ANMF/FRGM are always owned.
|
||||
WebPData data_;
|
||||
WebPChunk* next_;
|
||||
};
|
||||
|
||||
// MuxImage object. Store a full webp image (including frame/tile chunk, alpha
|
||||
// MuxImage object. Store a full WebP image (including ANMF/FRGM chunk, ALPH
|
||||
// chunk and VP8/VP8L chunk),
|
||||
typedef struct WebPMuxImage WebPMuxImage;
|
||||
struct WebPMuxImage {
|
||||
WebPChunk* header_; // Corresponds to WEBP_CHUNK_FRAME/WEBP_CHUNK_TILE.
|
||||
WebPChunk* header_; // Corresponds to WEBP_CHUNK_ANMF/WEBP_CHUNK_FRGM.
|
||||
WebPChunk* alpha_; // Corresponds to WEBP_CHUNK_ALPHA.
|
||||
WebPChunk* img_; // Corresponds to WEBP_CHUNK_IMAGE.
|
||||
WebPChunk* unknown_; // Corresponds to WEBP_CHUNK_UNKNOWN.
|
||||
int width_;
|
||||
int height_;
|
||||
int has_alpha_; // Through ALPH chunk or as part of VP8L.
|
||||
int is_partial_; // True if only some of the chunks are filled.
|
||||
WebPMuxImage* next_;
|
||||
};
|
||||
@ -51,11 +60,14 @@ struct WebPMuxImage {
|
||||
struct WebPMux {
|
||||
WebPMuxImage* images_;
|
||||
WebPChunk* iccp_;
|
||||
WebPChunk* meta_;
|
||||
WebPChunk* loop_;
|
||||
WebPChunk* exif_;
|
||||
WebPChunk* xmp_;
|
||||
WebPChunk* anim_;
|
||||
WebPChunk* vp8x_;
|
||||
|
||||
WebPChunk* unknown_;
|
||||
WebPChunk* unknown_;
|
||||
int canvas_width_;
|
||||
int canvas_height_;
|
||||
};
|
||||
|
||||
// CHUNK_INDEX enum: used for indexing within 'kChunks' (defined below) only.
|
||||
@ -65,13 +77,14 @@ struct WebPMux {
|
||||
typedef enum {
|
||||
IDX_VP8X = 0,
|
||||
IDX_ICCP,
|
||||
IDX_LOOP,
|
||||
IDX_FRAME,
|
||||
IDX_TILE,
|
||||
IDX_ANIM,
|
||||
IDX_ANMF,
|
||||
IDX_FRGM,
|
||||
IDX_ALPHA,
|
||||
IDX_VP8,
|
||||
IDX_VP8L,
|
||||
IDX_META,
|
||||
IDX_EXIF,
|
||||
IDX_XMP,
|
||||
IDX_UNKNOWN,
|
||||
|
||||
IDX_NIL,
|
||||
@ -80,8 +93,6 @@ typedef enum {
|
||||
|
||||
#define NIL_TAG 0x00000000u // To signal void chunk.
|
||||
|
||||
#define MKFOURCC(a, b, c, d) ((uint32_t)(a) | (b) << 8 | (c) << 16 | (d) << 24)
|
||||
|
||||
typedef struct {
|
||||
uint32_t tag;
|
||||
WebPChunkId id;
|
||||
@ -90,56 +101,24 @@ typedef struct {
|
||||
|
||||
extern const ChunkInfo kChunks[IDX_LAST_CHUNK];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper functions.
|
||||
|
||||
// Read 16, 24 or 32 bits stored in little-endian order.
|
||||
static WEBP_INLINE int GetLE16(const uint8_t* const data) {
|
||||
return (int)(data[0] << 0) | (data[1] << 8);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int GetLE24(const uint8_t* const data) {
|
||||
return GetLE16(data) | (data[2] << 16);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t GetLE32(const uint8_t* const data) {
|
||||
return (uint32_t)GetLE16(data) | (GetLE16(data + 2) << 16);
|
||||
}
|
||||
|
||||
// Store 16, 24 or 32 bits in little-endian order.
|
||||
static WEBP_INLINE void PutLE16(uint8_t* const data, int val) {
|
||||
assert(val < (1 << 16));
|
||||
data[0] = (val >> 0);
|
||||
data[1] = (val >> 8);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void PutLE24(uint8_t* const data, int val) {
|
||||
assert(val < (1 << 24));
|
||||
PutLE16(data, val & 0xffff);
|
||||
data[2] = (val >> 16);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
|
||||
PutLE16(data, (int)(val & 0xffff));
|
||||
PutLE16(data + 2, (int)(val >> 16));
|
||||
}
|
||||
|
||||
static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) {
|
||||
return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Chunk object management.
|
||||
|
||||
// Initialize.
|
||||
void ChunkInit(WebPChunk* const chunk);
|
||||
|
||||
// Get chunk index from chunk tag. Returns IDX_NIL if not found.
|
||||
// Get chunk index from chunk tag. Returns IDX_UNKNOWN if not found.
|
||||
CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag);
|
||||
|
||||
// Get chunk id from chunk tag. Returns WEBP_CHUNK_NIL if not found.
|
||||
// Get chunk id from chunk tag. Returns WEBP_CHUNK_UNKNOWN if not found.
|
||||
WebPChunkId ChunkGetIdFromTag(uint32_t tag);
|
||||
|
||||
// Convert a fourcc string to a tag.
|
||||
uint32_t ChunkGetTagFromFourCC(const char fourcc[4]);
|
||||
|
||||
// Get chunk index from fourcc. Returns IDX_UNKNOWN if given fourcc is unknown.
|
||||
CHUNK_INDEX ChunkGetIndexFromFourCC(const char fourcc[4]);
|
||||
|
||||
// Search for nth chunk with given 'tag' in the chunk list.
|
||||
// nth = 0 means "last of the list".
|
||||
WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag);
|
||||
@ -150,7 +129,8 @@ WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
|
||||
|
||||
// Sets 'chunk' at nth position in the 'chunk_list'.
|
||||
// nth = 0 has the special meaning "last of the list".
|
||||
WebPMuxError ChunkSetNth(const WebPChunk* chunk, WebPChunk** chunk_list,
|
||||
// On success ownership is transferred from 'chunk' to the 'chunk_list'.
|
||||
WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list,
|
||||
uint32_t nth);
|
||||
|
||||
// Releases chunk and returns chunk->next_.
|
||||
@ -159,23 +139,27 @@ WebPChunk* ChunkRelease(WebPChunk* const chunk);
|
||||
// Deletes given chunk & returns chunk->next_.
|
||||
WebPChunk* ChunkDelete(WebPChunk* const chunk);
|
||||
|
||||
// Deletes all chunks in the given chunk list.
|
||||
void ChunkListDelete(WebPChunk** const chunk_list);
|
||||
|
||||
// Returns size of the chunk including chunk header and padding byte (if any).
|
||||
static WEBP_INLINE size_t SizeWithPadding(size_t chunk_size) {
|
||||
return CHUNK_HEADER_SIZE + ((chunk_size + 1) & ~1U);
|
||||
}
|
||||
|
||||
// Size of a chunk including header and padding.
|
||||
static WEBP_INLINE size_t ChunkDiskSize(const WebPChunk* chunk) {
|
||||
const size_t data_size = chunk->data_.size_;
|
||||
const size_t data_size = chunk->data_.size;
|
||||
assert(data_size < MAX_CHUNK_PAYLOAD);
|
||||
return SizeWithPadding(data_size);
|
||||
}
|
||||
|
||||
// Total size of a list of chunks.
|
||||
size_t ChunksListDiskSize(const WebPChunk* chunk_list);
|
||||
size_t ChunkListDiskSize(const WebPChunk* chunk_list);
|
||||
|
||||
// Write out the given list of chunks into 'dst'.
|
||||
uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst);
|
||||
|
||||
// Get the width & height of image stored in 'image_chunk'.
|
||||
WebPMuxError MuxGetImageWidthHeight(const WebPChunk* const image_chunk,
|
||||
int* const width, int* const height);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MuxImage object management.
|
||||
|
||||
@ -189,82 +173,59 @@ WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi);
|
||||
// 'wpi' can be NULL.
|
||||
WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi);
|
||||
|
||||
// Delete all images in 'wpi_list'.
|
||||
void MuxImageDeleteAll(WebPMuxImage** const wpi_list);
|
||||
|
||||
// Count number of images matching the given tag id in the 'wpi_list'.
|
||||
// If id == WEBP_CHUNK_NIL, all images will be matched.
|
||||
int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id);
|
||||
|
||||
// Update width/height/has_alpha info from chunks within wpi.
|
||||
// Also remove ALPH chunk if not needed.
|
||||
int MuxImageFinalize(WebPMuxImage* const wpi);
|
||||
|
||||
// Check if given ID corresponds to an image related chunk.
|
||||
static WEBP_INLINE int IsWPI(WebPChunkId id) {
|
||||
switch (id) {
|
||||
case WEBP_CHUNK_FRAME:
|
||||
case WEBP_CHUNK_TILE:
|
||||
case WEBP_CHUNK_ANMF:
|
||||
case WEBP_CHUNK_FRGM:
|
||||
case WEBP_CHUNK_ALPHA:
|
||||
case WEBP_CHUNK_IMAGE: return 1;
|
||||
default: return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Get a reference to appropriate chunk list within an image given chunk tag.
|
||||
static WEBP_INLINE WebPChunk** MuxImageGetListFromId(
|
||||
const WebPMuxImage* const wpi, WebPChunkId id) {
|
||||
assert(wpi != NULL);
|
||||
switch (id) {
|
||||
case WEBP_CHUNK_FRAME:
|
||||
case WEBP_CHUNK_TILE: return (WebPChunk**)&wpi->header_;
|
||||
case WEBP_CHUNK_ALPHA: return (WebPChunk**)&wpi->alpha_;
|
||||
case WEBP_CHUNK_IMAGE: return (WebPChunk**)&wpi->img_;
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Pushes 'wpi' at the end of 'wpi_list'.
|
||||
WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list);
|
||||
|
||||
// Delete nth image in the image list with given tag id.
|
||||
WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth,
|
||||
WebPChunkId id);
|
||||
// Delete nth image in the image list.
|
||||
WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth);
|
||||
|
||||
// Get nth image in the image list with given tag id.
|
||||
// Get nth image in the image list.
|
||||
WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth,
|
||||
WebPChunkId id, WebPMuxImage** wpi);
|
||||
WebPMuxImage** wpi);
|
||||
|
||||
// Total size of the given image.
|
||||
size_t MuxImageDiskSize(const WebPMuxImage* const wpi);
|
||||
|
||||
// Total size of a list of images.
|
||||
size_t MuxImageListDiskSize(const WebPMuxImage* wpi_list);
|
||||
|
||||
// Write out the given image into 'dst'.
|
||||
uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst);
|
||||
|
||||
// Write out the given list of images into 'dst'.
|
||||
uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper methods for mux.
|
||||
|
||||
// Checks if the given image list contains at least one lossless image.
|
||||
int MuxHasLosslessImages(const WebPMuxImage* images);
|
||||
// Checks if the given image list contains at least one image with alpha.
|
||||
int MuxHasAlpha(const WebPMuxImage* images);
|
||||
|
||||
// Write out RIFF header into 'data', given total data size 'size'.
|
||||
uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size);
|
||||
|
||||
// Returns the list where chunk with given ID is to be inserted in mux.
|
||||
// Return value is NULL if this chunk should be inserted in mux->images_ list
|
||||
// or if 'id' is not known.
|
||||
WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id);
|
||||
|
||||
// Validates that the given mux has a single image.
|
||||
WebPMuxError MuxValidateForImage(const WebPMux* const mux);
|
||||
|
||||
// Validates the given mux object.
|
||||
WebPMuxError MuxValidate(const WebPMux* const mux);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Internal objects and utils for mux.
|
||||
@ -12,28 +14,32 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include "./muxi.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#define UNDEFINED_CHUNK_SIZE (-1)
|
||||
|
||||
const ChunkInfo kChunks[] = {
|
||||
{ MKFOURCC('V', 'P', '8', 'X'), WEBP_CHUNK_VP8X, VP8X_CHUNK_SIZE },
|
||||
{ MKFOURCC('I', 'C', 'C', 'P'), WEBP_CHUNK_ICCP, UNDEFINED_CHUNK_SIZE },
|
||||
{ MKFOURCC('L', 'O', 'O', 'P'), WEBP_CHUNK_LOOP, LOOP_CHUNK_SIZE },
|
||||
{ MKFOURCC('F', 'R', 'M', ' '), WEBP_CHUNK_FRAME, FRAME_CHUNK_SIZE },
|
||||
{ MKFOURCC('T', 'I', 'L', 'E'), WEBP_CHUNK_TILE, TILE_CHUNK_SIZE },
|
||||
{ MKFOURCC('A', 'N', 'I', 'M'), WEBP_CHUNK_ANIM, ANIM_CHUNK_SIZE },
|
||||
{ MKFOURCC('A', 'N', 'M', 'F'), WEBP_CHUNK_ANMF, ANMF_CHUNK_SIZE },
|
||||
{ MKFOURCC('F', 'R', 'G', 'M'), WEBP_CHUNK_FRGM, FRGM_CHUNK_SIZE },
|
||||
{ MKFOURCC('A', 'L', 'P', 'H'), WEBP_CHUNK_ALPHA, UNDEFINED_CHUNK_SIZE },
|
||||
{ MKFOURCC('V', 'P', '8', ' '), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE },
|
||||
{ MKFOURCC('V', 'P', '8', 'L'), WEBP_CHUNK_IMAGE, UNDEFINED_CHUNK_SIZE },
|
||||
{ MKFOURCC('M', 'E', 'T', 'A'), WEBP_CHUNK_META, UNDEFINED_CHUNK_SIZE },
|
||||
{ MKFOURCC('U', 'N', 'K', 'N'), WEBP_CHUNK_UNKNOWN, UNDEFINED_CHUNK_SIZE },
|
||||
{ MKFOURCC('E', 'X', 'I', 'F'), WEBP_CHUNK_EXIF, UNDEFINED_CHUNK_SIZE },
|
||||
{ MKFOURCC('X', 'M', 'P', ' '), WEBP_CHUNK_XMP, UNDEFINED_CHUNK_SIZE },
|
||||
{ NIL_TAG, WEBP_CHUNK_UNKNOWN, UNDEFINED_CHUNK_SIZE },
|
||||
|
||||
{ NIL_TAG, WEBP_CHUNK_NIL, UNDEFINED_CHUNK_SIZE }
|
||||
{ NIL_TAG, WEBP_CHUNK_NIL, UNDEFINED_CHUNK_SIZE }
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int WebPGetMuxVersion(void) {
|
||||
return (MUX_MAJ_VERSION << 16) | (MUX_MIN_VERSION << 8) | MUX_REV_VERSION;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Life of a chunk object.
|
||||
|
||||
@ -60,9 +66,9 @@ WebPChunk* ChunkRelease(WebPChunk* const chunk) {
|
||||
CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag) {
|
||||
int i;
|
||||
for (i = 0; kChunks[i].tag != NIL_TAG; ++i) {
|
||||
if (tag == kChunks[i].tag) return i;
|
||||
if (tag == kChunks[i].tag) return (CHUNK_INDEX)i;
|
||||
}
|
||||
return IDX_NIL;
|
||||
return IDX_UNKNOWN;
|
||||
}
|
||||
|
||||
WebPChunkId ChunkGetIdFromTag(uint32_t tag) {
|
||||
@ -70,7 +76,16 @@ WebPChunkId ChunkGetIdFromTag(uint32_t tag) {
|
||||
for (i = 0; kChunks[i].tag != NIL_TAG; ++i) {
|
||||
if (tag == kChunks[i].tag) return kChunks[i].id;
|
||||
}
|
||||
return WEBP_CHUNK_NIL;
|
||||
return WEBP_CHUNK_UNKNOWN;
|
||||
}
|
||||
|
||||
uint32_t ChunkGetTagFromFourCC(const char fourcc[4]) {
|
||||
return MKFOURCC(fourcc[0], fourcc[1], fourcc[2], fourcc[3]);
|
||||
}
|
||||
|
||||
CHUNK_INDEX ChunkGetIndexFromFourCC(const char fourcc[4]) {
|
||||
const uint32_t tag = ChunkGetTagFromFourCC(fourcc);
|
||||
return ChunkGetIndexFromTag(tag);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -78,7 +93,7 @@ WebPChunkId ChunkGetIdFromTag(uint32_t tag) {
|
||||
|
||||
// Returns next chunk in the chunk list with the given tag.
|
||||
static WebPChunk* ChunkSearchNextInList(WebPChunk* chunk, uint32_t tag) {
|
||||
while (chunk && chunk->tag_ != tag) {
|
||||
while (chunk != NULL && chunk->tag_ != tag) {
|
||||
chunk = chunk->next_;
|
||||
}
|
||||
return chunk;
|
||||
@ -87,7 +102,7 @@ static WebPChunk* ChunkSearchNextInList(WebPChunk* chunk, uint32_t tag) {
|
||||
WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) {
|
||||
uint32_t iter = nth;
|
||||
first = ChunkSearchNextInList(first, tag);
|
||||
if (!first) return NULL;
|
||||
if (first == NULL) return NULL;
|
||||
|
||||
while (--iter != 0) {
|
||||
WebPChunk* next_chunk = ChunkSearchNextInList(first->next_, tag);
|
||||
@ -99,14 +114,14 @@ WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) {
|
||||
|
||||
// Outputs a pointer to 'prev_chunk->next_',
|
||||
// where 'prev_chunk' is the pointer to the chunk at position (nth - 1).
|
||||
// Returns 1 if nth chunk was found, 0 otherwise.
|
||||
// Returns true if nth chunk was found.
|
||||
static int ChunkSearchListToSet(WebPChunk** chunk_list, uint32_t nth,
|
||||
WebPChunk*** const location) {
|
||||
uint32_t count = 0;
|
||||
assert(chunk_list);
|
||||
assert(chunk_list != NULL);
|
||||
*location = chunk_list;
|
||||
|
||||
while (*chunk_list) {
|
||||
while (*chunk_list != NULL) {
|
||||
WebPChunk* const cur_chunk = *chunk_list;
|
||||
++count;
|
||||
if (count == nth) return 1; // Found.
|
||||
@ -124,34 +139,25 @@ static int ChunkSearchListToSet(WebPChunk** chunk_list, uint32_t nth,
|
||||
WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data,
|
||||
int copy_data, uint32_t tag) {
|
||||
// For internally allocated chunks, always copy data & make it owner of data.
|
||||
if (tag == kChunks[IDX_VP8X].tag || tag == kChunks[IDX_LOOP].tag) {
|
||||
if (tag == kChunks[IDX_VP8X].tag || tag == kChunks[IDX_ANIM].tag) {
|
||||
copy_data = 1;
|
||||
}
|
||||
|
||||
ChunkRelease(chunk);
|
||||
|
||||
if (data != NULL) {
|
||||
if (copy_data) {
|
||||
// Copy data.
|
||||
chunk->data_.bytes_ = (uint8_t*)malloc(data->size_);
|
||||
if (chunk->data_.bytes_ == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||
memcpy((uint8_t*)chunk->data_.bytes_, data->bytes_, data->size_);
|
||||
chunk->data_.size_ = data->size_;
|
||||
|
||||
// Chunk is owner of data.
|
||||
chunk->owner_ = 1;
|
||||
} else {
|
||||
// Don't copy data.
|
||||
if (copy_data) { // Copy data.
|
||||
if (!WebPDataCopy(data, &chunk->data_)) return WEBP_MUX_MEMORY_ERROR;
|
||||
chunk->owner_ = 1; // Chunk is owner of data.
|
||||
} else { // Don't copy data.
|
||||
chunk->data_ = *data;
|
||||
}
|
||||
}
|
||||
|
||||
chunk->tag_ = tag;
|
||||
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
WebPMuxError ChunkSetNth(const WebPChunk* chunk, WebPChunk** chunk_list,
|
||||
WebPMuxError ChunkSetNth(WebPChunk* chunk, WebPChunk** chunk_list,
|
||||
uint32_t nth) {
|
||||
WebPChunk* new_chunk;
|
||||
|
||||
@ -159,9 +165,10 @@ WebPMuxError ChunkSetNth(const WebPChunk* chunk, WebPChunk** chunk_list,
|
||||
return WEBP_MUX_NOT_FOUND;
|
||||
}
|
||||
|
||||
new_chunk = (WebPChunk*)malloc(sizeof(*new_chunk));
|
||||
new_chunk = (WebPChunk*)WebPSafeMalloc(1ULL, sizeof(*new_chunk));
|
||||
if (new_chunk == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||
*new_chunk = *chunk;
|
||||
chunk->owner_ = 0;
|
||||
new_chunk->next_ = *chunk_list;
|
||||
*chunk_list = new_chunk;
|
||||
return WEBP_MUX_OK;
|
||||
@ -172,70 +179,47 @@ WebPMuxError ChunkSetNth(const WebPChunk* chunk, WebPChunk** chunk_list,
|
||||
|
||||
WebPChunk* ChunkDelete(WebPChunk* const chunk) {
|
||||
WebPChunk* const next = ChunkRelease(chunk);
|
||||
free(chunk);
|
||||
WebPSafeFree(chunk);
|
||||
return next;
|
||||
}
|
||||
|
||||
void ChunkListDelete(WebPChunk** const chunk_list) {
|
||||
while (*chunk_list != NULL) {
|
||||
*chunk_list = ChunkDelete(*chunk_list);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Chunk serialization methods.
|
||||
|
||||
size_t ChunksListDiskSize(const WebPChunk* chunk_list) {
|
||||
size_t size = 0;
|
||||
while (chunk_list) {
|
||||
size += ChunkDiskSize(chunk_list);
|
||||
chunk_list = chunk_list->next_;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static uint8_t* ChunkEmit(const WebPChunk* const chunk, uint8_t* dst) {
|
||||
const size_t chunk_size = chunk->data_.size_;
|
||||
const size_t chunk_size = chunk->data_.size;
|
||||
assert(chunk);
|
||||
assert(chunk->tag_ != NIL_TAG);
|
||||
PutLE32(dst + 0, chunk->tag_);
|
||||
PutLE32(dst + TAG_SIZE, (uint32_t)chunk_size);
|
||||
assert(chunk_size == (uint32_t)chunk_size);
|
||||
memcpy(dst + CHUNK_HEADER_SIZE, chunk->data_.bytes_, chunk_size);
|
||||
memcpy(dst + CHUNK_HEADER_SIZE, chunk->data_.bytes, chunk_size);
|
||||
if (chunk_size & 1)
|
||||
dst[CHUNK_HEADER_SIZE + chunk_size] = 0; // Add padding.
|
||||
return dst + ChunkDiskSize(chunk);
|
||||
}
|
||||
|
||||
uint8_t* ChunkListEmit(const WebPChunk* chunk_list, uint8_t* dst) {
|
||||
while (chunk_list) {
|
||||
while (chunk_list != NULL) {
|
||||
dst = ChunkEmit(chunk_list, dst);
|
||||
chunk_list = chunk_list->next_;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Manipulation of a WebPData object.
|
||||
|
||||
void WebPDataInit(WebPData* webp_data) {
|
||||
if (webp_data != NULL) {
|
||||
memset(webp_data, 0, sizeof(*webp_data));
|
||||
size_t ChunkListDiskSize(const WebPChunk* chunk_list) {
|
||||
size_t size = 0;
|
||||
while (chunk_list != NULL) {
|
||||
size += ChunkDiskSize(chunk_list);
|
||||
chunk_list = chunk_list->next_;
|
||||
}
|
||||
}
|
||||
|
||||
void WebPDataClear(WebPData* webp_data) {
|
||||
if (webp_data != NULL) {
|
||||
free((void*)webp_data->bytes_);
|
||||
WebPDataInit(webp_data);
|
||||
}
|
||||
}
|
||||
|
||||
int WebPDataCopy(const WebPData* src, WebPData* dst) {
|
||||
if (src == NULL || dst == NULL) return 0;
|
||||
|
||||
WebPDataInit(dst);
|
||||
if (src->bytes_ != NULL && src->size_ != 0) {
|
||||
dst->bytes_ = (uint8_t*)malloc(src->size_);
|
||||
if (dst->bytes_ == NULL) return 0;
|
||||
memcpy((void*)dst->bytes_, src->bytes_, src->size_);
|
||||
dst->size_ = src->size_;
|
||||
}
|
||||
return 1;
|
||||
return size;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -252,6 +236,7 @@ WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) {
|
||||
ChunkDelete(wpi->header_);
|
||||
ChunkDelete(wpi->alpha_);
|
||||
ChunkDelete(wpi->img_);
|
||||
ChunkListDelete(&wpi->unknown_);
|
||||
|
||||
next = wpi->next_;
|
||||
MuxImageInit(wpi);
|
||||
@ -261,14 +246,31 @@ WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) {
|
||||
//------------------------------------------------------------------------------
|
||||
// MuxImage search methods.
|
||||
|
||||
// Get a reference to appropriate chunk list within an image given chunk tag.
|
||||
static WebPChunk** GetChunkListFromId(const WebPMuxImage* const wpi,
|
||||
WebPChunkId id) {
|
||||
assert(wpi != NULL);
|
||||
switch (id) {
|
||||
case WEBP_CHUNK_ANMF:
|
||||
case WEBP_CHUNK_FRGM: return (WebPChunk**)&wpi->header_;
|
||||
case WEBP_CHUNK_ALPHA: return (WebPChunk**)&wpi->alpha_;
|
||||
case WEBP_CHUNK_IMAGE: return (WebPChunk**)&wpi->img_;
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id) {
|
||||
int count = 0;
|
||||
const WebPMuxImage* current;
|
||||
for (current = wpi_list; current != NULL; current = current->next_) {
|
||||
const WebPChunk* const wpi_chunk = *MuxImageGetListFromId(current, id);
|
||||
if (wpi_chunk != NULL) {
|
||||
const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_);
|
||||
if (wpi_chunk_id == id) ++count;
|
||||
if (id == WEBP_CHUNK_NIL) {
|
||||
++count; // Special case: count all images.
|
||||
} else {
|
||||
const WebPChunk* const wpi_chunk = *GetChunkListFromId(current, id);
|
||||
if (wpi_chunk != NULL) {
|
||||
const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_);
|
||||
if (wpi_chunk_id == id) ++count; // Count images with a matching 'id'.
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
@ -276,34 +278,22 @@ int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id) {
|
||||
|
||||
// Outputs a pointer to 'prev_wpi->next_',
|
||||
// where 'prev_wpi' is the pointer to the image at position (nth - 1).
|
||||
// Returns 1 if nth image with given id was found, 0 otherwise.
|
||||
// Returns true if nth image was found.
|
||||
static int SearchImageToGetOrDelete(WebPMuxImage** wpi_list, uint32_t nth,
|
||||
WebPChunkId id,
|
||||
WebPMuxImage*** const location) {
|
||||
uint32_t count = 0;
|
||||
assert(wpi_list);
|
||||
*location = wpi_list;
|
||||
|
||||
// Search makes sense only for the following.
|
||||
assert(id == WEBP_CHUNK_FRAME || id == WEBP_CHUNK_TILE ||
|
||||
id == WEBP_CHUNK_IMAGE);
|
||||
assert(id != WEBP_CHUNK_IMAGE || nth == 1);
|
||||
|
||||
if (nth == 0) {
|
||||
nth = MuxImageCount(*wpi_list, id);
|
||||
nth = MuxImageCount(*wpi_list, WEBP_CHUNK_NIL);
|
||||
if (nth == 0) return 0; // Not found.
|
||||
}
|
||||
|
||||
while (*wpi_list) {
|
||||
while (*wpi_list != NULL) {
|
||||
WebPMuxImage* const cur_wpi = *wpi_list;
|
||||
const WebPChunk* const wpi_chunk = *MuxImageGetListFromId(cur_wpi, id);
|
||||
if (wpi_chunk != NULL) {
|
||||
const WebPChunkId wpi_chunk_id = ChunkGetIdFromTag(wpi_chunk->tag_);
|
||||
if (wpi_chunk_id == id) {
|
||||
++count;
|
||||
if (count == nth) return 1; // Found.
|
||||
}
|
||||
}
|
||||
++count;
|
||||
if (count == nth) return 1; // Found.
|
||||
wpi_list = &cur_wpi->next_;
|
||||
*location = wpi_list;
|
||||
}
|
||||
@ -322,7 +312,7 @@ WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list) {
|
||||
wpi_list = &cur_wpi->next_;
|
||||
}
|
||||
|
||||
new_wpi = (WebPMuxImage*)malloc(sizeof(*new_wpi));
|
||||
new_wpi = (WebPMuxImage*)WebPSafeMalloc(1ULL, sizeof(*new_wpi));
|
||||
if (new_wpi == NULL) return WEBP_MUX_MEMORY_ERROR;
|
||||
*new_wpi = *wpi;
|
||||
new_wpi->next_ = NULL;
|
||||
@ -341,20 +331,13 @@ WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list) {
|
||||
WebPMuxImage* MuxImageDelete(WebPMuxImage* const wpi) {
|
||||
// Delete the components of wpi. If wpi is NULL this is a noop.
|
||||
WebPMuxImage* const next = MuxImageRelease(wpi);
|
||||
free(wpi);
|
||||
WebPSafeFree(wpi);
|
||||
return next;
|
||||
}
|
||||
|
||||
void MuxImageDeleteAll(WebPMuxImage** const wpi_list) {
|
||||
while (*wpi_list) {
|
||||
*wpi_list = MuxImageDelete(*wpi_list);
|
||||
}
|
||||
}
|
||||
|
||||
WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth,
|
||||
WebPChunkId id) {
|
||||
WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth) {
|
||||
assert(wpi_list);
|
||||
if (!SearchImageToGetOrDelete(wpi_list, nth, id, &wpi_list)) {
|
||||
if (!SearchImageToGetOrDelete(wpi_list, nth, &wpi_list)) {
|
||||
return WEBP_MUX_NOT_FOUND;
|
||||
}
|
||||
*wpi_list = MuxImageDelete(*wpi_list);
|
||||
@ -365,10 +348,10 @@ WebPMuxError MuxImageDeleteNth(WebPMuxImage** wpi_list, uint32_t nth,
|
||||
// MuxImage reader methods.
|
||||
|
||||
WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth,
|
||||
WebPChunkId id, WebPMuxImage** wpi) {
|
||||
WebPMuxImage** wpi) {
|
||||
assert(wpi_list);
|
||||
assert(wpi);
|
||||
if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth, id,
|
||||
if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth,
|
||||
(WebPMuxImage***)&wpi_list)) {
|
||||
return WEBP_MUX_NOT_FOUND;
|
||||
}
|
||||
@ -385,47 +368,48 @@ size_t MuxImageDiskSize(const WebPMuxImage* const wpi) {
|
||||
if (wpi->header_ != NULL) size += ChunkDiskSize(wpi->header_);
|
||||
if (wpi->alpha_ != NULL) size += ChunkDiskSize(wpi->alpha_);
|
||||
if (wpi->img_ != NULL) size += ChunkDiskSize(wpi->img_);
|
||||
if (wpi->unknown_ != NULL) size += ChunkListDiskSize(wpi->unknown_);
|
||||
return size;
|
||||
}
|
||||
|
||||
size_t MuxImageListDiskSize(const WebPMuxImage* wpi_list) {
|
||||
size_t size = 0;
|
||||
while (wpi_list) {
|
||||
size += MuxImageDiskSize(wpi_list);
|
||||
wpi_list = wpi_list->next_;
|
||||
// Special case as ANMF/FRGM chunk encapsulates other image chunks.
|
||||
static uint8_t* ChunkEmitSpecial(const WebPChunk* const header,
|
||||
size_t total_size, uint8_t* dst) {
|
||||
const size_t header_size = header->data_.size;
|
||||
const size_t offset_to_next = total_size - CHUNK_HEADER_SIZE;
|
||||
assert(header->tag_ == kChunks[IDX_ANMF].tag ||
|
||||
header->tag_ == kChunks[IDX_FRGM].tag);
|
||||
PutLE32(dst + 0, header->tag_);
|
||||
PutLE32(dst + TAG_SIZE, (uint32_t)offset_to_next);
|
||||
assert(header_size == (uint32_t)header_size);
|
||||
memcpy(dst + CHUNK_HEADER_SIZE, header->data_.bytes, header_size);
|
||||
if (header_size & 1) {
|
||||
dst[CHUNK_HEADER_SIZE + header_size] = 0; // Add padding.
|
||||
}
|
||||
return size;
|
||||
return dst + ChunkDiskSize(header);
|
||||
}
|
||||
|
||||
uint8_t* MuxImageEmit(const WebPMuxImage* const wpi, uint8_t* dst) {
|
||||
// Ordering of chunks to be emitted is strictly as follows:
|
||||
// 1. Frame/Tile chunk (if present).
|
||||
// 2. Alpha chunk (if present).
|
||||
// 1. ANMF/FRGM chunk (if present).
|
||||
// 2. ALPH chunk (if present).
|
||||
// 3. VP8/VP8L chunk.
|
||||
assert(wpi);
|
||||
if (wpi->header_ != NULL) dst = ChunkEmit(wpi->header_, dst);
|
||||
if (wpi->header_ != NULL) {
|
||||
dst = ChunkEmitSpecial(wpi->header_, MuxImageDiskSize(wpi), dst);
|
||||
}
|
||||
if (wpi->alpha_ != NULL) dst = ChunkEmit(wpi->alpha_, dst);
|
||||
if (wpi->img_ != NULL) dst = ChunkEmit(wpi->img_, dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
uint8_t* MuxImageListEmit(const WebPMuxImage* wpi_list, uint8_t* dst) {
|
||||
while (wpi_list) {
|
||||
dst = MuxImageEmit(wpi_list, dst);
|
||||
wpi_list = wpi_list->next_;
|
||||
}
|
||||
if (wpi->unknown_ != NULL) dst = ChunkListEmit(wpi->unknown_, dst);
|
||||
return dst;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper methods for mux.
|
||||
|
||||
int MuxHasLosslessImages(const WebPMuxImage* images) {
|
||||
int MuxHasAlpha(const WebPMuxImage* images) {
|
||||
while (images != NULL) {
|
||||
assert(images->img_ != NULL);
|
||||
if (images->img_->tag_ == kChunks[IDX_VP8L].tag) {
|
||||
return 1;
|
||||
}
|
||||
if (images->has_alpha_) return 1;
|
||||
images = images->next_;
|
||||
}
|
||||
return 0;
|
||||
@ -441,30 +425,13 @@ uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size) {
|
||||
|
||||
WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id) {
|
||||
assert(mux != NULL);
|
||||
switch(id) {
|
||||
switch (id) {
|
||||
case WEBP_CHUNK_VP8X: return (WebPChunk**)&mux->vp8x_;
|
||||
case WEBP_CHUNK_ICCP: return (WebPChunk**)&mux->iccp_;
|
||||
case WEBP_CHUNK_LOOP: return (WebPChunk**)&mux->loop_;
|
||||
case WEBP_CHUNK_META: return (WebPChunk**)&mux->meta_;
|
||||
case WEBP_CHUNK_UNKNOWN: return (WebPChunk**)&mux->unknown_;
|
||||
default: return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
WebPMuxError MuxValidateForImage(const WebPMux* const mux) {
|
||||
const int num_images = MuxImageCount(mux->images_, WEBP_CHUNK_IMAGE);
|
||||
const int num_frames = MuxImageCount(mux->images_, WEBP_CHUNK_FRAME);
|
||||
const int num_tiles = MuxImageCount(mux->images_, WEBP_CHUNK_TILE);
|
||||
|
||||
if (num_images == 0) {
|
||||
// No images in mux.
|
||||
return WEBP_MUX_NOT_FOUND;
|
||||
} else if (num_images == 1 && num_frames == 0 && num_tiles == 0) {
|
||||
// Valid case (single image).
|
||||
return WEBP_MUX_OK;
|
||||
} else {
|
||||
// Frame/Tile case OR an invalid mux.
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
case WEBP_CHUNK_ANIM: return (WebPChunk**)&mux->anim_;
|
||||
case WEBP_CHUNK_EXIF: return (WebPChunk**)&mux->exif_;
|
||||
case WEBP_CHUNK_XMP: return (WebPChunk**)&mux->xmp_;
|
||||
default: return (WebPChunk**)&mux->unknown_;
|
||||
}
|
||||
}
|
||||
|
||||
@ -480,7 +447,7 @@ static int IsNotCompatible(int feature, int num_items) {
|
||||
// On success returns WEBP_MUX_OK and stores the chunk count in *num.
|
||||
static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx,
|
||||
WebPFeatureFlags feature,
|
||||
WebPFeatureFlags vp8x_flags,
|
||||
uint32_t vp8x_flags,
|
||||
int max, int* num) {
|
||||
const WebPMuxError err =
|
||||
WebPMuxNumChunks(mux, kChunks[idx].id, num);
|
||||
@ -494,10 +461,11 @@ static WebPMuxError ValidateChunk(const WebPMux* const mux, CHUNK_INDEX idx,
|
||||
|
||||
WebPMuxError MuxValidate(const WebPMux* const mux) {
|
||||
int num_iccp;
|
||||
int num_meta;
|
||||
int num_loop_chunks;
|
||||
int num_exif;
|
||||
int num_xmp;
|
||||
int num_anim;
|
||||
int num_frames;
|
||||
int num_tiles;
|
||||
int num_fragments;
|
||||
int num_vp8x;
|
||||
int num_images;
|
||||
int num_alpha;
|
||||
@ -517,29 +485,33 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
|
||||
err = ValidateChunk(mux, IDX_ICCP, ICCP_FLAG, flags, 1, &num_iccp);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
|
||||
// At most one XMP metadata.
|
||||
err = ValidateChunk(mux, IDX_META, META_FLAG, flags, 1, &num_meta);
|
||||
// At most one EXIF metadata.
|
||||
err = ValidateChunk(mux, IDX_EXIF, EXIF_FLAG, flags, 1, &num_exif);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
|
||||
// Animation: ANIMATION_FLAG, loop chunk and frame chunk(s) are consistent.
|
||||
// At most one loop chunk.
|
||||
err = ValidateChunk(mux, IDX_LOOP, NO_FLAG, flags, 1, &num_loop_chunks);
|
||||
// At most one XMP metadata.
|
||||
err = ValidateChunk(mux, IDX_XMP, XMP_FLAG, flags, 1, &num_xmp);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
err = ValidateChunk(mux, IDX_FRAME, NO_FLAG, flags, -1, &num_frames);
|
||||
|
||||
// Animation: ANIMATION_FLAG, ANIM chunk and ANMF chunk(s) are consistent.
|
||||
// At most one ANIM chunk.
|
||||
err = ValidateChunk(mux, IDX_ANIM, NO_FLAG, flags, 1, &num_anim);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
err = ValidateChunk(mux, IDX_ANMF, NO_FLAG, flags, -1, &num_frames);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
|
||||
{
|
||||
const int has_animation = !!(flags & ANIMATION_FLAG);
|
||||
if (has_animation && (num_loop_chunks == 0 || num_frames == 0)) {
|
||||
if (has_animation && (num_anim == 0 || num_frames == 0)) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
if (!has_animation && (num_loop_chunks == 1 || num_frames > 0)) {
|
||||
if (!has_animation && (num_anim == 1 || num_frames > 0)) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
// Tiling: TILE_FLAG and tile chunk(s) are consistent.
|
||||
err = ValidateChunk(mux, IDX_TILE, TILE_FLAG, flags, -1, &num_tiles);
|
||||
// Fragmentation: FRAGMENTS_FLAG and FRGM chunk(s) are consistent.
|
||||
err = ValidateChunk(mux, IDX_FRGM, FRAGMENTS_FLAG, flags, -1, &num_fragments);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
|
||||
// Verify either VP8X chunk is present OR there is only one elem in
|
||||
@ -551,16 +523,22 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
|
||||
if (num_vp8x == 0 && num_images != 1) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
|
||||
// ALPHA_FLAG & alpha chunk(s) are consistent.
|
||||
if (num_vp8x > 0 && MuxHasLosslessImages(mux->images_)) {
|
||||
// Special case: we have a VP8X chunk as well as some lossless images.
|
||||
if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
} else {
|
||||
err = ValidateChunk(mux, IDX_ALPHA, ALPHA_FLAG, flags, -1, &num_alpha);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (MuxHasAlpha(mux->images_)) {
|
||||
if (num_vp8x > 0) {
|
||||
// VP8X chunk is present, so it should contain ALPHA_FLAG.
|
||||
if (!(flags & ALPHA_FLAG)) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
} else {
|
||||
// VP8X chunk is not present, so ALPH chunks should NOT be present either.
|
||||
err = WebPMuxNumChunks(mux, WEBP_CHUNK_ALPHA, &num_alpha);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (num_alpha > 0) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
} else { // Mux doesn't need alpha. So, ALPHA_FLAG should NOT be present.
|
||||
if (flags & ALPHA_FLAG) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// num_tiles & num_images are consistent.
|
||||
if (num_tiles > 0 && num_images != num_tiles) {
|
||||
// num_fragments & num_images are consistent.
|
||||
if (num_fragments > 0 && num_images != num_fragments) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
@ -571,6 +549,3 @@ WebPMuxError MuxValidate(const WebPMux* const mux) {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Read APIs for mux.
|
||||
@ -12,10 +14,7 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include "./muxi.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "../utils/utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helper method(s).
|
||||
@ -41,8 +40,9 @@ static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
|
||||
|
||||
SWITCH_ID_LIST(IDX_VP8X, mux->vp8x_);
|
||||
SWITCH_ID_LIST(IDX_ICCP, mux->iccp_);
|
||||
SWITCH_ID_LIST(IDX_LOOP, mux->loop_);
|
||||
SWITCH_ID_LIST(IDX_META, mux->meta_);
|
||||
SWITCH_ID_LIST(IDX_ANIM, mux->anim_);
|
||||
SWITCH_ID_LIST(IDX_EXIF, mux->exif_);
|
||||
SWITCH_ID_LIST(IDX_XMP, mux->xmp_);
|
||||
SWITCH_ID_LIST(IDX_UNKNOWN, mux->unknown_);
|
||||
return WEBP_MUX_NOT_FOUND;
|
||||
}
|
||||
@ -50,15 +50,14 @@ static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx,
|
||||
|
||||
// Fill the chunk with the given data (includes chunk header bytes), after some
|
||||
// verifications.
|
||||
static WebPMuxError ChunkVerifyAndAssignData(WebPChunk* chunk,
|
||||
const uint8_t* data,
|
||||
size_t data_size, size_t riff_size,
|
||||
int copy_data) {
|
||||
static WebPMuxError ChunkVerifyAndAssign(WebPChunk* chunk,
|
||||
const uint8_t* data, size_t data_size,
|
||||
size_t riff_size, int copy_data) {
|
||||
uint32_t chunk_size;
|
||||
WebPData chunk_data;
|
||||
|
||||
// Sanity checks.
|
||||
if (data_size < TAG_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA;
|
||||
if (data_size < CHUNK_HEADER_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA;
|
||||
chunk_size = GetLE32(data + TAG_SIZE);
|
||||
|
||||
{
|
||||
@ -68,11 +67,103 @@ static WebPMuxError ChunkVerifyAndAssignData(WebPChunk* chunk,
|
||||
}
|
||||
|
||||
// Data assignment.
|
||||
chunk_data.bytes_ = data + CHUNK_HEADER_SIZE;
|
||||
chunk_data.size_ = chunk_size;
|
||||
chunk_data.bytes = data + CHUNK_HEADER_SIZE;
|
||||
chunk_data.size = chunk_size;
|
||||
return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0));
|
||||
}
|
||||
|
||||
int MuxImageFinalize(WebPMuxImage* const wpi) {
|
||||
const WebPChunk* const img = wpi->img_;
|
||||
const WebPData* const image = &img->data_;
|
||||
const int is_lossless = (img->tag_ == kChunks[IDX_VP8L].tag);
|
||||
int w, h;
|
||||
int vp8l_has_alpha = 0;
|
||||
const int ok = is_lossless ?
|
||||
VP8LGetInfo(image->bytes, image->size, &w, &h, &vp8l_has_alpha) :
|
||||
VP8GetInfo(image->bytes, image->size, image->size, &w, &h);
|
||||
assert(img != NULL);
|
||||
if (ok) {
|
||||
// Ignore ALPH chunk accompanying VP8L.
|
||||
if (is_lossless && (wpi->alpha_ != NULL)) {
|
||||
ChunkDelete(wpi->alpha_);
|
||||
wpi->alpha_ = NULL;
|
||||
}
|
||||
wpi->width_ = w;
|
||||
wpi->height_ = h;
|
||||
wpi->has_alpha_ = vp8l_has_alpha || (wpi->alpha_ != NULL);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int MuxImageParse(const WebPChunk* const chunk, int copy_data,
|
||||
WebPMuxImage* const wpi) {
|
||||
const uint8_t* bytes = chunk->data_.bytes;
|
||||
size_t size = chunk->data_.size;
|
||||
const uint8_t* const last = bytes + size;
|
||||
WebPChunk subchunk;
|
||||
size_t subchunk_size;
|
||||
ChunkInit(&subchunk);
|
||||
|
||||
assert(chunk->tag_ == kChunks[IDX_ANMF].tag ||
|
||||
chunk->tag_ == kChunks[IDX_FRGM].tag);
|
||||
assert(!wpi->is_partial_);
|
||||
|
||||
// ANMF/FRGM.
|
||||
{
|
||||
const size_t hdr_size = (chunk->tag_ == kChunks[IDX_ANMF].tag) ?
|
||||
ANMF_CHUNK_SIZE : FRGM_CHUNK_SIZE;
|
||||
const WebPData temp = { bytes, hdr_size };
|
||||
// Each of ANMF and FRGM chunk contain a header at the beginning. So, its
|
||||
// size should at least be 'hdr_size'.
|
||||
if (size < hdr_size) goto Fail;
|
||||
ChunkAssignData(&subchunk, &temp, copy_data, chunk->tag_);
|
||||
}
|
||||
ChunkSetNth(&subchunk, &wpi->header_, 1);
|
||||
wpi->is_partial_ = 1; // Waiting for ALPH and/or VP8/VP8L chunks.
|
||||
|
||||
// Rest of the chunks.
|
||||
subchunk_size = ChunkDiskSize(&subchunk) - CHUNK_HEADER_SIZE;
|
||||
bytes += subchunk_size;
|
||||
size -= subchunk_size;
|
||||
|
||||
while (bytes != last) {
|
||||
ChunkInit(&subchunk);
|
||||
if (ChunkVerifyAndAssign(&subchunk, bytes, size, size,
|
||||
copy_data) != WEBP_MUX_OK) {
|
||||
goto Fail;
|
||||
}
|
||||
switch (ChunkGetIdFromTag(subchunk.tag_)) {
|
||||
case WEBP_CHUNK_ALPHA:
|
||||
if (wpi->alpha_ != NULL) goto Fail; // Consecutive ALPH chunks.
|
||||
if (ChunkSetNth(&subchunk, &wpi->alpha_, 1) != WEBP_MUX_OK) goto Fail;
|
||||
wpi->is_partial_ = 1; // Waiting for a VP8 chunk.
|
||||
break;
|
||||
case WEBP_CHUNK_IMAGE:
|
||||
if (ChunkSetNth(&subchunk, &wpi->img_, 1) != WEBP_MUX_OK) goto Fail;
|
||||
if (!MuxImageFinalize(wpi)) goto Fail;
|
||||
wpi->is_partial_ = 0; // wpi is completely filled.
|
||||
break;
|
||||
case WEBP_CHUNK_UNKNOWN:
|
||||
if (wpi->is_partial_) goto Fail; // Encountered an unknown chunk
|
||||
// before some image chunks.
|
||||
if (ChunkSetNth(&subchunk, &wpi->unknown_, 0) != WEBP_MUX_OK) goto Fail;
|
||||
break;
|
||||
default:
|
||||
goto Fail;
|
||||
break;
|
||||
}
|
||||
subchunk_size = ChunkDiskSize(&subchunk);
|
||||
bytes += subchunk_size;
|
||||
size -= subchunk_size;
|
||||
}
|
||||
if (wpi->is_partial_) goto Fail;
|
||||
return 1;
|
||||
|
||||
Fail:
|
||||
ChunkRelease(&subchunk);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Create a mux object from WebP-RIFF data.
|
||||
|
||||
@ -94,8 +185,8 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
||||
}
|
||||
if (bitstream == NULL) return NULL;
|
||||
|
||||
data = bitstream->bytes_;
|
||||
size = bitstream->size_;
|
||||
data = bitstream->bytes;
|
||||
size = bitstream->size;
|
||||
|
||||
if (data == NULL) return NULL;
|
||||
if (size < RIFF_HEADER_SIZE) return NULL;
|
||||
@ -129,48 +220,55 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
||||
data += RIFF_HEADER_SIZE;
|
||||
size -= RIFF_HEADER_SIZE;
|
||||
|
||||
wpi = (WebPMuxImage*)malloc(sizeof(*wpi));
|
||||
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;
|
||||
WebPMuxError err;
|
||||
|
||||
err = ChunkVerifyAndAssignData(&chunk, data, size, riff_size, copy_data);
|
||||
if (err != WEBP_MUX_OK) goto Err;
|
||||
|
||||
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_);
|
||||
|
||||
if (IsWPI(id)) { // An image chunk (frame/tile/alpha/vp8).
|
||||
WebPChunk** wpi_chunk_ptr =
|
||||
MuxImageGetListFromId(wpi, id); // Image chunk to set.
|
||||
assert(wpi_chunk_ptr != NULL);
|
||||
if (*wpi_chunk_ptr != NULL) goto Err; // Consecutive alpha chunks or
|
||||
// consecutive frame/tile chunks.
|
||||
if (ChunkSetNth(&chunk, wpi_chunk_ptr, 1) != WEBP_MUX_OK) goto Err;
|
||||
if (id == WEBP_CHUNK_IMAGE) {
|
||||
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.
|
||||
} else {
|
||||
wpi->is_partial_ = 1; // wpi is only partially filled.
|
||||
}
|
||||
} else { // A non-image chunk.
|
||||
WebPChunk** chunk_list;
|
||||
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 (chunk_list == NULL) chunk_list = &mux->unknown_;
|
||||
if (ChunkSetNth(&chunk, chunk_list, 0) != WEBP_MUX_OK) goto Err;
|
||||
}
|
||||
{
|
||||
const size_t data_size = ChunkDiskSize(&chunk);
|
||||
data += data_size;
|
||||
size -= data_size;
|
||||
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);
|
||||
}
|
||||
|
||||
@ -190,31 +288,74 @@ WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data,
|
||||
//------------------------------------------------------------------------------
|
||||
// Get API(s).
|
||||
|
||||
WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, uint32_t* flags) {
|
||||
WebPData data;
|
||||
WebPMuxError err;
|
||||
// Validates that the given mux has a single image.
|
||||
static WebPMuxError ValidateForSingleImage(const WebPMux* const mux) {
|
||||
const int num_images = MuxImageCount(mux->images_, WEBP_CHUNK_IMAGE);
|
||||
const int num_frames = MuxImageCount(mux->images_, WEBP_CHUNK_ANMF);
|
||||
const int num_fragments = MuxImageCount(mux->images_, WEBP_CHUNK_FRGM);
|
||||
|
||||
if (mux == NULL || flags == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
*flags = 0;
|
||||
if (num_images == 0) {
|
||||
// No images in mux.
|
||||
return WEBP_MUX_NOT_FOUND;
|
||||
} else if (num_images == 1 && num_frames == 0 && num_fragments == 0) {
|
||||
// Valid case (single image).
|
||||
return WEBP_MUX_OK;
|
||||
} else {
|
||||
// Frame/Fragment case OR an invalid mux.
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the canvas width, height and flags after validating that VP8X/VP8/VP8L
|
||||
// chunk and canvas size are valid.
|
||||
static WebPMuxError MuxGetCanvasInfo(const WebPMux* const mux,
|
||||
int* width, int* height, uint32_t* flags) {
|
||||
int w, h;
|
||||
uint32_t f = 0;
|
||||
WebPData data;
|
||||
assert(mux != NULL);
|
||||
|
||||
// Check if VP8X chunk is present.
|
||||
err = MuxGet(mux, IDX_VP8X, 1, &data);
|
||||
if (err == WEBP_MUX_NOT_FOUND) {
|
||||
// Check if VP8/VP8L chunk is present.
|
||||
err = WebPMuxGetImage(mux, &data);
|
||||
WebPDataClear(&data);
|
||||
return err;
|
||||
} else if (err != WEBP_MUX_OK) {
|
||||
return err;
|
||||
if (MuxGet(mux, IDX_VP8X, 1, &data) == WEBP_MUX_OK) {
|
||||
if (data.size < VP8X_CHUNK_SIZE) return WEBP_MUX_BAD_DATA;
|
||||
f = GetLE32(data.bytes + 0);
|
||||
w = GetLE24(data.bytes + 4) + 1;
|
||||
h = GetLE24(data.bytes + 7) + 1;
|
||||
} else {
|
||||
const WebPMuxImage* const wpi = mux->images_;
|
||||
// Grab user-forced canvas size as default.
|
||||
w = mux->canvas_width_;
|
||||
h = mux->canvas_height_;
|
||||
if (w == 0 && h == 0 && ValidateForSingleImage(mux) == WEBP_MUX_OK) {
|
||||
// single image and not forced canvas size => use dimension of first frame
|
||||
assert(wpi != NULL);
|
||||
w = wpi->width_;
|
||||
h = wpi->height_;
|
||||
}
|
||||
if (wpi != NULL) {
|
||||
if (wpi->has_alpha_) f |= ALPHA_FLAG;
|
||||
}
|
||||
}
|
||||
if (w * (uint64_t)h >= MAX_IMAGE_AREA) return WEBP_MUX_BAD_DATA;
|
||||
|
||||
if (data.size_ < CHUNK_SIZE_BYTES) return WEBP_MUX_BAD_DATA;
|
||||
|
||||
// All OK. Fill up flags.
|
||||
*flags = GetLE32(data.bytes_);
|
||||
if (width != NULL) *width = w;
|
||||
if (height != NULL) *height = h;
|
||||
if (flags != NULL) *flags = f;
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetCanvasSize(const WebPMux* mux, int* width, int* height) {
|
||||
if (mux == NULL || width == NULL || height == NULL) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
return MuxGetCanvasInfo(mux, width, height, NULL);
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, uint32_t* flags) {
|
||||
if (mux == NULL || flags == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
return MuxGetCanvasInfo(mux, NULL, NULL, flags);
|
||||
}
|
||||
|
||||
static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width,
|
||||
int height, uint32_t flags) {
|
||||
const size_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
|
||||
@ -230,7 +371,7 @@ static uint8_t* EmitVP8XChunk(uint8_t* const dst, int width,
|
||||
}
|
||||
|
||||
// Assemble a single image WebP bitstream from 'wpi'.
|
||||
static WebPMuxError SynthesizeBitstream(WebPMuxImage* const wpi,
|
||||
static WebPMuxError SynthesizeBitstream(const WebPMuxImage* const wpi,
|
||||
WebPData* const bitstream) {
|
||||
uint8_t* dst;
|
||||
|
||||
@ -238,25 +379,17 @@ static WebPMuxError SynthesizeBitstream(WebPMuxImage* const wpi,
|
||||
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 FRM/TILE chunk for a single image.
|
||||
// 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*)malloc(size);
|
||||
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) {
|
||||
int w, h;
|
||||
WebPMuxError err;
|
||||
assert(wpi->img_ != NULL);
|
||||
err = MuxGetImageWidthHeight(wpi->img_, &w, &h);
|
||||
if (err != WEBP_MUX_OK) {
|
||||
free(data);
|
||||
return err;
|
||||
}
|
||||
dst = EmitVP8XChunk(dst, w, h, ALPHA_FLAG); // VP8X.
|
||||
dst = EmitVP8XChunk(dst, wpi->width_, wpi->height_, ALPHA_FLAG); // VP8X.
|
||||
dst = ChunkListEmit(wpi->alpha_, dst); // ALPH.
|
||||
}
|
||||
|
||||
@ -265,107 +398,115 @@ static WebPMuxError SynthesizeBitstream(WebPMuxImage* const wpi,
|
||||
assert(dst == data + size);
|
||||
|
||||
// Output.
|
||||
bitstream->bytes_ = data;
|
||||
bitstream->size_ = size;
|
||||
bitstream->bytes = data;
|
||||
bitstream->size = size;
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetImage(const WebPMux* mux, WebPData* bitstream) {
|
||||
WebPMuxError err;
|
||||
WebPMuxImage* wpi = NULL;
|
||||
|
||||
if (mux == NULL || bitstream == NULL) {
|
||||
WebPMuxError WebPMuxGetChunk(const WebPMux* mux, const char fourcc[4],
|
||||
WebPData* chunk_data) {
|
||||
CHUNK_INDEX idx;
|
||||
if (mux == NULL || fourcc == NULL || chunk_data == NULL) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
err = MuxValidateForImage(mux);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
|
||||
// All well. Get the image.
|
||||
err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, 1, WEBP_CHUNK_IMAGE,
|
||||
&wpi);
|
||||
assert(err == WEBP_MUX_OK); // Already tested above.
|
||||
|
||||
return SynthesizeBitstream(wpi, bitstream);
|
||||
idx = ChunkGetIndexFromFourCC(fourcc);
|
||||
if (IsWPI(kChunks[idx].id)) { // An image chunk.
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
} else if (idx != IDX_UNKNOWN) { // A known chunk type.
|
||||
return MuxGet(mux, idx, 1, chunk_data);
|
||||
} else { // An unknown chunk type.
|
||||
const WebPChunk* const chunk =
|
||||
ChunkSearchList(mux->unknown_, 1, ChunkGetTagFromFourCC(fourcc));
|
||||
if (chunk == NULL) return WEBP_MUX_NOT_FOUND;
|
||||
*chunk_data = chunk->data_;
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetMetadata(const WebPMux* mux, WebPData* metadata) {
|
||||
if (mux == NULL || metadata == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
return MuxGet(mux, IDX_META, 1, metadata);
|
||||
static WebPMuxError MuxGetImageInternal(const WebPMuxImage* const wpi,
|
||||
WebPMuxFrameInfo* const info) {
|
||||
// Set some defaults for unrelated fields.
|
||||
info->x_offset = 0;
|
||||
info->y_offset = 0;
|
||||
info->duration = 1;
|
||||
info->dispose_method = WEBP_MUX_DISPOSE_NONE;
|
||||
info->blend_method = WEBP_MUX_BLEND;
|
||||
// Extract data for related fields.
|
||||
info->id = ChunkGetIdFromTag(wpi->img_->tag_);
|
||||
return SynthesizeBitstream(wpi, &info->bitstream);
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetColorProfile(const WebPMux* mux,
|
||||
WebPData* color_profile) {
|
||||
if (mux == NULL || color_profile == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
return MuxGet(mux, IDX_ICCP, 1, color_profile);
|
||||
static WebPMuxError MuxGetFrameFragmentInternal(const WebPMuxImage* const wpi,
|
||||
WebPMuxFrameInfo* const frame) {
|
||||
const int is_frame = (wpi->header_->tag_ == kChunks[IDX_ANMF].tag);
|
||||
const CHUNK_INDEX idx = is_frame ? IDX_ANMF : IDX_FRGM;
|
||||
const WebPData* frame_frgm_data;
|
||||
if (!is_frame) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
assert(wpi->header_ != NULL); // Already checked by WebPMuxGetFrame().
|
||||
// Get frame/fragment chunk.
|
||||
frame_frgm_data = &wpi->header_->data_;
|
||||
if (frame_frgm_data->size < kChunks[idx].size) return WEBP_MUX_BAD_DATA;
|
||||
// Extract info.
|
||||
frame->x_offset = 2 * GetLE24(frame_frgm_data->bytes + 0);
|
||||
frame->y_offset = 2 * GetLE24(frame_frgm_data->bytes + 3);
|
||||
if (is_frame) {
|
||||
const uint8_t bits = frame_frgm_data->bytes[15];
|
||||
frame->duration = GetLE24(frame_frgm_data->bytes + 12);
|
||||
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;
|
||||
} else { // Defaults for unused values.
|
||||
frame->duration = 1;
|
||||
frame->dispose_method = WEBP_MUX_DISPOSE_NONE;
|
||||
frame->blend_method = WEBP_MUX_BLEND;
|
||||
}
|
||||
frame->id = ChunkGetIdFromTag(wpi->header_->tag_);
|
||||
return SynthesizeBitstream(wpi, &frame->bitstream);
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetLoopCount(const WebPMux* mux, int* loop_count) {
|
||||
WebPData image;
|
||||
WebPMuxError err;
|
||||
|
||||
if (mux == NULL || loop_count == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
|
||||
err = MuxGet(mux, IDX_LOOP, 1, &image);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (image.size_ < kChunks[WEBP_CHUNK_LOOP].size) return WEBP_MUX_BAD_DATA;
|
||||
*loop_count = GetLE16(image.bytes_);
|
||||
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
static WebPMuxError MuxGetFrameTileInternal(
|
||||
const WebPMux* const mux, uint32_t nth, WebPData* const bitstream,
|
||||
int* const x_offset, int* const y_offset, int* const duration,
|
||||
uint32_t tag) {
|
||||
const WebPData* frame_tile_data;
|
||||
WebPMuxError WebPMuxGetFrame(
|
||||
const WebPMux* mux, uint32_t nth, WebPMuxFrameInfo* frame) {
|
||||
WebPMuxError err;
|
||||
WebPMuxImage* wpi;
|
||||
|
||||
const int is_frame = (tag == kChunks[WEBP_CHUNK_FRAME].tag) ? 1 : 0;
|
||||
const CHUNK_INDEX idx = is_frame ? IDX_FRAME : IDX_TILE;
|
||||
const WebPChunkId id = kChunks[idx].id;
|
||||
|
||||
if (mux == NULL || bitstream == NULL ||
|
||||
x_offset == NULL || y_offset == NULL || (is_frame && duration == NULL)) {
|
||||
// Sanity checks.
|
||||
if (mux == NULL || frame == NULL) {
|
||||
return WEBP_MUX_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
// Get the nth WebPMuxImage.
|
||||
err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, id, &wpi);
|
||||
err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, &wpi);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
|
||||
// Get frame chunk.
|
||||
assert(wpi->header_ != NULL); // As MuxImageGetNth() already checked header_.
|
||||
frame_tile_data = &wpi->header_->data_;
|
||||
|
||||
if (frame_tile_data->size_ < kChunks[idx].size) return WEBP_MUX_BAD_DATA;
|
||||
*x_offset = 2 * GetLE24(frame_tile_data->bytes_ + 0);
|
||||
*y_offset = 2 * GetLE24(frame_tile_data->bytes_ + 3);
|
||||
if (is_frame) *duration = 1 + GetLE24(frame_tile_data->bytes_ + 12);
|
||||
|
||||
return SynthesizeBitstream(wpi, bitstream);
|
||||
// Get frame info.
|
||||
if (wpi->header_ == NULL) {
|
||||
return MuxGetImageInternal(wpi, frame);
|
||||
} else {
|
||||
return MuxGetFrameFragmentInternal(wpi, frame);
|
||||
}
|
||||
}
|
||||
|
||||
WebPMuxError WebPMuxGetFrame(const WebPMux* mux, uint32_t nth,
|
||||
WebPData* bitstream,
|
||||
int* x_offset, int* y_offset, int* duration) {
|
||||
return MuxGetFrameTileInternal(mux, nth, bitstream, x_offset, y_offset,
|
||||
duration, kChunks[IDX_FRAME].tag);
|
||||
}
|
||||
WebPMuxError WebPMuxGetAnimationParams(const WebPMux* mux,
|
||||
WebPMuxAnimParams* params) {
|
||||
WebPData anim;
|
||||
WebPMuxError err;
|
||||
|
||||
WebPMuxError WebPMuxGetTile(const WebPMux* mux, uint32_t nth,
|
||||
WebPData* bitstream,
|
||||
int* x_offset, int* y_offset) {
|
||||
return MuxGetFrameTileInternal(mux, nth, bitstream, x_offset, y_offset, NULL,
|
||||
kChunks[IDX_TILE].tag);
|
||||
if (mux == NULL || params == NULL) return WEBP_MUX_INVALID_ARGUMENT;
|
||||
|
||||
err = MuxGet(mux, IDX_ANIM, 1, &anim);
|
||||
if (err != WEBP_MUX_OK) return err;
|
||||
if (anim.size < kChunks[WEBP_CHUNK_ANIM].size) return WEBP_MUX_BAD_DATA;
|
||||
params->bgcolor = GetLE32(anim.bytes);
|
||||
params->loop_count = GetLE16(anim.bytes + 4);
|
||||
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
// Get chunk index from chunk id. Returns IDX_NIL if not found.
|
||||
static CHUNK_INDEX ChunkGetIndexFromId(WebPChunkId id) {
|
||||
int i;
|
||||
for (i = 0; kChunks[i].id != WEBP_CHUNK_NIL; ++i) {
|
||||
if (id == kChunks[i].id) return i;
|
||||
if (id == kChunks[i].id) return (CHUNK_INDEX)i;
|
||||
}
|
||||
return IDX_NIL;
|
||||
}
|
||||
@ -393,19 +534,11 @@ WebPMuxError WebPMuxNumChunks(const WebPMux* mux,
|
||||
*num_elements = MuxImageCount(mux->images_, id);
|
||||
} else {
|
||||
WebPChunk* const* chunk_list = MuxGetChunkListFromId(mux, id);
|
||||
if (chunk_list == NULL) {
|
||||
*num_elements = 0;
|
||||
} else {
|
||||
const CHUNK_INDEX idx = ChunkGetIndexFromId(id);
|
||||
*num_elements = CountChunks(*chunk_list, kChunks[idx].tag);
|
||||
}
|
||||
const CHUNK_INDEX idx = ChunkGetIndexFromId(id);
|
||||
*num_elements = CountChunks(*chunk_list, kChunks[idx].tag);
|
||||
}
|
||||
|
||||
return WEBP_MUX_OK;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Common types
|
||||
@ -16,10 +18,11 @@
|
||||
|
||||
#ifndef _MSC_VER
|
||||
#include <inttypes.h>
|
||||
#ifdef __STRICT_ANSI__
|
||||
#define WEBP_INLINE
|
||||
#else /* __STRICT_ANSI__ */
|
||||
#if defined(__cplusplus) || !defined(__STRICT_ANSI__) || \
|
||||
(defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L)
|
||||
#define WEBP_INLINE inline
|
||||
#else
|
||||
#define WEBP_INLINE
|
||||
#endif
|
||||
#else
|
||||
typedef signed char int8_t;
|
||||
@ -36,7 +39,11 @@ typedef long long int int64_t;
|
||||
#ifndef WEBP_EXTERN
|
||||
// This explicitly marks library functions and allows for changing the
|
||||
// signature for e.g., Windows DLL builds.
|
||||
#define WEBP_EXTERN(type) extern type
|
||||
# if defined(__GNUC__) && __GNUC__ >= 4
|
||||
# define WEBP_EXTERN(type) extern __attribute__ ((visibility ("default"))) type
|
||||
# else
|
||||
# define WEBP_EXTERN(type) extern type
|
||||
# endif /* __GNUC__ >= 4 */
|
||||
#endif /* WEBP_EXTERN */
|
||||
|
||||
// Macro to check ABI compatibility (same major revision number)
|
||||
|
||||
@ -1,36 +1,54 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Boolean decoder
|
||||
// Boolean decoder non-inlined methods
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "./bit_reader.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../webp/config.h"
|
||||
#endif
|
||||
|
||||
#define MK(X) (((bit_t)(X) << (BITS)) | (MASK))
|
||||
#include "./bit_reader_inl.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8BitReader
|
||||
|
||||
void VP8BitReaderSetBuffer(VP8BitReader* const br,
|
||||
const uint8_t* const start,
|
||||
size_t size) {
|
||||
br->buf_ = start;
|
||||
br->buf_end_ = start + size;
|
||||
br->buf_max_ =
|
||||
(size >= sizeof(lbit_t)) ? start + size - sizeof(lbit_t) + 1
|
||||
: start;
|
||||
}
|
||||
|
||||
void VP8InitBitReader(VP8BitReader* const br,
|
||||
const uint8_t* const start, const uint8_t* const end) {
|
||||
const uint8_t* const start, size_t size) {
|
||||
assert(br != NULL);
|
||||
assert(start != NULL);
|
||||
assert(start <= end);
|
||||
br->range_ = MK(255 - 1);
|
||||
br->buf_ = start;
|
||||
br->buf_end_ = end;
|
||||
assert(size < (1u << 31)); // limit ensured by format and upstream checks
|
||||
br->range_ = 255 - 1;
|
||||
br->value_ = 0;
|
||||
br->missing_ = 8; // to load the very first 8bits
|
||||
br->bits_ = -8; // to load the very first 8bits
|
||||
br->eof_ = 0;
|
||||
VP8BitReaderSetBuffer(br, start, size);
|
||||
VP8LoadNewBytes(br);
|
||||
}
|
||||
|
||||
void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) {
|
||||
if (br->buf_ != NULL) {
|
||||
br->buf_ += offset;
|
||||
br->buf_end_ += offset;
|
||||
br->buf_max_ += offset;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t kVP8Log2Range[128] = {
|
||||
@ -45,36 +63,38 @@ const uint8_t kVP8Log2Range[128] = {
|
||||
0
|
||||
};
|
||||
|
||||
// range = (range << kVP8Log2Range[range]) + trailing 1's
|
||||
const bit_t kVP8NewRange[128] = {
|
||||
MK(127), MK(127), MK(191), MK(127), MK(159), MK(191), MK(223), MK(127),
|
||||
MK(143), MK(159), MK(175), MK(191), MK(207), MK(223), MK(239), MK(127),
|
||||
MK(135), MK(143), MK(151), MK(159), MK(167), MK(175), MK(183), MK(191),
|
||||
MK(199), MK(207), MK(215), MK(223), MK(231), MK(239), MK(247), MK(127),
|
||||
MK(131), MK(135), MK(139), MK(143), MK(147), MK(151), MK(155), MK(159),
|
||||
MK(163), MK(167), MK(171), MK(175), MK(179), MK(183), MK(187), MK(191),
|
||||
MK(195), MK(199), MK(203), MK(207), MK(211), MK(215), MK(219), MK(223),
|
||||
MK(227), MK(231), MK(235), MK(239), MK(243), MK(247), MK(251), MK(127),
|
||||
MK(129), MK(131), MK(133), MK(135), MK(137), MK(139), MK(141), MK(143),
|
||||
MK(145), MK(147), MK(149), MK(151), MK(153), MK(155), MK(157), MK(159),
|
||||
MK(161), MK(163), MK(165), MK(167), MK(169), MK(171), MK(173), MK(175),
|
||||
MK(177), MK(179), MK(181), MK(183), MK(185), MK(187), MK(189), MK(191),
|
||||
MK(193), MK(195), MK(197), MK(199), MK(201), MK(203), MK(205), MK(207),
|
||||
MK(209), MK(211), MK(213), MK(215), MK(217), MK(219), MK(221), MK(223),
|
||||
MK(225), MK(227), MK(229), MK(231), MK(233), MK(235), MK(237), MK(239),
|
||||
MK(241), MK(243), MK(245), MK(247), MK(249), MK(251), MK(253), MK(127)
|
||||
// range = ((range - 1) << kVP8Log2Range[range]) + 1
|
||||
const uint8_t kVP8NewRange[128] = {
|
||||
127, 127, 191, 127, 159, 191, 223, 127,
|
||||
143, 159, 175, 191, 207, 223, 239, 127,
|
||||
135, 143, 151, 159, 167, 175, 183, 191,
|
||||
199, 207, 215, 223, 231, 239, 247, 127,
|
||||
131, 135, 139, 143, 147, 151, 155, 159,
|
||||
163, 167, 171, 175, 179, 183, 187, 191,
|
||||
195, 199, 203, 207, 211, 215, 219, 223,
|
||||
227, 231, 235, 239, 243, 247, 251, 127,
|
||||
129, 131, 133, 135, 137, 139, 141, 143,
|
||||
145, 147, 149, 151, 153, 155, 157, 159,
|
||||
161, 163, 165, 167, 169, 171, 173, 175,
|
||||
177, 179, 181, 183, 185, 187, 189, 191,
|
||||
193, 195, 197, 199, 201, 203, 205, 207,
|
||||
209, 211, 213, 215, 217, 219, 221, 223,
|
||||
225, 227, 229, 231, 233, 235, 237, 239,
|
||||
241, 243, 245, 247, 249, 251, 253, 127
|
||||
};
|
||||
|
||||
#undef MK
|
||||
|
||||
void VP8LoadFinalBytes(VP8BitReader* const br) {
|
||||
assert(br != NULL && br->buf_ != NULL);
|
||||
// Only read 8bits at a time
|
||||
if (br->buf_ < br->buf_end_) {
|
||||
br->value_ |= (bit_t)(*br->buf_++) << ((BITS) - 8 + br->missing_);
|
||||
br->missing_ -= 8;
|
||||
} else {
|
||||
br->bits_ += 8;
|
||||
br->value_ = (bit_t)(*br->buf_++) | (br->value_ << 8);
|
||||
} else if (!br->eof_) {
|
||||
br->value_ <<= 8;
|
||||
br->bits_ += 8;
|
||||
br->eof_ = 1;
|
||||
} else {
|
||||
br->bits_ = 0; // This is to avoid undefined behaviour with shifts.
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,32 +117,47 @@ int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) {
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8LBitReader
|
||||
|
||||
#define MAX_NUM_BIT_READ 25
|
||||
#define VP8L_LOG8_WBITS 4 // Number of bytes needed to store VP8L_WBITS bits.
|
||||
|
||||
static const uint32_t kBitMask[MAX_NUM_BIT_READ] = {
|
||||
0, 1, 3, 7, 15, 31, 63, 127, 255, 511, 1023, 2047, 4095, 8191, 16383, 32767,
|
||||
65535, 131071, 262143, 524287, 1048575, 2097151, 4194303, 8388607, 16777215
|
||||
#if !defined(WEBP_FORCE_ALIGNED) && \
|
||||
(defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || \
|
||||
defined(__i386__) || defined(_M_IX86) || \
|
||||
defined(__x86_64__) || defined(_M_X64))
|
||||
#define VP8L_USE_UNALIGNED_LOAD
|
||||
#endif
|
||||
|
||||
static const uint32_t kBitMask[VP8L_MAX_NUM_BIT_READ + 1] = {
|
||||
0,
|
||||
0x000001, 0x000003, 0x000007, 0x00000f,
|
||||
0x00001f, 0x00003f, 0x00007f, 0x0000ff,
|
||||
0x0001ff, 0x0003ff, 0x0007ff, 0x000fff,
|
||||
0x001fff, 0x003fff, 0x007fff, 0x00ffff,
|
||||
0x01ffff, 0x03ffff, 0x07ffff, 0x0fffff,
|
||||
0x1fffff, 0x3fffff, 0x7fffff, 0xffffff
|
||||
};
|
||||
|
||||
void VP8LInitBitReader(VP8LBitReader* const br,
|
||||
const uint8_t* const start,
|
||||
void VP8LInitBitReader(VP8LBitReader* const br, const uint8_t* const start,
|
||||
size_t length) {
|
||||
size_t i;
|
||||
vp8l_val_t value = 0;
|
||||
assert(br != NULL);
|
||||
assert(start != NULL);
|
||||
assert(length < 0xfffffff8u); // can't happen with a RIFF chunk.
|
||||
|
||||
br->buf_ = start;
|
||||
br->len_ = length;
|
||||
br->val_ = 0;
|
||||
br->pos_ = 0;
|
||||
br->bit_pos_ = 0;
|
||||
br->eos_ = 0;
|
||||
br->error_ = 0;
|
||||
for (i = 0; i < sizeof(br->val_) && i < br->len_; ++i) {
|
||||
br->val_ |= ((uint64_t)br->buf_[br->pos_]) << (8 * i);
|
||||
++br->pos_;
|
||||
|
||||
if (length > sizeof(br->val_)) {
|
||||
length = sizeof(br->val_);
|
||||
}
|
||||
for (i = 0; i < length; ++i) {
|
||||
value |= (vp8l_val_t)start[i] << (8 * i);
|
||||
}
|
||||
br->val_ = value;
|
||||
br->pos_ = length;
|
||||
br->buf_ = start;
|
||||
}
|
||||
|
||||
void VP8LBitReaderSetBuffer(VP8LBitReader* const br,
|
||||
@ -130,100 +165,62 @@ void VP8LBitReaderSetBuffer(VP8LBitReader* const br,
|
||||
assert(br != NULL);
|
||||
assert(buf != NULL);
|
||||
assert(len < 0xfffffff8u); // can't happen with a RIFF chunk.
|
||||
br->eos_ = (br->pos_ >= len);
|
||||
br->buf_ = buf;
|
||||
br->len_ = len;
|
||||
// pos_ > len_ should be considered a param error.
|
||||
br->eos_ = (br->pos_ > br->len_) || VP8LIsEndOfStream(br);
|
||||
}
|
||||
|
||||
static void VP8LSetEndOfStream(VP8LBitReader* const br) {
|
||||
br->eos_ = 1;
|
||||
br->bit_pos_ = 0; // To avoid undefined behaviour with shifts.
|
||||
}
|
||||
|
||||
// If not at EOS, reload up to VP8L_LBITS byte-by-byte
|
||||
static void ShiftBytes(VP8LBitReader* const br) {
|
||||
while (br->bit_pos_ >= 8 && br->pos_ < br->len_) {
|
||||
br->val_ >>= 8;
|
||||
br->val_ |= ((uint64_t)br->buf_[br->pos_]) << 56;
|
||||
br->val_ |= ((vp8l_val_t)br->buf_[br->pos_]) << (VP8L_LBITS - 8);
|
||||
++br->pos_;
|
||||
br->bit_pos_ -= 8;
|
||||
}
|
||||
if (VP8LIsEndOfStream(br)) {
|
||||
VP8LSetEndOfStream(br);
|
||||
}
|
||||
}
|
||||
|
||||
void VP8LFillBitWindow(VP8LBitReader* const br) {
|
||||
if (br->bit_pos_ >= 32) {
|
||||
#if defined(__x86_64__) || defined(_M_X64)
|
||||
if (br->pos_ + 8 < br->len_) {
|
||||
br->val_ >>= 32;
|
||||
// The expression below needs a little-endian arch to work correctly.
|
||||
// This gives a large speedup for decoding speed.
|
||||
br->val_ |= *(const uint64_t *)(br->buf_ + br->pos_) << 32;
|
||||
br->pos_ += 4;
|
||||
br->bit_pos_ -= 32;
|
||||
} else {
|
||||
// Slow path.
|
||||
ShiftBytes(br);
|
||||
}
|
||||
#else
|
||||
// Always the slow path.
|
||||
ShiftBytes(br);
|
||||
void VP8LDoFillBitWindow(VP8LBitReader* const br) {
|
||||
assert(br->bit_pos_ >= VP8L_WBITS);
|
||||
// TODO(jzern): given the fixed read size it may be possible to force
|
||||
// alignment in this block.
|
||||
#if defined(VP8L_USE_UNALIGNED_LOAD)
|
||||
if (br->pos_ + sizeof(br->val_) < br->len_) {
|
||||
br->val_ >>= VP8L_WBITS;
|
||||
br->bit_pos_ -= VP8L_WBITS;
|
||||
// The expression below needs a little-endian arch to work correctly.
|
||||
// This gives a large speedup for decoding speed.
|
||||
br->val_ |= (vp8l_val_t)*(const uint32_t*)(br->buf_ + br->pos_) <<
|
||||
(VP8L_LBITS - VP8L_WBITS);
|
||||
br->pos_ += VP8L_LOG8_WBITS;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (br->pos_ == br->len_ && br->bit_pos_ == 64) {
|
||||
br->eos_ = 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t VP8LReadOneBit(VP8LBitReader* const br) {
|
||||
const uint32_t val = (br->val_ >> br->bit_pos_) & 1;
|
||||
// Flag an error at end_of_stream.
|
||||
if (!br->eos_) {
|
||||
++br->bit_pos_;
|
||||
if (br->bit_pos_ >= 32) {
|
||||
ShiftBytes(br);
|
||||
}
|
||||
// After this last bit is read, check if eos needs to be flagged.
|
||||
if (br->pos_ == br->len_ && br->bit_pos_ == 64) {
|
||||
br->eos_ = 1;
|
||||
}
|
||||
} else {
|
||||
br->error_ = 1;
|
||||
}
|
||||
return val;
|
||||
ShiftBytes(br); // Slow path.
|
||||
}
|
||||
|
||||
uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits) {
|
||||
uint32_t val = 0;
|
||||
assert(n_bits >= 0);
|
||||
// Flag an error if end_of_stream or n_bits is more than allowed limit.
|
||||
if (!br->eos_ && n_bits < MAX_NUM_BIT_READ) {
|
||||
// If this read is going to cross the read buffer, set the eos flag.
|
||||
if (br->pos_ == br->len_) {
|
||||
if ((br->bit_pos_ + n_bits) >= 64) {
|
||||
br->eos_ = 1;
|
||||
if ((br->bit_pos_ + n_bits) > 64) return val;
|
||||
}
|
||||
}
|
||||
val = (br->val_ >> br->bit_pos_) & kBitMask[n_bits];
|
||||
br->bit_pos_ += n_bits;
|
||||
if (br->bit_pos_ >= 40) {
|
||||
if (br->pos_ + 5 < br->len_) {
|
||||
br->val_ >>= 40;
|
||||
br->val_ |=
|
||||
(((uint64_t)br->buf_[br->pos_ + 0]) << 24) |
|
||||
(((uint64_t)br->buf_[br->pos_ + 1]) << 32) |
|
||||
(((uint64_t)br->buf_[br->pos_ + 2]) << 40) |
|
||||
(((uint64_t)br->buf_[br->pos_ + 3]) << 48) |
|
||||
(((uint64_t)br->buf_[br->pos_ + 4]) << 56);
|
||||
br->pos_ += 5;
|
||||
br->bit_pos_ -= 40;
|
||||
}
|
||||
if (br->bit_pos_ >= 8) {
|
||||
ShiftBytes(br);
|
||||
}
|
||||
}
|
||||
if (!br->eos_ && n_bits <= VP8L_MAX_NUM_BIT_READ) {
|
||||
const uint32_t val = VP8LPrefetchBits(br) & kBitMask[n_bits];
|
||||
const int new_bits = br->bit_pos_ + n_bits;
|
||||
br->bit_pos_ = new_bits;
|
||||
ShiftBytes(br);
|
||||
return val;
|
||||
} else {
|
||||
br->error_ = 1;
|
||||
VP8LSetEndOfStream(br);
|
||||
return 0;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
//
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Boolean decoder
|
||||
@ -18,44 +19,75 @@
|
||||
#ifdef _MSC_VER
|
||||
#include <stdlib.h> // _byteswap_ulong
|
||||
#endif
|
||||
#include <string.h> // For memcpy
|
||||
#include "../types.h"
|
||||
#include "../webp/types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define BITS 32 // can be 32, 16 or 8
|
||||
#define MASK ((((bit_t)1) << (BITS)) - 1)
|
||||
#if (BITS == 32)
|
||||
typedef uint64_t bit_t; // natural register type
|
||||
typedef uint32_t lbit_t; // natural type for memory I/O
|
||||
#elif (BITS == 16)
|
||||
typedef uint32_t bit_t;
|
||||
typedef uint16_t lbit_t;
|
||||
#else
|
||||
typedef uint32_t bit_t;
|
||||
typedef uint8_t lbit_t;
|
||||
// The Boolean decoder needs to maintain infinite precision on the value_ field.
|
||||
// However, since range_ is only 8bit, we only need an active window of 8 bits
|
||||
// for value_. Left bits (MSB) gets zeroed and shifted away when value_ falls
|
||||
// below 128, range_ is updated, and fresh bits read from the bitstream are
|
||||
// brought in as LSB. To avoid reading the fresh bits one by one (slow), we
|
||||
// cache BITS of them ahead. The total of (BITS + 8) bits must fit into a
|
||||
// natural register (with type bit_t). To fetch BITS bits from bitstream we
|
||||
// use a type lbit_t.
|
||||
//
|
||||
// BITS can be any multiple of 8 from 8 to 56 (inclusive).
|
||||
// Pick values that fit natural register size.
|
||||
|
||||
#if defined(__i386__) || defined(_M_IX86) // x86 32bit
|
||||
#define BITS 24
|
||||
#elif defined(__x86_64__) || defined(_M_X64) // x86 64bit
|
||||
#define BITS 56
|
||||
#elif defined(__arm__) || defined(_M_ARM) // ARM
|
||||
#define BITS 24
|
||||
#elif defined(__mips__) // MIPS
|
||||
#define BITS 24
|
||||
#else // reasonable default
|
||||
#define BITS 24 // TODO(skal): test aarch64 and find the proper BITS value.
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Bitreader and code-tree reader
|
||||
// Derived types and constants:
|
||||
// bit_t = natural register type for storing 'value_' (which is BITS+8 bits)
|
||||
// range_t = register for 'range_' (which is 8bits only)
|
||||
|
||||
#if (BITS > 24)
|
||||
typedef uint64_t bit_t;
|
||||
#else
|
||||
typedef uint32_t bit_t;
|
||||
#endif
|
||||
|
||||
typedef uint32_t range_t;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Bitreader
|
||||
|
||||
typedef struct VP8BitReader VP8BitReader;
|
||||
struct VP8BitReader {
|
||||
// boolean decoder (keep the field ordering as is!)
|
||||
bit_t value_; // current value
|
||||
range_t range_; // current range minus 1. In [127, 254] interval.
|
||||
int bits_; // number of valid bits left
|
||||
// read buffer
|
||||
const uint8_t* buf_; // next byte to be read
|
||||
const uint8_t* buf_end_; // end of read buffer
|
||||
const uint8_t* buf_max_; // max packed-read position on buffer
|
||||
int eof_; // true if input is exhausted
|
||||
|
||||
// boolean decoder
|
||||
bit_t range_; // current range minus 1. In [127, 254] interval.
|
||||
bit_t value_; // current value
|
||||
int missing_; // number of missing bits in value_ (8bit)
|
||||
};
|
||||
|
||||
// Initialize the bit reader and the boolean decoder.
|
||||
void VP8InitBitReader(VP8BitReader* const br,
|
||||
const uint8_t* const start, const uint8_t* const end);
|
||||
const uint8_t* const start, size_t size);
|
||||
// Sets the working read buffer.
|
||||
void VP8BitReaderSetBuffer(VP8BitReader* const br,
|
||||
const uint8_t* const start, size_t size);
|
||||
|
||||
// Update internal pointers to displace the byte buffer by the
|
||||
// relative offset 'offset'.
|
||||
void VP8RemapBitReader(VP8BitReader* const br, ptrdiff_t offset);
|
||||
|
||||
// return the next value made of 'num_bits' bits
|
||||
uint32_t VP8GetValue(VP8BitReader* const br, int num_bits);
|
||||
@ -66,100 +98,31 @@ static WEBP_INLINE uint32_t VP8Get(VP8BitReader* const br) {
|
||||
// return the next value with sign-extension.
|
||||
int32_t VP8GetSignedValue(VP8BitReader* const br, int num_bits);
|
||||
|
||||
// Read a bit with proba 'prob'. Speed-critical function!
|
||||
extern const uint8_t kVP8Log2Range[128];
|
||||
extern const bit_t kVP8NewRange[128];
|
||||
|
||||
void VP8LoadFinalBytes(VP8BitReader* const br); // special case for the tail
|
||||
|
||||
static WEBP_INLINE void VP8LoadNewBytes(VP8BitReader* const br) {
|
||||
assert(br && br->buf_);
|
||||
// Read 'BITS' bits at a time if possible.
|
||||
if (br->buf_ + sizeof(lbit_t) <= br->buf_end_) {
|
||||
// convert memory type to register type (with some zero'ing!)
|
||||
bit_t bits;
|
||||
lbit_t in_bits = *(lbit_t*)br->buf_;
|
||||
br->buf_ += (BITS) >> 3;
|
||||
#if !defined(__BIG_ENDIAN__)
|
||||
#if (BITS == 32)
|
||||
#if defined(__i386__) || defined(__x86_64__)
|
||||
__asm__ volatile("bswap %k0" : "=r"(in_bits) : "0"(in_bits));
|
||||
bits = (bit_t)in_bits; // 32b -> 64b zero-extension
|
||||
#elif defined(_MSC_VER)
|
||||
bits = _byteswap_ulong(in_bits);
|
||||
#else
|
||||
bits = (bit_t)(in_bits >> 24) | ((in_bits >> 8) & 0xff00)
|
||||
| ((in_bits << 8) & 0xff0000) | (in_bits << 24);
|
||||
#endif // x86
|
||||
#elif (BITS == 16)
|
||||
// gcc will recognize a 'rorw $8, ...' here:
|
||||
bits = (bit_t)(in_bits >> 8) | ((in_bits & 0xff) << 8);
|
||||
#endif
|
||||
#else // LITTLE_ENDIAN
|
||||
bits = (bit_t)in_bits;
|
||||
#endif
|
||||
br->value_ |= bits << br->missing_;
|
||||
br->missing_ -= (BITS);
|
||||
} else {
|
||||
VP8LoadFinalBytes(br); // no need to be inlined
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8BitUpdate(VP8BitReader* const br, bit_t split) {
|
||||
const bit_t value_split = split | (MASK);
|
||||
if (br->missing_ > 0) { // Make sure we have a least BITS bits in 'value_'
|
||||
VP8LoadNewBytes(br);
|
||||
}
|
||||
if (br->value_ > value_split) {
|
||||
br->range_ -= value_split + 1;
|
||||
br->value_ -= value_split + 1;
|
||||
return 1;
|
||||
} else {
|
||||
br->range_ = value_split;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8Shift(VP8BitReader* const br) {
|
||||
// range_ is in [0..127] interval here.
|
||||
const int idx = br->range_ >> (BITS);
|
||||
const int shift = kVP8Log2Range[idx];
|
||||
br->range_ = kVP8NewRange[idx];
|
||||
br->value_ <<= shift;
|
||||
br->missing_ += shift;
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob) {
|
||||
// It's important to avoid generating a 64bit x 64bit multiply here.
|
||||
// We just need an 8b x 8b after all.
|
||||
const bit_t split =
|
||||
(bit_t)((uint32_t)(br->range_ >> (BITS)) * prob) << ((BITS) - 8);
|
||||
const int bit = VP8BitUpdate(br, split);
|
||||
if (br->range_ <= (((bit_t)0x7e << (BITS)) | (MASK))) {
|
||||
VP8Shift(br);
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
|
||||
static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v) {
|
||||
const bit_t split = (br->range_ >> 1);
|
||||
const int bit = VP8BitUpdate(br, split);
|
||||
VP8Shift(br);
|
||||
return bit ? -v : v;
|
||||
}
|
||||
|
||||
// bit_reader_inl.h will implement the following methods:
|
||||
// static WEBP_INLINE int VP8GetBit(VP8BitReader* const br, int prob)
|
||||
// static WEBP_INLINE int VP8GetSigned(VP8BitReader* const br, int v)
|
||||
// and should be included by the .c files that actually need them.
|
||||
// This is to avoid recompiling the whole library whenever this file is touched,
|
||||
// and also allowing platform-specific ad-hoc hacks.
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Bitreader
|
||||
// Bitreader for lossless format
|
||||
|
||||
// maximum number of bits (inclusive) the bit-reader can handle:
|
||||
#define VP8L_MAX_NUM_BIT_READ 24
|
||||
|
||||
#define VP8L_LBITS 64 // Number of bits prefetched (= bit-size of vp8l_val_t).
|
||||
#define VP8L_WBITS 32 // Minimum number of bytes ready after VP8LFillBitWindow.
|
||||
|
||||
typedef uint64_t vp8l_val_t; // right now, this bit-reader can only use 64bit.
|
||||
|
||||
typedef struct {
|
||||
uint64_t val_;
|
||||
const uint8_t* buf_;
|
||||
size_t len_;
|
||||
size_t pos_;
|
||||
int bit_pos_;
|
||||
int eos_;
|
||||
int error_;
|
||||
vp8l_val_t val_; // pre-fetched bits
|
||||
const uint8_t* buf_; // input byte buffer
|
||||
size_t len_; // buffer length
|
||||
size_t pos_; // byte position in buf_
|
||||
int bit_pos_; // current bit-reading position in val_
|
||||
int eos_; // true if a bit was read past the end of buffer
|
||||
} VP8LBitReader;
|
||||
|
||||
void VP8LInitBitReader(VP8LBitReader* const br,
|
||||
@ -170,28 +133,39 @@ void VP8LInitBitReader(VP8LBitReader* const br,
|
||||
void VP8LBitReaderSetBuffer(VP8LBitReader* const br,
|
||||
const uint8_t* const buffer, size_t length);
|
||||
|
||||
// Reads the specified number of bits from Read Buffer.
|
||||
// Flags an error in case end_of_stream or n_bits is more than allowed limit.
|
||||
// Flags eos if this read attempt is going to cross the read buffer.
|
||||
// Reads the specified number of bits from read buffer.
|
||||
// Flags an error in case end_of_stream or n_bits is more than the allowed limit
|
||||
// of VP8L_MAX_NUM_BIT_READ (inclusive).
|
||||
// Flags eos_ if this read attempt is going to cross the read buffer.
|
||||
uint32_t VP8LReadBits(VP8LBitReader* const br, int n_bits);
|
||||
|
||||
// Reads one bit from Read Buffer. Flags an error in case end_of_stream.
|
||||
// Flags eos after reading last bit from the buffer.
|
||||
uint32_t VP8LReadOneBit(VP8LBitReader* const br);
|
||||
|
||||
// VP8LReadOneBitUnsafe is faster than VP8LReadOneBit, but it can be called only
|
||||
// 32 times after the last VP8LFillBitWindow. Any subsequent calls
|
||||
// (without VP8LFillBitWindow) will return invalid data.
|
||||
static WEBP_INLINE uint32_t VP8LReadOneBitUnsafe(VP8LBitReader* const br) {
|
||||
const uint32_t val = (br->val_ >> br->bit_pos_) & 1;
|
||||
++br->bit_pos_;
|
||||
return val;
|
||||
// Return the prefetched bits, so they can be looked up.
|
||||
static WEBP_INLINE uint32_t VP8LPrefetchBits(VP8LBitReader* const br) {
|
||||
return (uint32_t)(br->val_ >> (br->bit_pos_ & (VP8L_LBITS - 1)));
|
||||
}
|
||||
|
||||
// Advances the Read buffer by 4 bytes to make room for reading next 32 bits.
|
||||
void VP8LFillBitWindow(VP8LBitReader* const br);
|
||||
// Returns true if there was an attempt at reading bit past the end of
|
||||
// the buffer. Doesn't set br->eos_ flag.
|
||||
static WEBP_INLINE int VP8LIsEndOfStream(const VP8LBitReader* const br) {
|
||||
assert(br->pos_ <= br->len_);
|
||||
return br->eos_ || ((br->pos_ == br->len_) && (br->bit_pos_ > VP8L_LBITS));
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
// For jumping over a number of bits in the bit stream when accessed with
|
||||
// VP8LPrefetchBits and VP8LFillBitWindow.
|
||||
static WEBP_INLINE void VP8LSetBitPos(VP8LBitReader* const br, int val) {
|
||||
br->bit_pos_ = val;
|
||||
br->eos_ = VP8LIsEndOfStream(br);
|
||||
}
|
||||
|
||||
// Advances the read buffer by 4 bytes to make room for reading next 32 bits.
|
||||
// Speed critical, but infrequent part of the code can be non-inlined.
|
||||
extern void VP8LDoFillBitWindow(VP8LBitReader* const br);
|
||||
static WEBP_INLINE void VP8LFillBitWindow(VP8LBitReader* const br) {
|
||||
if (br->bit_pos_ >= VP8L_WBITS) VP8LDoFillBitWindow(br);
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Bit writing and boolean coder
|
||||
@ -13,11 +15,10 @@
|
||||
#include <assert.h>
|
||||
#include <string.h> // for memcpy()
|
||||
#include <stdlib.h>
|
||||
#include "./bit_writer.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "./bit_writer.h"
|
||||
#include "./endian_inl.h"
|
||||
#include "./utils.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8BitWriter
|
||||
@ -36,19 +37,22 @@ static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) {
|
||||
new_size = 2 * bw->max_pos_;
|
||||
if (new_size < needed_size) new_size = needed_size;
|
||||
if (new_size < 1024) new_size = 1024;
|
||||
new_buf = (uint8_t*)malloc(new_size);
|
||||
new_buf = (uint8_t*)WebPSafeMalloc(1ULL, new_size);
|
||||
if (new_buf == NULL) {
|
||||
bw->error_ = 1;
|
||||
return 0;
|
||||
}
|
||||
memcpy(new_buf, bw->buf_, bw->pos_);
|
||||
free(bw->buf_);
|
||||
if (bw->pos_ > 0) {
|
||||
assert(bw->buf_ != NULL);
|
||||
memcpy(new_buf, bw->buf_, bw->pos_);
|
||||
}
|
||||
WebPSafeFree(bw->buf_);
|
||||
bw->buf_ = new_buf;
|
||||
bw->max_pos_ = new_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void kFlush(VP8BitWriter* const bw) {
|
||||
static void Flush(VP8BitWriter* const bw) {
|
||||
const int s = 8 + bw->nb_bits_;
|
||||
const int32_t bits = bw->value_ >> s;
|
||||
assert(bw->nb_bits_ >= 0);
|
||||
@ -114,7 +118,7 @@ int VP8PutBit(VP8BitWriter* const bw, int bit, int prob) {
|
||||
bw->range_ = kNewRange[bw->range_];
|
||||
bw->value_ <<= shift;
|
||||
bw->nb_bits_ += shift;
|
||||
if (bw->nb_bits_ > 0) kFlush(bw);
|
||||
if (bw->nb_bits_ > 0) Flush(bw);
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
@ -131,24 +135,25 @@ int VP8PutBitUniform(VP8BitWriter* const bw, int bit) {
|
||||
bw->range_ = kNewRange[bw->range_];
|
||||
bw->value_ <<= 1;
|
||||
bw->nb_bits_ += 1;
|
||||
if (bw->nb_bits_ > 0) kFlush(bw);
|
||||
if (bw->nb_bits_ > 0) Flush(bw);
|
||||
}
|
||||
return bit;
|
||||
}
|
||||
|
||||
void VP8PutValue(VP8BitWriter* const bw, int value, int nb_bits) {
|
||||
int mask;
|
||||
for (mask = 1 << (nb_bits - 1); mask; mask >>= 1)
|
||||
void VP8PutBits(VP8BitWriter* const bw, uint32_t value, int nb_bits) {
|
||||
uint32_t mask;
|
||||
assert(nb_bits > 0 && nb_bits < 32);
|
||||
for (mask = 1u << (nb_bits - 1); mask; mask >>= 1)
|
||||
VP8PutBitUniform(bw, value & mask);
|
||||
}
|
||||
|
||||
void VP8PutSignedValue(VP8BitWriter* const bw, int value, int nb_bits) {
|
||||
void VP8PutSignedBits(VP8BitWriter* const bw, int value, int nb_bits) {
|
||||
if (!VP8PutBitUniform(bw, value != 0))
|
||||
return;
|
||||
if (value < 0) {
|
||||
VP8PutValue(bw, ((-value) << 1) | 1, nb_bits + 1);
|
||||
VP8PutBits(bw, ((-value) << 1) | 1, nb_bits + 1);
|
||||
} else {
|
||||
VP8PutValue(bw, value << 1, nb_bits + 1);
|
||||
VP8PutBits(bw, value << 1, nb_bits + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -167,16 +172,16 @@ int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size) {
|
||||
}
|
||||
|
||||
uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw) {
|
||||
VP8PutValue(bw, 0, 9 - bw->nb_bits_);
|
||||
VP8PutBits(bw, 0, 9 - bw->nb_bits_);
|
||||
bw->nb_bits_ = 0; // pad with zeroes
|
||||
kFlush(bw);
|
||||
Flush(bw);
|
||||
return bw->buf_;
|
||||
}
|
||||
|
||||
int VP8BitWriterAppend(VP8BitWriter* const bw,
|
||||
const uint8_t* data, size_t size) {
|
||||
assert(data);
|
||||
if (bw->nb_bits_ != -8) return 0; // kFlush() must have been called
|
||||
assert(data != NULL);
|
||||
if (bw->nb_bits_ != -8) return 0; // Flush() must have been called
|
||||
if (!BitWriterResize(bw, size)) return 0;
|
||||
memcpy(bw->buf_ + bw->pos_, data, size);
|
||||
bw->pos_ += size;
|
||||
@ -184,8 +189,8 @@ int VP8BitWriterAppend(VP8BitWriter* const bw,
|
||||
}
|
||||
|
||||
void VP8BitWriterWipeOut(VP8BitWriter* const bw) {
|
||||
if (bw) {
|
||||
free(bw->buf_);
|
||||
if (bw != NULL) {
|
||||
WebPSafeFree(bw->buf_);
|
||||
memset(bw, 0, sizeof(*bw));
|
||||
}
|
||||
}
|
||||
@ -193,32 +198,39 @@ void VP8BitWriterWipeOut(VP8BitWriter* const bw) {
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8LBitWriter
|
||||
|
||||
// This is the minimum amount of size the memory buffer is guaranteed to grow
|
||||
// when extra space is needed.
|
||||
#define MIN_EXTRA_SIZE (32768ULL)
|
||||
|
||||
// Returns 1 on success.
|
||||
static int VP8LBitWriterResize(VP8LBitWriter* const bw, size_t extra_size) {
|
||||
uint8_t* allocated_buf;
|
||||
size_t allocated_size;
|
||||
const size_t current_size = VP8LBitWriterNumBytes(bw);
|
||||
const size_t max_bytes = bw->end_ - bw->buf_;
|
||||
const size_t current_size = bw->cur_ - bw->buf_;
|
||||
const uint64_t size_required_64b = (uint64_t)current_size + extra_size;
|
||||
const size_t size_required = (size_t)size_required_64b;
|
||||
if (size_required != size_required_64b) {
|
||||
bw->error_ = 1;
|
||||
return 0;
|
||||
}
|
||||
if (bw->max_bytes_ > 0 && size_required <= bw->max_bytes_) return 1;
|
||||
allocated_size = (3 * bw->max_bytes_) >> 1;
|
||||
if (max_bytes > 0 && size_required <= max_bytes) return 1;
|
||||
allocated_size = (3 * max_bytes) >> 1;
|
||||
if (allocated_size < size_required) allocated_size = size_required;
|
||||
// make allocated size multiple of 1k
|
||||
allocated_size = (((allocated_size >> 10) + 1) << 10);
|
||||
allocated_buf = (uint8_t*)malloc(allocated_size);
|
||||
allocated_buf = (uint8_t*)WebPSafeMalloc(1ULL, allocated_size);
|
||||
if (allocated_buf == NULL) {
|
||||
bw->error_ = 1;
|
||||
return 0;
|
||||
}
|
||||
memcpy(allocated_buf, bw->buf_, current_size);
|
||||
free(bw->buf_);
|
||||
if (current_size > 0) {
|
||||
memcpy(allocated_buf, bw->buf_, current_size);
|
||||
}
|
||||
WebPSafeFree(bw->buf_);
|
||||
bw->buf_ = allocated_buf;
|
||||
bw->max_bytes_ = allocated_size;
|
||||
memset(allocated_buf + current_size, 0, allocated_size - current_size);
|
||||
bw->cur_ = bw->buf_ + current_size;
|
||||
bw->end_ = bw->buf_ + allocated_size;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@ -227,58 +239,81 @@ int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size) {
|
||||
return VP8LBitWriterResize(bw, expected_size);
|
||||
}
|
||||
|
||||
void VP8LBitWriterDestroy(VP8LBitWriter* const bw) {
|
||||
void VP8LBitWriterWipeOut(VP8LBitWriter* const bw) {
|
||||
if (bw != NULL) {
|
||||
free(bw->buf_);
|
||||
WebPSafeFree(bw->buf_);
|
||||
memset(bw, 0, sizeof(*bw));
|
||||
}
|
||||
}
|
||||
|
||||
void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits) {
|
||||
if (n_bits < 1) return;
|
||||
#if !defined(__BIG_ENDIAN__)
|
||||
// Technically, this branch of the code can write up to 25 bits at a time,
|
||||
// but in prefix encoding, the maximum number of bits written is 18 at a time.
|
||||
{
|
||||
uint8_t* const p = &bw->buf_[bw->bit_pos_ >> 3];
|
||||
uint32_t v = *(const uint32_t*)p;
|
||||
v |= bits << (bw->bit_pos_ & 7);
|
||||
*(uint32_t*)p = v;
|
||||
bw->bit_pos_ += n_bits;
|
||||
}
|
||||
#else // BIG_ENDIAN
|
||||
{
|
||||
uint8_t* p = &bw->buf_[bw->bit_pos_ >> 3];
|
||||
const int bits_reserved_in_first_byte = bw->bit_pos_ & 7;
|
||||
const int bits_left_to_write = n_bits - 8 + bits_reserved_in_first_byte;
|
||||
// implicit & 0xff is assumed for uint8_t arithmetics
|
||||
*p++ |= bits << bits_reserved_in_first_byte;
|
||||
bits >>= 8 - bits_reserved_in_first_byte;
|
||||
if (bits_left_to_write >= 1) {
|
||||
*p++ = bits;
|
||||
bits >>= 8;
|
||||
if (bits_left_to_write >= 9) {
|
||||
*p++ = bits;
|
||||
bits >>= 8;
|
||||
}
|
||||
}
|
||||
assert(n_bits <= 25);
|
||||
*p = bits;
|
||||
bw->bit_pos_ += n_bits;
|
||||
}
|
||||
#endif
|
||||
if ((bw->bit_pos_ >> 3) > (bw->max_bytes_ - 8)) {
|
||||
const uint64_t extra_size = 32768ULL + bw->max_bytes_;
|
||||
void VP8LPutBitsFlushBits(VP8LBitWriter* const bw) {
|
||||
// If needed, make some room by flushing some bits out.
|
||||
if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) {
|
||||
const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE;
|
||||
if (extra_size != (size_t)extra_size ||
|
||||
!VP8LBitWriterResize(bw, (size_t)extra_size)) {
|
||||
bw->bit_pos_ = 0;
|
||||
bw->cur_ = bw->buf_;
|
||||
bw->error_ = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*(vp8l_wtype_t*)bw->cur_ = (vp8l_wtype_t)WSWAP((vp8l_wtype_t)bw->bits_);
|
||||
bw->cur_ += VP8L_WRITER_BYTES;
|
||||
bw->bits_ >>= VP8L_WRITER_BITS;
|
||||
bw->used_ -= VP8L_WRITER_BITS;
|
||||
}
|
||||
|
||||
void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits) {
|
||||
assert(n_bits <= 32);
|
||||
// That's the max we can handle:
|
||||
assert(sizeof(vp8l_wtype_t) == 2);
|
||||
if (n_bits > 0) {
|
||||
vp8l_atype_t lbits = bw->bits_;
|
||||
int used = bw->used_;
|
||||
// Special case of overflow handling for 32bit accumulator (2-steps flush).
|
||||
#if VP8L_WRITER_BITS == 16
|
||||
if (used + n_bits >= VP8L_WRITER_MAX_BITS) {
|
||||
// Fill up all the VP8L_WRITER_MAX_BITS so it can be flushed out below.
|
||||
const int shift = VP8L_WRITER_MAX_BITS - used;
|
||||
lbits |= (vp8l_atype_t)bits << used;
|
||||
used = VP8L_WRITER_MAX_BITS;
|
||||
n_bits -= shift;
|
||||
bits >>= shift;
|
||||
assert(n_bits <= VP8L_WRITER_MAX_BITS);
|
||||
}
|
||||
#endif
|
||||
// If needed, make some room by flushing some bits out.
|
||||
while (used >= VP8L_WRITER_BITS) {
|
||||
if (bw->cur_ + VP8L_WRITER_BYTES > bw->end_) {
|
||||
const uint64_t extra_size = (bw->end_ - bw->buf_) + MIN_EXTRA_SIZE;
|
||||
if (extra_size != (size_t)extra_size ||
|
||||
!VP8LBitWriterResize(bw, (size_t)extra_size)) {
|
||||
bw->cur_ = bw->buf_;
|
||||
bw->error_ = 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
*(vp8l_wtype_t*)bw->cur_ = (vp8l_wtype_t)WSWAP((vp8l_wtype_t)lbits);
|
||||
bw->cur_ += VP8L_WRITER_BYTES;
|
||||
lbits >>= VP8L_WRITER_BITS;
|
||||
used -= VP8L_WRITER_BITS;
|
||||
}
|
||||
bw->bits_ = lbits | ((vp8l_atype_t)bits << used);
|
||||
bw->used_ = used + n_bits;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) {
|
||||
// flush leftover bits
|
||||
if (VP8LBitWriterResize(bw, (bw->used_ + 7) >> 3)) {
|
||||
while (bw->used_ > 0) {
|
||||
*bw->cur_++ = (uint8_t)bw->bits_;
|
||||
bw->bits_ >>= 8;
|
||||
bw->used_ -= 8;
|
||||
}
|
||||
bw->used_ = 0;
|
||||
}
|
||||
return bw->buf_;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Bit writing and boolean coder
|
||||
@ -12,9 +14,9 @@
|
||||
#ifndef WEBP_UTILS_BIT_WRITER_H_
|
||||
#define WEBP_UTILS_BIT_WRITER_H_
|
||||
|
||||
#include "../types.h"
|
||||
#include "../webp/types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -43,8 +45,8 @@ void VP8BitWriterWipeOut(VP8BitWriter* const bw);
|
||||
|
||||
int VP8PutBit(VP8BitWriter* const bw, int bit, int prob);
|
||||
int VP8PutBitUniform(VP8BitWriter* const bw, int bit);
|
||||
void VP8PutValue(VP8BitWriter* const bw, int value, int nb_bits);
|
||||
void VP8PutSignedValue(VP8BitWriter* const bw, int value, int nb_bits);
|
||||
void VP8PutBits(VP8BitWriter* const bw, uint32_t value, int nb_bits);
|
||||
void VP8PutSignedBits(VP8BitWriter* const bw, int value, int nb_bits);
|
||||
|
||||
// Appends some bytes to the internal buffer. Data is copied.
|
||||
int VP8BitWriterAppend(VP8BitWriter* const bw,
|
||||
@ -66,57 +68,77 @@ static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8LBitWriter
|
||||
// TODO(vikasa): VP8LBitWriter is copied as-is from lossless code. There's scope
|
||||
// of re-using VP8BitWriter. Will evaluate once basic lossless encoder is
|
||||
// implemented.
|
||||
|
||||
#if defined(__x86_64__) || defined(_M_X64) // 64bit
|
||||
typedef uint64_t vp8l_atype_t; // accumulator type
|
||||
typedef uint32_t vp8l_wtype_t; // writing type
|
||||
#define WSWAP HToLE32
|
||||
#define VP8L_WRITER_BYTES 4 // sizeof(vp8l_wtype_t)
|
||||
#define VP8L_WRITER_BITS 32 // 8 * sizeof(vp8l_wtype_t)
|
||||
#define VP8L_WRITER_MAX_BITS 64 // 8 * sizeof(vp8l_atype_t)
|
||||
#else
|
||||
typedef uint32_t vp8l_atype_t;
|
||||
typedef uint16_t vp8l_wtype_t;
|
||||
#define WSWAP HToLE16
|
||||
#define VP8L_WRITER_BYTES 2
|
||||
#define VP8L_WRITER_BITS 16
|
||||
#define VP8L_WRITER_MAX_BITS 32
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint8_t* buf_;
|
||||
size_t bit_pos_;
|
||||
size_t max_bytes_;
|
||||
vp8l_atype_t bits_; // bit accumulator
|
||||
int used_; // number of bits used in accumulator
|
||||
uint8_t* buf_; // start of buffer
|
||||
uint8_t* cur_; // current write position
|
||||
uint8_t* end_; // end of buffer
|
||||
|
||||
// After all bits are written, the caller must observe the state of
|
||||
// error_. A value of 1 indicates that a memory allocation failure
|
||||
// has happened during bit writing. A value of 0 indicates successful
|
||||
// After all bits are written (VP8LBitWriterFinish()), the caller must observe
|
||||
// the state of error_. A value of 1 indicates that a memory allocation
|
||||
// failure has happened during bit writing. A value of 0 indicates successful
|
||||
// writing of bits.
|
||||
int error_;
|
||||
} VP8LBitWriter;
|
||||
|
||||
static WEBP_INLINE size_t VP8LBitWriterNumBytes(VP8LBitWriter* const bw) {
|
||||
return (bw->bit_pos_ + 7) >> 3;
|
||||
return (bw->cur_ - bw->buf_) + ((bw->used_ + 7) >> 3);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) {
|
||||
return bw->buf_;
|
||||
}
|
||||
|
||||
// Returns 0 in case of memory allocation error.
|
||||
// Returns false in case of memory allocation error.
|
||||
int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size);
|
||||
// Finalize the bitstream coding. Returns a pointer to the internal buffer.
|
||||
uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw);
|
||||
// Release any pending memory and zeroes the object.
|
||||
void VP8LBitWriterWipeOut(VP8LBitWriter* const bw);
|
||||
|
||||
void VP8LBitWriterDestroy(VP8LBitWriter* const bw);
|
||||
// Internal function for VP8LPutBits flushing 32 bits from the written state.
|
||||
void VP8LPutBitsFlushBits(VP8LBitWriter* const bw);
|
||||
|
||||
// This function writes bits into bytes in increasing addresses, and within
|
||||
// a byte least-significant-bit first.
|
||||
//
|
||||
// The function can write up to 16 bits in one go with WriteBits
|
||||
// Example: let's assume that 3 bits (Rs below) have been written already:
|
||||
//
|
||||
// BYTE-0 BYTE+1 BYTE+2
|
||||
//
|
||||
// 0000 0RRR 0000 0000 0000 0000
|
||||
//
|
||||
// Now, we could write 5 or less bits in MSB by just sifting by 3
|
||||
// and OR'ing to BYTE-0.
|
||||
//
|
||||
// For n bits, we take the last 5 bytes, OR that with high bits in BYTE-0,
|
||||
// and locate the rest in BYTE+1 and BYTE+2.
|
||||
//
|
||||
// PutBits internal function used in the 16 bit vp8l_wtype_t case.
|
||||
void VP8LPutBitsInternal(VP8LBitWriter* const bw, uint32_t bits, int n_bits);
|
||||
|
||||
// This function writes bits into bytes in increasing addresses (little endian),
|
||||
// and within a byte least-significant-bit first.
|
||||
// This function can write up to 32 bits in one go, but VP8LBitReader can only
|
||||
// read 24 bits max (VP8L_MAX_NUM_BIT_READ).
|
||||
// VP8LBitWriter's error_ flag is set in case of memory allocation error.
|
||||
void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits);
|
||||
static WEBP_INLINE void VP8LPutBits(VP8LBitWriter* const bw,
|
||||
uint32_t bits, int n_bits) {
|
||||
if (sizeof(vp8l_wtype_t) == 4) {
|
||||
if (n_bits > 0) {
|
||||
if (bw->used_ >= 32) {
|
||||
VP8LPutBitsFlushBits(bw);
|
||||
}
|
||||
bw->bits_ |= (vp8l_atype_t)bits << bw->used_;
|
||||
bw->used_ += n_bits;
|
||||
}
|
||||
} else {
|
||||
VP8LPutBitsInternal(bw, bits, n_bits);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Color Cache for WebP Lossless
|
||||
@ -11,13 +13,10 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "./color_cache.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8LColorCache.
|
||||
|
||||
@ -29,16 +28,22 @@ int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) {
|
||||
sizeof(*cc->colors_));
|
||||
if (cc->colors_ == NULL) return 0;
|
||||
cc->hash_shift_ = 32 - hash_bits;
|
||||
cc->hash_bits_ = hash_bits;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void VP8LColorCacheClear(VP8LColorCache* const cc) {
|
||||
if (cc != NULL) {
|
||||
free(cc->colors_);
|
||||
WebPSafeFree(cc->colors_);
|
||||
cc->colors_ = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
void VP8LColorCacheCopy(const VP8LColorCache* const src,
|
||||
VP8LColorCache* const dst) {
|
||||
assert(src != NULL);
|
||||
assert(dst != NULL);
|
||||
assert(src->hash_bits_ == dst->hash_bits_);
|
||||
memcpy(dst->colors_, src->colors_,
|
||||
((size_t)1u << dst->hash_bits_) * sizeof(*dst->colors_));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Color Cache for WebP Lossless
|
||||
@ -13,26 +15,33 @@
|
||||
#ifndef WEBP_UTILS_COLOR_CACHE_H_
|
||||
#define WEBP_UTILS_COLOR_CACHE_H_
|
||||
|
||||
#include "../types.h"
|
||||
#include "../webp/types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Main color cache struct.
|
||||
typedef struct {
|
||||
uint32_t *colors_; // color entries
|
||||
int hash_shift_; // Hash shift: 32 - hash_bits.
|
||||
int hash_shift_; // Hash shift: 32 - hash_bits_.
|
||||
int hash_bits_;
|
||||
} VP8LColorCache;
|
||||
|
||||
static const uint32_t kHashMul = 0x1e35a7bd;
|
||||
|
||||
static WEBP_INLINE uint32_t VP8LColorCacheLookup(
|
||||
const VP8LColorCache* const cc, uint32_t key) {
|
||||
assert(key <= (~0U >> cc->hash_shift_));
|
||||
assert((key >> cc->hash_bits_) == 0u);
|
||||
return cc->colors_[key];
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8LColorCacheSet(const VP8LColorCache* const cc,
|
||||
uint32_t key, uint32_t argb) {
|
||||
assert((key >> cc->hash_bits_) == 0u);
|
||||
cc->colors_[key] = argb;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void VP8LColorCacheInsert(const VP8LColorCache* const cc,
|
||||
uint32_t argb) {
|
||||
const uint32_t key = (kHashMul * argb) >> cc->hash_shift_;
|
||||
@ -47,7 +56,7 @@ static WEBP_INLINE int VP8LColorCacheGetIndex(const VP8LColorCache* const cc,
|
||||
static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc,
|
||||
uint32_t argb) {
|
||||
const uint32_t key = (kHashMul * argb) >> cc->hash_shift_;
|
||||
return cc->colors_[key] == argb;
|
||||
return (cc->colors_[key] == argb);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -56,12 +65,15 @@ static WEBP_INLINE int VP8LColorCacheContains(const VP8LColorCache* const cc,
|
||||
// Returns false in case of memory error.
|
||||
int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits);
|
||||
|
||||
void VP8LColorCacheCopy(const VP8LColorCache* const src,
|
||||
VP8LColorCache* const dst);
|
||||
|
||||
// Delete the memory associated to color cache.
|
||||
void VP8LColorCacheClear(VP8LColorCache* const color_cache);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,171 +1,37 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Spatial prediction using various filters
|
||||
// filter estimation
|
||||
//
|
||||
// Author: Urvang (urvang@google.com)
|
||||
|
||||
#include "./filters.h"
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Helpful macro.
|
||||
|
||||
# define SANITY_CHECK(in, out) \
|
||||
assert(in != NULL); \
|
||||
assert(out != NULL); \
|
||||
assert(width > 0); \
|
||||
assert(height > 0); \
|
||||
assert(bpp > 0); \
|
||||
assert(stride >= width * bpp);
|
||||
|
||||
static WEBP_INLINE void PredictLine(const uint8_t* src, const uint8_t* pred,
|
||||
uint8_t* dst, int length, int inverse) {
|
||||
int i;
|
||||
if (inverse) {
|
||||
for (i = 0; i < length; ++i) dst[i] = src[i] + pred[i];
|
||||
} else {
|
||||
for (i = 0; i < length; ++i) dst[i] = src[i] - pred[i];
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Horizontal filter.
|
||||
|
||||
static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in,
|
||||
int width, int height, int bpp, int stride, int inverse, uint8_t* out) {
|
||||
int h;
|
||||
const uint8_t* preds = (inverse ? out : in);
|
||||
SANITY_CHECK(in, out);
|
||||
|
||||
// Filter line-by-line.
|
||||
for (h = 0; h < height; ++h) {
|
||||
// Leftmost pixel is predicted from above (except for topmost scanline).
|
||||
if (h == 0) {
|
||||
memcpy((void*)out, (const void*)in, bpp);
|
||||
} else {
|
||||
PredictLine(in, preds - stride, out, bpp, inverse);
|
||||
}
|
||||
PredictLine(in + bpp, preds, out + bpp, bpp * (width - 1), inverse);
|
||||
preds += stride;
|
||||
in += stride;
|
||||
out += stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void HorizontalFilter(const uint8_t* data, int width, int height,
|
||||
int bpp, int stride, uint8_t* filtered_data) {
|
||||
DoHorizontalFilter(data, width, height, bpp, stride, 0, filtered_data);
|
||||
}
|
||||
|
||||
static void HorizontalUnfilter(const uint8_t* data, int width, int height,
|
||||
int bpp, int stride, uint8_t* recon_data) {
|
||||
DoHorizontalFilter(data, width, height, bpp, stride, 1, recon_data);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Vertical filter.
|
||||
|
||||
static WEBP_INLINE void DoVerticalFilter(const uint8_t* in,
|
||||
int width, int height, int bpp, int stride, int inverse, uint8_t* out) {
|
||||
int h;
|
||||
const uint8_t* preds = (inverse ? out : in);
|
||||
SANITY_CHECK(in, out);
|
||||
|
||||
// Very first top-left pixel is copied.
|
||||
memcpy((void*)out, (const void*)in, bpp);
|
||||
// Rest of top scan-line is left-predicted.
|
||||
PredictLine(in + bpp, preds, out + bpp, bpp * (width - 1), inverse);
|
||||
|
||||
// Filter line-by-line.
|
||||
for (h = 1; h < height; ++h) {
|
||||
in += stride;
|
||||
out += stride;
|
||||
PredictLine(in, preds, out, bpp * width, inverse);
|
||||
preds += stride;
|
||||
}
|
||||
}
|
||||
|
||||
static void VerticalFilter(const uint8_t* data, int width, int height,
|
||||
int bpp, int stride, uint8_t* filtered_data) {
|
||||
DoVerticalFilter(data, width, height, bpp, stride, 0, filtered_data);
|
||||
}
|
||||
|
||||
static void VerticalUnfilter(const uint8_t* data, int width, int height,
|
||||
int bpp, int stride, uint8_t* recon_data) {
|
||||
DoVerticalFilter(data, width, height, bpp, stride, 1, recon_data);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Gradient filter.
|
||||
|
||||
static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) {
|
||||
const int g = a + b - c;
|
||||
return (g < 0) ? 0 : (g > 255) ? 255 : g;
|
||||
}
|
||||
|
||||
static WEBP_INLINE
|
||||
void DoGradientFilter(const uint8_t* in, int width, int height,
|
||||
int bpp, int stride, int inverse, uint8_t* out) {
|
||||
const uint8_t* preds = (inverse ? out : in);
|
||||
int h;
|
||||
SANITY_CHECK(in, out);
|
||||
|
||||
// left prediction for top scan-line
|
||||
memcpy((void*)out, (const void*)in, bpp);
|
||||
PredictLine(in + bpp, preds, out + bpp, bpp * (width - 1), inverse);
|
||||
|
||||
// Filter line-by-line.
|
||||
for (h = 1; h < height; ++h) {
|
||||
int w;
|
||||
preds += stride;
|
||||
in += stride;
|
||||
out += stride;
|
||||
// leftmost pixel: predict from above.
|
||||
PredictLine(in, preds - stride, out, bpp, inverse);
|
||||
for (w = bpp; w < width * bpp; ++w) {
|
||||
const int pred = GradientPredictor(preds[w - bpp],
|
||||
preds[w - stride],
|
||||
preds[w - stride - bpp]);
|
||||
out[w] = in[w] + (inverse ? pred : -pred);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void GradientFilter(const uint8_t* data, int width, int height,
|
||||
int bpp, int stride, uint8_t* filtered_data) {
|
||||
DoGradientFilter(data, width, height, bpp, stride, 0, filtered_data);
|
||||
}
|
||||
|
||||
static void GradientUnfilter(const uint8_t* data, int width, int height,
|
||||
int bpp, int stride, uint8_t* recon_data) {
|
||||
DoGradientFilter(data, width, height, bpp, stride, 1, recon_data);
|
||||
}
|
||||
|
||||
#undef SANITY_CHECK
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Quick estimate of a potentially interesting filter mode to try, in addition
|
||||
// to the default NONE.
|
||||
// Quick estimate of a potentially interesting filter mode to try.
|
||||
|
||||
#define SMAX 16
|
||||
#define SDIFF(a, b) (abs((a) - (b)) >> 4) // Scoring diff, in [0..SMAX)
|
||||
|
||||
WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
||||
int width, int height, int stride) {
|
||||
static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) {
|
||||
const int g = a + b - c;
|
||||
return ((g & ~0xff) == 0) ? g : (g < 0) ? 0 : 255; // clip to 8bit
|
||||
}
|
||||
|
||||
WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data,
|
||||
int width, int height, int stride) {
|
||||
int i, j;
|
||||
int bins[WEBP_FILTER_LAST][SMAX];
|
||||
memset(bins, 0, sizeof(bins));
|
||||
|
||||
// We only sample every other pixels. That's enough.
|
||||
for (j = 2; j < height - 1; j += 2) {
|
||||
const uint8_t* const p = data + j * stride;
|
||||
@ -185,7 +51,8 @@ WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
||||
}
|
||||
}
|
||||
{
|
||||
WEBP_FILTER_TYPE filter, best_filter = WEBP_FILTER_NONE;
|
||||
int filter;
|
||||
WEBP_FILTER_TYPE best_filter = WEBP_FILTER_NONE;
|
||||
int best_score = 0x7fffffff;
|
||||
for (filter = WEBP_FILTER_NONE; filter < WEBP_FILTER_LAST; ++filter) {
|
||||
int score = 0;
|
||||
@ -196,7 +63,7 @@ WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
||||
}
|
||||
if (score < best_score) {
|
||||
best_score = score;
|
||||
best_filter = filter;
|
||||
best_filter = (WEBP_FILTER_TYPE)filter;
|
||||
}
|
||||
}
|
||||
return best_filter;
|
||||
@ -207,23 +74,3 @@ WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
||||
#undef SDIFF
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST] = {
|
||||
NULL, // WEBP_FILTER_NONE
|
||||
HorizontalFilter, // WEBP_FILTER_HORIZONTAL
|
||||
VerticalFilter, // WEBP_FILTER_VERTICAL
|
||||
GradientFilter // WEBP_FILTER_GRADIENT
|
||||
};
|
||||
|
||||
const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST] = {
|
||||
NULL, // WEBP_FILTER_NONE
|
||||
HorizontalUnfilter, // WEBP_FILTER_HORIZONTAL
|
||||
VerticalUnfilter, // WEBP_FILTER_VERTICAL
|
||||
GradientUnfilter // WEBP_FILTER_GRADIENT
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Spatial prediction using various filters
|
||||
@ -12,42 +14,18 @@
|
||||
#ifndef WEBP_UTILS_FILTERS_H_
|
||||
#define WEBP_UTILS_FILTERS_H_
|
||||
|
||||
#include "../types.h"
|
||||
#include "../webp/types.h"
|
||||
#include "../dsp/dsp.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Filters.
|
||||
typedef enum {
|
||||
WEBP_FILTER_NONE = 0,
|
||||
WEBP_FILTER_HORIZONTAL,
|
||||
WEBP_FILTER_VERTICAL,
|
||||
WEBP_FILTER_GRADIENT,
|
||||
WEBP_FILTER_LAST = WEBP_FILTER_GRADIENT + 1, // end marker
|
||||
WEBP_FILTER_BEST,
|
||||
WEBP_FILTER_FAST
|
||||
} WEBP_FILTER_TYPE;
|
||||
|
||||
typedef void (*WebPFilterFunc)(const uint8_t* in, int width, int height,
|
||||
int bpp, int stride, uint8_t* out);
|
||||
|
||||
// Filter the given data using the given predictor.
|
||||
// 'in' corresponds to a 2-dimensional pixel array of size (stride * height)
|
||||
// in raster order.
|
||||
// 'bpp' is number of bytes per pixel, and
|
||||
// 'stride' is number of bytes per scan line (with possible padding).
|
||||
// 'out' should be pre-allocated.
|
||||
extern const WebPFilterFunc WebPFilters[WEBP_FILTER_LAST];
|
||||
|
||||
// Reconstruct the original data from the given filtered data.
|
||||
extern const WebPFilterFunc WebPUnfilters[WEBP_FILTER_LAST];
|
||||
|
||||
// Fast estimate of a potentially good filter.
|
||||
extern WEBP_FILTER_TYPE EstimateBestFilter(const uint8_t* data,
|
||||
int width, int height, int stride);
|
||||
WEBP_FILTER_TYPE WebPEstimateBestFilter(const uint8_t* data,
|
||||
int width, int height, int stride);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Utilities for building and looking up Huffman trees.
|
||||
@ -11,228 +13,193 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "./huffman.h"
|
||||
#include "../utils/utils.h"
|
||||
#include "../format_constants.h"
|
||||
#include "../webp/format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
// Huffman data read via DecodeImageStream is represented in two (red and green)
|
||||
// bytes.
|
||||
#define MAX_HTREE_GROUPS 0x10000
|
||||
|
||||
#define NON_EXISTENT_SYMBOL (-1)
|
||||
|
||||
static void TreeNodeInit(HuffmanTreeNode* const node) {
|
||||
node->children_ = -1; // means: 'unassigned so far'
|
||||
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;
|
||||
}
|
||||
|
||||
static int NodeIsEmpty(const HuffmanTreeNode* const node) {
|
||||
return (node->children_ < 0);
|
||||
}
|
||||
|
||||
static int IsFull(const HuffmanTree* const tree) {
|
||||
return (tree->num_nodes_ == tree->max_nodes_);
|
||||
}
|
||||
|
||||
static void AssignChildren(HuffmanTree* const tree,
|
||||
HuffmanTreeNode* const node) {
|
||||
HuffmanTreeNode* const children = tree->root_ + tree->num_nodes_;
|
||||
node->children_ = (int)(children - node);
|
||||
assert(children - node == (int)(children - node));
|
||||
tree->num_nodes_ += 2;
|
||||
TreeNodeInit(children + 0);
|
||||
TreeNodeInit(children + 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;
|
||||
}
|
||||
|
||||
void HuffmanTreeRelease(HuffmanTree* const tree) {
|
||||
if (tree != NULL) {
|
||||
free(tree->root_);
|
||||
tree->root_ = NULL;
|
||||
tree->max_nodes_ = 0;
|
||||
tree->num_nodes_ = 0;
|
||||
void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups) {
|
||||
if (htree_groups != NULL) {
|
||||
WebPSafeFree(htree_groups);
|
||||
}
|
||||
}
|
||||
|
||||
int HuffmanCodeLengthsToCodes(const int* const code_lengths,
|
||||
int code_lengths_size, int* const huff_codes) {
|
||||
int symbol;
|
||||
int code_len;
|
||||
int code_length_hist[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 };
|
||||
int curr_code;
|
||||
int next_codes[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 };
|
||||
int max_code_length = 0;
|
||||
// Returns reverse(reverse(key, len) + 1, len), where reverse(key, len) is the
|
||||
// bit-wise reversal of the len least significant bits of key.
|
||||
static WEBP_INLINE uint32_t GetNextKey(uint32_t key, int len) {
|
||||
uint32_t step = 1 << (len - 1);
|
||||
while (key & step) {
|
||||
step >>= 1;
|
||||
}
|
||||
return (key & (step - 1)) + step;
|
||||
}
|
||||
|
||||
// Stores code in table[0], table[step], table[2*step], ..., table[end].
|
||||
// Assumes that end is an integer multiple of step.
|
||||
static WEBP_INLINE void ReplicateValue(HuffmanCode* table,
|
||||
int step, int end,
|
||||
HuffmanCode code) {
|
||||
assert(end % step == 0);
|
||||
do {
|
||||
end -= step;
|
||||
table[end] = code;
|
||||
} while (end > 0);
|
||||
}
|
||||
|
||||
// Returns the table width of the next 2nd level table. count is the histogram
|
||||
// of bit lengths for the remaining symbols, len is the code length of the next
|
||||
// processed symbol
|
||||
static WEBP_INLINE int NextTableBitSize(const int* const count,
|
||||
int len, int root_bits) {
|
||||
int left = 1 << (len - root_bits);
|
||||
while (len < MAX_ALLOWED_CODE_LENGTH) {
|
||||
left -= count[len];
|
||||
if (left <= 0) break;
|
||||
++len;
|
||||
left <<= 1;
|
||||
}
|
||||
return len - root_bits;
|
||||
}
|
||||
|
||||
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(code_lengths_size > 0);
|
||||
assert(huff_codes != NULL);
|
||||
assert(root_table != NULL);
|
||||
assert(root_bits > 0);
|
||||
|
||||
// Calculate max code length.
|
||||
// Build histogram of code lengths.
|
||||
for (symbol = 0; symbol < code_lengths_size; ++symbol) {
|
||||
if (code_lengths[symbol] > max_code_length) {
|
||||
max_code_length = code_lengths[symbol];
|
||||
}
|
||||
}
|
||||
if (max_code_length > MAX_ALLOWED_CODE_LENGTH) return 0;
|
||||
|
||||
// Calculate code length histogram.
|
||||
for (symbol = 0; symbol < code_lengths_size; ++symbol) {
|
||||
++code_length_hist[code_lengths[symbol]];
|
||||
}
|
||||
code_length_hist[0] = 0;
|
||||
|
||||
// Calculate the initial values of 'next_codes' for each code length.
|
||||
// next_codes[code_len] denotes the code to be assigned to the next symbol
|
||||
// of code length 'code_len'.
|
||||
curr_code = 0;
|
||||
next_codes[0] = -1; // Unused, as code length = 0 implies code doesn't exist.
|
||||
for (code_len = 1; code_len <= max_code_length; ++code_len) {
|
||||
curr_code = (curr_code + code_length_hist[code_len - 1]) << 1;
|
||||
next_codes[code_len] = curr_code;
|
||||
}
|
||||
|
||||
// Get symbols.
|
||||
for (symbol = 0; symbol < code_lengths_size; ++symbol) {
|
||||
if (code_lengths[symbol] > 0) {
|
||||
huff_codes[symbol] = next_codes[code_lengths[symbol]]++;
|
||||
} else {
|
||||
huff_codes[symbol] = NON_EXISTENT_SYMBOL;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int TreeAddSymbol(HuffmanTree* const tree,
|
||||
int symbol, int code, int code_length) {
|
||||
HuffmanTreeNode* node = tree->root_;
|
||||
const HuffmanTreeNode* const max_node = tree->root_ + tree->max_nodes_;
|
||||
while (code_length-- > 0) {
|
||||
if (node >= max_node) {
|
||||
if (code_lengths[symbol] > MAX_ALLOWED_CODE_LENGTH) {
|
||||
return 0;
|
||||
}
|
||||
if (NodeIsEmpty(node)) {
|
||||
if (IsFull(tree)) return 0; // error: too many symbols.
|
||||
AssignChildren(tree, node);
|
||||
} else if (HuffmanTreeNodeIsLeaf(node)) {
|
||||
return 0; // leaf is already occupied.
|
||||
}
|
||||
node += node->children_ + ((code >> code_length) & 1);
|
||||
}
|
||||
if (NodeIsEmpty(node)) {
|
||||
node->children_ = 0; // turn newly created node into a leaf.
|
||||
} else if (!HuffmanTreeNodeIsLeaf(node)) {
|
||||
return 0; // trying to assign a symbol to already used code.
|
||||
}
|
||||
node->symbol_ = symbol; // Add symbol in this node.
|
||||
return 1;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
++count[code_lengths[symbol]];
|
||||
}
|
||||
|
||||
// Initialize the tree. Will fail for num_symbols = 0
|
||||
if (!TreeInit(tree, num_symbols)) return 0;
|
||||
// Error, all code lengths are zeros.
|
||||
if (count[0] == code_lengths_size) {
|
||||
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);
|
||||
// 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;
|
||||
}
|
||||
return TreeAddSymbol(tree, root_symbol, 0, 0);
|
||||
} else { // Normal case.
|
||||
int ok = 0;
|
||||
offset[len + 1] = offset[len] + count[len];
|
||||
}
|
||||
|
||||
// Get Huffman codes from the code lengths.
|
||||
int* const codes =
|
||||
(int*)WebPSafeMalloc((uint64_t)code_lengths_size, sizeof(*codes));
|
||||
if (codes == NULL) goto End;
|
||||
sorted = (int*)WebPSafeMalloc(code_lengths_size, sizeof(*sorted));
|
||||
if (sorted == NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!HuffmanCodeLengthsToCodes(code_lengths, code_lengths_size, codes)) {
|
||||
goto End;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
ok = 1;
|
||||
End:
|
||||
free(codes);
|
||||
ok = ok && IsFull(tree);
|
||||
if (!ok) HuffmanTreeRelease(tree);
|
||||
return ok;
|
||||
}
|
||||
}
|
||||
|
||||
int HuffmanTreeBuildExplicit(HuffmanTree* const tree,
|
||||
const int* const code_lengths,
|
||||
const int* const codes,
|
||||
const int* const symbols, int max_symbol,
|
||||
int num_symbols) {
|
||||
int ok = 0;
|
||||
int i;
|
||||
|
||||
assert(tree != NULL);
|
||||
assert(code_lengths != NULL);
|
||||
assert(codes != NULL);
|
||||
assert(symbols != NULL);
|
||||
|
||||
// Initialize the tree. Will fail if num_symbols = 0.
|
||||
if (!TreeInit(tree, num_symbols)) return 0;
|
||||
|
||||
// Add symbols one-by-one.
|
||||
for (i = 0; i < num_symbols; ++i) {
|
||||
if (codes[i] != NON_EXISTENT_SYMBOL) {
|
||||
if (symbols[i] < 0 || symbols[i] >= max_symbol) {
|
||||
goto End;
|
||||
}
|
||||
if (!TreeAddSymbol(tree, symbols[i], codes[i], code_lengths[i])) {
|
||||
goto End;
|
||||
}
|
||||
// Check if tree is full.
|
||||
if (num_nodes != 2 * offset[MAX_ALLOWED_CODE_LENGTH] - 1) {
|
||||
WebPSafeFree(sorted);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ok = 1;
|
||||
End:
|
||||
ok = ok && IsFull(tree);
|
||||
if (!ok) HuffmanTreeRelease(tree);
|
||||
return ok;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
WebPSafeFree(sorted);
|
||||
return total_size;
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Utilities for building and looking up Huffman trees.
|
||||
@ -13,65 +15,73 @@
|
||||
#define WEBP_UTILS_HUFFMAN_H_
|
||||
|
||||
#include <assert.h>
|
||||
#include "../types.h"
|
||||
#include "../webp/format_constants.h"
|
||||
#include "../webp/types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// A node of a Huffman tree.
|
||||
typedef struct {
|
||||
int symbol_;
|
||||
int children_; // delta offset to both children (contiguous) or 0 if leaf.
|
||||
} HuffmanTreeNode;
|
||||
#define HUFFMAN_TABLE_BITS 8
|
||||
#define HUFFMAN_TABLE_MASK ((1 << HUFFMAN_TABLE_BITS) - 1)
|
||||
|
||||
// Huffman Tree.
|
||||
typedef struct HuffmanTree HuffmanTree;
|
||||
struct HuffmanTree {
|
||||
HuffmanTreeNode* root_; // all the nodes, starting at root.
|
||||
int max_nodes_; // max number of nodes
|
||||
int num_nodes_; // number of currently occupied nodes
|
||||
#define LENGTHS_TABLE_BITS 7
|
||||
#define LENGTHS_TABLE_MASK ((1 << LENGTHS_TABLE_BITS) - 1)
|
||||
|
||||
|
||||
// Huffman lookup table entry
|
||||
typedef struct {
|
||||
uint8_t bits; // number of bits used for this symbol
|
||||
uint16_t value; // symbol value or table offset
|
||||
} HuffmanCode;
|
||||
|
||||
// long version for holding 32b values
|
||||
typedef struct {
|
||||
int bits; // number of bits used for this symbol,
|
||||
// or an impossible value if not a literal code.
|
||||
uint32_t value; // 32b packed ARGB value if literal,
|
||||
// or non-literal symbol otherwise
|
||||
} HuffmanCode32;
|
||||
|
||||
#define HUFFMAN_PACKED_BITS 6
|
||||
#define HUFFMAN_PACKED_TABLE_SIZE (1u << HUFFMAN_PACKED_BITS)
|
||||
|
||||
// Huffman table group.
|
||||
// Includes special handling for the following cases:
|
||||
// - is_trivial_literal: one common literal base for RED/BLUE/ALPHA (not GREEN)
|
||||
// - is_trivial_code: only 1 code (no bit is read from bitstream)
|
||||
// - use_packed_table: few enough literal symbols, so all the bit codes
|
||||
// can fit into a small look-up table packed_table[]
|
||||
// The common literal base, if applicable, is stored in 'literal_arb'.
|
||||
typedef struct HTreeGroup HTreeGroup;
|
||||
struct HTreeGroup {
|
||||
HuffmanCode* htrees[HUFFMAN_CODES_PER_META_CODE];
|
||||
int is_trivial_literal; // True, if huffman trees for Red, Blue & Alpha
|
||||
// Symbols are trivial (have a single code).
|
||||
uint32_t literal_arb; // If is_trivial_literal is true, this is the
|
||||
// ARGB value of the pixel, with Green channel
|
||||
// being set to zero.
|
||||
int is_trivial_code; // true if is_trivial_literal with only one code
|
||||
int use_packed_table; // use packed table below for short literal code
|
||||
// table mapping input bits to a packed values, or escape case to literal code
|
||||
HuffmanCode32 packed_table[HUFFMAN_PACKED_TABLE_SIZE];
|
||||
};
|
||||
|
||||
// Returns true if the given node is a leaf of the Huffman tree.
|
||||
static WEBP_INLINE int HuffmanTreeNodeIsLeaf(
|
||||
const HuffmanTreeNode* const node) {
|
||||
return (node->children_ == 0);
|
||||
}
|
||||
// Creates the instance of HTreeGroup with specified number of tree-groups.
|
||||
HTreeGroup* VP8LHtreeGroupsNew(int num_htree_groups);
|
||||
|
||||
// Go down one level. Most critical function. 'right_child' must be 0 or 1.
|
||||
static WEBP_INLINE const HuffmanTreeNode* HuffmanTreeNextNode(
|
||||
const HuffmanTreeNode* node, int right_child) {
|
||||
return node + node->children_ + right_child;
|
||||
}
|
||||
// Releases the memory allocated for HTreeGroup.
|
||||
void VP8LHtreeGroupsFree(HTreeGroup* const htree_groups);
|
||||
|
||||
// Releases the nodes of the Huffman tree.
|
||||
// Note: It does NOT free 'tree' itself.
|
||||
void HuffmanTreeRelease(HuffmanTree* const tree);
|
||||
// Builds Huffman lookup table assuming code lengths are in symbol order.
|
||||
// The 'code_lengths' is pre-allocated temporary memory buffer used for creating
|
||||
// the huffman table.
|
||||
// Returns built table size or 0 in case of error (invalid tree or
|
||||
// memory error).
|
||||
int VP8LBuildHuffmanTable(HuffmanCode* const root_table, int root_bits,
|
||||
const int code_lengths[], int code_lengths_size);
|
||||
|
||||
// Builds Huffman tree assuming code lengths are implicitly in symbol order.
|
||||
// Returns false in case of error (invalid tree or memory error).
|
||||
int HuffmanTreeBuildImplicit(HuffmanTree* const tree,
|
||||
const int* const code_lengths,
|
||||
int code_lengths_size);
|
||||
|
||||
// Build a Huffman tree with explicitly given lists of code lengths, codes
|
||||
// and symbols. Verifies that all symbols added are smaller than max_symbol.
|
||||
// Returns false in case of an invalid symbol, invalid tree or memory error.
|
||||
int HuffmanTreeBuildExplicit(HuffmanTree* const tree,
|
||||
const int* const code_lengths,
|
||||
const int* const codes,
|
||||
const int* const symbols, int max_symbol,
|
||||
int num_symbols);
|
||||
|
||||
// Utility: converts Huffman code lengths to corresponding Huffman codes.
|
||||
// 'huff_codes' should be pre-allocated.
|
||||
// Returns false in case of error (memory allocation, invalid codes).
|
||||
int HuffmanCodeLengthsToCodes(const int* const code_lengths,
|
||||
int code_lengths_size, int* const huff_codes);
|
||||
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Author: Jyrki Alakuijala (jyrki@google.com)
|
||||
@ -14,7 +16,7 @@
|
||||
#include <string.h>
|
||||
#include "./huffman_encode.h"
|
||||
#include "../utils/utils.h"
|
||||
#include "../format_constants.h"
|
||||
#include "../webp/format_constants.h"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Util function to optimize the symbol map for RLE coding
|
||||
@ -25,14 +27,14 @@ static int ValuesShouldBeCollapsedToStrideAverage(int a, int b) {
|
||||
}
|
||||
|
||||
// Change the population counts in a way that the consequent
|
||||
// Hufmann tree compression, especially its RLE-part, give smaller output.
|
||||
static int OptimizeHuffmanForRle(int length, int* const counts) {
|
||||
uint8_t* good_for_rle;
|
||||
// Huffman tree compression, especially its RLE-part, give smaller output.
|
||||
static void OptimizeHuffmanForRle(int length, uint8_t* const good_for_rle,
|
||||
uint32_t* const counts) {
|
||||
// 1) Let's make the Huffman code more compatible with rle encoding.
|
||||
int i;
|
||||
for (; length >= 0; --length) {
|
||||
if (length == 0) {
|
||||
return 1; // All zeros.
|
||||
return; // All zeros.
|
||||
}
|
||||
if (counts[length - 1] != 0) {
|
||||
// Now counts[0..length - 1] does not have trailing zeros.
|
||||
@ -41,15 +43,11 @@ static int OptimizeHuffmanForRle(int length, int* const counts) {
|
||||
}
|
||||
// 2) Let's mark all population counts that already can be encoded
|
||||
// with an rle code.
|
||||
good_for_rle = (uint8_t*)calloc(length, 1);
|
||||
if (good_for_rle == NULL) {
|
||||
return 0;
|
||||
}
|
||||
{
|
||||
// Let's not spoil any of the existing good rle codes.
|
||||
// Mark any seq of 0's that is longer as 5 as a good_for_rle.
|
||||
// Mark any seq of non-0's that is longer as 7 as a good_for_rle.
|
||||
int symbol = counts[0];
|
||||
uint32_t symbol = counts[0];
|
||||
int stride = 0;
|
||||
for (i = 0; i < length + 1; ++i) {
|
||||
if (i == length || counts[i] != symbol) {
|
||||
@ -71,17 +69,17 @@ static int OptimizeHuffmanForRle(int length, int* const counts) {
|
||||
}
|
||||
// 3) Let's replace those population counts that lead to more rle codes.
|
||||
{
|
||||
int stride = 0;
|
||||
int limit = counts[0];
|
||||
int sum = 0;
|
||||
uint32_t stride = 0;
|
||||
uint32_t limit = counts[0];
|
||||
uint32_t sum = 0;
|
||||
for (i = 0; i < length + 1; ++i) {
|
||||
if (i == length || good_for_rle[i] ||
|
||||
(i != 0 && good_for_rle[i - 1]) ||
|
||||
!ValuesShouldBeCollapsedToStrideAverage(counts[i], limit)) {
|
||||
if (stride >= 4 || (stride >= 3 && sum == 0)) {
|
||||
int k;
|
||||
uint32_t k;
|
||||
// The stride must end, collapse what we have, if we have enough (4).
|
||||
int count = (sum + stride / 2) / stride;
|
||||
uint32_t count = (sum + stride / 2) / stride;
|
||||
if (count < 1) {
|
||||
count = 1;
|
||||
}
|
||||
@ -117,17 +115,8 @@ static int OptimizeHuffmanForRle(int length, int* const counts) {
|
||||
}
|
||||
}
|
||||
}
|
||||
free(good_for_rle);
|
||||
return 1;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
int total_count_;
|
||||
int value_;
|
||||
int pool_index_left_;
|
||||
int pool_index_right_;
|
||||
} HuffmanTree;
|
||||
|
||||
// A comparer function for two Huffman trees: sorts first by 'total count'
|
||||
// (more comes first), and then by 'value' (more comes first).
|
||||
static int CompareHuffmanTrees(const void* ptr1, const void* ptr2) {
|
||||
@ -138,13 +127,8 @@ static int CompareHuffmanTrees(const void* ptr1, const void* ptr2) {
|
||||
} else if (t1->total_count_ < t2->total_count_) {
|
||||
return 1;
|
||||
} else {
|
||||
if (t1->value_ < t2->value_) {
|
||||
return -1;
|
||||
}
|
||||
if (t1->value_ > t2->value_) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
assert(t1->value_ != t2->value_);
|
||||
return (t1->value_ < t2->value_) ? -1 : 1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -178,12 +162,12 @@ static void SetBitDepths(const HuffmanTree* const tree,
|
||||
// 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;
|
||||
static void GenerateOptimalTree(const uint32_t* const histogram,
|
||||
int histogram_size,
|
||||
HuffmanTree* tree, int tree_depth_limit,
|
||||
uint8_t* const bit_depths) {
|
||||
uint32_t count_min;
|
||||
HuffmanTree* tree_pool;
|
||||
HuffmanTree* tree;
|
||||
int tree_size_orig = 0;
|
||||
int i;
|
||||
|
||||
@ -193,12 +177,10 @@ static int GenerateOptimalTree(const int* const histogram, int histogram_size,
|
||||
}
|
||||
}
|
||||
|
||||
// 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;
|
||||
if (tree_size_orig == 0) { // pretty optimal already!
|
||||
return;
|
||||
}
|
||||
|
||||
tree_pool = tree + tree_size_orig;
|
||||
|
||||
// For block sizes with less than 64k symbols we never need to do a
|
||||
@ -214,7 +196,7 @@ static int GenerateOptimalTree(const int* const histogram, int histogram_size,
|
||||
int j;
|
||||
for (j = 0; j < histogram_size; ++j) {
|
||||
if (histogram[j] != 0) {
|
||||
const int count =
|
||||
const uint32_t count =
|
||||
(histogram[j] < count_min) ? count_min : histogram[j];
|
||||
tree[idx].total_count_ = count;
|
||||
tree[idx].value_ = j;
|
||||
@ -230,11 +212,11 @@ static int GenerateOptimalTree(const int* const histogram, int histogram_size,
|
||||
if (tree_size > 1) { // Normal case.
|
||||
int tree_pool_size = 0;
|
||||
while (tree_size > 1) { // Finish when we have only one root.
|
||||
int count;
|
||||
uint32_t 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_pool[tree_pool_size - 2].total_count_;
|
||||
tree_size -= 2;
|
||||
{
|
||||
// Search for the insertion point.
|
||||
@ -271,8 +253,6 @@ static int GenerateOptimalTree(const int* const histogram, int histogram_size,
|
||||
}
|
||||
}
|
||||
}
|
||||
free(tree);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
@ -423,17 +403,15 @@ static void ConvertBitDepthsToSymbols(HuffmanTreeCode* const tree) {
|
||||
// -----------------------------------------------------------------------------
|
||||
// Main entry point
|
||||
|
||||
int VP8LCreateHuffmanTree(int* const histogram, int tree_depth_limit,
|
||||
HuffmanTreeCode* const tree) {
|
||||
const int num_symbols = tree->num_symbols;
|
||||
if (!OptimizeHuffmanForRle(num_symbols, histogram)) {
|
||||
return 0;
|
||||
}
|
||||
if (!GenerateOptimalTree(histogram, num_symbols,
|
||||
tree_depth_limit, tree->code_lengths)) {
|
||||
return 0;
|
||||
}
|
||||
void VP8LCreateHuffmanTree(uint32_t* const histogram, int tree_depth_limit,
|
||||
uint8_t* const buf_rle,
|
||||
HuffmanTree* const huff_tree,
|
||||
HuffmanTreeCode* const huff_code) {
|
||||
const int num_symbols = huff_code->num_symbols;
|
||||
memset(buf_rle, 0, num_symbols * sizeof(*buf_rle));
|
||||
OptimizeHuffmanForRle(num_symbols, buf_rle, histogram);
|
||||
GenerateOptimalTree(histogram, num_symbols, huff_tree, tree_depth_limit,
|
||||
huff_code->code_lengths);
|
||||
// Create the actual bit codes for the bit lengths.
|
||||
ConvertBitDepthsToSymbols(tree);
|
||||
return 1;
|
||||
ConvertBitDepthsToSymbols(huff_code);
|
||||
}
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Author: Jyrki Alakuijala (jyrki@google.com)
|
||||
@ -12,9 +14,9 @@
|
||||
#ifndef WEBP_UTILS_HUFFMAN_ENCODE_H_
|
||||
#define WEBP_UTILS_HUFFMAN_ENCODE_H_
|
||||
|
||||
#include "../types.h"
|
||||
#include "../webp/types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -31,16 +33,27 @@ typedef struct {
|
||||
uint16_t* codes; // Symbol Codes.
|
||||
} HuffmanTreeCode;
|
||||
|
||||
// Struct to represent the Huffman tree.
|
||||
typedef struct {
|
||||
uint32_t total_count_; // Symbol frequency.
|
||||
int value_; // Symbol value.
|
||||
int pool_index_left_; // Index for the left sub-tree.
|
||||
int pool_index_right_; // Index for the right sub-tree.
|
||||
} HuffmanTree;
|
||||
|
||||
// Turn the Huffman tree into a token sequence.
|
||||
// Returns the number of tokens used.
|
||||
int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree,
|
||||
HuffmanTreeToken* tokens, int max_tokens);
|
||||
|
||||
// Create an optimized tree, and tokenize it.
|
||||
int VP8LCreateHuffmanTree(int* const histogram, int tree_depth_limit,
|
||||
HuffmanTreeCode* const tree);
|
||||
// 'buf_rle' and 'huff_tree' are pre-allocated and the 'tree' is the constructed
|
||||
// huffman code tree.
|
||||
void VP8LCreateHuffmanTree(uint32_t* const histogram, int tree_depth_limit,
|
||||
uint8_t* const buf_rle, HuffmanTree* const huff_tree,
|
||||
HuffmanTreeCode* const tree);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Quantize levels for specified number of quantization-levels ([2, 256]).
|
||||
@ -14,10 +16,6 @@
|
||||
|
||||
#include "./quant_levels.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define NUM_SYMBOLS 256
|
||||
|
||||
#define MAX_ITER 6 // Maximum number of convergence steps.
|
||||
@ -140,15 +138,3 @@ int QuantizeLevels(uint8_t* const data, int width, int height,
|
||||
return 1;
|
||||
}
|
||||
|
||||
int DequantizeLevels(uint8_t* const data, int width, int height) {
|
||||
if (data == NULL || width <= 0 || height <= 0) return 0;
|
||||
// TODO(skal): implement gradient smoothing.
|
||||
(void)data;
|
||||
(void)width;
|
||||
(void)height;
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Alpha plane quantization utility
|
||||
@ -14,9 +16,9 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "../types.h"
|
||||
#include "../webp/types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -27,12 +29,7 @@ extern "C" {
|
||||
int QuantizeLevels(uint8_t* const data, int width, int height, int num_levels,
|
||||
uint64_t* const sse);
|
||||
|
||||
// Apply post-processing to input 'data' of size 'width'x'height' assuming
|
||||
// that the source was quantized to a reduced number of levels.
|
||||
// Returns false in case of error (data is NULL, invalid parameters, ...).
|
||||
int DequantizeLevels(uint8_t* const data, int width, int height);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Rescaling functions
|
||||
@ -11,124 +13,116 @@
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "../dsp/dsp.h"
|
||||
#include "./rescaler.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define RFIX 30
|
||||
#define MULT_FIX(x,y) (((int64_t)(x) * (y) + (1 << (RFIX - 1))) >> RFIX)
|
||||
|
||||
void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height,
|
||||
uint8_t* const dst, int dst_width, int dst_height,
|
||||
int dst_stride, int num_channels, int x_add, int x_sub,
|
||||
int y_add, int y_sub, int32_t* const work) {
|
||||
uint8_t* const dst,
|
||||
int dst_width, int dst_height, int dst_stride,
|
||||
int num_channels, rescaler_t* const work) {
|
||||
const int x_add = src_width, x_sub = dst_width;
|
||||
const int y_add = src_height, y_sub = dst_height;
|
||||
wrk->x_expand = (src_width < dst_width);
|
||||
wrk->y_expand = (src_height < dst_height);
|
||||
wrk->src_width = src_width;
|
||||
wrk->src_height = src_height;
|
||||
wrk->dst_width = dst_width;
|
||||
wrk->dst_height = dst_height;
|
||||
wrk->src_y = 0;
|
||||
wrk->dst_y = 0;
|
||||
wrk->dst = dst;
|
||||
wrk->dst_stride = dst_stride;
|
||||
wrk->num_channels = num_channels;
|
||||
|
||||
// for 'x_expand', we use bilinear interpolation
|
||||
wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add - x_sub;
|
||||
wrk->x_add = wrk->x_expand ? (x_sub - 1) : x_add;
|
||||
wrk->x_sub = wrk->x_expand ? (x_add - 1) : x_sub;
|
||||
wrk->y_accum = y_add;
|
||||
wrk->y_add = y_add;
|
||||
wrk->y_sub = y_sub;
|
||||
wrk->fx_scale = (1 << RFIX) / x_sub;
|
||||
wrk->fy_scale = (1 << RFIX) / y_sub;
|
||||
wrk->fxy_scale = wrk->x_expand ?
|
||||
((int64_t)dst_height << RFIX) / (x_sub * src_height) :
|
||||
((int64_t)dst_height << RFIX) / (x_add * src_height);
|
||||
if (!wrk->x_expand) { // fx_scale is not used otherwise
|
||||
wrk->fx_scale = WEBP_RESCALER_FRAC(1, wrk->x_sub);
|
||||
}
|
||||
// vertical scaling parameters
|
||||
wrk->y_add = wrk->y_expand ? y_add - 1 : y_add;
|
||||
wrk->y_sub = wrk->y_expand ? y_sub - 1 : y_sub;
|
||||
wrk->y_accum = wrk->y_expand ? wrk->y_sub : wrk->y_add;
|
||||
if (!wrk->y_expand) {
|
||||
// this is WEBP_RESCALER_FRAC(dst_height, x_add * y_add) without the cast.
|
||||
const uint64_t ratio =
|
||||
(uint64_t)dst_height * WEBP_RESCALER_ONE / (wrk->x_add * wrk->y_add);
|
||||
if (ratio != (uint32_t)ratio) {
|
||||
// We can't represent the ratio with the current fixed-point precision.
|
||||
// => We special-case fxy_scale = 0, in WebPRescalerExportRow().
|
||||
wrk->fxy_scale = 0;
|
||||
} else {
|
||||
wrk->fxy_scale = (uint32_t)ratio;
|
||||
}
|
||||
wrk->fy_scale = WEBP_RESCALER_FRAC(1, wrk->y_sub);
|
||||
} else {
|
||||
wrk->fy_scale = WEBP_RESCALER_FRAC(1, wrk->x_add);
|
||||
// wrk->fxy_scale is unused here.
|
||||
}
|
||||
wrk->irow = work;
|
||||
wrk->frow = work + num_channels * dst_width;
|
||||
memset(work, 0, 2 * dst_width * num_channels * sizeof(*work));
|
||||
|
||||
WebPRescalerDspInit();
|
||||
}
|
||||
|
||||
void WebPRescalerImportRow(WebPRescaler* const wrk,
|
||||
const uint8_t* const src, int channel) {
|
||||
const int x_stride = wrk->num_channels;
|
||||
const int x_out_max = wrk->dst_width * wrk->num_channels;
|
||||
int x_in = channel;
|
||||
int x_out;
|
||||
int accum = 0;
|
||||
if (!wrk->x_expand) {
|
||||
int sum = 0;
|
||||
for (x_out = channel; x_out < x_out_max; x_out += x_stride) {
|
||||
accum += wrk->x_add;
|
||||
for (; accum > 0; accum -= wrk->x_sub) {
|
||||
sum += src[x_in];
|
||||
x_in += x_stride;
|
||||
}
|
||||
{ // Emit next horizontal pixel.
|
||||
const int32_t base = src[x_in];
|
||||
const int32_t frac = base * (-accum);
|
||||
x_in += x_stride;
|
||||
wrk->frow[x_out] = (sum + base) * wrk->x_sub - frac;
|
||||
// fresh fractional start for next pixel
|
||||
sum = (int)MULT_FIX(frac, wrk->fx_scale);
|
||||
}
|
||||
int WebPRescalerGetScaledDimensions(int src_width, int src_height,
|
||||
int* const scaled_width,
|
||||
int* const scaled_height) {
|
||||
assert(scaled_width != NULL);
|
||||
assert(scaled_height != NULL);
|
||||
{
|
||||
int width = *scaled_width;
|
||||
int height = *scaled_height;
|
||||
|
||||
// if width is unspecified, scale original proportionally to height ratio.
|
||||
if (width == 0) {
|
||||
width = (src_width * height + src_height / 2) / src_height;
|
||||
}
|
||||
} else { // simple bilinear interpolation
|
||||
int left = src[channel], right = src[channel];
|
||||
for (x_out = channel; x_out < x_out_max; x_out += x_stride) {
|
||||
if (accum < 0) {
|
||||
left = right;
|
||||
x_in += x_stride;
|
||||
right = src[x_in];
|
||||
accum += wrk->x_add;
|
||||
}
|
||||
wrk->frow[x_out] = right * wrk->x_add + (left - right) * accum;
|
||||
accum -= wrk->x_sub;
|
||||
// if height is unspecified, scale original proportionally to width ratio.
|
||||
if (height == 0) {
|
||||
height = (src_height * width + src_width / 2) / src_width;
|
||||
}
|
||||
}
|
||||
// Accumulate the new row's contribution
|
||||
for (x_out = channel; x_out < x_out_max; x_out += x_stride) {
|
||||
wrk->irow[x_out] += wrk->frow[x_out];
|
||||
// Check if the overall dimensions still make sense.
|
||||
if (width <= 0 || height <= 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
*scaled_width = width;
|
||||
*scaled_height = height;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t* WebPRescalerExportRow(WebPRescaler* const wrk) {
|
||||
if (wrk->y_accum <= 0) {
|
||||
int x_out;
|
||||
uint8_t* const dst = wrk->dst;
|
||||
int32_t* const irow = wrk->irow;
|
||||
const int32_t* const frow = wrk->frow;
|
||||
const int yscale = wrk->fy_scale * (-wrk->y_accum);
|
||||
const int x_out_max = wrk->dst_width * wrk->num_channels;
|
||||
|
||||
for (x_out = 0; x_out < x_out_max; ++x_out) {
|
||||
const int frac = (int)MULT_FIX(frow[x_out], yscale);
|
||||
const int v = (int)MULT_FIX(irow[x_out] - frac, wrk->fxy_scale);
|
||||
dst[x_out] = (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
|
||||
irow[x_out] = frac; // new fractional start
|
||||
}
|
||||
wrk->y_accum += wrk->y_add;
|
||||
wrk->dst += wrk->dst_stride;
|
||||
return dst;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#undef MULT_FIX
|
||||
#undef RFIX
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// all-in-one calls
|
||||
|
||||
int WebPRescaleNeededLines(const WebPRescaler* const wrk, int max_num_lines) {
|
||||
const int num_lines = (wrk->y_accum + wrk->y_sub - 1) / wrk->y_sub;
|
||||
return (num_lines > max_num_lines) ? max_num_lines : num_lines;
|
||||
}
|
||||
|
||||
int WebPRescalerImport(WebPRescaler* const wrk, int num_lines,
|
||||
const uint8_t* src, int src_stride) {
|
||||
int total_imported = 0;
|
||||
while (total_imported < num_lines && wrk->y_accum > 0) {
|
||||
int channel;
|
||||
for (channel = 0; channel < wrk->num_channels; ++channel) {
|
||||
WebPRescalerImportRow(wrk, src, channel);
|
||||
while (total_imported < num_lines && !WebPRescalerHasPendingOutput(wrk)) {
|
||||
if (wrk->y_expand) {
|
||||
rescaler_t* const tmp = wrk->irow;
|
||||
wrk->irow = wrk->frow;
|
||||
wrk->frow = tmp;
|
||||
}
|
||||
WebPRescalerImportRow(wrk, src);
|
||||
if (!wrk->y_expand) { // Accumulate the contribution of the new row.
|
||||
int x;
|
||||
for (x = 0; x < wrk->num_channels * wrk->dst_width; ++x) {
|
||||
wrk->irow[x] += wrk->frow[x];
|
||||
}
|
||||
}
|
||||
++wrk->src_y;
|
||||
src += src_stride;
|
||||
++total_imported;
|
||||
wrk->y_accum -= wrk->y_sub;
|
||||
@ -146,7 +140,3 @@ int WebPRescalerExport(WebPRescaler* const rescaler) {
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Rescaling functions
|
||||
@ -12,64 +14,87 @@
|
||||
#ifndef WEBP_UTILS_RESCALER_H_
|
||||
#define WEBP_UTILS_RESCALER_H_
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../types.h"
|
||||
#include "../webp/types.h"
|
||||
|
||||
#define WEBP_RESCALER_RFIX 32 // fixed-point precision for multiplies
|
||||
#define WEBP_RESCALER_ONE (1ull << WEBP_RESCALER_RFIX)
|
||||
#define WEBP_RESCALER_FRAC(x, y) \
|
||||
((uint32_t)(((uint64_t)(x) << WEBP_RESCALER_RFIX) / (y)))
|
||||
|
||||
// Structure used for on-the-fly rescaling
|
||||
typedef struct {
|
||||
typedef uint32_t rescaler_t; // type for side-buffer
|
||||
typedef struct WebPRescaler WebPRescaler;
|
||||
struct WebPRescaler {
|
||||
int x_expand; // true if we're expanding in the x direction
|
||||
int y_expand; // true if we're expanding in the y direction
|
||||
int num_channels; // bytes to jump between pixels
|
||||
int fy_scale, fx_scale; // fixed-point scaling factor
|
||||
int64_t fxy_scale; // ''
|
||||
// we need hpel-precise add/sub increments, for the downsampled U/V planes.
|
||||
uint32_t fx_scale; // fixed-point scaling factors
|
||||
uint32_t fy_scale; // ''
|
||||
uint32_t fxy_scale; // ''
|
||||
int y_accum; // vertical accumulator
|
||||
int y_add, y_sub; // vertical increments (add ~= src, sub ~= dst)
|
||||
int x_add, x_sub; // horizontal increments (add ~= src, sub ~= dst)
|
||||
int y_add, y_sub; // vertical increments
|
||||
int x_add, x_sub; // horizontal increments
|
||||
int src_width, src_height; // source dimensions
|
||||
int dst_width, dst_height; // destination dimensions
|
||||
int src_y, dst_y; // row counters for input and output
|
||||
uint8_t* dst;
|
||||
int dst_stride;
|
||||
int32_t* irow, *frow; // work buffer
|
||||
} WebPRescaler;
|
||||
rescaler_t* irow, *frow; // work buffer
|
||||
};
|
||||
|
||||
// Initialize a rescaler given scratch area 'work' and dimensions of src & dst.
|
||||
void WebPRescalerInit(WebPRescaler* const wrk, int src_width, int src_height,
|
||||
void WebPRescalerInit(WebPRescaler* const rescaler,
|
||||
int src_width, int src_height,
|
||||
uint8_t* const dst,
|
||||
int dst_width, int dst_height, int dst_stride,
|
||||
int num_channels,
|
||||
int x_add, int x_sub,
|
||||
int y_add, int y_sub,
|
||||
int32_t* const work);
|
||||
rescaler_t* const work);
|
||||
|
||||
// Import a row of data and save its contribution in the rescaler.
|
||||
// 'channel' denotes the channel number to be imported.
|
||||
void WebPRescalerImportRow(WebPRescaler* const rescaler,
|
||||
const uint8_t* const src, int channel);
|
||||
// If either 'scaled_width' or 'scaled_height' (but not both) is 0 the value
|
||||
// will be calculated preserving the aspect ratio, otherwise the values are
|
||||
// left unmodified. Returns true on success, false if either value is 0 after
|
||||
// performing the scaling calculation.
|
||||
int WebPRescalerGetScaledDimensions(int src_width, int src_height,
|
||||
int* const scaled_width,
|
||||
int* const scaled_height);
|
||||
|
||||
// Returns the number of input lines needed next to produce one output line,
|
||||
// considering that the maximum available input lines are 'max_num_lines'.
|
||||
int WebPRescaleNeededLines(const WebPRescaler* const rescaler,
|
||||
int max_num_lines);
|
||||
|
||||
// Import multiple rows over all channels, until at least one row is ready to
|
||||
// be exported. Returns the actual number of lines that were imported.
|
||||
int WebPRescalerImport(WebPRescaler* const rescaler, int num_rows,
|
||||
const uint8_t* src, int src_stride);
|
||||
|
||||
// Return true if there is pending output rows ready.
|
||||
// Export as many rows as possible. Return the numbers of rows written.
|
||||
int WebPRescalerExport(WebPRescaler* const rescaler);
|
||||
|
||||
// Return true if input is finished
|
||||
static WEBP_INLINE
|
||||
int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) {
|
||||
return (rescaler->y_accum <= 0);
|
||||
int WebPRescalerInputDone(const WebPRescaler* const rescaler) {
|
||||
return (rescaler->src_y >= rescaler->src_height);
|
||||
}
|
||||
// Return true if output is finished
|
||||
static WEBP_INLINE
|
||||
int WebPRescalerOutputDone(const WebPRescaler* const rescaler) {
|
||||
return (rescaler->dst_y >= rescaler->dst_height);
|
||||
}
|
||||
|
||||
// Export one row from rescaler. Returns the pointer where output was written,
|
||||
// or NULL if no row was pending.
|
||||
uint8_t* WebPRescalerExportRow(WebPRescaler* const wrk);
|
||||
|
||||
// Export as many rows as possible. Return the numbers of rows written.
|
||||
int WebPRescalerExport(WebPRescaler* const wrk);
|
||||
// Return true if there are pending output rows ready.
|
||||
static WEBP_INLINE
|
||||
int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) {
|
||||
return !WebPRescalerOutputDone(rescaler) && (rescaler->y_accum <= 0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,30 +1,63 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Multi-threaded worker
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h> // for memset()
|
||||
#include "./thread.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
#include "./utils.h"
|
||||
|
||||
#ifdef WEBP_USE_THREAD
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
typedef HANDLE pthread_t;
|
||||
typedef CRITICAL_SECTION pthread_mutex_t;
|
||||
|
||||
#if _WIN32_WINNT >= 0x0600 // Windows Vista / Server 2008 or greater
|
||||
#define USE_WINDOWS_CONDITION_VARIABLE
|
||||
typedef CONDITION_VARIABLE pthread_cond_t;
|
||||
#else
|
||||
typedef struct {
|
||||
HANDLE waiting_sem_;
|
||||
HANDLE received_sem_;
|
||||
HANDLE signal_event_;
|
||||
} pthread_cond_t;
|
||||
#endif // _WIN32_WINNT >= 0x600
|
||||
|
||||
#ifndef WINAPI_FAMILY_PARTITION
|
||||
#define WINAPI_PARTITION_DESKTOP 1
|
||||
#define WINAPI_FAMILY_PARTITION(x) x
|
||||
#endif
|
||||
|
||||
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
|
||||
#define USE_CREATE_THREAD
|
||||
#endif
|
||||
|
||||
#else // !_WIN32
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
struct WebPWorkerImpl {
|
||||
pthread_mutex_t mutex_;
|
||||
pthread_cond_t condition_;
|
||||
pthread_t thread_;
|
||||
};
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// simplistic pthread emulation layer
|
||||
|
||||
@ -34,15 +67,29 @@ extern "C" {
|
||||
#define THREADFN unsigned int __stdcall
|
||||
#define THREAD_RETURN(val) (unsigned int)((DWORD_PTR)val)
|
||||
|
||||
#if _WIN32_WINNT >= 0x0501 // Windows XP or greater
|
||||
#define WaitForSingleObject(obj, timeout) \
|
||||
WaitForSingleObjectEx(obj, timeout, FALSE /*bAlertable*/)
|
||||
#endif
|
||||
|
||||
static int pthread_create(pthread_t* const thread, const void* attr,
|
||||
unsigned int (__stdcall *start)(void*), void* arg) {
|
||||
(void)attr;
|
||||
#ifdef USE_CREATE_THREAD
|
||||
*thread = CreateThread(NULL, /* lpThreadAttributes */
|
||||
0, /* dwStackSize */
|
||||
start,
|
||||
arg,
|
||||
0, /* dwStackSize */
|
||||
NULL); /* lpThreadId */
|
||||
#else
|
||||
*thread = (pthread_t)_beginthreadex(NULL, /* void *security */
|
||||
0, /* unsigned stack_size */
|
||||
start,
|
||||
arg,
|
||||
0, /* unsigned initflag */
|
||||
NULL); /* unsigned *thrdaddr */
|
||||
#endif
|
||||
if (*thread == NULL) return 1;
|
||||
SetThreadPriority(*thread, THREAD_PRIORITY_ABOVE_NORMAL);
|
||||
return 0;
|
||||
@ -57,7 +104,11 @@ static int pthread_join(pthread_t thread, void** value_ptr) {
|
||||
// Mutex
|
||||
static int pthread_mutex_init(pthread_mutex_t* const mutex, void* mutexattr) {
|
||||
(void)mutexattr;
|
||||
#if _WIN32_WINNT >= 0x0600 // Windows Vista / Server 2008 or greater
|
||||
InitializeCriticalSectionEx(mutex, 0 /*dwSpinCount*/, 0 /*Flags*/);
|
||||
#else
|
||||
InitializeCriticalSection(mutex);
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -79,14 +130,21 @@ static int pthread_mutex_destroy(pthread_mutex_t* const mutex) {
|
||||
// Condition
|
||||
static int pthread_cond_destroy(pthread_cond_t* const condition) {
|
||||
int ok = 1;
|
||||
#ifdef USE_WINDOWS_CONDITION_VARIABLE
|
||||
(void)condition;
|
||||
#else
|
||||
ok &= (CloseHandle(condition->waiting_sem_) != 0);
|
||||
ok &= (CloseHandle(condition->received_sem_) != 0);
|
||||
ok &= (CloseHandle(condition->signal_event_) != 0);
|
||||
#endif
|
||||
return !ok;
|
||||
}
|
||||
|
||||
static int pthread_cond_init(pthread_cond_t* const condition, void* cond_attr) {
|
||||
(void)cond_attr;
|
||||
#ifdef USE_WINDOWS_CONDITION_VARIABLE
|
||||
InitializeConditionVariable(condition);
|
||||
#else
|
||||
condition->waiting_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
|
||||
condition->received_sem_ = CreateSemaphore(NULL, 0, 1, NULL);
|
||||
condition->signal_event_ = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
@ -96,11 +154,15 @@ static int pthread_cond_init(pthread_cond_t* const condition, void* cond_attr) {
|
||||
pthread_cond_destroy(condition);
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pthread_cond_signal(pthread_cond_t* const condition) {
|
||||
int ok = 1;
|
||||
#ifdef USE_WINDOWS_CONDITION_VARIABLE
|
||||
WakeConditionVariable(condition);
|
||||
#else
|
||||
if (WaitForSingleObject(condition->waiting_sem_, 0) == WAIT_OBJECT_0) {
|
||||
// a thread is waiting in pthread_cond_wait: allow it to be notified
|
||||
ok = SetEvent(condition->signal_event_);
|
||||
@ -109,12 +171,16 @@ static int pthread_cond_signal(pthread_cond_t* const condition) {
|
||||
ok &= (WaitForSingleObject(condition->received_sem_, INFINITE) !=
|
||||
WAIT_OBJECT_0);
|
||||
}
|
||||
#endif
|
||||
return !ok;
|
||||
}
|
||||
|
||||
static int pthread_cond_wait(pthread_cond_t* const condition,
|
||||
pthread_mutex_t* const mutex) {
|
||||
int ok;
|
||||
#ifdef USE_WINDOWS_CONDITION_VARIABLE
|
||||
ok = SleepConditionVariableCS(condition, mutex, INFINITE);
|
||||
#else
|
||||
// note that there is a consumer available so the signal isn't dropped in
|
||||
// pthread_cond_signal
|
||||
if (!ReleaseSemaphore(condition->waiting_sem_, 1, NULL))
|
||||
@ -125,123 +191,168 @@ static int pthread_cond_wait(pthread_cond_t* const condition,
|
||||
WAIT_OBJECT_0);
|
||||
ok &= ReleaseSemaphore(condition->received_sem_, 1, NULL);
|
||||
pthread_mutex_lock(mutex);
|
||||
#endif
|
||||
return !ok;
|
||||
}
|
||||
|
||||
#else // _WIN32
|
||||
#else // !_WIN32
|
||||
# define THREADFN void*
|
||||
# define THREAD_RETURN(val) val
|
||||
#endif
|
||||
#endif // _WIN32
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static THREADFN WebPWorkerThreadLoop(void *ptr) { // thread loop
|
||||
static void Execute(WebPWorker* const worker); // Forward declaration.
|
||||
|
||||
static THREADFN ThreadLoop(void* ptr) {
|
||||
WebPWorker* const worker = (WebPWorker*)ptr;
|
||||
int done = 0;
|
||||
while (!done) {
|
||||
pthread_mutex_lock(&worker->mutex_);
|
||||
pthread_mutex_lock(&worker->impl_->mutex_);
|
||||
while (worker->status_ == OK) { // wait in idling mode
|
||||
pthread_cond_wait(&worker->condition_, &worker->mutex_);
|
||||
pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
|
||||
}
|
||||
if (worker->status_ == WORK) {
|
||||
if (worker->hook) {
|
||||
worker->had_error |= !worker->hook(worker->data1, worker->data2);
|
||||
}
|
||||
Execute(worker);
|
||||
worker->status_ = OK;
|
||||
} else if (worker->status_ == NOT_OK) { // finish the worker
|
||||
done = 1;
|
||||
}
|
||||
// signal to the main thread that we're done (for Sync())
|
||||
pthread_cond_signal(&worker->condition_);
|
||||
pthread_mutex_unlock(&worker->mutex_);
|
||||
pthread_cond_signal(&worker->impl_->condition_);
|
||||
pthread_mutex_unlock(&worker->impl_->mutex_);
|
||||
}
|
||||
return THREAD_RETURN(NULL); // Thread is finished
|
||||
}
|
||||
|
||||
// main thread state control
|
||||
static void WebPWorkerChangeState(WebPWorker* const worker,
|
||||
WebPWorkerStatus new_status) {
|
||||
// no-op when attempting to change state on a thread that didn't come up
|
||||
if (worker->status_ < OK) return;
|
||||
static void ChangeState(WebPWorker* const worker,
|
||||
WebPWorkerStatus new_status) {
|
||||
// No-op when attempting to change state on a thread that didn't come up.
|
||||
// Checking status_ without acquiring the lock first would result in a data
|
||||
// race.
|
||||
if (worker->impl_ == NULL) return;
|
||||
|
||||
pthread_mutex_lock(&worker->mutex_);
|
||||
// wait for the worker to finish
|
||||
while (worker->status_ != OK) {
|
||||
pthread_cond_wait(&worker->condition_, &worker->mutex_);
|
||||
pthread_mutex_lock(&worker->impl_->mutex_);
|
||||
if (worker->status_ >= OK) {
|
||||
// wait for the worker to finish
|
||||
while (worker->status_ != OK) {
|
||||
pthread_cond_wait(&worker->impl_->condition_, &worker->impl_->mutex_);
|
||||
}
|
||||
// assign new status and release the working thread if needed
|
||||
if (new_status != OK) {
|
||||
worker->status_ = new_status;
|
||||
pthread_cond_signal(&worker->impl_->condition_);
|
||||
}
|
||||
}
|
||||
// assign new status and release the working thread if needed
|
||||
if (new_status != OK) {
|
||||
worker->status_ = new_status;
|
||||
pthread_cond_signal(&worker->condition_);
|
||||
}
|
||||
pthread_mutex_unlock(&worker->mutex_);
|
||||
pthread_mutex_unlock(&worker->impl_->mutex_);
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif // WEBP_USE_THREAD
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void WebPWorkerInit(WebPWorker* const worker) {
|
||||
static void Init(WebPWorker* const worker) {
|
||||
memset(worker, 0, sizeof(*worker));
|
||||
worker->status_ = NOT_OK;
|
||||
}
|
||||
|
||||
int WebPWorkerSync(WebPWorker* const worker) {
|
||||
static int Sync(WebPWorker* const worker) {
|
||||
#ifdef WEBP_USE_THREAD
|
||||
WebPWorkerChangeState(worker, OK);
|
||||
ChangeState(worker, OK);
|
||||
#endif
|
||||
assert(worker->status_ <= OK);
|
||||
return !worker->had_error;
|
||||
}
|
||||
|
||||
int WebPWorkerReset(WebPWorker* const worker) {
|
||||
static int Reset(WebPWorker* const worker) {
|
||||
int ok = 1;
|
||||
worker->had_error = 0;
|
||||
if (worker->status_ < OK) {
|
||||
#ifdef WEBP_USE_THREAD
|
||||
if (pthread_mutex_init(&worker->mutex_, NULL) ||
|
||||
pthread_cond_init(&worker->condition_, NULL)) {
|
||||
worker->impl_ = (WebPWorkerImpl*)WebPSafeCalloc(1, sizeof(*worker->impl_));
|
||||
if (worker->impl_ == NULL) {
|
||||
return 0;
|
||||
}
|
||||
pthread_mutex_lock(&worker->mutex_);
|
||||
ok = !pthread_create(&worker->thread_, NULL, WebPWorkerThreadLoop, worker);
|
||||
if (pthread_mutex_init(&worker->impl_->mutex_, NULL)) {
|
||||
goto Error;
|
||||
}
|
||||
if (pthread_cond_init(&worker->impl_->condition_, NULL)) {
|
||||
pthread_mutex_destroy(&worker->impl_->mutex_);
|
||||
goto Error;
|
||||
}
|
||||
pthread_mutex_lock(&worker->impl_->mutex_);
|
||||
ok = !pthread_create(&worker->impl_->thread_, NULL, ThreadLoop, worker);
|
||||
if (ok) worker->status_ = OK;
|
||||
pthread_mutex_unlock(&worker->mutex_);
|
||||
pthread_mutex_unlock(&worker->impl_->mutex_);
|
||||
if (!ok) {
|
||||
pthread_mutex_destroy(&worker->impl_->mutex_);
|
||||
pthread_cond_destroy(&worker->impl_->condition_);
|
||||
Error:
|
||||
WebPSafeFree(worker->impl_);
|
||||
worker->impl_ = NULL;
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
worker->status_ = OK;
|
||||
#endif
|
||||
} else if (worker->status_ > OK) {
|
||||
ok = WebPWorkerSync(worker);
|
||||
ok = Sync(worker);
|
||||
}
|
||||
assert(!ok || (worker->status_ == OK));
|
||||
return ok;
|
||||
}
|
||||
|
||||
void WebPWorkerLaunch(WebPWorker* const worker) {
|
||||
#ifdef WEBP_USE_THREAD
|
||||
WebPWorkerChangeState(worker, WORK);
|
||||
#else
|
||||
if (worker->hook)
|
||||
static void Execute(WebPWorker* const worker) {
|
||||
if (worker->hook != NULL) {
|
||||
worker->had_error |= !worker->hook(worker->data1, worker->data2);
|
||||
}
|
||||
}
|
||||
|
||||
static void Launch(WebPWorker* const worker) {
|
||||
#ifdef WEBP_USE_THREAD
|
||||
ChangeState(worker, WORK);
|
||||
#else
|
||||
Execute(worker);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebPWorkerEnd(WebPWorker* const worker) {
|
||||
if (worker->status_ >= OK) {
|
||||
static void End(WebPWorker* const worker) {
|
||||
#ifdef WEBP_USE_THREAD
|
||||
WebPWorkerChangeState(worker, NOT_OK);
|
||||
pthread_join(worker->thread_, NULL);
|
||||
pthread_mutex_destroy(&worker->mutex_);
|
||||
pthread_cond_destroy(&worker->condition_);
|
||||
#else
|
||||
worker->status_ = NOT_OK;
|
||||
#endif
|
||||
if (worker->impl_ != NULL) {
|
||||
ChangeState(worker, NOT_OK);
|
||||
pthread_join(worker->impl_->thread_, NULL);
|
||||
pthread_mutex_destroy(&worker->impl_->mutex_);
|
||||
pthread_cond_destroy(&worker->impl_->condition_);
|
||||
WebPSafeFree(worker->impl_);
|
||||
worker->impl_ = NULL;
|
||||
}
|
||||
#else
|
||||
worker->status_ = NOT_OK;
|
||||
assert(worker->impl_ == NULL);
|
||||
#endif
|
||||
assert(worker->status_ == NOT_OK);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
static WebPWorkerInterface g_worker_interface = {
|
||||
Init, Reset, Sync, Launch, Execute, End
|
||||
};
|
||||
|
||||
int WebPSetWorkerInterface(const WebPWorkerInterface* const winterface) {
|
||||
if (winterface == NULL ||
|
||||
winterface->Init == NULL || winterface->Reset == NULL ||
|
||||
winterface->Sync == NULL || winterface->Launch == NULL ||
|
||||
winterface->Execute == NULL || winterface->End == NULL) {
|
||||
return 0;
|
||||
}
|
||||
g_worker_interface = *winterface;
|
||||
return 1;
|
||||
}
|
||||
|
||||
const WebPWorkerInterface* WebPGetWorkerInterface(void) {
|
||||
return &g_worker_interface;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Multi-threaded worker
|
||||
@ -12,29 +14,15 @@
|
||||
#ifndef WEBP_UTILS_THREAD_H_
|
||||
#define WEBP_UTILS_THREAD_H_
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "../webp/config.h"
|
||||
#endif
|
||||
|
||||
#if WEBP_USE_THREAD
|
||||
#include "../webp/types.h"
|
||||
|
||||
#if defined(_WIN32)
|
||||
|
||||
#include <windows.h>
|
||||
typedef HANDLE pthread_t;
|
||||
typedef CRITICAL_SECTION pthread_mutex_t;
|
||||
typedef struct {
|
||||
HANDLE waiting_sem_;
|
||||
HANDLE received_sem_;
|
||||
HANDLE signal_event_;
|
||||
} pthread_cond_t;
|
||||
|
||||
#else
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#endif /* _WIN32 */
|
||||
#endif /* WEBP_USE_THREAD */
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// State of the worker thread object
|
||||
typedef enum {
|
||||
@ -47,13 +35,12 @@ typedef enum {
|
||||
// arguments (data1 and data2), and should return false in case of error.
|
||||
typedef int (*WebPWorkerHook)(void*, void*);
|
||||
|
||||
// Synchronize object used to launch job in the worker thread
|
||||
// Platform-dependent implementation details for the worker.
|
||||
typedef struct WebPWorkerImpl WebPWorkerImpl;
|
||||
|
||||
// Synchronization object used to launch job in the worker thread
|
||||
typedef struct {
|
||||
#if WEBP_USE_THREAD
|
||||
pthread_mutex_t mutex_;
|
||||
pthread_cond_t condition_;
|
||||
pthread_t thread_;
|
||||
#endif
|
||||
WebPWorkerImpl* impl_;
|
||||
WebPWorkerStatus status_;
|
||||
WebPWorkerHook hook; // hook to call
|
||||
void* data1; // first argument passed to 'hook'
|
||||
@ -61,25 +48,45 @@ typedef struct {
|
||||
int had_error; // return value of the last call to 'hook'
|
||||
} WebPWorker;
|
||||
|
||||
// Must be called first, before any other method.
|
||||
void WebPWorkerInit(WebPWorker* const worker);
|
||||
// Must be called initialize the object and spawn the thread. Re-entrant.
|
||||
// Will potentially launch the thread. Returns false in case of error.
|
||||
int WebPWorkerReset(WebPWorker* const worker);
|
||||
// Make sure the previous work is finished. Returns true if worker->had_error
|
||||
// was not set and not error condition was triggered by the working thread.
|
||||
int WebPWorkerSync(WebPWorker* const worker);
|
||||
// Trigger the thread to call hook() with data1 and data2 argument. These
|
||||
// hook/data1/data2 can be changed at any time before calling this function,
|
||||
// but not be changed afterward until the next call to WebPWorkerSync().
|
||||
void WebPWorkerLaunch(WebPWorker* const worker);
|
||||
// Kill the thread and terminate the object. To use the object again, one
|
||||
// must call WebPWorkerReset() again.
|
||||
void WebPWorkerEnd(WebPWorker* const worker);
|
||||
// The interface for all thread-worker related functions. All these functions
|
||||
// must be implemented.
|
||||
typedef struct {
|
||||
// Must be called first, before any other method.
|
||||
void (*Init)(WebPWorker* const worker);
|
||||
// Must be called to initialize the object and spawn the thread. Re-entrant.
|
||||
// Will potentially launch the thread. Returns false in case of error.
|
||||
int (*Reset)(WebPWorker* const worker);
|
||||
// Makes sure the previous work is finished. Returns true if worker->had_error
|
||||
// was not set and no error condition was triggered by the working thread.
|
||||
int (*Sync)(WebPWorker* const worker);
|
||||
// Triggers the thread to call hook() with data1 and data2 arguments. These
|
||||
// hook/data1/data2 values can be changed at any time before calling this
|
||||
// function, but not be changed afterward until the next call to Sync().
|
||||
void (*Launch)(WebPWorker* const worker);
|
||||
// This function is similar to Launch() except that it calls the
|
||||
// hook directly instead of using a thread. Convenient to bypass the thread
|
||||
// mechanism while still using the WebPWorker structs. Sync() must
|
||||
// still be called afterward (for error reporting).
|
||||
void (*Execute)(WebPWorker* const worker);
|
||||
// Kill the thread and terminate the object. To use the object again, one
|
||||
// must call Reset() again.
|
||||
void (*End)(WebPWorker* const worker);
|
||||
} WebPWorkerInterface;
|
||||
|
||||
// Install a new set of threading functions, overriding the defaults. This
|
||||
// should be done before any workers are started, i.e., before any encoding or
|
||||
// decoding takes place. The contents of the interface struct are copied, it
|
||||
// is safe to free the corresponding memory after this call. This function is
|
||||
// not thread-safe. Return false in case of invalid pointer or methods.
|
||||
WEBP_EXTERN(int) WebPSetWorkerInterface(
|
||||
const WebPWorkerInterface* const winterface);
|
||||
|
||||
// Retrieve the currently set thread worker interface.
|
||||
WEBP_EXTERN(const WebPWorkerInterface*) WebPGetWorkerInterface(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Misc. common utility functions
|
||||
@ -10,35 +12,228 @@
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h> // for memcpy()
|
||||
#include "../webp/decode.h"
|
||||
#include "../webp/encode.h"
|
||||
#include "./utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
// If PRINT_MEM_INFO is defined, extra info (like total memory used, number of
|
||||
// alloc/free etc) is printed. For debugging/tuning purpose only (it's slow,
|
||||
// and not multi-thread safe!).
|
||||
// An interesting alternative is valgrind's 'massif' tool:
|
||||
// http://valgrind.org/docs/manual/ms-manual.html
|
||||
// Here is an example command line:
|
||||
/* valgrind --tool=massif --massif-out-file=massif.out \
|
||||
--stacks=yes --alloc-fn=WebPSafeAlloc --alloc-fn=WebPSafeCalloc
|
||||
ms_print massif.out
|
||||
*/
|
||||
// In addition:
|
||||
// * if PRINT_MEM_TRAFFIC is defined, all the details of the malloc/free cycles
|
||||
// are printed.
|
||||
// * if MALLOC_FAIL_AT is defined, the global environment variable
|
||||
// $MALLOC_FAIL_AT is used to simulate a memory error when calloc or malloc
|
||||
// is called for the nth time. Example usage:
|
||||
// export MALLOC_FAIL_AT=50 && ./examples/cwebp input.png
|
||||
// * if MALLOC_LIMIT is defined, the global environment variable $MALLOC_LIMIT
|
||||
// sets the maximum amount of memory (in bytes) made available to libwebp.
|
||||
// This can be used to emulate environment with very limited memory.
|
||||
// Example: export MALLOC_LIMIT=64000000 && ./examples/dwebp picture.webp
|
||||
|
||||
// #define PRINT_MEM_INFO
|
||||
// #define PRINT_MEM_TRAFFIC
|
||||
// #define MALLOC_FAIL_AT
|
||||
// #define MALLOC_LIMIT
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Checked memory allocation
|
||||
|
||||
static int CheckSizeArguments(uint64_t nmemb, size_t size) {
|
||||
#if defined(PRINT_MEM_INFO)
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
static int num_malloc_calls = 0;
|
||||
static int num_calloc_calls = 0;
|
||||
static int num_free_calls = 0;
|
||||
static int countdown_to_fail = 0; // 0 = off
|
||||
|
||||
typedef struct MemBlock MemBlock;
|
||||
struct MemBlock {
|
||||
void* ptr_;
|
||||
size_t size_;
|
||||
MemBlock* next_;
|
||||
};
|
||||
|
||||
static MemBlock* all_blocks = NULL;
|
||||
static size_t total_mem = 0;
|
||||
static size_t total_mem_allocated = 0;
|
||||
static size_t high_water_mark = 0;
|
||||
static size_t mem_limit = 0;
|
||||
|
||||
static int exit_registered = 0;
|
||||
|
||||
static void PrintMemInfo(void) {
|
||||
fprintf(stderr, "\nMEMORY INFO:\n");
|
||||
fprintf(stderr, "num calls to: malloc = %4d\n", num_malloc_calls);
|
||||
fprintf(stderr, " calloc = %4d\n", num_calloc_calls);
|
||||
fprintf(stderr, " free = %4d\n", num_free_calls);
|
||||
fprintf(stderr, "total_mem: %u\n", (uint32_t)total_mem);
|
||||
fprintf(stderr, "total_mem allocated: %u\n", (uint32_t)total_mem_allocated);
|
||||
fprintf(stderr, "high-water mark: %u\n", (uint32_t)high_water_mark);
|
||||
while (all_blocks != NULL) {
|
||||
MemBlock* b = all_blocks;
|
||||
all_blocks = b->next_;
|
||||
free(b);
|
||||
}
|
||||
}
|
||||
|
||||
static void Increment(int* const v) {
|
||||
if (!exit_registered) {
|
||||
#if defined(MALLOC_FAIL_AT)
|
||||
{
|
||||
const char* const malloc_fail_at_str = getenv("MALLOC_FAIL_AT");
|
||||
if (malloc_fail_at_str != NULL) {
|
||||
countdown_to_fail = atoi(malloc_fail_at_str);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#if defined(MALLOC_LIMIT)
|
||||
{
|
||||
const char* const malloc_limit_str = getenv("MALLOC_LIMIT");
|
||||
if (malloc_limit_str != NULL) {
|
||||
mem_limit = atoi(malloc_limit_str);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
(void)countdown_to_fail;
|
||||
(void)mem_limit;
|
||||
atexit(PrintMemInfo);
|
||||
exit_registered = 1;
|
||||
}
|
||||
++*v;
|
||||
}
|
||||
|
||||
static void AddMem(void* ptr, size_t size) {
|
||||
if (ptr != NULL) {
|
||||
MemBlock* const b = (MemBlock*)malloc(sizeof(*b));
|
||||
if (b == NULL) abort();
|
||||
b->next_ = all_blocks;
|
||||
all_blocks = b;
|
||||
b->ptr_ = ptr;
|
||||
b->size_ = size;
|
||||
total_mem += size;
|
||||
total_mem_allocated += size;
|
||||
#if defined(PRINT_MEM_TRAFFIC)
|
||||
#if defined(MALLOC_FAIL_AT)
|
||||
fprintf(stderr, "fail-count: %5d [mem=%u]\n",
|
||||
num_malloc_calls + num_calloc_calls, (uint32_t)total_mem);
|
||||
#else
|
||||
fprintf(stderr, "Mem: %u (+%u)\n", (uint32_t)total_mem, (uint32_t)size);
|
||||
#endif
|
||||
#endif
|
||||
if (total_mem > high_water_mark) high_water_mark = total_mem;
|
||||
}
|
||||
}
|
||||
|
||||
static void SubMem(void* ptr) {
|
||||
if (ptr != NULL) {
|
||||
MemBlock** b = &all_blocks;
|
||||
// Inefficient search, but that's just for debugging.
|
||||
while (*b != NULL && (*b)->ptr_ != ptr) b = &(*b)->next_;
|
||||
if (*b == NULL) {
|
||||
fprintf(stderr, "Invalid pointer free! (%p)\n", ptr);
|
||||
abort();
|
||||
}
|
||||
{
|
||||
MemBlock* const block = *b;
|
||||
*b = block->next_;
|
||||
total_mem -= block->size_;
|
||||
#if defined(PRINT_MEM_TRAFFIC)
|
||||
fprintf(stderr, "Mem: %u (-%u)\n",
|
||||
(uint32_t)total_mem, (uint32_t)block->size_);
|
||||
#endif
|
||||
free(block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
#define Increment(v) do {} while (0)
|
||||
#define AddMem(p, s) do {} while (0)
|
||||
#define SubMem(p) do {} while (0)
|
||||
#endif
|
||||
|
||||
// Returns 0 in case of overflow of nmemb * size.
|
||||
static int CheckSizeArgumentsOverflow(uint64_t nmemb, size_t size) {
|
||||
const uint64_t total_size = nmemb * size;
|
||||
if (nmemb == 0) return 1;
|
||||
if ((uint64_t)size > WEBP_MAX_ALLOCABLE_MEMORY / nmemb) return 0;
|
||||
if (total_size != (size_t)total_size) return 0;
|
||||
#if defined(PRINT_MEM_INFO) && defined(MALLOC_FAIL_AT)
|
||||
if (countdown_to_fail > 0 && --countdown_to_fail == 0) {
|
||||
return 0; // fake fail!
|
||||
}
|
||||
#endif
|
||||
#if defined(MALLOC_LIMIT)
|
||||
if (mem_limit > 0 && total_mem + total_size >= mem_limit) {
|
||||
return 0; // fake fail!
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void* WebPSafeMalloc(uint64_t nmemb, size_t size) {
|
||||
if (!CheckSizeArguments(nmemb, size)) return NULL;
|
||||
return malloc((size_t)(nmemb * size));
|
||||
void* ptr;
|
||||
Increment(&num_malloc_calls);
|
||||
if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
|
||||
assert(nmemb * size > 0);
|
||||
ptr = malloc((size_t)(nmemb * size));
|
||||
AddMem(ptr, (size_t)(nmemb * size));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void* WebPSafeCalloc(uint64_t nmemb, size_t size) {
|
||||
if (!CheckSizeArguments(nmemb, size)) return NULL;
|
||||
return calloc((size_t)nmemb, size);
|
||||
void* ptr;
|
||||
Increment(&num_calloc_calls);
|
||||
if (!CheckSizeArgumentsOverflow(nmemb, size)) return NULL;
|
||||
assert(nmemb * size > 0);
|
||||
ptr = calloc((size_t)nmemb, size);
|
||||
AddMem(ptr, (size_t)(nmemb * size));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void WebPSafeFree(void* const ptr) {
|
||||
if (ptr != NULL) {
|
||||
Increment(&num_free_calls);
|
||||
SubMem(ptr);
|
||||
}
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
// Public API function.
|
||||
void WebPFree(void* ptr) {
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
void WebPCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height) {
|
||||
assert(src != NULL && dst != NULL);
|
||||
assert(src_stride >= width && dst_stride >= width);
|
||||
while (height-- > 0) {
|
||||
memcpy(dst, src, width);
|
||||
src += src_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) {
|
||||
assert(src != NULL && dst != NULL);
|
||||
assert(src->width == dst->width && src->height == dst->height);
|
||||
assert(src->use_argb && dst->use_argb);
|
||||
WebPCopyPlane((uint8_t*)src->argb, 4 * src->argb_stride, (uint8_t*)dst->argb,
|
||||
4 * dst->argb_stride, 4 * src->width, src->height);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
@ -1,20 +1,25 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// Use of this source code is governed by a BSD-style license
|
||||
// that can be found in the COPYING file in the root of the source
|
||||
// tree. An additional intellectual property rights grant can be found
|
||||
// in the file PATENTS. All contributing project authors may
|
||||
// be found in the AUTHORS file in the root of the source tree.
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Misc. common utility functions
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
// Authors: Skal (pascal.massimino@gmail.com)
|
||||
// Urvang (urvang@google.com)
|
||||
|
||||
#ifndef WEBP_UTILS_UTILS_H_
|
||||
#define WEBP_UTILS_UTILS_H_
|
||||
|
||||
#include "../types.h"
|
||||
#include <assert.h>
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#include "../webp/types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
@ -30,14 +35,107 @@ extern "C" {
|
||||
// somewhere (like: malloc(num_pixels * sizeof(*something))). That's why this
|
||||
// safe malloc() borrows the signature from calloc(), pointing at the dangerous
|
||||
// underlying multiply involved.
|
||||
void* WebPSafeMalloc(uint64_t nmemb, size_t size);
|
||||
WEBP_EXTERN(void*) WebPSafeMalloc(uint64_t nmemb, size_t size);
|
||||
// Note that WebPSafeCalloc() expects the second argument type to be 'size_t'
|
||||
// in order to favor the "calloc(num_foo, sizeof(foo))" pattern.
|
||||
void* WebPSafeCalloc(uint64_t nmemb, size_t size);
|
||||
WEBP_EXTERN(void*) WebPSafeCalloc(uint64_t nmemb, size_t size);
|
||||
|
||||
// Companion deallocation function to the above allocations.
|
||||
WEBP_EXTERN(void) WebPSafeFree(void* const ptr);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Alignment
|
||||
|
||||
#define WEBP_ALIGN_CST 31
|
||||
#define WEBP_ALIGN(PTR) ((uintptr_t)((PTR) + WEBP_ALIGN_CST) & ~WEBP_ALIGN_CST)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Reading/writing data.
|
||||
|
||||
// Read 16, 24 or 32 bits stored in little-endian order.
|
||||
static WEBP_INLINE int GetLE16(const uint8_t* const data) {
|
||||
return (int)(data[0] << 0) | (data[1] << 8);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int GetLE24(const uint8_t* const data) {
|
||||
return GetLE16(data) | (data[2] << 16);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t GetLE32(const uint8_t* const data) {
|
||||
return GetLE16(data) | ((uint32_t)GetLE16(data + 2) << 16);
|
||||
}
|
||||
|
||||
// Store 16, 24 or 32 bits in little-endian order.
|
||||
static WEBP_INLINE void PutLE16(uint8_t* const data, int val) {
|
||||
assert(val < (1 << 16));
|
||||
data[0] = (val >> 0);
|
||||
data[1] = (val >> 8);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void PutLE24(uint8_t* const data, int val) {
|
||||
assert(val < (1 << 24));
|
||||
PutLE16(data, val & 0xffff);
|
||||
data[2] = (val >> 16);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void PutLE32(uint8_t* const data, uint32_t val) {
|
||||
PutLE16(data, (int)(val & 0xffff));
|
||||
PutLE16(data + 2, (int)(val >> 16));
|
||||
}
|
||||
|
||||
// Returns (int)floor(log2(n)). n must be > 0.
|
||||
// use GNU builtins where available.
|
||||
#if defined(__GNUC__) && \
|
||||
((__GNUC__ == 3 && __GNUC_MINOR__ >= 4) || __GNUC__ >= 4)
|
||||
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
|
||||
return 31 ^ __builtin_clz(n);
|
||||
}
|
||||
#elif defined(_MSC_VER) && _MSC_VER > 1310 && \
|
||||
(defined(_M_X64) || defined(_M_IX86))
|
||||
#include <intrin.h>
|
||||
#pragma intrinsic(_BitScanReverse)
|
||||
|
||||
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
|
||||
unsigned long first_set_bit;
|
||||
_BitScanReverse(&first_set_bit, n);
|
||||
return first_set_bit;
|
||||
}
|
||||
#else
|
||||
static WEBP_INLINE int BitsLog2Floor(uint32_t n) {
|
||||
int log = 0;
|
||||
uint32_t value = n;
|
||||
int i;
|
||||
|
||||
for (i = 4; i >= 0; --i) {
|
||||
const int shift = (1 << i);
|
||||
const uint32_t x = value >> shift;
|
||||
if (x != 0) {
|
||||
value = x;
|
||||
log += shift;
|
||||
}
|
||||
}
|
||||
return log;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Pixel copying.
|
||||
|
||||
struct WebPPicture;
|
||||
|
||||
// Copy width x height pixels from 'src' to 'dst' honoring the strides.
|
||||
WEBP_EXTERN(void) WebPCopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride,
|
||||
int width, int height);
|
||||
|
||||
// Copy ARGB pixels from 'src' to 'dst' honoring strides. 'src' and 'dst' are
|
||||
// assumed to be already allocated and using ARGB data.
|
||||
WEBP_EXTERN(void) WebPCopyPixels(const struct WebPPicture* const src,
|
||||
struct WebPPicture* const dst);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
|
||||
63
drivers/webpold/SCsub
Normal file
63
drivers/webpold/SCsub
Normal file
@ -0,0 +1,63 @@
|
||||
Import('env')
|
||||
|
||||
|
||||
webp_sources = [
|
||||
"webp/mux/muxedit.c",
|
||||
"webp/mux/muxread.c",
|
||||
"webp/mux/muxinternal.c",
|
||||
"webp/mux/demux.c",
|
||||
"webp/enc/tree.c",
|
||||
"webp/enc/analysis.c",
|
||||
"webp/enc/backward_references.c",
|
||||
"webp/enc/alpha.c",
|
||||
"webp/enc/picture.c",
|
||||
"webp/enc/frame.c",
|
||||
"webp/enc/webpenc.c",
|
||||
"webp/enc/cost.c",
|
||||
"webp/enc/filter.c",
|
||||
"webp/enc/vp8l.c",
|
||||
"webp/enc/quant.c",
|
||||
"webp/enc/histogram.c",
|
||||
"webp/enc/syntax.c",
|
||||
"webp/enc/config.c",
|
||||
"webp/enc/layer.c",
|
||||
"webp/enc/iterator.c",
|
||||
"webp/dsp/dec_sse2.c",
|
||||
"webp/dsp/upsampling_sse2.c",
|
||||
"webp/dsp/dec_neon.c",
|
||||
"webp/dsp/enc.c",
|
||||
"webp/dsp/enc_sse2.c",
|
||||
"webp/dsp/upsampling.c",
|
||||
"webp/dsp/lossless.c",
|
||||
"webp/dsp/cpu.c",
|
||||
"webp/dsp/dec.c",
|
||||
"webp/dsp/yuv.c",
|
||||
"webp/utils/bit_reader.c",
|
||||
"webp/utils/filters.c",
|
||||
"webp/utils/bit_writer.c",
|
||||
"webp/utils/thread.c",
|
||||
"webp/utils/quant_levels.c",
|
||||
"webp/utils/color_cache.c",
|
||||
"webp/utils/rescaler.c",
|
||||
"webp/utils/utils.c",
|
||||
"webp/utils/huffman.c",
|
||||
"webp/utils/huffman_encode.c",
|
||||
"webp/dec/tree.c",
|
||||
"webp/dec/alpha.c",
|
||||
"webp/dec/frame.c",
|
||||
"webp/dec/vp8l.c",
|
||||
"webp/dec/vp8.c",
|
||||
"webp/dec/quant.c",
|
||||
"webp/dec/webp.c",
|
||||
"webp/dec/buffer.c",
|
||||
"webp/dec/io.c",
|
||||
"webp/dec/layer.c",
|
||||
"webp/dec/idec.c",
|
||||
"webp/image_loader_webp.cpp"
|
||||
]
|
||||
|
||||
env.drivers_sources+=webp_sources
|
||||
|
||||
#env.add_source_files(env.drivers_sources, webp_sources)
|
||||
|
||||
Export('env')
|
||||
140
drivers/webpold/dec/alpha.c
Normal file
140
drivers/webpold/dec/alpha.c
Normal file
@ -0,0 +1,140 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Alpha-plane decompression.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "./vp8i.h"
|
||||
#include "./vp8li.h"
|
||||
#include "../utils/filters.h"
|
||||
#include "../utils/quant_levels.h"
|
||||
#include "../format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// TODO(skal): move to dsp/ ?
|
||||
static void CopyPlane(const uint8_t* src, int src_stride,
|
||||
uint8_t* dst, int dst_stride, int width, int height) {
|
||||
while (height-- > 0) {
|
||||
memcpy(dst, src, width);
|
||||
src += src_stride;
|
||||
dst += dst_stride;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Decodes the compressed data 'data' of size 'data_size' into the 'output'.
|
||||
// The 'output' buffer should be pre-allocated and must be of the same
|
||||
// dimension 'height'x'stride', as that of the image.
|
||||
//
|
||||
// Returns 1 on successfully decoding the compressed alpha and
|
||||
// 0 if either:
|
||||
// error in bit-stream header (invalid compression mode or filter), or
|
||||
// error returned by appropriate compression method.
|
||||
|
||||
static int DecodeAlpha(const uint8_t* data, size_t data_size,
|
||||
int width, int height, int stride, uint8_t* output) {
|
||||
uint8_t* decoded_data = NULL;
|
||||
const size_t decoded_size = height * width;
|
||||
uint8_t* unfiltered_data = NULL;
|
||||
WEBP_FILTER_TYPE filter;
|
||||
int pre_processing;
|
||||
int rsrv;
|
||||
int ok = 0;
|
||||
int method;
|
||||
|
||||
assert(width > 0 && height > 0 && stride >= width);
|
||||
assert(data != NULL && output != NULL);
|
||||
|
||||
if (data_size <= ALPHA_HEADER_LEN) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
method = (data[0] >> 0) & 0x03;
|
||||
filter = (data[0] >> 2) & 0x03;
|
||||
pre_processing = (data[0] >> 4) & 0x03;
|
||||
rsrv = (data[0] >> 6) & 0x03;
|
||||
if (method < ALPHA_NO_COMPRESSION ||
|
||||
method > ALPHA_LOSSLESS_COMPRESSION ||
|
||||
filter >= WEBP_FILTER_LAST ||
|
||||
pre_processing > ALPHA_PREPROCESSED_LEVELS ||
|
||||
rsrv != 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (method == ALPHA_NO_COMPRESSION) {
|
||||
ok = (data_size >= decoded_size);
|
||||
decoded_data = (uint8_t*)data + ALPHA_HEADER_LEN;
|
||||
} else {
|
||||
decoded_data = (uint8_t*)malloc(decoded_size);
|
||||
if (decoded_data == NULL) return 0;
|
||||
ok = VP8LDecodeAlphaImageStream(width, height,
|
||||
data + ALPHA_HEADER_LEN,
|
||||
data_size - ALPHA_HEADER_LEN,
|
||||
decoded_data);
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
WebPFilterFunc unfilter_func = WebPUnfilters[filter];
|
||||
if (unfilter_func != NULL) {
|
||||
unfiltered_data = (uint8_t*)malloc(decoded_size);
|
||||
if (unfiltered_data == NULL) {
|
||||
ok = 0;
|
||||
goto Error;
|
||||
}
|
||||
// TODO(vikas): Implement on-the-fly decoding & filter mechanism to decode
|
||||
// and apply filter per image-row.
|
||||
unfilter_func(decoded_data, width, height, 1, width, unfiltered_data);
|
||||
// Construct raw_data (height x stride) from alpha data (height x width).
|
||||
CopyPlane(unfiltered_data, width, output, stride, width, height);
|
||||
free(unfiltered_data);
|
||||
} else {
|
||||
// Construct raw_data (height x stride) from alpha data (height x width).
|
||||
CopyPlane(decoded_data, width, output, stride, width, height);
|
||||
}
|
||||
if (pre_processing == ALPHA_PREPROCESSED_LEVELS) {
|
||||
ok = DequantizeLevels(decoded_data, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
Error:
|
||||
if (method != ALPHA_NO_COMPRESSION) {
|
||||
free(decoded_data);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
int row, int num_rows) {
|
||||
const int stride = dec->pic_hdr_.width_;
|
||||
|
||||
if (row < 0 || num_rows < 0 || row + num_rows > dec->pic_hdr_.height_) {
|
||||
return NULL; // sanity check.
|
||||
}
|
||||
|
||||
if (row == 0) {
|
||||
// Decode everything during the first call.
|
||||
if (!DecodeAlpha(dec->alpha_data_, (size_t)dec->alpha_data_size_,
|
||||
dec->pic_hdr_.width_, dec->pic_hdr_.height_, stride,
|
||||
dec->alpha_plane_)) {
|
||||
return NULL; // Error.
|
||||
}
|
||||
}
|
||||
|
||||
// Return a pointer to the current decoded row.
|
||||
return dec->alpha_plane_ + row * stride;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
215
drivers/webpold/dec/buffer.c
Normal file
215
drivers/webpold/dec/buffer.c
Normal file
@ -0,0 +1,215 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Everything about WebPDecBuffer
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./vp8i.h"
|
||||
#include "./webpi.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecBuffer
|
||||
|
||||
// Number of bytes per pixel for the different color-spaces.
|
||||
static const int kModeBpp[MODE_LAST] = {
|
||||
3, 4, 3, 4, 4, 2, 2,
|
||||
4, 4, 4, 2, // pre-multiplied modes
|
||||
1, 1 };
|
||||
|
||||
// Check that webp_csp_mode is within the bounds of WEBP_CSP_MODE.
|
||||
// Convert to an integer to handle both the unsigned/signed enum cases
|
||||
// without the need for casting to remove type limit warnings.
|
||||
static int IsValidColorspace(int webp_csp_mode) {
|
||||
return (webp_csp_mode >= MODE_RGB && webp_csp_mode < MODE_LAST);
|
||||
}
|
||||
|
||||
static VP8StatusCode CheckDecBuffer(const WebPDecBuffer* const buffer) {
|
||||
int ok = 1;
|
||||
const WEBP_CSP_MODE mode = buffer->colorspace;
|
||||
const int width = buffer->width;
|
||||
const int height = buffer->height;
|
||||
if (!IsValidColorspace(mode)) {
|
||||
ok = 0;
|
||||
} else if (!WebPIsRGBMode(mode)) { // YUV checks
|
||||
const WebPYUVABuffer* const buf = &buffer->u.YUVA;
|
||||
const uint64_t y_size = (uint64_t)buf->y_stride * height;
|
||||
const uint64_t u_size = (uint64_t)buf->u_stride * ((height + 1) / 2);
|
||||
const uint64_t v_size = (uint64_t)buf->v_stride * ((height + 1) / 2);
|
||||
const uint64_t a_size = (uint64_t)buf->a_stride * height;
|
||||
ok &= (y_size <= buf->y_size);
|
||||
ok &= (u_size <= buf->u_size);
|
||||
ok &= (v_size <= buf->v_size);
|
||||
ok &= (buf->y_stride >= width);
|
||||
ok &= (buf->u_stride >= (width + 1) / 2);
|
||||
ok &= (buf->v_stride >= (width + 1) / 2);
|
||||
ok &= (buf->y != NULL);
|
||||
ok &= (buf->u != NULL);
|
||||
ok &= (buf->v != NULL);
|
||||
if (mode == MODE_YUVA) {
|
||||
ok &= (buf->a_stride >= width);
|
||||
ok &= (a_size <= buf->a_size);
|
||||
ok &= (buf->a != NULL);
|
||||
}
|
||||
} else { // RGB checks
|
||||
const WebPRGBABuffer* const buf = &buffer->u.RGBA;
|
||||
const uint64_t size = (uint64_t)buf->stride * height;
|
||||
ok &= (size <= buf->size);
|
||||
ok &= (buf->stride >= width * kModeBpp[mode]);
|
||||
ok &= (buf->rgba != NULL);
|
||||
}
|
||||
return ok ? VP8_STATUS_OK : VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
VP8StatusCode WebPAllocateDecBuffer(int w, int h,
|
||||
const WebPDecoderOptions* const options,
|
||||
WebPDecBuffer* const out) {
|
||||
if (out == NULL || w <= 0 || h <= 0) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
if (options != NULL) { // First, apply options if there is any.
|
||||
if (options->use_cropping) {
|
||||
const int cw = options->crop_width;
|
||||
const int ch = options->crop_height;
|
||||
const int x = options->crop_left & ~1;
|
||||
const int y = options->crop_top & ~1;
|
||||
if (x < 0 || y < 0 || cw <= 0 || ch <= 0 || x + cw > w || y + ch > h) {
|
||||
return VP8_STATUS_INVALID_PARAM; // out of frame boundary.
|
||||
}
|
||||
w = cw;
|
||||
h = ch;
|
||||
}
|
||||
if (options->use_scaling) {
|
||||
if (options->scaled_width <= 0 || options->scaled_height <= 0) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
w = options->scaled_width;
|
||||
h = options->scaled_height;
|
||||
}
|
||||
}
|
||||
out->width = w;
|
||||
out->height = h;
|
||||
|
||||
// Then, allocate buffer for real
|
||||
return AllocateBuffer(out);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// constructors / destructors
|
||||
|
||||
int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) {
|
||||
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
|
||||
return 0; // version mismatch
|
||||
}
|
||||
if (buffer == NULL) return 0;
|
||||
memset(buffer, 0, sizeof(*buffer));
|
||||
return 1;
|
||||
}
|
||||
|
||||
void WebPFreeDecBuffer(WebPDecBuffer* buffer) {
|
||||
if (buffer != NULL) {
|
||||
if (!buffer->is_external_memory)
|
||||
free(buffer->private_memory);
|
||||
buffer->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void WebPCopyDecBuffer(const WebPDecBuffer* const src,
|
||||
WebPDecBuffer* const dst) {
|
||||
if (src != NULL && dst != NULL) {
|
||||
*dst = *src;
|
||||
if (src->private_memory != NULL) {
|
||||
dst->is_external_memory = 1; // dst buffer doesn't own the memory.
|
||||
dst->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy and transfer ownership from src to dst (beware of parameter order!)
|
||||
void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) {
|
||||
if (src != NULL && dst != NULL) {
|
||||
*dst = *src;
|
||||
if (src->private_memory != NULL) {
|
||||
src->is_external_memory = 1; // src relinquishes ownership
|
||||
src->private_memory = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
182
drivers/webpold/dec/decode_vp8.h
Normal file
182
drivers/webpold/dec/decode_vp8.h
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Low-level API for VP8 decoder
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_WEBP_DECODE_VP8_H_
|
||||
#define WEBP_WEBP_DECODE_VP8_H_
|
||||
|
||||
#include "../decode.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Lower-level API
|
||||
//
|
||||
// These functions provide fine-grained control of the decoding process.
|
||||
// The call flow should resemble:
|
||||
//
|
||||
// VP8Io io;
|
||||
// VP8InitIo(&io);
|
||||
// io.data = data;
|
||||
// io.data_size = size;
|
||||
// /* customize io's functions (setup()/put()/teardown()) if needed. */
|
||||
//
|
||||
// VP8Decoder* dec = VP8New();
|
||||
// bool ok = VP8Decode(dec);
|
||||
// if (!ok) printf("Error: %s\n", VP8StatusMessage(dec));
|
||||
// VP8Delete(dec);
|
||||
// return ok;
|
||||
|
||||
// Input / Output
|
||||
typedef struct VP8Io VP8Io;
|
||||
typedef int (*VP8IoPutHook)(const VP8Io* io);
|
||||
typedef int (*VP8IoSetupHook)(VP8Io* io);
|
||||
typedef void (*VP8IoTeardownHook)(const VP8Io* io);
|
||||
|
||||
struct VP8Io {
|
||||
// set by VP8GetHeaders()
|
||||
int width, height; // picture dimensions, in pixels (invariable).
|
||||
// These are the original, uncropped dimensions.
|
||||
// The actual area passed to put() is stored
|
||||
// in mb_w / mb_h fields.
|
||||
|
||||
// set before calling put()
|
||||
int mb_y; // position of the current rows (in pixels)
|
||||
int mb_w; // number of columns in the sample
|
||||
int mb_h; // number of rows in the sample
|
||||
const uint8_t* y, *u, *v; // rows to copy (in yuv420 format)
|
||||
int y_stride; // row stride for luma
|
||||
int uv_stride; // row stride for chroma
|
||||
|
||||
void* opaque; // user data
|
||||
|
||||
// called when fresh samples are available. Currently, samples are in
|
||||
// YUV420 format, and can be up to width x 24 in size (depending on the
|
||||
// in-loop filtering level, e.g.). Should return false in case of error
|
||||
// or abort request. The actual size of the area to update is mb_w x mb_h
|
||||
// in size, taking cropping into account.
|
||||
VP8IoPutHook put;
|
||||
|
||||
// called just before starting to decode the blocks.
|
||||
// Must return false in case of setup error, true otherwise. If false is
|
||||
// returned, teardown() will NOT be called. But if the setup succeeded
|
||||
// and true is returned, then teardown() will always be called afterward.
|
||||
VP8IoSetupHook setup;
|
||||
|
||||
// Called just after block decoding is finished (or when an error occurred
|
||||
// during put()). Is NOT called if setup() failed.
|
||||
VP8IoTeardownHook teardown;
|
||||
|
||||
// this is a recommendation for the user-side yuv->rgb converter. This flag
|
||||
// is set when calling setup() hook and can be overwritten by it. It then
|
||||
// can be taken into consideration during the put() method.
|
||||
int fancy_upsampling;
|
||||
|
||||
// Input buffer.
|
||||
size_t data_size;
|
||||
const uint8_t* data;
|
||||
|
||||
// If true, in-loop filtering will not be performed even if present in the
|
||||
// bitstream. Switching off filtering may speed up decoding at the expense
|
||||
// of more visible blocking. Note that output will also be non-compliant
|
||||
// with the VP8 specifications.
|
||||
int bypass_filtering;
|
||||
|
||||
// Cropping parameters.
|
||||
int use_cropping;
|
||||
int crop_left, crop_right, crop_top, crop_bottom;
|
||||
|
||||
// Scaling parameters.
|
||||
int use_scaling;
|
||||
int scaled_width, scaled_height;
|
||||
|
||||
// If non NULL, pointer to the alpha data (if present) corresponding to the
|
||||
// start of the current row (That is: it is pre-offset by mb_y and takes
|
||||
// cropping into account).
|
||||
const uint8_t* a;
|
||||
};
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
int VP8InitIoInternal(VP8Io* const, int);
|
||||
|
||||
// Set the custom IO function pointers and user-data. The setter for IO hooks
|
||||
// should be called before initiating incremental decoding. Returns true if
|
||||
// WebPIDecoder object is successfully modified, false otherwise.
|
||||
int WebPISetIOHooks(WebPIDecoder* const idec,
|
||||
VP8IoPutHook put,
|
||||
VP8IoSetupHook setup,
|
||||
VP8IoTeardownHook teardown,
|
||||
void* user_data);
|
||||
|
||||
// Main decoding object. This is an opaque structure.
|
||||
typedef struct VP8Decoder VP8Decoder;
|
||||
|
||||
// Create a new decoder object.
|
||||
VP8Decoder* VP8New(void);
|
||||
|
||||
// Must be called to make sure 'io' is initialized properly.
|
||||
// Returns false in case of version mismatch. Upon such failure, no other
|
||||
// decoding function should be called (VP8Decode, VP8GetHeaders, ...)
|
||||
static WEBP_INLINE int VP8InitIo(VP8Io* const io) {
|
||||
return VP8InitIoInternal(io, WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Start decoding a new picture. Returns true if ok.
|
||||
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io);
|
||||
|
||||
// Decode a picture. Will call VP8GetHeaders() if it wasn't done already.
|
||||
// Returns false in case of error.
|
||||
int VP8Decode(VP8Decoder* const dec, VP8Io* const io);
|
||||
|
||||
// Return current status of the decoder:
|
||||
VP8StatusCode VP8Status(VP8Decoder* const dec);
|
||||
|
||||
// return readable string corresponding to the last status.
|
||||
const char* VP8StatusMessage(VP8Decoder* const dec);
|
||||
|
||||
// Resets the decoder in its initial state, reclaiming memory.
|
||||
// Not a mandatory call between calls to VP8Decode().
|
||||
void VP8Clear(VP8Decoder* const dec);
|
||||
|
||||
// Destroy the decoder object.
|
||||
void VP8Delete(VP8Decoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Miscellaneous VP8/VP8L bitstream probing functions.
|
||||
|
||||
// Returns true if the next 3 bytes in data contain the VP8 signature.
|
||||
WEBP_EXTERN(int) VP8CheckSignature(const uint8_t* const data, size_t data_size);
|
||||
|
||||
// Validates the VP8 data-header and retrieves basic header information viz
|
||||
// width and height. Returns 0 in case of formatting error. *width/*height
|
||||
// can be passed NULL.
|
||||
WEBP_EXTERN(int) VP8GetInfo(
|
||||
const uint8_t* data,
|
||||
size_t data_size, // data available so far
|
||||
size_t chunk_size, // total data size expected in the chunk
|
||||
int* const width, int* const height);
|
||||
|
||||
// Returns true if the next byte(s) in data is a VP8L signature.
|
||||
WEBP_EXTERN(int) VP8LCheckSignature(const uint8_t* const data, size_t size);
|
||||
|
||||
// Validates the VP8L data-header and retrieves basic header information viz
|
||||
// width, height and alpha. Returns 0 in case of formatting error.
|
||||
// width/height/has_alpha can be passed NULL.
|
||||
WEBP_EXTERN(int) VP8LGetInfo(
|
||||
const uint8_t* data, size_t data_size, // data available so far
|
||||
int* const width, int* const height, int* const has_alpha);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_WEBP_DECODE_VP8_H_ */
|
||||
679
drivers/webpold/dec/frame.c
Normal file
679
drivers/webpold/dec/frame.c
Normal file
@ -0,0 +1,679 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Frame-reconstruction function. Memory allocation.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
#include "./vp8i.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define ALIGN_MASK (32 - 1)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Filtering
|
||||
|
||||
// kFilterExtraRows[] = How many extra lines are needed on the MB boundary
|
||||
// for caching, given a filtering level.
|
||||
// Simple filter: up to 2 luma samples are read and 1 is written.
|
||||
// Complex filter: up to 4 luma samples are read and 3 are written. Same for
|
||||
// U/V, so it's 8 samples total (because of the 2x upsampling).
|
||||
static const uint8_t kFilterExtraRows[3] = { 0, 2, 8 };
|
||||
|
||||
static WEBP_INLINE int hev_thresh_from_level(int level, int keyframe) {
|
||||
if (keyframe) {
|
||||
return (level >= 40) ? 2 : (level >= 15) ? 1 : 0;
|
||||
} else {
|
||||
return (level >= 40) ? 3 : (level >= 20) ? 2 : (level >= 15) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void DoFilter(const VP8Decoder* const dec, int mb_x, int mb_y) {
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int y_bps = dec->cache_y_stride_;
|
||||
VP8FInfo* const f_info = ctx->f_info_ + mb_x;
|
||||
uint8_t* const y_dst = dec->cache_y_ + ctx->id_ * 16 * y_bps + mb_x * 16;
|
||||
const int level = f_info->f_level_;
|
||||
const int ilevel = f_info->f_ilevel_;
|
||||
const int limit = 2 * level + ilevel;
|
||||
if (level == 0) {
|
||||
return;
|
||||
}
|
||||
if (dec->filter_type_ == 1) { // simple
|
||||
if (mb_x > 0) {
|
||||
VP8SimpleHFilter16(y_dst, y_bps, limit + 4);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
VP8SimpleHFilter16i(y_dst, y_bps, limit);
|
||||
}
|
||||
if (mb_y > 0) {
|
||||
VP8SimpleVFilter16(y_dst, y_bps, limit + 4);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
VP8SimpleVFilter16i(y_dst, y_bps, limit);
|
||||
}
|
||||
} else { // complex
|
||||
const int uv_bps = dec->cache_uv_stride_;
|
||||
uint8_t* const u_dst = dec->cache_u_ + ctx->id_ * 8 * uv_bps + mb_x * 8;
|
||||
uint8_t* const v_dst = dec->cache_v_ + ctx->id_ * 8 * uv_bps + mb_x * 8;
|
||||
const int hev_thresh =
|
||||
hev_thresh_from_level(level, dec->frm_hdr_.key_frame_);
|
||||
if (mb_x > 0) {
|
||||
VP8HFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
|
||||
VP8HFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
VP8HFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
|
||||
VP8HFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
|
||||
}
|
||||
if (mb_y > 0) {
|
||||
VP8VFilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh);
|
||||
VP8VFilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh);
|
||||
}
|
||||
if (f_info->f_inner_) {
|
||||
VP8VFilter16i(y_dst, y_bps, limit, ilevel, hev_thresh);
|
||||
VP8VFilter8i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter the decoded macroblock row (if needed)
|
||||
static void FilterRow(const VP8Decoder* const dec) {
|
||||
int mb_x;
|
||||
const int mb_y = dec->thread_ctx_.mb_y_;
|
||||
assert(dec->thread_ctx_.filter_row_);
|
||||
for (mb_x = dec->tl_mb_x_; mb_x < dec->br_mb_x_; ++mb_x) {
|
||||
DoFilter(dec, mb_x, mb_y);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void VP8StoreBlock(VP8Decoder* const dec) {
|
||||
if (dec->filter_type_ > 0) {
|
||||
VP8FInfo* const info = dec->f_info_ + dec->mb_x_;
|
||||
const int skip = dec->mb_info_[dec->mb_x_].skip_;
|
||||
int level = dec->filter_levels_[dec->segment_];
|
||||
if (dec->filter_hdr_.use_lf_delta_) {
|
||||
// TODO(skal): only CURRENT is handled for now.
|
||||
level += dec->filter_hdr_.ref_lf_delta_[0];
|
||||
if (dec->is_i4x4_) {
|
||||
level += dec->filter_hdr_.mode_lf_delta_[0];
|
||||
}
|
||||
}
|
||||
level = (level < 0) ? 0 : (level > 63) ? 63 : level;
|
||||
info->f_level_ = level;
|
||||
|
||||
if (dec->filter_hdr_.sharpness_ > 0) {
|
||||
if (dec->filter_hdr_.sharpness_ > 4) {
|
||||
level >>= 2;
|
||||
} else {
|
||||
level >>= 1;
|
||||
}
|
||||
if (level > 9 - dec->filter_hdr_.sharpness_) {
|
||||
level = 9 - dec->filter_hdr_.sharpness_;
|
||||
}
|
||||
}
|
||||
|
||||
info->f_ilevel_ = (level < 1) ? 1 : level;
|
||||
info->f_inner_ = (!skip || dec->is_i4x4_);
|
||||
}
|
||||
{
|
||||
// Transfer samples to row cache
|
||||
int y;
|
||||
const int y_offset = dec->cache_id_ * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = dec->cache_id_ * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const ydst = dec->cache_y_ + dec->mb_x_ * 16 + y_offset;
|
||||
uint8_t* const udst = dec->cache_u_ + dec->mb_x_ * 8 + uv_offset;
|
||||
uint8_t* const vdst = dec->cache_v_ + dec->mb_x_ * 8 + uv_offset;
|
||||
for (y = 0; y < 16; ++y) {
|
||||
memcpy(ydst + y * dec->cache_y_stride_,
|
||||
dec->yuv_b_ + Y_OFF + y * BPS, 16);
|
||||
}
|
||||
for (y = 0; y < 8; ++y) {
|
||||
memcpy(udst + y * dec->cache_uv_stride_,
|
||||
dec->yuv_b_ + U_OFF + y * BPS, 8);
|
||||
memcpy(vdst + y * dec->cache_uv_stride_,
|
||||
dec->yuv_b_ + V_OFF + y * BPS, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// This function is called after a row of macroblocks is finished decoding.
|
||||
// It also takes into account the following restrictions:
|
||||
// * In case of in-loop filtering, we must hold off sending some of the bottom
|
||||
// pixels as they are yet unfiltered. They will be when the next macroblock
|
||||
// row is decoded. Meanwhile, we must preserve them by rotating them in the
|
||||
// cache area. This doesn't hold for the very bottom row of the uncropped
|
||||
// picture of course.
|
||||
// * we must clip the remaining pixels against the cropping area. The VP8Io
|
||||
// struct must have the following fields set correctly before calling put():
|
||||
|
||||
#define MACROBLOCK_VPOS(mb_y) ((mb_y) * 16) // vertical position of a MB
|
||||
|
||||
// Finalize and transmit a complete row. Return false in case of user-abort.
|
||||
static int FinishRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
const VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
const int extra_y_rows = kFilterExtraRows[dec->filter_type_];
|
||||
const int ysize = extra_y_rows * dec->cache_y_stride_;
|
||||
const int uvsize = (extra_y_rows / 2) * dec->cache_uv_stride_;
|
||||
const int y_offset = ctx->id_ * 16 * dec->cache_y_stride_;
|
||||
const int uv_offset = ctx->id_ * 8 * dec->cache_uv_stride_;
|
||||
uint8_t* const ydst = dec->cache_y_ - ysize + y_offset;
|
||||
uint8_t* const udst = dec->cache_u_ - uvsize + uv_offset;
|
||||
uint8_t* const vdst = dec->cache_v_ - uvsize + uv_offset;
|
||||
const int first_row = (ctx->mb_y_ == 0);
|
||||
const int last_row = (ctx->mb_y_ >= dec->br_mb_y_ - 1);
|
||||
int y_start = MACROBLOCK_VPOS(ctx->mb_y_);
|
||||
int y_end = MACROBLOCK_VPOS(ctx->mb_y_ + 1);
|
||||
|
||||
if (ctx->filter_row_) {
|
||||
FilterRow(dec);
|
||||
}
|
||||
|
||||
if (io->put) {
|
||||
if (!first_row) {
|
||||
y_start -= extra_y_rows;
|
||||
io->y = ydst;
|
||||
io->u = udst;
|
||||
io->v = vdst;
|
||||
} else {
|
||||
io->y = dec->cache_y_ + y_offset;
|
||||
io->u = dec->cache_u_ + uv_offset;
|
||||
io->v = dec->cache_v_ + uv_offset;
|
||||
}
|
||||
|
||||
if (!last_row) {
|
||||
y_end -= extra_y_rows;
|
||||
}
|
||||
if (y_end > io->crop_bottom) {
|
||||
y_end = io->crop_bottom; // make sure we don't overflow on last row.
|
||||
}
|
||||
io->a = NULL;
|
||||
if (dec->alpha_data_ != NULL && y_start < y_end) {
|
||||
// TODO(skal): several things to correct here:
|
||||
// * testing presence of alpha with dec->alpha_data_ is not a good idea
|
||||
// * we're actually decompressing the full plane only once. It should be
|
||||
// more obvious from signature.
|
||||
// * we could free alpha_data_ right after this call, but we don't own.
|
||||
io->a = VP8DecompressAlphaRows(dec, y_start, y_end - y_start);
|
||||
if (io->a == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Could not decode alpha data.");
|
||||
}
|
||||
}
|
||||
if (y_start < io->crop_top) {
|
||||
const int delta_y = io->crop_top - y_start;
|
||||
y_start = io->crop_top;
|
||||
assert(!(delta_y & 1));
|
||||
io->y += dec->cache_y_stride_ * delta_y;
|
||||
io->u += dec->cache_uv_stride_ * (delta_y >> 1);
|
||||
io->v += dec->cache_uv_stride_ * (delta_y >> 1);
|
||||
if (io->a != NULL) {
|
||||
io->a += io->width * delta_y;
|
||||
}
|
||||
}
|
||||
if (y_start < y_end) {
|
||||
io->y += io->crop_left;
|
||||
io->u += io->crop_left >> 1;
|
||||
io->v += io->crop_left >> 1;
|
||||
if (io->a != NULL) {
|
||||
io->a += io->crop_left;
|
||||
}
|
||||
io->mb_y = y_start - io->crop_top;
|
||||
io->mb_w = io->crop_right - io->crop_left;
|
||||
io->mb_h = y_end - y_start;
|
||||
ok = io->put(io);
|
||||
}
|
||||
}
|
||||
// rotate top samples if needed
|
||||
if (ctx->id_ + 1 == dec->num_caches_) {
|
||||
if (!last_row) {
|
||||
memcpy(dec->cache_y_ - ysize, ydst + 16 * dec->cache_y_stride_, ysize);
|
||||
memcpy(dec->cache_u_ - uvsize, udst + 8 * dec->cache_uv_stride_, uvsize);
|
||||
memcpy(dec->cache_v_ - uvsize, vdst + 8 * dec->cache_uv_stride_, uvsize);
|
||||
}
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
#undef MACROBLOCK_VPOS
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
VP8ThreadContext* const ctx = &dec->thread_ctx_;
|
||||
if (!dec->use_threads_) {
|
||||
// ctx->id_ and ctx->f_info_ are already set
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = dec->filter_row_;
|
||||
ok = FinishRow(dec, io);
|
||||
} else {
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
// Finish previous job *before* updating context
|
||||
ok &= WebPWorkerSync(worker);
|
||||
assert(worker->status_ == OK);
|
||||
if (ok) { // spawn a new deblocking/output job
|
||||
ctx->io_ = *io;
|
||||
ctx->id_ = dec->cache_id_;
|
||||
ctx->mb_y_ = dec->mb_y_;
|
||||
ctx->filter_row_ = dec->filter_row_;
|
||||
if (ctx->filter_row_) { // just swap filter info
|
||||
VP8FInfo* const tmp = ctx->f_info_;
|
||||
ctx->f_info_ = dec->f_info_;
|
||||
dec->f_info_ = tmp;
|
||||
}
|
||||
WebPWorkerLaunch(worker);
|
||||
if (++dec->cache_id_ == dec->num_caches_) {
|
||||
dec->cache_id_ = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Finish setting up the decoding parameter once user's setup() is called.
|
||||
|
||||
VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
// Call setup() first. This may trigger additional decoding features on 'io'.
|
||||
// Note: Afterward, we must call teardown() not matter what.
|
||||
if (io->setup && !io->setup(io)) {
|
||||
VP8SetError(dec, VP8_STATUS_USER_ABORT, "Frame setup failed");
|
||||
return dec->status_;
|
||||
}
|
||||
|
||||
// Disable filtering per user request
|
||||
if (io->bypass_filtering) {
|
||||
dec->filter_type_ = 0;
|
||||
}
|
||||
// TODO(skal): filter type / strength / sharpness forcing
|
||||
|
||||
// Define the area where we can skip in-loop filtering, in case of cropping.
|
||||
//
|
||||
// 'Simple' filter reads two luma samples outside of the macroblock and
|
||||
// and filters one. It doesn't filter the chroma samples. Hence, we can
|
||||
// avoid doing the in-loop filtering before crop_top/crop_left position.
|
||||
// For the 'Complex' filter, 3 samples are read and up to 3 are filtered.
|
||||
// Means: there's a dependency chain that goes all the way up to the
|
||||
// top-left corner of the picture (MB #0). We must filter all the previous
|
||||
// macroblocks.
|
||||
// TODO(skal): add an 'approximate_decoding' option, that won't produce
|
||||
// a 1:1 bit-exactness for complex filtering?
|
||||
{
|
||||
const int extra_pixels = kFilterExtraRows[dec->filter_type_];
|
||||
if (dec->filter_type_ == 2) {
|
||||
// For complex filter, we need to preserve the dependency chain.
|
||||
dec->tl_mb_x_ = 0;
|
||||
dec->tl_mb_y_ = 0;
|
||||
} else {
|
||||
// For simple filter, we can filter only the cropped region.
|
||||
// We include 'extra_pixels' on the other side of the boundary, since
|
||||
// vertical or horizontal filtering of the previous macroblock can
|
||||
// modify some abutting pixels.
|
||||
dec->tl_mb_x_ = (io->crop_left - extra_pixels) >> 4;
|
||||
dec->tl_mb_y_ = (io->crop_top - extra_pixels) >> 4;
|
||||
if (dec->tl_mb_x_ < 0) dec->tl_mb_x_ = 0;
|
||||
if (dec->tl_mb_y_ < 0) dec->tl_mb_y_ = 0;
|
||||
}
|
||||
// We need some 'extra' pixels on the right/bottom.
|
||||
dec->br_mb_y_ = (io->crop_bottom + 15 + extra_pixels) >> 4;
|
||||
dec->br_mb_x_ = (io->crop_right + 15 + extra_pixels) >> 4;
|
||||
if (dec->br_mb_x_ > dec->mb_w_) {
|
||||
dec->br_mb_x_ = dec->mb_w_;
|
||||
}
|
||||
if (dec->br_mb_y_ > dec->mb_h_) {
|
||||
dec->br_mb_y_ = dec->mb_h_;
|
||||
}
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 1;
|
||||
if (dec->use_threads_) {
|
||||
ok = WebPWorkerSync(&dec->worker_);
|
||||
}
|
||||
|
||||
if (io->teardown) {
|
||||
io->teardown(io);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// For multi-threaded decoding we need to use 3 rows of 16 pixels as delay line.
|
||||
//
|
||||
// Reason is: the deblocking filter cannot deblock the bottom horizontal edges
|
||||
// immediately, and needs to wait for first few rows of the next macroblock to
|
||||
// be decoded. Hence, deblocking is lagging behind by 4 or 8 pixels (depending
|
||||
// on strength).
|
||||
// With two threads, the vertical positions of the rows being decoded are:
|
||||
// Decode: [ 0..15][16..31][32..47][48..63][64..79][...
|
||||
// Deblock: [ 0..11][12..27][28..43][44..59][...
|
||||
// If we use two threads and two caches of 16 pixels, the sequence would be:
|
||||
// Decode: [ 0..15][16..31][ 0..15!!][16..31][ 0..15][...
|
||||
// Deblock: [ 0..11][12..27!!][-4..11][12..27][...
|
||||
// The problem occurs during row [12..15!!] that both the decoding and
|
||||
// deblocking threads are writing simultaneously.
|
||||
// With 3 cache lines, one get a safe write pattern:
|
||||
// Decode: [ 0..15][16..31][32..47][ 0..15][16..31][32..47][0..
|
||||
// Deblock: [ 0..11][12..27][28..43][-4..11][12..27][28...
|
||||
// Note that multi-threaded output _without_ deblocking can make use of two
|
||||
// cache lines of 16 pixels only, since there's no lagging behind. The decoding
|
||||
// and output process have non-concurrent writing:
|
||||
// Decode: [ 0..15][16..31][ 0..15][16..31][...
|
||||
// io->put: [ 0..15][16..31][ 0..15][...
|
||||
|
||||
#define MT_CACHE_LINES 3
|
||||
#define ST_CACHE_LINES 1 // 1 cache row only for single-threaded case
|
||||
|
||||
// Initialize multi/single-thread worker
|
||||
static int InitThreadContext(VP8Decoder* const dec) {
|
||||
dec->cache_id_ = 0;
|
||||
if (dec->use_threads_) {
|
||||
WebPWorker* const worker = &dec->worker_;
|
||||
if (!WebPWorkerReset(worker)) {
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"thread initialization failed.");
|
||||
}
|
||||
worker->data1 = dec;
|
||||
worker->data2 = (void*)&dec->thread_ctx_.io_;
|
||||
worker->hook = (WebPWorkerHook)FinishRow;
|
||||
dec->num_caches_ =
|
||||
(dec->filter_type_ > 0) ? MT_CACHE_LINES : MT_CACHE_LINES - 1;
|
||||
} else {
|
||||
dec->num_caches_ = ST_CACHE_LINES;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
#undef MT_CACHE_LINES
|
||||
#undef ST_CACHE_LINES
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Memory setup
|
||||
|
||||
static int AllocateMemory(VP8Decoder* const dec) {
|
||||
const int num_caches = dec->num_caches_;
|
||||
const int mb_w = dec->mb_w_;
|
||||
// Note: we use 'size_t' when there's no overflow risk, uint64_t otherwise.
|
||||
const size_t intra_pred_mode_size = 4 * mb_w * sizeof(uint8_t);
|
||||
const size_t top_size = (16 + 8 + 8) * mb_w;
|
||||
const size_t mb_info_size = (mb_w + 1) * sizeof(VP8MB);
|
||||
const size_t f_info_size =
|
||||
(dec->filter_type_ > 0) ?
|
||||
mb_w * (dec->use_threads_ ? 2 : 1) * sizeof(VP8FInfo)
|
||||
: 0;
|
||||
const size_t yuv_size = YUV_SIZE * sizeof(*dec->yuv_b_);
|
||||
const size_t coeffs_size = 384 * sizeof(*dec->coeffs_);
|
||||
const size_t cache_height = (16 * num_caches
|
||||
+ kFilterExtraRows[dec->filter_type_]) * 3 / 2;
|
||||
const size_t cache_size = top_size * cache_height;
|
||||
// alpha_size is the only one that scales as width x height.
|
||||
const uint64_t alpha_size = (dec->alpha_data_ != NULL) ?
|
||||
(uint64_t)dec->pic_hdr_.width_ * dec->pic_hdr_.height_ : 0ULL;
|
||||
const uint64_t needed = (uint64_t)intra_pred_mode_size
|
||||
+ top_size + mb_info_size + f_info_size
|
||||
+ yuv_size + coeffs_size
|
||||
+ cache_size + alpha_size + ALIGN_MASK;
|
||||
uint8_t* mem;
|
||||
|
||||
if (needed != (size_t)needed) return 0; // check for overflow
|
||||
if (needed > dec->mem_size_) {
|
||||
free(dec->mem_);
|
||||
dec->mem_size_ = 0;
|
||||
dec->mem_ = WebPSafeMalloc(needed, sizeof(uint8_t));
|
||||
if (dec->mem_ == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_OUT_OF_MEMORY,
|
||||
"no memory during frame initialization.");
|
||||
}
|
||||
// down-cast is ok, thanks to WebPSafeAlloc() above.
|
||||
dec->mem_size_ = (size_t)needed;
|
||||
}
|
||||
|
||||
mem = (uint8_t*)dec->mem_;
|
||||
dec->intra_t_ = (uint8_t*)mem;
|
||||
mem += intra_pred_mode_size;
|
||||
|
||||
dec->y_t_ = (uint8_t*)mem;
|
||||
mem += 16 * mb_w;
|
||||
dec->u_t_ = (uint8_t*)mem;
|
||||
mem += 8 * mb_w;
|
||||
dec->v_t_ = (uint8_t*)mem;
|
||||
mem += 8 * mb_w;
|
||||
|
||||
dec->mb_info_ = ((VP8MB*)mem) + 1;
|
||||
mem += mb_info_size;
|
||||
|
||||
dec->f_info_ = f_info_size ? (VP8FInfo*)mem : NULL;
|
||||
mem += f_info_size;
|
||||
dec->thread_ctx_.id_ = 0;
|
||||
dec->thread_ctx_.f_info_ = dec->f_info_;
|
||||
if (dec->use_threads_) {
|
||||
// secondary cache line. The deblocking process need to make use of the
|
||||
// filtering strength from previous macroblock row, while the new ones
|
||||
// are being decoded in parallel. We'll just swap the pointers.
|
||||
dec->thread_ctx_.f_info_ += mb_w;
|
||||
}
|
||||
|
||||
mem = (uint8_t*)((uintptr_t)(mem + ALIGN_MASK) & ~ALIGN_MASK);
|
||||
assert((yuv_size & ALIGN_MASK) == 0);
|
||||
dec->yuv_b_ = (uint8_t*)mem;
|
||||
mem += yuv_size;
|
||||
|
||||
dec->coeffs_ = (int16_t*)mem;
|
||||
mem += coeffs_size;
|
||||
|
||||
dec->cache_y_stride_ = 16 * mb_w;
|
||||
dec->cache_uv_stride_ = 8 * mb_w;
|
||||
{
|
||||
const int extra_rows = kFilterExtraRows[dec->filter_type_];
|
||||
const int extra_y = extra_rows * dec->cache_y_stride_;
|
||||
const int extra_uv = (extra_rows / 2) * dec->cache_uv_stride_;
|
||||
dec->cache_y_ = ((uint8_t*)mem) + extra_y;
|
||||
dec->cache_u_ = dec->cache_y_
|
||||
+ 16 * num_caches * dec->cache_y_stride_ + extra_uv;
|
||||
dec->cache_v_ = dec->cache_u_
|
||||
+ 8 * num_caches * dec->cache_uv_stride_ + extra_uv;
|
||||
dec->cache_id_ = 0;
|
||||
}
|
||||
mem += cache_size;
|
||||
|
||||
// alpha plane
|
||||
dec->alpha_plane_ = alpha_size ? (uint8_t*)mem : NULL;
|
||||
mem += alpha_size;
|
||||
|
||||
// note: left-info is initialized once for all.
|
||||
memset(dec->mb_info_ - 1, 0, mb_info_size);
|
||||
|
||||
// initialize top
|
||||
memset(dec->intra_t_, B_DC_PRED, intra_pred_mode_size);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void InitIo(VP8Decoder* const dec, VP8Io* io) {
|
||||
// prepare 'io'
|
||||
io->mb_y = 0;
|
||||
io->y = dec->cache_y_;
|
||||
io->u = dec->cache_u_;
|
||||
io->v = dec->cache_v_;
|
||||
io->y_stride = dec->cache_y_stride_;
|
||||
io->uv_stride = dec->cache_uv_stride_;
|
||||
io->a = NULL;
|
||||
}
|
||||
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io) {
|
||||
if (!InitThreadContext(dec)) return 0; // call first. Sets dec->num_caches_.
|
||||
if (!AllocateMemory(dec)) return 0;
|
||||
InitIo(dec, io);
|
||||
VP8DspInit(); // Init critical function pointers and look-up tables.
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main reconstruction function.
|
||||
|
||||
static const int kScan[16] = {
|
||||
0 + 0 * BPS, 4 + 0 * BPS, 8 + 0 * BPS, 12 + 0 * BPS,
|
||||
0 + 4 * BPS, 4 + 4 * BPS, 8 + 4 * BPS, 12 + 4 * BPS,
|
||||
0 + 8 * BPS, 4 + 8 * BPS, 8 + 8 * BPS, 12 + 8 * BPS,
|
||||
0 + 12 * BPS, 4 + 12 * BPS, 8 + 12 * BPS, 12 + 12 * BPS
|
||||
};
|
||||
|
||||
static WEBP_INLINE int CheckMode(VP8Decoder* const dec, int mode) {
|
||||
if (mode == B_DC_PRED) {
|
||||
if (dec->mb_x_ == 0) {
|
||||
return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOPLEFT : B_DC_PRED_NOLEFT;
|
||||
} else {
|
||||
return (dec->mb_y_ == 0) ? B_DC_PRED_NOTOP : B_DC_PRED;
|
||||
}
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
static WEBP_INLINE void Copy32b(uint8_t* dst, uint8_t* src) {
|
||||
*(uint32_t*)dst = *(uint32_t*)src;
|
||||
}
|
||||
|
||||
void VP8ReconstructBlock(VP8Decoder* const dec) {
|
||||
uint8_t* const y_dst = dec->yuv_b_ + Y_OFF;
|
||||
uint8_t* const u_dst = dec->yuv_b_ + U_OFF;
|
||||
uint8_t* const v_dst = dec->yuv_b_ + V_OFF;
|
||||
|
||||
// Rotate in the left samples from previously decoded block. We move four
|
||||
// pixels at a time for alignment reason, and because of in-loop filter.
|
||||
if (dec->mb_x_ > 0) {
|
||||
int j;
|
||||
for (j = -1; j < 16; ++j) {
|
||||
Copy32b(&y_dst[j * BPS - 4], &y_dst[j * BPS + 12]);
|
||||
}
|
||||
for (j = -1; j < 8; ++j) {
|
||||
Copy32b(&u_dst[j * BPS - 4], &u_dst[j * BPS + 4]);
|
||||
Copy32b(&v_dst[j * BPS - 4], &v_dst[j * BPS + 4]);
|
||||
}
|
||||
} else {
|
||||
int j;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
y_dst[j * BPS - 1] = 129;
|
||||
}
|
||||
for (j = 0; j < 8; ++j) {
|
||||
u_dst[j * BPS - 1] = 129;
|
||||
v_dst[j * BPS - 1] = 129;
|
||||
}
|
||||
// Init top-left sample on left column too
|
||||
if (dec->mb_y_ > 0) {
|
||||
y_dst[-1 - BPS] = u_dst[-1 - BPS] = v_dst[-1 - BPS] = 129;
|
||||
}
|
||||
}
|
||||
{
|
||||
// bring top samples into the cache
|
||||
uint8_t* const top_y = dec->y_t_ + dec->mb_x_ * 16;
|
||||
uint8_t* const top_u = dec->u_t_ + dec->mb_x_ * 8;
|
||||
uint8_t* const top_v = dec->v_t_ + dec->mb_x_ * 8;
|
||||
const int16_t* coeffs = dec->coeffs_;
|
||||
int n;
|
||||
|
||||
if (dec->mb_y_ > 0) {
|
||||
memcpy(y_dst - BPS, top_y, 16);
|
||||
memcpy(u_dst - BPS, top_u, 8);
|
||||
memcpy(v_dst - BPS, top_v, 8);
|
||||
} else if (dec->mb_x_ == 0) {
|
||||
// we only need to do this init once at block (0,0).
|
||||
// Afterward, it remains valid for the whole topmost row.
|
||||
memset(y_dst - BPS - 1, 127, 16 + 4 + 1);
|
||||
memset(u_dst - BPS - 1, 127, 8 + 1);
|
||||
memset(v_dst - BPS - 1, 127, 8 + 1);
|
||||
}
|
||||
|
||||
// predict and add residuals
|
||||
|
||||
if (dec->is_i4x4_) { // 4x4
|
||||
uint32_t* const top_right = (uint32_t*)(y_dst - BPS + 16);
|
||||
|
||||
if (dec->mb_y_ > 0) {
|
||||
if (dec->mb_x_ >= dec->mb_w_ - 1) { // on rightmost border
|
||||
top_right[0] = top_y[15] * 0x01010101u;
|
||||
} else {
|
||||
memcpy(top_right, top_y + 16, sizeof(*top_right));
|
||||
}
|
||||
}
|
||||
// replicate the top-right pixels below
|
||||
top_right[BPS] = top_right[2 * BPS] = top_right[3 * BPS] = top_right[0];
|
||||
|
||||
// predict and add residues for all 4x4 blocks in turn.
|
||||
for (n = 0; n < 16; n++) {
|
||||
uint8_t* const dst = y_dst + kScan[n];
|
||||
VP8PredLuma4[dec->imodes_[n]](dst);
|
||||
if (dec->non_zero_ac_ & (1 << n)) {
|
||||
VP8Transform(coeffs + n * 16, dst, 0);
|
||||
} else if (dec->non_zero_ & (1 << n)) { // only DC is present
|
||||
VP8TransformDC(coeffs + n * 16, dst);
|
||||
}
|
||||
}
|
||||
} else { // 16x16
|
||||
const int pred_func = CheckMode(dec, dec->imodes_[0]);
|
||||
VP8PredLuma16[pred_func](y_dst);
|
||||
if (dec->non_zero_) {
|
||||
for (n = 0; n < 16; n++) {
|
||||
uint8_t* const dst = y_dst + kScan[n];
|
||||
if (dec->non_zero_ac_ & (1 << n)) {
|
||||
VP8Transform(coeffs + n * 16, dst, 0);
|
||||
} else if (dec->non_zero_ & (1 << n)) { // only DC is present
|
||||
VP8TransformDC(coeffs + n * 16, dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
// Chroma
|
||||
const int pred_func = CheckMode(dec, dec->uvmode_);
|
||||
VP8PredChroma8[pred_func](u_dst);
|
||||
VP8PredChroma8[pred_func](v_dst);
|
||||
|
||||
if (dec->non_zero_ & 0x0f0000) { // chroma-U
|
||||
const int16_t* const u_coeffs = dec->coeffs_ + 16 * 16;
|
||||
if (dec->non_zero_ac_ & 0x0f0000) {
|
||||
VP8TransformUV(u_coeffs, u_dst);
|
||||
} else {
|
||||
VP8TransformDCUV(u_coeffs, u_dst);
|
||||
}
|
||||
}
|
||||
if (dec->non_zero_ & 0xf00000) { // chroma-V
|
||||
const int16_t* const v_coeffs = dec->coeffs_ + 20 * 16;
|
||||
if (dec->non_zero_ac_ & 0xf00000) {
|
||||
VP8TransformUV(v_coeffs, v_dst);
|
||||
} else {
|
||||
VP8TransformDCUV(v_coeffs, v_dst);
|
||||
}
|
||||
}
|
||||
|
||||
// stash away top samples for next block
|
||||
if (dec->mb_y_ < dec->mb_h_ - 1) {
|
||||
memcpy(top_y, y_dst + 15 * BPS, 16);
|
||||
memcpy(top_u, u_dst + 7 * BPS, 8);
|
||||
memcpy(top_v, v_dst + 7 * BPS, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
785
drivers/webpold/dec/idec.c
Normal file
785
drivers/webpold/dec/idec.c
Normal file
@ -0,0 +1,785 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Incremental decoding
|
||||
//
|
||||
// Author: somnath@google.com (Somnath Banerjee)
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./webpi.h"
|
||||
#include "./vp8i.h"
|
||||
#include "../utils/utils.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// In append mode, buffer allocations increase as multiples of this value.
|
||||
// Needs to be a power of 2.
|
||||
#define CHUNK_SIZE 4096
|
||||
#define MAX_MB_SIZE 4096
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Data structures for memory and states
|
||||
|
||||
// Decoding states. State normally flows like HEADER->PARTS0->DATA->DONE.
|
||||
// If there is any error the decoder goes into state ERROR.
|
||||
typedef enum {
|
||||
STATE_PRE_VP8, // All data before that of the first VP8 chunk.
|
||||
STATE_VP8_FRAME_HEADER, // For VP8 Frame header (within VP8 chunk).
|
||||
STATE_VP8_PARTS0,
|
||||
STATE_VP8_DATA,
|
||||
STATE_VP8L_HEADER,
|
||||
STATE_VP8L_DATA,
|
||||
STATE_DONE,
|
||||
STATE_ERROR
|
||||
} DecState;
|
||||
|
||||
// Operating state for the MemBuffer
|
||||
typedef enum {
|
||||
MEM_MODE_NONE = 0,
|
||||
MEM_MODE_APPEND,
|
||||
MEM_MODE_MAP
|
||||
} MemBufferMode;
|
||||
|
||||
// storage for partition #0 and partial data (in a rolling fashion)
|
||||
typedef struct {
|
||||
MemBufferMode mode_; // Operation mode
|
||||
size_t start_; // start location of the data to be decoded
|
||||
size_t end_; // end location
|
||||
size_t buf_size_; // size of the allocated buffer
|
||||
uint8_t* buf_; // We don't own this buffer in case WebPIUpdate()
|
||||
|
||||
size_t part0_size_; // size of partition #0
|
||||
const uint8_t* part0_buf_; // buffer to store partition #0
|
||||
} MemBuffer;
|
||||
|
||||
struct WebPIDecoder {
|
||||
DecState state_; // current decoding state
|
||||
WebPDecParams params_; // Params to store output info
|
||||
int is_lossless_; // for down-casting 'dec_'.
|
||||
void* dec_; // either a VP8Decoder or a VP8LDecoder instance
|
||||
VP8Io io_;
|
||||
|
||||
MemBuffer mem_; // input memory buffer.
|
||||
WebPDecBuffer output_; // output buffer (when no external one is supplied)
|
||||
size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header.
|
||||
};
|
||||
|
||||
// MB context to restore in case VP8DecodeMB() fails
|
||||
typedef struct {
|
||||
VP8MB left_;
|
||||
VP8MB info_;
|
||||
uint8_t intra_t_[4];
|
||||
uint8_t intra_l_[4];
|
||||
VP8BitReader br_;
|
||||
VP8BitReader token_br_;
|
||||
} MBContext;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MemBuffer: incoming data handling
|
||||
|
||||
static void RemapBitReader(VP8BitReader* const br, ptrdiff_t offset) {
|
||||
if (br->buf_ != NULL) {
|
||||
br->buf_ += offset;
|
||||
br->buf_end_ += offset;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE size_t MemDataSize(const MemBuffer* mem) {
|
||||
return (mem->end_ - mem->start_);
|
||||
}
|
||||
|
||||
static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* const new_base = mem->buf_ + mem->start_;
|
||||
// note: for VP8, setting up idec->io_ is only really needed at the beginning
|
||||
// of the decoding, till partition #0 is complete.
|
||||
idec->io_.data = new_base;
|
||||
idec->io_.data_size = MemDataSize(mem);
|
||||
|
||||
if (idec->dec_ != NULL) {
|
||||
if (!idec->is_lossless_) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
const int last_part = dec->num_parts_ - 1;
|
||||
if (offset != 0) {
|
||||
int p;
|
||||
for (p = 0; p <= last_part; ++p) {
|
||||
RemapBitReader(dec->parts_ + p, offset);
|
||||
}
|
||||
// Remap partition #0 data pointer to new offset, but only in MAP
|
||||
// mode (in APPEND mode, partition #0 is copied into a fixed memory).
|
||||
if (mem->mode_ == MEM_MODE_MAP) {
|
||||
RemapBitReader(&dec->br_, offset);
|
||||
}
|
||||
}
|
||||
assert(last_part >= 0);
|
||||
dec->parts_[last_part].buf_end_ = mem->buf_ + mem->end_;
|
||||
} else { // Resize lossless bitreader
|
||||
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
|
||||
VP8LBitReaderSetBuffer(&dec->br_, new_base, MemDataSize(mem));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Appends data to the end of MemBuffer->buf_. It expands the allocated memory
|
||||
// size if required and also updates VP8BitReader's if new memory is allocated.
|
||||
static int AppendToMemBuffer(WebPIDecoder* const idec,
|
||||
const uint8_t* const data, size_t data_size) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* const old_base = mem->buf_ + mem->start_;
|
||||
assert(mem->mode_ == MEM_MODE_APPEND);
|
||||
if (data_size > MAX_CHUNK_PAYLOAD) {
|
||||
// security safeguard: trying to allocate more than what the format
|
||||
// allows for a chunk should be considered a smoke smell.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mem->end_ + data_size > mem->buf_size_) { // Need some free memory
|
||||
const size_t current_size = MemDataSize(mem);
|
||||
const uint64_t new_size = (uint64_t)current_size + data_size;
|
||||
const uint64_t extra_size = (new_size + CHUNK_SIZE - 1) & ~(CHUNK_SIZE - 1);
|
||||
uint8_t* const new_buf =
|
||||
(uint8_t*)WebPSafeMalloc(extra_size, sizeof(*new_buf));
|
||||
if (new_buf == NULL) return 0;
|
||||
memcpy(new_buf, old_base, current_size);
|
||||
free(mem->buf_);
|
||||
mem->buf_ = new_buf;
|
||||
mem->buf_size_ = (size_t)extra_size;
|
||||
mem->start_ = 0;
|
||||
mem->end_ = current_size;
|
||||
}
|
||||
|
||||
memcpy(mem->buf_ + mem->end_, data, data_size);
|
||||
mem->end_ += data_size;
|
||||
assert(mem->end_ <= mem->buf_size_);
|
||||
|
||||
DoRemap(idec, mem->buf_ + mem->start_ - old_base);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int RemapMemBuffer(WebPIDecoder* const idec,
|
||||
const uint8_t* const data, size_t data_size) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* const old_base = mem->buf_ + mem->start_;
|
||||
assert(mem->mode_ == MEM_MODE_MAP);
|
||||
|
||||
if (data_size < mem->buf_size_) return 0; // can't remap to a shorter buffer!
|
||||
|
||||
mem->buf_ = (uint8_t*)data;
|
||||
mem->end_ = mem->buf_size_ = data_size;
|
||||
|
||||
DoRemap(idec, mem->buf_ + mem->start_ - old_base);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void InitMemBuffer(MemBuffer* const mem) {
|
||||
mem->mode_ = MEM_MODE_NONE;
|
||||
mem->buf_ = NULL;
|
||||
mem->buf_size_ = 0;
|
||||
mem->part0_buf_ = NULL;
|
||||
mem->part0_size_ = 0;
|
||||
}
|
||||
|
||||
static void ClearMemBuffer(MemBuffer* const mem) {
|
||||
assert(mem);
|
||||
if (mem->mode_ == MEM_MODE_APPEND) {
|
||||
free(mem->buf_);
|
||||
free((void*)mem->part0_buf_);
|
||||
}
|
||||
}
|
||||
|
||||
static int CheckMemBufferMode(MemBuffer* const mem, MemBufferMode expected) {
|
||||
if (mem->mode_ == MEM_MODE_NONE) {
|
||||
mem->mode_ = expected; // switch to the expected mode
|
||||
} else if (mem->mode_ != expected) {
|
||||
return 0; // we mixed the modes => error
|
||||
}
|
||||
assert(mem->mode_ == expected); // mode is ok
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Macroblock-decoding contexts
|
||||
|
||||
static void SaveContext(const VP8Decoder* dec, const VP8BitReader* token_br,
|
||||
MBContext* const context) {
|
||||
const VP8BitReader* const br = &dec->br_;
|
||||
const VP8MB* const left = dec->mb_info_ - 1;
|
||||
const VP8MB* const info = dec->mb_info_ + dec->mb_x_;
|
||||
|
||||
context->left_ = *left;
|
||||
context->info_ = *info;
|
||||
context->br_ = *br;
|
||||
context->token_br_ = *token_br;
|
||||
memcpy(context->intra_t_, dec->intra_t_ + 4 * dec->mb_x_, 4);
|
||||
memcpy(context->intra_l_, dec->intra_l_, 4);
|
||||
}
|
||||
|
||||
static void RestoreContext(const MBContext* context, VP8Decoder* const dec,
|
||||
VP8BitReader* const token_br) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
VP8MB* const info = dec->mb_info_ + dec->mb_x_;
|
||||
|
||||
*left = context->left_;
|
||||
*info = context->info_;
|
||||
*br = context->br_;
|
||||
*token_br = context->token_br_;
|
||||
memcpy(dec->intra_t_ + 4 * dec->mb_x_, context->intra_t_, 4);
|
||||
memcpy(dec->intra_l_, context->intra_l_, 4);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static VP8StatusCode IDecError(WebPIDecoder* const idec, VP8StatusCode error) {
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
VP8Io* const io = &idec->io_;
|
||||
if (io->teardown) {
|
||||
io->teardown(io);
|
||||
}
|
||||
}
|
||||
idec->state_ = STATE_ERROR;
|
||||
return error;
|
||||
}
|
||||
|
||||
static void ChangeState(WebPIDecoder* const idec, DecState new_state,
|
||||
size_t consumed_bytes) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
idec->state_ = new_state;
|
||||
mem->start_ += consumed_bytes;
|
||||
assert(mem->start_ <= mem->end_);
|
||||
idec->io_.data = mem->buf_ + mem->start_;
|
||||
idec->io_.data_size = MemDataSize(mem);
|
||||
}
|
||||
|
||||
// Headers
|
||||
static VP8StatusCode DecodeWebPHeaders(WebPIDecoder* const idec) {
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
const uint8_t* data = mem->buf_ + mem->start_;
|
||||
size_t curr_size = MemDataSize(mem);
|
||||
VP8StatusCode status;
|
||||
WebPHeaderStructure headers;
|
||||
|
||||
headers.data = data;
|
||||
headers.data_size = curr_size;
|
||||
status = WebPParseHeaders(&headers);
|
||||
if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
return VP8_STATUS_SUSPENDED; // We haven't found a VP8 chunk yet.
|
||||
} else if (status != VP8_STATUS_OK) {
|
||||
return IDecError(idec, status);
|
||||
}
|
||||
|
||||
idec->chunk_size_ = headers.compressed_size;
|
||||
idec->is_lossless_ = headers.is_lossless;
|
||||
if (!idec->is_lossless_) {
|
||||
VP8Decoder* const dec = VP8New();
|
||||
if (dec == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
idec->dec_ = dec;
|
||||
#ifdef WEBP_USE_THREAD
|
||||
dec->use_threads_ = (idec->params_.options != NULL) &&
|
||||
(idec->params_.options->use_threads > 0);
|
||||
#else
|
||||
dec->use_threads_ = 0;
|
||||
#endif
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
ChangeState(idec, STATE_VP8_FRAME_HEADER, headers.offset);
|
||||
} else {
|
||||
VP8LDecoder* const dec = VP8LNew();
|
||||
if (dec == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
idec->dec_ = dec;
|
||||
ChangeState(idec, STATE_VP8L_HEADER, headers.offset);
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
static VP8StatusCode DecodeVP8FrameHeader(WebPIDecoder* const idec) {
|
||||
const uint8_t* data = idec->mem_.buf_ + idec->mem_.start_;
|
||||
const size_t curr_size = MemDataSize(&idec->mem_);
|
||||
uint32_t bits;
|
||||
|
||||
if (curr_size < VP8_FRAME_HEADER_SIZE) {
|
||||
// Not enough data bytes to extract VP8 Frame Header.
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
if (!VP8GetInfo(data, curr_size, idec->chunk_size_, NULL, NULL)) {
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
|
||||
bits = data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
idec->mem_.part0_size_ = (bits >> 5) + VP8_FRAME_HEADER_SIZE;
|
||||
|
||||
idec->io_.data = data;
|
||||
idec->io_.data_size = curr_size;
|
||||
idec->state_ = STATE_VP8_PARTS0;
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
// Partition #0
|
||||
static int CopyParts0Data(WebPIDecoder* const idec) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
const size_t psize = br->buf_end_ - br->buf_;
|
||||
MemBuffer* const mem = &idec->mem_;
|
||||
assert(!idec->is_lossless_);
|
||||
assert(mem->part0_buf_ == NULL);
|
||||
assert(psize > 0);
|
||||
assert(psize <= mem->part0_size_); // Format limit: no need for runtime check
|
||||
if (mem->mode_ == MEM_MODE_APPEND) {
|
||||
// We copy and grab ownership of the partition #0 data.
|
||||
uint8_t* const part0_buf = (uint8_t*)malloc(psize);
|
||||
if (part0_buf == NULL) {
|
||||
return 0;
|
||||
}
|
||||
memcpy(part0_buf, br->buf_, psize);
|
||||
mem->part0_buf_ = part0_buf;
|
||||
br->buf_ = part0_buf;
|
||||
br->buf_end_ = part0_buf + psize;
|
||||
} else {
|
||||
// Else: just keep pointers to the partition #0's data in dec_->br_.
|
||||
}
|
||||
mem->start_ += psize;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static VP8StatusCode DecodePartition0(WebPIDecoder* const idec) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
VP8Io* const io = &idec->io_;
|
||||
const WebPDecParams* const params = &idec->params_;
|
||||
WebPDecBuffer* const output = params->output;
|
||||
|
||||
// Wait till we have enough data for the whole partition #0
|
||||
if (MemDataSize(&idec->mem_) < idec->mem_.part0_size_) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
|
||||
if (!VP8GetHeaders(dec, io)) {
|
||||
const VP8StatusCode status = dec->status_;
|
||||
if (status == VP8_STATUS_SUSPENDED ||
|
||||
status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
// treating NOT_ENOUGH_DATA as SUSPENDED state
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
return IDecError(idec, status);
|
||||
}
|
||||
|
||||
// Allocate/Verify output buffer now
|
||||
dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,
|
||||
output);
|
||||
if (dec->status_ != VP8_STATUS_OK) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
|
||||
if (!CopyParts0Data(idec)) {
|
||||
return IDecError(idec, VP8_STATUS_OUT_OF_MEMORY);
|
||||
}
|
||||
|
||||
// Finish setting up the decoding parameters. Will call io->setup().
|
||||
if (VP8EnterCritical(dec, io) != VP8_STATUS_OK) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
|
||||
// Note: past this point, teardown() must always be called
|
||||
// in case of error.
|
||||
idec->state_ = STATE_VP8_DATA;
|
||||
// Allocate memory and prepare everything.
|
||||
if (!VP8InitFrame(dec, io)) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
// Remaining partitions
|
||||
static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)idec->dec_;
|
||||
VP8Io* const io = &idec->io_;
|
||||
|
||||
assert(dec->ready_);
|
||||
|
||||
for (; dec->mb_y_ < dec->mb_h_; ++dec->mb_y_) {
|
||||
VP8BitReader* token_br = &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
|
||||
if (dec->mb_x_ == 0) {
|
||||
VP8InitScanline(dec);
|
||||
}
|
||||
for (; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
|
||||
MBContext context;
|
||||
SaveContext(dec, token_br, &context);
|
||||
|
||||
if (!VP8DecodeMB(dec, token_br)) {
|
||||
RestoreContext(&context, dec, token_br);
|
||||
// We shouldn't fail when MAX_MB data was available
|
||||
if (dec->num_parts_ == 1 && MemDataSize(&idec->mem_) > MAX_MB_SIZE) {
|
||||
return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR);
|
||||
}
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
VP8ReconstructBlock(dec);
|
||||
// Store data and save block's filtering params
|
||||
VP8StoreBlock(dec);
|
||||
|
||||
// Release buffer only if there is only one partition
|
||||
if (dec->num_parts_ == 1) {
|
||||
idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_;
|
||||
assert(idec->mem_.start_ <= idec->mem_.end_);
|
||||
}
|
||||
}
|
||||
if (!VP8ProcessRow(dec, io)) {
|
||||
return IDecError(idec, VP8_STATUS_USER_ABORT);
|
||||
}
|
||||
dec->mb_x_ = 0;
|
||||
}
|
||||
// Synchronize the thread and check for errors.
|
||||
if (!VP8ExitCritical(dec, io)) {
|
||||
return IDecError(idec, VP8_STATUS_USER_ABORT);
|
||||
}
|
||||
dec->ready_ = 0;
|
||||
idec->state_ = STATE_DONE;
|
||||
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
static int ErrorStatusLossless(WebPIDecoder* const idec, VP8StatusCode status) {
|
||||
if (status == VP8_STATUS_SUSPENDED || status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
return IDecError(idec, status);
|
||||
}
|
||||
|
||||
static VP8StatusCode DecodeVP8LHeader(WebPIDecoder* const idec) {
|
||||
VP8Io* const io = &idec->io_;
|
||||
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
|
||||
const WebPDecParams* const params = &idec->params_;
|
||||
WebPDecBuffer* const output = params->output;
|
||||
size_t curr_size = MemDataSize(&idec->mem_);
|
||||
assert(idec->is_lossless_);
|
||||
|
||||
// Wait until there's enough data for decoding header.
|
||||
if (curr_size < (idec->chunk_size_ >> 3)) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
if (!VP8LDecodeHeader(dec, io)) {
|
||||
return ErrorStatusLossless(idec, dec->status_);
|
||||
}
|
||||
// Allocate/verify output buffer now.
|
||||
dec->status_ = WebPAllocateDecBuffer(io->width, io->height, params->options,
|
||||
output);
|
||||
if (dec->status_ != VP8_STATUS_OK) {
|
||||
return IDecError(idec, dec->status_);
|
||||
}
|
||||
|
||||
idec->state_ = STATE_VP8L_DATA;
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
static VP8StatusCode DecodeVP8LData(WebPIDecoder* const idec) {
|
||||
VP8LDecoder* const dec = (VP8LDecoder*)idec->dec_;
|
||||
const size_t curr_size = MemDataSize(&idec->mem_);
|
||||
assert(idec->is_lossless_);
|
||||
|
||||
// At present Lossless decoder can't decode image incrementally. So wait till
|
||||
// all the image data is aggregated before image can be decoded.
|
||||
if (curr_size < idec->chunk_size_) {
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
|
||||
if (!VP8LDecodeImage(dec)) {
|
||||
return ErrorStatusLossless(idec, dec->status_);
|
||||
}
|
||||
|
||||
idec->state_ = STATE_DONE;
|
||||
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
// Main decoding loop
|
||||
static VP8StatusCode IDecode(WebPIDecoder* idec) {
|
||||
VP8StatusCode status = VP8_STATUS_SUSPENDED;
|
||||
|
||||
if (idec->state_ == STATE_PRE_VP8) {
|
||||
status = DecodeWebPHeaders(idec);
|
||||
} else {
|
||||
if (idec->dec_ == NULL) {
|
||||
return VP8_STATUS_SUSPENDED; // can't continue if we have no decoder.
|
||||
}
|
||||
}
|
||||
if (idec->state_ == STATE_VP8_FRAME_HEADER) {
|
||||
status = DecodeVP8FrameHeader(idec);
|
||||
}
|
||||
if (idec->state_ == STATE_VP8_PARTS0) {
|
||||
status = DecodePartition0(idec);
|
||||
}
|
||||
if (idec->state_ == STATE_VP8_DATA) {
|
||||
status = DecodeRemaining(idec);
|
||||
}
|
||||
if (idec->state_ == STATE_VP8L_HEADER) {
|
||||
status = DecodeVP8LHeader(idec);
|
||||
}
|
||||
if (idec->state_ == STATE_VP8L_DATA) {
|
||||
status = DecodeVP8LData(idec);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public functions
|
||||
|
||||
WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) {
|
||||
WebPIDecoder* idec = (WebPIDecoder*)calloc(1, sizeof(*idec));
|
||||
if (idec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
idec->state_ = STATE_PRE_VP8;
|
||||
idec->chunk_size_ = 0;
|
||||
|
||||
InitMemBuffer(&idec->mem_);
|
||||
WebPInitDecBuffer(&idec->output_);
|
||||
VP8InitIo(&idec->io_);
|
||||
|
||||
WebPResetDecParams(&idec->params_);
|
||||
idec->params_.output = output_buffer ? output_buffer : &idec->output_;
|
||||
WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions.
|
||||
|
||||
return idec;
|
||||
}
|
||||
|
||||
WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config) {
|
||||
WebPIDecoder* idec;
|
||||
|
||||
// Parse the bitstream's features, if requested:
|
||||
if (data != NULL && data_size > 0 && config != NULL) {
|
||||
if (WebPGetFeatures(data, data_size, &config->input) != VP8_STATUS_OK) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
// Create an instance of the incremental decoder
|
||||
idec = WebPINewDecoder(config ? &config->output : NULL);
|
||||
if (idec == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
// Finish initialization
|
||||
if (config != NULL) {
|
||||
idec->params_.options = &config->options;
|
||||
}
|
||||
return idec;
|
||||
}
|
||||
|
||||
void WebPIDelete(WebPIDecoder* idec) {
|
||||
if (idec == NULL) return;
|
||||
if (idec->dec_ != NULL) {
|
||||
if (!idec->is_lossless_) {
|
||||
VP8Delete(idec->dec_);
|
||||
} else {
|
||||
VP8LDelete(idec->dec_);
|
||||
}
|
||||
}
|
||||
ClearMemBuffer(&idec->mem_);
|
||||
WebPFreeDecBuffer(&idec->output_);
|
||||
free(idec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Wrapper toward WebPINewDecoder
|
||||
|
||||
WebPIDecoder* WebPINewRGB(WEBP_CSP_MODE mode, uint8_t* output_buffer,
|
||||
size_t output_buffer_size, int output_stride) {
|
||||
WebPIDecoder* idec;
|
||||
if (mode >= MODE_YUV) return NULL;
|
||||
idec = WebPINewDecoder(NULL);
|
||||
if (idec == NULL) return NULL;
|
||||
idec->output_.colorspace = mode;
|
||||
idec->output_.is_external_memory = 1;
|
||||
idec->output_.u.RGBA.rgba = output_buffer;
|
||||
idec->output_.u.RGBA.stride = output_stride;
|
||||
idec->output_.u.RGBA.size = output_buffer_size;
|
||||
return idec;
|
||||
}
|
||||
|
||||
WebPIDecoder* WebPINewYUVA(uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride,
|
||||
uint8_t* a, size_t a_size, int a_stride) {
|
||||
WebPIDecoder* const idec = WebPINewDecoder(NULL);
|
||||
if (idec == NULL) return NULL;
|
||||
idec->output_.colorspace = (a == NULL) ? MODE_YUV : MODE_YUVA;
|
||||
idec->output_.is_external_memory = 1;
|
||||
idec->output_.u.YUVA.y = luma;
|
||||
idec->output_.u.YUVA.y_stride = luma_stride;
|
||||
idec->output_.u.YUVA.y_size = luma_size;
|
||||
idec->output_.u.YUVA.u = u;
|
||||
idec->output_.u.YUVA.u_stride = u_stride;
|
||||
idec->output_.u.YUVA.u_size = u_size;
|
||||
idec->output_.u.YUVA.v = v;
|
||||
idec->output_.u.YUVA.v_stride = v_stride;
|
||||
idec->output_.u.YUVA.v_size = v_size;
|
||||
idec->output_.u.YUVA.a = a;
|
||||
idec->output_.u.YUVA.a_stride = a_stride;
|
||||
idec->output_.u.YUVA.a_size = a_size;
|
||||
return idec;
|
||||
}
|
||||
|
||||
WebPIDecoder* WebPINewYUV(uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride) {
|
||||
return WebPINewYUVA(luma, luma_size, luma_stride,
|
||||
u, u_size, u_stride,
|
||||
v, v_size, v_stride,
|
||||
NULL, 0, 0);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static VP8StatusCode IDecCheckStatus(const WebPIDecoder* const idec) {
|
||||
assert(idec);
|
||||
if (idec->state_ == STATE_ERROR) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (idec->state_ == STATE_DONE) {
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
return VP8_STATUS_SUSPENDED;
|
||||
}
|
||||
|
||||
VP8StatusCode WebPIAppend(WebPIDecoder* idec,
|
||||
const uint8_t* data, size_t data_size) {
|
||||
VP8StatusCode status;
|
||||
if (idec == NULL || data == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
status = IDecCheckStatus(idec);
|
||||
if (status != VP8_STATUS_SUSPENDED) {
|
||||
return status;
|
||||
}
|
||||
// Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
|
||||
if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_APPEND)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
// Append data to memory buffer
|
||||
if (!AppendToMemBuffer(idec, data, data_size)) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
return IDecode(idec);
|
||||
}
|
||||
|
||||
VP8StatusCode WebPIUpdate(WebPIDecoder* idec,
|
||||
const uint8_t* data, size_t data_size) {
|
||||
VP8StatusCode status;
|
||||
if (idec == NULL || data == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
status = IDecCheckStatus(idec);
|
||||
if (status != VP8_STATUS_SUSPENDED) {
|
||||
return status;
|
||||
}
|
||||
// Check mixed calls between RemapMemBuffer and AppendToMemBuffer.
|
||||
if (!CheckMemBufferMode(&idec->mem_, MEM_MODE_MAP)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
// Make the memory buffer point to the new buffer
|
||||
if (!RemapMemBuffer(idec, data, data_size)) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
return IDecode(idec);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) {
|
||||
if (idec == NULL || idec->dec_ == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
if (idec->state_ <= STATE_VP8_PARTS0) {
|
||||
return NULL;
|
||||
}
|
||||
return idec->params_.output;
|
||||
}
|
||||
|
||||
const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec,
|
||||
int* left, int* top,
|
||||
int* width, int* height) {
|
||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||
if (left != NULL) *left = 0;
|
||||
if (top != NULL) *top = 0;
|
||||
// TODO(skal): later include handling of rotations.
|
||||
if (src) {
|
||||
if (width != NULL) *width = src->width;
|
||||
if (height != NULL) *height = idec->params_.last_y;
|
||||
} else {
|
||||
if (width != NULL) *width = 0;
|
||||
if (height != NULL) *height = 0;
|
||||
}
|
||||
return src;
|
||||
}
|
||||
|
||||
uint8_t* WebPIDecGetRGB(const WebPIDecoder* idec, int* last_y,
|
||||
int* width, int* height, int* stride) {
|
||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||
if (src == NULL) return NULL;
|
||||
if (src->colorspace >= MODE_YUV) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (last_y != NULL) *last_y = idec->params_.last_y;
|
||||
if (width != NULL) *width = src->width;
|
||||
if (height != NULL) *height = src->height;
|
||||
if (stride != NULL) *stride = src->u.RGBA.stride;
|
||||
|
||||
return src->u.RGBA.rgba;
|
||||
}
|
||||
|
||||
uint8_t* WebPIDecGetYUVA(const WebPIDecoder* idec, int* last_y,
|
||||
uint8_t** u, uint8_t** v, uint8_t** a,
|
||||
int* width, int* height,
|
||||
int* stride, int* uv_stride, int* a_stride) {
|
||||
const WebPDecBuffer* const src = GetOutputBuffer(idec);
|
||||
if (src == NULL) return NULL;
|
||||
if (src->colorspace < MODE_YUV) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (last_y != NULL) *last_y = idec->params_.last_y;
|
||||
if (u != NULL) *u = src->u.YUVA.u;
|
||||
if (v != NULL) *v = src->u.YUVA.v;
|
||||
if (a != NULL) *a = src->u.YUVA.a;
|
||||
if (width != NULL) *width = src->width;
|
||||
if (height != NULL) *height = src->height;
|
||||
if (stride != NULL) *stride = src->u.YUVA.y_stride;
|
||||
if (uv_stride != NULL) *uv_stride = src->u.YUVA.u_stride;
|
||||
if (a_stride != NULL) *a_stride = src->u.YUVA.a_stride;
|
||||
|
||||
return src->u.YUVA.y;
|
||||
}
|
||||
|
||||
int WebPISetIOHooks(WebPIDecoder* const idec,
|
||||
VP8IoPutHook put,
|
||||
VP8IoSetupHook setup,
|
||||
VP8IoTeardownHook teardown,
|
||||
void* user_data) {
|
||||
if (idec == NULL || idec->state_ > STATE_PRE_VP8) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
idec->io_.put = put;
|
||||
idec->io_.setup = setup;
|
||||
idec->io_.teardown = teardown;
|
||||
idec->io_.opaque = user_data;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
633
drivers/webpold/dec/io.c
Normal file
633
drivers/webpold/dec/io.c
Normal file
@ -0,0 +1,633 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// functions for sample output.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include "../dec/vp8i.h"
|
||||
#include "./webpi.h"
|
||||
#include "../dsp/dsp.h"
|
||||
#include "../dsp/yuv.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main YUV<->RGB conversion functions
|
||||
|
||||
static int EmitYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
WebPDecBuffer* output = p->output;
|
||||
const WebPYUVABuffer* const buf = &output->u.YUVA;
|
||||
uint8_t* const y_dst = buf->y + io->mb_y * buf->y_stride;
|
||||
uint8_t* const u_dst = buf->u + (io->mb_y >> 1) * buf->u_stride;
|
||||
uint8_t* const v_dst = buf->v + (io->mb_y >> 1) * buf->v_stride;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
const int uv_w = (mb_w + 1) / 2;
|
||||
const int uv_h = (mb_h + 1) / 2;
|
||||
int j;
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
memcpy(y_dst + j * buf->y_stride, io->y + j * io->y_stride, mb_w);
|
||||
}
|
||||
for (j = 0; j < uv_h; ++j) {
|
||||
memcpy(u_dst + j * buf->u_stride, io->u + j * io->uv_stride, uv_w);
|
||||
memcpy(v_dst + j * buf->v_stride, io->v + j * io->uv_stride, uv_w);
|
||||
}
|
||||
return io->mb_h;
|
||||
}
|
||||
|
||||
// Point-sampling U/V sampler.
|
||||
static int EmitSampledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
WebPDecBuffer* output = p->output;
|
||||
const WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
|
||||
const uint8_t* y_src = io->y;
|
||||
const uint8_t* u_src = io->u;
|
||||
const uint8_t* v_src = io->v;
|
||||
const WebPSampleLinePairFunc sample = WebPSamplers[output->colorspace];
|
||||
const int mb_w = io->mb_w;
|
||||
const int last = io->mb_h - 1;
|
||||
int j;
|
||||
for (j = 0; j < last; j += 2) {
|
||||
sample(y_src, y_src + io->y_stride, u_src, v_src,
|
||||
dst, dst + buf->stride, mb_w);
|
||||
y_src += 2 * io->y_stride;
|
||||
u_src += io->uv_stride;
|
||||
v_src += io->uv_stride;
|
||||
dst += 2 * buf->stride;
|
||||
}
|
||||
if (j == last) { // Just do the last line twice
|
||||
sample(y_src, y_src, u_src, v_src, dst, dst, mb_w);
|
||||
}
|
||||
return io->mb_h;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// YUV444 -> RGB conversion
|
||||
|
||||
#if 0 // TODO(skal): this is for future rescaling.
|
||||
static int EmitRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
WebPDecBuffer* output = p->output;
|
||||
const WebPRGBABuffer* const buf = &output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
|
||||
const uint8_t* y_src = io->y;
|
||||
const uint8_t* u_src = io->u;
|
||||
const uint8_t* v_src = io->v;
|
||||
const WebPYUV444Converter convert = WebPYUV444Converters[output->colorspace];
|
||||
const int mb_w = io->mb_w;
|
||||
const int last = io->mb_h;
|
||||
int j;
|
||||
for (j = 0; j < last; ++j) {
|
||||
convert(y_src, u_src, v_src, dst, mb_w);
|
||||
y_src += io->y_stride;
|
||||
u_src += io->uv_stride;
|
||||
v_src += io->uv_stride;
|
||||
dst += buf->stride;
|
||||
}
|
||||
return io->mb_h;
|
||||
}
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Fancy upsampling
|
||||
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
int num_lines_out = io->mb_h; // a priori guess
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + io->mb_y * buf->stride;
|
||||
WebPUpsampleLinePairFunc upsample = WebPUpsamplers[p->output->colorspace];
|
||||
const uint8_t* cur_y = io->y;
|
||||
const uint8_t* cur_u = io->u;
|
||||
const uint8_t* cur_v = io->v;
|
||||
const uint8_t* top_u = p->tmp_u;
|
||||
const uint8_t* top_v = p->tmp_v;
|
||||
int y = io->mb_y;
|
||||
const int y_end = io->mb_y + io->mb_h;
|
||||
const int mb_w = io->mb_w;
|
||||
const int uv_w = (mb_w + 1) / 2;
|
||||
|
||||
if (y == 0) {
|
||||
// First line is special cased. We mirror the u/v samples at boundary.
|
||||
upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, mb_w);
|
||||
} else {
|
||||
// We can finish the left-over line from previous call.
|
||||
upsample(p->tmp_y, cur_y, top_u, top_v, cur_u, cur_v,
|
||||
dst - buf->stride, dst, mb_w);
|
||||
++num_lines_out;
|
||||
}
|
||||
// Loop over each output pairs of row.
|
||||
for (; y + 2 < y_end; y += 2) {
|
||||
top_u = cur_u;
|
||||
top_v = cur_v;
|
||||
cur_u += io->uv_stride;
|
||||
cur_v += io->uv_stride;
|
||||
dst += 2 * buf->stride;
|
||||
cur_y += 2 * io->y_stride;
|
||||
upsample(cur_y - io->y_stride, cur_y,
|
||||
top_u, top_v, cur_u, cur_v,
|
||||
dst - buf->stride, dst, mb_w);
|
||||
}
|
||||
// move to last row
|
||||
cur_y += io->y_stride;
|
||||
if (io->crop_top + y_end < io->crop_bottom) {
|
||||
// Save the unfinished samples for next call (as we're not done yet).
|
||||
memcpy(p->tmp_y, cur_y, mb_w * sizeof(*p->tmp_y));
|
||||
memcpy(p->tmp_u, cur_u, uv_w * sizeof(*p->tmp_u));
|
||||
memcpy(p->tmp_v, cur_v, uv_w * sizeof(*p->tmp_v));
|
||||
// The fancy upsampler leaves a row unfinished behind
|
||||
// (except for the very last row)
|
||||
num_lines_out--;
|
||||
} else {
|
||||
// Process the very last row of even-sized picture
|
||||
if (!(y_end & 1)) {
|
||||
upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v,
|
||||
dst + buf->stride, NULL, mb_w);
|
||||
}
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
#endif /* FANCY_UPSAMPLING */
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const uint8_t* alpha = io->a;
|
||||
const WebPYUVABuffer* const buf = &p->output->u.YUVA;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
uint8_t* dst = buf->a + io->mb_y * buf->a_stride;
|
||||
int j;
|
||||
|
||||
if (alpha != NULL) {
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
memcpy(dst, alpha, mb_w * sizeof(*dst));
|
||||
alpha += io->width;
|
||||
dst += buf->a_stride;
|
||||
}
|
||||
} else if (buf->a != NULL) {
|
||||
// the user requested alpha, but there is none, set it to opaque.
|
||||
for (j = 0; j < mb_h; ++j) {
|
||||
memset(dst, 0xff, mb_w * sizeof(*dst));
|
||||
dst += buf->a_stride;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int GetAlphaSourceRow(const VP8Io* const io,
|
||||
const uint8_t** alpha, int* const num_rows) {
|
||||
int start_y = io->mb_y;
|
||||
*num_rows = io->mb_h;
|
||||
|
||||
// Compensate for the 1-line delay of the fancy upscaler.
|
||||
// This is similar to EmitFancyRGB().
|
||||
if (io->fancy_upsampling) {
|
||||
if (start_y == 0) {
|
||||
// We don't process the last row yet. It'll be done during the next call.
|
||||
--*num_rows;
|
||||
} else {
|
||||
--start_y;
|
||||
// Fortunately, *alpha data is persistent, so we can go back
|
||||
// one row and finish alpha blending, now that the fancy upscaler
|
||||
// completed the YUV->RGB interpolation.
|
||||
*alpha -= io->width;
|
||||
}
|
||||
if (io->crop_top + io->mb_y + io->mb_h == io->crop_bottom) {
|
||||
// If it's the very last call, we process all the remaining rows!
|
||||
*num_rows = io->crop_bottom - io->crop_top - start_y;
|
||||
}
|
||||
}
|
||||
return start_y;
|
||||
}
|
||||
|
||||
static int EmitAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const uint8_t* alpha = io->a;
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int alpha_first =
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
int num_rows;
|
||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
uint32_t alpha_mask = 0xff;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
const uint32_t alpha_value = alpha[i];
|
||||
dst[4 * i] = alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha += io->width;
|
||||
dst += buf->stride;
|
||||
}
|
||||
// alpha_mask is < 0xff if there's non-trivial alpha to premultiply with.
|
||||
if (alpha_mask != 0xff && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
mb_w, num_rows, buf->stride);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int EmitAlphaRGBA4444(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const uint8_t* alpha = io->a;
|
||||
if (alpha != NULL) {
|
||||
const int mb_w = io->mb_w;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
int num_rows;
|
||||
const int start_y = GetAlphaSourceRow(io, &alpha, &num_rows);
|
||||
uint8_t* const base_rgba = buf->rgba + start_y * buf->stride;
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
uint32_t alpha_mask = 0x0f;
|
||||
int i, j;
|
||||
|
||||
for (j = 0; j < num_rows; ++j) {
|
||||
for (i = 0; i < mb_w; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
const uint32_t alpha_value = alpha[i] >> 4;
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha += io->width;
|
||||
alpha_dst += buf->stride;
|
||||
}
|
||||
if (alpha_mask != 0x0f && WebPIsPremultipliedMode(colorspace)) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, mb_w, num_rows, buf->stride);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// YUV rescaling (no final RGB conversion needed)
|
||||
|
||||
static int Rescale(const uint8_t* src, int src_stride,
|
||||
int new_lines, WebPRescaler* const wrk) {
|
||||
int num_lines_out = 0;
|
||||
while (new_lines > 0) { // import new contributions of source rows.
|
||||
const int lines_in = WebPRescalerImport(wrk, new_lines, src, src_stride);
|
||||
src += lines_in * src_stride;
|
||||
new_lines -= lines_in;
|
||||
num_lines_out += WebPRescalerExport(wrk); // emit output row(s)
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int mb_h = io->mb_h;
|
||||
const int uv_mb_h = (mb_h + 1) >> 1;
|
||||
const int num_lines_out = Rescale(io->y, io->y_stride, mb_h, &p->scaler_y);
|
||||
Rescale(io->u, io->uv_stride, uv_mb_h, &p->scaler_u);
|
||||
Rescale(io->v, io->uv_stride, uv_mb_h, &p->scaler_v);
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p) {
|
||||
if (io->a != NULL) {
|
||||
Rescale(io->a, io->width, io->mb_h, &p->scaler_a);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
if (has_alpha) {
|
||||
tmp_size += work_size;
|
||||
}
|
||||
p->memory = calloc(1, tmp_size * sizeof(*work));
|
||||
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;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RGBA rescaling
|
||||
|
||||
static int ExportRGB(WebPDecParams* const p, int y_pos) {
|
||||
const WebPYUV444Converter convert =
|
||||
WebPYUV444Converters[p->output->colorspace];
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* dst = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
||||
int num_lines_out = 0;
|
||||
// For RGB rescaling, because of the YUV420, current scan position
|
||||
// U/V can be +1/-1 line from the Y one. Hence the double test.
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_y) &&
|
||||
WebPRescalerHasPendingOutput(&p->scaler_u)) {
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
assert(p->scaler_u.y_accum == p->scaler_v.y_accum);
|
||||
WebPRescalerExportRow(&p->scaler_y);
|
||||
WebPRescalerExportRow(&p->scaler_u);
|
||||
WebPRescalerExportRow(&p->scaler_v);
|
||||
convert(p->scaler_y.dst, p->scaler_u.dst, p->scaler_v.dst,
|
||||
dst, p->scaler_y.dst_width);
|
||||
dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
const int mb_h = io->mb_h;
|
||||
const int uv_mb_h = (mb_h + 1) >> 1;
|
||||
int j = 0, uv_j = 0;
|
||||
int num_lines_out = 0;
|
||||
while (j < mb_h) {
|
||||
const int y_lines_in =
|
||||
WebPRescalerImport(&p->scaler_y, mb_h - j,
|
||||
io->y + j * io->y_stride, io->y_stride);
|
||||
const int u_lines_in =
|
||||
WebPRescalerImport(&p->scaler_u, uv_mb_h - uv_j,
|
||||
io->u + uv_j * io->uv_stride, io->uv_stride);
|
||||
const int v_lines_in =
|
||||
WebPRescalerImport(&p->scaler_v, uv_mb_h - uv_j,
|
||||
io->v + uv_j * io->uv_stride, io->uv_stride);
|
||||
(void)v_lines_in; // remove a gcc warning
|
||||
assert(u_lines_in == v_lines_in);
|
||||
j += y_lines_in;
|
||||
uv_j += u_lines_in;
|
||||
num_lines_out += ExportRGB(p, num_lines_out);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int ExportAlpha(WebPDecParams* const p, int y_pos) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int alpha_first =
|
||||
(colorspace == MODE_ARGB || colorspace == MODE_Argb);
|
||||
uint8_t* dst = base_rgba + (alpha_first ? 0 : 3);
|
||||
int num_lines_out = 0;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
uint32_t alpha_mask = 0xff;
|
||||
const int width = p->scaler_a.dst_width;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||
int i;
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(&p->scaler_a);
|
||||
for (i = 0; i < width; ++i) {
|
||||
const uint32_t alpha_value = p->scaler_a.dst[i];
|
||||
dst[4 * i] = alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha && alpha_mask != 0xff) {
|
||||
WebPApplyAlphaMultiply(base_rgba, alpha_first,
|
||||
width, num_lines_out, buf->stride);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int ExportAlphaRGBA4444(WebPDecParams* const p, int y_pos) {
|
||||
const WebPRGBABuffer* const buf = &p->output->u.RGBA;
|
||||
uint8_t* const base_rgba = buf->rgba + (p->last_y + y_pos) * buf->stride;
|
||||
uint8_t* alpha_dst = base_rgba + 1;
|
||||
int num_lines_out = 0;
|
||||
const WEBP_CSP_MODE colorspace = p->output->colorspace;
|
||||
const int width = p->scaler_a.dst_width;
|
||||
const int is_premult_alpha = WebPIsPremultipliedMode(colorspace);
|
||||
uint32_t alpha_mask = 0x0f;
|
||||
|
||||
while (WebPRescalerHasPendingOutput(&p->scaler_a)) {
|
||||
int i;
|
||||
assert(p->last_y + y_pos + num_lines_out < p->output->height);
|
||||
WebPRescalerExportRow(&p->scaler_a);
|
||||
for (i = 0; i < width; ++i) {
|
||||
// Fill in the alpha value (converted to 4 bits).
|
||||
const uint32_t alpha_value = p->scaler_a.dst[i] >> 4;
|
||||
alpha_dst[2 * i] = (alpha_dst[2 * i] & 0xf0) | alpha_value;
|
||||
alpha_mask &= alpha_value;
|
||||
}
|
||||
alpha_dst += buf->stride;
|
||||
++num_lines_out;
|
||||
}
|
||||
if (is_premult_alpha && alpha_mask != 0x0f) {
|
||||
WebPApplyAlphaMultiply4444(base_rgba, width, num_lines_out, buf->stride);
|
||||
}
|
||||
return num_lines_out;
|
||||
}
|
||||
|
||||
static int EmitRescaledAlphaRGB(const VP8Io* const io, WebPDecParams* const p) {
|
||||
if (io->a != NULL) {
|
||||
WebPRescaler* const scaler = &p->scaler_a;
|
||||
int j = 0;
|
||||
int pos = 0;
|
||||
while (j < io->mb_h) {
|
||||
j += WebPRescalerImport(scaler, io->mb_h - j,
|
||||
io->a + j * io->width, io->width);
|
||||
pos += p->emit_alpha_row(p, pos);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
tmp_size1 = 3 * work_size;
|
||||
tmp_size2 = 3 * out_width;
|
||||
if (has_alpha) {
|
||||
tmp_size1 += work_size;
|
||||
tmp_size2 += out_width;
|
||||
}
|
||||
p->memory = calloc(1, tmp_size1 * sizeof(*work) + tmp_size2 * sizeof(*tmp));
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Default custom functions
|
||||
|
||||
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 (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
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
if (io->fancy_upsampling) {
|
||||
const int uv_width = (io->mb_w + 1) >> 1;
|
||||
p->memory = malloc(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
|
||||
if (WebPIsPremultipliedMode(colorspace)) WebPInitPremultiply();
|
||||
p->emit_alpha =
|
||||
(colorspace == MODE_RGBA_4444 || colorspace == MODE_rgbA_4444) ?
|
||||
EmitAlphaRGBA4444
|
||||
: is_rgb ? EmitAlphaRGB
|
||||
: EmitAlphaYUV;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_rgb) {
|
||||
VP8YUVInit();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static int CustomPut(const VP8Io* io) {
|
||||
WebPDecParams* const p = (WebPDecParams*)io->opaque;
|
||||
const int mb_w = io->mb_w;
|
||||
const int mb_h = io->mb_h;
|
||||
int num_lines_out;
|
||||
assert(!(io->mb_y & 1));
|
||||
|
||||
if (mb_w <= 0 || mb_h <= 0) {
|
||||
return 0;
|
||||
}
|
||||
num_lines_out = p->emit(io, p);
|
||||
if (p->emit_alpha) {
|
||||
p->emit_alpha(io, p);
|
||||
}
|
||||
p->last_y += num_lines_out;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void CustomTeardown(const VP8Io* io) {
|
||||
WebPDecParams* const p = (WebPDecParams*)io->opaque;
|
||||
free(p->memory);
|
||||
p->memory = NULL;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main entry point
|
||||
|
||||
void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io) {
|
||||
io->put = CustomPut;
|
||||
io->setup = CustomSetup;
|
||||
io->teardown = CustomTeardown;
|
||||
io->opaque = params;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
113
drivers/webpold/dec/quant.c
Normal file
113
drivers/webpold/dec/quant.c
Normal file
@ -0,0 +1,113 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Quantizer initialization
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "./vp8i.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
static WEBP_INLINE int clip(int v, int M) {
|
||||
return v < 0 ? 0 : v > M ? M : v;
|
||||
}
|
||||
|
||||
// Paragraph 14.1
|
||||
static const uint8_t kDcTable[128] = {
|
||||
4, 5, 6, 7, 8, 9, 10, 10,
|
||||
11, 12, 13, 14, 15, 16, 17, 17,
|
||||
18, 19, 20, 20, 21, 21, 22, 22,
|
||||
23, 23, 24, 25, 25, 26, 27, 28,
|
||||
29, 30, 31, 32, 33, 34, 35, 36,
|
||||
37, 37, 38, 39, 40, 41, 42, 43,
|
||||
44, 45, 46, 46, 47, 48, 49, 50,
|
||||
51, 52, 53, 54, 55, 56, 57, 58,
|
||||
59, 60, 61, 62, 63, 64, 65, 66,
|
||||
67, 68, 69, 70, 71, 72, 73, 74,
|
||||
75, 76, 76, 77, 78, 79, 80, 81,
|
||||
82, 83, 84, 85, 86, 87, 88, 89,
|
||||
91, 93, 95, 96, 98, 100, 101, 102,
|
||||
104, 106, 108, 110, 112, 114, 116, 118,
|
||||
122, 124, 126, 128, 130, 132, 134, 136,
|
||||
138, 140, 143, 145, 148, 151, 154, 157
|
||||
};
|
||||
|
||||
static const uint16_t kAcTable[128] = {
|
||||
4, 5, 6, 7, 8, 9, 10, 11,
|
||||
12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27,
|
||||
28, 29, 30, 31, 32, 33, 34, 35,
|
||||
36, 37, 38, 39, 40, 41, 42, 43,
|
||||
44, 45, 46, 47, 48, 49, 50, 51,
|
||||
52, 53, 54, 55, 56, 57, 58, 60,
|
||||
62, 64, 66, 68, 70, 72, 74, 76,
|
||||
78, 80, 82, 84, 86, 88, 90, 92,
|
||||
94, 96, 98, 100, 102, 104, 106, 108,
|
||||
110, 112, 114, 116, 119, 122, 125, 128,
|
||||
131, 134, 137, 140, 143, 146, 149, 152,
|
||||
155, 158, 161, 164, 167, 170, 173, 177,
|
||||
181, 185, 189, 193, 197, 201, 205, 209,
|
||||
213, 217, 221, 225, 229, 234, 239, 245,
|
||||
249, 254, 259, 264, 269, 274, 279, 284
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Paragraph 9.6
|
||||
|
||||
void VP8ParseQuant(VP8Decoder* const dec) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
const int base_q0 = VP8GetValue(br, 7);
|
||||
const int dqy1_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
|
||||
const int dqy2_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
|
||||
const int dqy2_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
|
||||
const int dquv_dc = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
|
||||
const int dquv_ac = VP8Get(br) ? VP8GetSignedValue(br, 4) : 0;
|
||||
|
||||
const VP8SegmentHeader* const hdr = &dec->segment_hdr_;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_MB_SEGMENTS; ++i) {
|
||||
int q;
|
||||
if (hdr->use_segment_) {
|
||||
q = hdr->quantizer_[i];
|
||||
if (!hdr->absolute_delta_) {
|
||||
q += base_q0;
|
||||
}
|
||||
} else {
|
||||
if (i > 0) {
|
||||
dec->dqm_[i] = dec->dqm_[0];
|
||||
continue;
|
||||
} else {
|
||||
q = base_q0;
|
||||
}
|
||||
}
|
||||
{
|
||||
VP8QuantMatrix* const m = &dec->dqm_[i];
|
||||
m->y1_mat_[0] = kDcTable[clip(q + dqy1_dc, 127)];
|
||||
m->y1_mat_[1] = kAcTable[clip(q + 0, 127)];
|
||||
|
||||
m->y2_mat_[0] = kDcTable[clip(q + dqy2_dc, 127)] * 2;
|
||||
// For all x in [0..284], x*155/100 is bitwise equal to (x*101581) >> 16.
|
||||
// The smallest precision for that is '(x*6349) >> 12' but 16 is a good
|
||||
// word size.
|
||||
m->y2_mat_[1] = (kAcTable[clip(q + dqy2_ac, 127)] * 101581) >> 16;
|
||||
if (m->y2_mat_[1] < 8) m->y2_mat_[1] = 8;
|
||||
|
||||
m->uv_mat_[0] = kDcTable[clip(q + dquv_dc, 117)];
|
||||
m->uv_mat_[1] = kAcTable[clip(q + dquv_ac, 127)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
589
drivers/webpold/dec/tree.c
Normal file
589
drivers/webpold/dec/tree.c
Normal file
@ -0,0 +1,589 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Coding trees and probas
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "vp8i.h"
|
||||
|
||||
#define USE_GENERIC_TREE
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef USE_GENERIC_TREE
|
||||
static const int8_t kYModesIntra4[18] = {
|
||||
-B_DC_PRED, 1,
|
||||
-B_TM_PRED, 2,
|
||||
-B_VE_PRED, 3,
|
||||
4, 6,
|
||||
-B_HE_PRED, 5,
|
||||
-B_RD_PRED, -B_VR_PRED,
|
||||
-B_LD_PRED, 7,
|
||||
-B_VL_PRED, 8,
|
||||
-B_HD_PRED, -B_HU_PRED
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
|
||||
// inter prediction modes
|
||||
enum {
|
||||
LEFT4 = 0, ABOVE4 = 1, ZERO4 = 2, NEW4 = 3,
|
||||
NEARESTMV, NEARMV, ZEROMV, NEWMV, SPLITMV };
|
||||
|
||||
static const int8_t kYModesInter[8] = {
|
||||
-DC_PRED, 1,
|
||||
2, 3,
|
||||
-V_PRED, -H_PRED,
|
||||
-TM_PRED, -B_PRED
|
||||
};
|
||||
|
||||
static const int8_t kMBSplit[6] = {
|
||||
-3, 1,
|
||||
-2, 2,
|
||||
-0, -1
|
||||
};
|
||||
|
||||
static const int8_t kMVRef[8] = {
|
||||
-ZEROMV, 1,
|
||||
-NEARESTMV, 2,
|
||||
-NEARMV, 3,
|
||||
-NEWMV, -SPLITMV
|
||||
};
|
||||
|
||||
static const int8_t kMVRef4[6] = {
|
||||
-LEFT4, 1,
|
||||
-ABOVE4, 2,
|
||||
-ZERO4, -NEW4
|
||||
};
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Default probabilities
|
||||
|
||||
// Inter
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
static const uint8_t kYModeProbaInter0[4] = { 112, 86, 140, 37 };
|
||||
static const uint8_t kUVModeProbaInter0[3] = { 162, 101, 204 };
|
||||
static const uint8_t kMVProba0[2][NUM_MV_PROBAS] = {
|
||||
{ 162, 128, 225, 146, 172, 147, 214, 39,
|
||||
156, 128, 129, 132, 75, 145, 178, 206,
|
||||
239, 254, 254 },
|
||||
{ 164, 128, 204, 170, 119, 235, 140, 230,
|
||||
228, 128, 130, 130, 74, 148, 180, 203,
|
||||
236, 254, 254 }
|
||||
};
|
||||
#endif
|
||||
|
||||
// Paragraph 13.5
|
||||
static const uint8_t
|
||||
CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
|
||||
// genereated using vp8_default_coef_probs() in entropy.c:129
|
||||
{ { { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 253, 136, 254, 255, 228, 219, 128, 128, 128, 128, 128 },
|
||||
{ 189, 129, 242, 255, 227, 213, 255, 219, 128, 128, 128 },
|
||||
{ 106, 126, 227, 252, 214, 209, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 98, 248, 255, 236, 226, 255, 255, 128, 128, 128 },
|
||||
{ 181, 133, 238, 254, 221, 234, 255, 154, 128, 128, 128 },
|
||||
{ 78, 134, 202, 247, 198, 180, 255, 219, 128, 128, 128 },
|
||||
},
|
||||
{ { 1, 185, 249, 255, 243, 255, 128, 128, 128, 128, 128 },
|
||||
{ 184, 150, 247, 255, 236, 224, 128, 128, 128, 128, 128 },
|
||||
{ 77, 110, 216, 255, 236, 230, 128, 128, 128, 128, 128 },
|
||||
},
|
||||
{ { 1, 101, 251, 255, 241, 255, 128, 128, 128, 128, 128 },
|
||||
{ 170, 139, 241, 252, 236, 209, 255, 255, 128, 128, 128 },
|
||||
{ 37, 116, 196, 243, 228, 255, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 204, 254, 255, 245, 255, 128, 128, 128, 128, 128 },
|
||||
{ 207, 160, 250, 255, 238, 128, 128, 128, 128, 128, 128 },
|
||||
{ 102, 103, 231, 255, 211, 171, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 152, 252, 255, 240, 255, 128, 128, 128, 128, 128 },
|
||||
{ 177, 135, 243, 255, 234, 225, 128, 128, 128, 128, 128 },
|
||||
{ 80, 129, 211, 255, 194, 224, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 246, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 255, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
},
|
||||
{ { { 198, 35, 237, 223, 193, 187, 162, 160, 145, 155, 62 },
|
||||
{ 131, 45, 198, 221, 172, 176, 220, 157, 252, 221, 1 },
|
||||
{ 68, 47, 146, 208, 149, 167, 221, 162, 255, 223, 128 }
|
||||
},
|
||||
{ { 1, 149, 241, 255, 221, 224, 255, 255, 128, 128, 128 },
|
||||
{ 184, 141, 234, 253, 222, 220, 255, 199, 128, 128, 128 },
|
||||
{ 81, 99, 181, 242, 176, 190, 249, 202, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 129, 232, 253, 214, 197, 242, 196, 255, 255, 128 },
|
||||
{ 99, 121, 210, 250, 201, 198, 255, 202, 128, 128, 128 },
|
||||
{ 23, 91, 163, 242, 170, 187, 247, 210, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 200, 246, 255, 234, 255, 128, 128, 128, 128, 128 },
|
||||
{ 109, 178, 241, 255, 231, 245, 255, 255, 128, 128, 128 },
|
||||
{ 44, 130, 201, 253, 205, 192, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 132, 239, 251, 219, 209, 255, 165, 128, 128, 128 },
|
||||
{ 94, 136, 225, 251, 218, 190, 255, 255, 128, 128, 128 },
|
||||
{ 22, 100, 174, 245, 186, 161, 255, 199, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 182, 249, 255, 232, 235, 128, 128, 128, 128, 128 },
|
||||
{ 124, 143, 241, 255, 227, 234, 128, 128, 128, 128, 128 },
|
||||
{ 35, 77, 181, 251, 193, 211, 255, 205, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 157, 247, 255, 236, 231, 255, 255, 128, 128, 128 },
|
||||
{ 121, 141, 235, 255, 225, 227, 255, 255, 128, 128, 128 },
|
||||
{ 45, 99, 188, 251, 195, 217, 255, 224, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 1, 251, 255, 213, 255, 128, 128, 128, 128, 128 },
|
||||
{ 203, 1, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 137, 1, 177, 255, 224, 255, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
},
|
||||
{ { { 253, 9, 248, 251, 207, 208, 255, 192, 128, 128, 128 },
|
||||
{ 175, 13, 224, 243, 193, 185, 249, 198, 255, 255, 128 },
|
||||
{ 73, 17, 171, 221, 161, 179, 236, 167, 255, 234, 128 }
|
||||
},
|
||||
{ { 1, 95, 247, 253, 212, 183, 255, 255, 128, 128, 128 },
|
||||
{ 239, 90, 244, 250, 211, 209, 255, 255, 128, 128, 128 },
|
||||
{ 155, 77, 195, 248, 188, 195, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 24, 239, 251, 218, 219, 255, 205, 128, 128, 128 },
|
||||
{ 201, 51, 219, 255, 196, 186, 128, 128, 128, 128, 128 },
|
||||
{ 69, 46, 190, 239, 201, 218, 255, 228, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 191, 251, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 223, 165, 249, 255, 213, 255, 128, 128, 128, 128, 128 },
|
||||
{ 141, 124, 248, 255, 255, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 16, 248, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 190, 36, 230, 255, 236, 255, 128, 128, 128, 128, 128 },
|
||||
{ 149, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 226, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 247, 192, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 240, 128, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 134, 252, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 213, 62, 250, 255, 255, 128, 128, 128, 128, 128, 128 },
|
||||
{ 55, 93, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
},
|
||||
{ { 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
},
|
||||
{ { { 202, 24, 213, 235, 186, 191, 220, 160, 240, 175, 255 },
|
||||
{ 126, 38, 182, 232, 169, 184, 228, 174, 255, 187, 128 },
|
||||
{ 61, 46, 138, 219, 151, 178, 240, 170, 255, 216, 128 }
|
||||
},
|
||||
{ { 1, 112, 230, 250, 199, 191, 247, 159, 255, 255, 128 },
|
||||
{ 166, 109, 228, 252, 211, 215, 255, 174, 128, 128, 128 },
|
||||
{ 39, 77, 162, 232, 172, 180, 245, 178, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 52, 220, 246, 198, 199, 249, 220, 255, 255, 128 },
|
||||
{ 124, 74, 191, 243, 183, 193, 250, 221, 255, 255, 128 },
|
||||
{ 24, 71, 130, 219, 154, 170, 243, 182, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 182, 225, 249, 219, 240, 255, 224, 128, 128, 128 },
|
||||
{ 149, 150, 226, 252, 216, 205, 255, 171, 128, 128, 128 },
|
||||
{ 28, 108, 170, 242, 183, 194, 254, 223, 255, 255, 128 }
|
||||
},
|
||||
{ { 1, 81, 230, 252, 204, 203, 255, 192, 128, 128, 128 },
|
||||
{ 123, 102, 209, 247, 188, 196, 255, 233, 128, 128, 128 },
|
||||
{ 20, 95, 153, 243, 164, 173, 255, 203, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 222, 248, 255, 216, 213, 128, 128, 128, 128, 128 },
|
||||
{ 168, 175, 246, 252, 235, 205, 255, 255, 128, 128, 128 },
|
||||
{ 47, 116, 215, 255, 211, 212, 255, 255, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 121, 236, 253, 212, 214, 255, 255, 128, 128, 128 },
|
||||
{ 141, 84, 213, 252, 201, 202, 255, 219, 128, 128, 128 },
|
||||
{ 42, 80, 160, 240, 162, 185, 255, 205, 128, 128, 128 }
|
||||
},
|
||||
{ { 1, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 244, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 },
|
||||
{ 238, 1, 255, 128, 128, 128, 128, 128, 128, 128, 128 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Paragraph 11.5
|
||||
static const uint8_t kBModesProba[NUM_BMODES][NUM_BMODES][NUM_BMODES - 1] = {
|
||||
{ { 231, 120, 48, 89, 115, 113, 120, 152, 112 },
|
||||
{ 152, 179, 64, 126, 170, 118, 46, 70, 95 },
|
||||
{ 175, 69, 143, 80, 85, 82, 72, 155, 103 },
|
||||
{ 56, 58, 10, 171, 218, 189, 17, 13, 152 },
|
||||
{ 114, 26, 17, 163, 44, 195, 21, 10, 173 },
|
||||
{ 121, 24, 80, 195, 26, 62, 44, 64, 85 },
|
||||
{ 144, 71, 10, 38, 171, 213, 144, 34, 26 },
|
||||
{ 170, 46, 55, 19, 136, 160, 33, 206, 71 },
|
||||
{ 63, 20, 8, 114, 114, 208, 12, 9, 226 },
|
||||
{ 81, 40, 11, 96, 182, 84, 29, 16, 36 } },
|
||||
{ { 134, 183, 89, 137, 98, 101, 106, 165, 148 },
|
||||
{ 72, 187, 100, 130, 157, 111, 32, 75, 80 },
|
||||
{ 66, 102, 167, 99, 74, 62, 40, 234, 128 },
|
||||
{ 41, 53, 9, 178, 241, 141, 26, 8, 107 },
|
||||
{ 74, 43, 26, 146, 73, 166, 49, 23, 157 },
|
||||
{ 65, 38, 105, 160, 51, 52, 31, 115, 128 },
|
||||
{ 104, 79, 12, 27, 217, 255, 87, 17, 7 },
|
||||
{ 87, 68, 71, 44, 114, 51, 15, 186, 23 },
|
||||
{ 47, 41, 14, 110, 182, 183, 21, 17, 194 },
|
||||
{ 66, 45, 25, 102, 197, 189, 23, 18, 22 } },
|
||||
{ { 88, 88, 147, 150, 42, 46, 45, 196, 205 },
|
||||
{ 43, 97, 183, 117, 85, 38, 35, 179, 61 },
|
||||
{ 39, 53, 200, 87, 26, 21, 43, 232, 171 },
|
||||
{ 56, 34, 51, 104, 114, 102, 29, 93, 77 },
|
||||
{ 39, 28, 85, 171, 58, 165, 90, 98, 64 },
|
||||
{ 34, 22, 116, 206, 23, 34, 43, 166, 73 },
|
||||
{ 107, 54, 32, 26, 51, 1, 81, 43, 31 },
|
||||
{ 68, 25, 106, 22, 64, 171, 36, 225, 114 },
|
||||
{ 34, 19, 21, 102, 132, 188, 16, 76, 124 },
|
||||
{ 62, 18, 78, 95, 85, 57, 50, 48, 51 } },
|
||||
{ { 193, 101, 35, 159, 215, 111, 89, 46, 111 },
|
||||
{ 60, 148, 31, 172, 219, 228, 21, 18, 111 },
|
||||
{ 112, 113, 77, 85, 179, 255, 38, 120, 114 },
|
||||
{ 40, 42, 1, 196, 245, 209, 10, 25, 109 },
|
||||
{ 88, 43, 29, 140, 166, 213, 37, 43, 154 },
|
||||
{ 61, 63, 30, 155, 67, 45, 68, 1, 209 },
|
||||
{ 100, 80, 8, 43, 154, 1, 51, 26, 71 },
|
||||
{ 142, 78, 78, 16, 255, 128, 34, 197, 171 },
|
||||
{ 41, 40, 5, 102, 211, 183, 4, 1, 221 },
|
||||
{ 51, 50, 17, 168, 209, 192, 23, 25, 82 } },
|
||||
{ { 138, 31, 36, 171, 27, 166, 38, 44, 229 },
|
||||
{ 67, 87, 58, 169, 82, 115, 26, 59, 179 },
|
||||
{ 63, 59, 90, 180, 59, 166, 93, 73, 154 },
|
||||
{ 40, 40, 21, 116, 143, 209, 34, 39, 175 },
|
||||
{ 47, 15, 16, 183, 34, 223, 49, 45, 183 },
|
||||
{ 46, 17, 33, 183, 6, 98, 15, 32, 183 },
|
||||
{ 57, 46, 22, 24, 128, 1, 54, 17, 37 },
|
||||
{ 65, 32, 73, 115, 28, 128, 23, 128, 205 },
|
||||
{ 40, 3, 9, 115, 51, 192, 18, 6, 223 },
|
||||
{ 87, 37, 9, 115, 59, 77, 64, 21, 47 } },
|
||||
{ { 104, 55, 44, 218, 9, 54, 53, 130, 226 },
|
||||
{ 64, 90, 70, 205, 40, 41, 23, 26, 57 },
|
||||
{ 54, 57, 112, 184, 5, 41, 38, 166, 213 },
|
||||
{ 30, 34, 26, 133, 152, 116, 10, 32, 134 },
|
||||
{ 39, 19, 53, 221, 26, 114, 32, 73, 255 },
|
||||
{ 31, 9, 65, 234, 2, 15, 1, 118, 73 },
|
||||
{ 75, 32, 12, 51, 192, 255, 160, 43, 51 },
|
||||
{ 88, 31, 35, 67, 102, 85, 55, 186, 85 },
|
||||
{ 56, 21, 23, 111, 59, 205, 45, 37, 192 },
|
||||
{ 55, 38, 70, 124, 73, 102, 1, 34, 98 } },
|
||||
{ { 125, 98, 42, 88, 104, 85, 117, 175, 82 },
|
||||
{ 95, 84, 53, 89, 128, 100, 113, 101, 45 },
|
||||
{ 75, 79, 123, 47, 51, 128, 81, 171, 1 },
|
||||
{ 57, 17, 5, 71, 102, 57, 53, 41, 49 },
|
||||
{ 38, 33, 13, 121, 57, 73, 26, 1, 85 },
|
||||
{ 41, 10, 67, 138, 77, 110, 90, 47, 114 },
|
||||
{ 115, 21, 2, 10, 102, 255, 166, 23, 6 },
|
||||
{ 101, 29, 16, 10, 85, 128, 101, 196, 26 },
|
||||
{ 57, 18, 10, 102, 102, 213, 34, 20, 43 },
|
||||
{ 117, 20, 15, 36, 163, 128, 68, 1, 26 } },
|
||||
{ { 102, 61, 71, 37, 34, 53, 31, 243, 192 },
|
||||
{ 69, 60, 71, 38, 73, 119, 28, 222, 37 },
|
||||
{ 68, 45, 128, 34, 1, 47, 11, 245, 171 },
|
||||
{ 62, 17, 19, 70, 146, 85, 55, 62, 70 },
|
||||
{ 37, 43, 37, 154, 100, 163, 85, 160, 1 },
|
||||
{ 63, 9, 92, 136, 28, 64, 32, 201, 85 },
|
||||
{ 75, 15, 9, 9, 64, 255, 184, 119, 16 },
|
||||
{ 86, 6, 28, 5, 64, 255, 25, 248, 1 },
|
||||
{ 56, 8, 17, 132, 137, 255, 55, 116, 128 },
|
||||
{ 58, 15, 20, 82, 135, 57, 26, 121, 40 } },
|
||||
{ { 164, 50, 31, 137, 154, 133, 25, 35, 218 },
|
||||
{ 51, 103, 44, 131, 131, 123, 31, 6, 158 },
|
||||
{ 86, 40, 64, 135, 148, 224, 45, 183, 128 },
|
||||
{ 22, 26, 17, 131, 240, 154, 14, 1, 209 },
|
||||
{ 45, 16, 21, 91, 64, 222, 7, 1, 197 },
|
||||
{ 56, 21, 39, 155, 60, 138, 23, 102, 213 },
|
||||
{ 83, 12, 13, 54, 192, 255, 68, 47, 28 },
|
||||
{ 85, 26, 85, 85, 128, 128, 32, 146, 171 },
|
||||
{ 18, 11, 7, 63, 144, 171, 4, 4, 246 },
|
||||
{ 35, 27, 10, 146, 174, 171, 12, 26, 128 } },
|
||||
{ { 190, 80, 35, 99, 180, 80, 126, 54, 45 },
|
||||
{ 85, 126, 47, 87, 176, 51, 41, 20, 32 },
|
||||
{ 101, 75, 128, 139, 118, 146, 116, 128, 85 },
|
||||
{ 56, 41, 15, 176, 236, 85, 37, 9, 62 },
|
||||
{ 71, 30, 17, 119, 118, 255, 17, 18, 138 },
|
||||
{ 101, 38, 60, 138, 55, 70, 43, 26, 142 },
|
||||
{ 146, 36, 19, 30, 171, 255, 97, 27, 20 },
|
||||
{ 138, 45, 61, 62, 219, 1, 81, 188, 64 },
|
||||
{ 32, 41, 20, 117, 151, 142, 20, 21, 163 },
|
||||
{ 112, 19, 12, 61, 195, 128, 48, 4, 24 } }
|
||||
};
|
||||
|
||||
void VP8ResetProba(VP8Proba* const proba) {
|
||||
memset(proba->segments_, 255u, sizeof(proba->segments_));
|
||||
memcpy(proba->coeffs_, CoeffsProba0, sizeof(CoeffsProba0));
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
memcpy(proba->mv_, kMVProba0, sizeof(kMVProba0));
|
||||
memcpy(proba->ymode_, kYModeProbaInter0, sizeof(kYModeProbaInter0));
|
||||
memcpy(proba->uvmode_, kUVModeProbaInter0, sizeof(kUVModeProbaInter0));
|
||||
#endif
|
||||
}
|
||||
|
||||
void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
uint8_t* const top = dec->intra_t_ + 4 * dec->mb_x_;
|
||||
uint8_t* const left = dec->intra_l_;
|
||||
// Hardcoded 16x16 intra-mode decision tree.
|
||||
dec->is_i4x4_ = !VP8GetBit(br, 145); // decide for B_PRED first
|
||||
if (!dec->is_i4x4_) {
|
||||
const int ymode =
|
||||
VP8GetBit(br, 156) ? (VP8GetBit(br, 128) ? TM_PRED : H_PRED)
|
||||
: (VP8GetBit(br, 163) ? V_PRED : DC_PRED);
|
||||
dec->imodes_[0] = ymode;
|
||||
memset(top, ymode, 4 * sizeof(top[0]));
|
||||
memset(left, ymode, 4 * sizeof(left[0]));
|
||||
} else {
|
||||
uint8_t* modes = dec->imodes_;
|
||||
int y;
|
||||
for (y = 0; y < 4; ++y) {
|
||||
int ymode = left[y];
|
||||
int x;
|
||||
for (x = 0; x < 4; ++x) {
|
||||
const uint8_t* const prob = kBModesProba[top[x]][ymode];
|
||||
#ifdef USE_GENERIC_TREE
|
||||
// Generic tree-parsing
|
||||
int i = 0;
|
||||
do {
|
||||
i = kYModesIntra4[2 * i + VP8GetBit(br, prob[i])];
|
||||
} while (i > 0);
|
||||
ymode = -i;
|
||||
#else
|
||||
// Hardcoded tree parsing
|
||||
ymode = !VP8GetBit(br, prob[0]) ? B_DC_PRED :
|
||||
!VP8GetBit(br, prob[1]) ? B_TM_PRED :
|
||||
!VP8GetBit(br, prob[2]) ? B_VE_PRED :
|
||||
!VP8GetBit(br, prob[3]) ?
|
||||
(!VP8GetBit(br, prob[4]) ? B_HE_PRED :
|
||||
(!VP8GetBit(br, prob[5]) ? B_RD_PRED : B_VR_PRED)) :
|
||||
(!VP8GetBit(br, prob[6]) ? B_LD_PRED :
|
||||
(!VP8GetBit(br, prob[7]) ? B_VL_PRED :
|
||||
(!VP8GetBit(br, prob[8]) ? B_HD_PRED : B_HU_PRED)));
|
||||
#endif // USE_GENERIC_TREE
|
||||
top[x] = ymode;
|
||||
*modes++ = ymode;
|
||||
}
|
||||
left[y] = ymode;
|
||||
}
|
||||
}
|
||||
// Hardcoded UVMode decision tree
|
||||
dec->uvmode_ = !VP8GetBit(br, 142) ? DC_PRED
|
||||
: !VP8GetBit(br, 114) ? V_PRED
|
||||
: VP8GetBit(br, 183) ? TM_PRED : H_PRED;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Paragraph 13
|
||||
|
||||
static const uint8_t
|
||||
CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS] = {
|
||||
{ { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 176, 246, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 223, 241, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 249, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 244, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 234, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 246, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 239, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 253, 255, 254, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 255, 254, 255, 254, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
},
|
||||
{ { { 217, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 225, 252, 241, 253, 255, 255, 254, 255, 255, 255, 255 },
|
||||
{ 234, 250, 241, 250, 253, 255, 253, 254, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 223, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 238, 253, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 248, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 249, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 247, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 252, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
},
|
||||
{ { { 186, 251, 250, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 234, 251, 244, 254, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 251, 243, 253, 254, 255, 254, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 236, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 251, 253, 253, 254, 254, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 254, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
},
|
||||
{ { { 248, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 254, 252, 254, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 248, 254, 249, 253, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 246, 253, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 252, 254, 251, 254, 254, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 254, 252, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 248, 254, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 255, 254, 254, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 245, 251, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 253, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 251, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 252, 253, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 254, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 252, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 249, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 254, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 253, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
},
|
||||
{ { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 254, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 },
|
||||
{ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
static const uint8_t MVUpdateProba[2][NUM_MV_PROBAS] = {
|
||||
{ 237, 246, 253, 253, 254, 254, 254, 254,
|
||||
254, 254, 254, 254, 254, 254, 250, 250,
|
||||
252, 254, 254 },
|
||||
{ 231, 243, 245, 253, 254, 254, 254, 254,
|
||||
254, 254, 254, 254, 254, 254, 251, 251,
|
||||
254, 254, 254 }
|
||||
};
|
||||
#endif
|
||||
|
||||
// Paragraph 9.9
|
||||
void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec) {
|
||||
VP8Proba* const proba = &dec->proba_;
|
||||
int t, b, c, p;
|
||||
for (t = 0; t < NUM_TYPES; ++t) {
|
||||
for (b = 0; b < NUM_BANDS; ++b) {
|
||||
for (c = 0; c < NUM_CTX; ++c) {
|
||||
for (p = 0; p < NUM_PROBAS; ++p) {
|
||||
if (VP8GetBit(br, CoeffsUpdateProba[t][b][c][p])) {
|
||||
proba->coeffs_[t][b][c][p] = VP8GetValue(br, 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dec->use_skip_proba_ = VP8Get(br);
|
||||
if (dec->use_skip_proba_) {
|
||||
dec->skip_p_ = VP8GetValue(br, 8);
|
||||
}
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
if (!dec->frm_hdr_.key_frame_) {
|
||||
int i;
|
||||
dec->intra_p_ = VP8GetValue(br, 8);
|
||||
dec->last_p_ = VP8GetValue(br, 8);
|
||||
dec->golden_p_ = VP8GetValue(br, 8);
|
||||
if (VP8Get(br)) { // update y-mode
|
||||
for (i = 0; i < 4; ++i) {
|
||||
proba->ymode_[i] = VP8GetValue(br, 8);
|
||||
}
|
||||
}
|
||||
if (VP8Get(br)) { // update uv-mode
|
||||
for (i = 0; i < 3; ++i) {
|
||||
proba->uvmode_[i] = VP8GetValue(br, 8);
|
||||
}
|
||||
}
|
||||
// update MV
|
||||
for (i = 0; i < 2; ++i) {
|
||||
int k;
|
||||
for (k = 0; k < NUM_MV_PROBAS; ++k) {
|
||||
if (VP8GetBit(br, MVUpdateProba[i][k])) {
|
||||
const int v = VP8GetValue(br, 7);
|
||||
proba->mv_[i][k] = v ? v << 1 : 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
787
drivers/webpold/dec/vp8.c
Normal file
787
drivers/webpold/dec/vp8.c
Normal file
@ -0,0 +1,787 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// main entry for the decoder
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./vp8i.h"
|
||||
#include "./vp8li.h"
|
||||
#include "./webpi.h"
|
||||
#include "../utils/bit_reader.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int WebPGetDecoderVersion(void) {
|
||||
return (DEC_MAJ_VERSION << 16) | (DEC_MIN_VERSION << 8) | DEC_REV_VERSION;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8Decoder
|
||||
|
||||
static void SetOk(VP8Decoder* const dec) {
|
||||
dec->status_ = VP8_STATUS_OK;
|
||||
dec->error_msg_ = "OK";
|
||||
}
|
||||
|
||||
int VP8InitIoInternal(VP8Io* const io, int version) {
|
||||
if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DECODER_ABI_VERSION)) {
|
||||
return 0; // mismatch error
|
||||
}
|
||||
if (io != NULL) {
|
||||
memset(io, 0, sizeof(*io));
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
VP8Decoder* VP8New(void) {
|
||||
VP8Decoder* const dec = (VP8Decoder*)calloc(1, sizeof(*dec));
|
||||
if (dec != NULL) {
|
||||
SetOk(dec);
|
||||
WebPWorkerInit(&dec->worker_);
|
||||
dec->ready_ = 0;
|
||||
dec->num_parts_ = 1;
|
||||
}
|
||||
return dec;
|
||||
}
|
||||
|
||||
VP8StatusCode VP8Status(VP8Decoder* const dec) {
|
||||
if (!dec) return VP8_STATUS_INVALID_PARAM;
|
||||
return dec->status_;
|
||||
}
|
||||
|
||||
const char* VP8StatusMessage(VP8Decoder* const dec) {
|
||||
if (dec == NULL) return "no object";
|
||||
if (!dec->error_msg_) return "OK";
|
||||
return dec->error_msg_;
|
||||
}
|
||||
|
||||
void VP8Delete(VP8Decoder* const dec) {
|
||||
if (dec != NULL) {
|
||||
VP8Clear(dec);
|
||||
free(dec);
|
||||
}
|
||||
}
|
||||
|
||||
int VP8SetError(VP8Decoder* const dec,
|
||||
VP8StatusCode error, const char* const msg) {
|
||||
// TODO This check would be unnecessary if alpha decompression was separated
|
||||
// from VP8ProcessRow/FinishRow. This avoids setting 'dec->status_' to
|
||||
// something other than VP8_STATUS_BITSTREAM_ERROR on alpha decompression
|
||||
// failure.
|
||||
if (dec->status_ == VP8_STATUS_OK) {
|
||||
dec->status_ = error;
|
||||
dec->error_msg_ = msg;
|
||||
dec->ready_ = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
int VP8CheckSignature(const uint8_t* const data, size_t data_size) {
|
||||
return (data_size >= 3 &&
|
||||
data[0] == 0x9d && data[1] == 0x01 && data[2] == 0x2a);
|
||||
}
|
||||
|
||||
int VP8GetInfo(const uint8_t* data, size_t data_size, size_t chunk_size,
|
||||
int* const width, int* const height) {
|
||||
if (data == NULL || data_size < VP8_FRAME_HEADER_SIZE) {
|
||||
return 0; // not enough data
|
||||
}
|
||||
// check signature
|
||||
if (!VP8CheckSignature(data + 3, data_size - 3)) {
|
||||
return 0; // Wrong signature.
|
||||
} else {
|
||||
const uint32_t bits = data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
const int key_frame = !(bits & 1);
|
||||
const int w = ((data[7] << 8) | data[6]) & 0x3fff;
|
||||
const int h = ((data[9] << 8) | data[8]) & 0x3fff;
|
||||
|
||||
if (!key_frame) { // Not a keyframe.
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (((bits >> 1) & 7) > 3) {
|
||||
return 0; // unknown profile
|
||||
}
|
||||
if (!((bits >> 4) & 1)) {
|
||||
return 0; // first frame is invisible!
|
||||
}
|
||||
if (((bits >> 5)) >= chunk_size) { // partition_length
|
||||
return 0; // inconsistent size information.
|
||||
}
|
||||
|
||||
if (width) {
|
||||
*width = w;
|
||||
}
|
||||
if (height) {
|
||||
*height = h;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Header parsing
|
||||
|
||||
static void ResetSegmentHeader(VP8SegmentHeader* const hdr) {
|
||||
assert(hdr != NULL);
|
||||
hdr->use_segment_ = 0;
|
||||
hdr->update_map_ = 0;
|
||||
hdr->absolute_delta_ = 1;
|
||||
memset(hdr->quantizer_, 0, sizeof(hdr->quantizer_));
|
||||
memset(hdr->filter_strength_, 0, sizeof(hdr->filter_strength_));
|
||||
}
|
||||
|
||||
// Paragraph 9.3
|
||||
static int ParseSegmentHeader(VP8BitReader* br,
|
||||
VP8SegmentHeader* hdr, VP8Proba* proba) {
|
||||
assert(br != NULL);
|
||||
assert(hdr != NULL);
|
||||
hdr->use_segment_ = VP8Get(br);
|
||||
if (hdr->use_segment_) {
|
||||
hdr->update_map_ = VP8Get(br);
|
||||
if (VP8Get(br)) { // update data
|
||||
int s;
|
||||
hdr->absolute_delta_ = VP8Get(br);
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
hdr->quantizer_[s] = VP8Get(br) ? VP8GetSignedValue(br, 7) : 0;
|
||||
}
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
hdr->filter_strength_[s] = VP8Get(br) ? VP8GetSignedValue(br, 6) : 0;
|
||||
}
|
||||
}
|
||||
if (hdr->update_map_) {
|
||||
int s;
|
||||
for (s = 0; s < MB_FEATURE_TREE_PROBS; ++s) {
|
||||
proba->segments_[s] = VP8Get(br) ? VP8GetValue(br, 8) : 255u;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
hdr->update_map_ = 0;
|
||||
}
|
||||
return !br->eof_;
|
||||
}
|
||||
|
||||
// Paragraph 9.5
|
||||
// This function returns VP8_STATUS_SUSPENDED if we don't have all the
|
||||
// necessary data in 'buf'.
|
||||
// This case is not necessarily an error (for incremental decoding).
|
||||
// Still, no bitreader is ever initialized to make it possible to read
|
||||
// unavailable memory.
|
||||
// If we don't even have the partitions' sizes, than VP8_STATUS_NOT_ENOUGH_DATA
|
||||
// is returned, and this is an unrecoverable error.
|
||||
// If the partitions were positioned ok, VP8_STATUS_OK is returned.
|
||||
static VP8StatusCode ParsePartitions(VP8Decoder* const dec,
|
||||
const uint8_t* buf, size_t size) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
const uint8_t* sz = buf;
|
||||
const uint8_t* buf_end = buf + size;
|
||||
const uint8_t* part_start;
|
||||
int last_part;
|
||||
int p;
|
||||
|
||||
dec->num_parts_ = 1 << VP8GetValue(br, 2);
|
||||
last_part = dec->num_parts_ - 1;
|
||||
part_start = buf + last_part * 3;
|
||||
if (buf_end < part_start) {
|
||||
// we can't even read the sizes with sz[]! That's a failure.
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
for (p = 0; p < last_part; ++p) {
|
||||
const uint32_t psize = sz[0] | (sz[1] << 8) | (sz[2] << 16);
|
||||
const uint8_t* part_end = part_start + psize;
|
||||
if (part_end > buf_end) part_end = buf_end;
|
||||
VP8InitBitReader(dec->parts_ + p, part_start, part_end);
|
||||
part_start = part_end;
|
||||
sz += 3;
|
||||
}
|
||||
VP8InitBitReader(dec->parts_ + last_part, part_start, buf_end);
|
||||
return (part_start < buf_end) ? VP8_STATUS_OK :
|
||||
VP8_STATUS_SUSPENDED; // Init is ok, but there's not enough data
|
||||
}
|
||||
|
||||
// Paragraph 9.4
|
||||
static int ParseFilterHeader(VP8BitReader* br, VP8Decoder* const dec) {
|
||||
VP8FilterHeader* const hdr = &dec->filter_hdr_;
|
||||
hdr->simple_ = VP8Get(br);
|
||||
hdr->level_ = VP8GetValue(br, 6);
|
||||
hdr->sharpness_ = VP8GetValue(br, 3);
|
||||
hdr->use_lf_delta_ = VP8Get(br);
|
||||
if (hdr->use_lf_delta_) {
|
||||
if (VP8Get(br)) { // update lf-delta?
|
||||
int i;
|
||||
for (i = 0; i < NUM_REF_LF_DELTAS; ++i) {
|
||||
if (VP8Get(br)) {
|
||||
hdr->ref_lf_delta_[i] = VP8GetSignedValue(br, 6);
|
||||
}
|
||||
}
|
||||
for (i = 0; i < NUM_MODE_LF_DELTAS; ++i) {
|
||||
if (VP8Get(br)) {
|
||||
hdr->mode_lf_delta_[i] = VP8GetSignedValue(br, 6);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dec->filter_type_ = (hdr->level_ == 0) ? 0 : hdr->simple_ ? 1 : 2;
|
||||
if (dec->filter_type_ > 0) { // precompute filter levels per segment
|
||||
if (dec->segment_hdr_.use_segment_) {
|
||||
int s;
|
||||
for (s = 0; s < NUM_MB_SEGMENTS; ++s) {
|
||||
int strength = dec->segment_hdr_.filter_strength_[s];
|
||||
if (!dec->segment_hdr_.absolute_delta_) {
|
||||
strength += hdr->level_;
|
||||
}
|
||||
dec->filter_levels_[s] = strength;
|
||||
}
|
||||
} else {
|
||||
dec->filter_levels_[0] = hdr->level_;
|
||||
}
|
||||
}
|
||||
return !br->eof_;
|
||||
}
|
||||
|
||||
// Topmost call
|
||||
int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) {
|
||||
const uint8_t* buf;
|
||||
size_t buf_size;
|
||||
VP8FrameHeader* frm_hdr;
|
||||
VP8PictureHeader* pic_hdr;
|
||||
VP8BitReader* br;
|
||||
VP8StatusCode status;
|
||||
WebPHeaderStructure headers;
|
||||
|
||||
if (dec == NULL) {
|
||||
return 0;
|
||||
}
|
||||
SetOk(dec);
|
||||
if (io == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
||||
"null VP8Io passed to VP8GetHeaders()");
|
||||
}
|
||||
|
||||
// Process Pre-VP8 chunks.
|
||||
headers.data = io->data;
|
||||
headers.data_size = io->data_size;
|
||||
status = WebPParseHeaders(&headers);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return VP8SetError(dec, status, "Incorrect/incomplete header.");
|
||||
}
|
||||
if (headers.is_lossless) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Unexpected lossless format encountered.");
|
||||
}
|
||||
|
||||
if (dec->alpha_data_ == NULL) {
|
||||
assert(dec->alpha_data_size_ == 0);
|
||||
// We have NOT set alpha data yet. Set it now.
|
||||
// (This is to ensure that dec->alpha_data_ is NOT reset to NULL if
|
||||
// WebPParseHeaders() is called more than once, as in incremental decoding
|
||||
// case.)
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
}
|
||||
|
||||
// Process the VP8 frame header.
|
||||
buf = headers.data + headers.offset;
|
||||
buf_size = headers.data_size - headers.offset;
|
||||
assert(headers.data_size >= headers.offset); // WebPParseHeaders' guarantee
|
||||
if (buf_size < 4) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Truncated header.");
|
||||
}
|
||||
|
||||
// Paragraph 9.1
|
||||
{
|
||||
const uint32_t bits = buf[0] | (buf[1] << 8) | (buf[2] << 16);
|
||||
frm_hdr = &dec->frm_hdr_;
|
||||
frm_hdr->key_frame_ = !(bits & 1);
|
||||
frm_hdr->profile_ = (bits >> 1) & 7;
|
||||
frm_hdr->show_ = (bits >> 4) & 1;
|
||||
frm_hdr->partition_length_ = (bits >> 5);
|
||||
if (frm_hdr->profile_ > 3)
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Incorrect keyframe parameters.");
|
||||
if (!frm_hdr->show_)
|
||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
"Frame not displayable.");
|
||||
buf += 3;
|
||||
buf_size -= 3;
|
||||
}
|
||||
|
||||
pic_hdr = &dec->pic_hdr_;
|
||||
if (frm_hdr->key_frame_) {
|
||||
// Paragraph 9.2
|
||||
if (buf_size < 7) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"cannot parse picture header");
|
||||
}
|
||||
if (!VP8CheckSignature(buf, buf_size)) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"Bad code word");
|
||||
}
|
||||
pic_hdr->width_ = ((buf[4] << 8) | buf[3]) & 0x3fff;
|
||||
pic_hdr->xscale_ = buf[4] >> 6; // ratio: 1, 5/4 5/3 or 2
|
||||
pic_hdr->height_ = ((buf[6] << 8) | buf[5]) & 0x3fff;
|
||||
pic_hdr->yscale_ = buf[6] >> 6;
|
||||
buf += 7;
|
||||
buf_size -= 7;
|
||||
|
||||
dec->mb_w_ = (pic_hdr->width_ + 15) >> 4;
|
||||
dec->mb_h_ = (pic_hdr->height_ + 15) >> 4;
|
||||
// Setup default output area (can be later modified during io->setup())
|
||||
io->width = pic_hdr->width_;
|
||||
io->height = pic_hdr->height_;
|
||||
io->use_scaling = 0;
|
||||
io->use_cropping = 0;
|
||||
io->crop_top = 0;
|
||||
io->crop_left = 0;
|
||||
io->crop_right = io->width;
|
||||
io->crop_bottom = io->height;
|
||||
io->mb_w = io->width; // sanity check
|
||||
io->mb_h = io->height; // ditto
|
||||
|
||||
VP8ResetProba(&dec->proba_);
|
||||
ResetSegmentHeader(&dec->segment_hdr_);
|
||||
dec->segment_ = 0; // default for intra
|
||||
}
|
||||
|
||||
// Check if we have all the partition #0 available, and initialize dec->br_
|
||||
// to read this partition (and this partition only).
|
||||
if (frm_hdr->partition_length_ > buf_size) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"bad partition length");
|
||||
}
|
||||
|
||||
br = &dec->br_;
|
||||
VP8InitBitReader(br, buf, buf + frm_hdr->partition_length_);
|
||||
buf += frm_hdr->partition_length_;
|
||||
buf_size -= frm_hdr->partition_length_;
|
||||
|
||||
if (frm_hdr->key_frame_) {
|
||||
pic_hdr->colorspace_ = VP8Get(br);
|
||||
pic_hdr->clamp_type_ = VP8Get(br);
|
||||
}
|
||||
if (!ParseSegmentHeader(br, &dec->segment_hdr_, &dec->proba_)) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"cannot parse segment header");
|
||||
}
|
||||
// Filter specs
|
||||
if (!ParseFilterHeader(br, dec)) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"cannot parse filter header");
|
||||
}
|
||||
status = ParsePartitions(dec, buf, buf_size);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return VP8SetError(dec, status, "cannot parse partitions");
|
||||
}
|
||||
|
||||
// quantizer change
|
||||
VP8ParseQuant(dec);
|
||||
|
||||
// Frame buffer marking
|
||||
if (!frm_hdr->key_frame_) {
|
||||
// Paragraph 9.7
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
dec->buffer_flags_ = VP8Get(br) << 0; // update golden
|
||||
dec->buffer_flags_ |= VP8Get(br) << 1; // update alt ref
|
||||
if (!(dec->buffer_flags_ & 1)) {
|
||||
dec->buffer_flags_ |= VP8GetValue(br, 2) << 2;
|
||||
}
|
||||
if (!(dec->buffer_flags_ & 2)) {
|
||||
dec->buffer_flags_ |= VP8GetValue(br, 2) << 4;
|
||||
}
|
||||
dec->buffer_flags_ |= VP8Get(br) << 6; // sign bias golden
|
||||
dec->buffer_flags_ |= VP8Get(br) << 7; // sign bias alt ref
|
||||
#else
|
||||
return VP8SetError(dec, VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
"Not a key frame.");
|
||||
#endif
|
||||
} else {
|
||||
dec->buffer_flags_ = 0x003 | 0x100;
|
||||
}
|
||||
|
||||
// Paragraph 9.8
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
dec->update_proba_ = VP8Get(br);
|
||||
if (!dec->update_proba_) { // save for later restore
|
||||
dec->proba_saved_ = dec->proba_;
|
||||
}
|
||||
dec->buffer_flags_ &= 1 << 8;
|
||||
dec->buffer_flags_ |=
|
||||
(frm_hdr->key_frame_ || VP8Get(br)) << 8; // refresh last frame
|
||||
#else
|
||||
VP8Get(br); // just ignore the value of update_proba_
|
||||
#endif
|
||||
|
||||
VP8ParseProba(br, dec);
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
// Extensions
|
||||
if (dec->pic_hdr_.colorspace_) {
|
||||
const size_t kTrailerSize = 8;
|
||||
const uint8_t kTrailerMarker = 0x01;
|
||||
const uint8_t* ext_buf = buf - kTrailerSize;
|
||||
size_t size;
|
||||
|
||||
if (frm_hdr->partition_length_ < kTrailerSize ||
|
||||
ext_buf[kTrailerSize - 1] != kTrailerMarker) {
|
||||
return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR,
|
||||
"RIFF: Inconsistent extra information.");
|
||||
}
|
||||
|
||||
// Layer
|
||||
size = (ext_buf[0] << 0) | (ext_buf[1] << 8) | (ext_buf[2] << 16);
|
||||
dec->layer_data_size_ = size;
|
||||
dec->layer_data_ = NULL; // will be set later
|
||||
dec->layer_colorspace_ = ext_buf[3];
|
||||
}
|
||||
#endif
|
||||
|
||||
// sanitized state
|
||||
dec->ready_ = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Residual decoding (Paragraph 13.2 / 13.3)
|
||||
|
||||
static const uint8_t kBands[16 + 1] = {
|
||||
0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
||||
0 // extra entry as sentinel
|
||||
};
|
||||
|
||||
static const uint8_t kCat3[] = { 173, 148, 140, 0 };
|
||||
static const uint8_t kCat4[] = { 176, 155, 140, 135, 0 };
|
||||
static const uint8_t kCat5[] = { 180, 157, 141, 134, 130, 0 };
|
||||
static const uint8_t kCat6[] =
|
||||
{ 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 };
|
||||
static const uint8_t* const kCat3456[] = { kCat3, kCat4, kCat5, kCat6 };
|
||||
static const uint8_t kZigzag[16] = {
|
||||
0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15
|
||||
};
|
||||
|
||||
typedef const uint8_t (*ProbaArray)[NUM_CTX][NUM_PROBAS]; // for const-casting
|
||||
|
||||
// Returns the position of the last non-zero coeff plus one
|
||||
// (and 0 if there's no coeff at all)
|
||||
static int GetCoeffs(VP8BitReader* const br, ProbaArray prob,
|
||||
int ctx, const quant_t dq, int n, int16_t* out) {
|
||||
// n is either 0 or 1 here. kBands[n] is not necessary for extracting '*p'.
|
||||
const uint8_t* p = prob[n][ctx];
|
||||
if (!VP8GetBit(br, p[0])) { // first EOB is more a 'CBP' bit.
|
||||
return 0;
|
||||
}
|
||||
while (1) {
|
||||
++n;
|
||||
if (!VP8GetBit(br, p[1])) {
|
||||
p = prob[kBands[n]][0];
|
||||
} else { // non zero coeff
|
||||
int v, j;
|
||||
if (!VP8GetBit(br, p[2])) {
|
||||
p = prob[kBands[n]][1];
|
||||
v = 1;
|
||||
} else {
|
||||
if (!VP8GetBit(br, p[3])) {
|
||||
if (!VP8GetBit(br, p[4])) {
|
||||
v = 2;
|
||||
} else {
|
||||
v = 3 + VP8GetBit(br, p[5]);
|
||||
}
|
||||
} else {
|
||||
if (!VP8GetBit(br, p[6])) {
|
||||
if (!VP8GetBit(br, p[7])) {
|
||||
v = 5 + VP8GetBit(br, 159);
|
||||
} else {
|
||||
v = 7 + 2 * VP8GetBit(br, 165);
|
||||
v += VP8GetBit(br, 145);
|
||||
}
|
||||
} else {
|
||||
const uint8_t* tab;
|
||||
const int bit1 = VP8GetBit(br, p[8]);
|
||||
const int bit0 = VP8GetBit(br, p[9 + bit1]);
|
||||
const int cat = 2 * bit1 + bit0;
|
||||
v = 0;
|
||||
for (tab = kCat3456[cat]; *tab; ++tab) {
|
||||
v += v + VP8GetBit(br, *tab);
|
||||
}
|
||||
v += 3 + (8 << cat);
|
||||
}
|
||||
}
|
||||
p = prob[kBands[n]][2];
|
||||
}
|
||||
j = kZigzag[n - 1];
|
||||
out[j] = VP8GetSigned(br, v) * dq[j > 0];
|
||||
if (n == 16 || !VP8GetBit(br, p[0])) { // EOB
|
||||
return n;
|
||||
}
|
||||
}
|
||||
if (n == 16) {
|
||||
return 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Alias-safe way of converting 4bytes to 32bits.
|
||||
typedef union {
|
||||
uint8_t i8[4];
|
||||
uint32_t i32;
|
||||
} PackedNz;
|
||||
|
||||
// Table to unpack four bits into four bytes
|
||||
static const PackedNz kUnpackTab[16] = {
|
||||
{{0, 0, 0, 0}}, {{1, 0, 0, 0}}, {{0, 1, 0, 0}}, {{1, 1, 0, 0}},
|
||||
{{0, 0, 1, 0}}, {{1, 0, 1, 0}}, {{0, 1, 1, 0}}, {{1, 1, 1, 0}},
|
||||
{{0, 0, 0, 1}}, {{1, 0, 0, 1}}, {{0, 1, 0, 1}}, {{1, 1, 0, 1}},
|
||||
{{0, 0, 1, 1}}, {{1, 0, 1, 1}}, {{0, 1, 1, 1}}, {{1, 1, 1, 1}} };
|
||||
|
||||
// Macro to pack four LSB of four bytes into four bits.
|
||||
#if defined(__PPC__) || defined(_M_PPC) || defined(_ARCH_PPC) || \
|
||||
defined(__BIG_ENDIAN__)
|
||||
#define PACK_CST 0x08040201U
|
||||
#else
|
||||
#define PACK_CST 0x01020408U
|
||||
#endif
|
||||
#define PACK(X, S) ((((X).i32 * PACK_CST) & 0xff000000) >> (S))
|
||||
|
||||
static void ParseResiduals(VP8Decoder* const dec,
|
||||
VP8MB* const mb, VP8BitReader* const token_br) {
|
||||
int out_t_nz, out_l_nz, first;
|
||||
ProbaArray ac_prob;
|
||||
const VP8QuantMatrix* q = &dec->dqm_[dec->segment_];
|
||||
int16_t* dst = dec->coeffs_;
|
||||
VP8MB* const left_mb = dec->mb_info_ - 1;
|
||||
PackedNz nz_ac, nz_dc;
|
||||
PackedNz tnz, lnz;
|
||||
uint32_t non_zero_ac = 0;
|
||||
uint32_t non_zero_dc = 0;
|
||||
int x, y, ch;
|
||||
|
||||
nz_dc.i32 = nz_ac.i32 = 0;
|
||||
memset(dst, 0, 384 * sizeof(*dst));
|
||||
if (!dec->is_i4x4_) { // parse DC
|
||||
int16_t dc[16] = { 0 };
|
||||
const int ctx = mb->dc_nz_ + left_mb->dc_nz_;
|
||||
mb->dc_nz_ = left_mb->dc_nz_ =
|
||||
(GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[1],
|
||||
ctx, q->y2_mat_, 0, dc) > 0);
|
||||
first = 1;
|
||||
ac_prob = (ProbaArray)dec->proba_.coeffs_[0];
|
||||
VP8TransformWHT(dc, dst);
|
||||
} else {
|
||||
first = 0;
|
||||
ac_prob = (ProbaArray)dec->proba_.coeffs_[3];
|
||||
}
|
||||
|
||||
tnz = kUnpackTab[mb->nz_ & 0xf];
|
||||
lnz = kUnpackTab[left_mb->nz_ & 0xf];
|
||||
for (y = 0; y < 4; ++y) {
|
||||
int l = lnz.i8[y];
|
||||
for (x = 0; x < 4; ++x) {
|
||||
const int ctx = l + tnz.i8[x];
|
||||
const int nz = GetCoeffs(token_br, ac_prob, ctx,
|
||||
q->y1_mat_, first, dst);
|
||||
tnz.i8[x] = l = (nz > 0);
|
||||
nz_dc.i8[x] = (dst[0] != 0);
|
||||
nz_ac.i8[x] = (nz > 1);
|
||||
dst += 16;
|
||||
}
|
||||
lnz.i8[y] = l;
|
||||
non_zero_dc |= PACK(nz_dc, 24 - y * 4);
|
||||
non_zero_ac |= PACK(nz_ac, 24 - y * 4);
|
||||
}
|
||||
out_t_nz = PACK(tnz, 24);
|
||||
out_l_nz = PACK(lnz, 24);
|
||||
|
||||
tnz = kUnpackTab[mb->nz_ >> 4];
|
||||
lnz = kUnpackTab[left_mb->nz_ >> 4];
|
||||
for (ch = 0; ch < 4; ch += 2) {
|
||||
for (y = 0; y < 2; ++y) {
|
||||
int l = lnz.i8[ch + y];
|
||||
for (x = 0; x < 2; ++x) {
|
||||
const int ctx = l + tnz.i8[ch + x];
|
||||
const int nz =
|
||||
GetCoeffs(token_br, (ProbaArray)dec->proba_.coeffs_[2],
|
||||
ctx, q->uv_mat_, 0, dst);
|
||||
tnz.i8[ch + x] = l = (nz > 0);
|
||||
nz_dc.i8[y * 2 + x] = (dst[0] != 0);
|
||||
nz_ac.i8[y * 2 + x] = (nz > 1);
|
||||
dst += 16;
|
||||
}
|
||||
lnz.i8[ch + y] = l;
|
||||
}
|
||||
non_zero_dc |= PACK(nz_dc, 8 - ch * 2);
|
||||
non_zero_ac |= PACK(nz_ac, 8 - ch * 2);
|
||||
}
|
||||
out_t_nz |= PACK(tnz, 20);
|
||||
out_l_nz |= PACK(lnz, 20);
|
||||
mb->nz_ = out_t_nz;
|
||||
left_mb->nz_ = out_l_nz;
|
||||
|
||||
dec->non_zero_ac_ = non_zero_ac;
|
||||
dec->non_zero_ = non_zero_ac | non_zero_dc;
|
||||
mb->skip_ = !dec->non_zero_;
|
||||
}
|
||||
#undef PACK
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Main loop
|
||||
|
||||
int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br) {
|
||||
VP8BitReader* const br = &dec->br_;
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
VP8MB* const info = dec->mb_info_ + dec->mb_x_;
|
||||
|
||||
// Note: we don't save segment map (yet), as we don't expect
|
||||
// to decode more than 1 keyframe.
|
||||
if (dec->segment_hdr_.update_map_) {
|
||||
// Hardcoded tree parsing
|
||||
dec->segment_ = !VP8GetBit(br, dec->proba_.segments_[0]) ?
|
||||
VP8GetBit(br, dec->proba_.segments_[1]) :
|
||||
2 + VP8GetBit(br, dec->proba_.segments_[2]);
|
||||
}
|
||||
info->skip_ = dec->use_skip_proba_ ? VP8GetBit(br, dec->skip_p_) : 0;
|
||||
|
||||
VP8ParseIntraMode(br, dec);
|
||||
if (br->eof_) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!info->skip_) {
|
||||
ParseResiduals(dec, info, token_br);
|
||||
} else {
|
||||
left->nz_ = info->nz_ = 0;
|
||||
if (!dec->is_i4x4_) {
|
||||
left->dc_nz_ = info->dc_nz_ = 0;
|
||||
}
|
||||
dec->non_zero_ = 0;
|
||||
dec->non_zero_ac_ = 0;
|
||||
}
|
||||
|
||||
return (!token_br->eof_);
|
||||
}
|
||||
|
||||
void VP8InitScanline(VP8Decoder* const dec) {
|
||||
VP8MB* const left = dec->mb_info_ - 1;
|
||||
left->nz_ = 0;
|
||||
left->dc_nz_ = 0;
|
||||
memset(dec->intra_l_, B_DC_PRED, sizeof(dec->intra_l_));
|
||||
dec->filter_row_ =
|
||||
(dec->filter_type_ > 0) &&
|
||||
(dec->mb_y_ >= dec->tl_mb_y_) && (dec->mb_y_ <= dec->br_mb_y_);
|
||||
}
|
||||
|
||||
static int ParseFrame(VP8Decoder* const dec, VP8Io* io) {
|
||||
for (dec->mb_y_ = 0; dec->mb_y_ < dec->br_mb_y_; ++dec->mb_y_) {
|
||||
VP8BitReader* const token_br =
|
||||
&dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)];
|
||||
VP8InitScanline(dec);
|
||||
for (dec->mb_x_ = 0; dec->mb_x_ < dec->mb_w_; dec->mb_x_++) {
|
||||
if (!VP8DecodeMB(dec, token_br)) {
|
||||
return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA,
|
||||
"Premature end-of-file encountered.");
|
||||
}
|
||||
VP8ReconstructBlock(dec);
|
||||
|
||||
// Store data and save block's filtering params
|
||||
VP8StoreBlock(dec);
|
||||
}
|
||||
if (!VP8ProcessRow(dec, io)) {
|
||||
return VP8SetError(dec, VP8_STATUS_USER_ABORT, "Output aborted.");
|
||||
}
|
||||
}
|
||||
if (dec->use_threads_ && !WebPWorkerSync(&dec->worker_)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Finish
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
if (!dec->update_proba_) {
|
||||
dec->proba_ = dec->proba_saved_;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef WEBP_EXPERIMENTAL_FEATURES
|
||||
if (dec->layer_data_size_ > 0) {
|
||||
if (!VP8DecodeLayer(dec)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Main entry point
|
||||
int VP8Decode(VP8Decoder* const dec, VP8Io* const io) {
|
||||
int ok = 0;
|
||||
if (dec == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (io == NULL) {
|
||||
return VP8SetError(dec, VP8_STATUS_INVALID_PARAM,
|
||||
"NULL VP8Io parameter in VP8Decode().");
|
||||
}
|
||||
|
||||
if (!dec->ready_) {
|
||||
if (!VP8GetHeaders(dec, io)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
assert(dec->ready_);
|
||||
|
||||
// Finish setting up the decoding parameter. Will call io->setup().
|
||||
ok = (VP8EnterCritical(dec, io) == VP8_STATUS_OK);
|
||||
if (ok) { // good to go.
|
||||
// Will allocate memory and prepare everything.
|
||||
if (ok) ok = VP8InitFrame(dec, io);
|
||||
|
||||
// Main decoding loop
|
||||
if (ok) ok = ParseFrame(dec, io);
|
||||
|
||||
// Exit.
|
||||
ok &= VP8ExitCritical(dec, io);
|
||||
}
|
||||
|
||||
if (!ok) {
|
||||
VP8Clear(dec);
|
||||
return 0;
|
||||
}
|
||||
|
||||
dec->ready_ = 0;
|
||||
return ok;
|
||||
}
|
||||
|
||||
void VP8Clear(VP8Decoder* const dec) {
|
||||
if (dec == NULL) {
|
||||
return;
|
||||
}
|
||||
if (dec->use_threads_) {
|
||||
WebPWorkerEnd(&dec->worker_);
|
||||
}
|
||||
if (dec->mem_) {
|
||||
free(dec->mem_);
|
||||
}
|
||||
dec->mem_ = NULL;
|
||||
dec->mem_size_ = 0;
|
||||
memset(&dec->br_, 0, sizeof(dec->br_));
|
||||
dec->ready_ = 0;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
335
drivers/webpold/dec/vp8i.h
Normal file
335
drivers/webpold/dec/vp8i.h
Normal file
@ -0,0 +1,335 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// VP8 decoder: internal header.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_DEC_VP8I_H_
|
||||
#define WEBP_DEC_VP8I_H_
|
||||
|
||||
#include <string.h> // for memcpy()
|
||||
#include "./vp8li.h"
|
||||
#include "../utils/bit_reader.h"
|
||||
#include "../utils/thread.h"
|
||||
#include "../dsp/dsp.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Various defines and enums
|
||||
|
||||
// version numbers
|
||||
#define DEC_MAJ_VERSION 0
|
||||
#define DEC_MIN_VERSION 2
|
||||
#define DEC_REV_VERSION 0
|
||||
|
||||
#define ONLY_KEYFRAME_CODE // to remove any code related to P-Frames
|
||||
|
||||
// intra prediction modes
|
||||
enum { B_DC_PRED = 0, // 4x4 modes
|
||||
B_TM_PRED,
|
||||
B_VE_PRED,
|
||||
B_HE_PRED,
|
||||
B_RD_PRED,
|
||||
B_VR_PRED,
|
||||
B_LD_PRED,
|
||||
B_VL_PRED,
|
||||
B_HD_PRED,
|
||||
B_HU_PRED,
|
||||
NUM_BMODES = B_HU_PRED + 1 - B_DC_PRED, // = 10
|
||||
|
||||
// Luma16 or UV modes
|
||||
DC_PRED = B_DC_PRED, V_PRED = B_VE_PRED,
|
||||
H_PRED = B_HE_PRED, TM_PRED = B_TM_PRED,
|
||||
B_PRED = NUM_BMODES, // refined I4x4 mode
|
||||
|
||||
// special modes
|
||||
B_DC_PRED_NOTOP = 4,
|
||||
B_DC_PRED_NOLEFT = 5,
|
||||
B_DC_PRED_NOTOPLEFT = 6,
|
||||
NUM_B_DC_MODES = 7 };
|
||||
|
||||
enum { MB_FEATURE_TREE_PROBS = 3,
|
||||
NUM_MB_SEGMENTS = 4,
|
||||
NUM_REF_LF_DELTAS = 4,
|
||||
NUM_MODE_LF_DELTAS = 4, // I4x4, ZERO, *, SPLIT
|
||||
MAX_NUM_PARTITIONS = 8,
|
||||
// Probabilities
|
||||
NUM_TYPES = 4,
|
||||
NUM_BANDS = 8,
|
||||
NUM_CTX = 3,
|
||||
NUM_PROBAS = 11,
|
||||
NUM_MV_PROBAS = 19 };
|
||||
|
||||
// YUV-cache parameters.
|
||||
// Constraints are: We need to store one 16x16 block of luma samples (y),
|
||||
// and two 8x8 chroma blocks (u/v). These are better be 16-bytes aligned,
|
||||
// in order to be SIMD-friendly. We also need to store the top, left and
|
||||
// top-left samples (from previously decoded blocks), along with four
|
||||
// extra top-right samples for luma (intra4x4 prediction only).
|
||||
// One possible layout is, using 32 * (17 + 9) bytes:
|
||||
//
|
||||
// .+------ <- only 1 pixel high
|
||||
// .|yyyyt.
|
||||
// .|yyyyt.
|
||||
// .|yyyyt.
|
||||
// .|yyyy..
|
||||
// .+--.+-- <- only 1 pixel high
|
||||
// .|uu.|vv
|
||||
// .|uu.|vv
|
||||
//
|
||||
// Every character is a 4x4 block, with legend:
|
||||
// '.' = unused
|
||||
// 'y' = y-samples 'u' = u-samples 'v' = u-samples
|
||||
// '|' = left sample, '-' = top sample, '+' = top-left sample
|
||||
// 't' = extra top-right sample for 4x4 modes
|
||||
// With this layout, BPS (=Bytes Per Scan-line) is one cacheline size.
|
||||
#define BPS 32 // this is the common stride used by yuv[]
|
||||
#define YUV_SIZE (BPS * 17 + BPS * 9)
|
||||
#define Y_SIZE (BPS * 17)
|
||||
#define Y_OFF (BPS * 1 + 8)
|
||||
#define U_OFF (Y_OFF + BPS * 16 + BPS)
|
||||
#define V_OFF (U_OFF + 16)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Headers
|
||||
|
||||
typedef struct {
|
||||
uint8_t key_frame_;
|
||||
uint8_t profile_;
|
||||
uint8_t show_;
|
||||
uint32_t partition_length_;
|
||||
} VP8FrameHeader;
|
||||
|
||||
typedef struct {
|
||||
uint16_t width_;
|
||||
uint16_t height_;
|
||||
uint8_t xscale_;
|
||||
uint8_t yscale_;
|
||||
uint8_t colorspace_; // 0 = YCbCr
|
||||
uint8_t clamp_type_;
|
||||
} VP8PictureHeader;
|
||||
|
||||
// segment features
|
||||
typedef struct {
|
||||
int use_segment_;
|
||||
int update_map_; // whether to update the segment map or not
|
||||
int absolute_delta_; // absolute or delta values for quantizer and filter
|
||||
int8_t quantizer_[NUM_MB_SEGMENTS]; // quantization changes
|
||||
int8_t filter_strength_[NUM_MB_SEGMENTS]; // filter strength for segments
|
||||
} VP8SegmentHeader;
|
||||
|
||||
// Struct collecting all frame-persistent probabilities.
|
||||
typedef struct {
|
||||
uint8_t segments_[MB_FEATURE_TREE_PROBS];
|
||||
// Type: 0:Intra16-AC 1:Intra16-DC 2:Chroma 3:Intra4
|
||||
uint8_t coeffs_[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS];
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
uint8_t ymode_[4], uvmode_[3];
|
||||
uint8_t mv_[2][NUM_MV_PROBAS];
|
||||
#endif
|
||||
} VP8Proba;
|
||||
|
||||
// Filter parameters
|
||||
typedef struct {
|
||||
int simple_; // 0=complex, 1=simple
|
||||
int level_; // [0..63]
|
||||
int sharpness_; // [0..7]
|
||||
int use_lf_delta_;
|
||||
int ref_lf_delta_[NUM_REF_LF_DELTAS];
|
||||
int mode_lf_delta_[NUM_MODE_LF_DELTAS];
|
||||
} VP8FilterHeader;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Informations about the macroblocks.
|
||||
|
||||
typedef struct { // filter specs
|
||||
unsigned int f_level_:6; // filter strength: 0..63
|
||||
unsigned int f_ilevel_:6; // inner limit: 1..63
|
||||
unsigned int f_inner_:1; // do inner filtering?
|
||||
} VP8FInfo;
|
||||
|
||||
typedef struct { // used for syntax-parsing
|
||||
unsigned int nz_; // non-zero AC/DC coeffs
|
||||
unsigned int dc_nz_:1; // non-zero DC coeffs
|
||||
unsigned int skip_:1; // block type
|
||||
} VP8MB;
|
||||
|
||||
// Dequantization matrices
|
||||
typedef int quant_t[2]; // [DC / AC]. Can be 'uint16_t[2]' too (~slower).
|
||||
typedef struct {
|
||||
quant_t y1_mat_, y2_mat_, uv_mat_;
|
||||
} VP8QuantMatrix;
|
||||
|
||||
// Persistent information needed by the parallel processing
|
||||
typedef struct {
|
||||
int id_; // cache row to process (in [0..2])
|
||||
int mb_y_; // macroblock position of the row
|
||||
int filter_row_; // true if row-filtering is needed
|
||||
VP8FInfo* f_info_; // filter strengths
|
||||
VP8Io io_; // copy of the VP8Io to pass to put()
|
||||
} VP8ThreadContext;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// VP8Decoder: the main opaque structure handed over to user
|
||||
|
||||
struct VP8Decoder {
|
||||
VP8StatusCode status_;
|
||||
int ready_; // true if ready to decode a picture with VP8Decode()
|
||||
const char* error_msg_; // set when status_ is not OK.
|
||||
|
||||
// Main data source
|
||||
VP8BitReader br_;
|
||||
|
||||
// headers
|
||||
VP8FrameHeader frm_hdr_;
|
||||
VP8PictureHeader pic_hdr_;
|
||||
VP8FilterHeader filter_hdr_;
|
||||
VP8SegmentHeader segment_hdr_;
|
||||
|
||||
// Worker
|
||||
WebPWorker worker_;
|
||||
int use_threads_; // use multi-thread
|
||||
int cache_id_; // current cache row
|
||||
int num_caches_; // number of cached rows of 16 pixels (1, 2 or 3)
|
||||
VP8ThreadContext thread_ctx_; // Thread context
|
||||
|
||||
// dimension, in macroblock units.
|
||||
int mb_w_, mb_h_;
|
||||
|
||||
// Macroblock to process/filter, depending on cropping and filter_type.
|
||||
int tl_mb_x_, tl_mb_y_; // top-left MB that must be in-loop filtered
|
||||
int br_mb_x_, br_mb_y_; // last bottom-right MB that must be decoded
|
||||
|
||||
// number of partitions.
|
||||
int num_parts_;
|
||||
// per-partition boolean decoders.
|
||||
VP8BitReader parts_[MAX_NUM_PARTITIONS];
|
||||
|
||||
// buffer refresh flags
|
||||
// bit 0: refresh Gold, bit 1: refresh Alt
|
||||
// bit 2-3: copy to Gold, bit 4-5: copy to Alt
|
||||
// bit 6: Gold sign bias, bit 7: Alt sign bias
|
||||
// bit 8: refresh last frame
|
||||
uint32_t buffer_flags_;
|
||||
|
||||
// dequantization (one set of DC/AC dequant factor per segment)
|
||||
VP8QuantMatrix dqm_[NUM_MB_SEGMENTS];
|
||||
|
||||
// probabilities
|
||||
VP8Proba proba_;
|
||||
int use_skip_proba_;
|
||||
uint8_t skip_p_;
|
||||
#ifndef ONLY_KEYFRAME_CODE
|
||||
uint8_t intra_p_, last_p_, golden_p_;
|
||||
VP8Proba proba_saved_;
|
||||
int update_proba_;
|
||||
#endif
|
||||
|
||||
// Boundary data cache and persistent buffers.
|
||||
uint8_t* intra_t_; // top intra modes values: 4 * mb_w_
|
||||
uint8_t intra_l_[4]; // left intra modes values
|
||||
uint8_t* y_t_; // top luma samples: 16 * mb_w_
|
||||
uint8_t* u_t_, *v_t_; // top u/v samples: 8 * mb_w_ each
|
||||
|
||||
VP8MB* mb_info_; // contextual macroblock info (mb_w_ + 1)
|
||||
VP8FInfo* f_info_; // filter strength info
|
||||
uint8_t* yuv_b_; // main block for Y/U/V (size = YUV_SIZE)
|
||||
int16_t* coeffs_; // 384 coeffs = (16+8+8) * 4*4
|
||||
|
||||
uint8_t* cache_y_; // macroblock row for storing unfiltered samples
|
||||
uint8_t* cache_u_;
|
||||
uint8_t* cache_v_;
|
||||
int cache_y_stride_;
|
||||
int cache_uv_stride_;
|
||||
|
||||
// main memory chunk for the above data. Persistent.
|
||||
void* mem_;
|
||||
size_t mem_size_;
|
||||
|
||||
// Per macroblock non-persistent infos.
|
||||
int mb_x_, mb_y_; // current position, in macroblock units
|
||||
uint8_t is_i4x4_; // true if intra4x4
|
||||
uint8_t imodes_[16]; // one 16x16 mode (#0) or sixteen 4x4 modes
|
||||
uint8_t uvmode_; // chroma prediction mode
|
||||
uint8_t segment_; // block's segment
|
||||
|
||||
// bit-wise info about the content of each sub-4x4 blocks: there are 16 bits
|
||||
// for luma (bits #0->#15), then 4 bits for chroma-u (#16->#19) and 4 bits for
|
||||
// chroma-v (#20->#23), each corresponding to one 4x4 block in decoding order.
|
||||
// If the bit is set, the 4x4 block contains some non-zero coefficients.
|
||||
uint32_t non_zero_;
|
||||
uint32_t non_zero_ac_;
|
||||
|
||||
// Filtering side-info
|
||||
int filter_type_; // 0=off, 1=simple, 2=complex
|
||||
int filter_row_; // per-row flag
|
||||
uint8_t filter_levels_[NUM_MB_SEGMENTS]; // precalculated per-segment
|
||||
|
||||
// extensions
|
||||
const uint8_t* alpha_data_; // compressed alpha data (if present)
|
||||
size_t alpha_data_size_;
|
||||
uint8_t* alpha_plane_; // output. Persistent, contains the whole data.
|
||||
|
||||
int layer_colorspace_;
|
||||
const uint8_t* layer_data_; // compressed layer data (if present)
|
||||
size_t layer_data_size_;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// internal functions. Not public.
|
||||
|
||||
// in vp8.c
|
||||
int VP8SetError(VP8Decoder* const dec,
|
||||
VP8StatusCode error, const char* const msg);
|
||||
|
||||
// in tree.c
|
||||
void VP8ResetProba(VP8Proba* const proba);
|
||||
void VP8ParseProba(VP8BitReader* const br, VP8Decoder* const dec);
|
||||
void VP8ParseIntraMode(VP8BitReader* const br, VP8Decoder* const dec);
|
||||
|
||||
// in quant.c
|
||||
void VP8ParseQuant(VP8Decoder* const dec);
|
||||
|
||||
// in frame.c
|
||||
int VP8InitFrame(VP8Decoder* const dec, VP8Io* io);
|
||||
// Predict a block and add residual
|
||||
void VP8ReconstructBlock(VP8Decoder* const dec);
|
||||
// Call io->setup() and finish setting up scan parameters.
|
||||
// After this call returns, one must always call VP8ExitCritical() with the
|
||||
// same parameters. Both functions should be used in pair. Returns VP8_STATUS_OK
|
||||
// if ok, otherwise sets and returns the error status on *dec.
|
||||
VP8StatusCode VP8EnterCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Must always be called in pair with VP8EnterCritical().
|
||||
// Returns false in case of error.
|
||||
int VP8ExitCritical(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Process the last decoded row (filtering + output)
|
||||
int VP8ProcessRow(VP8Decoder* const dec, VP8Io* const io);
|
||||
// Store a block, along with filtering params
|
||||
void VP8StoreBlock(VP8Decoder* const dec);
|
||||
// To be called at the start of a new scanline, to initialize predictors.
|
||||
void VP8InitScanline(VP8Decoder* const dec);
|
||||
// Decode one macroblock. Returns false if there is not enough data.
|
||||
int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br);
|
||||
|
||||
// in alpha.c
|
||||
const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec,
|
||||
int row, int num_rows);
|
||||
|
||||
// in layer.c
|
||||
int VP8DecodeLayer(VP8Decoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_DEC_VP8I_H_ */
|
||||
1200
drivers/webpold/dec/vp8l.c
Normal file
1200
drivers/webpold/dec/vp8l.c
Normal file
File diff suppressed because it is too large
Load Diff
121
drivers/webpold/dec/vp8li.h
Normal file
121
drivers/webpold/dec/vp8li.h
Normal file
@ -0,0 +1,121 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Lossless decoder: internal header.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
// Vikas Arora(vikaas.arora@gmail.com)
|
||||
|
||||
#ifndef WEBP_DEC_VP8LI_H_
|
||||
#define WEBP_DEC_VP8LI_H_
|
||||
|
||||
#include <string.h> // for memcpy()
|
||||
#include "./webpi.h"
|
||||
#include "../utils/bit_reader.h"
|
||||
#include "../utils/color_cache.h"
|
||||
#include "../utils/huffman.h"
|
||||
#include "../format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
READ_DATA = 0,
|
||||
READ_HDR = 1,
|
||||
READ_DIM = 2
|
||||
} VP8LDecodeState;
|
||||
|
||||
typedef struct VP8LTransform VP8LTransform;
|
||||
struct VP8LTransform {
|
||||
VP8LImageTransformType type_; // transform type.
|
||||
int bits_; // subsampling bits defining transform window.
|
||||
int xsize_; // transform window X index.
|
||||
int ysize_; // transform window Y index.
|
||||
uint32_t *data_; // transform data.
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
HuffmanTree htrees_[HUFFMAN_CODES_PER_META_CODE];
|
||||
} HTreeGroup;
|
||||
|
||||
typedef struct {
|
||||
int color_cache_size_;
|
||||
VP8LColorCache color_cache_;
|
||||
|
||||
int huffman_mask_;
|
||||
int huffman_subsample_bits_;
|
||||
int huffman_xsize_;
|
||||
uint32_t *huffman_image_;
|
||||
int num_htree_groups_;
|
||||
HTreeGroup *htree_groups_;
|
||||
} VP8LMetadata;
|
||||
|
||||
typedef struct {
|
||||
VP8StatusCode status_;
|
||||
VP8LDecodeState action_;
|
||||
VP8LDecodeState state_;
|
||||
VP8Io *io_;
|
||||
|
||||
const WebPDecBuffer *output_; // shortcut to io->opaque->output
|
||||
|
||||
uint32_t *argb_; // Internal data: always in BGRA color mode.
|
||||
uint32_t *argb_cache_; // Scratch buffer for temporary BGRA storage.
|
||||
|
||||
VP8LBitReader br_;
|
||||
|
||||
int width_;
|
||||
int height_;
|
||||
int last_row_; // last input row decoded so far.
|
||||
int last_out_row_; // last row output so far.
|
||||
|
||||
VP8LMetadata hdr_;
|
||||
|
||||
int next_transform_;
|
||||
VP8LTransform transforms_[NUM_TRANSFORMS];
|
||||
// or'd bitset storing the transforms types.
|
||||
uint32_t transforms_seen_;
|
||||
|
||||
uint8_t *rescaler_memory; // Working memory for rescaling work.
|
||||
WebPRescaler *rescaler; // Common rescaler for all channels.
|
||||
} VP8LDecoder;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// internal functions. Not public.
|
||||
|
||||
// in vp8l.c
|
||||
|
||||
// Decodes a raw image stream (without header) and store the alpha data
|
||||
// into *output, which must be of size width x height. Returns false in case
|
||||
// of error.
|
||||
int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data,
|
||||
size_t data_size, uint8_t* const output);
|
||||
|
||||
// Allocates and initialize a new lossless decoder instance.
|
||||
VP8LDecoder* VP8LNew(void);
|
||||
|
||||
// Decodes the image header. Returns false in case of error.
|
||||
int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io);
|
||||
|
||||
// Decodes an image. It's required to decode the lossless header before calling
|
||||
// this function. Returns false in case of error, with updated dec->status_.
|
||||
int VP8LDecodeImage(VP8LDecoder* const dec);
|
||||
|
||||
// Resets the decoder in its initial state, reclaiming memory.
|
||||
// Preserves the dec->status_ value.
|
||||
void VP8LClear(VP8LDecoder* const dec);
|
||||
|
||||
// Clears and deallocate a lossless decoder instance.
|
||||
void VP8LDelete(VP8LDecoder* const dec);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_DEC_VP8LI_H_ */
|
||||
771
drivers/webpold/dec/webp.c
Normal file
771
drivers/webpold/dec/webp.c
Normal file
@ -0,0 +1,771 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Main decoding functions for WEBP images.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "./vp8i.h"
|
||||
#include "./vp8li.h"
|
||||
#include "./webpi.h"
|
||||
#include "../format_constants.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// RIFF layout is:
|
||||
// Offset tag
|
||||
// 0...3 "RIFF" 4-byte tag
|
||||
// 4...7 size of image data (including metadata) starting at offset 8
|
||||
// 8...11 "WEBP" our form-type signature
|
||||
// The RIFF container (12 bytes) is followed by appropriate chunks:
|
||||
// 12..15 "VP8 ": 4-bytes tags, signaling the use of VP8 video format
|
||||
// 16..19 size of the raw VP8 image data, starting at offset 20
|
||||
// 20.... the VP8 bytes
|
||||
// Or,
|
||||
// 12..15 "VP8L": 4-bytes tags, signaling the use of VP8L lossless format
|
||||
// 16..19 size of the raw VP8L image data, starting at offset 20
|
||||
// 20.... the VP8L bytes
|
||||
// Or,
|
||||
// 12..15 "VP8X": 4-bytes tags, describing the extended-VP8 chunk.
|
||||
// 16..19 size of the VP8X chunk starting at offset 20.
|
||||
// 20..23 VP8X flags bit-map corresponding to the chunk-types present.
|
||||
// 24..26 Width of the Canvas Image.
|
||||
// 27..29 Height of the Canvas Image.
|
||||
// There can be extra chunks after the "VP8X" chunk (ICCP, TILE, FRM, VP8,
|
||||
// META ...)
|
||||
// All sizes are in little-endian order.
|
||||
// Note: chunk data size must be padded to multiple of 2 when written.
|
||||
|
||||
static WEBP_INLINE uint32_t get_le24(const uint8_t* const data) {
|
||||
return data[0] | (data[1] << 8) | (data[2] << 16);
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint32_t get_le32(const uint8_t* const data) {
|
||||
return (uint32_t)get_le24(data) | (data[3] << 24);
|
||||
}
|
||||
|
||||
// Validates the RIFF container (if detected) and skips over it.
|
||||
// If a RIFF container is detected,
|
||||
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid header, and
|
||||
// VP8_STATUS_OK otherwise.
|
||||
// In case there are not enough bytes (partial RIFF container), return 0 for
|
||||
// *riff_size. Else return the RIFF size extracted from the header.
|
||||
static VP8StatusCode ParseRIFF(const uint8_t** const data,
|
||||
size_t* const data_size,
|
||||
size_t* const riff_size) {
|
||||
assert(data != NULL);
|
||||
assert(data_size != NULL);
|
||||
assert(riff_size != NULL);
|
||||
|
||||
*riff_size = 0; // Default: no RIFF present.
|
||||
if (*data_size >= RIFF_HEADER_SIZE && !memcmp(*data, "RIFF", TAG_SIZE)) {
|
||||
if (memcmp(*data + 8, "WEBP", TAG_SIZE)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Wrong image file signature.
|
||||
} else {
|
||||
const uint32_t size = get_le32(*data + TAG_SIZE);
|
||||
// Check that we have at least one chunk (i.e "WEBP" + "VP8?nnnn").
|
||||
if (size < TAG_SIZE + CHUNK_HEADER_SIZE) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
// We have a RIFF container. Skip it.
|
||||
*riff_size = size;
|
||||
*data += RIFF_HEADER_SIZE;
|
||||
*data_size -= RIFF_HEADER_SIZE;
|
||||
}
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
// Validates the VP8X header and skips over it.
|
||||
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid VP8X header,
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
|
||||
// VP8_STATUS_OK otherwise.
|
||||
// If a VP8X chunk is found, found_vp8x is set to true and *width_ptr,
|
||||
// *height_ptr and *flags_ptr are set to the corresponding values extracted
|
||||
// from the VP8X chunk.
|
||||
static VP8StatusCode ParseVP8X(const uint8_t** const data,
|
||||
size_t* const data_size,
|
||||
int* const found_vp8x,
|
||||
int* const width_ptr, int* const height_ptr,
|
||||
uint32_t* const flags_ptr) {
|
||||
const uint32_t vp8x_size = CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE;
|
||||
assert(data != NULL);
|
||||
assert(data_size != NULL);
|
||||
assert(found_vp8x != NULL);
|
||||
|
||||
*found_vp8x = 0;
|
||||
|
||||
if (*data_size < CHUNK_HEADER_SIZE) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
|
||||
}
|
||||
|
||||
if (!memcmp(*data, "VP8X", TAG_SIZE)) {
|
||||
int width, height;
|
||||
uint32_t flags;
|
||||
const uint32_t chunk_size = get_le32(*data + TAG_SIZE);
|
||||
if (chunk_size != VP8X_CHUNK_SIZE) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Wrong chunk size.
|
||||
}
|
||||
|
||||
// Verify if enough data is available to validate the VP8X chunk.
|
||||
if (*data_size < vp8x_size) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
|
||||
}
|
||||
flags = get_le32(*data + 8);
|
||||
width = 1 + get_le24(*data + 12);
|
||||
height = 1 + get_le24(*data + 15);
|
||||
if (width * (uint64_t)height >= MAX_IMAGE_AREA) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // image is too large
|
||||
}
|
||||
|
||||
if (flags_ptr != NULL) *flags_ptr = flags;
|
||||
if (width_ptr != NULL) *width_ptr = width;
|
||||
if (height_ptr != NULL) *height_ptr = height;
|
||||
// Skip over VP8X header bytes.
|
||||
*data += vp8x_size;
|
||||
*data_size -= vp8x_size;
|
||||
*found_vp8x = 1;
|
||||
}
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
// Skips to the next VP8/VP8L chunk header in the data given the size of the
|
||||
// RIFF chunk 'riff_size'.
|
||||
// Returns VP8_STATUS_BITSTREAM_ERROR if any invalid chunk size is encountered,
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
|
||||
// VP8_STATUS_OK otherwise.
|
||||
// If an alpha chunk is found, *alpha_data and *alpha_size are set
|
||||
// appropriately.
|
||||
static VP8StatusCode ParseOptionalChunks(const uint8_t** const data,
|
||||
size_t* const data_size,
|
||||
size_t const riff_size,
|
||||
const uint8_t** const alpha_data,
|
||||
size_t* const alpha_size) {
|
||||
const uint8_t* buf;
|
||||
size_t buf_size;
|
||||
uint32_t total_size = TAG_SIZE + // "WEBP".
|
||||
CHUNK_HEADER_SIZE + // "VP8Xnnnn".
|
||||
VP8X_CHUNK_SIZE; // data.
|
||||
assert(data != NULL);
|
||||
assert(data_size != NULL);
|
||||
buf = *data;
|
||||
buf_size = *data_size;
|
||||
|
||||
assert(alpha_data != NULL);
|
||||
assert(alpha_size != NULL);
|
||||
*alpha_data = NULL;
|
||||
*alpha_size = 0;
|
||||
|
||||
while (1) {
|
||||
uint32_t chunk_size;
|
||||
uint32_t disk_chunk_size; // chunk_size with padding
|
||||
|
||||
*data = buf;
|
||||
*data_size = buf_size;
|
||||
|
||||
if (buf_size < CHUNK_HEADER_SIZE) { // Insufficient data.
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
|
||||
chunk_size = get_le32(buf + TAG_SIZE);
|
||||
// For odd-sized chunk-payload, there's one byte padding at the end.
|
||||
disk_chunk_size = (CHUNK_HEADER_SIZE + chunk_size + 1) & ~1;
|
||||
total_size += disk_chunk_size;
|
||||
|
||||
// Check that total bytes skipped so far does not exceed riff_size.
|
||||
if (riff_size > 0 && (total_size > riff_size)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Not a valid chunk size.
|
||||
}
|
||||
|
||||
if (buf_size < disk_chunk_size) { // Insufficient data.
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
|
||||
if (!memcmp(buf, "ALPH", TAG_SIZE)) { // A valid ALPH header.
|
||||
*alpha_data = buf + CHUNK_HEADER_SIZE;
|
||||
*alpha_size = chunk_size;
|
||||
} else if (!memcmp(buf, "VP8 ", TAG_SIZE) ||
|
||||
!memcmp(buf, "VP8L", TAG_SIZE)) { // A valid VP8/VP8L header.
|
||||
return VP8_STATUS_OK; // Found.
|
||||
}
|
||||
|
||||
// We have a full and valid chunk; skip it.
|
||||
buf += disk_chunk_size;
|
||||
buf_size -= disk_chunk_size;
|
||||
}
|
||||
}
|
||||
|
||||
// Validates the VP8/VP8L Header ("VP8 nnnn" or "VP8L nnnn") and skips over it.
|
||||
// Returns VP8_STATUS_BITSTREAM_ERROR for invalid (chunk larger than
|
||||
// riff_size) VP8/VP8L header,
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA in case of insufficient data, and
|
||||
// VP8_STATUS_OK otherwise.
|
||||
// If a VP8/VP8L chunk is found, *chunk_size is set to the total number of bytes
|
||||
// extracted from the VP8/VP8L chunk header.
|
||||
// The flag '*is_lossless' is set to 1 in case of VP8L chunk / raw VP8L data.
|
||||
static VP8StatusCode ParseVP8Header(const uint8_t** const data_ptr,
|
||||
size_t* const data_size,
|
||||
size_t riff_size,
|
||||
size_t* const chunk_size,
|
||||
int* const is_lossless) {
|
||||
const uint8_t* const data = *data_ptr;
|
||||
const int is_vp8 = !memcmp(data, "VP8 ", TAG_SIZE);
|
||||
const int is_vp8l = !memcmp(data, "VP8L", TAG_SIZE);
|
||||
const uint32_t minimal_size =
|
||||
TAG_SIZE + CHUNK_HEADER_SIZE; // "WEBP" + "VP8 nnnn" OR
|
||||
// "WEBP" + "VP8Lnnnn"
|
||||
assert(data != NULL);
|
||||
assert(data_size != NULL);
|
||||
assert(chunk_size != NULL);
|
||||
assert(is_lossless != NULL);
|
||||
|
||||
if (*data_size < CHUNK_HEADER_SIZE) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA; // Insufficient data.
|
||||
}
|
||||
|
||||
if (is_vp8 || is_vp8l) {
|
||||
// Bitstream contains VP8/VP8L header.
|
||||
const uint32_t size = get_le32(data + TAG_SIZE);
|
||||
if ((riff_size >= minimal_size) && (size > riff_size - minimal_size)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Inconsistent size information.
|
||||
}
|
||||
// Skip over CHUNK_HEADER_SIZE bytes from VP8/VP8L Header.
|
||||
*chunk_size = size;
|
||||
*data_ptr += CHUNK_HEADER_SIZE;
|
||||
*data_size -= CHUNK_HEADER_SIZE;
|
||||
*is_lossless = is_vp8l;
|
||||
} else {
|
||||
// Raw VP8/VP8L bitstream (no header).
|
||||
*is_lossless = VP8LCheckSignature(data, *data_size);
|
||||
*chunk_size = *data_size;
|
||||
}
|
||||
|
||||
return VP8_STATUS_OK;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// Fetch '*width', '*height', '*has_alpha' and fill out 'headers' based on
|
||||
// 'data'. All the output parameters may be NULL. If 'headers' is NULL only the
|
||||
// minimal amount will be read to fetch the remaining parameters.
|
||||
// If 'headers' is non-NULL this function will attempt to locate both alpha
|
||||
// data (with or without a VP8X chunk) and the bitstream chunk (VP8/VP8L).
|
||||
// Note: The following chunk sequences (before the raw VP8/VP8L data) are
|
||||
// considered valid by this function:
|
||||
// RIFF + VP8(L)
|
||||
// RIFF + VP8X + (optional chunks) + VP8(L)
|
||||
// ALPH + VP8 <-- Not a valid WebP format: only allowed for internal purpose.
|
||||
// VP8(L) <-- Not a valid WebP format: only allowed for internal purpose.
|
||||
static VP8StatusCode ParseHeadersInternal(const uint8_t* data,
|
||||
size_t data_size,
|
||||
int* const width,
|
||||
int* const height,
|
||||
int* const has_alpha,
|
||||
WebPHeaderStructure* const headers) {
|
||||
int found_riff = 0;
|
||||
int found_vp8x = 0;
|
||||
VP8StatusCode status;
|
||||
WebPHeaderStructure hdrs;
|
||||
|
||||
if (data == NULL || data_size < RIFF_HEADER_SIZE) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
memset(&hdrs, 0, sizeof(hdrs));
|
||||
hdrs.data = data;
|
||||
hdrs.data_size = data_size;
|
||||
|
||||
// Skip over RIFF header.
|
||||
status = ParseRIFF(&data, &data_size, &hdrs.riff_size);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong RIFF header / insufficient data.
|
||||
}
|
||||
found_riff = (hdrs.riff_size > 0);
|
||||
|
||||
// Skip over VP8X.
|
||||
{
|
||||
uint32_t flags = 0;
|
||||
status = ParseVP8X(&data, &data_size, &found_vp8x, width, height, &flags);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong VP8X / insufficient data.
|
||||
}
|
||||
if (!found_riff && found_vp8x) {
|
||||
// Note: This restriction may be removed in the future, if it becomes
|
||||
// necessary to send VP8X chunk to the decoder.
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
if (has_alpha != NULL) *has_alpha = !!(flags & ALPHA_FLAG_BIT);
|
||||
if (found_vp8x && headers == NULL) {
|
||||
return VP8_STATUS_OK; // Return features from VP8X header.
|
||||
}
|
||||
}
|
||||
|
||||
if (data_size < TAG_SIZE) return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
|
||||
// Skip over optional chunks if data started with "RIFF + VP8X" or "ALPH".
|
||||
if ((found_riff && found_vp8x) ||
|
||||
(!found_riff && !found_vp8x && !memcmp(data, "ALPH", TAG_SIZE))) {
|
||||
status = ParseOptionalChunks(&data, &data_size, hdrs.riff_size,
|
||||
&hdrs.alpha_data, &hdrs.alpha_data_size);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Found an invalid chunk size / insufficient data.
|
||||
}
|
||||
}
|
||||
|
||||
// Skip over VP8/VP8L header.
|
||||
status = ParseVP8Header(&data, &data_size, hdrs.riff_size,
|
||||
&hdrs.compressed_size, &hdrs.is_lossless);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status; // Wrong VP8/VP8L chunk-header / insufficient data.
|
||||
}
|
||||
if (hdrs.compressed_size > MAX_CHUNK_PAYLOAD) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
|
||||
if (!hdrs.is_lossless) {
|
||||
if (data_size < VP8_FRAME_HEADER_SIZE) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
// Validates raw VP8 data.
|
||||
if (!VP8GetInfo(data, data_size,
|
||||
(uint32_t)hdrs.compressed_size, width, height)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
} else {
|
||||
if (data_size < VP8L_FRAME_HEADER_SIZE) {
|
||||
return VP8_STATUS_NOT_ENOUGH_DATA;
|
||||
}
|
||||
// Validates raw VP8L data.
|
||||
if (!VP8LGetInfo(data, data_size, width, height, has_alpha)) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_alpha != NULL) {
|
||||
// If the data did not contain a VP8X/VP8L chunk the only definitive way
|
||||
// to set this is by looking for alpha data (from an ALPH chunk).
|
||||
*has_alpha |= (hdrs.alpha_data != NULL);
|
||||
}
|
||||
if (headers != NULL) {
|
||||
*headers = hdrs;
|
||||
headers->offset = data - headers->data;
|
||||
assert((uint64_t)(data - headers->data) < MAX_CHUNK_PAYLOAD);
|
||||
assert(headers->offset == headers->data_size - data_size);
|
||||
}
|
||||
return VP8_STATUS_OK; // Return features from VP8 header.
|
||||
}
|
||||
|
||||
VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) {
|
||||
assert(headers != NULL);
|
||||
// fill out headers, ignore width/height/has_alpha.
|
||||
return ParseHeadersInternal(headers->data, headers->data_size,
|
||||
NULL, NULL, NULL, headers);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecParams
|
||||
|
||||
void WebPResetDecParams(WebPDecParams* const params) {
|
||||
if (params) {
|
||||
memset(params, 0, sizeof(*params));
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// "Into" decoding variants
|
||||
|
||||
// Main flow
|
||||
static VP8StatusCode DecodeInto(const uint8_t* const data, size_t data_size,
|
||||
WebPDecParams* const params) {
|
||||
VP8StatusCode status;
|
||||
VP8Io io;
|
||||
WebPHeaderStructure headers;
|
||||
|
||||
headers.data = data;
|
||||
headers.data_size = data_size;
|
||||
status = WebPParseHeaders(&headers); // Process Pre-VP8 chunks.
|
||||
if (status != VP8_STATUS_OK) {
|
||||
return status;
|
||||
}
|
||||
|
||||
assert(params != NULL);
|
||||
VP8InitIo(&io);
|
||||
io.data = headers.data + headers.offset;
|
||||
io.data_size = headers.data_size - headers.offset;
|
||||
WebPInitCustomIo(params, &io); // Plug the I/O functions.
|
||||
|
||||
if (!headers.is_lossless) {
|
||||
VP8Decoder* const dec = VP8New();
|
||||
if (dec == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
#ifdef WEBP_USE_THREAD
|
||||
dec->use_threads_ = params->options && (params->options->use_threads > 0);
|
||||
#else
|
||||
dec->use_threads_ = 0;
|
||||
#endif
|
||||
dec->alpha_data_ = headers.alpha_data;
|
||||
dec->alpha_data_size_ = headers.alpha_data_size;
|
||||
|
||||
// Decode bitstream header, update io->width/io->height.
|
||||
if (!VP8GetHeaders(dec, &io)) {
|
||||
status = dec->status_; // An error occurred. Grab error status.
|
||||
} else {
|
||||
// Allocate/check output buffers.
|
||||
status = WebPAllocateDecBuffer(io.width, io.height, params->options,
|
||||
params->output);
|
||||
if (status == VP8_STATUS_OK) { // Decode
|
||||
if (!VP8Decode(dec, &io)) {
|
||||
status = dec->status_;
|
||||
}
|
||||
}
|
||||
}
|
||||
VP8Delete(dec);
|
||||
} else {
|
||||
VP8LDecoder* const dec = VP8LNew();
|
||||
if (dec == NULL) {
|
||||
return VP8_STATUS_OUT_OF_MEMORY;
|
||||
}
|
||||
if (!VP8LDecodeHeader(dec, &io)) {
|
||||
status = dec->status_; // An error occurred. Grab error status.
|
||||
} else {
|
||||
// Allocate/check output buffers.
|
||||
status = WebPAllocateDecBuffer(io.width, io.height, params->options,
|
||||
params->output);
|
||||
if (status == VP8_STATUS_OK) { // Decode
|
||||
if (!VP8LDecodeImage(dec)) {
|
||||
status = dec->status_;
|
||||
}
|
||||
}
|
||||
}
|
||||
VP8LDelete(dec);
|
||||
}
|
||||
|
||||
if (status != VP8_STATUS_OK) {
|
||||
WebPFreeDecBuffer(params->output);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// Helpers
|
||||
static uint8_t* DecodeIntoRGBABuffer(WEBP_CSP_MODE colorspace,
|
||||
const uint8_t* const data,
|
||||
size_t data_size,
|
||||
uint8_t* const rgba,
|
||||
int stride, size_t size) {
|
||||
WebPDecParams params;
|
||||
WebPDecBuffer buf;
|
||||
if (rgba == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
WebPInitDecBuffer(&buf);
|
||||
WebPResetDecParams(¶ms);
|
||||
params.output = &buf;
|
||||
buf.colorspace = colorspace;
|
||||
buf.u.RGBA.rgba = rgba;
|
||||
buf.u.RGBA.stride = stride;
|
||||
buf.u.RGBA.size = size;
|
||||
buf.is_external_memory = 1;
|
||||
if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
|
||||
return NULL;
|
||||
}
|
||||
return rgba;
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeRGBInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* output, size_t size, int stride) {
|
||||
return DecodeIntoRGBABuffer(MODE_RGB, data, data_size, output, stride, size);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeRGBAInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* output, size_t size, int stride) {
|
||||
return DecodeIntoRGBABuffer(MODE_RGBA, data, data_size, output, stride, size);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeARGBInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* output, size_t size, int stride) {
|
||||
return DecodeIntoRGBABuffer(MODE_ARGB, data, data_size, output, stride, size);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeBGRInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* output, size_t size, int stride) {
|
||||
return DecodeIntoRGBABuffer(MODE_BGR, data, data_size, output, stride, size);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeBGRAInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* output, size_t size, int stride) {
|
||||
return DecodeIntoRGBABuffer(MODE_BGRA, data, data_size, output, stride, size);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeYUVInto(const uint8_t* data, size_t data_size,
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride) {
|
||||
WebPDecParams params;
|
||||
WebPDecBuffer output;
|
||||
if (luma == NULL) return NULL;
|
||||
WebPInitDecBuffer(&output);
|
||||
WebPResetDecParams(¶ms);
|
||||
params.output = &output;
|
||||
output.colorspace = MODE_YUV;
|
||||
output.u.YUVA.y = luma;
|
||||
output.u.YUVA.y_stride = luma_stride;
|
||||
output.u.YUVA.y_size = luma_size;
|
||||
output.u.YUVA.u = u;
|
||||
output.u.YUVA.u_stride = u_stride;
|
||||
output.u.YUVA.u_size = u_size;
|
||||
output.u.YUVA.v = v;
|
||||
output.u.YUVA.v_stride = v_stride;
|
||||
output.u.YUVA.v_size = v_size;
|
||||
output.is_external_memory = 1;
|
||||
if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
|
||||
return NULL;
|
||||
}
|
||||
return luma;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static uint8_t* Decode(WEBP_CSP_MODE mode, const uint8_t* const data,
|
||||
size_t data_size, int* const width, int* const height,
|
||||
WebPDecBuffer* const keep_info) {
|
||||
WebPDecParams params;
|
||||
WebPDecBuffer output;
|
||||
|
||||
WebPInitDecBuffer(&output);
|
||||
WebPResetDecParams(¶ms);
|
||||
params.output = &output;
|
||||
output.colorspace = mode;
|
||||
|
||||
// Retrieve (and report back) the required dimensions from bitstream.
|
||||
if (!WebPGetInfo(data, data_size, &output.width, &output.height)) {
|
||||
return NULL;
|
||||
}
|
||||
if (width != NULL) *width = output.width;
|
||||
if (height != NULL) *height = output.height;
|
||||
|
||||
// Decode
|
||||
if (DecodeInto(data, data_size, ¶ms) != VP8_STATUS_OK) {
|
||||
return NULL;
|
||||
}
|
||||
if (keep_info != NULL) { // keep track of the side-info
|
||||
WebPCopyDecBuffer(&output, keep_info);
|
||||
}
|
||||
// return decoded samples (don't clear 'output'!)
|
||||
return WebPIsRGBMode(mode) ? output.u.RGBA.rgba : output.u.YUVA.y;
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeRGB(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
return Decode(MODE_RGB, data, data_size, width, height, NULL);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeRGBA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
return Decode(MODE_RGBA, data, data_size, width, height, NULL);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeARGB(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
return Decode(MODE_ARGB, data, data_size, width, height, NULL);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeBGR(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
return Decode(MODE_BGR, data, data_size, width, height, NULL);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeBGRA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
return Decode(MODE_BGRA, data, data_size, width, height, NULL);
|
||||
}
|
||||
|
||||
uint8_t* WebPDecodeYUV(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height, uint8_t** u, uint8_t** v,
|
||||
int* stride, int* uv_stride) {
|
||||
WebPDecBuffer output; // only to preserve the side-infos
|
||||
uint8_t* const out = Decode(MODE_YUV, data, data_size,
|
||||
width, height, &output);
|
||||
|
||||
if (out != NULL) {
|
||||
const WebPYUVABuffer* const buf = &output.u.YUVA;
|
||||
*u = buf->u;
|
||||
*v = buf->v;
|
||||
*stride = buf->y_stride;
|
||||
*uv_stride = buf->u_stride;
|
||||
assert(buf->u_stride == buf->v_stride);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void DefaultFeatures(WebPBitstreamFeatures* const features) {
|
||||
assert(features != NULL);
|
||||
memset(features, 0, sizeof(*features));
|
||||
features->bitstream_version = 0;
|
||||
}
|
||||
|
||||
static VP8StatusCode GetFeatures(const uint8_t* const data, size_t data_size,
|
||||
WebPBitstreamFeatures* const features) {
|
||||
if (features == NULL || data == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
DefaultFeatures(features);
|
||||
|
||||
// Only parse enough of the data to retrieve width/height/has_alpha.
|
||||
return ParseHeadersInternal(data, data_size,
|
||||
&features->width, &features->height,
|
||||
&features->has_alpha, NULL);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPGetInfo()
|
||||
|
||||
int WebPGetInfo(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height) {
|
||||
WebPBitstreamFeatures features;
|
||||
|
||||
if (GetFeatures(data, data_size, &features) != VP8_STATUS_OK) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (width != NULL) {
|
||||
*width = features.width;
|
||||
}
|
||||
if (height != NULL) {
|
||||
*height = features.height;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Advance decoding API
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config) {
|
||||
WebPDecParams params;
|
||||
VP8StatusCode status;
|
||||
|
||||
if (config == NULL) {
|
||||
return VP8_STATUS_INVALID_PARAM;
|
||||
}
|
||||
|
||||
status = GetFeatures(data, data_size, &config->input);
|
||||
if (status != VP8_STATUS_OK) {
|
||||
if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
|
||||
return VP8_STATUS_BITSTREAM_ERROR; // Not-enough-data treated as error.
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
WebPResetDecParams(¶ms);
|
||||
params.output = &config->output;
|
||||
params.options = &config->options;
|
||||
status = DecodeInto(data, data_size, ¶ms);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Cropping and rescaling.
|
||||
|
||||
int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
VP8Io* const io, WEBP_CSP_MODE src_colorspace) {
|
||||
const int W = io->width;
|
||||
const int H = io->height;
|
||||
int x = 0, y = 0, w = W, h = H;
|
||||
|
||||
// Cropping
|
||||
io->use_cropping = (options != NULL) && (options->use_cropping > 0);
|
||||
if (io->use_cropping) {
|
||||
w = options->crop_width;
|
||||
h = options->crop_height;
|
||||
x = options->crop_left;
|
||||
y = options->crop_top;
|
||||
if (!WebPIsRGBMode(src_colorspace)) { // only snap for YUV420 or YUV422
|
||||
x &= ~1;
|
||||
y &= ~1; // TODO(later): only for YUV420, not YUV422.
|
||||
}
|
||||
if (x < 0 || y < 0 || w <= 0 || h <= 0 || x + w > W || y + h > H) {
|
||||
return 0; // out of frame boundary error
|
||||
}
|
||||
}
|
||||
io->crop_left = x;
|
||||
io->crop_top = y;
|
||||
io->crop_right = x + w;
|
||||
io->crop_bottom = y + h;
|
||||
io->mb_w = w;
|
||||
io->mb_h = h;
|
||||
|
||||
// Scaling
|
||||
io->use_scaling = (options != NULL) && (options->use_scaling > 0);
|
||||
if (io->use_scaling) {
|
||||
if (options->scaled_width <= 0 || options->scaled_height <= 0) {
|
||||
return 0;
|
||||
}
|
||||
io->scaled_width = options->scaled_width;
|
||||
io->scaled_height = options->scaled_height;
|
||||
}
|
||||
|
||||
// Filter
|
||||
io->bypass_filtering = options && options->bypass_filtering;
|
||||
|
||||
// Fancy upsampler
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
io->fancy_upsampling = (options == NULL) || (!options->no_fancy_upsampling);
|
||||
#endif
|
||||
|
||||
if (io->use_scaling) {
|
||||
// disable filter (only for large downscaling ratio).
|
||||
io->bypass_filtering = (io->scaled_width < W * 3 / 4) &&
|
||||
(io->scaled_height < H * 3 / 4);
|
||||
io->fancy_upsampling = 0;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
114
drivers/webpold/dec/webpi.h
Normal file
114
drivers/webpold/dec/webpi.h
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Internal header: WebP decoding parameters and custom IO on buffer
|
||||
//
|
||||
// Author: somnath@google.com (Somnath Banerjee)
|
||||
|
||||
#ifndef WEBP_DEC_WEBPI_H_
|
||||
#define WEBP_DEC_WEBPI_H_
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "../utils/rescaler.h"
|
||||
#include "./decode_vp8.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecParams: Decoding output parameters. Transient internal object.
|
||||
|
||||
typedef struct WebPDecParams WebPDecParams;
|
||||
typedef int (*OutputFunc)(const VP8Io* const io, WebPDecParams* const p);
|
||||
typedef int (*OutputRowFunc)(WebPDecParams* const p, int y_pos);
|
||||
|
||||
struct WebPDecParams {
|
||||
WebPDecBuffer* output; // output buffer.
|
||||
uint8_t* tmp_y, *tmp_u, *tmp_v; // cache for the fancy upsampler
|
||||
// or used for tmp rescaling
|
||||
|
||||
int last_y; // coordinate of the line that was last output
|
||||
const WebPDecoderOptions* options; // if not NULL, use alt decoding features
|
||||
// rescalers
|
||||
WebPRescaler scaler_y, scaler_u, scaler_v, scaler_a;
|
||||
void* memory; // overall scratch memory for the output work.
|
||||
|
||||
OutputFunc emit; // output RGB or YUV samples
|
||||
OutputFunc emit_alpha; // output alpha channel
|
||||
OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values
|
||||
};
|
||||
|
||||
// Should be called first, before any use of the WebPDecParams object.
|
||||
void WebPResetDecParams(WebPDecParams* const params);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Header parsing helpers
|
||||
|
||||
// Structure storing a description of the RIFF headers.
|
||||
typedef struct {
|
||||
const uint8_t* data; // input buffer
|
||||
size_t data_size; // input buffer size
|
||||
size_t offset; // offset to main data chunk (VP8 or VP8L)
|
||||
const uint8_t* alpha_data; // points to alpha chunk (if present)
|
||||
size_t alpha_data_size; // alpha chunk size
|
||||
size_t compressed_size; // VP8/VP8L compressed data size
|
||||
size_t riff_size; // size of the riff payload (or 0 if absent)
|
||||
int is_lossless; // true if a VP8L chunk is present
|
||||
} WebPHeaderStructure;
|
||||
|
||||
// Skips over all valid chunks prior to the first VP8/VP8L frame header.
|
||||
// Returns VP8_STATUS_OK on success,
|
||||
// VP8_STATUS_BITSTREAM_ERROR if an invalid header/chunk is found, and
|
||||
// VP8_STATUS_NOT_ENOUGH_DATA if case of insufficient data.
|
||||
// In 'headers', compressed_size, offset, alpha_data, alpha_size and lossless
|
||||
// fields are updated appropriately upon success.
|
||||
VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Misc utils
|
||||
|
||||
// Initializes VP8Io with custom setup, io and teardown functions. The default
|
||||
// hooks will use the supplied 'params' as io->opaque handle.
|
||||
void WebPInitCustomIo(WebPDecParams* const params, VP8Io* const io);
|
||||
|
||||
// Setup crop_xxx fields, mb_w and mb_h in io. 'src_colorspace' refers
|
||||
// to the *compressed* format, not the output one.
|
||||
int WebPIoInitFromOptions(const WebPDecoderOptions* const options,
|
||||
VP8Io* const io, WEBP_CSP_MODE src_colorspace);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Internal functions regarding WebPDecBuffer memory (in buffer.c).
|
||||
// Don't really need to be externally visible for now.
|
||||
|
||||
// Prepare 'buffer' with the requested initial dimensions width/height.
|
||||
// If no external storage is supplied, initializes buffer by allocating output
|
||||
// memory and setting up the stride information. Validate the parameters. Return
|
||||
// an error code in case of problem (no memory, or invalid stride / size /
|
||||
// dimension / etc.). If *options is not NULL, also verify that the options'
|
||||
// parameters are valid and apply them to the width/height dimensions of the
|
||||
// output buffer. This takes cropping / scaling / rotation into account.
|
||||
VP8StatusCode WebPAllocateDecBuffer(int width, int height,
|
||||
const WebPDecoderOptions* const options,
|
||||
WebPDecBuffer* const buffer);
|
||||
|
||||
// Copy 'src' into 'dst' buffer, making sure 'dst' is not marked as owner of the
|
||||
// memory (still held by 'src').
|
||||
void WebPCopyDecBuffer(const WebPDecBuffer* const src,
|
||||
WebPDecBuffer* const dst);
|
||||
|
||||
// Copy and transfer ownership from src to dst (beware of parameter order!)
|
||||
void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst);
|
||||
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_DEC_WEBPI_H_ */
|
||||
454
drivers/webpold/decode.h
Normal file
454
drivers/webpold/decode.h
Normal file
@ -0,0 +1,454 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Main decoding functions for WebP images.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_WEBP_DECODE_H_
|
||||
#define WEBP_WEBP_DECODE_H_
|
||||
|
||||
#include "./types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WEBP_DECODER_ABI_VERSION 0x0200 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
// Return the decoder's version number, packed in hexadecimal using 8bits for
|
||||
// each of major/minor/revision. E.g: v2.5.7 is 0x020507.
|
||||
WEBP_EXTERN(int) WebPGetDecoderVersion(void);
|
||||
|
||||
// Retrieve basic header information: width, height.
|
||||
// This function will also validate the header and return 0 in
|
||||
// case of formatting error.
|
||||
// Pointers 'width' and 'height' can be passed NULL if deemed irrelevant.
|
||||
WEBP_EXTERN(int) WebPGetInfo(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
// Decodes WebP images pointed to by 'data' and returns RGBA samples, along
|
||||
// with the dimensions in *width and *height. The ordering of samples in
|
||||
// memory is R, G, B, A, R, G, B, A... in scan order (endian-independent).
|
||||
// The returned pointer should be deleted calling free().
|
||||
// Returns NULL in case of error.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeRGBA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGBA, but returning A, R, G, B, A, R, G, B... ordered data.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeARGB(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGBA, but returning B, G, R, A, B, G, R, A... ordered data.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeBGRA(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGBA, but returning R, G, B, R, G, B... ordered data.
|
||||
// If the bitstream contains transparency, it is ignored.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeRGB(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
// Same as WebPDecodeRGB, but returning B, G, R, B, G, R... ordered data.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeBGR(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height);
|
||||
|
||||
|
||||
// Decode WebP images pointed to by 'data' to Y'UV format(*). The pointer
|
||||
// returned is the Y samples buffer. Upon return, *u and *v will point to
|
||||
// the U and V chroma data. These U and V buffers need NOT be free()'d,
|
||||
// unlike the returned Y luma one. The dimension of the U and V planes
|
||||
// are both (*width + 1) / 2 and (*height + 1)/ 2.
|
||||
// Upon return, the Y buffer has a stride returned as '*stride', while U and V
|
||||
// have a common stride returned as '*uv_stride'.
|
||||
// Return NULL in case of error.
|
||||
// (*) Also named Y'CbCr. See: http://en.wikipedia.org/wiki/YCbCr
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeYUV(const uint8_t* data, size_t data_size,
|
||||
int* width, int* height,
|
||||
uint8_t** u, uint8_t** v,
|
||||
int* stride, int* uv_stride);
|
||||
|
||||
// These five functions are variants of the above ones, that decode the image
|
||||
// directly into a pre-allocated buffer 'output_buffer'. The maximum storage
|
||||
// available in this buffer is indicated by 'output_buffer_size'. If this
|
||||
// storage is not sufficient (or an error occurred), NULL is returned.
|
||||
// Otherwise, output_buffer is returned, for convenience.
|
||||
// The parameter 'output_stride' specifies the distance (in bytes)
|
||||
// between scanlines. Hence, output_buffer_size is expected to be at least
|
||||
// output_stride x picture-height.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeRGBAInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeARGBInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeBGRAInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
|
||||
// RGB and BGR variants. Here too the transparency information, if present,
|
||||
// will be dropped and ignored.
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeRGBInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeBGRInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
|
||||
// WebPDecodeYUVInto() is a variant of WebPDecodeYUV() that operates directly
|
||||
// into pre-allocated luma/chroma plane buffers. This function requires the
|
||||
// strides to be passed: one for the luma plane and one for each of the
|
||||
// chroma ones. The size of each plane buffer is passed as 'luma_size',
|
||||
// 'u_size' and 'v_size' respectively.
|
||||
// Pointer to the luma plane ('*luma') is returned or NULL if an error occurred
|
||||
// during decoding (or because some buffers were found to be too small).
|
||||
WEBP_EXTERN(uint8_t*) WebPDecodeYUVInto(
|
||||
const uint8_t* data, size_t data_size,
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Output colorspaces and buffer
|
||||
|
||||
// Colorspaces
|
||||
// Note: the naming describes the byte-ordering of packed samples in memory.
|
||||
// For instance, MODE_BGRA relates to samples ordered as B,G,R,A,B,G,R,A,...
|
||||
// Non-capital names (e.g.:MODE_Argb) relates to pre-multiplied RGB channels.
|
||||
// RGB-565 and RGBA-4444 are also endian-agnostic and byte-oriented.
|
||||
typedef enum { MODE_RGB = 0, MODE_RGBA = 1,
|
||||
MODE_BGR = 2, MODE_BGRA = 3,
|
||||
MODE_ARGB = 4, MODE_RGBA_4444 = 5,
|
||||
MODE_RGB_565 = 6,
|
||||
// RGB-premultiplied transparent modes (alpha value is preserved)
|
||||
MODE_rgbA = 7,
|
||||
MODE_bgrA = 8,
|
||||
MODE_Argb = 9,
|
||||
MODE_rgbA_4444 = 10,
|
||||
// YUV modes must come after RGB ones.
|
||||
MODE_YUV = 11, MODE_YUVA = 12, // yuv 4:2:0
|
||||
MODE_LAST = 13
|
||||
} WEBP_CSP_MODE;
|
||||
|
||||
// Some useful macros:
|
||||
static WEBP_INLINE int WebPIsPremultipliedMode(WEBP_CSP_MODE mode) {
|
||||
return (mode == MODE_rgbA || mode == MODE_bgrA || mode == MODE_Argb ||
|
||||
mode == MODE_rgbA_4444);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int WebPIsAlphaMode(WEBP_CSP_MODE mode) {
|
||||
return (mode == MODE_RGBA || mode == MODE_BGRA || mode == MODE_ARGB ||
|
||||
mode == MODE_RGBA_4444 || mode == MODE_YUVA ||
|
||||
WebPIsPremultipliedMode(mode));
|
||||
}
|
||||
|
||||
static WEBP_INLINE int WebPIsRGBMode(WEBP_CSP_MODE mode) {
|
||||
return (mode < MODE_YUV);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebPDecBuffer: Generic structure for describing the output sample buffer.
|
||||
|
||||
typedef struct { // view as RGBA
|
||||
uint8_t* rgba; // pointer to RGBA samples
|
||||
int stride; // stride in bytes from one scanline to the next.
|
||||
size_t size; // total size of the *rgba buffer.
|
||||
} WebPRGBABuffer;
|
||||
|
||||
typedef struct { // view as YUVA
|
||||
uint8_t* y, *u, *v, *a; // pointer to luma, chroma U/V, alpha samples
|
||||
int y_stride; // luma stride
|
||||
int u_stride, v_stride; // chroma strides
|
||||
int a_stride; // alpha stride
|
||||
size_t y_size; // luma plane size
|
||||
size_t u_size, v_size; // chroma planes size
|
||||
size_t a_size; // alpha-plane size
|
||||
} WebPYUVABuffer;
|
||||
|
||||
// Output buffer
|
||||
typedef struct {
|
||||
WEBP_CSP_MODE colorspace; // Colorspace.
|
||||
int width, height; // Dimensions.
|
||||
int is_external_memory; // If true, 'internal_memory' pointer is not used.
|
||||
union {
|
||||
WebPRGBABuffer RGBA;
|
||||
WebPYUVABuffer YUVA;
|
||||
} u; // Nameless union of buffer parameters.
|
||||
uint32_t pad[4]; // padding for later use
|
||||
|
||||
uint8_t* private_memory; // Internally allocated memory (only when
|
||||
// is_external_memory is false). Should not be used
|
||||
// externally, but accessed via the buffer union.
|
||||
} WebPDecBuffer;
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(int) WebPInitDecBufferInternal(WebPDecBuffer*, int);
|
||||
|
||||
// Initialize the structure as empty. Must be called before any other use.
|
||||
// Returns false in case of version mismatch
|
||||
static WEBP_INLINE int WebPInitDecBuffer(WebPDecBuffer* buffer) {
|
||||
return WebPInitDecBufferInternal(buffer, WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Free any memory associated with the buffer. Must always be called last.
|
||||
// Note: doesn't free the 'buffer' structure itself.
|
||||
WEBP_EXTERN(void) WebPFreeDecBuffer(WebPDecBuffer* buffer);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Enumeration of the status codes
|
||||
|
||||
typedef enum {
|
||||
VP8_STATUS_OK = 0,
|
||||
VP8_STATUS_OUT_OF_MEMORY,
|
||||
VP8_STATUS_INVALID_PARAM,
|
||||
VP8_STATUS_BITSTREAM_ERROR,
|
||||
VP8_STATUS_UNSUPPORTED_FEATURE,
|
||||
VP8_STATUS_SUSPENDED,
|
||||
VP8_STATUS_USER_ABORT,
|
||||
VP8_STATUS_NOT_ENOUGH_DATA
|
||||
} VP8StatusCode;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Incremental decoding
|
||||
//
|
||||
// This API allows streamlined decoding of partial data.
|
||||
// Picture can be incrementally decoded as data become available thanks to the
|
||||
// WebPIDecoder object. This object can be left in a SUSPENDED state if the
|
||||
// picture is only partially decoded, pending additional input.
|
||||
// Code example:
|
||||
//
|
||||
// WebPInitDecBuffer(&buffer);
|
||||
// buffer.colorspace = mode;
|
||||
// ...
|
||||
// WebPIDecoder* idec = WebPINewDecoder(&buffer);
|
||||
// while (has_more_data) {
|
||||
// // ... (get additional data)
|
||||
// status = WebPIAppend(idec, new_data, new_data_size);
|
||||
// if (status != VP8_STATUS_SUSPENDED ||
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// // The above call decodes the current available buffer.
|
||||
// // Part of the image can now be refreshed by calling to
|
||||
// // WebPIDecGetRGB()/WebPIDecGetYUVA() etc.
|
||||
// }
|
||||
// WebPIDelete(idec);
|
||||
|
||||
typedef struct WebPIDecoder WebPIDecoder;
|
||||
|
||||
// Creates a new incremental decoder with the supplied buffer parameter.
|
||||
// This output_buffer can be passed NULL, in which case a default output buffer
|
||||
// is used (with MODE_RGB). Otherwise, an internal reference to 'output_buffer'
|
||||
// is kept, which means that the lifespan of 'output_buffer' must be larger than
|
||||
// that of the returned WebPIDecoder object.
|
||||
// Returns NULL if the allocation failed.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewDecoder(WebPDecBuffer* output_buffer);
|
||||
|
||||
// This function allocates and initializes an incremental-decoder object, which
|
||||
// will output the RGB/A samples specified by 'csp' into a preallocated
|
||||
// buffer 'output_buffer'. The size of this buffer is at least
|
||||
// 'output_buffer_size' and the stride (distance in bytes between two scanlines)
|
||||
// is specified by 'output_stride'. Returns NULL if the allocation failed.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewRGB(
|
||||
WEBP_CSP_MODE csp,
|
||||
uint8_t* output_buffer, size_t output_buffer_size, int output_stride);
|
||||
|
||||
// This function allocates and initializes an incremental-decoder object, which
|
||||
// will output the raw luma/chroma samples into a preallocated planes. The luma
|
||||
// plane is specified by its pointer 'luma', its size 'luma_size' and its stride
|
||||
// 'luma_stride'. Similarly, the chroma-u plane is specified by the 'u',
|
||||
// 'u_size' and 'u_stride' parameters, and the chroma-v plane by 'v'
|
||||
// and 'v_size'. And same for the alpha-plane. The 'a' pointer can be pass
|
||||
// NULL in case one is not interested in the transparency plane.
|
||||
// Returns NULL if the allocation failed.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewYUVA(
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride,
|
||||
uint8_t* a, size_t a_size, int a_stride);
|
||||
|
||||
// Deprecated version of the above, without the alpha plane.
|
||||
// Kept for backward compatibility.
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPINewYUV(
|
||||
uint8_t* luma, size_t luma_size, int luma_stride,
|
||||
uint8_t* u, size_t u_size, int u_stride,
|
||||
uint8_t* v, size_t v_size, int v_stride);
|
||||
|
||||
// Deletes the WebPIDecoder object and associated memory. Must always be called
|
||||
// if WebPINewDecoder, WebPINewRGB or WebPINewYUV succeeded.
|
||||
WEBP_EXTERN(void) WebPIDelete(WebPIDecoder* idec);
|
||||
|
||||
// Copies and decodes the next available data. Returns VP8_STATUS_OK when
|
||||
// the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more
|
||||
// data is expected. Returns error in other cases.
|
||||
WEBP_EXTERN(VP8StatusCode) WebPIAppend(
|
||||
WebPIDecoder* idec, const uint8_t* data, size_t data_size);
|
||||
|
||||
// A variant of the above function to be used when data buffer contains
|
||||
// partial data from the beginning. In this case data buffer is not copied
|
||||
// to the internal memory.
|
||||
// Note that the value of the 'data' pointer can change between calls to
|
||||
// WebPIUpdate, for instance when the data buffer is resized to fit larger data.
|
||||
WEBP_EXTERN(VP8StatusCode) WebPIUpdate(
|
||||
WebPIDecoder* idec, const uint8_t* data, size_t data_size);
|
||||
|
||||
// Returns the RGB/A image decoded so far. Returns NULL if output params
|
||||
// are not initialized yet. The RGB/A output type corresponds to the colorspace
|
||||
// specified during call to WebPINewDecoder() or WebPINewRGB().
|
||||
// *last_y is the index of last decoded row in raster scan order. Some pointers
|
||||
// (*last_y, *width etc.) can be NULL if corresponding information is not
|
||||
// needed.
|
||||
WEBP_EXTERN(uint8_t*) WebPIDecGetRGB(
|
||||
const WebPIDecoder* idec, int* last_y,
|
||||
int* width, int* height, int* stride);
|
||||
|
||||
// Same as above function to get a YUVA image. Returns pointer to the luma
|
||||
// plane or NULL in case of error. If there is no alpha information
|
||||
// the alpha pointer '*a' will be returned NULL.
|
||||
WEBP_EXTERN(uint8_t*) WebPIDecGetYUVA(
|
||||
const WebPIDecoder* idec, int* last_y,
|
||||
uint8_t** u, uint8_t** v, uint8_t** a,
|
||||
int* width, int* height, int* stride, int* uv_stride, int* a_stride);
|
||||
|
||||
// Deprecated alpha-less version of WebPIDecGetYUVA(): it will ignore the
|
||||
// alpha information (if present). Kept for backward compatibility.
|
||||
static WEBP_INLINE uint8_t* WebPIDecGetYUV(
|
||||
const WebPIDecoder* idec, int* last_y, uint8_t** u, uint8_t** v,
|
||||
int* width, int* height, int* stride, int* uv_stride) {
|
||||
return WebPIDecGetYUVA(idec, last_y, u, v, NULL, width, height,
|
||||
stride, uv_stride, NULL);
|
||||
}
|
||||
|
||||
// Generic call to retrieve information about the displayable area.
|
||||
// If non NULL, the left/right/width/height pointers are filled with the visible
|
||||
// rectangular area so far.
|
||||
// Returns NULL in case the incremental decoder object is in an invalid state.
|
||||
// Otherwise returns the pointer to the internal representation. This structure
|
||||
// is read-only, tied to WebPIDecoder's lifespan and should not be modified.
|
||||
WEBP_EXTERN(const WebPDecBuffer*) WebPIDecodedArea(
|
||||
const WebPIDecoder* idec, int* left, int* top, int* width, int* height);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Advanced decoding parametrization
|
||||
//
|
||||
// Code sample for using the advanced decoding API
|
||||
/*
|
||||
// A) Init a configuration object
|
||||
WebPDecoderConfig config;
|
||||
CHECK(WebPInitDecoderConfig(&config));
|
||||
|
||||
// B) optional: retrieve the bitstream's features.
|
||||
CHECK(WebPGetFeatures(data, data_size, &config.input) == VP8_STATUS_OK);
|
||||
|
||||
// C) Adjust 'config', if needed
|
||||
config.no_fancy = 1;
|
||||
config.output.colorspace = MODE_BGRA;
|
||||
// etc.
|
||||
|
||||
// Note that you can also make config.output point to an externally
|
||||
// supplied memory buffer, provided it's big enough to store the decoded
|
||||
// picture. Otherwise, config.output will just be used to allocate memory
|
||||
// and store the decoded picture.
|
||||
|
||||
// D) Decode!
|
||||
CHECK(WebPDecode(data, data_size, &config) == VP8_STATUS_OK);
|
||||
|
||||
// E) Decoded image is now in config.output (and config.output.u.RGBA)
|
||||
|
||||
// F) Reclaim memory allocated in config's object. It's safe to call
|
||||
// this function even if the memory is external and wasn't allocated
|
||||
// by WebPDecode().
|
||||
WebPFreeDecBuffer(&config.output);
|
||||
*/
|
||||
|
||||
// Features gathered from the bitstream
|
||||
typedef struct {
|
||||
int width; // Width in pixels, as read from the bitstream.
|
||||
int height; // Height in pixels, as read from the bitstream.
|
||||
int has_alpha; // True if the bitstream contains an alpha channel.
|
||||
|
||||
// Unused for now:
|
||||
int bitstream_version; // should be 0 for now. TODO(later)
|
||||
int no_incremental_decoding; // if true, using incremental decoding is not
|
||||
// recommended.
|
||||
int rotate; // TODO(later)
|
||||
int uv_sampling; // should be 0 for now. TODO(later)
|
||||
uint32_t pad[3]; // padding for later use
|
||||
} WebPBitstreamFeatures;
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(VP8StatusCode) WebPGetFeaturesInternal(
|
||||
const uint8_t*, size_t, WebPBitstreamFeatures*, int);
|
||||
|
||||
// Retrieve features from the bitstream. The *features structure is filled
|
||||
// with information gathered from the bitstream.
|
||||
// Returns false in case of error or version mismatch.
|
||||
// In case of error, features->bitstream_status will reflect the error code.
|
||||
static WEBP_INLINE VP8StatusCode WebPGetFeatures(
|
||||
const uint8_t* data, size_t data_size,
|
||||
WebPBitstreamFeatures* features) {
|
||||
return WebPGetFeaturesInternal(data, data_size, features,
|
||||
WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Decoding options
|
||||
typedef struct {
|
||||
int bypass_filtering; // if true, skip the in-loop filtering
|
||||
int no_fancy_upsampling; // if true, use faster pointwise upsampler
|
||||
int use_cropping; // if true, cropping is applied _first_
|
||||
int crop_left, crop_top; // top-left position for cropping.
|
||||
// Will be snapped to even values.
|
||||
int crop_width, crop_height; // dimension of the cropping area
|
||||
int use_scaling; // if true, scaling is applied _afterward_
|
||||
int scaled_width, scaled_height; // final resolution
|
||||
int use_threads; // if true, use multi-threaded decoding
|
||||
|
||||
// Unused for now:
|
||||
int force_rotation; // forced rotation (to be applied _last_)
|
||||
int no_enhancement; // if true, discard enhancement layer
|
||||
uint32_t pad[6]; // padding for later use
|
||||
} WebPDecoderOptions;
|
||||
|
||||
// Main object storing the configuration for advanced decoding.
|
||||
typedef struct {
|
||||
WebPBitstreamFeatures input; // Immutable bitstream features (optional)
|
||||
WebPDecBuffer output; // Output buffer (can point to external mem)
|
||||
WebPDecoderOptions options; // Decoding options
|
||||
} WebPDecoderConfig;
|
||||
|
||||
// Internal, version-checked, entry point
|
||||
WEBP_EXTERN(int) WebPInitDecoderConfigInternal(WebPDecoderConfig*, int);
|
||||
|
||||
// Initialize the configuration as empty. This function must always be
|
||||
// called first, unless WebPGetFeatures() is to be called.
|
||||
// Returns false in case of mismatched version.
|
||||
static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) {
|
||||
return WebPInitDecoderConfigInternal(config, WEBP_DECODER_ABI_VERSION);
|
||||
}
|
||||
|
||||
// Instantiate a new incremental decoder object with the requested
|
||||
// configuration. The bitstream can be passed using 'data' and 'data_size'
|
||||
// parameter, in which case the features will be parsed and stored into
|
||||
// config->input. Otherwise, 'data' can be NULL and no parsing will occur.
|
||||
// Note that 'config' can be NULL too, in which case a default configuration
|
||||
// is used.
|
||||
// The return WebPIDecoder object must always be deleted calling WebPIDelete().
|
||||
// Returns NULL in case of error (and config->status will then reflect
|
||||
// the error condition).
|
||||
WEBP_EXTERN(WebPIDecoder*) WebPIDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config);
|
||||
|
||||
// Non-incremental version. This version decodes the full data at once, taking
|
||||
// 'config' into account. Returns decoding status (which should be VP8_STATUS_OK
|
||||
// if the decoding was successful).
|
||||
WEBP_EXTERN(VP8StatusCode) WebPDecode(const uint8_t* data, size_t data_size,
|
||||
WebPDecoderConfig* config);
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_WEBP_DECODE_H_ */
|
||||
85
drivers/webpold/dsp/cpu.c
Normal file
85
drivers/webpold/dsp/cpu.c
Normal file
@ -0,0 +1,85 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// CPU detection
|
||||
//
|
||||
// Author: Christian Duvivier (cduvivier@google.com)
|
||||
|
||||
#include "./dsp.h"
|
||||
|
||||
#if defined(__ANDROID__)
|
||||
#include <cpu-features.h>
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// SSE2 detection.
|
||||
//
|
||||
|
||||
// apple/darwin gcc-4.0.1 defines __PIC__, but not __pic__ with -fPIC.
|
||||
#if (defined(__pic__) || defined(__PIC__)) && defined(__i386__)
|
||||
static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
|
||||
__asm__ volatile (
|
||||
"mov %%ebx, %%edi\n"
|
||||
"cpuid\n"
|
||||
"xchg %%edi, %%ebx\n"
|
||||
: "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
|
||||
: "a"(info_type));
|
||||
}
|
||||
#elif defined(__i386__) || defined(__x86_64__)
|
||||
static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) {
|
||||
__asm__ volatile (
|
||||
"cpuid\n"
|
||||
: "=a"(cpu_info[0]), "=b"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3])
|
||||
: "a"(info_type));
|
||||
}
|
||||
#elif defined(WEBP_MSC_SSE2)
|
||||
#define GetCPUInfo __cpuid
|
||||
#endif
|
||||
|
||||
#if defined(__i386__) || defined(__x86_64__) || defined(WEBP_MSC_SSE2)
|
||||
static int x86CPUInfo(CPUFeature feature) {
|
||||
int cpu_info[4];
|
||||
GetCPUInfo(cpu_info, 1);
|
||||
if (feature == kSSE2) {
|
||||
return 0 != (cpu_info[3] & 0x04000000);
|
||||
}
|
||||
if (feature == kSSE3) {
|
||||
return 0 != (cpu_info[2] & 0x00000001);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
VP8CPUInfo VP8GetCPUInfo = x86CPUInfo;
|
||||
#elif defined(WEBP_ANDROID_NEON)
|
||||
static int AndroidCPUInfo(CPUFeature feature) {
|
||||
const AndroidCpuFamily cpu_family = android_getCpuFamily();
|
||||
const uint64_t cpu_features = android_getCpuFeatures();
|
||||
if (feature == kNEON) {
|
||||
return (cpu_family == ANDROID_CPU_FAMILY_ARM &&
|
||||
0 != (cpu_features & ANDROID_CPU_ARM_FEATURE_NEON));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo;
|
||||
#elif defined(__ARM_NEON__)
|
||||
// define a dummy function to enable turning off NEON at runtime by setting
|
||||
// VP8DecGetCPUInfo = NULL
|
||||
static int armCPUInfo(CPUFeature feature) {
|
||||
(void)feature;
|
||||
return 1;
|
||||
}
|
||||
VP8CPUInfo VP8GetCPUInfo = armCPUInfo;
|
||||
#else
|
||||
VP8CPUInfo VP8GetCPUInfo = NULL;
|
||||
#endif
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
732
drivers/webpold/dsp/dec.c
Normal file
732
drivers/webpold/dsp/dec.c
Normal file
@ -0,0 +1,732 @@
|
||||
// Copyright 2010 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Speed-critical decoding functions.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#include "./dsp.h"
|
||||
#include "../dec/vp8i.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// run-time tables (~4k)
|
||||
|
||||
static uint8_t abs0[255 + 255 + 1]; // abs(i)
|
||||
static uint8_t abs1[255 + 255 + 1]; // abs(i)>>1
|
||||
static int8_t sclip1[1020 + 1020 + 1]; // clips [-1020, 1020] to [-128, 127]
|
||||
static int8_t sclip2[112 + 112 + 1]; // clips [-112, 112] to [-16, 15]
|
||||
static uint8_t clip1[255 + 510 + 1]; // clips [-255,510] to [0,255]
|
||||
|
||||
// We declare this variable 'volatile' to prevent instruction reordering
|
||||
// and make sure it's set to true _last_ (so as to be thread-safe)
|
||||
static volatile int tables_ok = 0;
|
||||
|
||||
static void DspInitTables(void) {
|
||||
if (!tables_ok) {
|
||||
int i;
|
||||
for (i = -255; i <= 255; ++i) {
|
||||
abs0[255 + i] = (i < 0) ? -i : i;
|
||||
abs1[255 + i] = abs0[255 + i] >> 1;
|
||||
}
|
||||
for (i = -1020; i <= 1020; ++i) {
|
||||
sclip1[1020 + i] = (i < -128) ? -128 : (i > 127) ? 127 : i;
|
||||
}
|
||||
for (i = -112; i <= 112; ++i) {
|
||||
sclip2[112 + i] = (i < -16) ? -16 : (i > 15) ? 15 : i;
|
||||
}
|
||||
for (i = -255; i <= 255 + 255; ++i) {
|
||||
clip1[255 + i] = (i < 0) ? 0 : (i > 255) ? 255 : i;
|
||||
}
|
||||
tables_ok = 1;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE uint8_t clip_8b(int v) {
|
||||
return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Transforms (Paragraph 14.4)
|
||||
|
||||
#define STORE(x, y, v) \
|
||||
dst[x + y * BPS] = clip_8b(dst[x + y * BPS] + ((v) >> 3))
|
||||
|
||||
static const int kC1 = 20091 + (1 << 16);
|
||||
static const int kC2 = 35468;
|
||||
#define MUL(a, b) (((a) * (b)) >> 16)
|
||||
|
||||
static void TransformOne(const int16_t* in, uint8_t* dst) {
|
||||
int C[4 * 4], *tmp;
|
||||
int i;
|
||||
tmp = C;
|
||||
for (i = 0; i < 4; ++i) { // vertical pass
|
||||
const int a = in[0] + in[8]; // [-4096, 4094]
|
||||
const int b = in[0] - in[8]; // [-4095, 4095]
|
||||
const int c = MUL(in[4], kC2) - MUL(in[12], kC1); // [-3783, 3783]
|
||||
const int d = MUL(in[4], kC1) + MUL(in[12], kC2); // [-3785, 3781]
|
||||
tmp[0] = a + d; // [-7881, 7875]
|
||||
tmp[1] = b + c; // [-7878, 7878]
|
||||
tmp[2] = b - c; // [-7878, 7878]
|
||||
tmp[3] = a - d; // [-7877, 7879]
|
||||
tmp += 4;
|
||||
in++;
|
||||
}
|
||||
// Each pass is expanding the dynamic range by ~3.85 (upper bound).
|
||||
// The exact value is (2. + (kC1 + kC2) / 65536).
|
||||
// After the second pass, maximum interval is [-3794, 3794], assuming
|
||||
// an input in [-2048, 2047] interval. We then need to add a dst value
|
||||
// in the [0, 255] range.
|
||||
// In the worst case scenario, the input to clip_8b() can be as large as
|
||||
// [-60713, 60968].
|
||||
tmp = C;
|
||||
for (i = 0; i < 4; ++i) { // horizontal pass
|
||||
const int dc = tmp[0] + 4;
|
||||
const int a = dc + tmp[8];
|
||||
const int b = dc - tmp[8];
|
||||
const int c = MUL(tmp[4], kC2) - MUL(tmp[12], kC1);
|
||||
const int d = MUL(tmp[4], kC1) + MUL(tmp[12], kC2);
|
||||
STORE(0, 0, a + d);
|
||||
STORE(1, 0, b + c);
|
||||
STORE(2, 0, b - c);
|
||||
STORE(3, 0, a - d);
|
||||
tmp++;
|
||||
dst += BPS;
|
||||
}
|
||||
}
|
||||
#undef MUL
|
||||
|
||||
static void TransformTwo(const int16_t* in, uint8_t* dst, int do_two) {
|
||||
TransformOne(in, dst);
|
||||
if (do_two) {
|
||||
TransformOne(in + 16, dst + 4);
|
||||
}
|
||||
}
|
||||
|
||||
static void TransformUV(const int16_t* in, uint8_t* dst) {
|
||||
VP8Transform(in + 0 * 16, dst, 1);
|
||||
VP8Transform(in + 2 * 16, dst + 4 * BPS, 1);
|
||||
}
|
||||
|
||||
static void TransformDC(const int16_t *in, uint8_t* dst) {
|
||||
const int DC = in[0] + 4;
|
||||
int i, j;
|
||||
for (j = 0; j < 4; ++j) {
|
||||
for (i = 0; i < 4; ++i) {
|
||||
STORE(i, j, DC);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void TransformDCUV(const int16_t* in, uint8_t* dst) {
|
||||
if (in[0 * 16]) TransformDC(in + 0 * 16, dst);
|
||||
if (in[1 * 16]) TransformDC(in + 1 * 16, dst + 4);
|
||||
if (in[2 * 16]) TransformDC(in + 2 * 16, dst + 4 * BPS);
|
||||
if (in[3 * 16]) TransformDC(in + 3 * 16, dst + 4 * BPS + 4);
|
||||
}
|
||||
|
||||
#undef STORE
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Paragraph 14.3
|
||||
|
||||
static void TransformWHT(const int16_t* in, int16_t* out) {
|
||||
int tmp[16];
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
const int a0 = in[0 + i] + in[12 + i];
|
||||
const int a1 = in[4 + i] + in[ 8 + i];
|
||||
const int a2 = in[4 + i] - in[ 8 + i];
|
||||
const int a3 = in[0 + i] - in[12 + i];
|
||||
tmp[0 + i] = a0 + a1;
|
||||
tmp[8 + i] = a0 - a1;
|
||||
tmp[4 + i] = a3 + a2;
|
||||
tmp[12 + i] = a3 - a2;
|
||||
}
|
||||
for (i = 0; i < 4; ++i) {
|
||||
const int dc = tmp[0 + i * 4] + 3; // w/ rounder
|
||||
const int a0 = dc + tmp[3 + i * 4];
|
||||
const int a1 = tmp[1 + i * 4] + tmp[2 + i * 4];
|
||||
const int a2 = tmp[1 + i * 4] - tmp[2 + i * 4];
|
||||
const int a3 = dc - tmp[3 + i * 4];
|
||||
out[ 0] = (a0 + a1) >> 3;
|
||||
out[16] = (a3 + a2) >> 3;
|
||||
out[32] = (a0 - a1) >> 3;
|
||||
out[48] = (a3 - a2) >> 3;
|
||||
out += 64;
|
||||
}
|
||||
}
|
||||
|
||||
void (*VP8TransformWHT)(const int16_t* in, int16_t* out) = TransformWHT;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Intra predictions
|
||||
|
||||
#define DST(x, y) dst[(x) + (y) * BPS]
|
||||
|
||||
static WEBP_INLINE void TrueMotion(uint8_t *dst, int size) {
|
||||
const uint8_t* top = dst - BPS;
|
||||
const uint8_t* const clip0 = clip1 + 255 - top[-1];
|
||||
int y;
|
||||
for (y = 0; y < size; ++y) {
|
||||
const uint8_t* const clip = clip0 + dst[-1];
|
||||
int x;
|
||||
for (x = 0; x < size; ++x) {
|
||||
dst[x] = clip[top[x]];
|
||||
}
|
||||
dst += BPS;
|
||||
}
|
||||
}
|
||||
static void TM4(uint8_t *dst) { TrueMotion(dst, 4); }
|
||||
static void TM8uv(uint8_t *dst) { TrueMotion(dst, 8); }
|
||||
static void TM16(uint8_t *dst) { TrueMotion(dst, 16); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 16x16
|
||||
|
||||
static void VE16(uint8_t *dst) { // vertical
|
||||
int j;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
memcpy(dst + j * BPS, dst - BPS, 16);
|
||||
}
|
||||
}
|
||||
|
||||
static void HE16(uint8_t *dst) { // horizontal
|
||||
int j;
|
||||
for (j = 16; j > 0; --j) {
|
||||
memset(dst, dst[-1], 16);
|
||||
dst += BPS;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE void Put16(int v, uint8_t* dst) {
|
||||
int j;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
memset(dst + j * BPS, v, 16);
|
||||
}
|
||||
}
|
||||
|
||||
static void DC16(uint8_t *dst) { // DC
|
||||
int DC = 16;
|
||||
int j;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
DC += dst[-1 + j * BPS] + dst[j - BPS];
|
||||
}
|
||||
Put16(DC >> 5, dst);
|
||||
}
|
||||
|
||||
static void DC16NoTop(uint8_t *dst) { // DC with top samples not available
|
||||
int DC = 8;
|
||||
int j;
|
||||
for (j = 0; j < 16; ++j) {
|
||||
DC += dst[-1 + j * BPS];
|
||||
}
|
||||
Put16(DC >> 4, dst);
|
||||
}
|
||||
|
||||
static void DC16NoLeft(uint8_t *dst) { // DC with left samples not available
|
||||
int DC = 8;
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
DC += dst[i - BPS];
|
||||
}
|
||||
Put16(DC >> 4, dst);
|
||||
}
|
||||
|
||||
static void DC16NoTopLeft(uint8_t *dst) { // DC with no top and left samples
|
||||
Put16(0x80, dst);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// 4x4
|
||||
|
||||
#define AVG3(a, b, c) (((a) + 2 * (b) + (c) + 2) >> 2)
|
||||
#define AVG2(a, b) (((a) + (b) + 1) >> 1)
|
||||
|
||||
static void VE4(uint8_t *dst) { // vertical
|
||||
const uint8_t* top = dst - BPS;
|
||||
const uint8_t vals[4] = {
|
||||
AVG3(top[-1], top[0], top[1]),
|
||||
AVG3(top[ 0], top[1], top[2]),
|
||||
AVG3(top[ 1], top[2], top[3]),
|
||||
AVG3(top[ 2], top[3], top[4])
|
||||
};
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) {
|
||||
memcpy(dst + i * BPS, vals, sizeof(vals));
|
||||
}
|
||||
}
|
||||
|
||||
static void HE4(uint8_t *dst) { // horizontal
|
||||
const int A = dst[-1 - BPS];
|
||||
const int B = dst[-1];
|
||||
const int C = dst[-1 + BPS];
|
||||
const int D = dst[-1 + 2 * BPS];
|
||||
const int E = dst[-1 + 3 * BPS];
|
||||
*(uint32_t*)(dst + 0 * BPS) = 0x01010101U * AVG3(A, B, C);
|
||||
*(uint32_t*)(dst + 1 * BPS) = 0x01010101U * AVG3(B, C, D);
|
||||
*(uint32_t*)(dst + 2 * BPS) = 0x01010101U * AVG3(C, D, E);
|
||||
*(uint32_t*)(dst + 3 * BPS) = 0x01010101U * AVG3(D, E, E);
|
||||
}
|
||||
|
||||
static void DC4(uint8_t *dst) { // DC
|
||||
uint32_t dc = 4;
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i) dc += dst[i - BPS] + dst[-1 + i * BPS];
|
||||
dc >>= 3;
|
||||
for (i = 0; i < 4; ++i) memset(dst + i * BPS, dc, 4);
|
||||
}
|
||||
|
||||
static void RD4(uint8_t *dst) { // Down-right
|
||||
const int I = dst[-1 + 0 * BPS];
|
||||
const int J = dst[-1 + 1 * BPS];
|
||||
const int K = dst[-1 + 2 * BPS];
|
||||
const int L = dst[-1 + 3 * BPS];
|
||||
const int X = dst[-1 - BPS];
|
||||
const int A = dst[0 - BPS];
|
||||
const int B = dst[1 - BPS];
|
||||
const int C = dst[2 - BPS];
|
||||
const int D = dst[3 - BPS];
|
||||
DST(0, 3) = AVG3(J, K, L);
|
||||
DST(0, 2) = DST(1, 3) = AVG3(I, J, K);
|
||||
DST(0, 1) = DST(1, 2) = DST(2, 3) = AVG3(X, I, J);
|
||||
DST(0, 0) = DST(1, 1) = DST(2, 2) = DST(3, 3) = AVG3(A, X, I);
|
||||
DST(1, 0) = DST(2, 1) = DST(3, 2) = AVG3(B, A, X);
|
||||
DST(2, 0) = DST(3, 1) = AVG3(C, B, A);
|
||||
DST(3, 0) = AVG3(D, C, B);
|
||||
}
|
||||
|
||||
static void LD4(uint8_t *dst) { // Down-Left
|
||||
const int A = dst[0 - BPS];
|
||||
const int B = dst[1 - BPS];
|
||||
const int C = dst[2 - BPS];
|
||||
const int D = dst[3 - BPS];
|
||||
const int E = dst[4 - BPS];
|
||||
const int F = dst[5 - BPS];
|
||||
const int G = dst[6 - BPS];
|
||||
const int H = dst[7 - BPS];
|
||||
DST(0, 0) = AVG3(A, B, C);
|
||||
DST(1, 0) = DST(0, 1) = AVG3(B, C, D);
|
||||
DST(2, 0) = DST(1, 1) = DST(0, 2) = AVG3(C, D, E);
|
||||
DST(3, 0) = DST(2, 1) = DST(1, 2) = DST(0, 3) = AVG3(D, E, F);
|
||||
DST(3, 1) = DST(2, 2) = DST(1, 3) = AVG3(E, F, G);
|
||||
DST(3, 2) = DST(2, 3) = AVG3(F, G, H);
|
||||
DST(3, 3) = AVG3(G, H, H);
|
||||
}
|
||||
|
||||
static void VR4(uint8_t *dst) { // Vertical-Right
|
||||
const int I = dst[-1 + 0 * BPS];
|
||||
const int J = dst[-1 + 1 * BPS];
|
||||
const int K = dst[-1 + 2 * BPS];
|
||||
const int X = dst[-1 - BPS];
|
||||
const int A = dst[0 - BPS];
|
||||
const int B = dst[1 - BPS];
|
||||
const int C = dst[2 - BPS];
|
||||
const int D = dst[3 - BPS];
|
||||
DST(0, 0) = DST(1, 2) = AVG2(X, A);
|
||||
DST(1, 0) = DST(2, 2) = AVG2(A, B);
|
||||
DST(2, 0) = DST(3, 2) = AVG2(B, C);
|
||||
DST(3, 0) = AVG2(C, D);
|
||||
|
||||
DST(0, 3) = AVG3(K, J, I);
|
||||
DST(0, 2) = AVG3(J, I, X);
|
||||
DST(0, 1) = DST(1, 3) = AVG3(I, X, A);
|
||||
DST(1, 1) = DST(2, 3) = AVG3(X, A, B);
|
||||
DST(2, 1) = DST(3, 3) = AVG3(A, B, C);
|
||||
DST(3, 1) = AVG3(B, C, D);
|
||||
}
|
||||
|
||||
static void VL4(uint8_t *dst) { // Vertical-Left
|
||||
const int A = dst[0 - BPS];
|
||||
const int B = dst[1 - BPS];
|
||||
const int C = dst[2 - BPS];
|
||||
const int D = dst[3 - BPS];
|
||||
const int E = dst[4 - BPS];
|
||||
const int F = dst[5 - BPS];
|
||||
const int G = dst[6 - BPS];
|
||||
const int H = dst[7 - BPS];
|
||||
DST(0, 0) = AVG2(A, B);
|
||||
DST(1, 0) = DST(0, 2) = AVG2(B, C);
|
||||
DST(2, 0) = DST(1, 2) = AVG2(C, D);
|
||||
DST(3, 0) = DST(2, 2) = AVG2(D, E);
|
||||
|
||||
DST(0, 1) = AVG3(A, B, C);
|
||||
DST(1, 1) = DST(0, 3) = AVG3(B, C, D);
|
||||
DST(2, 1) = DST(1, 3) = AVG3(C, D, E);
|
||||
DST(3, 1) = DST(2, 3) = AVG3(D, E, F);
|
||||
DST(3, 2) = AVG3(E, F, G);
|
||||
DST(3, 3) = AVG3(F, G, H);
|
||||
}
|
||||
|
||||
static void HU4(uint8_t *dst) { // Horizontal-Up
|
||||
const int I = dst[-1 + 0 * BPS];
|
||||
const int J = dst[-1 + 1 * BPS];
|
||||
const int K = dst[-1 + 2 * BPS];
|
||||
const int L = dst[-1 + 3 * BPS];
|
||||
DST(0, 0) = AVG2(I, J);
|
||||
DST(2, 0) = DST(0, 1) = AVG2(J, K);
|
||||
DST(2, 1) = DST(0, 2) = AVG2(K, L);
|
||||
DST(1, 0) = AVG3(I, J, K);
|
||||
DST(3, 0) = DST(1, 1) = AVG3(J, K, L);
|
||||
DST(3, 1) = DST(1, 2) = AVG3(K, L, L);
|
||||
DST(3, 2) = DST(2, 2) =
|
||||
DST(0, 3) = DST(1, 3) = DST(2, 3) = DST(3, 3) = L;
|
||||
}
|
||||
|
||||
static void HD4(uint8_t *dst) { // Horizontal-Down
|
||||
const int I = dst[-1 + 0 * BPS];
|
||||
const int J = dst[-1 + 1 * BPS];
|
||||
const int K = dst[-1 + 2 * BPS];
|
||||
const int L = dst[-1 + 3 * BPS];
|
||||
const int X = dst[-1 - BPS];
|
||||
const int A = dst[0 - BPS];
|
||||
const int B = dst[1 - BPS];
|
||||
const int C = dst[2 - BPS];
|
||||
|
||||
DST(0, 0) = DST(2, 1) = AVG2(I, X);
|
||||
DST(0, 1) = DST(2, 2) = AVG2(J, I);
|
||||
DST(0, 2) = DST(2, 3) = AVG2(K, J);
|
||||
DST(0, 3) = AVG2(L, K);
|
||||
|
||||
DST(3, 0) = AVG3(A, B, C);
|
||||
DST(2, 0) = AVG3(X, A, B);
|
||||
DST(1, 0) = DST(3, 1) = AVG3(I, X, A);
|
||||
DST(1, 1) = DST(3, 2) = AVG3(J, I, X);
|
||||
DST(1, 2) = DST(3, 3) = AVG3(K, J, I);
|
||||
DST(1, 3) = AVG3(L, K, J);
|
||||
}
|
||||
|
||||
#undef DST
|
||||
#undef AVG3
|
||||
#undef AVG2
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Chroma
|
||||
|
||||
static void VE8uv(uint8_t *dst) { // vertical
|
||||
int j;
|
||||
for (j = 0; j < 8; ++j) {
|
||||
memcpy(dst + j * BPS, dst - BPS, 8);
|
||||
}
|
||||
}
|
||||
|
||||
static void HE8uv(uint8_t *dst) { // horizontal
|
||||
int j;
|
||||
for (j = 0; j < 8; ++j) {
|
||||
memset(dst, dst[-1], 8);
|
||||
dst += BPS;
|
||||
}
|
||||
}
|
||||
|
||||
// helper for chroma-DC predictions
|
||||
static WEBP_INLINE void Put8x8uv(uint64_t v, uint8_t* dst) {
|
||||
int j;
|
||||
for (j = 0; j < 8; ++j) {
|
||||
*(uint64_t*)(dst + j * BPS) = v;
|
||||
}
|
||||
}
|
||||
|
||||
static void DC8uv(uint8_t *dst) { // DC
|
||||
int dc0 = 8;
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
dc0 += dst[i - BPS] + dst[-1 + i * BPS];
|
||||
}
|
||||
Put8x8uv((uint64_t)((dc0 >> 4) * 0x0101010101010101ULL), dst);
|
||||
}
|
||||
|
||||
static void DC8uvNoLeft(uint8_t *dst) { // DC with no left samples
|
||||
int dc0 = 4;
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
dc0 += dst[i - BPS];
|
||||
}
|
||||
Put8x8uv((uint64_t)((dc0 >> 3) * 0x0101010101010101ULL), dst);
|
||||
}
|
||||
|
||||
static void DC8uvNoTop(uint8_t *dst) { // DC with no top samples
|
||||
int dc0 = 4;
|
||||
int i;
|
||||
for (i = 0; i < 8; ++i) {
|
||||
dc0 += dst[-1 + i * BPS];
|
||||
}
|
||||
Put8x8uv((uint64_t)((dc0 >> 3) * 0x0101010101010101ULL), dst);
|
||||
}
|
||||
|
||||
static void DC8uvNoTopLeft(uint8_t *dst) { // DC with nothing
|
||||
Put8x8uv(0x8080808080808080ULL, dst);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// default C implementations
|
||||
|
||||
const VP8PredFunc VP8PredLuma4[NUM_BMODES] = {
|
||||
DC4, TM4, VE4, HE4, RD4, VR4, LD4, VL4, HD4, HU4
|
||||
};
|
||||
|
||||
const VP8PredFunc VP8PredLuma16[NUM_B_DC_MODES] = {
|
||||
DC16, TM16, VE16, HE16,
|
||||
DC16NoTop, DC16NoLeft, DC16NoTopLeft
|
||||
};
|
||||
|
||||
const VP8PredFunc VP8PredChroma8[NUM_B_DC_MODES] = {
|
||||
DC8uv, TM8uv, VE8uv, HE8uv,
|
||||
DC8uvNoTop, DC8uvNoLeft, DC8uvNoTopLeft
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Edge filtering functions
|
||||
|
||||
// 4 pixels in, 2 pixels out
|
||||
static WEBP_INLINE void do_filter2(uint8_t* p, int step) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
const int a = 3 * (q0 - p0) + sclip1[1020 + p1 - q1];
|
||||
const int a1 = sclip2[112 + ((a + 4) >> 3)];
|
||||
const int a2 = sclip2[112 + ((a + 3) >> 3)];
|
||||
p[-step] = clip1[255 + p0 + a2];
|
||||
p[ 0] = clip1[255 + q0 - a1];
|
||||
}
|
||||
|
||||
// 4 pixels in, 4 pixels out
|
||||
static WEBP_INLINE void do_filter4(uint8_t* p, int step) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
const int a = 3 * (q0 - p0);
|
||||
const int a1 = sclip2[112 + ((a + 4) >> 3)];
|
||||
const int a2 = sclip2[112 + ((a + 3) >> 3)];
|
||||
const int a3 = (a1 + 1) >> 1;
|
||||
p[-2*step] = clip1[255 + p1 + a3];
|
||||
p[- step] = clip1[255 + p0 + a2];
|
||||
p[ 0] = clip1[255 + q0 - a1];
|
||||
p[ step] = clip1[255 + q1 - a3];
|
||||
}
|
||||
|
||||
// 6 pixels in, 6 pixels out
|
||||
static WEBP_INLINE void do_filter6(uint8_t* p, int step) {
|
||||
const int p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
|
||||
const int q0 = p[0], q1 = p[step], q2 = p[2*step];
|
||||
const int a = sclip1[1020 + 3 * (q0 - p0) + sclip1[1020 + p1 - q1]];
|
||||
const int a1 = (27 * a + 63) >> 7; // eq. to ((3 * a + 7) * 9) >> 7
|
||||
const int a2 = (18 * a + 63) >> 7; // eq. to ((2 * a + 7) * 9) >> 7
|
||||
const int a3 = (9 * a + 63) >> 7; // eq. to ((1 * a + 7) * 9) >> 7
|
||||
p[-3*step] = clip1[255 + p2 + a3];
|
||||
p[-2*step] = clip1[255 + p1 + a2];
|
||||
p[- step] = clip1[255 + p0 + a1];
|
||||
p[ 0] = clip1[255 + q0 - a1];
|
||||
p[ step] = clip1[255 + q1 - a2];
|
||||
p[ 2*step] = clip1[255 + q2 - a3];
|
||||
}
|
||||
|
||||
static WEBP_INLINE int hev(const uint8_t* p, int step, int thresh) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
return (abs0[255 + p1 - p0] > thresh) || (abs0[255 + q1 - q0] > thresh);
|
||||
}
|
||||
|
||||
static WEBP_INLINE int needs_filter(const uint8_t* p, int step, int thresh) {
|
||||
const int p1 = p[-2*step], p0 = p[-step], q0 = p[0], q1 = p[step];
|
||||
return (2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) <= thresh;
|
||||
}
|
||||
|
||||
static WEBP_INLINE int needs_filter2(const uint8_t* p,
|
||||
int step, int t, int it) {
|
||||
const int p3 = p[-4*step], p2 = p[-3*step], p1 = p[-2*step], p0 = p[-step];
|
||||
const int q0 = p[0], q1 = p[step], q2 = p[2*step], q3 = p[3*step];
|
||||
if ((2 * abs0[255 + p0 - q0] + abs1[255 + p1 - q1]) > t)
|
||||
return 0;
|
||||
return abs0[255 + p3 - p2] <= it && abs0[255 + p2 - p1] <= it &&
|
||||
abs0[255 + p1 - p0] <= it && abs0[255 + q3 - q2] <= it &&
|
||||
abs0[255 + q2 - q1] <= it && abs0[255 + q1 - q0] <= it;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Simple In-loop filtering (Paragraph 15.2)
|
||||
|
||||
static void SimpleVFilter16(uint8_t* p, int stride, int thresh) {
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
if (needs_filter(p + i, stride, thresh)) {
|
||||
do_filter2(p + i, stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SimpleHFilter16(uint8_t* p, int stride, int thresh) {
|
||||
int i;
|
||||
for (i = 0; i < 16; ++i) {
|
||||
if (needs_filter(p + i * stride, 1, thresh)) {
|
||||
do_filter2(p + i * stride, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void SimpleVFilter16i(uint8_t* p, int stride, int thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4 * stride;
|
||||
SimpleVFilter16(p, stride, thresh);
|
||||
}
|
||||
}
|
||||
|
||||
static void SimpleHFilter16i(uint8_t* p, int stride, int thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4;
|
||||
SimpleHFilter16(p, stride, thresh);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Complex In-loop filtering (Paragraph 15.3)
|
||||
|
||||
static WEBP_INLINE void FilterLoop26(uint8_t* p,
|
||||
int hstride, int vstride, int size,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
while (size-- > 0) {
|
||||
if (needs_filter2(p, hstride, thresh, ithresh)) {
|
||||
if (hev(p, hstride, hev_thresh)) {
|
||||
do_filter2(p, hstride);
|
||||
} else {
|
||||
do_filter6(p, hstride);
|
||||
}
|
||||
}
|
||||
p += vstride;
|
||||
}
|
||||
}
|
||||
|
||||
static WEBP_INLINE void FilterLoop24(uint8_t* p,
|
||||
int hstride, int vstride, int size,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
while (size-- > 0) {
|
||||
if (needs_filter2(p, hstride, thresh, ithresh)) {
|
||||
if (hev(p, hstride, hev_thresh)) {
|
||||
do_filter2(p, hstride);
|
||||
} else {
|
||||
do_filter4(p, hstride);
|
||||
}
|
||||
}
|
||||
p += vstride;
|
||||
}
|
||||
}
|
||||
|
||||
// on macroblock edges
|
||||
static void VFilter16(uint8_t* p, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
FilterLoop26(p, stride, 1, 16, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
|
||||
static void HFilter16(uint8_t* p, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
FilterLoop26(p, 1, stride, 16, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
|
||||
// on three inner edges
|
||||
static void VFilter16i(uint8_t* p, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4 * stride;
|
||||
FilterLoop24(p, stride, 1, 16, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
}
|
||||
|
||||
static void HFilter16i(uint8_t* p, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4;
|
||||
FilterLoop24(p, 1, stride, 16, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
}
|
||||
|
||||
// 8-pixels wide variant, for chroma filtering
|
||||
static void VFilter8(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
FilterLoop26(u, stride, 1, 8, thresh, ithresh, hev_thresh);
|
||||
FilterLoop26(v, stride, 1, 8, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
|
||||
static void HFilter8(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
FilterLoop26(u, 1, stride, 8, thresh, ithresh, hev_thresh);
|
||||
FilterLoop26(v, 1, stride, 8, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
|
||||
static void VFilter8i(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
FilterLoop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
|
||||
FilterLoop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
|
||||
static void HFilter8i(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
FilterLoop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
|
||||
FilterLoop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
VP8DecIdct2 VP8Transform;
|
||||
VP8DecIdct VP8TransformUV;
|
||||
VP8DecIdct VP8TransformDC;
|
||||
VP8DecIdct VP8TransformDCUV;
|
||||
|
||||
VP8LumaFilterFunc VP8VFilter16;
|
||||
VP8LumaFilterFunc VP8HFilter16;
|
||||
VP8ChromaFilterFunc VP8VFilter8;
|
||||
VP8ChromaFilterFunc VP8HFilter8;
|
||||
VP8LumaFilterFunc VP8VFilter16i;
|
||||
VP8LumaFilterFunc VP8HFilter16i;
|
||||
VP8ChromaFilterFunc VP8VFilter8i;
|
||||
VP8ChromaFilterFunc VP8HFilter8i;
|
||||
VP8SimpleFilterFunc VP8SimpleVFilter16;
|
||||
VP8SimpleFilterFunc VP8SimpleHFilter16;
|
||||
VP8SimpleFilterFunc VP8SimpleVFilter16i;
|
||||
VP8SimpleFilterFunc VP8SimpleHFilter16i;
|
||||
|
||||
extern void VP8DspInitSSE2(void);
|
||||
extern void VP8DspInitNEON(void);
|
||||
|
||||
void VP8DspInit(void) {
|
||||
DspInitTables();
|
||||
|
||||
VP8Transform = TransformTwo;
|
||||
VP8TransformUV = TransformUV;
|
||||
VP8TransformDC = TransformDC;
|
||||
VP8TransformDCUV = TransformDCUV;
|
||||
|
||||
VP8VFilter16 = VFilter16;
|
||||
VP8HFilter16 = HFilter16;
|
||||
VP8VFilter8 = VFilter8;
|
||||
VP8HFilter8 = HFilter8;
|
||||
VP8VFilter16i = VFilter16i;
|
||||
VP8HFilter16i = HFilter16i;
|
||||
VP8VFilter8i = VFilter8i;
|
||||
VP8HFilter8i = HFilter8i;
|
||||
VP8SimpleVFilter16 = SimpleVFilter16;
|
||||
VP8SimpleHFilter16 = SimpleHFilter16;
|
||||
VP8SimpleVFilter16i = SimpleVFilter16i;
|
||||
VP8SimpleHFilter16i = SimpleHFilter16i;
|
||||
|
||||
// If defined, use CPUInfo() to overwrite some pointers with faster versions.
|
||||
if (VP8GetCPUInfo) {
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
if (VP8GetCPUInfo(kSSE2)) {
|
||||
VP8DspInitSSE2();
|
||||
}
|
||||
#elif defined(WEBP_USE_NEON)
|
||||
if (VP8GetCPUInfo(kNEON)) {
|
||||
VP8DspInitNEON();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
329
drivers/webpold/dsp/dec_neon.c
Normal file
329
drivers/webpold/dsp/dec_neon.c
Normal file
@ -0,0 +1,329 @@
|
||||
// Copyright 2012 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// ARM NEON version of dsp functions and loop filtering.
|
||||
//
|
||||
// Authors: Somnath Banerjee (somnath@google.com)
|
||||
// Johann Koenig (johannkoenig@google.com)
|
||||
|
||||
#include "./dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_NEON)
|
||||
|
||||
#include "../dec/vp8i.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define QRegs "q0", "q1", "q2", "q3", "q4", "q5", "q6", "q7", \
|
||||
"q8", "q9", "q10", "q11", "q12", "q13", "q14", "q15"
|
||||
|
||||
#define FLIP_SIGN_BIT2(a, b, s) \
|
||||
"veor " #a "," #a "," #s " \n" \
|
||||
"veor " #b "," #b "," #s " \n" \
|
||||
|
||||
#define FLIP_SIGN_BIT4(a, b, c, d, s) \
|
||||
FLIP_SIGN_BIT2(a, b, s) \
|
||||
FLIP_SIGN_BIT2(c, d, s) \
|
||||
|
||||
#define NEEDS_FILTER(p1, p0, q0, q1, thresh, mask) \
|
||||
"vabd.u8 q15," #p0 "," #q0 " \n" /* abs(p0 - q0) */ \
|
||||
"vabd.u8 q14," #p1 "," #q1 " \n" /* abs(p1 - q1) */ \
|
||||
"vqadd.u8 q15, q15, q15 \n" /* abs(p0 - q0) * 2 */ \
|
||||
"vshr.u8 q14, q14, #1 \n" /* abs(p1 - q1) / 2 */ \
|
||||
"vqadd.u8 q15, q15, q14 \n" /* abs(p0 - q0) * 2 + abs(p1 - q1) / 2 */ \
|
||||
"vdup.8 q14, " #thresh " \n" \
|
||||
"vcge.u8 " #mask ", q14, q15 \n" /* mask <= thresh */
|
||||
|
||||
#define GET_BASE_DELTA(p1, p0, q0, q1, o) \
|
||||
"vqsub.s8 q15," #q0 "," #p0 " \n" /* (q0 - p0) */ \
|
||||
"vqsub.s8 " #o "," #p1 "," #q1 " \n" /* (p1 - q1) */ \
|
||||
"vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 1 * (p0 - q0) */ \
|
||||
"vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 2 * (p0 - q0) */ \
|
||||
"vqadd.s8 " #o "," #o ", q15 \n" /* (p1 - q1) + 3 * (p0 - q0) */
|
||||
|
||||
#define DO_SIMPLE_FILTER(p0, q0, fl) \
|
||||
"vmov.i8 q15, #0x03 \n" \
|
||||
"vqadd.s8 q15, q15, " #fl " \n" /* filter1 = filter + 3 */ \
|
||||
"vshr.s8 q15, q15, #3 \n" /* filter1 >> 3 */ \
|
||||
"vqadd.s8 " #p0 "," #p0 ", q15 \n" /* p0 += filter1 */ \
|
||||
\
|
||||
"vmov.i8 q15, #0x04 \n" \
|
||||
"vqadd.s8 q15, q15, " #fl " \n" /* filter1 = filter + 4 */ \
|
||||
"vshr.s8 q15, q15, #3 \n" /* filter2 >> 3 */ \
|
||||
"vqsub.s8 " #q0 "," #q0 ", q15 \n" /* q0 -= filter2 */
|
||||
|
||||
// Applies filter on 2 pixels (p0 and q0)
|
||||
#define DO_FILTER2(p1, p0, q0, q1, thresh) \
|
||||
NEEDS_FILTER(p1, p0, q0, q1, thresh, q9) /* filter mask in q9 */ \
|
||||
"vmov.i8 q10, #0x80 \n" /* sign bit */ \
|
||||
FLIP_SIGN_BIT4(p1, p0, q0, q1, q10) /* convert to signed value */ \
|
||||
GET_BASE_DELTA(p1, p0, q0, q1, q11) /* get filter level */ \
|
||||
"vand q9, q9, q11 \n" /* apply filter mask */ \
|
||||
DO_SIMPLE_FILTER(p0, q0, q9) /* apply filter */ \
|
||||
FLIP_SIGN_BIT2(p0, q0, q10)
|
||||
|
||||
// Load/Store vertical edge
|
||||
#define LOAD8x4(c1, c2, c3, c4, b1, b2, stride) \
|
||||
"vld4.8 {" #c1"[0], " #c2"[0], " #c3"[0], " #c4"[0]}," #b1 "," #stride"\n" \
|
||||
"vld4.8 {" #c1"[1], " #c2"[1], " #c3"[1], " #c4"[1]}," #b2 "," #stride"\n" \
|
||||
"vld4.8 {" #c1"[2], " #c2"[2], " #c3"[2], " #c4"[2]}," #b1 "," #stride"\n" \
|
||||
"vld4.8 {" #c1"[3], " #c2"[3], " #c3"[3], " #c4"[3]}," #b2 "," #stride"\n" \
|
||||
"vld4.8 {" #c1"[4], " #c2"[4], " #c3"[4], " #c4"[4]}," #b1 "," #stride"\n" \
|
||||
"vld4.8 {" #c1"[5], " #c2"[5], " #c3"[5], " #c4"[5]}," #b2 "," #stride"\n" \
|
||||
"vld4.8 {" #c1"[6], " #c2"[6], " #c3"[6], " #c4"[6]}," #b1 "," #stride"\n" \
|
||||
"vld4.8 {" #c1"[7], " #c2"[7], " #c3"[7], " #c4"[7]}," #b2 "," #stride"\n"
|
||||
|
||||
#define STORE8x2(c1, c2, p,stride) \
|
||||
"vst2.8 {" #c1"[0], " #c2"[0]}," #p "," #stride " \n" \
|
||||
"vst2.8 {" #c1"[1], " #c2"[1]}," #p "," #stride " \n" \
|
||||
"vst2.8 {" #c1"[2], " #c2"[2]}," #p "," #stride " \n" \
|
||||
"vst2.8 {" #c1"[3], " #c2"[3]}," #p "," #stride " \n" \
|
||||
"vst2.8 {" #c1"[4], " #c2"[4]}," #p "," #stride " \n" \
|
||||
"vst2.8 {" #c1"[5], " #c2"[5]}," #p "," #stride " \n" \
|
||||
"vst2.8 {" #c1"[6], " #c2"[6]}," #p "," #stride " \n" \
|
||||
"vst2.8 {" #c1"[7], " #c2"[7]}," #p "," #stride " \n"
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Simple In-loop filtering (Paragraph 15.2)
|
||||
|
||||
static void SimpleVFilter16NEON(uint8_t* p, int stride, int thresh) {
|
||||
__asm__ volatile (
|
||||
"sub %[p], %[p], %[stride], lsl #1 \n" // p -= 2 * stride
|
||||
|
||||
"vld1.u8 {q1}, [%[p]], %[stride] \n" // p1
|
||||
"vld1.u8 {q2}, [%[p]], %[stride] \n" // p0
|
||||
"vld1.u8 {q3}, [%[p]], %[stride] \n" // q0
|
||||
"vld1.u8 {q4}, [%[p]] \n" // q1
|
||||
|
||||
DO_FILTER2(q1, q2, q3, q4, %[thresh])
|
||||
|
||||
"sub %[p], %[p], %[stride], lsl #1 \n" // p -= 2 * stride
|
||||
|
||||
"vst1.u8 {q2}, [%[p]], %[stride] \n" // store op0
|
||||
"vst1.u8 {q3}, [%[p]] \n" // store oq0
|
||||
: [p] "+r"(p)
|
||||
: [stride] "r"(stride), [thresh] "r"(thresh)
|
||||
: "memory", QRegs
|
||||
);
|
||||
}
|
||||
|
||||
static void SimpleHFilter16NEON(uint8_t* p, int stride, int thresh) {
|
||||
__asm__ volatile (
|
||||
"sub r4, %[p], #2 \n" // base1 = p - 2
|
||||
"lsl r6, %[stride], #1 \n" // r6 = 2 * stride
|
||||
"add r5, r4, %[stride] \n" // base2 = base1 + stride
|
||||
|
||||
LOAD8x4(d2, d3, d4, d5, [r4], [r5], r6)
|
||||
LOAD8x4(d6, d7, d8, d9, [r4], [r5], r6)
|
||||
"vswp d3, d6 \n" // p1:q1 p0:q3
|
||||
"vswp d5, d8 \n" // q0:q2 q1:q4
|
||||
"vswp q2, q3 \n" // p1:q1 p0:q2 q0:q3 q1:q4
|
||||
|
||||
DO_FILTER2(q1, q2, q3, q4, %[thresh])
|
||||
|
||||
"sub %[p], %[p], #1 \n" // p - 1
|
||||
|
||||
"vswp d5, d6 \n"
|
||||
STORE8x2(d4, d5, [%[p]], %[stride])
|
||||
STORE8x2(d6, d7, [%[p]], %[stride])
|
||||
|
||||
: [p] "+r"(p)
|
||||
: [stride] "r"(stride), [thresh] "r"(thresh)
|
||||
: "memory", "r4", "r5", "r6", QRegs
|
||||
);
|
||||
}
|
||||
|
||||
static void SimpleVFilter16iNEON(uint8_t* p, int stride, int thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4 * stride;
|
||||
SimpleVFilter16NEON(p, stride, thresh);
|
||||
}
|
||||
}
|
||||
|
||||
static void SimpleHFilter16iNEON(uint8_t* p, int stride, int thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4;
|
||||
SimpleHFilter16NEON(p, stride, thresh);
|
||||
}
|
||||
}
|
||||
|
||||
static void TransformOneNEON(const int16_t *in, uint8_t *dst) {
|
||||
const int kBPS = BPS;
|
||||
const int16_t constants[] = {20091, 17734, 0, 0};
|
||||
/* kC1, kC2. Padded because vld1.16 loads 8 bytes
|
||||
* Technically these are unsigned but vqdmulh is only available in signed.
|
||||
* vqdmulh returns high half (effectively >> 16) but also doubles the value,
|
||||
* changing the >> 16 to >> 15 and requiring an additional >> 1.
|
||||
* We use this to our advantage with kC2. The canonical value is 35468.
|
||||
* However, the high bit is set so treating it as signed will give incorrect
|
||||
* results. We avoid this by down shifting by 1 here to clear the highest bit.
|
||||
* Combined with the doubling effect of vqdmulh we get >> 16.
|
||||
* This can not be applied to kC1 because the lowest bit is set. Down shifting
|
||||
* the constant would reduce precision.
|
||||
*/
|
||||
|
||||
/* libwebp uses a trick to avoid some extra addition that libvpx does.
|
||||
* Instead of:
|
||||
* temp2 = ip[12] + ((ip[12] * cospi8sqrt2minus1) >> 16);
|
||||
* libwebp adds 1 << 16 to cospi8sqrt2minus1 (kC1). However, this causes the
|
||||
* same issue with kC1 and vqdmulh that we work around by down shifting kC2
|
||||
*/
|
||||
|
||||
/* Adapted from libvpx: vp8/common/arm/neon/shortidct4x4llm_neon.asm */
|
||||
__asm__ volatile (
|
||||
"vld1.16 {q1, q2}, [%[in]] \n"
|
||||
"vld1.16 {d0}, [%[constants]] \n"
|
||||
|
||||
/* d2: in[0]
|
||||
* d3: in[8]
|
||||
* d4: in[4]
|
||||
* d5: in[12]
|
||||
*/
|
||||
"vswp d3, d4 \n"
|
||||
|
||||
/* q8 = {in[4], in[12]} * kC1 * 2 >> 16
|
||||
* q9 = {in[4], in[12]} * kC2 >> 16
|
||||
*/
|
||||
"vqdmulh.s16 q8, q2, d0[0] \n"
|
||||
"vqdmulh.s16 q9, q2, d0[1] \n"
|
||||
|
||||
/* d22 = a = in[0] + in[8]
|
||||
* d23 = b = in[0] - in[8]
|
||||
*/
|
||||
"vqadd.s16 d22, d2, d3 \n"
|
||||
"vqsub.s16 d23, d2, d3 \n"
|
||||
|
||||
/* The multiplication should be x * kC1 >> 16
|
||||
* However, with vqdmulh we get x * kC1 * 2 >> 16
|
||||
* (multiply, double, return high half)
|
||||
* We avoided this in kC2 by pre-shifting the constant.
|
||||
* q8 = in[4]/[12] * kC1 >> 16
|
||||
*/
|
||||
"vshr.s16 q8, q8, #1 \n"
|
||||
|
||||
/* Add {in[4], in[12]} back after the multiplication. This is handled by
|
||||
* adding 1 << 16 to kC1 in the libwebp C code.
|
||||
*/
|
||||
"vqadd.s16 q8, q2, q8 \n"
|
||||
|
||||
/* d20 = c = in[4]*kC2 - in[12]*kC1
|
||||
* d21 = d = in[4]*kC1 + in[12]*kC2
|
||||
*/
|
||||
"vqsub.s16 d20, d18, d17 \n"
|
||||
"vqadd.s16 d21, d19, d16 \n"
|
||||
|
||||
/* d2 = tmp[0] = a + d
|
||||
* d3 = tmp[1] = b + c
|
||||
* d4 = tmp[2] = b - c
|
||||
* d5 = tmp[3] = a - d
|
||||
*/
|
||||
"vqadd.s16 d2, d22, d21 \n"
|
||||
"vqadd.s16 d3, d23, d20 \n"
|
||||
"vqsub.s16 d4, d23, d20 \n"
|
||||
"vqsub.s16 d5, d22, d21 \n"
|
||||
|
||||
"vzip.16 q1, q2 \n"
|
||||
"vzip.16 q1, q2 \n"
|
||||
|
||||
"vswp d3, d4 \n"
|
||||
|
||||
/* q8 = {tmp[4], tmp[12]} * kC1 * 2 >> 16
|
||||
* q9 = {tmp[4], tmp[12]} * kC2 >> 16
|
||||
*/
|
||||
"vqdmulh.s16 q8, q2, d0[0] \n"
|
||||
"vqdmulh.s16 q9, q2, d0[1] \n"
|
||||
|
||||
/* d22 = a = tmp[0] + tmp[8]
|
||||
* d23 = b = tmp[0] - tmp[8]
|
||||
*/
|
||||
"vqadd.s16 d22, d2, d3 \n"
|
||||
"vqsub.s16 d23, d2, d3 \n"
|
||||
|
||||
/* See long winded explanations prior */
|
||||
"vshr.s16 q8, q8, #1 \n"
|
||||
"vqadd.s16 q8, q2, q8 \n"
|
||||
|
||||
/* d20 = c = in[4]*kC2 - in[12]*kC1
|
||||
* d21 = d = in[4]*kC1 + in[12]*kC2
|
||||
*/
|
||||
"vqsub.s16 d20, d18, d17 \n"
|
||||
"vqadd.s16 d21, d19, d16 \n"
|
||||
|
||||
/* d2 = tmp[0] = a + d
|
||||
* d3 = tmp[1] = b + c
|
||||
* d4 = tmp[2] = b - c
|
||||
* d5 = tmp[3] = a - d
|
||||
*/
|
||||
"vqadd.s16 d2, d22, d21 \n"
|
||||
"vqadd.s16 d3, d23, d20 \n"
|
||||
"vqsub.s16 d4, d23, d20 \n"
|
||||
"vqsub.s16 d5, d22, d21 \n"
|
||||
|
||||
"vld1.32 d6[0], [%[dst]], %[kBPS] \n"
|
||||
"vld1.32 d6[1], [%[dst]], %[kBPS] \n"
|
||||
"vld1.32 d7[0], [%[dst]], %[kBPS] \n"
|
||||
"vld1.32 d7[1], [%[dst]], %[kBPS] \n"
|
||||
|
||||
"sub %[dst], %[dst], %[kBPS], lsl #2 \n"
|
||||
|
||||
/* (val) + 4 >> 3 */
|
||||
"vrshr.s16 d2, d2, #3 \n"
|
||||
"vrshr.s16 d3, d3, #3 \n"
|
||||
"vrshr.s16 d4, d4, #3 \n"
|
||||
"vrshr.s16 d5, d5, #3 \n"
|
||||
|
||||
"vzip.16 q1, q2 \n"
|
||||
"vzip.16 q1, q2 \n"
|
||||
|
||||
/* Must accumulate before saturating */
|
||||
"vmovl.u8 q8, d6 \n"
|
||||
"vmovl.u8 q9, d7 \n"
|
||||
|
||||
"vqadd.s16 q1, q1, q8 \n"
|
||||
"vqadd.s16 q2, q2, q9 \n"
|
||||
|
||||
"vqmovun.s16 d0, q1 \n"
|
||||
"vqmovun.s16 d1, q2 \n"
|
||||
|
||||
"vst1.32 d0[0], [%[dst]], %[kBPS] \n"
|
||||
"vst1.32 d0[1], [%[dst]], %[kBPS] \n"
|
||||
"vst1.32 d1[0], [%[dst]], %[kBPS] \n"
|
||||
"vst1.32 d1[1], [%[dst]] \n"
|
||||
|
||||
: [in] "+r"(in), [dst] "+r"(dst) /* modified registers */
|
||||
: [kBPS] "r"(kBPS), [constants] "r"(constants) /* constants */
|
||||
: "memory", "q0", "q1", "q2", "q8", "q9", "q10", "q11" /* clobbered */
|
||||
);
|
||||
}
|
||||
|
||||
static void TransformTwoNEON(const int16_t* in, uint8_t* dst, int do_two) {
|
||||
TransformOneNEON(in, dst);
|
||||
if (do_two) {
|
||||
TransformOneNEON(in + 16, dst + 4);
|
||||
}
|
||||
}
|
||||
|
||||
extern void VP8DspInitNEON(void);
|
||||
|
||||
void VP8DspInitNEON(void) {
|
||||
VP8Transform = TransformTwoNEON;
|
||||
|
||||
VP8SimpleVFilter16 = SimpleVFilter16NEON;
|
||||
VP8SimpleHFilter16 = SimpleHFilter16NEON;
|
||||
VP8SimpleVFilter16i = SimpleVFilter16iNEON;
|
||||
VP8SimpleHFilter16i = SimpleHFilter16iNEON;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_USE_NEON
|
||||
903
drivers/webpold/dsp/dec_sse2.c
Normal file
903
drivers/webpold/dsp/dec_sse2.c
Normal file
@ -0,0 +1,903 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// SSE2 version of some decoding functions (idct, loop filtering).
|
||||
//
|
||||
// Author: somnath@google.com (Somnath Banerjee)
|
||||
// cduvivier@google.com (Christian Duvivier)
|
||||
|
||||
#include "./dsp.h"
|
||||
|
||||
#if defined(WEBP_USE_SSE2)
|
||||
|
||||
#include <emmintrin.h>
|
||||
#include "../dec/vp8i.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Transforms (Paragraph 14.4)
|
||||
|
||||
static void TransformSSE2(const int16_t* in, uint8_t* dst, int do_two) {
|
||||
// This implementation makes use of 16-bit fixed point versions of two
|
||||
// multiply constants:
|
||||
// K1 = sqrt(2) * cos (pi/8) ~= 85627 / 2^16
|
||||
// K2 = sqrt(2) * sin (pi/8) ~= 35468 / 2^16
|
||||
//
|
||||
// To be able to use signed 16-bit integers, we use the following trick to
|
||||
// have constants within range:
|
||||
// - Associated constants are obtained by subtracting the 16-bit fixed point
|
||||
// version of one:
|
||||
// k = K - (1 << 16) => K = k + (1 << 16)
|
||||
// K1 = 85267 => k1 = 20091
|
||||
// K2 = 35468 => k2 = -30068
|
||||
// - The multiplication of a variable by a constant become the sum of the
|
||||
// variable and the multiplication of that variable by the associated
|
||||
// constant:
|
||||
// (x * K) >> 16 = (x * (k + (1 << 16))) >> 16 = ((x * k ) >> 16) + x
|
||||
const __m128i k1 = _mm_set1_epi16(20091);
|
||||
const __m128i k2 = _mm_set1_epi16(-30068);
|
||||
__m128i T0, T1, T2, T3;
|
||||
|
||||
// Load and concatenate the transform coefficients (we'll do two transforms
|
||||
// in parallel). In the case of only one transform, the second half of the
|
||||
// vectors will just contain random value we'll never use nor store.
|
||||
__m128i in0, in1, in2, in3;
|
||||
{
|
||||
in0 = _mm_loadl_epi64((__m128i*)&in[0]);
|
||||
in1 = _mm_loadl_epi64((__m128i*)&in[4]);
|
||||
in2 = _mm_loadl_epi64((__m128i*)&in[8]);
|
||||
in3 = _mm_loadl_epi64((__m128i*)&in[12]);
|
||||
// a00 a10 a20 a30 x x x x
|
||||
// a01 a11 a21 a31 x x x x
|
||||
// a02 a12 a22 a32 x x x x
|
||||
// a03 a13 a23 a33 x x x x
|
||||
if (do_two) {
|
||||
const __m128i inB0 = _mm_loadl_epi64((__m128i*)&in[16]);
|
||||
const __m128i inB1 = _mm_loadl_epi64((__m128i*)&in[20]);
|
||||
const __m128i inB2 = _mm_loadl_epi64((__m128i*)&in[24]);
|
||||
const __m128i inB3 = _mm_loadl_epi64((__m128i*)&in[28]);
|
||||
in0 = _mm_unpacklo_epi64(in0, inB0);
|
||||
in1 = _mm_unpacklo_epi64(in1, inB1);
|
||||
in2 = _mm_unpacklo_epi64(in2, inB2);
|
||||
in3 = _mm_unpacklo_epi64(in3, inB3);
|
||||
// a00 a10 a20 a30 b00 b10 b20 b30
|
||||
// a01 a11 a21 a31 b01 b11 b21 b31
|
||||
// a02 a12 a22 a32 b02 b12 b22 b32
|
||||
// a03 a13 a23 a33 b03 b13 b23 b33
|
||||
}
|
||||
}
|
||||
|
||||
// Vertical pass and subsequent transpose.
|
||||
{
|
||||
// First pass, c and d calculations are longer because of the "trick"
|
||||
// multiplications.
|
||||
const __m128i a = _mm_add_epi16(in0, in2);
|
||||
const __m128i b = _mm_sub_epi16(in0, in2);
|
||||
// c = MUL(in1, K2) - MUL(in3, K1) = MUL(in1, k2) - MUL(in3, k1) + in1 - in3
|
||||
const __m128i c1 = _mm_mulhi_epi16(in1, k2);
|
||||
const __m128i c2 = _mm_mulhi_epi16(in3, k1);
|
||||
const __m128i c3 = _mm_sub_epi16(in1, in3);
|
||||
const __m128i c4 = _mm_sub_epi16(c1, c2);
|
||||
const __m128i c = _mm_add_epi16(c3, c4);
|
||||
// d = MUL(in1, K1) + MUL(in3, K2) = MUL(in1, k1) + MUL(in3, k2) + in1 + in3
|
||||
const __m128i d1 = _mm_mulhi_epi16(in1, k1);
|
||||
const __m128i d2 = _mm_mulhi_epi16(in3, k2);
|
||||
const __m128i d3 = _mm_add_epi16(in1, in3);
|
||||
const __m128i d4 = _mm_add_epi16(d1, d2);
|
||||
const __m128i d = _mm_add_epi16(d3, d4);
|
||||
|
||||
// Second pass.
|
||||
const __m128i tmp0 = _mm_add_epi16(a, d);
|
||||
const __m128i tmp1 = _mm_add_epi16(b, c);
|
||||
const __m128i tmp2 = _mm_sub_epi16(b, c);
|
||||
const __m128i tmp3 = _mm_sub_epi16(a, d);
|
||||
|
||||
// Transpose the two 4x4.
|
||||
// a00 a01 a02 a03 b00 b01 b02 b03
|
||||
// a10 a11 a12 a13 b10 b11 b12 b13
|
||||
// a20 a21 a22 a23 b20 b21 b22 b23
|
||||
// a30 a31 a32 a33 b30 b31 b32 b33
|
||||
const __m128i transpose0_0 = _mm_unpacklo_epi16(tmp0, tmp1);
|
||||
const __m128i transpose0_1 = _mm_unpacklo_epi16(tmp2, tmp3);
|
||||
const __m128i transpose0_2 = _mm_unpackhi_epi16(tmp0, tmp1);
|
||||
const __m128i transpose0_3 = _mm_unpackhi_epi16(tmp2, tmp3);
|
||||
// a00 a10 a01 a11 a02 a12 a03 a13
|
||||
// a20 a30 a21 a31 a22 a32 a23 a33
|
||||
// b00 b10 b01 b11 b02 b12 b03 b13
|
||||
// b20 b30 b21 b31 b22 b32 b23 b33
|
||||
const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
|
||||
const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
|
||||
const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
|
||||
const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
|
||||
// a00 a10 a20 a30 a01 a11 a21 a31
|
||||
// b00 b10 b20 b30 b01 b11 b21 b31
|
||||
// a02 a12 a22 a32 a03 a13 a23 a33
|
||||
// b02 b12 a22 b32 b03 b13 b23 b33
|
||||
T0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
|
||||
T1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
|
||||
T2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
|
||||
T3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
|
||||
// a00 a10 a20 a30 b00 b10 b20 b30
|
||||
// a01 a11 a21 a31 b01 b11 b21 b31
|
||||
// a02 a12 a22 a32 b02 b12 b22 b32
|
||||
// a03 a13 a23 a33 b03 b13 b23 b33
|
||||
}
|
||||
|
||||
// Horizontal pass and subsequent transpose.
|
||||
{
|
||||
// First pass, c and d calculations are longer because of the "trick"
|
||||
// multiplications.
|
||||
const __m128i four = _mm_set1_epi16(4);
|
||||
const __m128i dc = _mm_add_epi16(T0, four);
|
||||
const __m128i a = _mm_add_epi16(dc, T2);
|
||||
const __m128i b = _mm_sub_epi16(dc, T2);
|
||||
// c = MUL(T1, K2) - MUL(T3, K1) = MUL(T1, k2) - MUL(T3, k1) + T1 - T3
|
||||
const __m128i c1 = _mm_mulhi_epi16(T1, k2);
|
||||
const __m128i c2 = _mm_mulhi_epi16(T3, k1);
|
||||
const __m128i c3 = _mm_sub_epi16(T1, T3);
|
||||
const __m128i c4 = _mm_sub_epi16(c1, c2);
|
||||
const __m128i c = _mm_add_epi16(c3, c4);
|
||||
// d = MUL(T1, K1) + MUL(T3, K2) = MUL(T1, k1) + MUL(T3, k2) + T1 + T3
|
||||
const __m128i d1 = _mm_mulhi_epi16(T1, k1);
|
||||
const __m128i d2 = _mm_mulhi_epi16(T3, k2);
|
||||
const __m128i d3 = _mm_add_epi16(T1, T3);
|
||||
const __m128i d4 = _mm_add_epi16(d1, d2);
|
||||
const __m128i d = _mm_add_epi16(d3, d4);
|
||||
|
||||
// Second pass.
|
||||
const __m128i tmp0 = _mm_add_epi16(a, d);
|
||||
const __m128i tmp1 = _mm_add_epi16(b, c);
|
||||
const __m128i tmp2 = _mm_sub_epi16(b, c);
|
||||
const __m128i tmp3 = _mm_sub_epi16(a, d);
|
||||
const __m128i shifted0 = _mm_srai_epi16(tmp0, 3);
|
||||
const __m128i shifted1 = _mm_srai_epi16(tmp1, 3);
|
||||
const __m128i shifted2 = _mm_srai_epi16(tmp2, 3);
|
||||
const __m128i shifted3 = _mm_srai_epi16(tmp3, 3);
|
||||
|
||||
// Transpose the two 4x4.
|
||||
// a00 a01 a02 a03 b00 b01 b02 b03
|
||||
// a10 a11 a12 a13 b10 b11 b12 b13
|
||||
// a20 a21 a22 a23 b20 b21 b22 b23
|
||||
// a30 a31 a32 a33 b30 b31 b32 b33
|
||||
const __m128i transpose0_0 = _mm_unpacklo_epi16(shifted0, shifted1);
|
||||
const __m128i transpose0_1 = _mm_unpacklo_epi16(shifted2, shifted3);
|
||||
const __m128i transpose0_2 = _mm_unpackhi_epi16(shifted0, shifted1);
|
||||
const __m128i transpose0_3 = _mm_unpackhi_epi16(shifted2, shifted3);
|
||||
// a00 a10 a01 a11 a02 a12 a03 a13
|
||||
// a20 a30 a21 a31 a22 a32 a23 a33
|
||||
// b00 b10 b01 b11 b02 b12 b03 b13
|
||||
// b20 b30 b21 b31 b22 b32 b23 b33
|
||||
const __m128i transpose1_0 = _mm_unpacklo_epi32(transpose0_0, transpose0_1);
|
||||
const __m128i transpose1_1 = _mm_unpacklo_epi32(transpose0_2, transpose0_3);
|
||||
const __m128i transpose1_2 = _mm_unpackhi_epi32(transpose0_0, transpose0_1);
|
||||
const __m128i transpose1_3 = _mm_unpackhi_epi32(transpose0_2, transpose0_3);
|
||||
// a00 a10 a20 a30 a01 a11 a21 a31
|
||||
// b00 b10 b20 b30 b01 b11 b21 b31
|
||||
// a02 a12 a22 a32 a03 a13 a23 a33
|
||||
// b02 b12 a22 b32 b03 b13 b23 b33
|
||||
T0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1);
|
||||
T1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1);
|
||||
T2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3);
|
||||
T3 = _mm_unpackhi_epi64(transpose1_2, transpose1_3);
|
||||
// a00 a10 a20 a30 b00 b10 b20 b30
|
||||
// a01 a11 a21 a31 b01 b11 b21 b31
|
||||
// a02 a12 a22 a32 b02 b12 b22 b32
|
||||
// a03 a13 a23 a33 b03 b13 b23 b33
|
||||
}
|
||||
|
||||
// Add inverse transform to 'dst' and store.
|
||||
{
|
||||
const __m128i zero = _mm_set1_epi16(0);
|
||||
// Load the reference(s).
|
||||
__m128i dst0, dst1, dst2, dst3;
|
||||
if (do_two) {
|
||||
// Load eight bytes/pixels per line.
|
||||
dst0 = _mm_loadl_epi64((__m128i*)&dst[0 * BPS]);
|
||||
dst1 = _mm_loadl_epi64((__m128i*)&dst[1 * BPS]);
|
||||
dst2 = _mm_loadl_epi64((__m128i*)&dst[2 * BPS]);
|
||||
dst3 = _mm_loadl_epi64((__m128i*)&dst[3 * BPS]);
|
||||
} else {
|
||||
// Load four bytes/pixels per line.
|
||||
dst0 = _mm_cvtsi32_si128(*(int*)&dst[0 * BPS]);
|
||||
dst1 = _mm_cvtsi32_si128(*(int*)&dst[1 * BPS]);
|
||||
dst2 = _mm_cvtsi32_si128(*(int*)&dst[2 * BPS]);
|
||||
dst3 = _mm_cvtsi32_si128(*(int*)&dst[3 * BPS]);
|
||||
}
|
||||
// Convert to 16b.
|
||||
dst0 = _mm_unpacklo_epi8(dst0, zero);
|
||||
dst1 = _mm_unpacklo_epi8(dst1, zero);
|
||||
dst2 = _mm_unpacklo_epi8(dst2, zero);
|
||||
dst3 = _mm_unpacklo_epi8(dst3, zero);
|
||||
// Add the inverse transform(s).
|
||||
dst0 = _mm_add_epi16(dst0, T0);
|
||||
dst1 = _mm_add_epi16(dst1, T1);
|
||||
dst2 = _mm_add_epi16(dst2, T2);
|
||||
dst3 = _mm_add_epi16(dst3, T3);
|
||||
// Unsigned saturate to 8b.
|
||||
dst0 = _mm_packus_epi16(dst0, dst0);
|
||||
dst1 = _mm_packus_epi16(dst1, dst1);
|
||||
dst2 = _mm_packus_epi16(dst2, dst2);
|
||||
dst3 = _mm_packus_epi16(dst3, dst3);
|
||||
// Store the results.
|
||||
if (do_two) {
|
||||
// Store eight bytes/pixels per line.
|
||||
_mm_storel_epi64((__m128i*)&dst[0 * BPS], dst0);
|
||||
_mm_storel_epi64((__m128i*)&dst[1 * BPS], dst1);
|
||||
_mm_storel_epi64((__m128i*)&dst[2 * BPS], dst2);
|
||||
_mm_storel_epi64((__m128i*)&dst[3 * BPS], dst3);
|
||||
} else {
|
||||
// Store four bytes/pixels per line.
|
||||
*((int32_t *)&dst[0 * BPS]) = _mm_cvtsi128_si32(dst0);
|
||||
*((int32_t *)&dst[1 * BPS]) = _mm_cvtsi128_si32(dst1);
|
||||
*((int32_t *)&dst[2 * BPS]) = _mm_cvtsi128_si32(dst2);
|
||||
*((int32_t *)&dst[3 * BPS]) = _mm_cvtsi128_si32(dst3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Loop Filter (Paragraph 15)
|
||||
|
||||
// Compute abs(p - q) = subs(p - q) OR subs(q - p)
|
||||
#define MM_ABS(p, q) _mm_or_si128( \
|
||||
_mm_subs_epu8((q), (p)), \
|
||||
_mm_subs_epu8((p), (q)))
|
||||
|
||||
// Shift each byte of "a" by N bits while preserving by the sign bit.
|
||||
//
|
||||
// It first shifts the lower bytes of the words and then the upper bytes and
|
||||
// then merges the results together.
|
||||
#define SIGNED_SHIFT_N(a, N) { \
|
||||
__m128i t = a; \
|
||||
t = _mm_slli_epi16(t, 8); \
|
||||
t = _mm_srai_epi16(t, N); \
|
||||
t = _mm_srli_epi16(t, 8); \
|
||||
\
|
||||
a = _mm_srai_epi16(a, N + 8); \
|
||||
a = _mm_slli_epi16(a, 8); \
|
||||
\
|
||||
a = _mm_or_si128(t, a); \
|
||||
}
|
||||
|
||||
#define FLIP_SIGN_BIT2(a, b) { \
|
||||
a = _mm_xor_si128(a, sign_bit); \
|
||||
b = _mm_xor_si128(b, sign_bit); \
|
||||
}
|
||||
|
||||
#define FLIP_SIGN_BIT4(a, b, c, d) { \
|
||||
FLIP_SIGN_BIT2(a, b); \
|
||||
FLIP_SIGN_BIT2(c, d); \
|
||||
}
|
||||
|
||||
#define GET_NOTHEV(p1, p0, q0, q1, hev_thresh, not_hev) { \
|
||||
const __m128i zero = _mm_setzero_si128(); \
|
||||
const __m128i t1 = MM_ABS(p1, p0); \
|
||||
const __m128i t2 = MM_ABS(q1, q0); \
|
||||
\
|
||||
const __m128i h = _mm_set1_epi8(hev_thresh); \
|
||||
const __m128i t3 = _mm_subs_epu8(t1, h); /* abs(p1 - p0) - hev_tresh */ \
|
||||
const __m128i t4 = _mm_subs_epu8(t2, h); /* abs(q1 - q0) - hev_tresh */ \
|
||||
\
|
||||
not_hev = _mm_or_si128(t3, t4); \
|
||||
not_hev = _mm_cmpeq_epi8(not_hev, zero); /* not_hev <= t1 && not_hev <= t2 */\
|
||||
}
|
||||
|
||||
#define GET_BASE_DELTA(p1, p0, q0, q1, o) { \
|
||||
const __m128i qp0 = _mm_subs_epi8(q0, p0); /* q0 - p0 */ \
|
||||
o = _mm_subs_epi8(p1, q1); /* p1 - q1 */ \
|
||||
o = _mm_adds_epi8(o, qp0); /* p1 - q1 + 1 * (q0 - p0) */ \
|
||||
o = _mm_adds_epi8(o, qp0); /* p1 - q1 + 2 * (q0 - p0) */ \
|
||||
o = _mm_adds_epi8(o, qp0); /* p1 - q1 + 3 * (q0 - p0) */ \
|
||||
}
|
||||
|
||||
#define DO_SIMPLE_FILTER(p0, q0, fl) { \
|
||||
const __m128i three = _mm_set1_epi8(3); \
|
||||
const __m128i four = _mm_set1_epi8(4); \
|
||||
__m128i v3 = _mm_adds_epi8(fl, three); \
|
||||
__m128i v4 = _mm_adds_epi8(fl, four); \
|
||||
\
|
||||
/* Do +4 side */ \
|
||||
SIGNED_SHIFT_N(v4, 3); /* v4 >> 3 */ \
|
||||
q0 = _mm_subs_epi8(q0, v4); /* q0 -= v4 */ \
|
||||
\
|
||||
/* Now do +3 side */ \
|
||||
SIGNED_SHIFT_N(v3, 3); /* v3 >> 3 */ \
|
||||
p0 = _mm_adds_epi8(p0, v3); /* p0 += v3 */ \
|
||||
}
|
||||
|
||||
// Updates values of 2 pixels at MB edge during complex filtering.
|
||||
// Update operations:
|
||||
// q = q - a and p = p + a; where a = [(a_hi >> 7), (a_lo >> 7)]
|
||||
#define UPDATE_2PIXELS(pi, qi, a_lo, a_hi) { \
|
||||
const __m128i a_lo7 = _mm_srai_epi16(a_lo, 7); \
|
||||
const __m128i a_hi7 = _mm_srai_epi16(a_hi, 7); \
|
||||
const __m128i a = _mm_packs_epi16(a_lo7, a_hi7); \
|
||||
pi = _mm_adds_epi8(pi, a); \
|
||||
qi = _mm_subs_epi8(qi, a); \
|
||||
}
|
||||
|
||||
static void NeedsFilter(const __m128i* p1, const __m128i* p0, const __m128i* q0,
|
||||
const __m128i* q1, int thresh, __m128i *mask) {
|
||||
__m128i t1 = MM_ABS(*p1, *q1); // abs(p1 - q1)
|
||||
*mask = _mm_set1_epi8(0xFE);
|
||||
t1 = _mm_and_si128(t1, *mask); // set lsb of each byte to zero
|
||||
t1 = _mm_srli_epi16(t1, 1); // abs(p1 - q1) / 2
|
||||
|
||||
*mask = MM_ABS(*p0, *q0); // abs(p0 - q0)
|
||||
*mask = _mm_adds_epu8(*mask, *mask); // abs(p0 - q0) * 2
|
||||
*mask = _mm_adds_epu8(*mask, t1); // abs(p0 - q0) * 2 + abs(p1 - q1) / 2
|
||||
|
||||
t1 = _mm_set1_epi8(thresh);
|
||||
*mask = _mm_subs_epu8(*mask, t1); // mask <= thresh
|
||||
*mask = _mm_cmpeq_epi8(*mask, _mm_setzero_si128());
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Edge filtering functions
|
||||
|
||||
// Applies filter on 2 pixels (p0 and q0)
|
||||
static WEBP_INLINE void DoFilter2(const __m128i* p1, __m128i* p0, __m128i* q0,
|
||||
const __m128i* q1, int thresh) {
|
||||
__m128i a, mask;
|
||||
const __m128i sign_bit = _mm_set1_epi8(0x80);
|
||||
const __m128i p1s = _mm_xor_si128(*p1, sign_bit);
|
||||
const __m128i q1s = _mm_xor_si128(*q1, sign_bit);
|
||||
|
||||
NeedsFilter(p1, p0, q0, q1, thresh, &mask);
|
||||
|
||||
// convert to signed values
|
||||
FLIP_SIGN_BIT2(*p0, *q0);
|
||||
|
||||
GET_BASE_DELTA(p1s, *p0, *q0, q1s, a);
|
||||
a = _mm_and_si128(a, mask); // mask filter values we don't care about
|
||||
DO_SIMPLE_FILTER(*p0, *q0, a);
|
||||
|
||||
// unoffset
|
||||
FLIP_SIGN_BIT2(*p0, *q0);
|
||||
}
|
||||
|
||||
// Applies filter on 4 pixels (p1, p0, q0 and q1)
|
||||
static WEBP_INLINE void DoFilter4(__m128i* p1, __m128i *p0,
|
||||
__m128i* q0, __m128i* q1,
|
||||
const __m128i* mask, int hev_thresh) {
|
||||
__m128i not_hev;
|
||||
__m128i t1, t2, t3;
|
||||
const __m128i sign_bit = _mm_set1_epi8(0x80);
|
||||
|
||||
// compute hev mask
|
||||
GET_NOTHEV(*p1, *p0, *q0, *q1, hev_thresh, not_hev);
|
||||
|
||||
// convert to signed values
|
||||
FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1);
|
||||
|
||||
t1 = _mm_subs_epi8(*p1, *q1); // p1 - q1
|
||||
t1 = _mm_andnot_si128(not_hev, t1); // hev(p1 - q1)
|
||||
t2 = _mm_subs_epi8(*q0, *p0); // q0 - p0
|
||||
t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 1 * (q0 - p0)
|
||||
t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 2 * (q0 - p0)
|
||||
t1 = _mm_adds_epi8(t1, t2); // hev(p1 - q1) + 3 * (q0 - p0)
|
||||
t1 = _mm_and_si128(t1, *mask); // mask filter values we don't care about
|
||||
|
||||
// Do +4 side
|
||||
t2 = _mm_set1_epi8(4);
|
||||
t2 = _mm_adds_epi8(t1, t2); // 3 * (q0 - p0) + (p1 - q1) + 4
|
||||
SIGNED_SHIFT_N(t2, 3); // (3 * (q0 - p0) + hev(p1 - q1) + 4) >> 3
|
||||
t3 = t2; // save t2
|
||||
*q0 = _mm_subs_epi8(*q0, t2); // q0 -= t2
|
||||
|
||||
// Now do +3 side
|
||||
t2 = _mm_set1_epi8(3);
|
||||
t2 = _mm_adds_epi8(t1, t2); // +3 instead of +4
|
||||
SIGNED_SHIFT_N(t2, 3); // (3 * (q0 - p0) + hev(p1 - q1) + 3) >> 3
|
||||
*p0 = _mm_adds_epi8(*p0, t2); // p0 += t2
|
||||
|
||||
t2 = _mm_set1_epi8(1);
|
||||
t3 = _mm_adds_epi8(t3, t2);
|
||||
SIGNED_SHIFT_N(t3, 1); // (3 * (q0 - p0) + hev(p1 - q1) + 4) >> 4
|
||||
|
||||
t3 = _mm_and_si128(not_hev, t3); // if !hev
|
||||
*q1 = _mm_subs_epi8(*q1, t3); // q1 -= t3
|
||||
*p1 = _mm_adds_epi8(*p1, t3); // p1 += t3
|
||||
|
||||
// unoffset
|
||||
FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1);
|
||||
}
|
||||
|
||||
// Applies filter on 6 pixels (p2, p1, p0, q0, q1 and q2)
|
||||
static WEBP_INLINE void DoFilter6(__m128i *p2, __m128i* p1, __m128i *p0,
|
||||
__m128i* q0, __m128i* q1, __m128i *q2,
|
||||
const __m128i* mask, int hev_thresh) {
|
||||
__m128i a, not_hev;
|
||||
const __m128i sign_bit = _mm_set1_epi8(0x80);
|
||||
|
||||
// compute hev mask
|
||||
GET_NOTHEV(*p1, *p0, *q0, *q1, hev_thresh, not_hev);
|
||||
|
||||
// convert to signed values
|
||||
FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1);
|
||||
FLIP_SIGN_BIT2(*p2, *q2);
|
||||
|
||||
GET_BASE_DELTA(*p1, *p0, *q0, *q1, a);
|
||||
|
||||
{ // do simple filter on pixels with hev
|
||||
const __m128i m = _mm_andnot_si128(not_hev, *mask);
|
||||
const __m128i f = _mm_and_si128(a, m);
|
||||
DO_SIMPLE_FILTER(*p0, *q0, f);
|
||||
}
|
||||
{ // do strong filter on pixels with not hev
|
||||
const __m128i zero = _mm_setzero_si128();
|
||||
const __m128i nine = _mm_set1_epi16(0x0900);
|
||||
const __m128i sixty_three = _mm_set1_epi16(63);
|
||||
|
||||
const __m128i m = _mm_and_si128(not_hev, *mask);
|
||||
const __m128i f = _mm_and_si128(a, m);
|
||||
const __m128i f_lo = _mm_unpacklo_epi8(zero, f);
|
||||
const __m128i f_hi = _mm_unpackhi_epi8(zero, f);
|
||||
|
||||
const __m128i f9_lo = _mm_mulhi_epi16(f_lo, nine); // Filter (lo) * 9
|
||||
const __m128i f9_hi = _mm_mulhi_epi16(f_hi, nine); // Filter (hi) * 9
|
||||
const __m128i f18_lo = _mm_add_epi16(f9_lo, f9_lo); // Filter (lo) * 18
|
||||
const __m128i f18_hi = _mm_add_epi16(f9_hi, f9_hi); // Filter (hi) * 18
|
||||
|
||||
const __m128i a2_lo = _mm_add_epi16(f9_lo, sixty_three); // Filter * 9 + 63
|
||||
const __m128i a2_hi = _mm_add_epi16(f9_hi, sixty_three); // Filter * 9 + 63
|
||||
|
||||
const __m128i a1_lo = _mm_add_epi16(f18_lo, sixty_three); // F... * 18 + 63
|
||||
const __m128i a1_hi = _mm_add_epi16(f18_hi, sixty_three); // F... * 18 + 63
|
||||
|
||||
const __m128i a0_lo = _mm_add_epi16(f18_lo, a2_lo); // Filter * 27 + 63
|
||||
const __m128i a0_hi = _mm_add_epi16(f18_hi, a2_hi); // Filter * 27 + 63
|
||||
|
||||
UPDATE_2PIXELS(*p2, *q2, a2_lo, a2_hi);
|
||||
UPDATE_2PIXELS(*p1, *q1, a1_lo, a1_hi);
|
||||
UPDATE_2PIXELS(*p0, *q0, a0_lo, a0_hi);
|
||||
}
|
||||
|
||||
// unoffset
|
||||
FLIP_SIGN_BIT4(*p1, *p0, *q0, *q1);
|
||||
FLIP_SIGN_BIT2(*p2, *q2);
|
||||
}
|
||||
|
||||
// reads 8 rows across a vertical edge.
|
||||
//
|
||||
// TODO(somnath): Investigate _mm_shuffle* also see if it can be broken into
|
||||
// two Load4x4() to avoid code duplication.
|
||||
static WEBP_INLINE void Load8x4(const uint8_t* b, int stride,
|
||||
__m128i* p, __m128i* q) {
|
||||
__m128i t1, t2;
|
||||
|
||||
// Load 0th, 1st, 4th and 5th rows
|
||||
__m128i r0 = _mm_cvtsi32_si128(*((int*)&b[0 * stride])); // 03 02 01 00
|
||||
__m128i r1 = _mm_cvtsi32_si128(*((int*)&b[1 * stride])); // 13 12 11 10
|
||||
__m128i r4 = _mm_cvtsi32_si128(*((int*)&b[4 * stride])); // 43 42 41 40
|
||||
__m128i r5 = _mm_cvtsi32_si128(*((int*)&b[5 * stride])); // 53 52 51 50
|
||||
|
||||
r0 = _mm_unpacklo_epi32(r0, r4); // 43 42 41 40 03 02 01 00
|
||||
r1 = _mm_unpacklo_epi32(r1, r5); // 53 52 51 50 13 12 11 10
|
||||
|
||||
// t1 = 53 43 52 42 51 41 50 40 13 03 12 02 11 01 10 00
|
||||
t1 = _mm_unpacklo_epi8(r0, r1);
|
||||
|
||||
// Load 2nd, 3rd, 6th and 7th rows
|
||||
r0 = _mm_cvtsi32_si128(*((int*)&b[2 * stride])); // 23 22 21 22
|
||||
r1 = _mm_cvtsi32_si128(*((int*)&b[3 * stride])); // 33 32 31 30
|
||||
r4 = _mm_cvtsi32_si128(*((int*)&b[6 * stride])); // 63 62 61 60
|
||||
r5 = _mm_cvtsi32_si128(*((int*)&b[7 * stride])); // 73 72 71 70
|
||||
|
||||
r0 = _mm_unpacklo_epi32(r0, r4); // 63 62 61 60 23 22 21 20
|
||||
r1 = _mm_unpacklo_epi32(r1, r5); // 73 72 71 70 33 32 31 30
|
||||
|
||||
// t2 = 73 63 72 62 71 61 70 60 33 23 32 22 31 21 30 20
|
||||
t2 = _mm_unpacklo_epi8(r0, r1);
|
||||
|
||||
// t1 = 33 23 13 03 32 22 12 02 31 21 11 01 30 20 10 00
|
||||
// t2 = 73 63 53 43 72 62 52 42 71 61 51 41 70 60 50 40
|
||||
r0 = t1;
|
||||
t1 = _mm_unpacklo_epi16(t1, t2);
|
||||
t2 = _mm_unpackhi_epi16(r0, t2);
|
||||
|
||||
// *p = 71 61 51 41 31 21 11 01 70 60 50 40 30 20 10 00
|
||||
// *q = 73 63 53 43 33 23 13 03 72 62 52 42 32 22 12 02
|
||||
*p = _mm_unpacklo_epi32(t1, t2);
|
||||
*q = _mm_unpackhi_epi32(t1, t2);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void Load16x4(const uint8_t* r0, const uint8_t* r8,
|
||||
int stride,
|
||||
__m128i* p1, __m128i* p0,
|
||||
__m128i* q0, __m128i* q1) {
|
||||
__m128i t1, t2;
|
||||
// Assume the pixels around the edge (|) are numbered as follows
|
||||
// 00 01 | 02 03
|
||||
// 10 11 | 12 13
|
||||
// ... | ...
|
||||
// e0 e1 | e2 e3
|
||||
// f0 f1 | f2 f3
|
||||
//
|
||||
// r0 is pointing to the 0th row (00)
|
||||
// r8 is pointing to the 8th row (80)
|
||||
|
||||
// Load
|
||||
// p1 = 71 61 51 41 31 21 11 01 70 60 50 40 30 20 10 00
|
||||
// q0 = 73 63 53 43 33 23 13 03 72 62 52 42 32 22 12 02
|
||||
// p0 = f1 e1 d1 c1 b1 a1 91 81 f0 e0 d0 c0 b0 a0 90 80
|
||||
// q1 = f3 e3 d3 c3 b3 a3 93 83 f2 e2 d2 c2 b2 a2 92 82
|
||||
Load8x4(r0, stride, p1, q0);
|
||||
Load8x4(r8, stride, p0, q1);
|
||||
|
||||
t1 = *p1;
|
||||
t2 = *q0;
|
||||
// p1 = f0 e0 d0 c0 b0 a0 90 80 70 60 50 40 30 20 10 00
|
||||
// p0 = f1 e1 d1 c1 b1 a1 91 81 71 61 51 41 31 21 11 01
|
||||
// q0 = f2 e2 d2 c2 b2 a2 92 82 72 62 52 42 32 22 12 02
|
||||
// q1 = f3 e3 d3 c3 b3 a3 93 83 73 63 53 43 33 23 13 03
|
||||
*p1 = _mm_unpacklo_epi64(t1, *p0);
|
||||
*p0 = _mm_unpackhi_epi64(t1, *p0);
|
||||
*q0 = _mm_unpacklo_epi64(t2, *q1);
|
||||
*q1 = _mm_unpackhi_epi64(t2, *q1);
|
||||
}
|
||||
|
||||
static WEBP_INLINE void Store4x4(__m128i* x, uint8_t* dst, int stride) {
|
||||
int i;
|
||||
for (i = 0; i < 4; ++i, dst += stride) {
|
||||
*((int32_t*)dst) = _mm_cvtsi128_si32(*x);
|
||||
*x = _mm_srli_si128(*x, 4);
|
||||
}
|
||||
}
|
||||
|
||||
// Transpose back and store
|
||||
static WEBP_INLINE void Store16x4(uint8_t* r0, uint8_t* r8, int stride,
|
||||
__m128i* p1, __m128i* p0,
|
||||
__m128i* q0, __m128i* q1) {
|
||||
__m128i t1;
|
||||
|
||||
// p0 = 71 70 61 60 51 50 41 40 31 30 21 20 11 10 01 00
|
||||
// p1 = f1 f0 e1 e0 d1 d0 c1 c0 b1 b0 a1 a0 91 90 81 80
|
||||
t1 = *p0;
|
||||
*p0 = _mm_unpacklo_epi8(*p1, t1);
|
||||
*p1 = _mm_unpackhi_epi8(*p1, t1);
|
||||
|
||||
// q0 = 73 72 63 62 53 52 43 42 33 32 23 22 13 12 03 02
|
||||
// q1 = f3 f2 e3 e2 d3 d2 c3 c2 b3 b2 a3 a2 93 92 83 82
|
||||
t1 = *q0;
|
||||
*q0 = _mm_unpacklo_epi8(t1, *q1);
|
||||
*q1 = _mm_unpackhi_epi8(t1, *q1);
|
||||
|
||||
// p0 = 33 32 31 30 23 22 21 20 13 12 11 10 03 02 01 00
|
||||
// q0 = 73 72 71 70 63 62 61 60 53 52 51 50 43 42 41 40
|
||||
t1 = *p0;
|
||||
*p0 = _mm_unpacklo_epi16(t1, *q0);
|
||||
*q0 = _mm_unpackhi_epi16(t1, *q0);
|
||||
|
||||
// p1 = b3 b2 b1 b0 a3 a2 a1 a0 93 92 91 90 83 82 81 80
|
||||
// q1 = f3 f2 f1 f0 e3 e2 e1 e0 d3 d2 d1 d0 c3 c2 c1 c0
|
||||
t1 = *p1;
|
||||
*p1 = _mm_unpacklo_epi16(t1, *q1);
|
||||
*q1 = _mm_unpackhi_epi16(t1, *q1);
|
||||
|
||||
Store4x4(p0, r0, stride);
|
||||
r0 += 4 * stride;
|
||||
Store4x4(q0, r0, stride);
|
||||
|
||||
Store4x4(p1, r8, stride);
|
||||
r8 += 4 * stride;
|
||||
Store4x4(q1, r8, stride);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Simple In-loop filtering (Paragraph 15.2)
|
||||
|
||||
static void SimpleVFilter16SSE2(uint8_t* p, int stride, int thresh) {
|
||||
// Load
|
||||
__m128i p1 = _mm_loadu_si128((__m128i*)&p[-2 * stride]);
|
||||
__m128i p0 = _mm_loadu_si128((__m128i*)&p[-stride]);
|
||||
__m128i q0 = _mm_loadu_si128((__m128i*)&p[0]);
|
||||
__m128i q1 = _mm_loadu_si128((__m128i*)&p[stride]);
|
||||
|
||||
DoFilter2(&p1, &p0, &q0, &q1, thresh);
|
||||
|
||||
// Store
|
||||
_mm_storeu_si128((__m128i*)&p[-stride], p0);
|
||||
_mm_storeu_si128((__m128i*)p, q0);
|
||||
}
|
||||
|
||||
static void SimpleHFilter16SSE2(uint8_t* p, int stride, int thresh) {
|
||||
__m128i p1, p0, q0, q1;
|
||||
|
||||
p -= 2; // beginning of p1
|
||||
|
||||
Load16x4(p, p + 8 * stride, stride, &p1, &p0, &q0, &q1);
|
||||
DoFilter2(&p1, &p0, &q0, &q1, thresh);
|
||||
Store16x4(p, p + 8 * stride, stride, &p1, &p0, &q0, &q1);
|
||||
}
|
||||
|
||||
static void SimpleVFilter16iSSE2(uint8_t* p, int stride, int thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4 * stride;
|
||||
SimpleVFilter16SSE2(p, stride, thresh);
|
||||
}
|
||||
}
|
||||
|
||||
static void SimpleHFilter16iSSE2(uint8_t* p, int stride, int thresh) {
|
||||
int k;
|
||||
for (k = 3; k > 0; --k) {
|
||||
p += 4;
|
||||
SimpleHFilter16SSE2(p, stride, thresh);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Complex In-loop filtering (Paragraph 15.3)
|
||||
|
||||
#define MAX_DIFF1(p3, p2, p1, p0, m) { \
|
||||
m = MM_ABS(p3, p2); \
|
||||
m = _mm_max_epu8(m, MM_ABS(p2, p1)); \
|
||||
m = _mm_max_epu8(m, MM_ABS(p1, p0)); \
|
||||
}
|
||||
|
||||
#define MAX_DIFF2(p3, p2, p1, p0, m) { \
|
||||
m = _mm_max_epu8(m, MM_ABS(p3, p2)); \
|
||||
m = _mm_max_epu8(m, MM_ABS(p2, p1)); \
|
||||
m = _mm_max_epu8(m, MM_ABS(p1, p0)); \
|
||||
}
|
||||
|
||||
#define LOAD_H_EDGES4(p, stride, e1, e2, e3, e4) { \
|
||||
e1 = _mm_loadu_si128((__m128i*)&(p)[0 * stride]); \
|
||||
e2 = _mm_loadu_si128((__m128i*)&(p)[1 * stride]); \
|
||||
e3 = _mm_loadu_si128((__m128i*)&(p)[2 * stride]); \
|
||||
e4 = _mm_loadu_si128((__m128i*)&(p)[3 * stride]); \
|
||||
}
|
||||
|
||||
#define LOADUV_H_EDGE(p, u, v, stride) { \
|
||||
p = _mm_loadl_epi64((__m128i*)&(u)[(stride)]); \
|
||||
p = _mm_unpacklo_epi64(p, _mm_loadl_epi64((__m128i*)&(v)[(stride)])); \
|
||||
}
|
||||
|
||||
#define LOADUV_H_EDGES4(u, v, stride, e1, e2, e3, e4) { \
|
||||
LOADUV_H_EDGE(e1, u, v, 0 * stride); \
|
||||
LOADUV_H_EDGE(e2, u, v, 1 * stride); \
|
||||
LOADUV_H_EDGE(e3, u, v, 2 * stride); \
|
||||
LOADUV_H_EDGE(e4, u, v, 3 * stride); \
|
||||
}
|
||||
|
||||
#define STOREUV(p, u, v, stride) { \
|
||||
_mm_storel_epi64((__m128i*)&u[(stride)], p); \
|
||||
p = _mm_srli_si128(p, 8); \
|
||||
_mm_storel_epi64((__m128i*)&v[(stride)], p); \
|
||||
}
|
||||
|
||||
#define COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask) { \
|
||||
__m128i fl_yes; \
|
||||
const __m128i it = _mm_set1_epi8(ithresh); \
|
||||
mask = _mm_subs_epu8(mask, it); \
|
||||
mask = _mm_cmpeq_epi8(mask, _mm_setzero_si128()); \
|
||||
NeedsFilter(&p1, &p0, &q0, &q1, thresh, &fl_yes); \
|
||||
mask = _mm_and_si128(mask, fl_yes); \
|
||||
}
|
||||
|
||||
// on macroblock edges
|
||||
static void VFilter16SSE2(uint8_t* p, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
__m128i t1;
|
||||
__m128i mask;
|
||||
__m128i p2, p1, p0, q0, q1, q2;
|
||||
|
||||
// Load p3, p2, p1, p0
|
||||
LOAD_H_EDGES4(p - 4 * stride, stride, t1, p2, p1, p0);
|
||||
MAX_DIFF1(t1, p2, p1, p0, mask);
|
||||
|
||||
// Load q0, q1, q2, q3
|
||||
LOAD_H_EDGES4(p, stride, q0, q1, q2, t1);
|
||||
MAX_DIFF2(t1, q2, q1, q0, mask);
|
||||
|
||||
COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
|
||||
DoFilter6(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
|
||||
|
||||
// Store
|
||||
_mm_storeu_si128((__m128i*)&p[-3 * stride], p2);
|
||||
_mm_storeu_si128((__m128i*)&p[-2 * stride], p1);
|
||||
_mm_storeu_si128((__m128i*)&p[-1 * stride], p0);
|
||||
_mm_storeu_si128((__m128i*)&p[0 * stride], q0);
|
||||
_mm_storeu_si128((__m128i*)&p[1 * stride], q1);
|
||||
_mm_storeu_si128((__m128i*)&p[2 * stride], q2);
|
||||
}
|
||||
|
||||
static void HFilter16SSE2(uint8_t* p, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
__m128i mask;
|
||||
__m128i p3, p2, p1, p0, q0, q1, q2, q3;
|
||||
|
||||
uint8_t* const b = p - 4;
|
||||
Load16x4(b, b + 8 * stride, stride, &p3, &p2, &p1, &p0); // p3, p2, p1, p0
|
||||
MAX_DIFF1(p3, p2, p1, p0, mask);
|
||||
|
||||
Load16x4(p, p + 8 * stride, stride, &q0, &q1, &q2, &q3); // q0, q1, q2, q3
|
||||
MAX_DIFF2(q3, q2, q1, q0, mask);
|
||||
|
||||
COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
|
||||
DoFilter6(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
|
||||
|
||||
Store16x4(b, b + 8 * stride, stride, &p3, &p2, &p1, &p0);
|
||||
Store16x4(p, p + 8 * stride, stride, &q0, &q1, &q2, &q3);
|
||||
}
|
||||
|
||||
// on three inner edges
|
||||
static void VFilter16iSSE2(uint8_t* p, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
int k;
|
||||
__m128i mask;
|
||||
__m128i t1, t2, p1, p0, q0, q1;
|
||||
|
||||
for (k = 3; k > 0; --k) {
|
||||
// Load p3, p2, p1, p0
|
||||
LOAD_H_EDGES4(p, stride, t2, t1, p1, p0);
|
||||
MAX_DIFF1(t2, t1, p1, p0, mask);
|
||||
|
||||
p += 4 * stride;
|
||||
|
||||
// Load q0, q1, q2, q3
|
||||
LOAD_H_EDGES4(p, stride, q0, q1, t1, t2);
|
||||
MAX_DIFF2(t2, t1, q1, q0, mask);
|
||||
|
||||
COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
|
||||
DoFilter4(&p1, &p0, &q0, &q1, &mask, hev_thresh);
|
||||
|
||||
// Store
|
||||
_mm_storeu_si128((__m128i*)&p[-2 * stride], p1);
|
||||
_mm_storeu_si128((__m128i*)&p[-1 * stride], p0);
|
||||
_mm_storeu_si128((__m128i*)&p[0 * stride], q0);
|
||||
_mm_storeu_si128((__m128i*)&p[1 * stride], q1);
|
||||
}
|
||||
}
|
||||
|
||||
static void HFilter16iSSE2(uint8_t* p, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
int k;
|
||||
uint8_t* b;
|
||||
__m128i mask;
|
||||
__m128i t1, t2, p1, p0, q0, q1;
|
||||
|
||||
for (k = 3; k > 0; --k) {
|
||||
b = p;
|
||||
Load16x4(b, b + 8 * stride, stride, &t2, &t1, &p1, &p0); // p3, p2, p1, p0
|
||||
MAX_DIFF1(t2, t1, p1, p0, mask);
|
||||
|
||||
b += 4; // beginning of q0
|
||||
Load16x4(b, b + 8 * stride, stride, &q0, &q1, &t1, &t2); // q0, q1, q2, q3
|
||||
MAX_DIFF2(t2, t1, q1, q0, mask);
|
||||
|
||||
COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
|
||||
DoFilter4(&p1, &p0, &q0, &q1, &mask, hev_thresh);
|
||||
|
||||
b -= 2; // beginning of p1
|
||||
Store16x4(b, b + 8 * stride, stride, &p1, &p0, &q0, &q1);
|
||||
|
||||
p += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// 8-pixels wide variant, for chroma filtering
|
||||
static void VFilter8SSE2(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
__m128i mask;
|
||||
__m128i t1, p2, p1, p0, q0, q1, q2;
|
||||
|
||||
// Load p3, p2, p1, p0
|
||||
LOADUV_H_EDGES4(u - 4 * stride, v - 4 * stride, stride, t1, p2, p1, p0);
|
||||
MAX_DIFF1(t1, p2, p1, p0, mask);
|
||||
|
||||
// Load q0, q1, q2, q3
|
||||
LOADUV_H_EDGES4(u, v, stride, q0, q1, q2, t1);
|
||||
MAX_DIFF2(t1, q2, q1, q0, mask);
|
||||
|
||||
COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
|
||||
DoFilter6(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
|
||||
|
||||
// Store
|
||||
STOREUV(p2, u, v, -3 * stride);
|
||||
STOREUV(p1, u, v, -2 * stride);
|
||||
STOREUV(p0, u, v, -1 * stride);
|
||||
STOREUV(q0, u, v, 0 * stride);
|
||||
STOREUV(q1, u, v, 1 * stride);
|
||||
STOREUV(q2, u, v, 2 * stride);
|
||||
}
|
||||
|
||||
static void HFilter8SSE2(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
__m128i mask;
|
||||
__m128i p3, p2, p1, p0, q0, q1, q2, q3;
|
||||
|
||||
uint8_t* const tu = u - 4;
|
||||
uint8_t* const tv = v - 4;
|
||||
Load16x4(tu, tv, stride, &p3, &p2, &p1, &p0); // p3, p2, p1, p0
|
||||
MAX_DIFF1(p3, p2, p1, p0, mask);
|
||||
|
||||
Load16x4(u, v, stride, &q0, &q1, &q2, &q3); // q0, q1, q2, q3
|
||||
MAX_DIFF2(q3, q2, q1, q0, mask);
|
||||
|
||||
COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
|
||||
DoFilter6(&p2, &p1, &p0, &q0, &q1, &q2, &mask, hev_thresh);
|
||||
|
||||
Store16x4(tu, tv, stride, &p3, &p2, &p1, &p0);
|
||||
Store16x4(u, v, stride, &q0, &q1, &q2, &q3);
|
||||
}
|
||||
|
||||
static void VFilter8iSSE2(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
__m128i mask;
|
||||
__m128i t1, t2, p1, p0, q0, q1;
|
||||
|
||||
// Load p3, p2, p1, p0
|
||||
LOADUV_H_EDGES4(u, v, stride, t2, t1, p1, p0);
|
||||
MAX_DIFF1(t2, t1, p1, p0, mask);
|
||||
|
||||
u += 4 * stride;
|
||||
v += 4 * stride;
|
||||
|
||||
// Load q0, q1, q2, q3
|
||||
LOADUV_H_EDGES4(u, v, stride, q0, q1, t1, t2);
|
||||
MAX_DIFF2(t2, t1, q1, q0, mask);
|
||||
|
||||
COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
|
||||
DoFilter4(&p1, &p0, &q0, &q1, &mask, hev_thresh);
|
||||
|
||||
// Store
|
||||
STOREUV(p1, u, v, -2 * stride);
|
||||
STOREUV(p0, u, v, -1 * stride);
|
||||
STOREUV(q0, u, v, 0 * stride);
|
||||
STOREUV(q1, u, v, 1 * stride);
|
||||
}
|
||||
|
||||
static void HFilter8iSSE2(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_thresh) {
|
||||
__m128i mask;
|
||||
__m128i t1, t2, p1, p0, q0, q1;
|
||||
Load16x4(u, v, stride, &t2, &t1, &p1, &p0); // p3, p2, p1, p0
|
||||
MAX_DIFF1(t2, t1, p1, p0, mask);
|
||||
|
||||
u += 4; // beginning of q0
|
||||
v += 4;
|
||||
Load16x4(u, v, stride, &q0, &q1, &t1, &t2); // q0, q1, q2, q3
|
||||
MAX_DIFF2(t2, t1, q1, q0, mask);
|
||||
|
||||
COMPLEX_FL_MASK(p1, p0, q0, q1, thresh, ithresh, mask);
|
||||
DoFilter4(&p1, &p0, &q0, &q1, &mask, hev_thresh);
|
||||
|
||||
u -= 2; // beginning of p1
|
||||
v -= 2;
|
||||
Store16x4(u, v, stride, &p1, &p0, &q0, &q1);
|
||||
}
|
||||
|
||||
extern void VP8DspInitSSE2(void);
|
||||
|
||||
void VP8DspInitSSE2(void) {
|
||||
VP8Transform = TransformSSE2;
|
||||
|
||||
VP8VFilter16 = VFilter16SSE2;
|
||||
VP8HFilter16 = HFilter16SSE2;
|
||||
VP8VFilter8 = VFilter8SSE2;
|
||||
VP8HFilter8 = HFilter8SSE2;
|
||||
VP8VFilter16i = VFilter16iSSE2;
|
||||
VP8HFilter16i = HFilter16iSSE2;
|
||||
VP8VFilter8i = VFilter8iSSE2;
|
||||
VP8HFilter8i = HFilter8iSSE2;
|
||||
|
||||
VP8SimpleVFilter16 = SimpleVFilter16SSE2;
|
||||
VP8SimpleHFilter16 = SimpleHFilter16SSE2;
|
||||
VP8SimpleVFilter16i = SimpleVFilter16iSSE2;
|
||||
VP8SimpleHFilter16i = SimpleHFilter16iSSE2;
|
||||
}
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // WEBP_USE_SSE2
|
||||
210
drivers/webpold/dsp/dsp.h
Normal file
210
drivers/webpold/dsp/dsp.h
Normal file
@ -0,0 +1,210 @@
|
||||
// Copyright 2011 Google Inc. All Rights Reserved.
|
||||
//
|
||||
// This code is licensed under the same terms as WebM:
|
||||
// Software License Agreement: http://www.webmproject.org/license/software/
|
||||
// Additional IP Rights Grant: http://www.webmproject.org/license/additional/
|
||||
// -----------------------------------------------------------------------------
|
||||
//
|
||||
// Speed-critical functions.
|
||||
//
|
||||
// Author: Skal (pascal.massimino@gmail.com)
|
||||
|
||||
#ifndef WEBP_DSP_DSP_H_
|
||||
#define WEBP_DSP_DSP_H_
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// CPU detection
|
||||
|
||||
#if defined(_MSC_VER) && (defined(_M_X64) || defined(_M_IX86))
|
||||
#define WEBP_MSC_SSE2 // Visual C++ SSE2 targets
|
||||
#endif
|
||||
|
||||
#if defined(__SSE2__) || defined(WEBP_MSC_SSE2)
|
||||
#define WEBP_USE_SSE2
|
||||
#endif
|
||||
|
||||
#if defined(__ANDROID__) && defined(__ARM_ARCH_7A__) && defined(__ARM_NEON__)
|
||||
#define WEBP_ANDROID_NEON // Android targets that might support NEON
|
||||
#endif
|
||||
|
||||
#if ( (defined(__ARM_NEON__) && !defined(__aarch64__)) || defined(WEBP_ANDROID_NEON)) && !defined(PSP2_ENABLED)
|
||||
#define WEBP_USE_NEON
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
kSSE2,
|
||||
kSSE3,
|
||||
kNEON
|
||||
} CPUFeature;
|
||||
// returns true if the CPU supports the feature.
|
||||
typedef int (*VP8CPUInfo)(CPUFeature feature);
|
||||
extern VP8CPUInfo VP8GetCPUInfo;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Encoding
|
||||
|
||||
int VP8GetAlpha(const int histo[]);
|
||||
|
||||
// Transforms
|
||||
// VP8Idct: Does one of two inverse transforms. If do_two is set, the transforms
|
||||
// will be done for (ref, in, dst) and (ref + 4, in + 16, dst + 4).
|
||||
typedef void (*VP8Idct)(const uint8_t* ref, const int16_t* in, uint8_t* dst,
|
||||
int do_two);
|
||||
typedef void (*VP8Fdct)(const uint8_t* src, const uint8_t* ref, int16_t* out);
|
||||
typedef void (*VP8WHT)(const int16_t* in, int16_t* out);
|
||||
extern VP8Idct VP8ITransform;
|
||||
extern VP8Fdct VP8FTransform;
|
||||
extern VP8WHT VP8ITransformWHT;
|
||||
extern VP8WHT VP8FTransformWHT;
|
||||
// Predictions
|
||||
// *dst is the destination block. *top and *left can be NULL.
|
||||
typedef void (*VP8IntraPreds)(uint8_t *dst, const uint8_t* left,
|
||||
const uint8_t* top);
|
||||
typedef void (*VP8Intra4Preds)(uint8_t *dst, const uint8_t* top);
|
||||
extern VP8Intra4Preds VP8EncPredLuma4;
|
||||
extern VP8IntraPreds VP8EncPredLuma16;
|
||||
extern VP8IntraPreds VP8EncPredChroma8;
|
||||
|
||||
typedef int (*VP8Metric)(const uint8_t* pix, const uint8_t* ref);
|
||||
extern VP8Metric VP8SSE16x16, VP8SSE16x8, VP8SSE8x8, VP8SSE4x4;
|
||||
typedef int (*VP8WMetric)(const uint8_t* pix, const uint8_t* ref,
|
||||
const uint16_t* const weights);
|
||||
extern VP8WMetric VP8TDisto4x4, VP8TDisto16x16;
|
||||
|
||||
typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst);
|
||||
extern VP8BlockCopy VP8Copy4x4;
|
||||
// Quantization
|
||||
struct VP8Matrix; // forward declaration
|
||||
typedef int (*VP8QuantizeBlock)(int16_t in[16], int16_t out[16],
|
||||
int n, const struct VP8Matrix* const mtx);
|
||||
extern VP8QuantizeBlock VP8EncQuantizeBlock;
|
||||
|
||||
// Compute susceptibility based on DCT-coeff histograms:
|
||||
// the higher, the "easier" the macroblock is to compress.
|
||||
typedef int (*VP8CHisto)(const uint8_t* ref, const uint8_t* pred,
|
||||
int start_block, int end_block);
|
||||
extern const int VP8DspScan[16 + 4 + 4];
|
||||
extern VP8CHisto VP8CollectHistogram;
|
||||
|
||||
void VP8EncDspInit(void); // must be called before using any of the above
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Decoding
|
||||
|
||||
typedef void (*VP8DecIdct)(const int16_t* coeffs, uint8_t* dst);
|
||||
// when doing two transforms, coeffs is actually int16_t[2][16].
|
||||
typedef void (*VP8DecIdct2)(const int16_t* coeffs, uint8_t* dst, int do_two);
|
||||
extern VP8DecIdct2 VP8Transform;
|
||||
extern VP8DecIdct VP8TransformUV;
|
||||
extern VP8DecIdct VP8TransformDC;
|
||||
extern VP8DecIdct VP8TransformDCUV;
|
||||
extern void (*VP8TransformWHT)(const int16_t* in, int16_t* out);
|
||||
|
||||
// *dst is the destination block, with stride BPS. Boundary samples are
|
||||
// assumed accessible when needed.
|
||||
typedef void (*VP8PredFunc)(uint8_t* dst);
|
||||
extern const VP8PredFunc VP8PredLuma16[/* NUM_B_DC_MODES */];
|
||||
extern const VP8PredFunc VP8PredChroma8[/* NUM_B_DC_MODES */];
|
||||
extern const VP8PredFunc VP8PredLuma4[/* NUM_BMODES */];
|
||||
|
||||
// simple filter (only for luma)
|
||||
typedef void (*VP8SimpleFilterFunc)(uint8_t* p, int stride, int thresh);
|
||||
extern VP8SimpleFilterFunc VP8SimpleVFilter16;
|
||||
extern VP8SimpleFilterFunc VP8SimpleHFilter16;
|
||||
extern VP8SimpleFilterFunc VP8SimpleVFilter16i; // filter 3 inner edges
|
||||
extern VP8SimpleFilterFunc VP8SimpleHFilter16i;
|
||||
|
||||
// regular filter (on both macroblock edges and inner edges)
|
||||
typedef void (*VP8LumaFilterFunc)(uint8_t* luma, int stride,
|
||||
int thresh, int ithresh, int hev_t);
|
||||
typedef void (*VP8ChromaFilterFunc)(uint8_t* u, uint8_t* v, int stride,
|
||||
int thresh, int ithresh, int hev_t);
|
||||
// on outer edge
|
||||
extern VP8LumaFilterFunc VP8VFilter16;
|
||||
extern VP8LumaFilterFunc VP8HFilter16;
|
||||
extern VP8ChromaFilterFunc VP8VFilter8;
|
||||
extern VP8ChromaFilterFunc VP8HFilter8;
|
||||
|
||||
// on inner edge
|
||||
extern VP8LumaFilterFunc VP8VFilter16i; // filtering 3 inner edges altogether
|
||||
extern VP8LumaFilterFunc VP8HFilter16i;
|
||||
extern VP8ChromaFilterFunc VP8VFilter8i; // filtering u and v altogether
|
||||
extern VP8ChromaFilterFunc VP8HFilter8i;
|
||||
|
||||
// must be called before anything using the above
|
||||
void VP8DspInit(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// WebP I/O
|
||||
|
||||
#define FANCY_UPSAMPLING // undefined to remove fancy upsampling support
|
||||
|
||||
typedef void (*WebPUpsampleLinePairFunc)(
|
||||
const uint8_t* top_y, const uint8_t* bottom_y,
|
||||
const uint8_t* top_u, const uint8_t* top_v,
|
||||
const uint8_t* cur_u, const uint8_t* cur_v,
|
||||
uint8_t* top_dst, uint8_t* bottom_dst, int len);
|
||||
|
||||
#ifdef FANCY_UPSAMPLING
|
||||
|
||||
// Fancy upsampling functions to convert YUV to RGB(A) modes
|
||||
extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */];
|
||||
|
||||
// Initializes SSE2 version of the fancy upsamplers.
|
||||
void WebPInitUpsamplersSSE2(void);
|
||||
|
||||
#endif // FANCY_UPSAMPLING
|
||||
|
||||
// Point-sampling methods.
|
||||
typedef void (*WebPSampleLinePairFunc)(
|
||||
const uint8_t* top_y, const uint8_t* bottom_y,
|
||||
const uint8_t* u, const uint8_t* v,
|
||||
uint8_t* top_dst, uint8_t* bottom_dst, int len);
|
||||
|
||||
extern const WebPSampleLinePairFunc WebPSamplers[/* MODE_LAST */];
|
||||
|
||||
// General function for converting two lines of ARGB or RGBA.
|
||||
// 'alpha_is_last' should be true if 0xff000000 is stored in memory as
|
||||
// as 0x00, 0x00, 0x00, 0xff (little endian).
|
||||
WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last);
|
||||
|
||||
// YUV444->RGB converters
|
||||
typedef void (*WebPYUV444Converter)(const uint8_t* y,
|
||||
const uint8_t* u, const uint8_t* v,
|
||||
uint8_t* dst, int len);
|
||||
|
||||
extern const WebPYUV444Converter WebPYUV444Converters[/* MODE_LAST */];
|
||||
|
||||
// Main function to be called
|
||||
void WebPInitUpsamplers(void);
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Pre-multiply planes with alpha values
|
||||
|
||||
// Apply alpha pre-multiply on an rgba, bgra or argb plane of size w * h.
|
||||
// alpha_first should be 0 for argb, 1 for rgba or bgra (where alpha is last).
|
||||
extern void (*WebPApplyAlphaMultiply)(
|
||||
uint8_t* rgba, int alpha_first, int w, int h, int stride);
|
||||
|
||||
// Same, buf specifically for RGBA4444 format
|
||||
extern void (*WebPApplyAlphaMultiply4444)(
|
||||
uint8_t* rgba4444, int w, int h, int stride);
|
||||
|
||||
// To be called first before using the above.
|
||||
void WebPInitPremultiply(void);
|
||||
|
||||
void WebPInitPremultiplySSE2(void); // should not be called directly.
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
#if defined(__cplusplus) || defined(c_plusplus)
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif /* WEBP_DSP_DSP_H_ */
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user