From a4fc74477002ae5c8e71f9bf92b9e8b265918ccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Tue, 26 May 2026 21:26:55 -0700 Subject: [PATCH] Added webp support. (#124) --- 3rdparty/simplewebp/LICENSE.md | 10 + 3rdparty/simplewebp/simplewebp.h | 5519 ++++++++++++++++++++++++++++++ src/config.h | 6 + src/image_decode.cpp | 157 +- 4 files changed, 5683 insertions(+), 9 deletions(-) create mode 100644 3rdparty/simplewebp/LICENSE.md create mode 100644 3rdparty/simplewebp/simplewebp.h diff --git a/3rdparty/simplewebp/LICENSE.md b/3rdparty/simplewebp/LICENSE.md new file mode 100644 index 0000000..6216a72 --- /dev/null +++ b/3rdparty/simplewebp/LICENSE.md @@ -0,0 +1,10 @@ +Copyright (c) 2010 Google Inc., 2023 Miku AuahDark +_All rights reserved._ + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/simplewebp/simplewebp.h b/3rdparty/simplewebp/simplewebp.h new file mode 100644 index 0000000..ba396f5 --- /dev/null +++ b/3rdparty/simplewebp/simplewebp.h @@ -0,0 +1,5519 @@ +/** + * @file simplewebp.h + * @author Google Inc., Miku AuahDark + * @brief A simple WebP decoder. + * @version 20251007 + * See license at the bottom of the file. + */ + +#ifndef _SIMPLE_WEBP_H_ +#define _SIMPLE_WEBP_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#if defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901) +#include + +typedef uint8_t simplewebp_u8; +typedef int8_t simplewebp_i8; +typedef uint16_t simplewebp_u16; +typedef int16_t simplewebp_i16; +typedef uint32_t simplewebp_u32; +typedef int32_t simplewebp_i32; + +#ifdef __cplusplus +typedef bool simplewebp_bool; +#else +#include +typedef _Bool simplewebp_bool; +#endif + +#else /* Yeah this must be good enough */ +typedef char simplewebp_bool; +typedef unsigned char simplewebp_u8; +typedef signed char simplewebp_i8; +typedef unsigned short simplewebp_u16; +typedef short simplewebp_i16; +typedef unsigned int simplewebp_u32; +typedef int simplewebp_i32; +#endif + +#define SIMPLEWEBP_VERSION 20251007 + +/** + * @brief SimpleWebP "input stream". + * + * For user-defined "input stream", populate the struct accordingly. + * This struct can be allocated on stack, and is assumed so. + */ +typedef struct simplewebp_input +{ + /** + * @brief Function-specific userdata. + */ + void *userdata; + + /** + * @brief Function to close **and** deallocate `userdata`. + * @param userdata Function-specific userdata. + */ + void (*close)(void *userdata); + + /** + * @brief Function to read from "input stream" + * @param size Amount of bytes to read. + * @param dest Output buffer. + * @param userdata Function-specific userdata. + * @return Amount of bytes read. + */ + size_t (*read)(size_t size, void *dest, void *userdata); + + /** + * @brief Function to seek "input stream". Stream must support seeking! + * @param pos New position of the stream, based on the beginning of the file. + * @param userdata Function-specific userdata. + * @return Non-zero on success, zero on failure. + */ + simplewebp_bool (*seek)(size_t pos, void *userdata); + + /** + * @brief Function to get current "input stream" position. Stream must support this! + * @return Stream position relative to the beginning. + */ + size_t (*tell)(void *userdata); +} simplewebp_input; + +/** + * @brief SimpleWebP allocator structure. + * + * SimpleWebP functions that needs an allocation need to pass this structure. + * This struct can be allocated on stack. + */ +typedef struct simplewebp_allocator +{ + /** + * @brief Allocate block of memory. + * @param userdata Allocator-specific data. + * @param size Amount of bytes to allocate. + * @return Pointer to tbe memory block, or `NULL` on failure. + */ + void *(*alloc)(void *userdata, size_t size); + + /** + * @brief Free allocated memory. + * @param userdata Allocator-specific data. + * @param mem Valid pointer to the memory allocated by `alloc` function. + * @note Passing `NULL` is undefined behavior, although most implementation treat it as no-op. + */ + void (*free)(void *userdata, void *mem); + + /** + * @brief Allocator-specific data. + * + * If your allocator is stateless, simply pass `NULL`. + */ + void *userdata; +} simplewebp_allocator; + +/** + * @brief SimpleWebP opaque handle. + */ +typedef struct simplewebp simplewebp; + +typedef enum simplewebp_error +{ + /** No error */ + SIMPLEWEBP_NO_ERROR = 0, + /** Failed to allocate memory */ + SIMPLEWEBP_ALLOC_ERROR, + /** Input read error (such as EOF) */ + SIMPLEWEBP_IO_ERROR, + /** Not a WebP image */ + SIMPLEWEBP_NOT_WEBP_ERROR, + /** WebP image corrupt */ + SIMPLEWEBP_CORRUPT_ERROR, + /** WebP image unsupported */ + SIMPLEWEBP_UNSUPPORTED_ERROR, + /** WebP image is lossless */ + SIMPLEWEBP_IS_LOSSLESS_ERROR +} simplewebp_error; + +/** + * @brief Get runtime SimpleWebP version. + * In most cases, this should be equivalent to `SIMPLEWEBP_VERSION` + * + * @return SimpleWebP numeric version. + */ +size_t simplewebp_version(void); +/** + * @brief Get error message associated by SimpleWebP error code. + * + * @param error Error code. + * @return Error message as null-terminated string. + */ +const char *simplewebp_get_error_text(simplewebp_error error); + +/** + * @brief Initialize `simplewebp_input` structure to load from memory. + * + * @param data Pointer to the WebP-encoded image in memory. + * @param size Size of `data` in bytes. + * @param out Destination structure. + * @param allocator Allocator structure, or `NULL` to use C default. + * @return Error codes. + */ +simplewebp_error simplewebp_input_from_memory(void *data, size_t size, simplewebp_input *out, const simplewebp_allocator *allocator); +/** + * @brief Close `simplewebp_input`, freeing the `userdata` and setting its `userdata` to `NULL`. + * @param input Pointer to the `simplewebp_input` structure. + * @note Application should not use the same `simplewebp_input` unless re-initialized again. + */ +void simplewebp_close_input(simplewebp_input *input); + +/** + * @brief Load a WebP image. + * + * @param input Pointer to the `simplewebp_input` structure. This function will take the ownership of the `simplewebp_input`. + * @param allocator Allocator structure, or `NULL` to use C default. + * @param out Pointer to the `simplewebp` opaque handle. + * @return Error codes. + */ +simplewebp_error simplewebp_load(simplewebp_input *input, const simplewebp_allocator *allocator, simplewebp **out); + +/** + * @brief Load a WebP image from memory. + * + * @param data Pointer to the WebP-encoded image in memory. + * @param size Size of `data` in bytes. + * @param allocator Allocator structure, or `NULL` to use C default. + * @param out Pointer to the `simplewebp` opaque handle. + * @return Error codes. + */ +simplewebp_error simplewebp_load_from_memory(void *data, size_t size, const simplewebp_allocator *allocator, simplewebp **out); + +/** + * @brief Frees and unload associated memory and closes the "input stream". + * @param simplewebp `simplewebp` opaque handle. + */ +void simplewebp_unload(simplewebp *simplewebp); + +/** + * @brief Get WebP image dimensions. + * @param simplewebp `simplewebp` opaque handle. + * @param width Where to store the image width. + * @param height Where to store the image height. + */ +void simplewebp_get_dimensions(simplewebp *simplewebp, size_t *width, size_t *height); + +/** + * @brief Check if the WebP image is lossless or lossy. + * @param simplewebp `simplewebp` opaque handle. + * @return 1 if lossless, 0 if lossy. + */ +simplewebp_bool simplewebp_is_lossless(simplewebp *simplewebp); + +/** + * @brief Decode WebP image to raw RGBA8 pixels data. + * @param simplewebp `simplewebp` opaque handle. + * @param buffer Block of memory with size of `width * height * 4` bytes. This is where the RGBA is stored. + * @param settings Ignored. Set this to `NULL`. + * @return Error codes. + */ +simplewebp_error simplewebp_decode(simplewebp *simplewebp, void *buffer, void *settings); + +/** + * @brief Decode WebP image to raw planar YUVA420 pixels data. Can only be used on lossy WebP image. + * @param simplewebp `simplewebp` opaque handle. + * @param y_buffer Block of memory with size of `width * height` bytes. + * @param u_buffer Block of memory with size of `((width + 1) / 2) * ((height + 1) / 2)` bytes. + * @param v_buffer Block of memory with size of `((width + 1) / 2) * ((height + 1) / 2)` bytes. + * @param a_buffer Block of memory with size of `width * height` bytes. + * @param settings Ignored. Set this to `NULL`. + * @return simplewebp_error Error codes. + */ +simplewebp_error simplewebp_decode_yuva(simplewebp *simplewebp, void *y_buffer, void *u_buffer, void *v_buffer, void *a_buffer, void *settings); + +#ifndef SIMPLEWEBP_DISABLE_STDIO +#include + +/** + * @brief Initialize `simplewebp_input` structure to load from `FILE*`. + * + * @param file C stdio `FILE*`. + * @param out Destination structure. + * @return Error codes. + */ +simplewebp_error simplewebp_input_from_file(FILE *file, simplewebp_input *out); + +/** + * @brief Initialize `simplewebp_input` structure to load from file path using `fopen`. + * + * @param filename Path to file. + * @param out Destination structure. + * @return Error codes. + */ +simplewebp_error simplewebp_input_from_filename(const char *filename, simplewebp_input *out); + +/** + * @brief Load a WebP image from `FILE*`. + * + * @param file C stdio `FILE*`. + * @param allocator Allocator structure, or `NULL` to use C default. + * @param out Pointer to the `simplewebp` opaque handle. + * @return Error codes. + */ +simplewebp_error simplewebp_load_from_file(FILE *file, const simplewebp_allocator *allocator, simplewebp **out); + +/** + * @brief Load a WebP image from file path using `fopen`. + * + * @param filename Path to file. + * @param allocator Allocator structure, or `NULL` to use C default. + * @param out Pointer to the `simplewebp` opaque handle. + * @return Error codes. + */ +simplewebp_error simplewebp_load_from_filename(const char *filename, const simplewebp_allocator *allocator, simplewebp **out); +#endif /* SIMPLEWEBP_DISABLE_STDIO */ + + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* _SIMPLE_WEBP_H_ */ + +#ifdef SIMPLEWEBP_IMPLEMENTATION + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include + +struct swebp__picture_header +{ + simplewebp_u16 width, height; + simplewebp_u8 xscale, yscale, colorspace, clamp_type; +}; + +struct swebp__finfo +{ + simplewebp_u8 limit, ilevel, inner, hev_thresh; +}; + +struct swebp__topsmp +{ + simplewebp_u8 y[16], u[8], v[8]; +}; + +struct swebp__mblock +{ + simplewebp_u8 nz, nz_dc; +}; + +struct swebp__mblockdata +{ + simplewebp_i16 coeffs[384]; + simplewebp_u32 nonzero_y, nonzero_uv; + simplewebp_u8 imodes[16], is_i4x4, uvmode, dither, skip, segment; +}; + +typedef simplewebp_u8 swebp__probarray[11]; + +struct swebp__bandprobas +{ + swebp__probarray probas[3]; +}; + +struct swebp__proba +{ + simplewebp_u8 segments[3]; + struct swebp__bandprobas bands[4][8]; + const struct swebp__bandprobas *bands_ptr[4][17]; +}; + +struct swebp__frame_header +{ + simplewebp_u8 key_frame, profile, show; + simplewebp_u32 partition_length; +}; + +struct swebp__filter_header +{ + simplewebp_u8 simple, level, sharpness, use_lf_delta; + simplewebp_i32 ref_lf_delta[4], mode_lf_delta[4]; +}; + +struct swebp__segment_header +{ + simplewebp_u8 use_segment, update_map, absolute_delta; + simplewebp_i8 quantizer[4], filter_strength[4]; +}; + +/* Bit reader and boolean decoder */ +struct swebp__bdec +{ + const simplewebp_u8 *buf, *buf_end, *buf_max; + simplewebp_u32 value; + simplewebp_u8 range, eof; + simplewebp_i8 bits; +}; + +typedef simplewebp_i32 swebp__quant_t[2]; +struct swebp__quantmat +{ + swebp__quant_t y1_mat, y2_mat, uv_mat; + simplewebp_i32 uv_quant, dither; +}; + +struct swebp__alpha +{ + simplewebp_u8 filter_method; + simplewebp_bool is_lossless_compressed; +}; + +struct swebp__vp8 +{ + simplewebp_u8 ready; + + struct swebp__bdec br; + struct swebp__frame_header frame_header; + struct swebp__picture_header picture_header; + struct swebp__filter_header filter_header; + struct swebp__segment_header segment_header; + + simplewebp_i32 mb_w, mb_h; + simplewebp_i32 tl_mb_x, tl_mb_y; + simplewebp_i32 br_mb_x, br_mb_y; + + simplewebp_u32 nparts_minus_1; + struct swebp__bdec parts[8]; + + struct swebp__quantmat dqm[4]; + + struct swebp__proba proba; + simplewebp_u8 use_skip_proba, skip_proba; + + simplewebp_u8 *intra_t, intra_l[4]; + + struct swebp__topsmp *yuv_t; + + struct swebp__mblock *mb_info; + struct swebp__finfo *f_info; + simplewebp_u8 *yuv_b; + + + simplewebp_u8 *cache_y, *cache_u, *cache_v; + simplewebp_i32 cache_y_stride, cache_uv_stride; + + + simplewebp_u8* mem; + size_t mem_size; + + simplewebp_i32 mb_x, mb_y; + struct swebp__mblockdata *mb_data; + + simplewebp_i8 filter_type; + struct swebp__finfo fstrengths[4][2]; + + simplewebp_u8 *alpha_plane; +}; + +struct swebp__vp8l_bdec +{ + simplewebp_u8 *buf; + size_t len, bit_pos; + simplewebp_u8 eos; +}; + +struct swebp__vp8l_decoder +{ + simplewebp_u32 width, height; + simplewebp_bool has_alpha; +}; + +union swebp__decoder_list +{ + struct swebp__vp8 vp8; + struct swebp__vp8l_decoder vp8l; +}; + +struct swebp__yuvdst +{ + simplewebp_u8 *y, *u, *v, *a; +}; + +struct swebp__pixel +{ + simplewebp_u8 r, g, b, a; +}; + +struct swebp__chroma +{ + simplewebp_u8 u, v; +}; + +struct simplewebp +{ + simplewebp_input input, riff_input, vp8_input, vp8l_input, alph_input; + simplewebp_allocator allocator; + struct swebp__alpha alpha_decoder; + + simplewebp_u8 webp_type; /* Simple lossy (0), Lossless (1) */ + union swebp__decoder_list decoder; +}; + +struct simplewebp_memoryinput_data +{ + simplewebp_allocator allocator; + void *data; + size_t size, pos; +}; + +static void *swebp__malloc(void *_unused, size_t size) +{ + return malloc(size); +} + +static void swebp__free(void *_unused, void *mem) +{ + free(mem); +} + +static void *swebp__alloc(simplewebp *simplewebp, size_t size) +{ + return simplewebp->allocator.alloc(simplewebp->allocator.userdata, size); +} + +static void swebp__dealloc(simplewebp *simplewebp, void *ptr) +{ + if (ptr) + simplewebp->allocator.free(simplewebp->allocator.userdata, ptr); +} + +static simplewebp_allocator swebp__default_allocator = {swebp__malloc, swebp__free, NULL}; + +size_t simplewebp_version(void) +{ + return SIMPLEWEBP_VERSION; +} + +const char *simplewebp_get_error_text(simplewebp_error error) +{ + switch (error) + { + case SIMPLEWEBP_NO_ERROR: + return "No error"; + case SIMPLEWEBP_ALLOC_ERROR: + return "Failed to allocate memory"; + case SIMPLEWEBP_IO_ERROR: + return "Input read error (such as EOF)"; + case SIMPLEWEBP_NOT_WEBP_ERROR: + return "Not a WebP image"; + case SIMPLEWEBP_CORRUPT_ERROR: + return "WebP image corrupt"; + case SIMPLEWEBP_UNSUPPORTED_ERROR: + return "WebP image unsupported"; + case SIMPLEWEBP_IS_LOSSLESS_ERROR: + return "WebP image is lossless"; + default: + return "Unknown error"; + } +} + +static size_t swebp__memoryinput_read(size_t size, void *dest, void *userdata) +{ + struct simplewebp_memoryinput_data *input_data; + size_t nextpos, readed; + + input_data = (struct simplewebp_memoryinput_data *) userdata; + nextpos = input_data->pos + size; + + if (nextpos >= input_data->size) + { + readed = input_data->size - input_data->pos; + nextpos = input_data->size; + } + else + readed = size; + + if (readed > 0) + { + memcpy(dest, ((char *) input_data->data) + input_data->pos, readed); + input_data->pos = nextpos; + } + + return readed; +} + +static simplewebp_bool swebp__memoryinput_seek(size_t pos, void *userdata) +{ + struct simplewebp_memoryinput_data *input_data = (struct simplewebp_memoryinput_data *) userdata; + + if (pos >= input_data->size) + input_data->pos = input_data->size; + else + input_data->pos = pos; + + return 1; +} + +static void swebp__memoryinput_close(void *userdata) +{ + struct simplewebp_memoryinput_data *input_data = (struct simplewebp_memoryinput_data *) userdata; + input_data->allocator.free(input_data->allocator.userdata, input_data); +} + +static size_t swebp__memoryinput_tell(void *userdata) +{ + struct simplewebp_memoryinput_data *input_data = (struct simplewebp_memoryinput_data *) userdata; + return input_data->pos; +} + +simplewebp_error simplewebp_input_from_memory(void *data, size_t size, simplewebp_input *out, const simplewebp_allocator *allocator) +{ + struct simplewebp_memoryinput_data *input_data; + + if (allocator == NULL) + allocator = &swebp__default_allocator; + + input_data = (struct simplewebp_memoryinput_data *) allocator->alloc( + allocator->userdata, + sizeof(struct simplewebp_memoryinput_data) + ); + if (input_data == NULL) + return SIMPLEWEBP_ALLOC_ERROR; + + input_data->allocator = *allocator; + input_data->data = data; + input_data->size = size; + input_data->pos = 0; + out->userdata = input_data; + out->read = swebp__memoryinput_read; + out->seek = swebp__memoryinput_seek; + out->tell = swebp__memoryinput_tell; + out->close = swebp__memoryinput_close; + return SIMPLEWEBP_NO_ERROR; +} + +struct simplewebp_input_proxy +{ + simplewebp_input input; + simplewebp_allocator allocator; + + size_t start, length; +}; + +static simplewebp_bool swebp__seek(size_t pos, simplewebp_input *input) +{ + return input->seek(pos, input->userdata); +} + +static size_t swebp__read(size_t size, void *dest, simplewebp_input *input) +{ + return input->read(size, dest, input->userdata); +} + +static simplewebp_bool swebp__read2(size_t size, void *dest, simplewebp_input *input) +{ + return swebp__read(size, dest, input) == size; +} + +static size_t swebp__tell(simplewebp_input *input) +{ + return input->tell(input->userdata); +} + +static size_t swebp__proxy_tell(void *userdata) +{ + struct simplewebp_input_proxy *proxy; + size_t pos; + + proxy = (struct simplewebp_input_proxy *) userdata; + pos = swebp__tell(&proxy->input); + + if (pos < proxy->start) + { + swebp__seek(proxy->start, &proxy->input); + pos = proxy->start; + } + + return pos - proxy->start; +} + +static size_t swebp__proxy_read(size_t size, void *dest, void *userdata) +{ + struct simplewebp_input_proxy *proxy; + size_t pos, nextpos, readed; + + proxy = (struct simplewebp_input_proxy *) userdata; + pos = swebp__proxy_tell(userdata); + + nextpos = pos + size; + + if (nextpos >= proxy->length) + { + readed = size - (proxy->length - nextpos); + nextpos = proxy->length; + } + else + readed = size; + + if (readed > 0) + readed = swebp__read(readed, dest, &proxy->input); + + return readed; +} + +static simplewebp_bool swebp__proxy_seek(size_t pos, void *userdata) +{ + struct simplewebp_input_proxy *proxy = (struct simplewebp_input_proxy *) userdata; + + if (pos > proxy->length) + pos = proxy->length; + + return swebp__seek(pos + proxy->start, &proxy->input); +} + +static void swebp__proxy_close(void *userdata) +{ + struct simplewebp_input_proxy *proxy = (struct simplewebp_input_proxy *) userdata; + proxy->allocator.free(proxy->allocator.userdata, proxy); +} + +static size_t swebp__proxy_size(void *userdata) +{ + struct simplewebp_input_proxy *proxy = (struct simplewebp_input_proxy *) userdata; + return proxy->length; +} + +static simplewebp_error swebp__proxy_create( + const simplewebp_allocator *allocator, + simplewebp_input *input, + simplewebp_input *out, + size_t start, + size_t length +) +{ + struct simplewebp_input_proxy *proxy = (struct simplewebp_input_proxy *) allocator->alloc( + allocator->userdata, + sizeof(struct simplewebp_input_proxy) + ); + if (proxy == NULL) + return SIMPLEWEBP_ALLOC_ERROR; + + proxy->input = *input; + proxy->allocator = *allocator; + proxy->start = start; + proxy->length = length; + out->userdata = proxy; + out->read = swebp__proxy_read; + out->seek = swebp__proxy_seek; + out->tell = swebp__proxy_tell; + out->close = swebp__proxy_close; + return SIMPLEWEBP_NO_ERROR; +} + +void simplewebp_close_input(simplewebp_input *input) +{ + input->close(input->userdata); + input->userdata = NULL; +} + +static simplewebp_u32 swebp__to_uint32(const simplewebp_u8 *buf) +{ + return buf[0] | (((simplewebp_u32) buf[1]) << 8) | (((simplewebp_u32) buf[2]) << 16) | (((simplewebp_u32) buf[3]) << 24); +} + +static simplewebp_error swebp__get_input_chunk_4cc(const simplewebp_allocator *allocator, simplewebp_input *input, simplewebp_input *outproxy, void *fourcc, size_t *chunk_size) +{ + simplewebp_u8 size[4]; + + if (!swebp__read2(4, fourcc, input)) + return SIMPLEWEBP_IO_ERROR; + if (!swebp__read2(4, size, input)) + return SIMPLEWEBP_IO_ERROR; + + *chunk_size = swebp__to_uint32(size); + return swebp__proxy_create(allocator, input, outproxy, swebp__tell(input), *chunk_size); +} + +static simplewebp_u16 swebp__to_uint16(const simplewebp_u8 *buf) +{ + return buf[0] | (((simplewebp_u16) buf[1]) << 8); +} + +static simplewebp_error swebp__load_lossy(simplewebp_input *vp8_input, simplewebp *result) +{ + simplewebp_u8 temp[8], profile; + simplewebp_u32 frametag, partition_size; + simplewebp_u16 width, height; + + if (!swebp__seek(0, vp8_input)) + return SIMPLEWEBP_IO_ERROR; + + if (!swebp__read2(3, temp, vp8_input)) + return SIMPLEWEBP_IO_ERROR; + + frametag = temp[0] | (((simplewebp_u32) temp[1]) << 8) | (((simplewebp_u32) temp[2]) << 16); + if (frametag & 1) + /* Intraframe in SimpleWebP? Nope */ + return SIMPLEWEBP_UNSUPPORTED_ERROR; + + profile = (frametag >> 1) & 7; + if (profile > 3) + /* Unsupported profile */ + return SIMPLEWEBP_UNSUPPORTED_ERROR; + + partition_size = frametag >> 5; + if (partition_size >= swebp__proxy_size(vp8_input->userdata)) + /* Inconsistent data */ + return SIMPLEWEBP_CORRUPT_ERROR; + + if (!swebp__read2(7, temp, vp8_input)) + return SIMPLEWEBP_IO_ERROR; + + if (memcmp(temp, "\x9D\x01\x2A", 3) != 0) + return SIMPLEWEBP_CORRUPT_ERROR; + + width = swebp__to_uint16(temp + 3); + height = swebp__to_uint16(temp + 5); + + result->webp_type = 0; + memset(&result->decoder.vp8, 0, sizeof(struct swebp__vp8)); + result->decoder.vp8.picture_header.width = width & 0x3FFF; + result->decoder.vp8.picture_header.height = height & 0x3FFF; + result->decoder.vp8.picture_header.xscale = (simplewebp_u8) (width >> 14); + result->decoder.vp8.picture_header.yscale = (simplewebp_u8) (height >> 14); + result->decoder.vp8.frame_header.partition_length = partition_size; + + return SIMPLEWEBP_NO_ERROR; +} + +static void swebp__vp8l_bitread_init(struct swebp__vp8l_bdec *bdec, simplewebp_u8 *buf, size_t size) +{ + bdec->buf = buf; + bdec->len = size; + bdec->bit_pos = 0; + bdec->eos = 0; +} + +static simplewebp_u32 swebp__vp8l_bitread_read(struct swebp__vp8l_bdec *bdec, int count) +{ + int i; + simplewebp_u32 value = 0; + + for (i = 0; i < count; i++, bdec->bit_pos++) + { + simplewebp_u8 b; + size_t bytepos = bdec->bit_pos >> 3; + + if (bytepos >= bdec->len) + { + bdec->eos = 1; + break; + } + + b = bdec->buf[bytepos]; + value |= ((b >> (bdec->bit_pos & 7)) & 1) << i; + } + + return value; +} + +static simplewebp_error swebp__load_lossless(simplewebp_input *vp8l_input, simplewebp *result) +{ + simplewebp_u8 temp[5]; + struct swebp__vp8l_bdec br; + + if (!swebp__seek(0, vp8l_input)) + return SIMPLEWEBP_IO_ERROR; + + if (!swebp__read2(5, temp, vp8l_input)) + return SIMPLEWEBP_IO_ERROR; + + if (temp[0] != 0x2F) + return SIMPLEWEBP_CORRUPT_ERROR; + + swebp__vp8l_bitread_init(&br, temp + 1, 4); + + memset(&result->decoder.vp8l, 0, sizeof(struct swebp__vp8l_decoder)); + result->decoder.vp8l.width = swebp__vp8l_bitread_read(&br, 14) + 1; + result->decoder.vp8l.height = swebp__vp8l_bitread_read(&br, 14) + 1; + result->decoder.vp8l.has_alpha = swebp__vp8l_bitread_read(&br, 1); + if (swebp__vp8l_bitread_read(&br, 3) != 0) + return SIMPLEWEBP_UNSUPPORTED_ERROR; + + result->webp_type = 1; + return SIMPLEWEBP_NO_ERROR; +} + +static simplewebp_u32 swebp__to_uint24(const simplewebp_u8 *buf) +{ + return buf[0] | (((simplewebp_u32) buf[1]) << 8) | (((simplewebp_u32) buf[2]) << 16); +} + +static simplewebp_error swebp__alpha_init(struct swebp__alpha *alpha, simplewebp_input *input) +{ + simplewebp_u8 flags; + + if (!swebp__seek(0, input)) + return SIMPLEWEBP_IO_ERROR; + + if (!swebp__read2(1, &flags, input)) + return SIMPLEWEBP_IO_ERROR; + + alpha->filter_method = (flags >> 2) & 3; + alpha->is_lossless_compressed = (flags & 3) > 0; + + return SIMPLEWEBP_NO_ERROR; +} + +static simplewebp_i32 swebp__clip(simplewebp_i32 v, simplewebp_i32 m) +{ + return (v < 0) ? 0 : ((v > m) ? m : v); +} + +static void swebp__alpha_apply_filters(simplewebp_u8 *ptr, size_t width, size_t height, simplewebp_u8 filter_type) +{ + size_t y, x; + + if (filter_type == 0) + /* No filters to be applied */ + return; + + for (y = 0; y < height; y++) + { + for (x = (y == 0); x < width; x++) + { + size_t i = y * width + x; + simplewebp_u8 predictor = 0; + + switch (filter_type) + { + case 0: + default: + /* Should not happen */ + predictor = 0; + case 1: + predictor = x == 0 ? ptr[i - width] : ptr[i - 1]; + break; + case 2: + predictor = y == 0 ? ptr[i - 1] : ptr[i - width]; + break; + case 3: + { + simplewebp_i16 a, b, c; + simplewebp_i16 val; + + a = x == 0 ? ptr[i - width] : ptr[i - 1]; + b = y == 0 ? ptr[i - 1] : ptr[i - width]; + if (x > 0) + { + if (y > 0) + c = ptr[i - width - 1]; + else + c = ptr[i - 1]; + } + else + { + if (y > 0) + c = ptr[i - width]; + else + c = ptr[0]; + } + val = a + b - c; + predictor = (simplewebp_u8) swebp__clip(val, 255); + break; + } + } + + ptr[i] += predictor; + } + } +} + +static simplewebp_error swebp__alpha_decode_simple(simplewebp *simplewebp, simplewebp_u8 *dst) +{ + size_t width, height; + simplewebp_input *input = &simplewebp->alph_input; + + if (!swebp__seek(1, input)) + return SIMPLEWEBP_IO_ERROR; + + simplewebp_get_dimensions(simplewebp, &width, &height); + if (!swebp__read2(width * height, dst, input)) + return SIMPLEWEBP_IO_ERROR; + + return SIMPLEWEBP_NO_ERROR; +} + +static simplewebp_error swebp__decode_lossless_bitstream( + simplewebp *simplewebp, + simplewebp_input *input, + size_t bitstreamsize, + struct swebp__pixel *rgba, + simplewebp_bool skipheader +); + +static simplewebp_error swebp__alpha_decode_lossless(simplewebp *simplewebp, simplewebp_u8 *dst) +{ + size_t vp8lsize, width, height, i; + struct swebp__pixel *rgba; + simplewebp_error err; + + simplewebp_get_dimensions(simplewebp, &width, &height); + + vp8lsize = swebp__proxy_size(simplewebp->alph_input.userdata) - 1; + if (!swebp__proxy_seek(1, simplewebp->alph_input.userdata)) + return SIMPLEWEBP_IO_ERROR; + + rgba = (struct swebp__pixel*) swebp__alloc(simplewebp, sizeof(struct swebp__pixel) * width * height); + if (!rgba) + return SIMPLEWEBP_ALLOC_ERROR; + + err = swebp__decode_lossless_bitstream( + simplewebp, + &simplewebp->alph_input, + vp8lsize, + rgba, + 1 + ); + if (err != SIMPLEWEBP_NO_ERROR) + { + swebp__dealloc(simplewebp, rgba); + return err; + } + + /* Pull green channel */ + for (i = 0; i < width * height; i++) + dst[i] = rgba[i].g; + + swebp__dealloc(simplewebp, rgba); + return SIMPLEWEBP_NO_ERROR; +} + +static simplewebp_error swebp__alpha_decode(simplewebp *simplewebp, simplewebp_u8 *dst) +{ + size_t width, height; + simplewebp_error err = SIMPLEWEBP_NO_ERROR; + + simplewebp_get_dimensions(simplewebp, &width, &height); + + if (simplewebp->alph_input.userdata) + { + if (simplewebp->alpha_decoder.is_lossless_compressed) + err = swebp__alpha_decode_lossless(simplewebp, dst); + else + err = swebp__alpha_decode_simple(simplewebp, dst); + + if (err == SIMPLEWEBP_NO_ERROR) + swebp__alpha_apply_filters(dst, width, height, simplewebp->alpha_decoder.filter_method); + } + else + memset(dst, 255, width * height); + + return err; +} + +static simplewebp_bool swebp__has_decoder(simplewebp *simplewebp) +{ + return simplewebp->vp8_input.userdata != NULL || simplewebp->vp8l_input.userdata != NULL; +} + +simplewebp_error simplewebp_load(simplewebp_input *input, const simplewebp_allocator *allocator, simplewebp **out) +{ + simplewebp_u8 temp[4]; + simplewebp_error err; + simplewebp_input riff_input; + simplewebp *result; + size_t chunk_size; + + if (allocator == NULL) + allocator = &swebp__default_allocator; + + *out = NULL; + + /* Read "RIFF" */ + err = swebp__get_input_chunk_4cc(allocator, input, &riff_input, temp, &chunk_size); + if (err != SIMPLEWEBP_NO_ERROR) + return err; + if (memcmp(temp, "RIFF", 4) != 0) + { + simplewebp_close_input(&riff_input); + return SIMPLEWEBP_NOT_WEBP_ERROR; + } + + /* Read "WEBP" */ + if (!swebp__read2(4, temp, &riff_input) || memcmp(temp, "WEBP", 4) != 0) + { + simplewebp_close_input(&riff_input); + return SIMPLEWEBP_IO_ERROR; + } + + /* Allocate simplewebp structure */ + result = (simplewebp *) allocator->alloc(allocator->userdata, sizeof(simplewebp)); + if (result == NULL) + return SIMPLEWEBP_ALLOC_ERROR; + memset(result, 0, sizeof(simplewebp)); + + result->allocator = *allocator; + result->riff_input = riff_input; + + while (1) + { + simplewebp_input chunk_input_proxy; + size_t current_position; + + err = swebp__get_input_chunk_4cc(allocator, &result->riff_input, &chunk_input_proxy, temp, &chunk_size); + if (err != SIMPLEWEBP_NO_ERROR) + { + if (swebp__has_decoder(result)) + /* Loaded successfully */ + err = SIMPLEWEBP_NO_ERROR; + + break; + } + + current_position = swebp__tell(&result->riff_input); + + if (memcmp(temp, "VP8 ", 4) == 0) + { + if (swebp__has_decoder(result)) + { + err = SIMPLEWEBP_UNSUPPORTED_ERROR; + break; + } + + err = swebp__load_lossy(&chunk_input_proxy, result); + if (err != SIMPLEWEBP_NO_ERROR) + { + simplewebp_close_input(&chunk_input_proxy); + break; + } + + result->vp8_input = chunk_input_proxy; + } + else if (memcmp(temp, "VP8L", 4) == 0) + { + if (swebp__has_decoder(result) || result->alph_input.userdata != NULL) + { + /* Alpha channel is present or existing VP8(L) decoder present. */ + simplewebp_close_input(&chunk_input_proxy); + err = SIMPLEWEBP_UNSUPPORTED_ERROR; + break; + } + + err = swebp__load_lossless(&chunk_input_proxy, result); + if (err != SIMPLEWEBP_NO_ERROR) + { + simplewebp_close_input(&chunk_input_proxy); + break; + } + + result->vp8l_input = chunk_input_proxy; + } + else if (memcmp(temp, "ALPH", 4) == 0) + { + if (result->vp8l_input.userdata != NULL) + { + /* Alpha channel already present in VP8L */ + simplewebp_close_input(&chunk_input_proxy); + err = SIMPLEWEBP_UNSUPPORTED_ERROR; + break; + } + + err = swebp__alpha_init(&result->alpha_decoder, &chunk_input_proxy); + if (err != SIMPLEWEBP_NO_ERROR) + { + simplewebp_close_input(&chunk_input_proxy); + break; + } + + result->alph_input = chunk_input_proxy; + } + else + /* Input unused. */ + simplewebp_close_input(&chunk_input_proxy); + + if (!swebp__seek(current_position + (chunk_size + 1) & (~((size_t) 1)), &result->riff_input)) + { + err = SIMPLEWEBP_IO_ERROR; + break; + } + } + + /* Success case */ + if (err == SIMPLEWEBP_NO_ERROR) + { + result->input = *input; + *out = result; + } + /* Failure case */ + else + { + simplewebp_unload(result); + *out = NULL; + } + + return err; +} + +simplewebp_error simplewebp_load_from_memory(void *data, size_t size, const simplewebp_allocator *allocator, simplewebp **out) +{ + simplewebp_input input; + simplewebp_error err; + + err = simplewebp_input_from_memory(data, size, &input, allocator); + if (err != SIMPLEWEBP_NO_ERROR) + return err; + + err = simplewebp_load(&input, allocator, out); + if (err != SIMPLEWEBP_NO_ERROR) + simplewebp_close_input(&input); + + return err; +} + +static void swebp__close_input(simplewebp_input *inp) +{ + if (inp->userdata) + simplewebp_close_input(inp); +} + +void simplewebp_unload(simplewebp *simplewebp) +{ + swebp__close_input(&simplewebp->alph_input); + swebp__close_input(&simplewebp->vp8_input); + swebp__close_input(&simplewebp->vp8l_input); + swebp__close_input(&simplewebp->riff_input); + swebp__close_input(&simplewebp->input); + + if (simplewebp->webp_type) + { + /* TODO: Clear lossless data*/ + } + else + { + + } + + swebp__dealloc(simplewebp, simplewebp); +} + +void simplewebp_get_dimensions(simplewebp *simplewebp, size_t *width, size_t *height) +{ + switch (simplewebp->webp_type) + { + case 0: + *width = simplewebp->decoder.vp8.picture_header.width; + *height = simplewebp->decoder.vp8.picture_header.height; + break; + case 1: + *width = simplewebp->decoder.vp8l.width; + *height = simplewebp->decoder.vp8l.height; + break; + default: + *width = 0; + *height = 0; + break; + } +} + +simplewebp_bool simplewebp_is_lossless(simplewebp *simplewebp) +{ + return simplewebp->webp_type == 1; +} + +static void swebp__bitread_setbuf(struct swebp__bdec *br, simplewebp_u8 *buf, size_t size) +{ + br->buf = buf; + br->buf_end = buf + size; + br->buf_max = (size >= sizeof(simplewebp_u32)) + ? (buf + size - sizeof(simplewebp_u32) + 1) + : buf; +} + +static void swebp__bitread_load(struct swebp__bdec *br) +{ + simplewebp_u32 bits; + + if (br->buf < br->buf_max) + { + /* Read 24 bits at a time in big endian order */ + bits = br->buf[2] | (br->buf[1] << 8) | (br->buf[0] << 16); + br->buf += 3; /* 24 / 8 */ + br->value = bits | (br->value << 24); + br->bits += 24; + } + else + { + /* Only read 8 bits at a time */ + if (br->buf < br->buf_end) + { + br->bits += 8; + br->value = (*br->buf++) | (br->value << 8); + } + else if (!br->eof) + { + br->value <<= 8; + br->bits += 8; + br->eof = 1; + } + else + br->bits = 0; + } +} + +static void swebp__bitread_init(struct swebp__bdec *br, simplewebp_u8 *buf, size_t size) +{ + br->range = 254; + br->value = 0; + br->bits = -8; + br->eof = 0; + swebp__bitread_setbuf(br, buf, size); + swebp__bitread_load(br); +} + +/* https://stackoverflow.com/a/11398748 */ +const simplewebp_u32 swebp__blog2_tab32[32] = { + 0, 9, 1, 10, 13, 21, 2, 29, + 11, 14, 16, 18, 22, 25, 3, 30, + 8, 12, 20, 28, 15, 17, 24, 7, + 19, 27, 23, 6, 26, 5, 4, 31 +}; + +static simplewebp_u32 swebp__bitslog2floor(simplewebp_u32 value) +{ + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + return swebp__blog2_tab32[(value * 0x07C4ACDDU) >> 27]; +} + +static simplewebp_u32 swebp__bitread_getbit(struct swebp__bdec *br, simplewebp_u32 prob) +{ + simplewebp_u32 bit; + simplewebp_u8 range, split, value, shift; + simplewebp_i8 pos; + + range = br->range; + + if (br->bits < 0) + swebp__bitread_load(br); + + pos = br->bits; + split = (simplewebp_u8) ((((simplewebp_u32) range) * prob) >> 8); + value = (simplewebp_u8) (br->value >> pos); + bit = value > split; + + if (bit) + { + range -= split; + br->value -= (((simplewebp_u32) split) + 1) << pos; + } + else + range = split + 1; + + shift = 7 ^ swebp__bitslog2floor(range); + range <<= shift; + br->bits -= shift; + br->range = range - 1; + return bit; +} + +static simplewebp_u32 swebp__bitread_getval(struct swebp__bdec *br, simplewebp_u8 bits) +{ + simplewebp_u32 value = 0; + + while (bits--) + value |= swebp__bitread_getbit(br, 0x80) << bits; + + return value; +} + +static simplewebp_i32 swebp__bitread_getvalsigned(struct swebp__bdec *br, simplewebp_u8 bits) +{ + simplewebp_i32 value = swebp__bitread_getval(br, bits); + return swebp__bitread_getval(br, 1) ? -value : value; +} + +static simplewebp_i32 swebp__bitread_getsigned(struct swebp__bdec *br, simplewebp_i32 v) +{ + simplewebp_i8 pos; + simplewebp_u32 split, value; + simplewebp_i32 mask; + + if (br->bits < 0) + swebp__bitread_load(br); + + pos = br->bits; + split = br->range >> 1; + value = br->value >> pos; + mask = ((simplewebp_i32) (split - value)) >> 31; + br->bits -= 1; + br->range += (simplewebp_u8) mask; + br->range |= 1; + br->value -= (simplewebp_u32) ((split + 1) & (simplewebp_u32) mask) << pos; + return (v ^ mask) - mask; +} + +/* RFC 6386 section 14.1 */ +static const simplewebp_u8 swebp__dctab[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 simplewebp_u16 swebp__actab[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 +}; + +/* RFC 6386 section 13 */ +static const simplewebp_u8 swebp__coeff_update_proba[4][8][3][11] = { + { { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 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 } + } + } +}; + +/* RFC 6386 section 13.5 */ +static const simplewebp_u8 swebp__coeff_proba0[4][8][3][11] = { + { { { 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 } + } + } +}; + +static const simplewebp_u8 swebp__fextrarows[3] = {0, 2, 8}; + +static simplewebp_u8 *swebp__align32(simplewebp_u8 *ptr) +{ + size_t uptr = (size_t) ptr; + return (simplewebp_u8 *) ((uptr + 31) & (~(size_t)31)); +} + +/* Clip tables */ + +static const simplewebp_u8 swebp__abs0[255 + 255 + 1] = { + 0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, + 0xf3, 0xf2, 0xf1, 0xf0, 0xef, 0xee, 0xed, 0xec, 0xeb, 0xea, 0xe9, 0xe8, + 0xe7, 0xe6, 0xe5, 0xe4, 0xe3, 0xe2, 0xe1, 0xe0, 0xdf, 0xde, 0xdd, 0xdc, + 0xdb, 0xda, 0xd9, 0xd8, 0xd7, 0xd6, 0xd5, 0xd4, 0xd3, 0xd2, 0xd1, 0xd0, + 0xcf, 0xce, 0xcd, 0xcc, 0xcb, 0xca, 0xc9, 0xc8, 0xc7, 0xc6, 0xc5, 0xc4, + 0xc3, 0xc2, 0xc1, 0xc0, 0xbf, 0xbe, 0xbd, 0xbc, 0xbb, 0xba, 0xb9, 0xb8, + 0xb7, 0xb6, 0xb5, 0xb4, 0xb3, 0xb2, 0xb1, 0xb0, 0xaf, 0xae, 0xad, 0xac, + 0xab, 0xaa, 0xa9, 0xa8, 0xa7, 0xa6, 0xa5, 0xa4, 0xa3, 0xa2, 0xa1, 0xa0, + 0x9f, 0x9e, 0x9d, 0x9c, 0x9b, 0x9a, 0x99, 0x98, 0x97, 0x96, 0x95, 0x94, + 0x93, 0x92, 0x91, 0x90, 0x8f, 0x8e, 0x8d, 0x8c, 0x8b, 0x8a, 0x89, 0x88, + 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81, 0x80, 0x7f, 0x7e, 0x7d, 0x7c, + 0x7b, 0x7a, 0x79, 0x78, 0x77, 0x76, 0x75, 0x74, 0x73, 0x72, 0x71, 0x70, + 0x6f, 0x6e, 0x6d, 0x6c, 0x6b, 0x6a, 0x69, 0x68, 0x67, 0x66, 0x65, 0x64, + 0x63, 0x62, 0x61, 0x60, 0x5f, 0x5e, 0x5d, 0x5c, 0x5b, 0x5a, 0x59, 0x58, + 0x57, 0x56, 0x55, 0x54, 0x53, 0x52, 0x51, 0x50, 0x4f, 0x4e, 0x4d, 0x4c, + 0x4b, 0x4a, 0x49, 0x48, 0x47, 0x46, 0x45, 0x44, 0x43, 0x42, 0x41, 0x40, + 0x3f, 0x3e, 0x3d, 0x3c, 0x3b, 0x3a, 0x39, 0x38, 0x37, 0x36, 0x35, 0x34, + 0x33, 0x32, 0x31, 0x30, 0x2f, 0x2e, 0x2d, 0x2c, 0x2b, 0x2a, 0x29, 0x28, + 0x27, 0x26, 0x25, 0x24, 0x23, 0x22, 0x21, 0x20, 0x1f, 0x1e, 0x1d, 0x1c, + 0x1b, 0x1a, 0x19, 0x18, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, + 0x03, 0x02, 0x01, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, + 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, + 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, + 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, + 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; +static const simplewebp_u8 *const swebp__kabs0 = &swebp__abs0[255]; + +static const simplewebp_u8 swebp__sclip1[1020 + 1020 + 1] = { + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, + 0x80, 0x80, 0x80, 0x80, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, + 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, + 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, + 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, + 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, + 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, + 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, + 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, + 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, + 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, + 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, + 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, + 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f, 0x7f +}; +static const simplewebp_i8 *const swebp__ksclip1 = (const simplewebp_i8 *) &swebp__sclip1[1020]; + +static const simplewebp_u8 swebp__sclip2[112 + 112 + 1] = { + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, + 0xfc, 0xfd, 0xfe, 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, + 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f +}; +static const simplewebp_i8 *const swebp__ksclip2 = (const simplewebp_i8 *) &swebp__sclip2[112]; + +static const simplewebp_u8 swebp__clip1[255 + 511 + 1] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, + 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, + 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, + 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, + 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, + 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, + 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, + 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, + 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, + 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, + 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, + 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, + 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, + 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, + 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff +}; +static const simplewebp_u8 *const swebp__kclip1 = &swebp__clip1[255]; + +/* RFC 6386 section 14.4 */ + +static void swebp__transform_wht(const simplewebp_i16 *in, simplewebp_i16 *out) +{ + simplewebp_i32 temp[16], i; + + for (i = 0; i < 4; i++) + { + simplewebp_i32 a0, a1, a2, a3; + + a0 = in[i] + in[i + 12]; + a1 = in[i + 4] + in[i + 8]; + a2 = in[i + 4] - in[i + 8]; + a3 = in[i] - in[i + 12]; + temp[i] = a0 + a1; + temp[i + 4] = a3 + a2; + temp[i + 8] = a0 - a1; + temp[i + 12] = a3 - a2; + } + for (i = 0; i < 4; i++) + { + simplewebp_i32 dc, a0, a1, a2, a3; + + dc = temp[i * 4] + 3; + a0 = dc + temp[i * 4 + 3]; + a1 = temp[i * 4 + 1] + temp[i * 4 + 2]; + a2 = temp[i * 4 + 1] - temp[i * 4 + 2]; + a3 = dc - temp[i * 4 + 3]; + out[i * 64] = (a0 + a1) >> 3; + out[i * 64 + 16] = (a3 + a2) >> 3; + out[i * 64 + 32] = (a0 - a1) >> 3; + out[i * 64 + 48] = (a3 - a2) >> 3; + } +} + +static simplewebp_i32 swebp__mul1(simplewebp_i16 a) +{ + return (((simplewebp_i32) a * 20091) >> 16) + a; +} + +static simplewebp_i32 swebp__mul2(simplewebp_i16 a) +{ + return ((simplewebp_i32) a * 35468) >> 16; +} + + +static simplewebp_u8 swebp__clip8b(simplewebp_i32 v) { + return (!(v & ~0xff)) ? v : (v < 0) ? 0 : 255; +} + +static void swebp__store(simplewebp_u8 *out, simplewebp_i32 x, simplewebp_i32 y, simplewebp_i32 v) +{ + out[y * 32 + x] = swebp__clip8b(out[y * 32 + x] + (v >> 3)); +} + +static void swebp__transform_one(const simplewebp_i16 *in, simplewebp_u8 *out) +{ + simplewebp_i32 tmp[16], i; + + /* Vertical pass */ + for (i = 0; i < 4; i++) + { + simplewebp_i32 a, b, c, d; + + a = in[i] + in[i + 8]; + b = in[i] - in[i + 8]; + c = swebp__mul2(in[i + 4]) - swebp__mul1(in[i + 12]); + d = swebp__mul1(in[i + 4]) + swebp__mul2(in[i + 12]); + tmp[i * 4] = a + d; + tmp[i * 4 + 1] = b + c; + tmp[i * 4 + 2] = b - c; + tmp[i * 4 + 3] = a - d; + } + /* Horizontal pass */ + for (i = 0; i < 4; i++) + { + simplewebp_i32 dc, a, b, c, d; + + dc = tmp[i] + 4; + a = dc + tmp[i + 8]; + b = dc - tmp[i + 8]; + c = swebp__mul2(tmp[i + 4]) - swebp__mul1(tmp[i + 12]); + d = swebp__mul1(tmp[i + 4]) + swebp__mul2(tmp[i + 12]); + swebp__store(out, 0, i, a + d); + swebp__store(out, 1, i, b + c); + swebp__store(out, 2, i, b - c); + swebp__store(out, 3, i, a - d); + } +} + +static void swebp__transform(const simplewebp_i16 *in, simplewebp_u8 *out, simplewebp_u8 do_2) +{ + swebp__transform_one(in, out); + if (do_2) + swebp__transform_one(in + 16, out + 4); +} + +static void swebp__transform_dc(const simplewebp_i16 *in, simplewebp_u8 *out) +{ + simplewebp_i32 dc, x, y; + dc = in[0] + 4; + + for (y = 0; y < 4; y++) + { + for (x = 0; x < 4; x++) + swebp__store(out, x, y, dc); + } +} + +static void swebp__store2(simplewebp_u8 *out, simplewebp_i32 y, simplewebp_i32 dc, simplewebp_i32 d, simplewebp_i32 c) +{ + swebp__store(out, 0, y, dc + d); + swebp__store(out, 1, y, dc + c); + swebp__store(out, 2, y, dc - c); + swebp__store(out, 3, y, dc - d); +} + +static void swebp__transform_ac3(const simplewebp_i16 *in, simplewebp_u8 *out) +{ + simplewebp_i32 a, c4, d4, c1, d1; + + a = in[0] + 4; + c4 = swebp__mul2(in[4]); + d4 = swebp__mul1(in[4]); + c1 = swebp__mul2(in[1]); + d1 = swebp__mul1(in[1]); + swebp__store2(out, 0, a + d4, d1, c1); + swebp__store2(out, 1, a + c4, d1, c1); + swebp__store2(out, 2, a - c4, d1, c1); + swebp__store2(out, 3, a - d4, d1, c1); +} + +static void swebp__transform_uv(const simplewebp_i16 *in, simplewebp_u8 *out) +{ + swebp__transform(in, out, 1); + swebp__transform(in + 32 /* 2*16 */, out + 128 /* 4*BPS */, 1); +} + +static void swebp__transform_dcuv(const simplewebp_i16 *in, simplewebp_u8 *out) +{ + if (in[0]) + swebp__transform_dc(in, out); + if (in[16]) + swebp__transform_dc(in + 16, out + 4); + if (in[32]) + swebp__transform_dc(in + 32, out + 128); + if (in[48]) + swebp__transform_dc(in + 48, out + 132); +} + +static simplewebp_i32 swebp__needsfilter2(const simplewebp_u8 *p, simplewebp_i32 step, simplewebp_i32 t, simplewebp_i32 it) +{ + simplewebp_i32 p3, p2, p1, p0, q0, q1, q2, q3; + p3 = p[-4 * step]; + p2 = p[-3 * step]; + p1 = p[-2 * step]; + p0 = p[-step]; + q0 = p[0]; + q1 = p[step]; + q2 = p[2 * step]; + q3 = p[3 * step]; + + if ((4 * swebp__kabs0[p0 - q0] + swebp__kabs0[p1 - q1]) > t) + return 0; + + return + swebp__kabs0[p3 - p2] <= it && + swebp__kabs0[p2 - p1] <= it && + swebp__kabs0[p1 - p0] <= it && + swebp__kabs0[q3 - q2] <= it && + swebp__kabs0[q2 - q1] <= it && + swebp__kabs0[q1 - q0] <= it; +} + +static simplewebp_i32 swebp__hev(const simplewebp_u8 *p, simplewebp_i32 step, simplewebp_i32 thresh) +{ + simplewebp_i32 p1, p0, q0, q1; + + p1 = p[-2 * step]; + p0 = p[-step]; + q0 = p[0]; + q1 = p[step]; + + return (swebp__kabs0[p1 - p0] > thresh) || (swebp__kabs0[q1 - q0] > thresh); +} + +static void swebp__do_filter2(simplewebp_u8 *p, simplewebp_i32 step) +{ + simplewebp_i32 p1, p0, q0, q1, a, a1, a2; + + p1 = p[-2 * step]; + p0 = p[-step]; + q0 = p[0]; + q1 = p[step]; + a = 3 * (q0 - p0) + swebp__ksclip1[p1 - q1]; + a1 = swebp__ksclip2[(a + 4) >> 3]; + a2 = swebp__ksclip2[(a + 3) >> 3]; + p[-step] = swebp__kclip1[p0 + a2]; + p[0] = swebp__kclip1[q0 - a1]; +} + +static void swebp__do_filter6(simplewebp_u8 *p, simplewebp_i32 step) +{ + simplewebp_i32 p2, p1, p0, q0, q1, q2, a, a1, a2, a3; + + p2 = p[-3 * step]; + p1 = p[-2 * step]; + p0 = p[-step]; + q0 = p[0]; + q1 = p[step]; + q2 = p[2 * step]; + a = swebp__ksclip1[3 * (q0 - p0) + swebp__ksclip1[p1 - q1]]; + a1 = (27 * a + 63) >> 7; + a2 = (18 * a + 63) >> 7; + a3 = (9 * a + 63) >> 7; + + p[-3 * step] = swebp__kclip1[p2 + a3]; + p[-2 * step] = swebp__kclip1[p1 + a2]; + p[-step] = swebp__kclip1[p0 + a1]; + p[0] = swebp__kclip1[q0 - a1]; + p[step] = swebp__kclip1[q1 - a2]; + p[2 * step] = swebp__kclip1[q2 - a3]; +} + +static void swebp__filterloop26(simplewebp_u8 *p, simplewebp_i32 hstride, simplewebp_i32 vstride, simplewebp_i32 size, simplewebp_i32 thresh, simplewebp_i32 ithresh, simplewebp_i32 hev_thresh) +{ + simplewebp_i32 thresh2 = 2 * thresh + 1; + + while (size-- > 0) + { + if (swebp__needsfilter2(p, hstride, thresh2, ithresh)) + { + if (swebp__hev(p, hstride, hev_thresh)) + swebp__do_filter2(p, hstride); + else + swebp__do_filter6(p, hstride); + } + + p += vstride; + } +} + +static void swebp__vfilter16(simplewebp_u8 *p, simplewebp_i32 stride, simplewebp_i32 thresh, simplewebp_i32 ithresh, simplewebp_i32 hev_thresh) +{ + swebp__filterloop26(p, stride, 1, 16, thresh, ithresh, hev_thresh); +} + +static void swebp__do_filter4(simplewebp_u8 *p, simplewebp_i32 step) +{ + simplewebp_i32 p1, p0, q0, q1, a, a1, a2, a3; + + p1 = p[-2 * step]; + p0 = p[-step]; + q0 = p[0]; + q1 = p[step]; + a = 3 * (q0 - p0); + a1 = swebp__ksclip2[(a + 4) >> 3]; + a2 = swebp__ksclip2[(a + 3) >> 3]; + a3 = (a1 + 1) >> 1; + + p[-2 * step] = swebp__kclip1[p1 + a3]; + p[-step] = swebp__kclip1[p0 + a2]; + p[0] = swebp__kclip1[q0 - a1]; + p[step] = swebp__kclip1[q1 - a3]; +} + +static void swebp__filterloop24(simplewebp_u8 *p, simplewebp_i32 hstride, simplewebp_i32 vstride, simplewebp_i32 size, simplewebp_i32 thresh, simplewebp_i32 ithresh, simplewebp_i32 hev_thresh) +{ + simplewebp_i32 thresh2 = 2 * thresh + 1; + + while (size-- > 0) + { + if (swebp__needsfilter2(p, hstride, thresh2, ithresh)) + { + if (swebp__hev(p, hstride, hev_thresh)) + swebp__do_filter2(p, hstride); + else + swebp__do_filter4(p, hstride); + } + + p += vstride; + } +} + +static void swebp__vfilter16_i(simplewebp_u8 *p, simplewebp_i32 stride, simplewebp_i32 thresh, simplewebp_i32 ithresh, simplewebp_i32 hev_thresh) +{ + simplewebp_i32 i; + + for (i = 3; i > 0; i--) + { + p += 4 * stride; + swebp__filterloop24(p, stride, 1, 16, thresh, ithresh, hev_thresh); + } +} + +static void swebp__hfilter16(simplewebp_u8 *p, simplewebp_i32 stride, simplewebp_i32 thresh, simplewebp_i32 ithresh, simplewebp_i32 hev_thresh) +{ + swebp__filterloop26(p, 1, stride, 16, thresh, ithresh, hev_thresh); +} + +static void swebp__vfilter8(simplewebp_u8 *u, simplewebp_u8 *v, simplewebp_i32 stride, simplewebp_i32 thresh, simplewebp_i32 ithresh, simplewebp_i32 hev_thresh) +{ + swebp__filterloop26(u, stride, 1, 8, thresh, ithresh, hev_thresh); + swebp__filterloop26(v, stride, 1, 8, thresh, ithresh, hev_thresh); +} + +static void swebp__vfilter8_i(simplewebp_u8 *u, simplewebp_u8 *v, simplewebp_i32 stride, simplewebp_i32 thresh, simplewebp_i32 ithresh, simplewebp_i32 hev_thresh) +{ + swebp__filterloop24(u + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh); + swebp__filterloop24(v + 4 * stride, stride, 1, 8, thresh, ithresh, hev_thresh); +} + +static simplewebp_bool swebp__needsfilter(const simplewebp_u8 *p, simplewebp_i32 step, simplewebp_i32 t) +{ + simplewebp_i32 p1, p0, q0, q1; + + p1 = p[-2 * step]; + p0 = p[-step]; + q0 = p[0]; + q1 = p[step]; + return (4 * swebp__kabs0[p0 - q0] + swebp__kabs0[p1 - q1]) <= t; +} + +static void swebp__simple_vfilter16(simplewebp_u8 *p, simplewebp_i32 stride, simplewebp_i32 thresh) +{ + simplewebp_i32 i, thresh2; + + thresh2 = 2 * thresh + 1; + for (i = 0; i < 16; i++) + { + if (swebp__needsfilter(p + i, stride, thresh2)) + swebp__do_filter2(p + i, stride); + } +} + +static void swebp__simple_hfilter16(simplewebp_u8 *p, simplewebp_i32 stride, simplewebp_i32 thresh) +{ + simplewebp_i32 i, thresh2; + simplewebp_u8 *target; + + thresh2 = 2 * thresh + 1; + for (i = 0; i < 16; i++) + { + target = p + i * stride; + + if (swebp__needsfilter(target, 1, thresh2)) + swebp__do_filter2(target, 1); + } +} + +static void swebp__simple_vfilter16_i(simplewebp_u8 *p, simplewebp_i32 stride, simplewebp_i32 thresh) +{ + simplewebp_i32 k; + + for (k = 3; k > 0; k--) + { + p += 4 * stride; + swebp__simple_vfilter16(p, stride, thresh); + } +} + +static void swebp__simple_hfilter16_i(simplewebp_u8 *p, simplewebp_i32 stride, simplewebp_i32 thresh) +{ + simplewebp_i32 k; + + for (k = 3; k > 0; k--) + { + p += 4; + swebp__simple_hfilter16(p, stride, thresh); + } +} + +static void swebp__hfilter16_i(simplewebp_u8 *p, simplewebp_i32 stride, simplewebp_i32 thresh, simplewebp_i32 ithresh, simplewebp_i32 hev_thresh) +{ + simplewebp_i32 k; + + for (k = 3; k > 0; k--) + { + p += 4; + swebp__filterloop24(p, 1, stride, 16, thresh, ithresh, hev_thresh); + } +} + +static void swebp__hfilter8(simplewebp_u8 *u, simplewebp_u8 *v, simplewebp_i32 stride, simplewebp_i32 thresh, simplewebp_i32 ithresh, simplewebp_i32 hev_thresh) +{ + swebp__filterloop26(u, 1, stride, 8, thresh, ithresh, hev_thresh); + swebp__filterloop26(v, 1, stride, 8, thresh, ithresh, hev_thresh); +} + +static void swebp__hfilter8_i(simplewebp_u8 *u, simplewebp_u8 *v, simplewebp_i32 stride, simplewebp_i32 thresh, simplewebp_i32 ithresh, simplewebp_i32 hev_thresh) +{ + swebp__filterloop24(u + 4, 1, stride, 8, thresh, ithresh, hev_thresh); + swebp__filterloop24(v + 4, 1, stride, 8, thresh, ithresh, hev_thresh); +} + +/* DC */ +static void swebp__predluma4_0(simplewebp_u8 *out) +{ + simplewebp_u32 dc; + simplewebp_i32 i; + + dc = 4; + for (i = 0; i < 4; i++) + dc += out[i - 32] + out[-1 + i * 32]; + dc >>= 3; + for (i = 0; i < 4; i++) + memset(out + i * 32, dc, 4); +} + +static void swebp__truemotion(simplewebp_u8 *out, simplewebp_i32 size) +{ + const simplewebp_u8 *top, *clip0, *clip; + simplewebp_i32 x, y; + + top = out - 32; + clip0 = swebp__kclip1 - top[-1]; + + for (y = 0; y < size; y++) + { + clip = clip0 + out[-1]; + + for (x = 0; x < size; x++) + out[x] = clip[top[x]]; + + out += 32; + } +} + +/* TM4 */ +static void swebp__predluma4_1(simplewebp_u8 *out) +{ + swebp__truemotion(out, 4); +} + +static simplewebp_u8 swebp__avg3(simplewebp_u32 a, simplewebp_u32 b, simplewebp_u32 c) +{ + return (simplewebp_u8) ((a + 2 * b + c + 2) >> 2); +} + +/* Vertical */ +static void swebp__predluma4_2(simplewebp_u8 *out) +{ + const simplewebp_u8 *top; + simplewebp_u8 vals[4], i; + + top = out - 32; + + for (i = 0; i < 4; i++) + vals[i] = swebp__avg3(top[i - 1], top[i], top[i + 1]); + for (i = 0; i < 4; i++) + memcpy(out + i * 32, vals, sizeof(vals)); +} + +static void swebp__from_uint32(simplewebp_u8 *out, simplewebp_u32 value) +{ + /* Little endian */ + out[0] = (simplewebp_u8) value; + out[1] = (simplewebp_u8) (value >> 8); + out[2] = (simplewebp_u8) (value >> 16); + out[3] = (simplewebp_u8) (value >> 24); +} + +/* Horizontal*/ +static void swebp__predluma4_3(simplewebp_u8 *out) +{ + simplewebp_i32 vals[5], i; + for (i = -1; i < 4; i++) + vals[i + 1] = out[-1 + i * 32]; + + swebp__from_uint32(out, 0x01010101U * swebp__avg3(vals[0], vals[1], vals[2])); + swebp__from_uint32(out + 32, 0x01010101U * swebp__avg3(vals[1], vals[2], vals[3])); + swebp__from_uint32(out + 64, 0x01010101U * swebp__avg3(vals[2], vals[3], vals[4])); + swebp__from_uint32(out + 96, 0x01010101U * swebp__avg3(vals[3], vals[4], vals[4])); +} + +/* Right Down */ +static void swebp__predluma4_4(simplewebp_u8 *out) +{ + simplewebp_u32 i, j, k, l, x, a, b, c, d; + + i = out[-1]; + j = out[-1 + 1 * 32]; + k = out[-1 + 2 * 32]; + l = out[-1 + 3 * 32]; + x = out[-1 - 32]; + a = out[0 - 32]; + b = out[1 - 32]; + c = out[2 - 32]; + d = out[3 - 32]; + + out[3 * 32 + 0] = swebp__avg3(j, k, l); + out[3 * 32 + 1] = out[2 * 32 + 0] = swebp__avg3(i, j, k); + out[3 * 32 + 2] = out[2 * 32 + 1] = out[1 * 32 + 0] = swebp__avg3(x, i, j); + out[3 * 32 + 3] = out[2 * 32 + 2] = out[1 * 32 + 1] = out[0 * 32 + 0] = swebp__avg3(a, x, i); + out[2 * 32 + 3] = out[1 * 32 + 2] = out[0 * 32 + 1] = swebp__avg3(b, a, x); + out[1 * 32 + 3] = out[0 * 32 + 2] = swebp__avg3(c, b, a); + out[0 * 32 + 3] = swebp__avg3(d, c, b); +} + +static simplewebp_u8 swebp__avg2(simplewebp_u32 a, simplewebp_u32 b) +{ + return (simplewebp_u8) ((a + b + 1) >> 1); +} + +/* Vertical-Right */ +static void swebp__predluma4_5(simplewebp_u8 *out) +{ + simplewebp_u32 i, j, k, x, a, b, c, d; + + i = out[-1]; + j = out[-1 + 1 * 32]; + k = out[-1 + 2 * 32]; + x = out[-1 - 32]; + a = out[0 - 32]; + b = out[1 - 32]; + c = out[2 - 32]; + d = out[3 - 32]; + + out[0 * 32 + 0] = out[2 * 32 + 1] = swebp__avg2(x, a); + out[0 * 32 + 1] = out[2 * 32 + 2] = swebp__avg2(a, b); + out[0 * 32 + 2] = out[2 * 32 + 3] = swebp__avg2(b, c); + out[0 * 32 + 3] = swebp__avg2(c, d); + out[3 * 32 + 0] = swebp__avg3(k, j, i); + out[2 * 32 + 0] = swebp__avg3(j, i, x); + out[1 * 32 + 0] = out[3 * 32 + 1] = swebp__avg3(i, x, a); + out[1 * 32 + 1] = out[3 * 32 + 2] = swebp__avg3(x, a, b); + out[1 * 32 + 2] = out[3 * 32 + 3] = swebp__avg3(a, b, c); + out[1 * 32 + 3] = swebp__avg3(b, c, d); +} + +/* Left Down */ +static void swebp__predluma4_6(simplewebp_u8 *out) +{ + simplewebp_i32 a, b, c, d, e, f, g, h; + + a = out[-32]; + b = out[-31]; + c = out[-30]; + d = out[-29]; + e = out[-28]; + f = out[-27]; + g = out[-26]; + h = out[-25]; + + out[0 * 32 + 0] = swebp__avg3(a, b, c); + out[0 * 32 + 1] = out[1 * 32 + 0] = swebp__avg3(b, c, d); + out[0 * 32 + 2] = out[1 * 32 + 1] = out[2 * 32 + 0] = swebp__avg3(c, d, e); + out[0 * 32 + 3] = out[1 * 32 + 2] = out[2 * 32 + 1] = out[3 * 32 + 0] = swebp__avg3(d, e, f); + out[1 * 32 + 3] = out[2 * 32 + 2] = out[3 * 32 + 1] = swebp__avg3(e, f, g); + out[2 * 32 + 3] = out[3 * 32 + 2] = swebp__avg3(f, g, h); + out[3 * 32 + 3] = swebp__avg3(g, h, h); +} + +/* Vertical-Left */ +static void swebp__predluma4_7(simplewebp_u8 *out) +{ + simplewebp_i32 a, b, c, d, e, f, g, h; + + a = out[-32]; + b = out[-31]; + c = out[-30]; + d = out[-29]; + e = out[-28]; + f = out[-27]; + g = out[-26]; + h = out[-25]; + + out[0 * 32 + 0] = swebp__avg2(a, b); + out[0 * 32 + 1] = out[2 * 32 + 0] = swebp__avg2(b, c); + out[0 * 32 + 2] = out[2 * 32 + 1] = swebp__avg2(c, d); + out[0 * 32 + 3] = out[2 * 32 + 2] = swebp__avg2(d, e); + out[1 * 32 + 0] = swebp__avg3(a, b, c); + out[1 * 32 + 1] = out[3 * 32 + 0] = swebp__avg3(b, c, d); + out[1 * 32 + 2] = out[3 * 32 + 1] = swebp__avg3(c, d, e); + out[1 * 32 + 3] = out[3 * 32 + 2] = swebp__avg3(d, e, f); + out[2 * 32 + 3] = swebp__avg3(e, f, g); + out[3 * 32 + 3] = swebp__avg3(f, g, h); +} + +/* Horizontal-Down */ +static void swebp__predluma4_8(simplewebp_u8 *out) +{ + simplewebp_u32 i, j, k, l, x, a, b, c; + + i = out[-1]; + j = out[-1 + 1 * 32]; + k = out[-1 + 2 * 32]; + l = out[-1 + 3 * 32]; + x = out[-1 - 32]; + a = out[0 - 32]; + b = out[1 - 32]; + c = out[2 - 32]; + + out[0 * 32 + 0] = out[1 * 32 + 2] = swebp__avg2(i, x); + out[1 * 32 + 0] = out[2 * 32 + 2] = swebp__avg2(j, i); + out[2 * 32 + 0] = out[3 * 32 + 2] = swebp__avg2(k, j); + out[3 * 32 + 0] = swebp__avg2(l, k); + out[0 * 32 + 3] = swebp__avg3(a, b, c); + out[0 * 32 + 2] = swebp__avg3(x, a, b); + out[0 * 32 + 1] = out[1 * 32 + 3] = swebp__avg3(i, x, a); + out[1 * 32 + 1] = out[2 * 32 + 3] = swebp__avg3(j, i, x); + out[2 * 32 + 1] = out[3 * 32 + 3] = swebp__avg3(k, j, i); + out[3 * 32 + 1] = swebp__avg3(l, k, j); +} + +/* Horizontal-Up */ +static void swebp__predluma4_9(simplewebp_u8 *out) +{ + simplewebp_u32 i, j, k, l; + + i = out[-1]; + j = out[-1 + 1 * 32]; + k = out[-1 + 2 * 32]; + l = out[-1 + 3 * 32]; + + out[0 * 32 + 0] = swebp__avg2(i, j); + out[0 * 32 + 2] = out[1 * 32 + 0] = swebp__avg2(j, k); + out[1 * 32 + 2] = out[2 * 32 + 0] = swebp__avg2(k, l); + out[0 * 32 + 1] = swebp__avg3(i, j, k); + out[0 * 32 + 3] = out[1 * 32 + 1] = swebp__avg3(j, k, l); + out[1 * 32 + 3] = out[2 * 32 + 1] = swebp__avg3(k, l, l); + out[2 * 32 + 3] = out[2 * 32 + 2] = + out[3 * 32 + 0] = out[3 * 32 + 1] = + out[3 * 32 + 2] = out[3 * 32 + 3] = l; +} + +static void swebp__predluma4(simplewebp_u8 num, simplewebp_u8 *out) +{ + switch (num) + { + case 0: + swebp__predluma4_0(out); + break; + case 1: + swebp__predluma4_1(out); + break; + case 2: + swebp__predluma4_2(out); + break; + case 3: + swebp__predluma4_3(out); + break; + case 4: + swebp__predluma4_4(out); + break; + case 5: + swebp__predluma4_5(out); + break; + case 6: + swebp__predluma4_6(out); + break; + case 7: + swebp__predluma4_7(out); + break; + case 8: + swebp__predluma4_8(out); + break; + case 9: + swebp__predluma4_9(out); + break; + default: + break; + } +} + +static void swebp__put16(simplewebp_i32 v, simplewebp_u8 *out) +{ + simplewebp_i32 j; + for (j = 0; j < 16; j++) + memset(out + j * 32, v, 16); +} + +/* DC */ +static void swebp__predluma16_0(simplewebp_u8 *out) +{ + simplewebp_i32 dc, j; + + dc = 16; + for (j = 0; j < 16; j++) + dc += out[-1 + j * 32] + out[j - 32]; + + swebp__put16(dc >> 5, out); +} + +/* TM */ +static void swebp__predluma16_1(simplewebp_u8 *out) +{ + swebp__truemotion(out, 16); +} + +/* Vertical */ +static void swebp__predluma16_2(simplewebp_u8 *out) +{ + simplewebp_i32 j; + for (j = 0; j < 16; j++) + memcpy(out + j * 32, out - 32, 16); +} + +/* Horizontal */ +static void swebp__predluma16_3(simplewebp_u8 *out) +{ + simplewebp_i32 j; + for (j = 16; j > 0; j--) + { + memset(out, out[-1], 16); + out += 32; + } +} + +/* DC w/o Top */ +static void swebp__predluma16_4(simplewebp_u8 *out) +{ + simplewebp_i32 dc, j; + + dc = 8; + for (j = 0; j < 16; j++) + dc += out[-1 + j * 32]; + + swebp__put16(dc >> 4, out); +} + +/* DC w/o Left */ +static void swebp__predluma16_5(simplewebp_u8 *out) +{ + simplewebp_i32 dc, j; + + dc = 8; + for (j = 0; j < 16; j++) + dc += out[j - 32]; + + swebp__put16(dc >> 4, out); +} + +/* DC w/o Top Left */ +static void swebp__predluma16_6(simplewebp_u8 *out) +{ + swebp__put16(128, out); +} + +static void swebp__predluma16(simplewebp_u8 num, simplewebp_u8 *out) +{ + switch (num) + { + case 0: + swebp__predluma16_0(out); + break; + case 1: + swebp__predluma16_1(out); + break; + case 2: + swebp__predluma16_2(out); + break; + case 3: + swebp__predluma16_3(out); + break; + case 4: + swebp__predluma16_4(out); + break; + case 5: + swebp__predluma16_5(out); + break; + case 6: + swebp__predluma16_6(out); + break; + default: + break; + } +} + +static void swebp__put8x8uv(simplewebp_i32 value, simplewebp_u8 *out) +{ + simplewebp_i32 j; + for (j = 0; j < 8; j++) + memset(out + j * 32, value, 8); +} + +/* DC */ +static void swebp__predchroma8_0(simplewebp_u8 *out) +{ + simplewebp_i32 dc0, i; + + dc0 = 8; + for (i = 0; i < 8; i++) + dc0 += out[i - 32] + out[-1 + i * 32]; + + swebp__put8x8uv(dc0 >> 4, out); +} + +/* TM */ +static void swebp__predchroma8_1(simplewebp_u8 *out) +{ + swebp__truemotion(out, 8); +} + +/* Vertical */ +static void swebp__predchroma8_2(simplewebp_u8 *out) +{ + simplewebp_i32 j; + for (j = 0; j < 8; j++) + memcpy(out + j * 32, out - 32, 8); +} + +/* Horizontal */ +static void swebp__predchroma8_3(simplewebp_u8 *out) +{ + simplewebp_i32 j; + for (j = 0; j < 8; j++) + memset(out + j * 32, out[j * 32 - 1], 8); +} + +/* DC w/o Top */ +static void swebp__predchroma8_4(simplewebp_u8 *out) +{ + simplewebp_i32 dc0, i; + + dc0 = 4; + for (i = 0; i < 8; i++) + dc0 += out[-1 + i * 32]; + + swebp__put8x8uv(dc0 >> 3, out); +} + +/* DC w/o Left */ +static void swebp__predchroma8_5(simplewebp_u8 *out) +{ + simplewebp_i32 dc0, i; + + dc0 = 4; + for (i = 0; i < 8; i++) + dc0 += out[i - 32]; + + swebp__put8x8uv(dc0 >> 3, out); +} + +/* DC w/o Top Left */ +static void swebp__predchroma8_6(simplewebp_u8 *out) +{ + swebp__put8x8uv(128, out); +} + +static void swebp__predchroma8(simplewebp_u8 num, simplewebp_u8 *out) +{ + switch (num) + { + case 0: + swebp__predchroma8_0(out); + break; + case 1: + swebp__predchroma8_1(out); + break; + case 2: + swebp__predchroma8_2(out); + break; + case 3: + swebp__predchroma8_3(out); + break; + case 4: + swebp__predchroma8_4(out); + break; + case 5: + swebp__predchroma8_5(out); + break; + case 6: + swebp__predchroma8_6(out); + break; + default: + break; + } +} + +/* RFC 6386 section 11.5 */ +static const simplewebp_u8 swebp__modes_proba[10][10][9] = { + { { 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 const simplewebp_u8 swebp__bands[17] = { + 0, 1, 2, 3, 6, 4, 5, 6, 6, 6, 6, 6, 6, 6, 6, 7, 0 +}; + +static simplewebp_error swebp__load_vp8_header(struct swebp__vp8 *vp8d, simplewebp_u8 *buf, size_t bufsize) +{ + struct swebp__segment_header *segmnt_hdr; + struct swebp__filter_header *filt_hdr; + struct swebp__bdec br; + + /* Populate picture headers */ + vp8d->mb_w = (vp8d->picture_header.width + 15) >> 4; + vp8d->mb_h = (vp8d->picture_header.height + 15) >> 4; + + /* Reset proba */ + memset(vp8d->proba.segments, 255, sizeof(vp8d->proba.segments)); + /* Reset segment header */ + vp8d->segment_header.use_segment = 0; + vp8d->segment_header.update_map = 0; + vp8d->segment_header.absolute_delta = 1; + memset(&vp8d->segment_header.quantizer, 0, sizeof(vp8d->segment_header.quantizer)); + memset(&vp8d->segment_header.filter_strength, 0, sizeof(vp8d->segment_header.filter_strength)); + + /* Initialize bitreader */ + swebp__bitread_init(&br, buf, vp8d->frame_header.partition_length); + buf += vp8d->frame_header.partition_length; + bufsize -= vp8d->frame_header.partition_length; + + /* Read more picture headers. This is a keyframe */ + vp8d->picture_header.colorspace = swebp__bitread_getval(&br, 1); + vp8d->picture_header.clamp_type = swebp__bitread_getval(&br, 1); + + /* Parse segment header */ + segmnt_hdr = &vp8d->segment_header; + segmnt_hdr->use_segment = swebp__bitread_getval(&br, 1); + if (segmnt_hdr->use_segment) + { + simplewebp_i32 s; + segmnt_hdr->update_map = swebp__bitread_getval(&br, 1); + + if (swebp__bitread_getval(&br, 1) /* update data */) + { + segmnt_hdr->absolute_delta = swebp__bitread_getval(&br, 1); + for (s = 0; s < 4; s++) + segmnt_hdr->quantizer[s] = swebp__bitread_getval(&br, 1) + ? swebp__bitread_getvalsigned(&br, 7) + : 0; + for (s = 0; s < 4; s++) + segmnt_hdr->filter_strength[s] = swebp__bitread_getval(&br, 1) + ? swebp__bitread_getvalsigned(&br, 6) + : 0; + + } + + if (segmnt_hdr->update_map) + { + for (s = 0; s < 3; s++) + vp8d->proba.segments[s] = swebp__bitread_getval(&br, 1) + ? swebp__bitread_getval(&br, 8) + : 255; + } + } + else + segmnt_hdr->update_map = 0; + + if (br.eof) + return SIMPLEWEBP_CORRUPT_ERROR; + + /* Parse filters */ + filt_hdr = &vp8d->filter_header; + filt_hdr->simple = swebp__bitread_getval(&br, 1); + filt_hdr->level = swebp__bitread_getval(&br, 6); + filt_hdr->sharpness = swebp__bitread_getval(&br, 3); + filt_hdr->use_lf_delta = swebp__bitread_getval(&br, 1); + + if (filt_hdr->use_lf_delta) + { + if (swebp__bitread_getval(&br, 1) /* update lf-delta? */) + { + simplewebp_i32 i; + + for (i = 0; i < 4; i++) + { + if (swebp__bitread_getval(&br, 1)) + filt_hdr->ref_lf_delta[i] = swebp__bitread_getvalsigned(&br, 6); + } + + for (i = 0; i < 4; i++) + { + if (swebp__bitread_getval(&br, 1)) + filt_hdr->mode_lf_delta[i] = swebp__bitread_getvalsigned(&br, 6); + } + } + } + + vp8d->filter_type = filt_hdr->level == 0 ? 0 : (filt_hdr->simple ? 1 : 2); + + if (br.eof) + return SIMPLEWEBP_CORRUPT_ERROR; + + /* Parse partitions */ + { + simplewebp_u8 *sz, *buf_end, *part_start; + size_t size_left, last_part, p; + + sz = buf; + buf_end = buf + bufsize; + size_left = bufsize; + + last_part = vp8d->nparts_minus_1 = (1 << swebp__bitread_getval(&br, 2)) - 1; + if (3 * last_part > bufsize) + return SIMPLEWEBP_CORRUPT_ERROR; + + part_start = buf + last_part * 3; + size_left -= last_part * 3; + + for (p = 0; p < last_part; p++) + { + size_t psize = swebp__to_uint24(sz); + if (psize > size_left) + psize = size_left; + + swebp__bitread_init(vp8d->parts + p, part_start, psize); + part_start += psize; + size_left -= psize; + sz += 3; + } + + swebp__bitread_init(vp8d->parts + last_part, part_start, size_left); + if (part_start >= buf_end) + return SIMPLEWEBP_CORRUPT_ERROR; + } + + /* Parse quantizer */ + { + simplewebp_i32 base_q0, dqy1_dc, dqy2_dc, dqy2_ac, dquv_dc, dquv_ac, i; + base_q0 = swebp__bitread_getval(&br, 7); + dqy1_dc = swebp__bitread_getval(&br, 1) + ? swebp__bitread_getvalsigned(&br, 4) + : 0; + dqy2_dc = swebp__bitread_getval(&br, 1) + ? swebp__bitread_getvalsigned(&br, 4) + : 0; + dqy2_ac = swebp__bitread_getval(&br, 1) + ? swebp__bitread_getvalsigned(&br, 4) + : 0; + dquv_dc = swebp__bitread_getval(&br, 1) + ? swebp__bitread_getvalsigned(&br, 4) + : 0; + dquv_ac = swebp__bitread_getval(&br, 1) + ? swebp__bitread_getvalsigned(&br, 4) + : 0; + + for (i = 0; i < 4; i++) + { + simplewebp_i32 q; + struct swebp__quantmat *m; + + if (segmnt_hdr->use_segment) + q = segmnt_hdr->quantizer[i] + (!segmnt_hdr->absolute_delta) * base_q0; + else + { + if (i > 0) + { + vp8d->dqm[i] = vp8d->dqm[0]; + continue; + } + else + q = base_q0; + } + + m = vp8d->dqm + i; + m->y1_mat[0] = swebp__dctab[swebp__clip(q + dqy1_dc, 127)]; + m->y1_mat[1] = swebp__actab[swebp__clip(q + 0, 127)]; + + m->y2_mat[0] = swebp__dctab[swebp__clip(q + dqy2_dc, 127)] * 2; + m->y2_mat[1] = (swebp__actab[swebp__clip(q + dqy2_ac, 127)] * 101581) >> 16; + if (m->y2_mat[1] < 8) + m->y2_mat[1] = 8; + + m->uv_mat[0] = swebp__dctab[swebp__clip(q + dquv_dc, 117)]; + m->uv_mat[1] = swebp__actab[swebp__clip(q + dquv_ac, 127)]; + + m->uv_quant = q + dquv_ac; + } + } + + /* Ignore "update proba" */ + swebp__bitread_getval(&br, 1); + + /* Parse proba */ + { + struct swebp__proba *proba; + simplewebp_i32 t, b, c, p, v; + + proba = &vp8d->proba; + + for (t = 0; t < 4; t++) + { + for (b = 0; b < 8; b++) + { + for (c = 0; c < 3; c++) + { + for (p = 0; p < 11; p++) + { + v = swebp__bitread_getbit(&br, swebp__coeff_update_proba[t][b][c][p]) + ? swebp__bitread_getval(&br, 8) + : swebp__coeff_proba0[t][b][c][p]; + proba->bands[t][b].probas[c][p] = v; + } + } + } + + for (b = 0; b < 17; b++) + proba->bands_ptr[t][b] = &proba->bands[t][swebp__bands[b]]; + } + + vp8d->use_skip_proba = swebp__bitread_getval(&br, 1); + if (vp8d->use_skip_proba) + vp8d->skip_proba = swebp__bitread_getval(&br, 8); + } + + if (br.eof) + return SIMPLEWEBP_CORRUPT_ERROR; + + vp8d->br = br; + vp8d->ready = 1; + return SIMPLEWEBP_NO_ERROR; +} + +static void swebp__vp8_enter_critical(struct swebp__vp8 *vp8d) +{ + vp8d->tl_mb_x = vp8d->tl_mb_y = 0; + vp8d->br_mb_x = vp8d->mb_w; + vp8d->br_mb_y = vp8d->mb_h; + + if (vp8d->filter_type > 0) + { + simplewebp_i32 s; + struct swebp__filter_header *filt_hdr; + + filt_hdr = &vp8d->filter_header; + + for (s = 0; s < 4; s++) + { + simplewebp_i32 i4x4, base_level; + + if (vp8d->segment_header.use_segment) + base_level = vp8d->segment_header.filter_strength[s] + + filt_hdr->level * (!vp8d->segment_header.absolute_delta); + else + base_level = filt_hdr->level; + + for (i4x4 = 0; i4x4 <= 1; i4x4++) + { + struct swebp__finfo *info; + simplewebp_i32 level; + + info = &vp8d->fstrengths[s][i4x4]; + level = base_level + + filt_hdr->ref_lf_delta[0] * filt_hdr->use_lf_delta + + filt_hdr->mode_lf_delta[0] * i4x4 * filt_hdr->use_lf_delta; + + level = (level < 0) ? 0 : ((level > 63) ? 63 : level); + if (level > 0) + { + simplewebp_i32 ilevel = level; + + if (filt_hdr->sharpness > 0) + { + ilevel >>= 1 + (filt_hdr->sharpness > 4); + + if (ilevel > 9 - filt_hdr->sharpness) + ilevel = 9 - filt_hdr->sharpness; + } + + if (ilevel < 1) + ilevel = 1; + + info->ilevel = ilevel; + info->limit = 2 * level + ilevel; + info->hev_thresh = (level >= 40) + (level >= 15); + } + else + info->limit = 0; + + info->inner = i4x4; + } + } + } +} + +static void swebp__vp8_parse_intra_mode(struct swebp__vp8 *vp8d, simplewebp_i32 mb_x) +{ + simplewebp_u8 *top, *left; + struct swebp__mblockdata *block; + struct swebp__bdec *br; + + top = vp8d->intra_t + 4 * mb_x; + left = vp8d->intra_l; + block = vp8d->mb_data + mb_x; + br = &vp8d->br; + + if (vp8d->segment_header.update_map) + block->segment = !swebp__bitread_getbit(br, vp8d->proba.segments[0]) + ? swebp__bitread_getbit(br, vp8d->proba.segments[1]) + : swebp__bitread_getbit(br, vp8d->proba.segments[2]) + 2; + else + block->segment = 0; + + if (vp8d->use_skip_proba) + block->skip = swebp__bitread_getbit(br, vp8d->skip_proba); + + block->is_i4x4 = !swebp__bitread_getbit(br, 145); + if (!block->is_i4x4) + { + simplewebp_i32 ymode = swebp__bitread_getbit(br, 156) + ? (swebp__bitread_getbit(br, 128) ? 1 : 3) + : (swebp__bitread_getbit(br, 163) ? 2 : 0); + + block->imodes[0] = ymode; + memset(top, ymode, 4); + memset(left, ymode, 4); + } + else + { + simplewebp_u8 *modes; + simplewebp_i32 y; + + modes = block->imodes; + for (y = 0; y < 4; y++) + { + simplewebp_i32 ymode, x; + + ymode = left[y]; + for (x = 0; x < 4; x++) + { + const simplewebp_u8 *const prob = swebp__modes_proba[top[x]][ymode]; + ymode = !swebp__bitread_getbit(br, prob[0]) ? 0 : + !swebp__bitread_getbit(br, prob[1]) ? 1 : + !swebp__bitread_getbit(br, prob[2]) ? 2 : + !swebp__bitread_getbit(br, prob[3]) ? + (!swebp__bitread_getbit(br, prob[4]) ? 3 : + (!swebp__bitread_getbit(br, prob[5]) ? 4 + : 5)) : + (!swebp__bitread_getbit(br, prob[6]) ? 6 : + (!swebp__bitread_getbit(br, prob[7]) ? 7 : + (!swebp__bitread_getbit(br, prob[8]) ? 8 + : 9)) + ); + top[x] = ymode; + } + + memcpy(modes, top, 4); + modes += 4; + left[y] = ymode; + } + } + + block->uvmode = !swebp__bitread_getbit(br, 142) ? 0 : ( + !swebp__bitread_getbit(br, 114) ? 2 : ( + swebp__bitread_getbit(br, 183) ? 1 : 3 + )); +} + +static simplewebp_i32 swebp__vp8_parse_intra_row(struct swebp__vp8 *vp8d) +{ + simplewebp_i32 mb_x; + + for (mb_x = 0; mb_x < vp8d->mb_w; mb_x++) + swebp__vp8_parse_intra_mode(vp8d, mb_x); + + return !vp8d->br.eof; +} + +static void swebp__vp8_init_scanline(struct swebp__vp8 *vp8d) +{ + struct swebp__mblock *const left = vp8d->mb_info - 1; + left->nz = 0; + left->nz_dc = 0; + memset(vp8d->intra_l, 0, sizeof(vp8d->intra_l)); + vp8d->mb_x = 0; +} + +static simplewebp_u8 *swebp__vp8_alloc_memory(struct swebp__vp8 *vp8d, simplewebp_allocator *allocator) +{ + simplewebp_i32 mb_w; + size_t intra_pred_mode_size, top_size, mb_info_size, f_info_size; + size_t yuv_size, mb_data_size, cache_height, cache_size, alpha_size, needed; + simplewebp_u8 *orig_mem, *mem; + + mb_w = vp8d->mb_w; + intra_pred_mode_size = 4 * mb_w; + top_size = sizeof(struct swebp__topsmp) * mb_w; + mb_info_size = (mb_w + 1) * sizeof(struct swebp__mblock); + f_info_size = vp8d->filter_type > 0 ? (mb_w * sizeof(struct swebp__finfo)) : 0; + yuv_size = (32 * 17 + 32 * 9) * sizeof(*vp8d->yuv_b); + mb_data_size = mb_w * sizeof(*vp8d->mb_data); + cache_height = (16 + swebp__fextrarows[vp8d->filter_type]) * 3 / 2; + cache_size = top_size * cache_height; + alpha_size = vp8d->picture_header.width * vp8d->picture_header.width; + + needed = intra_pred_mode_size + + top_size + + mb_info_size + + f_info_size + + yuv_size + + mb_data_size + + cache_size + + alpha_size + 31; + + mem = orig_mem = (simplewebp_u8 *) allocator->alloc(allocator->userdata, needed); + + if (mem) + { + vp8d->mem = mem; + vp8d->mem_size = needed; + + vp8d->intra_t = mem; + mem += intra_pred_mode_size; + + vp8d->yuv_t = (struct swebp__topsmp *) mem; + mem += top_size; + + vp8d->mb_info = ((struct swebp__mblock *) mem) + 1; + mem += mb_info_size; + + vp8d->f_info = f_info_size ? (struct swebp__finfo *) mem : NULL; + mem += f_info_size; + + mem = swebp__align32(mem); + vp8d->yuv_b = mem; + mem += yuv_size; + + vp8d->mb_data = (struct swebp__mblockdata *) mem; + mem += mb_data_size; + + vp8d->cache_y_stride = 16 * mb_w; + vp8d->cache_uv_stride = 8 * mb_w; + { + simplewebp_i32 extra_rows, extra_y, extra_uv; + + extra_rows = swebp__fextrarows[vp8d->filter_type]; + extra_y = extra_rows * vp8d->cache_y_stride; + extra_uv = (extra_rows / 2) * vp8d->cache_uv_stride; + vp8d->cache_y = mem + extra_y; + vp8d->cache_u = vp8d->cache_y + 16 * vp8d->cache_y_stride + extra_uv; + vp8d->cache_v = vp8d->cache_u + 8 * vp8d->cache_uv_stride + extra_uv; + } + mem += cache_size; + + vp8d->alpha_plane = alpha_size ? mem : NULL; + mem += alpha_size; + + memset(vp8d->mb_info - 1, 0, mb_info_size); + swebp__vp8_init_scanline(vp8d); + memset(vp8d->intra_t, 0, intra_pred_mode_size); + } + + return orig_mem; +} + +static const simplewebp_u8 swebp__cat3[] = { 173, 148, 140, 0 }; +static const simplewebp_u8 swebp__cat4[] = { 176, 155, 140, 135, 0 }; +static const simplewebp_u8 swebp__cat5[] = { 180, 157, 141, 134, 130, 0 }; +static const simplewebp_u8 swebp__cat6[] = { 254, 254, 243, 230, 196, 177, 153, 140, 133, 130, 129, 0 }; +static const simplewebp_u8* const swebp__cat3456[] = { + swebp__cat3, + swebp__cat4, + swebp__cat5, + swebp__cat6 +}; +static const simplewebp_u8 swebp__zigzag[16] = { + 0, 1, 4, 8, + 5, 2, 3, 6, + 9, 12, 13, 10, + 7, 11, 14, 15 +}; + +static simplewebp_i32 swebp__get_large_value(struct swebp__bdec *br, const simplewebp_u8 *p) +{ + simplewebp_i32 v; + + if (!swebp__bitread_getbit(br, p[3])) + { + if (!swebp__bitread_getbit(br, p[4])) + v = 2; + else + v = 3 + swebp__bitread_getbit(br, p[5]); + } + else + { + if (!swebp__bitread_getbit(br, p[6])) + { + if (!swebp__bitread_getbit(br, p[7])) + v = 5 + swebp__bitread_getbit(br, 159); + else + { + v = 7 + 2 * swebp__bitread_getbit(br, 165); + v += swebp__bitread_getbit(br, 145); + } + } + else + { + const simplewebp_u8 *tab; + simplewebp_i32 bit1, bit0, cat; + + bit1 = swebp__bitread_getbit(br, p[8]); + bit0 = swebp__bitread_getbit(br, p[9 + bit1]); + cat = 2 * bit1 + bit0; + v = 0; + + for (tab = swebp__cat3456[cat]; *tab; ++tab) + v += v + swebp__bitread_getbit(br, *tab); + + v += 3 + (8 << cat); + } + } + + return v; +} + +static simplewebp_i32 swebp__get_coeffs( + struct swebp__bdec *br, + const struct swebp__bandprobas *prob[], + simplewebp_i32 ctx, + const swebp__quant_t dq, + simplewebp_i32 n, + simplewebp_i16 *out +) +{ + const simplewebp_u8 *p = prob[n]->probas[ctx]; + + for (; n < 16; n++) + { + if (!swebp__bitread_getbit(br, p[0])) + return n; + + while (!swebp__bitread_getbit(br, p[1])) + { + p = prob[++n]->probas[0]; + if (n == 16) + return 1; + } + + { + const swebp__probarray *p_ctx; + simplewebp_i32 v; + + p_ctx = &prob[n + 1]->probas[0]; + + if (!swebp__bitread_getbit(br, p[2])) + { + v = 1; + p = p_ctx[1]; + } + else + { + v = swebp__get_large_value(br, p); + p = p_ctx[2]; + } + + out[swebp__zigzag[n]] = swebp__bitread_getsigned(br, v) * dq[n > 0]; + } + } + + return 16; +} + +static simplewebp_u32 swebp__nz_code_bits(simplewebp_u32 nz_coeffs, simplewebp_i32 nz, simplewebp_i8 dc_nz) +{ + nz_coeffs <<= 2; + nz_coeffs |= (nz > 3) ? 3 : (nz > 1) ? 2 : dc_nz; + return nz_coeffs; +} + +static simplewebp_i32 swebp__vp8_decode_macroblock(struct swebp__vp8 *vp8d, struct swebp__bdec *token_br) +{ + struct swebp__mblock *left, *mb; + struct swebp__mblockdata *block; + simplewebp_u8 skip; + + left = vp8d->mb_info - 1; + mb = vp8d->mb_info + vp8d->mb_x; + block = vp8d->mb_data + vp8d->mb_x; + skip = vp8d->use_skip_proba ? block->skip : 0; + + if (!skip) + { + /* Parse residuals */ + const struct swebp__bandprobas * (*bands)[17], **ac_proba; + struct swebp__quantmat *q; + simplewebp_i16 *dst; + struct swebp__mblock *left_mb; + simplewebp_u8 tnz, lnz; + simplewebp_u32 non0_y, non0_uv, out_t_nz, out_l_nz; + simplewebp_i32 x, y, ch, first; + + bands = vp8d->proba.bands_ptr; + q = &vp8d->dqm[block->segment]; + dst = block->coeffs; + left_mb = vp8d->mb_info - 1; + non0_y = non0_uv = 0; + + memset(dst, 0, 384 * sizeof(simplewebp_i16)); + if (!block->is_i4x4) + { + simplewebp_i16 dc[16]; + simplewebp_i32 ctx, nz; + + memset(dc, 0, sizeof(dc)); + ctx = mb->nz_dc + left_mb->nz_dc; + nz = swebp__get_coeffs(token_br, bands[1], ctx, q->y2_mat, 0, dc); + + mb->nz_dc = left_mb->nz_dc = nz > 0; + if (nz > 1) + swebp__transform_wht(dc, dst); + else + { + simplewebp_i32 i, dc0; + dc0 = (dc[0] + 3) >> 3; + for (i = 0; i < 16; i++) + dst[i * 16] = dc0; + } + + first = 1; + ac_proba = bands[0]; + } + else + { + first = 0; + ac_proba = bands[3]; + } + + tnz = mb->nz & 0xf; + lnz = left_mb->nz & 0xf; + + for (y = 0; y < 4; y++) + { + simplewebp_i32 l; + simplewebp_u32 nz_coeffs; + + l = lnz & 1; + nz_coeffs = 0; + + for (x = 0; x < 4; x++) + { + simplewebp_i32 ctx, nz; + + ctx = l + (tnz & 1); + nz = swebp__get_coeffs(token_br, ac_proba, ctx, q->y1_mat, first, dst); + l = nz > first; + tnz = (tnz >> 1) | (l << 7); + nz_coeffs = swebp__nz_code_bits(nz_coeffs, nz, dst[0] != 0); + dst += 16; + } + + tnz >>= 4; + lnz = (lnz >> 1) | (l << 7); + non0_y = (non0_y << 8) | nz_coeffs; + } + + out_t_nz = tnz; + out_l_nz = lnz >> 4; + + for (ch = 0; ch < 4; ch += 2) + { + simplewebp_u32 nz_coeffs = 0; + + tnz = mb->nz >> (4 + ch); + lnz = left_mb->nz >> (4 + ch); + + for (y = 0; y < 2; y++) + { + simplewebp_i32 l = lnz & 1; + + for (x = 0; x < 2; x++) + { + simplewebp_i32 ctx, nz; + + ctx = l + (tnz & 1); + nz = swebp__get_coeffs(token_br, bands[2], ctx, q->uv_mat, 0, dst); + l = nz > 0; + tnz = (tnz >> 1) | (l << 3); + nz_coeffs = swebp__nz_code_bits(nz_coeffs, nz, dst[0] != 0); + dst += 16; + } + + tnz >>= 2; + lnz = (lnz >> 1) | (l << 5); + } + + non0_uv |= nz_coeffs << (4 * ch); + out_t_nz |= tnz << (4 + ch); + out_l_nz |= (lnz & 0xf0) << ch; + } + + mb->nz = out_t_nz; + left_mb->nz = out_l_nz; + + block->nonzero_y = non0_y; + block->nonzero_uv = non0_uv; + block->dither = (non0_uv & 0xaaaa) ? 0 : q->dither; + + skip = !(non0_y | non0_uv); + } + else + { + left->nz = mb->nz = 0; + if (!block->is_i4x4) + left->nz_dc = mb->nz_dc = 0; + + block->nonzero_y = block->nonzero_uv = block->dither = 0; + } + + if (vp8d->filter_type > 0) + { + struct swebp__finfo *const finfo = vp8d->f_info + vp8d->mb_x; + *finfo = vp8d->fstrengths[block->segment][block->is_i4x4]; + finfo->inner |= !skip; + } + + return !token_br->eof; +} + +static void swebp__do_transform(simplewebp_u32 bits, const simplewebp_i16 *src, simplewebp_u8 *dst) +{ + switch (bits >> 30) + { + case 3: + swebp__transform(src, dst, 0); + break; + case 2: + swebp__transform_ac3(src, dst); + break; + case 1: + swebp__transform_dc(src, dst); + break; + default: + break; + } +} + +static simplewebp_i32 swebp__check_mode(simplewebp_i32 mb_x, simplewebp_i32 mb_y, simplewebp_i32 mode) +{ + if (mode == 0) { + if (mb_x == 0) + return (mb_y == 0) ? 6 : 5; + else + return (mb_y == 0) ? 4 : 0; + } + + return mode; +} + +static void swebp__do_transform_uv(simplewebp_u32 bits, const simplewebp_i16 *src, simplewebp_u8 *dst) +{ + if (bits & 0xff) + { + if (bits & 0xaa) + swebp__transform_uv(src, dst); + else + swebp__transform_dcuv(src, dst); + } +} + +static simplewebp_i32 swebp__multhi(simplewebp_i32 v, simplewebp_i32 coeff) +{ + return (v * coeff) >> 8; +} + +static simplewebp_u8 swebp__yuv2rgb_clip8(simplewebp_i32 v) +{ + return ((v & ~16383) == 0) ? ((simplewebp_u8) (v >> 6)) : (v < 0) ? 0 : 255; +} + +static void swebp__yuv2rgb_plain(simplewebp_u8 y, simplewebp_u8 u, simplewebp_u8 v, struct swebp__pixel *rgb) +{ + simplewebp_i32 yhi = swebp__multhi(y, 19077); + + rgb->r = swebp__yuv2rgb_clip8(yhi + swebp__multhi(v, 26149) - 14234); + rgb->g = swebp__yuv2rgb_clip8(yhi - swebp__multhi(u, 6419) - swebp__multhi(v, 13320) + 8708); + rgb->b = swebp__yuv2rgb_clip8(yhi + swebp__multhi(u, 33050) - 17685); +} + +/* r = top-left, g = top-right, b = bottom-left, a = bottom-right */ +static struct swebp__pixel swebp__do_upsample_center( + const simplewebp_u8 *vtop, + const simplewebp_u8 *vmid, + const simplewebp_u8 *vbot, + size_t xm1, + size_t x, + size_t xp1 +) +{ + /* + Consider these layout: + 0 1 2 3 4 5 + y y y y y y 0 + t1 t2 t3 + y y y y y y 1 + + y y y y y y 2 + u1 u2 u3 + y y y y y y 3 + + y y y y y y 4 + v1 v2 v3 + y y y y y y 5 + + t, u, v are the chroma (CbCr/UV). + + We want to place all of them in y, but note that t, u, and v are centered. + So (note this is XY order, not YX) + y22 = 9 * u2 + 3 * u1 + 3 * t2 + t1 + y32 = 9 * u2 + 3 * u3 + 3 * t2 + t3 + y23 = 9 * u2 + 3 * u1 + 3 * v2 + v1 + y33 = 9 * u2 + 3 * u3 + 3 * v2 + v3 + + Now let's generalize it: assume x, y span from 0 to chroma dimensions: + y[x * 2 +0, y * 2 +0] = 9 * uv[x, y] + 3 * uv[x-1, y] + 3 * uv[x, y-1] + uv[x-1, y-1] + y[x * 2 +1, y * 2 +0] = 9 * uv[x, y] + 3 * uv[x+1, y] + 3 * uv[x, y-1] + uv[x+1, y-1] + y[x * 2 +0, y * 2 +1] = 9 * uv[x, y] + 3 * uv[x-1, y] + 3 * uv[x, y+1] + uv[x-1, y+1] + y[x * 2 +1, y * 2 +1] = 9 * uv[x, y] + 3 * uv[x+1, y] + 3 * uv[x, y+1] + uv[x+1, y+1] + + For rounding, add + 8 to each, then divide the result by 16, so: (formula_above + 8) / 16 + */ + + struct swebp__pixel out; + + out.r = (simplewebp_u8) ((9u * vmid[x] + 3u * vmid[xm1] + 3u * vtop[x] + vtop[xm1] + 8u) / 16u); + out.g = (simplewebp_u8) ((9u * vmid[x] + 3u * vmid[xp1] + 3u * vtop[x] + vtop[xp1] + 8u) / 16u); + out.b = (simplewebp_u8) ((9u * vmid[x] + 3u * vmid[xm1] + 3u * vbot[x] + vbot[xm1] + 8u) / 16u); + out.a = (simplewebp_u8) ((9u * vmid[x] + 3u * vmid[xp1] + 3u * vbot[x] + vbot[xp1] + 8u) / 16u); + return out; +} + +/* This is bilinear interpolation with center chroma. */ +/* See https://stackoverflow.com/a/43784809 */ +static void swebp__upsample_chroma( + const simplewebp_u8 *u, + const simplewebp_u8 *v, + struct swebp__chroma *dst, + size_t w, + size_t h +) +{ + size_t y, fw; + fw = w * 2; + + for (y = 0; y < h; y++) + { + size_t prev_y, next_y, x; + const simplewebp_u8 *uline; + const simplewebp_u8 *vline; + const simplewebp_u8 *ulineprev; + const simplewebp_u8 *vlineprev; + const simplewebp_u8 *ulinenext; + const simplewebp_u8 *vlinenext; + + prev_y = y == 0 ? 0 : (y - 1); + next_y = y == (h - 1) ? y : (y + 1); + ulineprev = u + prev_y * w; + vlineprev = v + prev_y * w; + ulinenext = u + next_y * w; + vlinenext = v + next_y * w; + uline = u + y * w; + vline = v + y * w; + + for (x = 0; x < w; x++) + { + size_t prev_x, next_x, i00, i10, i01, i11; + struct swebp__pixel uvalue, vvalue; + + prev_x = x == 0 ? x : (x - 1); + next_x = x == (w - 1) ? x : (x + 1); + uvalue = swebp__do_upsample_center(ulineprev, uline, ulinenext, prev_x, x, next_x); + vvalue = swebp__do_upsample_center(vlineprev, vline, vlinenext, prev_x, x, next_x); + i00 = (y * 2) * fw + (x * 2); + i10 = (y * 2) * fw + (x * 2 + 1); + i01 = (y * 2 + 1) * fw + (x * 2); + i11 = (y * 2 + 1) * fw + (x * 2 + 1); + + dst[i00].u = uvalue.r; + dst[i00].v = vvalue.r; + dst[i10].u = uvalue.g; + dst[i10].v = vvalue.g; + dst[i01].u = uvalue.b; + dst[i01].v = vvalue.b; + dst[i11].u = uvalue.a; + dst[i11].v = vvalue.a; + } + } +} + +static void swebp__yuva2rgba( + const simplewebp_u8 *yp, + const struct swebp__chroma *uv, + const simplewebp_u8 *a, + size_t w, + size_t h, + struct swebp__pixel *rgba +) +{ + size_t y, x, uvw; + + uvw = ((w + 1) / 2) * 2; + + for (y = 0; y < h; y++) + { + for (x = 0; x < w; x++) + { + swebp__yuv2rgb_plain(yp[y * w + x], uv[y * uvw + x].u, uv[y * uvw + x].v, &rgba[y * w + x]); + rgba[y * w + x].a = a[y * w + x]; + } + } +} + +static simplewebp_error swebp__vp8_process_row(struct swebp__vp8 *vp8d, struct swebp__yuvdst *destination) +{ + simplewebp_i32 filter_row; + + filter_row = vp8d->filter_type > 0 && vp8d->mb_y >= vp8d->tl_mb_y && vp8d->mb_y <= vp8d->br_mb_y; + + /* Reconstruct row */ + { + simplewebp_i32 j, mb_x, mb_y; + simplewebp_u8 *y_dst, *u_dst, *v_dst; + + mb_y = vp8d->mb_y; + y_dst = vp8d->yuv_b + 40; + u_dst = vp8d->yuv_b + 584; + v_dst = vp8d->yuv_b + 600; + + for (j = 0; j < 16; j++) + y_dst[j * 32 - 1] = 129; + for (j = 0; j < 8; j++) + { + u_dst[j * 32 - 1] = 129; + v_dst[j * 32 - 1] = 129; + } + + if (mb_y > 0) + y_dst[-33] = u_dst[-33] = v_dst[-33] = 129; + else + { + memset(y_dst - 33, 127, 21); + memset(u_dst - 33, 127, 9); + memset(v_dst - 33, 127, 9); + } + + for (mb_x = 0; mb_x < vp8d->mb_w; mb_x++) + { + struct swebp__mblockdata *const block = vp8d->mb_data + mb_x; + + if (mb_x > 0) + { + for (j = -1; j < 16; j++) + memcpy(y_dst + j * 32 - 4, y_dst + j * 32 + 12, 4); + for (j = -1; j < 8; j++) + { + memcpy(u_dst + j * 32 - 4, u_dst + j * 32 + 4, 4); + memcpy(v_dst + j * 32 - 4, v_dst + j * 32 + 4, 4); + } + } + + { + struct swebp__topsmp *top_yuv; + const simplewebp_i16 *coeffs; + simplewebp_u32 bits; + simplewebp_i32 n; + + top_yuv = vp8d->yuv_t + mb_x; + coeffs = block->coeffs; + bits = block->nonzero_y; + + if (mb_y > 0) + { + memcpy(y_dst - 32, top_yuv[0].y, 16); + memcpy(u_dst - 32, top_yuv[0].u, 8); + memcpy(v_dst - 32, top_yuv[0].v, 8); + } + + if (block->is_i4x4) + { + simplewebp_u32 *const top_right = (simplewebp_u32 *) (y_dst - 32 + 16); + + if (mb_y > 0) + { + if (mb_x >= vp8d->mb_w - 1) + memset(top_right, top_yuv[0].y[15], 4); + else + memcpy(top_right, top_yuv[1].y, 4); + } + + top_right[32] = top_right[64] = top_right[96] = top_right[0]; + + for (n = 0; n < 16; n++, bits <<= 2) + { + /* kScan[n] = (n & 3) * 4 + (n >> 2) * 128 */ + simplewebp_u8 *const dst = y_dst + ((n & 3) * 4 + (n >> 2) * 128); + swebp__predluma4(block->imodes[n], dst); + swebp__do_transform(bits, coeffs + n * 16, dst); + } + } + else + { + const simplewebp_i32 pred_func = swebp__check_mode(mb_x, mb_y, block->imodes[0]); + swebp__predluma16(pred_func, y_dst); + + if (bits) + { + for (n = 0; n < 16; n++, bits <<= 2) + { + simplewebp_u8 *const dst = y_dst + ((n & 3) * 4 + (n >> 2) * 128); + swebp__do_transform(bits, coeffs + n * 16, dst); + } + } + } + + { + /* Chroma */ + simplewebp_u32 bits_uv; + simplewebp_i32 pred_func; + + bits_uv = block->nonzero_uv; + pred_func = swebp__check_mode(mb_x, mb_y, block->uvmode); + + swebp__predchroma8(pred_func, u_dst); + swebp__predchroma8(pred_func, v_dst); + swebp__do_transform_uv(bits_uv >> 0, coeffs + 16 * 16, u_dst); + swebp__do_transform_uv(bits_uv >> 8, coeffs + 20 * 16, v_dst); + } + + if (mb_y < vp8d->mb_h - 1) + { + memcpy(top_yuv[0].y, y_dst + 15 * 32, 16); + memcpy(top_yuv[0].u, u_dst + 7 * 32, 8); + memcpy(top_yuv[0].v, v_dst + 7 * 32, 8); + } + } + + /* Transfer reconstructed samples */ + { + simplewebp_u8 *y_out, *u_out, *v_out; + + y_out = vp8d->cache_y + mb_x * 16; + u_out = vp8d->cache_u + mb_x * 8; + v_out = vp8d->cache_v + mb_x * 8; + + for (j = 0; j < 16; j++) + memcpy(y_out + j * vp8d->cache_y_stride, y_dst + j * 32, 16); + for (j = 0; j < 8; j++) + { + memcpy(u_out + j * vp8d->cache_uv_stride, u_dst + j * 32, 8); + memcpy(v_out + j * vp8d->cache_uv_stride, v_dst + j * 32, 8); + } + } + } + } + + /* Finish row */ + { + simplewebp_i32 extra_y_rows, ysize, uvsize, mb_y; + simplewebp_u8 *ydst, *udst, *vdst, is_first_row, is_last_row; + + extra_y_rows = swebp__fextrarows[vp8d->filter_type]; + ysize = extra_y_rows * vp8d->cache_y_stride; + uvsize = (extra_y_rows / 2) * vp8d->cache_uv_stride; + ydst = vp8d->cache_y - ysize; + udst = vp8d->cache_u - uvsize; + vdst = vp8d->cache_v - uvsize; + mb_y = vp8d->mb_y; + is_first_row = mb_y == 0; + is_last_row = mb_y >= vp8d->br_mb_y - 1; + + if (filter_row) + { + /* Filter row */ + simplewebp_i32 mb_x; + + for (mb_x = vp8d->tl_mb_x; mb_x < vp8d->br_mb_x; mb_x++) + { + simplewebp_i32 y_bps, ilevel, limit; + struct swebp__finfo *f_info; + simplewebp_u8 *y_dst; + + f_info = vp8d->f_info + mb_x; + limit = f_info->limit; + + if (limit > 0) + { + ilevel = f_info->ilevel; + y_bps = vp8d->cache_y_stride; + y_dst = vp8d->cache_y + mb_x * 16; + + if (vp8d->filter_type == 1) + { + /* Simple filter */ + if (mb_x > 0) + swebp__simple_hfilter16(y_dst, y_bps, limit + 4); + if (f_info->inner) + swebp__simple_hfilter16_i(y_dst, y_bps, limit); + if (mb_y > 0) + swebp__simple_vfilter16(y_dst, y_bps, limit + 4); + if (f_info->inner) + swebp__simple_vfilter16_i(y_dst, y_bps, limit); + } + else + { + /* Complex filter */ + simplewebp_i32 uv_bps, hev_thresh; + simplewebp_u8 *u_dst, *v_dst; + + uv_bps = vp8d->cache_uv_stride; + hev_thresh = f_info->hev_thresh; + u_dst = vp8d->cache_u + mb_x * 8; + v_dst = vp8d->cache_v + mb_x * 8; + + if (mb_x > 0) + { + swebp__hfilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh); + swebp__hfilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh); + } + if (f_info->inner) + { + swebp__hfilter16_i(y_dst, y_bps, limit, ilevel, hev_thresh); + swebp__hfilter8_i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh); + } + if (mb_y > 0) + { + swebp__vfilter16(y_dst, y_bps, limit + 4, ilevel, hev_thresh); + swebp__vfilter8(u_dst, v_dst, uv_bps, limit + 4, ilevel, hev_thresh); + } + if (f_info->inner) + { + swebp__vfilter16_i(y_dst, y_bps, limit, ilevel, hev_thresh); + swebp__vfilter8_i(u_dst, v_dst, uv_bps, limit, ilevel, hev_thresh); + } + } + } + } + } + + { + /* Write YUV */ + simplewebp_u8 *y_out, *u_out, *v_out; + simplewebp_i32 y_start, y_end; + + y_start = mb_y * 16; + y_end = (mb_y + 1) * 16; + if (!is_first_row) + { + y_start -= extra_y_rows; + y_out = ydst; + u_out = udst; + v_out = vdst; + } + else + { + y_out = vp8d->cache_y; + u_out = vp8d->cache_u; + v_out = vp8d->cache_v; + } + + if (!is_last_row) + y_end -= extra_y_rows; + if (y_end > vp8d->picture_header.height) + y_end = vp8d->picture_header.height; + + { + /* Copy YUV buffer */ + simplewebp_i32 row, iwidth, iwidth2, uv_start, uv_end; + + iwidth = vp8d->picture_header.width; + iwidth2 = (iwidth + 1) / 2; + uv_start = y_start / 2; + uv_end = (y_end + 1) / 2; + + /* Copy Y and A buffer */ + for (row = y_start; row < y_end; row++) + memcpy(destination->y + row * iwidth, y_out + (row - y_start) * vp8d->cache_y_stride, iwidth); + + /* Copy U and V buffer */ + for (row = uv_start; row < uv_end; row++) + { + memcpy(destination->u + row * iwidth2, u_out + (row - uv_start) * vp8d->cache_uv_stride, iwidth2); + memcpy(destination->v + row * iwidth2, v_out + (row - uv_start) * vp8d->cache_uv_stride, iwidth2); + } + } + } + + /* Rotate top samples */ + if (!is_last_row) + { + memcpy(vp8d->cache_y - ysize, ydst + 16 * vp8d->cache_y_stride, ysize); + memcpy(vp8d->cache_u - uvsize, udst + 8 * vp8d->cache_uv_stride, uvsize); + memcpy(vp8d->cache_v - uvsize, vdst + 8 * vp8d->cache_uv_stride, uvsize); + } + } + + return SIMPLEWEBP_NO_ERROR; +} + +static simplewebp_error swebp__vp8_parse_frame(struct swebp__vp8 *vp8d, struct swebp__yuvdst *destination) +{ + simplewebp_error err; + + for (vp8d->mb_y = 0; vp8d->mb_y < vp8d->br_mb_y; vp8d->mb_y++) + { + struct swebp__bdec *token_br = &vp8d->parts[vp8d->mb_y & vp8d->nparts_minus_1]; + + if (!swebp__vp8_parse_intra_row(vp8d)) + return SIMPLEWEBP_CORRUPT_ERROR; + + for (vp8d->mb_x = 0; vp8d->mb_x < vp8d->mb_w; vp8d->mb_x++) + { + if (!swebp__vp8_decode_macroblock(vp8d, token_br)) + return SIMPLEWEBP_CORRUPT_ERROR; + } + + /* Prepare for next scanline and reconstruct it */ + swebp__vp8_init_scanline(vp8d); + err = swebp__vp8_process_row(vp8d, destination); + + if (err != SIMPLEWEBP_NO_ERROR) + return err; + } + + return SIMPLEWEBP_NO_ERROR; +} + +static simplewebp_error swebp__decode_lossy(simplewebp *simplewebp, struct swebp__yuvdst *destination, void *settings) +{ + simplewebp_input input; + size_t vp8size; + simplewebp_u8 *vp8buffer, *decoder_mem; + struct swebp__vp8 *vp8d; + simplewebp_error err; + + vp8d = &simplewebp->decoder.vp8; + input = simplewebp->vp8_input; + + if (!swebp__seek(0, &input)) + return SIMPLEWEBP_IO_ERROR; + + vp8size = swebp__proxy_size(input.userdata); + /* Sanity check */ + if (vp8d->frame_header.partition_length > vp8size) + return SIMPLEWEBP_CORRUPT_ERROR; + + /* Read all VP8 chunk */ + vp8buffer = (simplewebp_u8 *) swebp__alloc(simplewebp, vp8size); + if (vp8buffer == NULL) + return SIMPLEWEBP_ALLOC_ERROR; + + if (!swebp__read2(vp8size, vp8buffer, &input)) + { + swebp__dealloc(simplewebp, vp8buffer); + return SIMPLEWEBP_CORRUPT_ERROR; + } + + /* Let's skip already-read stuff at swebp__load_lossy */ + if (!swebp__seek(10, &input)) + { + swebp__dealloc(simplewebp, vp8buffer); + return SIMPLEWEBP_CORRUPT_ERROR; + } + + err = swebp__load_vp8_header(&simplewebp->decoder.vp8, vp8buffer + 10, vp8size - 10); + if (err != SIMPLEWEBP_NO_ERROR) + { + swebp__dealloc(simplewebp, vp8buffer); + return err; + } + + /* Enter critical */ + swebp__vp8_enter_critical(&simplewebp->decoder.vp8); + decoder_mem = swebp__vp8_alloc_memory(vp8d, &simplewebp->allocator); + + if (decoder_mem == NULL) + { + swebp__dealloc(simplewebp, vp8buffer); + return SIMPLEWEBP_ALLOC_ERROR; + } + + err = swebp__vp8_parse_frame(vp8d, destination); + if (err != SIMPLEWEBP_NO_ERROR) + { + swebp__dealloc(simplewebp, decoder_mem); + swebp__dealloc(simplewebp, vp8buffer); + return err; + } + + /* Exit critical and cleanup */ + swebp__dealloc(simplewebp, decoder_mem); + swebp__dealloc(simplewebp, vp8buffer); + vp8d->mem = NULL; + vp8d->mem_size = 0; + memset(&vp8d->br, 0, sizeof(struct swebp__bdec)); + vp8d->ready = 0; + + return swebp__alpha_decode(simplewebp, destination->a); +} + +struct swebp__vp8l_code_node +{ + simplewebp_u16 child[2]; +}; + +struct swebp__vp8l_code +{ + simplewebp_u16 size; + /* Constructing root: symbol[0] | (symbol[1] << 8) */ + simplewebp_u8 symbol[2]; + struct swebp__vp8l_code_node *tree; +}; + +struct swebp__vp8l_group +{ + struct swebp__vp8l_code code[5]; +}; + +const simplewebp_u16 swebp__vp8l_literals_count = 256; +const simplewebp_u16 swebp__vp8l_distances_count = 40; +const simplewebp_u16 swebp__vp8l_litlen_count = 256 + 24; +const size_t swebp__vp8l_offset_count = 120; + +static const simplewebp_u8 swebp__vp8l_lencode_order[19] = { + 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, +}; + +static const simplewebp_i8 swebp__vp8l_offsets[120][2] = { + { 0, 1}, { 1, 0 }, { 1, 1}, {-1, 1}, { 0, 2}, { 2, 0}, + { 1, 2}, {-1, 2 }, { 2, 1}, {-2, 1}, { 2, 2}, {-2, 2}, + { 0, 3}, { 3, 0 }, { 1, 3}, {-1, 3}, { 3, 1}, {-3, 1}, + { 2, 3}, {-2, 3 }, { 3, 2}, {-3, 2}, { 0, 4}, { 4, 0}, + { 1, 4}, {-1, 4 }, { 4, 1}, {-4, 1}, { 3, 3}, {-3, 3}, + { 2, 4}, {-2, 4 }, { 4, 2}, {-4, 2}, { 0, 5}, { 3, 4}, + {-3, 4}, { 4, 3 }, {-4, 3}, { 5, 0}, { 1, 5}, {-1, 5}, + { 5, 1}, {-5, 1 }, { 2, 5}, {-2, 5}, { 5, 2}, {-5, 2}, + { 4, 4}, {-4, 4 }, { 3, 5}, {-3, 5}, { 5, 3}, {-5, 3}, + { 0, 6}, { 6, 0 }, { 1, 6}, {-1, 6}, { 6, 1}, {-6, 1}, + { 2, 6}, {-2, 6 }, { 6, 2}, {-6, 2}, { 4, 5}, {-4, 5}, + { 5, 4}, {-5, 4 }, { 3, 6}, {-3, 6}, { 6, 3}, {-6, 3}, + { 0, 7}, { 7, 0 }, { 1, 7}, {-1, 7}, { 5, 5}, {-5, 5}, + { 7, 1}, {-7, 1 }, { 4, 6}, {-4, 6}, { 6, 4}, {-6, 4}, + { 2, 7}, {-2, 7 }, { 7, 2}, {-7, 2}, { 3, 7}, {-3, 7}, + { 7, 3}, {-7, 3 }, { 5, 6}, {-5, 6}, { 6, 5}, {-6, 5}, + { 8, 0}, { 4, 7 }, {-4, 7}, { 7, 4}, {-7, 4}, { 8, 1}, + { 8, 2}, { 6, 6 }, {-6, 6}, { 8, 3}, { 5, 7}, {-5, 7}, + { 7, 5}, {-7, 5 }, { 8, 4}, { 6, 7}, {-6, 7}, { 7, 6}, + {-7, 6}, { 8, 5 }, { 7, 7}, {-7, 7}, { 8, 6}, { 8, 7}, +}; + +static void swebp__vp8l_insert_code( + struct swebp__vp8l_code_node *tree, + simplewebp_u16 *root, + simplewebp_u16 size, + simplewebp_u16 symbol, + simplewebp_u16 word, + simplewebp_u16 *used, + simplewebp_u8 length +) +{ + while (length) + { + simplewebp_u16 index; + struct swebp__vp8l_code_node *node; + + index = *root; + if (index < size) + { + *root = (*used)++; + node = tree + (*root - size); + } + else + node = tree + (index - size); + + root = &node->child[(word >> --length) & 1]; + } + + *root = symbol; +} + +static simplewebp_error swebp__vp8l_canonical_code( + simplewebp *simplewebp, + simplewebp_u8 *lengths, + simplewebp_u16 size, + struct swebp__vp8l_code *code, + struct swebp__vp8l_code_node *treemem +) +{ + simplewebp_u16 base[16], real_size, current_base, root, used; + struct swebp__vp8l_code_node *tree; + size_t i; + + memset(base, 0, sizeof(base)); + real_size = 0; + current_base = 0; + root = 0; + used = size; + tree = NULL; + + for (i = 0; i < size; i++) + { + simplewebp_u8 length = lengths[i]; + if (length > 0) + { + base[length - 1]++; + real_size++; + } + } + real_size--; + + for (i = 0; i < 16; i++) + { + simplewebp_u16 c = base[i]; + base[i] = current_base; + current_base = (current_base + c) << 1; + } + + if (treemem) + tree = treemem; + else + { + tree = (struct swebp__vp8l_code_node*) swebp__alloc( + simplewebp, + sizeof(struct swebp__vp8l_code_node) * real_size + ); + if (!tree) + return SIMPLEWEBP_ALLOC_ERROR; + } + + for (i = 0; i < real_size; i++) + { + tree[i].child[0] = 0; + tree[i].child[1] = 0; + } + + for (i = 0; i < size; i++) + { + simplewebp_u8 l = lengths[i]; + if (l > 0) + swebp__vp8l_insert_code(tree, &root, size, (simplewebp_u16) i, base[l - 1]++, &used, l); + } + + code->size = size; + code->tree = tree; + code->symbol[0] = root & 0xFF; + code->symbol[1] = (root >> 8) & 0xFF; + + return SIMPLEWEBP_NO_ERROR; +} + +static simplewebp_u16 swebp__vp8l_read_code(struct swebp__vp8l_bdec *br, struct swebp__vp8l_code *code) +{ + if (code->tree) + { + simplewebp_u16 index = code->symbol[0] | (((simplewebp_u16) code->symbol[1]) << 8); + + while (index >= code->size) + index = code->tree[index - code->size].child[swebp__vp8l_bitread_read(br, 1)]; + + return index; + } + else + return code->symbol[code->size < 2 ? 0 : swebp__vp8l_bitread_read(br, 1)]; +} + +static simplewebp_error swebp__vp8l_decode_code_complex( + simplewebp *simplewebp, + struct swebp__vp8l_bdec *br, + struct swebp__vp8l_code *code, + simplewebp_u16 size +) +{ + size_t i; + simplewebp_u8 lencode_lengths[19], lencode_read; + simplewebp_u8 lengths[256 + 24 + 2048]; + simplewebp_u16 limit, count, p; + simplewebp_error err; + struct swebp__vp8l_code_node lencode_treemem[18]; + struct swebp__vp8l_code lc; + + lencode_read = (simplewebp_u8) swebp__vp8l_bitread_read(br, 4) + 4; + for (i = 0; i < 19; i++) + lencode_lengths[swebp__vp8l_lencode_order[i]] = i < lencode_read ? swebp__vp8l_bitread_read(br, 3) : 0; + + if (swebp__vp8l_bitread_read(br, 1)) + limit = swebp__vp8l_bitread_read(br, swebp__vp8l_bitread_read(br, 3) * 2 + 2) + 2; + else + limit = size; + + if (br->eos) + return SIMPLEWEBP_IO_ERROR; + + err = swebp__vp8l_canonical_code(simplewebp, lencode_lengths, 19, &lc, lencode_treemem); + if (err != SIMPLEWEBP_NO_ERROR) + return err; + + count = 0; + p = 8; + + for (i = 0; count < size && i < limit; i++) + { + simplewebp_u16 s, c; + + s = swebp__vp8l_read_code(br, &lc); + c = 0; + + if (br->eos) + { + swebp__dealloc(simplewebp, lc.tree); + return SIMPLEWEBP_IO_ERROR; + } + + switch (s) + { + default: + case 0: + lengths[count++] = (simplewebp_u8) s; + continue; + /* fall through */ + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + lengths[count++] = (simplewebp_u8) (p = s); + continue; + case 16: + s = 3 + swebp__vp8l_bitread_read(br, 2); + c = p; + break; + case 17: + s = 3 + swebp__vp8l_bitread_read(br, 3); + c = 0; + break; + case 18: + s = 11 + swebp__vp8l_bitread_read(br, 7); + c = 0; + break; + } + + if (br->eos) + { + swebp__dealloc(simplewebp, lc.tree); + return SIMPLEWEBP_IO_ERROR; + } + + while (s--) + lengths[count++] = (simplewebp_u8) c; + } + + err = swebp__vp8l_canonical_code(simplewebp, lengths, count, code, NULL); + return err; +} + +static simplewebp_error swebp__vp8l_decode_code( + simplewebp *simplewebp, + struct swebp__vp8l_bdec *br, + struct swebp__vp8l_code *code, + simplewebp_u16 size +) +{ + if (swebp__vp8l_bitread_read(br, 1)) + { + simplewebp_bool two_symbols = (simplewebp_bool) swebp__vp8l_bitread_read(br, 1); + code->tree = NULL; + code->size = two_symbols + 1; + code->symbol[0] = swebp__vp8l_bitread_read(br, 1 + swebp__vp8l_bitread_read(br, 1) * 7); + code->symbol[1] = two_symbols ? swebp__vp8l_bitread_read(br, 8) : 0; + return br->eos ? SIMPLEWEBP_IO_ERROR : SIMPLEWEBP_NO_ERROR; + } + else + return swebp__vp8l_decode_code_complex(simplewebp, br, code, size); +} + +static simplewebp_error swebp__vp8l_decode_group( + simplewebp *simplewebp, + struct swebp__vp8l_bdec *br, + struct swebp__vp8l_group *group, + simplewebp_u8 bits +) +{ + size_t i; + simplewebp_u16 sizes[5]; + sizes[0] = swebp__vp8l_litlen_count + (bits ? 1 << bits : 0); + sizes[1] = sizes[2] = sizes[3] = swebp__vp8l_literals_count; + sizes[4] = swebp__vp8l_distances_count; + + for (i = 0; i < 5; i++) + { + simplewebp_error err; + struct swebp__vp8l_code *code; + + code = group->code + i; + err = swebp__vp8l_decode_code(simplewebp, br, code, sizes[i]); + + if (err != SIMPLEWEBP_NO_ERROR) + { + /* Free all tree pointers */ + for (; i != (size_t) -1; i--) + { + code = group->code + i; + swebp__dealloc(simplewebp, code->tree); + code->tree = NULL; + } + + return err; + } + } + + return SIMPLEWEBP_NO_ERROR; +} + +static size_t swebp__hash_color(simplewebp_u8 bits, struct swebp__pixel c) +{ + size_t value = (c.a << 24) | (c.r << 16) | (c.g << 8) | c.b; + return ((0x1e35a7bd * value) & 0xFFFFFFFF) >> (32 - bits); +} + +static void swebp__vp8l_put_cache(simplewebp_u8 bits, struct swebp__pixel *ccache, struct swebp__pixel color) +{ + if (bits) + ccache[swebp__hash_color(bits, color)] = color; +} + +static size_t swebp__vp8l_lendst(struct swebp__vp8l_bdec *br, simplewebp_u16 c) +{ + size_t extra; + + if (c < 4) + return c; + + extra = (c - 2) >> 1; + return ((2 + (c & 1)) << extra) + swebp__vp8l_bitread_read(br, (int) extra); +} + +static simplewebp_error swebp__decode_vp8l_image( + simplewebp *simplewebp, + struct swebp__vp8l_bdec *br, + simplewebp_bool is_main, + size_t width, + size_t height, + struct swebp__pixel **dest +) +{ + simplewebp_u8 ccache_bits, entropy_bits; + struct swebp__pixel *color_cache, *entropy, *image; + struct swebp__vp8l_group *groups; + size_t group_count, entropy_stride, i; + simplewebp_error err; + + color_cache = NULL; + group_count = 1; + entropy_bits = 0; + entropy_stride = 0; + entropy = NULL; + err = SIMPLEWEBP_NO_ERROR; + + if (*dest == NULL) + { + *dest = (struct swebp__pixel *) swebp__alloc(simplewebp, width * height * 4); + if (*dest == NULL) + return SIMPLEWEBP_ALLOC_ERROR; + } + image = *dest; + + ccache_bits = swebp__vp8l_bitread_read(br, 1) ? swebp__vp8l_bitread_read(br, 4) : 0; + if (br->eos) + return SIMPLEWEBP_IO_ERROR; + if (ccache_bits) + { + color_cache = (struct swebp__pixel*) swebp__alloc(simplewebp, (1 << ccache_bits) * 4); + if (!color_cache) + return SIMPLEWEBP_ALLOC_ERROR; + } + + if (is_main) + { + if (swebp__vp8l_bitread_read(br, 1)) + { + size_t block_mask, ew, eh, group; + struct swebp__pixel *ep; + + entropy_bits = swebp__vp8l_bitread_read(br, 3) + 2; + block_mask = (1 << entropy_bits) - 1; + ew = (width + block_mask) >> entropy_bits; + eh = (height + block_mask) >> entropy_bits; + ep = NULL; + entropy_stride = ew; + + err = swebp__decode_vp8l_image(simplewebp, br, 0, ew, eh, &ep); + if (err != SIMPLEWEBP_NO_ERROR) + { + swebp__dealloc(simplewebp, ep); + swebp__dealloc(simplewebp, color_cache); + return err; + } + + entropy = ep; + + for (i = 0; i < ew * eh; i++) + { + group = (ep[i].r << 8) | ep[i].g; + if (group_count <= group) + group_count = group + 1; + } + } + + if (br->eos) + { + swebp__dealloc(simplewebp, entropy); + swebp__dealloc(simplewebp, color_cache); + return SIMPLEWEBP_IO_ERROR; + } + } + + groups = (struct swebp__vp8l_group*) swebp__alloc(simplewebp, sizeof(struct swebp__vp8l_group) * group_count); + if (!groups) + { + swebp__dealloc(simplewebp, entropy); + swebp__dealloc(simplewebp, color_cache); + return SIMPLEWEBP_ALLOC_ERROR; + } + memset(groups, 0, sizeof(struct swebp__vp8l_group) * group_count); + + for (i = 0; i < group_count; i++) + { + err = swebp__vp8l_decode_group(simplewebp, br, groups + i, ccache_bits); + if (err != SIMPLEWEBP_NO_ERROR) + break; + } + + if (err == SIMPLEWEBP_NO_ERROR) + { + /* Main image decoding routines. */ + for (i = 0; i < width * height;) + { + struct swebp__pixel *pixel; + struct swebp__vp8l_group *g = groups; + simplewebp_u16 codeword; + size_t j; + + pixel = image + i; + + if (entropy) + { + size_t ex, ey; + struct swebp__pixel *ep; + + ex = (i % width) >> entropy_bits; + ey = (i / width) >> entropy_bits; + ep = entropy + entropy_stride * ey + ex; + g += (ep->r << 8) | ep->g; + } + + codeword = swebp__vp8l_read_code(br, &g->code[0]); + + if (codeword < swebp__vp8l_literals_count) + { + struct swebp__pixel color = {0}; + color.r = (simplewebp_u8) swebp__vp8l_read_code(br, &g->code[1]); + color.g = (simplewebp_u8) codeword; + color.b = (simplewebp_u8) swebp__vp8l_read_code(br, &g->code[2]); + color.a = (simplewebp_u8) swebp__vp8l_read_code(br, &g->code[3]); + *pixel = color; + i++; + + swebp__vp8l_put_cache(ccache_bits, color_cache, color); + } + else if (codeword < swebp__vp8l_litlen_count) + { + size_t length, distance; + ptrdiff_t offset; + simplewebp_u16 distcode; + + length = swebp__vp8l_lendst(br, codeword - swebp__vp8l_literals_count); + distcode = swebp__vp8l_read_code(br, &g->code[4]); + distance = swebp__vp8l_lendst(br, distcode); + + if (distance < swebp__vp8l_offset_count) + offset = swebp__vp8l_offsets[distance][0] + swebp__vp8l_offsets[distance][1] * width; + else + offset = distance - swebp__vp8l_offset_count + 1; + offset = offset < 1 ? 1 : offset; + + for (j = 0; j <= length; j++) + { + *pixel = pixel[-offset]; + swebp__vp8l_put_cache(ccache_bits, color_cache, *pixel); + i++; + pixel++; + } + } + else + { + *pixel = color_cache[codeword - swebp__vp8l_litlen_count]; + i++; + } + } + } + + /* Loop free memory */ + for (i = 0; i < group_count; i++) + { + size_t j; + /* And the tree */ + for (j = 0; j < 5; j++) + swebp__dealloc(simplewebp, groups[i].code[j].tree); + } + swebp__dealloc(simplewebp, entropy); + swebp__dealloc(simplewebp, color_cache); + swebp__dealloc(simplewebp, groups); + + return err; +} + +static simplewebp_error swebp__decode_vp8l_transform_data( + simplewebp *simplewebp, + struct swebp__vp8l_bdec *br, + size_t width, + size_t height, + simplewebp_u8 *bits_out, + struct swebp__pixel **dest +) +{ + int bits; + size_t block_mask, data_w, data_h; + + bits = swebp__vp8l_bitread_read(br, 3) + 2; + block_mask = (1 << bits) - 1; + data_w = (width + block_mask) >> bits; + data_h = (height + block_mask) >> bits; + *bits_out = (simplewebp_u8) bits; + + return swebp__decode_vp8l_image(simplewebp, br, 0, data_w, data_h, dest); +} + +static void swebp__batch_free(simplewebp *simplewebp, void **ptr, size_t count) +{ + size_t i; + for (i = 0; i < count; i++) + { + if (ptr[i]) + swebp__dealloc(simplewebp, ptr[i]); + } +} + +static simplewebp_u8 swebp__vp8l_index_reduction(simplewebp_u32 color_count) +{ + if (color_count > 16) + return 0; + else if (color_count > 4) + return 1; + else if (color_count > 2) + return 2; + else + return 3; +} + +static simplewebp_error swebp__decode_color_index_transform( + simplewebp *simplewebp, + struct swebp__vp8l_bdec *br, + size_t width, + size_t height, + simplewebp_u8 *index_size_out, + struct swebp__pixel **dest +) +{ + simplewebp_u32 color_count, i; + simplewebp_error err; + struct swebp__pixel *pixel_data; + + color_count = swebp__vp8l_bitread_read(br, 8); + *index_size_out = (simplewebp_u8) color_count++; + if (br->eos) + return SIMPLEWEBP_CORRUPT_ERROR; + + err = swebp__decode_vp8l_image(simplewebp, br, 0, color_count, 1, dest); + if (err != SIMPLEWEBP_NO_ERROR) + return err; + + pixel_data = *dest; + /* Reverse subtraction coding */ + for (i = 1; i < color_count; i++) + { + struct swebp__pixel *prev = &pixel_data[i - 1]; + pixel_data[i].r += prev->r; + pixel_data[i].g += prev->g; + pixel_data[i].b += prev->b; + pixel_data[i].a += prev->a; + } + + return SIMPLEWEBP_NO_ERROR; +} + +static struct swebp__pixel swebp__average2(struct swebp__pixel a, struct swebp__pixel b) +{ + struct swebp__pixel result; + result.r = ((simplewebp_u16) a.r + (simplewebp_u16) b.r) / 2; + result.g = ((simplewebp_u16) a.g + (simplewebp_u16) b.g) / 2; + result.b = ((simplewebp_u16) a.b + (simplewebp_u16) b.b) / 2; + result.a = ((simplewebp_u16) a.a + (simplewebp_u16) b.a) / 2; + return result; +} + +static struct swebp__pixel swebp__select(struct swebp__pixel l, struct swebp__pixel t, struct swebp__pixel tl) +{ + int red = (int) l.r + (int) t.r - (int) tl.r; + int green = (int) l.g + (int) t.g - (int) tl.g; + int blue = (int) l.b + (int) t.b - (int) tl.b; + int alpha = (int) l.a + (int) t.a - (int) tl.a; + int pl = abs(alpha - l.a) + abs(red - l.r) + abs(green - l.g) + abs(blue - l.b); + int pt = abs(alpha - t.a) + abs(red - t.r) + abs(green - t.g) + abs(blue - t.b); + + return pl < pt ? l : t; +} + +static struct swebp__pixel swebp__clamp_add_subtract_full( + struct swebp__pixel a, + struct swebp__pixel b, + struct swebp__pixel c +) +{ + struct swebp__pixel result; + result.r = swebp__clip((int) a.r + (int) b.r - (int) c.r, 255); + result.g = swebp__clip((int) a.g + (int) b.g - (int) c.g, 255); + result.b = swebp__clip((int) a.b + (int) b.b - (int) c.b, 255); + result.a = swebp__clip((int) a.a + (int) b.a - (int) c.a, 255); + return result; +} + +static struct swebp__pixel swebp__clamp_add_subtract_half(struct swebp__pixel a, struct swebp__pixel b) +{ + struct swebp__pixel result; + result.r = swebp__clip((int) a.r + ((int) a.r - (int) b.r) / 2, 255); + result.g = swebp__clip((int) a.g + ((int) a.g - (int) b.g) / 2, 255); + result.b = swebp__clip((int) a.b + ((int) a.b - (int) b.b) / 2, 255); + result.a = swebp__clip((int) a.a + ((int) a.a - (int) b.a) / 2, 255); + return result; +} + +static struct swebp__pixel swebp__apply_predictor( + simplewebp_u8 type, + struct swebp__pixel *l, + struct swebp__pixel *tl, + struct swebp__pixel *t, + struct swebp__pixel *tr +) +{ + const struct swebp__pixel black = {0, 0, 0, 255}; + + switch (type) + { + case 0: + default: + return black; + case 1: + return *l; + case 2: + return *t; + case 3: + return *tr; + case 4: + return *tl; + case 5: + return swebp__average2(swebp__average2(*l, *tr), *t); + case 6: + return swebp__average2(*l, *tl); + case 7: + return swebp__average2(*l, *t); + case 8: + return swebp__average2(*tl, *t); + case 9: + return swebp__average2(*tr, *t); + case 10: + return swebp__average2(swebp__average2(*l, *tl), swebp__average2(*tr, *t)); + case 11: + return swebp__select(*l, *t, *tl); + case 12: + return swebp__clamp_add_subtract_full(*l, *t, *tl); + case 13: + return swebp__clamp_add_subtract_half(swebp__average2(*l, *t), *tl); + } +} + +static size_t swebp__subsample_size(size_t size, size_t sampling_bits) +{ + return (size + (((size_t) 1) << sampling_bits) - 1) >> sampling_bits; +} + +static void swebp__apply_predictor_transform( + struct swebp__pixel *rgba, + size_t width, + size_t height, + simplewebp_u8 bits, + struct swebp__pixel *predictor_data +) +{ + size_t x, y; + size_t tiles_per_row = swebp__subsample_size(width, bits); + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + struct swebp__pixel result; + simplewebp_u8 type = 0; + + if (x > 0) + { + if (y > 0) + { + size_t tile_x = x >> bits; + size_t tile_y = y >> bits; + size_t tile_index = tile_y * tiles_per_row + tile_x; + type = predictor_data[tile_index].g; + } + else + type = 1; + } + else + type = y > 0 ? 2 : 0; + + result = swebp__apply_predictor(type, rgba - 1, rgba - (width + 1), rgba - width, rgba - (width - 1)); + rgba->r += result.r; + rgba->g += result.g; + rgba->b += result.b; + rgba->a += result.a; + rgba++; + } + } +} + +static simplewebp_u8 swebp__color_delta(int c1, int c2) { + int sc1 = c1 >= 128 ? -256 + c1 : c1; + int sc2 = c2 >= 128 ? -256 + c2 : c2; + return (simplewebp_u8) ((sc1 * sc2) >> 5); +} + +static void swebp__apply_color_transform( + struct swebp__pixel *rgba, + size_t width, + size_t height, + simplewebp_u8 bits, + struct swebp__pixel *color_data +) +{ + size_t x, y; + size_t tiles_per_row = swebp__subsample_size(width, bits); + + for (y = 0; y < height; y++) + { + struct swebp__pixel *line = color_data + (y >> bits) * tiles_per_row; + + for (x = 0; x < width; x++) + { + struct swebp__pixel *ref = &line[x >> bits]; + rgba->r += swebp__color_delta(ref->b, rgba->g); + rgba->b += swebp__color_delta(ref->g, rgba->g); + rgba->b += swebp__color_delta(ref->r, rgba->r); + rgba++; + } + } +} + +static void swebp__apply_green_sub_transform( + struct swebp__pixel *rgba, + size_t width, + size_t height +) +{ + size_t x, y; + + for (y = 0; y < height; y++) + { + for (x = 0; x < width; x++) + { + struct swebp__pixel *col = &rgba[y * width + x]; + col->r += col->g; + col->b += col->g; + } + } +} + +static void swebp__apply_index_transform( + struct swebp__pixel *rgba, + size_t width, + size_t height, + simplewebp_u8 size, + struct swebp__pixel *index_data +) +{ + size_t x, y; + simplewebp_u32 color_count; + simplewebp_u8 bits, mask, mod, rb; + size_t stride; + const struct swebp__pixel transparent_black = {0}; + + color_count = ((simplewebp_u32) size) + 1; + bits = swebp__vp8l_index_reduction(color_count); + stride = swebp__subsample_size(width, bits); + mask = (1 << (8 >> bits)) - 1; + mod = (1 << bits) - 1; + rb = 3 - bits; + + for (y = height - 1; y != (size_t) -1; y--) + { + for (x = width - 1; x != (size_t) -1; x--) + { + const struct swebp__pixel *l = rgba + stride * y; + simplewebp_u8 i = (l[x >> bits].g >> ((x & mod) << rb)) & mask; + rgba[y * width + x] = i < color_count ? index_data[i] : transparent_black; + } + } +} + +static simplewebp_error swebp__decode_lossless_bitstream_main( + simplewebp *simplewebp, + struct swebp__vp8l_bdec *br, + struct swebp__pixel *rgba, + simplewebp_bool skipheader +) +{ + size_t actual_width, actual_height; + size_t full_width; + simplewebp_u32 width, height; + simplewebp_u8 filter_list; + struct swebp__pixel *filter_data[4]; + simplewebp_u8 filter_bits[4]; + simplewebp_bool filter_active[4]; + int i; + simplewebp_error err; + + simplewebp_get_dimensions(simplewebp, &actual_width, &actual_height); + + if (!skipheader) + { + simplewebp_u32 version_num; + + /* Validate header */ + if (swebp__vp8l_bitread_read(br, 8) != 0x2F) + return SIMPLEWEBP_CORRUPT_ERROR; + + /* Validate dimensions */ + full_width = width = swebp__vp8l_bitread_read(br, 14) + 1; + height = swebp__vp8l_bitread_read(br, 14) + 1; + if (width != actual_width || height != actual_height) + return SIMPLEWEBP_CORRUPT_ERROR; + + /* Skip alpha */ + swebp__vp8l_bitread_read(br, 1); + version_num = swebp__vp8l_bitread_read(br, 3); + if (version_num != 0) + return SIMPLEWEBP_UNSUPPORTED_ERROR; + } + else + { + width = (simplewebp_u32) (full_width = actual_width); + height = (simplewebp_u32) actual_height; + } + + /* Read transforms */ + err = SIMPLEWEBP_NO_ERROR; + filter_list = 0; + memset(filter_active, 0, sizeof(simplewebp_bool) * 4); + memset(filter_bits, 0, sizeof(simplewebp_u8) * 4); + memset(filter_data, 0, sizeof(filter_data)); + for (i = 0; i < sizeof(simplewebp_u8) * 4; i++) + { + simplewebp_u8 ttype, transform_bits; + struct swebp__pixel *filter_out = NULL; + + if (swebp__vp8l_bitread_read(br, 1) == 0) + break; + + ttype = swebp__vp8l_bitread_read(br, 2); + if (br->eos) + return SIMPLEWEBP_CORRUPT_ERROR; + + switch (ttype) + { + case 0: + /* Predictor transform */ + case 1: + /* Color transform */ + err = swebp__decode_vp8l_transform_data(simplewebp, br, width, height, &transform_bits, &filter_out); + break; + case 2: + /* Green subtraction transform */ + err = SIMPLEWEBP_NO_ERROR; + transform_bits = 0; + filter_out = NULL; + break; + case 3: + /* Color index transform */ + err = swebp__decode_color_index_transform( + simplewebp, + br, + width, + height, + &transform_bits, + &filter_out + ); + width = (simplewebp_u32) swebp__subsample_size(width, swebp__vp8l_index_reduction(transform_bits + 1)); + break; + default: + /* Unreachable */ + err = SIMPLEWEBP_CORRUPT_ERROR; + break; + } + + if (err != SIMPLEWEBP_NO_ERROR) + { + /* Error occured. Rollback. */ + swebp__batch_free(simplewebp, (void **) filter_data, 4); + return err; + } + + if (filter_active[ttype]) + { + /* Multiple same filters defined, likely corrupt */ + swebp__dealloc(simplewebp, filter_out); + swebp__batch_free(simplewebp, (void **) filter_data, 4); + return SIMPLEWEBP_CORRUPT_ERROR; + } + + filter_list = (filter_list) << 2 | ttype; + filter_bits[i] = transform_bits; + filter_data[i] = filter_out; + filter_active[ttype] = 1; + } + + /* Decode image */ + err = swebp__decode_vp8l_image(simplewebp, br, 1, width, height, &rgba); + if (err != SIMPLEWEBP_NO_ERROR) + { + swebp__batch_free(simplewebp, (void **) filter_data, sizeof(simplewebp_u8) * 4); + return err; + } + + /* Apply transform */ + i--; + for (; i >= 0; i--) + { + simplewebp_u8 ttype; + ttype = filter_list & 3; + + switch (ttype) + { + case 0: + /* Predictor Transform */ + swebp__apply_predictor_transform(rgba, width, height, filter_bits[i], filter_data[i]); + break; + case 1: + /* Color Transform */ + swebp__apply_color_transform(rgba, width, height, filter_bits[i], filter_data[i]); + break; + case 2: + /* Green Transform */ + swebp__apply_green_sub_transform(rgba, width, height); + break; + case 3: + /* Color Index Transform */ + width = (simplewebp_u32) full_width; + swebp__apply_index_transform(rgba, width, height, filter_bits[i], filter_data[i]); + break; + } + + filter_list >>= 2; + } + + swebp__batch_free(simplewebp, (void **) filter_data, sizeof(simplewebp_u8) * 4); + return err; +} + +/* Also used to decode alpha channel. */ +static simplewebp_error swebp__decode_lossless_bitstream( + simplewebp *simplewebp, + simplewebp_input *input, + size_t bitstreamsize, + struct swebp__pixel *rgba, + simplewebp_bool skipheader +) +{ + struct swebp__vp8l_bdec br; + simplewebp_error err; + simplewebp_u8 *bitstreambuffer = (simplewebp_u8 *) swebp__alloc(simplewebp, bitstreamsize); + + if (!bitstreambuffer) + return SIMPLEWEBP_ALLOC_ERROR; + + if (!swebp__read2(bitstreamsize, bitstreambuffer, input)) + { + swebp__dealloc(simplewebp, bitstreambuffer); + return SIMPLEWEBP_IO_ERROR; + } + + swebp__vp8l_bitread_init(&br, bitstreambuffer, bitstreamsize); + err = swebp__decode_lossless_bitstream_main(simplewebp, &br, rgba, skipheader); + + swebp__dealloc(simplewebp, bitstreambuffer); + return err; +} + +static simplewebp_error swebp__decode_lossless(simplewebp *simplewebp, void *buffer) +{ + size_t vp8lsize = swebp__proxy_size(simplewebp->vp8l_input.userdata); + if (!swebp__seek(0, &simplewebp->vp8l_input)) + return SIMPLEWEBP_IO_ERROR; + return swebp__decode_lossless_bitstream( + simplewebp, + &simplewebp->vp8l_input, + vp8lsize, + (struct swebp__pixel *) buffer, + 0 + ); +} + +simplewebp_error simplewebp_decode_yuva(simplewebp *simplewebp, void *y_buffer, void *u_buffer, void *v_buffer, void *a_buffer, void *settings) +{ + if (simplewebp->webp_type == 0) + { + struct swebp__yuvdst destination; + destination.y = (simplewebp_u8*) y_buffer; + destination.u = (simplewebp_u8*) u_buffer; + destination.v = (simplewebp_u8*) v_buffer; + destination.a = (simplewebp_u8*) a_buffer; + return swebp__decode_lossy(simplewebp, &destination, settings); + } + + return SIMPLEWEBP_IS_LOSSLESS_ERROR; +} + +simplewebp_error simplewebp_decode(simplewebp *simplewebp, void *buffer, void *settings) +{ + simplewebp_error err = SIMPLEWEBP_NO_ERROR; + + if (simplewebp->webp_type == 0) + { + struct swebp__yuvdst dest; + struct swebp__chroma *upscaled; + simplewebp_u8 *mem, *orig_mem; + size_t needed, yw, yh, uvw, uvh; + + /* Calculate allocation size. The allocated memory layout is [y, a, u, v] */ + yw = simplewebp->decoder.vp8.picture_header.width; + yh = simplewebp->decoder.vp8.picture_header.height; + uvw = (yw + 1) / 2; + uvh = (yh + 1) / 2; + needed = ((yw * yh) + (uvw * uvh)) * 2; + /* For upsampling the UV */ + needed += uvw * uvh * 4 * sizeof(struct swebp__chroma); + + mem = orig_mem = (simplewebp_u8 *) swebp__alloc(simplewebp, needed); + if (mem == NULL) + return SIMPLEWEBP_ALLOC_ERROR; + + dest.y = mem; + mem += yw * yh; + dest.a = mem; + mem += yw * yh; + dest.u = mem; + mem += uvw * uvh; + dest.v = mem; + mem += uvw * uvh; + upscaled = (struct swebp__chroma*) mem; + + err = swebp__decode_lossy(simplewebp, &dest, settings); + if (err != SIMPLEWEBP_NO_ERROR) + { + swebp__dealloc(simplewebp, orig_mem); + return err; + } + + /* Upsample UV */ + swebp__upsample_chroma(dest.u, dest.v, upscaled, uvw, uvh); + /* Convert YUVA to RGBA */ + swebp__yuva2rgba(dest.y, upscaled, dest.a, yw, yh, (struct swebp__pixel*) buffer); + swebp__dealloc(simplewebp, orig_mem); + } + else + { + err = swebp__decode_lossless(simplewebp, buffer); + if (err != SIMPLEWEBP_NO_ERROR) + return err; + + if (!simplewebp->decoder.vp8l.has_alpha) + { + size_t i, w, h; + struct swebp__pixel *rgba; + + w = simplewebp->decoder.vp8l.width; + h = simplewebp->decoder.vp8l.height; + + /* libwebp sets the alpha to 255 if the has_alpha bit is not set. */ + /* Interestingly, WebPShop as of 0.4.3 doesn't seem to respect that flag (it's always RGBA). */ + rgba = (struct swebp__pixel*) buffer; + for (i = 0; i < w * h; i++) + rgba[i].a = 255; + } + } + + return err; +} + +#ifndef SIMPLEWEBP_DISABLE_STDIO + +static size_t swebp__stdfile_read(size_t size, void *dest, void *userdata) +{ + FILE *f = (FILE *) userdata; + return fread(dest, 1, size, f); +} + +static simplewebp_bool swebp__stdfile_seek(size_t pos, void *userdata) +{ + FILE *f = (FILE *) userdata; + return fseek(f, (long) pos, SEEK_SET) == 0; +} + +static void swebp__stdfile_close(void *userdata) +{ + FILE *f = (FILE *) userdata; + fclose(f); +} + +static size_t swebp__stdfile_tell(void *userdata) +{ + FILE *f = (FILE *) userdata; + return ftell(f); +} + +simplewebp_error simplewebp_input_from_file(FILE *file, simplewebp_input *out) +{ + out->userdata = file; + out->read = swebp__stdfile_read; + out->seek = swebp__stdfile_seek; + out->tell = swebp__stdfile_tell; + out->close = swebp__stdfile_close; + return SIMPLEWEBP_NO_ERROR; +} + +simplewebp_error simplewebp_input_from_filename(const char *filename, simplewebp_input *out) +{ + FILE *f = fopen(filename, "rb"); + if (f == NULL) + return SIMPLEWEBP_IO_ERROR; + + return simplewebp_input_from_file(f, out); +} + +simplewebp_error simplewebp_load_from_file(FILE *file, const simplewebp_allocator *allocator, simplewebp **out) +{ + simplewebp_input input; + simplewebp_error err; + + err = simplewebp_input_from_file(file, &input); + if (err != SIMPLEWEBP_NO_ERROR) + return err; + + err = simplewebp_load(&input, allocator, out); + if (err != SIMPLEWEBP_NO_ERROR) + simplewebp_close_input(&input); + + return err; +} + +simplewebp_error simplewebp_load_from_filename(const char *filename, const simplewebp_allocator *allocator, simplewebp **out) +{ + simplewebp_input input; + simplewebp_error err; + + err = simplewebp_input_from_filename(filename, &input); + if (err != SIMPLEWEBP_NO_ERROR) + return err; + + err = simplewebp_load(&input, allocator, out); + if (err != SIMPLEWEBP_NO_ERROR) + simplewebp_close_input(&input); + + return err; +} +#endif /* SIMPLEWEBP_DISABLE_STDIO */ + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* SIMPLEWEBP_IMPLEMENTATION */ + +/** + * This software is available under BSD-3-Clause License + * Some parts of WebP Lossless code is taken from "whale" project by Matej Fencl + * + * Copyright (c) 2010 Google Inc., 2023 Miku AuahDark + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the + * following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the + * following disclaimer in the documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote + * products derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE + * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * Additional IP Rights Grant (Patents) + * ------------------------------------ + * + * "These implementations" means the copyrightable works that implement the WebM codecs distributed by Google as part + * of the WebM Project. + * + * Google hereby grants to you a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as + * stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer, and + * otherwise run, modify and propagate the contents of these implementations of WebM, where such license applies only + * to those patent claims, both currently owned by Google and acquired in the future, licensable by Google that are + * necessarily infringed by these implementations of WebM. This grant does not include claims that would be infringed + * only as a consequence of further modification of these implementations. If you or your agent or exclusive licensee + * institute or order or agree to the institution of patent litigation or any other patent enforcement activity + * against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that any of these + * implementations of WebM or any code incorporated within any of these implementations of WebM constitute direct or + * contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under + * this License for these implementations of WebM shall terminate as of the date such litigation is filed. + */ diff --git a/src/config.h b/src/config.h index 1af6367..2bf9932 100644 --- a/src/config.h +++ b/src/config.h @@ -132,4 +132,10 @@ # define BIMG_CONFIG_PARSE_TGA BIMG_CONFIG_PARSE_ENABLE #endif // BIMG_CONFIG_PARSE_TGA +/// WebP - lossy and lossless image format by Google. +/// +#ifndef BIMG_CONFIG_PARSE_WEBP +# define BIMG_CONFIG_PARSE_WEBP BIMG_CONFIG_PARSE_ENABLE +#endif // BIMG_CONFIG_PARSE_WEBP + #endif // BIMG_CONFIG_H_HEADER_GUARD diff --git a/src/image_decode.cpp b/src/image_decode.cpp index ca4e402..b64cb40 100644 --- a/src/image_decode.cpp +++ b/src/image_decode.cpp @@ -12,6 +12,7 @@ BX_PRAGMA_DIAGNOSTIC_PUSH() BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wtype-limits") BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-parameter") BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-value") +BX_PRAGMA_DIAGNOSTIC_IGNORED_GCC("-Wcalloc-transposed-args") BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG("-Wdeprecated-declarations") BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4018) // warning C4018: '<': signed/unsigned mismatch BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4100) // error C4100: '' : unreferenced formal parameter @@ -58,6 +59,30 @@ void lodepng_free(void* _ptr) # include #endif // BIMG_CONFIG_PARSE_HEIF +#if BIMG_CONFIG_PARSE_WEBP +BX_PRAGMA_DIAGNOSTIC_PUSH(); +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-function") +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-parameter") +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-variable") +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wsign-compare") +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wshadow") +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wmissing-field-initializers") +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wparentheses") +BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wimplicit-fallthrough") +BX_PRAGMA_DIAGNOSTIC_IGNORED_GCC("-Wtype-limits") +BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4100) // warning C4100: unreferenced formal parameter +BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4127) // warning C4127: conditional expression is constant +BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4244) // warning C4244: conversion from 'X' to 'Y', possible loss of data +BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4245) // warning C4245: conversion from 'X' to 'Y', signed/unsigned mismatch +BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4267) // warning C4267: conversion from 'size_t' to 'X', possible loss of data +BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4505) // warning C4505: unreferenced function with internal linkage has been removed +BX_PRAGMA_DIAGNOSTIC_IGNORED_MSVC(4701) // warning C4701: potentially uninitialized local variable used +#define SIMPLEWEBP_IMPLEMENTATION +#define SIMPLEWEBP_DISABLE_STDIO +#include +BX_PRAGMA_DIAGNOSTIC_POP(); +#endif // BIMG_CONFIG_PARSE_WEBP + #define BIMG_USE_STB_IMAGE 0 \ || BIMG_CONFIG_PARSE_JPEG \ || BIMG_CONFIG_PARSE_BMP \ @@ -994,19 +1019,132 @@ namespace bimg #endif // BIMG_CONFIG_PARSE_HEIF } +#if BIMG_CONFIG_PARSE_WEBP + static void* simpleWebpAlloc(void* _userdata, size_t _size) + { + bx::AllocatorI* allocator = (bx::AllocatorI*)_userdata; + return bx::alloc(allocator, _size); + } + + static void simpleWebpFree(void* _userdata, void* _ptr) + { + bx::AllocatorI* allocator = (bx::AllocatorI*)_userdata; + bx::free(allocator, _ptr); + } + + static void errorSetSimpleWebp(simplewebp_error _result, bx::Error* _err) + { + switch (_result) + { + case SIMPLEWEBP_ALLOC_ERROR: BX_ERROR_SET(_err, BIMG_ERROR, "WebP: Failed to allocate memory."); break; + case SIMPLEWEBP_IO_ERROR: BX_ERROR_SET(_err, BIMG_ERROR, "WebP: Input read error."); break; + case SIMPLEWEBP_NOT_WEBP_ERROR: BX_ERROR_SET(_err, BIMG_ERROR, "WebP: Not a WebP image."); break; + case SIMPLEWEBP_CORRUPT_ERROR: BX_ERROR_SET(_err, BIMG_ERROR, "WebP: Image corrupt."); break; + case SIMPLEWEBP_UNSUPPORTED_ERROR: BX_ERROR_SET(_err, BIMG_ERROR, "WebP: Image unsupported."); break; + case SIMPLEWEBP_IS_LOSSLESS_ERROR: BX_ERROR_SET(_err, BIMG_ERROR, "WebP: Image is lossless."); break; + default: BX_ERROR_SET(_err, BIMG_ERROR, "WebP: Failed to parse image."); break; + } + } +#endif // BIMG_CONFIG_PARSE_WEBP + + static ImageContainer* imageParseSimpleWebp(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, bx::Error* _err) + { + BX_ERROR_SCOPE(_err); + + // WebP file format: + // bytes 0-3 : "RIFF" + // bytes 4-7 : file size + // bytes 8-11: "WEBP" + static uint8_t riffMagic[] = { 0x52, 0x49, 0x46, 0x46 }; // "RIFF" + static uint8_t webpMagic[] = { 0x57, 0x45, 0x42, 0x50 }; // "WEBP" + + if (12 > _size + || 0 != bx::memCmp(_data, riffMagic, sizeof(riffMagic) ) + || 0 != bx::memCmp( (const uint8_t*)_data + 8, webpMagic, sizeof(webpMagic) ) ) + { + return NULL; + } + +#if BIMG_CONFIG_PARSE_WEBP + simplewebp_allocator allocator; + allocator.alloc = simpleWebpAlloc; + allocator.free = simpleWebpFree; + allocator.userdata = _allocator; + + simplewebp* webp = NULL; + simplewebp_error err = simplewebp_load_from_memory( (void*)_data, _size, &allocator, &webp); + + if (SIMPLEWEBP_NO_ERROR != err) + { + errorSetSimpleWebp(err, _err); + return NULL; + } + + size_t width = 0; + size_t height = 0; + simplewebp_get_dimensions(webp, &width, &height); + + const uint32_t bufferSize = uint32_t(width * height * 4); + uint8_t* data = (uint8_t*)bx::alloc(_allocator, bufferSize); + + err = simplewebp_decode(webp, data, NULL); + simplewebp_unload(webp); + + if (SIMPLEWEBP_NO_ERROR != err) + { + bx::free(_allocator, data); + errorSetSimpleWebp(err, _err); + return NULL; + } + + ImageContainer* output = imageAlloc(_allocator + , bimg::TextureFormat::RGBA8 + , bx::narrowCast(width) + , bx::narrowCast(height) + , 0 + , 1 + , false + , false + , data + ); + + bool hasAlpha = false; + + for (uint32_t ii = 0, num = uint32_t(width * height); ii < num; ++ii) + { + if (data[ii * 4 + 3] < UINT8_MAX) + { + hasAlpha = true; + break; + } + } + + output->m_hasAlpha = hasAlpha; + + bx::free(_allocator, data); + + return output; +#else + BX_UNUSED(_allocator, _data, _size); + BX_ERROR_SET(_err, BIMG_ERROR, "WebP parsing is disabled (BIMG_CONFIG_PARSE_WEBP)."); + return NULL; +#endif // BIMG_CONFIG_PARSE_WEBP + } + ImageContainer* imageParse(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, TextureFormat::Enum _dstFormat, bx::Error* _err) { BX_ERROR_SCOPE(_err); - ImageContainer* input = imageParseDds (_allocator, _data, _size, _err) ; - input = NULL == input ? imageParseKtx (_allocator, _data, _size, _err) : input; - input = NULL == input ? imageParsePvr3 (_allocator, _data, _size, _err) : input; - input = NULL == input ? imageParseGnf (_allocator, _data, _size, _err) : input; - input = NULL == input ? imageParseLodePng (_allocator, _data, _size, _err) : input; - input = NULL == input ? imageParseTinyExr (_allocator, _data, _size, _err) : input; - input = NULL == input ? imageParseJpeg (_allocator, _data, _size, _err) : input; - input = NULL == input ? imageParseStbImage(_allocator, _data, _size, _err) : input; - input = NULL == input ? imageParseLibHeif (_allocator, _data, _size, _err) : input; + ImageContainer* input = imageParseDds (_allocator, _data, _size, _err) ; + input = NULL == input ? imageParseKtx (_allocator, _data, _size, _err) : input; + input = NULL == input ? imageParsePvr3 (_allocator, _data, _size, _err) : input; + input = NULL == input ? imageParseGnf (_allocator, _data, _size, _err) : input; + input = NULL == input ? imageParseLodePng (_allocator, _data, _size, _err) : input; + input = NULL == input ? imageParseTinyExr (_allocator, _data, _size, _err) : input; + input = NULL == input ? imageParseJpeg (_allocator, _data, _size, _err) : input; + input = NULL == input ? imageParseSimpleWebp(_allocator, _data, _size, _err) : input; + input = NULL == input ? imageParseStbImage (_allocator, _data, _size, _err) : input; + input = NULL == input ? imageParseLibHeif (_allocator, _data, _size, _err) : input; if (NULL == input) { @@ -1014,6 +1152,7 @@ namespace bimg { BX_ERROR_SET(_err, BIMG_ERROR, "Unrecognized image format."); } + return NULL; }