/**
 * @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 <stddef.h>
#include <stdlib.h>
#if defined(__cplusplus) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901)
#include <stdint.h>

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 <stdbool.h>
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 <stdio.h>

/**
 * @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 <assert.h>
#include <string.h>

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.
 */
