diff options
Diffstat (limited to 'drivers')
161 files changed, 5271 insertions, 32890 deletions
diff --git a/drivers/unix/os_unix.cpp b/drivers/unix/os_unix.cpp index 8cb7c7b698..fd515d6dd3 100644 --- a/drivers/unix/os_unix.cpp +++ b/drivers/unix/os_unix.cpp @@ -357,7 +357,6 @@ Error OS_Unix::execute(const String& p_path, const List<String>& p_arguments,boo pid_t pid = fork(); ERR_FAIL_COND_V(pid<0,ERR_CANT_FORK); - //print("execute: %s\n",p_path.utf8().get_data()); if (pid==0) { @@ -394,8 +393,6 @@ Error OS_Unix::execute(const String& p_path, const List<String>& p_arguments,boo pid_t rpid = waitpid(pid,&status,0); if (r_exitcode) *r_exitcode=WEXITSTATUS(status); - - print("returned: %i, waiting for: %i\n",rpid,pid); } else { if (r_child_id) @@ -498,7 +495,6 @@ String OS_Unix::get_executable_path() const { char buf[256]; memset(buf,0,256); readlink("/proc/self/exe", buf, sizeof(buf)); - //print_line("Exec path is:"+String(buf)); String b; b.parse_utf8(buf); if (b=="") { diff --git a/drivers/webp/config.h b/drivers/webp/config.h index d85e5d1da6..0ce1c7064d 100644 --- a/drivers/webp/config.h +++ b/drivers/webp/config.h @@ -1,136 +1,141 @@ -/* src/webp/config.h. Generated from config.h.in by configure. */ /* src/webp/config.h.in. Generated from configure.ac by autoheader. */ /* Define if building universal (internal helper macro) */ -/* #undef AC_APPLE_UNIVERSAL_BUILD */ +#undef AC_APPLE_UNIVERSAL_BUILD /* Set to 1 if __builtin_bswap16 is available */ -#define HAVE_BUILTIN_BSWAP16 1 +#undef HAVE_BUILTIN_BSWAP16 /* Set to 1 if __builtin_bswap32 is available */ -#define HAVE_BUILTIN_BSWAP32 1 +#undef HAVE_BUILTIN_BSWAP32 /* Set to 1 if __builtin_bswap64 is available */ -#define HAVE_BUILTIN_BSWAP64 1 +#undef HAVE_BUILTIN_BSWAP64 /* Define to 1 if you have the <dlfcn.h> header file. */ -#define HAVE_DLFCN_H 1 +#undef HAVE_DLFCN_H /* Define to 1 if you have the <GLUT/glut.h> header file. */ -/* #undef HAVE_GLUT_GLUT_H */ +#undef HAVE_GLUT_GLUT_H /* Define to 1 if you have the <GL/glut.h> header file. */ -#define HAVE_GL_GLUT_H 1 +#undef HAVE_GL_GLUT_H /* Define to 1 if you have the <inttypes.h> header file. */ -#define HAVE_INTTYPES_H 1 +#undef HAVE_INTTYPES_H /* Define to 1 if you have the <memory.h> header file. */ -#define HAVE_MEMORY_H 1 +#undef HAVE_MEMORY_H /* Define to 1 if you have the <OpenGL/glut.h> header file. */ -/* #undef HAVE_OPENGL_GLUT_H */ +#undef HAVE_OPENGL_GLUT_H /* Have PTHREAD_PRIO_INHERIT. */ -#define HAVE_PTHREAD_PRIO_INHERIT 1 +#undef HAVE_PTHREAD_PRIO_INHERIT /* Define to 1 if you have the <shlwapi.h> header file. */ -/* #undef HAVE_SHLWAPI_H */ +#undef HAVE_SHLWAPI_H /* Define to 1 if you have the <stdint.h> header file. */ -#define HAVE_STDINT_H 1 +#undef HAVE_STDINT_H /* Define to 1 if you have the <stdlib.h> header file. */ -#define HAVE_STDLIB_H 1 +#undef HAVE_STDLIB_H /* Define to 1 if you have the <strings.h> header file. */ -#define HAVE_STRINGS_H 1 +#undef HAVE_STRINGS_H /* Define to 1 if you have the <string.h> header file. */ -#define HAVE_STRING_H 1 +#undef HAVE_STRING_H /* Define to 1 if you have the <sys/stat.h> header file. */ -#define HAVE_SYS_STAT_H 1 +#undef HAVE_SYS_STAT_H /* Define to 1 if you have the <sys/types.h> header file. */ -#define HAVE_SYS_TYPES_H 1 +#undef HAVE_SYS_TYPES_H /* Define to 1 if you have the <unistd.h> header file. */ -#define HAVE_UNISTD_H 1 +#undef HAVE_UNISTD_H /* Define to 1 if you have the <wincodec.h> header file. */ -/* #undef HAVE_WINCODEC_H */ +#undef HAVE_WINCODEC_H /* Define to 1 if you have the <windows.h> header file. */ -/* #undef HAVE_WINDOWS_H */ +#undef HAVE_WINDOWS_H /* Define to the sub-directory in which libtool stores uninstalled libraries. */ -#define LT_OBJDIR ".libs/" +#undef LT_OBJDIR /* Name of package */ -#define PACKAGE "libwebp" +#undef PACKAGE /* Define to the address where bug reports for this package should be sent. */ -#define PACKAGE_BUGREPORT "http://code.google.com/p/webp/issues" +#undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ -#define PACKAGE_NAME "libwebp" +#undef PACKAGE_NAME /* Define to the full name and version of this package. */ -#define PACKAGE_STRING "libwebp 0.4.4" +#undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ -#define PACKAGE_TARNAME "libwebp" +#undef PACKAGE_TARNAME /* Define to the home page for this package. */ -#define PACKAGE_URL "http://developers.google.com/speed/webp" +#undef PACKAGE_URL /* Define to the version of this package. */ -#define PACKAGE_VERSION "0.4.4" +#undef PACKAGE_VERSION /* Define to necessary symbol if this constant uses a non-standard name on your system. */ -/* #undef PTHREAD_CREATE_JOINABLE */ +#undef PTHREAD_CREATE_JOINABLE /* Define to 1 if you have the ANSI C header files. */ -#define STDC_HEADERS 1 +#undef STDC_HEADERS /* Version number of package */ -#define VERSION "0.4.4" +#undef VERSION /* Enable experimental code */ -/* #undef WEBP_EXPERIMENTAL_FEATURES */ +#undef WEBP_EXPERIMENTAL_FEATURES /* Define to 1 to force aligned memory operations */ -/* #undef WEBP_FORCE_ALIGNED */ +#undef WEBP_FORCE_ALIGNED /* Set to 1 if AVX2 is supported */ -#define WEBP_HAVE_AVX2 1 +#undef WEBP_HAVE_AVX2 /* Set to 1 if GIF library is installed */ -/* #undef WEBP_HAVE_GIF */ +#undef WEBP_HAVE_GIF /* Set to 1 if OpenGL is supported */ -#define WEBP_HAVE_GL 1 +#undef WEBP_HAVE_GL /* Set to 1 if JPEG library is installed */ -/* #undef WEBP_HAVE_JPEG */ +#undef WEBP_HAVE_JPEG + +/* Set to 1 if NEON is supported */ +#undef WEBP_HAVE_NEON + +/* Set to 1 if runtime detection of NEON is enabled */ +#undef WEBP_HAVE_NEON_RTCD /* Set to 1 if PNG library is installed */ -#define WEBP_HAVE_PNG 1 +#undef WEBP_HAVE_PNG /* Set to 1 if SSE2 is supported */ -#define WEBP_HAVE_SSE2 1 +#undef WEBP_HAVE_SSE2 /* Set to 1 if SSE4.1 is supported */ -#define WEBP_HAVE_SSE41 1 +#undef WEBP_HAVE_SSE41 /* Set to 1 if TIFF library is installed */ -/* #undef WEBP_HAVE_TIFF */ +#undef WEBP_HAVE_TIFF /* Undefine this to disable thread support. */ -#define WEBP_USE_THREAD 1 +#undef WEBP_USE_THREAD /* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel). */ @@ -140,6 +145,6 @@ # endif #else # ifndef WORDS_BIGENDIAN -/* # undef WORDS_BIGENDIAN */ +# undef WORDS_BIGENDIAN # endif #endif diff --git a/drivers/webp/dec/alpha.c b/drivers/webp/dec/alpha.c index 1d029b0e6a..19ce548e96 100644 --- a/drivers/webp/dec/alpha.c +++ b/drivers/webp/dec/alpha.c @@ -23,12 +23,14 @@ //------------------------------------------------------------------------------ // ALPHDecoder object. -ALPHDecoder* ALPHNew(void) { +// Allocates a new alpha decoder instance. +static ALPHDecoder* ALPHNew(void) { ALPHDecoder* const dec = (ALPHDecoder*)WebPSafeCalloc(1ULL, sizeof(*dec)); return dec; } -void ALPHDelete(ALPHDecoder* const dec) { +// Clears and deallocates an alpha decoder instance. +static void ALPHDelete(ALPHDecoder* const dec) { if (dec != NULL) { VP8LDelete(dec->vp8l_dec_); dec->vp8l_dec_ = NULL; @@ -44,17 +46,21 @@ void ALPHDelete(ALPHDecoder* const dec) { // 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) { + size_t data_size, const VP8Io* const src_io, + uint8_t* output) { int ok = 0; const uint8_t* const alpha_data = data + ALPHA_HEADER_LEN; const size_t alpha_data_size = data_size - ALPHA_HEADER_LEN; int rsrv; + VP8Io* const io = &dec->io_; - assert(width > 0 && height > 0); - assert(data != NULL && output != NULL); + assert(data != NULL && output != NULL && src_io != NULL); - dec->width_ = width; - dec->height_ = height; + VP8FiltersInit(); + dec->output_ = output; + dec->width_ = src_io->width; + dec->height_ = src_io->height; + assert(dec->width_ > 0 && dec->height_ > 0); if (data_size <= ALPHA_HEADER_LEN) { return 0; @@ -72,14 +78,28 @@ static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, return 0; } + // Copy the necessary parameters from src_io to io + VP8InitIo(io); + WebPInitCustomIo(NULL, io); + io->opaque = dec; + io->width = src_io->width; + io->height = src_io->height; + + io->use_cropping = src_io->use_cropping; + io->crop_left = src_io->crop_left; + io->crop_right = src_io->crop_right; + io->crop_top = src_io->crop_top; + io->crop_bottom = src_io->crop_bottom; + // No need to copy the scaling parameters. + if (dec->method_ == ALPHA_NO_COMPRESSION) { const size_t alpha_decoded_size = dec->width_ * dec->height_; ok = (alpha_data_size >= alpha_decoded_size); } else { assert(dec->method_ == ALPHA_LOSSLESS_COMPRESSION); - ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size, output); + ok = VP8LDecodeAlphaHeader(dec, alpha_data, alpha_data_size); } - VP8FiltersInit(); + return ok; } @@ -90,15 +110,30 @@ static int ALPHInit(ALPHDecoder* const dec, const uint8_t* data, 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_; + const int height = alph_dec->io_.crop_bottom; 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); + int y; + const uint8_t* prev_line = dec->alpha_prev_line_; + const uint8_t* deltas = dec->alpha_data_ + ALPHA_HEADER_LEN + row * width; + uint8_t* dst = dec->alpha_plane_ + row * width; + assert(deltas <= &dec->alpha_data_[dec->alpha_data_size_]); + if (alph_dec->filter_ != WEBP_FILTER_NONE) { + assert(WebPUnfilters[alph_dec->filter_] != NULL); + for (y = 0; y < num_rows; ++y) { + WebPUnfilters[alph_dec->filter_](prev_line, deltas, dst, width); + prev_line = dst; + dst += width; + deltas += width; + } + } else { + for (y = 0; y < num_rows; ++y) { + memcpy(dst, deltas, width * sizeof(*dst)); + prev_line = dst; + dst += width; + deltas += width; + } + } + dec->alpha_prev_line_ = prev_line; } else { // alph_dec->method_ == ALPHA_LOSSLESS_COMPRESSION assert(alph_dec->vp8l_dec_ != NULL); if (!VP8LDecodeAlphaImageStream(alph_dec, row + num_rows)) { @@ -106,62 +141,92 @@ static int ALPHDecode(VP8Decoder* const dec, int row, int num_rows) { } } - if (unfilter_func != NULL) { - unfilter_func(width, height, width, row, num_rows, output); + if (row + num_rows >= height) { + dec->is_alpha_decoded_ = 1; } + return 1; +} - if (row + num_rows == dec->pic_hdr_.height_) { - dec->is_alpha_decoded_ = 1; +static int AllocateAlphaPlane(VP8Decoder* const dec, const VP8Io* const io) { + const int stride = io->width; + const int height = io->crop_bottom; + const uint64_t alpha_size = (uint64_t)stride * height; + assert(dec->alpha_plane_mem_ == NULL); + dec->alpha_plane_mem_ = + (uint8_t*)WebPSafeMalloc(alpha_size, sizeof(*dec->alpha_plane_)); + if (dec->alpha_plane_mem_ == NULL) { + return 0; } + dec->alpha_plane_ = dec->alpha_plane_mem_; + dec->alpha_prev_line_ = NULL; return 1; } +void WebPDeallocateAlphaMemory(VP8Decoder* const dec) { + assert(dec != NULL); + WebPSafeFree(dec->alpha_plane_mem_); + dec->alpha_plane_mem_ = NULL; + dec->alpha_plane_ = NULL; + ALPHDelete(dec->alph_dec_); + dec->alph_dec_ = NULL; +} + //------------------------------------------------------------------------------ // Main entry point. const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, + const VP8Io* const io, int row, int num_rows) { - const int width = dec->pic_hdr_.width_; - const int height = dec->pic_hdr_.height_; + const int width = io->width; + const int height = io->crop_bottom; + + assert(dec != NULL && io != NULL); if (row < 0 || num_rows <= 0 || row + num_rows > height) { return NULL; // sanity check. } - if (row == 0) { - // 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 + if (!dec->is_alpha_decoded_) { + if (dec->alph_dec_ == NULL) { // Initialize decoder. + dec->alph_dec_ = ALPHNew(); + if (dec->alph_dec_ == NULL) return NULL; + if (!AllocateAlphaPlane(dec, io)) goto Error; + if (!ALPHInit(dec->alph_dec_, dec->alpha_data_, dec->alpha_data_size_, + io, dec->alpha_plane_)) { + goto Error; + } + // 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 - row; // decode everything in one pass + } } - } - 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_) { + assert(row + num_rows <= height); + if (!ALPHDecode(dec, row, num_rows)) goto Error; + + if (dec->is_alpha_decoded_) { // finished? ALPHDelete(dec->alph_dec_); dec->alph_dec_ = NULL; + if (dec->alpha_dithering_ > 0) { + uint8_t* const alpha = dec->alpha_plane_ + io->crop_top * width + + io->crop_left; + if (!WebPDequantizeLevels(alpha, + io->crop_right - io->crop_left, + io->crop_bottom - io->crop_top, + width, dec->alpha_dithering_)) { + goto Error; + } + } } - if (!ok) return NULL; // Error. } // Return a pointer to the current decoded row. return dec->alpha_plane_ + row * width; + + Error: + WebPDeallocateAlphaMemory(dec); + return NULL; } diff --git a/drivers/webp/dec/alphai.h b/drivers/webp/dec/alphai.h index 5fa230ca82..69dd7c0f5d 100644 --- a/drivers/webp/dec/alphai.h +++ b/drivers/webp/dec/alphai.h @@ -32,19 +32,18 @@ struct ALPHDecoder { int pre_processing_; struct VP8LDecoder* vp8l_dec_; VP8Io io_; - int use_8b_decode; // Although alpha channel requires only 1 byte per - // pixel, sometimes VP8LDecoder may need to allocate - // 4 bytes per pixel internally during decode. + int use_8b_decode_; // Although alpha channel requires only 1 byte per + // pixel, sometimes VP8LDecoder may need to allocate + // 4 bytes per pixel internally during decode. + uint8_t* output_; + const uint8_t* prev_line_; // last output row (or NULL) }; //------------------------------------------------------------------------------ // internal functions. Not public. -// Allocates a new alpha decoder instance. -ALPHDecoder* ALPHNew(void); - -// Clears and deallocates an alpha decoder instance. -void ALPHDelete(ALPHDecoder* const dec); +// Deallocate memory associated to dec->alpha_plane_ decoding +void WebPDeallocateAlphaMemory(VP8Decoder* const dec); //------------------------------------------------------------------------------ diff --git a/drivers/webp/dec/buffer.c b/drivers/webp/dec/buffer.c index 9ed2b3fe1a..547e69b434 100644 --- a/drivers/webp/dec/buffer.c +++ b/drivers/webp/dec/buffer.c @@ -92,7 +92,7 @@ static VP8StatusCode AllocateBuffer(WebPDecBuffer* const buffer) { return VP8_STATUS_INVALID_PARAM; } - if (!buffer->is_external_memory && buffer->private_memory == NULL) { + if (buffer->is_external_memory <= 0 && buffer->private_memory == NULL) { uint8_t* output; int uv_stride = 0, a_stride = 0; uint64_t uv_size = 0, a_size = 0, total_size; @@ -227,7 +227,7 @@ int WebPInitDecBufferInternal(WebPDecBuffer* buffer, int version) { void WebPFreeDecBuffer(WebPDecBuffer* buffer) { if (buffer != NULL) { - if (!buffer->is_external_memory) { + if (buffer->is_external_memory <= 0) { WebPSafeFree(buffer->private_memory); } buffer->private_memory = NULL; @@ -256,5 +256,45 @@ void WebPGrabDecBuffer(WebPDecBuffer* const src, WebPDecBuffer* const dst) { } } -//------------------------------------------------------------------------------ +VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src_buf, + WebPDecBuffer* const dst_buf) { + assert(src_buf != NULL && dst_buf != NULL); + assert(src_buf->colorspace == dst_buf->colorspace); + + dst_buf->width = src_buf->width; + dst_buf->height = src_buf->height; + if (CheckDecBuffer(dst_buf) != VP8_STATUS_OK) { + return VP8_STATUS_INVALID_PARAM; + } + if (WebPIsRGBMode(src_buf->colorspace)) { + const WebPRGBABuffer* const src = &src_buf->u.RGBA; + const WebPRGBABuffer* const dst = &dst_buf->u.RGBA; + WebPCopyPlane(src->rgba, src->stride, dst->rgba, dst->stride, + src_buf->width * kModeBpp[src_buf->colorspace], + src_buf->height); + } else { + const WebPYUVABuffer* const src = &src_buf->u.YUVA; + const WebPYUVABuffer* const dst = &dst_buf->u.YUVA; + WebPCopyPlane(src->y, src->y_stride, dst->y, dst->y_stride, + src_buf->width, src_buf->height); + WebPCopyPlane(src->u, src->u_stride, dst->u, dst->u_stride, + (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); + WebPCopyPlane(src->v, src->v_stride, dst->v, dst->v_stride, + (src_buf->width + 1) / 2, (src_buf->height + 1) / 2); + if (WebPIsAlphaMode(src_buf->colorspace)) { + WebPCopyPlane(src->a, src->a_stride, dst->a, dst->a_stride, + src_buf->width, src_buf->height); + } + } + return VP8_STATUS_OK; +} +int WebPAvoidSlowMemory(const WebPDecBuffer* const output, + const WebPBitstreamFeatures* const features) { + assert(output != NULL); + return (output->is_external_memory >= 2) && + WebPIsPremultipliedMode(output->colorspace) && + (features != NULL && features->has_alpha); +} + +//------------------------------------------------------------------------------ diff --git a/drivers/webp/dec/frame.c b/drivers/webp/dec/frame.c index b882133eab..22d291d2cd 100644 --- a/drivers/webp/dec/frame.c +++ b/drivers/webp/dec/frame.c @@ -316,6 +316,9 @@ static void PrecomputeFilterStrengths(VP8Decoder* const dec) { //------------------------------------------------------------------------------ // Dithering +// minimal amp that will provide a non-zero dithering effect +#define MIN_DITHER_AMP 4 + #define DITHER_AMP_TAB_SIZE 12 static const int kQuantToDitherAmp[DITHER_AMP_TAB_SIZE] = { // roughly, it's dqm->uv_mat_[1] @@ -356,27 +359,14 @@ void VP8InitDithering(const WebPDecoderOptions* const options, } } -// 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) - +// Convert to range: [-2,2] for dither=50, [-4,4] for dither=100 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; + uint8_t dither[64]; + int i; + for (i = 0; i < 8 * 8; ++i) { + dither[i] = VP8RandomBits2(rg, VP8_DITHER_AMP_BITS + 1, amp); } + VP8DitherCombine8x8(dither, dst, bps); } static void DitherRow(VP8Decoder* const dec) { @@ -462,7 +452,7 @@ static int FinishRow(VP8Decoder* const dec, VP8Io* const io) { if (dec->alpha_data_ != NULL && y_start < y_end) { // 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); + io->a = VP8DecompressAlphaRows(dec, io, y_start, y_end - y_start); if (io->a == NULL) { return VP8SetError(dec, VP8_STATUS_BITSTREAM_ERROR, "Could not decode alpha data."); diff --git a/drivers/webp/dec/idec.c b/drivers/webp/dec/idec.c index abafb9f3d1..8de131916e 100644 --- a/drivers/webp/dec/idec.c +++ b/drivers/webp/dec/idec.c @@ -70,7 +70,9 @@ struct WebPIDecoder { VP8Io io_; MemBuffer mem_; // input memory buffer. - WebPDecBuffer output_; // output buffer (when no external one is supplied) + WebPDecBuffer output_; // output buffer (when no external one is supplied, + // or if the external one has slow-memory) + WebPDecBuffer* final_output_; // Slow-memory output to copy to eventually. size_t chunk_size_; // Compressed VP8/VP8L size extracted from Header. int last_mb_y_; // last row reached for intra-mode decoding @@ -118,9 +120,9 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t offset) { if (idec->dec_ != NULL) { if (!idec->is_lossless_) { VP8Decoder* const dec = (VP8Decoder*)idec->dec_; - const int last_part = dec->num_parts_ - 1; + const uint32_t last_part = dec->num_parts_minus_one_; if (offset != 0) { - int p; + uint32_t p; for (p = 0; p <= last_part; ++p) { VP8RemapBitReader(dec->parts_ + p, offset); } @@ -132,7 +134,6 @@ static void DoRemap(WebPIDecoder* const idec, ptrdiff_t 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); } @@ -249,10 +250,16 @@ static VP8StatusCode FinishDecoding(WebPIDecoder* const idec) { idec->state_ = STATE_DONE; if (options != NULL && options->flip) { - return WebPFlipBuffer(output); - } else { - return VP8_STATUS_OK; + const VP8StatusCode status = WebPFlipBuffer(output); + if (status != VP8_STATUS_OK) return status; + } + if (idec->final_output_ != NULL) { + WebPCopyDecBufferPixels(output, idec->final_output_); // do the slow-copy + WebPFreeDecBuffer(&idec->output_); + *output = *idec->final_output_; + idec->final_output_ = NULL; } + return VP8_STATUS_OK; } //------------------------------------------------------------------------------ @@ -457,19 +464,20 @@ static VP8StatusCode DecodeRemaining(WebPIDecoder* const idec) { } for (; dec->mb_x_ < dec->mb_w_; ++dec->mb_x_) { VP8BitReader* const token_br = - &dec->parts_[dec->mb_y_ & (dec->num_parts_ - 1)]; + &dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_]; MBContext context; SaveContext(dec, token_br, &context); if (!VP8DecodeMB(dec, token_br)) { // We shouldn't fail when MAX_MB data was available - if (dec->num_parts_ == 1 && MemDataSize(&idec->mem_) > MAX_MB_SIZE) { + if (dec->num_parts_minus_one_ == 0 && + MemDataSize(&idec->mem_) > MAX_MB_SIZE) { return IDecError(idec, VP8_STATUS_BITSTREAM_ERROR); } RestoreContext(&context, dec, token_br); return VP8_STATUS_SUSPENDED; } // Release buffer only if there is only one partition - if (dec->num_parts_ == 1) { + if (dec->num_parts_minus_one_ == 0) { idec->mem_.start_ = token_br->buf_ - idec->mem_.buf_; assert(idec->mem_.start_ <= idec->mem_.end_); } @@ -575,9 +583,10 @@ static VP8StatusCode IDecode(WebPIDecoder* idec) { } //------------------------------------------------------------------------------ -// Public functions +// Internal constructor -WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { +static WebPIDecoder* NewDecoder(WebPDecBuffer* const output_buffer, + const WebPBitstreamFeatures* const features) { WebPIDecoder* idec = (WebPIDecoder*)WebPSafeCalloc(1ULL, sizeof(*idec)); if (idec == NULL) { return NULL; @@ -593,25 +602,46 @@ WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { VP8InitIo(&idec->io_); WebPResetDecParams(&idec->params_); - idec->params_.output = (output_buffer != NULL) ? output_buffer - : &idec->output_; + if (output_buffer == NULL || WebPAvoidSlowMemory(output_buffer, features)) { + idec->params_.output = &idec->output_; + idec->final_output_ = output_buffer; + if (output_buffer != NULL) { + idec->params_.output->colorspace = output_buffer->colorspace; + } + } else { + idec->params_.output = output_buffer; + idec->final_output_ = NULL; + } WebPInitCustomIo(&idec->params_, &idec->io_); // Plug the I/O functions. return idec; } +//------------------------------------------------------------------------------ +// Public functions + +WebPIDecoder* WebPINewDecoder(WebPDecBuffer* output_buffer) { + return NewDecoder(output_buffer, NULL); +} + WebPIDecoder* WebPIDecode(const uint8_t* data, size_t data_size, WebPDecoderConfig* config) { WebPIDecoder* idec; + WebPBitstreamFeatures tmp_features; + WebPBitstreamFeatures* const features = + (config == NULL) ? &tmp_features : &config->input; + memset(&tmp_features, 0, sizeof(tmp_features)); // 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) { + if (data != NULL && data_size > 0) { + if (WebPGetFeatures(data, data_size, features) != VP8_STATUS_OK) { return NULL; } } + // Create an instance of the incremental decoder - idec = WebPINewDecoder(config ? &config->output : NULL); + idec = (config != NULL) ? NewDecoder(&config->output, features) + : NewDecoder(NULL, features); if (idec == NULL) { return NULL; } @@ -645,11 +675,11 @@ 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); + const int is_external_memory = (output_buffer != NULL) ? 1 : 0; WebPIDecoder* idec; if (mode >= MODE_YUV) return NULL; - if (!is_external_memory) { // Overwrite parameters to sane values. + if (is_external_memory == 0) { // Overwrite parameters to sane values. output_buffer_size = 0; output_stride = 0; } else { // A buffer was passed. Validate the other params. @@ -671,11 +701,11 @@ 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) { - const int is_external_memory = (luma != NULL); + const int is_external_memory = (luma != NULL) ? 1 : 0; WebPIDecoder* idec; WEBP_CSP_MODE colorspace; - if (!is_external_memory) { // Overwrite parameters to sane values. + if (is_external_memory == 0) { // 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; @@ -783,6 +813,9 @@ static const WebPDecBuffer* GetOutputBuffer(const WebPIDecoder* const idec) { if (idec->state_ <= STATE_VP8_PARTS0) { return NULL; } + if (idec->final_output_ != NULL) { + return NULL; // not yet slow-copied + } return idec->params_.output; } @@ -792,8 +825,7 @@ const WebPDecBuffer* WebPIDecodedArea(const WebPIDecoder* idec, 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 (src != NULL) { if (width != NULL) *width = src->width; if (height != NULL) *height = idec->params_.last_y; } else { diff --git a/drivers/webp/dec/io.c b/drivers/webp/dec/io.c index 13e469ab73..8d5c43f325 100644 --- a/drivers/webp/dec/io.c +++ b/drivers/webp/dec/io.c @@ -119,6 +119,14 @@ static int EmitFancyRGB(const VP8Io* const io, WebPDecParams* const p) { //------------------------------------------------------------------------------ +static void FillAlphaPlane(uint8_t* dst, int w, int h, int stride) { + int j; + for (j = 0; j < h; ++j) { + memset(dst, 0xff, w * sizeof(*dst)); + dst += stride; + } +} + static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p, int expected_num_lines_out) { const uint8_t* alpha = io->a; @@ -137,10 +145,7 @@ static int EmitAlphaYUV(const VP8Io* const io, WebPDecParams* const p, } } 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; - } + FillAlphaPlane(dst, mb_w, mb_h, buf->a_stride); } return 0; } @@ -269,8 +274,8 @@ static int EmitRescaledYUV(const VP8Io* const io, WebPDecParams* const p) { static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p, int expected_num_lines_out) { + const WebPYUVABuffer* const buf = &p->output->u.YUVA; if (io->a != NULL) { - 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); @@ -280,6 +285,11 @@ static int EmitRescaledAlphaYUV(const VP8Io* const io, WebPDecParams* const p, WebPMultRows(dst_y, buf->y_stride, src_a, buf->a_stride, p->scaler_a.dst_width, num_lines_out, 1); } + } else if (buf->a != NULL) { + // the user requested alpha, but there is none, set it to opaque. + assert(p->last_y + expected_num_lines_out <= io->scaled_height); + FillAlphaPlane(buf->a + p->last_y * buf->a_stride, + io->scaled_width, expected_num_lines_out, buf->a_stride); } return 0; } diff --git a/drivers/webp/dec/vp8.c b/drivers/webp/dec/vp8.c index d89eb1c59e..336680c38c 100644 --- a/drivers/webp/dec/vp8.c +++ b/drivers/webp/dec/vp8.c @@ -50,7 +50,7 @@ VP8Decoder* VP8New(void) { SetOk(dec); WebPGetWorkerInterface()->Init(&dec->worker_); dec->ready_ = 0; - dec->num_parts_ = 1; + dec->num_parts_minus_one_ = 0; } return dec; } @@ -194,8 +194,8 @@ static VP8StatusCode ParsePartitions(VP8Decoder* const dec, size_t last_part; size_t p; - dec->num_parts_ = 1 << VP8GetValue(br, 2); - last_part = dec->num_parts_ - 1; + dec->num_parts_minus_one_ = (1 << VP8GetValue(br, 2)) - 1; + last_part = dec->num_parts_minus_one_; if (size < 3 * last_part) { // we can't even read the sizes with sz[]! That's a failure. return VP8_STATUS_NOT_ENOUGH_DATA; @@ -303,15 +303,22 @@ int VP8GetHeaders(VP8Decoder* const dec, VP8Io* const io) { 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; + // IMPORTANT! use some sane dimensions in crop_* and scaled_* fields. + // So they can be used interchangeably without always testing for + // 'use_cropping'. io->use_cropping = 0; io->crop_top = 0; io->crop_left = 0; io->crop_right = io->width; io->crop_bottom = io->height; + io->use_scaling = 0; + io->scaled_width = io->width; + io->scaled_height = io->height; + io->mb_w = io->width; // sanity check io->mb_h = io->height; // ditto @@ -579,7 +586,7 @@ 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)]; + &dec->parts_[dec->mb_y_ & dec->num_parts_minus_one_]; if (!VP8ParseIntraModeRow(&dec->br_, dec)) { return VP8SetError(dec, VP8_STATUS_NOT_ENOUGH_DATA, "Premature end-of-partition0 encountered."); @@ -649,8 +656,7 @@ void VP8Clear(VP8Decoder* const dec) { return; } WebPGetWorkerInterface()->End(&dec->worker_); - ALPHDelete(dec->alph_dec_); - dec->alph_dec_ = NULL; + WebPDeallocateAlphaMemory(dec); WebPSafeFree(dec->mem_); dec->mem_ = NULL; dec->mem_size_ = 0; @@ -659,4 +665,3 @@ void VP8Clear(VP8Decoder* const dec) { } //------------------------------------------------------------------------------ - diff --git a/drivers/webp/dec/vp8i.h b/drivers/webp/dec/vp8i.h index b5f2b23009..00da02badc 100644 --- a/drivers/webp/dec/vp8i.h +++ b/drivers/webp/dec/vp8i.h @@ -31,8 +31,8 @@ extern "C" { // version numbers #define DEC_MAJ_VERSION 0 -#define DEC_MIN_VERSION 4 -#define DEC_REV_VERSION 4 +#define DEC_MIN_VERSION 5 +#define DEC_REV_VERSION 1 // YUV-cache parameters. Cache is 32-bytes wide (= one cacheline). // Constraints are: We need to store one 16x16 block of luma samples (y), @@ -209,8 +209,8 @@ struct VP8Decoder { 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_; + // number of partitions minus one. + uint32_t num_parts_minus_one_; // per-partition boolean decoders. VP8BitReader parts_[MAX_NUM_PARTITIONS]; @@ -258,9 +258,11 @@ struct VP8Decoder { struct ALPHDecoder* alph_dec_; // alpha-plane decoder object const uint8_t* alpha_data_; // compressed alpha data (if present) size_t alpha_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). + int is_alpha_decoded_; // true if alpha_data_ is decoded in alpha_plane_ + uint8_t* alpha_plane_mem_; // memory allocated for alpha_plane_ + uint8_t* alpha_plane_; // output. Persistent, contains the whole data. + const uint8_t* alpha_prev_line_; // last decoded alpha row (or NULL) + int alpha_dithering_; // derived from decoding options (0=off, 100=full) }; //------------------------------------------------------------------------------ @@ -306,6 +308,7 @@ int VP8DecodeMB(VP8Decoder* const dec, VP8BitReader* const token_br); // in alpha.c const uint8_t* VP8DecompressAlphaRows(VP8Decoder* const dec, + const VP8Io* const io, int row, int num_rows); //------------------------------------------------------------------------------ diff --git a/drivers/webp/dec/vp8l.c b/drivers/webp/dec/vp8l.c index 19665a007d..cb2e3176b6 100644 --- a/drivers/webp/dec/vp8l.c +++ b/drivers/webp/dec/vp8l.c @@ -428,14 +428,14 @@ static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, alphabet_size += 1 << color_cache_bits; } size = ReadHuffmanCode(alphabet_size, dec, code_lengths, next); + if (size == 0) { + goto Error; + } if (is_trivial_literal && kLiteralMap[j] == 1) { is_trivial_literal = (next->bits == 0); } total_size += next->bits; next += size; - if (size == 0) { - goto Error; - } if (j <= ALPHA) { int local_max_bits = code_lengths[0]; int k; @@ -714,34 +714,22 @@ static void ApplyInverseTransforms(VP8LDecoder* const dec, int num_rows, } } -// Special method for paletted alpha data. -static void ApplyInverseTransformsAlpha(VP8LDecoder* const dec, int num_rows, - const uint8_t* const rows) { - const int start_row = dec->last_row_; - const int end_row = start_row + num_rows; - const uint8_t* rows_in = rows; - uint8_t* rows_out = (uint8_t*)dec->io_->opaque + dec->io_->width * start_row; - VP8LTransform* const transform = &dec->transforms_[0]; - assert(dec->next_transform_ == 1); - assert(transform->type_ == COLOR_INDEXING_TRANSFORM); - VP8LColorIndexInverseTransformAlpha(transform, start_row, end_row, rows_in, - rows_out); -} - // Processes (transforms, scales & color-converts) the rows decoded after the // last call. static void ProcessRows(VP8LDecoder* const dec, int row) { const uint32_t* const rows = dec->pixels_ + dec->width_ * dec->last_row_; const int num_rows = row - dec->last_row_; - if (num_rows <= 0) return; // Nothing to be done. - ApplyInverseTransforms(dec, num_rows, rows); - - // Emit output. - { + assert(row <= dec->io_->crop_bottom); + // We can't process more than NUM_ARGB_CACHE_ROWS at a time (that's the size + // of argb_cache_), but we currently don't need more than that. + assert(num_rows <= NUM_ARGB_CACHE_ROWS); + if (num_rows > 0) { // Emit output. VP8Io* const io = dec->io_; uint8_t* rows_data = (uint8_t*)dec->argb_cache_; const int in_stride = io->width * sizeof(uint32_t); // in unit of RGBA + + ApplyInverseTransforms(dec, num_rows, rows); if (!SetCropWindow(io, dec->last_row_, row, &rows_data, in_stride)) { // Nothing to output (this time). } else { @@ -786,14 +774,46 @@ static int Is8bOptimizable(const VP8LMetadata* const hdr) { return 1; } -static void ExtractPalettedAlphaRows(VP8LDecoder* const dec, int row) { - const int num_rows = row - dec->last_row_; - const uint8_t* const in = - (uint8_t*)dec->pixels_ + dec->width_ * dec->last_row_; - if (num_rows > 0) { - ApplyInverseTransformsAlpha(dec, num_rows, in); +static void AlphaApplyFilter(ALPHDecoder* const alph_dec, + int first_row, int last_row, + uint8_t* out, int stride) { + if (alph_dec->filter_ != WEBP_FILTER_NONE) { + int y; + const uint8_t* prev_line = alph_dec->prev_line_; + assert(WebPUnfilters[alph_dec->filter_] != NULL); + for (y = first_row; y < last_row; ++y) { + WebPUnfilters[alph_dec->filter_](prev_line, out, out, stride); + prev_line = out; + out += stride; + } + alph_dec->prev_line_ = prev_line; } - dec->last_row_ = dec->last_out_row_ = row; +} + +static void ExtractPalettedAlphaRows(VP8LDecoder* const dec, int last_row) { + // For vertical and gradient filtering, we need to decode the part above the + // crop_top row, in order to have the correct spatial predictors. + ALPHDecoder* const alph_dec = (ALPHDecoder*)dec->io_->opaque; + const int top_row = + (alph_dec->filter_ == WEBP_FILTER_NONE || + alph_dec->filter_ == WEBP_FILTER_HORIZONTAL) ? dec->io_->crop_top + : dec->last_row_; + const int first_row = (dec->last_row_ < top_row) ? top_row : dec->last_row_; + assert(last_row <= dec->io_->crop_bottom); + if (last_row > first_row) { + // Special method for paletted alpha data. We only process the cropped area. + const int width = dec->io_->width; + uint8_t* out = alph_dec->output_ + width * first_row; + const uint8_t* const in = + (uint8_t*)dec->pixels_ + dec->width_ * first_row; + VP8LTransform* const transform = &dec->transforms_[0]; + assert(dec->next_transform_ == 1); + assert(transform->type_ == COLOR_INDEXING_TRANSFORM); + VP8LColorIndexInverseTransformAlpha(transform, first_row, last_row, + in, out); + AlphaApplyFilter(alph_dec, first_row, last_row, out, width); + } + dec->last_row_ = dec->last_out_row_ = last_row; } //------------------------------------------------------------------------------ @@ -922,14 +942,14 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, int col = dec->last_pixel_ % width; VP8LBitReader* const br = &dec->br_; VP8LMetadata* const hdr = &dec->hdr_; - const HTreeGroup* htree_group = GetHtreeGroupForPos(hdr, col, row); int pos = dec->last_pixel_; // current position const int end = width * height; // End of data const int last = width * last_row; // Last pixel to decode const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES; const int mask = hdr->huffman_mask_; - assert(htree_group != NULL); - assert(pos < end); + const HTreeGroup* htree_group = + (pos < last) ? GetHtreeGroupForPos(hdr, col, row) : NULL; + assert(pos <= end); assert(last_row <= height); assert(Is8bOptimizable(hdr)); @@ -939,6 +959,7 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, if ((col & mask) == 0) { htree_group = GetHtreeGroupForPos(hdr, col, row); } + assert(htree_group != NULL); VP8LFillBitWindow(br); code = ReadSymbol(htree_group->htrees[GREEN], br); if (code < NUM_LITERAL_CODES) { // Literal @@ -948,7 +969,7 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, if (col >= width) { col = 0; ++row; - if (row % NUM_ARGB_CACHE_ROWS == 0) { + if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { ExtractPalettedAlphaRows(dec, row); } } @@ -971,7 +992,7 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, while (col >= width) { col -= width; ++row; - if (row % NUM_ARGB_CACHE_ROWS == 0) { + if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { ExtractPalettedAlphaRows(dec, row); } } @@ -985,7 +1006,7 @@ static int DecodeAlphaData(VP8LDecoder* const dec, uint8_t* const data, assert(br->eos_ == VP8LIsEndOfStream(br)); } // Process the remaining rows corresponding to last row-block. - ExtractPalettedAlphaRows(dec, row); + ExtractPalettedAlphaRows(dec, row > last_row ? last_row : row); End: if (!ok || (br->eos_ && pos < end)) { @@ -1025,7 +1046,6 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, int col = dec->last_pixel_ % width; VP8LBitReader* const br = &dec->br_; VP8LMetadata* const hdr = &dec->hdr_; - HTreeGroup* htree_group = GetHtreeGroupForPos(hdr, col, row); uint32_t* src = data + dec->last_pixel_; uint32_t* last_cached = src; uint32_t* const src_end = data + width * height; // End of data @@ -1036,8 +1056,9 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, VP8LColorCache* const color_cache = (hdr->color_cache_size_ > 0) ? &hdr->color_cache_ : NULL; const int mask = hdr->huffman_mask_; - assert(htree_group != NULL); - assert(src < src_end); + const HTreeGroup* htree_group = + (src < src_last) ? GetHtreeGroupForPos(hdr, col, row) : NULL; + assert(dec->last_row_ < last_row); assert(src_last <= src_end); while (src < src_last) { @@ -1049,7 +1070,10 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, // Only update when changing tile. Note we could use this test: // if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed // but that's actually slower and needs storing the previous col/row. - if ((col & mask) == 0) htree_group = GetHtreeGroupForPos(hdr, col, row); + if ((col & mask) == 0) { + htree_group = GetHtreeGroupForPos(hdr, col, row); + } + assert(htree_group != NULL); if (htree_group->is_trivial_code) { *src = htree_group->literal_arb; goto AdvanceByOne; @@ -1080,8 +1104,10 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, if (col >= width) { col = 0; ++row; - if ((row % NUM_ARGB_CACHE_ROWS == 0) && (process_func != NULL)) { - process_func(dec, row); + if (process_func != NULL) { + if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { + process_func(dec, row); + } } if (color_cache != NULL) { while (last_cached < src) { @@ -1108,8 +1134,10 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, while (col >= width) { col -= width; ++row; - if ((row % NUM_ARGB_CACHE_ROWS == 0) && (process_func != NULL)) { - process_func(dec, row); + if (process_func != NULL) { + if (row <= last_row && (row % NUM_ARGB_CACHE_ROWS == 0)) { + process_func(dec, row); + } } } // Because of the check done above (before 'src' was incremented by @@ -1140,7 +1168,7 @@ static int DecodeImageData(VP8LDecoder* const dec, uint32_t* const data, } else if (!br->eos_) { // Process the remaining rows corresponding to last row-block. if (process_func != NULL) { - process_func(dec, row); + process_func(dec, row > last_row ? last_row : row); } dec->status_ = VP8_STATUS_OK; dec->last_pixel_ = (int)(src - data); // end-of-scan marker @@ -1438,46 +1466,51 @@ static int AllocateInternalBuffers8b(VP8LDecoder* const dec) { //------------------------------------------------------------------------------ // Special row-processing that only stores the alpha data. -static void ExtractAlphaRows(VP8LDecoder* const dec, int row) { - const int num_rows = row - dec->last_row_; - const uint32_t* const in = dec->pixels_ + dec->width_ * dec->last_row_; - - if (num_rows <= 0) return; // Nothing to be done. - ApplyInverseTransforms(dec, num_rows, in); - - // Extract alpha (which is stored in the green plane). - { +static void ExtractAlphaRows(VP8LDecoder* const dec, int last_row) { + int cur_row = dec->last_row_; + int num_rows = last_row - cur_row; + const uint32_t* in = dec->pixels_ + dec->width_ * cur_row; + + assert(last_row <= dec->io_->crop_bottom); + while (num_rows > 0) { + const int num_rows_to_process = + (num_rows > NUM_ARGB_CACHE_ROWS) ? NUM_ARGB_CACHE_ROWS : num_rows; + // Extract alpha (which is stored in the green plane). + ALPHDecoder* const alph_dec = (ALPHDecoder*)dec->io_->opaque; + uint8_t* const output = alph_dec->output_; const int width = dec->io_->width; // the final width (!= dec->width_) - const int cache_pixs = width * num_rows; - uint8_t* const dst = (uint8_t*)dec->io_->opaque + width * dec->last_row_; + const int cache_pixs = width * num_rows_to_process; + uint8_t* const dst = output + width * cur_row; const uint32_t* const src = dec->argb_cache_; int i; + ApplyInverseTransforms(dec, num_rows_to_process, in); for (i = 0; i < cache_pixs; ++i) dst[i] = (src[i] >> 8) & 0xff; - } - dec->last_row_ = dec->last_out_row_ = row; + AlphaApplyFilter(alph_dec, + cur_row, cur_row + num_rows_to_process, dst, width); + num_rows -= num_rows_to_process; + in += num_rows_to_process * dec->width_; + cur_row += num_rows_to_process; + } + assert(cur_row == last_row); + dec->last_row_ = dec->last_out_row_ = last_row; } int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, - const uint8_t* const data, size_t data_size, - uint8_t* const output) { + const uint8_t* const data, size_t data_size) { int ok = 0; - VP8LDecoder* dec; - VP8Io* io; + VP8LDecoder* dec = VP8LNew(); + + if (dec == NULL) return 0; + assert(alph_dec != NULL); - alph_dec->vp8l_dec_ = VP8LNew(); - if (alph_dec->vp8l_dec_ == NULL) return 0; - dec = alph_dec->vp8l_dec_; + alph_dec->vp8l_dec_ = dec; dec->width_ = alph_dec->width_; dec->height_ = alph_dec->height_; dec->io_ = &alph_dec->io_; - io = dec->io_; - - VP8InitIo(io); - WebPInitCustomIo(NULL, io); // Just a sanity Init. io won't be used. - io->opaque = output; - io->width = alph_dec->width_; - io->height = alph_dec->height_; + dec->io_->opaque = alph_dec; + dec->io_->width = alph_dec->width_; + dec->io_->height = alph_dec->height_; dec->status_ = VP8_STATUS_OK; VP8LInitBitReader(&dec->br_, data, data_size); @@ -1492,11 +1525,11 @@ int VP8LDecodeAlphaHeader(ALPHDecoder* const alph_dec, if (dec->next_transform_ == 1 && dec->transforms_[0].type_ == COLOR_INDEXING_TRANSFORM && Is8bOptimizable(&dec->hdr_)) { - alph_dec->use_8b_decode = 1; + alph_dec->use_8b_decode_ = 1; ok = AllocateInternalBuffers8b(dec); } else { // Allocate internal buffers (note that dec->width_ may have changed here). - alph_dec->use_8b_decode = 0; + alph_dec->use_8b_decode_ = 0; ok = AllocateInternalBuffers32b(dec, alph_dec->width_); } @@ -1515,12 +1548,12 @@ int VP8LDecodeAlphaImageStream(ALPHDecoder* const alph_dec, int last_row) { assert(dec != NULL); assert(last_row <= dec->height_); - if (dec->last_pixel_ == dec->width_ * dec->height_) { + if (dec->last_row_ >= last_row) { return 1; // done } // Decode (with special row processing). - return alph_dec->use_8b_decode ? + return alph_dec->use_8b_decode_ ? DecodeAlphaData(dec, (uint8_t*)dec->pixels_, dec->width_, dec->height_, last_row) : DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_, @@ -1611,7 +1644,7 @@ int VP8LDecodeImage(VP8LDecoder* const dec) { // Decode. if (!DecodeImageData(dec, dec->pixels_, dec->width_, dec->height_, - dec->height_, ProcessRows)) { + io->crop_bottom, ProcessRows)) { goto Err; } diff --git a/drivers/webp/dec/vp8li.h b/drivers/webp/dec/vp8li.h index 8886e47f62..9313bdc0af 100644 --- a/drivers/webp/dec/vp8li.h +++ b/drivers/webp/dec/vp8li.h @@ -100,8 +100,7 @@ struct ALPHDecoder; // Defined in dec/alphai.h. // 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); + const uint8_t* const data, size_t data_size); // 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 diff --git a/drivers/webp/dec/webp.c b/drivers/webp/dec/webp.c index 93a113a48d..dd6c090f00 100644 --- a/drivers/webp/dec/webp.c +++ b/drivers/webp/dec/webp.c @@ -415,7 +415,8 @@ static VP8StatusCode ParseHeadersInternal(const uint8_t* data, } VP8StatusCode WebPParseHeaders(WebPHeaderStructure* const headers) { - VP8StatusCode status; + // status is marked volatile as a workaround for a clang-3.8 (aarch64) bug + volatile VP8StatusCode status; int has_animation = 0; assert(headers != NULL); // fill out headers, ignore width/height/has_alpha. @@ -512,10 +513,12 @@ 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); + } else { + if (params->options != NULL && params->options->flip) { + // This restores the original stride values if options->flip was used + // during the call to WebPAllocateDecBuffer above. + status = WebPFlipBuffer(params->output); + } } return status; } @@ -758,9 +761,24 @@ VP8StatusCode WebPDecode(const uint8_t* data, size_t data_size, } WebPResetDecParams(¶ms); - params.output = &config->output; params.options = &config->options; - status = DecodeInto(data, data_size, ¶ms); + params.output = &config->output; + if (WebPAvoidSlowMemory(params.output, &config->input)) { + // decoding to slow memory: use a temporary in-mem buffer to decode into. + WebPDecBuffer in_mem_buffer; + WebPInitDecBuffer(&in_mem_buffer); + in_mem_buffer.colorspace = config->output.colorspace; + in_mem_buffer.width = config->input.width; + in_mem_buffer.height = config->input.height; + params.output = &in_mem_buffer; + status = DecodeInto(data, data_size, ¶ms); + if (status == VP8_STATUS_OK) { // do the slow-copy + status = WebPCopyDecBufferPixels(&in_mem_buffer, &config->output); + } + WebPFreeDecBuffer(&in_mem_buffer); + } else { + status = DecodeInto(data, data_size, ¶ms); + } return status; } @@ -809,7 +827,7 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options, } // Filter - io->bypass_filtering = options && options->bypass_filtering; + io->bypass_filtering = (options != NULL) && options->bypass_filtering; // Fancy upsampler #ifdef FANCY_UPSAMPLING @@ -826,4 +844,3 @@ int WebPIoInitFromOptions(const WebPDecoderOptions* const options, } //------------------------------------------------------------------------------ - diff --git a/drivers/webp/dec/webpi.h b/drivers/webp/dec/webpi.h index c75a2e4a5b..991b194c22 100644 --- a/drivers/webp/dec/webpi.h +++ b/drivers/webp/dec/webpi.h @@ -45,11 +45,20 @@ struct WebPDecParams { OutputFunc emit; // output RGB or YUV samples OutputAlphaFunc emit_alpha; // output alpha channel OutputRowFunc emit_alpha_row; // output one line of rescaled alpha values + + WebPDecBuffer* final_output; // In case the user supplied a slow-memory + // output, we decode image in temporary buffer + // (this::output) and copy it here. + WebPDecBuffer tmp_buffer; // this::output will point to this one in case + // of slow memory. }; // Should be called first, before any use of the WebPDecParams object. void WebPResetDecParams(WebPDecParams* const params); +// Delete all memory (after an error occurred, for instance) +void WebPFreeDecParams(WebPDecParams* const params); + //------------------------------------------------------------------------------ // Header parsing helpers @@ -107,13 +116,23 @@ VP8StatusCode WebPAllocateDecBuffer(int width, int height, 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'). +// memory (still held by 'src'). No pixels are copied. 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); +// Copy pixels from 'src' into a *preallocated* 'dst' buffer. Returns +// VP8_STATUS_INVALID_PARAM if the 'dst' is not set up correctly for the copy. +VP8StatusCode WebPCopyDecBufferPixels(const WebPDecBuffer* const src, + WebPDecBuffer* const dst); + +// Returns true if decoding will be slow with the current configuration +// and bitstream features. +int WebPAvoidSlowMemory(const WebPDecBuffer* const output, + const WebPBitstreamFeatures* const features); + //------------------------------------------------------------------------------ #ifdef __cplusplus diff --git a/drivers/webp/decode.h b/drivers/webp/decode.h index fa4b13411d..7a3bed93a4 100644 --- a/drivers/webp/decode.h +++ b/drivers/webp/decode.h @@ -20,7 +20,7 @@ extern "C" { #endif -#define WEBP_DECODER_ABI_VERSION 0x0207 // MAJOR(8b) + MINOR(8b) +#define WEBP_DECODER_ABI_VERSION 0x0208 // MAJOR(8b) + MINOR(8b) // Note: forward declaring enumerations is not allowed in (strict) C and C++, // the types are left here for reference. @@ -39,8 +39,8 @@ typedef struct WebPDecoderConfig WebPDecoderConfig; 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. +// This function will also validate the header, returning true on success, +// false otherwise. '*width' and '*height' are only valid on successful return. // 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); @@ -197,7 +197,10 @@ struct WebPYUVABuffer { // view as YUVA struct WebPDecBuffer { WEBP_CSP_MODE colorspace; // Colorspace. int width, height; // Dimensions. - int is_external_memory; // If true, 'internal_memory' pointer is not used. + int is_external_memory; // If non-zero, 'internal_memory' pointer is not + // used. If value is '2' or more, the external + // memory is considered 'slow' and multiple + // read/write will be avoided. union { WebPRGBABuffer RGBA; WebPYUVABuffer YUVA; @@ -205,7 +208,7 @@ struct WebPDecBuffer { 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 + // is_external_memory is 0). Should not be used // externally, but accessed via the buffer union. }; @@ -269,7 +272,7 @@ typedef enum VP8StatusCode { // 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 +// not set to 0. 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. @@ -468,16 +471,18 @@ static WEBP_INLINE int WebPInitDecoderConfig(WebPDecoderConfig* config) { // 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. +// is used. If 'config' is not NULL, it must outlive the WebPIDecoder object +// as some references to its fields will be used. No internal copy of 'config' +// is made. // 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). +// the error condition, if available). 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). +// if the decoding was successful). Note that 'config' cannot be NULL. WEBP_EXTERN(VP8StatusCode) WebPDecode(const uint8_t* data, size_t data_size, WebPDecoderConfig* config); diff --git a/drivers/webp/demux.h b/drivers/webp/demux.h index 6fbe775851..454f6914b2 100644 --- a/drivers/webp/demux.h +++ b/drivers/webp/demux.h @@ -55,7 +55,7 @@ extern "C" { #endif -#define WEBP_DEMUX_ABI_VERSION 0x0105 // MAJOR(8b) + MINOR(8b) +#define WEBP_DEMUX_ABI_VERSION 0x0107 // MAJOR(8b) + MINOR(8b) // Note: forward declaring enumerations is not allowed in (strict) C and C++, // the types are left here for reference. @@ -88,7 +88,8 @@ typedef enum WebPDemuxState { WEBP_EXTERN(WebPDemuxer*) WebPDemuxInternal( const WebPData*, int, WebPDemuxState*, int); -// Parses the full WebP file given by 'data'. +// Parses the full WebP file given by 'data'. For single images the WebP file +// header alone or the file header and the chunk header may be absent. // 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); @@ -137,17 +138,15 @@ WEBP_EXTERN(uint32_t) WebPDemuxGetI( struct WebPIterator { int frame_num; int num_frames; // equivalent to WEBP_FF_FRAME_COUNT. - int fragment_num; - int num_fragments; int x_offset, y_offset; // offset relative to the canvas. - int width, height; // dimensions of this frame or fragment. + int width, height; // dimensions of this frame. int duration; // display duration in milliseconds. WebPMuxAnimDispose dispose_method; // dispose method for the frame. int complete; // true if 'fragment' contains a full frame. partial images // may still be decoded with the WebP incremental decoder. - WebPData fragment; // The frame or fragment given by 'frame_num' and - // 'fragment_num'. - int has_alpha; // True if the frame or fragment contains transparency. + WebPData fragment; // The frame given by 'frame_num'. Note for historical + // reasons this is called a fragment. + int has_alpha; // True if the frame contains transparency. WebPMuxAnimBlend blend_method; // Blend operation for the frame. uint32_t pad[2]; // padding for later use. @@ -155,8 +154,7 @@ struct WebPIterator { }; // Retrieves frame 'frame_number' from 'dmux'. -// 'iter->fragment' points to the first fragment on return from this function. -// Individual fragments may be extracted using WebPDemuxSelectFragment(). +// 'iter->fragment' points to the frame on return from this function. // 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. @@ -170,10 +168,6 @@ WEBP_EXTERN(int) WebPDemuxGetFrame( WEBP_EXTERN(int) WebPDemuxNextFrame(WebPIterator* iter); WEBP_EXTERN(int) WebPDemuxPrevFrame(WebPIterator* iter); -// Sets 'iter->fragment' to reflect fragment number 'fragment_num'. -// Returns true if fragment 'fragment_num' is present, false otherwise. -WEBP_EXTERN(int) WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num); - // Releases any memory associated with 'iter'. // Must be called before any subsequent calls to WebPDemuxGetChunk() on the same // iter. Also, must be called before destroying the associated WebPDemuxer with diff --git a/drivers/webp/demux/anim_decode.c b/drivers/webp/demux/anim_decode.c index c81cedfba0..39cf3de197 100644 --- a/drivers/webp/demux/anim_decode.c +++ b/drivers/webp/demux/anim_decode.c @@ -51,7 +51,7 @@ static void DefaultDecoderOptions(WebPAnimDecoderOptions* const dec_options) { } int WebPAnimDecoderOptionsInitInternal(WebPAnimDecoderOptions* dec_options, - int abi_version) { + int abi_version) { if (dec_options == NULL || WEBP_ABI_IS_INCOMPATIBLE(abi_version, WEBP_DEMUX_ABI_VERSION)) { return 0; @@ -380,12 +380,12 @@ int WebPAnimDecoderGetNext(WebPAnimDecoder* dec, if (width1 > 0) { const size_t offset1 = canvas_y * width + left1; blend_row((uint32_t*)dec->curr_frame_ + offset1, - (uint32_t*)dec->prev_frame_disposed_ + offset1, width1); + (uint32_t*)dec->prev_frame_disposed_ + offset1, width1); } if (width2 > 0) { const size_t offset2 = canvas_y * width + left2; blend_row((uint32_t*)dec->curr_frame_ + offset2, - (uint32_t*)dec->prev_frame_disposed_ + offset2, width2); + (uint32_t*)dec->prev_frame_disposed_ + offset2, width2); } } } diff --git a/drivers/webp/demux/demux.c b/drivers/webp/demux/demux.c index 3717e21165..df93c5b379 100644 --- a/drivers/webp/demux/demux.c +++ b/drivers/webp/demux/demux.c @@ -24,8 +24,8 @@ #include "webp/format_constants.h" #define DMUX_MAJ_VERSION 0 -#define DMUX_MIN_VERSION 2 -#define DMUX_REV_VERSION 2 +#define DMUX_MIN_VERSION 3 +#define DMUX_REV_VERSION 0 typedef struct { size_t start_; // start location of the data @@ -47,8 +47,7 @@ typedef struct Frame { int duration_; WebPMuxAnimDispose dispose_method_; WebPMuxAnimBlend blend_method_; - int is_fragment_; // this is a frame fragment (and not a full frame). - int frame_num_; // the referent frame number for use in assembling fragments. + int frame_num_; int complete_; // img_components_ contains a full image. ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH struct Frame* next_; @@ -193,6 +192,19 @@ static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) { return 1; } +static void SetFrameInfo(size_t start_offset, size_t size, + int frame_num, int complete, + const WebPBitstreamFeatures* const features, + Frame* const frame) { + frame->img_components_[0].offset_ = start_offset; + frame->img_components_[0].size_ = size; + frame->width_ = features->width; + frame->height_ = features->height; + frame->has_alpha_ |= features->has_alpha; + frame->frame_num_ = frame_num; + frame->complete_ = complete; +} + // Store image bearing chunks to 'frame'. static ParseStatus StoreFrame(int frame_num, uint32_t min_size, MemBuffer* const mem, Frame* const frame) { @@ -248,13 +260,8 @@ static ParseStatus StoreFrame(int frame_num, uint32_t min_size, return PARSE_ERROR; } ++image_chunks; - frame->img_components_[0].offset_ = chunk_start_offset; - frame->img_components_[0].size_ = chunk_size; - frame->width_ = features.width; - frame->height_ = features.height; - frame->has_alpha_ |= features.has_alpha; - frame->frame_num_ = frame_num; - frame->complete_ = (status == PARSE_OK); + SetFrameInfo(chunk_start_offset, chunk_size, frame_num, + status == PARSE_OK, &features, frame); Skip(mem, payload_available); } else { goto Done; @@ -564,8 +571,6 @@ static int IsValidSimpleFormat(const WebPDemuxer* const dmux) { // If 'exact' is true, check that the image resolution matches the canvas. // If 'exact' is false, check that the x/y offsets do not exceed the canvas. -// TODO(jzern): this is insufficient in the fragmented image case if the -// expectation is that the fragments completely cover the canvas. static int CheckFrameBounds(const Frame* const frame, int exact, int canvas_width, int canvas_height) { if (exact) { @@ -597,16 +602,13 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { while (f != NULL) { const int cur_frame_set = f->frame_num_; - int frame_count = 0, fragment_count = 0; + int frame_count = 0; - // Check frame properties and if the image is composed of fragments that - // each fragment came from a fragment. + // Check frame properties. for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) { const ChunkData* const image = f->img_components_; const ChunkData* const alpha = f->img_components_ + 1; - if (is_fragmented && !f->is_fragment_) return 0; - if (!is_fragmented && f->is_fragment_) return 0; if (!is_animation && f->frame_num_ > 1) return 0; if (f->complete_) { @@ -631,16 +633,13 @@ static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { } if (f->width_ > 0 && f->height_ > 0 && - !CheckFrameBounds(f, !(is_animation || is_fragmented), + !CheckFrameBounds(f, !is_animation, dmux->canvas_width_, dmux->canvas_height_)) { return 0; } - fragment_count += f->is_fragment_; ++frame_count; } - if (!is_fragmented && frame_count > 1) return 0; - if (fragment_count > 0 && frame_count != fragment_count) return 0; } return 1; } @@ -659,6 +658,41 @@ static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) { dmux->mem_ = *mem; } +static ParseStatus CreateRawImageDemuxer(MemBuffer* const mem, + WebPDemuxer** demuxer) { + WebPBitstreamFeatures features; + const VP8StatusCode status = + WebPGetFeatures(mem->buf_, mem->buf_size_, &features); + *demuxer = NULL; + if (status != VP8_STATUS_OK) { + return (status == VP8_STATUS_NOT_ENOUGH_DATA) ? PARSE_NEED_MORE_DATA + : PARSE_ERROR; + } + + { + WebPDemuxer* const dmux = (WebPDemuxer*)WebPSafeCalloc(1ULL, sizeof(*dmux)); + Frame* const frame = (Frame*)WebPSafeCalloc(1ULL, sizeof(*frame)); + if (dmux == NULL || frame == NULL) goto Error; + InitDemux(dmux, mem); + SetFrameInfo(0, mem->buf_size_, 1 /*frame_num*/, 1 /*complete*/, &features, + frame); + if (!AddFrame(dmux, frame)) goto Error; + dmux->state_ = WEBP_DEMUX_DONE; + dmux->canvas_width_ = frame->width_; + dmux->canvas_height_ = frame->height_; + dmux->feature_flags_ |= frame->has_alpha_ ? ALPHA_FLAG : 0; + dmux->num_frames_ = 1; + assert(IsValidSimpleFormat(dmux)); + *demuxer = dmux; + return PARSE_OK; + + Error: + WebPSafeFree(dmux); + WebPSafeFree(frame); + return PARSE_ERROR; + } +} + WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, WebPDemuxState* state, int version) { const ChunkParser* parser; @@ -675,6 +709,15 @@ WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, if (!InitMemBuffer(&mem, data->bytes, data->size)) return NULL; status = ReadHeader(&mem); if (status != PARSE_OK) { + // If parsing of the webp file header fails attempt to handle a raw + // VP8/VP8L frame. Note 'allow_partial' is ignored in this case. + if (status == PARSE_ERROR) { + status = CreateRawImageDemuxer(&mem, &dmux); + if (status == PARSE_OK) { + if (state != NULL) *state = WEBP_DEMUX_DONE; + return dmux; + } + } if (state != NULL) { *state = (status == PARSE_NEED_MORE_DATA) ? WEBP_DEMUX_PARSING_HEADER : WEBP_DEMUX_PARSE_ERROR; @@ -746,8 +789,6 @@ uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) { // ----------------------------------------------------------------------------- // Frame iteration -// Find the first 'frame_num' frame. There may be multiple such frames in a -// fragmented frame. static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) { const Frame* f; for (f = dmux->frames_; f != NULL; f = f->next_) { @@ -756,21 +797,6 @@ static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) { return f; } -// Returns fragment 'fragment_num' and the total count. -static const Frame* GetFragment( - const Frame* const frame_set, int fragment_num, int* const count) { - const int this_frame = frame_set->frame_num_; - const Frame* f = frame_set; - const Frame* fragment = NULL; - int total; - - for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) { - if (++total == fragment_num) fragment = f; - } - *count = total; - return fragment; -} - static const uint8_t* GetFramePayload(const uint8_t* const mem_buf, const Frame* const frame, size_t* const data_size) { @@ -797,31 +823,25 @@ static const uint8_t* GetFramePayload(const uint8_t* const mem_buf, // Create a whole 'frame' from VP8 (+ alpha) or lossless. static int SynthesizeFrame(const WebPDemuxer* const dmux, - const Frame* const first_frame, - int fragment_num, WebPIterator* const iter) { + const Frame* const frame, + WebPIterator* const iter) { const uint8_t* const mem_buf = dmux->mem_.buf_; - int num_fragments; size_t payload_size = 0; - const Frame* const fragment = - GetFragment(first_frame, fragment_num, &num_fragments); - const uint8_t* const payload = - GetFramePayload(mem_buf, fragment, &payload_size); + const uint8_t* const payload = GetFramePayload(mem_buf, frame, &payload_size); if (payload == NULL) return 0; - assert(first_frame != NULL); + assert(frame != NULL); - iter->frame_num = first_frame->frame_num_; + iter->frame_num = frame->frame_num_; iter->num_frames = dmux->num_frames_; - iter->fragment_num = fragment_num; - iter->num_fragments = num_fragments; - iter->x_offset = fragment->x_offset_; - iter->y_offset = fragment->y_offset_; - iter->width = fragment->width_; - iter->height = fragment->height_; - iter->has_alpha = fragment->has_alpha_; - iter->duration = fragment->duration_; - iter->dispose_method = fragment->dispose_method_; - iter->blend_method = fragment->blend_method_; - iter->complete = fragment->complete_; + iter->x_offset = frame->x_offset_; + iter->y_offset = frame->y_offset_; + iter->width = frame->width_; + iter->height = frame->height_; + iter->has_alpha = frame->has_alpha_; + iter->duration = frame->duration_; + iter->dispose_method = frame->dispose_method_; + iter->blend_method = frame->blend_method_; + iter->complete = frame->complete_; iter->fragment.bytes = payload; iter->fragment.size = payload_size; return 1; @@ -837,7 +857,7 @@ static int SetFrame(int frame_num, WebPIterator* const iter) { frame = GetFrame(dmux, frame_num); if (frame == NULL) return 0; - return SynthesizeFrame(dmux, frame, 1, iter); + return SynthesizeFrame(dmux, frame, iter); } int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) { @@ -859,17 +879,6 @@ int WebPDemuxPrevFrame(WebPIterator* iter) { return SetFrame(iter->frame_num - 1, iter); } -int WebPDemuxSelectFragment(WebPIterator* iter, int fragment_num) { - if (iter != NULL && iter->private_ != NULL && fragment_num > 0) { - const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; - const Frame* const frame = GetFrame(dmux, iter->frame_num); - if (frame == NULL) return 0; - - return SynthesizeFrame(dmux, frame, fragment_num, iter); - } - return 0; -} - void WebPDemuxReleaseIterator(WebPIterator* iter) { (void)iter; } diff --git a/drivers/webp/dsp/common_sse2.h b/drivers/webp/dsp/common_sse2.h new file mode 100644 index 0000000000..7cea13fb3c --- /dev/null +++ b/drivers/webp/dsp/common_sse2.h @@ -0,0 +1,109 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// 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 code common to several files. +// +// Author: Vincent Rabaud (vrabaud@google.com) + +#ifndef WEBP_DSP_COMMON_SSE2_H_ +#define WEBP_DSP_COMMON_SSE2_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(WEBP_USE_SSE2) + +#include <emmintrin.h> + +//------------------------------------------------------------------------------ +// Quite useful macro for debugging. Left here for convenience. + +#if 0 +#include <stdio.h> +static WEBP_INLINE void PrintReg(const __m128i r, const char* const name, + int size) { + int n; + union { + __m128i r; + uint8_t i8[16]; + uint16_t i16[8]; + uint32_t i32[4]; + uint64_t i64[2]; + } tmp; + tmp.r = r; + fprintf(stderr, "%s\t: ", name); + if (size == 8) { + for (n = 0; n < 16; ++n) fprintf(stderr, "%.2x ", tmp.i8[n]); + } else if (size == 16) { + for (n = 0; n < 8; ++n) fprintf(stderr, "%.4x ", tmp.i16[n]); + } else if (size == 32) { + for (n = 0; n < 4; ++n) fprintf(stderr, "%.8x ", tmp.i32[n]); + } else { + for (n = 0; n < 2; ++n) fprintf(stderr, "%.16lx ", tmp.i64[n]); + } + fprintf(stderr, "\n"); +} +#endif + +//------------------------------------------------------------------------------ +// Math functions. + +// Return the sum of all the 8b in the register. +static WEBP_INLINE int VP8HorizontalAdd8b(const __m128i* const a) { + const __m128i zero = _mm_setzero_si128(); + const __m128i sad8x2 = _mm_sad_epu8(*a, zero); + // sum the two sads: sad8x2[0:1] + sad8x2[8:9] + const __m128i sum = _mm_add_epi32(sad8x2, _mm_shuffle_epi32(sad8x2, 2)); + return _mm_cvtsi128_si32(sum); +} + +// Transpose two 4x4 16b matrices horizontally stored in registers. +static WEBP_INLINE void VP8Transpose_2_4x4_16b( + const __m128i* const in0, const __m128i* const in1, + const __m128i* const in2, const __m128i* const in3, __m128i* const out0, + __m128i* const out1, __m128i* const out2, __m128i* const out3) { + // 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(*in0, *in1); + const __m128i transpose0_1 = _mm_unpacklo_epi16(*in2, *in3); + const __m128i transpose0_2 = _mm_unpackhi_epi16(*in0, *in1); + const __m128i transpose0_3 = _mm_unpackhi_epi16(*in2, *in3); + // 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 + *out0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1); + *out1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1); + *out2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3); + *out3 = _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 +} + +#endif // WEBP_USE_SSE2 + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // WEBP_DSP_COMMON_SSE2_H_ diff --git a/drivers/webp/dsp/cpu.c b/drivers/webp/dsp/cpu.c index 35c2af7f58..cbb08db90a 100644 --- a/drivers/webp/dsp/cpu.c +++ b/drivers/webp/dsp/cpu.c @@ -13,6 +13,11 @@ #include "./dsp.h" +#if defined(WEBP_HAVE_NEON_RTCD) +#include <stdio.h> +#include <string.h> +#endif + #if defined(WEBP_ANDROID_NEON) #include <cpu-features.h> #endif @@ -31,6 +36,18 @@ static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) { : "=a"(cpu_info[0]), "=D"(cpu_info[1]), "=c"(cpu_info[2]), "=d"(cpu_info[3]) : "a"(info_type), "c"(0)); } +#elif defined(__x86_64__) && \ + (defined(__code_model_medium__) || defined(__code_model_large__)) && \ + defined(__PIC__) +static WEBP_INLINE void GetCPUInfo(int cpu_info[4], int info_type) { + __asm__ volatile ( + "xchg{q}\t{%%rbx}, %q1\n" + "cpuid\n" + "xchg{q}\t{%%rbx}, %q1\n" + : "=a"(cpu_info[0]), "=&r"(cpu_info[1]), "=c"(cpu_info[2]), + "=d"(cpu_info[3]) + : "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 ( @@ -130,13 +147,33 @@ VP8CPUInfo VP8GetCPUInfo = AndroidCPUInfo; // define a dummy function to enable turning off NEON at runtime by setting // VP8DecGetCPUInfo = NULL static int armCPUInfo(CPUFeature feature) { - (void)feature; + if (feature != kNEON) return 0; +#if defined(__linux__) && defined(WEBP_HAVE_NEON_RTCD) + { + int has_neon = 0; + char line[200]; + FILE* const cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo == NULL) return 0; + while (fgets(line, sizeof(line), cpuinfo)) { + if (!strncmp(line, "Features", 8)) { + if (strstr(line, " neon ") != NULL) { + has_neon = 1; + break; + } + } + } + fclose(cpuinfo); + return has_neon; + } +#else return 1; +#endif } VP8CPUInfo VP8GetCPUInfo = armCPUInfo; -#elif defined(WEBP_USE_MIPS32) || defined(WEBP_USE_MIPS_DSP_R2) +#elif defined(WEBP_USE_MIPS32) || defined(WEBP_USE_MIPS_DSP_R2) || \ + defined(WEBP_USE_MSA) static int mipsCPUInfo(CPUFeature feature) { - if ((feature == kMIPS32) || (feature == kMIPSdspR2)) { + if ((feature == kMIPS32) || (feature == kMIPSdspR2) || (feature == kMSA)) { return 1; } else { return 0; diff --git a/drivers/webp/dsp/dec.c b/drivers/webp/dsp/dec.c index 77a00381c5..e92d693362 100644 --- a/drivers/webp/dsp/dec.c +++ b/drivers/webp/dsp/dec.c @@ -13,6 +13,7 @@ #include "./dsp.h" #include "../dec/vp8i.h" +#include "../utils/utils.h" //------------------------------------------------------------------------------ @@ -261,10 +262,10 @@ static void HE4(uint8_t* dst) { // horizontal 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); + WebPUint32ToMem(dst + 0 * BPS, 0x01010101U * AVG3(A, B, C)); + WebPUint32ToMem(dst + 1 * BPS, 0x01010101U * AVG3(B, C, D)); + WebPUint32ToMem(dst + 2 * BPS, 0x01010101U * AVG3(C, D, E)); + WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(D, E, E)); } static void DC4(uint8_t* dst) { // DC @@ -654,6 +655,23 @@ static void HFilter8i(uint8_t* u, uint8_t* v, int stride, //------------------------------------------------------------------------------ +static void DitherCombine8x8(const uint8_t* dither, uint8_t* dst, + int dst_stride) { + int i, j; + for (j = 0; j < 8; ++j) { + for (i = 0; i < 8; ++i) { + const int delta0 = dither[i] - VP8_DITHER_AMP_CENTER; + const int delta1 = + (delta0 + VP8_DITHER_DESCALE_ROUNDER) >> VP8_DITHER_DESCALE; + dst[i] = clip_8b((int)dst[i] + delta1); + } + dst += dst_stride; + dither += 8; + } +} + +//------------------------------------------------------------------------------ + VP8DecIdct2 VP8Transform; VP8DecIdct VP8TransformAC3; VP8DecIdct VP8TransformUV; @@ -673,11 +691,15 @@ VP8SimpleFilterFunc VP8SimpleHFilter16; VP8SimpleFilterFunc VP8SimpleVFilter16i; VP8SimpleFilterFunc VP8SimpleHFilter16i; +void (*VP8DitherCombine8x8)(const uint8_t* dither, uint8_t* dst, + int dst_stride); + extern void VP8DspInitSSE2(void); extern void VP8DspInitSSE41(void); extern void VP8DspInitNEON(void); extern void VP8DspInitMIPS32(void); extern void VP8DspInitMIPSdspR2(void); +extern void VP8DspInitMSA(void); static volatile VP8CPUInfo dec_last_cpuinfo_used = (VP8CPUInfo)&dec_last_cpuinfo_used; @@ -734,6 +756,8 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8DspInit(void) { VP8PredChroma8[5] = DC8uvNoLeft; VP8PredChroma8[6] = DC8uvNoTopLeft; + VP8DitherCombine8x8 = DitherCombine8x8; + // If defined, use CPUInfo() to overwrite some pointers with faster versions. if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_SSE2) @@ -761,6 +785,11 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8DspInit(void) { VP8DspInitMIPSdspR2(); } #endif +#if defined(WEBP_USE_MSA) + if (VP8GetCPUInfo(kMSA)) { + VP8DspInitMSA(); + } +#endif } dec_last_cpuinfo_used = VP8GetCPUInfo; } diff --git a/drivers/webp/dsp/dec_msa.c b/drivers/webp/dsp/dec_msa.c new file mode 100644 index 0000000000..f76055cab0 --- /dev/null +++ b/drivers/webp/dsp/dec_msa.c @@ -0,0 +1,172 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// 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. +// ----------------------------------------------------------------------------- +// +// MSA version of dsp functions +// +// Author(s): Prashant Patil (prashant.patil@imgtec.com) + + +#include "./dsp.h" + +#if defined(WEBP_USE_MSA) + +#include "./msa_macro.h" + +//------------------------------------------------------------------------------ +// Transforms + +#define IDCT_1D_W(in0, in1, in2, in3, out0, out1, out2, out3) { \ + v4i32 a1_m, b1_m, c1_m, d1_m; \ + v4i32 c_tmp1_m, c_tmp2_m, d_tmp1_m, d_tmp2_m; \ + const v4i32 cospi8sqrt2minus1 = __msa_fill_w(20091); \ + const v4i32 sinpi8sqrt2 = __msa_fill_w(35468); \ + \ + a1_m = in0 + in2; \ + b1_m = in0 - in2; \ + c_tmp1_m = (in1 * sinpi8sqrt2) >> 16; \ + c_tmp2_m = in3 + ((in3 * cospi8sqrt2minus1) >> 16); \ + c1_m = c_tmp1_m - c_tmp2_m; \ + d_tmp1_m = in1 + ((in1 * cospi8sqrt2minus1) >> 16); \ + d_tmp2_m = (in3 * sinpi8sqrt2) >> 16; \ + d1_m = d_tmp1_m + d_tmp2_m; \ + BUTTERFLY_4(a1_m, b1_m, c1_m, d1_m, out0, out1, out2, out3); \ +} +#define MULT1(a) ((((a) * 20091) >> 16) + (a)) +#define MULT2(a) (((a) * 35468) >> 16) + +static void TransformOne(const int16_t* in, uint8_t* dst) { + v8i16 input0, input1; + v4i32 in0, in1, in2, in3, hz0, hz1, hz2, hz3, vt0, vt1, vt2, vt3; + v4i32 res0, res1, res2, res3; + const v16i8 zero = { 0 }; + v16i8 dest0, dest1, dest2, dest3; + + LD_SH2(in, 8, input0, input1); + UNPCK_SH_SW(input0, in0, in1); + UNPCK_SH_SW(input1, in2, in3); + IDCT_1D_W(in0, in1, in2, in3, hz0, hz1, hz2, hz3); + TRANSPOSE4x4_SW_SW(hz0, hz1, hz2, hz3, hz0, hz1, hz2, hz3); + IDCT_1D_W(hz0, hz1, hz2, hz3, vt0, vt1, vt2, vt3); + SRARI_W4_SW(vt0, vt1, vt2, vt3, 3); + TRANSPOSE4x4_SW_SW(vt0, vt1, vt2, vt3, vt0, vt1, vt2, vt3); + LD_SB4(dst, BPS, dest0, dest1, dest2, dest3); + ILVR_B4_SW(zero, dest0, zero, dest1, zero, dest2, zero, dest3, + res0, res1, res2, res3); + ILVR_H4_SW(zero, res0, zero, res1, zero, res2, zero, res3, + res0, res1, res2, res3); + ADD4(res0, vt0, res1, vt1, res2, vt2, res3, vt3, res0, res1, res2, res3); + CLIP_SW4_0_255(res0, res1, res2, res3); + PCKEV_B2_SW(res0, res1, res2, res3, vt0, vt1); + res0 = (v4i32)__msa_pckev_b((v16i8)vt0, (v16i8)vt1); + ST4x4_UB(res0, res0, 3, 2, 1, 0, dst, BPS); +} + +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 TransformWHT(const int16_t* in, int16_t* out) { + v8i16 input0, input1; + const v8i16 mask0 = { 0, 1, 2, 3, 8, 9, 10, 11 }; + const v8i16 mask1 = { 4, 5, 6, 7, 12, 13, 14, 15 }; + const v8i16 mask2 = { 0, 4, 8, 12, 1, 5, 9, 13 }; + const v8i16 mask3 = { 3, 7, 11, 15, 2, 6, 10, 14 }; + v8i16 tmp0, tmp1, tmp2, tmp3; + v8i16 out0, out1; + + LD_SH2(in, 8, input0, input1); + input1 = SLDI_SH(input1, input1, 8); + tmp0 = input0 + input1; + tmp1 = input0 - input1; + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3); + out0 = tmp2 + tmp3; + out1 = tmp2 - tmp3; + VSHF_H2_SH(out0, out1, out0, out1, mask2, mask3, input0, input1); + tmp0 = input0 + input1; + tmp1 = input0 - input1; + VSHF_H2_SH(tmp0, tmp1, tmp0, tmp1, mask0, mask1, tmp2, tmp3); + tmp0 = tmp2 + tmp3; + tmp1 = tmp2 - tmp3; + ADDVI_H2_SH(tmp0, 3, tmp1, 3, out0, out1); + SRAI_H2_SH(out0, out1, 3); + out[0] = __msa_copy_s_h(out0, 0); + out[16] = __msa_copy_s_h(out0, 4); + out[32] = __msa_copy_s_h(out1, 0); + out[48] = __msa_copy_s_h(out1, 4); + out[64] = __msa_copy_s_h(out0, 1); + out[80] = __msa_copy_s_h(out0, 5); + out[96] = __msa_copy_s_h(out1, 1); + out[112] = __msa_copy_s_h(out1, 5); + out[128] = __msa_copy_s_h(out0, 2); + out[144] = __msa_copy_s_h(out0, 6); + out[160] = __msa_copy_s_h(out1, 2); + out[176] = __msa_copy_s_h(out1, 6); + out[192] = __msa_copy_s_h(out0, 3); + out[208] = __msa_copy_s_h(out0, 7); + out[224] = __msa_copy_s_h(out1, 3); + out[240] = __msa_copy_s_h(out1, 7); +} + +static void TransformDC(const int16_t* in, uint8_t* dst) { + const int DC = (in[0] + 4) >> 3; + const v8i16 tmp0 = __msa_fill_h(DC); + ADDBLK_ST4x4_UB(tmp0, tmp0, tmp0, tmp0, dst, BPS); +} + +static void TransformAC3(const int16_t* in, uint8_t* dst) { + const int a = in[0] + 4; + const int c4 = MULT2(in[4]); + const int d4 = MULT1(in[4]); + const int in2 = MULT2(in[1]); + const int in3 = MULT1(in[1]); + v4i32 tmp0 = { 0 }; + v4i32 out0 = __msa_fill_w(a + d4); + v4i32 out1 = __msa_fill_w(a + c4); + v4i32 out2 = __msa_fill_w(a - c4); + v4i32 out3 = __msa_fill_w(a - d4); + v4i32 res0, res1, res2, res3; + const v4i32 zero = { 0 }; + v16u8 dest0, dest1, dest2, dest3; + + INSERT_W4_SW(in3, in2, -in2, -in3, tmp0); + ADD4(out0, tmp0, out1, tmp0, out2, tmp0, out3, tmp0, + out0, out1, out2, out3); + SRAI_W4_SW(out0, out1, out2, out3, 3); + LD_UB4(dst, BPS, dest0, dest1, dest2, dest3); + ILVR_B4_SW(zero, dest0, zero, dest1, zero, dest2, zero, dest3, + res0, res1, res2, res3); + ILVR_H4_SW(zero, res0, zero, res1, zero, res2, zero, res3, + res0, res1, res2, res3); + ADD4(res0, out0, res1, out1, res2, out2, res3, out3, res0, res1, res2, res3); + CLIP_SW4_0_255(res0, res1, res2, res3); + PCKEV_B2_SW(res0, res1, res2, res3, out0, out1); + res0 = (v4i32)__msa_pckev_b((v16i8)out0, (v16i8)out1); + ST4x4_UB(res0, res0, 3, 2, 1, 0, dst, BPS); +} + +//------------------------------------------------------------------------------ +// Entry point + +extern void VP8DspInitMSA(void); + +WEBP_TSAN_IGNORE_FUNCTION void VP8DspInitMSA(void) { + VP8TransformWHT = TransformWHT; + VP8Transform = TransformTwo; + VP8TransformDC = TransformDC; + VP8TransformAC3 = TransformAC3; +} + +#else // !WEBP_USE_MSA + +WEBP_DSP_INIT_STUB(VP8DspInitMSA) + +#endif // WEBP_USE_MSA diff --git a/drivers/webp/dsp/dec_sse2.c b/drivers/webp/dsp/dec_sse2.c index d4838b9210..f0a8ddcaf3 100644 --- a/drivers/webp/dsp/dec_sse2.c +++ b/drivers/webp/dsp/dec_sse2.c @@ -21,7 +21,9 @@ // #define USE_TRANSFORM_AC3 #include <emmintrin.h> +#include "./common_sse2.h" #include "../dec/vp8i.h" +#include "../utils/utils.h" //------------------------------------------------------------------------------ // Transforms (Paragraph 14.4) @@ -102,34 +104,7 @@ static void Transform(const int16_t* in, uint8_t* dst, int do_two) { 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 + VP8Transpose_2_4x4_16b(&tmp0, &tmp1, &tmp2, &tmp3, &T0, &T1, &T2, &T3); } // Horizontal pass and subsequent transpose. @@ -164,34 +139,8 @@ static void Transform(const int16_t* in, uint8_t* dst, int do_two) { 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 + VP8Transpose_2_4x4_16b(&shifted0, &shifted1, &shifted2, &shifted3, &T0, &T1, + &T2, &T3); } // Add inverse transform to 'dst' and store. @@ -207,10 +156,10 @@ static void Transform(const int16_t* in, uint8_t* dst, int do_two) { 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)); + dst0 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 0 * BPS)); + dst1 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 1 * BPS)); + dst2 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 2 * BPS)); + dst3 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 3 * BPS)); } // Convert to 16b. dst0 = _mm_unpacklo_epi8(dst0, zero); @@ -236,10 +185,10 @@ static void Transform(const int16_t* in, uint8_t* dst, int do_two) { _mm_storel_epi64((__m128i*)(dst + 3 * BPS), dst3); } else { // Store four bytes/pixels per line. - *(int*)(dst + 0 * BPS) = _mm_cvtsi128_si32(dst0); - *(int*)(dst + 1 * BPS) = _mm_cvtsi128_si32(dst1); - *(int*)(dst + 2 * BPS) = _mm_cvtsi128_si32(dst2); - *(int*)(dst + 3 * BPS) = _mm_cvtsi128_si32(dst3); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0)); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1)); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2)); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3)); } } } @@ -262,10 +211,10 @@ static void TransformAC3(const int16_t* in, uint8_t* dst) { const __m128i m3 = _mm_subs_epi16(B, d4); const __m128i zero = _mm_setzero_si128(); // Load the source pixels. - __m128i dst0 = _mm_cvtsi32_si128(*(int*)(dst + 0 * BPS)); - __m128i dst1 = _mm_cvtsi32_si128(*(int*)(dst + 1 * BPS)); - __m128i dst2 = _mm_cvtsi32_si128(*(int*)(dst + 2 * BPS)); - __m128i dst3 = _mm_cvtsi32_si128(*(int*)(dst + 3 * BPS)); + __m128i dst0 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 0 * BPS)); + __m128i dst1 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 1 * BPS)); + __m128i dst2 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 2 * BPS)); + __m128i dst3 = _mm_cvtsi32_si128(WebPMemToUint32(dst + 3 * BPS)); // Convert to 16b. dst0 = _mm_unpacklo_epi8(dst0, zero); dst1 = _mm_unpacklo_epi8(dst1, zero); @@ -282,10 +231,10 @@ static void TransformAC3(const int16_t* in, uint8_t* dst) { dst2 = _mm_packus_epi16(dst2, dst2); dst3 = _mm_packus_epi16(dst3, dst3); // Store the results. - *(int*)(dst + 0 * BPS) = _mm_cvtsi128_si32(dst0); - *(int*)(dst + 1 * BPS) = _mm_cvtsi128_si32(dst1); - *(int*)(dst + 2 * BPS) = _mm_cvtsi128_si32(dst2); - *(int*)(dst + 3 * BPS) = _mm_cvtsi128_si32(dst3); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(dst0)); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(dst1)); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(dst2)); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(dst3)); } #undef MUL #endif // USE_TRANSFORM_AC3 @@ -517,24 +466,17 @@ static WEBP_INLINE void DoFilter6(__m128i* const p2, __m128i* const p1, } } -// memcpy() is the safe way of moving potentially unaligned 32b memory. -static WEBP_INLINE uint32_t MemToUint32(const uint8_t* const ptr) { - uint32_t A; - memcpy(&A, (const int*)ptr, sizeof(A)); - return A; -} - // reads 8 rows across a vertical edge. static WEBP_INLINE void Load8x4(const uint8_t* const b, int stride, __m128i* const p, __m128i* const q) { // A0 = 63 62 61 60 23 22 21 20 43 42 41 40 03 02 01 00 // A1 = 73 72 71 70 33 32 31 30 53 52 51 50 13 12 11 10 const __m128i A0 = _mm_set_epi32( - MemToUint32(&b[6 * stride]), MemToUint32(&b[2 * stride]), - MemToUint32(&b[4 * stride]), MemToUint32(&b[0 * stride])); + WebPMemToUint32(&b[6 * stride]), WebPMemToUint32(&b[2 * stride]), + WebPMemToUint32(&b[4 * stride]), WebPMemToUint32(&b[0 * stride])); const __m128i A1 = _mm_set_epi32( - MemToUint32(&b[7 * stride]), MemToUint32(&b[3 * stride]), - MemToUint32(&b[5 * stride]), MemToUint32(&b[1 * stride])); + WebPMemToUint32(&b[7 * stride]), WebPMemToUint32(&b[3 * stride]), + WebPMemToUint32(&b[5 * stride]), WebPMemToUint32(&b[1 * stride])); // B0 = 53 43 52 42 51 41 50 40 13 03 12 02 11 01 10 00 // B1 = 73 63 72 62 71 61 70 60 33 23 32 22 31 21 30 20 @@ -592,7 +534,7 @@ static WEBP_INLINE void Load16x4(const uint8_t* const r0, static WEBP_INLINE void Store4x4(__m128i* const x, uint8_t* dst, int stride) { int i; for (i = 0; i < 4; ++i, dst += stride) { - *((int32_t*)dst) = _mm_cvtsi128_si32(*x); + WebPUint32ToMem(dst, _mm_cvtsi128_si32(*x)); *x = _mm_srli_si128(*x, 4); } } @@ -963,7 +905,7 @@ static void VE4(uint8_t* dst) { // vertical const uint32_t vals = _mm_cvtsi128_si32(avg); int i; for (i = 0; i < 4; ++i) { - *(uint32_t*)(dst + i * BPS) = vals; + WebPUint32ToMem(dst + i * BPS, vals); } } @@ -977,10 +919,10 @@ static void LD4(uint8_t* dst) { // Down-Left const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGHH0), one); const __m128i avg2 = _mm_subs_epu8(avg1, lsb); const __m128i abcdefg = _mm_avg_epu8(avg2, BCDEFGH0); - *(uint32_t*)(dst + 0 * BPS) = _mm_cvtsi128_si32( abcdefg ); - *(uint32_t*)(dst + 1 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)); - *(uint32_t*)(dst + 2 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)); - *(uint32_t*)(dst + 3 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcdefg )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1))); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3))); } static void VR4(uint8_t* dst) { // Vertical-Right @@ -998,10 +940,10 @@ static void VR4(uint8_t* dst) { // Vertical-Right const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one); const __m128i avg2 = _mm_subs_epu8(avg1, lsb); const __m128i efgh = _mm_avg_epu8(avg2, XABCD); - *(uint32_t*)(dst + 0 * BPS) = _mm_cvtsi128_si32( abcd ); - *(uint32_t*)(dst + 1 * BPS) = _mm_cvtsi128_si32( efgh ); - *(uint32_t*)(dst + 2 * BPS) = _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)); - *(uint32_t*)(dst + 3 * BPS) = _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcd )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( efgh )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1))); // these two are hard to implement in SSE2, so we keep the C-version: DST(0, 2) = AVG3(J, I, X); @@ -1023,10 +965,10 @@ static void VL4(uint8_t* dst) { // Vertical-Left const __m128i lsb2 = _mm_and_si128(abbc, lsb1); const __m128i avg4 = _mm_subs_epu8(avg3, lsb2); const uint32_t extra_out = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 4)); - *(uint32_t*)(dst + 0 * BPS) = _mm_cvtsi128_si32( avg1 ); - *(uint32_t*)(dst + 1 * BPS) = _mm_cvtsi128_si32( avg4 ); - *(uint32_t*)(dst + 2 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)); - *(uint32_t*)(dst + 3 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( avg1 )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( avg4 )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1))); // these two are hard to get and irregular DST(3, 2) = (extra_out >> 0) & 0xff; @@ -1050,10 +992,10 @@ static void RD4(uint8_t* dst) { // Down-right const __m128i lsb = _mm_and_si128(_mm_xor_si128(JIXABCD__, LKJIXABCD), one); const __m128i avg2 = _mm_subs_epu8(avg1, lsb); const __m128i abcdefg = _mm_avg_epu8(avg2, KJIXABCD_); - *(uint32_t*)(dst + 3 * BPS) = _mm_cvtsi128_si32( abcdefg ); - *(uint32_t*)(dst + 2 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)); - *(uint32_t*)(dst + 1 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)); - *(uint32_t*)(dst + 0 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32( abcdefg )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1))); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2))); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3))); } #undef DST @@ -1067,13 +1009,13 @@ static WEBP_INLINE void TrueMotion(uint8_t* dst, int size) { const __m128i zero = _mm_setzero_si128(); int y; if (size == 4) { - const __m128i top_values = _mm_cvtsi32_si128(MemToUint32(top)); + const __m128i top_values = _mm_cvtsi32_si128(WebPMemToUint32(top)); const __m128i top_base = _mm_unpacklo_epi8(top_values, zero); for (y = 0; y < 4; ++y, dst += BPS) { const int val = dst[-1] - top[-1]; const __m128i base = _mm_set1_epi16(val); const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero); - *(int*)dst = _mm_cvtsi128_si32(out); + WebPUint32ToMem(dst, _mm_cvtsi128_si32(out)); } } else if (size == 8) { const __m128i top_values = _mm_loadl_epi64((const __m128i*)top); diff --git a/drivers/webp/dsp/dec_sse41.c b/drivers/webp/dsp/dec_sse41.c index dc1e70428d..8d6aed13e6 100644 --- a/drivers/webp/dsp/dec_sse41.c +++ b/drivers/webp/dsp/dec_sse41.c @@ -17,12 +17,13 @@ #include <smmintrin.h> #include "../dec/vp8i.h" +#include "../utils/utils.h" static void HE16(uint8_t* dst) { // horizontal int j; const __m128i kShuffle3 = _mm_set1_epi8(3); for (j = 16; j > 0; --j) { - const __m128i in = _mm_cvtsi32_si128(*(int*)(dst - 4)); + const __m128i in = _mm_cvtsi32_si128(WebPMemToUint32(dst - 4)); const __m128i values = _mm_shuffle_epi8(in, kShuffle3); _mm_storeu_si128((__m128i*)dst, values); dst += BPS; diff --git a/drivers/webp/dsp/dsp.h b/drivers/webp/dsp/dsp.h index 4613d9c3ff..2469f7d3ac 100644 --- a/drivers/webp/dsp/dsp.h +++ b/drivers/webp/dsp/dsp.h @@ -75,7 +75,8 @@ extern "C" { // 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__) + defined(__aarch64__) || defined(WEBP_HAVE_NEON)) && \ + !defined(__native_client__) #define WEBP_USE_NEON #endif @@ -95,6 +96,10 @@ extern "C" { #endif #endif +#if defined(__mips_msa) && defined(__mips_isa_rev) && (__mips_isa_rev >= 5) +#define WEBP_USE_MSA +#endif + // This macro prevents thread_sanitizer from reporting known concurrent writes. #define WEBP_TSAN_IGNORE_FUNCTION #if defined(__has_feature) @@ -104,6 +109,27 @@ extern "C" { #endif #endif +#define WEBP_UBSAN_IGNORE_UNDEF +#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW +#if !defined(WEBP_FORCE_ALIGNED) && defined(__clang__) && \ + defined(__has_attribute) +#if __has_attribute(no_sanitize) +// This macro prevents the undefined behavior sanitizer from reporting +// failures. This is only meant to silence unaligned loads on platforms that +// are known to support them. +#undef WEBP_UBSAN_IGNORE_UNDEF +#define WEBP_UBSAN_IGNORE_UNDEF \ + __attribute__((no_sanitize("undefined"))) + +// This macro prevents the undefined behavior sanitizer from reporting +// failures related to unsigned integer overflows. This is only meant to +// silence cases where this well defined behavior is expected. +#undef WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW +#define WEBP_UBSAN_IGNORE_UNSIGNED_OVERFLOW \ + __attribute__((no_sanitize("unsigned-integer-overflow"))) +#endif +#endif + typedef enum { kSSE2, kSSE3, @@ -112,7 +138,8 @@ typedef enum { kAVX2, kNEON, kMIPS32, - kMIPSdspR2 + kMIPSdspR2, + kMSA } CPUFeature; // returns true if the CPU supports the feature. typedef int (*VP8CPUInfo)(CPUFeature feature); @@ -154,6 +181,8 @@ 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); +// The weights for VP8TDisto4x4 and VP8TDisto16x16 contain a row-major +// 4 by 4 symmetric matrix. extern VP8WMetric VP8TDisto4x4, VP8TDisto16x16; typedef void (*VP8BlockCopy)(const uint8_t* src, uint8_t* dst); @@ -217,6 +246,35 @@ extern VP8GetResidualCostFunc VP8GetResidualCost; void VP8EncDspCostInit(void); //------------------------------------------------------------------------------ +// SSIM utils + +// struct for accumulating statistical moments +typedef struct { + double w; // sum(w_i) : sum of weights + double xm, ym; // sum(w_i * x_i), sum(w_i * y_i) + double xxm, xym, yym; // sum(w_i * x_i * x_i), etc. +} VP8DistoStats; + +#define VP8_SSIM_KERNEL 3 // total size of the kernel: 2 * VP8_SSIM_KERNEL + 1 +typedef void (*VP8SSIMAccumulateClippedFunc)(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2, + int xo, int yo, // center position + int W, int H, // plane dimension + VP8DistoStats* const stats); + +// This version is called with the guarantee that you can load 8 bytes and +// 8 rows at offset src1 and src2 +typedef void (*VP8SSIMAccumulateFunc)(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2, + VP8DistoStats* const stats); + +extern VP8SSIMAccumulateFunc VP8SSIMAccumulate; // unclipped / unchecked +extern VP8SSIMAccumulateClippedFunc VP8SSIMAccumulateClipped; // with clipping + +// must be called before using any of the above directly +void VP8SSIMDspInit(void); + +//------------------------------------------------------------------------------ // Decoding typedef void (*VP8DecIdct)(const int16_t* coeffs, uint8_t* dst); @@ -268,6 +326,15 @@ extern VP8LumaFilterFunc VP8HFilter16i; extern VP8ChromaFilterFunc VP8VFilter8i; // filtering u and v altogether extern VP8ChromaFilterFunc VP8HFilter8i; +// Dithering. Combines dithering values (centered around 128) with dst[], +// according to: dst[] = clip(dst[] + (((dither[]-128) + 8) >> 4) +#define VP8_DITHER_DESCALE 4 +#define VP8_DITHER_DESCALE_ROUNDER (1 << (VP8_DITHER_DESCALE - 1)) +#define VP8_DITHER_AMP_BITS 7 +#define VP8_DITHER_AMP_CENTER (1 << VP8_DITHER_AMP_BITS) +extern void (*VP8DitherCombine8x8)(const uint8_t* dither, uint8_t* dst, + int dst_stride); + // must be called before anything using the above void VP8DspInit(void); @@ -475,8 +542,10 @@ typedef enum { // Filter types. 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); +// In-place un-filtering. +// Warning! 'prev_line' pointer can be equal to 'cur_line' or 'preds'. +typedef void (*WebPUnfilterFunc)(const uint8_t* prev_line, const uint8_t* preds, + uint8_t* cur_line, int width); // Filter the given data using the given predictor. // 'in' corresponds to a 2-dimensional pixel array of size (stride * height) diff --git a/drivers/webp/dsp/enc.c b/drivers/webp/dsp/enc.c index 95e63f89ab..f639f5570c 100644 --- a/drivers/webp/dsp/enc.c +++ b/drivers/webp/dsp/enc.c @@ -69,7 +69,7 @@ static void CollectHistogram(const uint8_t* ref, const uint8_t* pred, // Convert coefficients to bin. for (k = 0; k < 16; ++k) { - const int v = abs(out[k]) >> 3; // TODO(skal): add rounding? + const int v = abs(out[k]) >> 3; const int clipped_value = clip_max(v, MAX_COEFF_THRESH); ++distribution[clipped_value]; } @@ -357,10 +357,10 @@ static void HE4(uint8_t* dst, const uint8_t* top) { // horizontal const int J = top[-3]; const int K = top[-4]; const int L = top[-5]; - *(uint32_t*)(dst + 0 * BPS) = 0x01010101U * AVG3(X, I, J); - *(uint32_t*)(dst + 1 * BPS) = 0x01010101U * AVG3(I, J, K); - *(uint32_t*)(dst + 2 * BPS) = 0x01010101U * AVG3(J, K, L); - *(uint32_t*)(dst + 3 * BPS) = 0x01010101U * AVG3(K, L, L); + WebPUint32ToMem(dst + 0 * BPS, 0x01010101U * AVG3(X, I, J)); + WebPUint32ToMem(dst + 1 * BPS, 0x01010101U * AVG3(I, J, K)); + WebPUint32ToMem(dst + 2 * BPS, 0x01010101U * AVG3(J, K, L)); + WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(K, L, L)); } static void DC4(uint8_t* dst, const uint8_t* top) { @@ -559,6 +559,7 @@ static int SSE4x4(const uint8_t* a, const uint8_t* b) { // Hadamard transform // Returns the weighted sum of the absolute value of transformed coefficients. +// w[] contains a row-major 4 by 4 symmetric matrix. static int TTransform(const uint8_t* in, const uint16_t* w) { int sum = 0; int tmp[16]; @@ -636,7 +637,7 @@ static int QuantizeBlock(int16_t in[16], int16_t out[16], int level = QUANTDIV(coeff, iQ, B); if (level > MAX_LEVEL) level = MAX_LEVEL; if (sign) level = -level; - in[j] = level * Q; + in[j] = level * (int)Q; out[n] = level; if (level) last = n; } else { @@ -670,7 +671,7 @@ static int QuantizeBlockWHT(int16_t in[16], int16_t out[16], int level = QUANTDIV(coeff, iQ, B); if (level > MAX_LEVEL) level = MAX_LEVEL; if (sign) level = -level; - in[j] = level * Q; + in[j] = level * (int)Q; out[n] = level; if (level) last = n; } else { @@ -702,6 +703,68 @@ static void Copy16x8(const uint8_t* src, uint8_t* dst) { } //------------------------------------------------------------------------------ + +static void SSIMAccumulateClipped(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2, + int xo, int yo, int W, int H, + VP8DistoStats* const stats) { + const int ymin = (yo - VP8_SSIM_KERNEL < 0) ? 0 : yo - VP8_SSIM_KERNEL; + const int ymax = (yo + VP8_SSIM_KERNEL > H - 1) ? H - 1 + : yo + VP8_SSIM_KERNEL; + const int xmin = (xo - VP8_SSIM_KERNEL < 0) ? 0 : xo - VP8_SSIM_KERNEL; + const int xmax = (xo + VP8_SSIM_KERNEL > W - 1) ? W - 1 + : xo + VP8_SSIM_KERNEL; + int x, y; + src1 += ymin * stride1; + src2 += ymin * stride2; + for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) { + for (x = xmin; x <= xmax; ++x) { + const int s1 = src1[x]; + const int s2 = src2[x]; + stats->w += 1; + stats->xm += s1; + stats->ym += s2; + stats->xxm += s1 * s1; + stats->xym += s1 * s2; + stats->yym += s2 * s2; + } + } +} + +static void SSIMAccumulate(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2, + VP8DistoStats* const stats) { + int x, y; + for (y = 0; y <= 2 * VP8_SSIM_KERNEL; ++y, src1 += stride1, src2 += stride2) { + for (x = 0; x <= 2 * VP8_SSIM_KERNEL; ++x) { + const int s1 = src1[x]; + const int s2 = src2[x]; + stats->w += 1; + stats->xm += s1; + stats->ym += s2; + stats->xxm += s1 * s1; + stats->xym += s1 * s2; + stats->yym += s2 * s2; + } + } +} + +VP8SSIMAccumulateFunc VP8SSIMAccumulate; +VP8SSIMAccumulateClippedFunc VP8SSIMAccumulateClipped; + +static volatile VP8CPUInfo ssim_last_cpuinfo_used = + (VP8CPUInfo)&ssim_last_cpuinfo_used; + +WEBP_TSAN_IGNORE_FUNCTION void VP8SSIMDspInit(void) { + if (ssim_last_cpuinfo_used == VP8GetCPUInfo) return; + + VP8SSIMAccumulate = SSIMAccumulate; + VP8SSIMAccumulateClipped = SSIMAccumulateClipped; + + ssim_last_cpuinfo_used = VP8GetCPUInfo; +} + +//------------------------------------------------------------------------------ // Initialization // Speed-critical function pointers. We have to initialize them to the default diff --git a/drivers/webp/dsp/enc_mips_dsp_r2.c b/drivers/webp/dsp/enc_mips_dsp_r2.c index 7c814fa04a..7ab96f6800 100644 --- a/drivers/webp/dsp/enc_mips_dsp_r2.c +++ b/drivers/webp/dsp/enc_mips_dsp_r2.c @@ -1393,8 +1393,6 @@ static void FTransformWHT(const int16_t* in, int16_t* out) { "absq_s.ph %[temp1], %[temp1] \n\t" \ "absq_s.ph %[temp2], %[temp2] \n\t" \ "absq_s.ph %[temp3], %[temp3] \n\t" \ - /* TODO(skal): add rounding ? shra_r.ph : shra.ph */ \ - /* for following 4 instructions */ \ "shra.ph %[temp0], %[temp0], 3 \n\t" \ "shra.ph %[temp1], %[temp1], 3 \n\t" \ "shra.ph %[temp2], %[temp2], 3 \n\t" \ diff --git a/drivers/webp/dsp/enc_neon.c b/drivers/webp/dsp/enc_neon.c index c2aef58e70..46f6bf9a33 100644 --- a/drivers/webp/dsp/enc_neon.c +++ b/drivers/webp/dsp/enc_neon.c @@ -560,21 +560,6 @@ static void FTransformWHT(const int16_t* src, int16_t* out) { // a 26ae, b 26ae // a 37bf, b 37bf // -static WEBP_INLINE uint8x8x4_t DistoTranspose4x4U8(uint8x8x4_t d4_in) { - const uint8x8x2_t d2_tmp0 = vtrn_u8(d4_in.val[0], d4_in.val[1]); - const uint8x8x2_t d2_tmp1 = vtrn_u8(d4_in.val[2], d4_in.val[3]); - const uint16x4x2_t d2_tmp2 = vtrn_u16(vreinterpret_u16_u8(d2_tmp0.val[0]), - vreinterpret_u16_u8(d2_tmp1.val[0])); - const uint16x4x2_t d2_tmp3 = vtrn_u16(vreinterpret_u16_u8(d2_tmp0.val[1]), - vreinterpret_u16_u8(d2_tmp1.val[1])); - - d4_in.val[0] = vreinterpret_u8_u16(d2_tmp2.val[0]); - d4_in.val[2] = vreinterpret_u8_u16(d2_tmp2.val[1]); - d4_in.val[1] = vreinterpret_u8_u16(d2_tmp3.val[0]); - d4_in.val[3] = vreinterpret_u8_u16(d2_tmp3.val[1]); - return d4_in; -} - static WEBP_INLINE int16x8x4_t DistoTranspose4x4S16(int16x8x4_t q4_in) { const int16x8x2_t q2_tmp0 = vtrnq_s16(q4_in.val[0], q4_in.val[1]); const int16x8x2_t q2_tmp1 = vtrnq_s16(q4_in.val[2], q4_in.val[3]); @@ -589,41 +574,40 @@ static WEBP_INLINE int16x8x4_t DistoTranspose4x4S16(int16x8x4_t q4_in) { return q4_in; } -static WEBP_INLINE int16x8x4_t DistoHorizontalPass(const uint8x8x4_t d4_in) { +static WEBP_INLINE int16x8x4_t DistoHorizontalPass(const int16x8x4_t q4_in) { // {a0, a1} = {in[0] + in[2], in[1] + in[3]} // {a3, a2} = {in[0] - in[2], in[1] - in[3]} - const int16x8_t q_a0 = vreinterpretq_s16_u16(vaddl_u8(d4_in.val[0], - d4_in.val[2])); - const int16x8_t q_a1 = vreinterpretq_s16_u16(vaddl_u8(d4_in.val[1], - d4_in.val[3])); - const int16x8_t q_a3 = vreinterpretq_s16_u16(vsubl_u8(d4_in.val[0], - d4_in.val[2])); - const int16x8_t q_a2 = vreinterpretq_s16_u16(vsubl_u8(d4_in.val[1], - d4_in.val[3])); + const int16x8_t q_a0 = vaddq_s16(q4_in.val[0], q4_in.val[2]); + const int16x8_t q_a1 = vaddq_s16(q4_in.val[1], q4_in.val[3]); + const int16x8_t q_a3 = vsubq_s16(q4_in.val[0], q4_in.val[2]); + const int16x8_t q_a2 = vsubq_s16(q4_in.val[1], q4_in.val[3]); int16x8x4_t q4_out; // tmp[0] = a0 + a1 // tmp[1] = a3 + a2 // tmp[2] = a3 - a2 // tmp[3] = a0 - a1 INIT_VECTOR4(q4_out, - vaddq_s16(q_a0, q_a1), vaddq_s16(q_a3, q_a2), - vsubq_s16(q_a3, q_a2), vsubq_s16(q_a0, q_a1)); + vabsq_s16(vaddq_s16(q_a0, q_a1)), + vabsq_s16(vaddq_s16(q_a3, q_a2)), + vabdq_s16(q_a3, q_a2), vabdq_s16(q_a0, q_a1)); return q4_out; } -static WEBP_INLINE int16x8x4_t DistoVerticalPass(int16x8x4_t q4_in) { - const int16x8_t q_a0 = vaddq_s16(q4_in.val[0], q4_in.val[2]); - const int16x8_t q_a1 = vaddq_s16(q4_in.val[1], q4_in.val[3]); - const int16x8_t q_a2 = vsubq_s16(q4_in.val[1], q4_in.val[3]); - const int16x8_t q_a3 = vsubq_s16(q4_in.val[0], q4_in.val[2]); +static WEBP_INLINE int16x8x4_t DistoVerticalPass(const uint8x8x4_t q4_in) { + const int16x8_t q_a0 = vreinterpretq_s16_u16(vaddl_u8(q4_in.val[0], + q4_in.val[2])); + const int16x8_t q_a1 = vreinterpretq_s16_u16(vaddl_u8(q4_in.val[1], + q4_in.val[3])); + const int16x8_t q_a2 = vreinterpretq_s16_u16(vsubl_u8(q4_in.val[1], + q4_in.val[3])); + const int16x8_t q_a3 = vreinterpretq_s16_u16(vsubl_u8(q4_in.val[0], + q4_in.val[2])); + int16x8x4_t q4_out; - q4_in.val[0] = vaddq_s16(q_a0, q_a1); - q4_in.val[1] = vaddq_s16(q_a3, q_a2); - q4_in.val[2] = vabdq_s16(q_a3, q_a2); - q4_in.val[3] = vabdq_s16(q_a0, q_a1); - q4_in.val[0] = vabsq_s16(q4_in.val[0]); - q4_in.val[1] = vabsq_s16(q4_in.val[1]); - return q4_in; + INIT_VECTOR4(q4_out, + vaddq_s16(q_a0, q_a1), vaddq_s16(q_a3, q_a2), + vsubq_s16(q_a3, q_a2), vsubq_s16(q_a0, q_a1)); + return q4_out; } static WEBP_INLINE int16x4x4_t DistoLoadW(const uint16_t* w) { @@ -667,6 +651,7 @@ static WEBP_INLINE int32x2_t DistoSum(const int16x8x4_t q4_in, // Hadamard transform // Returns the weighted sum of the absolute value of transformed coefficients. +// w[] contains a row-major 4 by 4 symmetric matrix. static int Disto4x4(const uint8_t* const a, const uint8_t* const b, const uint16_t* const w) { uint32x2_t d_in_ab_0123 = vdup_n_u32(0); @@ -691,18 +676,19 @@ static int Disto4x4(const uint8_t* const a, const uint8_t* const b, vreinterpret_u8_u32(d_in_ab_cdef)); { - // horizontal pass - const uint8x8x4_t d4_t = DistoTranspose4x4U8(d4_in); - const int16x8x4_t q4_h = DistoHorizontalPass(d4_t); + // Vertical pass first to avoid a transpose (vertical and horizontal passes + // are commutative because w/kWeightY is symmetric) and subsequent + // transpose. + const int16x8x4_t q4_v = DistoVerticalPass(d4_in); const int16x4x4_t d4_w = DistoLoadW(w); - // vertical pass - const int16x8x4_t q4_t = DistoTranspose4x4S16(q4_h); - const int16x8x4_t q4_v = DistoVerticalPass(q4_t); - int32x2_t d_sum = DistoSum(q4_v, d4_w); + // horizontal pass + const int16x8x4_t q4_t = DistoTranspose4x4S16(q4_v); + const int16x8x4_t q4_h = DistoHorizontalPass(q4_t); + int32x2_t d_sum = DistoSum(q4_h, d4_w); // abs(sum2 - sum1) >> 5 d_sum = vabs_s32(d_sum); - d_sum = vshr_n_s32(d_sum, 5); + d_sum = vshr_n_s32(d_sum, 5); return vget_lane_s32(d_sum, 0); } } diff --git a/drivers/webp/dsp/enc_sse2.c b/drivers/webp/dsp/enc_sse2.c index 63d9cecd85..4a2e3ce14f 100644 --- a/drivers/webp/dsp/enc_sse2.c +++ b/drivers/webp/dsp/enc_sse2.c @@ -17,48 +17,9 @@ #include <stdlib.h> // for abs() #include <emmintrin.h> +#include "./common_sse2.h" #include "../enc/cost.h" #include "../enc/vp8enci.h" -#include "../utils/utils.h" - -//------------------------------------------------------------------------------ -// Quite useful macro for debugging. Left here for convenience. - -#if 0 -#include <stdio.h> -static void PrintReg(const __m128i r, const char* const name, int size) { - int n; - union { - __m128i r; - uint8_t i8[16]; - uint16_t i16[8]; - uint32_t i32[4]; - uint64_t i64[2]; - } tmp; - tmp.r = r; - fprintf(stderr, "%s\t: ", name); - if (size == 8) { - for (n = 0; n < 16; ++n) fprintf(stderr, "%.2x ", tmp.i8[n]); - } else if (size == 16) { - for (n = 0; n < 8; ++n) fprintf(stderr, "%.4x ", tmp.i16[n]); - } else if (size == 32) { - for (n = 0; n < 4; ++n) fprintf(stderr, "%.8x ", tmp.i32[n]); - } else { - for (n = 0; n < 2; ++n) fprintf(stderr, "%.16lx ", tmp.i64[n]); - } - fprintf(stderr, "\n"); -} -#endif - -//------------------------------------------------------------------------------ -// util for unaligned loads. - -// memcpy() is the safe way of moving potentially unaligned 32b memory. -static WEBP_INLINE uint32_t MemToUint32(const uint8_t* const ptr) { - uint32_t A; - memcpy(&A, (const int*)ptr, sizeof(A)); - return A; -} //------------------------------------------------------------------------------ // Transforms (Paragraph 14.4) @@ -142,34 +103,7 @@ static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst, 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 + VP8Transpose_2_4x4_16b(&tmp0, &tmp1, &tmp2, &tmp3, &T0, &T1, &T2, &T3); } // Horizontal pass and subsequent transpose. @@ -204,34 +138,8 @@ static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst, 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 + VP8Transpose_2_4x4_16b(&shifted0, &shifted1, &shifted2, &shifted3, &T0, &T1, + &T2, &T3); } // Add inverse transform to 'ref' and store. @@ -247,10 +155,10 @@ static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst, ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]); } else { // Load four bytes/pixels per line. - ref0 = _mm_cvtsi32_si128(MemToUint32(&ref[0 * BPS])); - ref1 = _mm_cvtsi32_si128(MemToUint32(&ref[1 * BPS])); - ref2 = _mm_cvtsi32_si128(MemToUint32(&ref[2 * BPS])); - ref3 = _mm_cvtsi32_si128(MemToUint32(&ref[3 * BPS])); + ref0 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[0 * BPS])); + ref1 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[1 * BPS])); + ref2 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[2 * BPS])); + ref3 = _mm_cvtsi32_si128(WebPMemToUint32(&ref[3 * BPS])); } // Convert to 16b. ref0 = _mm_unpacklo_epi8(ref0, zero); @@ -276,10 +184,10 @@ static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst, _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3); } else { // Store four bytes/pixels per line. - *((int32_t *)&dst[0 * BPS]) = _mm_cvtsi128_si32(ref0); - *((int32_t *)&dst[1 * BPS]) = _mm_cvtsi128_si32(ref1); - *((int32_t *)&dst[2 * BPS]) = _mm_cvtsi128_si32(ref2); - *((int32_t *)&dst[3 * BPS]) = _mm_cvtsi128_si32(ref3); + WebPUint32ToMem(&dst[0 * BPS], _mm_cvtsi128_si32(ref0)); + WebPUint32ToMem(&dst[1 * BPS], _mm_cvtsi128_si32(ref1)); + WebPUint32ToMem(&dst[2 * BPS], _mm_cvtsi128_si32(ref2)); + WebPUint32ToMem(&dst[3 * BPS], _mm_cvtsi128_si32(ref3)); } } } @@ -384,42 +292,42 @@ static void FTransformPass2(const __m128i* const v01, const __m128i* const v32, static void FTransform(const uint8_t* src, const uint8_t* ref, int16_t* out) { const __m128i zero = _mm_setzero_si128(); - - // Load src and convert to 16b. + // Load src. const __m128i src0 = _mm_loadl_epi64((const __m128i*)&src[0 * BPS]); const __m128i src1 = _mm_loadl_epi64((const __m128i*)&src[1 * BPS]); const __m128i src2 = _mm_loadl_epi64((const __m128i*)&src[2 * BPS]); const __m128i src3 = _mm_loadl_epi64((const __m128i*)&src[3 * BPS]); - const __m128i src_0 = _mm_unpacklo_epi8(src0, zero); - const __m128i src_1 = _mm_unpacklo_epi8(src1, zero); - const __m128i src_2 = _mm_unpacklo_epi8(src2, zero); - const __m128i src_3 = _mm_unpacklo_epi8(src3, zero); - // Load ref and convert to 16b. + // 00 01 02 03 * + // 10 11 12 13 * + // 20 21 22 23 * + // 30 31 32 33 * + // Shuffle. + const __m128i src_0 = _mm_unpacklo_epi16(src0, src1); + const __m128i src_1 = _mm_unpacklo_epi16(src2, src3); + // 00 01 10 11 02 03 12 13 * * ... + // 20 21 30 31 22 22 32 33 * * ... + + // Load ref. const __m128i ref0 = _mm_loadl_epi64((const __m128i*)&ref[0 * BPS]); const __m128i ref1 = _mm_loadl_epi64((const __m128i*)&ref[1 * BPS]); const __m128i ref2 = _mm_loadl_epi64((const __m128i*)&ref[2 * BPS]); const __m128i ref3 = _mm_loadl_epi64((const __m128i*)&ref[3 * BPS]); - const __m128i ref_0 = _mm_unpacklo_epi8(ref0, zero); - const __m128i ref_1 = _mm_unpacklo_epi8(ref1, zero); - const __m128i ref_2 = _mm_unpacklo_epi8(ref2, zero); - const __m128i ref_3 = _mm_unpacklo_epi8(ref3, zero); - // Compute difference. -> 00 01 02 03 00 00 00 00 - const __m128i diff0 = _mm_sub_epi16(src_0, ref_0); - const __m128i diff1 = _mm_sub_epi16(src_1, ref_1); - const __m128i diff2 = _mm_sub_epi16(src_2, ref_2); - const __m128i diff3 = _mm_sub_epi16(src_3, ref_3); - - // Unpack and shuffle - // 00 01 02 03 0 0 0 0 - // 10 11 12 13 0 0 0 0 - // 20 21 22 23 0 0 0 0 - // 30 31 32 33 0 0 0 0 - const __m128i shuf01 = _mm_unpacklo_epi32(diff0, diff1); - const __m128i shuf23 = _mm_unpacklo_epi32(diff2, diff3); + const __m128i ref_0 = _mm_unpacklo_epi16(ref0, ref1); + const __m128i ref_1 = _mm_unpacklo_epi16(ref2, ref3); + + // Convert both to 16 bit. + const __m128i src_0_16b = _mm_unpacklo_epi8(src_0, zero); + const __m128i src_1_16b = _mm_unpacklo_epi8(src_1, zero); + const __m128i ref_0_16b = _mm_unpacklo_epi8(ref_0, zero); + const __m128i ref_1_16b = _mm_unpacklo_epi8(ref_1, zero); + + // Compute the difference. + const __m128i row01 = _mm_sub_epi16(src_0_16b, ref_0_16b); + const __m128i row23 = _mm_sub_epi16(src_1_16b, ref_1_16b); __m128i v01, v32; // First pass - FTransformPass1(&shuf01, &shuf23, &v01, &v32); + FTransformPass1(&row01, &row23, &v01, &v32); // Second pass FTransformPass2(&v01, &v32, out); @@ -474,8 +382,7 @@ static void FTransform2(const uint8_t* src, const uint8_t* ref, int16_t* out) { } static void FTransformWHTRow(const int16_t* const in, __m128i* const out) { - const __m128i kMult1 = _mm_set_epi16(0, 0, 0, 0, 1, 1, 1, 1); - const __m128i kMult2 = _mm_set_epi16(0, 0, 0, 0, -1, 1, -1, 1); + const __m128i kMult = _mm_set_epi16(-1, 1, -1, 1, 1, 1, 1, 1); const __m128i src0 = _mm_loadl_epi64((__m128i*)&in[0 * 16]); const __m128i src1 = _mm_loadl_epi64((__m128i*)&in[1 * 16]); const __m128i src2 = _mm_loadl_epi64((__m128i*)&in[2 * 16]); @@ -484,33 +391,38 @@ static void FTransformWHTRow(const int16_t* const in, __m128i* const out) { const __m128i A23 = _mm_unpacklo_epi16(src2, src3); // A2 A3 | ... const __m128i B0 = _mm_adds_epi16(A01, A23); // a0 | a1 | ... const __m128i B1 = _mm_subs_epi16(A01, A23); // a3 | a2 | ... - const __m128i C0 = _mm_unpacklo_epi32(B0, B1); // a0 | a1 | a3 | a2 - const __m128i C1 = _mm_unpacklo_epi32(B1, B0); // a3 | a2 | a0 | a1 - const __m128i D0 = _mm_madd_epi16(C0, kMult1); // out0, out1 - const __m128i D1 = _mm_madd_epi16(C1, kMult2); // out2, out3 - *out = _mm_unpacklo_epi64(D0, D1); + const __m128i C0 = _mm_unpacklo_epi32(B0, B1); // a0 | a1 | a3 | a2 | ... + const __m128i C1 = _mm_unpacklo_epi32(B1, B0); // a3 | a2 | a0 | a1 | ... + const __m128i D = _mm_unpacklo_epi64(C0, C1); // a0 a1 a3 a2 a3 a2 a0 a1 + *out = _mm_madd_epi16(D, kMult); } static void FTransformWHT(const int16_t* in, int16_t* out) { + // Input is 12b signed. __m128i row0, row1, row2, row3; + // Rows are 14b signed. FTransformWHTRow(in + 0 * 64, &row0); FTransformWHTRow(in + 1 * 64, &row1); FTransformWHTRow(in + 2 * 64, &row2); FTransformWHTRow(in + 3 * 64, &row3); { + // The a* are 15b signed. const __m128i a0 = _mm_add_epi32(row0, row2); const __m128i a1 = _mm_add_epi32(row1, row3); const __m128i a2 = _mm_sub_epi32(row1, row3); const __m128i a3 = _mm_sub_epi32(row0, row2); - const __m128i b0 = _mm_srai_epi32(_mm_add_epi32(a0, a1), 1); - const __m128i b1 = _mm_srai_epi32(_mm_add_epi32(a3, a2), 1); - const __m128i b2 = _mm_srai_epi32(_mm_sub_epi32(a3, a2), 1); - const __m128i b3 = _mm_srai_epi32(_mm_sub_epi32(a0, a1), 1); - const __m128i out0 = _mm_packs_epi32(b0, b1); - const __m128i out1 = _mm_packs_epi32(b2, b3); - _mm_storeu_si128((__m128i*)&out[0], out0); - _mm_storeu_si128((__m128i*)&out[8], out1); + const __m128i a0a3 = _mm_packs_epi32(a0, a3); + const __m128i a1a2 = _mm_packs_epi32(a1, a2); + + // The b* are 16b signed. + const __m128i b0b1 = _mm_add_epi16(a0a3, a1a2); + const __m128i b3b2 = _mm_sub_epi16(a0a3, a1a2); + const __m128i tmp_b2b3 = _mm_unpackhi_epi64(b3b2, b3b2); + const __m128i b2b3 = _mm_unpacklo_epi64(tmp_b2b3, b3b2); + + _mm_storeu_si128((__m128i*)&out[0], _mm_srai_epi16(b0b1, 1)); + _mm_storeu_si128((__m128i*)&out[8], _mm_srai_epi16(b2b3, 1)); } } @@ -703,12 +615,10 @@ static WEBP_INLINE void TrueMotion(uint8_t* dst, const uint8_t* left, static WEBP_INLINE void DC8uv(uint8_t* dst, const uint8_t* left, const uint8_t* top) { - const __m128i zero = _mm_setzero_si128(); const __m128i top_values = _mm_loadl_epi64((const __m128i*)top); const __m128i left_values = _mm_loadl_epi64((const __m128i*)left); - const __m128i sum_top = _mm_sad_epu8(top_values, zero); - const __m128i sum_left = _mm_sad_epu8(left_values, zero); - const int DC = _mm_cvtsi128_si32(sum_top) + _mm_cvtsi128_si32(sum_left) + 8; + const __m128i combined = _mm_unpacklo_epi64(top_values, left_values); + const int DC = VP8HorizontalAdd8b(&combined) + 8; Put8x8uv(DC >> 4, dst); } @@ -746,27 +656,16 @@ static WEBP_INLINE void DC8uvMode(uint8_t* dst, const uint8_t* left, static WEBP_INLINE void DC16(uint8_t* dst, const uint8_t* left, const uint8_t* top) { - const __m128i zero = _mm_setzero_si128(); const __m128i top_row = _mm_load_si128((const __m128i*)top); const __m128i left_row = _mm_load_si128((const __m128i*)left); - const __m128i sad8x2 = _mm_sad_epu8(top_row, zero); - // sum the two sads: sad8x2[0:1] + sad8x2[8:9] - const __m128i sum_top = _mm_add_epi16(sad8x2, _mm_shuffle_epi32(sad8x2, 2)); - const __m128i sad8x2_left = _mm_sad_epu8(left_row, zero); - // sum the two sads: sad8x2[0:1] + sad8x2[8:9] - const __m128i sum_left = - _mm_add_epi16(sad8x2_left, _mm_shuffle_epi32(sad8x2_left, 2)); - const int DC = _mm_cvtsi128_si32(sum_top) + _mm_cvtsi128_si32(sum_left) + 16; + const int DC = + VP8HorizontalAdd8b(&top_row) + VP8HorizontalAdd8b(&left_row) + 16; Put16(DC >> 5, dst); } static WEBP_INLINE void DC16NoLeft(uint8_t* dst, const uint8_t* top) { - const __m128i zero = _mm_setzero_si128(); const __m128i top_row = _mm_load_si128((const __m128i*)top); - const __m128i sad8x2 = _mm_sad_epu8(top_row, zero); - // sum the two sads: sad8x2[0:1] + sad8x2[8:9] - const __m128i sum = _mm_add_epi16(sad8x2, _mm_shuffle_epi32(sad8x2, 2)); - const int DC = _mm_cvtsi128_si32(sum) + 8; + const int DC = VP8HorizontalAdd8b(&top_row) + 8; Put16(DC >> 4, dst); } @@ -821,7 +720,7 @@ static WEBP_INLINE void VE4(uint8_t* dst, const uint8_t* top) { // vertical const uint32_t vals = _mm_cvtsi128_si32(avg); int i; for (i = 0; i < 4; ++i) { - *(uint32_t*)(dst + i * BPS) = vals; + WebPUint32ToMem(dst + i * BPS, vals); } } @@ -831,10 +730,10 @@ static WEBP_INLINE void HE4(uint8_t* dst, const uint8_t* top) { // horizontal const int J = top[-3]; const int K = top[-4]; const int L = top[-5]; - *(uint32_t*)(dst + 0 * BPS) = 0x01010101U * AVG3(X, I, J); - *(uint32_t*)(dst + 1 * BPS) = 0x01010101U * AVG3(I, J, K); - *(uint32_t*)(dst + 2 * BPS) = 0x01010101U * AVG3(J, K, L); - *(uint32_t*)(dst + 3 * BPS) = 0x01010101U * AVG3(K, L, L); + WebPUint32ToMem(dst + 0 * BPS, 0x01010101U * AVG3(X, I, J)); + WebPUint32ToMem(dst + 1 * BPS, 0x01010101U * AVG3(I, J, K)); + WebPUint32ToMem(dst + 2 * BPS, 0x01010101U * AVG3(J, K, L)); + WebPUint32ToMem(dst + 3 * BPS, 0x01010101U * AVG3(K, L, L)); } static WEBP_INLINE void DC4(uint8_t* dst, const uint8_t* top) { @@ -854,10 +753,10 @@ static WEBP_INLINE void LD4(uint8_t* dst, const uint8_t* top) { // Down-Left const __m128i lsb = _mm_and_si128(_mm_xor_si128(ABCDEFGH, CDEFGHH0), one); const __m128i avg2 = _mm_subs_epu8(avg1, lsb); const __m128i abcdefg = _mm_avg_epu8(avg2, BCDEFGH0); - *(uint32_t*)(dst + 0 * BPS) = _mm_cvtsi128_si32( abcdefg ); - *(uint32_t*)(dst + 1 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)); - *(uint32_t*)(dst + 2 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)); - *(uint32_t*)(dst + 3 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcdefg )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1))); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3))); } static WEBP_INLINE void VR4(uint8_t* dst, @@ -876,10 +775,10 @@ static WEBP_INLINE void VR4(uint8_t* dst, const __m128i lsb = _mm_and_si128(_mm_xor_si128(IXABCD, ABCD0), one); const __m128i avg2 = _mm_subs_epu8(avg1, lsb); const __m128i efgh = _mm_avg_epu8(avg2, XABCD); - *(uint32_t*)(dst + 0 * BPS) = _mm_cvtsi128_si32( abcd ); - *(uint32_t*)(dst + 1 * BPS) = _mm_cvtsi128_si32( efgh ); - *(uint32_t*)(dst + 2 * BPS) = _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1)); - *(uint32_t*)(dst + 3 * BPS) = _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1)); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( abcd )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( efgh )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(abcd, 1))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_slli_si128(efgh, 1))); // these two are hard to implement in SSE2, so we keep the C-version: DST(0, 2) = AVG3(J, I, X); @@ -902,10 +801,10 @@ static WEBP_INLINE void VL4(uint8_t* dst, const __m128i lsb2 = _mm_and_si128(abbc, lsb1); const __m128i avg4 = _mm_subs_epu8(avg3, lsb2); const uint32_t extra_out = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 4)); - *(uint32_t*)(dst + 0 * BPS) = _mm_cvtsi128_si32( avg1 ); - *(uint32_t*)(dst + 1 * BPS) = _mm_cvtsi128_si32( avg4 ); - *(uint32_t*)(dst + 2 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1)); - *(uint32_t*)(dst + 3 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1)); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32( avg1 )); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32( avg4 )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg1, 1))); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(avg4, 1))); // these two are hard to get and irregular DST(3, 2) = (extra_out >> 0) & 0xff; @@ -922,10 +821,10 @@ static WEBP_INLINE void RD4(uint8_t* dst, const uint8_t* top) { // Down-right const __m128i lsb = _mm_and_si128(_mm_xor_si128(JIXABCD__, LKJIXABCD), one); const __m128i avg2 = _mm_subs_epu8(avg1, lsb); const __m128i abcdefg = _mm_avg_epu8(avg2, KJIXABCD_); - *(uint32_t*)(dst + 3 * BPS) = _mm_cvtsi128_si32( abcdefg ); - *(uint32_t*)(dst + 2 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1)); - *(uint32_t*)(dst + 1 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2)); - *(uint32_t*)(dst + 0 * BPS) = _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3)); + WebPUint32ToMem(dst + 3 * BPS, _mm_cvtsi128_si32( abcdefg )); + WebPUint32ToMem(dst + 2 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 1))); + WebPUint32ToMem(dst + 1 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 2))); + WebPUint32ToMem(dst + 0 * BPS, _mm_cvtsi128_si32(_mm_srli_si128(abcdefg, 3))); } static WEBP_INLINE void HU4(uint8_t* dst, const uint8_t* top) { @@ -968,14 +867,14 @@ static WEBP_INLINE void HD4(uint8_t* dst, const uint8_t* top) { static WEBP_INLINE void TM4(uint8_t* dst, const uint8_t* top) { const __m128i zero = _mm_setzero_si128(); - const __m128i top_values = _mm_cvtsi32_si128(MemToUint32(top)); + const __m128i top_values = _mm_cvtsi32_si128(WebPMemToUint32(top)); const __m128i top_base = _mm_unpacklo_epi8(top_values, zero); int y; for (y = 0; y < 4; ++y, dst += BPS) { const int val = top[-2 - y] - top[-1]; const __m128i base = _mm_set1_epi16(val); const __m128i out = _mm_packus_epi16(_mm_add_epi16(base, top_base), zero); - *(int*)dst = _mm_cvtsi128_si32(out); + WebPUint32ToMem(dst, _mm_cvtsi128_si32(out)); } } @@ -1153,15 +1052,15 @@ static int SSE4x4(const uint8_t* a, const uint8_t* b) { // reconstructed samples. // Hadamard transform -// Returns the difference between the weighted sum of the absolute value of -// transformed coefficients. +// Returns the weighted sum of the absolute value of transformed coefficients. +// w[] contains a row-major 4 by 4 symmetric matrix. static int TTransform(const uint8_t* inA, const uint8_t* inB, const uint16_t* const w) { int32_t sum[4]; __m128i tmp_0, tmp_1, tmp_2, tmp_3; const __m128i zero = _mm_setzero_si128(); - // Load, combine and transpose inputs. + // Load and combine inputs. { const __m128i inA_0 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 0]); const __m128i inA_1 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 1]); @@ -1173,37 +1072,22 @@ static int TTransform(const uint8_t* inA, const uint8_t* inB, const __m128i inB_3 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 3]); // Combine inA and inB (we'll do two transforms in parallel). - const __m128i inAB_0 = _mm_unpacklo_epi8(inA_0, inB_0); - const __m128i inAB_1 = _mm_unpacklo_epi8(inA_1, inB_1); - const __m128i inAB_2 = _mm_unpacklo_epi8(inA_2, inB_2); - const __m128i inAB_3 = _mm_unpacklo_epi8(inA_3, inB_3); - // a00 b00 a01 b01 a02 b03 a03 b03 0 0 0 0 0 0 0 0 - // a10 b10 a11 b11 a12 b12 a13 b13 0 0 0 0 0 0 0 0 - // a20 b20 a21 b21 a22 b22 a23 b23 0 0 0 0 0 0 0 0 - // a30 b30 a31 b31 a32 b32 a33 b33 0 0 0 0 0 0 0 0 - - // Transpose the two 4x4, discarding the filling zeroes. - const __m128i transpose0_0 = _mm_unpacklo_epi8(inAB_0, inAB_2); - const __m128i transpose0_1 = _mm_unpacklo_epi8(inAB_1, inAB_3); - // a00 a20 b00 b20 a01 a21 b01 b21 a02 a22 b02 b22 a03 a23 b03 b23 - // a10 a30 b10 b30 a11 a31 b11 b31 a12 a32 b12 b32 a13 a33 b13 b33 - const __m128i transpose1_0 = _mm_unpacklo_epi8(transpose0_0, transpose0_1); - const __m128i transpose1_1 = _mm_unpackhi_epi8(transpose0_0, transpose0_1); - // 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 - - // Convert to 16b. - tmp_0 = _mm_unpacklo_epi8(transpose1_0, zero); - tmp_1 = _mm_unpackhi_epi8(transpose1_0, zero); - tmp_2 = _mm_unpacklo_epi8(transpose1_1, zero); - tmp_3 = _mm_unpackhi_epi8(transpose1_1, zero); - // 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 + const __m128i inAB_0 = _mm_unpacklo_epi32(inA_0, inB_0); + const __m128i inAB_1 = _mm_unpacklo_epi32(inA_1, inB_1); + const __m128i inAB_2 = _mm_unpacklo_epi32(inA_2, inB_2); + const __m128i inAB_3 = _mm_unpacklo_epi32(inA_3, inB_3); + tmp_0 = _mm_unpacklo_epi8(inAB_0, zero); + tmp_1 = _mm_unpacklo_epi8(inAB_1, zero); + tmp_2 = _mm_unpacklo_epi8(inAB_2, zero); + tmp_3 = _mm_unpacklo_epi8(inAB_3, zero); + // 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 } - // Horizontal pass and subsequent transpose. + // Vertical pass first to avoid a transpose (vertical and horizontal passes + // are commutative because w/kWeightY is symmetric) and subsequent transpose. { // Calculate a and b (two 4x4 at once). const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2); @@ -1220,37 +1104,12 @@ static int TTransform(const uint8_t* inA, const uint8_t* inB, // a30 a31 a32 a33 b30 b31 b32 b33 // Transpose the two 4x4. - const __m128i transpose0_0 = _mm_unpacklo_epi16(b0, b1); - const __m128i transpose0_1 = _mm_unpacklo_epi16(b2, b3); - const __m128i transpose0_2 = _mm_unpackhi_epi16(b0, b1); - const __m128i transpose0_3 = _mm_unpackhi_epi16(b2, b3); - // 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 - tmp_0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1); - tmp_1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1); - tmp_2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3); - tmp_3 = _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 + VP8Transpose_2_4x4_16b(&b0, &b1, &b2, &b3, &tmp_0, &tmp_1, &tmp_2, &tmp_3); } - // Vertical pass and difference of weighted sums. + // Horizontal pass and difference of weighted sums. { // Load all inputs. - // TODO(cduvivier): Make variable declarations and allocations aligned so - // we can use _mm_load_si128 instead of _mm_loadu_si128. const __m128i w_0 = _mm_loadu_si128((const __m128i*)&w[0]); const __m128i w_8 = _mm_loadu_si128((const __m128i*)&w[8]); @@ -1328,8 +1187,6 @@ static WEBP_INLINE int DoQuantizeBlock(int16_t in[16], int16_t out[16], __m128i packed_out; // Load all inputs. - // TODO(cduvivier): Make variable declarations and allocations aligned so that - // we can use _mm_load_si128 instead of _mm_loadu_si128. __m128i in0 = _mm_loadu_si128((__m128i*)&in[0]); __m128i in8 = _mm_loadu_si128((__m128i*)&in[8]); const __m128i iq0 = _mm_loadu_si128((const __m128i*)&mtx->iq_[0]); diff --git a/drivers/webp/dsp/enc_sse41.c b/drivers/webp/dsp/enc_sse41.c index 27f4189833..a1783901a6 100644 --- a/drivers/webp/dsp/enc_sse41.c +++ b/drivers/webp/dsp/enc_sse41.c @@ -17,6 +17,7 @@ #include <smmintrin.h> #include <stdlib.h> // for abs() +#include "./common_sse2.h" #include "../enc/vp8enci.h" //------------------------------------------------------------------------------ @@ -67,55 +68,45 @@ static void CollectHistogram(const uint8_t* ref, const uint8_t* pred, // reconstructed samples. // Hadamard transform -// Returns the difference between the weighted sum of the absolute value of -// transformed coefficients. +// Returns the weighted sum of the absolute value of transformed coefficients. +// w[] contains a row-major 4 by 4 symmetric matrix. static int TTransform(const uint8_t* inA, const uint8_t* inB, const uint16_t* const w) { + int32_t sum[4]; __m128i tmp_0, tmp_1, tmp_2, tmp_3; - // Load, combine and transpose inputs. + // Load and combine inputs. { - const __m128i inA_0 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 0]); - const __m128i inA_1 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 1]); - const __m128i inA_2 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 2]); + const __m128i inA_0 = _mm_loadu_si128((const __m128i*)&inA[BPS * 0]); + const __m128i inA_1 = _mm_loadu_si128((const __m128i*)&inA[BPS * 1]); + const __m128i inA_2 = _mm_loadu_si128((const __m128i*)&inA[BPS * 2]); + // In SSE4.1, with gcc 4.8 at least (maybe other versions), + // _mm_loadu_si128 is faster than _mm_loadl_epi64. But for the last lump + // of inA and inB, _mm_loadl_epi64 is still used not to have an out of + // bound read. const __m128i inA_3 = _mm_loadl_epi64((const __m128i*)&inA[BPS * 3]); - const __m128i inB_0 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 0]); - const __m128i inB_1 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 1]); - const __m128i inB_2 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 2]); + const __m128i inB_0 = _mm_loadu_si128((const __m128i*)&inB[BPS * 0]); + const __m128i inB_1 = _mm_loadu_si128((const __m128i*)&inB[BPS * 1]); + const __m128i inB_2 = _mm_loadu_si128((const __m128i*)&inB[BPS * 2]); const __m128i inB_3 = _mm_loadl_epi64((const __m128i*)&inB[BPS * 3]); // Combine inA and inB (we'll do two transforms in parallel). - const __m128i inAB_0 = _mm_unpacklo_epi8(inA_0, inB_0); - const __m128i inAB_1 = _mm_unpacklo_epi8(inA_1, inB_1); - const __m128i inAB_2 = _mm_unpacklo_epi8(inA_2, inB_2); - const __m128i inAB_3 = _mm_unpacklo_epi8(inA_3, inB_3); - // a00 b00 a01 b01 a02 b03 a03 b03 0 0 0 0 0 0 0 0 - // a10 b10 a11 b11 a12 b12 a13 b13 0 0 0 0 0 0 0 0 - // a20 b20 a21 b21 a22 b22 a23 b23 0 0 0 0 0 0 0 0 - // a30 b30 a31 b31 a32 b32 a33 b33 0 0 0 0 0 0 0 0 - - // Transpose the two 4x4, discarding the filling zeroes. - const __m128i transpose0_0 = _mm_unpacklo_epi8(inAB_0, inAB_2); - const __m128i transpose0_1 = _mm_unpacklo_epi8(inAB_1, inAB_3); - // a00 a20 b00 b20 a01 a21 b01 b21 a02 a22 b02 b22 a03 a23 b03 b23 - // a10 a30 b10 b30 a11 a31 b11 b31 a12 a32 b12 b32 a13 a33 b13 b33 - const __m128i transpose1_0 = _mm_unpacklo_epi8(transpose0_0, transpose0_1); - const __m128i transpose1_1 = _mm_unpackhi_epi8(transpose0_0, transpose0_1); - // 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 - - // Convert to 16b. - tmp_0 = _mm_cvtepu8_epi16(transpose1_0); - tmp_1 = _mm_cvtepu8_epi16(_mm_srli_si128(transpose1_0, 8)); - tmp_2 = _mm_cvtepu8_epi16(transpose1_1); - tmp_3 = _mm_cvtepu8_epi16(_mm_srli_si128(transpose1_1, 8)); - // 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 + const __m128i inAB_0 = _mm_unpacklo_epi32(inA_0, inB_0); + const __m128i inAB_1 = _mm_unpacklo_epi32(inA_1, inB_1); + const __m128i inAB_2 = _mm_unpacklo_epi32(inA_2, inB_2); + const __m128i inAB_3 = _mm_unpacklo_epi32(inA_3, inB_3); + tmp_0 = _mm_cvtepu8_epi16(inAB_0); + tmp_1 = _mm_cvtepu8_epi16(inAB_1); + tmp_2 = _mm_cvtepu8_epi16(inAB_2); + tmp_3 = _mm_cvtepu8_epi16(inAB_3); + // 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 } - // Horizontal pass and subsequent transpose. + // Vertical pass first to avoid a transpose (vertical and horizontal passes + // are commutative because w/kWeightY is symmetric) and subsequent transpose. { // Calculate a and b (two 4x4 at once). const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2); @@ -132,33 +123,10 @@ static int TTransform(const uint8_t* inA, const uint8_t* inB, // a30 a31 a32 a33 b30 b31 b32 b33 // Transpose the two 4x4. - const __m128i transpose0_0 = _mm_unpacklo_epi16(b0, b1); - const __m128i transpose0_1 = _mm_unpacklo_epi16(b2, b3); - const __m128i transpose0_2 = _mm_unpackhi_epi16(b0, b1); - const __m128i transpose0_3 = _mm_unpackhi_epi16(b2, b3); - // 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 - tmp_0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1); - tmp_1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1); - tmp_2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3); - tmp_3 = _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 + VP8Transpose_2_4x4_16b(&b0, &b1, &b2, &b3, &tmp_0, &tmp_1, &tmp_2, &tmp_3); } - // Vertical pass and difference of weighted sums. + // Horizontal pass and difference of weighted sums. { // Load all inputs. const __m128i w_0 = _mm_loadu_si128((const __m128i*)&w[0]); @@ -195,11 +163,9 @@ static int TTransform(const uint8_t* inA, const uint8_t* inB, // difference of weighted sums A_b2 = _mm_sub_epi32(A_b0, B_b0); - // cascading summation of the differences - B_b0 = _mm_hadd_epi32(A_b2, A_b2); - B_b2 = _mm_hadd_epi32(B_b0, B_b0); - return _mm_cvtsi128_si32(B_b2); + _mm_storeu_si128((__m128i*)&sum[0], A_b2); } + return sum[0] + sum[1] + sum[2] + sum[3]; } static int Disto4x4(const uint8_t* const a, const uint8_t* const b, @@ -240,8 +206,6 @@ static WEBP_INLINE int DoQuantizeBlock(int16_t in[16], int16_t out[16], __m128i packed_out; // Load all inputs. - // TODO(cduvivier): Make variable declarations and allocations aligned so that - // we can use _mm_load_si128 instead of _mm_loadu_si128. __m128i in0 = _mm_loadu_si128((__m128i*)&in[0]); __m128i in8 = _mm_loadu_si128((__m128i*)&in[8]); const __m128i iq0 = _mm_loadu_si128((const __m128i*)&mtx->iq_[0]); diff --git a/drivers/webp/dsp/filters.c b/drivers/webp/dsp/filters.c index 5c30f2e457..9f04faf0cb 100644 --- a/drivers/webp/dsp/filters.c +++ b/drivers/webp/dsp/filters.c @@ -184,19 +184,40 @@ static void GradientFilter(const uint8_t* data, int width, int height, //------------------------------------------------------------------------------ -static void VerticalUnfilter(int width, int height, int stride, int row, - int num_rows, uint8_t* data) { - DoVerticalFilter(data, width, height, stride, row, num_rows, 1, data); +static void HorizontalUnfilter(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + uint8_t pred = (prev == NULL) ? 0 : prev[0]; + int i; + for (i = 0; i < width; ++i) { + out[i] = pred + in[i]; + pred = out[i]; + } } -static void HorizontalUnfilter(int width, int height, int stride, int row, - int num_rows, uint8_t* data) { - DoHorizontalFilter(data, width, height, stride, row, num_rows, 1, data); +static void VerticalUnfilter(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter(NULL, in, out, width); + } else { + int i; + for (i = 0; i < width; ++i) out[i] = prev[i] + in[i]; + } } -static void GradientUnfilter(int width, int height, int stride, int row, - int num_rows, uint8_t* data) { - DoGradientFilter(data, width, height, stride, row, num_rows, 1, data); +static void GradientUnfilter(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter(NULL, in, out, width); + } else { + uint8_t top = prev[0], top_left = top, left = top; + int i; + for (i = 0; i < width; ++i) { + top = prev[i]; // need to read this first, in case prev==out + left = in[i] + GradientPredictor(left, top, top_left); + top_left = top; + out[i] = left; + } + } } //------------------------------------------------------------------------------ diff --git a/drivers/webp/dsp/filters_mips_dsp_r2.c b/drivers/webp/dsp/filters_mips_dsp_r2.c index 8134af511b..1d82e3c2e1 100644 --- a/drivers/webp/dsp/filters_mips_dsp_r2.c +++ b/drivers/webp/dsp/filters_mips_dsp_r2.c @@ -33,10 +33,6 @@ assert(row >= 0 && num_rows > 0 && row + num_rows <= height); \ (void)height; // Silence unused warning. -// if INVERSE -// preds == &dst[-1] == &src[-1] -// else -// preds == &src[-1] != &dst[-1] #define DO_PREDICT_LINE(SRC, DST, LENGTH, INVERSE) do { \ const uint8_t* psrc = (uint8_t*)(SRC); \ uint8_t* pdst = (uint8_t*)(DST); \ @@ -45,27 +41,28 @@ __asm__ volatile ( \ ".set push \n\t" \ ".set noreorder \n\t" \ - "srl %[temp0], %[length], 0x2 \n\t" \ + "srl %[temp0], %[length], 2 \n\t" \ "beqz %[temp0], 4f \n\t" \ - " andi %[temp6], %[length], 0x3 \n\t" \ + " andi %[temp6], %[length], 3 \n\t" \ ".if " #INVERSE " \n\t" \ - "lbu %[temp1], -1(%[src]) \n\t" \ "1: \n\t" \ + "lbu %[temp1], -1(%[dst]) \n\t" \ "lbu %[temp2], 0(%[src]) \n\t" \ "lbu %[temp3], 1(%[src]) \n\t" \ "lbu %[temp4], 2(%[src]) \n\t" \ "lbu %[temp5], 3(%[src]) \n\t" \ + "addu %[temp1], %[temp1], %[temp2] \n\t" \ + "addu %[temp2], %[temp1], %[temp3] \n\t" \ + "addu %[temp3], %[temp2], %[temp4] \n\t" \ + "addu %[temp4], %[temp3], %[temp5] \n\t" \ + "sb %[temp1], 0(%[dst]) \n\t" \ + "sb %[temp2], 1(%[dst]) \n\t" \ + "sb %[temp3], 2(%[dst]) \n\t" \ + "sb %[temp4], 3(%[dst]) \n\t" \ "addiu %[src], %[src], 4 \n\t" \ "addiu %[temp0], %[temp0], -1 \n\t" \ - "addu %[temp2], %[temp2], %[temp1] \n\t" \ - "addu %[temp3], %[temp3], %[temp2] \n\t" \ - "addu %[temp4], %[temp4], %[temp3] \n\t" \ - "addu %[temp1], %[temp5], %[temp4] \n\t" \ - "sb %[temp2], -4(%[src]) \n\t" \ - "sb %[temp3], -3(%[src]) \n\t" \ - "sb %[temp4], -2(%[src]) \n\t" \ "bnez %[temp0], 1b \n\t" \ - " sb %[temp1], -1(%[src]) \n\t" \ + " addiu %[dst], %[dst], 4 \n\t" \ ".else \n\t" \ "1: \n\t" \ "ulw %[temp1], -1(%[src]) \n\t" \ @@ -81,16 +78,16 @@ "beqz %[temp6], 3f \n\t" \ " nop \n\t" \ "2: \n\t" \ - "lbu %[temp1], -1(%[src]) \n\t" \ "lbu %[temp2], 0(%[src]) \n\t" \ - "addiu %[src], %[src], 1 \n\t" \ ".if " #INVERSE " \n\t" \ + "lbu %[temp1], -1(%[dst]) \n\t" \ "addu %[temp3], %[temp1], %[temp2] \n\t" \ - "sb %[temp3], -1(%[src]) \n\t" \ ".else \n\t" \ + "lbu %[temp1], -1(%[src]) \n\t" \ "subu %[temp3], %[temp1], %[temp2] \n\t" \ - "sb %[temp3], 0(%[dst]) \n\t" \ ".endif \n\t" \ + "addiu %[src], %[src], 1 \n\t" \ + "sb %[temp3], 0(%[dst]) \n\t" \ "addiu %[temp6], %[temp6], -1 \n\t" \ "bnez %[temp6], 2b \n\t" \ " addiu %[dst], %[dst], 1 \n\t" \ @@ -105,12 +102,8 @@ } while (0) static WEBP_INLINE void PredictLine(const uint8_t* src, uint8_t* dst, - int length, int inverse) { - if (inverse) { - DO_PREDICT_LINE(src, dst, length, 1); - } else { - DO_PREDICT_LINE(src, dst, length, 0); - } + int length) { + DO_PREDICT_LINE(src, dst, length, 0); } #define DO_PREDICT_LINE_VERTICAL(SRC, PRED, DST, LENGTH, INVERSE) do { \ @@ -172,16 +165,12 @@ static WEBP_INLINE void PredictLine(const uint8_t* src, uint8_t* dst, ); \ } while (0) -#define PREDICT_LINE_ONE_PASS(SRC, PRED, DST, INVERSE) do { \ +#define PREDICT_LINE_ONE_PASS(SRC, PRED, DST) do { \ int temp1, temp2, temp3; \ __asm__ volatile ( \ "lbu %[temp1], 0(%[src]) \n\t" \ "lbu %[temp2], 0(%[pred]) \n\t" \ - ".if " #INVERSE " \n\t" \ - "addu %[temp3], %[temp1], %[temp2] \n\t" \ - ".else \n\t" \ "subu %[temp3], %[temp1], %[temp2] \n\t" \ - ".endif \n\t" \ "sb %[temp3], 0(%[dst]) \n\t" \ : [temp1]"=&r"(temp1), [temp2]"=&r"(temp2), [temp3]"=&r"(temp3) \ : [pred]"r"((PRED)), [dst]"r"((DST)), [src]"r"((SRC)) \ @@ -192,10 +181,10 @@ static WEBP_INLINE void PredictLine(const uint8_t* src, uint8_t* dst, //------------------------------------------------------------------------------ // Horizontal filter. -#define FILTER_LINE_BY_LINE(INVERSE) do { \ +#define FILTER_LINE_BY_LINE do { \ while (row < last_row) { \ - PREDICT_LINE_ONE_PASS(in, preds - stride, out, INVERSE); \ - DO_PREDICT_LINE(in + 1, out + 1, width - 1, INVERSE); \ + PREDICT_LINE_ONE_PASS(in, preds - stride, out); \ + DO_PREDICT_LINE(in + 1, out + 1, width - 1, 0); \ ++row; \ preds += stride; \ in += stride; \ @@ -206,19 +195,19 @@ static WEBP_INLINE void PredictLine(const uint8_t* src, uint8_t* dst, static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in, int width, int height, int stride, int row, int num_rows, - int inverse, uint8_t* out) { + uint8_t* out) { const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; SANITY_CHECK(in, out); in += start_offset; out += start_offset; - preds = inverse ? out : in; + preds = in; if (row == 0) { // Leftmost pixel is the same as input for topmost scanline. out[0] = in[0]; - PredictLine(in + 1, out + 1, width - 1, inverse); + PredictLine(in + 1, out + 1, width - 1); row = 1; preds += stride; in += stride; @@ -226,31 +215,21 @@ static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in, } // Filter line-by-line. - if (inverse) { - FILTER_LINE_BY_LINE(1); - } else { - FILTER_LINE_BY_LINE(0); - } + FILTER_LINE_BY_LINE; } - #undef FILTER_LINE_BY_LINE static void HorizontalFilter(const uint8_t* data, int width, int height, int stride, uint8_t* filtered_data) { - DoHorizontalFilter(data, width, height, stride, 0, height, 0, filtered_data); -} - -static void HorizontalUnfilter(int width, int height, int stride, int row, - int num_rows, uint8_t* data) { - DoHorizontalFilter(data, width, height, stride, row, num_rows, 1, data); + DoHorizontalFilter(data, width, height, stride, 0, height, filtered_data); } //------------------------------------------------------------------------------ // Vertical filter. -#define FILTER_LINE_BY_LINE(INVERSE) do { \ +#define FILTER_LINE_BY_LINE do { \ while (row < last_row) { \ - DO_PREDICT_LINE_VERTICAL(in, preds, out, width, INVERSE); \ + DO_PREDICT_LINE_VERTICAL(in, preds, out, width, 0); \ ++row; \ preds += stride; \ in += stride; \ @@ -260,21 +239,20 @@ static void HorizontalUnfilter(int width, int height, int stride, int row, static WEBP_INLINE void DoVerticalFilter(const uint8_t* in, int width, int height, int stride, - int row, int num_rows, - int inverse, uint8_t* out) { + int row, int num_rows, uint8_t* out) { const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; SANITY_CHECK(in, out); in += start_offset; out += start_offset; - preds = inverse ? out : in; + preds = in; if (row == 0) { // Very first top-left pixel is copied. out[0] = in[0]; // Rest of top scan-line is left-predicted. - PredictLine(in + 1, out + 1, width - 1, inverse); + PredictLine(in + 1, out + 1, width - 1); row = 1; in += stride; out += stride; @@ -284,24 +262,13 @@ static WEBP_INLINE void DoVerticalFilter(const uint8_t* in, } // Filter line-by-line. - if (inverse) { - FILTER_LINE_BY_LINE(1); - } else { - FILTER_LINE_BY_LINE(0); - } + FILTER_LINE_BY_LINE; } - #undef FILTER_LINE_BY_LINE -#undef DO_PREDICT_LINE_VERTICAL static void VerticalFilter(const uint8_t* data, int width, int height, int stride, uint8_t* filtered_data) { - DoVerticalFilter(data, width, height, stride, 0, height, 0, filtered_data); -} - -static void VerticalUnfilter(int width, int height, int stride, int row, - int num_rows, uint8_t* data) { - DoVerticalFilter(data, width, height, stride, row, num_rows, 1, data); + DoVerticalFilter(data, width, height, stride, 0, height, filtered_data); } //------------------------------------------------------------------------------ @@ -321,10 +288,10 @@ static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) { return temp0; } -#define FILTER_LINE_BY_LINE(INVERSE, PREDS, OPERATION) do { \ +#define FILTER_LINE_BY_LINE(PREDS, OPERATION) do { \ while (row < last_row) { \ int w; \ - PREDICT_LINE_ONE_PASS(in, PREDS - stride, out, INVERSE); \ + PREDICT_LINE_ONE_PASS(in, PREDS - stride, out); \ for (w = 1; w < width; ++w) { \ const int pred = GradientPredictor(PREDS[w - 1], \ PREDS[w - stride], \ @@ -339,20 +306,19 @@ static WEBP_INLINE int GradientPredictor(uint8_t a, uint8_t b, uint8_t c) { static WEBP_INLINE void DoGradientFilter(const uint8_t* in, int width, int height, int stride, - int row, int num_rows, - int inverse, uint8_t* out) { + int row, int num_rows, uint8_t* out) { const uint8_t* preds; const size_t start_offset = row * stride; const int last_row = row + num_rows; SANITY_CHECK(in, out); in += start_offset; out += start_offset; - preds = inverse ? out : in; + preds = in; // left prediction for top scan-line if (row == 0) { out[0] = in[0]; - PredictLine(in + 1, out + 1, width - 1, inverse); + PredictLine(in + 1, out + 1, width - 1); row = 1; preds += stride; in += stride; @@ -360,25 +326,49 @@ static WEBP_INLINE void DoGradientFilter(const uint8_t* in, } // Filter line-by-line. - if (inverse) { - FILTER_LINE_BY_LINE(1, out, +); - } else { - FILTER_LINE_BY_LINE(0, in, -); - } + FILTER_LINE_BY_LINE(in, -); } - #undef FILTER_LINE_BY_LINE static void GradientFilter(const uint8_t* data, int width, int height, int stride, uint8_t* filtered_data) { - DoGradientFilter(data, width, height, stride, 0, height, 0, filtered_data); + DoGradientFilter(data, width, height, stride, 0, height, filtered_data); } -static void GradientUnfilter(int width, int height, int stride, int row, - int num_rows, uint8_t* data) { - DoGradientFilter(data, width, height, stride, row, num_rows, 1, data); +//------------------------------------------------------------------------------ + +static void HorizontalUnfilter(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + out[0] = in[0] + (prev == NULL ? 0 : prev[0]); + DO_PREDICT_LINE(in + 1, out + 1, width - 1, 1); } +static void VerticalUnfilter(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter(NULL, in, out, width); + } else { + DO_PREDICT_LINE_VERTICAL(in, prev, out, width, 1); + } +} + +static void GradientUnfilter(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter(NULL, in, out, width); + } else { + uint8_t top = prev[0], top_left = top, left = top; + int i; + for (i = 0; i < width; ++i) { + top = prev[i]; // need to read this first, in case prev==dst + left = in[i] + GradientPredictor(left, top, top_left); + top_left = top; + out[i] = left; + } + } +} + +#undef DO_PREDICT_LINE_VERTICAL #undef PREDICT_LINE_ONE_PASS #undef DO_PREDICT_LINE #undef SANITY_CHECK @@ -389,13 +379,13 @@ static void GradientUnfilter(int width, int height, int stride, int row, extern void VP8FiltersInitMIPSdspR2(void); WEBP_TSAN_IGNORE_FUNCTION void VP8FiltersInitMIPSdspR2(void) { - WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter; - WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter; - WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter; - WebPUnfilters[WEBP_FILTER_HORIZONTAL] = HorizontalUnfilter; WebPUnfilters[WEBP_FILTER_VERTICAL] = VerticalUnfilter; WebPUnfilters[WEBP_FILTER_GRADIENT] = GradientUnfilter; + + WebPFilters[WEBP_FILTER_HORIZONTAL] = HorizontalFilter; + WebPFilters[WEBP_FILTER_VERTICAL] = VerticalFilter; + WebPFilters[WEBP_FILTER_GRADIENT] = GradientFilter; } #else // !WEBP_USE_MIPS_DSP_R2 diff --git a/drivers/webp/dsp/filters_sse2.c b/drivers/webp/dsp/filters_sse2.c index bf93342eb7..67f77999e6 100644 --- a/drivers/webp/dsp/filters_sse2.c +++ b/drivers/webp/dsp/filters_sse2.c @@ -33,82 +33,39 @@ (void)height; // Silence unused warning. static void PredictLineTop(const uint8_t* src, const uint8_t* pred, - uint8_t* dst, int length, int inverse) { + uint8_t* dst, int length) { int i; const int max_pos = length & ~31; assert(length >= 0); - if (inverse) { - for (i = 0; i < max_pos; i += 32) { - const __m128i A0 = _mm_loadu_si128((const __m128i*)&src[i + 0]); - const __m128i A1 = _mm_loadu_si128((const __m128i*)&src[i + 16]); - const __m128i B0 = _mm_loadu_si128((const __m128i*)&pred[i + 0]); - const __m128i B1 = _mm_loadu_si128((const __m128i*)&pred[i + 16]); - const __m128i C0 = _mm_add_epi8(A0, B0); - const __m128i C1 = _mm_add_epi8(A1, B1); - _mm_storeu_si128((__m128i*)&dst[i + 0], C0); - _mm_storeu_si128((__m128i*)&dst[i + 16], C1); - } - for (; i < length; ++i) dst[i] = src[i] + pred[i]; - } else { - for (i = 0; i < max_pos; i += 32) { - const __m128i A0 = _mm_loadu_si128((const __m128i*)&src[i + 0]); - const __m128i A1 = _mm_loadu_si128((const __m128i*)&src[i + 16]); - const __m128i B0 = _mm_loadu_si128((const __m128i*)&pred[i + 0]); - const __m128i B1 = _mm_loadu_si128((const __m128i*)&pred[i + 16]); - const __m128i C0 = _mm_sub_epi8(A0, B0); - const __m128i C1 = _mm_sub_epi8(A1, B1); - _mm_storeu_si128((__m128i*)&dst[i + 0], C0); - _mm_storeu_si128((__m128i*)&dst[i + 16], C1); - } - for (; i < length; ++i) dst[i] = src[i] - pred[i]; + for (i = 0; i < max_pos; i += 32) { + const __m128i A0 = _mm_loadu_si128((const __m128i*)&src[i + 0]); + const __m128i A1 = _mm_loadu_si128((const __m128i*)&src[i + 16]); + const __m128i B0 = _mm_loadu_si128((const __m128i*)&pred[i + 0]); + const __m128i B1 = _mm_loadu_si128((const __m128i*)&pred[i + 16]); + const __m128i C0 = _mm_sub_epi8(A0, B0); + const __m128i C1 = _mm_sub_epi8(A1, B1); + _mm_storeu_si128((__m128i*)&dst[i + 0], C0); + _mm_storeu_si128((__m128i*)&dst[i + 16], C1); } + for (; i < length; ++i) dst[i] = src[i] - pred[i]; } // Special case for left-based prediction (when preds==dst-1 or preds==src-1). -static void PredictLineLeft(const uint8_t* src, uint8_t* dst, int length, - int inverse) { +static void PredictLineLeft(const uint8_t* src, uint8_t* dst, int length) { int i; - if (length <= 0) return; - if (inverse) { - const int max_pos = length & ~7; - __m128i last = _mm_set_epi32(0, 0, 0, dst[-1]); - for (i = 0; i < max_pos; i += 8) { - const __m128i A0 = _mm_loadl_epi64((const __m128i*)(src + i)); - const __m128i A1 = _mm_add_epi8(A0, last); - const __m128i A2 = _mm_slli_si128(A1, 1); - const __m128i A3 = _mm_add_epi8(A1, A2); - const __m128i A4 = _mm_slli_si128(A3, 2); - const __m128i A5 = _mm_add_epi8(A3, A4); - const __m128i A6 = _mm_slli_si128(A5, 4); - const __m128i A7 = _mm_add_epi8(A5, A6); - _mm_storel_epi64((__m128i*)(dst + i), A7); - last = _mm_srli_epi64(A7, 56); - } - for (; i < length; ++i) dst[i] = src[i] + dst[i - 1]; - } else { - const int max_pos = length & ~31; - for (i = 0; i < max_pos; i += 32) { - const __m128i A0 = _mm_loadu_si128((const __m128i*)(src + i + 0 )); - const __m128i B0 = _mm_loadu_si128((const __m128i*)(src + i + 0 - 1)); - const __m128i A1 = _mm_loadu_si128((const __m128i*)(src + i + 16 )); - const __m128i B1 = _mm_loadu_si128((const __m128i*)(src + i + 16 - 1)); - const __m128i C0 = _mm_sub_epi8(A0, B0); - const __m128i C1 = _mm_sub_epi8(A1, B1); - _mm_storeu_si128((__m128i*)(dst + i + 0), C0); - _mm_storeu_si128((__m128i*)(dst + i + 16), C1); - } - for (; i < length; ++i) dst[i] = src[i] - src[i - 1]; - } -} - -static void PredictLineC(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]; + const int max_pos = length & ~31; + assert(length >= 0); + for (i = 0; i < max_pos; i += 32) { + const __m128i A0 = _mm_loadu_si128((const __m128i*)(src + i + 0 )); + const __m128i B0 = _mm_loadu_si128((const __m128i*)(src + i + 0 - 1)); + const __m128i A1 = _mm_loadu_si128((const __m128i*)(src + i + 16 )); + const __m128i B1 = _mm_loadu_si128((const __m128i*)(src + i + 16 - 1)); + const __m128i C0 = _mm_sub_epi8(A0, B0); + const __m128i C1 = _mm_sub_epi8(A1, B1); + _mm_storeu_si128((__m128i*)(dst + i + 0), C0); + _mm_storeu_si128((__m128i*)(dst + i + 16), C1); } + for (; i < length; ++i) dst[i] = src[i] - src[i - 1]; } //------------------------------------------------------------------------------ @@ -117,21 +74,18 @@ static void PredictLineC(const uint8_t* src, const uint8_t* pred, static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in, int width, int height, int stride, int row, int num_rows, - int inverse, uint8_t* out) { - const uint8_t* preds; + uint8_t* out) { const size_t start_offset = row * stride; const int last_row = row + num_rows; SANITY_CHECK(in, out); in += start_offset; out += start_offset; - preds = inverse ? out : in; if (row == 0) { // Leftmost pixel is the same as input for topmost scanline. out[0] = in[0]; - PredictLineLeft(in + 1, out + 1, width - 1, inverse); + PredictLineLeft(in + 1, out + 1, width - 1); row = 1; - preds += stride; in += stride; out += stride; } @@ -139,10 +93,9 @@ static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in, // Filter line-by-line. while (row < last_row) { // Leftmost pixel is predicted from above. - PredictLineC(in, preds - stride, out, 1, inverse); - PredictLineLeft(in + 1, out + 1, width - 1, inverse); + out[0] = in[0] - in[-stride]; + PredictLineLeft(in + 1, out + 1, width - 1); ++row; - preds += stride; in += stride; out += stride; } @@ -153,34 +106,27 @@ static WEBP_INLINE void DoHorizontalFilter(const uint8_t* in, static WEBP_INLINE void DoVerticalFilter(const uint8_t* in, int width, int height, int stride, - int row, int num_rows, - int inverse, uint8_t* out) { - const uint8_t* preds; + int row, int num_rows, uint8_t* out) { const size_t start_offset = row * stride; const int last_row = row + num_rows; SANITY_CHECK(in, out); in += start_offset; out += start_offset; - preds = inverse ? out : in; if (row == 0) { // Very first top-left pixel is copied. out[0] = in[0]; // Rest of top scan-line is left-predicted. - PredictLineLeft(in + 1, out + 1, width - 1, inverse); + PredictLineLeft(in + 1, out + 1, width - 1); row = 1; in += stride; out += stride; - } else { - // We are starting from in-between. Make sure 'preds' points to prev row. - preds -= stride; } // Filter line-by-line. while (row < last_row) { - PredictLineTop(in, preds, out, width, inverse); + PredictLineTop(in, in - stride, out, width); ++row; - preds += stride; in += stride; out += stride; } @@ -219,49 +165,10 @@ static void GradientPredictDirect(const uint8_t* const row, } } -static void GradientPredictInverse(const uint8_t* const in, - const uint8_t* const top, - uint8_t* const row, int length) { - if (length > 0) { - int i; - const int max_pos = length & ~7; - const __m128i zero = _mm_setzero_si128(); - __m128i A = _mm_set_epi32(0, 0, 0, row[-1]); // left sample - for (i = 0; i < max_pos; i += 8) { - const __m128i tmp0 = _mm_loadl_epi64((const __m128i*)&top[i]); - const __m128i tmp1 = _mm_loadl_epi64((const __m128i*)&top[i - 1]); - const __m128i B = _mm_unpacklo_epi8(tmp0, zero); - const __m128i C = _mm_unpacklo_epi8(tmp1, zero); - const __m128i tmp2 = _mm_loadl_epi64((const __m128i*)&in[i]); - const __m128i D = _mm_unpacklo_epi8(tmp2, zero); // base input - const __m128i E = _mm_sub_epi16(B, C); // unclipped gradient basis B - C - __m128i out = zero; // accumulator for output - __m128i mask_hi = _mm_set_epi32(0, 0, 0, 0xff); - int k = 8; - while (1) { - const __m128i tmp3 = _mm_add_epi16(A, E); // delta = A + B - C - const __m128i tmp4 = _mm_min_epi16(tmp3, mask_hi); - const __m128i tmp5 = _mm_max_epi16(tmp4, zero); // clipped delta - const __m128i tmp6 = _mm_add_epi16(tmp5, D); // add to in[] values - A = _mm_and_si128(tmp6, mask_hi); // 1-complement clip - out = _mm_or_si128(out, A); // accumulate output - if (--k == 0) break; - A = _mm_slli_si128(A, 2); // rotate left sample - mask_hi = _mm_slli_si128(mask_hi, 2); // rotate mask - } - A = _mm_srli_si128(A, 14); // prepare left sample for next iteration - _mm_storel_epi64((__m128i*)&row[i], _mm_packus_epi16(out, zero)); - } - for (; i < length; ++i) { - row[i] = in[i] + GradientPredictorC(row[i - 1], top[i], top[i - 1]); - } - } -} - static WEBP_INLINE void DoGradientFilter(const uint8_t* in, int width, int height, int stride, int row, int num_rows, - int inverse, uint8_t* out) { + uint8_t* out) { const size_t start_offset = row * stride; const int last_row = row + num_rows; SANITY_CHECK(in, out); @@ -271,7 +178,7 @@ static WEBP_INLINE void DoGradientFilter(const uint8_t* in, // left prediction for top scan-line if (row == 0) { out[0] = in[0]; - PredictLineLeft(in + 1, out + 1, width - 1, inverse); + PredictLineLeft(in + 1, out + 1, width - 1); row = 1; in += stride; out += stride; @@ -279,13 +186,8 @@ static WEBP_INLINE void DoGradientFilter(const uint8_t* in, // Filter line-by-line. while (row < last_row) { - if (inverse) { - PredictLineC(in, out - stride, out, 1, inverse); // predict from above - GradientPredictInverse(in + 1, out + 1 - stride, out + 1, width - 1); - } else { - PredictLineC(in, in - stride, out, 1, inverse); - GradientPredictDirect(in + 1, in + 1 - stride, out + 1, width - 1); - } + out[0] = in[0] - in[-stride]; + GradientPredictDirect(in + 1, in + 1 - stride, out + 1, width - 1); ++row; in += stride; out += stride; @@ -298,36 +200,112 @@ static WEBP_INLINE void DoGradientFilter(const uint8_t* in, static void HorizontalFilter(const uint8_t* data, int width, int height, int stride, uint8_t* filtered_data) { - DoHorizontalFilter(data, width, height, stride, 0, height, 0, filtered_data); + DoHorizontalFilter(data, width, height, stride, 0, height, filtered_data); } static void VerticalFilter(const uint8_t* data, int width, int height, int stride, uint8_t* filtered_data) { - DoVerticalFilter(data, width, height, stride, 0, height, 0, filtered_data); + DoVerticalFilter(data, width, height, stride, 0, height, filtered_data); } - static void GradientFilter(const uint8_t* data, int width, int height, int stride, uint8_t* filtered_data) { - DoGradientFilter(data, width, height, stride, 0, height, 0, filtered_data); + DoGradientFilter(data, width, height, stride, 0, height, filtered_data); } - //------------------------------------------------------------------------------ +// Inverse transforms -static void VerticalUnfilter(int width, int height, int stride, int row, - int num_rows, uint8_t* data) { - DoVerticalFilter(data, width, height, stride, row, num_rows, 1, data); +static void HorizontalUnfilter(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + int i; + __m128i last; + out[0] = in[0] + (prev == NULL ? 0 : prev[0]); + if (width <= 1) return; + last = _mm_set_epi32(0, 0, 0, out[0]); + for (i = 1; i + 8 <= width; i += 8) { + const __m128i A0 = _mm_loadl_epi64((const __m128i*)(in + i)); + const __m128i A1 = _mm_add_epi8(A0, last); + const __m128i A2 = _mm_slli_si128(A1, 1); + const __m128i A3 = _mm_add_epi8(A1, A2); + const __m128i A4 = _mm_slli_si128(A3, 2); + const __m128i A5 = _mm_add_epi8(A3, A4); + const __m128i A6 = _mm_slli_si128(A5, 4); + const __m128i A7 = _mm_add_epi8(A5, A6); + _mm_storel_epi64((__m128i*)(out + i), A7); + last = _mm_srli_epi64(A7, 56); + } + for (; i < width; ++i) out[i] = in[i] + out[i - 1]; } -static void HorizontalUnfilter(int width, int height, int stride, int row, - int num_rows, uint8_t* data) { - DoHorizontalFilter(data, width, height, stride, row, num_rows, 1, data); +static void VerticalUnfilter(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter(NULL, in, out, width); + } else { + int i; + const int max_pos = width & ~31; + assert(width >= 0); + for (i = 0; i < max_pos; i += 32) { + const __m128i A0 = _mm_loadu_si128((const __m128i*)&in[i + 0]); + const __m128i A1 = _mm_loadu_si128((const __m128i*)&in[i + 16]); + const __m128i B0 = _mm_loadu_si128((const __m128i*)&prev[i + 0]); + const __m128i B1 = _mm_loadu_si128((const __m128i*)&prev[i + 16]); + const __m128i C0 = _mm_add_epi8(A0, B0); + const __m128i C1 = _mm_add_epi8(A1, B1); + _mm_storeu_si128((__m128i*)&out[i + 0], C0); + _mm_storeu_si128((__m128i*)&out[i + 16], C1); + } + for (; i < width; ++i) out[i] = in[i] + prev[i]; + } } -static void GradientUnfilter(int width, int height, int stride, int row, - int num_rows, uint8_t* data) { - DoGradientFilter(data, width, height, stride, row, num_rows, 1, data); +static void GradientPredictInverse(const uint8_t* const in, + const uint8_t* const top, + uint8_t* const row, int length) { + if (length > 0) { + int i; + const int max_pos = length & ~7; + const __m128i zero = _mm_setzero_si128(); + __m128i A = _mm_set_epi32(0, 0, 0, row[-1]); // left sample + for (i = 0; i < max_pos; i += 8) { + const __m128i tmp0 = _mm_loadl_epi64((const __m128i*)&top[i]); + const __m128i tmp1 = _mm_loadl_epi64((const __m128i*)&top[i - 1]); + const __m128i B = _mm_unpacklo_epi8(tmp0, zero); + const __m128i C = _mm_unpacklo_epi8(tmp1, zero); + const __m128i D = _mm_loadl_epi64((const __m128i*)&in[i]); // base input + const __m128i E = _mm_sub_epi16(B, C); // unclipped gradient basis B - C + __m128i out = zero; // accumulator for output + __m128i mask_hi = _mm_set_epi32(0, 0, 0, 0xff); + int k = 8; + while (1) { + const __m128i tmp3 = _mm_add_epi16(A, E); // delta = A + B - C + const __m128i tmp4 = _mm_packus_epi16(tmp3, zero); // saturate delta + const __m128i tmp5 = _mm_add_epi8(tmp4, D); // add to in[] + A = _mm_and_si128(tmp5, mask_hi); // 1-complement clip + out = _mm_or_si128(out, A); // accumulate output + if (--k == 0) break; + A = _mm_slli_si128(A, 1); // rotate left sample + mask_hi = _mm_slli_si128(mask_hi, 1); // rotate mask + A = _mm_unpacklo_epi8(A, zero); // convert 8b->16b + } + A = _mm_srli_si128(A, 7); // prepare left sample for next iteration + _mm_storel_epi64((__m128i*)&row[i], out); + } + for (; i < length; ++i) { + row[i] = in[i] + GradientPredictorC(row[i - 1], top[i], top[i - 1]); + } + } +} + +static void GradientUnfilter(const uint8_t* prev, const uint8_t* in, + uint8_t* out, int width) { + if (prev == NULL) { + HorizontalUnfilter(NULL, in, out, width); + } else { + out[0] = in[0] + prev[0]; // predict from above + GradientPredictInverse(in + 1, prev + 1, out + 1, width - 1); + } } //------------------------------------------------------------------------------ diff --git a/drivers/webp/dsp/lossless.c b/drivers/webp/dsp/lossless.c index 5702eb3b17..af913efccb 100644 --- a/drivers/webp/dsp/lossless.c +++ b/drivers/webp/dsp/lossless.c @@ -28,9 +28,7 @@ // In-place sum of each component with mod 256. static WEBP_INLINE void AddPixelsEq(uint32_t* a, uint32_t b) { - const uint32_t alpha_and_green = (*a & 0xff00ff00u) + (b & 0xff00ff00u); - const uint32_t red_and_blue = (*a & 0x00ff00ffu) + (b & 0x00ff00ffu); - *a = (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); + *a = VP8LAddPixels(*a, b); } static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) { @@ -490,7 +488,7 @@ static void CopyOrSwap(const uint32_t* src, int num_pixels, uint8_t* dst, #if !defined(WORDS_BIGENDIAN) #if !defined(WEBP_REFERENCE_IMPLEMENTATION) - *(uint32_t*)dst = BSwap32(argb); + WebPUint32ToMem(dst, BSwap32(argb)); #else // WEBP_REFERENCE_IMPLEMENTATION dst[0] = (argb >> 24) & 0xff; dst[1] = (argb >> 16) & 0xff; diff --git a/drivers/webp/dsp/lossless.h b/drivers/webp/dsp/lossless.h index 149c6a01d3..7709b4fe85 100644 --- a/drivers/webp/dsp/lossless.h +++ b/drivers/webp/dsp/lossless.h @@ -29,9 +29,6 @@ extern "C" { #include "../enc/delta_palettization.h" #endif // WEBP_EXPERIMENTAL_FEATURES -// Not a trivial literal symbol. -#define VP8L_NON_TRIVIAL_SYM (0xffffffff) - //------------------------------------------------------------------------------ // Decoding @@ -161,7 +158,8 @@ void VP8LCollectColorBlueTransforms_C(const uint32_t* argb, int stride, void VP8LResidualImage(int width, int height, int bits, int low_effort, uint32_t* const argb, uint32_t* const argb_scratch, - uint32_t* const image); + uint32_t* const image, int near_lossless, int exact, + int used_subtract_green); void VP8LColorSpaceTransform(int width, int height, int bits, int quality, uint32_t* const argb, uint32_t* image); @@ -175,8 +173,27 @@ static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size, return (size + (1 << sampling_bits) - 1) >> sampling_bits; } +// Converts near lossless quality into max number of bits shaved off. +static WEBP_INLINE int VP8LNearLosslessBits(int near_lossless_quality) { + // 100 -> 0 + // 80..99 -> 1 + // 60..79 -> 2 + // 40..59 -> 3 + // 20..39 -> 4 + // 0..19 -> 5 + return 5 - near_lossless_quality / 20; +} + // ----------------------------------------------------------------------------- // Faster logarithm for integers. Small values use a look-up table. + +// The threshold till approximate version of log_2 can be used. +// Practically, we can get rid of the call to log() as the two values match to +// very high degree (the ratio of these two is 0.99999x). +// Keeping a high threshold for now. +#define APPROX_LOG_WITH_CORRECTION_MAX 65536 +#define APPROX_LOG_MAX 4096 +#define LOG_2_RECIPROCAL 1.44269504088896338700465094007086 #define LOG_LOOKUP_IDX_MAX 256 extern const float kLog2Table[LOG_LOOKUP_IDX_MAX]; extern const float kSLog2Table[LOG_LOOKUP_IDX_MAX]; @@ -199,42 +216,55 @@ static WEBP_INLINE float VP8LFastSLog2(uint32_t v) { typedef double (*VP8LCostFunc)(const uint32_t* population, int length); typedef double (*VP8LCostCombinedFunc)(const uint32_t* X, const uint32_t* Y, int length); +typedef float (*VP8LCombinedShannonEntropyFunc)(const int X[256], + const int Y[256]); extern VP8LCostFunc VP8LExtraCost; extern VP8LCostCombinedFunc VP8LExtraCostCombined; +extern VP8LCombinedShannonEntropyFunc VP8LCombinedShannonEntropy; 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 struct { // small struct to hold bit entropy results + double entropy; // entropy + uint32_t sum; // sum of the population + int nonzeros; // number of non-zero elements in the population + uint32_t max_val; // maximum value in the population + uint32_t nonzero_code; // index of the last non-zero in the population +} VP8LBitEntropy; + +void VP8LBitEntropyInit(VP8LBitEntropy* const entropy); + +// Get the combined symbol bit entropy and Huffman cost stats for the +// distributions 'X' and 'Y'. Those results can then be refined according to +// codec specific heuristics. +void VP8LGetCombinedEntropyUnrefined(const uint32_t* const X, + const uint32_t* const Y, int length, + VP8LBitEntropy* const bit_entropy, + VP8LStreaks* const stats); +// Get the entropy for the distribution 'X'. +void VP8LGetEntropyUnrefined(const uint32_t* const X, int length, + VP8LBitEntropy* const bit_entropy, + VP8LStreaks* const stats); + +void VP8LBitsEntropyUnrefined(const uint32_t* const array, int n, + VP8LBitEntropy* const entropy); + +typedef void (*GetEntropyUnrefinedHelperFunc)(uint32_t val, int i, + uint32_t* const val_prev, + int* const i_prev, + VP8LBitEntropy* const bit_entropy, + VP8LStreaks* const stats); +// Internal function used by VP8LGet*EntropyUnrefined. +extern GetEntropyUnrefinedHelperFunc VP8LGetEntropyUnrefinedHelper; typedef void (*VP8LHistogramAddFunc)(const VP8LHistogram* const a, const VP8LHistogram* const b, @@ -244,6 +274,11 @@ extern VP8LHistogramAddFunc VP8LHistogramAdd; // ----------------------------------------------------------------------------- // PrefixEncode() +typedef int (*VP8LVectorMismatchFunc)(const uint32_t* const array1, + const uint32_t* const array2, int length); +// Returns the first index where array1 and array2 are different. +extern VP8LVectorMismatchFunc VP8LVectorMismatch; + 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. @@ -306,7 +341,14 @@ static WEBP_INLINE void VP8LPrefixEncode(int distance, int* const code, } } -// In-place difference of each component with mod 256. +// Sum of each component, mod 256. +static WEBP_INLINE uint32_t VP8LAddPixels(uint32_t a, uint32_t b) { + const uint32_t alpha_and_green = (a & 0xff00ff00u) + (b & 0xff00ff00u); + const uint32_t red_and_blue = (a & 0x00ff00ffu) + (b & 0x00ff00ffu); + return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); +} + +// Difference of each component, mod 256. static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { const uint32_t alpha_and_green = 0x00ff00ffu + (a & 0xff00ff00u) - (b & 0xff00ff00u); diff --git a/drivers/webp/dsp/lossless_enc.c b/drivers/webp/dsp/lossless_enc.c index b3036f5384..256f6f5f8b 100644 --- a/drivers/webp/dsp/lossless_enc.c +++ b/drivers/webp/dsp/lossless_enc.c @@ -24,6 +24,9 @@ #define MAX_DIFF_COST (1e30f) +static const int kPredLowEffort = 11; +static const uint32_t kMaskAlpha = 0xff000000; + // lookup table for small values of log2(int) const float kLog2Table[LOG_LOOKUP_IDX_MAX] = { 0.0000000000000000f, 0.0000000000000000f, @@ -326,13 +329,6 @@ const uint8_t kPrefixEncodeExtraBitsValue[PREFIX_LOOKUP_IDX_MAX] = { 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126 }; -// The threshold till approximate version of log_2 can be used. -// Practically, we can get rid of the call to log() as the two values match to -// very high degree (the ratio of these two is 0.99999x). -// Keeping a high threshold for now. -#define APPROX_LOG_WITH_CORRECTION_MAX 65536 -#define APPROX_LOG_MAX 4096 -#define LOG_2_RECIPROCAL 1.44269504088896338700465094007086 static float FastSLog2Slow(uint32_t v) { assert(v >= LOG_LOOKUP_IDX_MAX); if (v < APPROX_LOG_WITH_CORRECTION_MAX) { @@ -386,6 +382,7 @@ static float FastLog2Slow(uint32_t v) { // Mostly used to reduce code size + readability static WEBP_INLINE int GetMin(int a, int b) { return (a > b) ? b : a; } +static WEBP_INLINE int GetMax(int a, int b) { return (a < b) ? b : a; } //------------------------------------------------------------------------------ // Methods to calculate Entropy (Shannon). @@ -410,15 +407,15 @@ static float CombinedShannonEntropy(const int X[256], const int Y[256]) { int sumX = 0, sumXY = 0; for (i = 0; i < 256; ++i) { const int x = X[i]; - const int xy = x + Y[i]; if (x != 0) { + const int xy = x + Y[i]; sumX += x; retval -= VP8LFastSLog2(x); sumXY += xy; retval -= VP8LFastSLog2(xy); - } else if (xy != 0) { - sumXY += xy; - retval -= VP8LFastSLog2(xy); + } else if (Y[i] != 0) { + sumXY += Y[i]; + retval -= VP8LFastSLog2(Y[i]); } } retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY); @@ -432,225 +429,327 @@ static float PredictionCostSpatialHistogram(const int accumulated[4][256], for (i = 0; i < 4; ++i) { const double kExpValue = 0.94; retval += PredictionCostSpatial(tile[i], 1, kExpValue); - retval += CombinedShannonEntropy(tile[i], accumulated[i]); + retval += VP8LCombinedShannonEntropy(tile[i], accumulated[i]); } return (float)retval; } -static WEBP_INLINE double BitsEntropyRefine(int nonzeros, int sum, int max_val, - double retval) { - double mix; - if (nonzeros < 5) { - if (nonzeros <= 1) { - return 0; - } - // Two symbols, they will be 0 and 1 in a Huffman code. - // Let's mix in a bit of entropy to favor good clustering when - // distributions of these are combined. - if (nonzeros == 2) { - return 0.99 * sum + 0.01 * retval; - } - // No matter what the entropy says, we cannot be better than min_limit - // with Huffman coding. I am mixing a bit of entropy into the - // min_limit since it produces much better (~0.5 %) compression results - // perhaps because of better entropy clustering. - if (nonzeros == 3) { - mix = 0.95; - } else { - mix = 0.7; // nonzeros == 4. - } - } else { - mix = 0.627; - } - - { - double min_limit = 2 * sum - max_val; - min_limit = mix * min_limit + (1.0 - mix) * retval; - return (retval < min_limit) ? min_limit : retval; - } +void VP8LBitEntropyInit(VP8LBitEntropy* const entropy) { + entropy->entropy = 0.; + entropy->sum = 0; + entropy->nonzeros = 0; + entropy->max_val = 0; + entropy->nonzero_code = VP8L_NON_TRIVIAL_SYM; } -// Returns the entropy for the symbols in the input array. -// Also sets trivial_symbol to the code value, if the array has only one code -// value. Otherwise, set it to VP8L_NON_TRIVIAL_SYM. -double VP8LBitsEntropy(const uint32_t* const array, int n, - uint32_t* const trivial_symbol) { - double retval = 0.; - uint32_t sum = 0; - uint32_t nonzero_code = VP8L_NON_TRIVIAL_SYM; - int nonzeros = 0; - uint32_t max_val = 0; +void VP8LBitsEntropyUnrefined(const uint32_t* const array, int n, + VP8LBitEntropy* const entropy) { int i; + + VP8LBitEntropyInit(entropy); + for (i = 0; i < n; ++i) { if (array[i] != 0) { - sum += array[i]; - nonzero_code = i; - ++nonzeros; - retval -= VP8LFastSLog2(array[i]); - if (max_val < array[i]) { - max_val = array[i]; + entropy->sum += array[i]; + entropy->nonzero_code = i; + ++entropy->nonzeros; + entropy->entropy -= VP8LFastSLog2(array[i]); + if (entropy->max_val < array[i]) { + entropy->max_val = array[i]; } } } - retval += VP8LFastSLog2(sum); - if (trivial_symbol != NULL) { - *trivial_symbol = (nonzeros == 1) ? nonzero_code : VP8L_NON_TRIVIAL_SYM; - } - return BitsEntropyRefine(nonzeros, sum, max_val, retval); + entropy->entropy += VP8LFastSLog2(entropy->sum); } -static double InitialHuffmanCost(void) { - // Small bias because Huffman code length is typically not stored in - // full length. - static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3; - static const double kSmallBias = 9.1; - return kHuffmanCodeOfHuffmanCodeSize - kSmallBias; -} - -// Finalize the Huffman cost based on streak numbers and length type (<3 or >=3) -static double FinalHuffmanCost(const VP8LStreaks* const stats) { - double retval = InitialHuffmanCost(); - retval += stats->counts[0] * 1.5625 + 0.234375 * stats->streaks[0][1]; - retval += stats->counts[1] * 2.578125 + 0.703125 * stats->streaks[1][1]; - retval += 1.796875 * stats->streaks[0][0]; - retval += 3.28125 * stats->streaks[1][0]; - return retval; -} +static WEBP_INLINE void GetEntropyUnrefinedHelper( + uint32_t val, int i, uint32_t* const val_prev, int* const i_prev, + VP8LBitEntropy* const bit_entropy, VP8LStreaks* const stats) { + const int streak = i - *i_prev; + + // Gather info for the bit entropy. + if (*val_prev != 0) { + bit_entropy->sum += (*val_prev) * streak; + bit_entropy->nonzeros += streak; + bit_entropy->nonzero_code = *i_prev; + bit_entropy->entropy -= VP8LFastSLog2(*val_prev) * streak; + if (bit_entropy->max_val < *val_prev) { + bit_entropy->max_val = *val_prev; + } + } -// Trampolines -static double HuffmanCost(const uint32_t* const population, int length) { - const VP8LStreaks stats = VP8LHuffmanCostCount(population, length); - return FinalHuffmanCost(&stats); -} + // Gather info for the Huffman cost. + stats->counts[*val_prev != 0] += (streak > 3); + stats->streaks[*val_prev != 0][(streak > 3)] += streak; -// Aggregated costs -double VP8LPopulationCost(const uint32_t* const population, int length, - uint32_t* const trivial_sym) { - return - VP8LBitsEntropy(population, length, trivial_sym) + - HuffmanCost(population, length); + *val_prev = val; + *i_prev = i; } -double VP8LGetCombinedEntropy(const uint32_t* const X, - const uint32_t* const Y, int length) { - double bits_entropy_combined; - double huffman_cost_combined; +void VP8LGetEntropyUnrefined(const uint32_t* const X, int length, + VP8LBitEntropy* const bit_entropy, + VP8LStreaks* const stats) { int i; + int i_prev = 0; + uint32_t x_prev = X[0]; - // Bit entropy variables. - double retval = 0.; - int sum = 0; - int nonzeros = 0; - uint32_t max_val = 0; - int i_prev; - uint32_t xy; - - // Huffman cost variables. - int streak = 0; - uint32_t xy_prev; - VP8LStreaks stats; - memset(&stats, 0, sizeof(stats)); - - // Treat the first value for the huffman cost: this is keeping the original - // behavior, even though there is no first streak. - // TODO(vrabaud): study proper behavior - xy = X[0] + Y[0]; - ++stats.streaks[xy != 0][0]; - xy_prev = xy; - i_prev = 0; + memset(stats, 0, sizeof(*stats)); + VP8LBitEntropyInit(bit_entropy); for (i = 1; i < length; ++i) { - xy = X[i] + Y[i]; + const uint32_t x = X[i]; + if (x != x_prev) { + VP8LGetEntropyUnrefinedHelper(x, i, &x_prev, &i_prev, bit_entropy, stats); + } + } + VP8LGetEntropyUnrefinedHelper(0, i, &x_prev, &i_prev, bit_entropy, stats); - // Process data by streaks for both bit entropy and huffman cost. - if (xy != xy_prev) { - streak = i - i_prev; - - // Gather info for the bit entropy. - if (xy_prev != 0) { - sum += xy_prev * streak; - nonzeros += streak; - retval -= VP8LFastSLog2(xy_prev) * streak; - if (max_val < xy_prev) { - max_val = xy_prev; - } - } + bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum); +} + +void VP8LGetCombinedEntropyUnrefined(const uint32_t* const X, + const uint32_t* const Y, int length, + VP8LBitEntropy* const bit_entropy, + VP8LStreaks* const stats) { + int i = 1; + int i_prev = 0; + uint32_t xy_prev = X[0] + Y[0]; - // Gather info for the huffman cost. - stats.counts[xy != 0] += (streak > 3); - stats.streaks[xy != 0][(streak > 3)] += streak; + memset(stats, 0, sizeof(*stats)); + VP8LBitEntropyInit(bit_entropy); - xy_prev = xy; - i_prev = i; + for (i = 1; i < length; ++i) { + const uint32_t xy = X[i] + Y[i]; + if (xy != xy_prev) { + VP8LGetEntropyUnrefinedHelper(xy, i, &xy_prev, &i_prev, bit_entropy, + stats); } } + VP8LGetEntropyUnrefinedHelper(0, i, &xy_prev, &i_prev, bit_entropy, stats); - // Finish off the last streak for bit entropy. - if (xy != 0) { - streak = i - i_prev; - sum += xy * streak; - nonzeros += streak; - retval -= VP8LFastSLog2(xy) * streak; - if (max_val < xy) { - max_val = xy; - } + bit_entropy->entropy += VP8LFastSLog2(bit_entropy->sum); +} + +static WEBP_INLINE void UpdateHisto(int histo_argb[4][256], uint32_t argb) { + ++histo_argb[0][argb >> 24]; + ++histo_argb[1][(argb >> 16) & 0xff]; + ++histo_argb[2][(argb >> 8) & 0xff]; + ++histo_argb[3][argb & 0xff]; +} + +//------------------------------------------------------------------------------ + +static WEBP_INLINE uint32_t Predict(VP8LPredictorFunc pred_func, + int x, int y, + const uint32_t* current_row, + const uint32_t* upper_row) { + if (y == 0) { + return (x == 0) ? ARGB_BLACK : current_row[x - 1]; // Left. + } else if (x == 0) { + return upper_row[x]; // Top. + } else { + return pred_func(current_row[x - 1], upper_row + x); } - // Huffman cost is not updated with the last streak to keep original behavior. - // TODO(vrabaud): study proper behavior +} - retval += VP8LFastSLog2(sum); - bits_entropy_combined = BitsEntropyRefine(nonzeros, sum, max_val, retval); +static int MaxDiffBetweenPixels(uint32_t p1, uint32_t p2) { + const int diff_a = abs((int)(p1 >> 24) - (int)(p2 >> 24)); + const int diff_r = abs((int)((p1 >> 16) & 0xff) - (int)((p2 >> 16) & 0xff)); + const int diff_g = abs((int)((p1 >> 8) & 0xff) - (int)((p2 >> 8) & 0xff)); + const int diff_b = abs((int)(p1 & 0xff) - (int)(p2 & 0xff)); + return GetMax(GetMax(diff_a, diff_r), GetMax(diff_g, diff_b)); +} - huffman_cost_combined = FinalHuffmanCost(&stats); +static int MaxDiffAroundPixel(uint32_t current, uint32_t up, uint32_t down, + uint32_t left, uint32_t right) { + const int diff_up = MaxDiffBetweenPixels(current, up); + const int diff_down = MaxDiffBetweenPixels(current, down); + const int diff_left = MaxDiffBetweenPixels(current, left); + const int diff_right = MaxDiffBetweenPixels(current, right); + return GetMax(GetMax(diff_up, diff_down), GetMax(diff_left, diff_right)); +} - return bits_entropy_combined + huffman_cost_combined; +static uint32_t AddGreenToBlueAndRed(uint32_t argb) { + const uint32_t green = (argb >> 8) & 0xff; + uint32_t red_blue = argb & 0x00ff00ffu; + red_blue += (green << 16) | green; + red_blue &= 0x00ff00ffu; + return (argb & 0xff00ff00u) | red_blue; } -// Estimates the Entropy + Huffman + other block overhead size cost. -double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { - return - VP8LPopulationCost( - p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), NULL) - + VP8LPopulationCost(p->red_, NUM_LITERAL_CODES, NULL) - + VP8LPopulationCost(p->blue_, NUM_LITERAL_CODES, NULL) - + VP8LPopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL) - + VP8LPopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL) - + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES) - + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); +static void MaxDiffsForRow(int width, int stride, const uint32_t* const argb, + uint8_t* const max_diffs, int used_subtract_green) { + uint32_t current, up, down, left, right; + int x; + if (width <= 2) return; + current = argb[0]; + right = argb[1]; + if (used_subtract_green) { + current = AddGreenToBlueAndRed(current); + right = AddGreenToBlueAndRed(right); + } + // max_diffs[0] and max_diffs[width - 1] are never used. + for (x = 1; x < width - 1; ++x) { + up = argb[-stride + x]; + down = argb[stride + x]; + left = current; + current = right; + right = argb[x + 1]; + if (used_subtract_green) { + up = AddGreenToBlueAndRed(up); + down = AddGreenToBlueAndRed(down); + right = AddGreenToBlueAndRed(right); + } + max_diffs[x] = MaxDiffAroundPixel(current, up, down, left, right); + } } -double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) { - return - VP8LBitsEntropy(p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), - NULL) - + VP8LBitsEntropy(p->red_, NUM_LITERAL_CODES, NULL) - + VP8LBitsEntropy(p->blue_, NUM_LITERAL_CODES, NULL) - + VP8LBitsEntropy(p->alpha_, NUM_LITERAL_CODES, NULL) - + VP8LBitsEntropy(p->distance_, NUM_DISTANCE_CODES, NULL) - + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES) - + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); +// Quantize the difference between the actual component value and its prediction +// to a multiple of quantization, working modulo 256, taking care not to cross +// a boundary (inclusive upper limit). +static uint8_t NearLosslessComponent(uint8_t value, uint8_t predict, + uint8_t boundary, int quantization) { + const int residual = (value - predict) & 0xff; + const int boundary_residual = (boundary - predict) & 0xff; + const int lower = residual & ~(quantization - 1); + const int upper = lower + quantization; + // Resolve ties towards a value closer to the prediction (i.e. towards lower + // if value comes after prediction and towards upper otherwise). + const int bias = ((boundary - value) & 0xff) < boundary_residual; + if (residual - lower < upper - residual + bias) { + // lower is closer to residual than upper. + if (residual > boundary_residual && lower <= boundary_residual) { + // Halve quantization step to avoid crossing boundary. This midpoint is + // on the same side of boundary as residual because midpoint >= residual + // (since lower is closer than upper) and residual is above the boundary. + return lower + (quantization >> 1); + } + return lower; + } else { + // upper is closer to residual than lower. + if (residual <= boundary_residual && upper > boundary_residual) { + // Halve quantization step to avoid crossing boundary. This midpoint is + // on the same side of boundary as residual because midpoint <= residual + // (since upper is closer than lower) and residual is below the boundary. + return lower + (quantization >> 1); + } + return upper & 0xff; + } } -static WEBP_INLINE void UpdateHisto(int histo_argb[4][256], uint32_t argb) { - ++histo_argb[0][argb >> 24]; - ++histo_argb[1][(argb >> 16) & 0xff]; - ++histo_argb[2][(argb >> 8) & 0xff]; - ++histo_argb[3][argb & 0xff]; +// Quantize every component of the difference between the actual pixel value and +// its prediction to a multiple of a quantization (a power of 2, not larger than +// max_quantization which is a power of 2, smaller than max_diff). Take care if +// value and predict have undergone subtract green, which means that red and +// blue are represented as offsets from green. +static uint32_t NearLossless(uint32_t value, uint32_t predict, + int max_quantization, int max_diff, + int used_subtract_green) { + int quantization; + uint8_t new_green = 0; + uint8_t green_diff = 0; + uint8_t a, r, g, b; + if (max_diff <= 2) { + return VP8LSubPixels(value, predict); + } + quantization = max_quantization; + while (quantization >= max_diff) { + quantization >>= 1; + } + if ((value >> 24) == 0 || (value >> 24) == 0xff) { + // Preserve transparency of fully transparent or fully opaque pixels. + a = ((value >> 24) - (predict >> 24)) & 0xff; + } else { + a = NearLosslessComponent(value >> 24, predict >> 24, 0xff, quantization); + } + g = NearLosslessComponent((value >> 8) & 0xff, (predict >> 8) & 0xff, 0xff, + quantization); + if (used_subtract_green) { + // The green offset will be added to red and blue components during decoding + // to obtain the actual red and blue values. + new_green = ((predict >> 8) + g) & 0xff; + // The amount by which green has been adjusted during quantization. It is + // subtracted from red and blue for compensation, to avoid accumulating two + // quantization errors in them. + green_diff = (new_green - (value >> 8)) & 0xff; + } + r = NearLosslessComponent(((value >> 16) - green_diff) & 0xff, + (predict >> 16) & 0xff, 0xff - new_green, + quantization); + b = NearLosslessComponent((value - green_diff) & 0xff, predict & 0xff, + 0xff - new_green, quantization); + return ((uint32_t)a << 24) | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; } -//------------------------------------------------------------------------------ +// Returns the difference between the pixel and its prediction. In case of a +// lossy encoding, updates the source image to avoid propagating the deviation +// further to pixels which depend on the current pixel for their predictions. +static WEBP_INLINE uint32_t GetResidual(int width, int height, + uint32_t* const upper_row, + uint32_t* const current_row, + const uint8_t* const max_diffs, + int mode, VP8LPredictorFunc pred_func, + int x, int y, int max_quantization, + int exact, int used_subtract_green) { + const uint32_t predict = Predict(pred_func, x, y, current_row, upper_row); + uint32_t residual; + if (max_quantization == 1 || mode == 0 || y == 0 || y == height - 1 || + x == 0 || x == width - 1) { + residual = VP8LSubPixels(current_row[x], predict); + } else { + residual = NearLossless(current_row[x], predict, max_quantization, + max_diffs[x], used_subtract_green); + // Update the source image. + current_row[x] = VP8LAddPixels(predict, residual); + // x is never 0 here so we do not need to update upper_row like below. + } + if (!exact && (current_row[x] & kMaskAlpha) == 0) { + // If alpha is 0, cleanup RGB. We can choose the RGB values of the residual + // for best compression. The prediction of alpha itself can be non-zero and + // must be kept though. We choose RGB of the residual to be 0. + residual &= kMaskAlpha; + // Update the source image. + current_row[x] = predict & ~kMaskAlpha; + // The prediction for the rightmost pixel in a row uses the leftmost pixel + // in that row as its top-right context pixel. Hence if we change the + // leftmost pixel of current_row, the corresponding change must be applied + // to upper_row as well where top-right context is being read from. + if (x == 0 && y != 0) upper_row[width] = current_row[0]; + } + return residual; +} // Returns best predictor and updates the accumulated histogram. +// If max_quantization > 1, assumes that near lossless processing will be +// applied, quantizing residuals to multiples of quantization levels up to +// max_quantization (the actual quantization level depends on smoothness near +// the given pixel). static int GetBestPredictorForTile(int width, int height, int tile_x, int tile_y, int bits, int accumulated[4][256], - const uint32_t* const argb_scratch) { + uint32_t* const argb_scratch, + const uint32_t* const argb, + int max_quantization, + int exact, int used_subtract_green) { const int kNumPredModes = 14; - const int col_start = tile_x << bits; - const int row_start = tile_y << bits; + const int start_x = tile_x << bits; + const int start_y = tile_y << bits; const int tile_size = 1 << bits; - const int max_y = GetMin(tile_size, height - row_start); - const int max_x = GetMin(tile_size, width - col_start); + const int max_y = GetMin(tile_size, height - start_y); + const int max_x = GetMin(tile_size, width - start_x); + // Whether there exist columns just outside the tile. + const int have_left = (start_x > 0); + const int have_right = (max_x < width - start_x); + // Position and size of the strip covering the tile and adjacent columns if + // they exist. + const int context_start_x = start_x - have_left; + const int context_width = max_x + have_left + have_right; + // The width of upper_row and current_row is one pixel larger than image width + // to allow the top right pixel to point to the leftmost pixel of the next row + // when at the right edge. + uint32_t* upper_row = argb_scratch; + uint32_t* current_row = upper_row + width + 1; + uint8_t* const max_diffs = (uint8_t*)(current_row + width + 1); float best_diff = MAX_DIFF_COST; int best_mode = 0; int mode; @@ -659,30 +758,46 @@ static int GetBestPredictorForTile(int width, int height, // Need pointers to be able to swap arrays. int (*histo_argb)[256] = histo_stack_1; int (*best_histo)[256] = histo_stack_2; - int i, j; + for (mode = 0; mode < kNumPredModes; ++mode) { - const uint32_t* current_row = argb_scratch; const VP8LPredictorFunc pred_func = VP8LPredictors[mode]; float cur_diff; - int y; + int relative_y; memset(histo_argb, 0, sizeof(histo_stack_1)); - for (y = 0; y < max_y; ++y) { - int x; - const int row = row_start + y; - const uint32_t* const upper_row = current_row; - current_row = upper_row + width; - for (x = 0; x < max_x; ++x) { - const int col = col_start + x; - uint32_t predict; - if (row == 0) { - predict = (col == 0) ? ARGB_BLACK : current_row[col - 1]; // Left. - } else if (col == 0) { - predict = upper_row[col]; // Top. - } else { - predict = pred_func(current_row[col - 1], upper_row + col); - } - UpdateHisto(histo_argb, VP8LSubPixels(current_row[col], predict)); + if (start_y > 0) { + // Read the row above the tile which will become the first upper_row. + // Include a pixel to the left if it exists; include a pixel to the right + // in all cases (wrapping to the leftmost pixel of the next row if it does + // not exist). + memcpy(current_row + context_start_x, + argb + (start_y - 1) * width + context_start_x, + sizeof(*argb) * (max_x + have_left + 1)); + } + for (relative_y = 0; relative_y < max_y; ++relative_y) { + const int y = start_y + relative_y; + int relative_x; + uint32_t* tmp = upper_row; + upper_row = current_row; + current_row = tmp; + // Read current_row. Include a pixel to the left if it exists; include a + // pixel to the right in all cases except at the bottom right corner of + // the image (wrapping to the leftmost pixel of the next row if it does + // not exist in the current row). + memcpy(current_row + context_start_x, + argb + y * width + context_start_x, + sizeof(*argb) * (max_x + have_left + (y + 1 < height))); + if (max_quantization > 1 && y >= 1 && y + 1 < height) { + MaxDiffsForRow(context_width, width, argb + y * width + context_start_x, + max_diffs + context_start_x, used_subtract_green); + } + + for (relative_x = 0; relative_x < max_x; ++relative_x) { + const int x = start_x + relative_x; + UpdateHisto(histo_argb, + GetResidual(width, height, upper_row, current_row, + max_diffs, mode, pred_func, x, y, + max_quantization, exact, used_subtract_green)); } } cur_diff = PredictionCostSpatialHistogram( @@ -705,80 +820,103 @@ static int GetBestPredictorForTile(int width, int height, return best_mode; } +// Converts pixels of the image to residuals with respect to predictions. +// If max_quantization > 1, applies near lossless processing, quantizing +// residuals to multiples of quantization levels up to max_quantization +// (the actual quantization level depends on smoothness near the given pixel). static void CopyImageWithPrediction(int width, int height, int bits, uint32_t* const modes, uint32_t* const argb_scratch, - uint32_t* const argb) { + uint32_t* const argb, + int low_effort, int max_quantization, + int exact, int used_subtract_green) { const int tiles_per_row = VP8LSubSampleSize(width, bits); const int mask = (1 << bits) - 1; - // The row size is one pixel longer to allow the top right pixel to point to - // the leftmost pixel of the next row when at the right edge. - uint32_t* current_row = argb_scratch; - uint32_t* upper_row = argb_scratch + width + 1; + // The width of upper_row and current_row is one pixel larger than image width + // to allow the top right pixel to point to the leftmost pixel of the next row + // when at the right edge. + uint32_t* upper_row = argb_scratch; + uint32_t* current_row = upper_row + width + 1; + uint8_t* current_max_diffs = (uint8_t*)(current_row + width + 1); + uint8_t* lower_max_diffs = current_max_diffs + width; int y; - VP8LPredictorFunc pred_func = 0; + int mode = 0; + VP8LPredictorFunc pred_func = NULL; for (y = 0; y < height; ++y) { int x; - uint32_t* tmp = upper_row; + uint32_t* const tmp32 = upper_row; upper_row = current_row; - current_row = tmp; - memcpy(current_row, argb + y * width, sizeof(*current_row) * width); - current_row[width] = (y + 1 < height) ? argb[(y + 1) * width] : ARGB_BLACK; - for (x = 0; x < width; ++x) { - uint32_t predict; - if ((x & mask) == 0) { - const int mode = - (modes[(y >> bits) * tiles_per_row + (x >> bits)] >> 8) & 0xff; - pred_func = VP8LPredictors[mode]; + current_row = tmp32; + memcpy(current_row, argb + y * width, + sizeof(*argb) * (width + (y + 1 < height))); + + if (low_effort) { + for (x = 0; x < width; ++x) { + const uint32_t predict = Predict(VP8LPredictors[kPredLowEffort], x, y, + current_row, upper_row); + argb[y * width + x] = VP8LSubPixels(current_row[x], predict); + } + } else { + if (max_quantization > 1) { + // Compute max_diffs for the lower row now, because that needs the + // contents of argb for the current row, which we will overwrite with + // residuals before proceeding with the next row. + uint8_t* const tmp8 = current_max_diffs; + current_max_diffs = lower_max_diffs; + lower_max_diffs = tmp8; + if (y + 2 < height) { + MaxDiffsForRow(width, width, argb + (y + 1) * width, lower_max_diffs, + used_subtract_green); + } } - if (y == 0) { - predict = (x == 0) ? ARGB_BLACK : current_row[x - 1]; // Left. - } else if (x == 0) { - predict = upper_row[x]; // Top. - } else { - predict = pred_func(current_row[x - 1], upper_row + x); + for (x = 0; x < width; ++x) { + if ((x & mask) == 0) { + mode = (modes[(y >> bits) * tiles_per_row + (x >> bits)] >> 8) & 0xff; + pred_func = VP8LPredictors[mode]; + } + argb[y * width + x] = GetResidual( + width, height, upper_row, current_row, current_max_diffs, mode, + pred_func, x, y, max_quantization, exact, used_subtract_green); } - argb[y * width + x] = VP8LSubPixels(current_row[x], predict); } } } +// Finds the best predictor for each tile, and converts the image to residuals +// with respect to predictions. If near_lossless_quality < 100, applies +// near lossless processing, shaving off more bits of residuals for lower +// qualities. void VP8LResidualImage(int width, int height, int bits, int low_effort, uint32_t* const argb, uint32_t* const argb_scratch, - uint32_t* const image) { - const int max_tile_size = 1 << bits; + uint32_t* const image, int near_lossless_quality, + int exact, int used_subtract_green) { const int tiles_per_row = VP8LSubSampleSize(width, bits); const int tiles_per_col = VP8LSubSampleSize(height, bits); - const int kPredLowEffort = 11; - uint32_t* const upper_row = argb_scratch; - uint32_t* const current_tile_rows = argb_scratch + width; int tile_y; int histo[4][256]; - if (!low_effort) memset(histo, 0, sizeof(histo)); - for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) { - const int tile_y_offset = tile_y * max_tile_size; - const int this_tile_height = - (tile_y < tiles_per_col - 1) ? max_tile_size : height - tile_y_offset; - int tile_x; - if (tile_y > 0) { - memcpy(upper_row, current_tile_rows + (max_tile_size - 1) * width, - width * sizeof(*upper_row)); + const int max_quantization = 1 << VP8LNearLosslessBits(near_lossless_quality); + if (low_effort) { + int i; + for (i = 0; i < tiles_per_row * tiles_per_col; ++i) { + image[i] = ARGB_BLACK | (kPredLowEffort << 8); } - memcpy(current_tile_rows, &argb[tile_y_offset * width], - this_tile_height * width * sizeof(*current_tile_rows)); - for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) { - const int pred = - low_effort ? kPredLowEffort : - GetBestPredictorForTile(width, height, - tile_x, tile_y, bits, - (int (*)[256])histo, - argb_scratch); - image[tile_y * tiles_per_row + tile_x] = 0xff000000u | (pred << 8); + } else { + memset(histo, 0, sizeof(histo)); + for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) { + int tile_x; + for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) { + const int pred = GetBestPredictorForTile(width, height, tile_x, tile_y, + bits, histo, argb_scratch, argb, max_quantization, exact, + used_subtract_green); + image[tile_y * tiles_per_row + tile_x] = ARGB_BLACK | (pred << 8); + } } } - CopyImageWithPrediction(width, height, bits, image, argb_scratch, argb); + CopyImageWithPrediction(width, height, bits, image, argb_scratch, argb, + low_effort, max_quantization, exact, + used_subtract_green); } void VP8LSubtractGreenFromBlueAndRed_C(uint32_t* argb_data, int num_pixels) { @@ -860,7 +998,7 @@ static float PredictionCostCrossColor(const int accumulated[256], // Favor low entropy, locally and globally. // Favor small absolute values for PredictionCostSpatial static const double kExpValue = 2.4; - return CombinedShannonEntropy(counts, accumulated) + + return VP8LCombinedShannonEntropy(counts, accumulated) + PredictionCostSpatial(counts, 3, kExpValue); } @@ -1124,6 +1262,17 @@ void VP8LColorSpaceTransform(int width, int height, int bits, int quality, } //------------------------------------------------------------------------------ + +static int VectorMismatch(const uint32_t* const array1, + const uint32_t* const array2, int length) { + int match_len = 0; + + while (match_len < length && array1[match_len] == array2[match_len]) { + ++match_len; + } + return match_len; +} + // Bundles multiple (1, 2, 4 or 8) pixels into a single pixel. void VP8LBundleColorMap(const uint8_t* const row, int width, int xbits, uint32_t* const dst) { @@ -1165,27 +1314,6 @@ static double ExtraCostCombined(const uint32_t* X, const uint32_t* Y, return cost; } -// Returns the various RLE counts -static VP8LStreaks HuffmanCostCount(const uint32_t* population, int length) { - int i; - int streak = 0; - VP8LStreaks stats; - memset(&stats, 0, sizeof(stats)); - for (i = 0; i < length - 1; ++i) { - ++streak; - if (population[i] == population[i + 1]) { - continue; - } - stats.counts[population[i] != 0] += (streak > 3); - stats.streaks[population[i] != 0][(streak > 3)] += streak; - streak = 0; - } - ++streak; - stats.counts[population[i] != 0] += (streak > 3); - stats.streaks[population[i] != 0][(streak > 3)] += streak; - return stats; -} - //------------------------------------------------------------------------------ static void HistogramAdd(const VP8LHistogram* const a, @@ -1235,11 +1363,14 @@ VP8LFastLog2SlowFunc VP8LFastSLog2Slow; VP8LCostFunc VP8LExtraCost; VP8LCostCombinedFunc VP8LExtraCostCombined; +VP8LCombinedShannonEntropyFunc VP8LCombinedShannonEntropy; -VP8LCostCountFunc VP8LHuffmanCostCount; +GetEntropyUnrefinedHelperFunc VP8LGetEntropyUnrefinedHelper; VP8LHistogramAddFunc VP8LHistogramAdd; +VP8LVectorMismatchFunc VP8LVectorMismatch; + extern void VP8LEncDspInitSSE2(void); extern void VP8LEncDspInitSSE41(void); extern void VP8LEncDspInitNEON(void); @@ -1266,11 +1397,14 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInit(void) { VP8LExtraCost = ExtraCost; VP8LExtraCostCombined = ExtraCostCombined; + VP8LCombinedShannonEntropy = CombinedShannonEntropy; - VP8LHuffmanCostCount = HuffmanCostCount; + VP8LGetEntropyUnrefinedHelper = GetEntropyUnrefinedHelper; VP8LHistogramAdd = HistogramAdd; + VP8LVectorMismatch = VectorMismatch; + // If defined, use CPUInfo() to overwrite some pointers with faster versions. if (VP8GetCPUInfo != NULL) { #if defined(WEBP_USE_SSE2) diff --git a/drivers/webp/dsp/lossless_enc_mips32.c b/drivers/webp/dsp/lossless_enc_mips32.c index 0468a5aac2..49c666d4fd 100644 --- a/drivers/webp/dsp/lossless_enc_mips32.c +++ b/drivers/webp/dsp/lossless_enc_mips32.c @@ -22,10 +22,6 @@ #include <stdlib.h> #include <string.h> -#define APPROX_LOG_WITH_CORRECTION_MAX 65536 -#define APPROX_LOG_MAX 4096 -#define LOG_2_RECIPROCAL 1.44269504088896338700465094007086 - static float FastSLog2Slow(uint32_t v) { assert(v >= LOG_LOOKUP_IDX_MAX); if (v < APPROX_LOG_WITH_CORRECTION_MAX) { @@ -217,51 +213,31 @@ static double ExtraCostCombined(const uint32_t* const X, ); // Returns the various RLE counts -static VP8LStreaks HuffmanCostCount(const uint32_t* population, int length) { - int i; - int streak = 0; - VP8LStreaks stats; - int* const pstreaks = &stats.streaks[0][0]; - int* const pcnts = &stats.counts[0]; +static WEBP_INLINE void GetEntropyUnrefinedHelper( + uint32_t val, int i, uint32_t* const val_prev, int* const i_prev, + VP8LBitEntropy* const bit_entropy, VP8LStreaks* const stats) { + int* const pstreaks = &stats->streaks[0][0]; + int* const pcnts = &stats->counts[0]; int temp0, temp1, temp2, temp3; - memset(&stats, 0, sizeof(stats)); - for (i = 0; i < length - 1; ++i) { - ++streak; - if (population[i] == population[i + 1]) { - continue; + const int streak = i - *i_prev; + + // Gather info for the bit entropy. + if (*val_prev != 0) { + bit_entropy->sum += (*val_prev) * streak; + bit_entropy->nonzeros += streak; + bit_entropy->nonzero_code = *i_prev; + bit_entropy->entropy -= VP8LFastSLog2(*val_prev) * streak; + if (bit_entropy->max_val < *val_prev) { + bit_entropy->max_val = *val_prev; } - temp0 = (population[i] != 0); - HUFFMAN_COST_PASS - streak = 0; } - ++streak; - temp0 = (population[i] != 0); - HUFFMAN_COST_PASS - return stats; -} + // Gather info for the Huffman cost. + temp0 = (*val_prev != 0); + HUFFMAN_COST_PASS -static VP8LStreaks HuffmanCostCombinedCount(const uint32_t* X, - const uint32_t* Y, int length) { - int i; - int streak = 0; - uint32_t xy_prev = 0xffffffff; - VP8LStreaks stats; - int* const pstreaks = &stats.streaks[0][0]; - int* const pcnts = &stats.counts[0]; - int temp0, temp1, temp2, temp3; - memset(&stats, 0, sizeof(stats)); - for (i = 0; i < length; ++i) { - const uint32_t xy = X[i] + Y[i]; - ++streak; - if (xy != xy_prev) { - temp0 = (xy != 0); - HUFFMAN_COST_PASS - streak = 0; - xy_prev = xy; - } - } - return stats; + *val_prev = val; + *i_prev = i; } #define ASM_START \ @@ -399,14 +375,7 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitMIPS32(void) { VP8LFastLog2Slow = FastLog2Slow; VP8LExtraCost = ExtraCost; VP8LExtraCostCombined = ExtraCostCombined; - VP8LHuffmanCostCount = HuffmanCostCount; -// TODO(mips team): rewrite VP8LGetCombinedEntropy (which used to use -// HuffmanCostCombinedCount) with MIPS optimizations -#if 0 - VP8LHuffmanCostCombinedCount = HuffmanCostCombinedCount; -#else - (void)HuffmanCostCombinedCount; -#endif + VP8LGetEntropyUnrefinedHelper = GetEntropyUnrefinedHelper; VP8LHistogramAdd = HistogramAdd; } diff --git a/drivers/webp/dsp/lossless_enc_sse2.c b/drivers/webp/dsp/lossless_enc_sse2.c index 1374b3ef64..7c894e7ca4 100644 --- a/drivers/webp/dsp/lossless_enc_sse2.c +++ b/drivers/webp/dsp/lossless_enc_sse2.c @@ -251,6 +251,131 @@ static void HistogramAdd(const VP8LHistogram* const a, } //------------------------------------------------------------------------------ +// Entropy + +// Checks whether the X or Y contribution is worth computing and adding. +// Used in loop unrolling. +#define ANALYZE_X_OR_Y(x_or_y, j) \ + do { \ + if (x_or_y[i + j] != 0) retval -= VP8LFastSLog2(x_or_y[i + j]); \ + } while (0) + +// Checks whether the X + Y contribution is worth computing and adding. +// Used in loop unrolling. +#define ANALYZE_XY(j) \ + do { \ + if (tmp[j] != 0) { \ + retval -= VP8LFastSLog2(tmp[j]); \ + ANALYZE_X_OR_Y(X, j); \ + } \ + } while (0) + +static float CombinedShannonEntropy(const int X[256], const int Y[256]) { + int i; + double retval = 0.; + int sumX, sumXY; + int32_t tmp[4]; + __m128i zero = _mm_setzero_si128(); + // Sums up X + Y, 4 ints at a time (and will merge it at the end for sumXY). + __m128i sumXY_128 = zero; + __m128i sumX_128 = zero; + + for (i = 0; i < 256; i += 4) { + const __m128i x = _mm_loadu_si128((const __m128i*)(X + i)); + const __m128i y = _mm_loadu_si128((const __m128i*)(Y + i)); + + // Check if any X is non-zero: this actually provides a speedup as X is + // usually sparse. + if (_mm_movemask_epi8(_mm_cmpeq_epi32(x, zero)) != 0xFFFF) { + const __m128i xy_128 = _mm_add_epi32(x, y); + sumXY_128 = _mm_add_epi32(sumXY_128, xy_128); + + sumX_128 = _mm_add_epi32(sumX_128, x); + + // Analyze the different X + Y. + _mm_storeu_si128((__m128i*)tmp, xy_128); + + ANALYZE_XY(0); + ANALYZE_XY(1); + ANALYZE_XY(2); + ANALYZE_XY(3); + } else { + // X is fully 0, so only deal with Y. + sumXY_128 = _mm_add_epi32(sumXY_128, y); + + ANALYZE_X_OR_Y(Y, 0); + ANALYZE_X_OR_Y(Y, 1); + ANALYZE_X_OR_Y(Y, 2); + ANALYZE_X_OR_Y(Y, 3); + } + } + + // Sum up sumX_128 to get sumX. + _mm_storeu_si128((__m128i*)tmp, sumX_128); + sumX = tmp[3] + tmp[2] + tmp[1] + tmp[0]; + + // Sum up sumXY_128 to get sumXY. + _mm_storeu_si128((__m128i*)tmp, sumXY_128); + sumXY = tmp[3] + tmp[2] + tmp[1] + tmp[0]; + + retval += VP8LFastSLog2(sumX) + VP8LFastSLog2(sumXY); + return (float)retval; +} +#undef ANALYZE_X_OR_Y +#undef ANALYZE_XY + +//------------------------------------------------------------------------------ + +static int VectorMismatch(const uint32_t* const array1, + const uint32_t* const array2, int length) { + int match_len; + + if (length >= 12) { + __m128i A0 = _mm_loadu_si128((const __m128i*)&array1[0]); + __m128i A1 = _mm_loadu_si128((const __m128i*)&array2[0]); + match_len = 0; + do { + // Loop unrolling and early load both provide a speedup of 10% for the + // current function. Also, max_limit can be MAX_LENGTH=4096 at most. + const __m128i cmpA = _mm_cmpeq_epi32(A0, A1); + const __m128i B0 = + _mm_loadu_si128((const __m128i*)&array1[match_len + 4]); + const __m128i B1 = + _mm_loadu_si128((const __m128i*)&array2[match_len + 4]); + if (_mm_movemask_epi8(cmpA) != 0xffff) break; + match_len += 4; + + { + const __m128i cmpB = _mm_cmpeq_epi32(B0, B1); + A0 = _mm_loadu_si128((const __m128i*)&array1[match_len + 4]); + A1 = _mm_loadu_si128((const __m128i*)&array2[match_len + 4]); + if (_mm_movemask_epi8(cmpB) != 0xffff) break; + match_len += 4; + } + } while (match_len + 12 < length); + } else { + match_len = 0; + // Unroll the potential first two loops. + if (length >= 4 && + _mm_movemask_epi8(_mm_cmpeq_epi32( + _mm_loadu_si128((const __m128i*)&array1[0]), + _mm_loadu_si128((const __m128i*)&array2[0]))) == 0xffff) { + match_len = 4; + if (length >= 8 && + _mm_movemask_epi8(_mm_cmpeq_epi32( + _mm_loadu_si128((const __m128i*)&array1[4]), + _mm_loadu_si128((const __m128i*)&array2[4]))) == 0xffff) + match_len = 8; + } + } + + while (match_len < length && array1[match_len] == array2[match_len]) { + ++match_len; + } + return match_len; +} + +//------------------------------------------------------------------------------ // Entry point extern void VP8LEncDspInitSSE2(void); @@ -261,6 +386,8 @@ WEBP_TSAN_IGNORE_FUNCTION void VP8LEncDspInitSSE2(void) { VP8LCollectColorBlueTransforms = CollectColorBlueTransforms; VP8LCollectColorRedTransforms = CollectColorRedTransforms; VP8LHistogramAdd = HistogramAdd; + VP8LCombinedShannonEntropy = CombinedShannonEntropy; + VP8LVectorMismatch = VectorMismatch; } #else // !WEBP_USE_SSE2 diff --git a/drivers/webp/dsp/msa_macro.h b/drivers/webp/dsp/msa_macro.h new file mode 100644 index 0000000000..5c707f476a --- /dev/null +++ b/drivers/webp/dsp/msa_macro.h @@ -0,0 +1,555 @@ +// Copyright 2016 Google Inc. All Rights Reserved. +// +// 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. +// ----------------------------------------------------------------------------- +// +// MSA common macros +// +// Author(s): Prashant Patil (prashant.patil@imgtec.com) + +#ifndef WEBP_DSP_MSA_MACRO_H_ +#define WEBP_DSP_MSA_MACRO_H_ + +#include <stdint.h> +#include <msa.h> + +#if defined(__clang__) + #define CLANG_BUILD +#endif + +#ifdef CLANG_BUILD + #define ADDVI_H(a, b) __msa_addvi_h((v8i16)a, b) + #define SRAI_H(a, b) __msa_srai_h((v8i16)a, b) + #define SRAI_W(a, b) __msa_srai_w((v4i32)a, b) +#else + #define ADDVI_H(a, b) (a + b) + #define SRAI_H(a, b) (a >> b) + #define SRAI_W(a, b) (a >> b) +#endif + +#define LD_B(RTYPE, psrc) *((RTYPE*)(psrc)) +#define LD_UB(...) LD_B(v16u8, __VA_ARGS__) +#define LD_SB(...) LD_B(v16i8, __VA_ARGS__) + +#define LD_H(RTYPE, psrc) *((RTYPE*)(psrc)) +#define LD_UH(...) LD_H(v8u16, __VA_ARGS__) +#define LD_SH(...) LD_H(v8i16, __VA_ARGS__) + +#define LD_W(RTYPE, psrc) *((RTYPE*)(psrc)) +#define LD_UW(...) LD_W(v4u32, __VA_ARGS__) +#define LD_SW(...) LD_W(v4i32, __VA_ARGS__) + +#define ST_B(RTYPE, in, pdst) *((RTYPE*)(pdst)) = in +#define ST_UB(...) ST_B(v16u8, __VA_ARGS__) +#define ST_SB(...) ST_B(v16i8, __VA_ARGS__) + +#define ST_H(RTYPE, in, pdst) *((RTYPE*)(pdst)) = in +#define ST_UH(...) ST_H(v8u16, __VA_ARGS__) +#define ST_SH(...) ST_H(v8i16, __VA_ARGS__) + +#define ST_W(RTYPE, in, pdst) *((RTYPE*)(pdst)) = in +#define ST_UW(...) ST_W(v4u32, __VA_ARGS__) +#define ST_SW(...) ST_W(v4i32, __VA_ARGS__) + +#define MSA_LOAD_FUNC(TYPE, INSTR, FUNC_NAME) \ + static inline TYPE FUNC_NAME(const void* const psrc) { \ + const uint8_t* const psrc_m = (const uint8_t*)psrc; \ + TYPE val_m; \ + asm volatile ( \ + "" #INSTR " %[val_m], %[psrc_m] \n\t" \ + : [val_m] "=r" (val_m) \ + : [psrc_m] "m" (*psrc_m)); \ + return val_m; \ + } + +#define MSA_LOAD(psrc, FUNC_NAME) FUNC_NAME(psrc) + +#define MSA_STORE_FUNC(TYPE, INSTR, FUNC_NAME) \ + static inline void FUNC_NAME(TYPE val, void* const pdst) { \ + uint8_t* const pdst_m = (uint8_t*)pdst; \ + TYPE val_m = val; \ + asm volatile ( \ + " " #INSTR " %[val_m], %[pdst_m] \n\t" \ + : [pdst_m] "=m" (*pdst_m) \ + : [val_m] "r" (val_m)); \ + } + +#define MSA_STORE(val, pdst, FUNC_NAME) FUNC_NAME(val, pdst) + +#if (__mips_isa_rev >= 6) + MSA_LOAD_FUNC(uint16_t, lh, msa_lh); + #define LH(psrc) MSA_LOAD(psrc, msa_lh) + MSA_LOAD_FUNC(uint32_t, lw, msa_lw); + #define LW(psrc) MSA_LOAD(psrc, msa_lw) + #if (__mips == 64) + MSA_LOAD_FUNC(uint64_t, ld, msa_ld); + #define LD(psrc) MSA_LOAD(psrc, msa_ld) + #else // !(__mips == 64) + #define LD(psrc) ((((uint64_t)MSA_LOAD(psrc + 4, msa_lw)) << 32) | \ + MSA_LOAD(psrc, msa_lw)) + #endif // (__mips == 64) + + MSA_STORE_FUNC(uint16_t, sh, msa_sh); + #define SH(val, pdst) MSA_STORE(val, pdst, msa_sh) + MSA_STORE_FUNC(uint32_t, sw, msa_sw); + #define SW(val, pdst) MSA_STORE(val, pdst, msa_sw) + MSA_STORE_FUNC(uint64_t, sd, msa_sd); + #define SD(val, pdst) MSA_STORE(val, pdst, msa_sd) +#else // !(__mips_isa_rev >= 6) + MSA_LOAD_FUNC(uint16_t, ulh, msa_ulh); + #define LH(psrc) MSA_LOAD(psrc, msa_ulh) + MSA_LOAD_FUNC(uint32_t, ulw, msa_ulw); + #define LW(psrc) MSA_LOAD(psrc, msa_ulw) + #if (__mips == 64) + MSA_LOAD_FUNC(uint64_t, uld, msa_uld); + #define LD(psrc) MSA_LOAD(psrc, msa_uld) + #else // !(__mips == 64) + #define LD(psrc) ((((uint64_t)MSA_LOAD(psrc + 4, msa_ulw)) << 32) | \ + MSA_LOAD(psrc, msa_ulw)) + #endif // (__mips == 64) + + MSA_STORE_FUNC(uint16_t, ush, msa_ush); + #define SH(val, pdst) MSA_STORE(val, pdst, msa_ush) + MSA_STORE_FUNC(uint32_t, usw, msa_usw); + #define SW(val, pdst) MSA_STORE(val, pdst, msa_usw) + #define SD(val, pdst) { \ + uint8_t* const pdst_sd_m = (uint8_t*)(pdst); \ + const uint32_t val0_m = (uint32_t)(val & 0x00000000FFFFFFFF); \ + const uint32_t val1_m = (uint32_t)((val >> 32) & 0x00000000FFFFFFFF); \ + SW(val0_m, pdst_sd_m); \ + SW(val1_m, pdst_sd_m + 4); \ + } +#endif // (__mips_isa_rev >= 6) + +/* Description : Load 4 words with stride + * Arguments : Inputs - psrc, stride + * Outputs - out0, out1, out2, out3 + * Details : Load word in 'out0' from (psrc) + * Load word in 'out1' from (psrc + stride) + * Load word in 'out2' from (psrc + 2 * stride) + * Load word in 'out3' from (psrc + 3 * stride) + */ +#define LW4(psrc, stride, out0, out1, out2, out3) { \ + const uint8_t* ptmp = (const uint8_t*)psrc; \ + out0 = LW(ptmp); \ + ptmp += stride; \ + out1 = LW(ptmp); \ + ptmp += stride; \ + out2 = LW(ptmp); \ + ptmp += stride; \ + out3 = LW(ptmp); \ +} + +/* Description : Store 4 words with stride + * Arguments : Inputs - in0, in1, in2, in3, pdst, stride + * Details : Store word from 'in0' to (pdst) + * Store word from 'in1' to (pdst + stride) + * Store word from 'in2' to (pdst + 2 * stride) + * Store word from 'in3' to (pdst + 3 * stride) + */ +#define SW4(in0, in1, in2, in3, pdst, stride) { \ + uint8_t* ptmp = (uint8_t*)pdst; \ + SW(in0, ptmp); \ + ptmp += stride; \ + SW(in1, ptmp); \ + ptmp += stride; \ + SW(in2, ptmp); \ + ptmp += stride; \ + SW(in3, ptmp); \ +} + +/* Description : Load vectors with 16 byte elements with stride + * Arguments : Inputs - psrc, stride + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Load 16 byte elements in 'out0' from (psrc) + * Load 16 byte elements in 'out1' from (psrc + stride) + */ +#define LD_B2(RTYPE, psrc, stride, out0, out1) { \ + out0 = LD_B(RTYPE, psrc); \ + out1 = LD_B(RTYPE, psrc + stride); \ +} +#define LD_UB2(...) LD_B2(v16u8, __VA_ARGS__) +#define LD_SB2(...) LD_B2(v16i8, __VA_ARGS__) + +#define LD_B4(RTYPE, psrc, stride, out0, out1, out2, out3) { \ + LD_B2(RTYPE, psrc, stride, out0, out1); \ + LD_B2(RTYPE, psrc + 2 * stride , stride, out2, out3); \ +} +#define LD_UB4(...) LD_B4(v16u8, __VA_ARGS__) +#define LD_SB4(...) LD_B4(v16i8, __VA_ARGS__) + +/* Description : Load vectors with 8 halfword elements with stride + * Arguments : Inputs - psrc, stride + * Outputs - out0, out1 + * Details : Load 8 halfword elements in 'out0' from (psrc) + * Load 8 halfword elements in 'out1' from (psrc + stride) + */ +#define LD_H2(RTYPE, psrc, stride, out0, out1) { \ + out0 = LD_H(RTYPE, psrc); \ + out1 = LD_H(RTYPE, psrc + stride); \ +} +#define LD_UH2(...) LD_H2(v8u16, __VA_ARGS__) +#define LD_SH2(...) LD_H2(v8i16, __VA_ARGS__) + +/* Description : Store 4x4 byte block to destination memory from input vector + * Arguments : Inputs - in0, in1, pdst, stride + * Details : 'Idx0' word element from input vector 'in0' is copied to the + * GP register and stored to (pdst) + * 'Idx1' word element from input vector 'in0' is copied to the + * GP register and stored to (pdst + stride) + * 'Idx2' word element from input vector 'in0' is copied to the + * GP register and stored to (pdst + 2 * stride) + * 'Idx3' word element from input vector 'in0' is copied to the + * GP register and stored to (pdst + 3 * stride) + */ +#define ST4x4_UB(in0, in1, idx0, idx1, idx2, idx3, pdst, stride) { \ + uint8_t* const pblk_4x4_m = (uint8_t*)pdst; \ + const uint32_t out0_m = __msa_copy_s_w((v4i32)in0, idx0); \ + const uint32_t out1_m = __msa_copy_s_w((v4i32)in0, idx1); \ + const uint32_t out2_m = __msa_copy_s_w((v4i32)in1, idx2); \ + const uint32_t out3_m = __msa_copy_s_w((v4i32)in1, idx3); \ + SW4(out0_m, out1_m, out2_m, out3_m, pblk_4x4_m, stride); \ +} + +/* Description : Immediate number of elements to slide + * Arguments : Inputs - in0, in1, slide_val + * Outputs - out + * Return Type - as per RTYPE + * Details : Byte elements from 'in1' vector are slid into 'in0' by + * value specified in the 'slide_val' + */ +#define SLDI_B(RTYPE, in0, in1, slide_val) \ + (RTYPE)__msa_sldi_b((v16i8)in0, (v16i8)in1, slide_val) \ + +#define SLDI_UB(...) SLDI_B(v16u8, __VA_ARGS__) +#define SLDI_SB(...) SLDI_B(v16i8, __VA_ARGS__) +#define SLDI_SH(...) SLDI_B(v8i16, __VA_ARGS__) + +/* Description : Shuffle halfword vector elements as per mask vector + * Arguments : Inputs - in0, in1, in2, in3, mask0, mask1 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : halfword elements from 'in0' & 'in1' are copied selectively to + * 'out0' as per control vector 'mask0' + */ +#define VSHF_H2(RTYPE, in0, in1, in2, in3, mask0, mask1, out0, out1) { \ + out0 = (RTYPE)__msa_vshf_h((v8i16)mask0, (v8i16)in1, (v8i16)in0); \ + out1 = (RTYPE)__msa_vshf_h((v8i16)mask1, (v8i16)in3, (v8i16)in2); \ +} +#define VSHF_H2_UH(...) VSHF_H2(v8u16, __VA_ARGS__) +#define VSHF_H2_SH(...) VSHF_H2(v8i16, __VA_ARGS__) + +/* Description : Clips all signed halfword elements of input vector + * between 0 & 255 + * Arguments : Input/output - val + * Return Type - signed halfword + */ +#define CLIP_SH_0_255(val) { \ + const v8i16 max_m = __msa_ldi_h(255); \ + val = __msa_maxi_s_h((v8i16)val, 0); \ + val = __msa_min_s_h(max_m, (v8i16)val); \ +} +#define CLIP_SH2_0_255(in0, in1) { \ + CLIP_SH_0_255(in0); \ + CLIP_SH_0_255(in1); \ +} + +/* Description : Clips all signed word elements of input vector + * between 0 & 255 + * Arguments : Input/output - val + * Return Type - signed word + */ +#define CLIP_SW_0_255(val) { \ + const v4i32 max_m = __msa_ldi_w(255); \ + val = __msa_maxi_s_w((v4i32)val, 0); \ + val = __msa_min_s_w(max_m, (v4i32)val); \ +} +#define CLIP_SW4_0_255(in0, in1, in2, in3) { \ + CLIP_SW_0_255(in0); \ + CLIP_SW_0_255(in1); \ + CLIP_SW_0_255(in2); \ + CLIP_SW_0_255(in3); \ +} + +/* Description : Set element n input vector to GPR value + * Arguments : Inputs - in0, in1, in2, in3 + * Output - out + * Return Type - as per RTYPE + * Details : Set element 0 in vector 'out' to value specified in 'in0' + */ +#define INSERT_W2(RTYPE, in0, in1, out) { \ + out = (RTYPE)__msa_insert_w((v4i32)out, 0, in0); \ + out = (RTYPE)__msa_insert_w((v4i32)out, 1, in1); \ +} +#define INSERT_W2_UB(...) INSERT_W2(v16u8, __VA_ARGS__) +#define INSERT_W2_SB(...) INSERT_W2(v16i8, __VA_ARGS__) + +#define INSERT_W4(RTYPE, in0, in1, in2, in3, out) { \ + out = (RTYPE)__msa_insert_w((v4i32)out, 0, in0); \ + out = (RTYPE)__msa_insert_w((v4i32)out, 1, in1); \ + out = (RTYPE)__msa_insert_w((v4i32)out, 2, in2); \ + out = (RTYPE)__msa_insert_w((v4i32)out, 3, in3); \ +} +#define INSERT_W4_UB(...) INSERT_W4(v16u8, __VA_ARGS__) +#define INSERT_W4_SB(...) INSERT_W4(v16i8, __VA_ARGS__) +#define INSERT_W4_SW(...) INSERT_W4(v4i32, __VA_ARGS__) + +/* Description : Interleave right half of byte elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Right half of byte elements of 'in0' and 'in1' are interleaved + * and written to out0. + */ +#define ILVR_B2(RTYPE, in0, in1, in2, in3, out0, out1) { \ + out0 = (RTYPE)__msa_ilvr_b((v16i8)in0, (v16i8)in1); \ + out1 = (RTYPE)__msa_ilvr_b((v16i8)in2, (v16i8)in3); \ +} +#define ILVR_B2_UB(...) ILVR_B2(v16u8, __VA_ARGS__) +#define ILVR_B2_SB(...) ILVR_B2(v16i8, __VA_ARGS__) +#define ILVR_B2_UH(...) ILVR_B2(v8u16, __VA_ARGS__) +#define ILVR_B2_SH(...) ILVR_B2(v8i16, __VA_ARGS__) +#define ILVR_B2_SW(...) ILVR_B2(v4i32, __VA_ARGS__) + +#define ILVR_B4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3) { \ + ILVR_B2(RTYPE, in0, in1, in2, in3, out0, out1); \ + ILVR_B2(RTYPE, in4, in5, in6, in7, out2, out3); \ +} +#define ILVR_B4_UB(...) ILVR_B4(v16u8, __VA_ARGS__) +#define ILVR_B4_SB(...) ILVR_B4(v16i8, __VA_ARGS__) +#define ILVR_B4_UH(...) ILVR_B4(v8u16, __VA_ARGS__) +#define ILVR_B4_SH(...) ILVR_B4(v8i16, __VA_ARGS__) +#define ILVR_B4_SW(...) ILVR_B4(v4i32, __VA_ARGS__) + +/* Description : Interleave right half of halfword elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Right half of halfword elements of 'in0' and 'in1' are + * interleaved and written to 'out0'. + */ +#define ILVR_H2(RTYPE, in0, in1, in2, in3, out0, out1) { \ + out0 = (RTYPE)__msa_ilvr_h((v8i16)in0, (v8i16)in1); \ + out1 = (RTYPE)__msa_ilvr_h((v8i16)in2, (v8i16)in3); \ +} +#define ILVR_H2_UB(...) ILVR_H2(v16u8, __VA_ARGS__) +#define ILVR_H2_SH(...) ILVR_H2(v8i16, __VA_ARGS__) +#define ILVR_H2_SW(...) ILVR_H2(v4i32, __VA_ARGS__) + +#define ILVR_H4(RTYPE, in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3) { \ + ILVR_H2(RTYPE, in0, in1, in2, in3, out0, out1); \ + ILVR_H2(RTYPE, in4, in5, in6, in7, out2, out3); \ +} +#define ILVR_H4_UB(...) ILVR_H4(v16u8, __VA_ARGS__) +#define ILVR_H4_SH(...) ILVR_H4(v8i16, __VA_ARGS__) +#define ILVR_H4_SW(...) ILVR_H4(v4i32, __VA_ARGS__) + +/* Description : Interleave right half of double word elements from vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Right half of double word elements of 'in0' and 'in1' are + * interleaved and written to 'out0'. + */ +#define ILVR_D2(RTYPE, in0, in1, in2, in3, out0, out1) { \ + out0 = (RTYPE)__msa_ilvr_d((v2i64)in0, (v2i64)in1); \ + out1 = (RTYPE)__msa_ilvr_d((v2i64)in2, (v2i64)in3); \ +} +#define ILVR_D2_UB(...) ILVR_D2(v16u8, __VA_ARGS__) +#define ILVR_D2_SB(...) ILVR_D2(v16i8, __VA_ARGS__) +#define ILVR_D2_SH(...) ILVR_D2(v8i16, __VA_ARGS__) + +#define ILVRL_H2(RTYPE, in0, in1, out0, out1) { \ + out0 = (RTYPE)__msa_ilvr_h((v8i16)in0, (v8i16)in1); \ + out1 = (RTYPE)__msa_ilvl_h((v8i16)in0, (v8i16)in1); \ +} +#define ILVRL_H2_UB(...) ILVRL_H2(v16u8, __VA_ARGS__) +#define ILVRL_H2_SB(...) ILVRL_H2(v16i8, __VA_ARGS__) +#define ILVRL_H2_SH(...) ILVRL_H2(v8i16, __VA_ARGS__) +#define ILVRL_H2_SW(...) ILVRL_H2(v4i32, __VA_ARGS__) +#define ILVRL_H2_UW(...) ILVRL_H2(v4u32, __VA_ARGS__) + +#define ILVRL_W2(RTYPE, in0, in1, out0, out1) { \ + out0 = (RTYPE)__msa_ilvr_w((v4i32)in0, (v4i32)in1); \ + out1 = (RTYPE)__msa_ilvl_w((v4i32)in0, (v4i32)in1); \ +} +#define ILVRL_W2_UB(...) ILVRL_W2(v16u8, __VA_ARGS__) +#define ILVRL_W2_SH(...) ILVRL_W2(v8i16, __VA_ARGS__) +#define ILVRL_W2_SW(...) ILVRL_W2(v4i32, __VA_ARGS__) + +/* Description : Pack even byte elements of vector pairs + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Return Type - as per RTYPE + * Details : Even byte elements of 'in0' are copied to the left half of + * 'out0' & even byte elements of 'in1' are copied to the right + * half of 'out0'. + */ +#define PCKEV_B2(RTYPE, in0, in1, in2, in3, out0, out1) { \ + out0 = (RTYPE)__msa_pckev_b((v16i8)in0, (v16i8)in1); \ + out1 = (RTYPE)__msa_pckev_b((v16i8)in2, (v16i8)in3); \ +} +#define PCKEV_B2_SB(...) PCKEV_B2(v16i8, __VA_ARGS__) +#define PCKEV_B2_UB(...) PCKEV_B2(v16u8, __VA_ARGS__) +#define PCKEV_B2_SH(...) PCKEV_B2(v8i16, __VA_ARGS__) +#define PCKEV_B2_SW(...) PCKEV_B2(v4i32, __VA_ARGS__) + +/* Description : Arithmetic immediate shift right all elements of word vector + * Arguments : Inputs - in0, in1, shift + * Outputs - in place operation + * Return Type - as per input vector RTYPE + * Details : Each element of vector 'in0' is right shifted by 'shift' and + * the result is written in-place. 'shift' is a GP variable. + */ +#define SRAI_W2(RTYPE, in0, in1, shift_val) { \ + in0 = (RTYPE)SRAI_W(in0, shift_val); \ + in1 = (RTYPE)SRAI_W(in1, shift_val); \ +} +#define SRAI_W2_SW(...) SRAI_W2(v4i32, __VA_ARGS__) +#define SRAI_W2_UW(...) SRAI_W2(v4u32, __VA_ARGS__) + +#define SRAI_W4(RTYPE, in0, in1, in2, in3, shift_val) { \ + SRAI_W2(RTYPE, in0, in1, shift_val); \ + SRAI_W2(RTYPE, in2, in3, shift_val); \ +} +#define SRAI_W4_SW(...) SRAI_W4(v4i32, __VA_ARGS__) +#define SRAI_W4_UW(...) SRAI_W4(v4u32, __VA_ARGS__) + +/* Description : Arithmetic shift right all elements of half-word vector + * Arguments : Inputs - in0, in1, shift + * Outputs - in place operation + * Return Type - as per input vector RTYPE + * Details : Each element of vector 'in0' is right shifted by 'shift' and + * the result is written in-place. 'shift' is a GP variable. + */ +#define SRAI_H2(RTYPE, in0, in1, shift_val) { \ + in0 = (RTYPE)SRAI_H(in0, shift_val); \ + in1 = (RTYPE)SRAI_H(in1, shift_val); \ +} +#define SRAI_H2_SH(...) SRAI_H2(v8i16, __VA_ARGS__) +#define SRAI_H2_UH(...) SRAI_H2(v8u16, __VA_ARGS__) + +/* Description : Arithmetic rounded shift right all elements of word vector + * Arguments : Inputs - in0, in1, shift + * Outputs - in place operation + * Return Type - as per input vector RTYPE + * Details : Each element of vector 'in0' is right shifted by 'shift' and + * the result is written in-place. 'shift' is a GP variable. + */ +#define SRARI_W2(RTYPE, in0, in1, shift) { \ + in0 = (RTYPE)__msa_srari_w((v4i32)in0, shift); \ + in1 = (RTYPE)__msa_srari_w((v4i32)in1, shift); \ +} +#define SRARI_W2_SW(...) SRARI_W2(v4i32, __VA_ARGS__) + +#define SRARI_W4(RTYPE, in0, in1, in2, in3, shift) { \ + SRARI_W2(RTYPE, in0, in1, shift); \ + SRARI_W2(RTYPE, in2, in3, shift); \ +} +#define SRARI_W4_SH(...) SRARI_W4(v8i16, __VA_ARGS__) +#define SRARI_W4_UW(...) SRARI_W4(v4u32, __VA_ARGS__) +#define SRARI_W4_SW(...) SRARI_W4(v4i32, __VA_ARGS__) + +/* Description : Addition of 2 pairs of half-word vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Details : Each element in 'in0' is added to 'in1' and result is written + * to 'out0'. + */ +#define ADDVI_H2(RTYPE, in0, in1, in2, in3, out0, out1) { \ + out0 = (RTYPE)ADDVI_H(in0, in1); \ + out1 = (RTYPE)ADDVI_H(in2, in3); \ +} +#define ADDVI_H2_SH(...) ADDVI_H2(v8i16, __VA_ARGS__) +#define ADDVI_H2_UH(...) ADDVI_H2(v8u16, __VA_ARGS__) + +/* Description : Addition of 2 pairs of vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1 + * Details : Each element in 'in0' is added to 'in1' and result is written + * to 'out0'. + */ +#define ADD2(in0, in1, in2, in3, out0, out1) { \ + out0 = in0 + in1; \ + out1 = in2 + in3; \ +} +#define ADD4(in0, in1, in2, in3, in4, in5, in6, in7, \ + out0, out1, out2, out3) { \ + ADD2(in0, in1, in2, in3, out0, out1); \ + ADD2(in4, in5, in6, in7, out2, out3); \ +} + +/* Description : Sign extend halfword elements from input vector and return + * the result in pair of vectors + * Arguments : Input - in (halfword vector) + * Outputs - out0, out1 (sign extended word vectors) + * Return Type - signed word + * Details : Sign bit of halfword elements from input vector 'in' is + * extracted and interleaved right with same vector 'in0' to + * generate 4 signed word elements in 'out0' + * Then interleaved left with same vector 'in0' to + * generate 4 signed word elements in 'out1' + */ +#define UNPCK_SH_SW(in, out0, out1) { \ + const v8i16 tmp_m = __msa_clti_s_h((v8i16)in, 0); \ + ILVRL_H2_SW(tmp_m, in, out0, out1); \ +} + +/* Description : Butterfly of 4 input vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + * Details : Butterfly operation + */ +#define BUTTERFLY_4(in0, in1, in2, in3, out0, out1, out2, out3) { \ + out0 = in0 + in3; \ + out1 = in1 + in2; \ + out2 = in1 - in2; \ + out3 = in0 - in3; \ +} + +/* Description : Transpose 4x4 block with word elements in vectors + * Arguments : Inputs - in0, in1, in2, in3 + * Outputs - out0, out1, out2, out3 + * Return Type - as per RTYPE + */ +#define TRANSPOSE4x4_W(RTYPE, in0, in1, in2, in3, out0, out1, out2, out3) { \ + v4i32 s0_m, s1_m, s2_m, s3_m; \ + ILVRL_W2_SW(in1, in0, s0_m, s1_m); \ + ILVRL_W2_SW(in3, in2, s2_m, s3_m); \ + out0 = (RTYPE)__msa_ilvr_d((v2i64)s2_m, (v2i64)s0_m); \ + out1 = (RTYPE)__msa_ilvl_d((v2i64)s2_m, (v2i64)s0_m); \ + out2 = (RTYPE)__msa_ilvr_d((v2i64)s3_m, (v2i64)s1_m); \ + out3 = (RTYPE)__msa_ilvl_d((v2i64)s3_m, (v2i64)s1_m); \ +} +#define TRANSPOSE4x4_SW_SW(...) TRANSPOSE4x4_W(v4i32, __VA_ARGS__) + +/* Description : Add block 4x4 + * Arguments : Inputs - in0, in1, in2, in3, pdst, stride + * Details : Least significant 4 bytes from each input vector are added to + * the destination bytes, clipped between 0-255 and stored. + */ +#define ADDBLK_ST4x4_UB(in0, in1, in2, in3, pdst, stride) { \ + uint32_t src0_m, src1_m, src2_m, src3_m; \ + v8i16 inp0_m, inp1_m, res0_m, res1_m; \ + v16i8 dst0_m = { 0 }; \ + v16i8 dst1_m = { 0 }; \ + const v16i8 zero_m = { 0 }; \ + ILVR_D2_SH(in1, in0, in3, in2, inp0_m, inp1_m); \ + LW4(pdst, stride, src0_m, src1_m, src2_m, src3_m); \ + INSERT_W2_SB(src0_m, src1_m, dst0_m); \ + INSERT_W2_SB(src2_m, src3_m, dst1_m); \ + ILVR_B2_SH(zero_m, dst0_m, zero_m, dst1_m, res0_m, res1_m); \ + ADD2(res0_m, inp0_m, res1_m, inp1_m, res0_m, res1_m); \ + CLIP_SH2_0_255(res0_m, res1_m); \ + PCKEV_B2_SB(res0_m, res0_m, res1_m, res1_m, dst0_m, dst1_m); \ + ST4x4_UB(dst0_m, dst1_m, 0, 1, 0, 1, pdst, stride); \ +} + +#endif /* WEBP_DSP_MSA_MACRO_H_ */ diff --git a/drivers/webp/dsp/rescaler_sse2.c b/drivers/webp/dsp/rescaler_sse2.c index 186edb653e..5b9702817c 100644 --- a/drivers/webp/dsp/rescaler_sse2.c +++ b/drivers/webp/dsp/rescaler_sse2.c @@ -18,6 +18,7 @@ #include <assert.h> #include "../utils/rescaler.h" +#include "../utils/utils.h" //------------------------------------------------------------------------------ // Implementations of critical functions ImportRow / ExportRow @@ -84,7 +85,8 @@ static void RescalerImportRowExpandSSE2(WebPRescaler* const wrk, while (1) { const __m128i mult = _mm_cvtsi32_si128(((x_add - accum) << 16) | accum); const __m128i out = _mm_madd_epi16(cur_pixels, mult); - *(uint32_t*)frow = _mm_cvtsi128_si32(out); + assert(sizeof(*frow) == sizeof(uint32_t)); + WebPUint32ToMem((uint8_t*)frow, _mm_cvtsi128_si32(out)); frow += 1; if (frow >= frow_end) break; accum -= wrk->x_sub; @@ -131,7 +133,7 @@ static void RescalerImportRowShrinkSSE2(WebPRescaler* const wrk, __m128i base = zero; accum += wrk->x_add; while (accum > 0) { - const __m128i A = _mm_cvtsi32_si128(*(int*)src); + const __m128i A = _mm_cvtsi32_si128(WebPMemToUint32(src)); src += 4; base = _mm_unpacklo_epi8(A, zero); // To avoid overflow, we need: base * x_add / x_sub < 32768 diff --git a/drivers/webp/dsp/upsampling_mips_dsp_r2.c b/drivers/webp/dsp/upsampling_mips_dsp_r2.c index 46f207b43e..ed2eb74825 100644 --- a/drivers/webp/dsp/upsampling_mips_dsp_r2.c +++ b/drivers/webp/dsp/upsampling_mips_dsp_r2.c @@ -22,21 +22,21 @@ #if !defined(WEBP_YUV_USE_TABLE) #define YUV_TO_RGB(Y, U, V, R, G, B) do { \ - const int t1 = kYScale * Y; \ - const int t2 = kVToG * V; \ - R = kVToR * V; \ - G = kUToG * U; \ - B = kUToB * U; \ + const int t1 = MultHi(Y, 19077); \ + const int t2 = MultHi(V, 13320); \ + R = MultHi(V, 26149); \ + G = MultHi(U, 6419); \ + B = MultHi(U, 33050); \ R = t1 + R; \ G = t1 - G; \ B = t1 + B; \ - R = R + kRCst; \ - G = G - t2 + kGCst; \ - B = B + kBCst; \ + R = R - 14234; \ + G = G - t2 + 8708; \ + B = B - 17685; \ __asm__ volatile ( \ - "shll_s.w %[" #R "], %[" #R "], 9 \n\t" \ - "shll_s.w %[" #G "], %[" #G "], 9 \n\t" \ - "shll_s.w %[" #B "], %[" #B "], 9 \n\t" \ + "shll_s.w %[" #R "], %[" #R "], 17 \n\t" \ + "shll_s.w %[" #G "], %[" #G "], 17 \n\t" \ + "shll_s.w %[" #B "], %[" #B "], 17 \n\t" \ "precrqu_s.qb.ph %[" #R "], %[" #R "], $zero \n\t" \ "precrqu_s.qb.ph %[" #G "], %[" #G "], $zero \n\t" \ "precrqu_s.qb.ph %[" #B "], %[" #B "], $zero \n\t" \ diff --git a/drivers/webp/dsp/upsampling_neon.c b/drivers/webp/dsp/upsampling_neon.c index a8384c2149..2b0c99bddb 100644 --- a/drivers/webp/dsp/upsampling_neon.c +++ b/drivers/webp/dsp/upsampling_neon.c @@ -89,9 +89,11 @@ static void Upsample16Pixels(const uint8_t *r1, const uint8_t *r2, //----------------------------------------------------------------------------- // YUV->RGB conversion -static const int16_t kCoeffs[4] = { kYScale, kVToR, kUToG, kVToG }; +// note: we represent the 33050 large constant as 32768 + 282 +static const int16_t kCoeffs1[4] = { 19077, 26149, 6419, 13320 }; #define v255 vdup_n_u8(255) +#define v_0x0f vdup_n_u8(15) #define STORE_Rgb(out, r, g, b) do { \ uint8x8x3_t r_g_b; \ @@ -117,38 +119,67 @@ static const int16_t kCoeffs[4] = { kYScale, kVToR, kUToG, kVToG }; vst4_u8(out, b_g_r_v255); \ } while (0) -#define CONVERT8(FMT, XSTEP, N, src_y, src_uv, out, cur_x) { \ +#define STORE_Argb(out, r, g, b) do { \ + uint8x8x4_t v255_r_g_b; \ + INIT_VECTOR4(v255_r_g_b, v255, r, g, b); \ + vst4_u8(out, v255_r_g_b); \ +} while (0) + +#if !defined(WEBP_SWAP_16BIT_CSP) +#define ZIP_U8(lo, hi) vzip_u8((lo), (hi)) +#else +#define ZIP_U8(lo, hi) vzip_u8((hi), (lo)) +#endif + +#define STORE_Rgba4444(out, r, g, b) do { \ + const uint8x8_t r1 = vshl_n_u8(vshr_n_u8(r, 4), 4); /* 4bits */ \ + const uint8x8_t g1 = vshr_n_u8(g, 4); \ + const uint8x8_t ba = vorr_u8(b, v_0x0f); \ + const uint8x8_t rg = vorr_u8(r1, g1); \ + const uint8x8x2_t rgba4444 = ZIP_U8(rg, ba); \ + vst1q_u8(out, vcombine_u8(rgba4444.val[0], rgba4444.val[1])); \ +} while (0) + +#define STORE_Rgb565(out, r, g, b) do { \ + const uint8x8_t r1 = vshl_n_u8(vshr_n_u8(r, 3), 3); /* 5bits */ \ + const uint8x8_t g1 = vshr_n_u8(g, 5); /* upper 3bits */\ + const uint8x8_t g2 = vshl_n_u8(vshr_n_u8(g, 2), 5); /* lower 3bits */\ + const uint8x8_t b1 = vshr_n_u8(b, 3); /* 5bits */ \ + const uint8x8_t rg = vorr_u8(r1, g1); \ + const uint8x8_t gb = vorr_u8(g2, b1); \ + const uint8x8x2_t rgb565 = ZIP_U8(rg, gb); \ + vst1q_u8(out, vcombine_u8(rgb565.val[0], rgb565.val[1])); \ +} while (0) + +#define CONVERT8(FMT, XSTEP, N, src_y, src_uv, out, cur_x) do { \ int i; \ for (i = 0; i < N; i += 8) { \ const int off = ((cur_x) + i) * XSTEP; \ - uint8x8_t y = vld1_u8((src_y) + (cur_x) + i); \ - uint8x8_t u = vld1_u8((src_uv) + i); \ - uint8x8_t v = vld1_u8((src_uv) + i + 16); \ - const int16x8_t yy = vreinterpretq_s16_u16(vsubl_u8(y, u16)); \ - const int16x8_t uu = vreinterpretq_s16_u16(vsubl_u8(u, u128)); \ - const int16x8_t vv = vreinterpretq_s16_u16(vsubl_u8(v, u128)); \ - int32x4_t yl = vmull_lane_s16(vget_low_s16(yy), cf16, 0); \ - int32x4_t yh = vmull_lane_s16(vget_high_s16(yy), cf16, 0); \ - const int32x4_t rl = vmlal_lane_s16(yl, vget_low_s16(vv), cf16, 1);\ - const int32x4_t rh = vmlal_lane_s16(yh, vget_high_s16(vv), cf16, 1);\ - int32x4_t gl = vmlsl_lane_s16(yl, vget_low_s16(uu), cf16, 2); \ - int32x4_t gh = vmlsl_lane_s16(yh, vget_high_s16(uu), cf16, 2); \ - const int32x4_t bl = vmovl_s16(vget_low_s16(uu)); \ - const int32x4_t bh = vmovl_s16(vget_high_s16(uu)); \ - gl = vmlsl_lane_s16(gl, vget_low_s16(vv), cf16, 3); \ - gh = vmlsl_lane_s16(gh, vget_high_s16(vv), cf16, 3); \ - yl = vmlaq_lane_s32(yl, bl, cf32, 0); \ - yh = vmlaq_lane_s32(yh, bh, cf32, 0); \ - /* vrshrn_n_s32() already incorporates the rounding constant */ \ - y = vqmovun_s16(vcombine_s16(vrshrn_n_s32(rl, YUV_FIX2), \ - vrshrn_n_s32(rh, YUV_FIX2))); \ - u = vqmovun_s16(vcombine_s16(vrshrn_n_s32(gl, YUV_FIX2), \ - vrshrn_n_s32(gh, YUV_FIX2))); \ - v = vqmovun_s16(vcombine_s16(vrshrn_n_s32(yl, YUV_FIX2), \ - vrshrn_n_s32(yh, YUV_FIX2))); \ - STORE_ ## FMT(out + off, y, u, v); \ + const uint8x8_t y = vld1_u8((src_y) + (cur_x) + i); \ + const uint8x8_t u = vld1_u8((src_uv) + i + 0); \ + const uint8x8_t v = vld1_u8((src_uv) + i + 16); \ + const int16x8_t Y0 = vreinterpretq_s16_u16(vshll_n_u8(y, 7)); \ + const int16x8_t U0 = vreinterpretq_s16_u16(vshll_n_u8(u, 7)); \ + const int16x8_t V0 = vreinterpretq_s16_u16(vshll_n_u8(v, 7)); \ + const int16x8_t Y1 = vqdmulhq_lane_s16(Y0, coeff1, 0); \ + const int16x8_t R0 = vqdmulhq_lane_s16(V0, coeff1, 1); \ + const int16x8_t G0 = vqdmulhq_lane_s16(U0, coeff1, 2); \ + const int16x8_t G1 = vqdmulhq_lane_s16(V0, coeff1, 3); \ + const int16x8_t B0 = vqdmulhq_n_s16(U0, 282); \ + const int16x8_t R1 = vqaddq_s16(Y1, R_Rounder); \ + const int16x8_t G2 = vqaddq_s16(Y1, G_Rounder); \ + const int16x8_t B1 = vqaddq_s16(Y1, B_Rounder); \ + const int16x8_t R2 = vqaddq_s16(R0, R1); \ + const int16x8_t G3 = vqaddq_s16(G0, G1); \ + const int16x8_t B2 = vqaddq_s16(B0, B1); \ + const int16x8_t G4 = vqsubq_s16(G2, G3); \ + const int16x8_t B3 = vqaddq_s16(B2, U0); \ + const uint8x8_t R = vqshrun_n_s16(R2, YUV_FIX2); \ + const uint8x8_t G = vqshrun_n_s16(G4, YUV_FIX2); \ + const uint8x8_t B = vqshrun_n_s16(B3, YUV_FIX2); \ + STORE_ ## FMT(out + off, R, G, B); \ } \ -} +} while (0) #define CONVERT1(FUNC, XSTEP, N, src_y, src_uv, rgb, cur_x) { \ int i; \ @@ -163,9 +194,9 @@ static const int16_t kCoeffs[4] = { kYScale, kVToR, kUToG, kVToG }; #define CONVERT2RGB_8(FMT, XSTEP, top_y, bottom_y, uv, \ top_dst, bottom_dst, cur_x, len) { \ - CONVERT8(FMT, XSTEP, len, top_y, uv, top_dst, cur_x) \ + CONVERT8(FMT, XSTEP, len, top_y, uv, top_dst, cur_x); \ if (bottom_y != NULL) { \ - CONVERT8(FMT, XSTEP, len, bottom_y, (uv) + 32, bottom_dst, cur_x) \ + CONVERT8(FMT, XSTEP, len, bottom_y, (uv) + 32, bottom_dst, cur_x); \ } \ } @@ -195,10 +226,10 @@ static void FUNC_NAME(const uint8_t *top_y, const uint8_t *bottom_y, \ 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 int16x4_t cf16 = vld1_s16(kCoeffs); \ - const int32x2_t cf32 = vdup_n_s32(kUToB); \ - const uint8x8_t u16 = vdup_n_u8(16); \ - const uint8x8_t u128 = vdup_n_u8(128); \ + const int16x4_t coeff1 = vld1_s16(kCoeffs1); \ + const int16x8_t R_Rounder = vdupq_n_s16(-14234); \ + const int16x8_t G_Rounder = vdupq_n_s16(8708); \ + const int16x8_t B_Rounder = vdupq_n_s16(-17685); \ \ /* Treat the first pixel in regular way */ \ assert(top_y != NULL); \ @@ -235,6 +266,9 @@ NEON_UPSAMPLE_FUNC(UpsampleRgbLinePair, Rgb, 3) NEON_UPSAMPLE_FUNC(UpsampleBgrLinePair, Bgr, 3) NEON_UPSAMPLE_FUNC(UpsampleRgbaLinePair, Rgba, 4) NEON_UPSAMPLE_FUNC(UpsampleBgraLinePair, Bgra, 4) +NEON_UPSAMPLE_FUNC(UpsampleArgbLinePair, Argb, 4) +NEON_UPSAMPLE_FUNC(UpsampleRgba4444LinePair, Rgba4444, 2) +NEON_UPSAMPLE_FUNC(UpsampleRgb565LinePair, Rgb565, 2) //------------------------------------------------------------------------------ // Entry point @@ -248,8 +282,13 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitUpsamplersNEON(void) { WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair; WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair; WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair; + WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair; WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair; WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair; + WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair; + WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair; + WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair; + WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair; } #endif // FANCY_UPSAMPLING diff --git a/drivers/webp/dsp/upsampling_sse2.c b/drivers/webp/dsp/upsampling_sse2.c index b85808e271..b5b668900f 100644 --- a/drivers/webp/dsp/upsampling_sse2.c +++ b/drivers/webp/dsp/upsampling_sse2.c @@ -173,6 +173,9 @@ SSE2_UPSAMPLE_FUNC(UpsampleRgbLinePair, VP8YuvToRgb, 3) SSE2_UPSAMPLE_FUNC(UpsampleBgrLinePair, VP8YuvToBgr, 3) SSE2_UPSAMPLE_FUNC(UpsampleRgbaLinePair, VP8YuvToRgba, 4) SSE2_UPSAMPLE_FUNC(UpsampleBgraLinePair, VP8YuvToBgra, 4) +SSE2_UPSAMPLE_FUNC(UpsampleArgbLinePair, VP8YuvToArgb, 4) +SSE2_UPSAMPLE_FUNC(UpsampleRgba4444LinePair, VP8YuvToRgba4444, 2) +SSE2_UPSAMPLE_FUNC(UpsampleRgb565LinePair, VP8YuvToRgb565, 2) #undef GET_M #undef PACK_AND_STORE @@ -190,13 +193,17 @@ extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; extern void WebPInitUpsamplersSSE2(void); 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_ARGB] = UpsampleArgbLinePair; WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePair; WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePair; + WebPUpsamplers[MODE_Argb] = UpsampleArgbLinePair; + WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair; + WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair; + WebPUpsamplers[MODE_rgbA_4444] = UpsampleRgba4444LinePair; } #endif // FANCY_UPSAMPLING @@ -225,7 +232,6 @@ 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; diff --git a/drivers/webp/dsp/yuv.h b/drivers/webp/dsp/yuv.h index af435a5b3e..01c40fcb84 100644 --- a/drivers/webp/dsp/yuv.h +++ b/drivers/webp/dsp/yuv.h @@ -21,16 +21,15 @@ // 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. // +// The fixed-point implementation used here is: +// R = (19077 . y + 26149 . v - 14234) >> 6 +// G = (19077 . y - 6419 . u - 13320 . v + 8708) >> 6 +// B = (19077 . y + 33050 . u - 17685) >> 6 +// where the '.' operator is the mulhi_epu16 variant: +// a . b = ((a << 8) * b) >> 16 +// that preserves 8 bits of fractional precision before final descaling. + // Author: Skal (pascal.massimino@gmail.com) #ifndef WEBP_DSP_YUV_H_ @@ -39,9 +38,6 @@ #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. @@ -66,41 +62,32 @@ enum { 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_FIX2 = 6, // 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) - //------------------------------------------------------------------------------ +// slower on x86 by ~7-8%, but bit-exact with the SSE2/NEON version -#if !defined(WEBP_YUV_USE_TABLE) - -// slower on x86 by ~7-8%, but bit-exact with the SSE2 version +static WEBP_INLINE int MultHi(int v, int coeff) { // _mm_mulhi_epu16 emulation + return (v * coeff) >> 8; +} 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); + return VP8Clip8(MultHi(y, 19077) + MultHi(v, 26149) - 14234); } static WEBP_INLINE int VP8YUVToG(int y, int u, int v) { - return VP8Clip8(kYScale * y - kUToG * u - kVToG * v + kGCst); + return VP8Clip8(MultHi(y, 19077) - MultHi(u, 6419) - MultHi(v, 13320) + 8708); } static WEBP_INLINE int VP8YUVToB(int y, int u) { - return VP8Clip8(kYScale * y + kUToB * u + kBCst); + return VP8Clip8(MultHi(y, 19077) + MultHi(u, 33050) - 17685); } static WEBP_INLINE void VP8YuvToRgb(int y, int u, int v, @@ -149,73 +136,6 @@ static WEBP_INLINE void VP8YuvToRgba4444(int y, int u, int v, #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(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]; - rgb[0] = VP8kClip[y + r_off - YUV_RANGE_MIN]; - rgb[1] = VP8kClip[y + g_off - YUV_RANGE_MIN]; - rgb[2] = VP8kClip[y + b_off - YUV_RANGE_MIN]; -} - -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; - const int b_off = VP8kUToB[u]; - bgr[0] = VP8kClip[y + b_off - YUV_RANGE_MIN]; - bgr[1] = VP8kClip[y + g_off - YUV_RANGE_MIN]; - 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 @@ -245,11 +165,7 @@ void VP8YUVInit(void); #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. +// Process 32 pixels and store the result (16b, 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, @@ -258,9 +174,12 @@ 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); +void VP8YuvToArgb32(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); +void VP8YuvToRgba444432(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); +void VP8YuvToRgb56532(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst); #endif // WEBP_USE_SSE2 diff --git a/drivers/webp/dsp/yuv_mips32.c b/drivers/webp/dsp/yuv_mips32.c index 018f8ab774..e61aac571f 100644 --- a/drivers/webp/dsp/yuv_mips32.c +++ b/drivers/webp/dsp/yuv_mips32.c @@ -28,19 +28,19 @@ static void FUNC_NAME(const uint8_t* y, \ int i, r, g, b; \ int temp0, temp1, temp2, temp3, temp4; \ for (i = 0; i < (len >> 1); i++) { \ - temp1 = kVToR * v[0]; \ - temp3 = kVToG * v[0]; \ - temp2 = kUToG * u[0]; \ - temp4 = kUToB * u[0]; \ - temp0 = kYScale * y[0]; \ - temp1 += kRCst; \ - temp3 -= kGCst; \ + temp1 = MultHi(v[0], 26149); \ + temp3 = MultHi(v[0], 13320); \ + temp2 = MultHi(u[0], 6419); \ + temp4 = MultHi(u[0], 33050); \ + temp0 = MultHi(y[0], 19077); \ + temp1 -= 14234; \ + temp3 -= 8708; \ temp2 += temp3; \ - temp4 += kBCst; \ + temp4 -= 17685; \ r = VP8Clip8(temp0 + temp1); \ g = VP8Clip8(temp0 - temp2); \ b = VP8Clip8(temp0 + temp4); \ - temp0 = kYScale * y[1]; \ + temp0 = MultHi(y[1], 19077); \ dst[R] = r; \ dst[G] = g; \ dst[B] = b; \ @@ -58,15 +58,15 @@ static void FUNC_NAME(const uint8_t* y, \ dst += 2 * XSTEP; \ } \ if (len & 1) { \ - temp1 = kVToR * v[0]; \ - temp3 = kVToG * v[0]; \ - temp2 = kUToG * u[0]; \ - temp4 = kUToB * u[0]; \ - temp0 = kYScale * y[0]; \ - temp1 += kRCst; \ - temp3 -= kGCst; \ + temp1 = MultHi(v[0], 26149); \ + temp3 = MultHi(v[0], 13320); \ + temp2 = MultHi(u[0], 6419); \ + temp4 = MultHi(u[0], 33050); \ + temp0 = MultHi(y[0], 19077); \ + temp1 -= 14234; \ + temp3 -= 8708; \ temp2 += temp3; \ - temp4 += kBCst; \ + temp4 -= 17685; \ r = VP8Clip8(temp0 + temp1); \ g = VP8Clip8(temp0 - temp2); \ b = VP8Clip8(temp0 + temp4); \ diff --git a/drivers/webp/dsp/yuv_mips_dsp_r2.c b/drivers/webp/dsp/yuv_mips_dsp_r2.c index 45a2200352..1720d4190f 100644 --- a/drivers/webp/dsp/yuv_mips_dsp_r2.c +++ b/drivers/webp/dsp/yuv_mips_dsp_r2.c @@ -30,10 +30,10 @@ "mul %[temp2], %[t_con_3], %[temp4] \n\t" \ "mul %[temp4], %[t_con_4], %[temp4] \n\t" \ "mul %[temp0], %[t_con_5], %[temp0] \n\t" \ - "addu %[temp1], %[temp1], %[t_con_6] \n\t" \ + "subu %[temp1], %[temp1], %[t_con_6] \n\t" \ "subu %[temp3], %[temp3], %[t_con_7] \n\t" \ "addu %[temp2], %[temp2], %[temp3] \n\t" \ - "addu %[temp4], %[temp4], %[t_con_8] \n\t" \ + "subu %[temp4], %[temp4], %[t_con_8] \n\t" \ #define ROW_FUNC_PART_2(R, G, B, K) \ "addu %[temp5], %[temp0], %[temp1] \n\t" \ @@ -42,12 +42,12 @@ ".if " #K " \n\t" \ "lbu %[temp0], 1(%[y]) \n\t" \ ".endif \n\t" \ - "shll_s.w %[temp5], %[temp5], 9 \n\t" \ - "shll_s.w %[temp6], %[temp6], 9 \n\t" \ + "shll_s.w %[temp5], %[temp5], 17 \n\t" \ + "shll_s.w %[temp6], %[temp6], 17 \n\t" \ ".if " #K " \n\t" \ "mul %[temp0], %[t_con_5], %[temp0] \n\t" \ ".endif \n\t" \ - "shll_s.w %[temp7], %[temp7], 9 \n\t" \ + "shll_s.w %[temp7], %[temp7], 17 \n\t" \ "precrqu_s.qb.ph %[temp5], %[temp5], $zero \n\t" \ "precrqu_s.qb.ph %[temp6], %[temp6], $zero \n\t" \ "precrqu_s.qb.ph %[temp7], %[temp7], $zero \n\t" \ @@ -74,14 +74,14 @@ static void FUNC_NAME(const uint8_t* y, \ uint8_t* dst, int len) { \ int i; \ uint32_t temp0, temp1, temp2, temp3, temp4, temp5, temp6, temp7; \ - const int t_con_1 = kVToR; \ - const int t_con_2 = kVToG; \ - const int t_con_3 = kUToG; \ - const int t_con_4 = kUToB; \ - const int t_con_5 = kYScale; \ - const int t_con_6 = kRCst; \ - const int t_con_7 = kGCst; \ - const int t_con_8 = kBCst; \ + const int t_con_1 = 26149; \ + const int t_con_2 = 13320; \ + const int t_con_3 = 6419; \ + const int t_con_4 = 33050; \ + const int t_con_5 = 19077; \ + const int t_con_6 = 14234; \ + const int t_con_7 = 8708; \ + const int t_con_8 = 17685; \ for (i = 0; i < (len >> 1); i++) { \ __asm__ volatile ( \ ROW_FUNC_PART_1() \ diff --git a/drivers/webp/dsp/yuv_sse2.c b/drivers/webp/dsp/yuv_sse2.c index 283b3af228..e19bddff6c 100644 --- a/drivers/webp/dsp/yuv_sse2.c +++ b/drivers/webp/dsp/yuv_sse2.c @@ -16,172 +16,294 @@ #if defined(WEBP_USE_SSE2) #include <emmintrin.h> -#include <string.h> // for memcpy -typedef union { // handy struct for converting SSE2 registers - int32_t i32[4]; - uint8_t u8[16]; - __m128i m; -} VP8kCstSSE2; - -#if defined(WEBP_YUV_USE_SSE2_TABLES) - -#include "./yuv_tables_sse2.h" - -WEBP_TSAN_IGNORE_FUNCTION void VP8YUVInitSSE2(void) {} +//----------------------------------------------------------------------------- +// Convert spans of 32 pixels to various RGB formats for the fancy upsampler. -#else +// These constants are 14b fixed-point version of ITU-R BT.601 constants. +// R = (19077 * y + 26149 * v - 14234) >> 6 +// G = (19077 * y - 6419 * u - 13320 * v + 8708) >> 6 +// B = (19077 * y + 33050 * u - 17685) >> 6 +static void ConvertYUV444ToRGB(const __m128i* const Y0, + const __m128i* const U0, + const __m128i* const V0, + __m128i* const R, + __m128i* const G, + __m128i* const B) { + const __m128i k19077 = _mm_set1_epi16(19077); + const __m128i k26149 = _mm_set1_epi16(26149); + const __m128i k14234 = _mm_set1_epi16(14234); + // 33050 doesn't fit in a signed short: only use this with unsigned arithmetic + const __m128i k33050 = _mm_set1_epi16((short)33050); + const __m128i k17685 = _mm_set1_epi16(17685); + const __m128i k6419 = _mm_set1_epi16(6419); + const __m128i k13320 = _mm_set1_epi16(13320); + const __m128i k8708 = _mm_set1_epi16(8708); + + const __m128i Y1 = _mm_mulhi_epu16(*Y0, k19077); + + const __m128i R0 = _mm_mulhi_epu16(*V0, k26149); + const __m128i R1 = _mm_sub_epi16(Y1, k14234); + const __m128i R2 = _mm_add_epi16(R1, R0); + + const __m128i G0 = _mm_mulhi_epu16(*U0, k6419); + const __m128i G1 = _mm_mulhi_epu16(*V0, k13320); + const __m128i G2 = _mm_add_epi16(Y1, k8708); + const __m128i G3 = _mm_add_epi16(G0, G1); + const __m128i G4 = _mm_sub_epi16(G2, G3); + + // be careful with the saturated *unsigned* arithmetic here! + const __m128i B0 = _mm_mulhi_epu16(*U0, k33050); + const __m128i B1 = _mm_adds_epu16(B0, Y1); + const __m128i B2 = _mm_subs_epu16(B1, k17685); + + // use logical shift for B2, which can be larger than 32767 + *R = _mm_srai_epi16(R2, 6); // range: [-14234, 30815] + *G = _mm_srai_epi16(G4, 6); // range: [-10953, 27710] + *B = _mm_srli_epi16(B2, 6); // range: [0, 34238] +} -static int done_sse2 = 0; -static VP8kCstSSE2 VP8kUtoRGBA[256], VP8kVtoRGBA[256], VP8kYtoRGBA[256]; - -WEBP_TSAN_IGNORE_FUNCTION void VP8YUVInitSSE2(void) { - if (!done_sse2) { - int i; - for (i = 0; i < 256; ++i) { - VP8kYtoRGBA[i].i32[0] = - VP8kYtoRGBA[i].i32[1] = - VP8kYtoRGBA[i].i32[2] = (i - 16) * kYScale + YUV_HALF2; - VP8kYtoRGBA[i].i32[3] = 0xff << YUV_FIX2; - - VP8kUtoRGBA[i].i32[0] = 0; - VP8kUtoRGBA[i].i32[1] = -kUToG * (i - 128); - VP8kUtoRGBA[i].i32[2] = kUToB * (i - 128); - VP8kUtoRGBA[i].i32[3] = 0; - - VP8kVtoRGBA[i].i32[0] = kVToR * (i - 128); - VP8kVtoRGBA[i].i32[1] = -kVToG * (i - 128); - VP8kVtoRGBA[i].i32[2] = 0; - VP8kVtoRGBA[i].i32[3] = 0; - } - done_sse2 = 1; - -#if 0 // code used to generate 'yuv_tables_sse2.h' - printf("static const VP8kCstSSE2 VP8kYtoRGBA[256] = {\n"); - for (i = 0; i < 256; ++i) { - printf(" {{0x%.8x, 0x%.8x, 0x%.8x, 0x%.8x}},\n", - VP8kYtoRGBA[i].i32[0], VP8kYtoRGBA[i].i32[1], - VP8kYtoRGBA[i].i32[2], VP8kYtoRGBA[i].i32[3]); - } - printf("};\n\n"); - printf("static const VP8kCstSSE2 VP8kUtoRGBA[256] = {\n"); - for (i = 0; i < 256; ++i) { - printf(" {{0, 0x%.8x, 0x%.8x, 0}},\n", - VP8kUtoRGBA[i].i32[1], VP8kUtoRGBA[i].i32[2]); - } - printf("};\n\n"); - printf("static VP8kCstSSE2 VP8kVtoRGBA[256] = {\n"); - for (i = 0; i < 256; ++i) { - printf(" {{0x%.8x, 0x%.8x, 0, 0}},\n", - VP8kVtoRGBA[i].i32[0], VP8kVtoRGBA[i].i32[1]); - } - printf("};\n\n"); -#endif - } +// Load the bytes into the *upper* part of 16b words. That's "<< 8", basically. +static WEBP_INLINE __m128i Load_HI_16(const uint8_t* src) { + const __m128i zero = _mm_setzero_si128(); + return _mm_unpacklo_epi8(zero, _mm_loadl_epi64((const __m128i*)src)); } -#endif // WEBP_YUV_USE_SSE2_TABLES +// Load and replicate the U/V samples +static WEBP_INLINE __m128i Load_UV_HI_8(const uint8_t* src) { + const __m128i zero = _mm_setzero_si128(); + const __m128i tmp0 = _mm_cvtsi32_si128(*(const uint32_t*)src); + const __m128i tmp1 = _mm_unpacklo_epi8(zero, tmp0); + return _mm_unpacklo_epi16(tmp1, tmp1); // replicate samples +} -//----------------------------------------------------------------------------- +// Convert 32 samples of YUV444 to R/G/B +static void YUV444ToRGB(const uint8_t* const y, + const uint8_t* const u, + const uint8_t* const v, + __m128i* const R, __m128i* const G, __m128i* const B) { + const __m128i Y0 = Load_HI_16(y), U0 = Load_HI_16(u), V0 = Load_HI_16(v); + ConvertYUV444ToRGB(&Y0, &U0, &V0, R, G, B); +} -static WEBP_INLINE __m128i LoadUVPart(int u, int v) { - const __m128i u_part = _mm_loadu_si128(&VP8kUtoRGBA[u].m); - const __m128i v_part = _mm_loadu_si128(&VP8kVtoRGBA[v].m); - const __m128i uv_part = _mm_add_epi32(u_part, v_part); - return uv_part; +// Convert 32 samples of YUV420 to R/G/B +static void YUV420ToRGB(const uint8_t* const y, + const uint8_t* const u, + const uint8_t* const v, + __m128i* const R, __m128i* const G, __m128i* const B) { + const __m128i Y0 = Load_HI_16(y), U0 = Load_UV_HI_8(u), V0 = Load_UV_HI_8(v); + ConvertYUV444ToRGB(&Y0, &U0, &V0, R, G, B); } -static WEBP_INLINE __m128i GetRGBA32bWithUV(int y, const __m128i uv_part) { - const __m128i y_part = _mm_loadu_si128(&VP8kYtoRGBA[y].m); - const __m128i rgba1 = _mm_add_epi32(y_part, uv_part); - const __m128i rgba2 = _mm_srai_epi32(rgba1, YUV_FIX2); - return rgba2; +// Pack R/G/B/A results into 32b output. +static WEBP_INLINE void PackAndStore4(const __m128i* const R, + const __m128i* const G, + const __m128i* const B, + const __m128i* const A, + uint8_t* const dst) { + const __m128i rb = _mm_packus_epi16(*R, *B); + const __m128i ga = _mm_packus_epi16(*G, *A); + const __m128i rg = _mm_unpacklo_epi8(rb, ga); + const __m128i ba = _mm_unpackhi_epi8(rb, ga); + const __m128i RGBA_lo = _mm_unpacklo_epi16(rg, ba); + const __m128i RGBA_hi = _mm_unpackhi_epi16(rg, ba); + _mm_storeu_si128((__m128i*)(dst + 0), RGBA_lo); + _mm_storeu_si128((__m128i*)(dst + 16), RGBA_hi); } -static WEBP_INLINE __m128i GetRGBA32b(int y, int u, int v) { - const __m128i uv_part = LoadUVPart(u, v); - return GetRGBA32bWithUV(y, uv_part); +// Pack R/G/B/A results into 16b output. +static WEBP_INLINE void PackAndStore4444(const __m128i* const R, + const __m128i* const G, + const __m128i* const B, + const __m128i* const A, + uint8_t* const dst) { +#if !defined(WEBP_SWAP_16BIT_CSP) + const __m128i rg0 = _mm_packus_epi16(*R, *G); + const __m128i ba0 = _mm_packus_epi16(*B, *A); +#else + const __m128i rg0 = _mm_packus_epi16(*B, *A); + const __m128i ba0 = _mm_packus_epi16(*R, *G); +#endif + const __m128i mask_0xf0 = _mm_set1_epi8(0xf0); + const __m128i rb1 = _mm_unpacklo_epi8(rg0, ba0); // rbrbrbrbrb... + const __m128i ga1 = _mm_unpackhi_epi8(rg0, ba0); // gagagagaga... + const __m128i rb2 = _mm_and_si128(rb1, mask_0xf0); + const __m128i ga2 = _mm_srli_epi16(_mm_and_si128(ga1, mask_0xf0), 4); + const __m128i rgba4444 = _mm_or_si128(rb2, ga2); + _mm_storeu_si128((__m128i*)dst, rgba4444); } -static WEBP_INLINE void YuvToRgbSSE2(uint8_t y, uint8_t u, uint8_t v, - uint8_t* const rgb) { - const __m128i tmp0 = GetRGBA32b(y, u, v); - const __m128i tmp1 = _mm_packs_epi32(tmp0, tmp0); - const __m128i tmp2 = _mm_packus_epi16(tmp1, tmp1); - // Note: we store 8 bytes at a time, not 3 bytes! -> memory stomp - _mm_storel_epi64((__m128i*)rgb, tmp2); +// Pack R/G/B results into 16b output. +static WEBP_INLINE void PackAndStore565(const __m128i* const R, + const __m128i* const G, + const __m128i* const B, + uint8_t* const dst) { + const __m128i r0 = _mm_packus_epi16(*R, *R); + const __m128i g0 = _mm_packus_epi16(*G, *G); + const __m128i b0 = _mm_packus_epi16(*B, *B); + const __m128i r1 = _mm_and_si128(r0, _mm_set1_epi8(0xf8)); + const __m128i b1 = _mm_and_si128(_mm_srli_epi16(b0, 3), _mm_set1_epi8(0x1f)); + const __m128i g1 = _mm_srli_epi16(_mm_and_si128(g0, _mm_set1_epi8(0xe0)), 5); + const __m128i g2 = _mm_slli_epi16(_mm_and_si128(g0, _mm_set1_epi8(0x1c)), 3); + const __m128i rg = _mm_or_si128(r1, g1); + const __m128i gb = _mm_or_si128(g2, b1); +#if !defined(WEBP_SWAP_16BIT_CSP) + const __m128i rgb565 = _mm_unpacklo_epi8(rg, gb); +#else + const __m128i rgb565 = _mm_unpacklo_epi8(gb, rg); +#endif + _mm_storeu_si128((__m128i*)dst, rgb565); } -static WEBP_INLINE void YuvToBgrSSE2(uint8_t y, uint8_t u, uint8_t v, - uint8_t* const bgr) { - const __m128i tmp0 = GetRGBA32b(y, u, v); - const __m128i tmp1 = _mm_shuffle_epi32(tmp0, _MM_SHUFFLE(3, 0, 1, 2)); - const __m128i tmp2 = _mm_packs_epi32(tmp1, tmp1); - const __m128i tmp3 = _mm_packus_epi16(tmp2, tmp2); - // Note: we store 8 bytes at a time, not 3 bytes! -> memory stomp - _mm_storel_epi64((__m128i*)bgr, tmp3); +// Function used several times in PlanarTo24b. +// It samples the in buffer as follows: one every two unsigned char is stored +// at the beginning of the buffer, while the other half is stored at the end. +static WEBP_INLINE void PlanarTo24bHelper(const __m128i* const in /*in[6]*/, + __m128i* const out /*out[6]*/) { + const __m128i v_mask = _mm_set1_epi16(0x00ff); + + // Take one every two upper 8b values. + out[0] = _mm_packus_epi16(_mm_and_si128(in[0], v_mask), + _mm_and_si128(in[1], v_mask)); + out[1] = _mm_packus_epi16(_mm_and_si128(in[2], v_mask), + _mm_and_si128(in[3], v_mask)); + out[2] = _mm_packus_epi16(_mm_and_si128(in[4], v_mask), + _mm_and_si128(in[5], v_mask)); + // Take one every two lower 8b values. + out[3] = _mm_packus_epi16(_mm_srli_epi16(in[0], 8), _mm_srli_epi16(in[1], 8)); + out[4] = _mm_packus_epi16(_mm_srli_epi16(in[2], 8), _mm_srli_epi16(in[3], 8)); + out[5] = _mm_packus_epi16(_mm_srli_epi16(in[4], 8), _mm_srli_epi16(in[5], 8)); } -//----------------------------------------------------------------------------- -// Convert spans of 32 pixels to various RGB formats for the fancy upsampler. +// Pack the planar buffers +// rrrr... rrrr... gggg... gggg... bbbb... bbbb.... +// triplet by triplet in the output buffer rgb as rgbrgbrgbrgb ... +static WEBP_INLINE void PlanarTo24b(__m128i* const in /*in[6]*/, uint8_t* rgb) { + // The input is 6 registers of sixteen 8b but for the sake of explanation, + // let's take 6 registers of four 8b values. + // To pack, we will keep taking one every two 8b integer and move it + // around as follows: + // Input: + // r0r1r2r3 | r4r5r6r7 | g0g1g2g3 | g4g5g6g7 | b0b1b2b3 | b4b5b6b7 + // Split the 6 registers in two sets of 3 registers: the first set as the even + // 8b bytes, the second the odd ones: + // r0r2r4r6 | g0g2g4g6 | b0b2b4b6 | r1r3r5r7 | g1g3g5g7 | b1b3b5b7 + // Repeat the same permutations twice more: + // r0r4g0g4 | b0b4r1r5 | g1g5b1b5 | r2r6g2g6 | b2b6r3r7 | g3g7b3b7 + // r0g0b0r1 | g1b1r2g2 | b2r3g3b3 | r4g4b4r5 | g5b5r6g6 | b6r7g7b7 + __m128i tmp[6]; + PlanarTo24bHelper(in, tmp); + PlanarTo24bHelper(tmp, in); + PlanarTo24bHelper(in, tmp); + // We need to do it two more times than the example as we have sixteen bytes. + PlanarTo24bHelper(tmp, in); + PlanarTo24bHelper(in, tmp); + + _mm_storeu_si128((__m128i*)(rgb + 0), tmp[0]); + _mm_storeu_si128((__m128i*)(rgb + 16), tmp[1]); + _mm_storeu_si128((__m128i*)(rgb + 32), tmp[2]); + _mm_storeu_si128((__m128i*)(rgb + 48), tmp[3]); + _mm_storeu_si128((__m128i*)(rgb + 64), tmp[4]); + _mm_storeu_si128((__m128i*)(rgb + 80), tmp[5]); +} +#undef MK_UINT32 void VP8YuvToRgba32(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst) { + const __m128i kAlpha = _mm_set1_epi16(255); int n; - for (n = 0; n < 32; n += 4) { - const __m128i tmp0_1 = GetRGBA32b(y[n + 0], u[n + 0], v[n + 0]); - const __m128i tmp0_2 = GetRGBA32b(y[n + 1], u[n + 1], v[n + 1]); - const __m128i tmp0_3 = GetRGBA32b(y[n + 2], u[n + 2], v[n + 2]); - const __m128i tmp0_4 = GetRGBA32b(y[n + 3], u[n + 3], v[n + 3]); - const __m128i tmp1_1 = _mm_packs_epi32(tmp0_1, tmp0_2); - const __m128i tmp1_2 = _mm_packs_epi32(tmp0_3, tmp0_4); - const __m128i tmp2 = _mm_packus_epi16(tmp1_1, tmp1_2); - _mm_storeu_si128((__m128i*)dst, tmp2); - dst += 4 * 4; + for (n = 0; n < 32; n += 8, dst += 32) { + __m128i R, G, B; + YUV444ToRGB(y + n, u + n, v + n, &R, &G, &B); + PackAndStore4(&R, &G, &B, &kAlpha, dst); } } void VP8YuvToBgra32(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst) { + const __m128i kAlpha = _mm_set1_epi16(255); int n; - for (n = 0; n < 32; n += 2) { - const __m128i tmp0_1 = GetRGBA32b(y[n + 0], u[n + 0], v[n + 0]); - const __m128i tmp0_2 = GetRGBA32b(y[n + 1], u[n + 1], v[n + 1]); - const __m128i tmp1_1 = _mm_shuffle_epi32(tmp0_1, _MM_SHUFFLE(3, 0, 1, 2)); - const __m128i tmp1_2 = _mm_shuffle_epi32(tmp0_2, _MM_SHUFFLE(3, 0, 1, 2)); - const __m128i tmp2_1 = _mm_packs_epi32(tmp1_1, tmp1_2); - const __m128i tmp3 = _mm_packus_epi16(tmp2_1, tmp2_1); - _mm_storel_epi64((__m128i*)dst, tmp3); - dst += 4 * 2; + for (n = 0; n < 32; n += 8, dst += 32) { + __m128i R, G, B; + YUV444ToRGB(y + n, u + n, v + n, &R, &G, &B); + PackAndStore4(&B, &G, &R, &kAlpha, dst); } } -void VP8YuvToRgb32(const uint8_t* y, const uint8_t* u, const uint8_t* v, - uint8_t* dst) { +void VP8YuvToArgb32(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst) { + const __m128i kAlpha = _mm_set1_epi16(255); int n; - uint8_t tmp0[2 * 3 + 5 + 15]; - uint8_t* const tmp = (uint8_t*)((uintptr_t)(tmp0 + 15) & ~15); // align - for (n = 0; n < 30; ++n) { // we directly stomp the *dst memory - YuvToRgbSSE2(y[n], u[n], v[n], dst + n * 3); + for (n = 0; n < 32; n += 8, dst += 32) { + __m128i R, G, B; + YUV444ToRGB(y + n, u + n, v + n, &R, &G, &B); + PackAndStore4(&kAlpha, &R, &G, &B, dst); } - // Last two pixels are special: we write in a tmp buffer before sending - // to dst. - YuvToRgbSSE2(y[n + 0], u[n + 0], v[n + 0], tmp + 0); - YuvToRgbSSE2(y[n + 1], u[n + 1], v[n + 1], tmp + 3); - memcpy(dst + n * 3, tmp, 2 * 3); } -void VP8YuvToBgr32(const uint8_t* y, const uint8_t* u, const uint8_t* v, - uint8_t* dst) { +void VP8YuvToRgba444432(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst) { + const __m128i kAlpha = _mm_set1_epi16(255); int n; - uint8_t tmp0[2 * 3 + 5 + 15]; - uint8_t* const tmp = (uint8_t*)((uintptr_t)(tmp0 + 15) & ~15); // align - for (n = 0; n < 30; ++n) { - YuvToBgrSSE2(y[n], u[n], v[n], dst + n * 3); + for (n = 0; n < 32; n += 8, dst += 16) { + __m128i R, G, B; + YUV444ToRGB(y + n, u + n, v + n, &R, &G, &B); + PackAndStore4444(&R, &G, &B, &kAlpha, dst); } - YuvToBgrSSE2(y[n + 0], u[n + 0], v[n + 0], tmp + 0); - YuvToBgrSSE2(y[n + 1], u[n + 1], v[n + 1], tmp + 3); - memcpy(dst + n * 3, tmp, 2 * 3); +} + +void VP8YuvToRgb56532(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst) { + int n; + for (n = 0; n < 32; n += 8, dst += 16) { + __m128i R, G, B; + YUV444ToRGB(y + n, u + n, v + n, &R, &G, &B); + PackAndStore565(&R, &G, &B, dst); + } +} + +void VP8YuvToRgb32(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst) { + __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; + __m128i rgb[6]; + + YUV444ToRGB(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV444ToRGB(y + 8, u + 8, v + 8, &R1, &G1, &B1); + YUV444ToRGB(y + 16, u + 16, v + 16, &R2, &G2, &B2); + YUV444ToRGB(y + 24, u + 24, v + 24, &R3, &G3, &B3); + + // Cast to 8b and store as RRRRGGGGBBBB. + rgb[0] = _mm_packus_epi16(R0, R1); + rgb[1] = _mm_packus_epi16(R2, R3); + rgb[2] = _mm_packus_epi16(G0, G1); + rgb[3] = _mm_packus_epi16(G2, G3); + rgb[4] = _mm_packus_epi16(B0, B1); + rgb[5] = _mm_packus_epi16(B2, B3); + + // Pack as RGBRGBRGBRGB. + PlanarTo24b(rgb, dst); +} + +void VP8YuvToBgr32(const uint8_t* y, const uint8_t* u, const uint8_t* v, + uint8_t* dst) { + __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; + __m128i bgr[6]; + + YUV444ToRGB(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV444ToRGB(y + 8, u + 8, v + 8, &R1, &G1, &B1); + YUV444ToRGB(y + 16, u + 16, v + 16, &R2, &G2, &B2); + YUV444ToRGB(y + 24, u + 24, v + 24, &R3, &G3, &B3); + + // Cast to 8b and store as BBBBGGGGRRRR. + bgr[0] = _mm_packus_epi16(B0, B1); + bgr[1] = _mm_packus_epi16(B2, B3); + bgr[2] = _mm_packus_epi16(G0, G1); + bgr[3] = _mm_packus_epi16(G2, G3); + bgr[4] = _mm_packus_epi16(R0, R1); + bgr[5] = _mm_packus_epi16(R2, R3); + + // Pack as BGRBGRBGRBGR. + PlanarTo24b(bgr, dst); } //----------------------------------------------------------------------------- @@ -189,110 +311,137 @@ void VP8YuvToBgr32(const uint8_t* y, const uint8_t* u, const uint8_t* v, static void YuvToRgbaRow(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst, int len) { + const __m128i kAlpha = _mm_set1_epi16(255); int n; - for (n = 0; n + 4 <= len; n += 4) { - const __m128i uv_0 = LoadUVPart(u[0], v[0]); - const __m128i uv_1 = LoadUVPart(u[1], v[1]); - const __m128i tmp0_1 = GetRGBA32bWithUV(y[0], uv_0); - const __m128i tmp0_2 = GetRGBA32bWithUV(y[1], uv_0); - const __m128i tmp0_3 = GetRGBA32bWithUV(y[2], uv_1); - const __m128i tmp0_4 = GetRGBA32bWithUV(y[3], uv_1); - const __m128i tmp1_1 = _mm_packs_epi32(tmp0_1, tmp0_2); - const __m128i tmp1_2 = _mm_packs_epi32(tmp0_3, tmp0_4); - const __m128i tmp2 = _mm_packus_epi16(tmp1_1, tmp1_2); - _mm_storeu_si128((__m128i*)dst, tmp2); - dst += 4 * 4; - y += 4; - u += 2; - v += 2; + for (n = 0; n + 8 <= len; n += 8, dst += 32) { + __m128i R, G, B; + YUV420ToRGB(y, u, v, &R, &G, &B); + PackAndStore4(&R, &G, &B, &kAlpha, dst); + y += 8; + u += 4; + v += 4; } - // Finish off - while (n < len) { + for (; n < len; ++n) { // Finish off VP8YuvToRgba(y[0], u[0], v[0], dst); dst += 4; - ++y; + y += 1; u += (n & 1); v += (n & 1); - ++n; } } static void YuvToBgraRow(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst, int len) { + const __m128i kAlpha = _mm_set1_epi16(255); int n; - for (n = 0; n + 2 <= len; n += 2) { - const __m128i uv_0 = LoadUVPart(u[0], v[0]); - const __m128i tmp0_1 = GetRGBA32bWithUV(y[0], uv_0); - const __m128i tmp0_2 = GetRGBA32bWithUV(y[1], uv_0); - const __m128i tmp1_1 = _mm_shuffle_epi32(tmp0_1, _MM_SHUFFLE(3, 0, 1, 2)); - const __m128i tmp1_2 = _mm_shuffle_epi32(tmp0_2, _MM_SHUFFLE(3, 0, 1, 2)); - const __m128i tmp2_1 = _mm_packs_epi32(tmp1_1, tmp1_2); - const __m128i tmp3 = _mm_packus_epi16(tmp2_1, tmp2_1); - _mm_storel_epi64((__m128i*)dst, tmp3); - dst += 4 * 2; - y += 2; - ++u; - ++v; + for (n = 0; n + 8 <= len; n += 8, dst += 32) { + __m128i R, G, B; + YUV420ToRGB(y, u, v, &R, &G, &B); + PackAndStore4(&B, &G, &R, &kAlpha, dst); + y += 8; + u += 4; + v += 4; } - // Finish off - if (len & 1) { + for (; n < len; ++n) { // Finish off VP8YuvToBgra(y[0], u[0], v[0], dst); + dst += 4; + y += 1; + u += (n & 1); + v += (n & 1); } } static void YuvToArgbRow(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst, int len) { + const __m128i kAlpha = _mm_set1_epi16(255); int n; - for (n = 0; n + 2 <= len; n += 2) { - const __m128i uv_0 = LoadUVPart(u[0], v[0]); - const __m128i tmp0_1 = GetRGBA32bWithUV(y[0], uv_0); - const __m128i tmp0_2 = GetRGBA32bWithUV(y[1], uv_0); - const __m128i tmp1_1 = _mm_shuffle_epi32(tmp0_1, _MM_SHUFFLE(2, 1, 0, 3)); - const __m128i tmp1_2 = _mm_shuffle_epi32(tmp0_2, _MM_SHUFFLE(2, 1, 0, 3)); - const __m128i tmp2_1 = _mm_packs_epi32(tmp1_1, tmp1_2); - const __m128i tmp3 = _mm_packus_epi16(tmp2_1, tmp2_1); - _mm_storel_epi64((__m128i*)dst, tmp3); - dst += 4 * 2; - y += 2; - ++u; - ++v; + for (n = 0; n + 8 <= len; n += 8, dst += 32) { + __m128i R, G, B; + YUV420ToRGB(y, u, v, &R, &G, &B); + PackAndStore4(&kAlpha, &R, &G, &B, dst); + y += 8; + u += 4; + v += 4; } - // Finish off - if (len & 1) { + for (; n < len; ++n) { // Finish off VP8YuvToArgb(y[0], u[0], v[0], dst); + dst += 4; + y += 1; + u += (n & 1); + v += (n & 1); } } static void YuvToRgbRow(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst, int len) { int n; - for (n = 0; n + 2 < len; ++n) { // we directly stomp the *dst memory - YuvToRgbSSE2(y[0], u[0], v[0], dst); // stomps 8 bytes + for (n = 0; n + 32 <= len; n += 32, dst += 32 * 3) { + __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; + __m128i rgb[6]; + + YUV420ToRGB(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV420ToRGB(y + 8, u + 4, v + 4, &R1, &G1, &B1); + YUV420ToRGB(y + 16, u + 8, v + 8, &R2, &G2, &B2); + YUV420ToRGB(y + 24, u + 12, v + 12, &R3, &G3, &B3); + + // Cast to 8b and store as RRRRGGGGBBBB. + rgb[0] = _mm_packus_epi16(R0, R1); + rgb[1] = _mm_packus_epi16(R2, R3); + rgb[2] = _mm_packus_epi16(G0, G1); + rgb[3] = _mm_packus_epi16(G2, G3); + rgb[4] = _mm_packus_epi16(B0, B1); + rgb[5] = _mm_packus_epi16(B2, B3); + + // Pack as RGBRGBRGBRGB. + PlanarTo24b(rgb, dst); + + y += 32; + u += 16; + v += 16; + } + for (; n < len; ++n) { // Finish off + VP8YuvToRgb(y[0], u[0], v[0], dst); dst += 3; - ++y; + y += 1; u += (n & 1); v += (n & 1); } - VP8YuvToRgb(y[0], u[0], v[0], dst); - if (len > 1) { - VP8YuvToRgb(y[1], u[n & 1], v[n & 1], dst + 3); - } } static void YuvToBgrRow(const uint8_t* y, const uint8_t* u, const uint8_t* v, uint8_t* dst, int len) { int n; - for (n = 0; n + 2 < len; ++n) { // we directly stomp the *dst memory - YuvToBgrSSE2(y[0], u[0], v[0], dst); // stomps 8 bytes + for (n = 0; n + 32 <= len; n += 32, dst += 32 * 3) { + __m128i R0, R1, R2, R3, G0, G1, G2, G3, B0, B1, B2, B3; + __m128i bgr[6]; + + YUV420ToRGB(y + 0, u + 0, v + 0, &R0, &G0, &B0); + YUV420ToRGB(y + 8, u + 4, v + 4, &R1, &G1, &B1); + YUV420ToRGB(y + 16, u + 8, v + 8, &R2, &G2, &B2); + YUV420ToRGB(y + 24, u + 12, v + 12, &R3, &G3, &B3); + + // Cast to 8b and store as BBBBGGGGRRRR. + bgr[0] = _mm_packus_epi16(B0, B1); + bgr[1] = _mm_packus_epi16(B2, B3); + bgr[2] = _mm_packus_epi16(G0, G1); + bgr[3] = _mm_packus_epi16(G2, G3); + bgr[4] = _mm_packus_epi16(R0, R1); + bgr[5] = _mm_packus_epi16(R2, R3); + + // Pack as BGRBGRBGRBGR. + PlanarTo24b(bgr, dst); + + y += 32; + u += 16; + v += 16; + } + for (; n < len; ++n) { // Finish off + VP8YuvToBgr(y[0], u[0], v[0], dst); dst += 3; - ++y; + y += 1; u += (n & 1); v += (n & 1); } - VP8YuvToBgr(y[0], u[0], v[0], dst + 0); - if (len > 1) { - VP8YuvToBgr(y[1], u[n & 1], v[n & 1], dst + 3); - } } //------------------------------------------------------------------------------ @@ -316,52 +465,36 @@ WEBP_TSAN_IGNORE_FUNCTION void WebPInitSamplersSSE2(void) { // Store either 16b-words into *dst #define STORE_16(V, dst) _mm_storeu_si128((__m128i*)(dst), (V)) -// Convert 8 packed RGB or BGR samples to r[], g[], b[] +// Function that inserts a value of the second half of the in buffer in between +// every two char of the first half. +static WEBP_INLINE void RGB24PackedToPlanarHelper( + const __m128i* const in /*in[6]*/, __m128i* const out /*out[6]*/) { + out[0] = _mm_unpacklo_epi8(in[0], in[3]); + out[1] = _mm_unpackhi_epi8(in[0], in[3]); + out[2] = _mm_unpacklo_epi8(in[1], in[4]); + out[3] = _mm_unpackhi_epi8(in[1], in[4]); + out[4] = _mm_unpacklo_epi8(in[2], in[5]); + out[5] = _mm_unpackhi_epi8(in[2], in[5]); +} + +// Unpack the 8b input rgbrgbrgbrgb ... as contiguous registers: +// rrrr... rrrr... gggg... gggg... bbbb... bbbb.... +// Similar to PlanarTo24bHelper(), but in reverse order. static WEBP_INLINE void RGB24PackedToPlanar(const uint8_t* const rgb, - __m128i* const r, - __m128i* const g, - __m128i* const b, - int input_is_bgr) { - const __m128i zero = _mm_setzero_si128(); - // in0: r0 g0 b0 r1 | g1 b1 r2 g2 | b2 r3 g3 b3 | r4 g4 b4 r5 - // in1: b2 r3 g3 b3 | r4 g4 b4 r5 | g5 b5 r6 g6 | b6 r7 g7 b7 - const __m128i in0 = LOAD_16(rgb + 0); - const __m128i in1 = LOAD_16(rgb + 8); - // A0: | r2 g2 b2 r3 | g3 b3 r4 g4 | b4 r5 ... - // A1: ... b2 r3 | g3 b3 r4 g4 | b4 r5 g5 b5 | - const __m128i A0 = _mm_srli_si128(in0, 6); - const __m128i A1 = _mm_slli_si128(in1, 6); - // B0: r0 r2 g0 g2 | b0 b2 r1 r3 | g1 g3 b1 b3 | r2 r4 b2 b4 - // B1: g3 g5 b3 b5 | r4 r6 g4 g6 | b4 b6 r5 r7 | g5 g7 b5 b7 - const __m128i B0 = _mm_unpacklo_epi8(in0, A0); - const __m128i B1 = _mm_unpackhi_epi8(A1, in1); - // C0: r1 r3 g1 g3 | b1 b3 r2 r4 | b2 b4 ... - // C1: ... g3 g5 | b3 b5 r4 r6 | g4 g6 b4 b6 - const __m128i C0 = _mm_srli_si128(B0, 6); - const __m128i C1 = _mm_slli_si128(B1, 6); - // D0: r0 r1 r2 r3 | g0 g1 g2 g3 | b0 b1 b2 b3 | r1 r2 r3 r4 - // D1: b3 b4 b5 b6 | r4 r5 r6 r7 | g4 g5 g6 g7 | b4 b5 b6 b7 | - const __m128i D0 = _mm_unpacklo_epi8(B0, C0); - const __m128i D1 = _mm_unpackhi_epi8(C1, B1); - // r4 r5 r6 r7 | g4 g5 g6 g7 | b4 b5 b6 b7 | 0 - const __m128i D2 = _mm_srli_si128(D1, 4); - // r0 r1 r2 r3 | r4 r5 r6 r7 | g0 g1 g2 g3 | g4 g5 g6 g7 - const __m128i E0 = _mm_unpacklo_epi32(D0, D2); - // b0 b1 b2 b3 | b4 b5 b6 b7 | r1 r2 r3 r4 | 0 - const __m128i E1 = _mm_unpackhi_epi32(D0, D2); - // g0 g1 g2 g3 | g4 g5 g6 g7 | 0 - const __m128i E2 = _mm_srli_si128(E0, 8); - const __m128i F0 = _mm_unpacklo_epi8(E0, zero); // -> R - const __m128i F1 = _mm_unpacklo_epi8(E1, zero); // -> B - const __m128i F2 = _mm_unpacklo_epi8(E2, zero); // -> G - *g = F2; - if (input_is_bgr) { - *r = F1; - *b = F0; - } else { - *r = F0; - *b = F1; - } + __m128i* const out /*out[6]*/) { + __m128i tmp[6]; + tmp[0] = _mm_loadu_si128((const __m128i*)(rgb + 0)); + tmp[1] = _mm_loadu_si128((const __m128i*)(rgb + 16)); + tmp[2] = _mm_loadu_si128((const __m128i*)(rgb + 32)); + tmp[3] = _mm_loadu_si128((const __m128i*)(rgb + 48)); + tmp[4] = _mm_loadu_si128((const __m128i*)(rgb + 64)); + tmp[5] = _mm_loadu_si128((const __m128i*)(rgb + 80)); + + RGB24PackedToPlanarHelper(tmp, out); + RGB24PackedToPlanarHelper(out, tmp); + RGB24PackedToPlanarHelper(tmp, out); + RGB24PackedToPlanarHelper(out, tmp); + RGB24PackedToPlanarHelper(tmp, out); } // Convert 8 packed ARGB to r[], g[], b[] @@ -445,15 +578,33 @@ static WEBP_INLINE void ConvertRGBToUV(const __m128i* const R, #undef TRANSFORM static void ConvertRGB24ToY(const uint8_t* rgb, uint8_t* y, int width) { - const int max_width = width & ~15; + const int max_width = width & ~31; int i; - for (i = 0; i < max_width; i += 16, rgb += 3 * 16) { - __m128i r, g, b, Y0, Y1; - RGB24PackedToPlanar(rgb + 0 * 8, &r, &g, &b, 0); - ConvertRGBToY(&r, &g, &b, &Y0); - RGB24PackedToPlanar(rgb + 3 * 8, &r, &g, &b, 0); - ConvertRGBToY(&r, &g, &b, &Y1); - STORE_16(_mm_packus_epi16(Y0, Y1), y + i); + for (i = 0; i < max_width; rgb += 3 * 16 * 2) { + __m128i rgb_plane[6]; + int j; + + RGB24PackedToPlanar(rgb, rgb_plane); + + for (j = 0; j < 2; ++j, i += 16) { + const __m128i zero = _mm_setzero_si128(); + __m128i r, g, b, Y0, Y1; + + // Convert to 16-bit Y. + r = _mm_unpacklo_epi8(rgb_plane[0 + j], zero); + g = _mm_unpacklo_epi8(rgb_plane[2 + j], zero); + b = _mm_unpacklo_epi8(rgb_plane[4 + j], zero); + ConvertRGBToY(&r, &g, &b, &Y0); + + // Convert to 16-bit Y. + r = _mm_unpackhi_epi8(rgb_plane[0 + j], zero); + g = _mm_unpackhi_epi8(rgb_plane[2 + j], zero); + b = _mm_unpackhi_epi8(rgb_plane[4 + j], zero); + ConvertRGBToY(&r, &g, &b, &Y1); + + // Cast to 8-bit and store. + STORE_16(_mm_packus_epi16(Y0, Y1), y + i); + } } for (; i < width; ++i, rgb += 3) { // left-over y[i] = VP8RGBToY(rgb[0], rgb[1], rgb[2], YUV_HALF); @@ -461,15 +612,33 @@ static void ConvertRGB24ToY(const uint8_t* rgb, uint8_t* y, int width) { } static void ConvertBGR24ToY(const uint8_t* bgr, uint8_t* y, int width) { + const int max_width = width & ~31; int i; - const int max_width = width & ~15; - for (i = 0; i < max_width; i += 16, bgr += 3 * 16) { - __m128i r, g, b, Y0, Y1; - RGB24PackedToPlanar(bgr + 0 * 8, &r, &g, &b, 1); - ConvertRGBToY(&r, &g, &b, &Y0); - RGB24PackedToPlanar(bgr + 3 * 8, &r, &g, &b, 1); - ConvertRGBToY(&r, &g, &b, &Y1); - STORE_16(_mm_packus_epi16(Y0, Y1), y + i); + for (i = 0; i < max_width; bgr += 3 * 16 * 2) { + __m128i bgr_plane[6]; + int j; + + RGB24PackedToPlanar(bgr, bgr_plane); + + for (j = 0; j < 2; ++j, i += 16) { + const __m128i zero = _mm_setzero_si128(); + __m128i r, g, b, Y0, Y1; + + // Convert to 16-bit Y. + b = _mm_unpacklo_epi8(bgr_plane[0 + j], zero); + g = _mm_unpacklo_epi8(bgr_plane[2 + j], zero); + r = _mm_unpacklo_epi8(bgr_plane[4 + j], zero); + ConvertRGBToY(&r, &g, &b, &Y0); + + // Convert to 16-bit Y. + b = _mm_unpackhi_epi8(bgr_plane[0 + j], zero); + g = _mm_unpackhi_epi8(bgr_plane[2 + j], zero); + r = _mm_unpackhi_epi8(bgr_plane[4 + j], zero); + ConvertRGBToY(&r, &g, &b, &Y1); + + // Cast to 8-bit and store. + STORE_16(_mm_packus_epi16(Y0, Y1), y + i); + } } for (; i < width; ++i, bgr += 3) { // left-over y[i] = VP8RGBToY(bgr[2], bgr[1], bgr[0], YUV_HALF); diff --git a/drivers/webp/enc/alpha.c b/drivers/webp/enc/alpha.c index 1842b36401..464df4db09 100644 --- a/drivers/webp/enc/alpha.c +++ b/drivers/webp/enc/alpha.c @@ -67,6 +67,11 @@ static int EncodeLossless(const uint8_t* const data, int width, int height, WebPConfigInit(&config); config.lossless = 1; + // Enable exact, or it would alter RGB values of transparent alpha, which is + // normally OK but not here since we are not encoding the input image but an + // internal encoding-related image containing necessary exact information in + // RGB channels. + config.exact = 1; config.method = effort_level; // impact is very small // 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 @@ -74,7 +79,11 @@ static int EncodeLossless(const uint8_t* const data, int width, int height, config.quality = 8.f * effort_level; assert(config.quality >= 0 && config.quality <= 100.f); - ok = (VP8LEncodeStream(&config, &picture, bw) == VP8_ENC_OK); + // TODO(urvang): Temporary fix to avoid generating images that trigger + // a decoder bug related to alpha with color cache. + // See: https://code.google.com/p/webp/issues/detail?id=239 + // Need to re-enable this later. + ok = (VP8LEncodeStream(&config, &picture, bw, 0 /*use_cache*/) == VP8_ENC_OK); WebPPictureFree(&picture); ok = ok && !bw->error_; if (!ok) { @@ -113,7 +122,6 @@ static int EncodeAlphaInternal(const uint8_t* const data, int width, int height, assert(method >= ALPHA_NO_COMPRESSION); assert(method <= ALPHA_LOSSLESS_COMPRESSION); assert(sizeof(header) == ALPHA_HEADER_LEN); - // TODO(skal): have a common function and #define's to validate alpha params. filter_func = WebPFilters[filter]; if (filter_func != NULL) { diff --git a/drivers/webp/enc/backward_references.c b/drivers/webp/enc/backward_references.c index 049125e521..136a24a8c3 100644 --- a/drivers/webp/enc/backward_references.c +++ b/drivers/webp/enc/backward_references.c @@ -16,6 +16,7 @@ #include "./backward_references.h" #include "./histogram.h" #include "../dsp/lossless.h" +#include "../dsp/dsp.h" #include "../utils/color_cache.h" #include "../utils/utils.h" @@ -26,11 +27,19 @@ #define MAX_ENTROPY (1e30f) // 1M window (4M bytes) minus 120 special codes for short distances. -#define WINDOW_SIZE ((1 << 20) - 120) +#define WINDOW_SIZE_BITS 20 +#define WINDOW_SIZE ((1 << WINDOW_SIZE_BITS) - 120) // Bounds for the match length. #define MIN_LENGTH 2 -#define MAX_LENGTH 4096 +// If you change this, you need MAX_LENGTH_BITS + WINDOW_SIZE_BITS <= 32 as it +// is used in VP8LHashChain. +#define MAX_LENGTH_BITS 12 +// We want the max value to be attainable and stored in MAX_LENGTH_BITS bits. +#define MAX_LENGTH ((1 << MAX_LENGTH_BITS) - 1) +#if MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32 +#error "MAX_LENGTH_BITS + WINDOW_SIZE_BITS > 32" +#endif // ----------------------------------------------------------------------------- @@ -56,46 +65,19 @@ static int DistanceToPlaneCode(int xsize, int dist) { return dist + 120; } +// Returns the exact index where array1 and array2 are different. For an index +// inferior or equal to best_len_match, the return value just has to be strictly +// inferior to best_len_match. The current behavior is to return 0 if this index +// is best_len_match, and the index itself otherwise. +// If no two elements are the same, it returns max_limit. static WEBP_INLINE int FindMatchLength(const uint32_t* const array1, const uint32_t* const array2, - int best_len_match, - int max_limit) { -#if !defined(__x86_64__) - // TODO(vrabaud): Compare on other architectures. - int match_len = 0; + int best_len_match, int max_limit) { // Before 'expensive' linear match, check if the two arrays match at the // current best length index. if (array1[best_len_match] != array2[best_len_match]) return 0; - while (match_len < max_limit && array1[match_len] == array2[match_len]) { - ++match_len; - } - return match_len; -#else - const uint32_t* array1_32 = array1; - const uint32_t* array2_32 = array2; - // max value is aligned to (uint64_t*) array1 - const uint32_t* const array1_32_max = array1 + (max_limit & ~1); - // Before 'expensive' linear match, check if the two arrays match at the - // current best length index. - if (array1[best_len_match] != array2[best_len_match]) return 0; - - // TODO(vrabaud): add __predict_true on bound checking? - while (array1_32 < array1_32_max) { - if (*(uint64_t*)array1_32 == *(uint64_t*)array2_32) { - array1_32 += 2; - array2_32 += 2; - } else { - // if the uint32_t pointed to are the same, then the following ones have - // to be different - return (array1_32 - array1) + (*array1_32 == *array2_32); - } - } - - // Deal with the potential last uint32_t. - if ((max_limit & 1) && (*array1_32 != *array2_32)) return max_limit - 1; - return max_limit; -#endif + return VP8LVectorMismatch(array1, array2, max_limit); } // ----------------------------------------------------------------------------- @@ -207,34 +189,24 @@ int VP8LBackwardRefsCopy(const VP8LBackwardRefs* const src, // ----------------------------------------------------------------------------- // Hash chains -// initialize as empty -static void HashChainReset(VP8LHashChain* const p) { - int i; - assert(p != NULL); - for (i = 0; i < p->size_; ++i) { - p->chain_[i] = -1; - } - for (i = 0; i < HASH_SIZE; ++i) { - p->hash_to_first_index_[i] = -1; - } -} - int VP8LHashChainInit(VP8LHashChain* const p, int size) { assert(p->size_ == 0); - assert(p->chain_ == NULL); + assert(p->offset_length_ == NULL); assert(size > 0); - p->chain_ = (int*)WebPSafeMalloc(size, sizeof(*p->chain_)); - if (p->chain_ == NULL) return 0; + p->offset_length_ = + (uint32_t*)WebPSafeMalloc(size, sizeof(*p->offset_length_)); + if (p->offset_length_ == NULL) return 0; p->size_ = size; - HashChainReset(p); + return 1; } void VP8LHashChainClear(VP8LHashChain* const p) { assert(p != NULL); - WebPSafeFree(p->chain_); + WebPSafeFree(p->offset_length_); + p->size_ = 0; - p->chain_ = NULL; + p->offset_length_ = NULL; } // ----------------------------------------------------------------------------- @@ -250,18 +222,10 @@ static WEBP_INLINE uint32_t GetPixPairHash64(const uint32_t* const argb) { return key; } -// Insertion of two pixels at a time. -static void HashChainInsert(VP8LHashChain* const p, - const uint32_t* const argb, int pos) { - const uint32_t hash_code = GetPixPairHash64(argb); - p->chain_[pos] = p->hash_to_first_index_[hash_code]; - p->hash_to_first_index_[hash_code] = pos; -} - // Returns the maximum number of hash chain lookups to do for a -// given compression quality. Return value in range [6, 86]. -static int GetMaxItersForQuality(int quality, int low_effort) { - return (low_effort ? 6 : 8) + (quality * quality) / 128; +// given compression quality. Return value in range [8, 86]. +static int GetMaxItersForQuality(int quality) { + return 8 + (quality * quality) / 128; } static int GetWindowSizeForHashChain(int quality, int xsize) { @@ -277,63 +241,120 @@ static WEBP_INLINE int MaxFindCopyLength(int len) { return (len < MAX_LENGTH) ? len : MAX_LENGTH; } -static void HashChainFindOffset(const VP8LHashChain* const p, int base_position, - const uint32_t* const argb, int len, - int window_size, int* const distance_ptr) { - const uint32_t* const argb_start = argb + base_position; - const int min_pos = - (base_position > window_size) ? base_position - window_size : 0; - int pos; - assert(len <= MAX_LENGTH); - for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)]; - pos >= min_pos; - pos = p->chain_[pos]) { - const int curr_length = - FindMatchLength(argb + pos, argb_start, len - 1, len); - if (curr_length == len) break; - } - *distance_ptr = base_position - pos; -} - -static int HashChainFindCopy(const VP8LHashChain* const p, - int base_position, - const uint32_t* const argb, int max_len, - int window_size, int iter_max, - int* const distance_ptr, - int* const length_ptr) { - const uint32_t* const argb_start = argb + base_position; - int iter = iter_max; - int best_length = 0; - int best_distance = 0; - const int min_pos = - (base_position > window_size) ? base_position - window_size : 0; +int VP8LHashChainFill(VP8LHashChain* const p, int quality, + const uint32_t* const argb, int xsize, int ysize) { + const int size = xsize * ysize; + const int iter_max = GetMaxItersForQuality(quality); + const int iter_min = iter_max - quality / 10; + const uint32_t window_size = GetWindowSizeForHashChain(quality, xsize); int pos; - int length_max = 256; - if (max_len < length_max) { - length_max = max_len; + uint32_t base_position; + int32_t* hash_to_first_index; + // Temporarily use the p->offset_length_ as a hash chain. + int32_t* chain = (int32_t*)p->offset_length_; + assert(p->size_ != 0); + assert(p->offset_length_ != NULL); + + hash_to_first_index = + (int32_t*)WebPSafeMalloc(HASH_SIZE, sizeof(*hash_to_first_index)); + if (hash_to_first_index == NULL) return 0; + + // Set the int32_t array to -1. + memset(hash_to_first_index, 0xff, HASH_SIZE * sizeof(*hash_to_first_index)); + // Fill the chain linking pixels with the same hash. + for (pos = 0; pos < size - 1; ++pos) { + const uint32_t hash_code = GetPixPairHash64(argb + pos); + chain[pos] = hash_to_first_index[hash_code]; + hash_to_first_index[hash_code] = pos; } - for (pos = p->hash_to_first_index_[GetPixPairHash64(argb_start)]; - pos >= min_pos; - pos = p->chain_[pos]) { - int curr_length; - int distance; - if (--iter < 0) { - break; + WebPSafeFree(hash_to_first_index); + + // Find the best match interval at each pixel, defined by an offset to the + // pixel and a length. The right-most pixel cannot match anything to the right + // (hence a best length of 0) and the left-most pixel nothing to the left + // (hence an offset of 0). + p->offset_length_[0] = p->offset_length_[size - 1] = 0; + for (base_position = size - 2 < 0 ? 0 : size - 2; base_position > 0;) { + const int max_len = MaxFindCopyLength(size - 1 - base_position); + const uint32_t* const argb_start = argb + base_position; + int iter = iter_max; + int best_length = 0; + uint32_t best_distance = 0; + const int min_pos = + (base_position > window_size) ? base_position - window_size : 0; + const int length_max = (max_len < 256) ? max_len : 256; + uint32_t max_base_position; + + for (pos = chain[base_position]; pos >= min_pos; pos = chain[pos]) { + int curr_length; + if (--iter < 0) { + break; + } + assert(base_position > (uint32_t)pos); + + curr_length = + FindMatchLength(argb + pos, argb_start, best_length, max_len); + if (best_length < curr_length) { + best_length = curr_length; + best_distance = base_position - pos; + // Stop if we have reached the maximum length. Otherwise, make sure + // we have executed a minimum number of iterations depending on the + // quality. + if ((best_length == MAX_LENGTH) || + (curr_length >= length_max && iter < iter_min)) { + break; + } + } } - - curr_length = FindMatchLength(argb + pos, argb_start, best_length, max_len); - if (best_length < curr_length) { - distance = base_position - pos; - best_length = curr_length; - best_distance = distance; - if (curr_length >= length_max) { + // We have the best match but in case the two intervals continue matching + // to the left, we have the best matches for the left-extended pixels. + max_base_position = base_position; + while (1) { + assert(best_length <= MAX_LENGTH); + assert(best_distance <= WINDOW_SIZE); + p->offset_length_[base_position] = + (best_distance << MAX_LENGTH_BITS) | (uint32_t)best_length; + --base_position; + // Stop if we don't have a match or if we are out of bounds. + if (best_distance == 0 || base_position == 0) break; + // Stop if we cannot extend the matching intervals to the left. + if (base_position < best_distance || + argb[base_position - best_distance] != argb[base_position]) { + break; + } + // Stop if we are matching at its limit because there could be a closer + // matching interval with the same maximum length. Then again, if the + // matching interval is as close as possible (best_distance == 1), we will + // never find anything better so let's continue. + if (best_length == MAX_LENGTH && best_distance != 1 && + base_position + MAX_LENGTH < max_base_position) { break; } + if (best_length < MAX_LENGTH) { + ++best_length; + max_base_position = base_position; + } } } - *distance_ptr = best_distance; - *length_ptr = best_length; - return (best_length >= MIN_LENGTH); + return 1; +} + +static WEBP_INLINE int HashChainFindOffset(const VP8LHashChain* const p, + const int base_position) { + return p->offset_length_[base_position] >> MAX_LENGTH_BITS; +} + +static WEBP_INLINE int HashChainFindLength(const VP8LHashChain* const p, + const int base_position) { + return p->offset_length_[base_position] & ((1U << MAX_LENGTH_BITS) - 1); +} + +static WEBP_INLINE void HashChainFindCopy(const VP8LHashChain* const p, + int base_position, + int* const offset_ptr, + int* const length_ptr) { + *offset_ptr = HashChainFindOffset(p, base_position); + *length_ptr = HashChainFindLength(p, base_position); } static WEBP_INLINE void AddSingleLiteral(uint32_t pixel, int use_color_cache, @@ -400,84 +421,62 @@ static int BackwardReferencesRle(int xsize, int ysize, static int BackwardReferencesLz77(int xsize, int ysize, const uint32_t* const argb, int cache_bits, - int quality, int low_effort, - VP8LHashChain* const hash_chain, + const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs) { int i; + int i_last_check = -1; int ok = 0; int cc_init = 0; const int use_color_cache = (cache_bits > 0); const int pix_count = xsize * ysize; VP8LColorCache hashers; - int iter_max = GetMaxItersForQuality(quality, low_effort); - const int window_size = GetWindowSizeForHashChain(quality, xsize); - int min_matches = 32; if (use_color_cache) { cc_init = VP8LColorCacheInit(&hashers, cache_bits); if (!cc_init) goto Error; } ClearBackwardRefs(refs); - HashChainReset(hash_chain); - for (i = 0; i < pix_count - 2; ) { + for (i = 0; i < pix_count;) { // Alternative#1: Code the pixels starting at 'i' using backward reference. int offset = 0; int len = 0; - const int max_len = MaxFindCopyLength(pix_count - i); - HashChainFindCopy(hash_chain, i, argb, max_len, window_size, - iter_max, &offset, &len); - if (len > MIN_LENGTH || (len == MIN_LENGTH && offset <= 512)) { - int offset2 = 0; - int len2 = 0; - int k; - min_matches = 8; - HashChainInsert(hash_chain, &argb[i], i); - if ((len < (max_len >> 2)) && !low_effort) { - // Evaluate Alternative#2: Insert the pixel at 'i' as literal, and code - // the pixels starting at 'i + 1' using backward reference. - HashChainFindCopy(hash_chain, i + 1, argb, max_len - 1, - window_size, iter_max, &offset2, - &len2); - if (len2 > len + 1) { - AddSingleLiteral(argb[i], use_color_cache, &hashers, refs); - i++; // Backward reference to be done for next pixel. - len = len2; - offset = offset2; - } - } - BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len)); - if (use_color_cache) { - for (k = 0; k < len; ++k) { - VP8LColorCacheInsert(&hashers, argb[i + k]); - } - } - // Add to the hash_chain (but cannot add the last pixel). - if (offset >= 3 && offset != xsize) { - const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i; - for (k = 2; k < last - 8; k += 2) { - HashChainInsert(hash_chain, &argb[i + k], i + k); - } - for (; k < last; ++k) { - HashChainInsert(hash_chain, &argb[i + k], i + k); + int j; + HashChainFindCopy(hash_chain, i, &offset, &len); + if (len > MIN_LENGTH + 1) { + const int len_ini = len; + int max_reach = 0; + assert(i + len < pix_count); + // Only start from what we have not checked already. + i_last_check = (i > i_last_check) ? i : i_last_check; + // We know the best match for the current pixel but we try to find the + // best matches for the current pixel AND the next one combined. + // The naive method would use the intervals: + // [i,i+len) + [i+len, length of best match at i+len) + // while we check if we can use: + // [i,j) (where j<=i+len) + [j, length of best match at j) + for (j = i_last_check + 1; j <= i + len_ini; ++j) { + const int len_j = HashChainFindLength(hash_chain, j); + const int reach = + j + (len_j > MIN_LENGTH + 1 ? len_j : 1); // 1 for single literal. + if (reach > max_reach) { + len = j - i; + max_reach = reach; } } - i += len; } else { + len = 1; + } + // Go with literal or backward reference. + assert(len > 0); + if (len == 1) { AddSingleLiteral(argb[i], use_color_cache, &hashers, refs); - HashChainInsert(hash_chain, &argb[i], i); - ++i; - --min_matches; - if (min_matches <= 0) { - AddSingleLiteral(argb[i], use_color_cache, &hashers, refs); - HashChainInsert(hash_chain, &argb[i], i); - ++i; + } else { + BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len)); + if (use_color_cache) { + for (j = i; j < i + len; ++j) VP8LColorCacheInsert(&hashers, argb[j]); } } - } - while (i < pix_count) { - // Handle the last pixel(s). - AddSingleLiteral(argb[i], use_color_cache, &hashers, refs); - ++i; + i += len; } ok = !refs->error_; @@ -498,7 +497,7 @@ typedef struct { static int BackwardReferencesTraceBackwards( int xsize, int ysize, const uint32_t* const argb, int quality, - int cache_bits, VP8LHashChain* const hash_chain, + int cache_bits, const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs); static void ConvertPopulationCountTableToBitEstimates( @@ -574,16 +573,14 @@ static WEBP_INLINE double GetDistanceCost(const CostModel* const m, return m->distance_[code] + extra_bits; } -static void AddSingleLiteralWithCostModel( - const uint32_t* const argb, VP8LHashChain* const hash_chain, - VP8LColorCache* const hashers, const CostModel* const cost_model, int idx, - int is_last, int use_color_cache, double prev_cost, float* const cost, - uint16_t* const dist_array) { +static void AddSingleLiteralWithCostModel(const uint32_t* const argb, + VP8LColorCache* const hashers, + const CostModel* const cost_model, + int idx, int use_color_cache, + double prev_cost, float* const cost, + uint16_t* const dist_array) { double cost_val = prev_cost; const uint32_t color = argb[0]; - if (!is_last) { - HashChainInsert(hash_chain, argb, idx); - } if (use_color_cache && VP8LColorCacheContains(hashers, color)) { const double mul0 = 0.68; const int ix = VP8LColorCacheGetIndex(hashers, color); @@ -599,30 +596,598 @@ static void AddSingleLiteralWithCostModel( } } +// ----------------------------------------------------------------------------- +// CostManager and interval handling + +// Empirical value to avoid high memory consumption but good for performance. +#define COST_CACHE_INTERVAL_SIZE_MAX 100 + +// To perform backward reference every pixel at index index_ is considered and +// the cost for the MAX_LENGTH following pixels computed. Those following pixels +// at index index_ + k (k from 0 to MAX_LENGTH) have a cost of: +// distance_cost_ at index_ + GetLengthCost(cost_model, k) +// (named cost) (named cached cost) +// and the minimum value is kept. GetLengthCost(cost_model, k) is cached in an +// array of size MAX_LENGTH. +// Instead of performing MAX_LENGTH comparisons per pixel, we keep track of the +// minimal values using intervals, for which lower_ and upper_ bounds are kept. +// An interval is defined by the index_ of the pixel that generated it and +// is only useful in a range of indices from start_ to end_ (exclusive), i.e. +// it contains the minimum value for pixels between start_ and end_. +// Intervals are stored in a linked list and ordered by start_. When a new +// interval has a better minimum, old intervals are split or removed. +typedef struct CostInterval CostInterval; +struct CostInterval { + double lower_; + double upper_; + int start_; + int end_; + double distance_cost_; + int index_; + CostInterval* previous_; + CostInterval* next_; +}; + +// The GetLengthCost(cost_model, k) part of the costs is also bounded for +// efficiency in a set of intervals of a different type. +// If those intervals are small enough, they are not used for comparison and +// written into the costs right away. +typedef struct { + double lower_; // Lower bound of the interval. + double upper_; // Upper bound of the interval. + int start_; + int end_; // Exclusive. + int do_write_; // If !=0, the interval is saved to cost instead of being kept + // for comparison. +} CostCacheInterval; + +// This structure is in charge of managing intervals and costs. +// It caches the different CostCacheInterval, caches the different +// GetLengthCost(cost_model, k) in cost_cache_ and the CostInterval's (whose +// count_ is limited by COST_CACHE_INTERVAL_SIZE_MAX). +#define COST_MANAGER_MAX_FREE_LIST 10 +typedef struct { + CostInterval* head_; + int count_; // The number of stored intervals. + CostCacheInterval* cache_intervals_; + size_t cache_intervals_size_; + double cost_cache_[MAX_LENGTH]; // Contains the GetLengthCost(cost_model, k). + double min_cost_cache_; // The minimum value in cost_cache_[1:]. + double max_cost_cache_; // The maximum value in cost_cache_[1:]. + float* costs_; + uint16_t* dist_array_; + // Most of the time, we only need few intervals -> use a free-list, to avoid + // fragmentation with small allocs in most common cases. + CostInterval intervals_[COST_MANAGER_MAX_FREE_LIST]; + CostInterval* free_intervals_; + // These are regularly malloc'd remains. This list can't grow larger than than + // size COST_CACHE_INTERVAL_SIZE_MAX - COST_MANAGER_MAX_FREE_LIST, note. + CostInterval* recycled_intervals_; + // Buffer used in BackwardReferencesHashChainDistanceOnly to store the ends + // of the intervals that can have impacted the cost at a pixel. + int* interval_ends_; + int interval_ends_size_; +} CostManager; + +static int IsCostCacheIntervalWritable(int start, int end) { + // 100 is the length for which we consider an interval for comparison, and not + // for writing. + // The first intervals are very small and go in increasing size. This constant + // helps merging them into one big interval (up to index 150/200 usually from + // which intervals start getting much bigger). + // This value is empirical. + return (end - start + 1 < 100); +} + +static void CostIntervalAddToFreeList(CostManager* const manager, + CostInterval* const interval) { + interval->next_ = manager->free_intervals_; + manager->free_intervals_ = interval; +} + +static int CostIntervalIsInFreeList(const CostManager* const manager, + const CostInterval* const interval) { + return (interval >= &manager->intervals_[0] && + interval <= &manager->intervals_[COST_MANAGER_MAX_FREE_LIST - 1]); +} + +static void CostManagerInitFreeList(CostManager* const manager) { + int i; + manager->free_intervals_ = NULL; + for (i = 0; i < COST_MANAGER_MAX_FREE_LIST; ++i) { + CostIntervalAddToFreeList(manager, &manager->intervals_[i]); + } +} + +static void DeleteIntervalList(CostManager* const manager, + const CostInterval* interval) { + while (interval != NULL) { + const CostInterval* const next = interval->next_; + if (!CostIntervalIsInFreeList(manager, interval)) { + WebPSafeFree((void*)interval); + } // else: do nothing + interval = next; + } +} + +static void CostManagerClear(CostManager* const manager) { + if (manager == NULL) return; + + WebPSafeFree(manager->costs_); + WebPSafeFree(manager->cache_intervals_); + WebPSafeFree(manager->interval_ends_); + + // Clear the interval lists. + DeleteIntervalList(manager, manager->head_); + manager->head_ = NULL; + DeleteIntervalList(manager, manager->recycled_intervals_); + manager->recycled_intervals_ = NULL; + + // Reset pointers, count_ and cache_intervals_size_. + memset(manager, 0, sizeof(*manager)); + CostManagerInitFreeList(manager); +} + +static int CostManagerInit(CostManager* const manager, + uint16_t* const dist_array, int pix_count, + const CostModel* const cost_model) { + int i; + const int cost_cache_size = (pix_count > MAX_LENGTH) ? MAX_LENGTH : pix_count; + // This constant is tied to the cost_model we use. + // Empirically, differences between intervals is usually of more than 1. + const double min_cost_diff = 0.1; + + manager->costs_ = NULL; + manager->cache_intervals_ = NULL; + manager->interval_ends_ = NULL; + manager->head_ = NULL; + manager->recycled_intervals_ = NULL; + manager->count_ = 0; + manager->dist_array_ = dist_array; + CostManagerInitFreeList(manager); + + // Fill in the cost_cache_. + manager->cache_intervals_size_ = 1; + manager->cost_cache_[0] = 0; + for (i = 1; i < cost_cache_size; ++i) { + manager->cost_cache_[i] = GetLengthCost(cost_model, i); + // Get an approximation of the number of bound intervals. + if (fabs(manager->cost_cache_[i] - manager->cost_cache_[i - 1]) > + min_cost_diff) { + ++manager->cache_intervals_size_; + } + // Compute the minimum of cost_cache_. + if (i == 1) { + manager->min_cost_cache_ = manager->cost_cache_[1]; + manager->max_cost_cache_ = manager->cost_cache_[1]; + } else if (manager->cost_cache_[i] < manager->min_cost_cache_) { + manager->min_cost_cache_ = manager->cost_cache_[i]; + } else if (manager->cost_cache_[i] > manager->max_cost_cache_) { + manager->max_cost_cache_ = manager->cost_cache_[i]; + } + } + + // With the current cost models, we have 15 intervals, so we are safe by + // setting a maximum of COST_CACHE_INTERVAL_SIZE_MAX. + if (manager->cache_intervals_size_ > COST_CACHE_INTERVAL_SIZE_MAX) { + manager->cache_intervals_size_ = COST_CACHE_INTERVAL_SIZE_MAX; + } + manager->cache_intervals_ = (CostCacheInterval*)WebPSafeMalloc( + manager->cache_intervals_size_, sizeof(*manager->cache_intervals_)); + if (manager->cache_intervals_ == NULL) { + CostManagerClear(manager); + return 0; + } + + // Fill in the cache_intervals_. + { + double cost_prev = -1e38f; // unprobably low initial value + CostCacheInterval* prev = NULL; + CostCacheInterval* cur = manager->cache_intervals_; + const CostCacheInterval* const end = + manager->cache_intervals_ + manager->cache_intervals_size_; + + // Consecutive values in cost_cache_ are compared and if a big enough + // difference is found, a new interval is created and bounded. + for (i = 0; i < cost_cache_size; ++i) { + const double cost_val = manager->cost_cache_[i]; + if (i == 0 || + (fabs(cost_val - cost_prev) > min_cost_diff && cur + 1 < end)) { + if (i > 1) { + const int is_writable = + IsCostCacheIntervalWritable(cur->start_, cur->end_); + // Merge with the previous interval if both are writable. + if (is_writable && cur != manager->cache_intervals_ && + prev->do_write_) { + // Update the previous interval. + prev->end_ = cur->end_; + if (cur->lower_ < prev->lower_) { + prev->lower_ = cur->lower_; + } else if (cur->upper_ > prev->upper_) { + prev->upper_ = cur->upper_; + } + } else { + cur->do_write_ = is_writable; + prev = cur; + ++cur; + } + } + // Initialize an interval. + cur->start_ = i; + cur->do_write_ = 0; + cur->lower_ = cost_val; + cur->upper_ = cost_val; + } else { + // Update the current interval bounds. + if (cost_val < cur->lower_) { + cur->lower_ = cost_val; + } else if (cost_val > cur->upper_) { + cur->upper_ = cost_val; + } + } + cur->end_ = i + 1; + cost_prev = cost_val; + } + manager->cache_intervals_size_ = cur + 1 - manager->cache_intervals_; + } + + manager->costs_ = (float*)WebPSafeMalloc(pix_count, sizeof(*manager->costs_)); + if (manager->costs_ == NULL) { + CostManagerClear(manager); + return 0; + } + // Set the initial costs_ high for every pixel as we will keep the minimum. + for (i = 0; i < pix_count; ++i) manager->costs_[i] = 1e38f; + + // The cost at pixel is influenced by the cost intervals from previous pixels. + // Let us take the specific case where the offset is the same (which actually + // happens a lot in case of uniform regions). + // pixel i contributes to j>i a cost of: offset cost + cost_cache_[j-i] + // pixel i+1 contributes to j>i a cost of: 2*offset cost + cost_cache_[j-i-1] + // pixel i+2 contributes to j>i a cost of: 3*offset cost + cost_cache_[j-i-2] + // and so on. + // A pixel i influences the following length(j) < MAX_LENGTH pixels. What is + // the value of j such that pixel i + j cannot influence any of those pixels? + // This value is such that: + // max of cost_cache_ < j*offset cost + min of cost_cache_ + // (pixel i + j 's cost cannot beat the worst cost given by pixel i). + // This value will be used to optimize the cost computation in + // BackwardReferencesHashChainDistanceOnly. + { + // The offset cost is computed in GetDistanceCost and has a minimum value of + // the minimum in cost_model->distance_. The case where the offset cost is 0 + // will be dealt with differently later so we are only interested in the + // minimum non-zero offset cost. + double offset_cost_min = 0.; + int size; + for (i = 0; i < NUM_DISTANCE_CODES; ++i) { + if (cost_model->distance_[i] != 0) { + if (offset_cost_min == 0.) { + offset_cost_min = cost_model->distance_[i]; + } else if (cost_model->distance_[i] < offset_cost_min) { + offset_cost_min = cost_model->distance_[i]; + } + } + } + // In case all the cost_model->distance_ is 0, the next non-zero cost we + // can have is from the extra bit in GetDistanceCost, hence 1. + if (offset_cost_min < 1.) offset_cost_min = 1.; + + size = 1 + (int)ceil((manager->max_cost_cache_ - manager->min_cost_cache_) / + offset_cost_min); + // Empirically, we usually end up with a value below 100. + if (size > MAX_LENGTH) size = MAX_LENGTH; + + manager->interval_ends_ = + (int*)WebPSafeMalloc(size, sizeof(*manager->interval_ends_)); + if (manager->interval_ends_ == NULL) { + CostManagerClear(manager); + return 0; + } + manager->interval_ends_size_ = size; + } + + return 1; +} + +// Given the distance_cost for pixel 'index', update the cost at pixel 'i' if it +// is smaller than the previously computed value. +static WEBP_INLINE void UpdateCost(CostManager* const manager, int i, int index, + double distance_cost) { + int k = i - index; + double cost_tmp; + assert(k >= 0 && k < MAX_LENGTH); + cost_tmp = distance_cost + manager->cost_cache_[k]; + + if (manager->costs_[i] > cost_tmp) { + manager->costs_[i] = (float)cost_tmp; + manager->dist_array_[i] = k + 1; + } +} + +// Given the distance_cost for pixel 'index', update the cost for all the pixels +// between 'start' and 'end' excluded. +static WEBP_INLINE void UpdateCostPerInterval(CostManager* const manager, + int start, int end, int index, + double distance_cost) { + int i; + for (i = start; i < end; ++i) UpdateCost(manager, i, index, distance_cost); +} + +// Given two intervals, make 'prev' be the previous one of 'next' in 'manager'. +static WEBP_INLINE void ConnectIntervals(CostManager* const manager, + CostInterval* const prev, + CostInterval* const next) { + if (prev != NULL) { + prev->next_ = next; + } else { + manager->head_ = next; + } + + if (next != NULL) next->previous_ = prev; +} + +// Pop an interval in the manager. +static WEBP_INLINE void PopInterval(CostManager* const manager, + CostInterval* const interval) { + CostInterval* const next = interval->next_; + + if (interval == NULL) return; + + ConnectIntervals(manager, interval->previous_, next); + if (CostIntervalIsInFreeList(manager, interval)) { + CostIntervalAddToFreeList(manager, interval); + } else { // recycle regularly malloc'd intervals too + interval->next_ = manager->recycled_intervals_; + manager->recycled_intervals_ = interval; + } + --manager->count_; + assert(manager->count_ >= 0); +} + +// Update the cost at index i by going over all the stored intervals that +// overlap with i. +static WEBP_INLINE void UpdateCostPerIndex(CostManager* const manager, int i) { + CostInterval* current = manager->head_; + + while (current != NULL && current->start_ <= i) { + if (current->end_ <= i) { + // We have an outdated interval, remove it. + CostInterval* next = current->next_; + PopInterval(manager, current); + current = next; + } else { + UpdateCost(manager, i, current->index_, current->distance_cost_); + current = current->next_; + } + } +} + +// Given a current orphan interval and its previous interval, before +// it was orphaned (which can be NULL), set it at the right place in the list +// of intervals using the start_ ordering and the previous interval as a hint. +static WEBP_INLINE void PositionOrphanInterval(CostManager* const manager, + CostInterval* const current, + CostInterval* previous) { + assert(current != NULL); + + if (previous == NULL) previous = manager->head_; + while (previous != NULL && current->start_ < previous->start_) { + previous = previous->previous_; + } + while (previous != NULL && previous->next_ != NULL && + previous->next_->start_ < current->start_) { + previous = previous->next_; + } + + if (previous != NULL) { + ConnectIntervals(manager, current, previous->next_); + } else { + ConnectIntervals(manager, current, manager->head_); + } + ConnectIntervals(manager, previous, current); +} + +// Insert an interval in the list contained in the manager by starting at +// interval_in as a hint. The intervals are sorted by start_ value. +static WEBP_INLINE void InsertInterval(CostManager* const manager, + CostInterval* const interval_in, + double distance_cost, double lower, + double upper, int index, int start, + int end) { + CostInterval* interval_new; + + if (IsCostCacheIntervalWritable(start, end) || + manager->count_ >= COST_CACHE_INTERVAL_SIZE_MAX) { + // Write down the interval if it is too small. + UpdateCostPerInterval(manager, start, end, index, distance_cost); + return; + } + if (manager->free_intervals_ != NULL) { + interval_new = manager->free_intervals_; + manager->free_intervals_ = interval_new->next_; + } else if (manager->recycled_intervals_ != NULL) { + interval_new = manager->recycled_intervals_; + manager->recycled_intervals_ = interval_new->next_; + } else { // malloc for good + interval_new = (CostInterval*)WebPSafeMalloc(1, sizeof(*interval_new)); + if (interval_new == NULL) { + // Write down the interval if we cannot create it. + UpdateCostPerInterval(manager, start, end, index, distance_cost); + return; + } + } + + interval_new->distance_cost_ = distance_cost; + interval_new->lower_ = lower; + interval_new->upper_ = upper; + interval_new->index_ = index; + interval_new->start_ = start; + interval_new->end_ = end; + PositionOrphanInterval(manager, interval_new, interval_in); + + ++manager->count_; +} + +// When an interval has its start_ or end_ modified, it needs to be +// repositioned in the linked list. +static WEBP_INLINE void RepositionInterval(CostManager* const manager, + CostInterval* const interval) { + if (IsCostCacheIntervalWritable(interval->start_, interval->end_)) { + // Maybe interval has been resized and is small enough to be removed. + UpdateCostPerInterval(manager, interval->start_, interval->end_, + interval->index_, interval->distance_cost_); + PopInterval(manager, interval); + return; + } + + // Early exit if interval is at the right spot. + if ((interval->previous_ == NULL || + interval->previous_->start_ <= interval->start_) && + (interval->next_ == NULL || + interval->start_ <= interval->next_->start_)) { + return; + } + + ConnectIntervals(manager, interval->previous_, interval->next_); + PositionOrphanInterval(manager, interval, interval->previous_); +} + +// Given a new cost interval defined by its start at index, its last value and +// distance_cost, add its contributions to the previous intervals and costs. +// If handling the interval or one of its subintervals becomes to heavy, its +// contribution is added to the costs right away. +static WEBP_INLINE void PushInterval(CostManager* const manager, + double distance_cost, int index, + int last) { + size_t i; + CostInterval* interval = manager->head_; + CostInterval* interval_next; + const CostCacheInterval* const cost_cache_intervals = + manager->cache_intervals_; + + for (i = 0; i < manager->cache_intervals_size_ && + cost_cache_intervals[i].start_ < last; + ++i) { + // Define the intersection of the ith interval with the new one. + int start = index + cost_cache_intervals[i].start_; + const int end = index + (cost_cache_intervals[i].end_ > last + ? last + : cost_cache_intervals[i].end_); + const double lower_in = cost_cache_intervals[i].lower_; + const double upper_in = cost_cache_intervals[i].upper_; + const double lower_full_in = distance_cost + lower_in; + const double upper_full_in = distance_cost + upper_in; + + if (cost_cache_intervals[i].do_write_) { + UpdateCostPerInterval(manager, start, end, index, distance_cost); + continue; + } + + for (; interval != NULL && interval->start_ < end && start < end; + interval = interval_next) { + const double lower_full_interval = + interval->distance_cost_ + interval->lower_; + const double upper_full_interval = + interval->distance_cost_ + interval->upper_; + + interval_next = interval->next_; + + // Make sure we have some overlap + if (start >= interval->end_) continue; + + if (lower_full_in >= upper_full_interval) { + // When intervals are represented, the lower, the better. + // [**********************************************************] + // start end + // [----------------------------------] + // interval->start_ interval->end_ + // If we are worse than what we already have, add whatever we have so + // far up to interval. + const int start_new = interval->end_; + InsertInterval(manager, interval, distance_cost, lower_in, upper_in, + index, start, interval->start_); + start = start_new; + continue; + } + + // We know the two intervals intersect. + if (upper_full_in >= lower_full_interval) { + // There is no clear cut on which is best, so let's keep both. + // [*********[*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*]***********] + // start interval->start_ interval->end_ end + // OR + // [*********[*-*-*-*-*-*-*-*-*-*-*-]----------------------] + // start interval->start_ end interval->end_ + const int end_new = (interval->end_ <= end) ? interval->end_ : end; + InsertInterval(manager, interval, distance_cost, lower_in, upper_in, + index, start, end_new); + start = end_new; + } else if (start <= interval->start_ && interval->end_ <= end) { + // [----------------------------------] + // interval->start_ interval->end_ + // [**************************************************************] + // start end + // We can safely remove the old interval as it is fully included. + PopInterval(manager, interval); + } else { + if (interval->start_ <= start && end <= interval->end_) { + // [--------------------------------------------------------------] + // interval->start_ interval->end_ + // [*****************************] + // start end + // We have to split the old interval as it fully contains the new one. + const int end_original = interval->end_; + interval->end_ = start; + InsertInterval(manager, interval, interval->distance_cost_, + interval->lower_, interval->upper_, interval->index_, + end, end_original); + } else if (interval->start_ < start) { + // [------------------------------------] + // interval->start_ interval->end_ + // [*****************************] + // start end + interval->end_ = start; + } else { + // [------------------------------------] + // interval->start_ interval->end_ + // [*****************************] + // start end + interval->start_ = end; + } + + // The interval has been modified, we need to reposition it or write it. + RepositionInterval(manager, interval); + } + } + // Insert the remaining interval from start to end. + InsertInterval(manager, interval, distance_cost, lower_in, upper_in, index, + start, end); + } +} + static int BackwardReferencesHashChainDistanceOnly( - int xsize, int ysize, const uint32_t* const argb, - int quality, int cache_bits, VP8LHashChain* const hash_chain, + int xsize, int ysize, const uint32_t* const argb, int quality, + int cache_bits, const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs, uint16_t* const dist_array) { int i; int ok = 0; int cc_init = 0; const int pix_count = xsize * ysize; const int use_color_cache = (cache_bits > 0); - float* const cost = - (float*)WebPSafeMalloc(pix_count, sizeof(*cost)); const size_t literal_array_size = sizeof(double) * (NUM_LITERAL_CODES + NUM_LENGTH_CODES + ((cache_bits > 0) ? (1 << cache_bits) : 0)); const size_t cost_model_size = sizeof(CostModel) + literal_array_size; CostModel* const cost_model = - (CostModel*)WebPSafeMalloc(1ULL, cost_model_size); + (CostModel*)WebPSafeCalloc(1ULL, cost_model_size); VP8LColorCache hashers; const int skip_length = 32 + quality; const int skip_min_distance_code = 2; - int iter_max = GetMaxItersForQuality(quality, 0); - const int window_size = GetWindowSizeForHashChain(quality, xsize); + CostManager* cost_manager = + (CostManager*)WebPSafeMalloc(1ULL, sizeof(*cost_manager)); - if (cost == NULL || cost_model == NULL) goto Error; + if (cost_model == NULL || cost_manager == NULL) goto Error; cost_model->literal_ = (double*)(cost_model + 1); if (use_color_cache) { @@ -634,34 +1199,91 @@ static int BackwardReferencesHashChainDistanceOnly( goto Error; } - for (i = 0; i < pix_count; ++i) cost[i] = 1e38f; + if (!CostManagerInit(cost_manager, dist_array, pix_count, cost_model)) { + goto Error; + } // We loop one pixel at a time, but store all currently best points to // non-processed locations from this point. dist_array[0] = 0; - HashChainReset(hash_chain); // Add first pixel as literal. - AddSingleLiteralWithCostModel(argb + 0, hash_chain, &hashers, cost_model, 0, - 0, use_color_cache, 0.0, cost, dist_array); + AddSingleLiteralWithCostModel(argb + 0, &hashers, cost_model, 0, + use_color_cache, 0.0, cost_manager->costs_, + dist_array); + for (i = 1; i < pix_count - 1; ++i) { - int offset = 0; - int len = 0; - double prev_cost = cost[i - 1]; - const int max_len = MaxFindCopyLength(pix_count - i); - HashChainFindCopy(hash_chain, i, argb, max_len, window_size, - iter_max, &offset, &len); + int offset = 0, len = 0; + double prev_cost = cost_manager->costs_[i - 1]; + HashChainFindCopy(hash_chain, i, &offset, &len); if (len >= MIN_LENGTH) { const int code = DistanceToPlaneCode(xsize, offset); - const double distance_cost = - prev_cost + GetDistanceCost(cost_model, code); - int k; - for (k = 1; k < len; ++k) { - const double cost_val = distance_cost + GetLengthCost(cost_model, k); - if (cost[i + k] > cost_val) { - cost[i + k] = (float)cost_val; - dist_array[i + k] = k + 1; + const double offset_cost = GetDistanceCost(cost_model, code); + const int first_i = i; + int j_max = 0, interval_ends_index = 0; + const int is_offset_zero = (offset_cost == 0.); + + if (!is_offset_zero) { + j_max = (int)ceil( + (cost_manager->max_cost_cache_ - cost_manager->min_cost_cache_) / + offset_cost); + if (j_max < 1) { + j_max = 1; + } else if (j_max > cost_manager->interval_ends_size_ - 1) { + // This could only happen in the case of MAX_LENGTH. + j_max = cost_manager->interval_ends_size_ - 1; + } + } // else j_max is unused anyway. + + // Instead of considering all contributions from a pixel i by calling: + // PushInterval(cost_manager, prev_cost + offset_cost, i, len); + // we optimize these contributions in case offset_cost stays the same for + // consecutive pixels. This describes a set of pixels similar to a + // previous set (e.g. constant color regions). + for (; i < pix_count - 1; ++i) { + int offset_next, len_next; + prev_cost = cost_manager->costs_[i - 1]; + + if (is_offset_zero) { + // No optimization can be made so we just push all of the + // contributions from i. + PushInterval(cost_manager, prev_cost, i, len); + } else { + // j_max is chosen as the smallest j such that: + // max of cost_cache_ < j*offset cost + min of cost_cache_ + // Therefore, the pixel influenced by i-j_max, cannot be influenced + // by i. Only the costs after the end of what i contributed need to be + // updated. cost_manager->interval_ends_ is a circular buffer that + // stores those ends. + const double distance_cost = prev_cost + offset_cost; + int j = cost_manager->interval_ends_[interval_ends_index]; + if (i - first_i <= j_max || + !IsCostCacheIntervalWritable(j, i + len)) { + PushInterval(cost_manager, distance_cost, i, len); + } else { + for (; j < i + len; ++j) { + UpdateCost(cost_manager, j, i, distance_cost); + } + } + // Store the new end in the circular buffer. + assert(interval_ends_index < cost_manager->interval_ends_size_); + cost_manager->interval_ends_[interval_ends_index] = i + len; + if (++interval_ends_index > j_max) interval_ends_index = 0; } + + // Check whether i is the last pixel to consider, as it is handled + // differently. + if (i + 1 >= pix_count - 1) break; + HashChainFindCopy(hash_chain, i + 1, &offset_next, &len_next); + if (offset_next != offset) break; + len = len_next; + UpdateCostPerIndex(cost_manager, i); + AddSingleLiteralWithCostModel(argb + i, &hashers, cost_model, i, + use_color_cache, prev_cost, + cost_manager->costs_, dist_array); } + // Submit the last pixel. + UpdateCostPerIndex(cost_manager, i + 1); + // This if is for speedup only. It roughly doubles the speed, and // makes compression worse by .1 %. if (len >= skip_length && code <= skip_min_distance_code) { @@ -669,53 +1291,55 @@ static int BackwardReferencesHashChainDistanceOnly( // lookups for better copies. // 1) insert the hashes. if (use_color_cache) { + int k; for (k = 0; k < len; ++k) { VP8LColorCacheInsert(&hashers, argb[i + k]); } } - // 2) Add to the hash_chain (but cannot add the last pixel) + // 2) jump. { - const int last = (len + i < pix_count - 1) ? len + i - : pix_count - 1; - for (k = i; k < last; ++k) { - HashChainInsert(hash_chain, &argb[k], k); - } + const int i_next = i + len - 1; // for loop does ++i, thus -1 here. + for (; i <= i_next; ++i) UpdateCostPerIndex(cost_manager, i + 1); + i = i_next; } - // 3) jump. - i += len - 1; // for loop does ++i, thus -1 here. goto next_symbol; } - if (len != MIN_LENGTH) { + if (len > MIN_LENGTH) { int code_min_length; double cost_total; - HashChainFindOffset(hash_chain, i, argb, MIN_LENGTH, window_size, - &offset); + offset = HashChainFindOffset(hash_chain, i); code_min_length = DistanceToPlaneCode(xsize, offset); cost_total = prev_cost + GetDistanceCost(cost_model, code_min_length) + GetLengthCost(cost_model, 1); - if (cost[i + 1] > cost_total) { - cost[i + 1] = (float)cost_total; + if (cost_manager->costs_[i + 1] > cost_total) { + cost_manager->costs_[i + 1] = (float)cost_total; dist_array[i + 1] = 2; } } + } else { // len < MIN_LENGTH + UpdateCostPerIndex(cost_manager, i + 1); } - AddSingleLiteralWithCostModel(argb + i, hash_chain, &hashers, cost_model, i, - 0, use_color_cache, prev_cost, cost, - dist_array); + + AddSingleLiteralWithCostModel(argb + i, &hashers, cost_model, i, + use_color_cache, prev_cost, + cost_manager->costs_, dist_array); + next_symbol: ; } // Handle the last pixel. if (i == (pix_count - 1)) { - AddSingleLiteralWithCostModel(argb + i, hash_chain, &hashers, cost_model, i, - 1, use_color_cache, cost[pix_count - 2], cost, - dist_array); + AddSingleLiteralWithCostModel( + argb + i, &hashers, cost_model, i, use_color_cache, + cost_manager->costs_[pix_count - 2], cost_manager->costs_, dist_array); } + ok = !refs->error_; Error: if (cc_init) VP8LColorCacheClear(&hashers); + CostManagerClear(cost_manager); WebPSafeFree(cost_model); - WebPSafeFree(cost); + WebPSafeFree(cost_manager); return ok; } @@ -739,18 +1363,14 @@ static void TraceBackwards(uint16_t* const dist_array, } static int BackwardReferencesHashChainFollowChosenPath( - int xsize, int ysize, const uint32_t* const argb, - int quality, int cache_bits, + const uint32_t* const argb, int cache_bits, const uint16_t* const chosen_path, int chosen_path_size, - VP8LHashChain* const hash_chain, - VP8LBackwardRefs* const refs) { - const int pix_count = xsize * ysize; + const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs) { const int use_color_cache = (cache_bits > 0); int ix; int i = 0; int ok = 0; int cc_init = 0; - const int window_size = GetWindowSizeForHashChain(quality, xsize); VP8LColorCache hashers; if (use_color_cache) { @@ -759,25 +1379,17 @@ static int BackwardReferencesHashChainFollowChosenPath( } ClearBackwardRefs(refs); - HashChainReset(hash_chain); for (ix = 0; ix < chosen_path_size; ++ix) { - int offset = 0; const int len = chosen_path[ix]; if (len != 1) { int k; - HashChainFindOffset(hash_chain, i, argb, len, window_size, &offset); + const int offset = HashChainFindOffset(hash_chain, i); BackwardRefsCursorAdd(refs, PixOrCopyCreateCopy(offset, len)); if (use_color_cache) { for (k = 0; k < len; ++k) { VP8LColorCacheInsert(&hashers, argb[i + k]); } } - { - const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i; - for (k = 0; k < last; ++k) { - HashChainInsert(hash_chain, &argb[i + k], i + k); - } - } i += len; } else { PixOrCopy v; @@ -790,9 +1402,6 @@ static int BackwardReferencesHashChainFollowChosenPath( v = PixOrCopyCreateLiteral(argb[i]); } BackwardRefsCursorAdd(refs, v); - if (i + 1 < pix_count) { - HashChainInsert(hash_chain, &argb[i], i); - } ++i; } } @@ -803,11 +1412,10 @@ static int BackwardReferencesHashChainFollowChosenPath( } // Returns 1 on success. -static int BackwardReferencesTraceBackwards(int xsize, int ysize, - const uint32_t* const argb, - int quality, int cache_bits, - VP8LHashChain* const hash_chain, - VP8LBackwardRefs* const refs) { +static int BackwardReferencesTraceBackwards( + int xsize, int ysize, const uint32_t* const argb, int quality, + int cache_bits, const VP8LHashChain* const hash_chain, + VP8LBackwardRefs* const refs) { int ok = 0; const int dist_array_size = xsize * ysize; uint16_t* chosen_path = NULL; @@ -824,8 +1432,7 @@ static int BackwardReferencesTraceBackwards(int xsize, int ysize, } TraceBackwards(dist_array, dist_array_size, &chosen_path, &chosen_path_size); if (!BackwardReferencesHashChainFollowChosenPath( - xsize, ysize, argb, quality, cache_bits, chosen_path, chosen_path_size, - hash_chain, refs)) { + argb, cache_bits, chosen_path, chosen_path_size, hash_chain, refs)) { goto Error; } ok = 1; @@ -913,7 +1520,7 @@ static double ComputeCacheEntropy(const uint32_t* argb, // Returns 0 in case of memory error. static int CalculateBestCacheSize(const uint32_t* const argb, int xsize, int ysize, int quality, - VP8LHashChain* const hash_chain, + const VP8LHashChain* const hash_chain, VP8LBackwardRefs* const refs, int* const lz77_computed, int* const best_cache_bits) { @@ -933,8 +1540,8 @@ static int CalculateBestCacheSize(const uint32_t* const argb, // Local color cache is disabled. return 1; } - if (!BackwardReferencesLz77(xsize, ysize, argb, cache_bits_low, quality, 0, - hash_chain, refs)) { + if (!BackwardReferencesLz77(xsize, ysize, argb, cache_bits_low, hash_chain, + refs)) { return 0; } // Do a binary search to find the optimal entropy for cache_bits. @@ -999,13 +1606,12 @@ static int BackwardRefsWithLocalCache(const uint32_t* const argb, } static VP8LBackwardRefs* GetBackwardReferencesLowEffort( - int width, int height, const uint32_t* const argb, int quality, - int* const cache_bits, VP8LHashChain* const hash_chain, + int width, int height, const uint32_t* const argb, + int* const cache_bits, const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2]) { VP8LBackwardRefs* refs_lz77 = &refs_array[0]; *cache_bits = 0; - if (!BackwardReferencesLz77(width, height, argb, 0, quality, - 1 /* Low effort. */, hash_chain, refs_lz77)) { + if (!BackwardReferencesLz77(width, height, argb, 0, hash_chain, refs_lz77)) { return NULL; } BackwardReferences2DLocality(width, refs_lz77); @@ -1014,7 +1620,7 @@ static VP8LBackwardRefs* GetBackwardReferencesLowEffort( static VP8LBackwardRefs* GetBackwardReferences( int width, int height, const uint32_t* const argb, int quality, - int* const cache_bits, VP8LHashChain* const hash_chain, + int* const cache_bits, const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2]) { int lz77_is_useful; int lz77_computed; @@ -1037,8 +1643,8 @@ static VP8LBackwardRefs* GetBackwardReferences( } } } else { - if (!BackwardReferencesLz77(width, height, argb, *cache_bits, quality, - 0 /* Low effort. */, hash_chain, refs_lz77)) { + if (!BackwardReferencesLz77(width, height, argb, *cache_bits, hash_chain, + refs_lz77)) { goto Error; } } @@ -1097,11 +1703,11 @@ static VP8LBackwardRefs* GetBackwardReferences( 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_array[2]) { + int low_effort, int* const cache_bits, + const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2]) { if (low_effort) { - return GetBackwardReferencesLowEffort(width, height, argb, quality, - cache_bits, hash_chain, refs_array); + return GetBackwardReferencesLowEffort(width, height, argb, cache_bits, + hash_chain, refs_array); } else { return GetBackwardReferences(width, height, argb, quality, cache_bits, hash_chain, refs_array); diff --git a/drivers/webp/enc/backward_references.h b/drivers/webp/enc/backward_references.h index e410b06f7d..b72a01fb0e 100644 --- a/drivers/webp/enc/backward_references.h +++ b/drivers/webp/enc/backward_references.h @@ -115,11 +115,12 @@ static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) { 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_; + // The 20 most significant bits contain the offset at which the best match + // is found. These 20 bits are the limit defined by GetWindowSizeForHashChain + // (through WINDOW_SIZE = 1<<20). + // The lower 12 bits contain the length of the match. The 12 bit limit is + // defined in MaxFindCopyLength with MAX_LENGTH=4096. + uint32_t* offset_length_; // 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_; @@ -127,6 +128,9 @@ struct VP8LHashChain { // Must be called first, to set size. int VP8LHashChainInit(VP8LHashChain* const p, int size); +// Pre-compute the best matches for argb. +int VP8LHashChainFill(VP8LHashChain* const p, int quality, + const uint32_t* const argb, int xsize, int ysize); void VP8LHashChainClear(VP8LHashChain* const p); // release memory // ----------------------------------------------------------------------------- @@ -192,8 +196,8 @@ static WEBP_INLINE void VP8LRefsCursorNext(VP8LRefsCursor* const c) { // 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]); + int low_effort, int* const cache_bits, + const VP8LHashChain* const hash_chain, VP8LBackwardRefs refs[2]); #ifdef __cplusplus } diff --git a/drivers/webp/enc/filter.c b/drivers/webp/enc/filter.c index 1a4dd947fb..e8ea8b4ff2 100644 --- a/drivers/webp/enc/filter.c +++ b/drivers/webp/enc/filter.c @@ -107,10 +107,9 @@ static void DoFilter(const VP8EncIterator* const it, int level) { //------------------------------------------------------------------------------ // SSIM metric -enum { KERNEL = 3 }; static const double kMinValue = 1.e-10; // minimal threshold -void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst) { +void VP8SSIMAddStats(const VP8DistoStats* const src, VP8DistoStats* const dst) { dst->w += src->w; dst->xm += src->xm; dst->ym += src->ym; @@ -119,32 +118,7 @@ void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst) { dst->yym += src->yym; } -static void VP8SSIMAccumulate(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - int xo, int yo, int W, int H, - DistoStats* const stats) { - const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL; - const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL; - const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL; - const int xmax = (xo + KERNEL > W - 1) ? W - 1 : xo + KERNEL; - int x, y; - src1 += ymin * stride1; - src2 += ymin * stride2; - for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) { - for (x = xmin; x <= xmax; ++x) { - const int s1 = src1[x]; - const int s2 = src2[x]; - stats->w += 1; - stats->xm += s1; - stats->ym += s2; - stats->xxm += s1 * s1; - stats->xym += s1 * s2; - stats->yym += s2 * s2; - } - } -} - -double VP8SSIMGet(const DistoStats* const stats) { +double VP8SSIMGet(const VP8DistoStats* const stats) { const double xmxm = stats->xm * stats->xm; const double ymym = stats->ym * stats->ym; const double xmym = stats->xm * stats->ym; @@ -165,7 +139,7 @@ double VP8SSIMGet(const DistoStats* const stats) { return (fden != 0.) ? fnum / fden : kMinValue; } -double VP8SSIMGetSquaredError(const DistoStats* const s) { +double VP8SSIMGetSquaredError(const VP8DistoStats* const s) { if (s->w > 0.) { const double iw2 = 1. / (s->w * s->w); const double sxx = s->xxm * s->w - s->xm * s->xm; @@ -177,34 +151,66 @@ double VP8SSIMGetSquaredError(const DistoStats* const s) { return kMinValue; } +#define LIMIT(A, M) ((A) > (M) ? (M) : (A)) +static void VP8SSIMAccumulateRow(const uint8_t* src1, int stride1, + const uint8_t* src2, int stride2, + int y, int W, int H, + VP8DistoStats* const stats) { + int x = 0; + const int w0 = LIMIT(VP8_SSIM_KERNEL, W); + for (x = 0; x < w0; ++x) { + VP8SSIMAccumulateClipped(src1, stride1, src2, stride2, x, y, W, H, stats); + } + for (; x <= W - 8 + VP8_SSIM_KERNEL; ++x) { + VP8SSIMAccumulate( + src1 + (y - VP8_SSIM_KERNEL) * stride1 + (x - VP8_SSIM_KERNEL), stride1, + src2 + (y - VP8_SSIM_KERNEL) * stride2 + (x - VP8_SSIM_KERNEL), stride2, + stats); + } + for (; x < W; ++x) { + VP8SSIMAccumulateClipped(src1, stride1, src2, stride2, x, y, W, H, stats); + } +} + void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1, const uint8_t* src2, int stride2, - int W, int H, DistoStats* const stats) { + int W, int H, VP8DistoStats* const stats) { int x, y; - for (y = 0; y < H; ++y) { + const int h0 = LIMIT(VP8_SSIM_KERNEL, H); + const int h1 = LIMIT(VP8_SSIM_KERNEL, H - VP8_SSIM_KERNEL); + for (y = 0; y < h0; ++y) { + for (x = 0; x < W; ++x) { + VP8SSIMAccumulateClipped(src1, stride1, src2, stride2, x, y, W, H, stats); + } + } + for (; y < h1; ++y) { + VP8SSIMAccumulateRow(src1, stride1, src2, stride2, y, W, H, stats); + } + for (; y < H; ++y) { for (x = 0; x < W; ++x) { - VP8SSIMAccumulate(src1, stride1, src2, stride2, x, y, W, H, stats); + VP8SSIMAccumulateClipped(src1, stride1, src2, stride2, x, y, W, H, stats); } } } +#undef LIMIT static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) { int x, y; - DistoStats s = { .0, .0, .0, .0, .0, .0 }; + VP8DistoStats s = { .0, .0, .0, .0, .0, .0 }; // compute SSIM in a 10 x 10 window - for (x = 3; x < 13; x++) { - for (y = 3; y < 13; y++) { - VP8SSIMAccumulate(yuv1 + Y_OFF_ENC, BPS, yuv2 + Y_OFF_ENC, BPS, - x, y, 16, 16, &s); + for (y = VP8_SSIM_KERNEL; y < 16 - VP8_SSIM_KERNEL; y++) { + for (x = VP8_SSIM_KERNEL; x < 16 - VP8_SSIM_KERNEL; x++) { + VP8SSIMAccumulateClipped(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_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); + VP8SSIMAccumulateClipped(yuv1 + U_OFF_ENC, BPS, yuv2 + U_OFF_ENC, BPS, + x, y, 8, 8, &s); + VP8SSIMAccumulateClipped(yuv1 + V_OFF_ENC, BPS, yuv2 + V_OFF_ENC, BPS, + x, y, 8, 8, &s); } } return VP8SSIMGet(&s); @@ -222,6 +228,7 @@ void VP8InitFilter(VP8EncIterator* const it) { (*it->lf_stats_)[s][i] = 0; } } + VP8SSIMDspInit(); } } @@ -229,7 +236,7 @@ void VP8StoreFilterStats(VP8EncIterator* const it) { int d; VP8Encoder* const enc = it->enc_; const int s = it->mb_->segment_; - const int level0 = enc->dqm_[s].fstrength_; // TODO: ref_lf_delta[] + const int level0 = enc->dqm_[s].fstrength_; // explore +/-quant range of values around level0 const int delta_min = -enc->dqm_[s].quant_; diff --git a/drivers/webp/enc/histogram.c b/drivers/webp/enc/histogram.c index 68c27fb1db..61544f4ccd 100644 --- a/drivers/webp/enc/histogram.c +++ b/drivers/webp/enc/histogram.c @@ -157,6 +157,109 @@ void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, } // ----------------------------------------------------------------------------- +// Entropy-related functions. + +static WEBP_INLINE double BitsEntropyRefine(const VP8LBitEntropy* entropy) { + double mix; + if (entropy->nonzeros < 5) { + if (entropy->nonzeros <= 1) { + return 0; + } + // Two symbols, they will be 0 and 1 in a Huffman code. + // Let's mix in a bit of entropy to favor good clustering when + // distributions of these are combined. + if (entropy->nonzeros == 2) { + return 0.99 * entropy->sum + 0.01 * entropy->entropy; + } + // No matter what the entropy says, we cannot be better than min_limit + // with Huffman coding. I am mixing a bit of entropy into the + // min_limit since it produces much better (~0.5 %) compression results + // perhaps because of better entropy clustering. + if (entropy->nonzeros == 3) { + mix = 0.95; + } else { + mix = 0.7; // nonzeros == 4. + } + } else { + mix = 0.627; + } + + { + double min_limit = 2 * entropy->sum - entropy->max_val; + min_limit = mix * min_limit + (1.0 - mix) * entropy->entropy; + return (entropy->entropy < min_limit) ? min_limit : entropy->entropy; + } +} + +double VP8LBitsEntropy(const uint32_t* const array, int n, + uint32_t* const trivial_symbol) { + VP8LBitEntropy entropy; + VP8LBitsEntropyUnrefined(array, n, &entropy); + if (trivial_symbol != NULL) { + *trivial_symbol = + (entropy.nonzeros == 1) ? entropy.nonzero_code : VP8L_NON_TRIVIAL_SYM; + } + + return BitsEntropyRefine(&entropy); +} + +static double InitialHuffmanCost(void) { + // Small bias because Huffman code length is typically not stored in + // full length. + static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3; + static const double kSmallBias = 9.1; + return kHuffmanCodeOfHuffmanCodeSize - kSmallBias; +} + +// Finalize the Huffman cost based on streak numbers and length type (<3 or >=3) +static double FinalHuffmanCost(const VP8LStreaks* const stats) { + double retval = InitialHuffmanCost(); + retval += stats->counts[0] * 1.5625 + 0.234375 * stats->streaks[0][1]; + retval += stats->counts[1] * 2.578125 + 0.703125 * stats->streaks[1][1]; + retval += 1.796875 * stats->streaks[0][0]; + retval += 3.28125 * stats->streaks[1][0]; + return retval; +} + +// Get the symbol entropy for the distribution 'population'. +// Set 'trivial_sym', if there's only one symbol present in the distribution. +static double PopulationCost(const uint32_t* const population, int length, + uint32_t* const trivial_sym) { + VP8LBitEntropy bit_entropy; + VP8LStreaks stats; + VP8LGetEntropyUnrefined(population, length, &bit_entropy, &stats); + if (trivial_sym != NULL) { + *trivial_sym = (bit_entropy.nonzeros == 1) ? bit_entropy.nonzero_code + : VP8L_NON_TRIVIAL_SYM; + } + + return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats); +} + +static WEBP_INLINE double GetCombinedEntropy(const uint32_t* const X, + const uint32_t* const Y, + int length) { + VP8LBitEntropy bit_entropy; + VP8LStreaks stats; + VP8LGetCombinedEntropyUnrefined(X, Y, length, &bit_entropy, &stats); + + return BitsEntropyRefine(&bit_entropy) + FinalHuffmanCost(&stats); +} + +// Estimates the Entropy + Huffman + other block overhead size cost. +double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { + return + PopulationCost( + p->literal_, VP8LHistogramNumCodes(p->palette_code_bits_), NULL) + + PopulationCost(p->red_, NUM_LITERAL_CODES, NULL) + + PopulationCost(p->blue_, NUM_LITERAL_CODES, NULL) + + PopulationCost(p->alpha_, NUM_LITERAL_CODES, NULL) + + PopulationCost(p->distance_, NUM_DISTANCE_CODES, NULL) + + VP8LExtraCost(p->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES) + + VP8LExtraCost(p->distance_, NUM_DISTANCE_CODES); +} + +// ----------------------------------------------------------------------------- // Various histogram combine/cost-eval functions static int GetCombinedHistogramEntropy(const VP8LHistogram* const a, @@ -165,26 +268,25 @@ static int GetCombinedHistogramEntropy(const VP8LHistogram* const a, double* cost) { const int palette_code_bits = a->palette_code_bits_; assert(a->palette_code_bits_ == b->palette_code_bits_); - *cost += VP8LGetCombinedEntropy(a->literal_, b->literal_, - VP8LHistogramNumCodes(palette_code_bits)); + *cost += GetCombinedEntropy(a->literal_, b->literal_, + VP8LHistogramNumCodes(palette_code_bits)); *cost += VP8LExtraCostCombined(a->literal_ + NUM_LITERAL_CODES, b->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES); if (*cost > cost_threshold) return 0; - *cost += VP8LGetCombinedEntropy(a->red_, b->red_, NUM_LITERAL_CODES); + *cost += GetCombinedEntropy(a->red_, b->red_, NUM_LITERAL_CODES); if (*cost > cost_threshold) return 0; - *cost += VP8LGetCombinedEntropy(a->blue_, b->blue_, NUM_LITERAL_CODES); + *cost += GetCombinedEntropy(a->blue_, b->blue_, NUM_LITERAL_CODES); if (*cost > cost_threshold) return 0; - *cost += VP8LGetCombinedEntropy(a->alpha_, b->alpha_, NUM_LITERAL_CODES); + *cost += GetCombinedEntropy(a->alpha_, b->alpha_, NUM_LITERAL_CODES); if (*cost > cost_threshold) return 0; - *cost += VP8LGetCombinedEntropy(a->distance_, b->distance_, - NUM_DISTANCE_CODES); - *cost += VP8LExtraCostCombined(a->distance_, b->distance_, - NUM_DISTANCE_CODES); + *cost += GetCombinedEntropy(a->distance_, b->distance_, NUM_DISTANCE_CODES); + *cost += + VP8LExtraCostCombined(a->distance_, b->distance_, NUM_DISTANCE_CODES); if (*cost > cost_threshold) return 0; return 1; @@ -262,17 +364,17 @@ static void UpdateDominantCostRange( static void UpdateHistogramCost(VP8LHistogram* const h) { uint32_t alpha_sym, red_sym, blue_sym; - const double alpha_cost = VP8LPopulationCost(h->alpha_, NUM_LITERAL_CODES, - &alpha_sym); + const double alpha_cost = + PopulationCost(h->alpha_, NUM_LITERAL_CODES, &alpha_sym); const double distance_cost = - VP8LPopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL) + + PopulationCost(h->distance_, NUM_DISTANCE_CODES, NULL) + VP8LExtraCost(h->distance_, NUM_DISTANCE_CODES); const int num_codes = VP8LHistogramNumCodes(h->palette_code_bits_); - h->literal_cost_ = VP8LPopulationCost(h->literal_, num_codes, NULL) + + h->literal_cost_ = PopulationCost(h->literal_, num_codes, NULL) + VP8LExtraCost(h->literal_ + NUM_LITERAL_CODES, NUM_LENGTH_CODES); - h->red_cost_ = VP8LPopulationCost(h->red_, NUM_LITERAL_CODES, &red_sym); - h->blue_cost_ = VP8LPopulationCost(h->blue_, NUM_LITERAL_CODES, &blue_sym); + h->red_cost_ = PopulationCost(h->red_, NUM_LITERAL_CODES, &red_sym); + h->blue_cost_ = PopulationCost(h->blue_, NUM_LITERAL_CODES, &blue_sym); h->bit_cost_ = h->literal_cost_ + h->red_cost_ + h->blue_cost_ + alpha_cost + distance_cost; if ((alpha_sym | red_sym | blue_sym) == VP8L_NON_TRIVIAL_SYM) { @@ -284,29 +386,27 @@ static void UpdateHistogramCost(VP8LHistogram* const h) { } static int GetBinIdForEntropy(double min, double max, double val) { - const double range = max - min + 1e-6; - const double delta = val - min; - return (int)(NUM_PARTITIONS * delta / range); + const double range = max - min; + if (range > 0.) { + const double delta = val - min; + return (int)((NUM_PARTITIONS - 1e-6) * delta / range); + } else { + return 0; + } } -static int GetHistoBinIndexLowEffort( - const VP8LHistogram* const h, const DominantCostRange* const c) { - const int bin_id = GetBinIdForEntropy(c->literal_min_, c->literal_max_, - h->literal_cost_); +static int GetHistoBinIndex(const VP8LHistogram* const h, + const DominantCostRange* const c, int low_effort) { + int bin_id = GetBinIdForEntropy(c->literal_min_, c->literal_max_, + h->literal_cost_); assert(bin_id < NUM_PARTITIONS); - return bin_id; -} - -static int GetHistoBinIndex( - const VP8LHistogram* const h, const DominantCostRange* const c) { - const int bin_id = - GetBinIdForEntropy(c->blue_min_, c->blue_max_, h->blue_cost_) + - NUM_PARTITIONS * GetBinIdForEntropy(c->red_min_, c->red_max_, - h->red_cost_) + - NUM_PARTITIONS * NUM_PARTITIONS * GetBinIdForEntropy(c->literal_min_, - c->literal_max_, - h->literal_cost_); - assert(bin_id < BIN_SIZE); + if (!low_effort) { + bin_id = bin_id * NUM_PARTITIONS + + GetBinIdForEntropy(c->red_min_, c->red_max_, h->red_cost_); + bin_id = bin_id * NUM_PARTITIONS + + GetBinIdForEntropy(c->blue_min_, c->blue_max_, h->blue_cost_); + assert(bin_id < BIN_SIZE); + } return bin_id; } @@ -367,16 +467,13 @@ static void HistogramAnalyzeEntropyBin(VP8LHistogramSet* const image_histo, // bin-hash histograms on three of the dominant (literal, red and blue) // symbol costs. for (i = 0; i < histo_size; ++i) { - int num_histos; - VP8LHistogram* const histo = histograms[i]; - const int16_t bin_id = low_effort ? - (int16_t)GetHistoBinIndexLowEffort(histo, &cost_range) : - (int16_t)GetHistoBinIndex(histo, &cost_range); + const VP8LHistogram* const histo = histograms[i]; + const int bin_id = GetHistoBinIndex(histo, &cost_range, low_effort); const int bin_offset = bin_id * bin_depth; // bin_map[n][0] for every bin 'n' maintains the counter for the number of // histograms in that bin. // Get and increment the num_histos in that bin. - num_histos = ++bin_map[bin_offset]; + const int num_histos = ++bin_map[bin_offset]; assert(bin_offset + num_histos < bin_depth * BIN_SIZE); // Add histogram i'th index at num_histos (last) position in the bin_map. bin_map[bin_offset + num_histos] = i; @@ -478,26 +575,31 @@ typedef struct { } HistogramPair; typedef struct { - HistogramPair* heap; - int* positions; + HistogramPair* queue; int size; - int max_index; -} HistoHeap; - -static int HistoHeapInit(HistoHeap* const histo_heap, const int max_index) { - histo_heap->size = 0; - histo_heap->max_index = max_index; - histo_heap->heap = WebPSafeMalloc(max_index * max_index, - sizeof(*histo_heap->heap)); - histo_heap->positions = WebPSafeMalloc(max_index * max_index, - sizeof(*histo_heap->positions)); - return histo_heap->heap != NULL && histo_heap->positions != NULL; -} - -static void HistoHeapClear(HistoHeap* const histo_heap) { - assert(histo_heap != NULL); - WebPSafeFree(histo_heap->heap); - WebPSafeFree(histo_heap->positions); + int max_size; +} HistoQueue; + +static int HistoQueueInit(HistoQueue* const histo_queue, const int max_index) { + histo_queue->size = 0; + // max_index^2 for the queue size is safe. If you look at + // HistogramCombineGreedy, and imagine that UpdateQueueFront always pushes + // data to the queue, you insert at most: + // - max_index*(max_index-1)/2 (the first two for loops) + // - max_index - 1 in the last for loop at the first iteration of the while + // loop, max_index - 2 at the second iteration ... therefore + // max_index*(max_index-1)/2 overall too + histo_queue->max_size = max_index * max_index; + // We allocate max_size + 1 because the last element at index "size" is + // used as temporary data (and it could be up to max_size). + histo_queue->queue = WebPSafeMalloc(histo_queue->max_size + 1, + sizeof(*histo_queue->queue)); + return histo_queue->queue != NULL; +} + +static void HistoQueueClear(HistoQueue* const histo_queue) { + assert(histo_queue != NULL); + WebPSafeFree(histo_queue->queue); } static void SwapHistogramPairs(HistogramPair *p1, @@ -507,66 +609,33 @@ static void SwapHistogramPairs(HistogramPair *p1, *p2 = tmp; } -// Given a valid min-heap in range [0, heap_size-1) this function places value -// heap[heap_size-1] into right location within heap and sets its position in -// positions array. -static void HeapPush(HistoHeap* const histo_heap) { - HistogramPair* const heap = histo_heap->heap - 1; - int* const positions = histo_heap->positions; - const int max_index = histo_heap->max_index; - int v; - ++histo_heap->size; - v = histo_heap->size; - while (v > 1 && heap[v].cost_diff < heap[v >> 1].cost_diff) { - SwapHistogramPairs(&heap[v], &heap[v >> 1]); - // Change position of moved pair in heap. - if (heap[v].idx1 >= 0) { - const int pos = heap[v].idx1 * max_index + heap[v].idx2; - assert(pos >= 0 && pos < max_index * max_index); - positions[pos] = v; - } - v >>= 1; - } - positions[heap[v].idx1 * max_index + heap[v].idx2] = v; -} - -// Given a valid min-heap in range [0, heap_size) this function shortens heap -// range by one and places element with the lowest value to (heap_size-1). -static void HeapPop(HistoHeap* const histo_heap) { - HistogramPair* const heap = histo_heap->heap - 1; - int* const positions = histo_heap->positions; - const int heap_size = histo_heap->size; - const int max_index = histo_heap->max_index; - int v = 1; - if (heap[v].idx1 >= 0) { - positions[heap[v].idx1 * max_index + heap[v].idx2] = -1; - } - SwapHistogramPairs(&heap[v], &heap[heap_size]); - while ((v << 1) < heap_size) { - int son = (heap[v << 1].cost_diff < heap[v].cost_diff) ? (v << 1) : v; - if (((v << 1) + 1) < heap_size && - heap[(v << 1) + 1].cost_diff < heap[son].cost_diff) { - son = (v << 1) + 1; - } - if (son == v) break; - SwapHistogramPairs(&heap[v], &heap[son]); - // Change position of moved pair in heap. - if (heap[v].idx1 >= 0) { - positions[heap[v].idx1 * max_index + heap[v].idx2] = v; - } - v = son; - } - if (heap[v].idx1 >= 0) { - positions[heap[v].idx1 * max_index + heap[v].idx2] = v; +// Given a valid priority queue in range [0, queue_size) this function checks +// whether histo_queue[queue_size] should be accepted and swaps it with the +// front if it is smaller. Otherwise, it leaves it as is. +static void UpdateQueueFront(HistoQueue* const histo_queue) { + if (histo_queue->queue[histo_queue->size].cost_diff >= 0) return; + + if (histo_queue->queue[histo_queue->size].cost_diff < + histo_queue->queue[0].cost_diff) { + SwapHistogramPairs(histo_queue->queue, + histo_queue->queue + histo_queue->size); } - --histo_heap->size; + ++histo_queue->size; + + // We cannot add more elements than the capacity. + // The allocation adds an extra element to the official capacity so that + // histo_queue->queue[histo_queue->max_size] is read/written within bound. + assert(histo_queue->size <= histo_queue->max_size); } // ----------------------------------------------------------------------------- static void PreparePair(VP8LHistogram** histograms, int idx1, int idx2, - HistogramPair* const pair, - VP8LHistogram* const histos) { + HistogramPair* const pair) { + VP8LHistogram* h1; + VP8LHistogram* h2; + double sum_cost; + if (idx1 > idx2) { const int tmp = idx2; idx2 = idx1; @@ -574,60 +643,27 @@ static void PreparePair(VP8LHistogram** histograms, int idx1, int idx2, } pair->idx1 = idx1; pair->idx2 = idx2; - pair->cost_diff = - HistogramAddEval(histograms[idx1], histograms[idx2], histos, 0); - pair->cost_combo = histos->bit_cost_; -} - -#define POSITION_INVALID (-1) - -// Invalidates pairs intersecting (idx1, idx2) in heap. -static void InvalidatePairs(int idx1, int idx2, - const HistoHeap* const histo_heap) { - HistogramPair* const heap = histo_heap->heap - 1; - int* const positions = histo_heap->positions; - const int max_index = histo_heap->max_index; - int i; - for (i = 0; i < idx1; ++i) { - const int pos = positions[i * max_index + idx1]; - if (pos >= 0) { - heap[pos].idx1 = POSITION_INVALID; - } - } - for (i = idx1 + 1; i < max_index; ++i) { - const int pos = positions[idx1 * max_index + i]; - if (pos >= 0) { - heap[pos].idx1 = POSITION_INVALID; - } - } - for (i = 0; i < idx2; ++i) { - const int pos = positions[i * max_index + idx2]; - if (pos >= 0) { - heap[pos].idx1 = POSITION_INVALID; - } - } - for (i = idx2 + 1; i < max_index; ++i) { - const int pos = positions[idx2 * max_index + i]; - if (pos >= 0) { - heap[pos].idx1 = POSITION_INVALID; - } - } + h1 = histograms[idx1]; + h2 = histograms[idx2]; + sum_cost = h1->bit_cost_ + h2->bit_cost_; + pair->cost_combo = 0.; + GetCombinedHistogramEntropy(h1, h2, sum_cost, &pair->cost_combo); + pair->cost_diff = pair->cost_combo - sum_cost; } // Combines histograms by continuously choosing the one with the highest cost // reduction. -static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, - VP8LHistogram* const histos) { +static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo) { int ok = 0; int image_histo_size = image_histo->size; int i, j; VP8LHistogram** const histograms = image_histo->histograms; // Indexes of remaining histograms. int* const clusters = WebPSafeMalloc(image_histo_size, sizeof(*clusters)); - // Heap of histogram pairs. - HistoHeap histo_heap; + // Priority queue of histogram pairs. + HistoQueue histo_queue; - if (!HistoHeapInit(&histo_heap, image_histo_size) || clusters == NULL) { + if (!HistoQueueInit(&histo_queue, image_histo_size) || clusters == NULL) { goto End; } @@ -636,19 +672,17 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, clusters[i] = i; for (j = i + 1; j < image_histo_size; ++j) { // Initialize positions array. - histo_heap.positions[i * histo_heap.max_index + j] = POSITION_INVALID; - PreparePair(histograms, i, j, &histo_heap.heap[histo_heap.size], histos); - if (histo_heap.heap[histo_heap.size].cost_diff < 0) { - HeapPush(&histo_heap); - } + PreparePair(histograms, i, j, &histo_queue.queue[histo_queue.size]); + UpdateQueueFront(&histo_queue); } } - while (image_histo_size > 1 && histo_heap.size > 0) { - const int idx1 = histo_heap.heap[0].idx1; - const int idx2 = histo_heap.heap[0].idx2; + while (image_histo_size > 1 && histo_queue.size > 0) { + HistogramPair* copy_to; + const int idx1 = histo_queue.queue[0].idx1; + const int idx2 = histo_queue.queue[0].idx2; VP8LHistogramAdd(histograms[idx2], histograms[idx1], histograms[idx1]); - histograms[idx1]->bit_cost_ = histo_heap.heap[0].cost_combo; + histograms[idx1]->bit_cost_ = histo_queue.queue[0].cost_combo; // Remove merged histogram. for (i = 0; i + 1 < image_histo_size; ++i) { if (clusters[i] >= idx2) { @@ -657,22 +691,31 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, } --image_histo_size; - // Invalidate pairs intersecting the just combined best pair. - InvalidatePairs(idx1, idx2, &histo_heap); - - // Pop invalid pairs from the top of the heap. - while (histo_heap.size > 0 && histo_heap.heap[0].idx1 < 0) { - HeapPop(&histo_heap); + // Remove pairs intersecting the just combined best pair. This will + // therefore pop the head of the queue. + copy_to = histo_queue.queue; + for (i = 0; i < histo_queue.size; ++i) { + HistogramPair* const p = histo_queue.queue + i; + if (p->idx1 == idx1 || p->idx2 == idx1 || + p->idx1 == idx2 || p->idx2 == idx2) { + // Do not copy the invalid pair. + continue; + } + if (p->cost_diff < histo_queue.queue[0].cost_diff) { + // Replace the top of the queue if we found better. + SwapHistogramPairs(histo_queue.queue, p); + } + SwapHistogramPairs(copy_to, p); + ++copy_to; } + histo_queue.size = (int)(copy_to - histo_queue.queue); - // Push new pairs formed with combined histogram to the heap. + // Push new pairs formed with combined histogram to the queue. for (i = 0; i < image_histo_size; ++i) { if (clusters[i] != idx1) { PreparePair(histograms, idx1, clusters[i], - &histo_heap.heap[histo_heap.size], histos); - if (histo_heap.heap[histo_heap.size].cost_diff < 0) { - HeapPush(&histo_heap); - } + &histo_queue.queue[histo_queue.size]); + UpdateQueueFront(&histo_queue); } } } @@ -688,15 +731,14 @@ static int HistogramCombineGreedy(VP8LHistogramSet* const image_histo, End: WebPSafeFree(clusters); - HistoHeapClear(&histo_heap); + HistoQueueClear(&histo_queue); return ok; } -static VP8LHistogram* HistogramCombineStochastic( - VP8LHistogramSet* const image_histo, - VP8LHistogram* tmp_histo, - VP8LHistogram* best_combo, - int quality, int min_cluster_size) { +static void HistogramCombineStochastic(VP8LHistogramSet* const image_histo, + VP8LHistogram* tmp_histo, + VP8LHistogram* best_combo, + int quality, int min_cluster_size) { int iter; uint32_t seed = 0; int tries_with_no_success = 0; @@ -756,7 +798,6 @@ static VP8LHistogram* HistogramCombineStochastic( } } image_histo->size = image_histo_size; - return best_combo; } // ----------------------------------------------------------------------------- @@ -764,24 +805,23 @@ static VP8LHistogram* HistogramCombineStochastic( // Find the best 'out' histogram for each of the 'in' histograms. // Note: we assume that out[]->bit_cost_ is already up-to-date. -static void HistogramRemap(const VP8LHistogramSet* const orig_histo, - const VP8LHistogramSet* const image_histo, +static void HistogramRemap(const VP8LHistogramSet* const in, + const VP8LHistogramSet* const out, uint16_t* const symbols) { int i; - VP8LHistogram** const orig_histograms = orig_histo->histograms; - VP8LHistogram** const histograms = image_histo->histograms; - const int orig_histo_size = orig_histo->size; - const int image_histo_size = image_histo->size; - if (image_histo_size > 1) { - for (i = 0; i < orig_histo_size; ++i) { + VP8LHistogram** const in_histo = in->histograms; + VP8LHistogram** const out_histo = out->histograms; + const int in_size = in->size; + const int out_size = out->size; + if (out_size > 1) { + for (i = 0; i < in_size; ++i) { int best_out = 0; - double best_bits = - HistogramAddThresh(histograms[0], orig_histograms[i], MAX_COST); + double best_bits = MAX_COST; int k; - for (k = 1; k < image_histo_size; ++k) { + for (k = 0; k < out_size; ++k) { const double cur_bits = - HistogramAddThresh(histograms[k], orig_histograms[i], best_bits); - if (cur_bits < best_bits) { + HistogramAddThresh(out_histo[k], in_histo[i], best_bits); + if (k == 0 || cur_bits < best_bits) { best_bits = cur_bits; best_out = k; } @@ -789,20 +829,20 @@ static void HistogramRemap(const VP8LHistogramSet* const orig_histo, symbols[i] = best_out; } } else { - assert(image_histo_size == 1); - for (i = 0; i < orig_histo_size; ++i) { + assert(out_size == 1); + for (i = 0; i < in_size; ++i) { symbols[i] = 0; } } // Recompute each out based on raw and symbols. - for (i = 0; i < image_histo_size; ++i) { - HistogramClear(histograms[i]); + for (i = 0; i < out_size; ++i) { + HistogramClear(out_histo[i]); } - for (i = 0; i < orig_histo_size; ++i) { + for (i = 0; i < in_size; ++i) { const int idx = symbols[i]; - VP8LHistogramAdd(orig_histograms[i], histograms[idx], histograms[idx]); + VP8LHistogramAdd(in_histo[i], out_histo[idx], out_histo[idx]); } } @@ -876,11 +916,10 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize, const float x = quality / 100.f; // cubic ramp between 1 and MAX_HISTO_GREEDY: const int threshold_size = (int)(1 + (x * x * x) * (MAX_HISTO_GREEDY - 1)); - cur_combo = HistogramCombineStochastic(image_histo, - tmp_histos->histograms[0], - cur_combo, quality, threshold_size); + HistogramCombineStochastic(image_histo, tmp_histos->histograms[0], + cur_combo, quality, threshold_size); if ((image_histo->size <= threshold_size) && - !HistogramCombineGreedy(image_histo, cur_combo)) { + !HistogramCombineGreedy(image_histo)) { goto Error; } } diff --git a/drivers/webp/enc/histogram.h b/drivers/webp/enc/histogram.h index 72f045793a..59de42b33e 100644 --- a/drivers/webp/enc/histogram.h +++ b/drivers/webp/enc/histogram.h @@ -24,6 +24,9 @@ extern "C" { #endif +// Not a trivial literal symbol. +#define VP8L_NON_TRIVIAL_SYM (0xffffffff) + // A simple container for histograms of data. typedef struct { // literal_ contains green literal, palette-code and @@ -103,6 +106,16 @@ int VP8LGetHistoImageSymbols(int xsize, int ysize, VP8LHistogramSet* const tmp_histos, uint16_t* const histogram_symbols); +// Returns the entropy for the symbols in the input array. +// Also sets trivial_symbol to the code value, if the array has only one code +// value. Otherwise, set it to VP8L_NON_TRIVIAL_SYM. +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); + #ifdef __cplusplus } #endif diff --git a/drivers/webp/enc/near_lossless.c b/drivers/webp/enc/near_lossless.c index 9bc0f0e786..f4ab91f571 100644 --- a/drivers/webp/enc/near_lossless.c +++ b/drivers/webp/enc/near_lossless.c @@ -14,6 +14,7 @@ // Author: Jyrki Alakuijala (jyrki@google.com) // Converted to C by Aleksander Kramarz (akramarz@google.com) +#include <assert.h> #include <stdlib.h> #include "../dsp/lossless.h" @@ -23,42 +24,14 @@ #define MIN_DIM_FOR_NEAR_LOSSLESS 64 #define MAX_LIMIT_BITS 5 -// Computes quantized pixel value and distance from original value. -static void GetValAndDistance(int a, int initial, int bits, - int* const val, int* const distance) { - const int mask = ~((1 << bits) - 1); - *val = (initial & mask) | (initial >> (8 - bits)); - *distance = 2 * abs(a - *val); -} - -// Clamps the value to range [0, 255]. -static int Clamp8b(int val) { - const int min_val = 0; - const int max_val = 0xff; - return (val < min_val) ? min_val : (val > max_val) ? max_val : val; -} - -// Quantizes values {a, a+(1<<bits), a-(1<<bits)} and returns the nearest one. +// Quantizes the value up or down to a multiple of 1<<bits (or to 255), +// choosing the closer one, resolving ties using bankers' rounding. static int FindClosestDiscretized(int a, int bits) { - int best_val = a, i; - int min_distance = 256; - - for (i = -1; i <= 1; ++i) { - int candidate, distance; - const int val = Clamp8b(a + i * (1 << bits)); - GetValAndDistance(a, val, bits, &candidate, &distance); - if (i != 0) { - ++distance; - } - // Smallest distance but favor i == 0 over i == -1 and i == 1 - // since that keeps the overall intensity more constant in the - // images. - if (distance < min_distance) { - min_distance = distance; - best_val = candidate; - } - } - return best_val; + const int mask = (1 << bits) - 1; + const int biased = a + (mask >> 1) + ((a >> bits) & 1); + assert(bits > 0); + if (biased > 0xff) return 0xff; + return biased & ~mask; } // Applies FindClosestDiscretized to all channels of pixel. @@ -124,22 +97,11 @@ static void NearLossless(int xsize, int ysize, uint32_t* argb, } } -static int QualityToLimitBits(int quality) { - // quality mapping: - // 0..19 -> 5 - // 0..39 -> 4 - // 0..59 -> 3 - // 0..79 -> 2 - // 0..99 -> 1 - // 100 -> 0 - return MAX_LIMIT_BITS - quality / 20; -} - int VP8ApplyNearLossless(int xsize, int ysize, uint32_t* argb, int quality) { int i; uint32_t* const copy_buffer = (uint32_t*)WebPSafeMalloc(xsize * 3, sizeof(*copy_buffer)); - const int limit_bits = QualityToLimitBits(quality); + const int limit_bits = VP8LNearLosslessBits(quality); assert(argb != NULL); assert(limit_bits >= 0); assert(limit_bits <= MAX_LIMIT_BITS); diff --git a/drivers/webp/enc/picture.c b/drivers/webp/enc/picture.c index 26679a72e4..d9befbc47d 100644 --- a/drivers/webp/enc/picture.c +++ b/drivers/webp/enc/picture.c @@ -237,6 +237,8 @@ static size_t Encode(const uint8_t* rgba, int width, int height, int stride, WebPMemoryWriter wrt; int ok; + if (output == NULL) return 0; + if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) || !WebPPictureInit(&pic)) { return 0; // shouldn't happen, except if system installation is broken diff --git a/drivers/webp/enc/picture_csp.c b/drivers/webp/enc/picture_csp.c index 0ef5f9eee2..607a6240b0 100644 --- a/drivers/webp/enc/picture_csp.c +++ b/drivers/webp/enc/picture_csp.c @@ -1125,32 +1125,44 @@ static int Import(WebPPicture* const picture, int WebPPictureImportRGB(WebPPicture* picture, const uint8_t* rgb, int rgb_stride) { - return (picture != NULL) ? Import(picture, rgb, rgb_stride, 3, 0, 0) : 0; + return (picture != NULL && rgb != NULL) + ? Import(picture, rgb, rgb_stride, 3, 0, 0) + : 0; } int WebPPictureImportBGR(WebPPicture* picture, const uint8_t* rgb, int rgb_stride) { - return (picture != NULL) ? Import(picture, rgb, rgb_stride, 3, 1, 0) : 0; + return (picture != NULL && rgb != NULL) + ? Import(picture, rgb, rgb_stride, 3, 1, 0) + : 0; } int WebPPictureImportRGBA(WebPPicture* picture, const uint8_t* rgba, int rgba_stride) { - return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 0, 1) : 0; + return (picture != NULL && rgba != NULL) + ? Import(picture, rgba, rgba_stride, 4, 0, 1) + : 0; } int WebPPictureImportBGRA(WebPPicture* picture, const uint8_t* rgba, int rgba_stride) { - return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 1, 1) : 0; + return (picture != NULL && rgba != NULL) + ? Import(picture, rgba, rgba_stride, 4, 1, 1) + : 0; } int WebPPictureImportRGBX(WebPPicture* picture, const uint8_t* rgba, int rgba_stride) { - return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 0, 0) : 0; + return (picture != NULL && rgba != NULL) + ? Import(picture, rgba, rgba_stride, 4, 0, 0) + : 0; } int WebPPictureImportBGRX(WebPPicture* picture, const uint8_t* rgba, int rgba_stride) { - return (picture != NULL) ? Import(picture, rgba, rgba_stride, 4, 1, 0) : 0; + return (picture != NULL && rgba != NULL) + ? Import(picture, rgba, rgba_stride, 4, 1, 0) + : 0; } //------------------------------------------------------------------------------ diff --git a/drivers/webp/enc/picture_psnr.c b/drivers/webp/enc/picture_psnr.c index 40214efc95..81ab1b5ca1 100644 --- a/drivers/webp/enc/picture_psnr.c +++ b/drivers/webp/enc/picture_psnr.c @@ -27,7 +27,7 @@ static void AccumulateLSIM(const uint8_t* src, int src_stride, const uint8_t* ref, int ref_stride, - int w, int h, DistoStats* stats) { + int w, int h, VP8DistoStats* stats) { int x, y; double total_sse = 0.; for (y = 0; y < h; ++y) { @@ -71,11 +71,13 @@ static float GetPSNR(const double v) { int WebPPictureDistortion(const WebPPicture* src, const WebPPicture* ref, int type, float result[5]) { - DistoStats stats[5]; + VP8DistoStats stats[5]; int w, h; memset(stats, 0, sizeof(stats)); + VP8SSIMDspInit(); + if (src == NULL || ref == NULL || src->width != ref->width || src->height != ref->height || src->use_argb != ref->use_argb || result == NULL) { diff --git a/drivers/webp/enc/picture_tools.c b/drivers/webp/enc/picture_tools.c index 7c73646397..bf97af8408 100644 --- a/drivers/webp/enc/picture_tools.c +++ b/drivers/webp/enc/picture_tools.c @@ -11,6 +11,8 @@ // // Author: Skal (pascal.massimino@gmail.com) +#include <assert.h> + #include "./vp8enci.h" #include "../dsp/yuv.h" @@ -120,6 +122,24 @@ void WebPCleanupTransparentArea(WebPPicture* pic) { #undef SIZE #undef SIZE2 +void WebPCleanupTransparentAreaLossless(WebPPicture* const pic) { + int x, y, w, h; + uint32_t* argb; + assert(pic != NULL && pic->use_argb); + w = pic->width; + h = pic->height; + argb = pic->argb; + + for (y = 0; y < h; ++y) { + for (x = 0; x < w; ++x) { + if ((argb[x] & 0xff000000) == 0) { + argb[x] = 0x00000000; + } + } + argb += pic->argb_stride; + } +} + //------------------------------------------------------------------------------ // Blend color and remove transparency info diff --git a/drivers/webp/enc/quant.c b/drivers/webp/enc/quant.c index 002c326b82..549ad26f93 100644 --- a/drivers/webp/enc/quant.c +++ b/drivers/webp/enc/quant.c @@ -30,8 +30,6 @@ #define SNS_TO_DQ 0.9 // Scaling constant between the sns value and the QP // power-law modulation. Must be strictly less than 1. -#define I4_PENALTY 4000 // Rate-penalty for quick i4/i16 decision - // number of non-zero coeffs below which we consider the block very flat // (and apply a penalty to complex predictions) #define FLATNESS_LIMIT_I16 10 // I16 mode @@ -41,6 +39,8 @@ #define MULT_8B(a, b) (((a) * (b) + 128) >> 8) +#define RD_DISTO_MULT 256 // distortion multiplier (equivalent of lambda) + // #define DEBUG_BLOCK //------------------------------------------------------------------------------ @@ -54,15 +54,37 @@ static void PrintBlockInfo(const VP8EncIterator* const it, const VP8ModeScore* const rd) { int i, j; const int is_i16 = (it->mb_->type_ == 1); + const uint8_t* const y_in = it->yuv_in_ + Y_OFF_ENC; + const uint8_t* const y_out = it->yuv_out_ + Y_OFF_ENC; + const uint8_t* const uv_in = it->yuv_in_ + U_OFF_ENC; + const uint8_t* const uv_out = it->yuv_out_ + U_OFF_ENC; printf("SOURCE / OUTPUT / ABS DELTA\n"); - for (j = 0; j < 24; ++j) { - if (j == 16) printf("\n"); // newline before the U/V block - for (i = 0; i < 16; ++i) printf("%3d ", it->yuv_in_[i + j * BPS]); + for (j = 0; j < 16; ++j) { + for (i = 0; i < 16; ++i) printf("%3d ", y_in[i + j * BPS]); printf(" "); - for (i = 0; i < 16; ++i) printf("%3d ", it->yuv_out_[i + j * BPS]); + for (i = 0; i < 16; ++i) printf("%3d ", y_out[i + j * BPS]); printf(" "); for (i = 0; i < 16; ++i) { - printf("%1d ", abs(it->yuv_out_[i + j * BPS] - it->yuv_in_[i + j * BPS])); + printf("%1d ", abs(y_in[i + j * BPS] - y_out[i + j * BPS])); + } + printf("\n"); + } + printf("\n"); // newline before the U/V block + for (j = 0; j < 8; ++j) { + for (i = 0; i < 8; ++i) printf("%3d ", uv_in[i + j * BPS]); + printf(" "); + for (i = 8; i < 16; ++i) printf("%3d ", uv_in[i + j * BPS]); + printf(" "); + for (i = 0; i < 8; ++i) printf("%3d ", uv_out[i + j * BPS]); + printf(" "); + for (i = 8; i < 16; ++i) printf("%3d ", uv_out[i + j * BPS]); + printf(" "); + for (i = 0; i < 8; ++i) { + printf("%1d ", abs(uv_out[i + j * BPS] - uv_in[i + j * BPS])); + } + printf(" "); + for (i = 8; i < 16; ++i) { + printf("%1d ", abs(uv_out[i + j * BPS] - uv_in[i + j * BPS])); } printf("\n"); } @@ -212,6 +234,8 @@ static int ExpandMatrix(VP8Matrix* const m, int type) { return (sum + 8) >> 4; } +static void CheckLambdaValue(int* const v) { if (*v < 1) *v = 1; } + static void SetupMatrices(VP8Encoder* enc) { int i; const int tlambda_scale = @@ -221,7 +245,7 @@ static void SetupMatrices(VP8Encoder* enc) { for (i = 0; i < num_segments; ++i) { VP8SegmentInfo* const m = &enc->dqm_[i]; const int q = m->quant_; - int q4, q16, quv; + int q_i4, q_i16, q_uv; m->y1_.q_[0] = kDcTable[clip(q + enc->dq_y1_dc_, 0, 127)]; m->y1_.q_[1] = kAcTable[clip(q, 0, 127)]; @@ -231,21 +255,33 @@ static void SetupMatrices(VP8Encoder* enc) { m->uv_.q_[0] = kDcTable[clip(q + enc->dq_uv_dc_, 0, 117)]; m->uv_.q_[1] = kAcTable[clip(q + enc->dq_uv_ac_, 0, 127)]; - q4 = ExpandMatrix(&m->y1_, 0); - q16 = ExpandMatrix(&m->y2_, 1); - quv = ExpandMatrix(&m->uv_, 2); - - m->lambda_i4_ = (3 * q4 * q4) >> 7; - m->lambda_i16_ = (3 * q16 * q16); - m->lambda_uv_ = (3 * quv * quv) >> 6; - m->lambda_mode_ = (1 * q4 * q4) >> 7; - m->lambda_trellis_i4_ = (7 * q4 * q4) >> 3; - m->lambda_trellis_i16_ = (q16 * q16) >> 2; - m->lambda_trellis_uv_ = (quv *quv) << 1; - m->tlambda_ = (tlambda_scale * q4) >> 5; + q_i4 = ExpandMatrix(&m->y1_, 0); + q_i16 = ExpandMatrix(&m->y2_, 1); + q_uv = ExpandMatrix(&m->uv_, 2); + + m->lambda_i4_ = (3 * q_i4 * q_i4) >> 7; + m->lambda_i16_ = (3 * q_i16 * q_i16); + m->lambda_uv_ = (3 * q_uv * q_uv) >> 6; + m->lambda_mode_ = (1 * q_i4 * q_i4) >> 7; + m->lambda_trellis_i4_ = (7 * q_i4 * q_i4) >> 3; + m->lambda_trellis_i16_ = (q_i16 * q_i16) >> 2; + m->lambda_trellis_uv_ = (q_uv * q_uv) << 1; + m->tlambda_ = (tlambda_scale * q_i4) >> 5; + + // none of these constants should be < 1 + CheckLambdaValue(&m->lambda_i4_); + CheckLambdaValue(&m->lambda_i16_); + CheckLambdaValue(&m->lambda_uv_); + CheckLambdaValue(&m->lambda_mode_); + CheckLambdaValue(&m->lambda_trellis_i4_); + CheckLambdaValue(&m->lambda_trellis_i16_); + CheckLambdaValue(&m->lambda_trellis_uv_); + CheckLambdaValue(&m->tlambda_); m->min_disto_ = 10 * m->y1_.q_[0]; // quantization-aware min disto m->max_edge_ = 0; + + m->i4_penalty_ = 1000 * q_i4 * q_i4; } } @@ -324,7 +360,12 @@ static int SegmentsAreEquivalent(const VP8SegmentInfo* const S1, static void SimplifySegments(VP8Encoder* const enc) { int map[NUM_MB_SEGMENTS] = { 0, 1, 2, 3 }; - const int num_segments = enc->segment_hdr_.num_segments_; + // 'num_segments_' is previously validated and <= NUM_MB_SEGMENTS, but an + // explicit check is needed to avoid a spurious warning about 'i' exceeding + // array bounds of 'dqm_' with some compilers (noticed with gcc-4.9). + const int num_segments = (enc->segment_hdr_.num_segments_ < NUM_MB_SEGMENTS) + ? enc->segment_hdr_.num_segments_ + : NUM_MB_SEGMENTS; int num_final_segments = 1; int s1, s2; for (s1 = 1; s1 < num_segments; ++s1) { // find similar segments @@ -535,13 +576,12 @@ typedef struct { #define SCORE_STATE(n, l) (score_states[n][(l) + MIN_DELTA]) static WEBP_INLINE void SetRDScore(int lambda, VP8ModeScore* const rd) { - // TODO: incorporate the "* 256" in the tables? - rd->score = (rd->R + rd->H) * lambda + 256 * (rd->D + rd->SD); + rd->score = (rd->R + rd->H) * lambda + RD_DISTO_MULT * (rd->D + rd->SD); } static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate, score_t distortion) { - return rate * lambda + 256 * distortion; + return rate * lambda + RD_DISTO_MULT * distortion; } static int TrellisQuantizeBlock(const VP8Encoder* const enc, @@ -1050,7 +1090,7 @@ static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) { // Compute RD-score rd_uv.D = VP8SSE16x8(src, tmp_dst); - rd_uv.SD = 0; // TODO: should we call TDisto? it tends to flatten areas. + rd_uv.SD = 0; // not calling TDisto here: it tends to flatten areas. rd_uv.H = VP8FixedCostsUV[mode]; rd_uv.R = VP8GetCostUV(it, &rd_uv); if (mode > 0 && IsFlat(rd_uv.uv_levels[0], kNumBlocks, FLATNESS_LIMIT_UV)) { @@ -1100,56 +1140,108 @@ static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) { } // Refine intra16/intra4 sub-modes based on distortion only (not rate). -static void DistoRefine(VP8EncIterator* const it, int try_both_i4_i16) { - const int is_i16 = (it->mb_->type_ == 1); +static void RefineUsingDistortion(VP8EncIterator* const it, + int try_both_modes, int refine_uv_mode, + VP8ModeScore* const rd) { score_t best_score = MAX_COST; + int nz = 0; + int mode; + int is_i16 = try_both_modes || (it->mb_->type_ == 1); - if (try_both_i4_i16 || is_i16) { - int mode; + const VP8SegmentInfo* const dqm = &it->enc_->dqm_[it->mb_->segment_]; + // Some empiric constants, of approximate order of magnitude. + const int lambda_d_i16 = 106; + const int lambda_d_i4 = 11; + const int lambda_d_uv = 120; + score_t score_i4 = dqm->i4_penalty_; + score_t i4_bit_sum = 0; + const score_t bit_limit = it->enc_->mb_header_limit_; + + if (is_i16) { // First, evaluate Intra16 distortion int best_mode = -1; + const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC; for (mode = 0; mode < NUM_PRED_MODES; ++mode) { const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode]; - const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC; - const score_t score = VP8SSE16x16(src, ref); + const score_t score = VP8SSE16x16(src, ref) * RD_DISTO_MULT + + VP8FixedCostsI16[mode] * lambda_d_i16; + if (mode > 0 && VP8FixedCostsI16[mode] > bit_limit) { + continue; + } if (score < best_score) { best_mode = mode; best_score = score; } } VP8SetIntra16Mode(it, best_mode); + // we'll reconstruct later, if i16 mode actually gets selected } - if (try_both_i4_i16 || !is_i16) { - uint8_t modes_i4[16]; + + // Next, evaluate Intra4 + if (try_both_modes || !is_i16) { // We don't evaluate the rate here, but just account for it through a // constant penalty (i4 mode usually needs more bits compared to i16). - score_t score_i4 = (score_t)I4_PENALTY; - + is_i16 = 0; VP8IteratorStartI4(it); do { - int mode; - int best_sub_mode = -1; - score_t best_sub_score = MAX_COST; + int best_i4_mode = -1; + score_t best_i4_score = MAX_COST; const uint8_t* const src = it->yuv_in_ + Y_OFF_ENC + VP8Scan[it->i4_]; + const uint16_t* const mode_costs = GetCostModeI4(it, rd->modes_i4); - // TODO(skal): we don't really need the prediction pixels here, - // but just the distortion against 'src'. VP8MakeIntra4Preds(it); for (mode = 0; mode < NUM_BMODES; ++mode) { const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode]; - const score_t score = VP8SSE4x4(src, ref); - if (score < best_sub_score) { - best_sub_mode = mode; - best_sub_score = score; + const score_t score = VP8SSE4x4(src, ref) * RD_DISTO_MULT + + mode_costs[mode] * lambda_d_i4; + if (score < best_i4_score) { + best_i4_mode = mode; + best_i4_score = score; } } - modes_i4[it->i4_] = best_sub_mode; - score_i4 += best_sub_score; - if (score_i4 >= best_score) break; - } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF_ENC)); - if (score_i4 < best_score) { - VP8SetIntra4Mode(it, modes_i4); + i4_bit_sum += mode_costs[best_i4_mode]; + rd->modes_i4[it->i4_] = best_i4_mode; + score_i4 += best_i4_score; + if (score_i4 >= best_score || i4_bit_sum > bit_limit) { + // Intra4 won't be better than Intra16. Bail out and pick Intra16. + is_i16 = 1; + break; + } else { // reconstruct partial block inside yuv_out2_ buffer + uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF_ENC + VP8Scan[it->i4_]; + nz |= ReconstructIntra4(it, rd->y_ac_levels[it->i4_], + src, tmp_dst, best_i4_mode) << it->i4_; + } + } while (VP8IteratorRotateI4(it, it->yuv_out2_ + Y_OFF_ENC)); + } + + // Final reconstruction, depending on which mode is selected. + if (!is_i16) { + VP8SetIntra4Mode(it, rd->modes_i4); + SwapOut(it); + best_score = score_i4; + } else { + nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF_ENC, it->preds_[0]); + } + + // ... and UV! + if (refine_uv_mode) { + int best_mode = -1; + score_t best_uv_score = MAX_COST; + const uint8_t* const src = it->yuv_in_ + U_OFF_ENC; + for (mode = 0; mode < NUM_PRED_MODES; ++mode) { + const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode]; + const score_t score = VP8SSE16x8(src, ref) * RD_DISTO_MULT + + VP8FixedCostsUV[mode] * lambda_d_uv; + if (score < best_uv_score) { + best_mode = mode; + best_uv_score = score; + } } + VP8SetIntraUVMode(it, best_mode); } + nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF_ENC, it->mb_->uv_mode_); + + rd->nz = nz; + rd->score = best_score; } //------------------------------------------------------------------------------ @@ -1179,13 +1271,13 @@ int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, SimpleQuantize(it, rd); } } else { - // For method == 2, pick the best intra4/intra16 based on SSE (~tad slower). - // For method <= 1, we refine intra4 or intra16 (but don't re-examine mode). - DistoRefine(it, (method >= 2)); - SimpleQuantize(it, rd); + // At this point we have heuristically decided intra16 / intra4. + // For method >= 2, pick the best intra4/intra16 based on SSE (~tad slower). + // For method <= 1, we don't re-examine the decision but just go ahead with + // quantization/reconstruction. + RefineUsingDistortion(it, (method >= 2), (method >= 1), rd); } is_skipped = (rd->nz == 0); VP8SetSkip(it, is_skipped); return is_skipped; } - diff --git a/drivers/webp/enc/vp8enci.h b/drivers/webp/enc/vp8enci.h index 0cb2ccc353..efd2f19a9b 100644 --- a/drivers/webp/enc/vp8enci.h +++ b/drivers/webp/enc/vp8enci.h @@ -22,10 +22,6 @@ #include "../utils/utils.h" #include "webp/encode.h" -#ifdef WEBP_EXPERIMENTAL_FEATURES -#include "./vp8li.h" -#endif // WEBP_EXPERIMENTAL_FEATURES - #ifdef __cplusplus extern "C" { #endif @@ -35,8 +31,8 @@ extern "C" { // version numbers #define ENC_MAJ_VERSION 0 -#define ENC_MIN_VERSION 4 -#define ENC_REV_VERSION 4 +#define ENC_MIN_VERSION 5 +#define ENC_REV_VERSION 1 enum { MAX_LF_LEVELS = 64, // Maximum loop filter level MAX_VARIABLE_LEVEL = 67, // last (inclusive) level with variable cost @@ -200,6 +196,9 @@ typedef struct { int lambda_i16_, lambda_i4_, lambda_uv_; int lambda_mode_, lambda_trellis_, tlambda_; int lambda_trellis_i16_, lambda_trellis_i4_, lambda_trellis_uv_; + + // lambda values for distortion-based evaluation + score_t i4_penalty_; // penalty for using Intra4 } VP8SegmentInfo; // Handy transient struct to accumulate score and info during RD-optimization @@ -395,6 +394,7 @@ struct VP8Encoder { int method_; // 0=fastest, 6=best/slowest. VP8RDLevel rd_opt_level_; // Deduced from method_. int max_i4_header_bits_; // partition #0 safeness factor + int mb_header_limit_; // rough limit for header bits per MB int thread_level_; // derived from config->thread_level int do_search_; // derived from config->target_XXX int use_tokens_; // if true, use token buffer @@ -477,17 +477,12 @@ int VP8EncFinishAlpha(VP8Encoder* const enc); // finalize compressed data int VP8EncDeleteAlpha(VP8Encoder* const enc); // delete compressed data // in filter.c - -// SSIM utils -typedef struct { - double w, xm, ym, xxm, xym, yym; -} DistoStats; -void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst); +void VP8SSIMAddStats(const VP8DistoStats* const src, VP8DistoStats* const dst); void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1, const uint8_t* src2, int stride2, - int W, int H, DistoStats* const stats); -double VP8SSIMGet(const DistoStats* const stats); -double VP8SSIMGetSquaredError(const DistoStats* const stats); + int W, int H, VP8DistoStats* const stats); +double VP8SSIMGet(const VP8DistoStats* const stats); +double VP8SSIMGetSquaredError(const VP8DistoStats* const stats); // autofilter void VP8InitFilter(VP8EncIterator* const it); @@ -514,6 +509,10 @@ int WebPPictureAllocARGB(WebPPicture* const picture, int width, int height); // Returns false in case of error (invalid param, out-of-memory). int WebPPictureAllocYUVA(WebPPicture* const picture, int width, int height); +// Clean-up the RGB samples under fully transparent area, to help lossless +// compressibility (no guarantee, though). Assumes that pic->use_argb is true. +void WebPCleanupTransparentAreaLossless(WebPPicture* const pic); + // in near_lossless.c // Near lossless preprocessing in RGB color-space. int VP8ApplyNearLossless(int xsize, int ysize, uint32_t* argb, int quality); diff --git a/drivers/webp/enc/vp8l.c b/drivers/webp/enc/vp8l.c index 284995e830..1f2d41ea91 100644 --- a/drivers/webp/enc/vp8l.c +++ b/drivers/webp/enc/vp8l.c @@ -16,6 +16,7 @@ #include <stdlib.h> #include "./backward_references.h" +#include "./histogram.h" #include "./vp8enci.h" #include "./vp8li.h" #include "../dsp/lossless.h" @@ -33,8 +34,8 @@ // Palette reordering for smaller sum of deltas (and for smaller storage). static int PaletteCompareColorsForQsort(const void* p1, const void* p2) { - const uint32_t a = *(const uint32_t*)p1; - const uint32_t b = *(const uint32_t*)p2; + const uint32_t a = WebPMemToUint32(p1); + const uint32_t b = WebPMemToUint32(p2); assert(a != b); return (a < b) ? -1 : 1; } @@ -125,54 +126,8 @@ static int AnalyzeAndCreatePalette(const WebPPicture* const pic, int low_effort, uint32_t palette[MAX_PALETTE_SIZE], int* const palette_size) { - int i, x, y, key; - int num_colors = 0; - uint8_t in_use[MAX_PALETTE_SIZE * 4] = { 0 }; - uint32_t colors[MAX_PALETTE_SIZE * 4]; - static const uint32_t kHashMul = 0x1e35a7bd; - const uint32_t* argb = pic->argb; - const int width = pic->width; - const int height = pic->height; - uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] - - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - if (argb[x] == last_pix) { - continue; - } - last_pix = argb[x]; - key = (kHashMul * last_pix) >> PALETTE_KEY_RIGHT_SHIFT; - while (1) { - if (!in_use[key]) { - colors[key] = last_pix; - in_use[key] = 1; - ++num_colors; - if (num_colors > MAX_PALETTE_SIZE) { - return 0; - } - break; - } else if (colors[key] == last_pix) { - // The color is already there. - break; - } else { - // Some other color sits there. - // Do linear conflict resolution. - ++key; - key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer. - } - } - } - argb += pic->argb_stride; - } - - // TODO(skal): could we reuse in_use[] to speed up EncodePalette()? - num_colors = 0; - for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) { - if (in_use[i]) { - palette[num_colors] = colors[i]; - ++num_colors; - } - } + const int num_colors = WebPGetColorPalette(pic, palette); + if (num_colors > MAX_PALETTE_SIZE) return 0; *palette_size = num_colors; qsort(palette, num_colors, sizeof(*palette), PaletteCompareColorsForQsort); if (!low_effort && PaletteHasNonMonotonousDeltas(palette, num_colors)) { @@ -335,7 +290,7 @@ static int AnalyzeEntropy(const uint32_t* argb, } } } - free(histo); + WebPSafeFree(histo); return 1; } else { return 0; @@ -760,6 +715,10 @@ static WebPEncodingError EncodeImageNoHuffman(VP8LBitWriter* const bw, } // Calculate backward references from ARGB image. + if (VP8LHashChainFill(hash_chain, quality, argb, width, height) == 0) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } refs = VP8LGetBackwardReferences(width, height, argb, quality, 0, &cache_bits, hash_chain, refs_array); if (refs == NULL) { @@ -823,7 +782,8 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, VP8LHashChain* const hash_chain, VP8LBackwardRefs refs_array[2], int width, int height, int quality, - int low_effort, int* cache_bits, + int low_effort, + int use_cache, int* cache_bits, int histogram_bits, size_t init_byte_position, int* const hdr_size, @@ -855,10 +815,14 @@ static WebPEncodingError EncodeImageInternal(VP8LBitWriter* const bw, goto Error; } - *cache_bits = MAX_COLOR_CACHE_BITS; + *cache_bits = use_cache ? MAX_COLOR_CACHE_BITS : 0; // 'best_refs' is the reference to the best backward refs and points to one // of refs_array[0] or refs_array[1]. // Calculate backward references from ARGB image. + if (VP8LHashChainFill(hash_chain, quality, argb, width, height) == 0) { + err = VP8_ENC_ERROR_OUT_OF_MEMORY; + goto Error; + } best_refs = VP8LGetBackwardReferences(width, height, argb, quality, low_effort, cache_bits, hash_chain, refs_array); @@ -1006,13 +970,19 @@ static void ApplySubtractGreen(VP8LEncoder* const enc, int width, int height, static WebPEncodingError ApplyPredictFilter(const VP8LEncoder* const enc, int width, int height, int quality, int low_effort, + int used_subtract_green, VP8LBitWriter* const bw) { const int pred_bits = enc->transform_bits_; const int transform_width = VP8LSubSampleSize(width, pred_bits); const int transform_height = VP8LSubSampleSize(height, pred_bits); + // we disable near-lossless quantization if palette is used. + const int near_lossless_strength = enc->use_palette_ ? 100 + : enc->config_->near_lossless; VP8LResidualImage(width, height, pred_bits, low_effort, enc->argb_, - enc->argb_scratch_, enc->transform_data_); + enc->argb_scratch_, enc->transform_data_, + near_lossless_strength, enc->config_->exact, + used_subtract_green); VP8LPutBits(bw, TRANSFORM_PRESENT, 1); VP8LPutBits(bw, PREDICTOR_TRANSFORM, 2); assert(pred_bits >= 2); @@ -1112,6 +1082,12 @@ static WebPEncodingError WriteImage(const WebPPicture* const pic, // ----------------------------------------------------------------------------- +static void ClearTransformBuffer(VP8LEncoder* const enc) { + WebPSafeFree(enc->transform_mem_); + enc->transform_mem_ = NULL; + enc->transform_mem_size_ = 0; +} + // Allocates the memory for argb (W x H) buffer, 2 rows of context for // prediction and transform data. // Flags influencing the memory allocated: @@ -1120,43 +1096,48 @@ static WebPEncodingError WriteImage(const WebPPicture* const pic, static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, int width, int height) { WebPEncodingError err = VP8_ENC_OK; - if (enc->argb_ == NULL) { - const int tile_size = 1 << enc->transform_bits_; - const uint64_t image_size = width * height; - // Ensure enough size for tiles, as well as for two scanlines and two - // extra pixels for CopyImageWithPrediction. - const uint64_t argb_scratch_size = - enc->use_predict_ ? tile_size * width + width + 2 : 0; - const int transform_data_size = - (enc->use_predict_ || enc->use_cross_color_) - ? VP8LSubSampleSize(width, enc->transform_bits_) * - VP8LSubSampleSize(height, enc->transform_bits_) - : 0; - const uint64_t total_size = - image_size + WEBP_ALIGN_CST + - argb_scratch_size + WEBP_ALIGN_CST + - (uint64_t)transform_data_size; - uint32_t* mem = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*mem)); + const uint64_t image_size = width * height; + // VP8LResidualImage needs room for 2 scanlines of uint32 pixels with an extra + // pixel in each, plus 2 regular scanlines of bytes. + // TODO(skal): Clean up by using arithmetic in bytes instead of words. + const uint64_t argb_scratch_size = + enc->use_predict_ + ? (width + 1) * 2 + + (width * 2 + sizeof(uint32_t) - 1) / sizeof(uint32_t) + : 0; + const uint64_t transform_data_size = + (enc->use_predict_ || enc->use_cross_color_) + ? VP8LSubSampleSize(width, enc->transform_bits_) * + VP8LSubSampleSize(height, enc->transform_bits_) + : 0; + const uint64_t max_alignment_in_words = + (WEBP_ALIGN_CST + sizeof(uint32_t) - 1) / sizeof(uint32_t); + const uint64_t mem_size = + image_size + max_alignment_in_words + + argb_scratch_size + max_alignment_in_words + + transform_data_size; + uint32_t* mem = enc->transform_mem_; + if (mem == NULL || mem_size > enc->transform_mem_size_) { + ClearTransformBuffer(enc); + mem = (uint32_t*)WebPSafeMalloc(mem_size, sizeof(*mem)); if (mem == NULL) { err = VP8_ENC_ERROR_OUT_OF_MEMORY; goto Error; } - enc->argb_ = mem; - mem = (uint32_t*)WEBP_ALIGN(mem + image_size); - enc->argb_scratch_ = mem; - mem = (uint32_t*)WEBP_ALIGN(mem + argb_scratch_size); - enc->transform_data_ = mem; - enc->current_width_ = width; + enc->transform_mem_ = mem; + enc->transform_mem_size_ = (size_t)mem_size; } + enc->argb_ = mem; + mem = (uint32_t*)WEBP_ALIGN(mem + image_size); + enc->argb_scratch_ = mem; + mem = (uint32_t*)WEBP_ALIGN(mem + argb_scratch_size); + enc->transform_data_ = mem; + + enc->current_width_ = width; Error: return err; } -static void ClearTransformBuffer(VP8LEncoder* const enc) { - WebPSafeFree(enc->argb_); - enc->argb_ = NULL; -} - static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) { WebPEncodingError err = VP8_ENC_OK; const WebPPicture* const picture = enc->pic_; @@ -1176,8 +1157,35 @@ static WebPEncodingError MakeInputImageCopy(VP8LEncoder* const enc) { // ----------------------------------------------------------------------------- -static void MapToPalette(const uint32_t palette[], int num_colors, +static int SearchColor(const uint32_t sorted[], uint32_t color, int hi) { + int low = 0; + if (sorted[low] == color) return low; // loop invariant: sorted[low] != color + while (1) { + const int mid = (low + hi) >> 1; + if (sorted[mid] == color) { + return mid; + } else if (sorted[mid] < color) { + low = mid; + } else { + hi = mid; + } + } +} + +// Sort palette in increasing order and prepare an inverse mapping array. +static void PrepareMapToPalette(const uint32_t palette[], int num_colors, + uint32_t sorted[], int idx_map[]) { + int i; + memcpy(sorted, palette, num_colors * sizeof(*sorted)); + qsort(sorted, num_colors, sizeof(*sorted), PaletteCompareColorsForQsort); + for (i = 0; i < num_colors; ++i) { + idx_map[SearchColor(sorted, palette[i], num_colors)] = i; + } +} + +static void MapToPalette(const uint32_t sorted_palette[], int num_colors, uint32_t* const last_pix, int* const last_idx, + const int idx_map[], const uint32_t* src, uint8_t* dst, int width) { int x; int prev_idx = *last_idx; @@ -1185,14 +1193,8 @@ static void MapToPalette(const uint32_t palette[], int num_colors, for (x = 0; x < width; ++x) { const uint32_t pix = src[x]; if (pix != prev_pix) { - int i; - for (i = 0; i < num_colors; ++i) { - if (pix == palette[i]) { - prev_idx = i; - prev_pix = pix; - break; - } - } + prev_idx = idx_map[SearchColor(sorted_palette, pix, num_colors)]; + prev_pix = pix; } dst[x] = prev_idx; } @@ -1239,11 +1241,16 @@ static WebPEncodingError ApplyPalette(const uint32_t* src, uint32_t src_stride, } } else { // Use 1 pixel cache for ARGB pixels. - uint32_t last_pix = palette[0]; - int last_idx = 0; + uint32_t last_pix; + int last_idx; + uint32_t sorted[MAX_PALETTE_SIZE]; + int idx_map[MAX_PALETTE_SIZE]; + PrepareMapToPalette(palette, palette_size, sorted, idx_map); + last_pix = palette[0]; + last_idx = 0; for (y = 0; y < height; ++y) { - MapToPalette(palette, palette_size, &last_pix, &last_idx, - src, tmp_row, width); + MapToPalette(sorted, palette_size, &last_pix, &last_idx, + idx_map, src, tmp_row, width); VP8LBundleColorMap(tmp_row, width, xbits, dst); src += src_stride; dst += dst_stride; @@ -1376,7 +1383,7 @@ static void VP8LEncoderDelete(VP8LEncoder* enc) { WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, const WebPPicture* const picture, - VP8LBitWriter* const bw) { + VP8LBitWriter* const bw, int use_cache) { WebPEncodingError err = VP8_ENC_OK; const int quality = (int)config->quality; const int low_effort = (config->method == 0); @@ -1403,7 +1410,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, } // Apply near-lossless preprocessing. - use_near_lossless = !enc->use_palette_ && (config->near_lossless < 100); + use_near_lossless = + (config->near_lossless < 100) && !enc->use_palette_ && !enc->use_predict_; if (use_near_lossless) { if (!VP8ApplyNearLossless(width, height, picture->argb, config->near_lossless)) { @@ -1455,7 +1463,7 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, if (enc->use_predict_) { err = ApplyPredictFilter(enc, enc->current_width_, height, quality, - low_effort, bw); + low_effort, enc->use_subtract_green_, bw); if (err != VP8_ENC_OK) goto Error; } @@ -1472,8 +1480,8 @@ WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, // Encode and write the transformed image. err = EncodeImageInternal(bw, enc->argb_, &enc->hash_chain_, enc->refs_, enc->current_width_, height, quality, low_effort, - &enc->cache_bits_, enc->histo_bits_, byte_position, - &hdr_size, &data_size); + use_cache, &enc->cache_bits_, enc->histo_bits_, + byte_position, &hdr_size, &data_size); if (err != VP8_ENC_OK) goto Error; if (picture->stats != NULL) { @@ -1558,7 +1566,7 @@ int VP8LEncodeImage(const WebPConfig* const config, if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort; // Encode main image stream. - err = VP8LEncodeStream(config, picture, &bw); + err = VP8LEncodeStream(config, picture, &bw, 1 /*use_cache*/); if (err != VP8_ENC_OK) goto Error; // TODO(skal): have a fine-grained progress report in VP8LEncodeStream(). diff --git a/drivers/webp/enc/vp8li.h b/drivers/webp/enc/vp8li.h index 4543c3b260..8e6b360d81 100644 --- a/drivers/webp/enc/vp8li.h +++ b/drivers/webp/enc/vp8li.h @@ -25,14 +25,17 @@ extern "C" { #endif typedef struct { - const WebPConfig* config_; // user configuration and parameters - const WebPPicture* pic_; // input picture. + const WebPConfig* config_; // user configuration and parameters + const WebPPicture* pic_; // input picture. - uint32_t* argb_; // Transformed argb image data. - uint32_t* argb_scratch_; // Scratch memory for argb rows - // (used for prediction). - uint32_t* transform_data_; // Scratch memory for transform data. - int current_width_; // Corresponds to packed image width. + uint32_t* argb_; // Transformed argb image data. + uint32_t* argb_scratch_; // Scratch memory for argb rows + // (used for prediction). + uint32_t* transform_data_; // Scratch memory for transform data. + uint32_t* transform_mem_; // Currently allocated memory. + size_t transform_mem_size_; // Currently allocated memory size. + + int current_width_; // Corresponds to packed image width. // Encoding parameters derived from quality parameter. int histo_bits_; @@ -64,9 +67,10 @@ int VP8LEncodeImage(const WebPConfig* const config, const WebPPicture* const picture); // Encodes the main image stream using the supplied bit writer. +// If 'use_cache' is false, disables the use of color cache. WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, const WebPPicture* const picture, - VP8LBitWriter* const bw); + VP8LBitWriter* const bw, int use_cache); //------------------------------------------------------------------------------ diff --git a/drivers/webp/enc/webpenc.c b/drivers/webp/enc/webpenc.c index 8ced07a2a3..a7d04ea2ce 100644 --- a/drivers/webp/enc/webpenc.c +++ b/drivers/webp/enc/webpenc.c @@ -79,7 +79,9 @@ static void ResetBoundaryPredictions(VP8Encoder* const enc) { //-------------------+---+---+---+---+---+---+---+ // basic rd-opt | | | | x | x | x | x | //-------------------+---+---+---+---+---+---+---+ -// disto-score i4/16 | | | x | | | | | +// disto-refine i4/16| x | x | x | | | | | +//-------------------+---+---+---+---+---+---+---+ +// disto-refine uv | | x | x | | | | | //-------------------+---+---+---+---+---+---+---+ // rd-opt i4/16 | | | ~ | x | x | x | x | //-------------------+---+---+---+---+---+---+---+ @@ -103,6 +105,10 @@ static void MapConfigToTools(VP8Encoder* const enc) { 256 * 16 * 16 * // upper bound: up to 16bit per 4x4 block (limit * limit) / (100 * 100); // ... modulated with a quadratic curve. + // partition0 = 512k max. + enc->mb_header_limit_ = + (score_t)256 * 510 * 8 * 1024 / (enc->mb_w_ * enc->mb_h_); + enc->thread_level_ = config->thread_level; enc->do_search_ = (config->target_size > 0 || config->target_PSNR > 0); @@ -137,29 +143,30 @@ static void MapConfigToTools(VP8Encoder* const enc) { static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, WebPPicture* const picture) { + VP8Encoder* enc; const int use_filter = (config->filter_strength > 0) || (config->autofilter > 0); const int mb_w = (picture->width + 15) >> 4; const int mb_h = (picture->height + 15) >> 4; const int preds_w = 4 * mb_w + 1; const int preds_h = 4 * mb_h + 1; - const size_t preds_size = preds_w * preds_h * sizeof(uint8_t); + const size_t preds_size = preds_w * preds_h * sizeof(*enc->preds_); const int top_stride = mb_w * 16; - 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 * sizeof(uint8_t) // top-luma/u/v - + WEBP_ALIGN_CST; // align all + const size_t nz_size = (mb_w + 1) * sizeof(*enc->nz_) + WEBP_ALIGN_CST; + const size_t info_size = mb_w * mb_h * sizeof(*enc->mb_info_); + const size_t samples_size = + 2 * top_stride * sizeof(*enc->y_top_) // top-luma/u/v + + WEBP_ALIGN_CST; // align all const size_t lf_stats_size = - config->autofilter ? sizeof(LFStats) + WEBP_ALIGN_CST : 0; - VP8Encoder* enc; + config->autofilter ? sizeof(*enc->lf_stats_) + WEBP_ALIGN_CST : 0; uint8_t* mem; - const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct - + WEBP_ALIGN_CST // cache alignment - + info_size // modes info - + preds_size // prediction modes - + samples_size // top/left samples - + nz_size // coeff context bits - + lf_stats_size; // autofilter stats + const uint64_t size = (uint64_t)sizeof(*enc) // main struct + + WEBP_ALIGN_CST // cache alignment + + info_size // modes info + + preds_size // prediction modes + + samples_size // top/left samples + + nz_size // coeff context bits + + lf_stats_size; // autofilter stats #ifdef PRINT_MEMORY_INFO printf("===================================\n"); @@ -171,7 +178,7 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, " non-zero: %ld\n" " lf-stats: %ld\n" " total: %ld\n", - sizeof(VP8Encoder) + WEBP_ALIGN_CST, info_size, + sizeof(*enc) + WEBP_ALIGN_CST, info_size, preds_size, samples_size, nz_size, lf_stats_size, size); printf("Transient object sizes:\n" " VP8EncIterator: %ld\n" @@ -201,7 +208,7 @@ static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, 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); + mem += preds_size; enc->nz_ = 1 + (uint32_t*)WEBP_ALIGN(mem); mem += nz_size; enc->lf_stats_ = lf_stats_size ? (LFStats*)WEBP_ALIGN(mem) : NULL; @@ -321,14 +328,15 @@ 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 (!config->exact) { + WebPCleanupTransparentArea(pic); + } + if (pic->use_argb || pic->y == NULL || pic->u == NULL || pic->v == NULL) { // Make sure we have YUVA samples. if (config->preprocessing & 4) { @@ -376,6 +384,10 @@ int WebPEncode(const WebPConfig* config, WebPPicture* pic) { return 0; } + if (!config->exact) { + WebPCleanupTransparentAreaLossless(pic); + } + ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem. } diff --git a/drivers/webp/encode.h b/drivers/webp/encode.h index c382ea7608..9291b7195c 100644 --- a/drivers/webp/encode.h +++ b/drivers/webp/encode.h @@ -134,8 +134,8 @@ struct WebPConfig { int thread_level; // If non-zero, try and use multi-threaded encoding. int low_memory; // If set, reduce memory usage (but increase CPU use). - int near_lossless; // Near lossless encoding [0 = off(default) .. 100]. - // This feature is experimental. + int near_lossless; // Near lossless encoding [0 = max loss .. 100 = off + // (default)]. int exact; // if non-zero, preserve the exact RGB values under // transparent area. Otherwise, discard this invisible // RGB information for better compression. The default diff --git a/drivers/webp/mux.h b/drivers/webp/mux.h index 1fddfb76d4..b72658c741 100644 --- a/drivers/webp/mux.h +++ b/drivers/webp/mux.h @@ -462,7 +462,7 @@ WEBP_EXTERN(WebPAnimEncoder*) WebPAnimEncoderNewInternal( // Parameters: // width/height - (in) canvas width and height of the animation. // enc_options - (in) encoding options; can be passed NULL to pick -// reasonable defaults. +// reasonable defaults. // Returns: // A pointer to the newly created WebPAnimEncoder object. // Or NULL in case of memory error. diff --git a/drivers/webp/mux/anim_encode.c b/drivers/webp/mux/anim_encode.c index bb7c0f50b9..2226febf13 100644 --- a/drivers/webp/mux/anim_encode.c +++ b/drivers/webp/mux/anim_encode.c @@ -12,7 +12,9 @@ #include <assert.h> #include <limits.h> +#include <math.h> // for pow() #include <stdio.h> +#include <stdlib.h> // for abs() #include "../utils/utils.h" #include "webp/decode.h" @@ -49,8 +51,10 @@ struct WebPAnimEncoder { FrameRect prev_rect_; // Previous WebP frame rectangle. WebPConfig last_config_; // Cached in case a re-encode is needed. - WebPConfig last_config2_; // 2nd cached config; only valid if - // 'options_.allow_mixed' is true. + WebPConfig last_config_reversed_; // If 'last_config_' uses lossless, then + // this config uses lossy and vice versa; + // only valid if 'options_.allow_mixed' + // is true. WebPPicture* curr_canvas_; // Only pointer; we don't own memory. @@ -173,6 +177,7 @@ static void DefaultEncoderOptions(WebPAnimEncoderOptions* const enc_options) { enc_options->minimize_size = 0; DisableKeyframes(enc_options); enc_options->allow_mixed = 0; + enc_options->verbose = 0; } int WebPAnimEncoderOptionsInitInternal(WebPAnimEncoderOptions* enc_options, @@ -185,7 +190,8 @@ int WebPAnimEncoderOptionsInitInternal(WebPAnimEncoderOptions* enc_options, return 1; } -#define TRANSPARENT_COLOR 0x00ffffff +// This starting value is more fit to WebPCleanupTransparentAreaLossless(). +#define TRANSPARENT_COLOR 0x00000000 static void ClearRectangle(WebPPicture* const picture, int left, int top, int width, int height) { @@ -338,11 +344,16 @@ static EncodedFrame* GetFrame(const WebPAnimEncoder* const enc, return &enc->encoded_frames_[enc->start_ + position]; } -// Returns true if 'length' number of pixels in 'src' and 'dst' are identical, +typedef int (*ComparePixelsFunc)(const uint32_t*, int, const uint32_t*, int, + int, int); + +// Returns true if 'length' number of pixels in 'src' and 'dst' are equal, // assuming the given step sizes between pixels. -static WEBP_INLINE int ComparePixels(const uint32_t* src, int src_step, - const uint32_t* dst, int dst_step, - int length) { +// 'max_allowed_diff' is unused and only there to allow function pointer use. +static WEBP_INLINE int ComparePixelsLossless(const uint32_t* src, int src_step, + const uint32_t* dst, int dst_step, + int length, int max_allowed_diff) { + (void)max_allowed_diff; assert(length > 0); while (length-- > 0) { if (*src != *dst) { @@ -354,15 +365,62 @@ static WEBP_INLINE int ComparePixels(const uint32_t* src, int src_step, return 1; } +// Helper to check if each channel in 'src' and 'dst' is at most off by +// 'max_allowed_diff'. +static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst, + int max_allowed_diff) { + const int src_a = (src >> 24) & 0xff; + const int src_r = (src >> 16) & 0xff; + const int src_g = (src >> 8) & 0xff; + const int src_b = (src >> 0) & 0xff; + const int dst_a = (dst >> 24) & 0xff; + const int dst_r = (dst >> 16) & 0xff; + const int dst_g = (dst >> 8) & 0xff; + const int dst_b = (dst >> 0) & 0xff; + + return (abs(src_r * src_a - dst_r * dst_a) <= (max_allowed_diff * 255)) && + (abs(src_g * src_a - dst_g * dst_a) <= (max_allowed_diff * 255)) && + (abs(src_b * src_a - dst_b * dst_a) <= (max_allowed_diff * 255)) && + (abs(src_a - dst_a) <= max_allowed_diff); +} + +// Returns true if 'length' number of pixels in 'src' and 'dst' are within an +// error bound, assuming the given step sizes between pixels. +static WEBP_INLINE int ComparePixelsLossy(const uint32_t* src, int src_step, + const uint32_t* dst, int dst_step, + int length, int max_allowed_diff) { + assert(length > 0); + while (length-- > 0) { + if (!PixelsAreSimilar(*src, *dst, max_allowed_diff)) { + return 0; + } + src += src_step; + dst += dst_step; + } + return 1; +} + static int IsEmptyRect(const FrameRect* const rect) { return (rect->width_ == 0) || (rect->height_ == 0); } +static int QualityToMaxDiff(float quality) { + const double val = pow(quality / 100., 0.5); + const double max_diff = 31 * (1 - val) + 1 * val; + return (int)(max_diff + 0.5); +} + // Assumes that an initial valid guess of change rectangle 'rect' is passed. static void MinimizeChangeRectangle(const WebPPicture* const src, const WebPPicture* const dst, - FrameRect* const rect) { + FrameRect* const rect, + int is_lossless, float quality) { int i, j; + const ComparePixelsFunc compare_pixels = + is_lossless ? ComparePixelsLossless : ComparePixelsLossy; + const int max_allowed_diff_lossy = QualityToMaxDiff(quality); + const int max_allowed_diff = is_lossless ? 0 : max_allowed_diff_lossy; + // Sanity checks. assert(src->width == dst->width && src->height == dst->height); assert(rect->x_offset_ + rect->width_ <= dst->width); @@ -374,8 +432,8 @@ static void MinimizeChangeRectangle(const WebPPicture* const src, &src->argb[rect->y_offset_ * src->argb_stride + i]; const uint32_t* const dst_argb = &dst->argb[rect->y_offset_ * dst->argb_stride + i]; - if (ComparePixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride, - rect->height_)) { + if (compare_pixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride, + rect->height_, max_allowed_diff)) { --rect->width_; // Redundant column. ++rect->x_offset_; } else { @@ -390,8 +448,8 @@ static void MinimizeChangeRectangle(const WebPPicture* const src, &src->argb[rect->y_offset_ * src->argb_stride + i]; const uint32_t* const dst_argb = &dst->argb[rect->y_offset_ * dst->argb_stride + i]; - if (ComparePixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride, - rect->height_)) { + if (compare_pixels(src_argb, src->argb_stride, dst_argb, dst->argb_stride, + rect->height_, max_allowed_diff)) { --rect->width_; // Redundant column. } else { break; @@ -405,7 +463,8 @@ static void MinimizeChangeRectangle(const WebPPicture* const src, &src->argb[j * src->argb_stride + rect->x_offset_]; const uint32_t* const dst_argb = &dst->argb[j * dst->argb_stride + rect->x_offset_]; - if (ComparePixels(src_argb, 1, dst_argb, 1, rect->width_)) { + if (compare_pixels(src_argb, 1, dst_argb, 1, rect->width_, + max_allowed_diff)) { --rect->height_; // Redundant row. ++rect->y_offset_; } else { @@ -420,7 +479,8 @@ static void MinimizeChangeRectangle(const WebPPicture* const src, &src->argb[j * src->argb_stride + rect->x_offset_]; const uint32_t* const dst_argb = &dst->argb[j * dst->argb_stride + rect->x_offset_]; - if (ComparePixels(src_argb, 1, dst_argb, 1, rect->width_)) { + if (compare_pixels(src_argb, 1, dst_argb, 1, rect->width_, + max_allowed_diff)) { --rect->height_; // Redundant row. } else { break; @@ -445,20 +505,46 @@ static WEBP_INLINE void SnapToEvenOffsets(FrameRect* const rect) { rect->y_offset_ &= ~1; } +typedef struct { + int should_try_; // Should try this set of parameters. + int empty_rect_allowed_; // Frame with empty rectangle can be skipped. + FrameRect rect_ll_; // Frame rectangle for lossless compression. + WebPPicture sub_frame_ll_; // Sub-frame pic for lossless compression. + FrameRect rect_lossy_; // Frame rectangle for lossy compression. + // Could be smaller than rect_ll_ as pixels + // with small diffs can be ignored. + WebPPicture sub_frame_lossy_; // Sub-frame pic for lossless compression. +} SubFrameParams; + +static int SubFrameParamsInit(SubFrameParams* const params, + int should_try, int empty_rect_allowed) { + params->should_try_ = should_try; + params->empty_rect_allowed_ = empty_rect_allowed; + if (!WebPPictureInit(¶ms->sub_frame_ll_) || + !WebPPictureInit(¶ms->sub_frame_lossy_)) { + return 0; + } + return 1; +} + +static void SubFrameParamsFree(SubFrameParams* const params) { + WebPPictureFree(¶ms->sub_frame_ll_); + WebPPictureFree(¶ms->sub_frame_lossy_); +} + // Given previous and current canvas, picks the optimal rectangle for the -// current frame. The initial guess for 'rect' will be the full canvas. +// current frame based on 'is_lossless' and other parameters. Assumes that the +// initial guess 'rect' is valid. static int GetSubRect(const WebPPicture* const prev_canvas, const WebPPicture* const curr_canvas, int is_key_frame, int is_first_frame, int empty_rect_allowed, - FrameRect* const rect, WebPPicture* const sub_frame) { - rect->x_offset_ = 0; - rect->y_offset_ = 0; - rect->width_ = curr_canvas->width; - rect->height_ = curr_canvas->height; + int is_lossless, float quality, FrameRect* const rect, + WebPPicture* const sub_frame) { if (!is_key_frame || is_first_frame) { // Optimize frame rectangle. // Note: This behaves as expected for first frame, as 'prev_canvas' is // initialized to a fully transparent canvas in the beginning. - MinimizeChangeRectangle(prev_canvas, curr_canvas, rect); + MinimizeChangeRectangle(prev_canvas, curr_canvas, rect, + is_lossless, quality); } if (IsEmptyRect(rect)) { @@ -477,6 +563,29 @@ static int GetSubRect(const WebPPicture* const prev_canvas, rect->width_, rect->height_, sub_frame); } +// Picks optimal frame rectangle for both lossless and lossy compression. The +// initial guess for frame rectangles will be the full canvas. +static int GetSubRects(const WebPPicture* const prev_canvas, + const WebPPicture* const curr_canvas, int is_key_frame, + int is_first_frame, float quality, + SubFrameParams* const params) { + // Lossless frame rectangle. + params->rect_ll_.x_offset_ = 0; + params->rect_ll_.y_offset_ = 0; + params->rect_ll_.width_ = curr_canvas->width; + params->rect_ll_.height_ = curr_canvas->height; + if (!GetSubRect(prev_canvas, curr_canvas, is_key_frame, is_first_frame, + params->empty_rect_allowed_, 1, quality, + ¶ms->rect_ll_, ¶ms->sub_frame_ll_)) { + return 0; + } + // Lossy frame rectangle. + params->rect_lossy_ = params->rect_ll_; // seed with lossless rect. + return GetSubRect(prev_canvas, curr_canvas, is_key_frame, is_first_frame, + params->empty_rect_allowed_, 0, quality, + ¶ms->rect_lossy_, ¶ms->sub_frame_lossy_); +} + static void DisposeFrameRectangle(int dispose_method, const FrameRect* const rect, WebPPicture* const curr_canvas) { @@ -490,9 +599,9 @@ static uint32_t RectArea(const FrameRect* const rect) { return (uint32_t)rect->width_ * rect->height_; } -static int IsBlendingPossible(const WebPPicture* const src, - const WebPPicture* const dst, - const FrameRect* const rect) { +static int IsLosslessBlendingPossible(const WebPPicture* const src, + const WebPPicture* const dst, + const FrameRect* const rect) { int i, j; assert(src->width == dst->width && src->height == dst->height); assert(rect->x_offset_ + rect->width_ <= dst->width); @@ -512,88 +621,66 @@ static int IsBlendingPossible(const WebPPicture* const src, return 1; } -#define MIN_COLORS_LOSSY 31 // Don't try lossy below this threshold. -#define MAX_COLORS_LOSSLESS 194 // Don't try lossless above this threshold. -#define MAX_COLOR_COUNT 256 // Power of 2 greater than MAX_COLORS_LOSSLESS. -#define HASH_SIZE (MAX_COLOR_COUNT * 4) -#define HASH_RIGHT_SHIFT 22 // 32 - log2(HASH_SIZE). - -// TODO(urvang): Also used in enc/vp8l.c. Move to utils. -// If the number of colors in the 'pic' is at least MAX_COLOR_COUNT, return -// MAX_COLOR_COUNT. Otherwise, return the exact number of colors in the 'pic'. -static int GetColorCount(const WebPPicture* const pic) { - int x, y; - int num_colors = 0; - uint8_t in_use[HASH_SIZE] = { 0 }; - uint32_t colors[HASH_SIZE]; - static const uint32_t kHashMul = 0x1e35a7bd; - const uint32_t* argb = pic->argb; - const int width = pic->width; - const int height = pic->height; - uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] - - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - int key; - if (argb[x] == last_pix) { - continue; - } - last_pix = argb[x]; - key = (kHashMul * last_pix) >> HASH_RIGHT_SHIFT; - while (1) { - if (!in_use[key]) { - colors[key] = last_pix; - in_use[key] = 1; - ++num_colors; - if (num_colors >= MAX_COLOR_COUNT) { - return MAX_COLOR_COUNT; // Exact count not needed. - } - break; - } else if (colors[key] == last_pix) { - break; // The color is already there. - } else { - // Some other color sits here, so do linear conflict resolution. - ++key; - key &= (HASH_SIZE - 1); // Key mask. - } +static int IsLossyBlendingPossible(const WebPPicture* const src, + const WebPPicture* const dst, + const FrameRect* const rect, + float quality) { + const int max_allowed_diff_lossy = QualityToMaxDiff(quality); + int i, j; + assert(src->width == dst->width && src->height == dst->height); + assert(rect->x_offset_ + rect->width_ <= dst->width); + assert(rect->y_offset_ + rect->height_ <= dst->height); + for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) { + for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) { + const uint32_t src_pixel = src->argb[j * src->argb_stride + i]; + const uint32_t dst_pixel = dst->argb[j * dst->argb_stride + i]; + const uint32_t dst_alpha = dst_pixel >> 24; + if (dst_alpha != 0xff && + !PixelsAreSimilar(src_pixel, dst_pixel, max_allowed_diff_lossy)) { + // In this case, if we use blending, we can't attain the desired + // 'dst_pixel' value for this pixel. So, blending is not possible. + return 0; } } - argb += pic->argb_stride; } - return num_colors; + return 1; } -#undef MAX_COLOR_COUNT -#undef HASH_SIZE -#undef HASH_RIGHT_SHIFT - // For pixels in 'rect', replace those pixels in 'dst' that are same as 'src' by // transparent pixels. -static void IncreaseTransparency(const WebPPicture* const src, - const FrameRect* const rect, - WebPPicture* const dst) { +// Returns true if at least one pixel gets modified. +static int IncreaseTransparency(const WebPPicture* const src, + const FrameRect* const rect, + WebPPicture* const dst) { int i, j; + int modified = 0; assert(src != NULL && dst != NULL && rect != NULL); assert(src->width == dst->width && src->height == dst->height); for (j = rect->y_offset_; j < rect->y_offset_ + rect->height_; ++j) { const uint32_t* const psrc = src->argb + j * src->argb_stride; uint32_t* const pdst = dst->argb + j * dst->argb_stride; for (i = rect->x_offset_; i < rect->x_offset_ + rect->width_; ++i) { - if (psrc[i] == pdst[i]) { + if (psrc[i] == pdst[i] && pdst[i] != TRANSPARENT_COLOR) { pdst[i] = TRANSPARENT_COLOR; + modified = 1; } } } + return modified; } #undef TRANSPARENT_COLOR // Replace similar blocks of pixels by a 'see-through' transparent block // with uniform average color. -static void FlattenSimilarBlocks(const WebPPicture* const src, - const FrameRect* const rect, - WebPPicture* const dst) { +// Assumes lossy compression is being used. +// Returns true if at least one pixel gets modified. +static int FlattenSimilarBlocks(const WebPPicture* const src, + const FrameRect* const rect, + WebPPicture* const dst, float quality) { + const int max_allowed_diff_lossy = QualityToMaxDiff(quality); int i, j; + int modified = 0; const int block_size = 8; const int y_start = (rect->y_offset_ + block_size) & ~(block_size - 1); const int y_end = (rect->y_offset_ + rect->height_) & ~(block_size - 1); @@ -615,11 +702,12 @@ static void FlattenSimilarBlocks(const WebPPicture* const src, const uint32_t src_pixel = psrc[x + y * src->argb_stride]; const int alpha = src_pixel >> 24; if (alpha == 0xff && - src_pixel == pdst[x + y * dst->argb_stride]) { - ++cnt; - avg_r += (src_pixel >> 16) & 0xff; - avg_g += (src_pixel >> 8) & 0xff; - avg_b += (src_pixel >> 0) & 0xff; + PixelsAreSimilar(src_pixel, pdst[x + y * dst->argb_stride], + max_allowed_diff_lossy)) { + ++cnt; + avg_r += (src_pixel >> 16) & 0xff; + avg_g += (src_pixel >> 8) & 0xff; + avg_b += (src_pixel >> 0) & 0xff; } } } @@ -635,9 +723,11 @@ static void FlattenSimilarBlocks(const WebPPicture* const src, pdst[x + y * dst->argb_stride] = color; } } + modified = 1; } } } + return modified; } static int EncodeFrame(const WebPConfig* const config, WebPPicture* const pic, @@ -662,9 +752,10 @@ typedef struct { // Generates a candidate encoded frame given a picture and metadata. static WebPEncodingError EncodeCandidate(WebPPicture* const sub_frame, const FrameRect* const rect, - const WebPConfig* const config, + const WebPConfig* const encoder_config, int use_blending, Candidate* const candidate) { + WebPConfig config = *encoder_config; WebPEncodingError error_code = VP8_ENC_OK; assert(candidate != NULL); memset(candidate, 0, sizeof(*candidate)); @@ -682,7 +773,13 @@ static WebPEncodingError EncodeCandidate(WebPPicture* const sub_frame, // Encode picture. WebPMemoryWriterInit(&candidate->mem_); - if (!EncodeFrame(config, sub_frame, &candidate->mem_)) { + if (!config.lossless && use_blending) { + // Disable filtering to avoid blockiness in reconstructed frames at the + // time of decoding. + config.autofilter = 0; + config.filter_strength = 0; + } + if (!EncodeFrame(&config, sub_frame, &candidate->mem_)) { error_code = sub_frame->error_code; goto Err; } @@ -698,6 +795,8 @@ static WebPEncodingError EncodeCandidate(WebPPicture* const sub_frame, static void CopyCurrentCanvas(WebPAnimEncoder* const enc) { if (enc->curr_canvas_copy_modified_) { WebPCopyPixels(enc->curr_canvas_, &enc->curr_canvas_copy_); + enc->curr_canvas_copy_.progress_hook = enc->curr_canvas_->progress_hook; + enc->curr_canvas_copy_.user_data = enc->curr_canvas_->user_data; enc->curr_canvas_copy_modified_ = 0; } } @@ -710,12 +809,15 @@ enum { CANDIDATE_COUNT }; -// Generates candidates for a given dispose method given pre-filled 'rect' -// and 'sub_frame'. +#define MIN_COLORS_LOSSY 31 // Don't try lossy below this threshold. +#define MAX_COLORS_LOSSLESS 194 // Don't try lossless above this threshold. + +// Generates candidates for a given dispose method given pre-filled sub-frame +// 'params'. static WebPEncodingError GenerateCandidates( WebPAnimEncoder* const enc, Candidate candidates[CANDIDATE_COUNT], WebPMuxAnimDispose dispose_method, int is_lossless, int is_key_frame, - const FrameRect* const rect, WebPPicture* sub_frame, + SubFrameParams* const params, const WebPConfig* const config_ll, const WebPConfig* const config_lossy) { WebPEncodingError error_code = VP8_ENC_OK; const int is_dispose_none = (dispose_method == WEBP_MUX_DISPOSE_NONE); @@ -727,16 +829,24 @@ static WebPEncodingError GenerateCandidates( WebPPicture* const curr_canvas = &enc->curr_canvas_copy_; const WebPPicture* const prev_canvas = is_dispose_none ? &enc->prev_canvas_ : &enc->prev_canvas_disposed_; - const int use_blending = + int use_blending_ll; + int use_blending_lossy; + + CopyCurrentCanvas(enc); + use_blending_ll = + !is_key_frame && + IsLosslessBlendingPossible(prev_canvas, curr_canvas, ¶ms->rect_ll_); + use_blending_lossy = !is_key_frame && - IsBlendingPossible(prev_canvas, curr_canvas, rect); + IsLossyBlendingPossible(prev_canvas, curr_canvas, ¶ms->rect_lossy_, + config_lossy->quality); // Pick candidates to be tried. if (!enc->options_.allow_mixed) { candidate_ll->evaluate_ = is_lossless; candidate_lossy->evaluate_ = !is_lossless; } else { // Use a heuristic for trying lossless and/or lossy compression. - const int num_colors = GetColorCount(sub_frame); + const int num_colors = WebPGetColorPalette(¶ms->sub_frame_ll_, NULL); candidate_ll->evaluate_ = (num_colors < MAX_COLORS_LOSSLESS); candidate_lossy->evaluate_ = (num_colors >= MIN_COLORS_LOSSY); } @@ -744,23 +854,26 @@ static WebPEncodingError GenerateCandidates( // Generate candidates. if (candidate_ll->evaluate_) { CopyCurrentCanvas(enc); - if (use_blending) { - IncreaseTransparency(prev_canvas, rect, curr_canvas); - enc->curr_canvas_copy_modified_ = 1; + if (use_blending_ll) { + enc->curr_canvas_copy_modified_ = + IncreaseTransparency(prev_canvas, ¶ms->rect_ll_, curr_canvas); } - error_code = EncodeCandidate(sub_frame, rect, config_ll, use_blending, - candidate_ll); + error_code = EncodeCandidate(¶ms->sub_frame_ll_, ¶ms->rect_ll_, + config_ll, use_blending_ll, candidate_ll); if (error_code != VP8_ENC_OK) return error_code; } if (candidate_lossy->evaluate_) { CopyCurrentCanvas(enc); - if (use_blending) { - FlattenSimilarBlocks(prev_canvas, rect, curr_canvas); - enc->curr_canvas_copy_modified_ = 1; + if (use_blending_lossy) { + enc->curr_canvas_copy_modified_ = + FlattenSimilarBlocks(prev_canvas, ¶ms->rect_lossy_, curr_canvas, + config_lossy->quality); } - error_code = EncodeCandidate(sub_frame, rect, config_lossy, use_blending, - candidate_lossy); + error_code = + EncodeCandidate(¶ms->sub_frame_lossy_, ¶ms->rect_lossy_, + config_lossy, use_blending_lossy, candidate_lossy); if (error_code != VP8_ENC_OK) return error_code; + enc->curr_canvas_copy_modified_ = 1; } return error_code; } @@ -918,13 +1031,16 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, const int is_lossless = config->lossless; const int is_first_frame = enc->is_first_frame_; - int try_dispose_none = 1; // Default. - FrameRect rect_none; - WebPPicture sub_frame_none; // First frame cannot be skipped as there is no 'previous frame' to merge it // to. So, empty rectangle is not allowed for the first frame. const int empty_rect_allowed_none = !is_first_frame; + // Even if there is exact pixel match between 'disposed previous canvas' and + // 'current canvas', we can't skip current frame, as there may not be exact + // pixel match between 'previous canvas' and 'current canvas'. So, we don't + // allow empty rectangle in this case. + const int empty_rect_allowed_bg = 0; + // If current frame is a key-frame, dispose method of previous frame doesn't // matter, so we don't try dispose to background. // Also, if key-frame insertion is on, and previous frame could be picked as @@ -933,19 +1049,20 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, // background. const int dispose_bg_possible = !is_key_frame && !enc->prev_candidate_undecided_; - int try_dispose_bg = 0; // Default. - FrameRect rect_bg; - WebPPicture sub_frame_bg; + + SubFrameParams dispose_none_params; + SubFrameParams dispose_bg_params; WebPConfig config_ll = *config; WebPConfig config_lossy = *config; config_ll.lossless = 1; config_lossy.lossless = 0; enc->last_config_ = *config; - enc->last_config2_ = config->lossless ? config_lossy : config_ll; + enc->last_config_reversed_ = config->lossless ? config_lossy : config_ll; *frame_skipped = 0; - if (!WebPPictureInit(&sub_frame_none) || !WebPPictureInit(&sub_frame_bg)) { + if (!SubFrameParamsInit(&dispose_none_params, 1, empty_rect_allowed_none) || + !SubFrameParamsInit(&dispose_bg_params, 0, empty_rect_allowed_bg)) { return VP8_ENC_ERROR_INVALID_CONFIGURATION; } @@ -954,10 +1071,14 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, } // Change-rectangle assuming previous frame was DISPOSE_NONE. - GetSubRect(prev_canvas, curr_canvas, is_key_frame, is_first_frame, - empty_rect_allowed_none, &rect_none, &sub_frame_none); + if (!GetSubRects(prev_canvas, curr_canvas, is_key_frame, is_first_frame, + config_lossy.quality, &dispose_none_params)) { + error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION; + goto Err; + } - if (IsEmptyRect(&rect_none)) { + if ((is_lossless && IsEmptyRect(&dispose_none_params.rect_ll_)) || + (!is_lossless && IsEmptyRect(&dispose_none_params.rect_lossy_))) { // Don't encode the frame at all. Instead, the duration of the previous // frame will be increased later. assert(empty_rect_allowed_none); @@ -971,36 +1092,43 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, WebPCopyPixels(prev_canvas, prev_canvas_disposed); DisposeFrameRectangle(WEBP_MUX_DISPOSE_BACKGROUND, &enc->prev_rect_, prev_canvas_disposed); - // Even if there is exact pixel match between 'disposed previous canvas' and - // 'current canvas', we can't skip current frame, as there may not be exact - // pixel match between 'previous canvas' and 'current canvas'. So, we don't - // allow empty rectangle in this case. - GetSubRect(prev_canvas_disposed, curr_canvas, is_key_frame, is_first_frame, - 0 /* empty_rect_allowed */, &rect_bg, &sub_frame_bg); - assert(!IsEmptyRect(&rect_bg)); + + if (!GetSubRects(prev_canvas_disposed, curr_canvas, is_key_frame, + is_first_frame, config_lossy.quality, + &dispose_bg_params)) { + error_code = VP8_ENC_ERROR_INVALID_CONFIGURATION; + goto Err; + } + assert(!IsEmptyRect(&dispose_bg_params.rect_ll_)); + assert(!IsEmptyRect(&dispose_bg_params.rect_lossy_)); if (enc->options_.minimize_size) { // Try both dispose methods. - try_dispose_bg = 1; - try_dispose_none = 1; - } else if (RectArea(&rect_bg) < RectArea(&rect_none)) { - try_dispose_bg = 1; // Pick DISPOSE_BACKGROUND. - try_dispose_none = 0; + dispose_bg_params.should_try_ = 1; + dispose_none_params.should_try_ = 1; + } else if ((is_lossless && + RectArea(&dispose_bg_params.rect_ll_) < + RectArea(&dispose_none_params.rect_ll_)) || + (!is_lossless && + RectArea(&dispose_bg_params.rect_lossy_) < + RectArea(&dispose_none_params.rect_lossy_))) { + dispose_bg_params.should_try_ = 1; // Pick DISPOSE_BACKGROUND. + dispose_none_params.should_try_ = 0; } } - if (try_dispose_none) { + if (dispose_none_params.should_try_) { error_code = GenerateCandidates( enc, candidates, WEBP_MUX_DISPOSE_NONE, is_lossless, is_key_frame, - &rect_none, &sub_frame_none, &config_ll, &config_lossy); + &dispose_none_params, &config_ll, &config_lossy); if (error_code != VP8_ENC_OK) goto Err; } - if (try_dispose_bg) { + if (dispose_bg_params.should_try_) { assert(!enc->is_first_frame_); assert(dispose_bg_possible); error_code = GenerateCandidates( enc, candidates, WEBP_MUX_DISPOSE_BACKGROUND, is_lossless, is_key_frame, - &rect_bg, &sub_frame_bg, &config_ll, &config_lossy); + &dispose_bg_params, &config_ll, &config_lossy); if (error_code != VP8_ENC_OK) goto Err; } @@ -1016,8 +1144,8 @@ static WebPEncodingError SetFrame(WebPAnimEncoder* const enc, } End: - WebPPictureFree(&sub_frame_none); - WebPPictureFree(&sub_frame_bg); + SubFrameParamsFree(&dispose_none_params); + SubFrameParamsFree(&dispose_bg_params); return error_code; } @@ -1163,6 +1291,7 @@ static int FlushFrames(WebPAnimEncoder* const enc) { int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp, const WebPConfig* encoder_config) { WebPConfig config; + int ok; if (enc == NULL) { return 0; @@ -1212,6 +1341,10 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp, } if (encoder_config != NULL) { + if (!WebPValidateConfig(encoder_config)) { + MarkError(enc, "ERROR adding frame: Invalid WebPConfig"); + return 0; + } config = *encoder_config; } else { WebPConfigInit(&config); @@ -1222,17 +1355,14 @@ int WebPAnimEncoderAdd(WebPAnimEncoder* enc, WebPPicture* frame, int timestamp, assert(enc->curr_canvas_copy_modified_ == 1); CopyCurrentCanvas(enc); - if (!CacheFrame(enc, &config)) { - return 0; - } + ok = CacheFrame(enc, &config) && FlushFrames(enc); - if (!FlushFrames(enc)) { - return 0; - } enc->curr_canvas_ = NULL; enc->curr_canvas_copy_modified_ = 1; - enc->prev_timestamp_ = timestamp; - return 1; + if (ok) { + enc->prev_timestamp_ = timestamp; + } + return ok; } // ----------------------------------------------------------------------------- @@ -1278,7 +1408,7 @@ static int FrameToFullCanvas(WebPAnimEncoder* const enc, GetEncodedData(&mem1, full_image); if (enc->options_.allow_mixed) { - if (!EncodeFrame(&enc->last_config_, canvas_buf, &mem2)) goto Err; + if (!EncodeFrame(&enc->last_config_reversed_, canvas_buf, &mem2)) goto Err; if (mem2.size < mem1.size) { GetEncodedData(&mem2, full_image); WebPMemoryWriterClear(&mem1); diff --git a/drivers/webp/mux/muxedit.c b/drivers/webp/mux/muxedit.c index b27663f87a..9bbed42b1a 100644 --- a/drivers/webp/mux/muxedit.c +++ b/drivers/webp/mux/muxedit.c @@ -558,8 +558,8 @@ static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { height = mux->canvas_height_; } - if (flags == 0) { - // For Simple Image, VP8X chunk should not be added. + if (flags == 0 && mux->unknown_ == NULL) { + // For simple file format, VP8X chunk should not be added. return WEBP_MUX_OK; } diff --git a/drivers/webp/mux/muxi.h b/drivers/webp/mux/muxi.h index 8bd5291661..3f6428c4df 100644 --- a/drivers/webp/mux/muxi.h +++ b/drivers/webp/mux/muxi.h @@ -27,8 +27,8 @@ extern "C" { // Defines and constants. #define MUX_MAJ_VERSION 0 -#define MUX_MIN_VERSION 2 -#define MUX_REV_VERSION 2 +#define MUX_MIN_VERSION 3 +#define MUX_REV_VERSION 1 // Chunk object. typedef struct WebPChunk WebPChunk; diff --git a/drivers/webp/utils/bit_reader.c b/drivers/webp/utils/bit_reader.c index 4d6b4f0164..13c6cf316e 100644 --- a/drivers/webp/utils/bit_reader.c +++ b/drivers/webp/utils/bit_reader.c @@ -16,8 +16,7 @@ #endif #include "./bit_reader_inl.h" - -#define JAVASCRIPT_ENABLED // testing +#include "../utils/utils.h" //------------------------------------------------------------------------------ // VP8BitReader @@ -42,10 +41,9 @@ void VP8InitBitReader(VP8BitReader* const br, br->bits_ = -8; // to load the very first 8bits br->eof_ = 0; VP8BitReaderSetBuffer(br, start, size); - #ifdef JAVASCRIPT_ENABLED // html5 required aligned reads while(((uintptr_t)br->buf_ & 1) != 0 && !br->eof_) - VP8LoadFinalBytes(br); + VP8LoadFinalBytes(br); #else VP8LoadNewBytes(br); #endif @@ -127,11 +125,10 @@ int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) { #define VP8L_LOG8_WBITS 4 // Number of bytes needed to store VP8L_WBITS bits. -#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 +#if defined(__arm__) || defined(_M_ARM) || defined(__aarch64__) || \ + defined(__i386__) || defined(_M_IX86) || \ + defined(__x86_64__) || defined(_M_X64) +#define VP8L_USE_FAST_LOAD #endif static const uint32_t kBitMask[VP8L_MAX_NUM_BIT_READ + 1] = { @@ -199,15 +196,11 @@ static void ShiftBytes(VP8LBitReader* const 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 defined(VP8L_USE_FAST_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_) << + br->val_ |= (vp8l_val_t)HToLE32(WebPMemToUint32(br->buf_ + br->pos_)) << (VP8L_LBITS - VP8L_WBITS); br->pos_ += VP8L_LOG8_WBITS; return; diff --git a/drivers/webp/utils/bit_reader.h b/drivers/webp/utils/bit_reader.h index f0f450231d..c2dcc711c0 100644 --- a/drivers/webp/utils/bit_reader.h +++ b/drivers/webp/utils/bit_reader.h @@ -49,10 +49,12 @@ extern "C" { #define BITS 56 #elif defined(__arm__) || defined(_M_ARM) // ARM #define BITS 24 +#elif defined(__aarch64__) // ARM 64bit +#define BITS 56 #elif defined(__mips__) // MIPS #define BITS 24 #else // reasonable default -#define BITS 24 // TODO(skal): test aarch64 and find the proper BITS value. +#define BITS 24 #endif #endif diff --git a/drivers/webp/utils/bit_reader_inl.h b/drivers/webp/utils/bit_reader_inl.h index 20ce5f3cc7..21faf14d83 100644 --- a/drivers/webp/utils/bit_reader_inl.h +++ b/drivers/webp/utils/bit_reader_inl.h @@ -55,7 +55,8 @@ void VP8LoadFinalBytes(VP8BitReader* const br); // Inlined critical functions // makes sure br->value_ has at least BITS bits worth of data -static WEBP_INLINE void VP8LoadNewBytes(VP8BitReader* const br) { +static WEBP_UBSAN_IGNORE_UNDEF WEBP_INLINE +void VP8LoadNewBytes(VP8BitReader* const br) { assert(br != NULL && br->buf_ != NULL); // Read 'BITS' bits at a time if possible. if (br->buf_ < br->buf_max_) { diff --git a/drivers/webp/utils/color_cache.c b/drivers/webp/utils/color_cache.c index f9ff4b5451..c34b2e7f1a 100644 --- a/drivers/webp/utils/color_cache.c +++ b/drivers/webp/utils/color_cache.c @@ -15,7 +15,7 @@ #include <stdlib.h> #include <string.h> #include "./color_cache.h" -#include "../utils/utils.h" +#include "./utils.h" //------------------------------------------------------------------------------ // VP8LColorCache. diff --git a/drivers/webp/utils/endian_inl.h b/drivers/webp/utils/endian_inl.h index 253b7be8ee..e11260ff7d 100644 --- a/drivers/webp/utils/endian_inl.h +++ b/drivers/webp/utils/endian_inl.h @@ -13,11 +13,11 @@ #define WEBP_UTILS_ENDIAN_INL_H_ #ifdef HAVE_CONFIG_H -#include "webp/config.h" +#include "../webp/config.h" #endif #include "../dsp/dsp.h" -#include "webp/types.h" +#include "../webp/types.h" // some endian fix (e.g.: mips-gcc doesn't define __BIG_ENDIAN__) #if !defined(WORDS_BIGENDIAN) && \ diff --git a/drivers/webp/utils/huffman.c b/drivers/webp/utils/huffman.c index e6f482a6a8..7c9d83db9a 100644 --- a/drivers/webp/utils/huffman.c +++ b/drivers/webp/utils/huffman.c @@ -15,8 +15,8 @@ #include <stdlib.h> #include <string.h> #include "./huffman.h" -#include "../utils/utils.h" #include "webp/format_constants.h" +#include "./utils.h" // Huffman data read via DecodeImageStream is represented in two (red and green) // bytes. diff --git a/drivers/webp/utils/huffman_encode.c b/drivers/webp/utils/huffman_encode.c index d7aad6f56d..0be414a8f8 100644 --- a/drivers/webp/utils/huffman_encode.c +++ b/drivers/webp/utils/huffman_encode.c @@ -15,8 +15,8 @@ #include <stdlib.h> #include <string.h> #include "./huffman_encode.h" -#include "../utils/utils.h" #include "webp/format_constants.h" +#include "./utils.h" // ----------------------------------------------------------------------------- // Util function to optimize the symbol map for RLE coding diff --git a/drivers/webp/utils/quant_levels_dec.c b/drivers/webp/utils/quant_levels_dec.c index 5b8b8b49e6..ee0a3fe127 100644 --- a/drivers/webp/utils/quant_levels_dec.c +++ b/drivers/webp/utils/quant_levels_dec.c @@ -44,6 +44,7 @@ static const uint8_t kOrderedDither[DSIZE][DSIZE] = { typedef struct { int width_, height_; // dimension + int stride_; // stride in bytes int row_; // current input row being processed uint8_t* src_; // input pointer uint8_t* dst_; // output pointer @@ -99,7 +100,7 @@ static void VFilter(SmoothParams* const p) { // We replicate edges, as it's somewhat easier as a boundary condition. // That's why we don't update the 'src' pointer on top/bottom area: if (p->row_ >= 0 && p->row_ < p->height_ - 1) { - p->src_ += p->width_; + p->src_ += p->stride_; } } @@ -149,7 +150,7 @@ static void ApplyFilter(SmoothParams* const p) { #endif } } - p->dst_ += w; // advance output pointer + p->dst_ += p->stride_; // advance output pointer } //------------------------------------------------------------------------------ @@ -178,17 +179,20 @@ static void InitCorrectionLUT(int16_t* const lut, int min_dist) { lut[0] = 0; } -static void CountLevels(const uint8_t* const data, int size, - SmoothParams* const p) { - int i, last_level; +static void CountLevels(SmoothParams* const p) { + int i, j, last_level; uint8_t used_levels[256] = { 0 }; + const uint8_t* data = p->src_; p->min_ = 255; p->max_ = 0; - for (i = 0; i < size; ++i) { - const int v = data[i]; - if (v < p->min_) p->min_ = v; - if (v > p->max_) p->max_ = v; - used_levels[v] = 1; + for (j = 0; j < p->height_; ++j) { + for (i = 0; i < p->width_; ++i) { + const int v = data[i]; + if (v < p->min_) p->min_ = v; + if (v > p->max_) p->max_ = v; + used_levels[v] = 1; + } + data += p->stride_; } // Compute the mininum distance between two non-zero levels. p->min_level_dist_ = p->max_ - p->min_; @@ -208,7 +212,7 @@ static void CountLevels(const uint8_t* const data, int size, } // Initialize all params. -static int InitParams(uint8_t* const data, int width, int height, +static int InitParams(uint8_t* const data, int width, int height, int stride, int radius, SmoothParams* const p) { const int R = 2 * radius + 1; // total size of the kernel @@ -233,6 +237,7 @@ static int InitParams(uint8_t* const data, int width, int height, p->width_ = width; p->height_ = height; + p->stride_ = stride; p->src_ = data; p->dst_ = data; p->radius_ = radius; @@ -240,7 +245,7 @@ static int InitParams(uint8_t* const data, int width, int height, p->row_ = -radius; // analyze the input distribution so we can best-fit the threshold - CountLevels(data, width * height, p); + CountLevels(p); // correction table p->correction_ = ((int16_t*)mem) + LUT_SIZE; @@ -253,7 +258,7 @@ static void CleanupParams(SmoothParams* const p) { WebPSafeFree(p->mem_); } -int WebPDequantizeLevels(uint8_t* const data, int width, int height, +int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride, int strength) { const int radius = 4 * strength / 100; if (strength < 0 || strength > 100) return 0; @@ -261,7 +266,7 @@ int WebPDequantizeLevels(uint8_t* const data, int width, int height, if (radius > 0) { SmoothParams p; memset(&p, 0, sizeof(p)); - if (!InitParams(data, width, height, radius, &p)) return 0; + if (!InitParams(data, width, height, stride, radius, &p)) return 0; if (p.num_levels_ > 2) { for (; p.row_ < p.height_; ++p.row_) { VFilter(&p); // accumulate average of input diff --git a/drivers/webp/utils/quant_levels_dec.h b/drivers/webp/utils/quant_levels_dec.h index 29c7e6e205..c99a475386 100644 --- a/drivers/webp/utils/quant_levels_dec.h +++ b/drivers/webp/utils/quant_levels_dec.h @@ -21,11 +21,11 @@ extern "C" { #endif // Apply post-processing to input 'data' of size 'width'x'height' assuming that -// the source was quantized to a reduced number of levels. +// the source was quantized to a reduced number of levels. 'stride' is in bytes. // Strength is in [0..100] and controls the amount of dithering applied. // Returns false in case of error (data is NULL, invalid parameters, // malloc failure, ...). -int WebPDequantizeLevels(uint8_t* const data, int width, int height, +int WebPDequantizeLevels(uint8_t* const data, int width, int height, int stride, int strength); #ifdef __cplusplus diff --git a/drivers/webp/utils/utils.c b/drivers/webp/utils/utils.c index 35aeae6ab8..b2193c4b47 100644 --- a/drivers/webp/utils/utils.c +++ b/drivers/webp/utils/utils.c @@ -15,6 +15,7 @@ #include <string.h> // for memcpy() #include "webp/decode.h" #include "webp/encode.h" +#include "webp/format_constants.h" // for MAX_PALETTE_SIZE #include "./utils.h" // If PRINT_MEM_INFO is defined, extra info (like total memory used, number of @@ -237,3 +238,68 @@ void WebPCopyPixels(const WebPPicture* const src, WebPPicture* const dst) { } //------------------------------------------------------------------------------ + +#define MAX_COLOR_COUNT MAX_PALETTE_SIZE +#define COLOR_HASH_SIZE (MAX_COLOR_COUNT * 4) +#define COLOR_HASH_RIGHT_SHIFT 22 // 32 - log2(COLOR_HASH_SIZE). + +int WebPGetColorPalette(const WebPPicture* const pic, uint32_t* const palette) { + int i; + int x, y; + int num_colors = 0; + uint8_t in_use[COLOR_HASH_SIZE] = { 0 }; + uint32_t colors[COLOR_HASH_SIZE]; + static const uint32_t kHashMul = 0x1e35a7bdU; + const uint32_t* argb = pic->argb; + const int width = pic->width; + const int height = pic->height; + uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] + assert(pic != NULL); + assert(pic->use_argb); + + for (y = 0; y < height; ++y) { + for (x = 0; x < width; ++x) { + int key; + if (argb[x] == last_pix) { + continue; + } + last_pix = argb[x]; + key = (kHashMul * last_pix) >> COLOR_HASH_RIGHT_SHIFT; + while (1) { + if (!in_use[key]) { + colors[key] = last_pix; + in_use[key] = 1; + ++num_colors; + if (num_colors > MAX_COLOR_COUNT) { + return MAX_COLOR_COUNT + 1; // Exact count not needed. + } + break; + } else if (colors[key] == last_pix) { + break; // The color is already there. + } else { + // Some other color sits here, so do linear conflict resolution. + ++key; + key &= (COLOR_HASH_SIZE - 1); // Key mask. + } + } + } + argb += pic->argb_stride; + } + + if (palette != NULL) { // Fill the colors into palette. + num_colors = 0; + for (i = 0; i < COLOR_HASH_SIZE; ++i) { + if (in_use[i]) { + palette[num_colors] = colors[i]; + ++num_colors; + } + } + } + return num_colors; +} + +#undef MAX_COLOR_COUNT +#undef COLOR_HASH_SIZE +#undef COLOR_HASH_RIGHT_SHIFT + +//------------------------------------------------------------------------------ diff --git a/drivers/webp/utils/utils.h b/drivers/webp/utils/utils.h index d0e1cb250a..cef496af78 100644 --- a/drivers/webp/utils/utils.h +++ b/drivers/webp/utils/utils.h @@ -15,8 +15,13 @@ #ifndef WEBP_UTILS_UTILS_H_ #define WEBP_UTILS_UTILS_H_ +#ifdef HAVE_CONFIG_H +#include "webp/config.h" +#endif + #include <assert.h> +#include "../dsp/dsp.h" #include "webp/types.h" #ifdef __cplusplus @@ -47,7 +52,29 @@ 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) +#define WEBP_ALIGN(PTR) (((uintptr_t)(PTR) + WEBP_ALIGN_CST) & ~WEBP_ALIGN_CST) + +#if defined(WEBP_FORCE_ALIGNED) +#include <string.h> +// memcpy() is the safe way of moving potentially unaligned 32b memory. +static WEBP_INLINE uint32_t WebPMemToUint32(const uint8_t* const ptr) { + uint32_t A; + memcpy(&A, (const int*)ptr, sizeof(A)); + return A; +} +static WEBP_INLINE void WebPUint32ToMem(uint8_t* const ptr, uint32_t val) { + memcpy(ptr, &val, sizeof(val)); +} +#else +static WEBP_UBSAN_IGNORE_UNDEF WEBP_INLINE +uint32_t WebPMemToUint32(const uint8_t* const ptr) { + return *(const uint32_t*)ptr; +} +static WEBP_UBSAN_IGNORE_UNDEF WEBP_INLINE +void WebPUint32ToMem(uint8_t* const ptr, uint32_t val) { + *(uint32_t*)ptr = val; +} +#endif //------------------------------------------------------------------------------ // Reading/writing data. @@ -134,6 +161,19 @@ WEBP_EXTERN(void) WebPCopyPixels(const struct WebPPicture* const src, struct WebPPicture* const dst); //------------------------------------------------------------------------------ +// Unique colors. + +// Returns count of unique colors in 'pic', assuming pic->use_argb is true. +// If the unique color count is more than MAX_COLOR_COUNT, returns +// MAX_COLOR_COUNT+1. +// If 'palette' is not NULL and number of unique colors is less than or equal to +// MAX_COLOR_COUNT, also outputs the actual unique colors into 'palette'. +// Note: 'palette' is assumed to be an array already allocated with at least +// MAX_COLOR_COUNT elements. +WEBP_EXTERN(int) WebPGetColorPalette(const struct WebPPicture* const pic, + uint32_t* const palette); + +//------------------------------------------------------------------------------ #ifdef __cplusplus } // extern "C" diff --git a/drivers/webpold/SCsub b/drivers/webpold/SCsub deleted file mode 100644 index 5596edbe09..0000000000 --- a/drivers/webpold/SCsub +++ /dev/null @@ -1,63 +0,0 @@ -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') diff --git a/drivers/webpold/dec/alpha.c b/drivers/webpold/dec/alpha.c deleted file mode 100644 index d1095fa555..0000000000 --- a/drivers/webpold/dec/alpha.c +++ /dev/null @@ -1,140 +0,0 @@ -// 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 diff --git a/drivers/webpold/dec/buffer.c b/drivers/webpold/dec/buffer.c deleted file mode 100644 index c159f6f248..0000000000 --- a/drivers/webpold/dec/buffer.c +++ /dev/null @@ -1,215 +0,0 @@ -// 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 diff --git a/drivers/webpold/dec/decode_vp8.h b/drivers/webpold/dec/decode_vp8.h deleted file mode 100644 index c26a9fc891..0000000000 --- a/drivers/webpold/dec/decode_vp8.h +++ /dev/null @@ -1,182 +0,0 @@ -// 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_ */ diff --git a/drivers/webpold/dec/frame.c b/drivers/webpold/dec/frame.c deleted file mode 100644 index 9c91a48e17..0000000000 --- a/drivers/webpold/dec/frame.c +++ /dev/null @@ -1,679 +0,0 @@ -// 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 diff --git a/drivers/webpold/dec/idec.c b/drivers/webpold/dec/idec.c deleted file mode 100644 index 7df790ced8..0000000000 --- a/drivers/webpold/dec/idec.c +++ /dev/null @@ -1,785 +0,0 @@ -// 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 diff --git a/drivers/webpold/dec/io.c b/drivers/webpold/dec/io.c deleted file mode 100644 index 594804c2e6..0000000000 --- a/drivers/webpold/dec/io.c +++ /dev/null @@ -1,633 +0,0 @@ -// 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 diff --git a/drivers/webpold/dec/layer.c b/drivers/webpold/dec/layer.c deleted file mode 100644 index a3a5bdcfe8..0000000000 --- a/drivers/webpold/dec/layer.c +++ /dev/null @@ -1,35 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Enhancement layer (for YUV444/422) -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <assert.h> -#include <stdlib.h> - -#include "./vp8i.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ - -int VP8DecodeLayer(VP8Decoder* const dec) { - assert(dec); - assert(dec->layer_data_size_ > 0); - (void)dec; - - // TODO: handle enhancement layer here. - - return 1; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/dec/quant.c b/drivers/webpold/dec/quant.c deleted file mode 100644 index d54097af0d..0000000000 --- a/drivers/webpold/dec/quant.c +++ /dev/null @@ -1,113 +0,0 @@ -// 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 diff --git a/drivers/webpold/dec/tree.c b/drivers/webpold/dec/tree.c deleted file mode 100644 index 82484e4c55..0000000000 --- a/drivers/webpold/dec/tree.c +++ /dev/null @@ -1,589 +0,0 @@ -// 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 diff --git a/drivers/webpold/dec/vp8.c b/drivers/webpold/dec/vp8.c deleted file mode 100644 index b0ccfa2a06..0000000000 --- a/drivers/webpold/dec/vp8.c +++ /dev/null @@ -1,787 +0,0 @@ -// 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 diff --git a/drivers/webpold/dec/vp8i.h b/drivers/webpold/dec/vp8i.h deleted file mode 100644 index 4382edfd8e..0000000000 --- a/drivers/webpold/dec/vp8i.h +++ /dev/null @@ -1,335 +0,0 @@ -// 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_ */ diff --git a/drivers/webpold/dec/vp8l.c b/drivers/webpold/dec/vp8l.c deleted file mode 100644 index 897e4395c7..0000000000 --- a/drivers/webpold/dec/vp8l.c +++ /dev/null @@ -1,1200 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// main entry for the decoder -// -// Authors: Vikas Arora (vikaas.arora@gmail.com) -// Jyrki Alakuijala (jyrki@google.com) - -#include <stdio.h> -#include <stdlib.h> -#include "./vp8li.h" -#include "../dsp/lossless.h" -#include "../dsp/yuv.h" -#include "../utils/huffman.h" -#include "../utils/utils.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define NUM_ARGB_CACHE_ROWS 16 - -static const int kCodeLengthLiterals = 16; -static const int kCodeLengthRepeatCode = 16; -static const int kCodeLengthExtraBits[3] = { 2, 3, 7 }; -static const int kCodeLengthRepeatOffsets[3] = { 3, 3, 11 }; - -// ----------------------------------------------------------------------------- -// Five Huffman codes are used at each meta code: -// 1. green + length prefix codes + color cache codes, -// 2. alpha, -// 3. red, -// 4. blue, and, -// 5. distance prefix codes. -typedef enum { - GREEN = 0, - RED = 1, - BLUE = 2, - ALPHA = 3, - DIST = 4 -} HuffIndex; - -static const uint16_t kAlphabetSize[HUFFMAN_CODES_PER_META_CODE] = { - NUM_LITERAL_CODES + NUM_LENGTH_CODES, - NUM_LITERAL_CODES, NUM_LITERAL_CODES, NUM_LITERAL_CODES, - NUM_DISTANCE_CODES -}; - - -#define NUM_CODE_LENGTH_CODES 19 -static const uint8_t kCodeLengthCodeOrder[NUM_CODE_LENGTH_CODES] = { - 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 -}; - -#define CODE_TO_PLANE_CODES 120 -static const uint8_t code_to_plane_lut[CODE_TO_PLANE_CODES] = { - 0x18, 0x07, 0x17, 0x19, 0x28, 0x06, 0x27, 0x29, 0x16, 0x1a, - 0x26, 0x2a, 0x38, 0x05, 0x37, 0x39, 0x15, 0x1b, 0x36, 0x3a, - 0x25, 0x2b, 0x48, 0x04, 0x47, 0x49, 0x14, 0x1c, 0x35, 0x3b, - 0x46, 0x4a, 0x24, 0x2c, 0x58, 0x45, 0x4b, 0x34, 0x3c, 0x03, - 0x57, 0x59, 0x13, 0x1d, 0x56, 0x5a, 0x23, 0x2d, 0x44, 0x4c, - 0x55, 0x5b, 0x33, 0x3d, 0x68, 0x02, 0x67, 0x69, 0x12, 0x1e, - 0x66, 0x6a, 0x22, 0x2e, 0x54, 0x5c, 0x43, 0x4d, 0x65, 0x6b, - 0x32, 0x3e, 0x78, 0x01, 0x77, 0x79, 0x53, 0x5d, 0x11, 0x1f, - 0x64, 0x6c, 0x42, 0x4e, 0x76, 0x7a, 0x21, 0x2f, 0x75, 0x7b, - 0x31, 0x3f, 0x63, 0x6d, 0x52, 0x5e, 0x00, 0x74, 0x7c, 0x41, - 0x4f, 0x10, 0x20, 0x62, 0x6e, 0x30, 0x73, 0x7d, 0x51, 0x5f, - 0x40, 0x72, 0x7e, 0x61, 0x6f, 0x50, 0x71, 0x7f, 0x60, 0x70 -}; - -static int DecodeImageStream(int xsize, int ysize, - int is_level0, - VP8LDecoder* const dec, - uint32_t** const decoded_data); - -//------------------------------------------------------------------------------ - -int VP8LCheckSignature(const uint8_t* const data, size_t size) { - return (size >= 1) && (data[0] == VP8L_MAGIC_BYTE); -} - -static int ReadImageInfo(VP8LBitReader* const br, - int* const width, int* const height, - int* const has_alpha) { - const uint8_t signature = VP8LReadBits(br, 8); - if (!VP8LCheckSignature(&signature, 1)) { - return 0; - } - *width = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; - *height = VP8LReadBits(br, VP8L_IMAGE_SIZE_BITS) + 1; - *has_alpha = VP8LReadBits(br, 1); - VP8LReadBits(br, VP8L_VERSION_BITS); // Read/ignore the version number. - return 1; -} - -int VP8LGetInfo(const uint8_t* data, size_t data_size, - int* const width, int* const height, int* const has_alpha) { - if (data == NULL || data_size < VP8L_FRAME_HEADER_SIZE) { - return 0; // not enough data - } else { - int w, h, a; - VP8LBitReader br; - VP8LInitBitReader(&br, data, data_size); - if (!ReadImageInfo(&br, &w, &h, &a)) { - return 0; - } - if (width != NULL) *width = w; - if (height != NULL) *height = h; - if (has_alpha != NULL) *has_alpha = a; - return 1; - } -} - -//------------------------------------------------------------------------------ - -static WEBP_INLINE int GetCopyDistance(int distance_symbol, - VP8LBitReader* const br) { - int extra_bits, offset; - if (distance_symbol < 4) { - return distance_symbol + 1; - } - extra_bits = (distance_symbol - 2) >> 1; - offset = (2 + (distance_symbol & 1)) << extra_bits; - return offset + VP8LReadBits(br, extra_bits) + 1; -} - -static WEBP_INLINE int GetCopyLength(int length_symbol, - VP8LBitReader* const br) { - // Length and distance prefixes are encoded the same way. - return GetCopyDistance(length_symbol, br); -} - -static WEBP_INLINE int PlaneCodeToDistance(int xsize, int plane_code) { - if (plane_code > CODE_TO_PLANE_CODES) { - return plane_code - CODE_TO_PLANE_CODES; - } else { - const int dist_code = code_to_plane_lut[plane_code - 1]; - const int yoffset = dist_code >> 4; - const int xoffset = 8 - (dist_code & 0xf); - const int dist = yoffset * xsize + xoffset; - return (dist >= 1) ? dist : 1; - } -} - -//------------------------------------------------------------------------------ -// Decodes the next Huffman code from bit-stream. -// FillBitWindow(br) needs to be called at minimum every second call -// to ReadSymbolUnsafe. -static int ReadSymbolUnsafe(const HuffmanTree* tree, VP8LBitReader* const br) { - const HuffmanTreeNode* node = tree->root_; - assert(node != NULL); - while (!HuffmanTreeNodeIsLeaf(node)) { - node = HuffmanTreeNextNode(node, VP8LReadOneBitUnsafe(br)); - } - return node->symbol_; -} - -static WEBP_INLINE int ReadSymbol(const HuffmanTree* tree, - VP8LBitReader* const br) { - const int read_safe = (br->pos_ + 8 > br->len_); - if (!read_safe) { - return ReadSymbolUnsafe(tree, br); - } else { - const HuffmanTreeNode* node = tree->root_; - assert(node != NULL); - while (!HuffmanTreeNodeIsLeaf(node)) { - node = HuffmanTreeNextNode(node, VP8LReadOneBit(br)); - } - return node->symbol_; - } -} - -static int ReadHuffmanCodeLengths( - VP8LDecoder* const dec, const int* const code_length_code_lengths, - int num_symbols, int* const code_lengths) { - int ok = 0; - VP8LBitReader* const br = &dec->br_; - int symbol; - int max_symbol; - int prev_code_len = DEFAULT_CODE_LENGTH; - HuffmanTree tree; - - if (!HuffmanTreeBuildImplicit(&tree, code_length_code_lengths, - NUM_CODE_LENGTH_CODES)) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; - } - - if (VP8LReadBits(br, 1)) { // use length - const int length_nbits = 2 + 2 * VP8LReadBits(br, 3); - max_symbol = 2 + VP8LReadBits(br, length_nbits); - if (max_symbol > num_symbols) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - goto End; - } - } else { - max_symbol = num_symbols; - } - - symbol = 0; - while (symbol < num_symbols) { - int code_len; - if (max_symbol-- == 0) break; - VP8LFillBitWindow(br); - code_len = ReadSymbol(&tree, br); - if (code_len < kCodeLengthLiterals) { - code_lengths[symbol++] = code_len; - if (code_len != 0) prev_code_len = code_len; - } else { - const int use_prev = (code_len == kCodeLengthRepeatCode); - const int slot = code_len - kCodeLengthLiterals; - const int extra_bits = kCodeLengthExtraBits[slot]; - const int repeat_offset = kCodeLengthRepeatOffsets[slot]; - int repeat = VP8LReadBits(br, extra_bits) + repeat_offset; - if (symbol + repeat > num_symbols) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - goto End; - } else { - const int length = use_prev ? prev_code_len : 0; - while (repeat-- > 0) code_lengths[symbol++] = length; - } - } - } - ok = 1; - - End: - HuffmanTreeRelease(&tree); - return ok; -} - -static int ReadHuffmanCode(int alphabet_size, VP8LDecoder* const dec, - HuffmanTree* const tree) { - int ok = 0; - VP8LBitReader* const br = &dec->br_; - const int simple_code = VP8LReadBits(br, 1); - - if (simple_code) { // Read symbols, codes & code lengths directly. - int symbols[2]; - int codes[2]; - int code_lengths[2]; - const int num_symbols = VP8LReadBits(br, 1) + 1; - const int first_symbol_len_code = VP8LReadBits(br, 1); - // The first code is either 1 bit or 8 bit code. - symbols[0] = VP8LReadBits(br, (first_symbol_len_code == 0) ? 1 : 8); - codes[0] = 0; - code_lengths[0] = num_symbols - 1; - // The second code (if present), is always 8 bit long. - if (num_symbols == 2) { - symbols[1] = VP8LReadBits(br, 8); - codes[1] = 1; - code_lengths[1] = num_symbols - 1; - } - ok = HuffmanTreeBuildExplicit(tree, code_lengths, codes, symbols, - alphabet_size, num_symbols); - } else { // Decode Huffman-coded code lengths. - int* code_lengths = NULL; - int i; - int code_length_code_lengths[NUM_CODE_LENGTH_CODES] = { 0 }; - const int num_codes = VP8LReadBits(br, 4) + 4; - if (num_codes > NUM_CODE_LENGTH_CODES) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; - } - - code_lengths = - (int*)WebPSafeCalloc((uint64_t)alphabet_size, sizeof(*code_lengths)); - if (code_lengths == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - return 0; - } - - for (i = 0; i < num_codes; ++i) { - code_length_code_lengths[kCodeLengthCodeOrder[i]] = VP8LReadBits(br, 3); - } - ok = ReadHuffmanCodeLengths(dec, code_length_code_lengths, alphabet_size, - code_lengths); - if (ok) { - ok = HuffmanTreeBuildImplicit(tree, code_lengths, alphabet_size); - } - free(code_lengths); - } - ok = ok && !br->error_; - if (!ok) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - return 0; - } - return 1; -} - -static void DeleteHtreeGroups(HTreeGroup* htree_groups, int num_htree_groups) { - if (htree_groups != NULL) { - int i, j; - for (i = 0; i < num_htree_groups; ++i) { - HuffmanTree* const htrees = htree_groups[i].htrees_; - for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { - HuffmanTreeRelease(&htrees[j]); - } - } - free(htree_groups); - } -} - -static int ReadHuffmanCodes(VP8LDecoder* const dec, int xsize, int ysize, - int color_cache_bits, int allow_recursion) { - int i, j; - VP8LBitReader* const br = &dec->br_; - VP8LMetadata* const hdr = &dec->hdr_; - uint32_t* huffman_image = NULL; - HTreeGroup* htree_groups = NULL; - int num_htree_groups = 1; - - if (allow_recursion && VP8LReadBits(br, 1)) { - // use meta Huffman codes. - const int huffman_precision = VP8LReadBits(br, 3) + 2; - const int huffman_xsize = VP8LSubSampleSize(xsize, huffman_precision); - const int huffman_ysize = VP8LSubSampleSize(ysize, huffman_precision); - const int huffman_pixs = huffman_xsize * huffman_ysize; - if (!DecodeImageStream(huffman_xsize, huffman_ysize, 0, dec, - &huffman_image)) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - goto Error; - } - hdr->huffman_subsample_bits_ = huffman_precision; - for (i = 0; i < huffman_pixs; ++i) { - // The huffman data is stored in red and green bytes. - const int index = (huffman_image[i] >> 8) & 0xffff; - huffman_image[i] = index; - if (index >= num_htree_groups) { - num_htree_groups = index + 1; - } - } - } - - if (br->error_) goto Error; - - assert(num_htree_groups <= 0x10000); - htree_groups = - (HTreeGroup*)WebPSafeCalloc((uint64_t)num_htree_groups, - sizeof(*htree_groups)); - if (htree_groups == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - goto Error; - } - - for (i = 0; i < num_htree_groups; ++i) { - HuffmanTree* const htrees = htree_groups[i].htrees_; - for (j = 0; j < HUFFMAN_CODES_PER_META_CODE; ++j) { - int alphabet_size = kAlphabetSize[j]; - if (j == 0 && color_cache_bits > 0) { - alphabet_size += 1 << color_cache_bits; - } - if (!ReadHuffmanCode(alphabet_size, dec, htrees + j)) goto Error; - } - } - - // All OK. Finalize pointers and return. - hdr->huffman_image_ = huffman_image; - hdr->num_htree_groups_ = num_htree_groups; - hdr->htree_groups_ = htree_groups; - return 1; - - Error: - free(huffman_image); - DeleteHtreeGroups(htree_groups, num_htree_groups); - return 0; -} - -//------------------------------------------------------------------------------ -// Scaling. - -static int AllocateAndInitRescaler(VP8LDecoder* const dec, VP8Io* const io) { - const int num_channels = 4; - const int in_width = io->mb_w; - const int out_width = io->scaled_width; - const int in_height = io->mb_h; - const int out_height = io->scaled_height; - const uint64_t work_size = 2 * num_channels * (uint64_t)out_width; - int32_t* work; // Rescaler work area. - const uint64_t scaled_data_size = num_channels * (uint64_t)out_width; - uint32_t* scaled_data; // Temporary storage for scaled BGRA data. - const uint64_t memory_size = sizeof(*dec->rescaler) + - work_size * sizeof(*work) + - scaled_data_size * sizeof(*scaled_data); - uint8_t* memory = (uint8_t*)WebPSafeCalloc(memory_size, sizeof(*memory)); - if (memory == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - return 0; - } - assert(dec->rescaler_memory == NULL); - dec->rescaler_memory = memory; - - dec->rescaler = (WebPRescaler*)memory; - memory += sizeof(*dec->rescaler); - work = (int32_t*)memory; - memory += work_size * sizeof(*work); - scaled_data = (uint32_t*)memory; - - WebPRescalerInit(dec->rescaler, in_width, in_height, (uint8_t*)scaled_data, - out_width, out_height, 0, num_channels, - in_width, out_width, in_height, out_height, work); - return 1; -} - -//------------------------------------------------------------------------------ -// Export to ARGB - -// We have special "export" function since we need to convert from BGRA -static int Export(WebPRescaler* const rescaler, WEBP_CSP_MODE colorspace, - int rgba_stride, uint8_t* const rgba) { - const uint32_t* const src = (const uint32_t*)rescaler->dst; - const int dst_width = rescaler->dst_width; - int num_lines_out = 0; - while (WebPRescalerHasPendingOutput(rescaler)) { - uint8_t* const dst = rgba + num_lines_out * rgba_stride; - WebPRescalerExportRow(rescaler); - VP8LConvertFromBGRA(src, dst_width, colorspace, dst); - ++num_lines_out; - } - return num_lines_out; -} - -// Emit scaled rows. -static int EmitRescaledRows(const VP8LDecoder* const dec, - const uint32_t* const data, int in_stride, int mb_h, - uint8_t* const out, int out_stride) { - const WEBP_CSP_MODE colorspace = dec->output_->colorspace; - const uint8_t* const in = (const uint8_t*)data; - int num_lines_in = 0; - int num_lines_out = 0; - while (num_lines_in < mb_h) { - const uint8_t* const row_in = in + num_lines_in * in_stride; - uint8_t* const row_out = out + num_lines_out * out_stride; - num_lines_in += WebPRescalerImport(dec->rescaler, mb_h - num_lines_in, - row_in, in_stride); - num_lines_out += Export(dec->rescaler, colorspace, out_stride, row_out); - } - return num_lines_out; -} - -// Emit rows without any scaling. -static int EmitRows(WEBP_CSP_MODE colorspace, - const uint32_t* const data, int in_stride, - int mb_w, int mb_h, - uint8_t* const out, int out_stride) { - int lines = mb_h; - const uint8_t* row_in = (const uint8_t*)data; - uint8_t* row_out = out; - while (lines-- > 0) { - VP8LConvertFromBGRA((const uint32_t*)row_in, mb_w, colorspace, row_out); - row_in += in_stride; - row_out += out_stride; - } - return mb_h; // Num rows out == num rows in. -} - -//------------------------------------------------------------------------------ -// Export to YUVA - -static void ConvertToYUVA(const uint32_t* const src, int width, int y_pos, - const WebPDecBuffer* const output) { - const WebPYUVABuffer* const buf = &output->u.YUVA; - // first, the luma plane - { - int i; - uint8_t* const y = buf->y + y_pos * buf->y_stride; - for (i = 0; i < width; ++i) { - const uint32_t p = src[i]; - y[i] = VP8RGBToY((p >> 16) & 0xff, (p >> 8) & 0xff, (p >> 0) & 0xff); - } - } - - // then U/V planes - { - uint8_t* const u = buf->u + (y_pos >> 1) * buf->u_stride; - uint8_t* const v = buf->v + (y_pos >> 1) * buf->v_stride; - const int uv_width = width >> 1; - int i; - for (i = 0; i < uv_width; ++i) { - const uint32_t v0 = src[2 * i + 0]; - const uint32_t v1 = src[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); - if (!(y_pos & 1)) { // even lines: store values - u[i] = VP8RGBToU(r, g, b); - v[i] = VP8RGBToV(r, g, b); - } else { // odd lines: average with previous values - const int tmp_u = VP8RGBToU(r, g, b); - const int tmp_v = VP8RGBToV(r, g, b); - // 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 (width & 1) { // last pixel - const uint32_t v0 = src[2 * i + 0]; - const int r = (v0 >> 14) & 0x3fc; - const int g = (v0 >> 6) & 0x3fc; - const int b = (v0 << 2) & 0x3fc; - if (!(y_pos & 1)) { // even lines - u[i] = VP8RGBToU(r, g, b); - v[i] = VP8RGBToV(r, g, b); - } else { // odd lines (note: we could just skip this) - const int tmp_u = VP8RGBToU(r, g, b); - const int tmp_v = VP8RGBToV(r, g, b); - u[i] = (u[i] + tmp_u + 1) >> 1; - v[i] = (v[i] + tmp_v + 1) >> 1; - } - } - } - // Lastly, store alpha if needed. - if (buf->a != NULL) { - int i; - uint8_t* const a = buf->a + y_pos * buf->a_stride; - for (i = 0; i < width; ++i) a[i] = (src[i] >> 24); - } -} - -static int ExportYUVA(const VP8LDecoder* const dec, int y_pos) { - WebPRescaler* const rescaler = dec->rescaler; - const uint32_t* const src = (const uint32_t*)rescaler->dst; - const int dst_width = rescaler->dst_width; - int num_lines_out = 0; - while (WebPRescalerHasPendingOutput(rescaler)) { - WebPRescalerExportRow(rescaler); - ConvertToYUVA(src, dst_width, y_pos, dec->output_); - ++y_pos; - ++num_lines_out; - } - return num_lines_out; -} - -static int EmitRescaledRowsYUVA(const VP8LDecoder* const dec, - const uint32_t* const data, - int in_stride, int mb_h) { - const uint8_t* const in = (const uint8_t*)data; - int num_lines_in = 0; - int y_pos = dec->last_out_row_; - while (num_lines_in < mb_h) { - const uint8_t* const row_in = in + num_lines_in * in_stride; - num_lines_in += WebPRescalerImport(dec->rescaler, mb_h - num_lines_in, - row_in, in_stride); - y_pos += ExportYUVA(dec, y_pos); - } - return y_pos; -} - -static int EmitRowsYUVA(const VP8LDecoder* const dec, - const uint32_t* const data, int in_stride, - int mb_w, int num_rows) { - int y_pos = dec->last_out_row_; - const uint8_t* row_in = (const uint8_t*)data; - while (num_rows-- > 0) { - ConvertToYUVA((const uint32_t*)row_in, mb_w, y_pos, dec->output_); - row_in += in_stride; - ++y_pos; - } - return y_pos; -} - -//------------------------------------------------------------------------------ -// Cropping. - -// Sets io->mb_y, io->mb_h & io->mb_w according to start row, end row and -// crop options. Also updates the input data pointer, so that it points to the -// start of the cropped window. -// Note that 'pixel_stride' is in units of 'uint32_t' (and not 'bytes). -// Returns true if the crop window is not empty. -static int SetCropWindow(VP8Io* const io, int y_start, int y_end, - const uint32_t** const in_data, int pixel_stride) { - assert(y_start < y_end); - assert(io->crop_left < io->crop_right); - if (y_end > io->crop_bottom) { - y_end = io->crop_bottom; // make sure we don't overflow on last row. - } - if (y_start < io->crop_top) { - const int delta = io->crop_top - y_start; - y_start = io->crop_top; - *in_data += pixel_stride * delta; - } - if (y_start >= y_end) return 0; // Crop window is empty. - - *in_data += 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; - return 1; // Non-empty crop window. -} - -//------------------------------------------------------------------------------ - -static WEBP_INLINE int GetMetaIndex( - const uint32_t* const image, int xsize, int bits, int x, int y) { - if (bits == 0) return 0; - return image[xsize * (y >> bits) + (x >> bits)]; -} - -static WEBP_INLINE HTreeGroup* GetHtreeGroupForPos(VP8LMetadata* const hdr, - int x, int y) { - const int meta_index = GetMetaIndex(hdr->huffman_image_, hdr->huffman_xsize_, - hdr->huffman_subsample_bits_, x, y); - assert(meta_index < hdr->num_htree_groups_); - return hdr->htree_groups_ + meta_index; -} - -//------------------------------------------------------------------------------ -// Main loop, with custom row-processing function - -typedef void (*ProcessRowsFunc)(VP8LDecoder* const dec, int row); - -static void ApplyInverseTransforms(VP8LDecoder* const dec, int num_rows, - const uint32_t* const rows) { - int n = dec->next_transform_; - const int cache_pixs = dec->width_ * num_rows; - const int start_row = dec->last_row_; - const int end_row = start_row + num_rows; - const uint32_t* rows_in = rows; - uint32_t* const rows_out = dec->argb_cache_; - - // Inverse transforms. - // TODO: most transforms only need to operate on the cropped region only. - memcpy(rows_out, rows_in, cache_pixs * sizeof(*rows_out)); - while (n-- > 0) { - VP8LTransform* const transform = &dec->transforms_[n]; - VP8LInverseTransform(transform, start_row, end_row, rows_in, rows_out); - rows_in = rows_out; - } -} - -// Processes (transforms, scales & color-converts) the rows decoded after the -// last call. -static void ProcessRows(VP8LDecoder* const dec, int row) { - const uint32_t* const rows = dec->argb_ + dec->width_ * dec->last_row_; - const int num_rows = row - dec->last_row_; - - if (num_rows <= 0) return; // Nothing to be done. - ApplyInverseTransforms(dec, num_rows, rows); - - // Emit output. - { - VP8Io* const io = dec->io_; - const uint32_t* rows_data = dec->argb_cache_; - if (!SetCropWindow(io, dec->last_row_, row, &rows_data, io->width)) { - // Nothing to output (this time). - } else { - const WebPDecBuffer* const output = dec->output_; - const int in_stride = io->width * sizeof(*rows_data); - if (output->colorspace < MODE_YUV) { // convert to RGBA - const WebPRGBABuffer* const buf = &output->u.RGBA; - uint8_t* const rgba = buf->rgba + dec->last_out_row_ * buf->stride; - const int num_rows_out = io->use_scaling ? - EmitRescaledRows(dec, rows_data, in_stride, io->mb_h, - rgba, buf->stride) : - EmitRows(output->colorspace, rows_data, in_stride, - io->mb_w, io->mb_h, rgba, buf->stride); - // Update 'last_out_row_'. - dec->last_out_row_ += num_rows_out; - } else { // convert to YUVA - dec->last_out_row_ = io->use_scaling ? - EmitRescaledRowsYUVA(dec, rows_data, in_stride, io->mb_h) : - EmitRowsYUVA(dec, rows_data, in_stride, io->mb_w, io->mb_h); - } - assert(dec->last_out_row_ <= output->height); - } - } - - // Update 'last_row_'. - dec->last_row_ = row; - assert(dec->last_row_ <= dec->height_); -} - -static int DecodeImageData(VP8LDecoder* const dec, - uint32_t* const data, int width, int height, - ProcessRowsFunc process_func) { - int ok = 1; - int col = 0, row = 0; - VP8LBitReader* const br = &dec->br_; - VP8LMetadata* const hdr = &dec->hdr_; - HTreeGroup* htree_group = hdr->htree_groups_; - uint32_t* src = data; - uint32_t* last_cached = data; - uint32_t* const src_end = data + width * height; - const int len_code_limit = NUM_LITERAL_CODES + NUM_LENGTH_CODES; - const int color_cache_limit = len_code_limit + hdr->color_cache_size_; - VP8LColorCache* const color_cache = - (hdr->color_cache_size_ > 0) ? &hdr->color_cache_ : NULL; - const int mask = hdr->huffman_mask_; - - assert(htree_group != NULL); - - while (!br->eos_ && src < src_end) { - int code; - // Only update when changing tile. Note we could use the following test: - // if "((((prev_col ^ col) | prev_row ^ row)) > mask)" -> tile changed - // but that's actually slower and requires storing the previous col/row - if ((col & mask) == 0) { - htree_group = GetHtreeGroupForPos(hdr, col, row); - } - VP8LFillBitWindow(br); - code = ReadSymbol(&htree_group->htrees_[GREEN], br); - if (code < NUM_LITERAL_CODES) { // Literal. - int red, green, blue, alpha; - red = ReadSymbol(&htree_group->htrees_[RED], br); - green = code; - VP8LFillBitWindow(br); - blue = ReadSymbol(&htree_group->htrees_[BLUE], br); - alpha = ReadSymbol(&htree_group->htrees_[ALPHA], br); - *src = (alpha << 24) + (red << 16) + (green << 8) + blue; - AdvanceByOne: - ++src; - ++col; - if (col >= width) { - col = 0; - ++row; - if ((process_func != NULL) && (row % NUM_ARGB_CACHE_ROWS == 0)) { - process_func(dec, row); - } - if (color_cache != NULL) { - while (last_cached < src) { - VP8LColorCacheInsert(color_cache, *last_cached++); - } - } - } - } else if (code < len_code_limit) { // Backward reference - int dist_code, dist; - const int length_sym = code - NUM_LITERAL_CODES; - const int length = GetCopyLength(length_sym, br); - const int dist_symbol = ReadSymbol(&htree_group->htrees_[DIST], br); - VP8LFillBitWindow(br); - dist_code = GetCopyDistance(dist_symbol, br); - dist = PlaneCodeToDistance(width, dist_code); - if (src - data < dist || src_end - src < length) { - ok = 0; - goto End; - } - { - int i; - for (i = 0; i < length; ++i) src[i] = src[i - dist]; - src += length; - } - col += length; - while (col >= width) { - col -= width; - ++row; - if ((process_func != NULL) && (row % NUM_ARGB_CACHE_ROWS == 0)) { - process_func(dec, row); - } - } - if (src < src_end) { - htree_group = GetHtreeGroupForPos(hdr, col, row); - if (color_cache != NULL) { - while (last_cached < src) { - VP8LColorCacheInsert(color_cache, *last_cached++); - } - } - } - } else if (code < color_cache_limit) { // Color cache. - const int key = code - len_code_limit; - assert(color_cache != NULL); - while (last_cached < src) { - VP8LColorCacheInsert(color_cache, *last_cached++); - } - *src = VP8LColorCacheLookup(color_cache, key); - goto AdvanceByOne; - } else { // Not reached. - ok = 0; - goto End; - } - ok = !br->error_; - if (!ok) goto End; - } - // Process the remaining rows corresponding to last row-block. - if (process_func != NULL) process_func(dec, row); - - End: - if (br->error_ || !ok || (br->eos_ && src < src_end)) { - ok = 0; - dec->status_ = (!br->eos_) ? - VP8_STATUS_BITSTREAM_ERROR : VP8_STATUS_SUSPENDED; - } else if (src == src_end) { - dec->state_ = READ_DATA; - } - - return ok; -} - -// ----------------------------------------------------------------------------- -// VP8LTransform - -static void ClearTransform(VP8LTransform* const transform) { - free(transform->data_); - transform->data_ = NULL; -} - -// For security reason, we need to remap the color map to span -// the total possible bundled values, and not just the num_colors. -static int ExpandColorMap(int num_colors, VP8LTransform* const transform) { - int i; - const int final_num_colors = 1 << (8 >> transform->bits_); - uint32_t* const new_color_map = - (uint32_t*)WebPSafeMalloc((uint64_t)final_num_colors, - sizeof(*new_color_map)); - if (new_color_map == NULL) { - return 0; - } else { - uint8_t* const data = (uint8_t*)transform->data_; - uint8_t* const new_data = (uint8_t*)new_color_map; - new_color_map[0] = transform->data_[0]; - for (i = 4; i < 4 * num_colors; ++i) { - // Equivalent to AddPixelEq(), on a byte-basis. - new_data[i] = (data[i] + new_data[i - 4]) & 0xff; - } - for (; i < 4 * final_num_colors; ++i) - new_data[i] = 0; // black tail. - free(transform->data_); - transform->data_ = new_color_map; - } - return 1; -} - -static int ReadTransform(int* const xsize, int const* ysize, - VP8LDecoder* const dec) { - int ok = 1; - VP8LBitReader* const br = &dec->br_; - VP8LTransform* transform = &dec->transforms_[dec->next_transform_]; - const VP8LImageTransformType type = - (VP8LImageTransformType)VP8LReadBits(br, 2); - - // Each transform type can only be present once in the stream. - if (dec->transforms_seen_ & (1U << type)) { - return 0; // Already there, let's not accept the second same transform. - } - dec->transforms_seen_ |= (1U << type); - - transform->type_ = type; - transform->xsize_ = *xsize; - transform->ysize_ = *ysize; - transform->data_ = NULL; - ++dec->next_transform_; - assert(dec->next_transform_ <= NUM_TRANSFORMS); - - switch (type) { - case PREDICTOR_TRANSFORM: - case CROSS_COLOR_TRANSFORM: - transform->bits_ = VP8LReadBits(br, 3) + 2; - ok = DecodeImageStream(VP8LSubSampleSize(transform->xsize_, - transform->bits_), - VP8LSubSampleSize(transform->ysize_, - transform->bits_), - 0, dec, &transform->data_); - break; - case COLOR_INDEXING_TRANSFORM: { - const int num_colors = VP8LReadBits(br, 8) + 1; - const int bits = (num_colors > 16) ? 0 - : (num_colors > 4) ? 1 - : (num_colors > 2) ? 2 - : 3; - *xsize = VP8LSubSampleSize(transform->xsize_, bits); - transform->bits_ = bits; - ok = DecodeImageStream(num_colors, 1, 0, dec, &transform->data_); - ok = ok && ExpandColorMap(num_colors, transform); - break; - } - case SUBTRACT_GREEN: - break; - default: - assert(0); // can't happen - break; - } - - return ok; -} - -// ----------------------------------------------------------------------------- -// VP8LMetadata - -static void InitMetadata(VP8LMetadata* const hdr) { - assert(hdr); - memset(hdr, 0, sizeof(*hdr)); -} - -static void ClearMetadata(VP8LMetadata* const hdr) { - assert(hdr); - - free(hdr->huffman_image_); - DeleteHtreeGroups(hdr->htree_groups_, hdr->num_htree_groups_); - VP8LColorCacheClear(&hdr->color_cache_); - InitMetadata(hdr); -} - -// ----------------------------------------------------------------------------- -// VP8LDecoder - -VP8LDecoder* VP8LNew(void) { - VP8LDecoder* const dec = (VP8LDecoder*)calloc(1, sizeof(*dec)); - if (dec == NULL) return NULL; - dec->status_ = VP8_STATUS_OK; - dec->action_ = READ_DIM; - dec->state_ = READ_DIM; - return dec; -} - -void VP8LClear(VP8LDecoder* const dec) { - int i; - if (dec == NULL) return; - ClearMetadata(&dec->hdr_); - - free(dec->argb_); - dec->argb_ = NULL; - for (i = 0; i < dec->next_transform_; ++i) { - ClearTransform(&dec->transforms_[i]); - } - dec->next_transform_ = 0; - dec->transforms_seen_ = 0; - - free(dec->rescaler_memory); - dec->rescaler_memory = NULL; - - dec->output_ = NULL; // leave no trace behind -} - -void VP8LDelete(VP8LDecoder* const dec) { - if (dec != NULL) { - VP8LClear(dec); - free(dec); - } -} - -static void UpdateDecoder(VP8LDecoder* const dec, int width, int height) { - VP8LMetadata* const hdr = &dec->hdr_; - const int num_bits = hdr->huffman_subsample_bits_; - dec->width_ = width; - dec->height_ = height; - - hdr->huffman_xsize_ = VP8LSubSampleSize(width, num_bits); - hdr->huffman_mask_ = (num_bits == 0) ? ~0 : (1 << num_bits) - 1; -} - -static int DecodeImageStream(int xsize, int ysize, - int is_level0, - VP8LDecoder* const dec, - uint32_t** const decoded_data) { - int ok = 1; - int transform_xsize = xsize; - int transform_ysize = ysize; - VP8LBitReader* const br = &dec->br_; - VP8LMetadata* const hdr = &dec->hdr_; - uint32_t* data = NULL; - int color_cache_bits = 0; - - // Read the transforms (may recurse). - if (is_level0) { - while (ok && VP8LReadBits(br, 1)) { - ok = ReadTransform(&transform_xsize, &transform_ysize, dec); - } - } - - // Color cache - if (ok && VP8LReadBits(br, 1)) { - color_cache_bits = VP8LReadBits(br, 4); - ok = (color_cache_bits >= 1 && color_cache_bits <= MAX_CACHE_BITS); - if (!ok) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - goto End; - } - } - - // Read the Huffman codes (may recurse). - ok = ok && ReadHuffmanCodes(dec, transform_xsize, transform_ysize, - color_cache_bits, is_level0); - if (!ok) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - goto End; - } - - // Finish setting up the color-cache - if (color_cache_bits > 0) { - hdr->color_cache_size_ = 1 << color_cache_bits; - if (!VP8LColorCacheInit(&hdr->color_cache_, color_cache_bits)) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - ok = 0; - goto End; - } - } else { - hdr->color_cache_size_ = 0; - } - UpdateDecoder(dec, transform_xsize, transform_ysize); - - if (is_level0) { // level 0 complete - dec->state_ = READ_HDR; - goto End; - } - - { - const uint64_t total_size = (uint64_t)transform_xsize * transform_ysize; - data = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*data)); - if (data == NULL) { - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - ok = 0; - goto End; - } - } - - // Use the Huffman trees to decode the LZ77 encoded data. - ok = DecodeImageData(dec, data, transform_xsize, transform_ysize, NULL); - ok = ok && !br->error_; - - End: - - if (!ok) { - free(data); - ClearMetadata(hdr); - // If not enough data (br.eos_) resulted in BIT_STREAM_ERROR, update the - // status appropriately. - if (dec->status_ == VP8_STATUS_BITSTREAM_ERROR && dec->br_.eos_) { - dec->status_ = VP8_STATUS_SUSPENDED; - } - } else { - if (decoded_data != NULL) { - *decoded_data = data; - } else { - // We allocate image data in this function only for transforms. At level 0 - // (that is: not the transforms), we shouldn't have allocated anything. - assert(data == NULL); - assert(is_level0); - } - if (!is_level0) ClearMetadata(hdr); // Clean up temporary data behind. - } - return ok; -} - -//------------------------------------------------------------------------------ -// Allocate dec->argb_ and dec->argb_cache_ using dec->width_ and dec->height_ - -static int AllocateARGBBuffers(VP8LDecoder* const dec, int final_width) { - const uint64_t num_pixels = (uint64_t)dec->width_ * dec->height_; - // Scratch buffer corresponding to top-prediction row for transforming the - // first row in the row-blocks. - const uint64_t cache_top_pixels = final_width; - // Scratch buffer for temporary BGRA storage. - const uint64_t cache_pixels = (uint64_t)final_width * NUM_ARGB_CACHE_ROWS; - const uint64_t total_num_pixels = - num_pixels + cache_top_pixels + cache_pixels; - - assert(dec->width_ <= final_width); - dec->argb_ = (uint32_t*)WebPSafeMalloc(total_num_pixels, sizeof(*dec->argb_)); - if (dec->argb_ == NULL) { - dec->argb_cache_ = NULL; // for sanity check - dec->status_ = VP8_STATUS_OUT_OF_MEMORY; - return 0; - } - dec->argb_cache_ = dec->argb_ + num_pixels + cache_top_pixels; - return 1; -} - -//------------------------------------------------------------------------------ -// Special row-processing that only stores the alpha data. - -static void ExtractAlphaRows(VP8LDecoder* const dec, int row) { - const int num_rows = row - dec->last_row_; - const uint32_t* const in = dec->argb_ + dec->width_ * dec->last_row_; - - if (num_rows <= 0) return; // Nothing to be done. - ApplyInverseTransforms(dec, num_rows, in); - - // Extract alpha (which is stored in the green plane). - { - const int width = dec->io_->width; // the final width (!= dec->width_) - const int cache_pixs = width * num_rows; - uint8_t* const dst = (uint8_t*)dec->io_->opaque + width * dec->last_row_; - const uint32_t* const src = dec->argb_cache_; - int i; - for (i = 0; i < cache_pixs; ++i) dst[i] = (src[i] >> 8) & 0xff; - } - - dec->last_row_ = dec->last_out_row_ = row; -} - -int VP8LDecodeAlphaImageStream(int width, int height, const uint8_t* const data, - size_t data_size, uint8_t* const output) { - VP8Io io; - int ok = 0; - VP8LDecoder* const dec = VP8LNew(); - if (dec == NULL) return 0; - - dec->width_ = width; - dec->height_ = height; - dec->io_ = &io; - - VP8InitIo(&io); - WebPInitCustomIo(NULL, &io); // Just a sanity Init. io won't be used. - io.opaque = output; - io.width = width; - io.height = height; - - dec->status_ = VP8_STATUS_OK; - VP8LInitBitReader(&dec->br_, data, data_size); - - dec->action_ = READ_HDR; - if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Err; - - // Allocate output (note that dec->width_ may have changed here). - if (!AllocateARGBBuffers(dec, width)) goto Err; - - // Decode (with special row processing). - dec->action_ = READ_DATA; - ok = DecodeImageData(dec, dec->argb_, dec->width_, dec->height_, - ExtractAlphaRows); - - Err: - VP8LDelete(dec); - return ok; -} - -//------------------------------------------------------------------------------ - -int VP8LDecodeHeader(VP8LDecoder* const dec, VP8Io* const io) { - int width, height, has_alpha; - - if (dec == NULL) return 0; - if (io == NULL) { - dec->status_ = VP8_STATUS_INVALID_PARAM; - return 0; - } - - dec->io_ = io; - dec->status_ = VP8_STATUS_OK; - VP8LInitBitReader(&dec->br_, io->data, io->data_size); - if (!ReadImageInfo(&dec->br_, &width, &height, &has_alpha)) { - dec->status_ = VP8_STATUS_BITSTREAM_ERROR; - goto Error; - } - dec->state_ = READ_DIM; - io->width = width; - io->height = height; - - dec->action_ = READ_HDR; - if (!DecodeImageStream(width, height, 1, dec, NULL)) goto Error; - return 1; - - Error: - VP8LClear(dec); - assert(dec->status_ != VP8_STATUS_OK); - return 0; -} - -int VP8LDecodeImage(VP8LDecoder* const dec) { - VP8Io* io = NULL; - WebPDecParams* params = NULL; - - // Sanity checks. - if (dec == NULL) return 0; - - io = dec->io_; - assert(io != NULL); - params = (WebPDecParams*)io->opaque; - assert(params != NULL); - dec->output_ = params->output; - assert(dec->output_ != NULL); - - // Initialization. - if (!WebPIoInitFromOptions(params->options, io, MODE_BGRA)) { - dec->status_ = VP8_STATUS_INVALID_PARAM; - goto Err; - } - - if (!AllocateARGBBuffers(dec, io->width)) goto Err; - - if (io->use_scaling && !AllocateAndInitRescaler(dec, io)) goto Err; - - // Decode. - dec->action_ = READ_DATA; - if (!DecodeImageData(dec, dec->argb_, dec->width_, dec->height_, - ProcessRows)) { - goto Err; - } - - // Cleanup. - params->last_y = dec->last_out_row_; - VP8LClear(dec); - return 1; - - Err: - VP8LClear(dec); - assert(dec->status_ != VP8_STATUS_OK); - return 0; -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/dec/vp8li.h b/drivers/webpold/dec/vp8li.h deleted file mode 100644 index 5f6cd6a01c..0000000000 --- a/drivers/webpold/dec/vp8li.h +++ /dev/null @@ -1,121 +0,0 @@ -// 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_ */ diff --git a/drivers/webpold/dec/webp.c b/drivers/webpold/dec/webp.c deleted file mode 100644 index f44bc2b8ae..0000000000 --- a/drivers/webpold/dec/webp.c +++ /dev/null @@ -1,771 +0,0 @@ -// 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 diff --git a/drivers/webpold/dec/webpi.h b/drivers/webpold/dec/webpi.h deleted file mode 100644 index 44e5744411..0000000000 --- a/drivers/webpold/dec/webpi.h +++ /dev/null @@ -1,114 +0,0 @@ -// 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_ */ diff --git a/drivers/webpold/decode.h b/drivers/webpold/decode.h deleted file mode 100644 index 43b6c58f4f..0000000000 --- a/drivers/webpold/decode.h +++ /dev/null @@ -1,454 +0,0 @@ -// 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_ */ diff --git a/drivers/webpold/dsp/cpu.c b/drivers/webpold/dsp/cpu.c deleted file mode 100644 index 0228734457..0000000000 --- a/drivers/webpold/dsp/cpu.c +++ /dev/null @@ -1,85 +0,0 @@ -// 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 diff --git a/drivers/webpold/dsp/dec.c b/drivers/webpold/dsp/dec.c deleted file mode 100644 index 9ae7b6fa76..0000000000 --- a/drivers/webpold/dsp/dec.c +++ /dev/null @@ -1,732 +0,0 @@ -// 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 diff --git a/drivers/webpold/dsp/dec_neon.c b/drivers/webpold/dsp/dec_neon.c deleted file mode 100644 index ec824b790b..0000000000 --- a/drivers/webpold/dsp/dec_neon.c +++ /dev/null @@ -1,329 +0,0 @@ -// 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 diff --git a/drivers/webpold/dsp/dec_sse2.c b/drivers/webpold/dsp/dec_sse2.c deleted file mode 100644 index 472b68ecb8..0000000000 --- a/drivers/webpold/dsp/dec_sse2.c +++ /dev/null @@ -1,903 +0,0 @@ -// 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 diff --git a/drivers/webpold/dsp/dsp.h b/drivers/webpold/dsp/dsp.h deleted file mode 100644 index fd686a8532..0000000000 --- a/drivers/webpold/dsp/dsp.h +++ /dev/null @@ -1,210 +0,0 @@ -// 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_ */ diff --git a/drivers/webpold/dsp/enc.c b/drivers/webpold/dsp/enc.c deleted file mode 100644 index 02234564be..0000000000 --- a/drivers/webpold/dsp/enc.c +++ /dev/null @@ -1,743 +0,0 @@ -// 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 encoding functions. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <stdlib.h> // for abs() -#include "./dsp.h" -#include "../enc/vp8enci.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// 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, - 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, - - 0 + 0 * BPS, 4 + 0 * BPS, 0 + 4 * BPS, 4 + 4 * BPS, // U - 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]]++; - } - } - - return VP8GetAlpha(histo); -} - -//------------------------------------------------------------------------------ -// run-time tables (~4k) - -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 InitTables(void) { - if (!tables_ok) { - int 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(ref[(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 WEBP_INLINE void ITransformOne(const uint8_t* ref, 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]; - const int b = in[0] - in[8]; - const int c = MUL(in[4], kC2) - MUL(in[12], kC1); - const int d = MUL(in[4], kC1) + MUL(in[12], kC2); - tmp[0] = a + d; - tmp[1] = b + c; - tmp[2] = b - c; - tmp[3] = a - d; - tmp += 4; - in++; - } - - 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, i, a + d); - STORE(1, i, b + c); - STORE(2, i, b - c); - STORE(3, i, a - d); - tmp++; - } -} - -static void ITransform(const uint8_t* ref, const int16_t* in, uint8_t* dst, - int do_two) { - ITransformOne(ref, in, dst); - if (do_two) { - ITransformOne(ref + 4, in + 16, dst + 4); - } -} - -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 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; - } - for (i = 0; i < 4; ++i) { - const int a0 = (tmp[0 + i] + tmp[12 + i]); - 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[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 FTransformWHT(const int16_t* in, int16_t* out) { - int 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); - 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 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; - 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; - } -} - -#undef MUL -#undef STORE - -//------------------------------------------------------------------------------ -// 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) { - memset(dst + j * BPS, value, size); - } -} - -static WEBP_INLINE void VerticalPred(uint8_t* dst, - const uint8_t* top, int size) { - int j; - if (top) { - for (j = 0; j < size; ++j) memcpy(dst + j * BPS, top, size); - } else { - Fill(dst, 127, size); - } -} - -static WEBP_INLINE void HorizontalPred(uint8_t* dst, - const uint8_t* left, int size) { - if (left) { - int j; - for (j = 0; j < size; ++j) { - memset(dst + j * BPS, left[j], size); - } - } else { - Fill(dst, 129, size); - } -} - -static WEBP_INLINE void TrueMotion(uint8_t* dst, const uint8_t* left, - const uint8_t* top, int size) { - int y; - if (left) { - if (top) { - const uint8_t* const clip = clip1 + 255 - left[-1]; - for (y = 0; y < size; ++y) { - const uint8_t* const clip_table = clip + left[y]; - int x; - for (x = 0; x < size; ++x) { - dst[x] = clip_table[top[x]]; - } - dst += BPS; - } - } else { - HorizontalPred(dst, left, size); - } - } else { - // true motion without left samples (hence: with default 129 value) - // 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) { - VerticalPred(dst, top, size); - } else { - Fill(dst, 129, size); - } - } -} - -static WEBP_INLINE void DCMode(uint8_t* dst, const uint8_t* left, - const uint8_t* top, - int size, int round, int shift) { - int DC = 0; - int j; - if (top) { - for (j = 0; j < size; ++j) DC += top[j]; - if (left) { // 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 - for (j = 0; j < size; ++j) DC += left[j]; - DC += DC; - DC = (DC + round) >> shift; - } else { // no top, no left, nothing. - DC = 0x80; - } - Fill(dst, DC, size); -} - -//------------------------------------------------------------------------------ -// Chroma 8x8 prediction (paragraph 12.2) - -static void IntraChromaPreds(uint8_t* dst, const uint8_t* left, - const uint8_t* top) { - // U block - DCMode(C8DC8 + dst, left, top, 8, 8, 4); - VerticalPred(C8VE8 + dst, top, 8); - HorizontalPred(C8HE8 + dst, left, 8); - TrueMotion(C8TM8 + dst, left, top, 8); - // V block - dst += 8; - if (top) top += 8; - if (left) left += 16; - DCMode(C8DC8 + dst, left, top, 8, 8, 4); - VerticalPred(C8VE8 + dst, top, 8); - HorizontalPred(C8HE8 + dst, left, 8); - TrueMotion(C8TM8 + dst, left, top, 8); -} - -//------------------------------------------------------------------------------ -// luma 16x16 prediction (paragraph 12.3) - -static void Intra16Preds(uint8_t* dst, - const uint8_t* left, const uint8_t* top) { - DCMode(I16DC16 + dst, left, top, 16, 16, 5); - VerticalPred(I16VE16 + dst, top, 16); - HorizontalPred(I16HE16 + dst, left, 16); - TrueMotion(I16TM16 + dst, left, top, 16); -} - -//------------------------------------------------------------------------------ -// luma 4x4 prediction - -#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, const uint8_t* top) { // vertical - 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, 4); - } -} - -static void HE4(uint8_t* dst, const uint8_t* top) { // horizontal - const int X = top[-1]; - const int I = top[-2]; - const int J = top[-3]; - const int K = top[-4]; - const int L = top[-5]; - *(uint32_t*)(dst + 0 * BPS) = 0x01010101U * AVG3(X, I, J); - *(uint32_t*)(dst + 1 * BPS) = 0x01010101U * AVG3(I, J, K); - *(uint32_t*)(dst + 2 * BPS) = 0x01010101U * AVG3(J, K, L); - *(uint32_t*)(dst + 3 * BPS) = 0x01010101U * AVG3(K, L, L); -} - -static void DC4(uint8_t* dst, const uint8_t* top) { - uint32_t dc = 4; - int i; - for (i = 0; i < 4; ++i) dc += top[i] + top[-5 + i]; - Fill(dst, dc >> 3, 4); -} - -static void RD4(uint8_t* dst, const uint8_t* top) { - const int X = top[-1]; - const int I = top[-2]; - const int J = top[-3]; - const int K = top[-4]; - const int L = top[-5]; - const int A = top[0]; - const int B = top[1]; - const int C = top[2]; - const int D = top[3]; - 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, const uint8_t* top) { - const int A = top[0]; - const int B = top[1]; - const int C = top[2]; - const int D = top[3]; - const int E = top[4]; - const int F = top[5]; - const int G = top[6]; - const int H = top[7]; - 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, const uint8_t* top) { - const int X = top[-1]; - const int I = top[-2]; - const int J = top[-3]; - const int K = top[-4]; - const int A = top[0]; - const int B = top[1]; - const int C = top[2]; - const int D = top[3]; - 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, const uint8_t* top) { - const int A = top[0]; - const int B = top[1]; - const int C = top[2]; - const int D = top[3]; - const int E = top[4]; - const int F = top[5]; - const int G = top[6]; - const int H = top[7]; - 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, const uint8_t* top) { - const int I = top[-2]; - const int J = top[-3]; - const int K = top[-4]; - const int L = top[-5]; - 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, const uint8_t* top) { - const int X = top[-1]; - const int I = top[-2]; - const int J = top[-3]; - const int K = top[-4]; - const int L = top[-5]; - const int A = top[0]; - const int B = top[1]; - const int C = top[2]; - - 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); -} - -static void TM4(uint8_t* dst, const uint8_t* top) { - int x, y; - const uint8_t* const clip = clip1 + 255 - top[-1]; - for (y = 0; y < 4; ++y) { - const uint8_t* const clip_table = clip + top[-2 - y]; - for (x = 0; x < 4; ++x) { - dst[x] = clip_table[top[x]]; - } - dst += BPS; - } -} - -#undef DST -#undef AVG3 -#undef AVG2 - -// Left samples are top[-5 .. -2], top_left is top[-1], top are -// located at top[0..3], and top right is top[4..7] -static void Intra4Preds(uint8_t* dst, const uint8_t* top) { - DC4(I4DC4 + dst, top); - TM4(I4TM4 + dst, top); - VE4(I4VE4 + dst, top); - HE4(I4HE4 + dst, top); - RD4(I4RD4 + dst, top); - VR4(I4VR4 + dst, top); - LD4(I4LD4 + dst, top); - VL4(I4VL4 + dst, top); - HD4(I4HD4 + dst, top); - HU4(I4HU4 + dst, top); -} - -//------------------------------------------------------------------------------ -// Metric - -static WEBP_INLINE int GetSSE(const uint8_t* a, const uint8_t* b, - int w, int h) { - int count = 0; - int y, x; - for (y = 0; y < h; ++y) { - for (x = 0; x < w; ++x) { - const int diff = (int)a[x] - b[x]; - count += diff * diff; - } - a += BPS; - b += BPS; - } - return count; -} - -static int SSE16x16(const uint8_t* a, const uint8_t* b) { - return GetSSE(a, b, 16, 16); -} -static int SSE16x8(const uint8_t* a, const uint8_t* b) { - return GetSSE(a, b, 16, 8); -} -static int SSE8x8(const uint8_t* a, const uint8_t* b) { - return GetSSE(a, b, 8, 8); -} -static int SSE4x4(const uint8_t* a, const uint8_t* b) { - return GetSSE(a, b, 4, 4); -} - -//------------------------------------------------------------------------------ -// Texture distortion -// -// We try to match the spectral content (weighted) between source and -// reconstructed samples. - -// Hadamard transform -// Returns the weighted sum of the absolute value of transformed coefficients. -static int TTransform(const uint8_t* in, const uint16_t* w) { - int sum = 0; - int tmp[16]; - 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); - 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 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); - } - return sum; -} - -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; -} - -static int Disto16x16(const uint8_t* const a, const uint8_t* const b, - const uint16_t* const w) { - int D = 0; - int x, y; - for (y = 0; y < 16 * BPS; y += 4 * BPS) { - for (x = 0; x < 16; x += 4) { - D += Disto4x4(a + x + y, b + x + y, w); - } - } - return D; -} - -//------------------------------------------------------------------------------ -// Quantization -// - -static const uint8_t kZigzag[16] = { - 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 -}; - -// Simple quantization -static int QuantizeBlock(int16_t in[16], int16_t out[16], - int n, const VP8Matrix* const mtx) { - int last = -1; - for (; 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; - 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; - } else { - out[n] = 0; - in[j] = 0; - } - } - return (last >= 0); -} - -//------------------------------------------------------------------------------ -// Block copy - -static WEBP_INLINE void Copy(const uint8_t* src, uint8_t* dst, int size) { - int y; - for (y = 0; y < size; ++y) { - memcpy(dst, src, size); - src += BPS; - dst += BPS; - } -} - -static void Copy4x4(const uint8_t* src, uint8_t* dst) { Copy(src, dst, 4); } - -//------------------------------------------------------------------------------ -// Initialization - -// Speed-critical function pointers. We have to initialize them to the default -// implementations within VP8EncDspInit(). -VP8CHisto VP8CollectHistogram; -VP8Idct VP8ITransform; -VP8Fdct VP8FTransform; -VP8WHT VP8ITransformWHT; -VP8WHT VP8FTransformWHT; -VP8Intra4Preds VP8EncPredLuma4; -VP8IntraPreds VP8EncPredLuma16; -VP8IntraPreds VP8EncPredChroma8; -VP8Metric VP8SSE16x16; -VP8Metric VP8SSE8x8; -VP8Metric VP8SSE16x8; -VP8Metric VP8SSE4x4; -VP8WMetric VP8TDisto4x4; -VP8WMetric VP8TDisto16x16; -VP8QuantizeBlock VP8EncQuantizeBlock; -VP8BlockCopy VP8Copy4x4; - -extern void VP8EncDspInitSSE2(void); - -void VP8EncDspInit(void) { - InitTables(); - - // default C implementations - VP8CollectHistogram = CollectHistogram; - VP8ITransform = ITransform; - VP8FTransform = FTransform; - VP8ITransformWHT = ITransformWHT; - VP8FTransformWHT = FTransformWHT; - VP8EncPredLuma4 = Intra4Preds; - VP8EncPredLuma16 = Intra16Preds; - VP8EncPredChroma8 = IntraChromaPreds; - VP8SSE16x16 = SSE16x16; - VP8SSE8x8 = SSE8x8; - VP8SSE16x8 = SSE16x8; - VP8SSE4x4 = SSE4x4; - VP8TDisto4x4 = Disto4x4; - VP8TDisto16x16 = Disto16x16; - VP8EncQuantizeBlock = QuantizeBlock; - VP8Copy4x4 = Copy4x4; - - // If defined, use CPUInfo() to overwrite some pointers with faster versions. - if (VP8GetCPUInfo) { -#if defined(WEBP_USE_SSE2) - if (VP8GetCPUInfo(kSSE2)) { - VP8EncDspInitSSE2(); - } -#endif - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/dsp/enc_sse2.c b/drivers/webpold/dsp/enc_sse2.c deleted file mode 100644 index b046761dc1..0000000000 --- a/drivers/webpold/dsp/enc_sse2.c +++ /dev/null @@ -1,837 +0,0 @@ -// 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 speed-critical encoding functions. -// -// Author: Christian Duvivier (cduvivier@google.com) - -#include "./dsp.h" - -#if defined(WEBP_USE_SSE2) -#include <stdlib.h> // for abs() -#include <emmintrin.h> - -#include "../enc/vp8enci.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Compute susceptibility based on DCT-coeff histograms: -// the higher, the "easier" the macroblock is to compress. - -static int CollectHistogramSSE2(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; - const __m128i max_coeff_thresh = _mm_set1_epi16(MAX_COEFF_THRESH); - for (j = start_block; j < end_block; ++j) { - VP8FTransform(ref + VP8DspScan[j], pred + VP8DspScan[j], out); - - // Convert coefficients to bin (within out[]). - { - // Load. - const __m128i out0 = _mm_loadu_si128((__m128i*)&out[0]); - const __m128i out1 = _mm_loadu_si128((__m128i*)&out[8]); - // sign(out) = out >> 15 (0x0000 if positive, 0xffff if negative) - const __m128i sign0 = _mm_srai_epi16(out0, 15); - const __m128i sign1 = _mm_srai_epi16(out1, 15); - // abs(out) = (out ^ sign) - sign - const __m128i xor0 = _mm_xor_si128(out0, sign0); - const __m128i xor1 = _mm_xor_si128(out1, sign1); - const __m128i abs0 = _mm_sub_epi16(xor0, sign0); - const __m128i abs1 = _mm_sub_epi16(xor1, sign1); - // v = abs(out) >> 2 - const __m128i v0 = _mm_srai_epi16(abs0, 2); - const __m128i v1 = _mm_srai_epi16(abs1, 2); - // bin = min(v, MAX_COEFF_THRESH) - const __m128i bin0 = _mm_min_epi16(v0, max_coeff_thresh); - const __m128i bin1 = _mm_min_epi16(v1, max_coeff_thresh); - // Store. - _mm_storeu_si128((__m128i*)&out[0], bin0); - _mm_storeu_si128((__m128i*)&out[8], bin1); - } - - // Use bin to update histogram. - for (k = 0; k < 16; ++k) { - histo[out[k]]++; - } - } - - return VP8GetAlpha(histo); -} - -//------------------------------------------------------------------------------ -// Transforms (Paragraph 14.4) - -// Does one or two inverse transforms. -static void ITransformSSE2(const uint8_t* ref, 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 inverse - // transforms in parallel). In the case of only one inverse 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 'ref' and store. - { - const __m128i zero = _mm_set1_epi16(0); - // Load the reference(s). - __m128i ref0, ref1, ref2, ref3; - if (do_two) { - // Load eight bytes/pixels per line. - ref0 = _mm_loadl_epi64((__m128i*)&ref[0 * BPS]); - ref1 = _mm_loadl_epi64((__m128i*)&ref[1 * BPS]); - ref2 = _mm_loadl_epi64((__m128i*)&ref[2 * BPS]); - ref3 = _mm_loadl_epi64((__m128i*)&ref[3 * BPS]); - } else { - // Load four bytes/pixels per line. - ref0 = _mm_cvtsi32_si128(*(int*)&ref[0 * BPS]); - ref1 = _mm_cvtsi32_si128(*(int*)&ref[1 * BPS]); - ref2 = _mm_cvtsi32_si128(*(int*)&ref[2 * BPS]); - ref3 = _mm_cvtsi32_si128(*(int*)&ref[3 * BPS]); - } - // Convert to 16b. - ref0 = _mm_unpacklo_epi8(ref0, zero); - ref1 = _mm_unpacklo_epi8(ref1, zero); - ref2 = _mm_unpacklo_epi8(ref2, zero); - ref3 = _mm_unpacklo_epi8(ref3, zero); - // Add the inverse transform(s). - ref0 = _mm_add_epi16(ref0, T0); - ref1 = _mm_add_epi16(ref1, T1); - ref2 = _mm_add_epi16(ref2, T2); - ref3 = _mm_add_epi16(ref3, T3); - // Unsigned saturate to 8b. - ref0 = _mm_packus_epi16(ref0, ref0); - ref1 = _mm_packus_epi16(ref1, ref1); - ref2 = _mm_packus_epi16(ref2, ref2); - ref3 = _mm_packus_epi16(ref3, ref3); - // Store the results. - if (do_two) { - // Store eight bytes/pixels per line. - _mm_storel_epi64((__m128i*)&dst[0 * BPS], ref0); - _mm_storel_epi64((__m128i*)&dst[1 * BPS], ref1); - _mm_storel_epi64((__m128i*)&dst[2 * BPS], ref2); - _mm_storel_epi64((__m128i*)&dst[3 * BPS], ref3); - } else { - // Store four bytes/pixels per line. - *((int32_t *)&dst[0 * BPS]) = _mm_cvtsi128_si32(ref0); - *((int32_t *)&dst[1 * BPS]) = _mm_cvtsi128_si32(ref1); - *((int32_t *)&dst[2 * BPS]) = _mm_cvtsi128_si32(ref2); - *((int32_t *)&dst[3 * BPS]) = _mm_cvtsi128_si32(ref3); - } - } -} - -static void FTransformSSE2(const uint8_t* src, const uint8_t* ref, - int16_t* out) { - const __m128i zero = _mm_setzero_si128(); - const __m128i seven = _mm_set1_epi16(7); - const __m128i k7500 = _mm_set1_epi32(7500); - const __m128i k14500 = _mm_set1_epi32(14500); - const __m128i k51000 = _mm_set1_epi32(51000); - const __m128i k12000_plus_one = _mm_set1_epi32(12000 + (1 << 16)); - const __m128i k5352_2217 = _mm_set_epi16(5352, 2217, 5352, 2217, - 5352, 2217, 5352, 2217); - const __m128i k2217_5352 = _mm_set_epi16(2217, -5352, 2217, -5352, - 2217, -5352, 2217, -5352); - - __m128i v01, v32; - - // Difference between src and ref and initial transpose. - { - // Load src and convert to 16b. - const __m128i src0 = _mm_loadl_epi64((__m128i*)&src[0 * BPS]); - const __m128i src1 = _mm_loadl_epi64((__m128i*)&src[1 * BPS]); - const __m128i src2 = _mm_loadl_epi64((__m128i*)&src[2 * BPS]); - const __m128i src3 = _mm_loadl_epi64((__m128i*)&src[3 * BPS]); - const __m128i src_0 = _mm_unpacklo_epi8(src0, zero); - const __m128i src_1 = _mm_unpacklo_epi8(src1, zero); - const __m128i src_2 = _mm_unpacklo_epi8(src2, zero); - const __m128i src_3 = _mm_unpacklo_epi8(src3, zero); - // Load ref and convert to 16b. - const __m128i ref0 = _mm_loadl_epi64((__m128i*)&ref[0 * BPS]); - const __m128i ref1 = _mm_loadl_epi64((__m128i*)&ref[1 * BPS]); - const __m128i ref2 = _mm_loadl_epi64((__m128i*)&ref[2 * BPS]); - const __m128i ref3 = _mm_loadl_epi64((__m128i*)&ref[3 * BPS]); - const __m128i ref_0 = _mm_unpacklo_epi8(ref0, zero); - const __m128i ref_1 = _mm_unpacklo_epi8(ref1, zero); - const __m128i ref_2 = _mm_unpacklo_epi8(ref2, zero); - const __m128i ref_3 = _mm_unpacklo_epi8(ref3, zero); - // Compute difference. - const __m128i diff0 = _mm_sub_epi16(src_0, ref_0); - const __m128i diff1 = _mm_sub_epi16(src_1, ref_1); - const __m128i diff2 = _mm_sub_epi16(src_2, ref_2); - const __m128i diff3 = _mm_sub_epi16(src_3, ref_3); - - // Transpose. - // 00 01 02 03 0 0 0 0 - // 10 11 12 13 0 0 0 0 - // 20 21 22 23 0 0 0 0 - // 30 31 32 33 0 0 0 0 - const __m128i transpose0_0 = _mm_unpacklo_epi16(diff0, diff1); - const __m128i transpose0_1 = _mm_unpacklo_epi16(diff2, diff3); - // 00 10 01 11 02 12 03 13 - // 20 30 21 31 22 32 23 33 - const __m128i v23 = _mm_unpackhi_epi32(transpose0_0, transpose0_1); - v01 = _mm_unpacklo_epi32(transpose0_0, transpose0_1); - v32 = _mm_shuffle_epi32(v23, _MM_SHUFFLE(1, 0, 3, 2)); - // a02 a12 a22 a32 a03 a13 a23 a33 - // a00 a10 a20 a30 a01 a11 a21 a31 - // a03 a13 a23 a33 a02 a12 a22 a32 - } - - // First pass and subsequent transpose. - { - // Same operations are done on the (0,3) and (1,2) pairs. - // b0 = (a0 + a3) << 3 - // b1 = (a1 + a2) << 3 - // b3 = (a0 - a3) << 3 - // b2 = (a1 - a2) << 3 - const __m128i a01 = _mm_add_epi16(v01, v32); - const __m128i a32 = _mm_sub_epi16(v01, v32); - const __m128i b01 = _mm_slli_epi16(a01, 3); - const __m128i b32 = _mm_slli_epi16(a32, 3); - const __m128i b11 = _mm_unpackhi_epi64(b01, b01); - const __m128i b22 = _mm_unpackhi_epi64(b32, b32); - - // e0 = b0 + b1 - // e2 = b0 - b1 - const __m128i e0 = _mm_add_epi16(b01, b11); - const __m128i e2 = _mm_sub_epi16(b01, b11); - const __m128i e02 = _mm_unpacklo_epi64(e0, e2); - - // e1 = (b3 * 5352 + b2 * 2217 + 14500) >> 12 - // e3 = (b3 * 2217 - b2 * 5352 + 7500) >> 12 - const __m128i b23 = _mm_unpacklo_epi16(b22, b32); - const __m128i c1 = _mm_madd_epi16(b23, k5352_2217); - const __m128i c3 = _mm_madd_epi16(b23, k2217_5352); - const __m128i d1 = _mm_add_epi32(c1, k14500); - const __m128i d3 = _mm_add_epi32(c3, k7500); - const __m128i e1 = _mm_srai_epi32(d1, 12); - const __m128i e3 = _mm_srai_epi32(d3, 12); - const __m128i e13 = _mm_packs_epi32(e1, e3); - - // Transpose. - // 00 01 02 03 20 21 22 23 - // 10 11 12 13 30 31 32 33 - const __m128i transpose0_0 = _mm_unpacklo_epi16(e02, e13); - const __m128i transpose0_1 = _mm_unpackhi_epi16(e02, e13); - // 00 10 01 11 02 12 03 13 - // 20 30 21 31 22 32 23 33 - const __m128i v23 = _mm_unpackhi_epi32(transpose0_0, transpose0_1); - v01 = _mm_unpacklo_epi32(transpose0_0, transpose0_1); - v32 = _mm_shuffle_epi32(v23, _MM_SHUFFLE(1, 0, 3, 2)); - // 02 12 22 32 03 13 23 33 - // 00 10 20 30 01 11 21 31 - // 03 13 23 33 02 12 22 32 - } - - // Second pass - { - // Same operations are done on the (0,3) and (1,2) pairs. - // a0 = v0 + v3 - // a1 = v1 + v2 - // a3 = v0 - v3 - // a2 = v1 - v2 - const __m128i a01 = _mm_add_epi16(v01, v32); - const __m128i a32 = _mm_sub_epi16(v01, v32); - const __m128i a11 = _mm_unpackhi_epi64(a01, a01); - const __m128i a22 = _mm_unpackhi_epi64(a32, a32); - - // d0 = (a0 + a1 + 7) >> 4; - // d2 = (a0 - a1 + 7) >> 4; - const __m128i b0 = _mm_add_epi16(a01, a11); - const __m128i b2 = _mm_sub_epi16(a01, a11); - const __m128i c0 = _mm_add_epi16(b0, seven); - const __m128i c2 = _mm_add_epi16(b2, seven); - const __m128i d0 = _mm_srai_epi16(c0, 4); - const __m128i d2 = _mm_srai_epi16(c2, 4); - - // f1 = ((b3 * 5352 + b2 * 2217 + 12000) >> 16) - // f3 = ((b3 * 2217 - b2 * 5352 + 51000) >> 16) - const __m128i b23 = _mm_unpacklo_epi16(a22, a32); - const __m128i c1 = _mm_madd_epi16(b23, k5352_2217); - const __m128i c3 = _mm_madd_epi16(b23, k2217_5352); - const __m128i d1 = _mm_add_epi32(c1, k12000_plus_one); - const __m128i d3 = _mm_add_epi32(c3, k51000); - const __m128i e1 = _mm_srai_epi32(d1, 16); - const __m128i e3 = _mm_srai_epi32(d3, 16); - const __m128i f1 = _mm_packs_epi32(e1, e1); - const __m128i f3 = _mm_packs_epi32(e3, e3); - // f1 = f1 + (a3 != 0); - // The compare will return (0xffff, 0) for (==0, !=0). To turn that into the - // desired (0, 1), we add one earlier through k12000_plus_one. - const __m128i g1 = _mm_add_epi16(f1, _mm_cmpeq_epi16(a32, zero)); - - _mm_storel_epi64((__m128i*)&out[ 0], d0); - _mm_storel_epi64((__m128i*)&out[ 4], g1); - _mm_storel_epi64((__m128i*)&out[ 8], d2); - _mm_storel_epi64((__m128i*)&out[12], f3); - } -} - -//------------------------------------------------------------------------------ -// Metric - -static int SSE4x4SSE2(const uint8_t* a, const uint8_t* b) { - const __m128i zero = _mm_set1_epi16(0); - - // Load values. - const __m128i a0 = _mm_loadl_epi64((__m128i*)&a[BPS * 0]); - const __m128i a1 = _mm_loadl_epi64((__m128i*)&a[BPS * 1]); - const __m128i a2 = _mm_loadl_epi64((__m128i*)&a[BPS * 2]); - const __m128i a3 = _mm_loadl_epi64((__m128i*)&a[BPS * 3]); - const __m128i b0 = _mm_loadl_epi64((__m128i*)&b[BPS * 0]); - const __m128i b1 = _mm_loadl_epi64((__m128i*)&b[BPS * 1]); - const __m128i b2 = _mm_loadl_epi64((__m128i*)&b[BPS * 2]); - const __m128i b3 = _mm_loadl_epi64((__m128i*)&b[BPS * 3]); - - // Combine pair of lines and convert to 16b. - const __m128i a01 = _mm_unpacklo_epi32(a0, a1); - const __m128i a23 = _mm_unpacklo_epi32(a2, a3); - const __m128i b01 = _mm_unpacklo_epi32(b0, b1); - const __m128i b23 = _mm_unpacklo_epi32(b2, b3); - const __m128i a01s = _mm_unpacklo_epi8(a01, zero); - const __m128i a23s = _mm_unpacklo_epi8(a23, zero); - const __m128i b01s = _mm_unpacklo_epi8(b01, zero); - const __m128i b23s = _mm_unpacklo_epi8(b23, zero); - - // Compute differences; (a-b)^2 = (abs(a-b))^2 = (sat8(a-b) + sat8(b-a))^2 - // TODO(cduvivier): Dissassemble and figure out why this is fastest. We don't - // need absolute values, there is no need to do calculation - // in 8bit as we are already in 16bit, ... Yet this is what - // benchmarks the fastest! - const __m128i d0 = _mm_subs_epu8(a01s, b01s); - const __m128i d1 = _mm_subs_epu8(b01s, a01s); - const __m128i d2 = _mm_subs_epu8(a23s, b23s); - const __m128i d3 = _mm_subs_epu8(b23s, a23s); - - // Square and add them all together. - const __m128i madd0 = _mm_madd_epi16(d0, d0); - const __m128i madd1 = _mm_madd_epi16(d1, d1); - const __m128i madd2 = _mm_madd_epi16(d2, d2); - const __m128i madd3 = _mm_madd_epi16(d3, d3); - const __m128i sum0 = _mm_add_epi32(madd0, madd1); - const __m128i sum1 = _mm_add_epi32(madd2, madd3); - const __m128i sum2 = _mm_add_epi32(sum0, sum1); - int32_t tmp[4]; - _mm_storeu_si128((__m128i*)tmp, sum2); - return (tmp[3] + tmp[2] + tmp[1] + tmp[0]); -} - -//------------------------------------------------------------------------------ -// Texture distortion -// -// We try to match the spectral content (weighted) between source and -// reconstructed samples. - -// Hadamard transform -// Returns the difference between the weighted sum of the absolute value of -// transformed coefficients. -static int TTransformSSE2(const uint8_t* inA, const uint8_t* inB, - const uint16_t* const w) { - int32_t sum[4]; - __m128i tmp_0, tmp_1, tmp_2, tmp_3; - const __m128i zero = _mm_setzero_si128(); - const __m128i one = _mm_set1_epi16(1); - const __m128i three = _mm_set1_epi16(3); - - // Load, combine and tranpose inputs. - { - const __m128i inA_0 = _mm_loadl_epi64((__m128i*)&inA[BPS * 0]); - const __m128i inA_1 = _mm_loadl_epi64((__m128i*)&inA[BPS * 1]); - const __m128i inA_2 = _mm_loadl_epi64((__m128i*)&inA[BPS * 2]); - const __m128i inA_3 = _mm_loadl_epi64((__m128i*)&inA[BPS * 3]); - const __m128i inB_0 = _mm_loadl_epi64((__m128i*)&inB[BPS * 0]); - const __m128i inB_1 = _mm_loadl_epi64((__m128i*)&inB[BPS * 1]); - const __m128i inB_2 = _mm_loadl_epi64((__m128i*)&inB[BPS * 2]); - const __m128i inB_3 = _mm_loadl_epi64((__m128i*)&inB[BPS * 3]); - - // Combine inA and inB (we'll do two transforms in parallel). - const __m128i inAB_0 = _mm_unpacklo_epi8(inA_0, inB_0); - const __m128i inAB_1 = _mm_unpacklo_epi8(inA_1, inB_1); - const __m128i inAB_2 = _mm_unpacklo_epi8(inA_2, inB_2); - const __m128i inAB_3 = _mm_unpacklo_epi8(inA_3, inB_3); - // a00 b00 a01 b01 a02 b03 a03 b03 0 0 0 0 0 0 0 0 - // a10 b10 a11 b11 a12 b12 a13 b13 0 0 0 0 0 0 0 0 - // a20 b20 a21 b21 a22 b22 a23 b23 0 0 0 0 0 0 0 0 - // a30 b30 a31 b31 a32 b32 a33 b33 0 0 0 0 0 0 0 0 - - // Transpose the two 4x4, discarding the filling zeroes. - const __m128i transpose0_0 = _mm_unpacklo_epi8(inAB_0, inAB_2); - const __m128i transpose0_1 = _mm_unpacklo_epi8(inAB_1, inAB_3); - // a00 a20 b00 b20 a01 a21 b01 b21 a02 a22 b02 b22 a03 a23 b03 b23 - // a10 a30 b10 b30 a11 a31 b11 b31 a12 a32 b12 b32 a13 a33 b13 b33 - const __m128i transpose1_0 = _mm_unpacklo_epi8(transpose0_0, transpose0_1); - const __m128i transpose1_1 = _mm_unpackhi_epi8(transpose0_0, transpose0_1); - // 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 - - // Convert to 16b. - tmp_0 = _mm_unpacklo_epi8(transpose1_0, zero); - tmp_1 = _mm_unpackhi_epi8(transpose1_0, zero); - tmp_2 = _mm_unpacklo_epi8(transpose1_1, zero); - tmp_3 = _mm_unpackhi_epi8(transpose1_1, zero); - // 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. - { - // Calculate a and b (two 4x4 at once). - const __m128i a0 = _mm_slli_epi16(_mm_add_epi16(tmp_0, tmp_2), 2); - const __m128i a1 = _mm_slli_epi16(_mm_add_epi16(tmp_1, tmp_3), 2); - const __m128i a2 = _mm_slli_epi16(_mm_sub_epi16(tmp_1, tmp_3), 2); - const __m128i a3 = _mm_slli_epi16(_mm_sub_epi16(tmp_0, tmp_2), 2); - // b0_extra = (a0 != 0); - const __m128i b0_extra = _mm_andnot_si128(_mm_cmpeq_epi16 (a0, zero), one); - const __m128i b0_base = _mm_add_epi16(a0, a1); - const __m128i b1 = _mm_add_epi16(a3, a2); - const __m128i b2 = _mm_sub_epi16(a3, a2); - const __m128i b3 = _mm_sub_epi16(a0, a1); - const __m128i b0 = _mm_add_epi16(b0_base, b0_extra); - // 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 - - // Transpose the two 4x4. - const __m128i transpose0_0 = _mm_unpacklo_epi16(b0, b1); - const __m128i transpose0_1 = _mm_unpacklo_epi16(b2, b3); - const __m128i transpose0_2 = _mm_unpackhi_epi16(b0, b1); - const __m128i transpose0_3 = _mm_unpackhi_epi16(b2, b3); - // 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 - tmp_0 = _mm_unpacklo_epi64(transpose1_0, transpose1_1); - tmp_1 = _mm_unpackhi_epi64(transpose1_0, transpose1_1); - tmp_2 = _mm_unpacklo_epi64(transpose1_2, transpose1_3); - tmp_3 = _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 - } - - // Vertical pass and difference of weighted sums. - { - // Load all inputs. - // TODO(cduvivier): Make variable declarations and allocations aligned so - // we can use _mm_load_si128 instead of _mm_loadu_si128. - const __m128i w_0 = _mm_loadu_si128((__m128i*)&w[0]); - const __m128i w_8 = _mm_loadu_si128((__m128i*)&w[8]); - - // Calculate a and b (two 4x4 at once). - const __m128i a0 = _mm_add_epi16(tmp_0, tmp_2); - const __m128i a1 = _mm_add_epi16(tmp_1, tmp_3); - const __m128i a2 = _mm_sub_epi16(tmp_1, tmp_3); - const __m128i a3 = _mm_sub_epi16(tmp_0, tmp_2); - const __m128i b0 = _mm_add_epi16(a0, a1); - const __m128i b1 = _mm_add_epi16(a3, a2); - const __m128i b2 = _mm_sub_epi16(a3, a2); - const __m128i b3 = _mm_sub_epi16(a0, a1); - - // Separate the transforms of inA and inB. - __m128i A_b0 = _mm_unpacklo_epi64(b0, b1); - __m128i A_b2 = _mm_unpacklo_epi64(b2, b3); - __m128i B_b0 = _mm_unpackhi_epi64(b0, b1); - __m128i B_b2 = _mm_unpackhi_epi64(b2, b3); - - { - // sign(b) = b >> 15 (0x0000 if positive, 0xffff if negative) - const __m128i sign_A_b0 = _mm_srai_epi16(A_b0, 15); - const __m128i sign_A_b2 = _mm_srai_epi16(A_b2, 15); - const __m128i sign_B_b0 = _mm_srai_epi16(B_b0, 15); - const __m128i sign_B_b2 = _mm_srai_epi16(B_b2, 15); - - // b = abs(b) = (b ^ sign) - sign - A_b0 = _mm_xor_si128(A_b0, sign_A_b0); - A_b2 = _mm_xor_si128(A_b2, sign_A_b2); - B_b0 = _mm_xor_si128(B_b0, sign_B_b0); - B_b2 = _mm_xor_si128(B_b2, sign_B_b2); - A_b0 = _mm_sub_epi16(A_b0, sign_A_b0); - A_b2 = _mm_sub_epi16(A_b2, sign_A_b2); - B_b0 = _mm_sub_epi16(B_b0, sign_B_b0); - B_b2 = _mm_sub_epi16(B_b2, sign_B_b2); - } - - // b = abs(b) + 3 - A_b0 = _mm_add_epi16(A_b0, three); - A_b2 = _mm_add_epi16(A_b2, three); - B_b0 = _mm_add_epi16(B_b0, three); - B_b2 = _mm_add_epi16(B_b2, three); - - // abs((b + (b<0) + 3) >> 3) = (abs(b) + 3) >> 3 - // b = (abs(b) + 3) >> 3 - A_b0 = _mm_srai_epi16(A_b0, 3); - A_b2 = _mm_srai_epi16(A_b2, 3); - B_b0 = _mm_srai_epi16(B_b0, 3); - B_b2 = _mm_srai_epi16(B_b2, 3); - - // weighted sums - A_b0 = _mm_madd_epi16(A_b0, w_0); - A_b2 = _mm_madd_epi16(A_b2, w_8); - B_b0 = _mm_madd_epi16(B_b0, w_0); - B_b2 = _mm_madd_epi16(B_b2, w_8); - A_b0 = _mm_add_epi32(A_b0, A_b2); - B_b0 = _mm_add_epi32(B_b0, B_b2); - - // difference of weighted sums - A_b0 = _mm_sub_epi32(A_b0, B_b0); - _mm_storeu_si128((__m128i*)&sum[0], A_b0); - } - return sum[0] + sum[1] + sum[2] + sum[3]; -} - -static int Disto4x4SSE2(const uint8_t* const a, const uint8_t* const b, - const uint16_t* const w) { - const int diff_sum = TTransformSSE2(a, b, w); - return (abs(diff_sum) + 8) >> 4; -} - -static int Disto16x16SSE2(const uint8_t* const a, const uint8_t* const b, - const uint16_t* const w) { - int D = 0; - int x, y; - for (y = 0; y < 16 * BPS; y += 4 * BPS) { - for (x = 0; x < 16; x += 4) { - D += Disto4x4SSE2(a + x + y, b + x + y, w); - } - } - return D; -} - - -//------------------------------------------------------------------------------ -// Quantization -// - -// Simple quantization -static int QuantizeBlockSSE2(int16_t in[16], int16_t out[16], - int n, const VP8Matrix* const mtx) { - const __m128i max_coeff_2047 = _mm_set1_epi16(2047); - const __m128i zero = _mm_set1_epi16(0); - __m128i sign0, sign8; - __m128i coeff0, coeff8; - __m128i out0, out8; - __m128i packed_out; - - // Load all inputs. - // TODO(cduvivier): Make variable declarations and allocations aligned so that - // we can use _mm_load_si128 instead of _mm_loadu_si128. - __m128i in0 = _mm_loadu_si128((__m128i*)&in[0]); - __m128i in8 = _mm_loadu_si128((__m128i*)&in[8]); - const __m128i sharpen0 = _mm_loadu_si128((__m128i*)&mtx->sharpen_[0]); - const __m128i sharpen8 = _mm_loadu_si128((__m128i*)&mtx->sharpen_[8]); - const __m128i iq0 = _mm_loadu_si128((__m128i*)&mtx->iq_[0]); - const __m128i iq8 = _mm_loadu_si128((__m128i*)&mtx->iq_[8]); - const __m128i bias0 = _mm_loadu_si128((__m128i*)&mtx->bias_[0]); - const __m128i bias8 = _mm_loadu_si128((__m128i*)&mtx->bias_[8]); - const __m128i q0 = _mm_loadu_si128((__m128i*)&mtx->q_[0]); - const __m128i q8 = _mm_loadu_si128((__m128i*)&mtx->q_[8]); - const __m128i zthresh0 = _mm_loadu_si128((__m128i*)&mtx->zthresh_[0]); - const __m128i zthresh8 = _mm_loadu_si128((__m128i*)&mtx->zthresh_[8]); - - // sign(in) = in >> 15 (0x0000 if positive, 0xffff if negative) - sign0 = _mm_srai_epi16(in0, 15); - sign8 = _mm_srai_epi16(in8, 15); - - // coeff = abs(in) = (in ^ sign) - sign - coeff0 = _mm_xor_si128(in0, sign0); - coeff8 = _mm_xor_si128(in8, sign8); - coeff0 = _mm_sub_epi16(coeff0, sign0); - coeff8 = _mm_sub_epi16(coeff8, sign8); - - // coeff = abs(in) + sharpen - coeff0 = _mm_add_epi16(coeff0, sharpen0); - coeff8 = _mm_add_epi16(coeff8, sharpen8); - - // if (coeff > 2047) coeff = 2047 - coeff0 = _mm_min_epi16(coeff0, max_coeff_2047); - coeff8 = _mm_min_epi16(coeff8, max_coeff_2047); - - // out = (coeff * iQ + B) >> QFIX; - { - // doing calculations with 32b precision (QFIX=17) - // out = (coeff * iQ) - __m128i coeff_iQ0H = _mm_mulhi_epu16(coeff0, iq0); - __m128i coeff_iQ0L = _mm_mullo_epi16(coeff0, iq0); - __m128i coeff_iQ8H = _mm_mulhi_epu16(coeff8, iq8); - __m128i coeff_iQ8L = _mm_mullo_epi16(coeff8, iq8); - __m128i out_00 = _mm_unpacklo_epi16(coeff_iQ0L, coeff_iQ0H); - __m128i out_04 = _mm_unpackhi_epi16(coeff_iQ0L, coeff_iQ0H); - __m128i out_08 = _mm_unpacklo_epi16(coeff_iQ8L, coeff_iQ8H); - __m128i out_12 = _mm_unpackhi_epi16(coeff_iQ8L, coeff_iQ8H); - // expand bias from 16b to 32b - __m128i bias_00 = _mm_unpacklo_epi16(bias0, zero); - __m128i bias_04 = _mm_unpackhi_epi16(bias0, zero); - __m128i bias_08 = _mm_unpacklo_epi16(bias8, zero); - __m128i bias_12 = _mm_unpackhi_epi16(bias8, zero); - // out = (coeff * iQ + B) - out_00 = _mm_add_epi32(out_00, bias_00); - out_04 = _mm_add_epi32(out_04, bias_04); - out_08 = _mm_add_epi32(out_08, bias_08); - out_12 = _mm_add_epi32(out_12, bias_12); - // out = (coeff * iQ + B) >> QFIX; - out_00 = _mm_srai_epi32(out_00, QFIX); - out_04 = _mm_srai_epi32(out_04, QFIX); - out_08 = _mm_srai_epi32(out_08, QFIX); - out_12 = _mm_srai_epi32(out_12, QFIX); - // pack result as 16b - out0 = _mm_packs_epi32(out_00, out_04); - out8 = _mm_packs_epi32(out_08, out_12); - } - - // get sign back (if (sign[j]) out_n = -out_n) - out0 = _mm_xor_si128(out0, sign0); - out8 = _mm_xor_si128(out8, sign8); - out0 = _mm_sub_epi16(out0, sign0); - out8 = _mm_sub_epi16(out8, sign8); - - // in = out * Q - in0 = _mm_mullo_epi16(out0, q0); - in8 = _mm_mullo_epi16(out8, q8); - - // if (coeff <= mtx->zthresh_) {in=0; out=0;} - { - __m128i cmp0 = _mm_cmpgt_epi16(coeff0, zthresh0); - __m128i cmp8 = _mm_cmpgt_epi16(coeff8, zthresh8); - in0 = _mm_and_si128(in0, cmp0); - in8 = _mm_and_si128(in8, cmp8); - _mm_storeu_si128((__m128i*)&in[0], in0); - _mm_storeu_si128((__m128i*)&in[8], in8); - out0 = _mm_and_si128(out0, cmp0); - out8 = _mm_and_si128(out8, cmp8); - } - - // zigzag the output before storing it. - // - // The zigzag pattern can almost be reproduced with a small sequence of - // shuffles. After it, we only need to swap the 7th (ending up in third - // position instead of twelfth) and 8th values. - { - __m128i outZ0, outZ8; - outZ0 = _mm_shufflehi_epi16(out0, _MM_SHUFFLE(2, 1, 3, 0)); - outZ0 = _mm_shuffle_epi32 (outZ0, _MM_SHUFFLE(3, 1, 2, 0)); - outZ0 = _mm_shufflehi_epi16(outZ0, _MM_SHUFFLE(3, 1, 0, 2)); - outZ8 = _mm_shufflelo_epi16(out8, _MM_SHUFFLE(3, 0, 2, 1)); - outZ8 = _mm_shuffle_epi32 (outZ8, _MM_SHUFFLE(3, 1, 2, 0)); - outZ8 = _mm_shufflelo_epi16(outZ8, _MM_SHUFFLE(1, 3, 2, 0)); - _mm_storeu_si128((__m128i*)&out[0], outZ0); - _mm_storeu_si128((__m128i*)&out[8], outZ8); - packed_out = _mm_packs_epi16(outZ0, outZ8); - } - { - const int16_t outZ_12 = out[12]; - const int16_t outZ_3 = out[3]; - out[3] = outZ_12; - out[12] = outZ_3; - } - - // detect if all 'out' values are zeroes or not - { - int32_t tmp[4]; - _mm_storeu_si128((__m128i*)tmp, packed_out); - if (n) { - tmp[0] &= ~0xff; - } - return (tmp[3] || tmp[2] || tmp[1] || tmp[0]); - } -} - -extern void VP8EncDspInitSSE2(void); -void VP8EncDspInitSSE2(void) { - VP8CollectHistogram = CollectHistogramSSE2; - VP8EncQuantizeBlock = QuantizeBlockSSE2; - VP8ITransform = ITransformSSE2; - VP8FTransform = FTransformSSE2; - VP8SSE4x4 = SSE4x4SSE2; - VP8TDisto4x4 = Disto4x4SSE2; - VP8TDisto16x16 = Disto16x16SSE2; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif // WEBP_USE_SSE2 diff --git a/drivers/webpold/dsp/lossless.c b/drivers/webpold/dsp/lossless.c deleted file mode 100644 index 62a6b7b15a..0000000000 --- a/drivers/webpold/dsp/lossless.c +++ /dev/null @@ -1,1138 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Image transforms and color space conversion methods for lossless decoder. -// -// Authors: Vikas Arora (vikaas.arora@gmail.com) -// Jyrki Alakuijala (jyrki@google.com) -// Urvang Joshi (urvang@google.com) - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#include <math.h> -#include <stdlib.h> -#include "./lossless.h" -#include "../dec/vp8li.h" -#include "../dsp/yuv.h" -#include "../dsp/dsp.h" -#include "../enc/histogram.h" - -#define MAX_DIFF_COST (1e30f) - -// lookup table for small values of log2(int) -#define APPROX_LOG_MAX 4096 -#define LOG_2_RECIPROCAL 1.44269504088896338700465094007086 -#define LOG_LOOKUP_IDX_MAX 256 -static const float kLog2Table[LOG_LOOKUP_IDX_MAX] = { - 0.0000000000000000f, 0.0000000000000000f, - 1.0000000000000000f, 1.5849625007211560f, - 2.0000000000000000f, 2.3219280948873621f, - 2.5849625007211560f, 2.8073549220576041f, - 3.0000000000000000f, 3.1699250014423121f, - 3.3219280948873621f, 3.4594316186372973f, - 3.5849625007211560f, 3.7004397181410921f, - 3.8073549220576041f, 3.9068905956085187f, - 4.0000000000000000f, 4.0874628412503390f, - 4.1699250014423121f, 4.2479275134435852f, - 4.3219280948873626f, 4.3923174227787606f, - 4.4594316186372973f, 4.5235619560570130f, - 4.5849625007211560f, 4.6438561897747243f, - 4.7004397181410917f, 4.7548875021634682f, - 4.8073549220576037f, 4.8579809951275718f, - 4.9068905956085187f, 4.9541963103868749f, - 5.0000000000000000f, 5.0443941193584533f, - 5.0874628412503390f, 5.1292830169449663f, - 5.1699250014423121f, 5.2094533656289501f, - 5.2479275134435852f, 5.2854022188622487f, - 5.3219280948873626f, 5.3575520046180837f, - 5.3923174227787606f, 5.4262647547020979f, - 5.4594316186372973f, 5.4918530963296747f, - 5.5235619560570130f, 5.5545888516776376f, - 5.5849625007211560f, 5.6147098441152083f, - 5.6438561897747243f, 5.6724253419714951f, - 5.7004397181410917f, 5.7279204545631987f, - 5.7548875021634682f, 5.7813597135246599f, - 5.8073549220576037f, 5.8328900141647412f, - 5.8579809951275718f, 5.8826430493618415f, - 5.9068905956085187f, 5.9307373375628866f, - 5.9541963103868749f, 5.9772799234999167f, - 6.0000000000000000f, 6.0223678130284543f, - 6.0443941193584533f, 6.0660891904577720f, - 6.0874628412503390f, 6.1085244567781691f, - 6.1292830169449663f, 6.1497471195046822f, - 6.1699250014423121f, 6.1898245588800175f, - 6.2094533656289501f, 6.2288186904958804f, - 6.2479275134435852f, 6.2667865406949010f, - 6.2854022188622487f, 6.3037807481771030f, - 6.3219280948873626f, 6.3398500028846243f, - 6.3575520046180837f, 6.3750394313469245f, - 6.3923174227787606f, 6.4093909361377017f, - 6.4262647547020979f, 6.4429434958487279f, - 6.4594316186372973f, 6.4757334309663976f, - 6.4918530963296747f, 6.5077946401986963f, - 6.5235619560570130f, 6.5391588111080309f, - 6.5545888516776376f, 6.5698556083309478f, - 6.5849625007211560f, 6.5999128421871278f, - 6.6147098441152083f, 6.6293566200796094f, - 6.6438561897747243f, 6.6582114827517946f, - 6.6724253419714951f, 6.6865005271832185f, - 6.7004397181410917f, 6.7142455176661224f, - 6.7279204545631987f, 6.7414669864011464f, - 6.7548875021634682f, 6.7681843247769259f, - 6.7813597135246599f, 6.7944158663501061f, - 6.8073549220576037f, 6.8201789624151878f, - 6.8328900141647412f, 6.8454900509443747f, - 6.8579809951275718f, 6.8703647195834047f, - 6.8826430493618415f, 6.8948177633079437f, - 6.9068905956085187f, 6.9188632372745946f, - 6.9307373375628866f, 6.9425145053392398f, - 6.9541963103868749f, 6.9657842846620869f, - 6.9772799234999167f, 6.9886846867721654f, - 7.0000000000000000f, 7.0112272554232539f, - 7.0223678130284543f, 7.0334230015374501f, - 7.0443941193584533f, 7.0552824355011898f, - 7.0660891904577720f, 7.0768155970508308f, - 7.0874628412503390f, 7.0980320829605263f, - 7.1085244567781691f, 7.1189410727235076f, - 7.1292830169449663f, 7.1395513523987936f, - 7.1497471195046822f, 7.1598713367783890f, - 7.1699250014423121f, 7.1799090900149344f, - 7.1898245588800175f, 7.1996723448363644f, - 7.2094533656289501f, 7.2191685204621611f, - 7.2288186904958804f, 7.2384047393250785f, - 7.2479275134435852f, 7.2573878426926521f, - 7.2667865406949010f, 7.2761244052742375f, - 7.2854022188622487f, 7.2946207488916270f, - 7.3037807481771030f, 7.3128829552843557f, - 7.3219280948873626f, 7.3309168781146167f, - 7.3398500028846243f, 7.3487281542310771f, - 7.3575520046180837f, 7.3663222142458160f, - 7.3750394313469245f, 7.3837042924740519f, - 7.3923174227787606f, 7.4008794362821843f, - 7.4093909361377017f, 7.4178525148858982f, - 7.4262647547020979f, 7.4346282276367245f, - 7.4429434958487279f, 7.4512111118323289f, - 7.4594316186372973f, 7.4676055500829976f, - 7.4757334309663976f, 7.4838157772642563f, - 7.4918530963296747f, 7.4998458870832056f, - 7.5077946401986963f, 7.5156998382840427f, - 7.5235619560570130f, 7.5313814605163118f, - 7.5391588111080309f, 7.5468944598876364f, - 7.5545888516776376f, 7.5622424242210728f, - 7.5698556083309478f, 7.5774288280357486f, - 7.5849625007211560f, 7.5924570372680806f, - 7.5999128421871278f, 7.6073303137496104f, - 7.6147098441152083f, 7.6220518194563764f, - 7.6293566200796094f, 7.6366246205436487f, - 7.6438561897747243f, 7.6510516911789281f, - 7.6582114827517946f, 7.6653359171851764f, - 7.6724253419714951f, 7.6794800995054464f, - 7.6865005271832185f, 7.6934869574993252f, - 7.7004397181410917f, 7.7073591320808825f, - 7.7142455176661224f, 7.7210991887071855f, - 7.7279204545631987f, 7.7347096202258383f, - 7.7414669864011464f, 7.7481928495894605f, - 7.7548875021634682f, 7.7615512324444795f, - 7.7681843247769259f, 7.7747870596011736f, - 7.7813597135246599f, 7.7879025593914317f, - 7.7944158663501061f, 7.8008998999203047f, - 7.8073549220576037f, 7.8137811912170374f, - 7.8201789624151878f, 7.8265484872909150f, - 7.8328900141647412f, 7.8392037880969436f, - 7.8454900509443747f, 7.8517490414160571f, - 7.8579809951275718f, 7.8641861446542797f, - 7.8703647195834047f, 7.8765169465649993f, - 7.8826430493618415f, 7.8887432488982591f, - 7.8948177633079437f, 7.9008668079807486f, - 7.9068905956085187f, 7.9128893362299619f, - 7.9188632372745946f, 7.9248125036057812f, - 7.9307373375628866f, 7.9366379390025709f, - 7.9425145053392398f, 7.9483672315846778f, - 7.9541963103868749f, 7.9600019320680805f, - 7.9657842846620869f, 7.9715435539507719f, - 7.9772799234999167f, 7.9829935746943103f, - 7.9886846867721654f, 7.9943534368588577f -}; - -float VP8LFastLog2(int v) { - if (v < LOG_LOOKUP_IDX_MAX) { - return kLog2Table[v]; - } else if (v < APPROX_LOG_MAX) { - int log_cnt = 0; - while (v >= LOG_LOOKUP_IDX_MAX) { - ++log_cnt; - v = v >> 1; - } - return kLog2Table[v] + (float)log_cnt; - } else { - return (float)(LOG_2_RECIPROCAL * log((double)v)); - } -} - -//------------------------------------------------------------------------------ -// Image transforms. - -// In-place sum of each component with mod 256. -static WEBP_INLINE void AddPixelsEq(uint32_t* a, uint32_t b) { - const uint32_t alpha_and_green = (*a & 0xff00ff00u) + (b & 0xff00ff00u); - const uint32_t red_and_blue = (*a & 0x00ff00ffu) + (b & 0x00ff00ffu); - *a = (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); -} - -static WEBP_INLINE uint32_t Average2(uint32_t a0, uint32_t a1) { - return (((a0 ^ a1) & 0xfefefefeL) >> 1) + (a0 & a1); -} - -static WEBP_INLINE uint32_t Average3(uint32_t a0, uint32_t a1, uint32_t a2) { - return Average2(Average2(a0, a2), a1); -} - -static WEBP_INLINE uint32_t Average4(uint32_t a0, uint32_t a1, - uint32_t a2, uint32_t a3) { - return Average2(Average2(a0, a1), Average2(a2, a3)); -} - -static WEBP_INLINE uint32_t Clip255(uint32_t a) { - if (a < 256) { - return a; - } - // return 0, when a is a negative integer. - // return 255, when a is positive. - return ~a >> 24; -} - -static WEBP_INLINE int AddSubtractComponentFull(int a, int b, int c) { - return Clip255(a + b - c); -} - -static WEBP_INLINE uint32_t ClampedAddSubtractFull(uint32_t c0, uint32_t c1, - uint32_t c2) { - const int a = AddSubtractComponentFull(c0 >> 24, c1 >> 24, c2 >> 24); - const int r = AddSubtractComponentFull((c0 >> 16) & 0xff, - (c1 >> 16) & 0xff, - (c2 >> 16) & 0xff); - const int g = AddSubtractComponentFull((c0 >> 8) & 0xff, - (c1 >> 8) & 0xff, - (c2 >> 8) & 0xff); - const int b = AddSubtractComponentFull(c0 & 0xff, c1 & 0xff, c2 & 0xff); - return (a << 24) | (r << 16) | (g << 8) | b; -} - -static WEBP_INLINE int AddSubtractComponentHalf(int a, int b) { - return Clip255(a + (a - b) / 2); -} - -static WEBP_INLINE uint32_t ClampedAddSubtractHalf(uint32_t c0, uint32_t c1, - uint32_t c2) { - const uint32_t ave = Average2(c0, c1); - const int a = AddSubtractComponentHalf(ave >> 24, c2 >> 24); - const int r = AddSubtractComponentHalf((ave >> 16) & 0xff, (c2 >> 16) & 0xff); - const int g = AddSubtractComponentHalf((ave >> 8) & 0xff, (c2 >> 8) & 0xff); - const int b = AddSubtractComponentHalf((ave >> 0) & 0xff, (c2 >> 0) & 0xff); - return (a << 24) | (r << 16) | (g << 8) | b; -} - -static WEBP_INLINE int Sub3(int a, int b, int c) { - const int pa = b - c; - const int pb = a - c; - return abs(pa) - abs(pb); -} - -static WEBP_INLINE uint32_t Select(uint32_t a, uint32_t b, uint32_t c) { - const int pa_minus_pb = - Sub3((a >> 24) , (b >> 24) , (c >> 24) ) + - Sub3((a >> 16) & 0xff, (b >> 16) & 0xff, (c >> 16) & 0xff) + - Sub3((a >> 8) & 0xff, (b >> 8) & 0xff, (c >> 8) & 0xff) + - Sub3((a ) & 0xff, (b ) & 0xff, (c ) & 0xff); - - return (pa_minus_pb <= 0) ? a : b; -} - -//------------------------------------------------------------------------------ -// Predictors - -static uint32_t Predictor0(uint32_t left, const uint32_t* const top) { - (void)top; - (void)left; - return ARGB_BLACK; -} -static uint32_t Predictor1(uint32_t left, const uint32_t* const top) { - (void)top; - return left; -} -static uint32_t Predictor2(uint32_t left, const uint32_t* const top) { - (void)left; - return top[0]; -} -static uint32_t Predictor3(uint32_t left, const uint32_t* const top) { - (void)left; - return top[1]; -} -static uint32_t Predictor4(uint32_t left, const uint32_t* const top) { - (void)left; - return top[-1]; -} -static uint32_t Predictor5(uint32_t left, const uint32_t* const top) { - const uint32_t pred = Average3(left, top[0], top[1]); - return pred; -} -static uint32_t Predictor6(uint32_t left, const uint32_t* const top) { - const uint32_t pred = Average2(left, top[-1]); - return pred; -} -static uint32_t Predictor7(uint32_t left, const uint32_t* const top) { - const uint32_t pred = Average2(left, top[0]); - return pred; -} -static uint32_t Predictor8(uint32_t left, const uint32_t* const top) { - const uint32_t pred = Average2(top[-1], top[0]); - (void)left; - return pred; -} -static uint32_t Predictor9(uint32_t left, const uint32_t* const top) { - const uint32_t pred = Average2(top[0], top[1]); - (void)left; - return pred; -} -static uint32_t Predictor10(uint32_t left, const uint32_t* const top) { - const uint32_t pred = Average4(left, top[-1], top[0], top[1]); - return pred; -} -static uint32_t Predictor11(uint32_t left, const uint32_t* const top) { - const uint32_t pred = Select(top[0], left, top[-1]); - return pred; -} -static uint32_t Predictor12(uint32_t left, const uint32_t* const top) { - const uint32_t pred = ClampedAddSubtractFull(left, top[0], top[-1]); - return pred; -} -static uint32_t Predictor13(uint32_t left, const uint32_t* const top) { - const uint32_t pred = ClampedAddSubtractHalf(left, top[0], top[-1]); - return pred; -} - -typedef uint32_t (*PredictorFunc)(uint32_t left, const uint32_t* const top); -static const PredictorFunc kPredictors[16] = { - Predictor0, Predictor1, Predictor2, Predictor3, - Predictor4, Predictor5, Predictor6, Predictor7, - Predictor8, Predictor9, Predictor10, Predictor11, - Predictor12, Predictor13, - Predictor0, Predictor0 // <- padding security sentinels -}; - -// TODO(vikasa): Replace 256 etc with defines. -static float PredictionCostSpatial(const int* counts, - int weight_0, double exp_val) { - const int significant_symbols = 16; - const double exp_decay_factor = 0.6; - double bits = weight_0 * counts[0]; - int i; - for (i = 1; i < significant_symbols; ++i) { - bits += exp_val * (counts[i] + counts[256 - i]); - exp_val *= exp_decay_factor; - } - return (float)(-0.1 * bits); -} - -// Compute the Shanon's entropy: Sum(p*log2(p)) -static float ShannonEntropy(const int* const array, int n) { - int i; - float retval = 0.f; - int sum = 0; - for (i = 0; i < n; ++i) { - if (array[i] != 0) { - sum += array[i]; - retval -= VP8LFastSLog2(array[i]); - } - } - retval += VP8LFastSLog2(sum); - return retval; -} - -static float PredictionCostSpatialHistogram(int accumulated[4][256], - int tile[4][256]) { - int i; - int k; - int combo[256]; - double retval = 0; - for (i = 0; i < 4; ++i) { - const double exp_val = 0.94; - retval += PredictionCostSpatial(&tile[i][0], 1, exp_val); - retval += ShannonEntropy(&tile[i][0], 256); - for (k = 0; k < 256; ++k) { - combo[k] = accumulated[i][k] + tile[i][k]; - } - retval += ShannonEntropy(&combo[0], 256); - } - return (float)retval; -} - -static int GetBestPredictorForTile(int width, int height, - int tile_x, int tile_y, int bits, - int accumulated[4][256], - const uint32_t* const argb_scratch) { - const int kNumPredModes = 14; - const int col_start = tile_x << bits; - const int row_start = tile_y << bits; - const int tile_size = 1 << bits; - const int ymax = (tile_size <= height - row_start) ? - tile_size : height - row_start; - const int xmax = (tile_size <= width - col_start) ? - tile_size : width - col_start; - int histo[4][256]; - float best_diff = MAX_DIFF_COST; - int best_mode = 0; - - int mode; - for (mode = 0; mode < kNumPredModes; ++mode) { - const uint32_t* current_row = argb_scratch; - const PredictorFunc pred_func = kPredictors[mode]; - float cur_diff; - int y; - memset(&histo[0][0], 0, sizeof(histo)); - for (y = 0; y < ymax; ++y) { - int x; - const int row = row_start + y; - const uint32_t* const upper_row = current_row; - current_row = upper_row + width; - for (x = 0; x < xmax; ++x) { - const int col = col_start + x; - uint32_t predict; - uint32_t predict_diff; - if (row == 0) { - predict = (col == 0) ? ARGB_BLACK : current_row[col - 1]; // Left. - } else if (col == 0) { - predict = upper_row[col]; // Top. - } else { - predict = pred_func(current_row[col - 1], upper_row + col); - } - predict_diff = VP8LSubPixels(current_row[col], predict); - ++histo[0][predict_diff >> 24]; - ++histo[1][((predict_diff >> 16) & 0xff)]; - ++histo[2][((predict_diff >> 8) & 0xff)]; - ++histo[3][(predict_diff & 0xff)]; - } - } - cur_diff = PredictionCostSpatialHistogram(accumulated, histo); - if (cur_diff < best_diff) { - best_diff = cur_diff; - best_mode = mode; - } - } - - return best_mode; -} - -static void CopyTileWithPrediction(int width, int height, - int tile_x, int tile_y, int bits, int mode, - const uint32_t* const argb_scratch, - uint32_t* const argb) { - const int col_start = tile_x << bits; - const int row_start = tile_y << bits; - const int tile_size = 1 << bits; - const int ymax = (tile_size <= height - row_start) ? - tile_size : height - row_start; - const int xmax = (tile_size <= width - col_start) ? - tile_size : width - col_start; - const PredictorFunc pred_func = kPredictors[mode]; - const uint32_t* current_row = argb_scratch; - - int y; - for (y = 0; y < ymax; ++y) { - int x; - const int row = row_start + y; - const uint32_t* const upper_row = current_row; - current_row = upper_row + width; - for (x = 0; x < xmax; ++x) { - const int col = col_start + x; - const int pix = row * width + col; - uint32_t predict; - if (row == 0) { - predict = (col == 0) ? ARGB_BLACK : current_row[col - 1]; // Left. - } else if (col == 0) { - predict = upper_row[col]; // Top. - } else { - predict = pred_func(current_row[col - 1], upper_row + col); - } - argb[pix] = VP8LSubPixels(current_row[col], predict); - } - } -} - -void VP8LResidualImage(int width, int height, int bits, - uint32_t* const argb, uint32_t* const argb_scratch, - uint32_t* const image) { - const int max_tile_size = 1 << bits; - const int tiles_per_row = VP8LSubSampleSize(width, bits); - const int tiles_per_col = VP8LSubSampleSize(height, bits); - uint32_t* const upper_row = argb_scratch; - uint32_t* const current_tile_rows = argb_scratch + width; - int tile_y; - int histo[4][256]; - memset(histo, 0, sizeof(histo)); - for (tile_y = 0; tile_y < tiles_per_col; ++tile_y) { - const int tile_y_offset = tile_y * max_tile_size; - const int this_tile_height = - (tile_y < tiles_per_col - 1) ? max_tile_size : height - tile_y_offset; - int tile_x; - if (tile_y > 0) { - memcpy(upper_row, current_tile_rows + (max_tile_size - 1) * width, - width * sizeof(*upper_row)); - } - memcpy(current_tile_rows, &argb[tile_y_offset * width], - this_tile_height * width * sizeof(*current_tile_rows)); - for (tile_x = 0; tile_x < tiles_per_row; ++tile_x) { - int pred; - int y; - const int tile_x_offset = tile_x * max_tile_size; - int all_x_max = tile_x_offset + max_tile_size; - if (all_x_max > width) { - all_x_max = width; - } - pred = GetBestPredictorForTile(width, height, tile_x, tile_y, bits, histo, - argb_scratch); - image[tile_y * tiles_per_row + tile_x] = 0xff000000u | (pred << 8); - CopyTileWithPrediction(width, height, tile_x, tile_y, bits, pred, - argb_scratch, argb); - for (y = 0; y < max_tile_size; ++y) { - int ix; - int all_x; - int all_y = tile_y_offset + y; - if (all_y >= height) { - break; - } - ix = all_y * width + tile_x_offset; - for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) { - const uint32_t a = argb[ix]; - ++histo[0][a >> 24]; - ++histo[1][((a >> 16) & 0xff)]; - ++histo[2][((a >> 8) & 0xff)]; - ++histo[3][(a & 0xff)]; - } - } - } - } -} - -// Inverse prediction. -static void PredictorInverseTransform(const VP8LTransform* const transform, - int y_start, int y_end, uint32_t* data) { - const int width = transform->xsize_; - if (y_start == 0) { // First Row follows the L (mode=1) mode. - int x; - const uint32_t pred0 = Predictor0(data[-1], NULL); - AddPixelsEq(data, pred0); - for (x = 1; x < width; ++x) { - const uint32_t pred1 = Predictor1(data[x - 1], NULL); - AddPixelsEq(data + x, pred1); - } - data += width; - ++y_start; - } - - { - int y = y_start; - const int mask = (1 << transform->bits_) - 1; - const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_); - const uint32_t* pred_mode_base = - transform->data_ + (y >> transform->bits_) * tiles_per_row; - - while (y < y_end) { - int x; - const uint32_t pred2 = Predictor2(data[-1], data - width); - const uint32_t* pred_mode_src = pred_mode_base; - PredictorFunc pred_func; - - // First pixel follows the T (mode=2) mode. - AddPixelsEq(data, pred2); - - // .. the rest: - pred_func = kPredictors[((*pred_mode_src++) >> 8) & 0xf]; - for (x = 1; x < width; ++x) { - uint32_t pred; - if ((x & mask) == 0) { // start of tile. Read predictor function. - pred_func = kPredictors[((*pred_mode_src++) >> 8) & 0xf]; - } - pred = pred_func(data[x - 1], data + x - width); - AddPixelsEq(data + x, pred); - } - data += width; - ++y; - if ((y & mask) == 0) { // Use the same mask, since tiles are squares. - pred_mode_base += tiles_per_row; - } - } - } -} - -void VP8LSubtractGreenFromBlueAndRed(uint32_t* argb_data, int num_pixs) { - int i; - for (i = 0; i < num_pixs; ++i) { - const uint32_t argb = argb_data[i]; - const uint32_t green = (argb >> 8) & 0xff; - const uint32_t new_r = (((argb >> 16) & 0xff) - green) & 0xff; - const uint32_t new_b = ((argb & 0xff) - green) & 0xff; - argb_data[i] = (argb & 0xff00ff00) | (new_r << 16) | new_b; - } -} - -// Add green to blue and red channels (i.e. perform the inverse transform of -// 'subtract green'). -static void AddGreenToBlueAndRed(const VP8LTransform* const transform, - int y_start, int y_end, uint32_t* data) { - const int width = transform->xsize_; - const uint32_t* const data_end = data + (y_end - y_start) * width; - while (data < data_end) { - const uint32_t argb = *data; - // "* 0001001u" is equivalent to "(green << 16) + green)" - const uint32_t green = ((argb >> 8) & 0xff); - uint32_t red_blue = (argb & 0x00ff00ffu); - red_blue += (green << 16) | green; - red_blue &= 0x00ff00ffu; - *data++ = (argb & 0xff00ff00u) | red_blue; - } -} - -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_; -} Multipliers; - -static WEBP_INLINE void MultipliersClear(Multipliers* m) { - m->green_to_red_ = 0; - m->green_to_blue_ = 0; - m->red_to_blue_ = 0; -} - -static WEBP_INLINE uint32_t ColorTransformDelta(int8_t color_pred, - int8_t color) { - return (uint32_t)((int)(color_pred) * color) >> 5; -} - -static WEBP_INLINE void ColorCodeToMultipliers(uint32_t color_code, - Multipliers* const m) { - m->green_to_red_ = (color_code >> 0) & 0xff; - m->green_to_blue_ = (color_code >> 8) & 0xff; - m->red_to_blue_ = (color_code >> 16) & 0xff; -} - -static WEBP_INLINE uint32_t MultipliersToColorCode(Multipliers* const m) { - return 0xff000000u | - ((uint32_t)(m->red_to_blue_) << 16) | - ((uint32_t)(m->green_to_blue_) << 8) | - m->green_to_red_; -} - -static WEBP_INLINE uint32_t TransformColor(const Multipliers* const m, - uint32_t argb, int inverse) { - const uint32_t green = argb >> 8; - const uint32_t red = argb >> 16; - uint32_t new_red = red; - uint32_t new_blue = argb; - - if (inverse) { - new_red += ColorTransformDelta(m->green_to_red_, green); - new_red &= 0xff; - new_blue += ColorTransformDelta(m->green_to_blue_, green); - new_blue += ColorTransformDelta(m->red_to_blue_, new_red); - new_blue &= 0xff; - } else { - new_red -= ColorTransformDelta(m->green_to_red_, green); - new_red &= 0xff; - new_blue -= ColorTransformDelta(m->green_to_blue_, green); - new_blue -= ColorTransformDelta(m->red_to_blue_, red); - new_blue &= 0xff; - } - return (argb & 0xff00ff00u) | (new_red << 16) | (new_blue); -} - -static WEBP_INLINE int SkipRepeatedPixels(const uint32_t* const argb, - int ix, int xsize) { - const uint32_t v = argb[ix]; - if (ix >= xsize + 3) { - if (v == argb[ix - xsize] && - argb[ix - 1] == argb[ix - xsize - 1] && - argb[ix - 2] == argb[ix - xsize - 2] && - argb[ix - 3] == argb[ix - xsize - 3]) { - return 1; - } - return v == argb[ix - 3] && v == argb[ix - 2] && v == argb[ix - 1]; - } else if (ix >= 3) { - return v == argb[ix - 3] && v == argb[ix - 2] && v == argb[ix - 1]; - } - return 0; -} - -static float PredictionCostCrossColor(const int accumulated[256], - const int counts[256]) { - // Favor low entropy, locally and globally. - int i; - int combo[256]; - for (i = 0; i < 256; ++i) { - combo[i] = accumulated[i] + counts[i]; - } - return ShannonEntropy(combo, 256) + - ShannonEntropy(counts, 256) + - PredictionCostSpatial(counts, 3, 2.4); // Favor small absolute values. -} - -static Multipliers GetBestColorTransformForTile( - int tile_x, int tile_y, int bits, - Multipliers prevX, - Multipliers prevY, - int step, int xsize, int ysize, - int* accumulated_red_histo, - int* accumulated_blue_histo, - const uint32_t* const argb) { - float best_diff = MAX_DIFF_COST; - float cur_diff; - const int halfstep = step / 2; - const int max_tile_size = 1 << bits; - const int tile_y_offset = tile_y * max_tile_size; - const int tile_x_offset = tile_x * max_tile_size; - int green_to_red; - int green_to_blue; - int red_to_blue; - int all_x_max = tile_x_offset + max_tile_size; - int all_y_max = tile_y_offset + max_tile_size; - Multipliers best_tx; - MultipliersClear(&best_tx); - if (all_x_max > xsize) { - all_x_max = xsize; - } - if (all_y_max > ysize) { - all_y_max = ysize; - } - for (green_to_red = -64; green_to_red <= 64; green_to_red += halfstep) { - int histo[256] = { 0 }; - int all_y; - Multipliers tx; - MultipliersClear(&tx); - tx.green_to_red_ = green_to_red & 0xff; - - for (all_y = tile_y_offset; all_y < all_y_max; ++all_y) { - uint32_t predict; - int ix = all_y * xsize + tile_x_offset; - int all_x; - for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) { - if (SkipRepeatedPixels(argb, ix, xsize)) { - continue; - } - predict = TransformColor(&tx, argb[ix], 0); - ++histo[(predict >> 16) & 0xff]; // red. - } - } - cur_diff = PredictionCostCrossColor(&accumulated_red_histo[0], &histo[0]); - if (tx.green_to_red_ == prevX.green_to_red_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if (tx.green_to_red_ == prevY.green_to_red_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if (tx.green_to_red_ == 0) { - cur_diff -= 3; - } - if (cur_diff < best_diff) { - best_diff = cur_diff; - best_tx = tx; - } - } - best_diff = MAX_DIFF_COST; - green_to_red = best_tx.green_to_red_; - for (green_to_blue = -32; green_to_blue <= 32; green_to_blue += step) { - for (red_to_blue = -32; red_to_blue <= 32; red_to_blue += step) { - int all_y; - int histo[256] = { 0 }; - Multipliers tx; - tx.green_to_red_ = green_to_red; - tx.green_to_blue_ = green_to_blue; - tx.red_to_blue_ = red_to_blue; - for (all_y = tile_y_offset; all_y < all_y_max; ++all_y) { - uint32_t predict; - int all_x; - int ix = all_y * xsize + tile_x_offset; - for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) { - if (SkipRepeatedPixels(argb, ix, xsize)) { - continue; - } - predict = TransformColor(&tx, argb[ix], 0); - ++histo[predict & 0xff]; // blue. - } - } - cur_diff = - PredictionCostCrossColor(&accumulated_blue_histo[0], &histo[0]); - if (tx.green_to_blue_ == prevX.green_to_blue_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if (tx.green_to_blue_ == prevY.green_to_blue_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if (tx.red_to_blue_ == prevX.red_to_blue_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if (tx.red_to_blue_ == prevY.red_to_blue_) { - cur_diff -= 3; // favor keeping the areas locally similar - } - if (tx.green_to_blue_ == 0) { - cur_diff -= 3; - } - if (tx.red_to_blue_ == 0) { - cur_diff -= 3; - } - if (cur_diff < best_diff) { - best_diff = cur_diff; - best_tx = tx; - } - } - } - return best_tx; -} - -static void CopyTileWithColorTransform(int xsize, int ysize, - int tile_x, int tile_y, int bits, - Multipliers color_transform, - uint32_t* const argb) { - int y; - int xscan = 1 << bits; - int yscan = 1 << bits; - tile_x <<= bits; - tile_y <<= bits; - if (xscan > xsize - tile_x) { - xscan = xsize - tile_x; - } - if (yscan > ysize - tile_y) { - yscan = ysize - tile_y; - } - yscan += tile_y; - for (y = tile_y; y < yscan; ++y) { - int ix = y * xsize + tile_x; - const int end_ix = ix + xscan; - for (; ix < end_ix; ++ix) { - argb[ix] = TransformColor(&color_transform, argb[ix], 0); - } - } -} - -void VP8LColorSpaceTransform(int width, int height, int bits, int step, - uint32_t* const argb, uint32_t* image) { - const int max_tile_size = 1 << bits; - int tile_xsize = VP8LSubSampleSize(width, bits); - int tile_ysize = VP8LSubSampleSize(height, bits); - int accumulated_red_histo[256] = { 0 }; - int accumulated_blue_histo[256] = { 0 }; - int tile_y; - int tile_x; - Multipliers prevX; - Multipliers prevY; - MultipliersClear(&prevY); - MultipliersClear(&prevX); - for (tile_y = 0; tile_y < tile_ysize; ++tile_y) { - for (tile_x = 0; tile_x < tile_xsize; ++tile_x) { - Multipliers color_transform; - int all_x_max; - int y; - const int tile_y_offset = tile_y * max_tile_size; - const int tile_x_offset = tile_x * max_tile_size; - if (tile_y != 0) { - ColorCodeToMultipliers(image[tile_y * tile_xsize + tile_x - 1], &prevX); - ColorCodeToMultipliers(image[(tile_y - 1) * tile_xsize + tile_x], - &prevY); - } else if (tile_x != 0) { - ColorCodeToMultipliers(image[tile_y * tile_xsize + tile_x - 1], &prevX); - } - color_transform = - GetBestColorTransformForTile(tile_x, tile_y, bits, - prevX, prevY, - step, width, height, - &accumulated_red_histo[0], - &accumulated_blue_histo[0], - argb); - image[tile_y * tile_xsize + tile_x] = - MultipliersToColorCode(&color_transform); - CopyTileWithColorTransform(width, height, tile_x, tile_y, bits, - color_transform, argb); - - // Gather accumulated histogram data. - all_x_max = tile_x_offset + max_tile_size; - if (all_x_max > width) { - all_x_max = width; - } - for (y = 0; y < max_tile_size; ++y) { - int ix; - int all_x; - int all_y = tile_y_offset + y; - if (all_y >= height) { - break; - } - ix = all_y * width + tile_x_offset; - for (all_x = tile_x_offset; all_x < all_x_max; ++all_x, ++ix) { - if (ix >= 2 && - argb[ix] == argb[ix - 2] && - argb[ix] == argb[ix - 1]) { - continue; // repeated pixels are handled by backward references - } - if (ix >= width + 2 && - argb[ix - 2] == argb[ix - width - 2] && - argb[ix - 1] == argb[ix - width - 1] && - argb[ix] == argb[ix - width]) { - continue; // repeated pixels are handled by backward references - } - ++accumulated_red_histo[(argb[ix] >> 16) & 0xff]; - ++accumulated_blue_histo[argb[ix] & 0xff]; - } - } - } - } -} - -// Color space inverse transform. -static void ColorSpaceInverseTransform(const VP8LTransform* const transform, - int y_start, int y_end, uint32_t* data) { - const int width = transform->xsize_; - const int mask = (1 << transform->bits_) - 1; - const int tiles_per_row = VP8LSubSampleSize(width, transform->bits_); - int y = y_start; - const uint32_t* pred_row = - transform->data_ + (y >> transform->bits_) * tiles_per_row; - - while (y < y_end) { - const uint32_t* pred = pred_row; - Multipliers m = { 0, 0, 0 }; - int x; - - for (x = 0; x < width; ++x) { - if ((x & mask) == 0) ColorCodeToMultipliers(*pred++, &m); - data[x] = TransformColor(&m, data[x], 1); - } - data += width; - ++y; - if ((y & mask) == 0) pred_row += tiles_per_row;; - } -} - -// Separate out pixels packed together using pixel-bundling. -static void ColorIndexInverseTransform( - const VP8LTransform* const transform, - int y_start, int y_end, const uint32_t* src, uint32_t* dst) { - int y; - const int bits_per_pixel = 8 >> transform->bits_; - const int width = transform->xsize_; - const uint32_t* const color_map = transform->data_; - if (bits_per_pixel < 8) { - const int pixels_per_byte = 1 << transform->bits_; - const int count_mask = pixels_per_byte - 1; - const uint32_t bit_mask = (1 << bits_per_pixel) - 1; - for (y = y_start; y < y_end; ++y) { - uint32_t packed_pixels = 0; - int x; - for (x = 0; x < width; ++x) { - // We need to load fresh 'packed_pixels' once every 'pixels_per_byte' - // increments of x. Fortunately, pixels_per_byte is a power of 2, so - // can just use a mask for that, instead of decrementing a counter. - if ((x & count_mask) == 0) packed_pixels = ((*src++) >> 8) & 0xff; - *dst++ = color_map[packed_pixels & bit_mask]; - packed_pixels >>= bits_per_pixel; - } - } - } else { - for (y = y_start; y < y_end; ++y) { - int x; - for (x = 0; x < width; ++x) { - *dst++ = color_map[((*src++) >> 8) & 0xff]; - } - } - } -} - -void VP8LInverseTransform(const VP8LTransform* const transform, - int row_start, int row_end, - const uint32_t* const in, uint32_t* const out) { - assert(row_start < row_end); - assert(row_end <= transform->ysize_); - switch (transform->type_) { - case SUBTRACT_GREEN: - AddGreenToBlueAndRed(transform, row_start, row_end, out); - break; - case PREDICTOR_TRANSFORM: - PredictorInverseTransform(transform, row_start, row_end, out); - if (row_end != transform->ysize_) { - // The last predicted row in this iteration will be the top-pred row - // for the first row in next iteration. - const int width = transform->xsize_; - memcpy(out - width, out + (row_end - row_start - 1) * width, - width * sizeof(*out)); - } - break; - case CROSS_COLOR_TRANSFORM: - ColorSpaceInverseTransform(transform, row_start, row_end, out); - break; - case COLOR_INDEXING_TRANSFORM: - if (in == out && transform->bits_ > 0) { - // Move packed pixels to the end of unpacked region, so that unpacking - // can occur seamlessly. - // Also, note that this is the only transform that applies on - // the effective width of VP8LSubSampleSize(xsize_, bits_). All other - // transforms work on effective width of xsize_. - const int out_stride = (row_end - row_start) * transform->xsize_; - const int in_stride = (row_end - row_start) * - VP8LSubSampleSize(transform->xsize_, transform->bits_); - uint32_t* const src = out + out_stride - in_stride; - memmove(src, out, in_stride * sizeof(*src)); - ColorIndexInverseTransform(transform, row_start, row_end, src, out); - } else { - ColorIndexInverseTransform(transform, row_start, row_end, in, out); - } - break; - } -} - -//------------------------------------------------------------------------------ -// Color space conversion. - -static int is_big_endian(void) { - static const union { - uint16_t w; - uint8_t b[2]; - } tmp = { 1 }; - return (tmp.b[0] != 1); -} - -static void ConvertBGRAToRGB(const uint32_t* src, - int num_pixels, uint8_t* dst) { - const uint32_t* const src_end = src + num_pixels; - while (src < src_end) { - const uint32_t argb = *src++; - *dst++ = (argb >> 16) & 0xff; - *dst++ = (argb >> 8) & 0xff; - *dst++ = (argb >> 0) & 0xff; - } -} - -static void ConvertBGRAToRGBA(const uint32_t* src, - int num_pixels, uint8_t* dst) { - const uint32_t* const src_end = src + num_pixels; - while (src < src_end) { - const uint32_t argb = *src++; - *dst++ = (argb >> 16) & 0xff; - *dst++ = (argb >> 8) & 0xff; - *dst++ = (argb >> 0) & 0xff; - *dst++ = (argb >> 24) & 0xff; - } -} - -static void ConvertBGRAToRGBA4444(const uint32_t* src, - int num_pixels, uint8_t* dst) { - const uint32_t* const src_end = src + num_pixels; - while (src < src_end) { - const uint32_t argb = *src++; - *dst++ = ((argb >> 16) & 0xf0) | ((argb >> 12) & 0xf); - *dst++ = ((argb >> 0) & 0xf0) | ((argb >> 28) & 0xf); - } -} - -static void ConvertBGRAToRGB565(const uint32_t* src, - int num_pixels, uint8_t* dst) { - const uint32_t* const src_end = src + num_pixels; - while (src < src_end) { - const uint32_t argb = *src++; - *dst++ = ((argb >> 16) & 0xf8) | ((argb >> 13) & 0x7); - *dst++ = ((argb >> 5) & 0xe0) | ((argb >> 3) & 0x1f); - } -} - -static void ConvertBGRAToBGR(const uint32_t* src, - int num_pixels, uint8_t* dst) { - const uint32_t* const src_end = src + num_pixels; - while (src < src_end) { - const uint32_t argb = *src++; - *dst++ = (argb >> 0) & 0xff; - *dst++ = (argb >> 8) & 0xff; - *dst++ = (argb >> 16) & 0xff; - } -} - -static void CopyOrSwap(const uint32_t* src, int num_pixels, uint8_t* dst, - int swap_on_big_endian) { - if (is_big_endian() == swap_on_big_endian) { - const uint32_t* const src_end = src + num_pixels; - while (src < src_end) { - uint32_t argb = *src++; -#if !defined(__BIG_ENDIAN__) && (defined(__i386__) || defined(__x86_64__)) - __asm__ volatile("bswap %0" : "=r"(argb) : "0"(argb)); - *(uint32_t*)dst = argb; - dst += sizeof(argb); -#elif !defined(__BIG_ENDIAN__) && defined(_MSC_VER) - argb = _byteswap_ulong(argb); - *(uint32_t*)dst = argb; - dst += sizeof(argb); -#else - *dst++ = (argb >> 24) & 0xff; - *dst++ = (argb >> 16) & 0xff; - *dst++ = (argb >> 8) & 0xff; - *dst++ = (argb >> 0) & 0xff; -#endif - } - } else { - memcpy(dst, src, num_pixels * sizeof(*src)); - } -} - -void VP8LConvertFromBGRA(const uint32_t* const in_data, int num_pixels, - WEBP_CSP_MODE out_colorspace, uint8_t* const rgba) { - switch (out_colorspace) { - case MODE_RGB: - ConvertBGRAToRGB(in_data, num_pixels, rgba); - break; - case MODE_RGBA: - ConvertBGRAToRGBA(in_data, num_pixels, rgba); - break; - case MODE_rgbA: - ConvertBGRAToRGBA(in_data, num_pixels, rgba); - WebPApplyAlphaMultiply(rgba, 0, num_pixels, 1, 0); - break; - case MODE_BGR: - ConvertBGRAToBGR(in_data, num_pixels, rgba); - break; - case MODE_BGRA: - CopyOrSwap(in_data, num_pixels, rgba, 1); - break; - case MODE_bgrA: - CopyOrSwap(in_data, num_pixels, rgba, 1); - WebPApplyAlphaMultiply(rgba, 0, num_pixels, 1, 0); - break; - case MODE_ARGB: - CopyOrSwap(in_data, num_pixels, rgba, 0); - break; - case MODE_Argb: - CopyOrSwap(in_data, num_pixels, rgba, 0); - WebPApplyAlphaMultiply(rgba, 1, num_pixels, 1, 0); - break; - case MODE_RGBA_4444: - ConvertBGRAToRGBA4444(in_data, num_pixels, rgba); - break; - case MODE_rgbA_4444: - ConvertBGRAToRGBA4444(in_data, num_pixels, rgba); - WebPApplyAlphaMultiply4444(rgba, num_pixels, 1, 0); - break; - case MODE_RGB_565: - ConvertBGRAToRGB565(in_data, num_pixels, rgba); - break; - default: - assert(0); // Code flow should not reach here. - } -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/dsp/lossless.h b/drivers/webpold/dsp/lossless.h deleted file mode 100644 index 7c7d5555ed..0000000000 --- a/drivers/webpold/dsp/lossless.h +++ /dev/null @@ -1,82 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Image transforms and color space conversion methods for lossless decoder. -// -// Authors: Vikas Arora (vikaas.arora@gmail.com) -// Jyrki Alakuijala (jyrki@google.com) - -#ifndef WEBP_DSP_LOSSLESS_H_ -#define WEBP_DSP_LOSSLESS_H_ - -#include "../types.h" -#include "../decode.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Image transforms. - -struct VP8LTransform; // Defined in dec/vp8li.h. - -// Performs inverse transform of data given transform information, start and end -// rows. Transform will be applied to rows [row_start, row_end[. -// The *in and *out pointers refer to source and destination data respectively -// corresponding to the intermediate row (row_start). -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. - -// 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); - -//------------------------------------------------------------------------------ -// Misc methods. - -// Computes sampled size of 'size' when sampling using 'sampling bits'. -static WEBP_INLINE uint32_t VP8LSubSampleSize(uint32_t size, - uint32_t sampling_bits) { - return (size + (1 << sampling_bits) - 1) >> sampling_bits; -} - -// Faster logarithm for integers, with the property of log2(0) == 0. -float VP8LFastLog2(int v); -// Fast calculation of v * log2(v) for integer input. -static WEBP_INLINE float VP8LFastSLog2(int v) { return VP8LFastLog2(v) * v; } - -// In-place difference of each component with mod 256. -static WEBP_INLINE uint32_t VP8LSubPixels(uint32_t a, uint32_t b) { - const uint32_t alpha_and_green = - 0x00ff00ffu + (a & 0xff00ff00u) - (b & 0xff00ff00u); - const uint32_t red_and_blue = - 0xff00ff00u + (a & 0x00ff00ffu) - (b & 0x00ff00ffu); - return (alpha_and_green & 0xff00ff00u) | (red_and_blue & 0x00ff00ffu); -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif // WEBP_DSP_LOSSLESS_H_ diff --git a/drivers/webpold/dsp/upsampling.c b/drivers/webpold/dsp/upsampling.c deleted file mode 100644 index 4855eb1432..0000000000 --- a/drivers/webpold/dsp/upsampling.c +++ /dev/null @@ -1,357 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// YUV to RGB upsampling functions. -// -// Author: somnath@google.com (Somnath Banerjee) - -#include "./dsp.h" -#include "./yuv.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Fancy upsampler - -#ifdef FANCY_UPSAMPLING - -// Fancy upsampling functions to convert YUV to RGB -WebPUpsampleLinePairFunc WebPUpsamplers[MODE_LAST]; - -// Given samples laid out in a square as: -// [a b] -// [c d] -// we interpolate u/v as: -// ([9*a + 3*b + 3*c + d 3*a + 9*b + 3*c + d] + [8 8]) / 16 -// ([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 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 x; \ - 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) { \ - const uint32_t uv0 = (3 * tl_uv + l_uv + 0x00020002u) >> 2; \ - FUNC(top_y[0], uv0 & 0xff, (uv0 >> 16), top_dst); \ - } \ - if (bottom_y) { \ - const uint32_t uv0 = (3 * l_uv + tl_uv + 0x00020002u) >> 2; \ - FUNC(bottom_y[0], uv0 & 0xff, (uv0 >> 16), bottom_dst); \ - } \ - for (x = 1; x <= last_pixel_pair; ++x) { \ - const uint32_t t_uv = LOAD_UV(top_u[x], top_v[x]); /* top sample */ \ - const uint32_t uv = LOAD_UV(cur_u[x], cur_v[x]); /* sample */ \ - /* precompute invariant values associated with first and second diagonals*/\ - 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), \ - top_dst + (2 * x - 1) * XSTEP); \ - FUNC(top_y[2 * x - 0], uv1 & 0xff, (uv1 >> 16), \ - top_dst + (2 * x - 0) * XSTEP); \ - } \ - if (bottom_y) { \ - 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), \ - bottom_dst + (2 * x - 1) * XSTEP); \ - FUNC(bottom_y[2 * x + 0], uv1 & 0xff, (uv1 >> 16), \ - bottom_dst + (2 * x + 0) * XSTEP); \ - } \ - tl_uv = t_uv; \ - 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) { \ - 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); \ - } \ - } \ -} - -// All variants implemented. -UPSAMPLE_FUNC(UpsampleRgbLinePair, VP8YuvToRgb, 3) -UPSAMPLE_FUNC(UpsampleBgrLinePair, VP8YuvToBgr, 3) -UPSAMPLE_FUNC(UpsampleRgbaLinePair, VP8YuvToRgba, 4) -UPSAMPLE_FUNC(UpsampleBgraLinePair, VP8YuvToBgra, 4) -UPSAMPLE_FUNC(UpsampleArgbLinePair, VP8YuvToArgb, 4) -UPSAMPLE_FUNC(UpsampleRgba4444LinePair, VP8YuvToRgba4444, 2) -UPSAMPLE_FUNC(UpsampleRgb565LinePair, VP8YuvToRgb565, 2) - -#undef LOAD_UV -#undef UPSAMPLE_FUNC - -#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) -#define DUAL_SAMPLE_FUNC(FUNC_NAME, FUNC) \ -static void FUNC_NAME(const uint8_t* top_y, const uint8_t* bot_y, \ - const uint8_t* top_u, const uint8_t* top_v, \ - const uint8_t* bot_u, const uint8_t* bot_v, \ - uint8_t* top_dst, uint8_t* bot_dst, int len) { \ - const int half_len = len >> 1; \ - int x; \ - if (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); \ - } \ - if (len & 1) FUNC(top_y[2 * x + 0], top_u[x], top_v[x], top_dst + 8 * x); \ - } \ - if (bot_dst != NULL) { \ - for (x = 0; x < half_len; ++x) { \ - FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x + 0); \ - FUNC(bot_y[2 * x + 1], bot_u[x], bot_v[x], bot_dst + 8 * x + 4); \ - } \ - if (len & 1) FUNC(bot_y[2 * x + 0], bot_u[x], bot_v[x], bot_dst + 8 * x); \ - } \ -} - -DUAL_SAMPLE_FUNC(DualLineSamplerBGRA, VP8YuvToBgra) -DUAL_SAMPLE_FUNC(DualLineSamplerARGB, VP8YuvToArgb) -#undef DUAL_SAMPLE_FUNC - -#endif // !FANCY_UPSAMPLING - -WebPUpsampleLinePairFunc WebPGetLinePairConverter(int alpha_is_last) { - WebPInitUpsamplers(); - VP8YUVInit(); -#ifdef FANCY_UPSAMPLING - return WebPUpsamplers[alpha_is_last ? MODE_BGRA : MODE_ARGB]; -#else - return (alpha_is_last ? DualLineSamplerBGRA : DualLineSamplerARGB); -#endif -} - -//------------------------------------------------------------------------------ -// 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) { \ - 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) - -#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 -}; - -//------------------------------------------------------------------------------ -// Premultiplied modes - -// non dithered-modes - -// (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) -#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); - } - } - rgba += stride; - } -} -#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 - -void WebPInitUpsamplers(void) { -#ifdef FANCY_UPSAMPLING - WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePair; - WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePair; - WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePair; - WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePair; - WebPUpsamplers[MODE_ARGB] = UpsampleArgbLinePair; - WebPUpsamplers[MODE_RGBA_4444] = UpsampleRgba4444LinePair; - WebPUpsamplers[MODE_RGB_565] = UpsampleRgb565LinePair; - - // If defined, use CPUInfo() to overwrite some pointers with faster versions. - if (VP8GetCPUInfo != NULL) { -#if defined(WEBP_USE_SSE2) - if (VP8GetCPUInfo(kSSE2)) { - 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(); - } -#endif - } -#endif // FANCY_UPSAMPLING -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/dsp/upsampling_sse2.c b/drivers/webpold/dsp/upsampling_sse2.c deleted file mode 100644 index 8cb275a02b..0000000000 --- a/drivers/webpold/dsp/upsampling_sse2.c +++ /dev/null @@ -1,209 +0,0 @@ -// 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 YUV to RGB upsampling functions. -// -// Author: somnath@google.com (Somnath Banerjee) - -#include "./dsp.h" - -#if defined(WEBP_USE_SSE2) - -#include <assert.h> -#include <emmintrin.h> -#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 -// u = (9*a + 3*b + 3*c + d + 8) / 16 -// = (a + (a + 3*b + 3*c + d) / 8 + 1) / 2 -// = (a + m + 1) / 2 -// where m = (a + 3*b + 3*c + d) / 8 -// = ((a + b + c + d) / 2 + b + c) / 4 -// -// Let's say k = (a + b + c + d) / 4. -// We can compute k as -// k = (s + t + 1) / 2 - ((a^d) | (b^c) | (s^t)) & 1 -// where s = (a + d + 1) / 2 and t = (b + c + 1) / 2 -// -// Then m can be written as -// m = (k + t + 1) / 2 - (((b^c) & (s^t)) | (k^t)) & 1 - -// Computes out = (k + in + 1) / 2 - ((ij & (s^t)) | (k^in)) & 1 -#define GET_M(ij, in, out) do { \ - const __m128i tmp0 = _mm_avg_epu8(k, (in)); /* (k + in + 1) / 2 */ \ - const __m128i tmp1 = _mm_and_si128((ij), st); /* (ij) & (s^t) */ \ - const __m128i tmp2 = _mm_xor_si128(k, (in)); /* (k^in) */ \ - const __m128i tmp3 = _mm_or_si128(tmp1, tmp2); /* ((ij) & (s^t)) | (k^in) */\ - const __m128i tmp4 = _mm_and_si128(tmp3, one); /* & 1 -> lsb_correction */ \ - (out) = _mm_sub_epi8(tmp0, tmp4); /* (k + in + 1) / 2 - lsb_correction */ \ -} while (0) - -// pack and store two alterning 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); \ -} 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 s = _mm_avg_epu8(a, d); /* s = (a + d + 1) / 2 */ \ - const __m128i t = _mm_avg_epu8(b, c); /* t = (b + c + 1) / 2 */ \ - const __m128i st = _mm_xor_si128(s, t); /* st = s^t */ \ - \ - const __m128i ad = _mm_xor_si128(a, d); /* ad = a^d */ \ - const __m128i bc = _mm_xor_si128(b, c); /* bc = b^c */ \ - \ - const __m128i t1 = _mm_or_si128(ad, bc); /* (a^d) | (b^c) */ \ - const __m128i t2 = _mm_or_si128(t1, st); /* (a^d) | (b^c) | (s^t) */ \ - const __m128i t3 = _mm_and_si128(t2, one); /* (a^d) | (b^c) | (s^t) & 1 */ \ - const __m128i t4 = _mm_avg_epu8(s, t); \ - const __m128i k = _mm_sub_epi8(t4, t3); /* k = (a + b + c + d) / 4 */ \ - __m128i diag1, diag2; \ - \ - GET_M(bc, t, diag1); /* diag1 = (a + 3b + 3c + d) / 8 */ \ - 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]); \ -} - -// Turn the macro into a function for reducing code-size when non-critical -static void Upsample32Pixels(const uint8_t r1[], const uint8_t r2[], - uint8_t* const out) { - UPSAMPLE_32PIXELS(r1, r2, out); -} - -#define UPSAMPLE_LAST_BLOCK(tb, bb, num_pixels, out) { \ - uint8_t r1[17], r2[17]; \ - memcpy(r1, (tb), (num_pixels)); \ - memcpy(r2, (bb), (num_pixels)); \ - /* replicate last byte */ \ - memset(r1 + (num_pixels), r1[(num_pixels) - 1], 17 - (num_pixels)); \ - memset(r2 + (num_pixels), r2[(num_pixels) - 1], 17 - (num_pixels)); \ - /* using the shared function instead of the macro saves ~3k code size */ \ - Upsample32Pixels(r1, r2, out); \ -} - -#define CONVERT2RGB(FUNC, XSTEP, top_y, bottom_y, uv, \ - 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); \ - } \ - } \ - if (bottom_y) { \ - for (n = 0; n < (num_pixels); ++n) { \ - FUNC(bottom_y[(cur_x) + n], (uv)[64 + n], (uv)[64 + 32 + n], \ - bottom_dst + ((cur_x) + n) * XSTEP); \ - } \ - } \ -} - -#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 */ \ - 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; \ - \ - 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); \ - } \ - 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 (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; \ - } \ - \ - 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) - -#undef GET_M -#undef PACK_AND_STORE -#undef UPSAMPLE_32PIXELS -#undef UPSAMPLE_LAST_BLOCK -#undef CONVERT2RGB -#undef SSE2_UPSAMPLE_FUNC - -//------------------------------------------------------------------------------ - -extern WebPUpsampleLinePairFunc WebPUpsamplers[/* MODE_LAST */]; - -void WebPInitUpsamplersSSE2(void) { - WebPUpsamplers[MODE_RGB] = UpsampleRgbLinePairSSE2; - WebPUpsamplers[MODE_RGBA] = UpsampleRgbaLinePairSSE2; - WebPUpsamplers[MODE_BGR] = UpsampleBgrLinePairSSE2; - WebPUpsamplers[MODE_BGRA] = UpsampleBgraLinePairSSE2; -} - -void WebPInitPremultiplySSE2(void) { - WebPUpsamplers[MODE_rgbA] = UpsampleRgbaLinePairSSE2; - WebPUpsamplers[MODE_bgrA] = UpsampleBgraLinePairSSE2; -} - -#endif // FANCY_UPSAMPLING - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif // WEBP_USE_SSE2 diff --git a/drivers/webpold/dsp/yuv.c b/drivers/webpold/dsp/yuv.c deleted file mode 100644 index 7f05f9a3aa..0000000000 --- a/drivers/webpold/dsp/yuv.c +++ /dev/null @@ -1,52 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// YUV->RGB conversion function -// -// 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]; - -static int done = 0; - -static WEBP_INLINE uint8_t clip(int v, int max_value) { - return v < 0 ? 0 : v > max_value ? max_value : v; -} - -void VP8YUVInit(void) { - int i; - if (done) { - return; - } - for (i = 0; i < 256; ++i) { - VP8kVToR[i] = (89858 * (i - 128) + YUV_HALF) >> YUV_FIX; - VP8kUToG[i] = -22014 * (i - 128) + YUV_HALF; - VP8kVToG[i] = -45773 * (i - 128); - VP8kUToB[i] = (113618 * (i - 128) + YUV_HALF) >> YUV_FIX; - } - for (i = YUV_RANGE_MIN; i < YUV_RANGE_MAX; ++i) { - const int k = ((i - 16) * 76283 + YUV_HALF) >> YUV_FIX; - VP8kClip[i - YUV_RANGE_MIN] = clip(k, 255); - VP8kClip4Bits[i - YUV_RANGE_MIN] = clip((k + 8) >> 4, 15); - } - done = 1; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/dsp/yuv.h b/drivers/webpold/dsp/yuv.h deleted file mode 100644 index a569109c54..0000000000 --- a/drivers/webpold/dsp/yuv.h +++ /dev/null @@ -1,128 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// inline YUV<->RGB conversion function -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_DSP_YUV_H_ -#define WEBP_DSP_YUV_H_ - -#include "../dec/decode_vp8.h" - -//------------------------------------------------------------------------------ -// YUV -> RGB conversion - -#if defined(__cplusplus) || defined(c_plusplus) -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 -}; -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, - 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]; - rgb[1] = VP8kClip[y + g_off - YUV_RANGE_MIN]; - 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, - uint8_t* const bgr) { - const int r_off = VP8kVToR[v]; - const int g_off = (VP8kVToG[v] + VP8kUToG[u]) >> YUV_FIX; - const int b_off = VP8kUToB[u]; - bgr[0] = VP8kClip[y + b_off - YUV_RANGE_MIN]; - bgr[1] = VP8kClip[y + g_off - YUV_RANGE_MIN]; - bgr[2] = VP8kClip[y + r_off - YUV_RANGE_MIN]; -} - -static WEBP_INLINE void VP8YuvToBgra(uint8_t y, uint8_t u, uint8_t v, - uint8_t* const bgra) { - VP8YuvToBgr(y, u, v, bgra); - bgra[3] = 0xff; -} - -static WEBP_INLINE void VP8YuvToRgba(uint8_t y, uint8_t u, uint8_t v, - uint8_t* const rgba) { - VP8YuvToRgb(y, u, v, rgba); - rgba[3] = 0xff; -} - -// Must be called before everything, to initialize the tables. -void VP8YUVInit(void); - -//------------------------------------------------------------------------------ -// 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; -} - -static WEBP_INLINE int VP8RGBToY(int r, int g, int b) { - const int kRound = (1 << (YUV_FIX - 1)) + (16 << YUV_FIX); - const int luma = 16839 * r + 33059 * g + 6420 * b; - return (luma + kRound) >> 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 VP8RGBToV(int r, int g, int b) { - return VP8ClipUV(+28800 * r - 24116 * g - 4684 * b); -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_DSP_YUV_H_ */ diff --git a/drivers/webpold/enc/alpha.c b/drivers/webpold/enc/alpha.c deleted file mode 100644 index e554eb7f30..0000000000 --- a/drivers/webpold/enc/alpha.c +++ /dev/null @@ -1,330 +0,0 @@ -// 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 compression. -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <assert.h> -#include <stdlib.h> - -#include "./vp8enci.h" -#include "../utils/filters.h" -#include "../utils/quant_levels.h" -#include "../format_constants.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -// ----------------------------------------------------------------------------- -// Encodes the given alpha data via specified compression method 'method'. -// The pre-processing (quantization) is performed if 'quality' is less than 100. -// For such cases, the encoding is lossy. The valid range is [0, 100] for -// 'quality' and [0, 1] for 'method': -// 'method = 0' - No compression; -// 'method = 1' - Use lossless coder on the alpha plane only -// 'filter' values [0, 4] correspond to prediction modes none, horizontal, -// vertical & gradient filters. The prediction mode 4 will try all the -// prediction modes 0 to 3 and pick the best one. -// 'effort_level': specifies how much effort must be spent to try and reduce -// the compressed output size. In range 0 (quick) to 6 (slow). -// -// 'output' corresponds to the buffer containing compressed alpha data. -// This buffer is allocated by this method and caller should call -// free(*output) when done. -// 'output_size' corresponds to size of this compressed alpha buffer. -// -// Returns 1 on successfully encoding the alpha and -// 0 if either: -// invalid quality or method, or -// memory allocation for the compressed data fails. - -#include "../enc/vp8li.h" - -static int EncodeLossless(const uint8_t* const data, int width, int height, - int effort_level, // in [0..6] range - VP8BitWriter* const bw, - WebPAuxStats* const stats) { - int ok = 0; - WebPConfig config; - WebPPicture picture; - VP8LBitWriter tmp_bw; - - WebPPictureInit(&picture); - picture.width = width; - picture.height = height; - picture.use_argb = 1; - picture.stats = stats; - 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; - } - } - - 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; - - ok = VP8LBitWriterInit(&tmp_bw, (width * height) >> 3); - ok = ok && (VP8LEncodeStream(&config, &picture, &tmp_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); - } - VP8LBitWriterDestroy(&tmp_bw); - return ok && !bw->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) { - 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; - - assert((uint64_t)data_size == (uint64_t)width * height); // as per spec - assert(filter >= 0 && filter < WEBP_FILTER_LAST); - assert(method >= ALPHA_NO_COMPRESSION); - assert(method <= ALPHA_LOSSLESS_COMPRESSION); - 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); - 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); - } - 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 EncodeAlpha(VP8Encoder* const enc, - int quality, int method, int filter, - int effort_level, - uint8_t** const output, size_t* const output_size) { - const WebPPicture* const pic = enc->pic_; - const int width = pic->width; - const int height = pic->height; - - uint8_t* quant_alpha = NULL; - const size_t data_size = width * height; - uint64_t sse = 0; - int ok = 1; - const int reduce_levels = (quality < 100); - - // quick sanity checks - assert((uint64_t)data_size == (uint64_t)width * height); // as per spec - assert(enc != NULL && pic != NULL && pic->a != NULL); - assert(output != NULL && output_size != NULL); - assert(width > 0 && height > 0); - assert(pic->a_stride >= width); - assert(filter >= WEBP_FILTER_NONE && filter <= WEBP_FILTER_FAST); - - if (quality < 0 || quality > 100) { - return 0; - } - - if (method < ALPHA_NO_COMPRESSION || method > ALPHA_LOSSLESS_COMPRESSION) { - return 0; - } - - quant_alpha = (uint8_t*)malloc(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); - - if (reduce_levels) { // No Quantization required for 'quality = 100'. - // 16 alpha levels gives quite a low MSE w.r.t original alpha plane hence - // mapped to moderate quality 70. Hence Quality:[0, 70] -> Levels:[2, 16] - // and Quality:]70, 100] -> Levels:]16, 256]. - const int alpha_levels = (quality <= 70) ? (2 + quality / 5) - : (16 + (quality - 70) * 8); - ok = QuantizeLevels(quant_alpha, width, height, alpha_levels, &sse); - } - - 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; - } - - 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); - return ok; -} - - -//------------------------------------------------------------------------------ -// Main calls - -void VP8EncInitAlpha(VP8Encoder* const enc) { - enc->has_alpha_ = WebPPictureHasTransparency(enc->pic_); - enc->alpha_data_ = NULL; - enc->alpha_data_size_ = 0; -} - -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 (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_); - enc->alpha_data_ = NULL; - enc->alpha_data_size_ = 0; - enc->has_alpha_ = 0; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/analysis.c b/drivers/webpold/enc/analysis.c deleted file mode 100644 index 22cfb492e7..0000000000 --- a/drivers/webpold/enc/analysis.c +++ /dev/null @@ -1,364 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Macroblock analysis -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <stdlib.h> -#include <string.h> -#include <assert.h> - -#include "./vp8enci.h" -#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. - -static void SmoothSegmentMap(VP8Encoder* const enc) { - int n, x, y; - const int w = enc->mb_w_; - const int h = enc->mb_h_; - const int majority_cnt_3_x_3_grid = 5; - uint8_t* const tmp = (uint8_t*)WebPSafeMalloc((uint64_t)w * h, sizeof(*tmp)); - assert((uint64_t)(w * h) == (uint64_t)w * h); // no overflow, as per spec - - if (tmp == NULL) return; - for (y = 1; y < h - 1; ++y) { - for (x = 1; x < w - 1; ++x) { - int cnt[NUM_MB_SEGMENTS] = { 0 }; - const VP8MBInfo* const mb = &enc->mb_info_[x + w * y]; - int majority_seg = mb->segment_; - // Check the 8 neighbouring segment values. - cnt[mb[-w - 1].segment_]++; // top-left - cnt[mb[-w + 0].segment_]++; // top - cnt[mb[-w + 1].segment_]++; // top-right - cnt[mb[ - 1].segment_]++; // left - cnt[mb[ + 1].segment_]++; // right - cnt[mb[ w - 1].segment_]++; // bottom-left - cnt[mb[ w + 0].segment_]++; // bottom - cnt[mb[ w + 1].segment_]++; // bottom-right - for (n = 0; n < NUM_MB_SEGMENTS; ++n) { - if (cnt[n] >= majority_cnt_3_x_3_grid) { - majority_seg = n; - } - } - tmp[x + y * w] = majority_seg; - } - } - for (y = 1; y < h - 1; ++y) { - for (x = 1; x < w - 1; ++x) { - VP8MBInfo* const mb = &enc->mb_info_[x + w * y]; - mb->segment_ = tmp[x + y * w]; - } - } - free(tmp); -} - -//------------------------------------------------------------------------------ -// 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; - } -} - -static WEBP_INLINE int clip(int v, int m, int M) { - return v < m ? m : v > M ? M : v; -} - -static void SetSegmentAlphas(VP8Encoder* const enc, - const int centers[NUM_MB_SEGMENTS], - int mid) { - const int nb = enc->segment_hdr_.num_segments_; - int min = centers[0], max = centers[0]; - int n; - - if (nb > 1) { - for (n = 0; n < nb; ++n) { - if (min > centers[n]) min = centers[n]; - if (max < centers[n]) max = centers[n]; - } - } - if (max == min) max = min + 1; - assert(mid <= max && mid >= min); - for (n = 0; n < nb; ++n) { - const int alpha = 255 * (centers[n] - mid) / (max - min); - const int beta = 255 * (centers[n] - min) / (max - min); - enc->dqm_[n].alpha_ = clip(alpha, -127, 127); - enc->dqm_[n].beta_ = clip(beta, 0, 255); - } -} - -//------------------------------------------------------------------------------ -// 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_; - int centers[NUM_MB_SEGMENTS]; - int weighted_average = 0; - int map[256]; - int a, n, k; - int min_a = 0, max_a = 255, range_a; - // 'int' type is ok for histo, and won't overflow - int accum[NUM_MB_SEGMENTS], dist_accum[NUM_MB_SEGMENTS]; - - // bracket the input - for (n = 0; n < 256 && alphas[n] == 0; ++n) {} - min_a = n; - for (n = 255; 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; k < MAX_ITERS_K_MEANS; ++k) { // few iters are enough - int total_weight; - int displaced; - // Reset stats - for (n = 0; n < nb; ++n) { - accum[n] = 0; - dist_accum[n] = 0; - } - // Assign nearest center for each 'a' - 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])) { - n++; - } - map[a] = n; - // accumulate contribution into best centroid - dist_accum[n] += a * alphas[a]; - accum[n] += alphas[a]; - } - } - // All point are classified. Move the centroids to the - // center of their respective cloud. - displaced = 0; - weighted_average = 0; - total_weight = 0; - for (n = 0; n < nb; ++n) { - if (accum[n]) { - const int new_center = (dist_accum[n] + accum[n] / 2) / accum[n]; - displaced += abs(centers[n] - new_center); - centers[n] = new_center; - weighted_average += new_center * accum[n]; - total_weight += accum[n]; - } - } - weighted_average = (weighted_average + total_weight / 2) / total_weight; - if (displaced < 5) break; // no need to keep on looping... - } - - // Map each original value to the closest centroid - for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) { - 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. - } - - if (nb > 1) { - const int smooth = (enc->config_->preprocessing & 1); - if (smooth) SmoothSegmentMap(enc); - } - - SetSegmentProbas(enc); // Assign final proba - SetSegmentAlphas(enc, centers, weighted_average); // pick some alphas. -} - -//------------------------------------------------------------------------------ -// Macroblock analysis: collect histogram for each mode, deduce the maximal -// 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. -#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; - int mode; - int best_alpha = -1; - 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) { - best_alpha = alpha; - best_mode = mode; - } - } - VP8SetIntra16Mode(it, best_mode); - return best_alpha; -} - -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; - VP8IteratorStartI4(it); - do { - int mode; - int best_mode_alpha = -1; - const uint8_t* const src = it->yuv_in_ + Y_OFF + 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) { - best_mode_alpha = alpha; - modes[it->i4_] = mode; - } - } - i4_alpha += best_mode_alpha; - // Note: we reuse the original samples for predictors - } while (VP8IteratorRotateI4(it, it->yuv_in_ + Y_OFF)); - - if (i4_alpha > best_alpha) { - VP8SetIntra4Mode(it, modes); - best_alpha = ClipAlpha(i4_alpha); - } - return best_alpha; -} - -static int MBAnalyzeBestUVMode(VP8EncIterator* const it) { - int best_alpha = -1; - int best_mode = 0; - const int max_mode = (it->enc_->method_ >= 3) ? MAX_UV_MODE : 4; - 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) { - best_alpha = alpha; - best_mode = mode; - } - } - VP8SetIntraUVMode(it, best_mode); - return best_alpha; -} - -static void MBAnalyze(VP8EncIterator* const it, - int alphas[256], int* const uv_alpha) { - const VP8Encoder* const enc = it->enc_; - int best_alpha, best_uv_alpha; - - VP8SetIntra16Mode(it, 0); // default: Intra16, DC_PRED - VP8SetSkip(it, 0); // not skipped - VP8SetSegment(it, 0); // default segment, spec-wise. - - best_alpha = MBAnalyzeBestIntra16Mode(it); - if (enc->method_ != 3) { - // 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. - // TODO(skal): improve criterion. - best_alpha = MBAnalyzeBestIntra4Mode(it, best_alpha); - } - best_uv_alpha = MBAnalyzeBestUVMode(it); - - // Final susceptibility mix - best_alpha = (best_alpha + best_uv_alpha + 1) / 2; - alphas[best_alpha]++; - *uv_alpha += best_uv_alpha; - it->mb_->alpha_ = best_alpha; // Informative only. -} - -//------------------------------------------------------------------------------ -// Main analysis loop: -// Collect all susceptibilities for each macroblock and record their -// distribution in alphas[]. Segments is assigned a-posteriori, based on -// this histogram. -// We also pick an intra16 prediction mode, which shouldn't be considered -// final except for fast-encode settings. We can also pick some intra4 modes -// 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); - 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); - - return ok; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/backward_references.c b/drivers/webpold/enc/backward_references.c deleted file mode 100644 index b8c8ece806..0000000000 --- a/drivers/webpold/enc/backward_references.c +++ /dev/null @@ -1,874 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// - -#include <assert.h> -#include <math.h> -#include <stdio.h> - -#include "./backward_references.h" -#include "./histogram.h" -#include "../dsp/lossless.h" -#include "../utils/color_cache.h" -#include "../utils/utils.h" - -#define VALUES_IN_BYTE 256 - -#define HASH_BITS 18 -#define HASH_SIZE (1 << HASH_BITS) -#define HASH_MULTIPLIER (0xc6a4a7935bd1e995ULL) - -// 1M window (4M bytes) minus 120 special codes for short distances. -#define WINDOW_SIZE ((1 << 20) - 120) - -// Bounds for the match length. -#define MIN_LENGTH 2 -#define MAX_LENGTH 4096 - -typedef struct { - // 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_; -} HashChain; - -// ----------------------------------------------------------------------------- - -static const uint8_t plane_to_code_lut[128] = { - 96, 73, 55, 39, 23, 13, 5, 1, 255, 255, 255, 255, 255, 255, 255, 255, - 101, 78, 58, 42, 26, 16, 8, 2, 0, 3, 9, 17, 27, 43, 59, 79, - 102, 86, 62, 46, 32, 20, 10, 6, 4, 7, 11, 21, 33, 47, 63, 87, - 105, 90, 70, 52, 37, 28, 18, 14, 12, 15, 19, 29, 38, 53, 71, 91, - 110, 99, 82, 66, 48, 35, 30, 24, 22, 25, 31, 36, 49, 67, 83, 100, - 115, 108, 94, 76, 64, 50, 44, 40, 34, 41, 45, 51, 65, 77, 95, 109, - 118, 113, 103, 92, 80, 68, 60, 56, 54, 57, 61, 69, 81, 93, 104, 114, - 119, 116, 111, 106, 97, 88, 84, 74, 72, 75, 85, 89, 98, 107, 112, 117 -}; - -static int DistanceToPlaneCode(int xsize, int dist) { - const int yoffset = dist / xsize; - const int xoffset = dist - yoffset * xsize; - if (xoffset <= 8 && yoffset < 8) { - return plane_to_code_lut[yoffset * 16 + 8 - xoffset] + 1; - } else if (xoffset > xsize - 8 && yoffset < 7) { - return plane_to_code_lut[(yoffset + 1) * 16 + 8 + (xsize - xoffset)] + 1; - } - return dist + 120; -} - -static WEBP_INLINE int FindMatchLength(const uint32_t* const array1, - const uint32_t* const array2, - const int max_limit) { - int match_len = 0; - while (match_len < max_limit && array1[match_len] == array2[match_len]) { - ++match_len; - } - return match_len; -} - -// ----------------------------------------------------------------------------- -// VP8LBackwardRefs - -void VP8LInitBackwardRefs(VP8LBackwardRefs* const refs) { - if (refs != NULL) { - refs->refs = NULL; - refs->size = 0; - refs->max_size = 0; - } -} - -void VP8LClearBackwardRefs(VP8LBackwardRefs* const refs) { - if (refs != NULL) { - free(refs->refs); - VP8LInitBackwardRefs(refs); - } -} - -int VP8LBackwardRefsAlloc(VP8LBackwardRefs* const refs, int max_size) { - assert(refs != NULL); - refs->size = 0; - refs->max_size = 0; - refs->refs = (PixOrCopy*)WebPSafeMalloc((uint64_t)max_size, - sizeof(*refs->refs)); - if (refs->refs == NULL) return 0; - refs->max_size = max_size; - return 1; -} - -// ----------------------------------------------------------------------------- -// Hash chains - -static WEBP_INLINE uint64_t GetPixPairHash64(const uint32_t* const argb) { - uint64_t key = ((uint64_t)(argb[1]) << 32) | argb[0]; - key = (key * HASH_MULTIPLIER) >> (64 - HASH_BITS); - return key; -} - -static int HashChainInit(HashChain* const p, int size) { - int i; - p->chain_ = (int*)WebPSafeMalloc((uint64_t)size, sizeof(*p->chain_)); - if (p->chain_ == NULL) { - return 0; - } - for (i = 0; i < size; ++i) { - p->chain_[i] = -1; - } - for (i = 0; i < HASH_SIZE; ++i) { - p->hash_to_first_index_[i] = -1; - } - return 1; -} - -static void HashChainDelete(HashChain* const p) { - if (p != NULL) { - free(p->chain_); - free(p); - } -} - -// Insertion of two pixels at a time. -static void HashChainInsert(HashChain* const p, - const uint32_t* const argb, int pos) { - const uint64_t hash_code = GetPixPairHash64(argb); - p->chain_[pos] = p->hash_to_first_index_[hash_code]; - p->hash_to_first_index_[hash_code] = pos; -} - -static int HashChainFindCopy(const HashChain* const p, - int quality, int index, int xsize, - const uint32_t* const argb, int maxlen, - int* const distance_ptr, - int* const length_ptr) { - const uint64_t hash_code = GetPixPairHash64(&argb[index]); - int prev_length = 0; - int64_t best_val = 0; - int best_length = 0; - int best_distance = 0; - const uint32_t* const argb_start = argb + index; - const int iter_min_mult = (quality < 50) ? 2 : (quality < 75) ? 4 : 8; - const int iter_min = -quality * iter_min_mult; - int iter_cnt = 10 + (quality >> 1); - const int min_pos = (index > WINDOW_SIZE) ? index - WINDOW_SIZE : 0; - int pos; - - assert(xsize > 0); - for (pos = p->hash_to_first_index_[hash_code]; - pos >= min_pos; - pos = p->chain_[pos]) { - int64_t val; - int curr_length; - if (iter_cnt < 0) { - if (iter_cnt < iter_min || best_val >= 0xff0000) { - break; - } - } - --iter_cnt; - if (best_length != 0 && - argb[pos + best_length - 1] != argb_start[best_length - 1]) { - continue; - } - curr_length = FindMatchLength(argb + pos, argb_start, maxlen); - if (curr_length < prev_length) { - continue; - } - val = 65536 * curr_length; - // Favoring 2d locality here gives savings for certain images. - if (index - pos < 9 * xsize) { - const int y = (index - pos) / xsize; - int x = (index - pos) % xsize; - if (x > xsize / 2) { - x = xsize - x; - } - if (x <= 7 && x >= -8) { - val -= y * y + x * x; - } else { - val -= 9 * 9 + 9 * 9; - } - } else { - val -= 9 * 9 + 9 * 9; - } - if (best_val < val) { - prev_length = curr_length; - best_val = val; - best_length = curr_length; - best_distance = index - pos; - if (curr_length >= MAX_LENGTH) { - break; - } - if ((best_distance == 1 || best_distance == xsize) && - best_length >= 128) { - break; - } - } - } - *distance_ptr = best_distance; - *length_ptr = best_length; - return (best_length >= MIN_LENGTH); -} - -static WEBP_INLINE void PushBackCopy(VP8LBackwardRefs* const refs, int length) { - int size = refs->size; - while (length >= MAX_LENGTH) { - refs->refs[size++] = PixOrCopyCreateCopy(1, MAX_LENGTH); - length -= MAX_LENGTH; - } - if (length > 0) { - refs->refs[size++] = PixOrCopyCreateCopy(1, length); - } - refs->size = size; -} - -static void BackwardReferencesRle(int xsize, int ysize, - const uint32_t* const argb, - VP8LBackwardRefs* const refs) { - const int pix_count = xsize * ysize; - int match_len = 0; - int i; - refs->size = 0; - PushBackCopy(refs, match_len); // i=0 case - refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[0]); - for (i = 1; i < pix_count; ++i) { - if (argb[i] == argb[i - 1]) { - ++match_len; - } else { - PushBackCopy(refs, match_len); - match_len = 0; - refs->refs[refs->size++] = PixOrCopyCreateLiteral(argb[i]); - } - } - PushBackCopy(refs, match_len); -} - -static int BackwardReferencesHashChain(int xsize, int ysize, - const uint32_t* const argb, - int cache_bits, int quality, - VP8LBackwardRefs* const refs) { - int i; - int ok = 0; - int cc_init = 0; - const int use_color_cache = (cache_bits > 0); - const int pix_count = xsize * ysize; - HashChain* const hash_chain = (HashChain*)malloc(sizeof(*hash_chain)); - VP8LColorCache hashers; - - if (hash_chain == NULL) return 0; - if (use_color_cache) { - cc_init = VP8LColorCacheInit(&hashers, cache_bits); - if (!cc_init) goto Error; - } - - if (!HashChainInit(hash_chain, pix_count)) goto Error; - - refs->size = 0; - for (i = 0; i < pix_count; ) { - // Alternative#1: Code the pixels starting at 'i' using backward reference. - int offset = 0; - int len = 0; - if (i < pix_count - 1) { // FindCopy(i,..) reads pixels at [i] and [i + 1]. - int maxlen = pix_count - i; - if (maxlen > MAX_LENGTH) { - maxlen = MAX_LENGTH; - } - HashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen, - &offset, &len); - } - if (len >= MIN_LENGTH) { - // Alternative#2: Insert the pixel at 'i' as literal, and code the - // pixels starting at 'i + 1' using backward reference. - int offset2 = 0; - int len2 = 0; - int k; - HashChainInsert(hash_chain, &argb[i], i); - if (i < pix_count - 2) { // FindCopy(i+1,..) reads [i + 1] and [i + 2]. - int maxlen = pix_count - (i + 1); - if (maxlen > MAX_LENGTH) { - maxlen = MAX_LENGTH; - } - HashChainFindCopy(hash_chain, quality, - i + 1, xsize, argb, maxlen, &offset2, &len2); - if (len2 > len + 1) { - const uint32_t pixel = argb[i]; - // Alternative#2 is a better match. So push pixel at 'i' as literal. - if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) { - const int ix = VP8LColorCacheGetIndex(&hashers, pixel); - refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix); - } else { - refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel); - } - ++refs->size; - if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel); - i++; // Backward reference to be done for next pixel. - len = len2; - offset = offset2; - } - } - if (len >= MAX_LENGTH) { - len = MAX_LENGTH - 1; - } - refs->refs[refs->size++] = PixOrCopyCreateCopy(offset, len); - if (use_color_cache) { - for (k = 0; k < len; ++k) { - VP8LColorCacheInsert(&hashers, argb[i + k]); - } - } - // Add to the hash_chain (but cannot add the last pixel). - { - const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i; - for (k = 1; k < last; ++k) { - HashChainInsert(hash_chain, &argb[i + k], i + k); - } - } - i += len; - } else { - const uint32_t pixel = argb[i]; - if (use_color_cache && VP8LColorCacheContains(&hashers, pixel)) { - // push pixel as a PixOrCopyCreateCacheIdx pixel - const int ix = VP8LColorCacheGetIndex(&hashers, pixel); - refs->refs[refs->size] = PixOrCopyCreateCacheIdx(ix); - } else { - refs->refs[refs->size] = PixOrCopyCreateLiteral(pixel); - } - ++refs->size; - if (use_color_cache) VP8LColorCacheInsert(&hashers, pixel); - if (i + 1 < pix_count) { - HashChainInsert(hash_chain, &argb[i], i); - } - ++i; - } - } - ok = 1; -Error: - if (cc_init) VP8LColorCacheClear(&hashers); - HashChainDelete(hash_chain); - return ok; -} - -// ----------------------------------------------------------------------------- - -typedef struct { - double alpha_[VALUES_IN_BYTE]; - double red_[VALUES_IN_BYTE]; - double literal_[PIX_OR_COPY_CODES_MAX]; - double blue_[VALUES_IN_BYTE]; - double distance_[NUM_DISTANCE_CODES]; -} CostModel; - -static int BackwardReferencesTraceBackwards( - int xsize, int ysize, int recursive_cost_model, - const uint32_t* const argb, int cache_bits, VP8LBackwardRefs* const refs); - -static void ConvertPopulationCountTableToBitEstimates( - int num_symbols, const int population_counts[], double output[]) { - int sum = 0; - int nonzeros = 0; - int i; - for (i = 0; i < num_symbols; ++i) { - sum += population_counts[i]; - if (population_counts[i] > 0) { - ++nonzeros; - } - } - if (nonzeros <= 1) { - memset(output, 0, num_symbols * sizeof(*output)); - } else { - const double logsum = VP8LFastLog2(sum); - for (i = 0; i < num_symbols; ++i) { - output[i] = logsum - VP8LFastLog2(population_counts[i]); - } - } -} - -static int CostModelBuild(CostModel* const m, int xsize, int ysize, - int recursion_level, const uint32_t* const argb, - int cache_bits) { - int ok = 0; - VP8LHistogram histo; - VP8LBackwardRefs refs; - const int quality = 100; - - if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize)) goto Error; - - if (recursion_level > 0) { - if (!BackwardReferencesTraceBackwards(xsize, ysize, recursion_level - 1, - argb, cache_bits, &refs)) { - goto Error; - } - } else { - if (!BackwardReferencesHashChain(xsize, ysize, argb, cache_bits, quality, - &refs)) { - goto Error; - } - } - VP8LHistogramCreate(&histo, &refs, cache_bits); - ConvertPopulationCountTableToBitEstimates( - VP8LHistogramNumCodes(&histo), histo.literal_, m->literal_); - ConvertPopulationCountTableToBitEstimates( - VALUES_IN_BYTE, histo.red_, m->red_); - ConvertPopulationCountTableToBitEstimates( - VALUES_IN_BYTE, histo.blue_, m->blue_); - ConvertPopulationCountTableToBitEstimates( - VALUES_IN_BYTE, histo.alpha_, m->alpha_); - ConvertPopulationCountTableToBitEstimates( - NUM_DISTANCE_CODES, histo.distance_, m->distance_); - ok = 1; - - Error: - VP8LClearBackwardRefs(&refs); - return ok; -} - -static WEBP_INLINE double GetLiteralCost(const CostModel* const m, uint32_t v) { - return m->alpha_[v >> 24] + - m->red_[(v >> 16) & 0xff] + - m->literal_[(v >> 8) & 0xff] + - m->blue_[v & 0xff]; -} - -static WEBP_INLINE double GetCacheCost(const CostModel* const m, uint32_t idx) { - const int literal_idx = VALUES_IN_BYTE + NUM_LENGTH_CODES + idx; - return m->literal_[literal_idx]; -} - -static WEBP_INLINE double GetLengthCost(const CostModel* const m, - uint32_t length) { - int code, extra_bits_count, extra_bits_value; - PrefixEncode(length, &code, &extra_bits_count, &extra_bits_value); - return m->literal_[VALUES_IN_BYTE + code] + extra_bits_count; -} - -static WEBP_INLINE double GetDistanceCost(const CostModel* const m, - uint32_t distance) { - int code, extra_bits_count, extra_bits_value; - PrefixEncode(distance, &code, &extra_bits_count, &extra_bits_value); - return m->distance_[code] + extra_bits_count; -} - -static int BackwardReferencesHashChainDistanceOnly( - int xsize, int ysize, int recursive_cost_model, const uint32_t* const argb, - int cache_bits, uint32_t* const dist_array) { - int i; - int ok = 0; - int cc_init = 0; - const int quality = 100; - const int pix_count = xsize * ysize; - const int use_color_cache = (cache_bits > 0); - double* const cost = - (double*)WebPSafeMalloc((uint64_t)pix_count, sizeof(*cost)); - CostModel* cost_model = (CostModel*)malloc(sizeof(*cost_model)); - HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain)); - VP8LColorCache hashers; - const double mul0 = (recursive_cost_model != 0) ? 1.0 : 0.68; - const double mul1 = (recursive_cost_model != 0) ? 1.0 : 0.82; - - if (cost == NULL || cost_model == NULL || hash_chain == NULL) goto Error; - - if (!HashChainInit(hash_chain, pix_count)) goto Error; - - if (use_color_cache) { - cc_init = VP8LColorCacheInit(&hashers, cache_bits); - if (!cc_init) goto Error; - } - - if (!CostModelBuild(cost_model, xsize, ysize, recursive_cost_model, argb, - cache_bits)) { - goto Error; - } - - for (i = 0; i < pix_count; ++i) cost[i] = 1e100; - - // We loop one pixel at a time, but store all currently best points to - // non-processed locations from this point. - dist_array[0] = 0; - for (i = 0; i < pix_count; ++i) { - double prev_cost = 0.0; - int shortmax; - if (i > 0) { - prev_cost = cost[i - 1]; - } - for (shortmax = 0; shortmax < 2; ++shortmax) { - int offset = 0; - int len = 0; - if (i < pix_count - 1) { // FindCopy reads pixels at [i] and [i + 1]. - int maxlen = shortmax ? 2 : MAX_LENGTH; - if (maxlen > pix_count - i) { - maxlen = pix_count - i; - } - HashChainFindCopy(hash_chain, quality, i, xsize, argb, maxlen, - &offset, &len); - } - if (len >= MIN_LENGTH) { - const int code = DistanceToPlaneCode(xsize, offset); - const double distance_cost = - prev_cost + GetDistanceCost(cost_model, code); - int k; - for (k = 1; k < len; ++k) { - const double cost_val = - distance_cost + GetLengthCost(cost_model, k); - if (cost[i + k] > cost_val) { - cost[i + k] = cost_val; - dist_array[i + k] = k + 1; - } - } - // This if is for speedup only. It roughly doubles the speed, and - // makes compression worse by .1 %. - if (len >= 128 && code < 2) { - // Long copy for short distances, let's skip the middle - // lookups for better copies. - // 1) insert the hashes. - if (use_color_cache) { - for (k = 0; k < len; ++k) { - VP8LColorCacheInsert(&hashers, argb[i + k]); - } - } - // 2) Add to the hash_chain (but cannot add the last pixel) - { - const int last = (len < pix_count - 1 - i) ? len - : pix_count - 1 - i; - for (k = 0; k < last; ++k) { - HashChainInsert(hash_chain, &argb[i + k], i + k); - } - } - // 3) jump. - i += len - 1; // for loop does ++i, thus -1 here. - goto next_symbol; - } - } - } - if (i < pix_count - 1) { - HashChainInsert(hash_chain, &argb[i], i); - } - { - // inserting a literal pixel - double cost_val = prev_cost; - if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { - const int ix = VP8LColorCacheGetIndex(&hashers, argb[i]); - cost_val += GetCacheCost(cost_model, ix) * mul0; - } else { - cost_val += GetLiteralCost(cost_model, argb[i]) * mul1; - } - if (cost[i] > cost_val) { - cost[i] = cost_val; - dist_array[i] = 1; // only one is inserted. - } - if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]); - } - next_symbol: ; - } - // Last pixel still to do, it can only be a single step if not reached - // through cheaper means already. - ok = 1; -Error: - if (cc_init) VP8LColorCacheClear(&hashers); - HashChainDelete(hash_chain); - free(cost_model); - free(cost); - return ok; -} - -static int TraceBackwards(const uint32_t* const dist_array, - int dist_array_size, - uint32_t** const chosen_path, - int* const chosen_path_size) { - int i; - // Count how many. - int count = 0; - for (i = dist_array_size - 1; i >= 0; ) { - int k = dist_array[i]; - assert(k >= 1); - ++count; - i -= k; - } - // Allocate. - *chosen_path_size = count; - *chosen_path = - (uint32_t*)WebPSafeMalloc((uint64_t)count, sizeof(**chosen_path)); - if (*chosen_path == NULL) return 0; - - // Write in reverse order. - for (i = dist_array_size - 1; i >= 0; ) { - int k = dist_array[i]; - assert(k >= 1); - (*chosen_path)[--count] = k; - i -= k; - } - return 1; -} - -static int BackwardReferencesHashChainFollowChosenPath( - int xsize, int ysize, const uint32_t* const argb, int cache_bits, - const uint32_t* const chosen_path, int chosen_path_size, - VP8LBackwardRefs* const refs) { - const int quality = 100; - const int pix_count = xsize * ysize; - const int use_color_cache = (cache_bits > 0); - int size = 0; - int i = 0; - int k; - int ix; - int ok = 0; - int cc_init = 0; - HashChain* hash_chain = (HashChain*)malloc(sizeof(*hash_chain)); - VP8LColorCache hashers; - - if (hash_chain == NULL || !HashChainInit(hash_chain, pix_count)) { - goto Error; - } - if (use_color_cache) { - cc_init = VP8LColorCacheInit(&hashers, cache_bits); - if (!cc_init) goto Error; - } - - refs->size = 0; - for (ix = 0; ix < chosen_path_size; ++ix, ++size) { - int offset = 0; - int len = 0; - int maxlen = chosen_path[ix]; - if (maxlen != 1) { - HashChainFindCopy(hash_chain, quality, - i, xsize, argb, maxlen, &offset, &len); - assert(len == maxlen); - refs->refs[size] = PixOrCopyCreateCopy(offset, len); - if (use_color_cache) { - for (k = 0; k < len; ++k) { - VP8LColorCacheInsert(&hashers, argb[i + k]); - } - } - { - const int last = (len < pix_count - 1 - i) ? len : pix_count - 1 - i; - for (k = 0; k < last; ++k) { - HashChainInsert(hash_chain, &argb[i + k], i + k); - } - } - i += len; - } else { - if (use_color_cache && VP8LColorCacheContains(&hashers, argb[i])) { - // push pixel as a color cache index - const int idx = VP8LColorCacheGetIndex(&hashers, argb[i]); - refs->refs[size] = PixOrCopyCreateCacheIdx(idx); - } else { - refs->refs[size] = PixOrCopyCreateLiteral(argb[i]); - } - if (use_color_cache) VP8LColorCacheInsert(&hashers, argb[i]); - if (i + 1 < pix_count) { - HashChainInsert(hash_chain, &argb[i], i); - } - ++i; - } - } - assert(size <= refs->max_size); - refs->size = size; - ok = 1; -Error: - if (cc_init) VP8LColorCacheClear(&hashers); - HashChainDelete(hash_chain); - return ok; -} - -// Returns 1 on success. -static int BackwardReferencesTraceBackwards(int xsize, int ysize, - int recursive_cost_model, - const uint32_t* const argb, - int cache_bits, - VP8LBackwardRefs* const refs) { - int ok = 0; - const int dist_array_size = xsize * ysize; - uint32_t* chosen_path = NULL; - int chosen_path_size = 0; - uint32_t* dist_array = - (uint32_t*)WebPSafeMalloc((uint64_t)dist_array_size, sizeof(*dist_array)); - - if (dist_array == NULL) goto Error; - - if (!BackwardReferencesHashChainDistanceOnly( - xsize, ysize, recursive_cost_model, argb, cache_bits, dist_array)) { - goto Error; - } - if (!TraceBackwards(dist_array, dist_array_size, - &chosen_path, &chosen_path_size)) { - goto Error; - } - free(dist_array); // no need to retain this memory any longer - dist_array = NULL; - if (!BackwardReferencesHashChainFollowChosenPath( - xsize, ysize, argb, cache_bits, chosen_path, chosen_path_size, refs)) { - goto Error; - } - ok = 1; - Error: - free(chosen_path); - free(dist_array); - return ok; -} - -static void BackwardReferences2DLocality(int xsize, - VP8LBackwardRefs* const refs) { - int i; - for (i = 0; i < refs->size; ++i) { - if (PixOrCopyIsCopy(&refs->refs[i])) { - const int dist = refs->refs[i].argb_or_distance; - const int transformed_dist = DistanceToPlaneCode(xsize, dist); - refs->refs[i].argb_or_distance = transformed_dist; - } - } -} - -int VP8LGetBackwardReferences(int width, int height, - const uint32_t* const argb, - int quality, int cache_bits, int use_2d_locality, - VP8LBackwardRefs* const best) { - int ok = 0; - int lz77_is_useful; - VP8LBackwardRefs refs_rle, refs_lz77; - const int num_pix = width * height; - - VP8LBackwardRefsAlloc(&refs_rle, num_pix); - VP8LBackwardRefsAlloc(&refs_lz77, num_pix); - VP8LInitBackwardRefs(best); - if (refs_rle.refs == NULL || refs_lz77.refs == NULL) { - Error1: - VP8LClearBackwardRefs(&refs_rle); - VP8LClearBackwardRefs(&refs_lz77); - goto End; - } - - if (!BackwardReferencesHashChain(width, height, argb, cache_bits, quality, - &refs_lz77)) { - goto End; - } - // Backward Reference using RLE only. - BackwardReferencesRle(width, height, argb, &refs_rle); - - { - double bit_cost_lz77, bit_cost_rle; - VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo)); - if (histo == NULL) goto Error1; - // Evaluate lz77 coding - VP8LHistogramCreate(histo, &refs_lz77, cache_bits); - bit_cost_lz77 = VP8LHistogramEstimateBits(histo); - // Evaluate RLE coding - VP8LHistogramCreate(histo, &refs_rle, cache_bits); - bit_cost_rle = VP8LHistogramEstimateBits(histo); - // Decide if LZ77 is useful. - lz77_is_useful = (bit_cost_lz77 < bit_cost_rle); - free(histo); - } - - // Choose appropriate backward reference. - if (lz77_is_useful) { - // TraceBackwards is costly. Run it for higher qualities. - const int try_lz77_trace_backwards = (quality >= 75); - *best = refs_lz77; // default guess: lz77 is better - VP8LClearBackwardRefs(&refs_rle); - if (try_lz77_trace_backwards) { - const int recursion_level = (num_pix < 320 * 200) ? 1 : 0; - VP8LBackwardRefs refs_trace; - if (!VP8LBackwardRefsAlloc(&refs_trace, num_pix)) { - goto End; - } - if (BackwardReferencesTraceBackwards( - width, height, recursion_level, argb, cache_bits, &refs_trace)) { - VP8LClearBackwardRefs(&refs_lz77); - *best = refs_trace; - } - } - } else { - VP8LClearBackwardRefs(&refs_lz77); - *best = refs_rle; - } - - if (use_2d_locality) BackwardReferences2DLocality(width, best); - - ok = 1; - - End: - if (!ok) { - VP8LClearBackwardRefs(best); - } - return ok; -} - -// Returns 1 on success. -static int ComputeCacheHistogram(const uint32_t* const argb, - int xsize, int ysize, - const VP8LBackwardRefs* const refs, - int cache_bits, - VP8LHistogram* const histo) { - int pixel_index = 0; - int i; - uint32_t k; - VP8LColorCache hashers; - const int use_color_cache = (cache_bits > 0); - int cc_init = 0; - - if (use_color_cache) { - cc_init = VP8LColorCacheInit(&hashers, cache_bits); - if (!cc_init) return 0; - } - - for (i = 0; i < refs->size; ++i) { - const PixOrCopy* const v = &refs->refs[i]; - if (PixOrCopyIsLiteral(v)) { - if (use_color_cache && - VP8LColorCacheContains(&hashers, argb[pixel_index])) { - // push pixel as a cache index - const int ix = VP8LColorCacheGetIndex(&hashers, argb[pixel_index]); - const PixOrCopy token = PixOrCopyCreateCacheIdx(ix); - VP8LHistogramAddSinglePixOrCopy(histo, &token); - } else { - VP8LHistogramAddSinglePixOrCopy(histo, v); - } - } else { - VP8LHistogramAddSinglePixOrCopy(histo, v); - } - if (use_color_cache) { - for (k = 0; k < PixOrCopyLength(v); ++k) { - VP8LColorCacheInsert(&hashers, argb[pixel_index + k]); - } - } - pixel_index += PixOrCopyLength(v); - } - assert(pixel_index == xsize * ysize); - (void)xsize; // xsize is not used in non-debug compilations otherwise. - (void)ysize; // ysize is not used in non-debug compilations otherwise. - if (cc_init) VP8LColorCacheClear(&hashers); - return 1; -} - -// Returns how many bits are to be used for a color cache. -int VP8LCalculateEstimateForCacheSize(const uint32_t* const argb, - int xsize, int ysize, - int* const best_cache_bits) { - int ok = 0; - int cache_bits; - double lowest_entropy = 1e99; - VP8LBackwardRefs refs; - static const double kSmallPenaltyForLargeCache = 4.0; - static const int quality = 30; - if (!VP8LBackwardRefsAlloc(&refs, xsize * ysize) || - !BackwardReferencesHashChain(xsize, ysize, argb, 0, quality, &refs)) { - goto Error; - } - for (cache_bits = 0; cache_bits <= MAX_COLOR_CACHE_BITS; ++cache_bits) { - double cur_entropy; - VP8LHistogram histo; - VP8LHistogramInit(&histo, cache_bits); - ComputeCacheHistogram(argb, xsize, ysize, &refs, cache_bits, &histo); - cur_entropy = VP8LHistogramEstimateBits(&histo) + - kSmallPenaltyForLargeCache * cache_bits; - if (cache_bits == 0 || cur_entropy < lowest_entropy) { - *best_cache_bits = cache_bits; - lowest_entropy = cur_entropy; - } - } - ok = 1; - Error: - VP8LClearBackwardRefs(&refs); - return ok; -} diff --git a/drivers/webpold/enc/backward_references.h b/drivers/webpold/enc/backward_references.h deleted file mode 100644 index 8006a56ba1..0000000000 --- a/drivers/webpold/enc/backward_references.h +++ /dev/null @@ -1,212 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// - -#ifndef WEBP_ENC_BACKWARD_REFERENCES_H_ -#define WEBP_ENC_BACKWARD_REFERENCES_H_ - -#include <assert.h> -#include <stdlib.h> -#include "../types.h" -#include "../format_constants.h" - -#if defined(__cplusplus) || defined(c_plusplus) -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; -} - -// ----------------------------------------------------------------------------- -// PixOrCopy - -enum Mode { - kLiteral, - kCacheIdx, - kCopy, - kNone -}; - -typedef struct { - // mode as uint8_t to make the memory layout to be exactly 8 bytes. - uint8_t mode; - uint16_t len; - uint32_t argb_or_distance; -} PixOrCopy; - -static WEBP_INLINE PixOrCopy PixOrCopyCreateCopy(uint32_t distance, - uint16_t len) { - PixOrCopy retval; - retval.mode = kCopy; - retval.argb_or_distance = distance; - retval.len = len; - return retval; -} - -static WEBP_INLINE PixOrCopy PixOrCopyCreateCacheIdx(int idx) { - PixOrCopy retval; - assert(idx >= 0); - assert(idx < (1 << MAX_COLOR_CACHE_BITS)); - retval.mode = kCacheIdx; - retval.argb_or_distance = idx; - retval.len = 1; - return retval; -} - -static WEBP_INLINE PixOrCopy PixOrCopyCreateLiteral(uint32_t argb) { - PixOrCopy retval; - retval.mode = kLiteral; - retval.argb_or_distance = argb; - retval.len = 1; - return retval; -} - -static WEBP_INLINE int PixOrCopyIsLiteral(const PixOrCopy* const p) { - return (p->mode == kLiteral); -} - -static WEBP_INLINE int PixOrCopyIsCacheIdx(const PixOrCopy* const p) { - return (p->mode == kCacheIdx); -} - -static WEBP_INLINE int PixOrCopyIsCopy(const PixOrCopy* const p) { - return (p->mode == kCopy); -} - -static WEBP_INLINE uint32_t PixOrCopyLiteral(const PixOrCopy* const p, - int component) { - assert(p->mode == kLiteral); - return (p->argb_or_distance >> (component * 8)) & 0xff; -} - -static WEBP_INLINE uint32_t PixOrCopyLength(const PixOrCopy* const p) { - return p->len; -} - -static WEBP_INLINE uint32_t PixOrCopyArgb(const PixOrCopy* const p) { - assert(p->mode == kLiteral); - return p->argb_or_distance; -} - -static WEBP_INLINE uint32_t PixOrCopyCacheIdx(const PixOrCopy* const p) { - assert(p->mode == kCacheIdx); - assert(p->argb_or_distance < (1U << MAX_COLOR_CACHE_BITS)); - return p->argb_or_distance; -} - -static WEBP_INLINE uint32_t PixOrCopyDistance(const PixOrCopy* const p) { - assert(p->mode == kCopy); - return p->argb_or_distance; -} - -// ----------------------------------------------------------------------------- -// VP8LBackwardRefs - -typedef struct { - PixOrCopy* refs; - int size; // currently used - int max_size; // maximum capacity -} VP8LBackwardRefs; - -// 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); - -// ----------------------------------------------------------------------------- -// 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); - -// 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) -} -#endif - -#endif // WEBP_ENC_BACKWARD_REFERENCES_H_ diff --git a/drivers/webpold/enc/config.c b/drivers/webpold/enc/config.c deleted file mode 100644 index 4136f6c227..0000000000 --- a/drivers/webpold/enc/config.c +++ /dev/null @@ -1,132 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Coding tools configuration -// -// Author: Skal (pascal.massimino@gmail.com) - -#include "../encode.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// WebPConfig -//------------------------------------------------------------------------------ - -int WebPConfigInitInternal(WebPConfig* config, - WebPPreset preset, float quality, int version) { - if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_ENCODER_ABI_VERSION)) { - return 0; // caller/system version mismatch! - } - if (config == NULL) return 0; - - config->quality = quality; - config->target_size = 0; - config->target_PSNR = 0.; - config->method = 4; - config->sns_strength = 50; - config->filter_strength = 20; // default: light filtering - config->filter_sharpness = 0; - config->filter_type = 0; // default: simple - config->partitions = 0; - config->segments = 4; - config->pass = 1; - config->show_compressed = 0; - config->preprocessing = 0; - config->autofilter = 0; - config->partition_limit = 0; - config->alpha_compression = 1; - config->alpha_filtering = 1; - config->alpha_quality = 100; - config->lossless = 0; - config->image_hint = WEBP_HINT_DEFAULT; - - // TODO(skal): tune. - switch (preset) { - case WEBP_PRESET_PICTURE: - config->sns_strength = 80; - config->filter_sharpness = 4; - config->filter_strength = 35; - break; - case WEBP_PRESET_PHOTO: - config->sns_strength = 80; - config->filter_sharpness = 3; - config->filter_strength = 30; - break; - case WEBP_PRESET_DRAWING: - config->sns_strength = 25; - config->filter_sharpness = 6; - config->filter_strength = 10; - break; - case WEBP_PRESET_ICON: - config->sns_strength = 0; - config->filter_strength = 0; // disable filtering to retain sharpness - break; - case WEBP_PRESET_TEXT: - config->sns_strength = 0; - config->filter_strength = 0; // disable filtering to retain sharpness - config->segments = 2; - break; - case WEBP_PRESET_DEFAULT: - default: - break; - } - return WebPValidateConfig(config); -} - -int WebPValidateConfig(const WebPConfig* config) { - if (config == NULL) return 0; - if (config->quality < 0 || config->quality > 100) - return 0; - if (config->target_size < 0) - return 0; - if (config->target_PSNR < 0) - return 0; - if (config->method < 0 || config->method > 6) - return 0; - if (config->segments < 1 || config->segments > 4) - return 0; - if (config->sns_strength < 0 || config->sns_strength > 100) - return 0; - if (config->filter_strength < 0 || config->filter_strength > 100) - return 0; - if (config->filter_sharpness < 0 || config->filter_sharpness > 7) - return 0; - if (config->filter_type < 0 || config->filter_type > 1) - return 0; - if (config->autofilter < 0 || config->autofilter > 1) - return 0; - if (config->pass < 1 || config->pass > 10) - return 0; - if (config->show_compressed < 0 || config->show_compressed > 1) - return 0; - if (config->preprocessing < 0 || config->preprocessing > 1) - return 0; - if (config->partitions < 0 || config->partitions > 3) - return 0; - if (config->partition_limit < 0 || config->partition_limit > 100) - return 0; - if (config->alpha_compression < 0) - return 0; - if (config->alpha_filtering < 0) - return 0; - if (config->alpha_quality < 0 || config->alpha_quality > 100) - return 0; - if (config->lossless < 0 || config->lossless > 1) - return 0; - if (config->image_hint >= WEBP_HINT_LAST) - return 0; - return 1; -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/cost.c b/drivers/webpold/enc/cost.c deleted file mode 100644 index 92e0cc713c..0000000000 --- a/drivers/webpold/enc/cost.c +++ /dev/null @@ -1,494 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Cost tables for level and modes -// -// Author: Skal (pascal.massimino@gmail.com) - -#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 - -// For each given level, the following table gives the pattern of contexts to -// use for coding it (in [][0]) as well as the bit value to use for each -// context (in [][1]). -const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2] = { - {0x001, 0x000}, {0x007, 0x001}, {0x00f, 0x005}, - {0x00f, 0x00d}, {0x033, 0x003}, {0x033, 0x003}, {0x033, 0x023}, - {0x033, 0x023}, {0x033, 0x023}, {0x033, 0x023}, {0x0d3, 0x013}, - {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, - {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x013}, {0x0d3, 0x093}, - {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, - {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, - {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, - {0x0d3, 0x093}, {0x0d3, 0x093}, {0x0d3, 0x093}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, {0x153, 0x053}, - {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]; - int cost = 0; - int i; - for (i = 2; pattern; ++i) { - if (pattern & 1) { - cost += VP8BitCost(bits & 1, probas[i]); - } - bits >>= 1; - pattern >>= 1; - } - return cost; -} - -//------------------------------------------------------------------------------ -// Pre-calc level costs once for all - -void VP8CalculateLevelCosts(VP8Proba* const proba) { - int ctype, band, ctx; - - if (!proba->dirty_) return; // nothing to do. - - for (ctype = 0; ctype < NUM_TYPES; ++ctype) { - for (band = 0; band < NUM_BANDS; ++band) { - 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]); - int v; - table[0] = VP8BitCost(0, p[1]); - for (v = 1; v <= MAX_VARIABLE_LEVEL; ++v) { - table[v] = cost_base + VariableLevelCost(v, p); - } - // Starting at level 67 and up, the variable part of the cost is - // actually constant. - } - } - } - proba->dirty_ = 0; -} - -//------------------------------------------------------------------------------ -// Mode cost tables. - -// These are the fixed probabilities (in the coding trees) turned into bit-cost -// by calling VP8BitCost(). -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 } }, -}; - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/cost.h b/drivers/webpold/enc/cost.h deleted file mode 100644 index 09b75b699d..0000000000 --- a/drivers/webpold/enc/cost.h +++ /dev/null @@ -1,48 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Cost tables for level and modes. -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_ENC_COST_H_ -#define WEBP_ENC_COST_H_ - -#include "./vp8enci.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -extern const uint16_t VP8LevelFixedCosts[2048]; // approximate cost per level -extern const uint16_t VP8EntropyCost[256]; // 8bit fixed-point log(p) - -// Cost of coding one event with probability 'proba'. -static WEBP_INLINE int VP8BitCost(int bit, uint8_t proba) { - return !bit ? VP8EntropyCost[proba] : VP8EntropyCost[255 - proba]; -} - -// Level cost calculations -extern const uint16_t VP8LevelCodes[MAX_VARIABLE_LEVEL][2]; -void VP8CalculateLevelCosts(VP8Proba* 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]; -} - -// Mode costs -extern const uint16_t VP8FixedCostsUV[4]; -extern const uint16_t VP8FixedCostsI16[4]; -extern const uint16_t VP8FixedCostsI4[NUM_BMODES][NUM_BMODES][NUM_BMODES]; - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_ENC_COST_H_ */ diff --git a/drivers/webpold/enc/filter.c b/drivers/webpold/enc/filter.c deleted file mode 100644 index 7fb78a3949..0000000000 --- a/drivers/webpold/enc/filter.c +++ /dev/null @@ -1,409 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Selecting filter level -// -// Author: somnath@google.com (Somnath Banerjee) - -#include "./vp8enci.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -// 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; - } -} - -//------------------------------------------------------------------------------ -// 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 - -static int GetILevel(int sharpness, int level) { - if (sharpness > 0) { - if (sharpness > 4) { - level >>= 2; - } else { - level >>= 1; - } - if (level > 9 - sharpness) { - level = 9 - sharpness; - } - } - if (level < 1) level = 1; - return level; -} - -static void DoFilter(const VP8EncIterator* const it, int level) { - const VP8Encoder* const enc = it->enc_; - 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; - - // copy current block to yuv_out2_ - memcpy(y_dst, it->yuv_out_, YUV_SIZE * sizeof(uint8_t)); - - if (enc->filter_hdr_.simple_ == 1) { // simple - VP8EncSimpleHFilter16i(y_dst, BPS, limit); - VP8EncSimpleVFilter16i(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); - } -} - -//------------------------------------------------------------------------------ -// SSIM metric - -enum { KERNEL = 3 }; -static const double kMinValue = 1.e-10; // minimal threshold - -void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst) { - dst->w += src->w; - dst->xm += src->xm; - dst->ym += src->ym; - dst->xxm += src->xxm; - dst->xym += src->xym; - dst->yym += src->yym; -} - -static void VP8SSIMAccumulate(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - int xo, int yo, int W, int H, - DistoStats* const stats) { - const int ymin = (yo - KERNEL < 0) ? 0 : yo - KERNEL; - const int ymax = (yo + KERNEL > H - 1) ? H - 1 : yo + KERNEL; - const int xmin = (xo - KERNEL < 0) ? 0 : xo - KERNEL; - const int xmax = (xo + KERNEL > W - 1) ? W - 1 : xo + KERNEL; - int x, y; - src1 += ymin * stride1; - src2 += ymin * stride2; - for (y = ymin; y <= ymax; ++y, src1 += stride1, src2 += stride2) { - for (x = xmin; x <= xmax; ++x) { - const int s1 = src1[x]; - const int s2 = src2[x]; - stats->w += 1; - stats->xm += s1; - stats->ym += s2; - stats->xxm += s1 * s1; - stats->xym += s1 * s2; - stats->yym += s2 * s2; - } - } -} - -double VP8SSIMGet(const DistoStats* const stats) { - const double xmxm = stats->xm * stats->xm; - const double ymym = stats->ym * stats->ym; - const double xmym = stats->xm * stats->ym; - const double w2 = stats->w * stats->w; - double sxx = stats->xxm * stats->w - xmxm; - double syy = stats->yym * stats->w - ymym; - double sxy = stats->xym * stats->w - xmym; - double C1, C2; - double fnum; - double fden; - // small errors are possible, due to rounding. Clamp to zero. - if (sxx < 0.) sxx = 0.; - if (syy < 0.) syy = 0.; - C1 = 6.5025 * w2; - C2 = 58.5225 * w2; - fnum = (2 * xmym + C1) * (2 * sxy + C2); - fden = (xmxm + ymym + C1) * (sxx + syy + C2); - return (fden != 0.) ? fnum / fden : kMinValue; -} - -double VP8SSIMGetSquaredError(const DistoStats* const s) { - if (s->w > 0.) { - const double iw2 = 1. / (s->w * s->w); - const double sxx = s->xxm * s->w - s->xm * s->xm; - const double syy = s->yym * s->w - s->ym * s->ym; - const double sxy = s->xym * s->w - s->xm * s->ym; - const double SSE = iw2 * (sxx + syy - 2. * sxy); - if (SSE > kMinValue) return SSE; - } - return kMinValue; -} - -void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - int W, int H, DistoStats* const stats) { - int x, y; - for (y = 0; y < H; ++y) { - for (x = 0; x < W; ++x) { - VP8SSIMAccumulate(src1, stride1, src2, stride2, x, y, W, H, stats); - } - } -} - -static double GetMBSSIM(const uint8_t* yuv1, const uint8_t* yuv2) { - int x, y; - DistoStats s = { .0, .0, .0, .0, .0, .0 }; - - // 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); - } - } - 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); - } - } - return VP8SSIMGet(&s); -} - -//------------------------------------------------------------------------------ -// Exposed APIs: Encoder should call the following 3 functions to adjust -// 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; - } - } -} - -void VP8StoreFilterStats(VP8EncIterator* const it) { - int d; - const int s = it->mb_->segment_; - const int level0 = it->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 step_size = (delta_max - delta_min >= 4) ? 4 : 1; - - if (!it->lf_stats_) return; - - // NOTE: Currently we are applying filter only across the sublock edges - // There are two reasons for that. - // 1. Applying filter on macro block edges will change the pixels in - // the left and top macro blocks. That will be hard to restore - // 2. Macro Blocks on the bottom and right are not yet compressed. So we - // cannot apply filter on the right and bottom macro block edges. - if (it->mb_->type_ == 1 && it->mb_->skip_) return; - - // Always try filter level zero - (*it->lf_stats_)[s][0] += GetMBSSIM(it->yuv_in_, it->yuv_out_); - - for (d = delta_min; d <= delta_max; d += step_size) { - const int level = level0 + d; - if (level <= 0 || level >= MAX_LF_LEVELS) { - continue; - } - DoFilter(it, level); - (*it->lf_stats_)[s][level] += GetMBSSIM(it->yuv_in_, it->yuv_out2_); - } -} - -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; - } - } - enc->dqm_[s].fstrength_ = best_level; - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/frame.c b/drivers/webpold/enc/frame.c deleted file mode 100644 index bdd360069b..0000000000 --- a/drivers/webpold/enc/frame.c +++ /dev/null @@ -1,939 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// frame coding and analysis -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <assert.h> -#include <stdlib.h> -#include <string.h> -#include <math.h> - -#include "./vp8enci.h" -#include "./cost.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define SEGMENT_VISU 0 -#define DEBUG_SEARCH 0 // useful to track search convergence - -// On-the-fly info about the current set of residuals. Handy to avoid -// passing zillions of params. -typedef struct { - int first; - int last; - const int16_t* coeffs; - - int coeff_type; - ProbaArray* prob; - StatsArray* stats; - CostArray* cost; -} VP8Residual; - -//------------------------------------------------------------------------------ -// Tables for level coding - -const uint8_t VP8EncBands[16 + 1] = { - 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, - 0 // sentinel -}; - -static const uint8_t kCat3[] = { 173, 148, 140 }; -static const uint8_t kCat4[] = { 176, 155, 140, 135 }; -static const uint8_t kCat5[] = { 180, 157, 141, 134, 130 }; -static const uint8_t kCat6[] = - { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129 }; - -//------------------------------------------------------------------------------ -// Reset the statistics about: number of skips, token proba, level cost,... - -static void ResetStats(VP8Encoder* const enc) { - VP8Proba* const proba = &enc->proba_; - VP8CalculateLevelCosts(proba); - proba->nb_skip_ = 0; -} - -//------------------------------------------------------------------------------ -// Skip decision probability - -#define SKIP_PROBA_THRESHOLD 250 // value below which using skip_proba is OK. - -static int CalcSkipProba(uint64_t nb, uint64_t total) { - return (int)(total ? (total - nb) * 255 / total : 255); -} - -// Returns the bit-cost for coding the skip probability. -static int FinalizeSkipProba(VP8Encoder* const enc) { - VP8Proba* const proba = &enc->proba_; - const int nb_mbs = enc->mb_w_ * enc->mb_h_; - const int nb_events = proba->nb_skip_; - int size; - proba->skip_proba_ = CalcSkipProba(nb_events, nb_mbs); - proba->use_skip_proba_ = (proba->skip_proba_ < SKIP_PROBA_THRESHOLD); - size = 256; // 'use_skip_proba' bit - if (proba->use_skip_proba_) { - size += nb_events * VP8BitCost(1, proba->skip_proba_) - + (nb_mbs - nb_events) * VP8BitCost(0, proba->skip_proba_); - size += 8 * 256; // cost of signaling the skip_proba_ itself. - } - return size; -} - -//------------------------------------------------------------------------------ -// Recording of token probabilities. - -static void ResetTokenStats(VP8Encoder* const enc) { - VP8Proba* const proba = &enc->proba_; - memset(proba->stats_, 0, sizeof(proba->stats_)); -} - -// 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. -static int RecordCoeffs(int ctx, const VP8Residual* const res) { - int n = res->first; - proba_t* s = res->stats[VP8EncBands[n]][ctx]; - if (res->last < 0) { - Record(0, s + 0); - return 0; - } - while (n <= res->last) { - int v; - Record(1, s + 0); - 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; -} - -// Collect statistics and deduce probabilities for next coding pass. -// Return the total bit-cost for coding the probability updates. -static int CalcTokenProba(int nb, int total) { - assert(nb <= total); - return nb ? (255 - nb * 255 / total) : 255; -} - -// Cost of coding 'nb' 1's and 'total-nb' 0's using 'proba' probability. -static int BranchCost(int nb, int total, int proba) { - return nb * VP8BitCost(1, proba) + (total - nb) * VP8BitCost(0, proba); -} - -static int FinalizeTokenProbas(VP8Encoder* const enc) { - VP8Proba* const proba = &enc->proba_; - int has_changed = 0; - int size = 0; - 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) { - const proba_t stats = proba->stats_[t][b][c][p]; - const int nb = (stats >> 0) & 0xffff; - const int total = (stats >> 16) & 0xffff; - const int update_proba = VP8CoeffsUpdateProba[t][b][c][p]; - const int old_p = VP8CoeffsProba0[t][b][c][p]; - const int new_p = CalcTokenProba(nb, total); - const int old_cost = BranchCost(nb, total, old_p) - + VP8BitCost(0, update_proba); - const int new_cost = BranchCost(nb, total, new_p) - + VP8BitCost(1, update_proba) - + 8 * 256; - const int use_new_p = (old_cost > new_cost); - size += VP8BitCost(use_new_p, update_proba); - if (use_new_p) { // only use proba that seem meaningful enough. - proba->coeffs_[t][b][c][p] = new_p; - has_changed |= (new_p != old_p); - size += 8 * 256; - } else { - proba->coeffs_[t][b][c][p] = old_p; - } - } - } - } - } - proba->dirty_ = has_changed; - return size; -} - -//------------------------------------------------------------------------------ -// helper functions for residuals struct VP8Residual. - -static void InitResidual(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->cost = enc->proba_.level_cost_[coeff_type]; - res->first = first; -} - -static void SetResidualCoeffs(const int16_t* const coeffs, - VP8Residual* const res) { - int n; - res->last = -1; - for (n = 15; n >= res->first; --n) { - if (coeffs[n]) { - res->last = n; - break; - } - } - res->coeffs = coeffs; -} - -//------------------------------------------------------------------------------ -// Mode costs - -static int GetResidualCost(int ctx, const VP8Residual* const res) { - int n = res->first; - int p0 = res->prob[VP8EncBands[n]][ctx][0]; - const uint16_t* t = res->cost[VP8EncBands[n]][ctx]; - int cost; - - if (res->last < 0) { - return VP8BitCost(0, p0); - } - cost = 0; - while (n <= res->last) { - const int v = res->coeffs[n]; - const int b = VP8EncBands[n + 1]; - ++n; - if (v == 0) { - // short-case for VP8LevelCost(t, 0) (note: VP8LevelFixedCosts[0] == 0): - cost += t[0]; - t = res->cost[b][0]; - continue; - } - cost += VP8BitCost(1, p0); - if (2u >= (unsigned int)(v + 1)) { // v = -1 or 1 - // short-case for "VP8LevelCost(t, 1)" (256 is VP8LevelFixedCosts[1]): - cost += 256 + t[1]; - p0 = res->prob[b][1][0]; - t = res->cost[b][1]; - } else { - cost += VP8LevelCost(t, abs(v)); - p0 = res->prob[b][2][0]; - t = res->cost[b][2]; - } - } - if (n < 16) cost += VP8BitCost(0, p0); - return cost; -} - -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; - - InitResidual(0, 3, enc, &res); - ctx = it->top_nz_[x] + it->left_nz_[y]; - SetResidualCoeffs(levels, &res); - R += GetResidualCost(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 - InitResidual(0, 1, enc, &res); - SetResidualCoeffs(rd->y_dc_levels, &res); - R += GetResidualCost(it->top_nz_[8] + it->left_nz_[8], &res); - - // AC - InitResidual(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]; - SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); - R += GetResidualCost(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 - - InitResidual(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]; - SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); - R += GetResidualCost(ctx, &res); - it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = (res.last >= 0); - } - } - } - return R; -} - -//------------------------------------------------------------------------------ -// Coefficient coding - -static int PutCoeffs(VP8BitWriter* const bw, int ctx, const VP8Residual* res) { - int n = res->first; - const uint8_t* p = res->prob[VP8EncBands[n]][ctx]; - if (!VP8PutBit(bw, res->last >= 0, p[0])) { - return 0; - } - - while (n < 16) { - const int c = res->coeffs[n++]; - const int sign = c < 0; - int v = sign ? -c : c; - if (!VP8PutBit(bw, v != 0, p[1])) { - p = res->prob[VP8EncBands[n]][0]; - continue; - } - if (!VP8PutBit(bw, v > 1, p[2])) { - p = res->prob[VP8EncBands[n]][1]; - } else { - if (!VP8PutBit(bw, v > 4, p[3])) { - if (VP8PutBit(bw, v != 2, p[4])) - VP8PutBit(bw, v == 4, p[5]); - } else if (!VP8PutBit(bw, v > 10, p[6])) { - if (!VP8PutBit(bw, v > 6, p[7])) { - VP8PutBit(bw, v == 6, 159); - } else { - VP8PutBit(bw, v >= 9, 165); - VP8PutBit(bw, !(v & 1), 145); - } - } else { - int mask; - const uint8_t* tab; - if (v < 3 + (8 << 1)) { // kCat3 (3b) - VP8PutBit(bw, 0, p[8]); - VP8PutBit(bw, 0, p[9]); - v -= 3 + (8 << 0); - mask = 1 << 2; - tab = kCat3; - } else if (v < 3 + (8 << 2)) { // kCat4 (4b) - VP8PutBit(bw, 0, p[8]); - VP8PutBit(bw, 1, p[9]); - v -= 3 + (8 << 1); - mask = 1 << 3; - tab = kCat4; - } else if (v < 3 + (8 << 3)) { // kCat5 (5b) - VP8PutBit(bw, 1, p[8]); - VP8PutBit(bw, 0, p[10]); - v -= 3 + (8 << 2); - mask = 1 << 4; - tab = kCat5; - } else { // kCat6 (11b) - VP8PutBit(bw, 1, p[8]); - VP8PutBit(bw, 1, p[10]); - v -= 3 + (8 << 3); - mask = 1 << 10; - tab = kCat6; - } - while (mask) { - VP8PutBit(bw, !!(v & mask), *tab++); - mask >>= 1; - } - } - p = res->prob[VP8EncBands[n]][2]; - } - VP8PutBitUniform(bw, sign); - if (n == 16 || !VP8PutBit(bw, n <= res->last, p[0])) { - return 1; // EOB - } - } - return 1; -} - -static void CodeResiduals(VP8BitWriter* const bw, - VP8EncIterator* const it, - const VP8ModeScore* const rd) { - int x, y, ch; - VP8Residual res; - uint64_t pos1, pos2, pos3; - const int i16 = (it->mb_->type_ == 1); - const int segment = it->mb_->segment_; - VP8Encoder* const enc = it->enc_; - - VP8IteratorNzToBytes(it); - - pos1 = VP8BitWriterPos(bw); - if (i16) { - InitResidual(0, 1, enc, &res); - SetResidualCoeffs(rd->y_dc_levels, &res); - it->top_nz_[8] = it->left_nz_[8] = - PutCoeffs(bw, it->top_nz_[8] + it->left_nz_[8], &res); - InitResidual(1, 0, enc, &res); - } else { - InitResidual(0, 3, enc, &res); - } - - // luma-AC - for (y = 0; y < 4; ++y) { - for (x = 0; x < 4; ++x) { - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); - it->top_nz_[x] = it->left_nz_[y] = PutCoeffs(bw, ctx, &res); - } - } - pos2 = VP8BitWriterPos(bw); - - // U/V - InitResidual(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]; - SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); - it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = - PutCoeffs(bw, ctx, &res); - } - } - } - pos3 = VP8BitWriterPos(bw); - it->luma_bits_ = pos2 - pos1; - it->uv_bits_ = pos3 - pos2; - it->bit_count_[segment][i16] += it->luma_bits_; - it->bit_count_[segment][2] += it->uv_bits_; - VP8IteratorBytesToNz(it); -} - -// Same as CodeResiduals, but doesn't actually write anything. -// Instead, it just records the event distribution. -static void RecordResiduals(VP8EncIterator* const it, - const VP8ModeScore* const rd) { - int x, y, ch; - VP8Residual res; - VP8Encoder* const enc = it->enc_; - - VP8IteratorNzToBytes(it); - - if (it->mb_->type_ == 1) { // i16x16 - InitResidual(0, 1, enc, &res); - SetResidualCoeffs(rd->y_dc_levels, &res); - it->top_nz_[8] = it->left_nz_[8] = - RecordCoeffs(it->top_nz_[8] + it->left_nz_[8], &res); - InitResidual(1, 0, enc, &res); - } else { - InitResidual(0, 3, enc, &res); - } - - // luma-AC - for (y = 0; y < 4; ++y) { - for (x = 0; x < 4; ++x) { - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); - it->top_nz_[x] = it->left_nz_[y] = RecordCoeffs(ctx, &res); - } - } - - // U/V - InitResidual(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]; - SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); - it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = - RecordCoeffs(ctx, &res); - } - } - } - - VP8IteratorBytesToNz(it); -} - -//------------------------------------------------------------------------------ -// Token buffer - -#ifdef USE_TOKEN_BUFFER - -void VP8TBufferInit(VP8TBuffer* const b) { - b->rows_ = NULL; - b->tokens_ = NULL; - b->last_ = &b->rows_; - b->left_ = 0; - b->error_ = 0; -} - -int VP8TBufferNewPage(VP8TBuffer* const b) { - VP8Tokens* const page = b->error_ ? NULL : (VP8Tokens*)malloc(sizeof(*page)); - if (page == NULL) { - b->error_ = 1; - return 0; - } - *b->last_ = page; - b->last_ = &page->next_; - b->left_ = MAX_NUM_TOKEN; - b->tokens_ = page->tokens_; - return 1; -} - -void VP8TBufferClear(VP8TBuffer* const b) { - if (b != NULL) { - const VP8Tokens* p = b->rows_; - while (p != NULL) { - const VP8Tokens* const next = p->next_; - free((void*)p); - p = next; - } - VP8TBufferInit(b); - } -} - -int VP8EmitTokens(const VP8TBuffer* const b, VP8BitWriter* const bw, - const uint8_t* const probas) { - VP8Tokens* p = b->rows_; - if (b->error_) return 0; - while (p != NULL) { - const int N = (p->next_ == NULL) ? b->left_ : 0; - int n = MAX_NUM_TOKEN; - while (n-- > N) { - VP8PutBit(bw, (p->tokens_[n] >> 15) & 1, probas[p->tokens_[n] & 0x7fff]); - } - p = p->next_; - } - return 1; -} - -#define TOKEN_ID(b, ctx, p) ((p) + NUM_PROBAS * ((ctx) + (b) * NUM_CTX)) - -static int RecordCoeffTokens(int ctx, const VP8Residual* const res, - VP8TBuffer* tokens) { - int n = res->first; - int b = VP8EncBands[n]; - if (!VP8AddToken(tokens, res->last >= 0, TOKEN_ID(b, ctx, 0))) { - return 0; - } - - while (n < 16) { - const int c = res->coeffs[n++]; - const int sign = c < 0; - int v = sign ? -c : c; - const int base_id = TOKEN_ID(b, ctx, 0); - if (!VP8AddToken(tokens, v != 0, base_id + 1)) { - b = VP8EncBands[n]; - ctx = 0; - continue; - } - if (!VP8AddToken(tokens, v > 1, base_id + 2)) { - b = VP8EncBands[n]; - ctx = 1; - } else { - if (!VP8AddToken(tokens, v > 4, base_id + 3)) { - if (VP8AddToken(tokens, v != 2, base_id + 4)) - VP8AddToken(tokens, v == 4, base_id + 5); - } else if (!VP8AddToken(tokens, v > 10, base_id + 6)) { - if (!VP8AddToken(tokens, v > 6, base_id + 7)) { -// VP8AddToken(tokens, v == 6, 159); - } else { -// VP8AddToken(tokens, v >= 9, 165); -// VP8AddToken(tokens, !(v & 1), 145); - } - } else { - int mask; - const uint8_t* tab; - if (v < 3 + (8 << 1)) { // kCat3 (3b) - VP8AddToken(tokens, 0, base_id + 8); - VP8AddToken(tokens, 0, base_id + 9); - v -= 3 + (8 << 0); - mask = 1 << 2; - tab = kCat3; - } else if (v < 3 + (8 << 2)) { // kCat4 (4b) - VP8AddToken(tokens, 0, base_id + 8); - VP8AddToken(tokens, 1, base_id + 9); - v -= 3 + (8 << 1); - mask = 1 << 3; - tab = kCat4; - } else if (v < 3 + (8 << 3)) { // kCat5 (5b) - VP8AddToken(tokens, 1, base_id + 8); - VP8AddToken(tokens, 0, base_id + 10); - v -= 3 + (8 << 2); - mask = 1 << 4; - tab = kCat5; - } else { // kCat6 (11b) - VP8AddToken(tokens, 1, base_id + 8); - VP8AddToken(tokens, 1, base_id + 10); - v -= 3 + (8 << 3); - mask = 1 << 10; - tab = kCat6; - } - while (mask) { - // VP8AddToken(tokens, !!(v & mask), *tab++); - mask >>= 1; - } - } - ctx = 2; - } - b = VP8EncBands[n]; - // VP8PutBitUniform(bw, sign); - if (n == 16 || !VP8AddToken(tokens, n <= res->last, TOKEN_ID(b, ctx, 0))) { - return 1; // EOB - } - } - return 1; -} - -static void RecordTokens(VP8EncIterator* const it, - const VP8ModeScore* const rd, VP8TBuffer tokens[2]) { - int x, y, ch; - VP8Residual res; - VP8Encoder* const enc = it->enc_; - - VP8IteratorNzToBytes(it); - if (it->mb_->type_ == 1) { // i16x16 - InitResidual(0, 1, enc, &res); - SetResidualCoeffs(rd->y_dc_levels, &res); -// TODO(skal): FIX -> it->top_nz_[8] = it->left_nz_[8] = - RecordCoeffTokens(it->top_nz_[8] + it->left_nz_[8], &res, &tokens[0]); - InitResidual(1, 0, enc, &res); - } else { - InitResidual(0, 3, enc, &res); - } - - // luma-AC - for (y = 0; y < 4; ++y) { - for (x = 0; x < 4; ++x) { - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - SetResidualCoeffs(rd->y_ac_levels[x + y * 4], &res); - it->top_nz_[x] = it->left_nz_[y] = - RecordCoeffTokens(ctx, &res, &tokens[0]); - } - } - - // U/V - InitResidual(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]; - SetResidualCoeffs(rd->uv_levels[ch * 2 + x + y * 2], &res); - it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = - RecordCoeffTokens(ctx, &res, &tokens[1]); - } - } - } -} - -#endif // USE_TOKEN_BUFFER - -//------------------------------------------------------------------------------ -// ExtraInfo map / Debug function - -#if SEGMENT_VISU -static void SetBlock(uint8_t* p, int value, int size) { - int y; - for (y = 0; y < size; ++y) { - memset(p, value, size); - p += BPS; - } -} -#endif - -static void ResetSSE(VP8Encoder* const enc) { - memset(enc->sse_, 0, sizeof(enc->sse_)); - enc->sse_count_ = 0; -} - -static void StoreSSE(const VP8EncIterator* const it) { - VP8Encoder* const enc = it->enc_; - const uint8_t* const in = it->yuv_in_; - const uint8_t* const out = it->yuv_out_; - // Note: not totally accurate at boundary. And doesn't include in-loop filter. - enc->sse_[0] += VP8SSE16x16(in + Y_OFF, out + Y_OFF); - enc->sse_[1] += VP8SSE8x8(in + U_OFF, out + U_OFF); - enc->sse_[2] += VP8SSE8x8(in + V_OFF, out + V_OFF); - enc->sse_count_ += 16 * 16; -} - -static void StoreSideInfo(const VP8EncIterator* const it) { - VP8Encoder* const enc = it->enc_; - const VP8MBInfo* const mb = it->mb_; - WebPPicture* const pic = enc->pic_; - - if (pic->stats != NULL) { - StoreSSE(it); - enc->block_count_[0] += (mb->type_ == 0); - enc->block_count_[1] += (mb->type_ == 1); - enc->block_count_[2] += (mb->skip_ != 0); - } - - if (pic->extra_info != NULL) { - uint8_t* const info = &pic->extra_info[it->x_ + it->y_ * enc->mb_w_]; - switch (pic->extra_info_type) { - case 1: *info = mb->type_; break; - case 2: *info = mb->segment_; break; - case 3: *info = enc->dqm_[mb->segment_].quant_; break; - case 4: *info = (mb->type_ == 1) ? it->preds_[0] : 0xff; break; - case 5: *info = mb->uv_mode_; break; - case 6: { - const int b = (int)((it->luma_bits_ + it->uv_bits_ + 7) >> 3); - *info = (b > 255) ? 255 : b; break; - } - default: *info = 0; break; - }; - } -#if SEGMENT_VISU // visualize segments and prediction modes - SetBlock(it->yuv_out_ + Y_OFF, mb->segment_ * 64, 16); - SetBlock(it->yuv_out_ + U_OFF, it->preds_[0] * 64, 8); - SetBlock(it->yuv_out_ + V_OFF, mb->uv_mode_ * 64, 8); -#endif -} - -//------------------------------------------------------------------------------ -// Main loops -// -// VP8EncLoop(): does the final bitstream coding. - -static void ResetAfterSkip(VP8EncIterator* const it) { - if (it->mb_->type_ == 1) { - *it->nz_ = 0; // reset all predictors - it->left_nz_[8] = 0; - } else { - *it->nz_ &= (1 << 24); // preserve the dc_nz bit - } -} - -int VP8EncLoop(VP8Encoder* const enc) { - int i, s, p; - int ok = 1; - VP8EncIterator it; - VP8ModeScore info; - const int dont_use_skip = !enc->proba_.use_skip_proba_; - const int rd_opt = enc->rd_opt_level_; - const int kAverageBytesPerMB = 5; // TODO: have a kTable[quality/10] - const int bytes_per_parts = - enc->mb_w_ * enc->mb_h_ * kAverageBytesPerMB / enc->num_parts_; - - // Initialize the bit-writers - for (p = 0; p < enc->num_parts_; ++p) { - VP8BitWriterInit(enc->parts_ + p, bytes_per_parts); - } - - ResetStats(enc); - ResetSSE(enc); - - VP8IteratorInit(enc, &it); - VP8InitFilter(&it); - do { - VP8IteratorImport(&it); - // Warning! order is important: first call VP8Decimate() and - // *then* decide how to code the skip decision if there's one. - if (!VP8Decimate(&it, &info, rd_opt) || dont_use_skip) { - CodeResiduals(it.bw_, &it, &info); - } else { // reset predictors after a skip - ResetAfterSkip(&it); - } -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (enc->use_layer_) { - VP8EncCodeLayerBlock(&it); - } -#endif - StoreSideInfo(&it); - VP8StoreFilterStats(&it); - VP8IteratorExport(&it); - ok = VP8IteratorProgress(&it, 20); - } while (ok && VP8IteratorNext(&it, it.yuv_out_)); - - if (ok) { // Finalize the partitions, check for extra errors. - for (p = 0; p < enc->num_parts_; ++p) { - VP8BitWriterFinish(enc->parts_ + p); - ok &= !enc->parts_[p].error_; - } - } - - if (ok) { // All good. Finish up. - if (enc->pic_->stats) { // finalize byte counters... - for (i = 0; i <= 2; ++i) { - for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - enc->residual_bytes_[i][s] = (int)((it.bit_count_[s][i] + 7) >> 3); - } - } - } - VP8AdjustFilterStrength(&it); // ...and store filter stats. - } else { - // Something bad happened -> need to do some memory cleanup. - VP8EncFreeBitWriters(enc); - } - - return ok; -} - -//------------------------------------------------------------------------------ -// VP8StatLoop(): only collect statistics (number of skips, token usage, ...) -// This is used for deciding optimal probabilities. It also -// modifies the quantizer value if some target (size, PNSR) -// was specified. - -#define kHeaderSizeEstimate (15 + 20 + 10) // TODO: fix better - -static int OneStatPass(VP8Encoder* const enc, float q, int rd_opt, int nb_mbs, - float* const PSNR, int percent_delta) { - VP8EncIterator it; - uint64_t size = 0; - uint64_t distortion = 0; - const uint64_t pixel_count = nb_mbs * 384; - - // Make sure the quality parameter is inside valid bounds - if (q < 0.) { - q = 0; - } else if (q > 100.) { - q = 100; - } - - VP8SetSegmentParams(enc, q); // setup segment quantizations and filters - - ResetStats(enc); - ResetTokenStats(enc); - - VP8IteratorInit(enc, &it); - do { - VP8ModeScore info; - VP8IteratorImport(&it); - if (VP8Decimate(&it, &info, rd_opt)) { - // Just record the number of skips and act like skip_proba is not used. - enc->proba_.nb_skip_++; - } - RecordResiduals(&it, &info); - size += info.R; - distortion += info.D; - if (percent_delta && !VP8IteratorProgress(&it, percent_delta)) - return 0; - } while (VP8IteratorNext(&it, it.yuv_out_) && --nb_mbs > 0); - size += FinalizeSkipProba(enc); - size += FinalizeTokenProbas(enc); - size += enc->segment_hdr_.size_; - size = ((size + 1024) >> 11) + kHeaderSizeEstimate; - - if (PSNR) { - *PSNR = (float)(10.* log10(255. * 255. * pixel_count / distortion)); - } - return (int)size; -} - -// successive refinement increments. -static const int dqs[] = { 20, 15, 10, 8, 6, 4, 2, 1, 0 }; - -int VP8StatLoop(VP8Encoder* const enc) { - const int do_search = - (enc->config_->target_size > 0 || enc->config_->target_PSNR > 0); - const int fast_probe = (enc->method_ < 2 && !do_search); - float q = enc->config_->quality; - const int max_passes = enc->config_->pass; - const int task_percent = 20; - const int percent_per_pass = (task_percent + max_passes / 2) / max_passes; - const int final_percent = enc->percent_ + task_percent; - int pass; - int nb_mbs; - - // Fast mode: quick analysis pass over few mbs. Better than nothing. - nb_mbs = enc->mb_w_ * enc->mb_h_; - if (fast_probe && nb_mbs > 100) nb_mbs = 100; - - // No target size: just do several pass without changing 'q' - if (!do_search) { - for (pass = 0; pass < max_passes; ++pass) { - const int rd_opt = (enc->method_ > 2); - if (!OneStatPass(enc, q, rd_opt, nb_mbs, NULL, percent_per_pass)) { - return 0; - } - } - } else { - // binary search for a size close to target - for (pass = 0; pass < max_passes && (dqs[pass] > 0); ++pass) { - const int rd_opt = 1; - float PSNR; - int criterion; - const int size = OneStatPass(enc, q, rd_opt, nb_mbs, &PSNR, - percent_per_pass); -#if DEBUG_SEARCH - printf("#%d size=%d PSNR=%.2f q=%.2f\n", pass, size, PSNR, q); -#endif - if (!size) return 0; - if (enc->config_->target_PSNR > 0) { - criterion = (PSNR < enc->config_->target_PSNR); - } else { - criterion = (size < enc->config_->target_size); - } - // dichotomize - if (criterion) { - q += dqs[pass]; - } else { - q -= dqs[pass]; - } - } - } - return WebPReportProgress(enc->pic_, final_percent, &enc->percent_); -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/histogram.c b/drivers/webpold/enc/histogram.c deleted file mode 100644 index ca838e064d..0000000000 --- a/drivers/webpold/enc/histogram.c +++ /dev/null @@ -1,406 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// -#ifdef HAVE_CONFIG_H -#include "config.h" -#endif - -#include <math.h> -#include <stdio.h> - -#include "./backward_references.h" -#include "./histogram.h" -#include "../dsp/lossless.h" -#include "../utils/utils.h" - -static void HistogramClear(VP8LHistogram* const p) { - memset(p->literal_, 0, sizeof(p->literal_)); - memset(p->red_, 0, sizeof(p->red_)); - memset(p->blue_, 0, sizeof(p->blue_)); - memset(p->alpha_, 0, sizeof(p->alpha_)); - memset(p->distance_, 0, sizeof(p->distance_)); - p->bit_cost_ = 0; -} - -void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs, - VP8LHistogram* const histo) { - int i; - for (i = 0; i < refs->size; ++i) { - VP8LHistogramAddSinglePixOrCopy(histo, &refs->refs[i]); - } -} - -void VP8LHistogramCreate(VP8LHistogram* const p, - const VP8LBackwardRefs* const refs, - int palette_code_bits) { - if (palette_code_bits >= 0) { - p->palette_code_bits_ = palette_code_bits; - } - HistogramClear(p); - VP8LHistogramStoreRefs(refs, p); -} - -void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits) { - p->palette_code_bits_ = palette_code_bits; - HistogramClear(p); -} - -VP8LHistogramSet* VP8LAllocateHistogramSet(int size, int cache_bits) { - int i; - VP8LHistogramSet* set; - VP8LHistogram* bulk; - const uint64_t total_size = (uint64_t)sizeof(*set) - + size * sizeof(*set->histograms) - + size * sizeof(**set->histograms); - uint8_t* memory = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*memory)); - if (memory == NULL) return NULL; - - set = (VP8LHistogramSet*)memory; - memory += sizeof(*set); - set->histograms = (VP8LHistogram**)memory; - memory += size * sizeof(*set->histograms); - bulk = (VP8LHistogram*)memory; - set->max_size = size; - set->size = size; - for (i = 0; i < size; ++i) { - set->histograms[i] = bulk + i; - VP8LHistogramInit(set->histograms[i], cache_bits); - } - return set; -} - -// ----------------------------------------------------------------------------- - -void VP8LHistogramAddSinglePixOrCopy(VP8LHistogram* const histo, - const PixOrCopy* const v) { - if (PixOrCopyIsLiteral(v)) { - ++histo->alpha_[PixOrCopyLiteral(v, 3)]; - ++histo->red_[PixOrCopyLiteral(v, 2)]; - ++histo->literal_[PixOrCopyLiteral(v, 1)]; - ++histo->blue_[PixOrCopyLiteral(v, 0)]; - } else if (PixOrCopyIsCacheIdx(v)) { - int literal_ix = 256 + NUM_LENGTH_CODES + PixOrCopyCacheIdx(v); - ++histo->literal_[literal_ix]; - } else { - int code, extra_bits_count, extra_bits_value; - PrefixEncode(PixOrCopyLength(v), - &code, &extra_bits_count, &extra_bits_value); - ++histo->literal_[256 + code]; - PrefixEncode(PixOrCopyDistance(v), - &code, &extra_bits_count, &extra_bits_value); - ++histo->distance_[code]; - } -} - - - -static double BitsEntropy(const int* const array, int n) { - double retval = 0.; - int sum = 0; - int nonzeros = 0; - int max_val = 0; - int i; - double mix; - for (i = 0; i < n; ++i) { - if (array[i] != 0) { - sum += array[i]; - ++nonzeros; - retval -= VP8LFastSLog2(array[i]); - if (max_val < array[i]) { - max_val = array[i]; - } - } - } - retval += VP8LFastSLog2(sum); - - if (nonzeros < 5) { - if (nonzeros <= 1) { - return 0; - } - // Two symbols, they will be 0 and 1 in a Huffman code. - // Let's mix in a bit of entropy to favor good clustering when - // distributions of these are combined. - if (nonzeros == 2) { - return 0.99 * sum + 0.01 * retval; - } - // No matter what the entropy says, we cannot be better than min_limit - // with Huffman coding. I am mixing a bit of entropy into the - // min_limit since it produces much better (~0.5 %) compression results - // perhaps because of better entropy clustering. - if (nonzeros == 3) { - mix = 0.95; - } else { - mix = 0.7; // nonzeros == 4. - } - } else { - mix = 0.627; - } - - { - double min_limit = 2 * sum - max_val; - min_limit = mix * min_limit + (1.0 - mix) * retval; - return (retval < min_limit) ? min_limit : retval; - } -} - -double VP8LHistogramEstimateBitsBulk(const VP8LHistogram* const p) { - double retval = BitsEntropy(&p->literal_[0], VP8LHistogramNumCodes(p)) - + BitsEntropy(&p->red_[0], 256) - + BitsEntropy(&p->blue_[0], 256) - + BitsEntropy(&p->alpha_[0], 256) - + BitsEntropy(&p->distance_[0], NUM_DISTANCE_CODES); - // Compute the extra bits cost. - int i; - for (i = 2; i < NUM_LENGTH_CODES - 2; ++i) { - retval += - (i >> 1) * p->literal_[256 + i + 2]; - } - for (i = 2; i < NUM_DISTANCE_CODES - 2; ++i) { - retval += (i >> 1) * p->distance_[i + 2]; - } - return retval; -} - - -// Returns the cost encode the rle-encoded entropy code. -// The constants in this function are experimental. -static double HuffmanCost(const int* const population, int length) { - // Small bias because Huffman code length is typically not stored in - // full length. - static const int kHuffmanCodeOfHuffmanCodeSize = CODE_LENGTH_CODES * 3; - static const double kSmallBias = 9.1; - double retval = kHuffmanCodeOfHuffmanCodeSize - kSmallBias; - int streak = 0; - int i = 0; - for (; i < length - 1; ++i) { - ++streak; - if (population[i] == population[i + 1]) { - continue; - } - last_streak_hack: - // population[i] points now to the symbol in the streak of same values. - if (streak > 3) { - if (population[i] == 0) { - retval += 1.5625 + 0.234375 * streak; - } else { - retval += 2.578125 + 0.703125 * streak; - } - } else { - if (population[i] == 0) { - retval += 1.796875 * streak; - } else { - retval += 3.28125 * streak; - } - } - streak = 0; - } - if (i == length - 1) { - ++streak; - goto last_streak_hack; - } - return retval; -} - -// Estimates the Huffman dictionary + other block overhead size. -static double HistogramEstimateBitsHeader(const VP8LHistogram* const p) { - return HuffmanCost(&p->alpha_[0], 256) + - HuffmanCost(&p->red_[0], 256) + - HuffmanCost(&p->literal_[0], VP8LHistogramNumCodes(p)) + - HuffmanCost(&p->blue_[0], 256) + - HuffmanCost(&p->distance_[0], NUM_DISTANCE_CODES); -} - -double VP8LHistogramEstimateBits(const VP8LHistogram* const p) { - return HistogramEstimateBitsHeader(p) + VP8LHistogramEstimateBitsBulk(p); -} - -static void HistogramBuildImage(int xsize, int histo_bits, - const VP8LBackwardRefs* const backward_refs, - VP8LHistogramSet* const image) { - int i; - int x = 0, y = 0; - const int histo_xsize = VP8LSubSampleSize(xsize, histo_bits); - VP8LHistogram** const histograms = image->histograms; - assert(histo_bits > 0); - for (i = 0; i < backward_refs->size; ++i) { - const PixOrCopy* const v = &backward_refs->refs[i]; - const int ix = (y >> histo_bits) * histo_xsize + (x >> histo_bits); - VP8LHistogramAddSinglePixOrCopy(histograms[ix], v); - x += PixOrCopyLength(v); - while (x >= xsize) { - x -= xsize; - ++y; - } - } -} - -static uint32_t MyRand(uint32_t *seed) { - *seed *= 16807U; - if (*seed == 0) { - *seed = 1; - } - return *seed; -} - -static int HistogramCombine(const VP8LHistogramSet* const in, - VP8LHistogramSet* const out, int num_pairs) { - int ok = 0; - int i, iter; - uint32_t seed = 0; - int tries_with_no_success = 0; - const int min_cluster_size = 2; - int out_size = in->size; - const int outer_iters = in->size * 3; - VP8LHistogram* const histos = (VP8LHistogram*)malloc(2 * sizeof(*histos)); - VP8LHistogram* cur_combo = histos + 0; // trial merged histogram - VP8LHistogram* best_combo = histos + 1; // best merged histogram so far - if (histos == NULL) goto End; - - // Copy histograms from in[] to out[]. - assert(in->size <= out->size); - for (i = 0; i < in->size; ++i) { - in->histograms[i]->bit_cost_ = VP8LHistogramEstimateBits(in->histograms[i]); - *out->histograms[i] = *in->histograms[i]; - } - - // Collapse similar histograms in 'out'. - for (iter = 0; iter < outer_iters && out_size >= min_cluster_size; ++iter) { - // We pick the best pair to be combined out of 'inner_iters' pairs. - double best_cost_diff = 0.; - int best_idx1 = 0, best_idx2 = 1; - int j; - seed += iter; - for (j = 0; j < num_pairs; ++j) { - double curr_cost_diff; - // Choose two histograms at random and try to combine them. - const uint32_t idx1 = MyRand(&seed) % out_size; - const uint32_t tmp = ((j & 7) + 1) % (out_size - 1); - const uint32_t diff = (tmp < 3) ? tmp : MyRand(&seed) % (out_size - 1); - const uint32_t idx2 = (idx1 + diff + 1) % out_size; - if (idx1 == idx2) { - continue; - } - *cur_combo = *out->histograms[idx1]; - VP8LHistogramAdd(cur_combo, out->histograms[idx2]); - cur_combo->bit_cost_ = VP8LHistogramEstimateBits(cur_combo); - // Calculate cost reduction on combining. - curr_cost_diff = cur_combo->bit_cost_ - - out->histograms[idx1]->bit_cost_ - - out->histograms[idx2]->bit_cost_; - if (best_cost_diff > curr_cost_diff) { // found a better pair? - { // swap cur/best combo histograms - VP8LHistogram* const tmp_histo = cur_combo; - cur_combo = best_combo; - best_combo = tmp_histo; - } - best_cost_diff = curr_cost_diff; - best_idx1 = idx1; - best_idx2 = idx2; - } - } - - if (best_cost_diff < 0.0) { - *out->histograms[best_idx1] = *best_combo; - // swap best_idx2 slot with last one (which is now unused) - --out_size; - if (best_idx2 != out_size) { - out->histograms[best_idx2] = out->histograms[out_size]; - out->histograms[out_size] = NULL; // just for sanity check. - } - tries_with_no_success = 0; - } - if (++tries_with_no_success >= 50) { - break; - } - } - out->size = out_size; - ok = 1; - - End: - free(histos); - return ok; -} - -// ----------------------------------------------------------------------------- -// Histogram refinement - -// What is the bit cost of moving square_histogram from -// cur_symbol to candidate_symbol. -// TODO(skal): we don't really need to copy the histogram and Add(). Instead -// we just need VP8LDualHistogramEstimateBits(A, B) estimation function. -static double HistogramDistance(const VP8LHistogram* const square_histogram, - const VP8LHistogram* const candidate) { - const double previous_bit_cost = candidate->bit_cost_; - double new_bit_cost; - VP8LHistogram modified_histo; - modified_histo = *candidate; - VP8LHistogramAdd(&modified_histo, square_histogram); - new_bit_cost = VP8LHistogramEstimateBits(&modified_histo); - - return new_bit_cost - previous_bit_cost; -} - -// Find the best 'out' histogram for each of the 'in' histograms. -// Note: we assume that out[]->bit_cost_ is already up-to-date. -static void HistogramRemap(const VP8LHistogramSet* const in, - const VP8LHistogramSet* const out, - uint16_t* const symbols) { - int i; - for (i = 0; i < in->size; ++i) { - int best_out = 0; - double best_bits = HistogramDistance(in->histograms[i], out->histograms[0]); - int k; - for (k = 1; k < out->size; ++k) { - const double cur_bits = - HistogramDistance(in->histograms[i], out->histograms[k]); - if (cur_bits < best_bits) { - best_bits = cur_bits; - best_out = k; - } - } - symbols[i] = best_out; - } - - // Recompute each out based on raw and symbols. - for (i = 0; i < out->size; ++i) { - HistogramClear(out->histograms[i]); - } - for (i = 0; i < in->size; ++i) { - VP8LHistogramAdd(out->histograms[symbols[i]], in->histograms[i]); - } -} - -int VP8LGetHistoImageSymbols(int xsize, int ysize, - const VP8LBackwardRefs* const refs, - int quality, int histo_bits, int cache_bits, - VP8LHistogramSet* const image_in, - uint16_t* const histogram_symbols) { - int ok = 0; - const int histo_xsize = histo_bits ? VP8LSubSampleSize(xsize, histo_bits) : 1; - const int histo_ysize = histo_bits ? VP8LSubSampleSize(ysize, histo_bits) : 1; - const int num_histo_pairs = 10 + quality / 2; // For HistogramCombine(). - const int histo_image_raw_size = histo_xsize * histo_ysize; - VP8LHistogramSet* const image_out = - VP8LAllocateHistogramSet(histo_image_raw_size, cache_bits); - if (image_out == NULL) return 0; - - // Build histogram image. - HistogramBuildImage(xsize, histo_bits, refs, image_out); - // Collapse similar histograms. - if (!HistogramCombine(image_out, image_in, num_histo_pairs)) { - goto Error; - } - // Find the optimal map from original histograms to the final ones. - HistogramRemap(image_out, image_in, histogram_symbols); - ok = 1; - -Error: - free(image_out); - return ok; -} diff --git a/drivers/webpold/enc/histogram.h b/drivers/webpold/enc/histogram.h deleted file mode 100644 index 5b5de25539..0000000000 --- a/drivers/webpold/enc/histogram.h +++ /dev/null @@ -1,115 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// -// Models the histograms of literal and distance codes. - -#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" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -// A simple container for histograms of data. -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]; - // Backward reference prefix-code histogram. - int distance_[NUM_DISTANCE_CODES]; - int palette_code_bits_; - double bit_cost_; // cached value of VP8LHistogramEstimateBits(this) -} VP8LHistogram; - -// Collection of histograms with fixed capacity, allocated as one -// big memory chunk. Can be destroyed by simply calling 'free()'. -typedef struct { - int size; // number of slots currently in use - int max_size; // maximum capacity - VP8LHistogram** histograms; -} VP8LHistogramSet; - -// Create the histogram. -// -// The input data is the PixOrCopy data, which models the literals, stop -// codes and backward references (both distances and lengths). Also: if -// palette_code_bits is >= 0, initialize the histogram with this value. -void VP8LHistogramCreate(VP8LHistogram* const p, - const VP8LBackwardRefs* const refs, - int palette_code_bits); - -// Set the palette_code_bits and reset the stats. -void VP8LHistogramInit(VP8LHistogram* const p, int palette_code_bits); - -// Collect all the references into a histogram (without reset) -void VP8LHistogramStoreRefs(const VP8LBackwardRefs* const refs, - VP8LHistogram* 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); - -// 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); -} - -// Builds the histogram image. -int VP8LGetHistoImageSymbols(int xsize, int ysize, - const VP8LBackwardRefs* const refs, - int quality, int histogram_bits, int cache_bits, - VP8LHistogramSet* const image_in, - uint16_t* const histogram_symbols); - -#if defined(__cplusplus) || defined(c_plusplus) -} -#endif - -#endif // WEBP_ENC_HISTOGRAM_H_ diff --git a/drivers/webpold/enc/iterator.c b/drivers/webpold/enc/iterator.c deleted file mode 100644 index 86e473bcf0..0000000000 --- a/drivers/webpold/enc/iterator.c +++ /dev/null @@ -1,422 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// VP8Iterator: block iterator -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <string.h> - -#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_ > 0) ? 129 : 127; - memset(enc->y_left_, 129, 16); - memset(enc->u_left_, 129, 8); - memset(enc->v_left_, 129, 8); - it->left_nz_[8] = 0; -} - -static void InitTop(VP8EncIterator* const it) { - const VP8Encoder* const enc = it->enc_; - const size_t top_size = enc->mb_w_ * 16; - memset(enc->y_top_, 127, 2 * top_size); - memset(enc->nz_, 0, enc->mb_w_ * sizeof(*enc->nz_)); -} - -void VP8IteratorReset(VP8EncIterator* const it) { - 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->nz_ = enc->nz_; - it->bw_ = &enc->parts_[0]; - it->done_ = enc->mb_w_* enc->mb_h_; - InitTop(it); - InitLeft(it); - memset(it->bit_count_, 0, sizeof(it->bit_count_)); - it->do_trellis_ = 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->lf_stats_ = enc->lf_stats_; - it->percent0_ = enc->percent_; - 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) - ? it->percent0_ - : it->percent0_ + delta * it->y_ / (enc->mb_h_ - 1); - return WebPReportProgress(enc->pic_, percent, &enc->percent_); - } - return 1; -} - -//------------------------------------------------------------------------------ -// Import the source samples into the cache. Takes care of replicating -// boundary pixels if necessary. - -static void ImportBlock(const uint8_t* src, int src_stride, - uint8_t* dst, int w, int h, int size) { - int i; - for (i = 0; i < h; ++i) { - memcpy(dst, src, w); - if (w < size) { - memset(dst + w, dst[w - 1], size - w); - } - dst += BPS; - src += src_stride; - } - for (i = h; i < size; ++i) { - memcpy(dst, dst - BPS, size); - dst += BPS; - } -} - -void VP8IteratorImport(const VP8EncIterator* const it) { - 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 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); - - if (w > 16) w = 16; - if (h > 16) h = 16; - - // Luma plane - ImportBlock(ysrc, pic->y_stride, ydst, w, h, 16); - - { // 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); - } -} - -//------------------------------------------------------------------------------ -// Copy back the compressed samples into user space if requested. - -static void ExportBlock(const uint8_t* src, uint8_t* dst, int dst_stride, - int w, int h) { - while (h-- > 0) { - memcpy(dst, src, w); - dst += dst_stride; - src += BPS; - } -} - -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 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; - uint8_t* const vdst = pic->v + (y * pic->uv_stride + x) * 8; - int w = (pic->width - x * 16); - int h = (pic->height - y * 16); - - if (w > 16) w = 16; - if (h > 16) h = 16; - - // Luma plane - ExportBlock(ysrc, ydst, pic->y_stride, w, h); - - { // U/V planes - const int uv_w = (w + 1) >> 1; - const int uv_h = (h + 1) >> 1; - ExportBlock(usrc, udst, pic->uv_stride, uv_w, uv_h); - ExportBlock(vsrc, vdst, pic->uv_stride, uv_w, uv_h); - } - } -} - -//------------------------------------------------------------------------------ -// Non-zero contexts setup/teardown - -// Nz bits: -// 0 1 2 3 Y -// 4 5 6 7 -// 8 9 10 11 -// 12 13 14 15 -// 16 17 U -// 18 19 -// 20 21 V -// 22 23 -// 24 DC-intra16 - -// Convert packed context to byte array -#define BIT(nz, n) (!!((nz) & (1 << (n)))) - -void VP8IteratorNzToBytes(VP8EncIterator* const it) { - const int tnz = it->nz_[0], lnz = it->nz_[-1]; - int* const top_nz = it->top_nz_; - int* const left_nz = it->left_nz_; - - // Top-Y - top_nz[0] = BIT(tnz, 12); - top_nz[1] = BIT(tnz, 13); - top_nz[2] = BIT(tnz, 14); - top_nz[3] = BIT(tnz, 15); - // Top-U - top_nz[4] = BIT(tnz, 18); - top_nz[5] = BIT(tnz, 19); - // Top-V - top_nz[6] = BIT(tnz, 22); - top_nz[7] = BIT(tnz, 23); - // DC - top_nz[8] = BIT(tnz, 24); - - // left-Y - left_nz[0] = BIT(lnz, 3); - left_nz[1] = BIT(lnz, 7); - left_nz[2] = BIT(lnz, 11); - left_nz[3] = BIT(lnz, 15); - // left-U - left_nz[4] = BIT(lnz, 17); - left_nz[5] = BIT(lnz, 19); - // left-V - left_nz[6] = BIT(lnz, 21); - left_nz[7] = BIT(lnz, 23); - // left-DC is special, iterated separately -} - -void VP8IteratorBytesToNz(VP8EncIterator* const it) { - uint32_t nz = 0; - const int* const top_nz = it->top_nz_; - const int* const left_nz = it->left_nz_; - // top - nz |= (top_nz[0] << 12) | (top_nz[1] << 13); - nz |= (top_nz[2] << 14) | (top_nz[3] << 15); - nz |= (top_nz[4] << 18) | (top_nz[5] << 19); - nz |= (top_nz[6] << 22) | (top_nz[7] << 23); - nz |= (top_nz[8] << 24); // we propagate the _top_ bit, esp. for intra4 - // left - nz |= (left_nz[0] << 3) | (left_nz[1] << 7); - nz |= (left_nz[2] << 11); - nz |= (left_nz[4] << 17) | (left_nz[6] << 21); - - *it->nz_ = nz; -} - -#undef BIT - -//------------------------------------------------------------------------------ -// Advance to the next position, doing the bookeeping. - -int VP8IteratorNext(VP8EncIterator* const it, - const uint8_t* const block_to_save) { - 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]; - } - 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); - } - } - - it->mb_++; - 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); - } - return (0 < --it->done_); -} - -//------------------------------------------------------------------------------ -// Helper function to set mode properties - -void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode) { - uint8_t* preds = it->preds_; - int y; - for (y = 0; y < 4; ++y) { - memset(preds, mode, 4); - preds += it->enc_->preds_w_; - } - it->mb_->type_ = 1; -} - -void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes) { - uint8_t* preds = it->preds_; - int y; - for (y = 4; y > 0; --y) { - memcpy(preds, modes, 4 * sizeof(*modes)); - preds += it->enc_->preds_w_; - modes += 4; - } - it->mb_->type_ = 0; -} - -void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode) { - it->mb_->uv_mode_ = mode; -} - -void VP8SetSkip(const VP8EncIterator* const it, int skip) { - it->mb_->skip_ = skip; -} - -void VP8SetSegment(const VP8EncIterator* const it, int segment) { - it->mb_->segment_ = segment; -} - -//------------------------------------------------------------------------------ -// Intra4x4 sub-blocks iteration -// -// We store and update the boundary samples into an array of 37 pixels. They -// are updated as we iterate and reconstructs each intra4x4 blocks in turn. -// The position of the samples has the following snake pattern: -// -// 16|17 18 19 20|21 22 23 24|25 26 27 28|29 30 31 32|33 34 35 36 <- Top-right -// --+-----------+-----------+-----------+-----------+ -// 15| 19| 23| 27| 31| -// 14| 18| 22| 26| 30| -// 13| 17| 21| 25| 29| -// 12|13 14 15 16|17 18 19 20|21 22 23 24|25 26 27 28| -// --+-----------+-----------+-----------+-----------+ -// 11| 15| 19| 23| 27| -// 10| 14| 18| 22| 26| -// 9| 13| 17| 21| 25| -// 8| 9 10 11 12|13 14 15 16|17 18 19 20|21 22 23 24| -// --+-----------+-----------+-----------+-----------+ -// 7| 11| 15| 19| 23| -// 6| 10| 14| 18| 22| -// 5| 9| 13| 17| 21| -// 4| 5 6 7 8| 9 10 11 12|13 14 15 16|17 18 19 20| -// --+-----------+-----------+-----------+-----------+ -// 3| 7| 11| 15| 19| -// 2| 6| 10| 14| 18| -// 1| 5| 9| 13| 17| -// 0| 1 2 3 4| 5 6 7 8| 9 10 11 12|13 14 15 16| -// --+-----------+-----------+-----------+-----------+ - -// Array to record the position of the top sample to pass to the prediction -// functions in dsp.c. -static const uint8_t VP8TopLeftI4[16] = { - 17, 21, 25, 29, - 13, 17, 21, 25, - 9, 13, 17, 21, - 5, 9, 13, 17 -}; - -void VP8IteratorStartI4(VP8EncIterator* const it) { - const VP8Encoder* const enc = it->enc_; - int i; - - it->i4_ = 0; // first 4x4 sub-block - it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[0]; - - // Import the boundary samples - for (i = 0; i < 17; ++i) { // left - it->i4_boundary_[i] = enc->y_left_[15 - i]; - } - for (i = 0; i < 16; ++i) { // top - it->i4_boundary_[17 + i] = enc->y_top_[it->x_ * 16 + 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]; - } - } else { // else, replicate the last valid pixel four times - for (i = 16; i < 16 + 4; ++i) { - it->i4_boundary_[17 + i] = it->i4_boundary_[17 + 15]; - } - } - VP8IteratorNzToBytes(it); // import the non-zero context -} - -int VP8IteratorRotateI4(VP8EncIterator* const it, - const uint8_t* const yuv_out) { - const uint8_t* const blk = yuv_out + VP8Scan[it->i4_]; - uint8_t* const top = it->i4_top_; - int i; - - // Update the cache with 7 fresh samples - for (i = 0; i <= 3; ++i) { - top[-4 + i] = blk[i + 3 * BPS]; // store future top samples - } - if ((it->i4_ & 3) != 3) { // if not on the right sub-blocks #3, #7, #11, #15 - for (i = 0; i <= 2; ++i) { // store future left samples - top[i] = blk[3 + (2 - i) * BPS]; - } - } else { // else replicate top-right samples, as says the specs. - for (i = 0; i <= 3; ++i) { - top[i] = top[i + 4]; - } - } - // move pointers to next sub-block - ++it->i4_; - if (it->i4_ == 16) { // we're done - return 0; - } - - it->i4_top_ = it->i4_boundary_ + VP8TopLeftI4[it->i4_]; - return 1; -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/layer.c b/drivers/webpold/enc/layer.c deleted file mode 100644 index 423127df63..0000000000 --- a/drivers/webpold/enc/layer.c +++ /dev/null @@ -1,49 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Enhancement layer (for YUV444/422) -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <stdlib.h> - -#include "./vp8enci.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ - -void VP8EncInitLayer(VP8Encoder* const enc) { - enc->use_layer_ = (enc->pic_->u0 != NULL); - enc->layer_data_size_ = 0; - enc->layer_data_ = NULL; - if (enc->use_layer_) { - VP8BitWriterInit(&enc->layer_bw_, enc->mb_w_ * enc->mb_h_ * 3); - } -} - -void VP8EncCodeLayerBlock(VP8EncIterator* it) { - (void)it; // remove a warning -} - -int VP8EncFinishLayer(VP8Encoder* const enc) { - if (enc->use_layer_) { - enc->layer_data_ = VP8BitWriterFinish(&enc->layer_bw_); - enc->layer_data_size_ = VP8BitWriterSize(&enc->layer_bw_); - } - return 1; -} - -void VP8EncDeleteLayer(VP8Encoder* enc) { - free(enc->layer_data_); -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/picture.c b/drivers/webpold/enc/picture.c deleted file mode 100644 index 44eed06083..0000000000 --- a/drivers/webpold/enc/picture.c +++ /dev/null @@ -1,1041 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// WebPPicture utils: colorspace conversion, crop, ... -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <assert.h> -#include <stdlib.h> -#include <math.h> - -#include "./vp8enci.h" -#include "../utils/rescaler.h" -#include "../utils/utils.h" -#include "../dsp/dsp.h" -#include "../dsp/yuv.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define HALVE(x) (((x) + 1) >> 1) -#define IS_YUV_CSP(csp, YUV_CSP) (((csp) & WEBP_CSP_UV_MASK) == (YUV_CSP)) - -static const union { - uint32_t argb; - uint8_t bytes[4]; -} test_endian = { 0xff000000u }; -#define ALPHA_IS_LAST (test_endian.bytes[3] == 0xff) - -//------------------------------------------------------------------------------ -// WebPPicture -//------------------------------------------------------------------------------ - -int WebPPictureAlloc(WebPPicture* picture) { - if (picture != NULL) { - const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; - const int has_alpha = picture->colorspace & WEBP_CSP_ALPHA_BIT; - const int width = picture->width; - const int height = picture->height; - - if (!picture->use_argb) { - const int y_stride = width; - const int uv_width = HALVE(width); - const int uv_height = HALVE(height); - const int uv_stride = uv_width; - int uv0_stride = 0; - int a_width, a_stride; - uint64_t y_size, uv_size, uv0_size, a_size, total_size; - uint8_t* mem; - - // U/V - switch (uv_csp) { - case WEBP_YUV420: - break; -#ifdef WEBP_EXPERIMENTAL_FEATURES - case WEBP_YUV400: // for now, we'll just reset the U/V samples - break; - case WEBP_YUV422: - uv0_stride = uv_width; - break; - case WEBP_YUV444: - uv0_stride = width; - break; -#endif - default: - return 0; - } - uv0_size = height * uv0_stride; - - // alpha - a_width = has_alpha ? width : 0; - a_stride = a_width; - y_size = (uint64_t)y_stride * height; - uv_size = (uint64_t)uv_stride * uv_height; - a_size = (uint64_t)a_stride * height; - - total_size = y_size + a_size + 2 * uv_size + 2 * uv0_size; - - // Security and validation checks - if (width <= 0 || height <= 0 || // luma/alpha param error - uv_width < 0 || uv_height < 0) { // u/v param error - return 0; - } - // Clear previous buffer and allocate a new one. - WebPPictureFree(picture); // erase previous buffer - mem = (uint8_t*)WebPSafeMalloc(total_size, sizeof(*mem)); - if (mem == NULL) return 0; - - // From now on, we're in the clear, we can no longer fail... - picture->memory_ = (void*)mem; - picture->y_stride = y_stride; - picture->uv_stride = uv_stride; - picture->a_stride = a_stride; - picture->uv0_stride = uv0_stride; - // TODO(skal): we could align the y/u/v planes and adjust stride. - picture->y = mem; - mem += y_size; - - picture->u = mem; - mem += uv_size; - picture->v = mem; - mem += uv_size; - - if (a_size) { - picture->a = mem; - mem += a_size; - } - if (uv0_size) { - picture->u0 = mem; - mem += uv0_size; - picture->v0 = mem; - mem += uv0_size; - } - } else { - void* memory; - const uint64_t argb_size = (uint64_t)width * height; - if (width <= 0 || height <= 0) { - return 0; - } - // Clear previous buffer and allocate a new one. - WebPPictureFree(picture); // erase previous buffer - memory = WebPSafeMalloc(argb_size, sizeof(*picture->argb)); - if (memory == NULL) return 0; - - // TODO(skal): align plane to cache line? - picture->memory_argb_ = memory; - picture->argb = (uint32_t*)memory; - picture->argb_stride = width; - } - } - return 1; -} - -// Remove reference to the ARGB buffer (doesn't free anything). -static void PictureResetARGB(WebPPicture* const picture) { - picture->memory_argb_ = NULL; - picture->argb = NULL; - picture->argb_stride = 0; -} - -// Remove reference to the YUVA buffer (doesn't free anything). -static void PictureResetYUVA(WebPPicture* const picture) { - picture->memory_ = NULL; - picture->y = picture->u = picture->v = picture->a = NULL; - picture->u0 = picture->v0 = NULL; - picture->y_stride = picture->uv_stride = 0; - picture->a_stride = 0; - picture->uv0_stride = 0; -} - -// Grab the 'specs' (writer, *opaque, width, height...) from 'src' and copy them -// into 'dst'. Mark 'dst' as not owning any memory. -static void WebPPictureGrabSpecs(const WebPPicture* const src, - WebPPicture* const dst) { - assert(src != NULL && dst != NULL); - *dst = *src; - PictureResetYUVA(dst); - PictureResetARGB(dst); -} - -// Allocate a new argb buffer, discarding any existing one and preserving -// the other YUV(A) buffer. -static int PictureAllocARGB(WebPPicture* const picture) { - WebPPicture tmp; - free(picture->memory_argb_); - PictureResetARGB(picture); - picture->use_argb = 1; - WebPPictureGrabSpecs(picture, &tmp); - if (!WebPPictureAlloc(&tmp)) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); - } - picture->memory_argb_ = tmp.memory_argb_; - picture->argb = tmp.argb; - picture->argb_stride = tmp.argb_stride; - return 1; -} - -// Release memory owned by 'picture' (both YUV and ARGB buffers). -void WebPPictureFree(WebPPicture* picture) { - if (picture != NULL) { - free(picture->memory_); - free(picture->memory_argb_); - PictureResetYUVA(picture); - PictureResetARGB(picture); - } -} - -//------------------------------------------------------------------------------ -// Picture copying - -// Not worth moving to dsp/enc.c (only used here). -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; - } -} - -// Adjust top-left corner to chroma sample position. -static void SnapTopLeftPosition(const WebPPicture* const pic, - int* const left, int* const top) { - if (!pic->use_argb) { - const int is_yuv422 = IS_YUV_CSP(pic->colorspace, WEBP_YUV422); - if (IS_YUV_CSP(pic->colorspace, WEBP_YUV420) || is_yuv422) { - *left &= ~1; - if (!is_yuv422) *top &= ~1; - } - } -} - -// Adjust top-left corner and verify that the sub-rectangle is valid. -static int AdjustAndCheckRectangle(const WebPPicture* const pic, - int* const left, int* const top, - int width, int height) { - SnapTopLeftPosition(pic, left, top); - if ((*left) < 0 || (*top) < 0) return 0; - if (width <= 0 || height <= 0) return 0; - if ((*left) + width > pic->width) return 0; - if ((*top) + height > pic->height) return 0; - return 1; -} - -int WebPPictureCopy(const WebPPicture* src, WebPPicture* dst) { - if (src == NULL || dst == NULL) return 0; - if (src == dst) return 1; - - WebPPictureGrabSpecs(src, dst); - if (!WebPPictureAlloc(dst)) return 0; - - if (!src->use_argb) { - CopyPlane(src->y, src->y_stride, - dst->y, dst->y_stride, dst->width, dst->height); - CopyPlane(src->u, src->uv_stride, - dst->u, dst->uv_stride, HALVE(dst->width), HALVE(dst->height)); - CopyPlane(src->v, src->uv_stride, - dst->v, dst->uv_stride, HALVE(dst->width), HALVE(dst->height)); - if (dst->a != NULL) { - CopyPlane(src->a, src->a_stride, - dst->a, dst->a_stride, dst->width, dst->height); - } -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (dst->u0 != NULL) { - int uv0_width = src->width; - if (IS_YUV_CSP(dst->colorspace, WEBP_YUV422)) { - uv0_width = HALVE(uv0_width); - } - CopyPlane(src->u0, src->uv0_stride, - dst->u0, dst->uv0_stride, uv0_width, dst->height); - CopyPlane(src->v0, src->uv0_stride, - dst->v0, dst->uv0_stride, uv0_width, dst->height); - } -#endif - } else { - CopyPlane((const uint8_t*)src->argb, 4 * src->argb_stride, - (uint8_t*)dst->argb, 4 * dst->argb_stride, - 4 * dst->width, dst->height); - } - return 1; -} - -int WebPPictureIsView(const WebPPicture* picture) { - if (picture == NULL) return 0; - if (picture->use_argb) { - return (picture->memory_argb_ == NULL); - } - return (picture->memory_ == NULL); -} - -int WebPPictureView(const WebPPicture* src, - int left, int top, int width, int height, - WebPPicture* dst) { - if (src == NULL || dst == NULL) return 0; - - // verify rectangle position. - if (!AdjustAndCheckRectangle(src, &left, &top, width, height)) return 0; - - if (src != dst) { // beware of aliasing! We don't want to leak 'memory_'. - WebPPictureGrabSpecs(src, dst); - } - dst->width = width; - dst->height = height; - if (!src->use_argb) { - dst->y = src->y + top * src->y_stride + left; - dst->u = src->u + (top >> 1) * src->uv_stride + (left >> 1); - dst->v = src->v + (top >> 1) * src->uv_stride + (left >> 1); - if (src->a != NULL) { - dst->a = src->a + top * src->a_stride + left; - } -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (src->u0 != NULL) { - const int left_pos = - IS_YUV_CSP(dst->colorspace, WEBP_YUV422) ? (left >> 1) : left; - dst->u0 = src->u0 + top * src->uv0_stride + left_pos; - dst->v0 = src->v0 + top * src->uv0_stride + left_pos; - } -#endif - } else { - dst->argb = src->argb + top * src->argb_stride + left; - } - return 1; -} - -//------------------------------------------------------------------------------ -// Picture cropping - -int WebPPictureCrop(WebPPicture* pic, - int left, int top, int width, int height) { - WebPPicture tmp; - - if (pic == NULL) return 0; - if (!AdjustAndCheckRectangle(pic, &left, &top, width, height)) return 0; - - WebPPictureGrabSpecs(pic, &tmp); - tmp.width = width; - tmp.height = height; - if (!WebPPictureAlloc(&tmp)) return 0; - - if (!pic->use_argb) { - const int y_offset = top * pic->y_stride + left; - const int uv_offset = (top / 2) * pic->uv_stride + left / 2; - CopyPlane(pic->y + y_offset, pic->y_stride, - tmp.y, tmp.y_stride, width, height); - CopyPlane(pic->u + uv_offset, pic->uv_stride, - tmp.u, tmp.uv_stride, HALVE(width), HALVE(height)); - CopyPlane(pic->v + uv_offset, pic->uv_stride, - tmp.v, tmp.uv_stride, HALVE(width), HALVE(height)); - - if (tmp.a != NULL) { - const int a_offset = top * pic->a_stride + left; - CopyPlane(pic->a + a_offset, pic->a_stride, - tmp.a, tmp.a_stride, width, height); - } -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (tmp.u0 != NULL) { - int w = width; - int left_pos = left; - if (IS_YUV_CSP(tmp.colorspace, WEBP_YUV422)) { - w = HALVE(w); - left_pos = HALVE(left_pos); - } - CopyPlane(pic->u0 + top * pic->uv0_stride + left_pos, pic->uv0_stride, - tmp.u0, tmp.uv0_stride, w, height); - CopyPlane(pic->v0 + top * pic->uv0_stride + left_pos, pic->uv0_stride, - tmp.v0, tmp.uv0_stride, w, height); - } -#endif - } else { - const uint8_t* const src = - (const uint8_t*)(pic->argb + top * pic->argb_stride + left); - CopyPlane(src, pic->argb_stride * 4, - (uint8_t*)tmp.argb, tmp.argb_stride * 4, - width * 4, height); - } - WebPPictureFree(pic); - *pic = tmp; - return 1; -} - -//------------------------------------------------------------------------------ -// Simple picture rescaler - -static void RescalePlane(const uint8_t* src, - int src_width, int src_height, int src_stride, - uint8_t* dst, - int dst_width, int dst_height, int dst_stride, - int32_t* const work, - int num_channels) { - WebPRescaler rescaler; - int y = 0; - WebPRescalerInit(&rescaler, src_width, src_height, - dst, dst_width, dst_height, dst_stride, - num_channels, - src_width, dst_width, - src_height, dst_height, - work); - memset(work, 0, 2 * dst_width * num_channels * sizeof(*work)); - while (y < src_height) { - y += WebPRescalerImport(&rescaler, src_height - y, - src + y * src_stride, src_stride); - WebPRescalerExport(&rescaler); - } -} - -int WebPPictureRescale(WebPPicture* pic, int width, int height) { - WebPPicture tmp; - int prev_width, prev_height; - int32_t* work; - - if (pic == NULL) return 0; - prev_width = pic->width; - prev_height = pic->height; - // if width is unspecified, scale original proportionally to height ratio. - if (width == 0) { - width = (prev_width * height + prev_height / 2) / prev_height; - } - // if height is unspecified, scale original proportionally to width ratio. - if (height == 0) { - height = (prev_height * width + prev_width / 2) / prev_width; - } - // Check if the overall dimensions still make sense. - if (width <= 0 || height <= 0) return 0; - - WebPPictureGrabSpecs(pic, &tmp); - tmp.width = width; - tmp.height = height; - if (!WebPPictureAlloc(&tmp)) return 0; - - if (!pic->use_argb) { - work = (int32_t*)WebPSafeMalloc(2ULL * width, sizeof(*work)); - if (work == NULL) { - WebPPictureFree(&tmp); - return 0; - } - - RescalePlane(pic->y, prev_width, prev_height, pic->y_stride, - tmp.y, width, height, tmp.y_stride, work, 1); - RescalePlane(pic->u, - HALVE(prev_width), HALVE(prev_height), pic->uv_stride, - tmp.u, - HALVE(width), HALVE(height), tmp.uv_stride, work, 1); - RescalePlane(pic->v, - HALVE(prev_width), HALVE(prev_height), pic->uv_stride, - tmp.v, - HALVE(width), HALVE(height), tmp.uv_stride, work, 1); - - if (tmp.a != NULL) { - RescalePlane(pic->a, prev_width, prev_height, pic->a_stride, - tmp.a, width, height, tmp.a_stride, work, 1); - } -#ifdef WEBP_EXPERIMENTAL_FEATURES - if (tmp.u0 != NULL) { - const int s = IS_YUV_CSP(tmp.colorspace, WEBP_YUV422) ? 2 : 1; - RescalePlane( - pic->u0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride, - tmp.u0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1); - RescalePlane( - pic->v0, (prev_width + s / 2) / s, prev_height, pic->uv0_stride, - tmp.v0, (width + s / 2) / s, height, tmp.uv0_stride, work, 1); - } -#endif - } else { - work = (int32_t*)WebPSafeMalloc(2ULL * width * 4, sizeof(*work)); - if (work == NULL) { - WebPPictureFree(&tmp); - return 0; - } - - RescalePlane((const uint8_t*)pic->argb, prev_width, prev_height, - pic->argb_stride * 4, - (uint8_t*)tmp.argb, width, height, - tmp.argb_stride * 4, - work, 4); - - } - WebPPictureFree(pic); - free(work); - *pic = tmp; - return 1; -} - -//------------------------------------------------------------------------------ -// WebPMemoryWriter: Write-to-memory - -void WebPMemoryWriterInit(WebPMemoryWriter* writer) { - writer->mem = NULL; - writer->size = 0; - writer->max_size = 0; -} - -int WebPMemoryWrite(const uint8_t* data, size_t data_size, - const WebPPicture* picture) { - WebPMemoryWriter* const w = (WebPMemoryWriter*)picture->custom_ptr; - uint64_t next_size; - if (w == NULL) { - return 1; - } - next_size = (uint64_t)w->size + data_size; - if (next_size > w->max_size) { - uint8_t* new_mem; - uint64_t next_max_size = 2ULL * w->max_size; - if (next_max_size < next_size) next_max_size = next_size; - if (next_max_size < 8192ULL) next_max_size = 8192ULL; - new_mem = (uint8_t*)WebPSafeMalloc(next_max_size, 1); - if (new_mem == NULL) { - return 0; - } - if (w->size > 0) { - memcpy(new_mem, w->mem, w->size); - } - free(w->mem); - w->mem = new_mem; - // down-cast is ok, thanks to WebPSafeMalloc - w->max_size = (size_t)next_max_size; - } - if (data_size > 0) { - memcpy(w->mem + w->size, data, data_size); - w->size += data_size; - } - return 1; -} - -//------------------------------------------------------------------------------ -// Detection of non-trivial transparency - -// Returns true if alpha[] has non-0xff values. -static int CheckNonOpaque(const uint8_t* alpha, int width, int height, - int x_step, int y_step) { - if (alpha == NULL) return 0; - while (height-- > 0) { - int x; - for (x = 0; x < width * x_step; x += x_step) { - if (alpha[x] != 0xff) return 1; // TODO(skal): check 4/8 bytes at a time. - } - alpha += y_step; - } - return 0; -} - -// Checking for the presence of non-opaque alpha. -int WebPPictureHasTransparency(const WebPPicture* picture) { - if (picture == NULL) return 0; - if (!picture->use_argb) { - return CheckNonOpaque(picture->a, picture->width, picture->height, - 1, picture->a_stride); - } else { - int x, y; - const uint32_t* argb = picture->argb; - if (argb == NULL) return 0; - for (y = 0; y < picture->height; ++y) { - for (x = 0; x < picture->width; ++x) { - if (argb[x] < 0xff000000u) return 1; // test any alpha values != 0xff - } - argb += picture->argb_stride; - } - } - return 0; -} - -//------------------------------------------------------------------------------ -// RGB -> YUV conversion - -// TODO: we can do better than simply 2x2 averaging on U/V samples. -#define SUM4(ptr) ((ptr)[0] + (ptr)[step] + \ - (ptr)[rgb_stride] + (ptr)[rgb_stride + step]) -#define SUM2H(ptr) (2 * (ptr)[0] + 2 * (ptr)[step]) -#define SUM2V(ptr) (2 * (ptr)[0] + 2 * (ptr)[rgb_stride]) -#define SUM1(ptr) (4 * (ptr)[0]) -#define RGB_TO_UV(x, y, SUM) { \ - const int src = (2 * (step * (x) + (y) * rgb_stride)); \ - const int dst = (x) + (y) * picture->uv_stride; \ - const int r = SUM(r_ptr + src); \ - const int g = SUM(g_ptr + src); \ - const int b = SUM(b_ptr + src); \ - picture->u[dst] = VP8RGBToU(r, g, b); \ - picture->v[dst] = VP8RGBToV(r, g, b); \ -} - -#define RGB_TO_UV0(x_in, x_out, y, SUM) { \ - const int src = (step * (x_in) + (y) * rgb_stride); \ - const int dst = (x_out) + (y) * picture->uv0_stride; \ - const int r = SUM(r_ptr + src); \ - const int g = SUM(g_ptr + src); \ - const int b = SUM(b_ptr + src); \ - picture->u0[dst] = VP8RGBToU(r, g, b); \ - picture->v0[dst] = VP8RGBToV(r, g, b); \ -} - -static void MakeGray(WebPPicture* const picture) { - int y; - const int uv_width = HALVE(picture->width); - const int uv_height = HALVE(picture->height); - for (y = 0; y < uv_height; ++y) { - memset(picture->u + y * picture->uv_stride, 128, uv_width); - memset(picture->v + y * picture->uv_stride, 128, uv_width); - } -} - -static int ImportYUVAFromRGBA(const uint8_t* const r_ptr, - const uint8_t* const g_ptr, - const uint8_t* const b_ptr, - const uint8_t* const a_ptr, - int step, // bytes per pixel - int rgb_stride, // bytes per scanline - WebPPicture* const picture) { - const WebPEncCSP uv_csp = picture->colorspace & WEBP_CSP_UV_MASK; - int x, y; - const int width = picture->width; - const int height = picture->height; - const int has_alpha = CheckNonOpaque(a_ptr, width, height, step, rgb_stride); - - picture->colorspace = uv_csp; - picture->use_argb = 0; - if (has_alpha) { - picture->colorspace |= WEBP_CSP_ALPHA_BIT; - } - if (!WebPPictureAlloc(picture)) return 0; - - // Import luma plane - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const int offset = step * x + y * rgb_stride; - picture->y[x + y * picture->y_stride] = - VP8RGBToY(r_ptr[offset], g_ptr[offset], b_ptr[offset]); - } - } - - // Downsample U/V plane - if (uv_csp != WEBP_YUV400) { - for (y = 0; y < (height >> 1); ++y) { - for (x = 0; x < (width >> 1); ++x) { - RGB_TO_UV(x, y, SUM4); - } - if (width & 1) { - RGB_TO_UV(x, y, SUM2V); - } - } - if (height & 1) { - for (x = 0; x < (width >> 1); ++x) { - RGB_TO_UV(x, y, SUM2H); - } - if (width & 1) { - RGB_TO_UV(x, y, SUM1); - } - } - -#ifdef WEBP_EXPERIMENTAL_FEATURES - // Store original U/V samples too - if (uv_csp == WEBP_YUV422) { - for (y = 0; y < height; ++y) { - for (x = 0; x < (width >> 1); ++x) { - RGB_TO_UV0(2 * x, x, y, SUM2H); - } - if (width & 1) { - RGB_TO_UV0(2 * x, x, y, SUM1); - } - } - } else if (uv_csp == WEBP_YUV444) { - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - RGB_TO_UV0(x, x, y, SUM1); - } - } - } -#endif - } else { - MakeGray(picture); - } - - if (has_alpha) { - assert(step >= 4); - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - picture->a[x + y * picture->a_stride] = - a_ptr[step * x + y * rgb_stride]; - } - } - } - return 1; -} - -static int Import(WebPPicture* const picture, - const uint8_t* const rgb, int rgb_stride, - int step, int swap_rb, int import_alpha) { - const uint8_t* const r_ptr = rgb + (swap_rb ? 2 : 0); - const uint8_t* const g_ptr = rgb + 1; - const uint8_t* const b_ptr = rgb + (swap_rb ? 0 : 2); - const uint8_t* const a_ptr = import_alpha ? rgb + 3 : NULL; - const int width = picture->width; - const int height = picture->height; - - if (!picture->use_argb) { - return ImportYUVAFromRGBA(r_ptr, g_ptr, b_ptr, a_ptr, step, rgb_stride, - picture); - } - if (import_alpha) { - picture->colorspace |= WEBP_CSP_ALPHA_BIT; - } else { - picture->colorspace &= ~WEBP_CSP_ALPHA_BIT; - } - if (!WebPPictureAlloc(picture)) return 0; - - if (!import_alpha) { - int x, y; - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const int offset = step * x + y * rgb_stride; - const uint32_t argb = - 0xff000000u | - (r_ptr[offset] << 16) | - (g_ptr[offset] << 8) | - (b_ptr[offset]); - picture->argb[x + y * picture->argb_stride] = argb; - } - } - } else { - int x, y; - assert(step >= 4); - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const int offset = step * x + y * rgb_stride; - const uint32_t argb = (a_ptr[offset] << 24) | - (r_ptr[offset] << 16) | - (g_ptr[offset] << 8) | - (b_ptr[offset]); - picture->argb[x + y * picture->argb_stride] = argb; - } - } - } - return 1; -} -#undef SUM4 -#undef SUM2V -#undef SUM2H -#undef SUM1 -#undef RGB_TO_UV - -int WebPPictureImportRGB(WebPPicture* picture, - const uint8_t* rgb, int rgb_stride) { - return Import(picture, rgb, rgb_stride, 3, 0, 0); -} - -int WebPPictureImportBGR(WebPPicture* picture, - const uint8_t* rgb, int rgb_stride) { - return Import(picture, rgb, rgb_stride, 3, 1, 0); -} - -int WebPPictureImportRGBA(WebPPicture* picture, - const uint8_t* rgba, int rgba_stride) { - return Import(picture, rgba, rgba_stride, 4, 0, 1); -} - -int WebPPictureImportBGRA(WebPPicture* picture, - const uint8_t* rgba, int rgba_stride) { - return Import(picture, rgba, rgba_stride, 4, 1, 1); -} - -int WebPPictureImportRGBX(WebPPicture* picture, - const uint8_t* rgba, int rgba_stride) { - return Import(picture, rgba, rgba_stride, 4, 0, 0); -} - -int WebPPictureImportBGRX(WebPPicture* picture, - const uint8_t* rgba, int rgba_stride) { - return Import(picture, rgba, rgba_stride, 4, 1, 0); -} - -//------------------------------------------------------------------------------ -// Automatic YUV <-> ARGB conversions. - -int WebPPictureYUVAToARGB(WebPPicture* picture) { - if (picture == NULL) return 0; - if (picture->memory_ == NULL || picture->y == NULL || - picture->u == NULL || picture->v == NULL) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); - } - if ((picture->colorspace & WEBP_CSP_ALPHA_BIT) && picture->a == NULL) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); - } - if ((picture->colorspace & WEBP_CSP_UV_MASK) != WEBP_YUV420) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_INVALID_CONFIGURATION); - } - // Allocate a new argb buffer (discarding the previous one). - if (!PictureAllocARGB(picture)) return 0; - - // Convert - { - int y; - const int width = picture->width; - const int height = picture->height; - const int argb_stride = 4 * picture->argb_stride; - uint8_t* dst = (uint8_t*)picture->argb; - const uint8_t *cur_u = picture->u, *cur_v = picture->v, *cur_y = picture->y; - WebPUpsampleLinePairFunc upsample = WebPGetLinePairConverter(ALPHA_IS_LAST); - - // First row, with replicated top samples. - upsample(NULL, cur_y, cur_u, cur_v, cur_u, cur_v, NULL, dst, width); - cur_y += picture->y_stride; - dst += argb_stride; - // Center rows. - for (y = 1; y + 1 < height; y += 2) { - const uint8_t* const top_u = cur_u; - const uint8_t* const top_v = cur_v; - cur_u += picture->uv_stride; - cur_v += picture->uv_stride; - upsample(cur_y, cur_y + picture->y_stride, top_u, top_v, cur_u, cur_v, - dst, dst + argb_stride, width); - cur_y += 2 * picture->y_stride; - dst += 2 * argb_stride; - } - // Last row (if needed), with replicated bottom samples. - if (height > 1 && !(height & 1)) { - upsample(cur_y, NULL, cur_u, cur_v, cur_u, cur_v, dst, NULL, width); - } - // Insert alpha values if needed, in replacement for the default 0xff ones. - if (picture->colorspace & WEBP_CSP_ALPHA_BIT) { - for (y = 0; y < height; ++y) { - uint32_t* const dst = picture->argb + y * picture->argb_stride; - const uint8_t* const src = picture->a + y * picture->a_stride; - int x; - for (x = 0; x < width; ++x) { - dst[x] = (dst[x] & 0x00ffffffu) | (src[x] << 24); - } - } - } - } - return 1; -} - -int WebPPictureARGBToYUVA(WebPPicture* picture, WebPEncCSP colorspace) { - if (picture == NULL) return 0; - if (picture->argb == NULL) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_NULL_PARAMETER); - } else { - const uint8_t* const argb = (const uint8_t*)picture->argb; - const uint8_t* const r = ALPHA_IS_LAST ? argb + 2 : argb + 1; - const uint8_t* const g = ALPHA_IS_LAST ? argb + 1 : argb + 2; - const uint8_t* const b = ALPHA_IS_LAST ? argb + 0 : argb + 3; - const uint8_t* const a = ALPHA_IS_LAST ? argb + 3 : argb + 0; - // We work on a tmp copy of 'picture', because ImportYUVAFromRGBA() - // would be calling WebPPictureFree(picture) otherwise. - WebPPicture tmp = *picture; - PictureResetARGB(&tmp); // reset ARGB buffer so that it's not free()'d. - tmp.use_argb = 0; - tmp.colorspace = colorspace & WEBP_CSP_UV_MASK; - if (!ImportYUVAFromRGBA(r, g, b, a, 4, 4 * picture->argb_stride, &tmp)) { - return WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); - } - // Copy back the YUV specs into 'picture'. - tmp.argb = picture->argb; - tmp.argb_stride = picture->argb_stride; - tmp.memory_argb_ = picture->memory_argb_; - *picture = tmp; - } - return 1; -} - -//------------------------------------------------------------------------------ -// Helper: clean up fully transparent area to help compressibility. - -#define SIZE 8 -#define SIZE2 (SIZE / 2) -static int is_transparent_area(const uint8_t* ptr, int stride, int size) { - int y, x; - for (y = 0; y < size; ++y) { - for (x = 0; x < size; ++x) { - if (ptr[x]) { - return 0; - } - } - ptr += stride; - } - return 1; -} - -static WEBP_INLINE void flatten(uint8_t* ptr, int v, int stride, int size) { - int y; - for (y = 0; y < size; ++y) { - memset(ptr, v, size); - ptr += stride; - } -} - -void WebPCleanupTransparentArea(WebPPicture* pic) { - int x, y, w, h; - const uint8_t* a_ptr; - int values[3] = { 0 }; - - if (pic == NULL) return; - - a_ptr = pic->a; - if (a_ptr == NULL) return; // nothing to do - - w = pic->width / SIZE; - h = pic->height / SIZE; - for (y = 0; y < h; ++y) { - int need_reset = 1; - for (x = 0; x < w; ++x) { - const int off_a = (y * pic->a_stride + x) * SIZE; - const int off_y = (y * pic->y_stride + x) * SIZE; - const int off_uv = (y * pic->uv_stride + x) * SIZE2; - if (is_transparent_area(a_ptr + off_a, pic->a_stride, SIZE)) { - if (need_reset) { - values[0] = pic->y[off_y]; - values[1] = pic->u[off_uv]; - values[2] = pic->v[off_uv]; - need_reset = 0; - } - flatten(pic->y + off_y, values[0], pic->y_stride, SIZE); - flatten(pic->u + off_uv, values[1], pic->uv_stride, SIZE2); - flatten(pic->v + off_uv, values[2], pic->uv_stride, SIZE2); - } else { - need_reset = 1; - } - } - // ignore the left-overs on right/bottom - } -} - -#undef SIZE -#undef SIZE2 - - -//------------------------------------------------------------------------------ -// Distortion - -// Max value returned in case of exact similarity. -static const double kMinDistortion_dB = 99.; - -int WebPPictureDistortion(const WebPPicture* pic1, const WebPPicture* pic2, - int type, float result[5]) { - int c; - DistoStats stats[5]; - int has_alpha; - - if (pic1 == NULL || pic2 == NULL || - pic1->width != pic2->width || pic1->height != pic2->height || - pic1->y == NULL || pic2->y == NULL || - pic1->u == NULL || pic2->u == NULL || - pic1->v == NULL || pic2->v == NULL || - result == NULL) { - return 0; - } - // TODO(skal): provide distortion for ARGB too. - if (pic1->use_argb == 1 || pic1->use_argb != pic2->use_argb) { - return 0; - } - - has_alpha = !!(pic1->colorspace & WEBP_CSP_ALPHA_BIT); - if (has_alpha != !!(pic2->colorspace & WEBP_CSP_ALPHA_BIT) || - (has_alpha && (pic1->a == NULL || pic2->a == NULL))) { - return 0; - } - - memset(stats, 0, sizeof(stats)); - VP8SSIMAccumulatePlane(pic1->y, pic1->y_stride, - pic2->y, pic2->y_stride, - pic1->width, pic1->height, &stats[0]); - VP8SSIMAccumulatePlane(pic1->u, pic1->uv_stride, - pic2->u, pic2->uv_stride, - (pic1->width + 1) >> 1, (pic1->height + 1) >> 1, - &stats[1]); - VP8SSIMAccumulatePlane(pic1->v, pic1->uv_stride, - pic2->v, pic2->uv_stride, - (pic1->width + 1) >> 1, (pic1->height + 1) >> 1, - &stats[2]); - if (has_alpha) { - VP8SSIMAccumulatePlane(pic1->a, pic1->a_stride, - pic2->a, pic2->a_stride, - pic1->width, pic1->height, &stats[3]); - } - for (c = 0; c <= 4; ++c) { - if (type == 1) { - const double v = VP8SSIMGet(&stats[c]); - result[c] = (float)((v < 1.) ? -10.0 * log10(1. - v) - : kMinDistortion_dB); - } else { - const double v = VP8SSIMGetSquaredError(&stats[c]); - result[c] = (float)((v > 0.) ? -4.3429448 * log(v / (255 * 255.)) - : kMinDistortion_dB); - } - // Accumulate forward - if (c < 4) VP8SSIMAddStats(&stats[c], &stats[4]); - } - return 1; -} - -//------------------------------------------------------------------------------ -// Simplest high-level calls: - -typedef int (*Importer)(WebPPicture* const, const uint8_t* const, int); - -static size_t Encode(const uint8_t* rgba, int width, int height, int stride, - Importer import, float quality_factor, int lossless, - uint8_t** output) { - WebPPicture pic; - WebPConfig config; - WebPMemoryWriter wrt; - int ok; - - if (!WebPConfigPreset(&config, WEBP_PRESET_DEFAULT, quality_factor) || - !WebPPictureInit(&pic)) { - return 0; // shouldn't happen, except if system installation is broken - } - - config.lossless = !!lossless; - pic.use_argb = !!lossless; - pic.width = width; - pic.height = height; - pic.writer = WebPMemoryWrite; - pic.custom_ptr = &wrt; - WebPMemoryWriterInit(&wrt); - - ok = import(&pic, rgba, stride) && WebPEncode(&config, &pic); - WebPPictureFree(&pic); - if (!ok) { - free(wrt.mem); - *output = NULL; - return 0; - } - *output = wrt.mem; - return wrt.size; -} - -#define ENCODE_FUNC(NAME, IMPORTER) \ -size_t NAME(const uint8_t* in, int w, int h, int bps, float q, \ - uint8_t** out) { \ - return Encode(in, w, h, bps, IMPORTER, q, 0, out); \ -} - -ENCODE_FUNC(WebPEncodeRGB, WebPPictureImportRGB); -ENCODE_FUNC(WebPEncodeBGR, WebPPictureImportBGR); -ENCODE_FUNC(WebPEncodeRGBA, WebPPictureImportRGBA); -ENCODE_FUNC(WebPEncodeBGRA, WebPPictureImportBGRA); - -#undef ENCODE_FUNC - -#define LOSSLESS_DEFAULT_QUALITY 70. -#define LOSSLESS_ENCODE_FUNC(NAME, IMPORTER) \ -size_t NAME(const uint8_t* in, int w, int h, int bps, uint8_t** out) { \ - return Encode(in, w, h, bps, IMPORTER, LOSSLESS_DEFAULT_QUALITY, 1, out); \ -} - -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGB, WebPPictureImportRGB); -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGR, WebPPictureImportBGR); -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessRGBA, WebPPictureImportRGBA); -LOSSLESS_ENCODE_FUNC(WebPEncodeLosslessBGRA, WebPPictureImportBGRA); - -#undef LOSSLESS_ENCODE_FUNC - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/quant.c b/drivers/webpold/enc/quant.c deleted file mode 100644 index ea153849c8..0000000000 --- a/drivers/webpold/enc/quant.c +++ /dev/null @@ -1,930 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Quantization -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <assert.h> -#include <math.h> - -#include "./vp8enci.h" -#include "./cost.h" - -#define DO_TRELLIS_I4 1 -#define DO_TRELLIS_I16 1 // not a huge gain, but ok at low bitrate. -#define DO_TRELLIS_UV 0 // disable trellis for UV. Risky. Not worth. -#define USE_TDISTO 1 - -#define MID_ALPHA 64 // neutral value for susceptibility -#define MIN_ALPHA 30 // lowest usable value for susceptibility -#define MAX_ALPHA 100 // higher meaninful value for susceptibility - -#define SNS_TO_DQ 0.9 // Scaling constant between the sns value and the QP - // power-law modulation. Must be strictly less than 1. - -#define MULT_8B(a, b) (((a) * (b) + 128) >> 8) - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ - -static WEBP_INLINE int clip(int v, int m, int M) { - return v < m ? m : v > M ? M : v; -} - -static const uint8_t kZigzag[16] = { - 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 -}; - -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 -}; - -static const uint16_t kAcTable2[128] = { - 8, 8, 9, 10, 12, 13, 15, 17, - 18, 20, 21, 23, 24, 26, 27, 29, - 31, 32, 34, 35, 37, 38, 40, 41, - 43, 44, 46, 48, 49, 51, 52, 54, - 55, 57, 58, 60, 62, 63, 65, 66, - 68, 69, 71, 72, 74, 75, 77, 79, - 80, 82, 83, 85, 86, 88, 89, 93, - 96, 99, 102, 105, 108, 111, 114, 117, - 120, 124, 127, 130, 133, 136, 139, 142, - 145, 148, 151, 155, 158, 161, 164, 167, - 170, 173, 176, 179, 184, 189, 193, 198, - 203, 207, 212, 217, 221, 226, 230, 235, - 240, 244, 249, 254, 258, 263, 268, 274, - 280, 286, 292, 299, 305, 311, 317, 323, - 330, 336, 342, 348, 354, 362, 370, 379, - 385, 393, 401, 409, 416, 424, 432, 440 -}; - -static const uint16_t kCoeffThresh[16] = { - 0, 10, 20, 30, - 10, 20, 30, 30, - 20, 30, 30, 30, - 30, 30, 30, 30 -}; - -// TODO(skal): tune more. Coeff thresholding? -static const uint8_t kBiasMatrices[3][16] = { // [3] = [luma-ac,luma-dc,chroma] - { 96, 96, 96, 96, - 96, 96, 96, 96, - 96, 96, 96, 96, - 96, 96, 96, 96 }, - { 96, 96, 96, 96, - 96, 96, 96, 96, - 96, 96, 96, 96, - 96, 96, 96, 96 }, - { 96, 96, 96, 96, - 96, 96, 96, 96, - 96, 96, 96, 96, - 96, 96, 96, 96 } -}; - -// Sharpening by (slightly) raising the hi-frequency coeffs (only for trellis). -// Hack-ish but helpful for mid-bitrate range. Use with care. -static const uint8_t kFreqSharpening[16] = { - 0, 30, 60, 90, - 30, 60, 90, 90, - 60, 90, 90, 90, - 90, 90, 90, 90 -}; - -//------------------------------------------------------------------------------ -// Initialize quantization parameters in VP8Matrix - -// Returns the average quantizer -static int ExpandMatrix(VP8Matrix* const m, int type) { - int i; - int sum = 0; - for (i = 2; i < 16; ++i) { - m->q_[i] = m->q_[1]; - } - for (i = 0; i < 16; ++i) { - const int j = kZigzag[i]; - const int bias = kBiasMatrices[type][j]; - m->iq_[j] = (1 << QFIX) / m->q_[j]; - m->bias_[j] = BIAS(bias); - // TODO(skal): tune kCoeffThresh[] - m->zthresh_[j] = ((256 /*+ kCoeffThresh[j]*/ - bias) * m->q_[j] + 127) >> 8; - m->sharpen_[j] = (kFreqSharpening[j] * m->q_[j]) >> 11; - sum += m->q_[j]; - } - return (sum + 8) >> 4; -} - -static void SetupMatrices(VP8Encoder* enc) { - int i; - const int tlambda_scale = - (enc->method_ >= 4) ? enc->config_->sns_strength - : 0; - const int num_segments = enc->segment_hdr_.num_segments_; - for (i = 0; i < num_segments; ++i) { - VP8SegmentInfo* const m = &enc->dqm_[i]; - const int q = m->quant_; - int q4, q16, quv; - m->y1_.q_[0] = kDcTable[clip(q + enc->dq_y1_dc_, 0, 127)]; - m->y1_.q_[1] = kAcTable[clip(q, 0, 127)]; - - m->y2_.q_[0] = kDcTable[ clip(q + enc->dq_y2_dc_, 0, 127)] * 2; - m->y2_.q_[1] = kAcTable2[clip(q + enc->dq_y2_ac_, 0, 127)]; - - m->uv_.q_[0] = kDcTable[clip(q + enc->dq_uv_dc_, 0, 117)]; - m->uv_.q_[1] = kAcTable[clip(q + enc->dq_uv_ac_, 0, 127)]; - - q4 = ExpandMatrix(&m->y1_, 0); - q16 = ExpandMatrix(&m->y2_, 1); - quv = ExpandMatrix(&m->uv_, 2); - - // TODO: Switch to kLambda*[] tables? - { - m->lambda_i4_ = (3 * q4 * q4) >> 7; - m->lambda_i16_ = (3 * q16 * q16); - m->lambda_uv_ = (3 * quv * quv) >> 6; - m->lambda_mode_ = (1 * q4 * q4) >> 7; - m->lambda_trellis_i4_ = (7 * q4 * q4) >> 3; - m->lambda_trellis_i16_ = (q16 * q16) >> 2; - m->lambda_trellis_uv_ = (quv *quv) << 1; - m->tlambda_ = (tlambda_scale * q4) >> 5; - } - } -} - -//------------------------------------------------------------------------------ -// Initialize filtering parameters - -// Very small filter-strength values have close to no visual effect. So we can -// save a little decoding-CPU by turning filtering off for these. -#define FSTRENGTH_CUTOFF 3 - -static void SetupFilterStrength(VP8Encoder* const enc) { - int i; - const int level0 = enc->config_->filter_strength; - for (i = 0; i < NUM_MB_SEGMENTS; ++i) { - // Segments with lower quantizer will be less filtered. TODO: tune (wrt SNS) - const int level = level0 * 256 * enc->dqm_[i].quant_ / 128; - const int f = level / (256 + enc->dqm_[i].beta_); - enc->dqm_[i].fstrength_ = (f < FSTRENGTH_CUTOFF) ? 0 : (f > 63) ? 63 : f; - } - // We record the initial strength (mainly for the case of 1-segment only). - enc->filter_hdr_.level_ = enc->dqm_[0].fstrength_; - enc->filter_hdr_.simple_ = (enc->config_->filter_type == 0); - enc->filter_hdr_.sharpness_ = enc->config_->filter_sharpness; -} - -//------------------------------------------------------------------------------ - -// Note: if you change the values below, remember that the max range -// allowed by the syntax for DQ_UV is [-16,16]. -#define MAX_DQ_UV (6) -#define MIN_DQ_UV (-4) - -// We want to emulate jpeg-like behaviour where the expected "good" quality -// is around q=75. Internally, our "good" middle is around c=50. So we -// map accordingly using linear piece-wise function -static double QualityToCompression(double q) { - const double c = q / 100.; - return (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.; -} - -void VP8SetSegmentParams(VP8Encoder* const enc, float quality) { - int i; - int dq_uv_ac, dq_uv_dc; - const int num_segments = enc->config_->segments; - const double amp = SNS_TO_DQ * enc->config_->sns_strength / 100. / 128.; - const double c_base = QualityToCompression(quality); - for (i = 0; i < num_segments; ++i) { - // The file size roughly scales as pow(quantizer, 3.). Actually, the - // exponent is somewhere between 2.8 and 3.2, but we're mostly interested - // in the mid-quant range. So we scale the compressibility inversely to - // this power-law: quant ~= compression ^ 1/3. This law holds well for - // low quant. Finer modelling for high-quant would make use of kAcTable[] - // more explicitely. - // Additionally, we modulate the base exponent 1/3 to accommodate for the - // quantization susceptibility and allow denser segments to be quantized - // more. - const double expn = (1. - amp * enc->dqm_[i].alpha_) / 3.; - const double c = pow(c_base, expn); - const int q = (int)(127. * (1. - c)); - assert(expn > 0.); - enc->dqm_[i].quant_ = clip(q, 0, 127); - } - - // purely indicative in the bitstream (except for the 1-segment case) - enc->base_quant_ = enc->dqm_[0].quant_; - - // fill-in values for the unused segments (required by the syntax) - for (i = num_segments; i < NUM_MB_SEGMENTS; ++i) { - enc->dqm_[i].quant_ = enc->base_quant_; - } - - // uv_alpha_ is normally spread around ~60. The useful range is - // typically ~30 (quite bad) to ~100 (ok to decimate UV more). - // We map it to the safe maximal range of MAX/MIN_DQ_UV for dq_uv. - dq_uv_ac = (enc->uv_alpha_ - MID_ALPHA) * (MAX_DQ_UV - MIN_DQ_UV) - / (MAX_ALPHA - MIN_ALPHA); - // we rescale by the user-defined strength of adaptation - dq_uv_ac = dq_uv_ac * enc->config_->sns_strength / 100; - // and make it safe. - dq_uv_ac = clip(dq_uv_ac, MIN_DQ_UV, MAX_DQ_UV); - // We also boost the dc-uv-quant a little, based on sns-strength, since - // U/V channels are quite more reactive to high quants (flat DC-blocks - // tend to appear, and are displeasant). - dq_uv_dc = -4 * enc->config_->sns_strength / 100; - dq_uv_dc = clip(dq_uv_dc, -15, 15); // 4bit-signed max allowed - - enc->dq_y1_dc_ = 0; // TODO(skal): dq-lum - enc->dq_y2_dc_ = 0; - enc->dq_y2_ac_ = 0; - enc->dq_uv_dc_ = dq_uv_dc; - enc->dq_uv_ac_ = dq_uv_ac; - - SetupMatrices(enc); - - SetupFilterStrength(enc); // initialize segments' filtering, eventually -} - -//------------------------------------------------------------------------------ -// Form the predictions in cache - -// Must be ordered using {DC_PRED, TM_PRED, V_PRED, H_PRED} as index -const int VP8I16ModeOffsets[4] = { I16DC16, I16TM16, I16VE16, I16HE16 }; -const int VP8UVModeOffsets[4] = { C8DC8, C8TM8, C8VE8, C8HE8 }; - -// Must be indexed using {B_DC_PRED -> B_HU_PRED} as index -const int VP8I4ModeOffsets[NUM_BMODES] = { - I4DC4, I4TM4, I4VE4, I4HE4, I4RD4, I4VR4, I4LD4, I4VL4, I4HD4, I4HU4 -}; - -void VP8MakeLuma16Preds(const VP8EncIterator* const it) { - const VP8Encoder* const enc = it->enc_; - const uint8_t* const left = it->x_ ? enc->y_left_ : NULL; - const uint8_t* const top = it->y_ ? enc->y_top_ + it->x_ * 16 : NULL; - VP8EncPredLuma16(it->yuv_p_, left, top); -} - -void VP8MakeChroma8Preds(const VP8EncIterator* const it) { - const VP8Encoder* const enc = it->enc_; - const uint8_t* const left = it->x_ ? enc->u_left_ : NULL; - const uint8_t* const top = it->y_ ? enc->uv_top_ + it->x_ * 16 : NULL; - VP8EncPredChroma8(it->yuv_p_, left, top); -} - -void VP8MakeIntra4Preds(const VP8EncIterator* const it) { - VP8EncPredLuma4(it->yuv_p_, it->i4_top_); -} - -//------------------------------------------------------------------------------ -// Quantize - -// Layout: -// +----+ -// |YYYY| 0 -// |YYYY| 4 -// |YYYY| 8 -// |YYYY| 12 -// +----+ -// |UUVV| 16 -// |UUVV| 20 -// +----+ - -const int VP8Scan[16 + 4 + 4] = { - // Luma - 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, - - 0 + 0 * BPS, 4 + 0 * BPS, 0 + 4 * BPS, 4 + 4 * BPS, // U - 8 + 0 * BPS, 12 + 0 * BPS, 8 + 4 * BPS, 12 + 4 * BPS // V -}; - -//------------------------------------------------------------------------------ -// Distortion measurement - -static const uint16_t kWeightY[16] = { - 38, 32, 20, 9, 32, 28, 17, 7, 20, 17, 10, 4, 9, 7, 4, 2 -}; - -static const uint16_t kWeightTrellis[16] = { -#if USE_TDISTO == 0 - 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16 -#else - 30, 27, 19, 11, - 27, 24, 17, 10, - 19, 17, 12, 8, - 11, 10, 8, 6 -#endif -}; - -// Init/Copy the common fields in score. -static void InitScore(VP8ModeScore* const rd) { - rd->D = 0; - rd->SD = 0; - rd->R = 0; - rd->nz = 0; - rd->score = MAX_COST; -} - -static void CopyScore(VP8ModeScore* const dst, const VP8ModeScore* const src) { - dst->D = src->D; - dst->SD = src->SD; - dst->R = src->R; - dst->nz = src->nz; // note that nz is not accumulated, but just copied. - dst->score = src->score; -} - -static void AddScore(VP8ModeScore* const dst, const VP8ModeScore* const src) { - dst->D += src->D; - dst->SD += src->SD; - dst->R += src->R; - dst->nz |= src->nz; // here, new nz bits are accumulated. - dst->score += src->score; -} - -//------------------------------------------------------------------------------ -// Performs trellis-optimized quantization. - -// Trellis - -typedef struct { - int prev; // best previous - int level; // level - int sign; // sign of coeff_i - score_t cost; // bit cost - score_t error; // distortion = sum of (|coeff_i| - level_i * Q_i)^2 - int ctx; // context (only depends on 'level'. Could be spared.) -} Node; - -// If a coefficient was quantized to a value Q (using a neutral bias), -// we test all alternate possibilities between [Q-MIN_DELTA, Q+MAX_DELTA] -// We don't test negative values though. -#define MIN_DELTA 0 // how much lower level to try -#define MAX_DELTA 1 // how much higher -#define NUM_NODES (MIN_DELTA + 1 + MAX_DELTA) -#define NODE(n, l) (nodes[(n) + 1][(l) + MIN_DELTA]) - -static WEBP_INLINE void SetRDScore(int lambda, VP8ModeScore* const rd) { - // TODO: incorporate the "* 256" in the tables? - rd->score = rd->R * lambda + 256 * (rd->D + rd->SD); -} - -static WEBP_INLINE score_t RDScoreTrellis(int lambda, score_t rate, - score_t distortion) { - return rate * lambda + 256 * distortion; -} - -static int TrellisQuantizeBlock(const VP8EncIterator* const it, - int16_t in[16], int16_t out[16], - int ctx0, int coeff_type, - const VP8Matrix* const mtx, - int lambda) { - ProbaArray* const last_costs = it->enc_->proba_.coeffs_[coeff_type]; - CostArray* const costs = it->enc_->proba_.level_cost_[coeff_type]; - const int first = (coeff_type == 0) ? 1 : 0; - Node nodes[17][NUM_NODES]; - int best_path[3] = {-1, -1, -1}; // store best-last/best-level/best-previous - score_t best_score; - int best_node; - int last = first - 1; - int n, m, p, nz; - - { - score_t cost; - score_t max_error; - const int thresh = mtx->q_[1] * mtx->q_[1] / 4; - const int last_proba = last_costs[VP8EncBands[first]][ctx0][0]; - - // compute maximal distortion. - max_error = 0; - for (n = first; n < 16; ++n) { - const int j = kZigzag[n]; - const int err = in[j] * in[j]; - max_error += kWeightTrellis[j] * err; - if (err > thresh) last = n; - } - // we don't need to go inspect up to n = 16 coeffs. We can just go up - // to last + 1 (inclusive) without losing much. - if (last < 15) ++last; - - // compute 'skip' score. This is the max score one can do. - cost = VP8BitCost(0, last_proba); - best_score = RDScoreTrellis(lambda, cost, max_error); - - // initialize source node. - n = first - 1; - for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) { - NODE(n, m).cost = 0; - NODE(n, m).error = max_error; - NODE(n, m).ctx = ctx0; - } - } - - // traverse trellis. - for (n = first; n <= last; ++n) { - const int j = kZigzag[n]; - const int Q = mtx->q_[j]; - const int iQ = mtx->iq_[j]; - const int B = BIAS(0x00); // neutral bias - // note: it's important to take sign of the _original_ coeff, - // so we don't have to consider level < 0 afterward. - const int sign = (in[j] < 0); - int coeff0 = (sign ? -in[j] : in[j]) + mtx->sharpen_[j]; - int level0; - if (coeff0 > 2047) coeff0 = 2047; - - level0 = QUANTDIV(coeff0, iQ, B); - // test all alternate level values around level0. - for (m = -MIN_DELTA; m <= MAX_DELTA; ++m) { - Node* const cur = &NODE(n, m); - int delta_error, new_error; - score_t cur_score = MAX_COST; - int level = level0 + m; - int last_proba; - - cur->sign = sign; - cur->level = level; - cur->ctx = (level == 0) ? 0 : (level == 1) ? 1 : 2; - if (level >= 2048 || level < 0) { // node is dead? - cur->cost = MAX_COST; - continue; - } - last_proba = last_costs[VP8EncBands[n + 1]][cur->ctx][0]; - - // Compute delta_error = how much coding this level will - // subtract as distortion to max_error - new_error = coeff0 - level * Q; - delta_error = - kWeightTrellis[j] * (coeff0 * coeff0 - new_error * new_error); - - // Inspect all possible non-dead predecessors. Retain only the best one. - for (p = -MIN_DELTA; p <= MAX_DELTA; ++p) { - const Node* const prev = &NODE(n - 1, p); - const int prev_ctx = prev->ctx; - const uint16_t* const tcost = costs[VP8EncBands[n]][prev_ctx]; - const score_t total_error = prev->error - delta_error; - score_t cost, base_cost, score; - - if (prev->cost >= MAX_COST) { // dead node? - continue; - } - - // Base cost of both terminal/non-terminal - base_cost = prev->cost + VP8LevelCost(tcost, level); - - // Examine node assuming it's a non-terminal one. - cost = base_cost; - if (level && n < 15) { - cost += VP8BitCost(1, last_proba); - } - score = RDScoreTrellis(lambda, cost, total_error); - if (score < cur_score) { - cur_score = score; - cur->cost = cost; - cur->error = total_error; - cur->prev = p; - } - - // Now, record best terminal node (and thus best entry in the graph). - if (level) { - cost = base_cost; - if (n < 15) cost += VP8BitCost(0, last_proba); - score = RDScoreTrellis(lambda, cost, total_error); - if (score < best_score) { - best_score = score; - best_path[0] = n; // best eob position - best_path[1] = m; // best level - best_path[2] = p; // best predecessor - } - } - } - } - } - - // Fresh start - memset(in + first, 0, (16 - first) * sizeof(*in)); - memset(out + first, 0, (16 - first) * sizeof(*out)); - if (best_path[0] == -1) { - return 0; // skip! - } - - // Unwind the best path. - // Note: best-prev on terminal node is not necessarily equal to the - // best_prev for non-terminal. So we patch best_path[2] in. - n = best_path[0]; - best_node = best_path[1]; - NODE(n, best_node).prev = best_path[2]; // force best-prev for terminal - nz = 0; - - for (; n >= first; --n) { - const Node* const node = &NODE(n, best_node); - const int j = kZigzag[n]; - out[n] = node->sign ? -node->level : node->level; - nz |= (node->level != 0); - in[j] = out[n] * mtx->q_[j]; - best_node = node->prev; - } - return nz; -} - -#undef NODE - -//------------------------------------------------------------------------------ -// Performs: difference, transform, quantize, back-transform, add -// all at once. Output is the reconstructed block in *yuv_out, and the -// quantized levels in *levels. - -static int ReconstructIntra16(VP8EncIterator* const it, - VP8ModeScore* const rd, - uint8_t* const yuv_out, - int mode) { - const VP8Encoder* const enc = it->enc_; - const uint8_t* const ref = it->yuv_p_ + VP8I16ModeOffsets[mode]; - const uint8_t* const src = it->yuv_in_ + Y_OFF; - const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; - int nz = 0; - int n; - int16_t tmp[16][16], dc_tmp[16]; - - for (n = 0; n < 16; ++n) { - VP8FTransform(src + VP8Scan[n], ref + VP8Scan[n], tmp[n]); - } - VP8FTransformWHT(tmp[0], dc_tmp); - nz |= VP8EncQuantizeBlock(dc_tmp, rd->y_dc_levels, 0, &dqm->y2_) << 24; - - if (DO_TRELLIS_I16 && it->do_trellis_) { - int x, y; - VP8IteratorNzToBytes(it); - for (y = 0, n = 0; y < 4; ++y) { - for (x = 0; x < 4; ++x, ++n) { - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - const int non_zero = - TrellisQuantizeBlock(it, tmp[n], rd->y_ac_levels[n], ctx, 0, - &dqm->y1_, dqm->lambda_trellis_i16_); - it->top_nz_[x] = it->left_nz_[y] = non_zero; - nz |= non_zero << n; - } - } - } else { - for (n = 0; n < 16; ++n) { - nz |= VP8EncQuantizeBlock(tmp[n], rd->y_ac_levels[n], 1, &dqm->y1_) << n; - } - } - - // Transform back - VP8ITransformWHT(dc_tmp, tmp[0]); - for (n = 0; n < 16; n += 2) { - VP8ITransform(ref + VP8Scan[n], tmp[n], yuv_out + VP8Scan[n], 1); - } - - return nz; -} - -static int ReconstructIntra4(VP8EncIterator* const it, - int16_t levels[16], - const uint8_t* const src, - uint8_t* const yuv_out, - int mode) { - const VP8Encoder* const enc = it->enc_; - const uint8_t* const ref = it->yuv_p_ + VP8I4ModeOffsets[mode]; - const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; - int nz = 0; - int16_t tmp[16]; - - VP8FTransform(src, ref, tmp); - if (DO_TRELLIS_I4 && it->do_trellis_) { - const int x = it->i4_ & 3, y = it->i4_ >> 2; - const int ctx = it->top_nz_[x] + it->left_nz_[y]; - nz = TrellisQuantizeBlock(it, tmp, levels, ctx, 3, &dqm->y1_, - dqm->lambda_trellis_i4_); - } else { - nz = VP8EncQuantizeBlock(tmp, levels, 0, &dqm->y1_); - } - VP8ITransform(ref, tmp, yuv_out, 0); - return nz; -} - -static int ReconstructUV(VP8EncIterator* const it, VP8ModeScore* const rd, - uint8_t* const yuv_out, int mode) { - const VP8Encoder* const enc = it->enc_; - const uint8_t* const ref = it->yuv_p_ + VP8UVModeOffsets[mode]; - const uint8_t* const src = it->yuv_in_ + U_OFF; - const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; - int nz = 0; - int n; - int16_t tmp[8][16]; - - for (n = 0; n < 8; ++n) { - VP8FTransform(src + VP8Scan[16 + n], ref + VP8Scan[16 + n], tmp[n]); - } - if (DO_TRELLIS_UV && it->do_trellis_) { - int ch, x, y; - for (ch = 0, n = 0; ch <= 2; ch += 2) { - for (y = 0; y < 2; ++y) { - for (x = 0; x < 2; ++x, ++n) { - const int ctx = it->top_nz_[4 + ch + x] + it->left_nz_[4 + ch + y]; - const int non_zero = - TrellisQuantizeBlock(it, tmp[n], rd->uv_levels[n], ctx, 2, - &dqm->uv_, dqm->lambda_trellis_uv_); - it->top_nz_[4 + ch + x] = it->left_nz_[4 + ch + y] = non_zero; - nz |= non_zero << n; - } - } - } - } else { - for (n = 0; n < 8; ++n) { - nz |= VP8EncQuantizeBlock(tmp[n], rd->uv_levels[n], 0, &dqm->uv_) << n; - } - } - - for (n = 0; n < 8; n += 2) { - VP8ITransform(ref + VP8Scan[16 + n], tmp[n], yuv_out + VP8Scan[16 + n], 1); - } - return (nz << 16); -} - -//------------------------------------------------------------------------------ -// RD-opt decision. Reconstruct each modes, evalue distortion and bit-cost. -// Pick the mode is lower RD-cost = Rate + lamba * Distortion. - -static void SwapPtr(uint8_t** a, uint8_t** b) { - uint8_t* const tmp = *a; - *a = *b; - *b = tmp; -} - -static void SwapOut(VP8EncIterator* const it) { - SwapPtr(&it->yuv_out_, &it->yuv_out2_); -} - -static void PickBestIntra16(VP8EncIterator* const it, VP8ModeScore* const rd) { - const VP8Encoder* const enc = it->enc_; - const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; - const int lambda = dqm->lambda_i16_; - const int tlambda = dqm->tlambda_; - const uint8_t* const src = it->yuv_in_ + Y_OFF; - VP8ModeScore rd16; - int mode; - - rd->mode_i16 = -1; - for (mode = 0; mode < 4; ++mode) { - uint8_t* const tmp_dst = it->yuv_out2_ + Y_OFF; // scratch buffer - int nz; - - // Reconstruct - nz = ReconstructIntra16(it, &rd16, tmp_dst, mode); - - // Measure RD-score - rd16.D = VP8SSE16x16(src, tmp_dst); - rd16.SD = tlambda ? MULT_8B(tlambda, VP8TDisto16x16(src, tmp_dst, kWeightY)) - : 0; - rd16.R = VP8GetCostLuma16(it, &rd16); - rd16.R += VP8FixedCostsI16[mode]; - - // Since we always examine Intra16 first, we can overwrite *rd directly. - SetRDScore(lambda, &rd16); - if (mode == 0 || rd16.score < rd->score) { - CopyScore(rd, &rd16); - rd->mode_i16 = mode; - rd->nz = nz; - memcpy(rd->y_ac_levels, rd16.y_ac_levels, sizeof(rd16.y_ac_levels)); - memcpy(rd->y_dc_levels, rd16.y_dc_levels, sizeof(rd16.y_dc_levels)); - SwapOut(it); - } - } - SetRDScore(dqm->lambda_mode_, rd); // finalize score for mode decision. - VP8SetIntra16Mode(it, rd->mode_i16); -} - -//------------------------------------------------------------------------------ - -// return the cost array corresponding to the surrounding prediction modes. -static const uint16_t* GetCostModeI4(VP8EncIterator* const it, - const uint8_t modes[16]) { - const int preds_w = it->enc_->preds_w_; - const int x = (it->i4_ & 3), y = it->i4_ >> 2; - const int left = (x == 0) ? it->preds_[y * preds_w - 1] : modes[it->i4_ - 1]; - const int top = (y == 0) ? it->preds_[-preds_w + x] : modes[it->i4_ - 4]; - return VP8FixedCostsI4[top][left]; -} - -static int PickBestIntra4(VP8EncIterator* const it, VP8ModeScore* const rd) { - const VP8Encoder* const enc = it->enc_; - const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; - const int lambda = dqm->lambda_i4_; - const int tlambda = dqm->tlambda_; - const uint8_t* const src0 = it->yuv_in_ + Y_OFF; - uint8_t* const best_blocks = it->yuv_out2_ + Y_OFF; - int total_header_bits = 0; - VP8ModeScore rd_best; - - if (enc->max_i4_header_bits_ == 0) { - return 0; - } - - InitScore(&rd_best); - rd_best.score = 211; // '211' is the value of VP8BitCost(0, 145) - VP8IteratorStartI4(it); - do { - VP8ModeScore rd_i4; - int mode; - int best_mode = -1; - const uint8_t* const src = src0 + VP8Scan[it->i4_]; - const uint16_t* const mode_costs = GetCostModeI4(it, rd->modes_i4); - uint8_t* best_block = best_blocks + VP8Scan[it->i4_]; - uint8_t* tmp_dst = it->yuv_p_ + I4TMP; // scratch buffer. - - InitScore(&rd_i4); - VP8MakeIntra4Preds(it); - for (mode = 0; mode < NUM_BMODES; ++mode) { - VP8ModeScore rd_tmp; - int16_t tmp_levels[16]; - - // Reconstruct - rd_tmp.nz = - ReconstructIntra4(it, tmp_levels, src, tmp_dst, mode) << it->i4_; - - // Compute RD-score - rd_tmp.D = VP8SSE4x4(src, tmp_dst); - rd_tmp.SD = - tlambda ? MULT_8B(tlambda, VP8TDisto4x4(src, tmp_dst, kWeightY)) - : 0; - rd_tmp.R = VP8GetCostLuma4(it, tmp_levels); - rd_tmp.R += mode_costs[mode]; - - SetRDScore(lambda, &rd_tmp); - if (best_mode < 0 || rd_tmp.score < rd_i4.score) { - CopyScore(&rd_i4, &rd_tmp); - best_mode = mode; - SwapPtr(&tmp_dst, &best_block); - memcpy(rd_best.y_ac_levels[it->i4_], tmp_levels, sizeof(tmp_levels)); - } - } - SetRDScore(dqm->lambda_mode_, &rd_i4); - AddScore(&rd_best, &rd_i4); - total_header_bits += mode_costs[best_mode]; - if (rd_best.score >= rd->score || - total_header_bits > enc->max_i4_header_bits_) { - return 0; - } - // Copy selected samples if not in the right place already. - if (best_block != best_blocks + VP8Scan[it->i4_]) - VP8Copy4x4(best_block, best_blocks + VP8Scan[it->i4_]); - rd->modes_i4[it->i4_] = best_mode; - it->top_nz_[it->i4_ & 3] = it->left_nz_[it->i4_ >> 2] = (rd_i4.nz ? 1 : 0); - } while (VP8IteratorRotateI4(it, best_blocks)); - - // finalize state - CopyScore(rd, &rd_best); - VP8SetIntra4Mode(it, rd->modes_i4); - SwapOut(it); - memcpy(rd->y_ac_levels, rd_best.y_ac_levels, sizeof(rd->y_ac_levels)); - return 1; // select intra4x4 over intra16x16 -} - -//------------------------------------------------------------------------------ - -static void PickBestUV(VP8EncIterator* const it, VP8ModeScore* const rd) { - const VP8Encoder* const enc = it->enc_; - const VP8SegmentInfo* const dqm = &enc->dqm_[it->mb_->segment_]; - const int lambda = dqm->lambda_uv_; - const uint8_t* const src = it->yuv_in_ + U_OFF; - uint8_t* const tmp_dst = it->yuv_out2_ + U_OFF; // scratch buffer - uint8_t* const dst0 = it->yuv_out_ + U_OFF; - VP8ModeScore rd_best; - int mode; - - rd->mode_uv = -1; - InitScore(&rd_best); - for (mode = 0; mode < 4; ++mode) { - VP8ModeScore rd_uv; - - // Reconstruct - rd_uv.nz = ReconstructUV(it, &rd_uv, tmp_dst, mode); - - // Compute RD-score - rd_uv.D = VP8SSE16x8(src, tmp_dst); - rd_uv.SD = 0; // TODO: should we call TDisto? it tends to flatten areas. - rd_uv.R = VP8GetCostUV(it, &rd_uv); - rd_uv.R += VP8FixedCostsUV[mode]; - - SetRDScore(lambda, &rd_uv); - if (mode == 0 || rd_uv.score < rd_best.score) { - CopyScore(&rd_best, &rd_uv); - rd->mode_uv = mode; - memcpy(rd->uv_levels, rd_uv.uv_levels, sizeof(rd->uv_levels)); - memcpy(dst0, tmp_dst, UV_SIZE); // TODO: SwapUVOut() ? - } - } - VP8SetIntraUVMode(it, rd->mode_uv); - AddScore(rd, &rd_best); -} - -//------------------------------------------------------------------------------ -// Final reconstruction and quantization. - -static void SimpleQuantize(VP8EncIterator* const it, VP8ModeScore* const rd) { - const VP8Encoder* const enc = it->enc_; - const int i16 = (it->mb_->type_ == 1); - int nz = 0; - - if (i16) { - nz = ReconstructIntra16(it, rd, it->yuv_out_ + Y_OFF, it->preds_[0]); - } else { - VP8IteratorStartI4(it); - do { - const int mode = - it->preds_[(it->i4_ & 3) + (it->i4_ >> 2) * enc->preds_w_]; - const uint8_t* const src = it->yuv_in_ + Y_OFF + VP8Scan[it->i4_]; - uint8_t* const dst = it->yuv_out_ + Y_OFF + VP8Scan[it->i4_]; - VP8MakeIntra4Preds(it); - nz |= ReconstructIntra4(it, rd->y_ac_levels[it->i4_], - src, dst, mode) << it->i4_; - } while (VP8IteratorRotateI4(it, it->yuv_out_ + Y_OFF)); - } - - nz |= ReconstructUV(it, rd, it->yuv_out_ + U_OFF, it->mb_->uv_mode_); - rd->nz = nz; -} - -//------------------------------------------------------------------------------ -// Entry point - -int VP8Decimate(VP8EncIterator* const it, VP8ModeScore* const rd, int rd_opt) { - int is_skipped; - - InitScore(rd); - - // We can perform predictions for Luma16x16 and Chroma8x8 already. - // Luma4x4 predictions needs to be done as-we-go. - VP8MakeLuma16Preds(it); - VP8MakeChroma8Preds(it); - - // for rd_opt = 2, we perform trellis-quant on the final decision only. - // for rd_opt > 2, we use it for every scoring (=much slower). - if (rd_opt > 0) { - it->do_trellis_ = (rd_opt > 2); - PickBestIntra16(it, rd); - if (it->enc_->method_ >= 2) { - PickBestIntra4(it, rd); - } - PickBestUV(it, rd); - if (rd_opt == 2) { - it->do_trellis_ = 1; - SimpleQuantize(it, rd); - } - } else { - // TODO: for method_ == 2, pick the best intra4/intra16 based on SSE - it->do_trellis_ = (it->enc_->method_ == 2); - SimpleQuantize(it, rd); - } - is_skipped = (rd->nz == 0); - VP8SetSkip(it, is_skipped); - return is_skipped; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/syntax.c b/drivers/webpold/enc/syntax.c deleted file mode 100644 index 4221436ff9..0000000000 --- a/drivers/webpold/enc/syntax.c +++ /dev/null @@ -1,437 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Header syntax writing -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <assert.h> - -#include "../format_constants.h" -#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); -} - -//------------------------------------------------------------------------------ -// Writers for header's various pieces (in order of appearance) - -static WebPEncodingError PutRIFFHeader(const VP8Encoder* const enc, - size_t riff_size) { - const WebPPicture* const pic = enc->pic_; - uint8_t riff[RIFF_HEADER_SIZE] = { - 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P' - }; - assert(riff_size == (uint32_t)riff_size); - PutLE32(riff + TAG_SIZE, (uint32_t)riff_size); - if (!pic->writer(riff, sizeof(riff), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -static WebPEncodingError PutVP8XHeader(const VP8Encoder* const enc) { - const WebPPicture* const pic = enc->pic_; - uint8_t vp8x[CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE] = { - 'V', 'P', '8', 'X' - }; - uint32_t flags = 0; - - assert(IsVP8XNeeded(enc)); - assert(pic->width >= 1 && pic->height >= 1); - assert(pic->width <= MAX_CANVAS_SIZE && pic->height <= MAX_CANVAS_SIZE); - - if (enc->has_alpha_) { - flags |= ALPHA_FLAG_BIT; - } - - 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)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -static WebPEncodingError PutAlphaChunk(const VP8Encoder* const enc) { - const WebPPicture* const pic = enc->pic_; - uint8_t alpha_chunk_hdr[CHUNK_HEADER_SIZE] = { - 'A', 'L', 'P', 'H' - }; - - assert(enc->has_alpha_); - - // Alpha chunk header. - PutLE32(alpha_chunk_hdr + TAG_SIZE, enc->alpha_data_size_); - if (!pic->writer(alpha_chunk_hdr, sizeof(alpha_chunk_hdr), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - - // Alpha chunk data. - if (!pic->writer(enc->alpha_data_, enc->alpha_data_size_, pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - - // Padding. - if ((enc->alpha_data_size_ & 1) && !PutPaddingByte(pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -static WebPEncodingError PutVP8Header(const WebPPicture* const pic, - size_t vp8_size) { - uint8_t vp8_chunk_hdr[CHUNK_HEADER_SIZE] = { - 'V', 'P', '8', ' ' - }; - assert(vp8_size == (uint32_t)vp8_size); - PutLE32(vp8_chunk_hdr + TAG_SIZE, (uint32_t)vp8_size); - if (!pic->writer(vp8_chunk_hdr, sizeof(vp8_chunk_hdr), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -static WebPEncodingError PutVP8FrameHeader(const WebPPicture* const pic, - int profile, size_t size0) { - uint8_t vp8_frm_hdr[VP8_FRAME_HEADER_SIZE]; - uint32_t bits; - - if (size0 >= VP8_MAX_PARTITION0_SIZE) { // partition #0 is too big to fit - return VP8_ENC_ERROR_PARTITION0_OVERFLOW; - } - - // Paragraph 9.1. - bits = 0 // keyframe (1b) - | (profile << 1) // profile (3b) - | (1 << 4) // visible (1b) - | ((uint32_t)size0 << 5); // partition length (19b) - vp8_frm_hdr[0] = (bits >> 0) & 0xff; - vp8_frm_hdr[1] = (bits >> 8) & 0xff; - vp8_frm_hdr[2] = (bits >> 16) & 0xff; - // signature - vp8_frm_hdr[3] = (VP8_SIGNATURE >> 16) & 0xff; - vp8_frm_hdr[4] = (VP8_SIGNATURE >> 8) & 0xff; - vp8_frm_hdr[5] = (VP8_SIGNATURE >> 0) & 0xff; - // dimensions - vp8_frm_hdr[6] = pic->width & 0xff; - vp8_frm_hdr[7] = pic->width >> 8; - vp8_frm_hdr[8] = pic->height & 0xff; - vp8_frm_hdr[9] = pic->height >> 8; - - if (!pic->writer(vp8_frm_hdr, sizeof(vp8_frm_hdr), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -// WebP Headers. -static int PutWebPHeaders(const VP8Encoder* const enc, size_t size0, - size_t vp8_size, size_t riff_size) { - WebPPicture* const pic = enc->pic_; - WebPEncodingError err = VP8_ENC_OK; - - // RIFF header. - err = PutRIFFHeader(enc, riff_size); - if (err != VP8_ENC_OK) goto Error; - - // VP8X. - if (IsVP8XNeeded(enc)) { - err = PutVP8XHeader(enc); - if (err != VP8_ENC_OK) goto Error; - } - - // Alpha. - if (enc->has_alpha_) { - err = PutAlphaChunk(enc); - if (err != VP8_ENC_OK) goto Error; - } - - // VP8 header. - err = PutVP8Header(pic, vp8_size); - if (err != VP8_ENC_OK) goto Error; - - // VP8 frame header. - err = PutVP8FrameHeader(pic, enc->profile_, size0); - if (err != VP8_ENC_OK) goto Error; - - // All OK. - return 1; - - // Error. - Error: - return WebPEncodingSetError(pic, err); -} - -// 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_; - if (VP8PutBitUniform(bw, (hdr->num_segments_ > 1))) { - // We always 'update' the quant and filter strength values - const int update_data = 1; - int s; - VP8PutBitUniform(bw, hdr->update_map_); - if (VP8PutBitUniform(bw, update_data)) { - // 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); - } - for (s = 0; s < NUM_MB_SEGMENTS; ++s) { - VP8PutSignedValue(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); - } - } - } - } -} - -// Filtering parameters header -static void PutFilterHeader(VP8BitWriter* const bw, - const VP8FilterHeader* 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); - 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); - // we use mode_lf_delta for i4x4 - VP8PutSignedValue(bw, hdr->i4x4_lf_delta_, 6); - VP8PutValue(bw, 0, 3); // all others unused - } - } -} - -// 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); -} - -// Partition sizes -static int EmitPartitionsSize(const VP8Encoder* const enc, - WebPPicture* const pic) { - uint8_t buf[3 * (MAX_NUM_PARTITIONS - 1)]; - int p; - for (p = 0; p < enc->num_parts_ - 1; ++p) { - const size_t part_size = VP8BitWriterSize(enc->parts_ + p); - if (part_size >= VP8_MAX_PARTITION_SIZE) { - return WebPEncodingSetError(pic, VP8_ENC_ERROR_PARTITION_OVERFLOW); - } - buf[3 * p + 0] = (part_size >> 0) & 0xff; - buf[3 * p + 1] = (part_size >> 8) & 0xff; - buf[3 * p + 2] = (part_size >> 16) & 0xff; - } - return p ? pic->writer(buf, 3 * p, pic) : 1; -} - -//------------------------------------------------------------------------------ - -#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) { - 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 - VP8PutBitUniform(bw, 0); // colorspace -#endif - VP8PutBitUniform(bw, 0); // clamp type - - PutSegmentHeader(bw, enc); - PutFilterHeader(bw, &enc->filter_hdr_); - VP8PutValue(bw, enc->config_->partitions, 2); - PutQuant(bw, enc); - VP8PutBitUniform(bw, 0); // no proba update - VP8WriteProbas(bw, &enc->proba_); - pos2 = VP8BitWriterPos(bw); - 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_; -} - -void VP8EncFreeBitWriters(VP8Encoder* const enc) { - int p; - VP8BitWriterWipeOut(&enc->bw_); - for (p = 0; p < enc->num_parts_; ++p) { - VP8BitWriterWipeOut(enc->parts_ + p); - } -} - -int VP8EncWrite(VP8Encoder* const enc) { - WebPPicture* const pic = enc->pic_; - VP8BitWriter* const bw = &enc->bw_; - const int task_percent = 19; - const int percent_per_part = task_percent / enc->num_parts_; - const int final_percent = enc->percent_ + task_percent; - int ok = 0; - size_t vp8_size, pad, riff_size; - int p; - - // Partition #0 with header and partition sizes - ok = !!GeneratePartition0(enc); - - // Compute VP8 size - vp8_size = VP8_FRAME_HEADER_SIZE + - VP8BitWriterSize(bw) + - 3 * (enc->num_parts_ - 1); - for (p = 0; p < enc->num_parts_; ++p) { - vp8_size += VP8BitWriterSize(enc->parts_ + p); - } - pad = vp8_size & 1; - vp8_size += pad; - - // Compute RIFF size - // At the minimum it is: "WEBPVP8 nnnn" + VP8 data size. - riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8_size; - if (IsVP8XNeeded(enc)) { // Add size for: VP8X header + data. - riff_size += CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE; - } - if (enc->has_alpha_) { // Add size for: ALPH header + data. - const uint32_t padded_alpha_size = enc->alpha_data_size_ + - (enc->alpha_data_size_ & 1); - riff_size += CHUNK_HEADER_SIZE + padded_alpha_size; - } - // Sanity check. - if (riff_size > 0xfffffffeU) { - return WebPEncodingSetError(pic, VP8_ENC_ERROR_FILE_TOO_BIG); - } - - // Emit headers and partition #0 - { - const uint8_t* const part0 = VP8BitWriterBuf(bw); - const size_t size0 = VP8BitWriterSize(bw); - ok = ok && PutWebPHeaders(enc, size0, vp8_size, riff_size) - && pic->writer(part0, size0, pic) - && EmitPartitionsSize(enc, pic); - VP8BitWriterWipeOut(bw); // will free the internal buffer. - } - - // Token partitions - for (p = 0; p < enc->num_parts_; ++p) { - const uint8_t* const buf = VP8BitWriterBuf(enc->parts_ + p); - const size_t size = VP8BitWriterSize(enc->parts_ + p); - if (size) - ok = ok && pic->writer(buf, size, pic); - VP8BitWriterWipeOut(enc->parts_ + p); // will free the internal buffer. - ok = ok && WebPReportProgress(pic, enc->percent_ + percent_per_part, - &enc->percent_); - } - - // Padding byte - if (ok && pad) { - ok = PutPaddingByte(pic); - } - - enc->coded_size_ = (int)(CHUNK_HEADER_SIZE + riff_size); - ok = ok && WebPReportProgress(pic, final_percent, &enc->percent_); - return ok; -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/tree.c b/drivers/webpold/enc/tree.c deleted file mode 100644 index 8b25e5e488..0000000000 --- a/drivers/webpold/enc/tree.c +++ /dev/null @@ -1,510 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Token probabilities -// -// 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 } - }, - { { 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 } - } - } -}; - -void VP8DefaultProbas(VP8Encoder* const enc) { - VP8Proba* const probas = &enc->proba_; - probas->use_skip_proba_ = 0; - memset(probas->segments_, 255u, sizeof(probas->segments_)); - memcpy(probas->coeffs_, VP8CoeffsProba0, sizeof(VP8CoeffsProba0)); - // Note: we could hard-code the level_costs_ corresponding to VP8CoeffsProba0, - // but that's ~11k of static data. Better call VP8CalculateLevelCosts() later. - probas->dirty_ = 1; -} - -// Paragraph 11.5. 900bytes. -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 } } -}; - -static int PutI4Mode(VP8BitWriter* const bw, int mode, - const uint8_t* const prob) { - if (VP8PutBit(bw, mode != B_DC_PRED, prob[0])) { - if (VP8PutBit(bw, mode != B_TM_PRED, prob[1])) { - if (VP8PutBit(bw, mode != B_VE_PRED, prob[2])) { - if (!VP8PutBit(bw, mode >= B_LD_PRED, prob[3])) { - if (VP8PutBit(bw, mode != B_HE_PRED, prob[4])) { - VP8PutBit(bw, mode != B_RD_PRED, prob[5]); - } - } else { - if (VP8PutBit(bw, mode != B_LD_PRED, prob[6])) { - if (VP8PutBit(bw, mode != B_VL_PRED, prob[7])) { - VP8PutBit(bw, mode != B_HD_PRED, prob[8]); - } - } - } - } - } - } - return mode; -} - -static void PutI16Mode(VP8BitWriter* const bw, int mode) { - if (VP8PutBit(bw, (mode == TM_PRED || mode == H_PRED), 156)) { - VP8PutBit(bw, mode == TM_PRED, 128); // TM or HE - } else { - VP8PutBit(bw, mode == V_PRED, 163); // VE or DC - } -} - -static void PutUVMode(VP8BitWriter* const bw, int uv_mode) { - if (VP8PutBit(bw, uv_mode != DC_PRED, 142)) { - if (VP8PutBit(bw, uv_mode != V_PRED, 114)) { - VP8PutBit(bw, uv_mode != H_PRED, 183); // else: TM_PRED - } - } -} - -static void PutSegment(VP8BitWriter* const bw, int s, const uint8_t* p) { - if (VP8PutBit(bw, s >= 2, p[0])) p += 1; - VP8PutBit(bw, s & 1, p[1]); -} - -void VP8CodeIntraModes(VP8Encoder* const enc) { - VP8BitWriter* const bw = &enc->bw_; - VP8EncIterator it; - VP8IteratorInit(enc, &it); - do { - const VP8MBInfo* mb = it.mb_; - const uint8_t* preds = it.preds_; - if (enc->segment_hdr_.update_map_) { - PutSegment(bw, mb->segment_, enc->proba_.segments_); - } - if (enc->proba_.use_skip_proba_) { - VP8PutBit(bw, mb->skip_, enc->proba_.skip_proba_); - } - if (VP8PutBit(bw, (mb->type_ != 0), 145)) { // i16x16 - PutI16Mode(bw, preds[0]); - } else { - const int preds_w = enc->preds_w_; - const uint8_t* top_pred = preds - preds_w; - int x, y; - for (y = 0; y < 4; ++y) { - int left = preds[-1]; - for (x = 0; x < 4; ++x) { - const uint8_t* const probas = kBModesProba[top_pred[x]][left]; - left = PutI4Mode(bw, preds[x], probas); - } - top_pred = preds; - preds += preds_w; - } - } - PutUVMode(bw, mb->uv_mode_); - } while (VP8IteratorNext(&it, 0)); -} - -//------------------------------------------------------------------------------ -// Paragraph 13 - -const uint8_t - VP8CoeffsUpdateProba[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 } - } - } -}; - -void VP8WriteProbas(VP8BitWriter* const bw, const VP8Proba* const probas) { - 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) { - 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); - } - } - } - } - } - if (VP8PutBitUniform(bw, probas->use_skip_proba_)) { - VP8PutValue(bw, probas->skip_proba_, 8); - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/vp8enci.h b/drivers/webpold/enc/vp8enci.h deleted file mode 100644 index 936e1c18ce..0000000000 --- a/drivers/webpold/enc/vp8enci.h +++ /dev/null @@ -1,525 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// WebP encoder: internal header. -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_ENC_VP8ENCI_H_ -#define WEBP_ENC_VP8ENCI_H_ - -#include <string.h> // for memcpy() -#include "../encode.h" -#include "../dsp/dsp.h" -#include "../utils/bit_writer.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Various defines and enums - -// version numbers -#define ENC_MAJ_VERSION 0 -#define ENC_MIN_VERSION 2 -#define ENC_REV_VERSION 0 - -// 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 { 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 - }; - -// YUV-cache parameters. Cache is 16-pixels wide. -// 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) - -extern const int VP8Scan[16 + 4 + 4]; // 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) -// 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) -// 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) - -typedef int64_t score_t; // type used for scores, rate, distortion -#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; -} -extern const uint8_t VP8Zigzag[16]; - -//------------------------------------------------------------------------------ -// Headers - -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 double LFStats[NUM_MB_SEGMENTS][MAX_LF_LEVELS]; // filter stats - -typedef struct VP8Encoder VP8Encoder; - -// segment features -typedef struct { - int num_segments_; // Actual number of segments. 1 segment only = unused. - 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; - -// 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 - StatsArray stats_[NUM_TYPES][NUM_BANDS]; // 4224 bytes - CostArray level_cost_[NUM_TYPES][NUM_BANDS]; // 11.4k - 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; - -// Filter parameters. Not actually used in the code (we don't perform -// the in-loop filtering), but filled from user's config -typedef struct { - int simple_; // filtering type: 0=complex, 1=simple - int level_; // base filter level [0..63] - int sharpness_; // [0..7] - int i4x4_lf_delta_; // delta filter level for i4x4 relative to i16x16 -} VP8FilterHeader; - -//------------------------------------------------------------------------------ -// Informations about the macroblocks. - -typedef struct { - // block type - unsigned int type_:2; // 0=i4x4, 1=i16x16 - unsigned int uv_mode_:2; - unsigned int skip_:1; - unsigned int segment_:2; - uint8_t alpha_; // quantization-susceptibility -} VP8MBInfo; - -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 - uint16_t sharpen_[16]; // frequency boosters for slight sharpening -} VP8Matrix; - -typedef struct { - VP8Matrix y1_, y2_, uv_; // quantization matrices - int alpha_; // quant-susceptibility, range [-127,127]. Zero is neutral. - // Lower values indicate a lower risk of blurriness. - int beta_; // filter-susceptibility, range [0,255]. - int quant_; // final segment quantizer. - int fstrength_; // final in-loop filtering strength - // 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 -// and mode evaluation. -typedef struct { - score_t D, SD, R, score; // Distortion, spectral distortion, 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]; - int mode_i16; // mode number for intra16 prediction - uint8_t modes_i4[16]; // mode numbers for intra4 predictions - int mode_uv; // mode number of chroma prediction - uint32_t nz; // non-zero blocks -} VP8ModeScore; - -// Iterator structure to iterate through macroblocks, pointing to the -// 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_; // '' - VP8Encoder* enc_; // back-pointer - VP8MBInfo* mb_; // current macroblock - VP8BitWriter* bw_; // current bit-writer - uint8_t* preds_; // intra mode predictors (4x4 blocks) - uint32_t* nz_; // non-zero pattern - uint8_t i4_boundary_[37]; // 32+5 boundary samples needed by intra4x4 - uint8_t* i4_top_; // pointer to the current top boundary sample - int i4_; // current intra4x4 mode being tested - int top_nz_[9]; // top-non-zero context. - int left_nz_[9]; // left-non-zero. left_nz[8] is independent. - uint64_t bit_count_[4][3]; // bit counters for coded levels. - uint64_t luma_bits_; // macroblock bit-cost for luma - 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 percent0_; // saved initial progress percent -} VP8EncIterator; - - // in iterator.c -// must be called first. -void VP8IteratorInit(VP8Encoder* const enc, VP8EncIterator* const it); -// restart a scan. -void VP8IteratorReset(VP8EncIterator* const it); -// import samples from source -void VP8IteratorImport(const VP8EncIterator* const it); -// 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); -// Report progression based on macroblock rows. Return 0 for user-abort request. -int VP8IteratorProgress(const VP8EncIterator* const it, - int final_delta_percent); -// Intra4x4 iterations -void VP8IteratorStartI4(VP8EncIterator* const it); -// returns true if not done. -int VP8IteratorRotateI4(VP8EncIterator* const it, - const uint8_t* const yuv_out); - -// Non-zero context setup/teardown -void VP8IteratorNzToBytes(VP8EncIterator* const it); -void VP8IteratorBytesToNz(VP8EncIterator* const it); - -// Helper functions to set mode properties -void VP8SetIntra16Mode(const VP8EncIterator* const it, int mode); -void VP8SetIntra4Mode(const VP8EncIterator* const it, const uint8_t* modes); -void VP8SetIntraUVMode(const VP8EncIterator* const it, int mode); -void VP8SetSkip(const VP8EncIterator* const it, int skip); -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* rows_; - uint16_t* tokens_; // set to (*last_)->tokens_ - VP8Tokens** last_; - int left_; - 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 - -int VP8EmitTokens(const VP8TBuffer* const b, VP8BitWriter* const bw, - const uint8_t* const probas); - -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; -} - -#endif // USE_TOKEN_BUFFER - -//------------------------------------------------------------------------------ -// VP8Encoder - -struct VP8Encoder { - const WebPConfig* config_; // user configuration and parameters - WebPPicture* pic_; // input / output picture - - // headers - VP8FilterHeader filter_hdr_; // filtering information - VP8SegmentHeader segment_hdr_; // segment information - - int profile_; // VP8's profile, deduced from Config. - - // dimension, in macroblock units. - int mb_w_, mb_h_; - int preds_w_; // stride of the *preds_ prediction plane (=4*mb_w + 1) - - // number of partitions (1, 2, 4 or 8 = MAX_NUM_PARTITIONS) - int num_parts_; - - // per-partition boolean decoders. - VP8BitWriter bw_; // part0 - VP8BitWriter parts_[MAX_NUM_PARTITIONS]; // token partitions - - int percent_; // for progress - - // transparency blob - 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_; - - // 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 uv_alpha_; // U/V quantization susceptibility - // global offset of quantizers, shared by all segments - int dq_y1_dc_; - int dq_y2_dc_, dq_y2_ac_; - 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]; - - // 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 - - // 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) -}; - -//------------------------------------------------------------------------------ -// internal functions. Not public. - - // in tree.c -extern const uint8_t VP8CoeffsProba0[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS]; -extern const uint8_t - VP8CoeffsUpdateProba[NUM_TYPES][NUM_BANDS][NUM_CTX][NUM_PROBAS]; -// 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); -// Writes the partition #0 modes (that is: all intra modes) -void VP8CodeIntraModes(VP8Encoder* const enc); - - // in syntax.c -// Generates the final bitstream by coding the partition0 and headers, -// and appending an assembly of all the pre-coded token partitions. -// Return true if everything is ok. -int VP8EncWrite(VP8Encoder* const enc); -// Release memory allocated for bit-writing in VP8EncLoop & seq. -void VP8EncFreeBitWriters(VP8Encoder* const enc); - - // in frame.c -extern const uint8_t VP8EncBands[16 + 1]; -// 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 -void VP8MakeChroma8Preds(const VP8EncIterator* const it); -// Form all the ten Intra4x4 predictions in the yuv_p_ cache -// for the 4x4 block it->i4_ -void VP8MakeIntra4Preds(const VP8EncIterator* const it); -// Rate calculation -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 -int VP8EncLoop(VP8Encoder* const enc); -int VP8StatLoop(VP8Encoder* const enc); - - // in webpenc.c -// Assign an error code to a picture. Return false for convenience. -int WebPEncodingSetError(const WebPPicture* const pic, WebPEncodingError error); -int WebPReportProgress(const WebPPicture* const pic, - int percent, int* const percent_store); - - // in analysis.c -// Main analysis loop. Decides the segmentations and complexity. -// Assigns a first guess for Intra16 and uvmode_ prediction modes. -int VP8EncAnalyze(VP8Encoder* const enc); - - // in quant.c -// 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); - - // in alpha.c -void VP8EncInitAlpha(VP8Encoder* const enc); // initialize alpha compression -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 - - // in filter.c - -// SSIM utils -typedef struct { - double w, xm, ym, xxm, xym, yym; -} DistoStats; -void VP8SSIMAddStats(const DistoStats* const src, DistoStats* const dst); -void VP8SSIMAccumulatePlane(const uint8_t* src1, int stride1, - const uint8_t* src2, int stride2, - int W, int H, DistoStats* const stats); -double VP8SSIMGet(const DistoStats* const stats); -double VP8SSIMGetSquaredError(const DistoStats* const stats); - -// autofilter -void VP8InitFilter(VP8EncIterator* const it); -void VP8StoreFilterStats(VP8EncIterator* const it); -void VP8AdjustFilterStrength(VP8EncIterator* const it); - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_ENC_VP8ENCI_H_ */ diff --git a/drivers/webpold/enc/vp8l.c b/drivers/webpold/enc/vp8l.c deleted file mode 100644 index f4eb6e783f..0000000000 --- a/drivers/webpold/enc/vp8l.c +++ /dev/null @@ -1,1150 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// main entry for the lossless encoder. -// -// Author: Vikas Arora (vikaas.arora@gmail.com) -// - -#include <assert.h> -#include <stdio.h> -#include <stdlib.h> - -#include "./backward_references.h" -#include "./vp8enci.h" -#include "./vp8li.h" -#include "../dsp/lossless.h" -#include "../utils/bit_writer.h" -#include "../utils/huffman_encode.h" -#include "../utils/utils.h" -#include "../format_constants.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define PALETTE_KEY_RIGHT_SHIFT 22 // Key for 1K buffer. -#define MAX_HUFF_IMAGE_SIZE (16 * 1024 * 1024) -#define MAX_COLORS_FOR_GRAPH 64 - -// ----------------------------------------------------------------------------- -// Palette - -static int CompareColors(const void* p1, const void* p2) { - const uint32_t a = *(const uint32_t*)p1; - const uint32_t b = *(const uint32_t*)p2; - return (a < b) ? -1 : (a > b) ? 1 : 0; -} - -// If number of colors in the image is less than or equal to MAX_PALETTE_SIZE, -// creates a palette and returns true, else returns false. -static int AnalyzeAndCreatePalette(const WebPPicture* const pic, - uint32_t palette[MAX_PALETTE_SIZE], - int* const palette_size) { - int i, x, y, key; - int num_colors = 0; - uint8_t in_use[MAX_PALETTE_SIZE * 4] = { 0 }; - uint32_t colors[MAX_PALETTE_SIZE * 4]; - static const uint32_t kHashMul = 0x1e35a7bd; - const uint32_t* argb = pic->argb; - const int width = pic->width; - const int height = pic->height; - uint32_t last_pix = ~argb[0]; // so we're sure that last_pix != argb[0] - - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - if (argb[x] == last_pix) { - continue; - } - last_pix = argb[x]; - key = (kHashMul * last_pix) >> PALETTE_KEY_RIGHT_SHIFT; - while (1) { - if (!in_use[key]) { - colors[key] = last_pix; - in_use[key] = 1; - ++num_colors; - if (num_colors > MAX_PALETTE_SIZE) { - return 0; - } - break; - } else if (colors[key] == last_pix) { - // The color is already there. - break; - } else { - // Some other color sits there. - // Do linear conflict resolution. - ++key; - key &= (MAX_PALETTE_SIZE * 4 - 1); // key mask for 1K buffer. - } - } - } - argb += pic->argb_stride; - } - - // TODO(skal): could we reuse in_use[] to speed up ApplyPalette()? - num_colors = 0; - for (i = 0; i < (int)(sizeof(in_use) / sizeof(in_use[0])); ++i) { - if (in_use[i]) { - palette[num_colors] = colors[i]; - ++num_colors; - } - } - - qsort(palette, num_colors, sizeof(*palette), CompareColors); - *palette_size = num_colors; - return 1; -} - -static int AnalyzeEntropy(const uint32_t* argb, - int width, int height, int argb_stride, - double* const nonpredicted_bits, - double* const predicted_bits) { - int x, y; - const uint32_t* last_line = NULL; - uint32_t last_pix = argb[0]; // so we're sure that pix_diff == 0 - - VP8LHistogram* nonpredicted = NULL; - VP8LHistogram* predicted = - (VP8LHistogram*)malloc(2 * sizeof(*predicted)); - if (predicted == NULL) return 0; - nonpredicted = predicted + 1; - - VP8LHistogramInit(predicted, 0); - VP8LHistogramInit(nonpredicted, 0); - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const uint32_t pix = argb[x]; - const uint32_t pix_diff = VP8LSubPixels(pix, last_pix); - if (pix_diff == 0) continue; - if (last_line != NULL && pix == last_line[x]) { - continue; - } - last_pix = pix; - { - const PixOrCopy pix_token = PixOrCopyCreateLiteral(pix); - const PixOrCopy pix_diff_token = PixOrCopyCreateLiteral(pix_diff); - VP8LHistogramAddSinglePixOrCopy(nonpredicted, &pix_token); - VP8LHistogramAddSinglePixOrCopy(predicted, &pix_diff_token); - } - } - last_line = argb; - argb += argb_stride; - } - *nonpredicted_bits = VP8LHistogramEstimateBitsBulk(nonpredicted); - *predicted_bits = VP8LHistogramEstimateBitsBulk(predicted); - free(predicted); - return 1; -} - -static int VP8LEncAnalyze(VP8LEncoder* const enc, WebPImageHint image_hint) { - const WebPPicture* const pic = enc->pic_; - assert(pic != NULL && pic->argb != NULL); - - enc->use_palette_ = - AnalyzeAndCreatePalette(pic, enc->palette_, &enc->palette_size_); - - if (image_hint == WEBP_HINT_GRAPH) { - if (enc->use_palette_ && enc->palette_size_ < MAX_COLORS_FOR_GRAPH) { - enc->use_palette_ = 0; - } - } - - if (!enc->use_palette_) { - if (image_hint == WEBP_HINT_PHOTO) { - enc->use_predict_ = 1; - enc->use_cross_color_ = 1; - } else { - double non_pred_entropy, pred_entropy; - if (!AnalyzeEntropy(pic->argb, pic->width, pic->height, pic->argb_stride, - &non_pred_entropy, &pred_entropy)) { - return 0; - } - if (pred_entropy < 0.95 * non_pred_entropy) { - enc->use_predict_ = 1; - // TODO(vikasa): Observed some correlation of cross_color transform with - // predict. Need to investigate this further and add separate heuristic - // for setting use_cross_color flag. - enc->use_cross_color_ = 1; - } - } - } - - return 1; -} - -static int GetHuffBitLengthsAndCodes( - const VP8LHistogramSet* const histogram_image, - HuffmanTreeCode* const huffman_codes) { - int i, k; - int ok = 1; - uint64_t total_length_size = 0; - uint8_t* mem_buf = NULL; - const int histogram_image_size = histogram_image->size; - - // Iterate over all histograms and get the aggregate number of codes used. - for (i = 0; i < histogram_image_size; ++i) { - const VP8LHistogram* const histo = histogram_image->histograms[i]; - HuffmanTreeCode* const codes = &huffman_codes[5 * i]; - for (k = 0; k < 5; ++k) { - const int num_symbols = (k == 0) ? VP8LHistogramNumCodes(histo) - : (k == 4) ? NUM_DISTANCE_CODES - : 256; - codes[k].num_symbols = num_symbols; - total_length_size += num_symbols; - } - } - - // Allocate and Set Huffman codes. - { - uint16_t* codes; - uint8_t* lengths; - mem_buf = (uint8_t*)WebPSafeCalloc(total_length_size, - sizeof(*lengths) + sizeof(*codes)); - if (mem_buf == NULL) { - ok = 0; - goto End; - } - codes = (uint16_t*)mem_buf; - lengths = (uint8_t*)&codes[total_length_size]; - for (i = 0; i < 5 * histogram_image_size; ++i) { - const int bit_length = huffman_codes[i].num_symbols; - huffman_codes[i].codes = codes; - huffman_codes[i].code_lengths = lengths; - codes += bit_length; - lengths += bit_length; - } - } - - // Create Huffman trees. - for (i = 0; i < histogram_image_size; ++i) { - HuffmanTreeCode* const codes = &huffman_codes[5 * i]; - VP8LHistogram* const histo = histogram_image->histograms[i]; - ok = ok && VP8LCreateHuffmanTree(histo->literal_, 15, codes + 0); - ok = ok && VP8LCreateHuffmanTree(histo->red_, 15, codes + 1); - ok = ok && VP8LCreateHuffmanTree(histo->blue_, 15, codes + 2); - ok = ok && VP8LCreateHuffmanTree(histo->alpha_, 15, codes + 3); - ok = ok && VP8LCreateHuffmanTree(histo->distance_, 15, codes + 4); - } - - End: - if (!ok) free(mem_buf); - return ok; -} - -static void StoreHuffmanTreeOfHuffmanTreeToBitMask( - VP8LBitWriter* const bw, const uint8_t* code_length_bitdepth) { - // RFC 1951 will calm you down if you are worried about this funny sequence. - // This sequence is tuned from that, but more weighted for lower symbol count, - // and more spiking histograms. - static const uint8_t kStorageOrder[CODE_LENGTH_CODES] = { - 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 - }; - int i; - // Throw away trailing zeros: - int codes_to_store = CODE_LENGTH_CODES; - for (; codes_to_store > 4; --codes_to_store) { - if (code_length_bitdepth[kStorageOrder[codes_to_store - 1]] != 0) { - break; - } - } - VP8LWriteBits(bw, 4, codes_to_store - 4); - for (i = 0; i < codes_to_store; ++i) { - VP8LWriteBits(bw, 3, code_length_bitdepth[kStorageOrder[i]]); - } -} - -static void ClearHuffmanTreeIfOnlyOneSymbol( - HuffmanTreeCode* const huffman_code) { - int k; - int count = 0; - for (k = 0; k < huffman_code->num_symbols; ++k) { - if (huffman_code->code_lengths[k] != 0) { - ++count; - if (count > 1) return; - } - } - for (k = 0; k < huffman_code->num_symbols; ++k) { - huffman_code->code_lengths[k] = 0; - huffman_code->codes[k] = 0; - } -} - -static void StoreHuffmanTreeToBitMask( - VP8LBitWriter* const bw, - const HuffmanTreeToken* const tokens, const int num_tokens, - const HuffmanTreeCode* const huffman_code) { - int i; - for (i = 0; i < num_tokens; ++i) { - const int ix = tokens[i].code; - const int extra_bits = tokens[i].extra_bits; - VP8LWriteBits(bw, huffman_code->code_lengths[ix], huffman_code->codes[ix]); - switch (ix) { - case 16: - VP8LWriteBits(bw, 2, extra_bits); - break; - case 17: - VP8LWriteBits(bw, 3, extra_bits); - break; - case 18: - VP8LWriteBits(bw, 7, extra_bits); - break; - } - } -} - -static int StoreFullHuffmanCode(VP8LBitWriter* const bw, - const HuffmanTreeCode* const tree) { - int ok = 0; - uint8_t code_length_bitdepth[CODE_LENGTH_CODES] = { 0 }; - uint16_t code_length_bitdepth_symbols[CODE_LENGTH_CODES] = { 0 }; - const int max_tokens = tree->num_symbols; - int num_tokens; - HuffmanTreeCode huffman_code; - HuffmanTreeToken* const tokens = - (HuffmanTreeToken*)WebPSafeMalloc((uint64_t)max_tokens, sizeof(*tokens)); - if (tokens == NULL) return 0; - - huffman_code.num_symbols = CODE_LENGTH_CODES; - huffman_code.code_lengths = code_length_bitdepth; - huffman_code.codes = code_length_bitdepth_symbols; - - VP8LWriteBits(bw, 1, 0); - num_tokens = VP8LCreateCompressedHuffmanTree(tree, tokens, max_tokens); - { - int histogram[CODE_LENGTH_CODES] = { 0 }; - int i; - for (i = 0; i < num_tokens; ++i) { - ++histogram[tokens[i].code]; - } - - if (!VP8LCreateHuffmanTree(histogram, 7, &huffman_code)) { - goto End; - } - } - - StoreHuffmanTreeOfHuffmanTreeToBitMask(bw, code_length_bitdepth); - ClearHuffmanTreeIfOnlyOneSymbol(&huffman_code); - { - int trailing_zero_bits = 0; - int trimmed_length = num_tokens; - int write_trimmed_length; - int length; - int i = num_tokens; - while (i-- > 0) { - const int ix = tokens[i].code; - if (ix == 0 || ix == 17 || ix == 18) { - --trimmed_length; // discount trailing zeros - trailing_zero_bits += code_length_bitdepth[ix]; - if (ix == 17) { - trailing_zero_bits += 3; - } else if (ix == 18) { - trailing_zero_bits += 7; - } - } else { - break; - } - } - write_trimmed_length = (trimmed_length > 1 && trailing_zero_bits > 12); - length = write_trimmed_length ? trimmed_length : num_tokens; - VP8LWriteBits(bw, 1, write_trimmed_length); - if (write_trimmed_length) { - const int nbits = VP8LBitsLog2Ceiling(trimmed_length - 1); - const int nbitpairs = (nbits == 0) ? 1 : (nbits + 1) / 2; - VP8LWriteBits(bw, 3, nbitpairs - 1); - assert(trimmed_length >= 2); - VP8LWriteBits(bw, nbitpairs * 2, trimmed_length - 2); - } - StoreHuffmanTreeToBitMask(bw, tokens, length, &huffman_code); - } - ok = 1; - End: - free(tokens); - return ok; -} - -static int StoreHuffmanCode(VP8LBitWriter* const bw, - const HuffmanTreeCode* const huffman_code) { - int i; - int count = 0; - int symbols[2] = { 0, 0 }; - const int kMaxBits = 8; - const int kMaxSymbol = 1 << kMaxBits; - - // Check whether it's a small tree. - for (i = 0; i < huffman_code->num_symbols && count < 3; ++i) { - if (huffman_code->code_lengths[i] != 0) { - if (count < 2) symbols[count] = i; - ++count; - } - } - - if (count == 0) { // emit minimal tree for empty cases - // bits: small tree marker: 1, count-1: 0, large 8-bit code: 0, code: 0 - VP8LWriteBits(bw, 4, 0x01); - return 1; - } else if (count <= 2 && symbols[0] < kMaxSymbol && symbols[1] < kMaxSymbol) { - VP8LWriteBits(bw, 1, 1); // Small tree marker to encode 1 or 2 symbols. - VP8LWriteBits(bw, 1, count - 1); - if (symbols[0] <= 1) { - VP8LWriteBits(bw, 1, 0); // Code bit for small (1 bit) symbol value. - VP8LWriteBits(bw, 1, symbols[0]); - } else { - VP8LWriteBits(bw, 1, 1); - VP8LWriteBits(bw, 8, symbols[0]); - } - if (count == 2) { - VP8LWriteBits(bw, 8, symbols[1]); - } - return 1; - } else { - return StoreFullHuffmanCode(bw, huffman_code); - } -} - -static void WriteHuffmanCode(VP8LBitWriter* const bw, - const HuffmanTreeCode* const code, int index) { - const int depth = code->code_lengths[index]; - const int symbol = code->codes[index]; - VP8LWriteBits(bw, depth, symbol); -} - -static void StoreImageToBitMask( - VP8LBitWriter* const bw, int width, int histo_bits, - const VP8LBackwardRefs* const refs, - const uint16_t* histogram_symbols, - const HuffmanTreeCode* const huffman_codes) { - // x and y trace the position in the image. - int x = 0; - int y = 0; - const int histo_xsize = histo_bits ? VP8LSubSampleSize(width, histo_bits) : 1; - int i; - for (i = 0; i < refs->size; ++i) { - const PixOrCopy* const v = &refs->refs[i]; - const int histogram_ix = histogram_symbols[histo_bits ? - (y >> histo_bits) * histo_xsize + - (x >> histo_bits) : 0]; - const HuffmanTreeCode* const codes = huffman_codes + 5 * histogram_ix; - if (PixOrCopyIsCacheIdx(v)) { - const int code = PixOrCopyCacheIdx(v); - const int literal_ix = 256 + NUM_LENGTH_CODES + code; - WriteHuffmanCode(bw, codes, literal_ix); - } else if (PixOrCopyIsLiteral(v)) { - static const int order[] = { 1, 2, 0, 3 }; - int k; - for (k = 0; k < 4; ++k) { - const int code = PixOrCopyLiteral(v, order[k]); - WriteHuffmanCode(bw, codes + k, code); - } - } else { - int bits, n_bits; - int code, distance; - - PrefixEncode(v->len, &code, &n_bits, &bits); - WriteHuffmanCode(bw, codes, 256 + code); - VP8LWriteBits(bw, n_bits, bits); - - distance = PixOrCopyDistance(v); - PrefixEncode(distance, &code, &n_bits, &bits); - WriteHuffmanCode(bw, codes + 4, code); - VP8LWriteBits(bw, n_bits, bits); - } - x += PixOrCopyLength(v); - while (x >= width) { - x -= width; - ++y; - } - } -} - -// Special case of EncodeImageInternal() for cache-bits=0, histo_bits=31 -static int EncodeImageNoHuffman(VP8LBitWriter* const bw, - const uint32_t* const argb, - int width, int height, int quality) { - int i; - int ok = 0; - VP8LBackwardRefs refs; - HuffmanTreeCode huffman_codes[5] = { { 0, NULL, NULL } }; - const uint16_t histogram_symbols[1] = { 0 }; // only one tree, one symbol - VP8LHistogramSet* const histogram_image = VP8LAllocateHistogramSet(1, 0); - if (histogram_image == NULL) return 0; - - // Calculate backward references from ARGB image. - if (!VP8LGetBackwardReferences(width, height, argb, quality, 0, 1, &refs)) { - goto Error; - } - // Build histogram image and symbols from backward references. - VP8LHistogramStoreRefs(&refs, histogram_image->histograms[0]); - - // Create Huffman bit lengths and codes for each histogram image. - assert(histogram_image->size == 1); - if (!GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) { - goto Error; - } - - // No color cache, no Huffman image. - VP8LWriteBits(bw, 1, 0); - - // Store Huffman codes. - for (i = 0; i < 5; ++i) { - HuffmanTreeCode* const codes = &huffman_codes[i]; - if (!StoreHuffmanCode(bw, codes)) { - goto Error; - } - ClearHuffmanTreeIfOnlyOneSymbol(codes); - } - - // Store actual literals. - StoreImageToBitMask(bw, width, 0, &refs, histogram_symbols, huffman_codes); - ok = 1; - - Error: - free(histogram_image); - VP8LClearBackwardRefs(&refs); - free(huffman_codes[0].codes); - return ok; -} - -static int EncodeImageInternal(VP8LBitWriter* const bw, - const uint32_t* const argb, - int width, int height, int quality, - int cache_bits, int histogram_bits) { - int ok = 0; - const int use_2d_locality = 1; - const int use_color_cache = (cache_bits > 0); - const uint32_t histogram_image_xysize = - VP8LSubSampleSize(width, histogram_bits) * - VP8LSubSampleSize(height, histogram_bits); - VP8LHistogramSet* histogram_image = - VP8LAllocateHistogramSet(histogram_image_xysize, 0); - int histogram_image_size = 0; - size_t bit_array_size = 0; - HuffmanTreeCode* huffman_codes = NULL; - VP8LBackwardRefs refs; - uint16_t* const histogram_symbols = - (uint16_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize, - sizeof(*histogram_symbols)); - assert(histogram_bits >= MIN_HUFFMAN_BITS); - assert(histogram_bits <= MAX_HUFFMAN_BITS); - if (histogram_image == NULL || histogram_symbols == NULL) goto Error; - - // Calculate backward references from ARGB image. - if (!VP8LGetBackwardReferences(width, height, argb, quality, cache_bits, - use_2d_locality, &refs)) { - goto Error; - } - // Build histogram image and symbols from backward references. - if (!VP8LGetHistoImageSymbols(width, height, &refs, - quality, histogram_bits, cache_bits, - histogram_image, - histogram_symbols)) { - goto Error; - } - // Create Huffman bit lengths and codes for each histogram image. - histogram_image_size = histogram_image->size; - bit_array_size = 5 * histogram_image_size; - huffman_codes = (HuffmanTreeCode*)WebPSafeCalloc(bit_array_size, - sizeof(*huffman_codes)); - if (huffman_codes == NULL || - !GetHuffBitLengthsAndCodes(histogram_image, huffman_codes)) { - goto Error; - } - - // Color Cache parameters. - VP8LWriteBits(bw, 1, use_color_cache); - if (use_color_cache) { - VP8LWriteBits(bw, 4, cache_bits); - } - - // Huffman image + meta huffman. - { - const int write_histogram_image = (histogram_image_size > 1); - VP8LWriteBits(bw, 1, write_histogram_image); - if (write_histogram_image) { - uint32_t* const histogram_argb = - (uint32_t*)WebPSafeMalloc((uint64_t)histogram_image_xysize, - sizeof(*histogram_argb)); - int max_index = 0; - uint32_t i; - if (histogram_argb == NULL) goto Error; - for (i = 0; i < histogram_image_xysize; ++i) { - const int index = histogram_symbols[i] & 0xffff; - histogram_argb[i] = 0xff000000 | (index << 8); - if (index >= max_index) { - max_index = index + 1; - } - } - histogram_image_size = max_index; - - VP8LWriteBits(bw, 3, histogram_bits - 2); - ok = EncodeImageNoHuffman(bw, histogram_argb, - VP8LSubSampleSize(width, histogram_bits), - VP8LSubSampleSize(height, histogram_bits), - quality); - free(histogram_argb); - if (!ok) goto Error; - } - } - - // Store Huffman codes. - { - int i; - for (i = 0; i < 5 * histogram_image_size; ++i) { - HuffmanTreeCode* const codes = &huffman_codes[i]; - if (!StoreHuffmanCode(bw, codes)) goto Error; - ClearHuffmanTreeIfOnlyOneSymbol(codes); - } - } - // Free combined histograms. - free(histogram_image); - histogram_image = NULL; - - // Store actual literals. - StoreImageToBitMask(bw, width, histogram_bits, &refs, - histogram_symbols, huffman_codes); - ok = 1; - - Error: - if (!ok) free(histogram_image); - - VP8LClearBackwardRefs(&refs); - if (huffman_codes != NULL) { - free(huffman_codes->codes); - free(huffman_codes); - } - free(histogram_symbols); - return ok; -} - -// ----------------------------------------------------------------------------- -// Transforms - -// Check if it would be a good idea to subtract green from red and blue. We -// only impact entropy in red/blue components, don't bother to look at others. -static int EvalAndApplySubtractGreen(VP8LEncoder* const enc, - int width, int height, - VP8LBitWriter* const bw) { - if (!enc->use_palette_) { - int i; - const uint32_t* const argb = enc->argb_; - double bit_cost_before, bit_cost_after; - VP8LHistogram* const histo = (VP8LHistogram*)malloc(sizeof(*histo)); - if (histo == NULL) return 0; - - VP8LHistogramInit(histo, 1); - for (i = 0; i < width * height; ++i) { - const uint32_t c = argb[i]; - ++histo->red_[(c >> 16) & 0xff]; - ++histo->blue_[(c >> 0) & 0xff]; - } - bit_cost_before = VP8LHistogramEstimateBits(histo); - - VP8LHistogramInit(histo, 1); - for (i = 0; i < width * height; ++i) { - const uint32_t c = argb[i]; - const int green = (c >> 8) & 0xff; - ++histo->red_[((c >> 16) - green) & 0xff]; - ++histo->blue_[((c >> 0) - green) & 0xff]; - } - bit_cost_after = VP8LHistogramEstimateBits(histo); - free(histo); - - // Check if subtracting green yields low entropy. - enc->use_subtract_green_ = (bit_cost_after < bit_cost_before); - if (enc->use_subtract_green_) { - VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); - VP8LWriteBits(bw, 2, SUBTRACT_GREEN); - VP8LSubtractGreenFromBlueAndRed(enc->argb_, width * height); - } - } - return 1; -} - -static int ApplyPredictFilter(const VP8LEncoder* const enc, - int width, int height, int quality, - VP8LBitWriter* const bw) { - const int pred_bits = enc->transform_bits_; - const int transform_width = VP8LSubSampleSize(width, pred_bits); - const int transform_height = VP8LSubSampleSize(height, pred_bits); - - VP8LResidualImage(width, height, pred_bits, enc->argb_, enc->argb_scratch_, - enc->transform_data_); - VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); - VP8LWriteBits(bw, 2, PREDICTOR_TRANSFORM); - assert(pred_bits >= 2); - VP8LWriteBits(bw, 3, pred_bits - 2); - if (!EncodeImageNoHuffman(bw, enc->transform_data_, - transform_width, transform_height, quality)) { - return 0; - } - return 1; -} - -static int ApplyCrossColorFilter(const VP8LEncoder* const enc, - int width, int height, int quality, - VP8LBitWriter* const bw) { - const int ccolor_transform_bits = enc->transform_bits_; - const int transform_width = VP8LSubSampleSize(width, ccolor_transform_bits); - const int transform_height = VP8LSubSampleSize(height, ccolor_transform_bits); - const int step = (quality == 0) ? 32 : 8; - - VP8LColorSpaceTransform(width, height, ccolor_transform_bits, step, - enc->argb_, enc->transform_data_); - VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); - VP8LWriteBits(bw, 2, CROSS_COLOR_TRANSFORM); - assert(ccolor_transform_bits >= 2); - VP8LWriteBits(bw, 3, ccolor_transform_bits - 2); - if (!EncodeImageNoHuffman(bw, enc->transform_data_, - transform_width, transform_height, quality)) { - return 0; - } - return 1; -} - -// ----------------------------------------------------------------------------- - -static void PutLE32(uint8_t* const data, uint32_t val) { - data[0] = (val >> 0) & 0xff; - data[1] = (val >> 8) & 0xff; - data[2] = (val >> 16) & 0xff; - data[3] = (val >> 24) & 0xff; -} - -static WebPEncodingError WriteRiffHeader(const WebPPicture* const pic, - size_t riff_size, size_t vp8l_size) { - uint8_t riff[RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE + VP8L_SIGNATURE_SIZE] = { - 'R', 'I', 'F', 'F', 0, 0, 0, 0, 'W', 'E', 'B', 'P', - 'V', 'P', '8', 'L', 0, 0, 0, 0, VP8L_MAGIC_BYTE, - }; - PutLE32(riff + TAG_SIZE, (uint32_t)riff_size); - PutLE32(riff + RIFF_HEADER_SIZE + TAG_SIZE, (uint32_t)vp8l_size); - if (!pic->writer(riff, sizeof(riff), pic)) { - return VP8_ENC_ERROR_BAD_WRITE; - } - return VP8_ENC_OK; -} - -static int WriteImageSize(const WebPPicture* const pic, - VP8LBitWriter* const bw) { - const int width = pic->width - 1; - const int height = pic->height - 1; - assert(width < WEBP_MAX_DIMENSION && height < WEBP_MAX_DIMENSION); - - VP8LWriteBits(bw, VP8L_IMAGE_SIZE_BITS, width); - VP8LWriteBits(bw, VP8L_IMAGE_SIZE_BITS, height); - return !bw->error_; -} - -static int WriteRealAlphaAndVersion(VP8LBitWriter* const bw, int has_alpha) { - VP8LWriteBits(bw, 1, has_alpha); - VP8LWriteBits(bw, VP8L_VERSION_BITS, VP8L_VERSION); - return !bw->error_; -} - -static WebPEncodingError WriteImage(const WebPPicture* const pic, - VP8LBitWriter* const bw, - size_t* const coded_size) { - WebPEncodingError err = VP8_ENC_OK; - const uint8_t* const webpll_data = VP8LBitWriterFinish(bw); - const size_t webpll_size = VP8LBitWriterNumBytes(bw); - const size_t vp8l_size = VP8L_SIGNATURE_SIZE + webpll_size; - const size_t pad = vp8l_size & 1; - const size_t riff_size = TAG_SIZE + CHUNK_HEADER_SIZE + vp8l_size + pad; - - err = WriteRiffHeader(pic, riff_size, vp8l_size); - if (err != VP8_ENC_OK) goto Error; - - if (!pic->writer(webpll_data, webpll_size, pic)) { - err = VP8_ENC_ERROR_BAD_WRITE; - goto Error; - } - - if (pad) { - const uint8_t pad_byte[1] = { 0 }; - if (!pic->writer(pad_byte, 1, pic)) { - err = VP8_ENC_ERROR_BAD_WRITE; - goto Error; - } - } - *coded_size = CHUNK_HEADER_SIZE + riff_size; - return VP8_ENC_OK; - - Error: - return err; -} - -// ----------------------------------------------------------------------------- - -// Allocates the memory for argb (W x H) buffer, 2 rows of context for -// prediction and transform data. -static WebPEncodingError AllocateTransformBuffer(VP8LEncoder* const enc, - int width, int height) { - WebPEncodingError err = VP8_ENC_OK; - const int tile_size = 1 << enc->transform_bits_; - const uint64_t image_size = width * height; - const uint64_t argb_scratch_size = tile_size * width + width; - const uint64_t transform_data_size = - (uint64_t)VP8LSubSampleSize(width, enc->transform_bits_) * - (uint64_t)VP8LSubSampleSize(height, enc->transform_bits_); - const uint64_t total_size = - image_size + argb_scratch_size + transform_data_size; - uint32_t* mem = (uint32_t*)WebPSafeMalloc(total_size, sizeof(*mem)); - if (mem == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - enc->argb_ = mem; - mem += image_size; - enc->argb_scratch_ = mem; - mem += argb_scratch_size; - enc->transform_data_ = mem; - enc->current_width_ = width; - - Error: - return err; -} - -// Bundles multiple (2, 4 or 8) pixels into a single pixel. -// Returns the new xsize. -static void BundleColorMap(const WebPPicture* const pic, - int xbits, uint32_t* bundled_argb, int xs) { - int y; - const int bit_depth = 1 << (3 - xbits); - uint32_t code = 0; - const uint32_t* argb = pic->argb; - const int width = pic->width; - const int height = pic->height; - - for (y = 0; y < height; ++y) { - int x; - for (x = 0; x < width; ++x) { - const int mask = (1 << xbits) - 1; - const int xsub = x & mask; - if (xsub == 0) { - code = 0; - } - // TODO(vikasa): simplify the bundling logic. - code |= (argb[x] & 0xff00) << (bit_depth * xsub); - bundled_argb[y * xs + (x >> xbits)] = 0xff000000 | code; - } - argb += pic->argb_stride; - } -} - -// Note: Expects "enc->palette_" to be set properly. -// Also, "enc->palette_" will be modified after this call and should not be used -// later. -static WebPEncodingError ApplyPalette(VP8LBitWriter* const bw, - VP8LEncoder* const enc, int quality) { - WebPEncodingError err = VP8_ENC_OK; - int i, x, y; - const WebPPicture* const pic = enc->pic_; - uint32_t* argb = pic->argb; - const int width = pic->width; - const int height = pic->height; - uint32_t* const palette = enc->palette_; - const int palette_size = enc->palette_size_; - - // Replace each input pixel by corresponding palette index. - for (y = 0; y < height; ++y) { - for (x = 0; x < width; ++x) { - const uint32_t pix = argb[x]; - for (i = 0; i < palette_size; ++i) { - if (pix == palette[i]) { - argb[x] = 0xff000000u | (i << 8); - break; - } - } - } - argb += pic->argb_stride; - } - - // Save palette to bitstream. - VP8LWriteBits(bw, 1, TRANSFORM_PRESENT); - VP8LWriteBits(bw, 2, COLOR_INDEXING_TRANSFORM); - assert(palette_size >= 1); - VP8LWriteBits(bw, 8, palette_size - 1); - for (i = palette_size - 1; i >= 1; --i) { - palette[i] = VP8LSubPixels(palette[i], palette[i - 1]); - } - if (!EncodeImageNoHuffman(bw, palette, palette_size, 1, quality)) { - err = VP8_ENC_ERROR_INVALID_CONFIGURATION; - goto Error; - } - - if (palette_size <= 16) { - // Image can be packed (multiple pixels per uint32_t). - int xbits = 1; - if (palette_size <= 2) { - xbits = 3; - } else if (palette_size <= 4) { - xbits = 2; - } - err = AllocateTransformBuffer(enc, VP8LSubSampleSize(width, xbits), height); - if (err != VP8_ENC_OK) goto Error; - BundleColorMap(pic, xbits, enc->argb_, enc->current_width_); - } - - Error: - return err; -} - -// ----------------------------------------------------------------------------- - -static int GetHistoBits(const WebPConfig* const config, - const WebPPicture* const pic) { - const int width = pic->width; - const int height = pic->height; - const size_t hist_size = sizeof(VP8LHistogram); - // Make tile size a function of encoding method (Range: 0 to 6). - int histo_bits = 7 - config->method; - while (1) { - const size_t huff_image_size = VP8LSubSampleSize(width, histo_bits) * - VP8LSubSampleSize(height, histo_bits) * - hist_size; - if (huff_image_size <= MAX_HUFF_IMAGE_SIZE) break; - ++histo_bits; - } - return (histo_bits < MIN_HUFFMAN_BITS) ? MIN_HUFFMAN_BITS : - (histo_bits > MAX_HUFFMAN_BITS) ? MAX_HUFFMAN_BITS : histo_bits; -} - -static void InitEncParams(VP8LEncoder* const enc) { - const WebPConfig* const config = enc->config_; - const WebPPicture* const picture = enc->pic_; - const int method = config->method; - const float quality = config->quality; - enc->transform_bits_ = (method < 4) ? 5 : (method > 4) ? 3 : 4; - enc->histo_bits_ = GetHistoBits(config, picture); - enc->cache_bits_ = (quality <= 25.f) ? 0 : 7; -} - -// ----------------------------------------------------------------------------- -// VP8LEncoder - -static VP8LEncoder* VP8LEncoderNew(const WebPConfig* const config, - const WebPPicture* const picture) { - VP8LEncoder* const enc = (VP8LEncoder*)calloc(1, sizeof(*enc)); - if (enc == NULL) { - WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); - return NULL; - } - enc->config_ = config; - enc->pic_ = picture; - return enc; -} - -static void VP8LEncoderDelete(VP8LEncoder* enc) { - free(enc->argb_); - free(enc); -} - -// ----------------------------------------------------------------------------- -// Main call - -WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, - const WebPPicture* const picture, - VP8LBitWriter* const bw) { - WebPEncodingError err = VP8_ENC_OK; - const int quality = (int)config->quality; - const int width = picture->width; - const int height = picture->height; - VP8LEncoder* const enc = VP8LEncoderNew(config, picture); - const size_t byte_position = VP8LBitWriterNumBytes(bw); - - if (enc == NULL) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - InitEncParams(enc); - - // --------------------------------------------------------------------------- - // Analyze image (entropy, num_palettes etc) - - if (!VP8LEncAnalyze(enc, config->image_hint)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - if (enc->use_palette_) { - err = ApplyPalette(bw, enc, quality); - if (err != VP8_ENC_OK) goto Error; - // Color cache is disabled for palette. - enc->cache_bits_ = 0; - } - - // In case image is not packed. - if (enc->argb_ == NULL) { - int y; - err = AllocateTransformBuffer(enc, width, height); - if (err != VP8_ENC_OK) goto Error; - for (y = 0; y < height; ++y) { - memcpy(enc->argb_ + y * width, - picture->argb + y * picture->argb_stride, - width * sizeof(*enc->argb_)); - } - enc->current_width_ = width; - } - - // --------------------------------------------------------------------------- - // Apply transforms and write transform data. - - if (!EvalAndApplySubtractGreen(enc, enc->current_width_, height, bw)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - if (enc->use_predict_) { - if (!ApplyPredictFilter(enc, enc->current_width_, height, quality, bw)) { - err = VP8_ENC_ERROR_INVALID_CONFIGURATION; - goto Error; - } - } - - if (enc->use_cross_color_) { - if (!ApplyCrossColorFilter(enc, enc->current_width_, height, quality, bw)) { - err = VP8_ENC_ERROR_INVALID_CONFIGURATION; - goto Error; - } - } - - VP8LWriteBits(bw, 1, !TRANSFORM_PRESENT); // No more transforms. - - // --------------------------------------------------------------------------- - // Estimate the color cache size. - - if (enc->cache_bits_ > 0) { - if (!VP8LCalculateEstimateForCacheSize(enc->argb_, enc->current_width_, - height, &enc->cache_bits_)) { - err = VP8_ENC_ERROR_INVALID_CONFIGURATION; - goto Error; - } - } - - // --------------------------------------------------------------------------- - // Encode and write the transformed image. - - if (!EncodeImageInternal(bw, enc->argb_, enc->current_width_, height, - quality, enc->cache_bits_, enc->histo_bits_)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - if (picture->stats != NULL) { - WebPAuxStats* const stats = picture->stats; - stats->lossless_features = 0; - if (enc->use_predict_) stats->lossless_features |= 1; - if (enc->use_cross_color_) stats->lossless_features |= 2; - if (enc->use_subtract_green_) stats->lossless_features |= 4; - if (enc->use_palette_) stats->lossless_features |= 8; - stats->histogram_bits = enc->histo_bits_; - stats->transform_bits = enc->transform_bits_; - stats->cache_bits = enc->cache_bits_; - stats->palette_size = enc->palette_size_; - stats->lossless_size = (int)(VP8LBitWriterNumBytes(bw) - byte_position); - } - - Error: - VP8LEncoderDelete(enc); - return err; -} - -int VP8LEncodeImage(const WebPConfig* const config, - const WebPPicture* const picture) { - int width, height; - int has_alpha; - size_t coded_size; - int percent = 0; - WebPEncodingError err = VP8_ENC_OK; - VP8LBitWriter bw; - - if (picture == NULL) return 0; - - if (config == NULL || picture->argb == NULL) { - err = VP8_ENC_ERROR_NULL_PARAMETER; - WebPEncodingSetError(picture, err); - return 0; - } - - width = picture->width; - height = picture->height; - if (!VP8LBitWriterInit(&bw, (width * height) >> 1)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - if (!WebPReportProgress(picture, 1, &percent)) { - UserAbort: - err = VP8_ENC_ERROR_USER_ABORT; - goto Error; - } - // Reset stats (for pure lossless coding) - if (picture->stats != NULL) { - WebPAuxStats* const stats = picture->stats; - memset(stats, 0, sizeof(*stats)); - stats->PSNR[0] = 99.f; - stats->PSNR[1] = 99.f; - stats->PSNR[2] = 99.f; - stats->PSNR[3] = 99.f; - stats->PSNR[4] = 99.f; - } - - // Write image size. - if (!WriteImageSize(picture, &bw)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - has_alpha = WebPPictureHasTransparency(picture); - // Write the non-trivial Alpha flag and lossless version. - if (!WriteRealAlphaAndVersion(&bw, has_alpha)) { - err = VP8_ENC_ERROR_OUT_OF_MEMORY; - goto Error; - } - - if (!WebPReportProgress(picture, 5, &percent)) goto UserAbort; - - // Encode main image stream. - err = VP8LEncodeStream(config, picture, &bw); - if (err != VP8_ENC_OK) goto Error; - - // TODO(skal): have a fine-grained progress report in VP8LEncodeStream(). - if (!WebPReportProgress(picture, 90, &percent)) goto UserAbort; - - // Finish the RIFF chunk. - err = WriteImage(picture, &bw, &coded_size); - if (err != VP8_ENC_OK) goto Error; - - if (!WebPReportProgress(picture, 100, &percent)) goto UserAbort; - - // Save size. - if (picture->stats != NULL) { - picture->stats->coded_size += (int)coded_size; - picture->stats->lossless_size = (int)coded_size; - } - - if (picture->extra_info != NULL) { - const int mb_w = (width + 15) >> 4; - const int mb_h = (height + 15) >> 4; - memset(picture->extra_info, 0, mb_w * mb_h * sizeof(*picture->extra_info)); - } - - Error: - if (bw.error_) err = VP8_ENC_ERROR_OUT_OF_MEMORY; - VP8LBitWriterDestroy(&bw); - if (err != VP8_ENC_OK) { - WebPEncodingSetError(picture, err); - return 0; - } - return 1; -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/enc/vp8li.h b/drivers/webpold/enc/vp8li.h deleted file mode 100644 index bb111aec33..0000000000 --- a/drivers/webpold/enc/vp8li.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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 encoder: internal header. -// -// Author: Vikas Arora (vikaas.arora@gmail.com) - -#ifndef WEBP_ENC_VP8LI_H_ -#define WEBP_ENC_VP8LI_H_ - -#include "./histogram.h" -#include "../utils/bit_writer.h" -#include "../encode.h" -#include "../format_constants.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -typedef struct { - const WebPConfig* config_; // user configuration and parameters - const WebPPicture* pic_; // input picture. - - uint32_t* argb_; // Transformed argb image data. - uint32_t* argb_scratch_; // Scratch memory for argb rows - // (used for prediction). - uint32_t* transform_data_; // Scratch memory for transform data. - int current_width_; // Corresponds to packed image width. - - // Encoding parameters derived from quality parameter. - int histo_bits_; - int transform_bits_; - int cache_bits_; // If equal to 0, don't use color cache. - - // Encoding parameters derived from image characteristics. - int use_cross_color_; - int use_subtract_green_; - int use_predict_; - int use_palette_; - int palette_size_; - uint32_t palette_[MAX_PALETTE_SIZE]; -} VP8LEncoder; - -//------------------------------------------------------------------------------ -// internal functions. Not public. - -// Encodes the picture. -// Returns 0 if config or picture is NULL or picture doesn't have valid argb -// input. -int VP8LEncodeImage(const WebPConfig* const config, - const WebPPicture* const picture); - -// Encodes the main image stream using the supplied bit writer. -WebPEncodingError VP8LEncodeStream(const WebPConfig* const config, - const WebPPicture* const picture, - VP8LBitWriter* const bw); - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_ENC_VP8LI_H_ */ diff --git a/drivers/webpold/enc/webpenc.c b/drivers/webpold/enc/webpenc.c deleted file mode 100644 index 3c275589fc..0000000000 --- a/drivers/webpold/enc/webpenc.c +++ /dev/null @@ -1,389 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// WebP encoder: main entry point -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <assert.h> -#include <stdlib.h> -#include <string.h> -#include <math.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 - -//------------------------------------------------------------------------------ - -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_; - 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_; - hdr->simple_ = 1; - hdr->level_ = 0; - hdr->sharpness_ = 0; - hdr->i4x4_lf_delta_ = 0; -} - -static void ResetBoundaryPredictions(VP8Encoder* const enc) { - // init boundary values once for all - // Note: actually, initializing the preds_[] is only needed for intra4. - int i; - uint8_t* const top = enc->preds_ - enc->preds_w_; - uint8_t* const left = enc->preds_ - 1; - for (i = -1; i < 4 * enc->mb_w_; ++i) { - top[i] = B_DC_PRED; - } - for (i = 0; i < 4 * enc->mb_h_; ++i) { - left[i * enc->preds_w_] = B_DC_PRED; - } - 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 | -//-------------+---+---+---+---+---+---+ - -static void MapConfigToTools(VP8Encoder* const enc) { - const int method = enc->config_->method; - const int limit = 100 - enc->config_->partition_limit; - enc->method_ = method; - enc->rd_opt_level_ = (method >= 6) ? 3 - : (method >= 5) ? 2 - : (method >= 3) ? 1 - : 0; - 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. -} - -// 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 -// LFStats: 2048 -// Picture size (yuv): 589824 - -static VP8Encoder* InitVP8Encoder(const WebPConfig* const config, - WebPPicture* const picture) { - const int use_filter = - (config->filter_strength > 0) || (config->autofilter > 0); - const int mb_w = (picture->width + 15) >> 4; - const int mb_h = (picture->height + 15) >> 4; - const int preds_w = 4 * mb_w + 1; - const int preds_h = 4 * mb_h + 1; - const size_t preds_size = preds_w * preds_h * sizeof(uint8_t); - const int top_stride = mb_w * 16; - const size_t nz_size = (mb_w + 1) * sizeof(uint32_t); - const size_t cache_size = (3 * YUV_SIZE + PRED_SIZE) * sizeof(uint8_t); - const size_t info_size = mb_w * mb_h * sizeof(VP8MBInfo); - const size_t samples_size = (2 * top_stride + // top-luma/u/v - 16 + 16 + 16 + 8 + 1 + // left y/u/v - 2 * ALIGN_CST) // align all - * sizeof(uint8_t); - const size_t lf_stats_size = - config->autofilter ? sizeof(LFStats) + ALIGN_CST : 0; - VP8Encoder* enc; - uint8_t* mem; - const uint64_t size = (uint64_t)sizeof(VP8Encoder) // main struct - + ALIGN_CST // cache alignment - + cache_size // working caches - + info_size // modes info - + preds_size // prediction modes - + samples_size // top/left samples - + nz_size // coeff context bits - + lf_stats_size; // autofilter stats - -#ifdef PRINT_MEMORY_INFO - printf("===================================\n"); - printf("Memory used:\n" - " encoder: %ld\n" - " block cache: %ld\n" - " info: %ld\n" - " preds: %ld\n" - " top samples: %ld\n" - " non-zero: %ld\n" - " lf-stats: %ld\n" - " total: %ld\n", - sizeof(VP8Encoder) + ALIGN_CST, cache_size, info_size, - preds_size, samples_size, nz_size, lf_stats_size, size); - printf("Transcient object sizes:\n" - " VP8EncIterator: %ld\n" - " VP8ModeScore: %ld\n" - " VP8SegmentInfo: %ld\n" - " VP8Proba: %ld\n" - " LFStats: %ld\n", - sizeof(VP8EncIterator), sizeof(VP8ModeScore), - sizeof(VP8SegmentInfo), sizeof(VP8Proba), - sizeof(LFStats)); - printf("Picture size (yuv): %ld\n", - mb_w * mb_h * 384 * sizeof(uint8_t)); - printf("===================================\n"); -#endif - mem = (uint8_t*)WebPSafeMalloc(size, sizeof(*mem)); - if (mem == NULL) { - WebPEncodingSetError(picture, VP8_ENC_ERROR_OUT_OF_MEMORY); - return NULL; - } - enc = (VP8Encoder*)mem; - mem = (uint8_t*)DO_ALIGN(mem + sizeof(*enc)); - memset(enc, 0, sizeof(*enc)); - enc->num_parts_ = 1 << config->partitions; - enc->mb_w_ = mb_w; - enc->mb_h_ = mb_h; - enc->preds_w_ = preds_w; - enc->yuv_in_ = (uint8_t*)mem; - mem += YUV_SIZE; - enc->yuv_out_ = (uint8_t*)mem; - mem += YUV_SIZE; - enc->yuv_out2_ = (uint8_t*)mem; - mem += YUV_SIZE; - enc->yuv_p_ = (uint8_t*)mem; - mem += PRED_SIZE; - enc->mb_info_ = (VP8MBInfo*)mem; - mem += info_size; - enc->preds_ = ((uint8_t*)mem) + 1 + enc->preds_w_; - mem += preds_w * preds_h * sizeof(uint8_t); - enc->nz_ = 1 + (uint32_t*)mem; - mem += nz_size; - enc->lf_stats_ = lf_stats_size ? (LFStats*)DO_ALIGN(mem) : NULL; - mem += lf_stats_size; - - // top samples (all 16-aligned) - mem = (uint8_t*)DO_ALIGN(mem); - enc->y_top_ = (uint8_t*)mem; - enc->uv_top_ = enc->y_top_ + top_stride; - mem += 2 * top_stride; - mem = (uint8_t*)DO_ALIGN(mem + 1); - enc->y_left_ = (uint8_t*)mem; - mem += 16 + 16; - enc->u_left_ = (uint8_t*)mem; - mem += 16; - enc->v_left_ = (uint8_t*)mem; - mem += 8; - - enc->config_ = config; - enc->profile_ = use_filter ? ((config->filter_type == 1) ? 0 : 1) : 2; - enc->pic_ = picture; - enc->percent_ = 0; - - MapConfigToTools(enc); - VP8EncDspInit(); - VP8DefaultProbas(enc); - ResetSegmentHeader(enc); - ResetFilterHeader(enc); - ResetBoundaryPredictions(enc); - - VP8EncInitAlpha(enc); -#ifdef WEBP_EXPERIMENTAL_FEATURES - VP8EncInitLayer(enc); -#endif - - return enc; -} - -static void DeleteVP8Encoder(VP8Encoder* enc) { - if (enc != NULL) { - VP8EncDeleteAlpha(enc); -#ifdef WEBP_EXPERIMENTAL_FEATURES - VP8EncDeleteLayer(enc); -#endif - free(enc); - } -} - -//------------------------------------------------------------------------------ - -static double GetPSNR(uint64_t err, uint64_t size) { - return err ? 10. * log10(255. * 255. * size / err) : 99.; -} - -static void FinalizePSNR(const VP8Encoder* const enc) { - WebPAuxStats* stats = enc->pic_->stats; - const uint64_t size = enc->sse_count_; - const uint64_t* const sse = enc->sse_; - stats->PSNR[0] = (float)GetPSNR(sse[0], size); - stats->PSNR[1] = (float)GetPSNR(sse[1], size / 4); - stats->PSNR[2] = (float)GetPSNR(sse[2], size / 4); - stats->PSNR[3] = (float)GetPSNR(sse[0] + sse[1] + sse[2], size * 3 / 2); - stats->PSNR[4] = (float)GetPSNR(sse[3], size); -} - -static void StoreStats(VP8Encoder* const enc) { - WebPAuxStats* const stats = enc->pic_->stats; - if (stats != NULL) { - int i, s; - for (i = 0; i < NUM_MB_SEGMENTS; ++i) { - stats->segment_level[i] = enc->dqm_[i].fstrength_; - stats->segment_quant[i] = enc->dqm_[i].quant_; - for (s = 0; s <= 2; ++s) { - stats->residual_bytes[s][i] = enc->residual_bytes_[s][i]; - } - } - FinalizePSNR(enc); - stats->coded_size = enc->coded_size_; - for (i = 0; i < 3; ++i) { - stats->block_count[i] = enc->block_count_[i]; - } - } - WebPReportProgress(enc->pic_, 100, &enc->percent_); // done! -} - -int WebPEncodingSetError(const WebPPicture* const pic, - WebPEncodingError error) { - assert((int)error < VP8_ENC_ERROR_LAST); - assert((int)error >= VP8_ENC_OK); - ((WebPPicture*)pic)->error_code = error; - return 0; -} - -int WebPReportProgress(const WebPPicture* const pic, - int percent, int* const percent_store) { - if (percent_store != NULL && percent != *percent_store) { - *percent_store = percent; - if (pic->progress_hook && !pic->progress_hook(percent, pic)) { - // user abort requested - WebPEncodingSetError(pic, VP8_ENC_ERROR_USER_ABORT); - return 0; - } - } - return 1; // ok -} -//------------------------------------------------------------------------------ - -int WebPEncode(const WebPConfig* config, WebPPicture* pic) { - int ok; - - if (pic == NULL) - return 0; - WebPEncodingSetError(pic, VP8_ENC_OK); // all ok so far - if (config == NULL) // bad params - return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); - if (!WebPValidateConfig(config)) - return WebPEncodingSetError(pic, VP8_ENC_ERROR_INVALID_CONFIGURATION); - if (pic->width <= 0 || pic->height <= 0) - return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); - if (pic->width > WEBP_MAX_DIMENSION || pic->height > WEBP_MAX_DIMENSION) - return WebPEncodingSetError(pic, VP8_ENC_ERROR_BAD_DIMENSION); - - 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; - } else { - return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); - } - } - - 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); - StoreStats(enc); - if (!ok) { - VP8EncFreeBitWriters(enc); - } - DeleteVP8Encoder(enc); - } else { - if (pic->argb == NULL) - return WebPEncodingSetError(pic, VP8_ENC_ERROR_NULL_PARAMETER); - - ok = VP8LEncodeImage(config, pic); // Sets pic->error in case of problem. - } - - return ok; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/encode.h b/drivers/webpold/encode.h deleted file mode 100644 index 2e37cfabe7..0000000000 --- a/drivers/webpold/encode.h +++ /dev/null @@ -1,463 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// WebP encoder: main interface -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_WEBP_ENCODE_H_ -#define WEBP_WEBP_ENCODE_H_ - -#include "./types.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define WEBP_ENCODER_ABI_VERSION 0x0200 // MAJOR(8b) + MINOR(8b) - -// Return the encoder's version number, packed in hexadecimal using 8bits for -// each of major/minor/revision. E.g: v2.5.7 is 0x020507. -WEBP_EXTERN(int) WebPGetEncoderVersion(void); - -//------------------------------------------------------------------------------ -// One-stop-shop call! No questions asked: - -// 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)'. -// 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). -WEBP_EXTERN(size_t) WebPEncodeRGB(const uint8_t* rgb, - int width, int height, int stride, - float quality_factor, uint8_t** output); -WEBP_EXTERN(size_t) WebPEncodeBGR(const uint8_t* bgr, - int width, int height, int stride, - float quality_factor, uint8_t** output); -WEBP_EXTERN(size_t) WebPEncodeRGBA(const uint8_t* rgba, - int width, int height, int stride, - float quality_factor, uint8_t** output); -WEBP_EXTERN(size_t) WebPEncodeBGRA(const uint8_t* bgra, - int width, int height, int stride, - float quality_factor, uint8_t** output); - -// These functions are the equivalent of the above, but compressing in a -// lossless manner. Files are usually larger than lossy format, but will -// not suffer any compression loss. -WEBP_EXTERN(size_t) WebPEncodeLosslessRGB(const uint8_t* rgb, - int width, int height, int stride, - uint8_t** output); -WEBP_EXTERN(size_t) WebPEncodeLosslessBGR(const uint8_t* bgr, - int width, int height, int stride, - uint8_t** output); -WEBP_EXTERN(size_t) WebPEncodeLosslessRGBA(const uint8_t* rgba, - int width, int height, int stride, - uint8_t** output); -WEBP_EXTERN(size_t) WebPEncodeLosslessBGRA(const uint8_t* bgra, - int width, int height, int stride, - uint8_t** output); - -//------------------------------------------------------------------------------ -// Coding parameters - -// Image characteristics hint for the underlying encoder. -typedef enum { - WEBP_HINT_DEFAULT = 0, // default preset. - WEBP_HINT_PICTURE, // digital picture, like portrait, inner shot - WEBP_HINT_PHOTO, // outdoor photograph, with natural lighting - WEBP_HINT_GRAPH, // Discrete tone image (graph, map-tile etc). - WEBP_HINT_LAST -} WebPImageHint; - -typedef struct { - 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) - - WebPImageHint image_hint; // Hint for image type (lossless only for now). - - // Parameters related to lossy compression only: - int target_size; // if non-zero, set the desired target size in bytes. - // Takes precedence over the 'compression' parameter. - float target_PSNR; // if non-zero, specifies the minimal distortion to - // try to achieve. Takes precedence over target_size. - int segments; // maximum number of segments to use, in [1..4] - int sns_strength; // Spatial Noise Shaping. 0=off, 100=maximum. - int filter_strength; // range: [0 = off .. 100 = strongest] - int filter_sharpness; // range: [0 = off .. 7 = least sharp] - int filter_type; // filtering type: 0 = simple, 1 = strong (only used - // if filter_strength > 0 or autofilter > 0) - int autofilter; // Auto adjust filter's strength [0 = off, 1 = on] - int alpha_compression; // Algorithm for encoding the alpha plane (0 = none, - // 1 = compressed with WebP lossless). Default is 1. - int alpha_filtering; // Predictive filtering method for alpha plane. - // 0: none, 1: fast, 2: best. Default if 1. - int alpha_quality; // Between 0 (smallest size) and 100 (lossless). - // Default is 100. - int pass; // number of entropy-analysis passes (in [1..10]). - - 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 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). - - uint32_t pad[8]; // padding for later use -} WebPConfig; - -// Enumerate some predefined settings for WebPConfig, depending on the type -// of source picture. These presets are used when calling WebPConfigPreset(). -typedef enum { - WEBP_PRESET_DEFAULT = 0, // default preset. - WEBP_PRESET_PICTURE, // digital picture, like portrait, inner shot - WEBP_PRESET_PHOTO, // outdoor photograph, with natural lighting - WEBP_PRESET_DRAWING, // hand or line drawing, with high-contrast details - WEBP_PRESET_ICON, // small-sized colorful images - WEBP_PRESET_TEXT // text-like -} WebPPreset; - -// Internal, version-checked, entry point -WEBP_EXTERN(int) WebPConfigInitInternal(WebPConfig*, WebPPreset, float, int); - -// Should always be called, to initialize a fresh WebPConfig structure before -// modification. Returns false in case of version mismatch. WebPConfigInit() -// must have succeeded before using the 'config' object. -// Note that the default values are lossless=0 and quality=75. -static WEBP_INLINE int WebPConfigInit(WebPConfig* config) { - return WebPConfigInitInternal(config, WEBP_PRESET_DEFAULT, 75.f, - WEBP_ENCODER_ABI_VERSION); -} - -// This function will initialize the configuration according to a predefined -// set of parameters (referred to by 'preset') and a given quality factor. -// This function can be called as a replacement to WebPConfigInit(). Will -// return false in case of error. -static WEBP_INLINE int WebPConfigPreset(WebPConfig* config, - WebPPreset preset, float quality) { - return WebPConfigInitInternal(config, preset, quality, - WEBP_ENCODER_ABI_VERSION); -} - -// 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 { - int coded_size; // final size - - float PSNR[5]; // peak-signal-to-noise ratio for Y/U/V/All/Alpha - int block_count[3]; // number of intra4/intra16/skipped macroblocks - int header_bytes[2]; // approximate number of bytes spent for header - // and mode-partition #0 - int residual_bytes[3][4]; // approximate number of bytes spent for - // DC/AC/uv coefficients for each (0..3) segments. - int segment_size[4]; // number of macroblocks in each segments - int segment_quant[4]; // quantizer values for each segments - int segment_level[4]; // filtering strength for each segments [0..63] - - int alpha_data_size; // size of the transparency data - int layer_data_size; // size of the enhancement layer data - - // lossless encoder statistics - uint32_t lossless_features; // bit0:predictor bit1:cross-color transform - // bit2:subtract-green bit3:color indexing - int histogram_bits; // number of precision bits of histogram - int transform_bits; // precision bits for transform - 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 - - uint32_t pad[4]; // padding for later use -} WebPAuxStats; - -// 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 -// reference (and so one can make use of picture->custom_ptr). -typedef int (*WebPWriterFunction)(const uint8_t* data, size_t data_size, - const WebPPicture* picture); - -// WebPMemoryWrite: a special WebPWriterFunction that writes to memory using -// the following WebPMemoryWriter object (to be set as a custom_ptr). -typedef struct { - 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 custom writer to be used with WebPMemoryWriter as custom_ptr. Upon -// completion, writer.mem and writer.size will hold the coded data. -WEBP_EXTERN(int) WebPMemoryWrite(const uint8_t* data, size_t data_size, - const WebPPicture* picture); - -// Progress hook, called from time to time to report progress. It can return -// false to request an abort of the encoding process, or true otherwise if -// everything is OK. -typedef int (*WebPProgressHook)(int percent, const WebPPicture* picture); - -typedef enum { - // 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_CSP_ALPHA_BIT = 4 // bit that is set if alpha is present -} WebPEncCSP; - -// Encoding error conditions. -typedef enum { - 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 - VP8_ENC_ERROR_NULL_PARAMETER, // a pointer parameter is NULL - VP8_ENC_ERROR_INVALID_CONFIGURATION, // configuration is invalid - VP8_ENC_ERROR_BAD_DIMENSION, // picture has invalid width/height - VP8_ENC_ERROR_PARTITION0_OVERFLOW, // partition is bigger than 512k - VP8_ENC_ERROR_PARTITION_OVERFLOW, // partition is bigger than 16M - VP8_ENC_ERROR_BAD_WRITE, // error while flushing bytes - VP8_ENC_ERROR_FILE_TOO_BIG, // file is bigger than 4G - VP8_ENC_ERROR_USER_ABORT, // abort request by user - VP8_ENC_ERROR_LAST // list terminator. always last. -} WebPEncodingError; - -// maximum width/height allowed (inclusive), in pixels -#define WEBP_MAX_DIMENSION 16383 - -// Main exchange structure (input samples, output bytes, statistics) -struct WebPPicture { - - // INPUT - ////////////// - // Main flag for encoder selecting between ARGB or YUV input. - // It is recommended to use ARGB input (*argb, argb_stride) for lossless - // compression, and YUV input (*y, *u, *v, etc.) for lossy compression - // since these are the respective native colorspace for these formats. - int use_argb; - - // YUV input (mostly used for input to lossy compression) - WebPEncCSP colorspace; // colorspace: should be YUV420 for now (=Y'CbCr). - int width, height; // dimensions (less or equal to WEBP_MAX_DIMENSION) - uint8_t *y, *u, *v; // pointers to luma/chroma planes. - int y_stride, uv_stride; // luma/chroma strides. - uint8_t* a; // pointer to the alpha plane - int a_stride; // stride of the alpha plane - uint32_t pad1[2]; // padding for later use - - // ARGB input (mostly used for input to lossless compression) - uint32_t* argb; // Pointer to argb (32 bit) plane. - int argb_stride; // This is stride in pixels units, not bytes. - uint32_t pad2[3]; // padding for later use - - // OUTPUT - /////////////// - // Byte-emission hook, to store compressed bytes as they are ready. - WebPWriterFunction writer; // can be NULL - void* custom_ptr; // can be used by the writer. - - // map for extra information (only for lossy compression mode) - int extra_info_type; // 1: intra type, 2: segment, 3: quant - // 4: intra-16 prediction mode, - // 5: chroma prediction mode, - // 6: bit cost, 7: distortion - uint8_t* extra_info; // if not NULL, points to an array of size - // ((width + 15) / 16) * ((height + 15) / 16) that - // will be filled with a macroblock map, depending - // on extra_info_type. - - // STATS AND REPORTS - /////////////////////////// - // Pointer to side statistics (updated only if not NULL) - WebPAuxStats* stats; - - // Error code for the latest error encountered during encoding - WebPEncodingError error_code; - - // If not NULL, report progress during encoding. - WebPProgressHook progress_hook; - - void* user_data; // this field is free to be set to any value and - // used during callbacks (like progress-report e.g.). - - 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 - - // 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 -}; - -// Internal, version-checked, entry point -WEBP_EXTERN(int) WebPPictureInitInternal(WebPPicture*, int); - -// Should always be called, to initialize the structure. Returns false in case -// of version mismatch. WebPPictureInit() must have succeeded before using the -// 'picture' object. -// Note that, by default, use_argb is false and colorspace is WEBP_YUV420. -static WEBP_INLINE int WebPPictureInit(WebPPicture* picture) { - return WebPPictureInitInternal(picture, WEBP_ENCODER_ABI_VERSION); -} - -//------------------------------------------------------------------------------ -// WebPPicture utils - -// Convenience allocation / deallocation based on picture->width/height: -// Allocate y/u/v buffers as per colorspace/width/height specification. -// Note! This function will free the previous buffer if needed. -// Returns false in case of memory error. -WEBP_EXTERN(int) WebPPictureAlloc(WebPPicture* picture); - -// Release the memory allocated by WebPPictureAlloc() or WebPPictureImport*(). -// Note that this function does _not_ free the memory used by the 'picture' -// object itself. -// Besides memory (which is reclaimed) all other fields of 'picture' are -// 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). -// 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, ...) -// Warning: this function is rather CPU-intensive. -WEBP_EXTERN(int) WebPPictureDistortion( - const WebPPicture* pic1, const WebPPicture* pic2, - int metric_type, // 0 = PSNR, 1 = SSIM - float result[5]); - -// self-crops a picture to the rectangle defined by top/left/width/height. -// Returns false in case of memory allocation error, or if the rectangle is -// outside of the source picture. -// The rectangle for the view is defined by the top-left corner pixel -// coordinates (left, top) as well as its width and height. This rectangle -// must be fully be comprised inside the 'src' source picture. If the source -// picture uses the YUV420 colorspace, the top and left coordinates will be -// snapped to even values. -WEBP_EXTERN(int) WebPPictureCrop(WebPPicture* picture, - int left, int top, int width, int height); - -// Extracts a view from 'src' picture into 'dst'. The rectangle for the view -// is defined by the top-left corner pixel coordinates (left, top) as well -// as its width and height. This rectangle must be fully be comprised inside -// the 'src' source picture. If the source picture uses the YUV420 colorspace, -// 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). -// 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, - WebPPicture* dst); - -// Returns true if the 'picture' is actually a view and therefore does -// not own the memory for pixels. -WEBP_EXTERN(int) WebPPictureIsView(const WebPPicture* picture); - -// Rescale a picture to new dimension width x height. -// Now 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); - -// Colorspace conversion function to import RGB samples. -// Previous buffer will be free'd, if any. -// *rgb buffer should have a size of at least height * rgb_stride. -// Returns false in case of memory error. -WEBP_EXTERN(int) WebPPictureImportRGB( - WebPPicture* picture, const uint8_t* rgb, int rgb_stride); -// Same, but for RGBA buffer. -WEBP_EXTERN(int) WebPPictureImportRGBA( - WebPPicture* picture, const uint8_t* rgba, int rgba_stride); -// Same, but for RGBA buffer. Imports the RGB direct from the 32-bit format -// input buffer ignoring the alpha channel. Avoids needing to copy the data -// to a temporary 24-bit RGB buffer to import the RGB only. -WEBP_EXTERN(int) WebPPictureImportRGBX( - WebPPicture* picture, const uint8_t* rgbx, int rgbx_stride); - -// Variants of the above, but taking BGR(A|X) input. -WEBP_EXTERN(int) WebPPictureImportBGR( - WebPPicture* picture, const uint8_t* bgr, int bgr_stride); -WEBP_EXTERN(int) WebPPictureImportBGRA( - WebPPicture* picture, const uint8_t* bgra, int bgra_stride); -WEBP_EXTERN(int) WebPPictureImportBGRX( - WebPPicture* picture, const uint8_t* bgrx, int bgrx_stride); - -// Converts picture->argb data to the YUVA format specified by 'colorspace'. -// 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); - -// Converts picture->yuv to picture->argb and sets picture->use_argb to true. -// The input format must be YUV_420 or YUV_420A. -// Note that the use of this method is discouraged if one has access to the -// raw ARGB samples, since using YUV420 is comparatively lossy. Also, the -// conversion from YUV420 to ARGB incurs a small loss too. -// 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). -WEBP_EXTERN(void) WebPCleanupTransparentArea(WebPPicture* picture); - -// Scan the picture 'picture' for the presence of non fully opaque alpha values. -// Returns true in such case. Otherwise returns false (indicating that the -// alpha plane can be ignored altogether e.g.). -WEBP_EXTERN(int) WebPPictureHasTransparency(const WebPPicture* picture); - -//------------------------------------------------------------------------------ -// Main call - -// Main encoding call, after config and picture have been initialized. -// 'picture' must be less than 16384x16384 in dimension (cf WEBP_MAX_DIMENSION), -// and the 'config' object must be a valid one. -// Returns false in case of error, true otherwise. -// In case of error, picture->error_code is updated accordingly. -// 'picture' can hold the source samples in both YUV(A) or ARGB input, depending -// on the value of 'picture->use_argb'. It is highly recommended to use -// the former for lossy encoding, and the latter for lossless encoding -// (when config.lossless is true). Automatic conversion from one format to -// another is provided but they both incur some loss. -WEBP_EXTERN(int) WebPEncode(const WebPConfig* config, WebPPicture* picture); - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_WEBP_ENCODE_H_ */ diff --git a/drivers/webpold/format_constants.h b/drivers/webpold/format_constants.h deleted file mode 100644 index 7ce498f672..0000000000 --- a/drivers/webpold/format_constants.h +++ /dev/null @@ -1,90 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Internal header for constants related to WebP file format. -// -// Author: Urvang (urvang@google.com) - -#ifndef WEBP_WEBP_FORMAT_CONSTANTS_H_ -#define WEBP_WEBP_FORMAT_CONSTANTS_H_ - -// VP8 related constants. -#define VP8_SIGNATURE 0x9d012a // Signature in VP8 data. -#define VP8_MAX_PARTITION0_SIZE (1 << 19) // max size of mode partition -#define VP8_MAX_PARTITION_SIZE (1 << 24) // max size for token partition -#define VP8_FRAME_HEADER_SIZE 10 // Size of the frame header within VP8 data. - -// VP8L related constants. -#define VP8L_SIGNATURE_SIZE 1 // VP8L signature size. -#define VP8L_MAGIC_BYTE 0x2f // VP8L signature byte. -#define VP8L_IMAGE_SIZE_BITS 14 // Number of bits used to store - // width and height. -#define VP8L_VERSION_BITS 3 // 3 bits reserved for version. -#define VP8L_VERSION 0 // version 0 -#define VP8L_FRAME_HEADER_SIZE 5 // Size of the VP8L frame header. - -#define MAX_PALETTE_SIZE 256 -#define MAX_CACHE_BITS 11 -#define HUFFMAN_CODES_PER_META_CODE 5 -#define ARGB_BLACK 0xff000000 - -#define DEFAULT_CODE_LENGTH 8 -#define MAX_ALLOWED_CODE_LENGTH 15 - -#define NUM_LITERAL_CODES 256 -#define NUM_LENGTH_CODES 24 -#define NUM_DISTANCE_CODES 40 -#define CODE_LENGTH_CODES 19 - -#define MIN_HUFFMAN_BITS 2 // min number of Huffman bits -#define MAX_HUFFMAN_BITS 9 // max number of Huffman bits - -#define TRANSFORM_PRESENT 1 // The bit to be written when next data - // to be read is a transform. -#define NUM_TRANSFORMS 4 // Maximum number of allowed transform - // in a bitstream. -typedef enum { - PREDICTOR_TRANSFORM = 0, - CROSS_COLOR_TRANSFORM = 1, - SUBTRACT_GREEN = 2, - COLOR_INDEXING_TRANSFORM = 3 -} VP8LImageTransformType; - -// Alpha related constants. -#define ALPHA_HEADER_LEN 1 -#define ALPHA_NO_COMPRESSION 0 -#define ALPHA_LOSSLESS_COMPRESSION 1 -#define ALPHA_PREPROCESSED_LEVELS 1 - -// Mux related constants. -#define TAG_SIZE 4 // Size of a chunk tag (e.g. "VP8L"). -#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 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 - -// Maximum chunk payload is such that adding the header and padding won't -// overflow a uint32_t. -#define MAX_CHUNK_PAYLOAD (~0U - CHUNK_HEADER_SIZE - 1) - -#endif /* WEBP_WEBP_FORMAT_CONSTANTS_H_ */ diff --git a/drivers/webpold/image_loader_webp.cpp b/drivers/webpold/image_loader_webp.cpp deleted file mode 100644 index 68bb857293..0000000000 --- a/drivers/webpold/image_loader_webp.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/*************************************************************************/ -/* image_loader_webp.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* http://www.godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#include "image_loader_webp.h" - -#include "print_string.h" -#include "os/os.h" -#include "drivers/webp/decode.h" -#include "drivers/webp/encode.h" -#include "io/marshalls.h" -#include <stdlib.h> - -static DVector<uint8_t> _webp_lossy_pack(const Image& p_image,float p_quality) { - - ERR_FAIL_COND_V(p_image.empty(),DVector<uint8_t>()); - - Image img=p_image; - if (img.detect_alpha()) - img.convert(Image::FORMAT_RGBA); - else - img.convert(Image::FORMAT_RGB); - - Size2 s(img.get_width(),img.get_height()); - DVector<uint8_t> data = img.get_data(); - DVector<uint8_t>::Read r = data.read(); - - uint8_t *dst_buff=NULL; - size_t dst_size=0; - if (img.get_format()==Image::FORMAT_RGB) { - - dst_size = WebPEncodeRGB(r.ptr(),s.width,s.height,3*s.width,CLAMP(p_quality*100.0,0,100.0),&dst_buff); - } else { - dst_size = WebPEncodeRGBA(r.ptr(),s.width,s.height,4*s.width,CLAMP(p_quality*100.0,0,100.0),&dst_buff); - } - - ERR_FAIL_COND_V(dst_size==0,DVector<uint8_t>()); - DVector<uint8_t> dst; - dst.resize(4+dst_size); - DVector<uint8_t>::Write w = dst.write(); - w[0]='W'; - w[1]='E'; - w[2]='B'; - w[3]='P'; - copymem(&w[4],dst_buff,dst_size); - free(dst_buff); - w=DVector<uint8_t>::Write(); - return dst; -} - -static Image _webp_lossy_unpack(const DVector<uint8_t>& p_buffer) { - - int size = p_buffer.size()-4; - ERR_FAIL_COND_V(size<=0,Image()); - DVector<uint8_t>::Read r = p_buffer.read(); - - ERR_FAIL_COND_V(r[0]!='W' || r[1]!='E' || r[2]!='B' || r[3]!='P',Image()); - WebPBitstreamFeatures features; - if (WebPGetFeatures(&r[4],size,&features)!=VP8_STATUS_OK) { - ERR_EXPLAIN("Error unpacking WEBP image:"); - ERR_FAIL_V(Image()); - } - - //print_line("width: "+itos(features.width)); - //print_line("height: "+itos(features.height)); - //print_line("alpha: "+itos(features.has_alpha)); - - DVector<uint8_t> dst_image; - int datasize = features.width*features.height*(features.has_alpha?4:3); - dst_image.resize(datasize); - - DVector<uint8_t>::Write dst_w = dst_image.write(); - - bool errdec=false; - if (features.has_alpha) { - errdec = WebPDecodeRGBAInto(&r[4],size,dst_w.ptr(),datasize,4*features.width)==NULL; - } else { - errdec = WebPDecodeRGBInto(&r[4],size,dst_w.ptr(),datasize,3*features.width)==NULL; - - } - - //ERR_EXPLAIN("Error decoding webp! - "+p_file); - ERR_FAIL_COND_V(errdec,Image()); - - dst_w = DVector<uint8_t>::Write(); - - return Image(features.width,features.height,0,features.has_alpha?Image::FORMAT_RGBA:Image::FORMAT_RGB,dst_image); - -} - - -Error ImageLoaderWEBP::load_image(Image *p_image,FileAccess *f) { - - - uint32_t size = f->get_len(); - DVector<uint8_t> src_image; - src_image.resize(size); - - WebPBitstreamFeatures features; - - DVector<uint8_t>::Write src_w = src_image.write(); - f->get_buffer(src_w.ptr(),size); - ERR_FAIL_COND_V(f->eof_reached(), ERR_FILE_EOF); - - if (WebPGetFeatures(src_w.ptr(),size,&features)!=VP8_STATUS_OK) { - f->close(); - //ERR_EXPLAIN("Error decoding WEBP image: "+p_file); - ERR_FAIL_V(ERR_FILE_CORRUPT); - } - - print_line("width: "+itos(features.width)); - print_line("height: "+itos(features.height)); - print_line("alpha: "+itos(features.has_alpha)); - - src_w = DVector<uint8_t>::Write(); - - DVector<uint8_t> dst_image; - int datasize = features.width*features.height*(features.has_alpha?4:3); - dst_image.resize(datasize); - - DVector<uint8_t>::Read src_r = src_image.read(); - DVector<uint8_t>::Write dst_w = dst_image.write(); - - - bool errdec=false; - if (features.has_alpha) { - errdec = WebPDecodeRGBAInto(src_r.ptr(),size,dst_w.ptr(),datasize,4*features.width)==NULL; - } else { - errdec = WebPDecodeRGBInto(src_r.ptr(),size,dst_w.ptr(),datasize,3*features.width)==NULL; - - } - - //ERR_EXPLAIN("Error decoding webp! - "+p_file); - ERR_FAIL_COND_V(errdec,ERR_FILE_CORRUPT); - - src_r = DVector<uint8_t>::Read(); - dst_w = DVector<uint8_t>::Write(); - - *p_image = Image(features.width,features.height,0,features.has_alpha?Image::FORMAT_RGBA:Image::FORMAT_RGB,dst_image); - - - return OK; - -} - -void ImageLoaderWEBP::get_recognized_extensions(List<String> *p_extensions) const { - - p_extensions->push_back("webp"); -} - - -ImageLoaderWEBP::ImageLoaderWEBP() { - - Image::lossy_packer=_webp_lossy_pack; - Image::lossy_unpacker=_webp_lossy_unpack; -} - - diff --git a/drivers/webpold/image_loader_webp.h b/drivers/webpold/image_loader_webp.h deleted file mode 100644 index 24f79708db..0000000000 --- a/drivers/webpold/image_loader_webp.h +++ /dev/null @@ -1,49 +0,0 @@ -/*************************************************************************/ -/* image_loader_webp.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* http://www.godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2016 Juan Linietsky, Ariel Manzur. */ -/* */ -/* Permission is hereby granted, free of charge, to any person obtaining */ -/* a copy of this software and associated documentation files (the */ -/* "Software"), to deal in the Software without restriction, including */ -/* without limitation the rights to use, copy, modify, merge, publish, */ -/* distribute, sublicense, and/or sell copies of the Software, and to */ -/* permit persons to whom the Software is furnished to do so, subject to */ -/* the following conditions: */ -/* */ -/* The above copyright notice and this permission notice shall be */ -/* included in all copies or substantial portions of the Software. */ -/* */ -/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ -/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ -/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/ -/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ -/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ -/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ -/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -/*************************************************************************/ -#ifndef IMAGE_LOADER_WEBP_H -#define IMAGE_LOADER_WEBP_H - -#include "io/image_loader.h" - -/** - @author Juan Linietsky <reduzio@gmail.com> -*/ -class ImageLoaderWEBP : public ImageFormatLoader { - - -public: - - virtual Error load_image(Image *p_image,FileAccess *f); - virtual void get_recognized_extensions(List<String> *p_extensions) const; - ImageLoaderWEBP(); -}; - - - -#endif diff --git a/drivers/webpold/mux.h b/drivers/webpold/mux.h deleted file mode 100644 index 5139af80fa..0000000000 --- a/drivers/webpold/mux.h +++ /dev/null @@ -1,604 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// RIFF container manipulation 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" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define WEBP_MUX_ABI_VERSION 0x0100 // MAJOR(8b) + MINOR(8b) - -// Error codes -typedef enum { - WEBP_MUX_OK = 1, - WEBP_MUX_NOT_FOUND = 0, - WEBP_MUX_INVALID_ARGUMENT = -1, - WEBP_MUX_BAD_DATA = -2, - WEBP_MUX_MEMORY_ERROR = -3, - 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 { - WEBP_CHUNK_VP8X, // VP8X - WEBP_CHUNK_ICCP, // ICCP - WEBP_CHUNK_LOOP, // LOOP - WEBP_CHUNK_FRAME, // FRM - WEBP_CHUNK_TILE, // TILE - WEBP_CHUNK_ALPHA, // ALPH - WEBP_CHUNK_IMAGE, // VP8/VP8L - WEBP_CHUNK_META, // META - 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); - -//------------------------------------------------------------------------------ -// Life of a Mux object - -// Internal, version-checked, entry point -WEBP_EXTERN(WebPMux*) WebPNewInternal(int); - -// Creates an empty mux object. -// Returns: -// A pointer to the newly created empty mux object. -static WEBP_INLINE WebPMux* WebPMuxNew(void) { - return WebPNewInternal(WEBP_MUX_ABI_VERSION); -} - -// Deletes the mux object. -// Parameters: -// mux - (in/out) object to be deleted -WEBP_EXTERN(void) WebPMuxDelete(WebPMux* mux); - -//------------------------------------------------------------------------------ -// Mux creation. - -// Internal, version-checked, entry point -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. -// Returns: -// A pointer to the mux object created from given data - on success. -// NULL - In case of invalid data or memory error. -static WEBP_INLINE WebPMux* WebPMuxCreate(const WebPData* bitstream, - int copy_data) { - return WebPMuxCreateInternal(bitstream, copy_data, WEBP_MUX_ABI_VERSION); -} - -//------------------------------------------------------------------------------ -// Single Image. - -// Sets the image in the mux object. Any existing images (including frame/tile) -// 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. -// 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); - -// 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(). -// Parameters: -// mux - (in) object from which the image is to be fetched -// bitstream - (out) the image data -// 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_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); - -// 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 -// 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) -// Returns: -// WEBP_MUX_INVALID_ARGUMENT - if either mux, bitstream, x_offset, -// y_offset, or duration 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_OK - on success. -WEBP_EXTERN(WebPMuxError) WebPMuxGetFrame( - const WebPMux* mux, uint32_t nth, WebPData* bitstream, - int* x_offset, int* y_offset, int* duration); - -// Deletes an animation 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_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. - -// 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 -// 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. -// 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) WebPMuxPushTile( - WebPMux* mux, const WebPData* bitstream, - int x_offset, int y_offset, int copy_data); - -// 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. -// 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 -// 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_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); - -//------------------------------------------------------------------------------ -// Misc Utilities. - -// Gets the feature flags from the mux object. -// 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_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. -// 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_OK - on success. -WEBP_EXTERN(WebPMuxError) WebPMuxNumChunks(const WebPMux* mux, - WebPChunkId id, int* num_elements); - -// Assembles all chunks in WebP RIFF format and returns in 'assembled_data'. -// This function also validates the mux object. -// 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(). -// 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_MEMORY_ERROR - on memory allocation error. -// 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. - -#define WEBP_DEMUX_ABI_VERSION 0x0100 // MAJOR(8b) + MINOR(8b) - -typedef struct WebPDemuxer WebPDemuxer; - -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; - -//------------------------------------------------------------------------------ -// Life of a Demux object - -// Internal, version-checked, entry point -WEBP_EXTERN(WebPDemuxer*) WebPDemuxInternal( - const WebPData*, int, WebPDemuxState*, 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); -} - -// 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); -} - -// Frees memory associated with 'dmux'. -WEBP_EXTERN(void) WebPDemuxDelete(WebPDemuxer* dmux); - -//------------------------------------------------------------------------------ -// Data/information extraction. - -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 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); - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_WEBP_MUX_H_ */ diff --git a/drivers/webpold/mux/demux.c b/drivers/webpold/mux/demux.c deleted file mode 100644 index 501d08f41d..0000000000 --- a/drivers/webpold/mux/demux.c +++ /dev/null @@ -1,902 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// WebP container demux. -// - -#include "../mux.h" - -#include <stdlib.h> -#include <string.h> - -#include "../decode.h" // WebPGetInfo -#include "../format_constants.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define MKFOURCC(a, b, c, d) ((uint32_t)(a) | (b) << 8 | (c) << 16 | (d) << 24) - -typedef struct { - size_t start_; // start location of the data - size_t end_; // end location - size_t riff_end_; // riff chunk end location, can be > end_. - size_t buf_size_; // size of the buffer - const uint8_t* buf_; -} MemBuffer; - -typedef struct { - size_t offset_; - size_t size_; -} ChunkData; - -typedef struct Frame { - int x_offset_, y_offset_; - int width_, height_; - int duration_; - int is_tile_; // this is an image fragment from a 'TILE'. - int frame_num_; // the referent frame number for use in assembling tiles. - int complete_; // img_components_ contains a full image. - ChunkData img_components_[2]; // 0=VP8{,L} 1=ALPH - struct Frame* next_; -} Frame; - -typedef struct Chunk { - ChunkData data_; - struct Chunk* next_; -} Chunk; - -struct WebPDemuxer { - MemBuffer mem_; - WebPDemuxState state_; - int is_ext_format_; - uint32_t feature_flags_; - int canvas_width_, canvas_height_; - int loop_count_; - int num_frames_; - Frame* frames_; - Chunk* chunks_; // non-image chunks -}; - -typedef enum { - PARSE_OK, - PARSE_NEED_MORE_DATA, - PARSE_ERROR -} ParseStatus; - -typedef struct ChunkParser { - uint8_t id[4]; - ParseStatus (*parse)(WebPDemuxer* const dmux); - int (*valid)(const WebPDemuxer* const dmux); -} ChunkParser; - -static ParseStatus ParseSingleImage(WebPDemuxer* const dmux); -static ParseStatus ParseVP8X(WebPDemuxer* const dmux); -static int IsValidSimpleFormat(const WebPDemuxer* const dmux); -static int IsValidExtendedFormat(const WebPDemuxer* const dmux); - -static const ChunkParser kMasterChunks[] = { - { { 'V', 'P', '8', ' ' }, ParseSingleImage, IsValidSimpleFormat }, - { { 'V', 'P', '8', 'L' }, ParseSingleImage, IsValidSimpleFormat }, - { { 'V', 'P', '8', 'X' }, ParseVP8X, IsValidExtendedFormat }, - { { '0', '0', '0', '0' }, NULL, NULL }, -}; - -// ----------------------------------------------------------------------------- -// MemBuffer - -static int RemapMemBuffer(MemBuffer* const mem, - const uint8_t* data, size_t size) { - if (size < mem->buf_size_) return 0; // can't remap to a shorter buffer! - - mem->buf_ = data; - mem->end_ = mem->buf_size_ = size; - return 1; -} - -static int InitMemBuffer(MemBuffer* const mem, - const uint8_t* data, size_t size) { - memset(mem, 0, sizeof(*mem)); - return RemapMemBuffer(mem, data, size); -} - -// Return the remaining data size available in 'mem'. -static WEBP_INLINE size_t MemDataSize(const MemBuffer* const mem) { - return (mem->end_ - mem->start_); -} - -// Return true if 'size' exceeds the end of the RIFF chunk. -static WEBP_INLINE int SizeIsInvalid(const MemBuffer* const mem, size_t size) { - return (size > mem->riff_end_ - mem->start_); -} - -static WEBP_INLINE void Skip(MemBuffer* const mem, size_t size) { - mem->start_ += size; -} - -static WEBP_INLINE void Rewind(MemBuffer* const mem, size_t size) { - mem->start_ -= size; -} - -static WEBP_INLINE const uint8_t* GetBuffer(MemBuffer* const mem) { - return mem->buf_ + mem->start_; -} - -static WEBP_INLINE uint8_t GetByte(MemBuffer* const mem) { - const uint8_t byte = mem->buf_[mem->start_]; - Skip(mem, 1); - return byte; -} - -// Read 16, 24 or 32 bits stored in little-endian order. -static WEBP_INLINE int ReadLE16s(const uint8_t* const data) { - return (int)(data[0] << 0) | (data[1] << 8); -} - -static WEBP_INLINE int ReadLE24s(const uint8_t* const data) { - return ReadLE16s(data) | (data[2] << 16); -} - -static WEBP_INLINE uint32_t ReadLE32(const uint8_t* const data) { - return (uint32_t)ReadLE24s(data) | (data[3] << 24); -} - -// In addition to reading, skip the read bytes. -static WEBP_INLINE int GetLE16s(MemBuffer* const mem) { - const uint8_t* const data = mem->buf_ + mem->start_; - const int val = ReadLE16s(data); - Skip(mem, 2); - return val; -} - -static WEBP_INLINE int GetLE24s(MemBuffer* const mem) { - const uint8_t* const data = mem->buf_ + mem->start_; - const int val = ReadLE24s(data); - Skip(mem, 3); - return val; -} - -static WEBP_INLINE uint32_t GetLE32(MemBuffer* const mem) { - const uint8_t* const data = mem->buf_ + mem->start_; - const uint32_t val = ReadLE32(data); - Skip(mem, 4); - return val; -} - -// ----------------------------------------------------------------------------- -// Secondary chunk parsing - -static void AddChunk(WebPDemuxer* const dmux, Chunk* const chunk) { - Chunk** c = &dmux->chunks_; - while (*c != NULL) c = &(*c)->next_; - *c = chunk; - chunk->next_ = NULL; -} - -// Add a frame to the end of the list, ensuring the last frame is complete. -// Returns true on success, false otherwise. -static int AddFrame(WebPDemuxer* const dmux, Frame* const frame) { - const Frame* last_frame = NULL; - Frame** f = &dmux->frames_; - while (*f != NULL) { - last_frame = *f; - f = &(*f)->next_; - } - if (last_frame != NULL && !last_frame->complete_) return 0; - *f = frame; - frame->next_ = NULL; - return 1; -} - -// Store image bearing chunks to 'frame'. -static ParseStatus StoreFrame(int frame_num, MemBuffer* const mem, - Frame* const frame) { - int alpha_chunks = 0; - int image_chunks = 0; - int done = (MemDataSize(mem) < CHUNK_HEADER_SIZE); - ParseStatus status = PARSE_OK; - - if (done) return PARSE_NEED_MORE_DATA; - - do { - const size_t chunk_start_offset = mem->start_; - const uint32_t fourcc = GetLE32(mem); - const uint32_t payload_size = GetLE32(mem); - const uint32_t payload_size_padded = payload_size + (payload_size & 1); - const size_t payload_available = (payload_size_padded > MemDataSize(mem)) - ? MemDataSize(mem) : payload_size_padded; - const size_t chunk_size = CHUNK_HEADER_SIZE + payload_available; - - if (payload_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; - if (SizeIsInvalid(mem, payload_size_padded)) return PARSE_ERROR; - if (payload_size_padded > MemDataSize(mem)) status = PARSE_NEED_MORE_DATA; - - switch (fourcc) { - case MKFOURCC('A', 'L', 'P', 'H'): - if (alpha_chunks == 0) { - ++alpha_chunks; - frame->img_components_[1].offset_ = chunk_start_offset; - frame->img_components_[1].size_ = chunk_size; - frame->frame_num_ = frame_num; - Skip(mem, payload_available); - } else { - goto Done; - } - break; - case MKFOURCC('V', 'P', '8', ' '): - case MKFOURCC('V', 'P', '8', 'L'): - if (image_chunks == 0) { - int width = 0, height = 0; - ++image_chunks; - frame->img_components_[0].offset_ = chunk_start_offset; - frame->img_components_[0].size_ = chunk_size; - // Extract the width and height from the bitstream, tolerating - // failures when the data is incomplete. - if (!WebPGetInfo(mem->buf_ + frame->img_components_[0].offset_, - frame->img_components_[0].size_, &width, &height) && - status != PARSE_NEED_MORE_DATA) { - return PARSE_ERROR; - } - - frame->width_ = width; - frame->height_ = height; - frame->frame_num_ = frame_num; - frame->complete_ = (status == PARSE_OK); - Skip(mem, payload_available); - } else { - goto Done; - } - break; - Done: - default: - // Restore fourcc/size when moving up one level in parsing. - Rewind(mem, CHUNK_HEADER_SIZE); - done = 1; - break; - } - - if (mem->start_ == mem->riff_end_) { - done = 1; - } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { - status = PARSE_NEED_MORE_DATA; - } - } while (!done && status == PARSE_OK); - - return status; -} - -// Creates a new Frame if 'actual_size' is within bounds and 'mem' contains -// enough data ('min_size') to parse the payload. -// Returns PARSE_OK on success with *frame pointing to the new Frame. -// Returns PARSE_NEED_MORE_DATA with insufficient data, PARSE_ERROR otherwise. -static ParseStatus NewFrame(const MemBuffer* const mem, - uint32_t min_size, uint32_t expected_size, - uint32_t actual_size, Frame** frame) { - if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; - if (actual_size < expected_size) return PARSE_ERROR; - if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; - - *frame = (Frame*)calloc(1, sizeof(**frame)); - return (*frame == NULL) ? PARSE_ERROR : PARSE_OK; -} - -// Parse a 'FRM ' chunk and any image bearing chunks that immediately follow. -// 'frame_chunk_size' is the previously validated, padded chunk size. -static ParseStatus ParseFrame( - WebPDemuxer* const dmux, uint32_t frame_chunk_size) { - const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG); - const uint32_t min_size = frame_chunk_size + CHUNK_HEADER_SIZE; - int added_frame = 0; - MemBuffer* const mem = &dmux->mem_; - Frame* frame; - ParseStatus status = - NewFrame(mem, min_size, FRAME_CHUNK_SIZE, frame_chunk_size, &frame); - if (status != PARSE_OK) return status; - - frame->x_offset_ = 2 * GetLE24s(mem); - frame->y_offset_ = 2 * GetLE24s(mem); - frame->width_ = 1 + GetLE24s(mem); - frame->height_ = 1 + GetLE24s(mem); - frame->duration_ = 1 + GetLE24s(mem); - Skip(mem, frame_chunk_size - FRAME_CHUNK_SIZE); // skip any trailing data. - if (frame->width_ * (uint64_t)frame->height_ >= MAX_IMAGE_AREA) { - return PARSE_ERROR; - } - - // Store a (potentially partial) frame only if the animation flag is set - // and there is some data in 'frame'. - status = StoreFrame(dmux->num_frames_ + 1, mem, frame); - if (status != PARSE_ERROR && has_frames && frame->frame_num_ > 0) { - added_frame = AddFrame(dmux, frame); - if (added_frame) { - ++dmux->num_frames_; - } else { - status = PARSE_ERROR; - } - } - - if (!added_frame) free(frame); - return status; -} - -// Parse a 'TILE' chunk and any image bearing chunks that immediately follow. -// 'tile_chunk_size' is the previously validated, padded chunk size. -static ParseStatus ParseTile(WebPDemuxer* const dmux, - uint32_t tile_chunk_size) { - const int has_tiles = !!(dmux->feature_flags_ & TILE_FLAG); - const uint32_t min_size = tile_chunk_size + CHUNK_HEADER_SIZE; - int added_tile = 0; - MemBuffer* const mem = &dmux->mem_; - Frame* frame; - ParseStatus status = - NewFrame(mem, min_size, TILE_CHUNK_SIZE, tile_chunk_size, &frame); - if (status != PARSE_OK) return status; - - frame->is_tile_ = 1; - frame->x_offset_ = 2 * GetLE24s(mem); - frame->y_offset_ = 2 * GetLE24s(mem); - Skip(mem, tile_chunk_size - TILE_CHUNK_SIZE); // skip any trailing data. - - // Store a (potentially partial) tile only if the tile flag is set - // and the tile contains some data. - status = StoreFrame(dmux->num_frames_, mem, frame); - if (status != PARSE_ERROR && has_tiles && frame->frame_num_ > 0) { - // Note num_frames_ is incremented only when all tiles have been consumed. - added_tile = AddFrame(dmux, frame); - if (!added_tile) status = PARSE_ERROR; - } - - if (!added_tile) free(frame); - return status; -} - -// General chunk storage starting with the header at 'start_offset' allowing -// the user to request the payload via a fourcc string. 'size' includes the -// header and the unpadded payload size. -// Returns true on success, false otherwise. -static int StoreChunk(WebPDemuxer* const dmux, - size_t start_offset, uint32_t size) { - Chunk* const chunk = (Chunk*)calloc(1, sizeof(*chunk)); - if (chunk == NULL) return 0; - - chunk->data_.offset_ = start_offset; - chunk->data_.size_ = size; - AddChunk(dmux, chunk); - return 1; -} - -// ----------------------------------------------------------------------------- -// Primary chunk parsing - -static int ReadHeader(MemBuffer* const mem) { - const size_t min_size = RIFF_HEADER_SIZE + CHUNK_HEADER_SIZE; - uint32_t riff_size; - - // Basic file level validation. - if (MemDataSize(mem) < min_size) return 0; - if (memcmp(GetBuffer(mem), "RIFF", CHUNK_SIZE_BYTES) || - memcmp(GetBuffer(mem) + CHUNK_HEADER_SIZE, "WEBP", CHUNK_SIZE_BYTES)) { - return 0; - } - - riff_size = ReadLE32(GetBuffer(mem) + TAG_SIZE); - if (riff_size < CHUNK_HEADER_SIZE) return 0; - if (riff_size > MAX_CHUNK_PAYLOAD) return 0; - - // There's no point in reading past the end of the RIFF chunk - mem->riff_end_ = riff_size + CHUNK_HEADER_SIZE; - if (mem->buf_size_ > mem->riff_end_) { - mem->buf_size_ = mem->end_ = mem->riff_end_; - } - - Skip(mem, RIFF_HEADER_SIZE); - return 1; -} - -static ParseStatus ParseSingleImage(WebPDemuxer* const dmux) { - const size_t min_size = CHUNK_HEADER_SIZE; - MemBuffer* const mem = &dmux->mem_; - Frame* frame; - ParseStatus status; - - if (dmux->frames_ != NULL) return PARSE_ERROR; - if (SizeIsInvalid(mem, min_size)) return PARSE_ERROR; - if (MemDataSize(mem) < min_size) return PARSE_NEED_MORE_DATA; - - frame = (Frame*)calloc(1, sizeof(*frame)); - if (frame == NULL) return PARSE_ERROR; - - status = StoreFrame(1, &dmux->mem_, frame); - if (status != PARSE_ERROR) { - const int has_alpha = !!(dmux->feature_flags_ & ALPHA_FLAG); - // Clear any alpha when the alpha flag is missing. - if (!has_alpha && frame->img_components_[1].size_ > 0) { - frame->img_components_[1].offset_ = 0; - frame->img_components_[1].size_ = 0; - } - - // Use the frame width/height as the canvas values for non-vp8x files. - if (!dmux->is_ext_format_ && frame->width_ > 0 && frame->height_ > 0) { - dmux->state_ = WEBP_DEMUX_PARSED_HEADER; - dmux->canvas_width_ = frame->width_; - dmux->canvas_height_ = frame->height_; - } - AddFrame(dmux, frame); - dmux->num_frames_ = 1; - } else { - free(frame); - } - - return status; -} - -static ParseStatus ParseVP8X(WebPDemuxer* const dmux) { - MemBuffer* const mem = &dmux->mem_; - int loop_chunks = 0; - uint32_t vp8x_size; - ParseStatus status = PARSE_OK; - - if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA; - - dmux->is_ext_format_ = 1; - Skip(mem, TAG_SIZE); // VP8X - vp8x_size = GetLE32(mem); - if (vp8x_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; - if (vp8x_size < VP8X_CHUNK_SIZE) return PARSE_ERROR; - vp8x_size += vp8x_size & 1; - if (SizeIsInvalid(mem, vp8x_size)) return PARSE_ERROR; - if (MemDataSize(mem) < vp8x_size) return PARSE_NEED_MORE_DATA; - - dmux->feature_flags_ = GetByte(mem); - Skip(mem, 3); // Reserved. - dmux->canvas_width_ = 1 + GetLE24s(mem); - dmux->canvas_height_ = 1 + GetLE24s(mem); - if (dmux->canvas_width_ * (uint64_t)dmux->canvas_height_ >= MAX_IMAGE_AREA) { - return PARSE_ERROR; // image final dimension is too large - } - Skip(mem, vp8x_size - VP8X_CHUNK_SIZE); // skip any trailing data. - dmux->state_ = WEBP_DEMUX_PARSED_HEADER; - - if (SizeIsInvalid(mem, CHUNK_HEADER_SIZE)) return PARSE_ERROR; - if (MemDataSize(mem) < CHUNK_HEADER_SIZE) return PARSE_NEED_MORE_DATA; - - do { - int store_chunk = 1; - const size_t chunk_start_offset = mem->start_; - const uint32_t fourcc = GetLE32(mem); - const uint32_t chunk_size = GetLE32(mem); - const uint32_t chunk_size_padded = chunk_size + (chunk_size & 1); - - if (chunk_size > MAX_CHUNK_PAYLOAD) return PARSE_ERROR; - if (SizeIsInvalid(mem, chunk_size_padded)) return PARSE_ERROR; - - switch (fourcc) { - case MKFOURCC('V', 'P', '8', 'X'): { - return PARSE_ERROR; - } - case MKFOURCC('A', 'L', 'P', 'H'): - case MKFOURCC('V', 'P', '8', ' '): - case MKFOURCC('V', 'P', '8', 'L'): { - Rewind(mem, CHUNK_HEADER_SIZE); - status = ParseSingleImage(dmux); - break; - } - case MKFOURCC('L', 'O', 'O', 'P'): { - if (chunk_size_padded < LOOP_CHUNK_SIZE) return PARSE_ERROR; - - if (MemDataSize(mem) < chunk_size_padded) { - status = PARSE_NEED_MORE_DATA; - } else if (loop_chunks == 0) { - ++loop_chunks; - dmux->loop_count_ = GetLE16s(mem); - Skip(mem, chunk_size_padded - LOOP_CHUNK_SIZE); - } else { - store_chunk = 0; - goto Skip; - } - break; - } - case MKFOURCC('F', 'R', 'M', ' '): { - status = ParseFrame(dmux, chunk_size_padded); - break; - } - case MKFOURCC('T', 'I', 'L', 'E'): { - if (dmux->num_frames_ == 0) dmux->num_frames_ = 1; - status = ParseTile(dmux, chunk_size_padded); - break; - } - case MKFOURCC('I', 'C', 'C', 'P'): { - store_chunk = !!(dmux->feature_flags_ & ICCP_FLAG); - goto Skip; - } - case MKFOURCC('M', 'E', 'T', 'A'): { - store_chunk = !!(dmux->feature_flags_ & META_FLAG); - goto Skip; - } - Skip: - default: { - if (chunk_size_padded <= MemDataSize(mem)) { - if (store_chunk) { - // Store only the chunk header and unpadded size as only the payload - // will be returned to the user. - if (!StoreChunk(dmux, chunk_start_offset, - CHUNK_HEADER_SIZE + chunk_size)) { - return PARSE_ERROR; - } - } - Skip(mem, chunk_size_padded); - } else { - status = PARSE_NEED_MORE_DATA; - } - } - } - - if (mem->start_ == mem->riff_end_) { - break; - } else if (MemDataSize(mem) < CHUNK_HEADER_SIZE) { - status = PARSE_NEED_MORE_DATA; - } - } while (status == PARSE_OK); - - return status; -} - -// ----------------------------------------------------------------------------- -// Format validation - -static int IsValidSimpleFormat(const WebPDemuxer* const dmux) { - const Frame* const frame = dmux->frames_; - if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; - - if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0; - if (dmux->state_ == WEBP_DEMUX_DONE && frame == NULL) return 0; - - if (frame->width_ <= 0 || frame->height_ <= 0) return 0; - return 1; -} - -static int IsValidExtendedFormat(const WebPDemuxer* const dmux) { - const int has_tiles = !!(dmux->feature_flags_ & TILE_FLAG); - const int has_frames = !!(dmux->feature_flags_ & ANIMATION_FLAG); - const Frame* f; - - if (dmux->state_ == WEBP_DEMUX_PARSING_HEADER) return 1; - - if (dmux->canvas_width_ <= 0 || dmux->canvas_height_ <= 0) return 0; - if (dmux->loop_count_ < 0) return 0; - if (dmux->state_ == WEBP_DEMUX_DONE && dmux->frames_ == NULL) return 0; - - for (f = dmux->frames_; f != NULL; f = f->next_) { - const int cur_frame_set = f->frame_num_; - int frame_count = 0, tile_count = 0; - - // Check frame properties and if the image is composed of tiles that each - // fragment came from a 'TILE'. - for (; f != NULL && f->frame_num_ == cur_frame_set; f = f->next_) { - const ChunkData* const image = f->img_components_; - const ChunkData* const alpha = f->img_components_ + 1; - - if (!has_tiles && f->is_tile_) return 0; - if (!has_frames && f->frame_num_ > 1) return 0; - if (f->x_offset_ < 0 || f->y_offset_ < 0) return 0; - if (f->complete_) { - if (alpha->size_ == 0 && image->size_ == 0) return 0; - // Ensure alpha precedes image bitstream. - if (alpha->size_ > 0 && alpha->offset_ > image->offset_) { - return 0; - } - - if (f->width_ <= 0 || f->height_ <= 0) return 0; - } else { - // Ensure alpha precedes image bitstream. - if (alpha->size_ > 0 && image->size_ > 0 && - alpha->offset_ > image->offset_) { - return 0; - } - // There shouldn't be any frames after an incomplete one. - if (f->next_ != NULL) return 0; - } - - tile_count += f->is_tile_; - ++frame_count; - } - if (!has_tiles && frame_count > 1) return 0; - if (tile_count > 0 && frame_count != tile_count) return 0; - if (f == NULL) break; - } - return 1; -} - -// ----------------------------------------------------------------------------- -// WebPDemuxer object - -static void InitDemux(WebPDemuxer* const dmux, const MemBuffer* const mem) { - dmux->state_ = WEBP_DEMUX_PARSING_HEADER; - dmux->loop_count_ = 1; - dmux->canvas_width_ = -1; - dmux->canvas_height_ = -1; - dmux->mem_ = *mem; -} - -WebPDemuxer* WebPDemuxInternal(const WebPData* data, int allow_partial, - WebPDemuxState* state, int version) { - const ChunkParser* parser; - int partial; - ParseStatus status = PARSE_ERROR; - MemBuffer mem; - WebPDemuxer* dmux; - - if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_DEMUX_ABI_VERSION)) return NULL; - if (data == NULL || data->bytes_ == NULL || data->size_ == 0) return NULL; - - if (!InitMemBuffer(&mem, data->bytes_, data->size_)) return NULL; - if (!ReadHeader(&mem)) return NULL; - - partial = (mem.buf_size_ < mem.riff_end_); - if (!allow_partial && partial) return NULL; - - dmux = (WebPDemuxer*)calloc(1, sizeof(*dmux)); - if (dmux == NULL) return NULL; - InitDemux(dmux, &mem); - - for (parser = kMasterChunks; parser->parse != NULL; ++parser) { - if (!memcmp(parser->id, GetBuffer(&dmux->mem_), TAG_SIZE)) { - status = parser->parse(dmux); - if (status == PARSE_OK) dmux->state_ = WEBP_DEMUX_DONE; - if (status != PARSE_ERROR && !parser->valid(dmux)) status = PARSE_ERROR; - break; - } - } - if (state) *state = dmux->state_; - - if (status == PARSE_ERROR) { - WebPDemuxDelete(dmux); - return NULL; - } - return dmux; -} - -void WebPDemuxDelete(WebPDemuxer* dmux) { - Chunk* c; - Frame* f; - if (dmux == NULL) return; - - for (f = dmux->frames_; f != NULL;) { - Frame* const cur_frame = f; - f = f->next_; - free(cur_frame); - } - for (c = dmux->chunks_; c != NULL;) { - Chunk* const cur_chunk = c; - c = c->next_; - free(cur_chunk); - } - free(dmux); -} - -// ----------------------------------------------------------------------------- - -uint32_t WebPDemuxGetI(const WebPDemuxer* dmux, WebPFormatFeature feature) { - if (dmux == NULL) return 0; - - switch (feature) { - case WEBP_FF_FORMAT_FLAGS: return dmux->feature_flags_; - case WEBP_FF_CANVAS_WIDTH: return (uint32_t)dmux->canvas_width_; - case WEBP_FF_CANVAS_HEIGHT: return (uint32_t)dmux->canvas_height_; - case WEBP_FF_LOOP_COUNT: return (uint32_t)dmux->loop_count_; - } - return 0; -} - -// ----------------------------------------------------------------------------- -// Frame iteration - -// Find the first 'frame_num' frame. There may be multiple in a tiled frame. -static const Frame* GetFrame(const WebPDemuxer* const dmux, int frame_num) { - const Frame* f; - for (f = dmux->frames_; f != NULL; f = f->next_) { - if (frame_num == f->frame_num_) break; - } - return f; -} - -// Returns tile 'tile_num' and the total count. -static const Frame* GetTile( - const Frame* const frame_set, int tile_num, int* const count) { - const int this_frame = frame_set->frame_num_; - const Frame* f = frame_set; - const Frame* tile = NULL; - int total; - - for (total = 0; f != NULL && f->frame_num_ == this_frame; f = f->next_) { - if (++total == tile_num) tile = f; - } - *count = total; - return tile; -} - -static const uint8_t* GetFramePayload(const uint8_t* const mem_buf, - const Frame* const frame, - size_t* const data_size) { - *data_size = 0; - if (frame != NULL) { - const ChunkData* const image = frame->img_components_; - const ChunkData* const alpha = frame->img_components_ + 1; - size_t start_offset = image->offset_; - *data_size = image->size_; - - // if alpha exists it precedes image, update the size allowing for - // intervening chunks. - if (alpha->size_ > 0) { - const size_t inter_size = (image->offset_ > 0) - ? image->offset_ - (alpha->offset_ + alpha->size_) - : 0; - start_offset = alpha->offset_; - *data_size += alpha->size_ + inter_size; - } - return mem_buf + start_offset; - } - return NULL; -} - -// Create a whole 'frame' from VP8 (+ alpha) or lossless. -static int SynthesizeFrame(const WebPDemuxer* const dmux, - const Frame* const first_frame, - int tile_num, WebPIterator* const iter) { - const uint8_t* const mem_buf = dmux->mem_.buf_; - int num_tiles; - size_t payload_size = 0; - const Frame* const tile = GetTile(first_frame, tile_num, &num_tiles); - const uint8_t* const payload = GetFramePayload(mem_buf, tile, &payload_size); - if (payload == NULL) return 0; - - iter->frame_num_ = first_frame->frame_num_; - iter->num_frames_ = dmux->num_frames_; - iter->tile_num_ = tile_num; - iter->num_tiles_ = num_tiles; - iter->x_offset_ = tile->x_offset_; - iter->y_offset_ = tile->y_offset_; - iter->width_ = tile->width_; - iter->height_ = tile->height_; - iter->duration_ = tile->duration_; - iter->complete_ = tile->complete_; - iter->tile_.bytes_ = payload; - iter->tile_.size_ = payload_size; - // TODO(jzern): adjust offsets for 'TILE's embedded in 'FRM 's - return 1; -} - -static int SetFrame(int frame_num, WebPIterator* const iter) { - const Frame* frame; - const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; - if (dmux == NULL || frame_num < 0) return 0; - if (frame_num > dmux->num_frames_) return 0; - if (frame_num == 0) frame_num = dmux->num_frames_; - - frame = GetFrame(dmux, frame_num); - return SynthesizeFrame(dmux, frame, 1, iter); -} - -int WebPDemuxGetFrame(const WebPDemuxer* dmux, int frame, WebPIterator* iter) { - if (iter == NULL) return 0; - - memset(iter, 0, sizeof(*iter)); - iter->private_ = (void*)dmux; - return SetFrame(frame, iter); -} - -int WebPDemuxNextFrame(WebPIterator* iter) { - if (iter == NULL) return 0; - return SetFrame(iter->frame_num_ + 1, iter); -} - -int WebPDemuxPrevFrame(WebPIterator* iter) { - if (iter == NULL) return 0; - if (iter->frame_num_ <= 1) return 0; - return SetFrame(iter->frame_num_ - 1, iter); -} - -int WebPDemuxSelectTile(WebPIterator* iter, int tile) { - if (iter != NULL && iter->private_ != NULL && tile > 0) { - const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; - const Frame* const frame = GetFrame(dmux, iter->frame_num_); - if (frame == NULL) return 0; - - return SynthesizeFrame(dmux, frame, tile, iter); - } - return 0; -} - -void WebPDemuxReleaseIterator(WebPIterator* iter) { - (void)iter; -} - -// ----------------------------------------------------------------------------- -// Chunk iteration - -static int ChunkCount(const WebPDemuxer* const dmux, const char fourcc[4]) { - const uint8_t* const mem_buf = dmux->mem_.buf_; - const Chunk* c; - int count = 0; - for (c = dmux->chunks_; c != NULL; c = c->next_) { - const uint8_t* const header = mem_buf + c->data_.offset_; - if (!memcmp(header, fourcc, TAG_SIZE)) ++count; - } - return count; -} - -static const Chunk* GetChunk(const WebPDemuxer* const dmux, - const char fourcc[4], int chunk_num) { - const uint8_t* const mem_buf = dmux->mem_.buf_; - const Chunk* c; - int count = 0; - for (c = dmux->chunks_; c != NULL; c = c->next_) { - const uint8_t* const header = mem_buf + c->data_.offset_; - if (!memcmp(header, fourcc, TAG_SIZE)) ++count; - if (count == chunk_num) break; - } - return c; -} - -static int SetChunk(const char fourcc[4], int chunk_num, - WebPChunkIterator* const iter) { - const WebPDemuxer* const dmux = (WebPDemuxer*)iter->private_; - int count; - - if (dmux == NULL || fourcc == NULL || chunk_num < 0) return 0; - count = ChunkCount(dmux, fourcc); - if (count == 0) return 0; - if (chunk_num == 0) chunk_num = count; - - if (chunk_num <= count) { - const uint8_t* const mem_buf = dmux->mem_.buf_; - const Chunk* const chunk = GetChunk(dmux, fourcc, chunk_num); - iter->chunk_.bytes_ = mem_buf + chunk->data_.offset_ + CHUNK_HEADER_SIZE; - iter->chunk_.size_ = chunk->data_.size_ - CHUNK_HEADER_SIZE; - iter->num_chunks_ = count; - iter->chunk_num_ = chunk_num; - return 1; - } - return 0; -} - -int WebPDemuxGetChunk(const WebPDemuxer* dmux, - const char fourcc[4], int chunk_num, - WebPChunkIterator* iter) { - if (iter == NULL) return 0; - - memset(iter, 0, sizeof(*iter)); - iter->private_ = (void*)dmux; - return SetChunk(fourcc, chunk_num, iter); -} - -int WebPDemuxNextChunk(WebPChunkIterator* iter) { - if (iter != NULL) { - const char* const fourcc = - (const char*)iter->chunk_.bytes_ - CHUNK_HEADER_SIZE; - return SetChunk(fourcc, iter->chunk_num_ + 1, iter); - } - return 0; -} - -int WebPDemuxPrevChunk(WebPChunkIterator* iter) { - if (iter != NULL && iter->chunk_num_ > 1) { - const char* const fourcc = - (const char*)iter->chunk_.bytes_ - CHUNK_HEADER_SIZE; - return SetChunk(fourcc, iter->chunk_num_ - 1, iter); - } - return 0; -} - -void WebPDemuxReleaseChunkIterator(WebPChunkIterator* iter) { - (void)iter; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/mux/muxedit.c b/drivers/webpold/mux/muxedit.c deleted file mode 100644 index 08629d4ae2..0000000000 --- a/drivers/webpold/mux/muxedit.c +++ /dev/null @@ -1,712 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Set and delete APIs for mux. -// -// Authors: Urvang (urvang@google.com) -// Vikas (vikasa@google.com) - -#include <assert.h> -#include "./muxi.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Life of a mux object. - -static void MuxInit(WebPMux* const mux) { - if (mux == NULL) return; - memset(mux, 0, sizeof(*mux)); -} - -WebPMux* WebPNewInternal(int version) { - if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) { - return NULL; - } else { - WebPMux* const mux = (WebPMux*)malloc(sizeof(WebPMux)); - // If mux is NULL MuxInit is a noop. - MuxInit(mux); - return mux; - } -} - -static void DeleteAllChunks(WebPChunk** const chunk_list) { - while (*chunk_list) { - *chunk_list = ChunkDelete(*chunk_list); - } -} - -static void MuxRelease(WebPMux* const mux) { - if (mux == NULL) return; - MuxImageDeleteAll(&mux->images_); - DeleteAllChunks(&mux->vp8x_); - DeleteAllChunks(&mux->iccp_); - DeleteAllChunks(&mux->loop_); - DeleteAllChunks(&mux->meta_); - DeleteAllChunks(&mux->unknown_); -} - -void WebPMuxDelete(WebPMux* mux) { - // If mux is NULL MuxRelease is a noop. - MuxRelease(mux); - free(mux); -} - -//------------------------------------------------------------------------------ -// Helper method(s). - -// Handy MACRO, makes MuxSet() very symmetric to MuxGet(). -#define SWITCH_ID_LIST(INDEX, LIST) \ - if (idx == (INDEX)) { \ - err = ChunkAssignData(&chunk, data, copy_data, kChunks[(INDEX)].tag); \ - if (err == WEBP_MUX_OK) { \ - err = ChunkSetNth(&chunk, (LIST), nth); \ - } \ - return err; \ - } - -static WebPMuxError MuxSet(WebPMux* const mux, CHUNK_INDEX idx, uint32_t nth, - const WebPData* const data, int copy_data) { - WebPChunk chunk; - WebPMuxError err = WEBP_MUX_NOT_FOUND; - assert(mux != NULL); - assert(!IsWPI(kChunks[idx].id)); - - ChunkInit(&chunk); - 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_); - if (idx == IDX_UNKNOWN && data->size_ > TAG_SIZE) { - // For raw-data unknown chunk, the first four bytes should be the tag to be - // used for the chunk. - const WebPData tmp = { data->bytes_ + TAG_SIZE, data->size_ - TAG_SIZE }; - err = ChunkAssignData(&chunk, &tmp, copy_data, GetLE32(data->bytes_ + 0)); - if (err == WEBP_MUX_OK) - err = ChunkSetNth(&chunk, &mux->unknown_, nth); - } - return err; -} -#undef SWITCH_ID_LIST - -static WebPMuxError MuxAddChunk(WebPMux* const mux, uint32_t nth, uint32_t tag, - const uint8_t* data, size_t size, - int copy_data) { - const CHUNK_INDEX idx = ChunkGetIndexFromTag(tag); - const WebPData chunk_data = { data, size }; - assert(mux != NULL); - assert(size <= MAX_CHUNK_PAYLOAD); - assert(idx != IDX_NIL); - return MuxSet(mux, idx, nth, &chunk_data, copy_data); -} - -// Create data for frame/tile given image data, offsets and duration. -static WebPMuxError CreateFrameTileData(const WebPData* const image, - int x_offset, int y_offset, - int duration, int is_lossless, - int is_frame, - WebPData* const frame_tile) { - int width; - int height; - uint8_t* frame_tile_bytes; - const size_t frame_tile_size = kChunks[is_frame ? IDX_FRAME : IDX_TILE].size; - - const int ok = is_lossless ? - VP8LGetInfo(image->bytes_, image->size_, &width, &height, NULL) : - VP8GetInfo(image->bytes_, image->size_, image->size_, &width, &height); - if (!ok) return WEBP_MUX_INVALID_ARGUMENT; - - assert(width > 0 && height > 0 && duration > 0); - // Note: assertion on upper bounds is done in PutLE24(). - - frame_tile_bytes = (uint8_t*)malloc(frame_tile_size); - if (frame_tile_bytes == NULL) return WEBP_MUX_MEMORY_ERROR; - - PutLE24(frame_tile_bytes + 0, x_offset / 2); - PutLE24(frame_tile_bytes + 3, y_offset / 2); - - if (is_frame) { - PutLE24(frame_tile_bytes + 6, width - 1); - PutLE24(frame_tile_bytes + 9, height - 1); - PutLE24(frame_tile_bytes + 12, duration - 1); - } - - frame_tile->bytes_ = frame_tile_bytes; - frame_tile->size_ = frame_tile_size; - return WEBP_MUX_OK; -} - -// Outputs image data given a bitstream. The bitstream can either be a -// single-image WebP file or raw VP8/VP8L data. -// Also outputs 'is_lossless' to be true if the given bitstream is lossless. -static WebPMuxError GetImageData(const WebPData* const bitstream, - WebPData* const image, WebPData* const alpha, - int* const is_lossless) { - WebPDataInit(alpha); // Default: no alpha. - if (bitstream->size_ < TAG_SIZE || - memcmp(bitstream->bytes_, "RIFF", TAG_SIZE)) { - // It is NOT webp file data. Return input data as is. - *image = *bitstream; - } else { - // It is webp file data. Extract image data from it. - const WebPMuxImage* wpi; - WebPMux* const mux = WebPMuxCreate(bitstream, 0); - if (mux == NULL) return WEBP_MUX_BAD_DATA; - wpi = mux->images_; - assert(wpi != NULL && wpi->img_ != NULL); - *image = wpi->img_->data_; - if (wpi->alpha_ != NULL) { - *alpha = wpi->alpha_->data_; - } - WebPMuxDelete(mux); - } - *is_lossless = VP8LCheckSignature(image->bytes_, image->size_); - return WEBP_MUX_OK; -} - -static WebPMuxError DeleteChunks(WebPChunk** chunk_list, uint32_t tag) { - WebPMuxError err = WEBP_MUX_NOT_FOUND; - assert(chunk_list); - while (*chunk_list) { - WebPChunk* const chunk = *chunk_list; - if (chunk->tag_ == tag) { - *chunk_list = ChunkDelete(chunk); - err = WEBP_MUX_OK; - } else { - chunk_list = &chunk->next_; - } - } - return err; -} - -static WebPMuxError MuxDeleteAllNamedData(WebPMux* const mux, CHUNK_INDEX idx) { - const WebPChunkId id = kChunks[idx].id; - WebPChunk** chunk_list; - - if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - if (IsWPI(id)) return WEBP_MUX_INVALID_ARGUMENT; - - chunk_list = MuxGetChunkListFromId(mux, id); - if (chunk_list == NULL) return WEBP_MUX_INVALID_ARGUMENT; - - return DeleteChunks(chunk_list, kChunks[idx].tag); -} - -static WebPMuxError DeleteLoopCount(WebPMux* const mux) { - return MuxDeleteAllNamedData(mux, IDX_LOOP); -} - -//------------------------------------------------------------------------------ -// Set API(s). - -WebPMuxError WebPMuxSetImage(WebPMux* mux, - const WebPData* bitstream, int copy_data) { - WebPMuxError err; - WebPChunk chunk; - WebPMuxImage wpi; - WebPData image; - WebPData alpha; - int is_lossless; - int image_tag; - - if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL || - bitstream->size_ > MAX_CHUNK_PAYLOAD) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - // If given data is for a whole webp file, - // extract only the VP8/VP8L data from it. - err = GetImageData(bitstream, &image, &alpha, &is_lossless); - if (err != WEBP_MUX_OK) return err; - image_tag = is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag; - - // Delete the existing images. - MuxImageDeleteAll(&mux->images_); - - MuxImageInit(&wpi); - - if (alpha.bytes_ != NULL) { // Add alpha chunk. - ChunkInit(&chunk); - err = ChunkAssignData(&chunk, &alpha, copy_data, kChunks[IDX_ALPHA].tag); - if (err != WEBP_MUX_OK) goto Err; - err = ChunkSetNth(&chunk, &wpi.alpha_, 1); - if (err != WEBP_MUX_OK) goto Err; - } - - // Add image chunk. - ChunkInit(&chunk); - err = ChunkAssignData(&chunk, &image, copy_data, image_tag); - if (err != WEBP_MUX_OK) goto Err; - err = ChunkSetNth(&chunk, &wpi.img_, 1); - if (err != WEBP_MUX_OK) goto Err; - - // Add this image to mux. - err = MuxImagePush(&wpi, &mux->images_); - if (err != WEBP_MUX_OK) goto Err; - - // All OK. - return WEBP_MUX_OK; - - Err: - // Something bad happened. - ChunkRelease(&chunk); - MuxImageRelease(&wpi); - return err; -} - -WebPMuxError WebPMuxSetMetadata(WebPMux* mux, const WebPData* metadata, - int copy_data) { - WebPMuxError err; - - if (mux == NULL || metadata == NULL || metadata->bytes_ == NULL || - metadata->size_ > MAX_CHUNK_PAYLOAD) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - // Delete the existing metadata chunk(s). - err = WebPMuxDeleteMetadata(mux); - if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; - - // Add the given metadata chunk. - return MuxSet(mux, IDX_META, 1, metadata, copy_data); -} - -WebPMuxError WebPMuxSetColorProfile(WebPMux* mux, const WebPData* color_profile, - int copy_data) { - WebPMuxError err; - - if (mux == NULL || color_profile == NULL || color_profile->bytes_ == NULL || - color_profile->size_ > MAX_CHUNK_PAYLOAD) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - // Delete the existing ICCP chunk(s). - err = WebPMuxDeleteColorProfile(mux); - if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; - - // Add the given ICCP chunk. - return MuxSet(mux, IDX_ICCP, 1, color_profile, copy_data); -} - -WebPMuxError WebPMuxSetLoopCount(WebPMux* mux, int loop_count) { - WebPMuxError err; - uint8_t* data = NULL; - - if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - if (loop_count >= MAX_LOOP_COUNT) return WEBP_MUX_INVALID_ARGUMENT; - - // Delete the existing LOOP chunk(s). - err = DeleteLoopCount(mux); - if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; - - // Add the given loop count. - data = (uint8_t*)malloc(kChunks[IDX_LOOP].size); - if (data == NULL) return WEBP_MUX_MEMORY_ERROR; - - PutLE16(data, loop_count); - err = MuxAddChunk(mux, 1, kChunks[IDX_LOOP].tag, data, - kChunks[IDX_LOOP].size, 1); - free(data); - return err; -} - -static WebPMuxError MuxPushFrameTileInternal( - WebPMux* const mux, const WebPData* const bitstream, int x_offset, - int y_offset, int duration, int copy_data, uint32_t tag) { - WebPChunk chunk; - WebPData image; - WebPData alpha; - WebPMuxImage wpi; - WebPMuxError err; - WebPData frame_tile; - const int is_frame = (tag == kChunks[IDX_FRAME].tag) ? 1 : 0; - int is_lossless; - int image_tag; - - // Sanity checks. - if (mux == NULL || bitstream == NULL || bitstream->bytes_ == NULL || - bitstream->size_ > MAX_CHUNK_PAYLOAD) { - return WEBP_MUX_INVALID_ARGUMENT; - } - if (x_offset < 0 || x_offset >= MAX_POSITION_OFFSET || - y_offset < 0 || y_offset >= MAX_POSITION_OFFSET || - duration <= 0 || duration > MAX_DURATION) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - // Snap offsets to even positions. - x_offset &= ~1; - y_offset &= ~1; - - // If given data is for a whole webp file, - // extract only the VP8/VP8L data from it. - err = GetImageData(bitstream, &image, &alpha, &is_lossless); - if (err != WEBP_MUX_OK) return err; - image_tag = is_lossless ? kChunks[IDX_VP8L].tag : kChunks[IDX_VP8].tag; - - WebPDataInit(&frame_tile); - ChunkInit(&chunk); - MuxImageInit(&wpi); - - if (alpha.bytes_ != NULL) { - // Add alpha chunk. - err = ChunkAssignData(&chunk, &alpha, copy_data, kChunks[IDX_ALPHA].tag); - if (err != WEBP_MUX_OK) goto Err; - err = ChunkSetNth(&chunk, &wpi.alpha_, 1); - if (err != WEBP_MUX_OK) goto Err; - ChunkInit(&chunk); // chunk owned by wpi.alpha_ now. - } - - // Add image chunk. - err = ChunkAssignData(&chunk, &image, copy_data, image_tag); - if (err != WEBP_MUX_OK) goto Err; - err = ChunkSetNth(&chunk, &wpi.img_, 1); - if (err != WEBP_MUX_OK) goto Err; - ChunkInit(&chunk); // chunk owned by wpi.img_ now. - - // Create frame/tile data. - err = CreateFrameTileData(&image, x_offset, y_offset, duration, is_lossless, - is_frame, &frame_tile); - if (err != WEBP_MUX_OK) goto Err; - - // Add frame/tile chunk (with copy_data = 1). - err = ChunkAssignData(&chunk, &frame_tile, 1, tag); - if (err != WEBP_MUX_OK) goto Err; - WebPDataClear(&frame_tile); - err = ChunkSetNth(&chunk, &wpi.header_, 1); - if (err != WEBP_MUX_OK) goto Err; - ChunkInit(&chunk); // chunk owned by wpi.header_ now. - - // Add this WebPMuxImage to mux. - err = MuxImagePush(&wpi, &mux->images_); - if (err != WEBP_MUX_OK) goto Err; - - // All is well. - return WEBP_MUX_OK; - - Err: // Something bad happened. - WebPDataClear(&frame_tile); - ChunkRelease(&chunk); - MuxImageRelease(&wpi); - return err; -} - -WebPMuxError WebPMuxPushFrame(WebPMux* mux, const WebPData* bitstream, - int x_offset, int y_offset, - int duration, int copy_data) { - return MuxPushFrameTileInternal(mux, bitstream, x_offset, y_offset, - duration, copy_data, kChunks[IDX_FRAME].tag); -} - -WebPMuxError WebPMuxPushTile(WebPMux* mux, const WebPData* bitstream, - int x_offset, int y_offset, - int copy_data) { - return MuxPushFrameTileInternal(mux, bitstream, x_offset, y_offset, - 1 /* unused duration */, copy_data, - kChunks[IDX_TILE].tag); -} - -//------------------------------------------------------------------------------ -// Delete API(s). - -WebPMuxError WebPMuxDeleteImage(WebPMux* mux) { - WebPMuxError err; - - if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - - err = MuxValidateForImage(mux); - if (err != WEBP_MUX_OK) return err; - - // All well, delete image. - MuxImageDeleteAll(&mux->images_); - return WEBP_MUX_OK; -} - -WebPMuxError WebPMuxDeleteMetadata(WebPMux* mux) { - return MuxDeleteAllNamedData(mux, IDX_META); -} - -WebPMuxError WebPMuxDeleteColorProfile(WebPMux* mux) { - return MuxDeleteAllNamedData(mux, IDX_ICCP); -} - -static WebPMuxError DeleteFrameTileInternal(WebPMux* const mux, uint32_t nth, - CHUNK_INDEX idx) { - const WebPChunkId id = kChunks[idx].id; - if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - - assert(idx == IDX_FRAME || idx == IDX_TILE); - return MuxImageDeleteNth(&mux->images_, nth, id); -} - -WebPMuxError WebPMuxDeleteFrame(WebPMux* mux, uint32_t nth) { - return DeleteFrameTileInternal(mux, nth, IDX_FRAME); -} - -WebPMuxError WebPMuxDeleteTile(WebPMux* mux, uint32_t nth) { - return DeleteFrameTileInternal(mux, nth, IDX_TILE); -} - -//------------------------------------------------------------------------------ -// Assembly of the WebP RIFF file. - -static WebPMuxError GetFrameTileInfo(const WebPChunk* const frame_tile_chunk, - int* const x_offset, int* const y_offset, - int* const duration) { - const uint32_t tag = frame_tile_chunk->tag_; - const int is_frame = (tag == kChunks[IDX_FRAME].tag); - const WebPData* const data = &frame_tile_chunk->data_; - const size_t expected_data_size = - is_frame ? FRAME_CHUNK_SIZE : TILE_CHUNK_SIZE; - assert(frame_tile_chunk != NULL); - assert(tag == kChunks[IDX_FRAME].tag || tag == kChunks[IDX_TILE].tag); - if (data->size_ != expected_data_size) return WEBP_MUX_INVALID_ARGUMENT; - - *x_offset = 2 * GetLE24(data->bytes_ + 0); - *y_offset = 2 * GetLE24(data->bytes_ + 3); - if (is_frame) *duration = 1 + GetLE24(data->bytes_ + 12); - return WEBP_MUX_OK; -} - -WebPMuxError MuxGetImageWidthHeight(const WebPChunk* const image_chunk, - int* const width, int* const height) { - const uint32_t tag = image_chunk->tag_; - const WebPData* const data = &image_chunk->data_; - int w, h; - int ok; - assert(image_chunk != NULL); - assert(tag == kChunks[IDX_VP8].tag || tag == kChunks[IDX_VP8L].tag); - ok = (tag == kChunks[IDX_VP8].tag) ? - VP8GetInfo(data->bytes_, data->size_, data->size_, &w, &h) : - VP8LGetInfo(data->bytes_, data->size_, &w, &h, NULL); - if (ok) { - *width = w; - *height = h; - return WEBP_MUX_OK; - } else { - return WEBP_MUX_BAD_DATA; - } -} - -static WebPMuxError GetImageInfo(const WebPMuxImage* const wpi, - int* const x_offset, int* const y_offset, - int* const duration, - int* const width, int* const height) { - const WebPChunk* const image_chunk = wpi->img_; - const WebPChunk* const frame_tile_chunk = wpi->header_; - - // Get offsets and duration from FRM/TILE chunk. - const WebPMuxError err = - GetFrameTileInfo(frame_tile_chunk, x_offset, y_offset, duration); - if (err != WEBP_MUX_OK) return err; - - // Get width and height from VP8/VP8L chunk. - return MuxGetImageWidthHeight(image_chunk, width, height); -} - -static WebPMuxError GetImageCanvasWidthHeight( - const WebPMux* const mux, uint32_t flags, - int* const width, int* const height) { - WebPMuxImage* wpi = NULL; - assert(mux != NULL); - assert(width != NULL && height != NULL); - - wpi = mux->images_; - assert(wpi != NULL); - assert(wpi->img_ != NULL); - - if (wpi->next_) { - int max_x = 0; - int max_y = 0; - int64_t image_area = 0; - // Aggregate the bounding box for animation frames & tiled images. - for (; wpi != NULL; wpi = wpi->next_) { - int x_offset, y_offset, duration, w, h; - const WebPMuxError err = GetImageInfo(wpi, &x_offset, &y_offset, - &duration, &w, &h); - const int max_x_pos = x_offset + w; - const int max_y_pos = y_offset + h; - if (err != WEBP_MUX_OK) return err; - assert(x_offset < MAX_POSITION_OFFSET); - assert(y_offset < MAX_POSITION_OFFSET); - - if (max_x_pos > max_x) max_x = max_x_pos; - if (max_y_pos > max_y) max_y = max_y_pos; - image_area += w * h; - } - *width = max_x; - *height = max_y; - // Crude check to validate that there are no image overlaps/holes for tile - // images. Check that the aggregated image area for individual tiles exactly - // matches the image area of the constructed canvas. However, the area-match - // is necessary but not sufficient condition. - if ((flags & TILE_FLAG) && (image_area != (max_x * max_y))) { - *width = 0; - *height = 0; - return WEBP_MUX_INVALID_ARGUMENT; - } - } else { - // For a single image, extract the width & height from VP8/VP8L image-data. - int w, h; - const WebPChunk* const image_chunk = wpi->img_; - const WebPMuxError err = MuxGetImageWidthHeight(image_chunk, &w, &h); - if (err != WEBP_MUX_OK) return err; - *width = w; - *height = h; - } - return WEBP_MUX_OK; -} - -// VP8X format: -// Total Size : 10, -// Flags : 4 bytes, -// Width : 3 bytes, -// Height : 3 bytes. -static WebPMuxError CreateVP8XChunk(WebPMux* const mux) { - WebPMuxError err = WEBP_MUX_OK; - uint32_t flags = 0; - int width = 0; - int height = 0; - uint8_t data[VP8X_CHUNK_SIZE]; - const size_t data_size = VP8X_CHUNK_SIZE; - const WebPMuxImage* images = NULL; - - assert(mux != NULL); - images = mux->images_; // First image. - if (images == NULL || images->img_ == NULL || - images->img_->data_.bytes_ == NULL) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - // If VP8X chunk(s) is(are) already present, remove them (and later add new - // VP8X chunk with updated flags). - err = MuxDeleteAllNamedData(mux, IDX_VP8X); - if (err != WEBP_MUX_OK && err != WEBP_MUX_NOT_FOUND) return err; - - // Set flags. - if (mux->iccp_ != NULL && mux->iccp_->data_.bytes_ != NULL) { - flags |= ICCP_FLAG; - } - - if (mux->meta_ != NULL && mux->meta_->data_.bytes_ != NULL) { - flags |= META_FLAG; - } - - if (images->header_ != NULL) { - if (images->header_->tag_ == kChunks[IDX_TILE].tag) { - // This is a tiled image. - flags |= TILE_FLAG; - } else if (images->header_->tag_ == kChunks[IDX_FRAME].tag) { - // This is an image with animation. - flags |= ANIMATION_FLAG; - } - } - - if (MuxImageCount(images, WEBP_CHUNK_ALPHA) > 0) { - flags |= ALPHA_FLAG; // Some images have an alpha channel. - } - - if (flags == 0) { - // For Simple Image, VP8X chunk should not be added. - return WEBP_MUX_OK; - } - - err = GetImageCanvasWidthHeight(mux, flags, &width, &height); - if (err != WEBP_MUX_OK) return err; - - if (width <= 0 || height <= 0) { - return WEBP_MUX_INVALID_ARGUMENT; - } - if (width > MAX_CANVAS_SIZE || height > MAX_CANVAS_SIZE) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - if (MuxHasLosslessImages(images)) { - // We have a file with a VP8X chunk having some lossless images. - // As lossless images implicitly contain alpha, force ALPHA_FLAG to be true. - // Note: This 'flags' update must NOT be done for a lossless image - // without a VP8X chunk! - flags |= ALPHA_FLAG; - } - - PutLE32(data + 0, flags); // VP8X chunk flags. - PutLE24(data + 4, width - 1); // canvas width. - PutLE24(data + 7, height - 1); // canvas height. - - err = MuxAddChunk(mux, 1, kChunks[IDX_VP8X].tag, data, data_size, 1); - return err; -} - -WebPMuxError WebPMuxAssemble(WebPMux* mux, WebPData* assembled_data) { - size_t size = 0; - uint8_t* data = NULL; - uint8_t* dst = NULL; - int num_frames; - int num_loop_chunks; - WebPMuxError err; - - if (mux == NULL || assembled_data == NULL) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - // Remove LOOP chunk if unnecessary. - err = WebPMuxNumChunks(mux, kChunks[IDX_LOOP].id, &num_loop_chunks); - if (err != WEBP_MUX_OK) return err; - if (num_loop_chunks >= 1) { - err = WebPMuxNumChunks(mux, kChunks[IDX_FRAME].id, &num_frames); - if (err != WEBP_MUX_OK) return err; - if (num_frames == 0) { - err = DeleteLoopCount(mux); - if (err != WEBP_MUX_OK) return err; - } - } - - // Create VP8X chunk. - err = CreateVP8XChunk(mux); - if (err != WEBP_MUX_OK) return err; - - // Allocate data. - size = ChunksListDiskSize(mux->vp8x_) + ChunksListDiskSize(mux->iccp_) - + ChunksListDiskSize(mux->loop_) + MuxImageListDiskSize(mux->images_) - + ChunksListDiskSize(mux->meta_) + ChunksListDiskSize(mux->unknown_) - + RIFF_HEADER_SIZE; - - data = (uint8_t*)malloc(size); - if (data == NULL) return WEBP_MUX_MEMORY_ERROR; - - // Emit header & chunks. - dst = MuxEmitRiffHeader(data, size); - dst = ChunkListEmit(mux->vp8x_, dst); - dst = ChunkListEmit(mux->iccp_, dst); - dst = ChunkListEmit(mux->loop_, dst); - dst = MuxImageListEmit(mux->images_, dst); - dst = ChunkListEmit(mux->meta_, dst); - dst = ChunkListEmit(mux->unknown_, dst); - assert(dst == data + size); - - // Validate mux. - err = MuxValidate(mux); - if (err != WEBP_MUX_OK) { - free(data); - data = NULL; - size = 0; - } - - // Finalize. - assembled_data->bytes_ = data; - assembled_data->size_ = size; - - return err; -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/mux/muxi.h b/drivers/webpold/mux/muxi.h deleted file mode 100644 index 2f06f3ed03..0000000000 --- a/drivers/webpold/mux/muxi.h +++ /dev/null @@ -1,271 +0,0 @@ -// 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 for mux library. -// -// Author: Urvang (urvang@google.com) - -#ifndef WEBP_MUX_MUXI_H_ -#define WEBP_MUX_MUXI_H_ - -#include <stdlib.h> -#include "../dec/vp8i.h" -#include "../dec/vp8li.h" -#include "../format_constants.h" -#include "../mux.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Defines and constants. - -// 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. - WebPData data_; - WebPChunk* next_; -}; - -// MuxImage object. Store a full webp image (including frame/tile chunk, alpha -// chunk and VP8/VP8L chunk), -typedef struct WebPMuxImage WebPMuxImage; -struct WebPMuxImage { - WebPChunk* header_; // Corresponds to WEBP_CHUNK_FRAME/WEBP_CHUNK_TILE. - WebPChunk* alpha_; // Corresponds to WEBP_CHUNK_ALPHA. - WebPChunk* img_; // Corresponds to WEBP_CHUNK_IMAGE. - int is_partial_; // True if only some of the chunks are filled. - WebPMuxImage* next_; -}; - -// Main mux object. Stores data chunks. -struct WebPMux { - WebPMuxImage* images_; - WebPChunk* iccp_; - WebPChunk* meta_; - WebPChunk* loop_; - WebPChunk* vp8x_; - - WebPChunk* unknown_; -}; - -// CHUNK_INDEX enum: used for indexing within 'kChunks' (defined below) only. -// Note: the reason for having two enums ('WebPChunkId' and 'CHUNK_INDEX') is to -// allow two different chunks to have the same id (e.g. WebPChunkId -// 'WEBP_CHUNK_IMAGE' can correspond to CHUNK_INDEX 'IDX_VP8' or 'IDX_VP8L'). -typedef enum { - IDX_VP8X = 0, - IDX_ICCP, - IDX_LOOP, - IDX_FRAME, - IDX_TILE, - IDX_ALPHA, - IDX_VP8, - IDX_VP8L, - IDX_META, - IDX_UNKNOWN, - - IDX_NIL, - IDX_LAST_CHUNK -} CHUNK_INDEX; - -#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; - uint32_t size; -} ChunkInfo; - -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. -CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag); - -// Get chunk id from chunk tag. Returns WEBP_CHUNK_NIL if not found. -WebPChunkId ChunkGetIdFromTag(uint32_t tag); - -// 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); - -// Fill the chunk with the given data. -WebPMuxError ChunkAssignData(WebPChunk* chunk, const WebPData* const data, - int copy_data, uint32_t tag); - -// 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, - uint32_t nth); - -// Releases chunk and returns chunk->next_. -WebPChunk* ChunkRelease(WebPChunk* const chunk); - -// Deletes given chunk & returns chunk->next_. -WebPChunk* ChunkDelete(WebPChunk* const chunk); - -// 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_; - assert(data_size < MAX_CHUNK_PAYLOAD); - return SizeWithPadding(data_size); -} - -// Total size of a list of chunks. -size_t ChunksListDiskSize(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. - -// Initialize. -void MuxImageInit(WebPMuxImage* const wpi); - -// Releases image 'wpi' and returns wpi->next. -WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi); - -// Delete image 'wpi' and return the next image in the list or NULL. -// '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'. -int MuxImageCount(const WebPMuxImage* wpi_list, WebPChunkId id); - -// 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_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); - -// Get nth image in the image list with given tag id. -WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth, - WebPChunkId id, 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); - -// 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) -} // extern "C" -#endif - -#endif /* WEBP_MUX_MUXI_H_ */ diff --git a/drivers/webpold/mux/muxinternal.c b/drivers/webpold/mux/muxinternal.c deleted file mode 100644 index 6c3c4fe60a..0000000000 --- a/drivers/webpold/mux/muxinternal.c +++ /dev/null @@ -1,576 +0,0 @@ -// 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 objects and utils for mux. -// -// Authors: Urvang (urvang@google.com) -// Vikas (vikasa@google.com) - -#include <assert.h> -#include "./muxi.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#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', '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 }, - - { NIL_TAG, WEBP_CHUNK_NIL, UNDEFINED_CHUNK_SIZE } -}; - -//------------------------------------------------------------------------------ -// Life of a chunk object. - -void ChunkInit(WebPChunk* const chunk) { - assert(chunk); - memset(chunk, 0, sizeof(*chunk)); - chunk->tag_ = NIL_TAG; -} - -WebPChunk* ChunkRelease(WebPChunk* const chunk) { - WebPChunk* next; - if (chunk == NULL) return NULL; - if (chunk->owner_) { - WebPDataClear(&chunk->data_); - } - next = chunk->next_; - ChunkInit(chunk); - return next; -} - -//------------------------------------------------------------------------------ -// Chunk misc methods. - -CHUNK_INDEX ChunkGetIndexFromTag(uint32_t tag) { - int i; - for (i = 0; kChunks[i].tag != NIL_TAG; ++i) { - if (tag == kChunks[i].tag) return i; - } - return IDX_NIL; -} - -WebPChunkId ChunkGetIdFromTag(uint32_t tag) { - int i; - for (i = 0; kChunks[i].tag != NIL_TAG; ++i) { - if (tag == kChunks[i].tag) return kChunks[i].id; - } - return WEBP_CHUNK_NIL; -} - -//------------------------------------------------------------------------------ -// Chunk search methods. - -// Returns next chunk in the chunk list with the given tag. -static WebPChunk* ChunkSearchNextInList(WebPChunk* chunk, uint32_t tag) { - while (chunk && chunk->tag_ != tag) { - chunk = chunk->next_; - } - return chunk; -} - -WebPChunk* ChunkSearchList(WebPChunk* first, uint32_t nth, uint32_t tag) { - uint32_t iter = nth; - first = ChunkSearchNextInList(first, tag); - if (!first) return NULL; - - while (--iter != 0) { - WebPChunk* next_chunk = ChunkSearchNextInList(first->next_, tag); - if (next_chunk == NULL) break; - first = next_chunk; - } - return ((nth > 0) && (iter > 0)) ? NULL : first; -} - -// 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. -static int ChunkSearchListToSet(WebPChunk** chunk_list, uint32_t nth, - WebPChunk*** const location) { - uint32_t count = 0; - assert(chunk_list); - *location = chunk_list; - - while (*chunk_list) { - WebPChunk* const cur_chunk = *chunk_list; - ++count; - if (count == nth) return 1; // Found. - chunk_list = &cur_chunk->next_; - *location = chunk_list; - } - - // *chunk_list is ok to be NULL if adding at last location. - return (nth == 0 || (count == nth - 1)) ? 1 : 0; -} - -//------------------------------------------------------------------------------ -// Chunk writer methods. - -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) { - 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. - chunk->data_ = *data; - } - } - - chunk->tag_ = tag; - - return WEBP_MUX_OK; -} - -WebPMuxError ChunkSetNth(const WebPChunk* chunk, WebPChunk** chunk_list, - uint32_t nth) { - WebPChunk* new_chunk; - - if (!ChunkSearchListToSet(chunk_list, nth, &chunk_list)) { - return WEBP_MUX_NOT_FOUND; - } - - new_chunk = (WebPChunk*)malloc(sizeof(*new_chunk)); - if (new_chunk == NULL) return WEBP_MUX_MEMORY_ERROR; - *new_chunk = *chunk; - new_chunk->next_ = *chunk_list; - *chunk_list = new_chunk; - return WEBP_MUX_OK; -} - -//------------------------------------------------------------------------------ -// Chunk deletion method(s). - -WebPChunk* ChunkDelete(WebPChunk* const chunk) { - WebPChunk* const next = ChunkRelease(chunk); - free(chunk); - return next; -} - -//------------------------------------------------------------------------------ -// 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_; - 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); - 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) { - 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)); - } -} - -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; -} - -//------------------------------------------------------------------------------ -// Life of a MuxImage object. - -void MuxImageInit(WebPMuxImage* const wpi) { - assert(wpi); - memset(wpi, 0, sizeof(*wpi)); -} - -WebPMuxImage* MuxImageRelease(WebPMuxImage* const wpi) { - WebPMuxImage* next; - if (wpi == NULL) return NULL; - ChunkDelete(wpi->header_); - ChunkDelete(wpi->alpha_); - ChunkDelete(wpi->img_); - - next = wpi->next_; - MuxImageInit(wpi); - return next; -} - -//------------------------------------------------------------------------------ -// MuxImage search methods. - -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; - } - } - return count; -} - -// 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. -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); - if (nth == 0) return 0; // Not found. - } - - while (*wpi_list) { - 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. - } - } - wpi_list = &cur_wpi->next_; - *location = wpi_list; - } - return 0; // Not found. -} - -//------------------------------------------------------------------------------ -// MuxImage writer methods. - -WebPMuxError MuxImagePush(const WebPMuxImage* wpi, WebPMuxImage** wpi_list) { - WebPMuxImage* new_wpi; - - while (*wpi_list != NULL) { - WebPMuxImage* const cur_wpi = *wpi_list; - if (cur_wpi->next_ == NULL) break; - wpi_list = &cur_wpi->next_; - } - - new_wpi = (WebPMuxImage*)malloc(sizeof(*new_wpi)); - if (new_wpi == NULL) return WEBP_MUX_MEMORY_ERROR; - *new_wpi = *wpi; - new_wpi->next_ = NULL; - - if (*wpi_list != NULL) { - (*wpi_list)->next_ = new_wpi; - } else { - *wpi_list = new_wpi; - } - return WEBP_MUX_OK; -} - -//------------------------------------------------------------------------------ -// MuxImage deletion methods. - -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); - 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) { - assert(wpi_list); - if (!SearchImageToGetOrDelete(wpi_list, nth, id, &wpi_list)) { - return WEBP_MUX_NOT_FOUND; - } - *wpi_list = MuxImageDelete(*wpi_list); - return WEBP_MUX_OK; -} - -//------------------------------------------------------------------------------ -// MuxImage reader methods. - -WebPMuxError MuxImageGetNth(const WebPMuxImage** wpi_list, uint32_t nth, - WebPChunkId id, WebPMuxImage** wpi) { - assert(wpi_list); - assert(wpi); - if (!SearchImageToGetOrDelete((WebPMuxImage**)wpi_list, nth, id, - (WebPMuxImage***)&wpi_list)) { - return WEBP_MUX_NOT_FOUND; - } - *wpi = (WebPMuxImage*)*wpi_list; - return WEBP_MUX_OK; -} - -//------------------------------------------------------------------------------ -// MuxImage serialization methods. - -// Size of an image. -size_t MuxImageDiskSize(const WebPMuxImage* const wpi) { - size_t size = 0; - if (wpi->header_ != NULL) size += ChunkDiskSize(wpi->header_); - if (wpi->alpha_ != NULL) size += ChunkDiskSize(wpi->alpha_); - if (wpi->img_ != NULL) size += ChunkDiskSize(wpi->img_); - 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_; - } - return size; -} - -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). - // 3. VP8/VP8L chunk. - assert(wpi); - if (wpi->header_ != NULL) dst = ChunkEmit(wpi->header_, 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_; - } - return dst; -} - -//------------------------------------------------------------------------------ -// Helper methods for mux. - -int MuxHasLosslessImages(const WebPMuxImage* images) { - while (images != NULL) { - assert(images->img_ != NULL); - if (images->img_->tag_ == kChunks[IDX_VP8L].tag) { - return 1; - } - images = images->next_; - } - return 0; -} - -uint8_t* MuxEmitRiffHeader(uint8_t* const data, size_t size) { - PutLE32(data + 0, MKFOURCC('R', 'I', 'F', 'F')); - PutLE32(data + TAG_SIZE, (uint32_t)size - CHUNK_HEADER_SIZE); - assert(size == (uint32_t)size); - PutLE32(data + TAG_SIZE + CHUNK_SIZE_BYTES, MKFOURCC('W', 'E', 'B', 'P')); - return data + RIFF_HEADER_SIZE; -} - -WebPChunk** MuxGetChunkListFromId(const WebPMux* mux, WebPChunkId id) { - assert(mux != NULL); - 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; - } -} - -static int IsNotCompatible(int feature, int num_items) { - return (feature != 0) != (num_items > 0); -} - -#define NO_FLAG 0 - -// Test basic constraints: -// retrieval, maximum number of chunks by index (use -1 to skip) -// and feature incompatibility (use NO_FLAG to skip). -// 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, - int max, int* num) { - const WebPMuxError err = - WebPMuxNumChunks(mux, kChunks[idx].id, num); - if (err != WEBP_MUX_OK) return err; - if (max > -1 && *num > max) return WEBP_MUX_INVALID_ARGUMENT; - if (feature != NO_FLAG && IsNotCompatible(vp8x_flags & feature, *num)) { - return WEBP_MUX_INVALID_ARGUMENT; - } - return WEBP_MUX_OK; -} - -WebPMuxError MuxValidate(const WebPMux* const mux) { - int num_iccp; - int num_meta; - int num_loop_chunks; - int num_frames; - int num_tiles; - int num_vp8x; - int num_images; - int num_alpha; - uint32_t flags; - WebPMuxError err; - - // Verify mux is not NULL. - if (mux == NULL) return WEBP_MUX_INVALID_ARGUMENT; - - // Verify mux has at least one image. - if (mux->images_ == NULL) return WEBP_MUX_INVALID_ARGUMENT; - - err = WebPMuxGetFeatures(mux, &flags); - if (err != WEBP_MUX_OK) return err; - - // At most one color profile chunk. - 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); - 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); - if (err != WEBP_MUX_OK) return err; - err = ValidateChunk(mux, IDX_FRAME, 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)) { - return WEBP_MUX_INVALID_ARGUMENT; - } - if (!has_animation && (num_loop_chunks == 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); - if (err != WEBP_MUX_OK) return err; - - // Verify either VP8X chunk is present OR there is only one elem in - // mux->images_. - err = ValidateChunk(mux, IDX_VP8X, NO_FLAG, flags, 1, &num_vp8x); - if (err != WEBP_MUX_OK) return err; - err = ValidateChunk(mux, IDX_VP8, NO_FLAG, flags, -1, &num_images); - if (err != WEBP_MUX_OK) return err; - 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; - } - - // num_tiles & num_images are consistent. - if (num_tiles > 0 && num_images != num_tiles) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - return WEBP_MUX_OK; -} - -#undef NO_FLAG - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/mux/muxread.c b/drivers/webpold/mux/muxread.c deleted file mode 100644 index 21c3cfbaeb..0000000000 --- a/drivers/webpold/mux/muxread.c +++ /dev/null @@ -1,411 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Read APIs for mux. -// -// Authors: Urvang (urvang@google.com) -// Vikas (vikasa@google.com) - -#include <assert.h> -#include "./muxi.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Helper method(s). - -// Handy MACRO. -#define SWITCH_ID_LIST(INDEX, LIST) \ - if (idx == (INDEX)) { \ - const WebPChunk* const chunk = ChunkSearchList((LIST), nth, \ - kChunks[(INDEX)].tag); \ - if (chunk) { \ - *data = chunk->data_; \ - return WEBP_MUX_OK; \ - } else { \ - return WEBP_MUX_NOT_FOUND; \ - } \ - } - -static WebPMuxError MuxGet(const WebPMux* const mux, CHUNK_INDEX idx, - uint32_t nth, WebPData* const data) { - assert(mux != NULL); - assert(!IsWPI(kChunks[idx].id)); - WebPDataInit(data); - - 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_UNKNOWN, mux->unknown_); - return WEBP_MUX_NOT_FOUND; -} -#undef SWITCH_ID_LIST - -// 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) { - uint32_t chunk_size; - WebPData chunk_data; - - // Sanity checks. - if (data_size < TAG_SIZE) return WEBP_MUX_NOT_ENOUGH_DATA; - chunk_size = GetLE32(data + TAG_SIZE); - - { - const size_t chunk_disk_size = SizeWithPadding(chunk_size); - if (chunk_disk_size > riff_size) return WEBP_MUX_BAD_DATA; - if (chunk_disk_size > data_size) return WEBP_MUX_NOT_ENOUGH_DATA; - } - - // Data assignment. - chunk_data.bytes_ = data + CHUNK_HEADER_SIZE; - chunk_data.size_ = chunk_size; - return ChunkAssignData(chunk, &chunk_data, copy_data, GetLE32(data + 0)); -} - -//------------------------------------------------------------------------------ -// Create a mux object from WebP-RIFF data. - -WebPMux* WebPMuxCreateInternal(const WebPData* bitstream, int copy_data, - int version) { - size_t riff_size; - uint32_t tag; - const uint8_t* end; - WebPMux* mux = NULL; - WebPMuxImage* wpi = NULL; - const uint8_t* data; - size_t size; - WebPChunk chunk; - ChunkInit(&chunk); - - // Sanity checks. - if (WEBP_ABI_IS_INCOMPATIBLE(version, WEBP_MUX_ABI_VERSION)) { - return NULL; // version mismatch - } - if (bitstream == NULL) return NULL; - - data = bitstream->bytes_; - size = bitstream->size_; - - if (data == NULL) return NULL; - if (size < RIFF_HEADER_SIZE) return NULL; - if (GetLE32(data + 0) != MKFOURCC('R', 'I', 'F', 'F') || - GetLE32(data + CHUNK_HEADER_SIZE) != MKFOURCC('W', 'E', 'B', 'P')) { - return NULL; - } - - mux = WebPMuxNew(); - if (mux == NULL) return NULL; - - if (size < RIFF_HEADER_SIZE + TAG_SIZE) goto Err; - - tag = GetLE32(data + RIFF_HEADER_SIZE); - if (tag != kChunks[IDX_VP8].tag && - tag != kChunks[IDX_VP8L].tag && - tag != kChunks[IDX_VP8X].tag) { - goto Err; // First chunk should be VP8, VP8L or VP8X. - } - - riff_size = SizeWithPadding(GetLE32(data + TAG_SIZE)); - if (riff_size > MAX_CHUNK_PAYLOAD || riff_size > size) { - goto Err; - } else { - if (riff_size < size) { // Redundant data after last chunk. - size = riff_size; // To make sure we don't read any data beyond mux_size. - } - } - - end = data + size; - data += RIFF_HEADER_SIZE; - size -= RIFF_HEADER_SIZE; - - wpi = (WebPMuxImage*)malloc(sizeof(*wpi)); - if (wpi == NULL) goto Err; - MuxImageInit(wpi); - - // Loop over chunks. - while (data != end) { - WebPChunkId id; - WebPMuxError err; - - err = ChunkVerifyAndAssignData(&chunk, data, size, riff_size, copy_data); - if (err != WEBP_MUX_OK) goto Err; - - 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) { - wpi->is_partial_ = 0; // wpi is completely filled. - // 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; - } - ChunkInit(&chunk); - } - - // Validate mux if complete. - if (MuxValidate(mux) != WEBP_MUX_OK) goto Err; - - MuxImageDelete(wpi); - return mux; // All OK; - - Err: // Something bad happened. - ChunkRelease(&chunk); - MuxImageDelete(wpi); - WebPMuxDelete(mux); - return NULL; -} - -//------------------------------------------------------------------------------ -// Get API(s). - -WebPMuxError WebPMuxGetFeatures(const WebPMux* mux, uint32_t* flags) { - WebPData data; - WebPMuxError err; - - if (mux == NULL || flags == NULL) return WEBP_MUX_INVALID_ARGUMENT; - *flags = 0; - - // 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 (data.size_ < CHUNK_SIZE_BYTES) return WEBP_MUX_BAD_DATA; - - // All OK. Fill up flags. - *flags = GetLE32(data.bytes_); - return WEBP_MUX_OK; -} - -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; - assert(width >= 1 && height >= 1); - assert(width <= MAX_CANVAS_SIZE && height <= MAX_CANVAS_SIZE); - assert(width * (uint64_t)height < MAX_IMAGE_AREA); - PutLE32(dst, MKFOURCC('V', 'P', '8', 'X')); - PutLE32(dst + TAG_SIZE, VP8X_CHUNK_SIZE); - PutLE32(dst + CHUNK_HEADER_SIZE, flags); - PutLE24(dst + CHUNK_HEADER_SIZE + 4, width - 1); - PutLE24(dst + CHUNK_HEADER_SIZE + 7, height - 1); - return dst + vp8x_size; -} - -// Assemble a single image WebP bitstream from 'wpi'. -static WebPMuxError SynthesizeBitstream(WebPMuxImage* const wpi, - WebPData* const bitstream) { - uint8_t* dst; - - // Allocate data. - const int need_vp8x = (wpi->alpha_ != NULL); - const size_t vp8x_size = need_vp8x ? CHUNK_HEADER_SIZE + VP8X_CHUNK_SIZE : 0; - const size_t alpha_size = need_vp8x ? ChunkDiskSize(wpi->alpha_) : 0; - // Note: No need to output FRM/TILE 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); - 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 = ChunkListEmit(wpi->alpha_, dst); // ALPH. - } - - // Bitstream. - dst = ChunkListEmit(wpi->img_, dst); - assert(dst == data + size); - - // Output. - bitstream->bytes_ = data; - bitstream->size_ = size; - return WEBP_MUX_OK; -} - -WebPMuxError WebPMuxGetImage(const WebPMux* mux, WebPData* bitstream) { - WebPMuxError err; - WebPMuxImage* wpi = NULL; - - if (mux == NULL || bitstream == 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); -} - -WebPMuxError WebPMuxGetMetadata(const WebPMux* mux, WebPData* metadata) { - if (mux == NULL || metadata == NULL) return WEBP_MUX_INVALID_ARGUMENT; - return MuxGet(mux, IDX_META, 1, metadata); -} - -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); -} - -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 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)) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - // Get the nth WebPMuxImage. - err = MuxImageGetNth((const WebPMuxImage**)&mux->images_, nth, id, &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); -} - -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 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); -} - -// 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; - } - return IDX_NIL; -} - -// Count number of chunks matching 'tag' in the 'chunk_list'. -// If tag == NIL_TAG, any tag will be matched. -static int CountChunks(const WebPChunk* const chunk_list, uint32_t tag) { - int count = 0; - const WebPChunk* current; - for (current = chunk_list; current != NULL; current = current->next_) { - if (tag == NIL_TAG || current->tag_ == tag) { - count++; // Count chunks whose tags match. - } - } - return count; -} - -WebPMuxError WebPMuxNumChunks(const WebPMux* mux, - WebPChunkId id, int* num_elements) { - if (mux == NULL || num_elements == NULL) { - return WEBP_MUX_INVALID_ARGUMENT; - } - - if (IsWPI(id)) { - *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); - } - } - - return WEBP_MUX_OK; -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/types.h b/drivers/webpold/types.h deleted file mode 100644 index 3e27190bef..0000000000 --- a/drivers/webpold/types.h +++ /dev/null @@ -1,45 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Common types -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_WEBP_TYPES_H_ -#define WEBP_WEBP_TYPES_H_ - -#include <stddef.h> // for size_t - -#ifndef _MSC_VER -#include <inttypes.h> -#ifdef __STRICT_ANSI__ -#define WEBP_INLINE -#else /* __STRICT_ANSI__ */ -#define WEBP_INLINE inline -#endif -#else -typedef signed char int8_t; -typedef unsigned char uint8_t; -typedef signed short int16_t; -typedef unsigned short uint16_t; -typedef signed int int32_t; -typedef unsigned int uint32_t; -typedef unsigned long long int uint64_t; -typedef long long int int64_t; -#define WEBP_INLINE __forceinline -#endif /* _MSC_VER */ - -#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 -#endif /* WEBP_EXTERN */ - -// Macro to check ABI compatibility (same major revision number) -#define WEBP_ABI_IS_INCOMPATIBLE(a, b) (((a) >> 8) != ((b) >> 8)) - -#endif /* WEBP_WEBP_TYPES_H_ */ diff --git a/drivers/webpold/utils/bit_reader.c b/drivers/webpold/utils/bit_reader.c deleted file mode 100644 index 1afb1db890..0000000000 --- a/drivers/webpold/utils/bit_reader.c +++ /dev/null @@ -1,229 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Boolean decoder -// -// Author: Skal (pascal.massimino@gmail.com) - -#include "./bit_reader.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define MK(X) (((bit_t)(X) << (BITS)) | (MASK)) - -//------------------------------------------------------------------------------ -// VP8BitReader - -void VP8InitBitReader(VP8BitReader* const br, - const uint8_t* const start, const uint8_t* const end) { - assert(br != NULL); - assert(start != NULL); - assert(start <= end); - br->range_ = MK(255 - 1); - br->buf_ = start; - br->buf_end_ = end; - br->value_ = 0; - br->missing_ = 8; // to load the very first 8bits - br->eof_ = 0; -} - -const uint8_t kVP8Log2Range[128] = { - 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 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) -}; - -#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->eof_ = 1; - } -} - -//------------------------------------------------------------------------------ -// Higher-level calls - -uint32_t VP8GetValue(VP8BitReader* const br, int bits) { - uint32_t v = 0; - while (bits-- > 0) { - v |= VP8GetBit(br, 0x80) << bits; - } - return v; -} - -int32_t VP8GetSignedValue(VP8BitReader* const br, int bits) { - const int value = VP8GetValue(br, bits); - return VP8Get(br) ? -value : value; -} - -//------------------------------------------------------------------------------ -// VP8LBitReader - -#define MAX_NUM_BIT_READ 25 - -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 -}; - -void VP8LInitBitReader(VP8LBitReader* const br, - const uint8_t* const start, - size_t length) { - size_t i; - 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_; - } -} - -void VP8LBitReaderSetBuffer(VP8LBitReader* const br, - const uint8_t* const buf, size_t len) { - 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; -} - -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->pos_; - br->bit_pos_ -= 8; - } -} - -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); -#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; -} - -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); - } - } - } else { - br->error_ = 1; - } - return val; -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/utils/bit_reader.h b/drivers/webpold/utils/bit_reader.h deleted file mode 100644 index 43cd948fd4..0000000000 --- a/drivers/webpold/utils/bit_reader.h +++ /dev/null @@ -1,198 +0,0 @@ -// -// 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/ -// ----------------------------------------------------------------------------- -// -// Boolean decoder -// -// Author: Skal (pascal.massimino@gmail.com) -// Vikas Arora (vikaas.arora@gmail.com) - -#ifndef WEBP_UTILS_BIT_READER_H_ -#define WEBP_UTILS_BIT_READER_H_ - -#include <assert.h> -#ifdef _MSC_VER -#include <stdlib.h> // _byteswap_ulong -#endif -#include <string.h> // For memcpy -#include "../types.h" - -#if defined(__cplusplus) || defined(c_plusplus) -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; -#endif - -//------------------------------------------------------------------------------ -// Bitreader and code-tree reader - -typedef struct VP8BitReader VP8BitReader; -struct VP8BitReader { - const uint8_t* buf_; // next byte to be read - const uint8_t* buf_end_; // end of read 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); - -// return the next value made of 'num_bits' bits -uint32_t VP8GetValue(VP8BitReader* const br, int num_bits); -static WEBP_INLINE uint32_t VP8Get(VP8BitReader* const br) { - return VP8GetValue(br, 1); -} - -// 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; -} - - -// ----------------------------------------------------------------------------- -// Bitreader - -typedef struct { - uint64_t val_; - const uint8_t* buf_; - size_t len_; - size_t pos_; - int bit_pos_; - int eos_; - int error_; -} VP8LBitReader; - -void VP8LInitBitReader(VP8LBitReader* const br, - const uint8_t* const start, - size_t length); - -// Sets a new data buffer. -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. -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; -} - -// Advances the Read buffer by 4 bytes to make room for reading next 32 bits. -void VP8LFillBitWindow(VP8LBitReader* const br); - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_UTILS_BIT_READER_H_ */ diff --git a/drivers/webpold/utils/bit_writer.c b/drivers/webpold/utils/bit_writer.c deleted file mode 100644 index 671159cacd..0000000000 --- a/drivers/webpold/utils/bit_writer.c +++ /dev/null @@ -1,284 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Bit writing and boolean coder -// -// Author: Skal (pascal.massimino@gmail.com) -// Vikas Arora (vikaas.arora@gmail.com) - -#include <assert.h> -#include <string.h> // for memcpy() -#include <stdlib.h> -#include "./bit_writer.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// VP8BitWriter - -static int BitWriterResize(VP8BitWriter* const bw, size_t extra_size) { - uint8_t* new_buf; - size_t new_size; - const uint64_t needed_size_64b = (uint64_t)bw->pos_ + extra_size; - const size_t needed_size = (size_t)needed_size_64b; - if (needed_size_64b != needed_size) { - bw->error_ = 1; - return 0; - } - if (needed_size <= bw->max_pos_) return 1; - // If the following line wraps over 32bit, the test just after will catch it. - 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); - if (new_buf == NULL) { - bw->error_ = 1; - return 0; - } - memcpy(new_buf, bw->buf_, bw->pos_); - free(bw->buf_); - bw->buf_ = new_buf; - bw->max_pos_ = new_size; - return 1; -} - -static void kFlush(VP8BitWriter* const bw) { - const int s = 8 + bw->nb_bits_; - const int32_t bits = bw->value_ >> s; - assert(bw->nb_bits_ >= 0); - bw->value_ -= bits << s; - bw->nb_bits_ -= 8; - if ((bits & 0xff) != 0xff) { - size_t pos = bw->pos_; - if (!BitWriterResize(bw, bw->run_ + 1)) { - return; - } - if (bits & 0x100) { // overflow -> propagate carry over pending 0xff's - if (pos > 0) bw->buf_[pos - 1]++; - } - if (bw->run_ > 0) { - const int value = (bits & 0x100) ? 0x00 : 0xff; - for (; bw->run_ > 0; --bw->run_) bw->buf_[pos++] = value; - } - bw->buf_[pos++] = bits; - bw->pos_ = pos; - } else { - bw->run_++; // delay writing of bytes 0xff, pending eventual carry. - } -} - -//------------------------------------------------------------------------------ -// renormalization - -static const uint8_t kNorm[128] = { // renorm_sizes[i] = 8 - log2(i) - 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 0 -}; - -// range = ((range + 1) << kVP8Log2Range[range]) - 1 -static const uint8_t kNewRange[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 -}; - -int VP8PutBit(VP8BitWriter* const bw, int bit, int prob) { - const int split = (bw->range_ * prob) >> 8; - if (bit) { - bw->value_ += split + 1; - bw->range_ -= split + 1; - } else { - bw->range_ = split; - } - if (bw->range_ < 127) { // emit 'shift' bits out and renormalize - const int shift = kNorm[bw->range_]; - bw->range_ = kNewRange[bw->range_]; - bw->value_ <<= shift; - bw->nb_bits_ += shift; - if (bw->nb_bits_ > 0) kFlush(bw); - } - return bit; -} - -int VP8PutBitUniform(VP8BitWriter* const bw, int bit) { - const int split = bw->range_ >> 1; - if (bit) { - bw->value_ += split + 1; - bw->range_ -= split + 1; - } else { - bw->range_ = split; - } - if (bw->range_ < 127) { - bw->range_ = kNewRange[bw->range_]; - bw->value_ <<= 1; - bw->nb_bits_ += 1; - if (bw->nb_bits_ > 0) kFlush(bw); - } - return bit; -} - -void VP8PutValue(VP8BitWriter* const bw, int value, int nb_bits) { - int mask; - for (mask = 1 << (nb_bits - 1); mask; mask >>= 1) - VP8PutBitUniform(bw, value & mask); -} - -void VP8PutSignedValue(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); - } else { - VP8PutValue(bw, value << 1, nb_bits + 1); - } -} - -//------------------------------------------------------------------------------ - -int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size) { - bw->range_ = 255 - 1; - bw->value_ = 0; - bw->run_ = 0; - bw->nb_bits_ = -8; - bw->pos_ = 0; - bw->max_pos_ = 0; - bw->error_ = 0; - bw->buf_ = NULL; - return (expected_size > 0) ? BitWriterResize(bw, expected_size) : 1; -} - -uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw) { - VP8PutValue(bw, 0, 9 - bw->nb_bits_); - bw->nb_bits_ = 0; // pad with zeroes - kFlush(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 - if (!BitWriterResize(bw, size)) return 0; - memcpy(bw->buf_ + bw->pos_, data, size); - bw->pos_ += size; - return 1; -} - -void VP8BitWriterWipeOut(VP8BitWriter* const bw) { - if (bw) { - free(bw->buf_); - memset(bw, 0, sizeof(*bw)); - } -} - -//------------------------------------------------------------------------------ -// VP8LBitWriter - -// 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 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 (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); - if (allocated_buf == NULL) { - bw->error_ = 1; - return 0; - } - memcpy(allocated_buf, bw->buf_, current_size); - free(bw->buf_); - bw->buf_ = allocated_buf; - bw->max_bytes_ = allocated_size; - memset(allocated_buf + current_size, 0, allocated_size - current_size); - return 1; -} - -int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size) { - memset(bw, 0, sizeof(*bw)); - return VP8LBitWriterResize(bw, expected_size); -} - -void VP8LBitWriterDestroy(VP8LBitWriter* const bw) { - if (bw != NULL) { - free(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_; - if (extra_size != (size_t)extra_size || - !VP8LBitWriterResize(bw, (size_t)extra_size)) { - bw->bit_pos_ = 0; - bw->error_ = 1; - } - } -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/utils/bit_writer.h b/drivers/webpold/utils/bit_writer.h deleted file mode 100644 index 57f39b11b1..0000000000 --- a/drivers/webpold/utils/bit_writer.h +++ /dev/null @@ -1,123 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Bit writing and boolean coder -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_UTILS_BIT_WRITER_H_ -#define WEBP_UTILS_BIT_WRITER_H_ - -#include "../types.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Bit-writing - -typedef struct VP8BitWriter VP8BitWriter; -struct VP8BitWriter { - int32_t range_; // range-1 - int32_t value_; - int run_; // number of outstanding bits - int nb_bits_; // number of pending bits - uint8_t* buf_; // internal buffer. Re-allocated regularly. Not owned. - size_t pos_; - size_t max_pos_; - int error_; // true in case of error -}; - -// Initialize the object. Allocates some initial memory based on expected_size. -int VP8BitWriterInit(VP8BitWriter* const bw, size_t expected_size); -// Finalize the bitstream coding. Returns a pointer to the internal buffer. -uint8_t* VP8BitWriterFinish(VP8BitWriter* const bw); -// Release any pending memory and zeroes the object. Not a mandatory call. -// Only useful in case of error, when the internal buffer hasn't been grabbed! -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); - -// Appends some bytes to the internal buffer. Data is copied. -int VP8BitWriterAppend(VP8BitWriter* const bw, - const uint8_t* data, size_t size); - -// return approximate write position (in bits) -static WEBP_INLINE uint64_t VP8BitWriterPos(const VP8BitWriter* const bw) { - return (uint64_t)(bw->pos_ + bw->run_) * 8 + 8 + bw->nb_bits_; -} - -// Returns a pointer to the internal buffer. -static WEBP_INLINE uint8_t* VP8BitWriterBuf(const VP8BitWriter* const bw) { - return bw->buf_; -} -// Returns the size of the internal buffer. -static WEBP_INLINE size_t VP8BitWriterSize(const VP8BitWriter* const bw) { - return bw->pos_; -} - -//------------------------------------------------------------------------------ -// 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. - -typedef struct { - uint8_t* buf_; - size_t bit_pos_; - size_t max_bytes_; - - // 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 - // writing of bits. - int error_; -} VP8LBitWriter; - -static WEBP_INLINE size_t VP8LBitWriterNumBytes(VP8LBitWriter* const bw) { - return (bw->bit_pos_ + 7) >> 3; -} - -static WEBP_INLINE uint8_t* VP8LBitWriterFinish(VP8LBitWriter* const bw) { - return bw->buf_; -} - -// Returns 0 in case of memory allocation error. -int VP8LBitWriterInit(VP8LBitWriter* const bw, size_t expected_size); - -void VP8LBitWriterDestroy(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. -// -// VP8LBitWriter's error_ flag is set in case of memory allocation error. -void VP8LWriteBits(VP8LBitWriter* const bw, int n_bits, uint32_t bits); - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_UTILS_BIT_WRITER_H_ */ diff --git a/drivers/webpold/utils/color_cache.c b/drivers/webpold/utils/color_cache.c deleted file mode 100644 index 560f81db10..0000000000 --- a/drivers/webpold/utils/color_cache.c +++ /dev/null @@ -1,44 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Color Cache for WebP Lossless -// -// Author: Jyrki Alakuijala (jyrki@google.com) - -#include <assert.h> -#include <stdlib.h> -#include "./color_cache.h" -#include "../utils/utils.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// VP8LColorCache. - -int VP8LColorCacheInit(VP8LColorCache* const cc, int hash_bits) { - const int hash_size = 1 << hash_bits; - assert(cc != NULL); - assert(hash_bits > 0); - cc->colors_ = (uint32_t*)WebPSafeCalloc((uint64_t)hash_size, - sizeof(*cc->colors_)); - if (cc->colors_ == NULL) return 0; - cc->hash_shift_ = 32 - hash_bits; - return 1; -} - -void VP8LColorCacheClear(VP8LColorCache* const cc) { - if (cc != NULL) { - free(cc->colors_); - cc->colors_ = NULL; - } -} - -#if defined(__cplusplus) || defined(c_plusplus) -} -#endif diff --git a/drivers/webpold/utils/color_cache.h b/drivers/webpold/utils/color_cache.h deleted file mode 100644 index da5e260195..0000000000 --- a/drivers/webpold/utils/color_cache.h +++ /dev/null @@ -1,68 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Color Cache for WebP Lossless -// -// Authors: Jyrki Alakuijala (jyrki@google.com) -// Urvang Joshi (urvang@google.com) - -#ifndef WEBP_UTILS_COLOR_CACHE_H_ -#define WEBP_UTILS_COLOR_CACHE_H_ - -#include "../types.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -// Main color cache struct. -typedef struct { - uint32_t *colors_; // color entries - int hash_shift_; // Hash shift: 32 - 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_)); - return cc->colors_[key]; -} - -static WEBP_INLINE void VP8LColorCacheInsert(const VP8LColorCache* const cc, - uint32_t argb) { - const uint32_t key = (kHashMul * argb) >> cc->hash_shift_; - cc->colors_[key] = argb; -} - -static WEBP_INLINE int VP8LColorCacheGetIndex(const VP8LColorCache* const cc, - uint32_t argb) { - return (kHashMul * argb) >> cc->hash_shift_; -} - -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; -} - -//------------------------------------------------------------------------------ - -// Initializes the color cache with 'hash_bits' bits for the keys. -// Returns false in case of memory error. -int VP8LColorCacheInit(VP8LColorCache* const color_cache, int hash_bits); - -// Delete the memory associated to color cache. -void VP8LColorCacheClear(VP8LColorCache* const color_cache); - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} -#endif - -#endif // WEBP_UTILS_COLOR_CACHE_H_ diff --git a/drivers/webpold/utils/filters.c b/drivers/webpold/utils/filters.c deleted file mode 100644 index 08f52a3d20..0000000000 --- a/drivers/webpold/utils/filters.c +++ /dev/null @@ -1,229 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Spatial prediction using various filters -// -// 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. - -#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) { - 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; - int mean = p[0]; - for (i = 2; i < width - 1; i += 2) { - const int diff0 = SDIFF(p[i], mean); - const int diff1 = SDIFF(p[i], p[i - 1]); - const int diff2 = SDIFF(p[i], p[i - width]); - const int grad_pred = - GradientPredictor(p[i - 1], p[i - width], p[i - width - 1]); - const int diff3 = SDIFF(p[i], grad_pred); - bins[WEBP_FILTER_NONE][diff0] = 1; - bins[WEBP_FILTER_HORIZONTAL][diff1] = 1; - bins[WEBP_FILTER_VERTICAL][diff2] = 1; - bins[WEBP_FILTER_GRADIENT][diff3] = 1; - mean = (3 * mean + p[i] + 2) >> 2; - } - } - { - WEBP_FILTER_TYPE filter, best_filter = WEBP_FILTER_NONE; - int best_score = 0x7fffffff; - for (filter = WEBP_FILTER_NONE; filter < WEBP_FILTER_LAST; ++filter) { - int score = 0; - for (i = 0; i < SMAX; ++i) { - if (bins[filter][i] > 0) { - score += i; - } - } - if (score < best_score) { - best_score = score; - best_filter = filter; - } - } - return best_filter; - } -} - -#undef SMAX -#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 diff --git a/drivers/webpold/utils/filters.h b/drivers/webpold/utils/filters.h deleted file mode 100644 index db886be29a..0000000000 --- a/drivers/webpold/utils/filters.h +++ /dev/null @@ -1,54 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Spatial prediction using various filters -// -// Author: Urvang (urvang@google.com) - -#ifndef WEBP_UTILS_FILTERS_H_ -#define WEBP_UTILS_FILTERS_H_ - -#include "../types.h" - -#if defined(__cplusplus) || defined(c_plusplus) -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); - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_UTILS_FILTERS_H_ */ diff --git a/drivers/webpold/utils/huffman.c b/drivers/webpold/utils/huffman.c deleted file mode 100644 index 1cc1cfd355..0000000000 --- a/drivers/webpold/utils/huffman.c +++ /dev/null @@ -1,238 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Utilities for building and looking up Huffman trees. -// -// Author: Urvang Joshi (urvang@google.com) - -#include <assert.h> -#include <stdlib.h> -#include "./huffman.h" -#include "../utils/utils.h" -#include "../format_constants.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#define NON_EXISTENT_SYMBOL (-1) - -static void TreeNodeInit(HuffmanTreeNode* const node) { - node->children_ = -1; // means: 'unassigned so far' -} - -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; - } -} - -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; - - assert(code_lengths != NULL); - assert(code_lengths_size > 0); - assert(huff_codes != NULL); - - // Calculate max code length. - 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) { - 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; - } - } - - // Initialize the tree. Will fail for num_symbols = 0 - if (!TreeInit(tree, num_symbols)) return 0; - - // Build tree. - if (num_symbols == 1) { // Trivial case. - const int max_symbol = code_lengths_size; - if (root_symbol < 0 || root_symbol >= max_symbol) { - HuffmanTreeRelease(tree); - return 0; - } - return TreeAddSymbol(tree, root_symbol, 0, 0); - } else { // Normal case. - int ok = 0; - - // Get Huffman codes from the code lengths. - int* const codes = - (int*)WebPSafeMalloc((uint64_t)code_lengths_size, sizeof(*codes)); - if (codes == NULL) goto End; - - if (!HuffmanCodeLengthsToCodes(code_lengths, code_lengths_size, codes)) { - goto End; - } - - // Add symbols one-by-one. - for (symbol = 0; symbol < code_lengths_size; ++symbol) { - if (code_lengths[symbol] > 0) { - if (!TreeAddSymbol(tree, symbol, codes[symbol], code_lengths[symbol])) { - goto End; - } - } - } - ok = 1; - End: - free(codes); - ok = ok && IsFull(tree); - if (!ok) HuffmanTreeRelease(tree); - return ok; - } -} - -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; - } - } - } - ok = 1; - End: - ok = ok && IsFull(tree); - if (!ok) HuffmanTreeRelease(tree); - return ok; -} - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/utils/huffman.h b/drivers/webpold/utils/huffman.h deleted file mode 100644 index f16447e649..0000000000 --- a/drivers/webpold/utils/huffman.h +++ /dev/null @@ -1,78 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Utilities for building and looking up Huffman trees. -// -// Author: Urvang Joshi (urvang@google.com) - -#ifndef WEBP_UTILS_HUFFMAN_H_ -#define WEBP_UTILS_HUFFMAN_H_ - -#include <assert.h> -#include "../types.h" - -#if defined(__cplusplus) || defined(c_plusplus) -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; - -// 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 -}; - -// 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); -} - -// 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 nodes of the Huffman tree. -// Note: It does NOT free 'tree' itself. -void HuffmanTreeRelease(HuffmanTree* const tree); - -// 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) -} // extern "C" -#endif - -#endif // WEBP_UTILS_HUFFMAN_H_ diff --git a/drivers/webpold/utils/huffman_encode.c b/drivers/webpold/utils/huffman_encode.c deleted file mode 100644 index e172b10a85..0000000000 --- a/drivers/webpold/utils/huffman_encode.c +++ /dev/null @@ -1,439 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// -// Entropy encoding (Huffman) for webp lossless. - -#include <assert.h> -#include <stdlib.h> -#include <string.h> -#include "./huffman_encode.h" -#include "../utils/utils.h" -#include "../format_constants.h" - -// ----------------------------------------------------------------------------- -// Util function to optimize the symbol map for RLE coding - -// Heuristics for selecting the stride ranges to collapse. -static int ValuesShouldBeCollapsedToStrideAverage(int a, int b) { - return abs(a - b) < 4; -} - -// 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; - // 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. - } - if (counts[length - 1] != 0) { - // Now counts[0..length - 1] does not have trailing zeros. - break; - } - } - // 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]; - int stride = 0; - for (i = 0; i < length + 1; ++i) { - if (i == length || counts[i] != symbol) { - if ((symbol == 0 && stride >= 5) || - (symbol != 0 && stride >= 7)) { - int k; - for (k = 0; k < stride; ++k) { - good_for_rle[i - k - 1] = 1; - } - } - stride = 1; - if (i != length) { - symbol = counts[i]; - } - } else { - ++stride; - } - } - } - // 3) Let's replace those population counts that lead to more rle codes. - { - int stride = 0; - int limit = counts[0]; - int 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; - // The stride must end, collapse what we have, if we have enough (4). - int count = (sum + stride / 2) / stride; - if (count < 1) { - count = 1; - } - if (sum == 0) { - // Don't make an all zeros stride to be upgraded to ones. - count = 0; - } - for (k = 0; k < stride; ++k) { - // We don't want to change value at counts[i], - // that is already belonging to the next stride. Thus - 1. - counts[i - k - 1] = count; - } - } - stride = 0; - sum = 0; - if (i < length - 3) { - // All interesting strides have a count of at least 4, - // at least when non-zeros. - limit = (counts[i] + counts[i + 1] + - counts[i + 2] + counts[i + 3] + 2) / 4; - } else if (i < length) { - limit = counts[i]; - } else { - limit = 0; - } - } - ++stride; - if (i != length) { - sum += counts[i]; - if (stride >= 4) { - limit = (sum + stride / 2) / stride; - } - } - } - } - 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) { - const HuffmanTree* const t1 = (const HuffmanTree*)ptr1; - const HuffmanTree* const t2 = (const HuffmanTree*)ptr2; - if (t1->total_count_ > t2->total_count_) { - return -1; - } 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; - } -} - -static void SetBitDepths(const HuffmanTree* const tree, - const HuffmanTree* const pool, - uint8_t* const bit_depths, int level) { - if (tree->pool_index_left_ >= 0) { - SetBitDepths(&pool[tree->pool_index_left_], pool, bit_depths, level + 1); - SetBitDepths(&pool[tree->pool_index_right_], pool, bit_depths, level + 1); - } else { - bit_depths[tree->value_] = level; - } -} - -// Create an optimal Huffman tree. -// -// (data,length): population counts. -// tree_limit: maximum bit depth (inclusive) of the codes. -// bit_depths[]: how many bits are used for the symbol. -// -// Returns 0 when an error has occurred. -// -// The catch here is that the tree cannot be arbitrarily deep -// -// count_limit is the value that is to be faked as the minimum value -// and this minimum value is raised until the tree matches the -// maximum length requirement. -// -// This algorithm is not of excellent performance for very long data blocks, -// especially when population counts are longer than 2**tree_limit, but -// we are not planning to use this with extremely long blocks. -// -// See http://en.wikipedia.org/wiki/Huffman_coding -static int GenerateOptimalTree(const int* const histogram, int histogram_size, - int tree_depth_limit, - uint8_t* const bit_depths) { - int count_min; - HuffmanTree* tree_pool; - HuffmanTree* tree; - int tree_size_orig = 0; - int i; - - for (i = 0; i < histogram_size; ++i) { - if (histogram[i] != 0) { - ++tree_size_orig; - } - } - - // 3 * tree_size is enough to cover all the nodes representing a - // population and all the inserted nodes combining two existing nodes. - // The tree pool needs 2 * (tree_size_orig - 1) entities, and the - // tree needs exactly tree_size_orig entities. - tree = (HuffmanTree*)WebPSafeMalloc(3ULL * tree_size_orig, sizeof(*tree)); - if (tree == NULL) return 0; - tree_pool = tree + tree_size_orig; - - // For block sizes with less than 64k symbols we never need to do a - // second iteration of this loop. - // If we actually start running inside this loop a lot, we would perhaps - // be better off with the Katajainen algorithm. - assert(tree_size_orig <= (1 << (tree_depth_limit - 1))); - for (count_min = 1; ; count_min *= 2) { - int tree_size = tree_size_orig; - // We need to pack the Huffman tree in tree_depth_limit bits. - // So, we try by faking histogram entries to be at least 'count_min'. - int idx = 0; - int j; - for (j = 0; j < histogram_size; ++j) { - if (histogram[j] != 0) { - const int count = - (histogram[j] < count_min) ? count_min : histogram[j]; - tree[idx].total_count_ = count; - tree[idx].value_ = j; - tree[idx].pool_index_left_ = -1; - tree[idx].pool_index_right_ = -1; - ++idx; - } - } - - // Build the Huffman tree. - qsort(tree, tree_size, sizeof(*tree), CompareHuffmanTrees); - - if (tree_size > 1) { // Normal case. - int tree_pool_size = 0; - while (tree_size > 1) { // Finish when we have only one root. - int count; - tree_pool[tree_pool_size++] = tree[tree_size - 1]; - tree_pool[tree_pool_size++] = tree[tree_size - 2]; - count = tree_pool[tree_pool_size - 1].total_count_ + - tree_pool[tree_pool_size - 2].total_count_; - tree_size -= 2; - { - // Search for the insertion point. - int k; - for (k = 0; k < tree_size; ++k) { - if (tree[k].total_count_ <= count) { - break; - } - } - memmove(tree + (k + 1), tree + k, (tree_size - k) * sizeof(*tree)); - tree[k].total_count_ = count; - tree[k].value_ = -1; - - tree[k].pool_index_left_ = tree_pool_size - 1; - tree[k].pool_index_right_ = tree_pool_size - 2; - tree_size = tree_size + 1; - } - } - SetBitDepths(&tree[0], tree_pool, bit_depths, 0); - } else if (tree_size == 1) { // Trivial case: only one element. - bit_depths[tree[0].value_] = 1; - } - - { - // Test if this Huffman tree satisfies our 'tree_depth_limit' criteria. - int max_depth = bit_depths[0]; - for (j = 1; j < histogram_size; ++j) { - if (max_depth < bit_depths[j]) { - max_depth = bit_depths[j]; - } - } - if (max_depth <= tree_depth_limit) { - break; - } - } - } - free(tree); - return 1; -} - -// ----------------------------------------------------------------------------- -// Coding of the Huffman tree values - -static HuffmanTreeToken* CodeRepeatedValues(int repetitions, - HuffmanTreeToken* tokens, - int value, int prev_value) { - assert(value <= MAX_ALLOWED_CODE_LENGTH); - if (value != prev_value) { - tokens->code = value; - tokens->extra_bits = 0; - ++tokens; - --repetitions; - } - while (repetitions >= 1) { - if (repetitions < 3) { - int i; - for (i = 0; i < repetitions; ++i) { - tokens->code = value; - tokens->extra_bits = 0; - ++tokens; - } - break; - } else if (repetitions < 7) { - tokens->code = 16; - tokens->extra_bits = repetitions - 3; - ++tokens; - break; - } else { - tokens->code = 16; - tokens->extra_bits = 3; - ++tokens; - repetitions -= 6; - } - } - return tokens; -} - -static HuffmanTreeToken* CodeRepeatedZeros(int repetitions, - HuffmanTreeToken* tokens) { - while (repetitions >= 1) { - if (repetitions < 3) { - int i; - for (i = 0; i < repetitions; ++i) { - tokens->code = 0; // 0-value - tokens->extra_bits = 0; - ++tokens; - } - break; - } else if (repetitions < 11) { - tokens->code = 17; - tokens->extra_bits = repetitions - 3; - ++tokens; - break; - } else if (repetitions < 139) { - tokens->code = 18; - tokens->extra_bits = repetitions - 11; - ++tokens; - break; - } else { - tokens->code = 18; - tokens->extra_bits = 0x7f; // 138 repeated 0s - ++tokens; - repetitions -= 138; - } - } - return tokens; -} - -int VP8LCreateCompressedHuffmanTree(const HuffmanTreeCode* const tree, - HuffmanTreeToken* tokens, int max_tokens) { - HuffmanTreeToken* const starting_token = tokens; - HuffmanTreeToken* const ending_token = tokens + max_tokens; - const int depth_size = tree->num_symbols; - int prev_value = 8; // 8 is the initial value for rle. - int i = 0; - assert(tokens != NULL); - while (i < depth_size) { - const int value = tree->code_lengths[i]; - int k = i + 1; - int runs; - while (k < depth_size && tree->code_lengths[k] == value) ++k; - runs = k - i; - if (value == 0) { - tokens = CodeRepeatedZeros(runs, tokens); - } else { - tokens = CodeRepeatedValues(runs, tokens, value, prev_value); - prev_value = value; - } - i += runs; - assert(tokens <= ending_token); - } - (void)ending_token; // suppress 'unused variable' warning - return (int)(tokens - starting_token); -} - -// ----------------------------------------------------------------------------- - -// Pre-reversed 4-bit values. -static const uint8_t kReversedBits[16] = { - 0x0, 0x8, 0x4, 0xc, 0x2, 0xa, 0x6, 0xe, - 0x1, 0x9, 0x5, 0xd, 0x3, 0xb, 0x7, 0xf -}; - -static uint32_t ReverseBits(int num_bits, uint32_t bits) { - uint32_t retval = 0; - int i = 0; - while (i < num_bits) { - i += 4; - retval |= kReversedBits[bits & 0xf] << (MAX_ALLOWED_CODE_LENGTH + 1 - i); - bits >>= 4; - } - retval >>= (MAX_ALLOWED_CODE_LENGTH + 1 - num_bits); - return retval; -} - -// Get the actual bit values for a tree of bit depths. -static void ConvertBitDepthsToSymbols(HuffmanTreeCode* const tree) { - // 0 bit-depth means that the symbol does not exist. - int i; - int len; - uint32_t next_code[MAX_ALLOWED_CODE_LENGTH + 1]; - int depth_count[MAX_ALLOWED_CODE_LENGTH + 1] = { 0 }; - - assert(tree != NULL); - len = tree->num_symbols; - for (i = 0; i < len; ++i) { - const int code_length = tree->code_lengths[i]; - assert(code_length <= MAX_ALLOWED_CODE_LENGTH); - ++depth_count[code_length]; - } - depth_count[0] = 0; // ignore unused symbol - next_code[0] = 0; - { - uint32_t code = 0; - for (i = 1; i <= MAX_ALLOWED_CODE_LENGTH; ++i) { - code = (code + depth_count[i - 1]) << 1; - next_code[i] = code; - } - } - for (i = 0; i < len; ++i) { - const int code_length = tree->code_lengths[i]; - tree->codes[i] = ReverseBits(code_length, next_code[code_length]++); - } -} - -// ----------------------------------------------------------------------------- -// 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; - } - // Create the actual bit codes for the bit lengths. - ConvertBitDepthsToSymbols(tree); - return 1; -} diff --git a/drivers/webpold/utils/huffman_encode.h b/drivers/webpold/utils/huffman_encode.h deleted file mode 100644 index 7f4aedc102..0000000000 --- a/drivers/webpold/utils/huffman_encode.h +++ /dev/null @@ -1,47 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Author: Jyrki Alakuijala (jyrki@google.com) -// -// Entropy encoding (Huffman) for webp lossless - -#ifndef WEBP_UTILS_HUFFMAN_ENCODE_H_ -#define WEBP_UTILS_HUFFMAN_ENCODE_H_ - -#include "../types.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -// Struct for holding the tree header in coded form. -typedef struct { - uint8_t code; // value (0..15) or escape code (16,17,18) - uint8_t extra_bits; // extra bits for escape codes -} HuffmanTreeToken; - -// Struct to represent the tree codes (depth and bits array). -typedef struct { - int num_symbols; // Number of symbols. - uint8_t* code_lengths; // Code lengths of the symbols. - uint16_t* codes; // Symbol Codes. -} HuffmanTreeCode; - -// 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); - -#if defined(__cplusplus) || defined(c_plusplus) -} -#endif - -#endif // WEBP_UTILS_HUFFMAN_ENCODE_H_ diff --git a/drivers/webpold/utils/quant_levels.c b/drivers/webpold/utils/quant_levels.c deleted file mode 100644 index f6884392aa..0000000000 --- a/drivers/webpold/utils/quant_levels.c +++ /dev/null @@ -1,154 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Quantize levels for specified number of quantization-levels ([2, 256]). -// Min and max values are preserved (usual 0 and 255 for alpha plane). -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <assert.h> - -#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. -#define ERROR_THRESHOLD 1e-4 // MSE stopping criterion. - -// ----------------------------------------------------------------------------- -// Quantize levels. - -int QuantizeLevels(uint8_t* const data, int width, int height, - int num_levels, uint64_t* const sse) { - int freq[NUM_SYMBOLS] = { 0 }; - int q_level[NUM_SYMBOLS] = { 0 }; - double inv_q_level[NUM_SYMBOLS] = { 0 }; - int min_s = 255, max_s = 0; - const size_t data_size = height * width; - int i, num_levels_in, iter; - double last_err = 1.e38, err = 0.; - const double err_threshold = ERROR_THRESHOLD * data_size; - - if (data == NULL) { - return 0; - } - - if (width <= 0 || height <= 0) { - return 0; - } - - if (num_levels < 2 || num_levels > 256) { - return 0; - } - - { - size_t n; - num_levels_in = 0; - for (n = 0; n < data_size; ++n) { - num_levels_in += (freq[data[n]] == 0); - if (min_s > data[n]) min_s = data[n]; - if (max_s < data[n]) max_s = data[n]; - ++freq[data[n]]; - } - } - - if (num_levels_in <= num_levels) goto End; // nothing to do! - - // Start with uniformly spread centroids. - for (i = 0; i < num_levels; ++i) { - inv_q_level[i] = min_s + (double)(max_s - min_s) * i / (num_levels - 1); - } - - // Fixed values. Won't be changed. - q_level[min_s] = 0; - q_level[max_s] = num_levels - 1; - assert(inv_q_level[0] == min_s); - assert(inv_q_level[num_levels - 1] == max_s); - - // k-Means iterations. - for (iter = 0; iter < MAX_ITER; ++iter) { - double q_sum[NUM_SYMBOLS] = { 0 }; - double q_count[NUM_SYMBOLS] = { 0 }; - int s, slot = 0; - - // Assign classes to representatives. - for (s = min_s; s <= max_s; ++s) { - // Keep track of the nearest neighbour 'slot' - while (slot < num_levels - 1 && - 2 * s > inv_q_level[slot] + inv_q_level[slot + 1]) { - ++slot; - } - if (freq[s] > 0) { - q_sum[slot] += s * freq[s]; - q_count[slot] += freq[s]; - } - q_level[s] = slot; - } - - // Assign new representatives to classes. - if (num_levels > 2) { - for (slot = 1; slot < num_levels - 1; ++slot) { - const double count = q_count[slot]; - if (count > 0.) { - inv_q_level[slot] = q_sum[slot] / count; - } - } - } - - // Compute convergence error. - err = 0.; - for (s = min_s; s <= max_s; ++s) { - const double error = s - inv_q_level[q_level[s]]; - err += freq[s] * error * error; - } - - // Check for convergence: we stop as soon as the error is no - // longer improving. - if (last_err - err < err_threshold) break; - last_err = err; - } - - // Remap the alpha plane to quantized values. - { - // double->int rounding operation can be costly, so we do it - // once for all before remapping. We also perform the data[] -> slot - // mapping, while at it (avoid one indirection in the final loop). - uint8_t map[NUM_SYMBOLS]; - int s; - size_t n; - for (s = min_s; s <= max_s; ++s) { - const int slot = q_level[s]; - map[s] = (uint8_t)(inv_q_level[slot] + .5); - } - // Final pass. - for (n = 0; n < data_size; ++n) { - data[n] = map[data[n]]; - } - } - End: - // Store sum of squared error if needed. - if (sse != NULL) *sse = (uint64_t)err; - - 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 diff --git a/drivers/webpold/utils/quant_levels.h b/drivers/webpold/utils/quant_levels.h deleted file mode 100644 index 4f165fd230..0000000000 --- a/drivers/webpold/utils/quant_levels.h +++ /dev/null @@ -1,39 +0,0 @@ -// 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 quantization utility -// -// Author: Vikas Arora (vikasa@google.com) - -#ifndef WEBP_UTILS_QUANT_LEVELS_H_ -#define WEBP_UTILS_QUANT_LEVELS_H_ - -#include <stdlib.h> - -#include "../types.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -// Replace the input 'data' of size 'width'x'height' with 'num-levels' -// quantized values. If not NULL, 'sse' will contain the sum of squared error. -// Valid range for 'num_levels' is [2, 256]. -// Returns false in case of error (data is NULL, or parameters are invalid). -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) -} // extern "C" -#endif - -#endif /* WEBP_UTILS_QUANT_LEVELS_H_ */ diff --git a/drivers/webpold/utils/rescaler.c b/drivers/webpold/utils/rescaler.c deleted file mode 100644 index 9825dcbc5f..0000000000 --- a/drivers/webpold/utils/rescaler.c +++ /dev/null @@ -1,152 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Rescaling functions -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <assert.h> -#include <stdlib.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) { - wrk->x_expand = (src_width < dst_width); - wrk->src_width = src_width; - wrk->src_height = src_height; - wrk->dst_width = dst_width; - wrk->dst_height = dst_height; - 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_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); - wrk->irow = work; - wrk->frow = work + num_channels * dst_width; -} - -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); - } - } - } 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; - } - } - // 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]; - } -} - -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 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); - } - src += src_stride; - ++total_imported; - wrk->y_accum -= wrk->y_sub; - } - return total_imported; -} - -int WebPRescalerExport(WebPRescaler* const rescaler) { - int total_exported = 0; - while (WebPRescalerHasPendingOutput(rescaler)) { - WebPRescalerExportRow(rescaler); - ++total_exported; - } - return total_exported; -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/utils/rescaler.h b/drivers/webpold/utils/rescaler.h deleted file mode 100644 index 9c9133d19b..0000000000 --- a/drivers/webpold/utils/rescaler.h +++ /dev/null @@ -1,76 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Rescaling functions -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_UTILS_RESCALER_H_ -#define WEBP_UTILS_RESCALER_H_ - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#include "../types.h" - -// Structure used for on-the-fly rescaling -typedef struct { - int x_expand; // true if we're expanding in the x 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. - 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 src_width, src_height; // source dimensions - int dst_width, dst_height; // destination dimensions - uint8_t* dst; - int dst_stride; - int32_t* irow, *frow; // work buffer -} WebPRescaler; - -// Initialize a rescaler given scratch area 'work' and dimensions of src & dst. -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); - -// 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); - -// 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. -static WEBP_INLINE -int WebPRescalerHasPendingOutput(const WebPRescaler* const rescaler) { - return (rescaler->y_accum <= 0); -} - -// 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); - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_UTILS_RESCALER_H_ */ diff --git a/drivers/webpold/utils/thread.c b/drivers/webpold/utils/thread.c deleted file mode 100644 index ce89cf9dc7..0000000000 --- a/drivers/webpold/utils/thread.c +++ /dev/null @@ -1,247 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// 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 - -#ifdef WEBP_USE_THREAD - -#if defined(_WIN32) - -//------------------------------------------------------------------------------ -// simplistic pthread emulation layer - -#include <process.h> - -// _beginthreadex requires __stdcall -#define THREADFN unsigned int __stdcall -#define THREAD_RETURN(val) (unsigned int)((DWORD_PTR)val) - -static int pthread_create(pthread_t* const thread, const void* attr, - unsigned int (__stdcall *start)(void*), void* arg) { - (void)attr; - *thread = (pthread_t)_beginthreadex(NULL, /* void *security */ - 0, /* unsigned stack_size */ - start, - arg, - 0, /* unsigned initflag */ - NULL); /* unsigned *thrdaddr */ - if (*thread == NULL) return 1; - SetThreadPriority(*thread, THREAD_PRIORITY_ABOVE_NORMAL); - return 0; -} - -static int pthread_join(pthread_t thread, void** value_ptr) { - (void)value_ptr; - return (WaitForSingleObject(thread, INFINITE) != WAIT_OBJECT_0 || - CloseHandle(thread) == 0); -} - -// Mutex -static int pthread_mutex_init(pthread_mutex_t* const mutex, void* mutexattr) { - (void)mutexattr; - InitializeCriticalSection(mutex); - return 0; -} - -static int pthread_mutex_lock(pthread_mutex_t* const mutex) { - EnterCriticalSection(mutex); - return 0; -} - -static int pthread_mutex_unlock(pthread_mutex_t* const mutex) { - LeaveCriticalSection(mutex); - return 0; -} - -static int pthread_mutex_destroy(pthread_mutex_t* const mutex) { - DeleteCriticalSection(mutex); - return 0; -} - -// Condition -static int pthread_cond_destroy(pthread_cond_t* const condition) { - int ok = 1; - ok &= (CloseHandle(condition->waiting_sem_) != 0); - ok &= (CloseHandle(condition->received_sem_) != 0); - ok &= (CloseHandle(condition->signal_event_) != 0); - return !ok; -} - -static int pthread_cond_init(pthread_cond_t* const condition, void* cond_attr) { - (void)cond_attr; - condition->waiting_sem_ = CreateSemaphore(NULL, 0, 1, NULL); - condition->received_sem_ = CreateSemaphore(NULL, 0, 1, NULL); - condition->signal_event_ = CreateEvent(NULL, FALSE, FALSE, NULL); - if (condition->waiting_sem_ == NULL || - condition->received_sem_ == NULL || - condition->signal_event_ == NULL) { - pthread_cond_destroy(condition); - return 1; - } - return 0; -} - -static int pthread_cond_signal(pthread_cond_t* const condition) { - int ok = 1; - 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_); - // wait until the event is consumed so the signaler cannot consume - // the event via its own pthread_cond_wait. - ok &= (WaitForSingleObject(condition->received_sem_, INFINITE) != - WAIT_OBJECT_0); - } - return !ok; -} - -static int pthread_cond_wait(pthread_cond_t* const condition, - pthread_mutex_t* const mutex) { - int ok; - // note that there is a consumer available so the signal isn't dropped in - // pthread_cond_signal - if (!ReleaseSemaphore(condition->waiting_sem_, 1, NULL)) - return 1; - // now unlock the mutex so pthread_cond_signal may be issued - pthread_mutex_unlock(mutex); - ok = (WaitForSingleObject(condition->signal_event_, INFINITE) == - WAIT_OBJECT_0); - ok &= ReleaseSemaphore(condition->received_sem_, 1, NULL); - pthread_mutex_lock(mutex); - return !ok; -} - -#else // _WIN32 -# define THREADFN void* -# define THREAD_RETURN(val) val -#endif - -//------------------------------------------------------------------------------ - -static THREADFN WebPWorkerThreadLoop(void *ptr) { // thread loop - WebPWorker* const worker = (WebPWorker*)ptr; - int done = 0; - while (!done) { - pthread_mutex_lock(&worker->mutex_); - while (worker->status_ == OK) { // wait in idling mode - pthread_cond_wait(&worker->condition_, &worker->mutex_); - } - if (worker->status_ == WORK) { - if (worker->hook) { - worker->had_error |= !worker->hook(worker->data1, worker->data2); - } - 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_); - } - 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; - - pthread_mutex_lock(&worker->mutex_); - // wait for the worker to finish - while (worker->status_ != OK) { - pthread_cond_wait(&worker->condition_, &worker->mutex_); - } - // 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_); -} - -#endif - -//------------------------------------------------------------------------------ - -void WebPWorkerInit(WebPWorker* const worker) { - memset(worker, 0, sizeof(*worker)); - worker->status_ = NOT_OK; -} - -int WebPWorkerSync(WebPWorker* const worker) { -#ifdef WEBP_USE_THREAD - WebPWorkerChangeState(worker, OK); -#endif - assert(worker->status_ <= OK); - return !worker->had_error; -} - -int WebPWorkerReset(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)) { - return 0; - } - pthread_mutex_lock(&worker->mutex_); - ok = !pthread_create(&worker->thread_, NULL, WebPWorkerThreadLoop, worker); - if (ok) worker->status_ = OK; - pthread_mutex_unlock(&worker->mutex_); -#else - worker->status_ = OK; -#endif - } else if (worker->status_ > OK) { - ok = WebPWorkerSync(worker); - } - assert(!ok || (worker->status_ == OK)); - return ok; -} - -void WebPWorkerLaunch(WebPWorker* const worker) { -#ifdef WEBP_USE_THREAD - WebPWorkerChangeState(worker, WORK); -#else - if (worker->hook) - worker->had_error |= !worker->hook(worker->data1, worker->data2); -#endif -} - -void WebPWorkerEnd(WebPWorker* const worker) { - if (worker->status_ >= OK) { -#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 - } - assert(worker->status_ == NOT_OK); -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/utils/thread.h b/drivers/webpold/utils/thread.h deleted file mode 100644 index 3191890b76..0000000000 --- a/drivers/webpold/utils/thread.h +++ /dev/null @@ -1,86 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Multi-threaded worker -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_UTILS_THREAD_H_ -#define WEBP_UTILS_THREAD_H_ - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -#if WEBP_USE_THREAD - -#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 */ - -// State of the worker thread object -typedef enum { - NOT_OK = 0, // object is unusable - OK, // ready to work - WORK // busy finishing the current task -} WebPWorkerStatus; - -// Function to be called by the worker thread. Takes two opaque pointers as -// 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 -typedef struct { -#if WEBP_USE_THREAD - pthread_mutex_t mutex_; - pthread_cond_t condition_; - pthread_t thread_; -#endif - WebPWorkerStatus status_; - WebPWorkerHook hook; // hook to call - void* data1; // first argument passed to 'hook' - void* data2; // second argument passed to 'hook' - 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); - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_UTILS_THREAD_H_ */ diff --git a/drivers/webpold/utils/utils.c b/drivers/webpold/utils/utils.c deleted file mode 100644 index 673b7e284c..0000000000 --- a/drivers/webpold/utils/utils.c +++ /dev/null @@ -1,44 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Misc. common utility functions -// -// Author: Skal (pascal.massimino@gmail.com) - -#include <stdlib.h> -#include "./utils.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Checked memory allocation - -static int CheckSizeArguments(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; - return 1; -} - -void* WebPSafeMalloc(uint64_t nmemb, size_t size) { - if (!CheckSizeArguments(nmemb, size)) return NULL; - return malloc((size_t)(nmemb * size)); -} - -void* WebPSafeCalloc(uint64_t nmemb, size_t size) { - if (!CheckSizeArguments(nmemb, size)) return NULL; - return calloc((size_t)nmemb, size); -} - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif diff --git a/drivers/webpold/utils/utils.h b/drivers/webpold/utils/utils.h deleted file mode 100644 index 316ac90612..0000000000 --- a/drivers/webpold/utils/utils.h +++ /dev/null @@ -1,44 +0,0 @@ -// 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/ -// ----------------------------------------------------------------------------- -// -// Misc. common utility functions -// -// Author: Skal (pascal.massimino@gmail.com) - -#ifndef WEBP_UTILS_UTILS_H_ -#define WEBP_UTILS_UTILS_H_ - -#include "../types.h" - -#if defined(__cplusplus) || defined(c_plusplus) -extern "C" { -#endif - -//------------------------------------------------------------------------------ -// Memory allocation - -// This is the maximum memory amount that libwebp will ever try to allocate. -#define WEBP_MAX_ALLOCABLE_MEMORY (1ULL << 40) - -// size-checking safe malloc/calloc: verify that the requested size is not too -// large, or return NULL. You don't need to call these for constructs like -// malloc(sizeof(foo)), but only if there's picture-dependent size involved -// 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); -// 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); - -//------------------------------------------------------------------------------ - -#if defined(__cplusplus) || defined(c_plusplus) -} // extern "C" -#endif - -#endif /* WEBP_UTILS_UTILS_H_ */ |