summaryrefslogtreecommitdiff
path: root/libpanto/src/cdeps/jebp.h
diff options
context:
space:
mode:
authort <t@tjp.lol>2026-07-07 11:29:23 -0600
committert <t@tjp.lol>2026-07-07 12:15:28 -0600
commit621d7fee0ace729f8d44126032d2c6e13f72ee7f (patch)
treea2938b0a9e5c0930e8644721abbe94875df9ff08 /libpanto/src/cdeps/jebp.h
parent0fa6d4209ff9b4a95e7d1955887aa4c73ee3423c (diff)
Move libpanto projects to libpantograph dependencyHEADmain
Remove the in-repo libpanto sources and binding projects from pantograph. Consume libpantograph through the Zig package URL at code.tjp.lol/libpantograph.git, including the Lua module artifact used by CLI extensions.
Diffstat (limited to 'libpanto/src/cdeps/jebp.h')
-rw-r--r--libpanto/src/cdeps/jebp.h2457
1 files changed, 0 insertions, 2457 deletions
diff --git a/libpanto/src/cdeps/jebp.h b/libpanto/src/cdeps/jebp.h
deleted file mode 100644
index dc95d06..0000000
--- a/libpanto/src/cdeps/jebp.h
+++ /dev/null
@@ -1,2457 +0,0 @@
-/**
- * JebP - Single header WebP decoder
- */
-
-/**
- * LICENSE
- **
- * MIT No Attribution
- *
- * Copyright 2022 Jasmine Minter
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-// Attribution is not required, but would be appreciated :)
-
-/**
- * DOCUMENTATION
- **
- * First and foremost, this project uses some custom types:
- * `jebp_byte`/`jebp_ubyte` is a singular byte.
- * `jebp_short`/jebp_ushort` is an integer of atleast 16-bits.
- * `jebp_int`/`jebp_uint` is an integer of atleast 32-bits.
- *
- * This is a header only file. This means that it operates as a standard header
- * and to generate the source file you define `JEBP_IMPLEMENTATION` in ONE file
- * only. For example:
- * ```c
- * #define JEBP_IMPLEMENTATION
- * #include "jebp.h"
- * ```
- *
- * The most basic API call in this library is:
- * ```c
- * err = jebp_decode(&image, size, data);
- * ```
- * where:
- * `jebp_image_t *image` is a pointer to an image structure to receive the
- * decoded data.
- * `size_t size` is the size of the WebP-encoded data buffer.
- * `const void *data` is a pointer to the WebP encoded data buffer, `size`
- * bytes large.
- * `jebp_error_t err` is the result of the operation (OK or an error code).
- *
- * For reading from a provided file path, this API call can be used instead:
- * ```c
- * err = jebp_read(&image, path);
- * ```
- * where:
- * `const char *path` is the path of the file to be read.
- *
- * It is currently not possible to read from a `FILE *` object.
- * If you only want to get the size of the image without a full read, these
- * functions can be used instead:
- * ```c
- * err = jebp_decode_size(&image, size, data);
- * err = jebp_read_size(&image, path);
- * ```
- *
- * The `jebp_image_t` structure has the following properties:
- * `jebp_int width` is the width of the image.
- * `jebp_int height` is the height of the image.
- * `jebp_color_t *pixels` is a pointer to an array pixels. Each `jebp_color_t`
- * structure contains four `jebp_ubyte` values for `r`,
- * `g`, `b` and `a`. This allows the `pixels` pointer
- * to be cast to `jebp_ubyte *` to get an RGBA pixel
- * buffer.
- * The allocated data in the image can be free'd with:
- * ```c
- * jebp_free_image(&image);
- * ```
- * This function will also clear the structure, notably width and height will be
- * set to 0.
- *
- * The `jebp_error_t` enumeration has the following values:
- * `JEBP_OK` means the operation completed successfully.
- * `JEBP_ERROR_INVAL` means one of the arguments provided is invalid, usually
- * this refers to a NULL pointer.
- * `JEBP_ERROR_INVDATA` means the WebP-encoded data is invalid or corrupted.
- * `JEBP_ERROR_INVDATA_HEADER` is a suberror of `INVDATA` that indicates that
- * the header bytes are invalid. This file is likely not a
- * WebP file.
- * `JEBP_ERROR_EOF` means the end of the file (or data buffer) was reached
- * before the operation could successfully complete.
- * `JEBP_ERROR_NOSUP` means there is a feature in the WebP stream that is not
- * currently supported (see below). This can also represent
- * new features, versions or RIFF-chunks that were not in
- * the specification when writing.
- * `JEBP_ERROR_NOSUP_CODEC` is a suberror of `NOSUP` that indicates that the
- * RIFF chunk that is most likely for the codec is not
- * recognized. Currently lossy images are not supported
- * (see below) and lossless image support can be disabled
- * (see `JEBP_NO_VP8L`).
- * `JEBP_ERROR_NOSUP_PALETTE` is a suberror of `NOSUP` that indicates that the
- * image has a color-index transform (in WebP terminology,
- * this would be a paletted image). Color-indexing
- * transforms are not currently supported (see below). Note
- * that this error code might be removed after
- * color-indexing transform support is added, this is only
- * here for now to help detecting common issues.
- * `JEBP_ERROR_NOMEM` means that a memory allocation failed, indicating that
- * there is no more memory available.
- * `JEBP_ERROR_IO` represents any generic I/O error, usually from
- * file-reading.
- * `JEBP_ERROR_UNKNOWN` means any unknown error. Currently this is only used
- * when an unknown value is passed into
- * `jebp_error_string`.
- * To get a human-readable string of the error, the following function can be
- * used:
- * ```c
- * const char *error = jebp_error_string(err);
- * ```
- *
- * This is not a feature-complete WebP decoder and has the following
- * limitations:
- * - Does not support decoding lossy files with VP8.
- * - Does not support extended file-formats with VP8X.
- * - Does not support VP8L lossless images with the color-indexing transform
- * (palleted images).
- * - Does not support VP8L images with more than 256 huffman groups. This is
- * an arbitrary limit to prevent bad images from using too much memory. In
- * theory, images requiring more groups should be very rare. This limit may
- * be increased in the future.
- *
- * Features that will probably never be supported due to complexity or API
- * constraints:
- * - Decoding color profiles.
- * - Decoding metadata.
- * - Full color-indexing/palette support will be a bit of a mess, so don't
- * expect full support of that coming anytime soon. Simple color-indexing
- * support (more than 16 colors, skipping the need for bit-packing) is
- * definitely alot more do-able.
- *
- * Along with `JEBP_IMPLEMENTATION` defined above, there are a few other macros
- * that can be defined to change how JebP operates:
- * `JEBP_NO_STDIO` will disable the file-reading API.
- * `JEBP_NO_SIMD` will disable SIMD optimizations. These are currently
- * not-used but the detection is there ready for further work.
- * `JEBP_NO_VP8L` will disable VP8L (lossless) decoding support. Note that
- * currently this will make all images fail since VP8L is the
- * only supported codec right now.
- * `JEBP_ALLOC` and `JEBP_FREE` can be defined to functions for a custom
- * allocator. They either both have to be defined or neither
- * defined.
- *
- * This single-header library requires C99 to be supported. Along with this it
- * requires the following headers from the system to successfully compile. Some
- * of these can be disabled with the above macros:
- * `stddef.h` is used for the definition of the `size_t` type.
- * `limits.h` is used for the `UINT_MAX` macro to check the size of `int`. If
- * `int` is not 32-bits, `long` will be used for `jebp_int`
- * instead.
- * `string.h` is used for `memset` to clear out memory.
- * `stdio.h` is used for I/O support and logging errors. If `JEBP_NO_STDIO` is
- * defined and `JEBP_LOG_ERRORS` is not defined, this will not be
- * included.
- * `stdlib.h` is used for the default implementations of `JEBP_ALLOC`
- * and `JEBP_FREE`, using `malloc` and `free` respectively. If
- * those macros are already defined to something else, this will
- * not be included.
- * `emmintrin.h` and `arm_neon.h` is used for SIMD intrinsice. If
- * `JEBP_NO_SIMD` is defined these will not be included.
- *
- * The following predefined macros are also used for compiler-feature, SIMD and
- * endianness detection. These can be changed or modified before import to
- * change JebP's detection logic:
- * `__STDC_VERSION__` is used to detect if the compiler supports C99 and also
- * checks for C11 support to use `_Noreturn`.
- * `__has_attribute` and `__has_builtin` are used to detect the `noreturn` and
- * `always_inline` attributes, along with the
- * `__builtin_bswap32` builtin. Note that `__has_attribute`
- * does not fallback to compiler-version checks since most
- * compilers already support `__has_attribute`.
- * `__GNUC__` and `__GNUC_MINOR__` are used to detect if the compiler is GCC
- * (or GCC compatible) and what version of GCC it is. This, in
- * turn, is used to polyfill `__has_builtin` on older compilers
- * that may not support it.
- * `__clang__` is used to detect the Clang compiler. This is only used to set
- * the detected GCC version higher since Clang still marks itself
- * as GCC 4.2 by default. No Clang version detection is done.
- * `_MSC_VER` is used to detect the MSVC compiler. This is used to check
- * support for `__declspec(noreturn)`, `__forceinline` and
- * `_byteswap_ulong`. No MSVC version detection is done.
- * `__LITTLE_ENDIAN__` is used to check if the architecture is little-endian.
- * Note that this is only checked either if the
- * architecture cannot be detected or, in special cases,
- * where there is not enough information from the
- * architecture or compiler to detect endianness. Also
- * note that big-endian and other more-obscure endian
- * types are not detected. Little-endian is the only
- * endianness detected and is used for optimization in a
- * few areas. If the architecture is not little-endian or
- * cannot be detected as such, a naive solution is used
- * instead.
- * `__i386`, `__i386__` and `_M_IX86` are used to detect if this is being
- * compiled for x86-32 (also known as x86, IA-32, or i386). If one of
- * these are defined, it is also assumed that the architecture is
- * little-endian. `_M_IX86` is usually present on MSVC, while
- * the other two are usually present on most other compilers.
- * `__SSE2__` and `_M_IX86_FP` are used to detect SSE2 support on x86-32.
- * `_M_IX86`, which is usually present on MSVC, must equal 2 to
- * indicate that the code is being compiled for a SSE2-compatible
- * floating-point unit. `__SSE2__` is usually present on most other
- * compilers.
- * `__x86_64`, `__x86_64__` and `_M_X64` are used to detect if this is being
- * compiled for x86-64 (also known as AMD64). If one of these are
- * defined, it is also assumed that the architecture is little-endian
- * and that SSE2 is supported (which is required for x86-64 support).
- * `_M_X64` is usually present on MSVC, while the other two are
- * usually present on most other compilers.
- * `__arm`, `__arm__` and `_M_ARM` are used to detect if this is being
- * compiled for AArch32 (also known as arm32 or armhf). If one of
- * these are defined on Windows, it is also assumed that Neon is
- * supported (which is required for Windows). `_M_ARM` is usually
- * present on MSVC while the other two are usually present on most
- * other compilers.
- * `__ARM_NEON` is used to detect Neon support on AArch32. MSVC doesn't seem
- * to support this and I can't find any info on detecting Neon
- * support for MSVC. I have found mentions of Windows requiring
- * Neon support but cannot find any concrete proof anywhere.
- * `__aarch64`, `__aarch64__` and `_M_ARM64` are used to detect if this is
- * being compiled for AArch64 (also known as arm64). If one of
- * these are defined, it is also assumed that Neon is supported
- * (which is required for AArch64 support). `_M_ARM64` is usually
- * present on MSVC, while the other two are usually present on
- * most other compilers.
- * `__ARM_BIG_ENDIAN` is used to detect, on AArch/ARM architectures, if it is
- * in big-endian mode. However, as mentioned above, there
- * is no special code for big-endian and it's worth noting
- * that this is just used to force-disable little-endian.
- * If this is not present, it falls back to using
- * `__LITTLE_ENDIAN__`. It is also worth noting that MSVC
- * does not seem to provide a way to detect endianness. It
- * may be that Windows requires little-endian but I can't
- * find any concrete sources on this so currently
- * little-endian detection is not supported on MSVC.
- */
-
-/**
- * HEADER
- */
-#ifndef JEBP__HEADER
-#define JEBP__HEADER
-#ifdef __cplusplus
-extern "C" {
-#endif // __cplusplus
-#include <limits.h>
-#include <stddef.h>
-
-#if UINT_MAX >= 0xffffffff
-#define JEBP__INT int
-#else
-#define JEBP__INT long
-#endif
-typedef signed char jebp_byte;
-typedef unsigned char jebp_ubyte;
-typedef short jebp_short;
-typedef unsigned short jebp_ushort;
-typedef JEBP__INT jebp_int;
-typedef unsigned JEBP__INT jebp_uint;
-
-typedef enum jebp_error_t {
- JEBP_OK,
- JEBP_ERROR_INVAL,
- JEBP_ERROR_INVDATA,
- JEBP_ERROR_INVDATA_HEADER,
- JEBP_ERROR_EOF,
- JEBP_ERROR_NOSUP,
- JEBP_ERROR_NOSUP_CODEC,
- JEBP_ERROR_NOSUP_PALETTE,
- JEBP_ERROR_NOMEM,
- JEBP_ERROR_IO,
- JEBP_ERROR_UNKNOWN,
- JEBP_NB_ERRORS
-} jebp_error_t;
-
-typedef struct jebp_color_t {
- jebp_ubyte r;
- jebp_ubyte g;
- jebp_ubyte b;
- jebp_ubyte a;
-} jebp_color_t;
-
-typedef struct jebp_image_t {
- jebp_int width;
- jebp_int height;
- jebp_color_t *pixels;
-} jebp_image_t;
-
-const char *jebp_error_string(jebp_error_t err);
-void jebp_free_image(jebp_image_t *image);
-jebp_error_t jebp_decode_size(jebp_image_t *image, size_t size,
- const void *data);
-jebp_error_t jebp_decode(jebp_image_t *image, size_t size, const void *data);
-
-// I/O API
-#ifndef JEBP_NO_STDIO
-jebp_error_t jebp_read_size(jebp_image_t *image, const char *path);
-jebp_error_t jebp_read(jebp_image_t *image, const char *path);
-#endif // JEBP_NO_STDIO
-
-#ifdef __cplusplus
-}
-#endif // __cplusplus
-#endif // JEBP__HEADER
-
-/**
- * IMPLEMENTATION
- */
-#ifdef JEBP_IMPLEMENTATION
-#include <string.h>
-#if !defined(JEBP_NO_STDIO) || defined(JEBP_LOG_ERRORS)
-#include <stdio.h>
-#endif
-#if !defined(JEBP_ALLOC) && !defined(JEBP_FREE)
-#include <stdlib.h>
-#define JEBP_ALLOC malloc
-#define JEBP_FREE free
-#elif !defined(JEBP_ALLOC) || !defined(JEBP_FREE)
-#error "Both JEBP_ALLOC and JEBP_FREE have to be defined"
-#endif
-
-/**
- * Predefined macro detection
- */
-#ifdef __STDC_VERSION__
-#if __STDC_VERSION__ < 199901
-#error "Standard C99 support is required"
-#endif
-#else // __STDC_VERSION__
-#if defined(__GNUC__)
-#warning "C version cannot be checked, compilation may fail"
-#elif defined(_MSC_VER)
-#pragma message( \
- "MSVC by default is C89 'with extensions', use /std:c11 to ensure there are no errors")
-#endif
-#endif // __STDC_VERSION__
-#if defined(__clang__)
-// The default GNUC version provided by Clang is just short of what we need
-#define JEBP__GNU_VERSION 403
-#elif defined(__GNUC__)
-#define JEBP__GNU_VERSION ((__GNUC__ * 100) + __GNUC_MINOR__)
-#else
-#define JEBP__GNU_VERSION 0
-#endif // __GNUC__
-
-#ifdef __has_attribute
-#define JEBP__HAS_ATTRIBUTE __has_attribute
-#else // __has_attribute
-// We don't add GCC version checks since, unlike __has_builtin, __has_attribute
-// has been out for so long that its more likely that the compiler supports it.
-#define JEBP__HAS_ATTRIBUTE(attr) 0
-#endif // __has_attribute
-#if defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
-#define JEBP__NORETURN _Noreturn
-#elif JEBP__HAS_ATTRIBUTE(noreturn)
-#define JEBP__NORETURN __attribute__((noreturn))
-#elif defined(_MSC_VER)
-#define JEBP__NORETURN __declspec(noreturn)
-#else
-#define JEBP__NORETURN
-#endif
-#if JEBP__HAS_ATTRIBUTE(always_inline)
-#define JEBP__ALWAYS_INLINE __attribute__((always_inline))
-#elif defined(_MSC_VER)
-#define JEBP__ALWAYS_INLINE __forceinline
-#else
-#define JEBP__ALWAYS_INLINE
-#endif
-#define JEBP__INLINE static inline JEBP__ALWAYS_INLINE
-
-#ifdef __has_builtin
-#define JEBP__HAS_BUILTIN __has_builtin
-#else // __has_builtin
-#define JEBP__HAS_BUILTIN(builtin) \
- JEBP__VERSION##builtin != 0 && JEBP__GNU_VERSION >= JEBP__VERSION##builtin
-// I believe this was added earlier but GCC 4.3 is the first time it was
-// mentioned in the changelog and manual.
-#define JEBP__VERSION__builtin_bswap32 403
-#endif // __has_builtin
-#if JEBP__HAS_BUILTIN(__builtin_bswap32)
-#define JEBP__SWAP32(value) __builtin_bswap32(value)
-#elif defined(_MSC_VER)
-#define JEBP__SWAP32(value) _byteswap_ulong(value)
-#endif
-
-// We don't do any SIMD runtime detection since that causes alot of
-// heavily-documented issues that I won't go into here. Instead, if the compiler
-// supports it (and requests it) we will use it. It helps that both x86-64 and
-// AArch64 always support the SIMD from their 32-bit counterparts.
-#if defined(__i386) || defined(__i386__) || defined(_M_IX86)
-#define JEBP__ARCH_X86
-#if defined(__SSE2__) || _M_IX86_FP == 2
-#define JEBP__SIMD_SSE2
-#endif
-#elif defined(__x86_64) || defined(__x86_64__) || defined(_M_X64)
-#define JEBP__ARCH_X86
-#define JEBP__SIMD_SSE2
-#elif defined(__arm) || defined(__arm__) || defined(_M_ARM)
-#define JEBP__ARCH_ARM
-#if defined(__ARM_NEON) || defined(_MSC_VER)
-// According to the following article, MSVC requires Neon support
-// https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions
-#define JEBP__SIMD_NEON
-#endif
-#elif defined(__aarch64) || defined(__aarch64__) || defined(_M_ARM64)
-#define JEBP__ARCH_ARM
-#define JEBP__SIMD_NEON
-#define JEBP__SIMD_NEON64
-#endif
-
-#if defined(JEBP__ARCH_X86)
-// x86 is always little-endian
-#define JEBP__LITTLE_ENDIAN
-#elif defined(JEBP__ARCH_ARM) && defined(__ARM_BIG_ENDIAN)
-// The ACLE big-endian define overrules everything else, including the defualt
-// endianness detection
-#elif defined(JEBP__ARCH_ARM) && (defined(__ARM_ACLE) || defined(_MSC_VER))
-// If ACLE is supported and big-endian is not defined, it must be little-endian
-// According to the article linked above, MSVC only supports little-endian
-#define JEBP__LITTLE_ENDIAN
-#elif defined(__LITTLE_ENDIAN__) || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define JEBP__LITTLE_ENDIAN
-#endif
-
-#ifdef JEBP_NO_SIMD
-#undef JEBP__SIMD_SSE2
-#undef JEBP__SIMD_NEON
-#endif // JEBP_NO_SIMD
-#ifdef JEBP__SIMD_SSE2
-#include <emmintrin.h>
-#endif // JEBP__SIMD_SSE2
-#ifdef JEBP__SIMD_NEON
-#include <arm_neon.h>
-#endif // JEBP__SIMD_NEON
-
-/**
- * Common utilities
- */
-// TODO: Maybe we should have a logging flag and add custom logs with more
-// information to each error (and maybe other stuff like allocations)
-#define JEBP__MIN(a, b) ((a) < (b) ? (a) : (b))
-#define JEBP__MAX(a, b) ((a) > (b) ? (a) : (b))
-#define JEBP__ABS(a) ((a) < 0 ? -(a) : (a))
-#define JEBP__AVG(a, b) (((a) + (b)) / 2)
-#define JEBP__CEIL_SHIFT(a, b) (((a) + (1 << (b)) - 1) >> (b))
-#define JEBP__CLAMP(x, min, max) JEBP__MIN(JEBP__MAX(x, min), max)
-#define JEBP__CLAMP_UBYTE(x) JEBP__CLAMP(x, 0, 255)
-#define JEBP__CLEAR(ptr, size) memset(ptr, 0, size)
-
-// A simple utility that updates an error pointer if it currently does not have
-// an error
-JEBP__INLINE jebp_error_t jebp__error(jebp_error_t *err, jebp_error_t error) {
- if (*err == JEBP_OK) {
- *err = error;
- }
- return *err;
-}
-
-// Currently only used by VP8L
-// TODO: after VP8(no-L) support is added, make it an error to remove both
-// VP8 and VP8L
-#ifndef JEBP_NO_VP8L
-static jebp_error_t jebp__alloc_image(jebp_image_t *image) {
- image->pixels =
- JEBP_ALLOC(image->width * image->height * sizeof(jebp_color_t));
- if (image->pixels == NULL) {
- return JEBP_ERROR_NOMEM;
- }
- return JEBP_OK;
-}
-#endif // JEBP_NO_VP8L
-
-/**
- * Reader abstraction
- */
-#define JEBP__BUFFER_SIZE 4096
-
-typedef struct jebp__reader_t {
- size_t nb_bytes;
- const jebp_ubyte *bytes;
-#ifndef JEBP_NO_STDIO
- FILE *file;
- void *buffer;
-#endif // JEBP_NO_STDIO
-} jebp__reader_t;
-
-static void jebp__init_memory(jebp__reader_t *reader, size_t size,
- const void *data) {
- reader->nb_bytes = size;
- reader->bytes = data;
-#ifndef JEBP_NO_STDIO
- reader->file = NULL;
-#endif // JEBP_NO_STDIO
-}
-
-#ifndef JEBP_NO_STDIO
-static jebp_error_t jebp__open_file(jebp__reader_t *reader, const char *path) {
- reader->nb_bytes = 0;
- reader->file = fopen(path, "rb");
- if (reader->file == NULL) {
- return JEBP_ERROR_IO;
- }
- reader->buffer = JEBP_ALLOC(JEBP__BUFFER_SIZE);
- if (reader->buffer == NULL) {
- fclose(reader->file);
- return JEBP_ERROR_NOMEM;
- }
- return JEBP_OK;
-}
-
-static void jebp__close_file(jebp__reader_t *reader) {
- JEBP_FREE(reader->buffer);
- fclose(reader->file);
-}
-#endif // JEBP_NO_STDIO
-
-static jebp_error_t jebp__buffer_bytes(jebp__reader_t *reader) {
- if (reader->nb_bytes > 0) {
- return JEBP_OK;
- }
-#ifndef JEBP_NO_STDIO
- if (reader->file != NULL) {
- reader->nb_bytes =
- fread(reader->buffer, 1, JEBP__BUFFER_SIZE, reader->file);
- reader->bytes = reader->buffer;
- if (ferror(reader->file)) {
- return JEBP_ERROR_IO;
- }
- }
-#endif // JEBP_NO_STDIO
- if (reader->nb_bytes == 0) {
- return JEBP_ERROR_EOF;
- }
- return JEBP_OK;
-}
-
-// TODO: Most reads are only a few bytes so maybe I should optimize for that
-static jebp_error_t jebp__read_bytes(jebp__reader_t *reader, size_t size,
- void *data) {
- jebp_error_t err;
- jebp_ubyte *bytes = data;
- while (size > 0) {
- if ((err = jebp__buffer_bytes(reader)) != JEBP_OK) {
- return err;
- }
- size_t nb_bytes = JEBP__MIN(size, reader->nb_bytes);
- if (bytes != NULL) {
- memcpy(bytes, reader->bytes, nb_bytes);
- bytes += nb_bytes;
- }
- size -= nb_bytes;
- reader->nb_bytes -= nb_bytes;
- reader->bytes += nb_bytes;
- }
- return JEBP_OK;
-}
-
-// 8-bit uint reading is currently only used by the bit-reader
-#ifndef JEBP_NO_VP8L
-static jebp_ubyte jebp__read_uint8(jebp__reader_t *reader, jebp_error_t *err) {
- if (*err != JEBP_OK) {
- return 0;
- }
- if ((*err = jebp__buffer_bytes(reader)) != JEBP_OK) {
- return 0;
- }
- reader->nb_bytes -= 1;
- return *(reader->bytes++);
-}
-#endif // JEBP_NO_VP8L
-
-static jebp_uint jebp__read_uint32(jebp__reader_t *reader, jebp_error_t *err) {
- if (*err != JEBP_OK) {
- return 0;
- }
-#ifdef JEBP__LITTLE_ENDIAN
- jebp_uint value = 0;
- *err = jebp__read_bytes(reader, 4, &value);
- return value;
-#else // JEBP__LITTLE_ENDIAN
- jebp_ubyte bytes[4];
- *err = jebp__read_bytes(reader, 4, bytes);
- return (jebp_uint)bytes[0] | ((jebp_uint)bytes[1] << 8) |
- ((jebp_uint)bytes[2] << 16) | ((jebp_uint)bytes[3] << 24);
-#endif // JEBP__LITTLE_ENDIAN
-}
-
-/**
- * RIFF container
- */
-#define JEBP__RIFF_TAG 0x46464952
-#define JEBP__WEBP_TAG 0x50424557
-
-typedef struct jebp__chunk_t {
- jebp_uint tag;
- jebp_uint size;
-} jebp__chunk_t;
-
-typedef struct jebp__riff_reader_t {
- jebp__reader_t *reader;
- jebp__chunk_t header;
-} jebp__riff_reader_t;
-
-static jebp_error_t jebp__read_chunk(jebp__riff_reader_t *riff,
- jebp__chunk_t *chunk) {
- jebp_error_t err = JEBP_OK;
- chunk->tag = jebp__read_uint32(riff->reader, &err);
- chunk->size = jebp__read_uint32(riff->reader, &err);
- chunk->size += chunk->size % 2; // round up to even
- return err;
-}
-
-static jebp_error_t jebp__read_riff_header(jebp__riff_reader_t *riff,
- jebp__reader_t *reader) {
- jebp_error_t err;
- riff->reader = reader;
- if ((err = jebp__read_chunk(riff, &riff->header)) != JEBP_OK) {
- return err;
- }
- if (riff->header.tag != JEBP__RIFF_TAG) {
- return JEBP_ERROR_INVDATA_HEADER;
- }
- if (jebp__read_uint32(reader, &err) != JEBP__WEBP_TAG) {
- return jebp__error(&err, JEBP_ERROR_INVDATA_HEADER);
- }
- return err;
-}
-
-static jebp_error_t jebp__read_riff_chunk(jebp__riff_reader_t *riff,
- jebp__chunk_t *chunk) {
- jebp_error_t err;
- if ((err = jebp__read_chunk(riff, chunk)) != JEBP_OK) {
- return err;
- }
- if (chunk->size > riff->header.size) {
- return JEBP_ERROR_INVDATA;
- }
- riff->header.size -= chunk->size;
- return JEBP_OK;
-}
-
-/**
- * Bit reader
- */
-#ifndef JEBP_NO_VP8L
-typedef struct jebp__bit_reader_t {
- jebp__reader_t *reader;
- size_t nb_bytes;
- jebp_int nb_bits;
- jebp_uint bits;
-} jebp__bit_reader_t;
-
-static void jepb__init_bit_reader(jebp__bit_reader_t *bits,
- jebp__reader_t *reader, size_t size) {
- bits->reader = reader;
- bits->nb_bytes = size;
- bits->nb_bits = 0;
- bits->bits = 0;
-}
-
-// buffer/peek/skip should be used together to optimize bit-reading
-static jebp_error_t jebp__buffer_bits(jebp__bit_reader_t *bits, jebp_int size) {
- jebp_error_t err = JEBP_OK;
- while (bits->nb_bits < size && bits->nb_bytes > 0) {
- bits->bits |= jebp__read_uint8(bits->reader, &err) << bits->nb_bits;
- bits->nb_bits += 8;
- bits->nb_bytes -= 1;
- }
- return err;
-}
-
-JEBP__INLINE jebp_int jepb__peek_bits(jebp__bit_reader_t *bits, jebp_int size) {
- return bits->bits & ((1 << size) - 1);
-}
-
-JEBP__INLINE jebp_error_t jebp__skip_bits(jebp__bit_reader_t *bits,
- jebp_int size) {
- if (size > bits->nb_bits) {
- return JEBP_ERROR_INVDATA;
- }
- bits->nb_bits -= size;
- bits->bits >>= size;
- return JEBP_OK;
-}
-
-static jebp_uint jebp__read_bits(jebp__bit_reader_t *bits, jebp_int size,
- jebp_error_t *err) {
- if (*err != JEBP_OK) {
- return 0;
- }
- if ((*err = jebp__buffer_bits(bits, size)) != JEBP_OK) {
- return 0;
- }
- jebp_uint value = jepb__peek_bits(bits, size);
- if ((*err = jebp__skip_bits(bits, size)) != JEBP_OK) {
- return 0;
- }
- return value;
-}
-
-/**
- * Huffman coding
- */
-#define JEBP__MAX_HUFFMAN_LENGTH 15
-#define JEBP__MAX_PRIMARY_LENGTH 8
-#define JEBP__MAX_SECONDARY_LENGTH \
- (JEBP__MAX_HUFFMAN_LENGTH - JEBP__MAX_PRIMARY_LENGTH)
-#define JEBP__NB_PRIMARY_HUFFMANS (1 << JEBP__MAX_PRIMARY_LENGTH)
-#define JEBP__NO_HUFFMAN_SYMBOL 0xffff
-
-#define JEBP__NB_META_SYMBOLS 19
-#define JEBP__NB_COLOR_SYMBOLS 256
-#define JEBP__NB_LENGTH_SYMBOLS 24
-#define JEBP__NB_DIST_SYMBOLS 40
-#define JEBP__NB_MAIN_SYMBOLS (JEBP__NB_COLOR_SYMBOLS + JEBP__NB_LENGTH_SYMBOLS)
-
-// The huffman decoding is done in one or two steps, both using a lookup table.
-// These tables are called the "primary" table and "secondary" tables. First
-// 8-bits are peeked from the stream to index the primary table. If the symbol
-// is in this table (indicated by length <= 8) then the symbol from that is used
-// and the length is used to skip that many bits. Codes which are smaller than
-// 8-bits are represented by filling the table such that any index with a prefix
-// of the given code will have the same entry. If the symbol requires more bits
-// (indiciated by length > 8) then the symbol is used as an offset pointing to
-// the secondary table which has an index size of (length - 8) bits.
-typedef struct jebp__huffman_t {
- // <= 8: length is the number of bits actually used, and symbol is the
- // decoded symbol or `JEBP__NO_HUFFMAN_SYMBOL` for an invalid code.
- // > 8: length is the maximum number of bits for any code with this prefix,
- // and symbol is the offset in the array to the secondary table.
- jebp_short length;
- jebp_ushort symbol;
-} jebp__huffman_t;
-
-typedef struct jebp__huffman_group_t {
- jebp__huffman_t *main;
- jebp__huffman_t *red;
- jebp__huffman_t *blue;
- jebp__huffman_t *alpha;
- jebp__huffman_t *dist;
-} jebp__huffman_group_t;
-
-static const jebp_byte jebp__meta_length_order[JEBP__NB_META_SYMBOLS];
-
-// Reverse increment, returns truthy on overflow
-JEBP__INLINE jebp_int jebp__increment_code(jebp_int *code, jebp_int length) {
- jebp_int inc = 1 << (length - 1);
- while (*code & inc) {
- inc >>= 1;
- }
- if (inc == 0) {
- return 1;
- }
- *code = (*code & (inc - 1)) + inc;
- return 0;
-}
-
-// This function is a bit confusing so I have attempted to document it well
-static jebp_error_t jebp__alloc_huffman(jebp__huffman_t **huffmans,
- jebp_int nb_lengths,
- const jebp_byte *lengths) {
- // Stack allocate the primary table and set it all to invalid values
- jebp__huffman_t primary[JEBP__NB_PRIMARY_HUFFMANS];
- for (jebp_int i = 0; i < JEBP__NB_PRIMARY_HUFFMANS; i += 1) {
- primary[i].symbol = JEBP__NO_HUFFMAN_SYMBOL;
- }
-
- // Fill in the 8-bit codes in the primary table
- jebp_int len = 1;
- jebp_int code = 0;
- jebp_int overflow = 0;
- jebp_ushort symbol = JEBP__NO_HUFFMAN_SYMBOL;
- jebp_int nb_symbols = 0;
- for (; len <= JEBP__MAX_PRIMARY_LENGTH; len += 1) {
- for (jebp_int i = 0; i < nb_lengths; i += 1) {
- if (lengths[i] != len) {
- continue;
- }
- if (overflow) {
- // Fail now if the last increment overflowed
- return JEBP_ERROR_INVDATA;
- }
- for (jebp_int c = code; c < JEBP__NB_PRIMARY_HUFFMANS;
- c += 1 << len) {
- primary[c].length = len;
- primary[c].symbol = i;
- }
- overflow = jebp__increment_code(&code, len);
- symbol = i;
- nb_symbols += 1;
- }
- }
-
- // Fill in the secondary table lengths in the primary table
- jebp_int secondary_code = code;
- for (; len <= JEBP__MAX_HUFFMAN_LENGTH; len += 1) {
- for (jebp_int i = 0; i < nb_lengths; i += 1) {
- if (lengths[i] != len) {
- continue;
- }
- if (overflow) {
- return JEBP_ERROR_INVDATA;
- }
- jebp_int prefix = code & (JEBP__NB_PRIMARY_HUFFMANS - 1);
- primary[prefix].length = len;
- overflow = jebp__increment_code(&code, len);
- symbol = i;
- nb_symbols += 1;
- }
- }
-
- // Calculate the total no. of huffman entries and fill in the secondary
- // table offsets
- jebp_int nb_huffmans = JEBP__NB_PRIMARY_HUFFMANS;
- for (jebp_int i = 0; i < JEBP__NB_PRIMARY_HUFFMANS; i += 1) {
- if (nb_symbols <= 1) {
- // Special case: if there is only one symbol, use this iteration to
- // instead fill the primary table with 0-length
- // entries
- primary[i].length = 0;
- primary[i].symbol = symbol;
- continue;
- }
- jebp_int suffix_length = primary[i].length - JEBP__MAX_PRIMARY_LENGTH;
- if (suffix_length > 0) {
- primary[i].symbol = nb_huffmans;
- nb_huffmans += 1 << suffix_length;
- }
- }
-
- // Allocate, copy over the primary table, and assign the rest to invalid
- // values
- *huffmans = JEBP_ALLOC(nb_huffmans * sizeof(jebp__huffman_t));
- if (*huffmans == NULL) {
- return JEBP_ERROR_NOMEM;
- }
- memcpy(*huffmans, primary, sizeof(primary));
- if (nb_huffmans == JEBP__NB_PRIMARY_HUFFMANS) {
- // Special case: we can stop here if we don't have to fill any secondary
- // tables
- return JEBP_OK;
- }
- for (jebp_int i = JEBP__NB_PRIMARY_HUFFMANS; i < nb_huffmans; i += 1) {
- (*huffmans)[i].symbol = JEBP__NO_HUFFMAN_SYMBOL;
- }
-
- // Fill in the secondary tables
- len = JEBP__MAX_PRIMARY_LENGTH + 1;
- code = secondary_code;
- for (; len <= JEBP__MAX_HUFFMAN_LENGTH; len += 1) {
- for (jebp_int i = 0; i < nb_lengths; i += 1) {
- if (lengths[i] != len) {
- continue;
- }
- jebp_int prefix = code & (JEBP__NB_PRIMARY_HUFFMANS - 1);
- jebp_int nb_secondary_huffmans = 1 << primary[prefix].length;
- jebp__huffman_t *secondary = *huffmans + primary[prefix].symbol;
- for (jebp_int c = code; c < nb_secondary_huffmans; c += 1 << len) {
- secondary[c >> JEBP__MAX_PRIMARY_LENGTH].length = len;
- secondary[c >> JEBP__MAX_PRIMARY_LENGTH].symbol = i;
- }
- jebp__increment_code(&code, len);
- }
- }
- return JEBP_OK;
-}
-
-static jebp_int jebp__read_symbol(jebp__huffman_t *huffmans,
- jebp__bit_reader_t *bits, jebp_error_t *err) {
- if (*err != JEBP_OK) {
- return 0;
- }
- if ((*err = jebp__buffer_bits(bits, JEBP__MAX_HUFFMAN_LENGTH)) != JEBP_OK) {
- return 0;
- }
- jebp_int code = jepb__peek_bits(bits, JEBP__MAX_PRIMARY_LENGTH);
- if (huffmans[code].symbol == JEBP__NO_HUFFMAN_SYMBOL) {
- *err = JEBP_ERROR_INVDATA;
- return 0;
- }
- jebp_int length = huffmans[code].length;
- jebp_int skip = JEBP__MIN(length, JEBP__MAX_PRIMARY_LENGTH);
- if ((*err = jebp__skip_bits(bits, skip)) != JEBP_OK) {
- return 0;
- }
- if (skip == length) {
- return huffmans[code].symbol;
- }
-
- huffmans += huffmans[code].symbol;
- code = jepb__peek_bits(bits, length - skip);
- if (huffmans[code].symbol == JEBP__NO_HUFFMAN_SYMBOL) {
- *err = JEBP_ERROR_INVDATA;
- return 0;
- }
- if ((*err = jebp__skip_bits(bits, huffmans[code].length - skip)) !=
- JEBP_OK) {
- return 0;
- }
- return huffmans[code].symbol;
-}
-
-static jebp_error_t jebp__read_huffman(jebp__huffman_t **huffmans,
- jebp__bit_reader_t *bits,
- jebp_int nb_lengths,
- jebp_byte *lengths) {
- // This part of the spec is INCREDIBLY wrong and partly missing
- jebp_error_t err = JEBP_OK;
- JEBP__CLEAR(lengths, nb_lengths);
-
- if (jebp__read_bits(bits, 1, &err)) {
- // simple length storage with only 1 (first) or 2 (second) symbols, both
- // with a length of 1
- jebp_int has_second = jebp__read_bits(bits, 1, &err);
- jebp_int first_bits = jebp__read_bits(bits, 1, &err) ? 8 : 1;
- jebp_int first = jebp__read_bits(bits, first_bits, &err);
- if (first >= nb_lengths) {
- return jebp__error(&err, JEBP_ERROR_INVDATA);
- }
- lengths[first] = 1;
- if (has_second) {
- jebp_int second = jebp__read_bits(bits, 8, &err);
- if (second >= nb_lengths) {
- return jebp__error(&err, JEBP_ERROR_INVDATA);
- }
- lengths[second] = 1;
- }
-
- } else {
- jebp_byte meta_lengths[JEBP__NB_META_SYMBOLS] = {0};
- jebp_int nb_meta_lengths = jebp__read_bits(bits, 4, &err) + 4;
- for (jebp_int i = 0; i < nb_meta_lengths; i += 1) {
- meta_lengths[jebp__meta_length_order[i]] =
- jebp__read_bits(bits, 3, &err);
- }
- if (err != JEBP_OK) {
- return err;
- }
- jebp__huffman_t *meta_huffmans;
- if ((err = jebp__alloc_huffman(&meta_huffmans, JEBP__NB_META_SYMBOLS,
- meta_lengths)) != JEBP_OK) {
- return err;
- }
-
- jebp_int nb_meta_symbols = nb_lengths;
- if (jebp__read_bits(bits, 1, &err)) {
- // limit codes
- jebp_int symbols_bits = jebp__read_bits(bits, 3, &err) * 2 + 2;
- nb_meta_symbols = jebp__read_bits(bits, symbols_bits, &err) + 2;
- }
-
- jebp_int prev_length = 8;
- for (jebp_int i = 0; i < nb_lengths && nb_meta_symbols > 0;
- nb_meta_symbols -= 1) {
- jebp_int symbol = jebp__read_symbol(meta_huffmans, bits, &err);
- jebp_int length;
- jebp_int repeat;
- switch (symbol) {
- case 16:
- length = prev_length;
- repeat = jebp__read_bits(bits, 2, &err) + 3;
- break;
- case 17:
- length = 0;
- repeat = jebp__read_bits(bits, 3, &err) + 3;
- break;
- case 18:
- length = 0;
- repeat = jebp__read_bits(bits, 7, &err) + 11;
- break;
- default:
- prev_length = symbol;
- /* fallthrough */
- case 0:
- // We don't ever repeat 0 values.
- lengths[i++] = symbol;
- continue;
- }
- if (i + repeat > nb_lengths) {
- jebp__error(&err, JEBP_ERROR_INVDATA);
- break;
- }
- for (jebp_int j = 0; j < repeat; j += 1) {
- lengths[i++] = length;
- }
- }
- JEBP_FREE(meta_huffmans);
- }
-
- if (err != JEBP_OK) {
- return err;
- }
- return jebp__alloc_huffman(huffmans, nb_lengths, lengths);
-}
-
-static jebp_error_t jebp__read_huffman_group(jebp__huffman_group_t *group,
- jebp__bit_reader_t *bits,
- jebp_int nb_main_symbols,
- jebp_byte *lengths) {
- jebp_error_t err;
- if ((err = jebp__read_huffman(&group->main, bits, nb_main_symbols,
- lengths)) != JEBP_OK) {
- return err;
- }
- if ((err = jebp__read_huffman(&group->red, bits, JEBP__NB_COLOR_SYMBOLS,
- lengths)) != JEBP_OK) {
- return err;
- }
- if ((err = jebp__read_huffman(&group->blue, bits, JEBP__NB_COLOR_SYMBOLS,
- lengths)) != JEBP_OK) {
- return err;
- }
- if ((err = jebp__read_huffman(&group->alpha, bits, JEBP__NB_COLOR_SYMBOLS,
- lengths)) != JEBP_OK) {
- return err;
- }
- if ((err = jebp__read_huffman(&group->dist, bits, JEBP__NB_DIST_SYMBOLS,
- lengths)) != JEBP_OK) {
- return err;
- }
- return JEBP_OK;
-}
-
-static void jebp__free_huffman_group(jebp__huffman_group_t *group) {
- JEBP_FREE(group->main);
- JEBP_FREE(group->red);
- JEBP_FREE(group->blue);
- JEBP_FREE(group->alpha);
- JEBP_FREE(group->dist);
-}
-
-/**
- * Color cache
- */
-typedef struct jebp__colcache_t {
- jebp_int bits;
- jebp_color_t *colors;
-} jebp__colcache_t;
-
-static jebp_error_t jebp__read_colcache(jebp__colcache_t *colcache,
- jebp__bit_reader_t *bits) {
- jebp_error_t err = JEBP_OK;
- if (!jebp__read_bits(bits, 1, &err)) {
- // no color cache
- colcache->bits = 0;
- return err;
- }
- colcache->bits = jebp__read_bits(bits, 4, &err);
- if (err != JEBP_OK || colcache->bits < 1 || colcache->bits > 11) {
- return jebp__error(&err, JEBP_ERROR_INVDATA);
- }
-
- size_t colcache_size = ((size_t)1 << colcache->bits) * sizeof(jebp_color_t);
- colcache->colors = JEBP_ALLOC(colcache_size);
- if (colcache->colors == NULL) {
- return JEBP_ERROR_NOMEM;
- }
- JEBP__CLEAR(colcache->colors, colcache_size);
- return JEBP_OK;
-}
-
-static void jebp__free_colcache(jebp__colcache_t *colcache) {
- if (colcache->bits > 0) {
- JEBP_FREE(colcache->colors);
- }
-}
-
-static void jebp__colcache_insert(jebp__colcache_t *colcache,
- jebp_color_t *color) {
- if (colcache->bits == 0) {
- return;
- }
-#if defined(JEBP__LITTLE_ENDIAN) && defined(JEBP__SWAP32)
- jebp_uint hash = *(jebp_uint *)color; // ABGR due to little-endian
- hash = JEBP__SWAP32(hash); // RGBA
- hash = (hash >> 8) | (hash << 24); // ARGB
-#else
- jebp_uint hash = ((jebp_uint)color->a << 24) | ((jebp_uint)color->r << 16) |
- ((jebp_uint)color->g << 8) | (jebp_uint)color->b;
-#endif
- hash = (0x1e35a7bd * hash) >> (32 - colcache->bits);
- colcache->colors[hash] = *color;
-}
-
-/**
- * VP8L image
- */
-#define JEBP__NB_VP8L_OFFSETS 120
-
-typedef struct jebp__subimage_t {
- jebp_int width;
- jebp_int height;
- jebp_color_t *pixels;
- jebp_int block_bits;
-} jebp__subimage_t;
-
-static const jebp_byte jebp__vp8l_offsets[JEBP__NB_VP8L_OFFSETS][2];
-
-JEBP__INLINE jebp_int jebp__read_vp8l_extrabits(jebp__bit_reader_t *bits,
- jebp_int symbol,
- jebp_error_t *err) {
- if (*err != JEBP_OK) {
- return 1;
- }
- if (symbol < 4) {
- return symbol + 1;
- }
- jebp_int extrabits = symbol / 2 - 1;
- symbol = ((symbol % 2 + 2) << extrabits) + 1;
- return symbol + jebp__read_bits(bits, extrabits, err);
-}
-
-static jebp_error_t jebp__read_vp8l_image(jebp_image_t *image,
- jebp__bit_reader_t *bits,
- jebp__colcache_t *colcache,
- jebp__subimage_t *huffman_image) {
- jebp_error_t err;
- jebp_int nb_groups = 1;
- jebp__huffman_group_t *groups = &(jebp__huffman_group_t){0};
- if (huffman_image != NULL) {
- for (jebp_int i = 0; i < huffman_image->width * huffman_image->height;
- i += 1) {
- jebp_color_t *huffman = &huffman_image->pixels[i];
- if (huffman->r != 0) {
- // Currently only 256 huffman groups are supported
- return JEBP_ERROR_NOSUP;
- }
- nb_groups = JEBP__MAX(nb_groups, huffman->g + 1);
- huffman += 1;
- }
- if (nb_groups > 1) {
- groups = JEBP_ALLOC(nb_groups * sizeof(jebp__huffman_group_t));
- if (groups == NULL) {
- return JEBP_ERROR_NOMEM;
- }
- }
- }
-
- jebp_int nb_main_symbols = JEBP__NB_MAIN_SYMBOLS;
- if (colcache->bits > 0) {
- nb_main_symbols += 1 << colcache->bits;
- }
- jebp_byte *lengths = JEBP_ALLOC(nb_main_symbols);
- if (lengths == NULL) {
- err = JEBP_ERROR_NOMEM;
- goto free_groups;
- }
- jebp_int nb_read_groups = 0;
- for (; nb_read_groups < nb_groups; nb_read_groups += 1) {
- if ((err = jebp__read_huffman_group(&groups[nb_read_groups], bits,
- nb_main_symbols, lengths)) !=
- JEBP_OK) {
- break;
- }
- }
- JEBP_FREE(lengths);
- if (err != JEBP_OK) {
- goto free_read_groups;
- }
- if ((err = jebp__alloc_image(image)) != JEBP_OK) {
- goto free_read_groups;
- }
-
- jebp_color_t *pixel = image->pixels;
- jebp_color_t *end = pixel + image->width * image->height;
- jebp_int x = 0;
- for (jebp_int y = 0; y < image->height;) {
- jebp_color_t *huffman_row = NULL;
- if (huffman_image != NULL) {
- huffman_row =
- &huffman_image->pixels[(y >> huffman_image->block_bits) *
- huffman_image->width];
- }
- do {
- jebp__huffman_group_t *group;
- if (huffman_image == NULL) {
- group = groups;
- } else {
- jebp_color_t *huffman =
- &huffman_row[x >> huffman_image->block_bits];
- group = &groups[huffman->g];
- }
-
- jebp_int main = jebp__read_symbol(group->main, bits, &err);
- if (main < JEBP__NB_COLOR_SYMBOLS) {
- pixel->g = main;
- pixel->r = jebp__read_symbol(group->red, bits, &err);
- pixel->b = jebp__read_symbol(group->blue, bits, &err);
- pixel->a = jebp__read_symbol(group->alpha, bits, &err);
- jebp__colcache_insert(colcache, pixel++);
- x += 1;
- } else if (main >= JEBP__NB_MAIN_SYMBOLS) {
- *(pixel++) = colcache->colors[main - JEBP__NB_MAIN_SYMBOLS];
- x += 1;
- } else {
- jebp_int length = jebp__read_vp8l_extrabits(
- bits, main - JEBP__NB_COLOR_SYMBOLS, &err);
- jebp_int dist = jebp__read_symbol(group->dist, bits, &err);
- dist = jebp__read_vp8l_extrabits(bits, dist, &err);
- if (dist > JEBP__NB_VP8L_OFFSETS) {
- dist -= JEBP__NB_VP8L_OFFSETS;
- } else {
- const jebp_byte *offset = jebp__vp8l_offsets[dist - 1];
- dist = offset[1] * image->width + offset[0];
- dist = JEBP__MAX(dist, 1);
- }
- jebp_color_t *repeat = pixel - dist;
- if (repeat < image->pixels || pixel + length > end) {
- jebp__error(&err, JEBP_ERROR_INVDATA);
- break;
- }
- for (jebp_int i = 0; i < length; i += 1) {
- jebp__colcache_insert(colcache, repeat);
- *(pixel++) = *(repeat++);
- }
- x += length;
- }
- } while (x < image->width);
- y += x / image->width;
- x %= image->width;
- }
-
- if (err != JEBP_OK) {
- jebp_free_image(image);
- }
-free_read_groups:
- for (nb_read_groups -= 1; nb_read_groups >= 0; nb_read_groups -= 1) {
- jebp__free_huffman_group(&groups[nb_read_groups]);
- }
-free_groups:
- if (nb_groups > 1) {
- JEBP_FREE(groups);
- }
- return err;
-}
-
-static jebp_error_t jebp__read_subimage(jebp__subimage_t *subimage,
- jebp__bit_reader_t *bits,
- jebp_image_t *image) {
- jebp_error_t err = JEBP_OK;
- subimage->block_bits = jebp__read_bits(bits, 3, &err) + 2;
- subimage->width = JEBP__CEIL_SHIFT(image->width, subimage->block_bits);
- subimage->height = JEBP__CEIL_SHIFT(image->height, subimage->block_bits);
- if (err != JEBP_OK) {
- return err;
- }
- jebp__colcache_t colcache;
- if ((err = jebp__read_colcache(&colcache, bits)) != JEBP_OK) {
- return err;
- }
- err =
- jebp__read_vp8l_image((jebp_image_t *)subimage, bits, &colcache, NULL);
- jebp__free_colcache(&colcache);
- return err;
-}
-
-/**
- * VP8L predictions
- */
-#define JEBP__NB_VP8L_PRED_TYPES 14
-
-// I don't like the way it formats this
-// clang-format off
-#define JEBP__UNROLL4(var, body) \
- { var = 0; body } \
- { var = 1; body } \
- { var = 2; body } \
- { var = 3; body }
-// clang-format on
-
-typedef void (*jebp__vp8l_pred_t)(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width);
-
-#ifdef JEBP__SIMD_SSE2
-typedef struct jebp__m128x4i {
- __m128i v[4];
-} jebp__m128x4i;
-
-JEBP__INLINE __m128i jebp__sse_move_px1(__m128i v_dst, __m128i v_src) {
- __m128 v_dstf = _mm_castsi128_ps(v_dst);
- __m128 v_srcf = _mm_castsi128_ps(v_src);
- __m128 v_movf = _mm_move_ss(v_dstf, v_srcf);
- return _mm_castps_si128(v_movf);
-}
-
-JEBP__INLINE __m128i jebp__sse_avg_u8x16(__m128i v1, __m128i v2) {
- __m128i v_one = _mm_set1_epi8(1);
- __m128i v_avg = _mm_avg_epu8(v1, v2);
- // SSE2 `avg` rounds up, we have to check if a round-up occured (one of the
- // low bits was set but the other wasn't) and subtract 1 if so
- __m128i v_err = _mm_xor_si128(v1, v2);
- v_err = _mm_and_si128(v_err, v_one);
- return _mm_sub_epi8(v_avg, v_err);
-}
-
-JEBP__INLINE __m128i jebp__sse_avg2_u8x16(__m128i v1, __m128i v2, __m128i v3) {
- __m128i v_one = _mm_set1_epi8(1);
- // We can further optimise two avg calls but noting that the error will
- // propogate
- __m128i v_avg1 = _mm_avg_epu8(v1, v2);
- __m128i v_err1 = _mm_xor_si128(v1, v2);
- __m128i v_avg2 = _mm_avg_epu8(v_avg1, v3);
- __m128i v_err2 = _mm_xor_si128(v_avg1, v3);
- v_err2 = _mm_or_si128(v_err1, v_err2);
- v_err2 = _mm_and_si128(v_err2, v_one);
- return _mm_sub_epi8(v_avg2, v_err2);
-}
-
-JEBP__INLINE __m128i jebp__sse_flatten_px4(jebp__m128x4i v_pixel4) {
- __m128i v_pixello = jebp__sse_move_px1(v_pixel4.v[1], v_pixel4.v[0]);
- __m128i v_pixel3 = _mm_bsrli_si128(v_pixel4.v[3], 4);
- __m128i v_pixelhi = _mm_unpackhi_epi32(v_pixel4.v[2], v_pixel3);
- return _mm_unpacklo_epi64(v_pixello, v_pixelhi);
-}
-
-// Bit-select and accumulate, used by prediction filters 11-13
-JEBP__INLINE __m128i jebp__sse_bsela_u8x16(__m128i v_acc, __m128i v_mask,
- __m128i v1, __m128i v0) {
- // This is faster than using and/andnot/or since SSE only supports two
- // operands so prefers chaining outputs
- __m128i v_sel = _mm_xor_si128(v0, v1);
- v_sel = _mm_and_si128(v_sel, v_mask);
- v_sel = _mm_xor_si128(v_sel, v0);
- return _mm_add_epi8(v_acc, v_sel);
-}
-#endif // JEBP__SIMD_SSE2
-
-#ifdef JEBP__SIMD_NEON
-JEBP__INLINE uint8x16_t jebp__neon_load_px1(jebp_color_t *pixel) {
- uint8x16_t v_pixel = vreinterpretq_u8_u32(vld1q_dup_u32((uint32_t *)pixel));
-#ifndef JEBP__LITTLE_ENDIAN
- v_pixel = vrev32q_u8(v_pixel);
-#endif // JEBP__LITTLE_ENDIAN
- return v_pixel;
-}
-
-JEBP__INLINE uint8x16_t jebp__neon_flatten_px4(uint8x16x4_t v_pixel4) {
-#ifdef JEBP__SIMD_NEON64
- uint8x16_t v_table = vcombine_u8(vcreate_u8(0x1716151403020100),
- vcreate_u8(0x3f3e3d3c2b2a2928));
- return vqtbl4q_u8(v_pixel4, v_table);
-#else // JEBP__SIMD_NEON64
- uint8x16_t v_mask1 =
- vcombine_u8(vcreate_u8((uint32_t)-1), vcreate_u8((uint32_t)-1));
- uint8x16_t v_mask2 = vcombine_u8(vcreate_u8((uint64_t)-1), vcreate_u8(0));
- uint8x16_t v_pixello = vbslq_u8(v_mask1, v_pixel4.val[0], v_pixel4.val[1]);
- uint8x16_t v_pixelhi = vbslq_u8(v_mask1, v_pixel4.val[2], v_pixel4.val[3]);
- return vbslq_u8(v_mask2, v_pixello, v_pixelhi);
-#endif // JEBP__SIMD_NEON64
-}
-
-JEBP__INLINE uint32x4_t jebp__neon_sad_px4(uint8x16_t v_pix1,
- uint8x16_t v_pix2) {
- uint8x16_t v_diff8 = vabdq_u8(v_pix1, v_pix2);
- uint16x8_t v_diff16 = vpaddlq_u8(v_diff8);
- return vpaddlq_u16(v_diff16);
-}
-#endif // JEBP__SIMD_NEON
-
-JEBP__INLINE void jebp__vp8l_pred_black(jebp_color_t *pixel, jebp_int width) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- __m128i v_black = _mm_set1_epi32((int)0xff000000);
- for (; x + 4 <= width; x += 4) {
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- v_pixel = _mm_add_epi8(v_pixel, v_black);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- }
-#elif defined(JEBP__SIMD_NEON)
- uint8x8_t v_black = vdup_n_u8(0xff);
- for (; x + 8 <= width; x += 8) {
- uint8x8x4_t v_pixel = vld4_u8((uint8_t *)&pixel[x]);
- v_pixel.val[3] = vadd_u8(v_pixel.val[3], v_black);
- vst4_u8((uint8_t *)&pixel[x], v_pixel);
- }
-#endif
- for (; x < width; x += 1) {
- pixel[x].a += 0xff;
- }
-}
-
-static void jebp__vp8l_pred0(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- (void)top;
- jebp__vp8l_pred_black(pixel, width);
-}
-
-JEBP__INLINE void jebp__vp8l_pred_left(jebp_color_t *pixel, jebp_int width) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- __m128i v_left;
- if (width >= 4) {
- v_left = _mm_cvtsi32_si128(*(int *)&pixel[-1]);
- }
- for (; x + 4 <= width; x += 4) {
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- v_pixel = _mm_add_epi8(v_pixel, v_left);
- v_left = _mm_bslli_si128(v_pixel, 4);
- v_pixel = _mm_add_epi8(v_pixel, v_left);
- v_left = _mm_bslli_si128(v_pixel, 8);
- v_pixel = _mm_add_epi8(v_pixel, v_left);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- v_left = _mm_bsrli_si128(v_pixel, 12);
- }
-#elif defined(JEBP__SIMD_NEON)
- uint8x16_t v_zero = vdupq_n_u8(0);
- uint8x16_t v_left;
- if (width >= 4) {
- v_left = jebp__neon_load_px1(&pixel[-1]);
- v_left = vextq_u8(v_left, v_zero, 12);
- }
- for (; x + 4 <= width; x += 4) {
- uint8x16_t v_pixel = vld1q_u8((uint8_t *)&pixel[x]);
- v_pixel = vaddq_u8(v_pixel, v_left);
- v_left = vextq_u8(v_zero, v_pixel, 12);
- v_pixel = vaddq_u8(v_pixel, v_left);
- v_left = vextq_u8(v_zero, v_pixel, 8);
- v_pixel = vaddq_u8(v_pixel, v_left);
- vst1q_u8((uint8_t *)&pixel[x], v_pixel);
- v_left = vextq_u8(v_pixel, v_zero, 12);
- }
-#endif
- for (; x < width; x += 1) {
- pixel[x].r += pixel[x - 1].r;
- pixel[x].g += pixel[x - 1].g;
- pixel[x].b += pixel[x - 1].b;
- pixel[x].a += pixel[x - 1].a;
- }
-}
-
-static void jebp__vp8l_pred1(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- (void)top;
- jebp__vp8l_pred_left(pixel, width);
-}
-
-JEBP__INLINE void jebp__vp8l_pred_top(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- for (; x + 4 <= width; x += 4) {
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- __m128i v_top = _mm_loadu_si128((__m128i *)&top[x]);
- v_pixel = _mm_add_epi8(v_pixel, v_top);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- }
-#elif defined(JEBP__SIMD_NEON)
- for (; x + 4 <= width; x += 4) {
- uint8x16_t v_pixel = vld1q_u8((uint8_t *)&pixel[x]);
- uint8x16_t v_top = vld1q_u8((uint8_t *)&top[x]);
- v_pixel = vaddq_u8(v_pixel, v_top);
- vst1q_u8((uint8_t *)&pixel[x], v_pixel);
- }
-#endif
- for (; x < width; x += 1) {
- pixel[x].r += top[x].r;
- pixel[x].g += top[x].g;
- pixel[x].b += top[x].b;
- pixel[x].a += top[x].a;
- }
-}
-
-static void jebp__vp8l_pred2(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp__vp8l_pred_top(pixel, top, width);
-}
-
-static void jebp__vp8l_pred3(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp__vp8l_pred_top(pixel, &top[1], width);
-}
-
-static void jebp__vp8l_pred4(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp__vp8l_pred_top(pixel, &top[-1], width);
-}
-
-static void jebp__vp8l_pred5(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- __m128i v_left;
- __m128i v_top;
- if (width >= 4) {
- v_left = _mm_cvtsi32_si128(*(int *)&pixel[-1]);
- v_top = _mm_loadu_si128((__m128i *)top);
- }
- for (; x + 4 <= width; x += 4) {
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- __m128i v_next = _mm_loadu_si128((__m128i *)&top[x + 4]);
- __m128i v_tr = jebp__sse_move_px1(v_top, v_next);
- v_tr = _mm_shuffle_epi32(v_tr, _MM_SHUFFLE(0, 3, 2, 1));
- jebp__m128x4i v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- __m128i v_avg = jebp__sse_avg2_u8x16(v_left, v_tr, v_top);
- v_pixel4.v[i] = _mm_add_epi8(v_pixel, v_avg);
- v_left = _mm_shuffle_epi32(v_pixel4.v[i], _MM_SHUFFLE(2, 1, 0, 3));
- })
- v_pixel = jebp__sse_flatten_px4(v_pixel4);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- v_top = v_next;
- }
-#elif defined(JEBP__SIMD_NEON)
- uint8x16_t v_left;
- uint8x16_t v_top;
- if (width >= 4) {
- v_left = jebp__neon_load_px1(&pixel[-1]);
- v_top = vld1q_u8((uint8_t *)top);
- }
- for (; x + 4 <= width; x += 4) {
- uint8x16_t v_pixel = vld1q_u8((uint8_t *)&pixel[x]);
- uint8x16_t v_next = vld1q_u8((uint8_t *)&top[x + 4]);
- uint8x16_t v_tr = vextq_u8(v_top, v_next, 4);
- uint8x16x4_t v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- uint8x16_t v_avg = vhaddq_u8(v_left, v_tr);
- v_avg = vhaddq_u8(v_avg, v_top);
- v_pixel4.val[i] = vaddq_u8(v_pixel, v_avg);
- v_left = vextq_u8(v_pixel4.val[i], v_pixel4.val[i], 12);
- })
- v_pixel = jebp__neon_flatten_px4(v_pixel4);
- vst1q_u8((uint8_t *)&pixel[x], v_pixel);
- v_top = v_next;
- }
-#endif
- for (; x < width; x += 1) {
- pixel[x].r +=
- JEBP__AVG(JEBP__AVG(pixel[x - 1].r, top[x + 1].r), top[x].r);
- pixel[x].g +=
- JEBP__AVG(JEBP__AVG(pixel[x - 1].g, top[x + 1].g), top[x].g);
- pixel[x].b +=
- JEBP__AVG(JEBP__AVG(pixel[x - 1].b, top[x + 1].b), top[x].b);
- pixel[x].a +=
- JEBP__AVG(JEBP__AVG(pixel[x - 1].a, top[x + 1].a), top[x].a);
- }
-}
-
-JEBP__INLINE void jebp__vp8l_pred_avgtl(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- __m128i v_left;
- if (width >= 4) {
- v_left = _mm_cvtsi32_si128(*(int *)&pixel[-1]);
- }
- for (; x + 4 <= width; x += 4) {
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- __m128i v_top = _mm_loadu_si128((__m128i *)&top[x]);
- jebp__m128x4i v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- __m128i v_avg = jebp__sse_avg_u8x16(v_left, v_top);
- v_pixel4.v[i] = _mm_add_epi8(v_pixel, v_avg);
- v_left = _mm_shuffle_epi32(v_pixel4.v[i], _MM_SHUFFLE(2, 1, 0, 3));
- })
- v_pixel = jebp__sse_flatten_px4(v_pixel4);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- }
-#elif defined(JEBP__SIMD_NEON)
- uint8x16_t v_left;
- if (width >= 4) {
- v_left = jebp__neon_load_px1(&pixel[-1]);
- }
- for (; x + 4 <= width; x += 4) {
- uint8x16_t v_pixel = vld1q_u8((uint8_t *)&pixel[x]);
- uint8x16_t v_top = vld1q_u8((uint8_t *)&top[x]);
- uint8x16x4_t v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- uint8x16_t v_avg = vhaddq_u8(v_left, v_top);
- v_pixel4.val[i] = vaddq_u8(v_pixel, v_avg);
- v_left = vextq_u8(v_pixel4.val[i], v_pixel4.val[i], 12);
- })
- v_pixel = jebp__neon_flatten_px4(v_pixel4);
- vst1q_u8((uint8_t *)&pixel[x], v_pixel);
- }
-#endif
- for (; x < width; x += 1) {
- pixel[x].r += JEBP__AVG(pixel[x - 1].r, top[x].r);
- pixel[x].g += JEBP__AVG(pixel[x - 1].g, top[x].g);
- pixel[x].b += JEBP__AVG(pixel[x - 1].b, top[x].b);
- pixel[x].a += JEBP__AVG(pixel[x - 1].a, top[x].a);
- }
-}
-
-static void jebp__vp8l_pred6(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp__vp8l_pred_avgtl(pixel, &top[-1], width);
-}
-
-static void jebp__vp8l_pred7(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp__vp8l_pred_avgtl(pixel, top, width);
-}
-
-JEBP__INLINE void jebp__vp8l_pred_avgtr(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- __m128i v_top;
- if (width >= 4) {
- v_top = _mm_loadu_si128((__m128i *)top);
- }
- for (; x + 4 <= width; x += 4) {
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- __m128i v_next = _mm_loadu_si128((__m128i *)&top[x + 4]);
- __m128i v_tr = jebp__sse_move_px1(v_top, v_next);
- v_tr = _mm_shuffle_epi32(v_tr, _MM_SHUFFLE(0, 3, 2, 1));
- v_tr = jebp__sse_avg_u8x16(v_top, v_tr);
- v_pixel = _mm_add_epi8(v_pixel, v_tr);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- v_top = v_next;
- }
-#elif defined(JEBP__SIMD_NEON)
- uint8x16_t v_top;
- if (width >= 4) {
- v_top = vld1q_u8((uint8_t *)top);
- }
- for (; x + 4 <= width; x += 4) {
- uint8x16_t v_pixel = vld1q_u8((uint8_t *)&pixel[x]);
- uint8x16_t v_next = vld1q_u8((uint8_t *)&top[x + 4]);
- uint8x16_t v_tr = vextq_u8(v_top, v_next, 4);
- v_tr = vhaddq_u8(v_top, v_tr);
- v_pixel = vaddq_u8(v_pixel, v_tr);
- vst1q_u8((uint8_t *)&pixel[x], v_pixel);
- v_top = v_next;
- }
-#endif
- for (; x < width; x += 1) {
- pixel[x].r += JEBP__AVG(top[x].r, top[x + 1].r);
- pixel[x].g += JEBP__AVG(top[x].g, top[x + 1].g);
- pixel[x].b += JEBP__AVG(top[x].b, top[x + 1].b);
- pixel[x].a += JEBP__AVG(top[x].a, top[x + 1].a);
- }
-}
-
-static void jebp__vp8l_pred8(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp__vp8l_pred_avgtr(pixel, &top[-1], width);
-}
-
-static void jebp__vp8l_pred9(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp__vp8l_pred_avgtr(pixel, top, width);
-}
-
-static void jebp__vp8l_pred10(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- __m128i v_left;
- __m128i v_tl;
- __m128i v_top;
- if (width >= 4) {
- v_left = _mm_cvtsi32_si128(*(int *)&pixel[-1]);
- v_tl = _mm_cvtsi32_si128(*(int *)&top[-1]);
- v_top = _mm_loadu_si128((__m128i *)top);
- }
- for (; x + 4 <= width; x += 4) {
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- __m128i v_next = _mm_loadu_si128((__m128i *)&top[x + 4]);
- __m128i v_rot = _mm_shuffle_epi32(v_top, _MM_SHUFFLE(2, 1, 0, 3));
- v_tl = jebp__sse_move_px1(v_rot, v_tl);
- __m128i v_tr = jebp__sse_move_px1(v_top, v_next);
- v_tr = _mm_shuffle_epi32(v_tr, _MM_SHUFFLE(0, 3, 2, 1));
- v_tr = jebp__sse_avg_u8x16(v_top, v_tr);
- jebp__m128x4i v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- __m128i v_avg = jebp__sse_avg2_u8x16(v_left, v_tl, v_tr);
- v_pixel4.v[i] = _mm_add_epi8(v_pixel, v_avg);
- v_left = _mm_shuffle_epi32(v_pixel4.v[i], _MM_SHUFFLE(2, 1, 0, 3));
- })
- v_pixel = jebp__sse_flatten_px4(v_pixel4);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- v_tl = v_rot;
- v_top = v_next;
- }
-#elif defined(JEBP__SIMD_NEON)
- uint8x16_t v_left;
- uint8x16_t v_tl;
- uint8x16_t v_top;
- if (width >= 4) {
- v_left = jebp__neon_load_px1(&pixel[-1]);
- v_tl = jebp__neon_load_px1(&top[-1]);
- v_top = vld1q_u8((uint8_t *)top);
- }
- for (; x + 4 <= width; x += 4) {
- uint8x16_t v_pixel = vld1q_u8((uint8_t *)&pixel[x]);
- uint8x16_t v_next = vld1q_u8((uint8_t *)&top[x + 4]);
- v_tl = vextq_u8(v_tl, v_top, 12);
- uint8x16_t v_tr = vextq_u8(v_top, v_next, 4);
- v_tr = vhaddq_u8(v_top, v_tr);
- uint8x16x4_t v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- uint8x16_t v_avg = vhaddq_u8(v_left, v_tl);
- v_avg = vhaddq_u8(v_avg, v_tr);
- v_pixel4.val[i] = vaddq_u8(v_pixel, v_avg);
- v_left = vextq_u8(v_pixel4.val[i], v_pixel4.val[i], 12);
- })
- v_pixel = jebp__neon_flatten_px4(v_pixel4);
- vst1q_u8((uint8_t *)&pixel[x], v_pixel);
- v_tl = v_top;
- v_top = v_next;
- }
-#endif
- for (; x < width; x += 1) {
- pixel[x].r += JEBP__AVG(JEBP__AVG(pixel[x - 1].r, top[x - 1].r),
- JEBP__AVG(top[x].r, top[x + 1].r));
- pixel[x].g += JEBP__AVG(JEBP__AVG(pixel[x - 1].g, top[x - 1].g),
- JEBP__AVG(top[x].g, top[x + 1].g));
- pixel[x].b += JEBP__AVG(JEBP__AVG(pixel[x - 1].b, top[x - 1].b),
- JEBP__AVG(top[x].b, top[x + 1].b));
- pixel[x].a += JEBP__AVG(JEBP__AVG(pixel[x - 1].a, top[x - 1].a),
- JEBP__AVG(top[x].a, top[x + 1].a));
- }
-}
-
-JEBP__INLINE jebp_int jebp__vp8l_pred_dist(jebp_color_t *pix1,
- jebp_color_t *pix2) {
- return JEBP__ABS(pix1->r - pix2->r) + JEBP__ABS(pix1->g - pix2->g) +
- JEBP__ABS(pix1->b - pix2->b) + JEBP__ABS(pix1->a - pix2->a);
-}
-
-static void jebp__vp8l_pred11(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- __m128i v_left;
- __m128i v_tl;
- if (width >= 4) {
- v_left = _mm_cvtsi32_si128(*(int *)&pixel[-1]);
- v_tl = _mm_cvtsi32_si128(*(int *)&top[-1]);
- }
- for (; x + 4 <= width; x += 4) {
- __m128i v_ldist, v_tdist, v_cmp, v_pixello, v_pixelhi;
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- __m128i v_top = _mm_loadu_si128((__m128i *)&top[x]);
- __m128i v_rot = _mm_shuffle_epi32(v_top, _MM_SHUFFLE(2, 1, 0, 3));
- v_tl = jebp__sse_move_px1(v_rot, v_tl);
- // Pixel 0
- // This does double the SAD result but if both distances are doubled the
- // comparison should still be the same
- __m128i v_tllo = _mm_unpacklo_epi32(v_tl, v_tl);
- __m128i v_toplo = _mm_unpacklo_epi32(v_top, v_top);
- v_ldist = _mm_sad_epu8(v_tllo, v_toplo);
- v_tdist = _mm_unpacklo_epi32(v_left, v_left);
- v_tdist = _mm_sad_epu8(v_tllo, v_tdist);
- v_cmp = _mm_cmplt_epi32(v_ldist, v_tdist);
- v_pixello = jebp__sse_bsela_u8x16(v_pixel, v_cmp, v_left, v_top);
- v_left = _mm_bslli_si128(v_pixello, 4);
- // Pixel 1
- v_tdist = _mm_unpacklo_epi32(v_left, v_left);
- v_tdist = _mm_sad_epu8(v_tllo, v_tdist);
- v_cmp = _mm_cmplt_epi32(v_ldist, v_tdist);
- v_cmp = _mm_bsrli_si128(v_cmp, 4);
- v_pixello = jebp__sse_bsela_u8x16(v_pixel, v_cmp, v_left, v_top);
- v_pixello = _mm_unpacklo_epi32(v_left, v_pixello);
- v_left = _mm_bsrli_si128(v_pixello, 4);
- // Pixel 2
- __m128i v_tlhi = _mm_shuffle_epi32(v_tl, _MM_SHUFFLE(2, 2, 3, 3));
- __m128i v_tophi = _mm_shuffle_epi32(v_top, _MM_SHUFFLE(2, 2, 3, 3));
- v_ldist = _mm_sad_epu8(v_tlhi, v_tophi);
- v_tdist = _mm_shuffle_epi32(v_left, _MM_SHUFFLE(2, 2, 3, 3));
- v_tdist = _mm_sad_epu8(v_tlhi, v_tdist);
- v_cmp = _mm_cmplt_epi32(v_ldist, v_tdist);
- v_pixelhi = jebp__sse_bsela_u8x16(v_pixel, v_cmp, v_left, v_top);
- v_left = _mm_bslli_si128(v_pixelhi, 4);
- // Pixel 3
- v_tdist = _mm_shuffle_epi32(v_left, _MM_SHUFFLE(2, 2, 3, 3));
- v_tdist = _mm_sad_epu8(v_tlhi, v_tdist);
- v_cmp = _mm_cmplt_epi32(v_ldist, v_tdist);
- v_cmp = _mm_bslli_si128(v_cmp, 12);
- v_pixelhi = jebp__sse_bsela_u8x16(v_pixel, v_cmp, v_left, v_top);
- v_pixelhi = _mm_unpackhi_epi32(v_left, v_pixelhi);
- v_left = _mm_bsrli_si128(v_pixelhi, 12);
- v_pixel = _mm_unpackhi_epi64(v_pixello, v_pixelhi);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- v_tl = v_rot;
- }
-#elif defined(JEBP__SIMD_NEON)
- uint8x16_t v_left;
- uint8x16_t v_tl;
- if (width >= 4) {
- v_left = jebp__neon_load_px1(&pixel[-1]);
- v_tl = jebp__neon_load_px1(&top[-1]);
- }
- for (; x + 4 <= width; x += 4) {
- uint8x16_t v_pixel = vld1q_u8((uint8_t *)&pixel[x]);
- uint8x16_t v_top = vld1q_u8((uint8_t *)&top[x]);
- v_tl = vextq_u8(v_tl, v_top, 12);
- uint32x4_t v_ldist = jebp__neon_sad_px4(v_tl, v_top);
- uint8x16x4_t v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- uint32x4_t v_tdist = jebp__neon_sad_px4(v_tl, v_left);
- uint32x4_t v_cmp = vcltq_u32(v_ldist, v_tdist);
- uint8x16_t v_pred = vbslq_u8((uint8x16_t)v_cmp, v_left, v_top);
- v_pixel4.val[i] = vaddq_u8(v_pixel, v_pred);
- v_left = vextq_u8(v_pixel4.val[i], v_pixel4.val[i], 12);
- })
- v_pixel = jebp__neon_flatten_px4(v_pixel4);
- vst1q_u8((uint8_t *)&pixel[x], v_pixel);
- v_tl = v_top;
- }
-#endif
- for (; x < width; x += 1) {
- jebp_int ldist = jebp__vp8l_pred_dist(&top[x - 1], &top[x]);
- jebp_int tdist = jebp__vp8l_pred_dist(&top[x - 1], &pixel[x - 1]);
- if (ldist < tdist) {
- jebp__vp8l_pred_left(&pixel[x], 1);
- } else {
- jebp__vp8l_pred_top(&pixel[x], &top[x], 1);
- }
- }
-}
-
-static void jebp__vp8l_pred12(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- __m128i v_left;
- __m128i v_tl;
- if (width >= 4) {
- v_left = _mm_cvtsi32_si128(*(int *)&pixel[-1]);
- v_tl = _mm_cvtsi32_si128(*(int *)&top[-1]);
- }
- for (; x + 4 <= width; x += 4) {
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- __m128i v_top = _mm_loadu_si128((__m128i *)&top[x]);
- __m128i v_rot = _mm_shuffle_epi32(v_top, _MM_SHUFFLE(2, 1, 0, 3));
- v_tl = jebp__sse_move_px1(v_rot, v_tl);
- __m128i v_max = _mm_max_epu8(v_top, v_tl);
- __m128i v_min = _mm_min_epu8(v_top, v_tl);
- __m128i v_diff = _mm_sub_epi8(v_max, v_min);
- __m128i v_pos = _mm_cmpeq_epi8(v_max, v_top);
- jebp__m128x4i v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- __m128i v_add = _mm_adds_epu8(v_left, v_diff);
- __m128i v_sub = _mm_subs_epu8(v_left, v_diff);
- v_pixel4.v[i] = jebp__sse_bsela_u8x16(v_pixel, v_pos, v_add, v_sub);
- v_left = _mm_shuffle_epi32(v_pixel4.v[i], _MM_SHUFFLE(2, 1, 0, 3));
- })
- v_pixel = jebp__sse_flatten_px4(v_pixel4);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- v_tl = v_rot;
- }
-#elif defined(JEBP__SIMD_NEON)
- uint8x16_t v_left;
- uint8x16_t v_tl;
- if (width >= 4) {
- v_left = jebp__neon_load_px1(&pixel[-1]);
- v_tl = jebp__neon_load_px1(&top[-1]);
- }
- for (; x + 4 <= width; x += 4) {
- uint8x16_t v_pixel = vld1q_u8((uint8_t *)&pixel[x]);
- uint8x16_t v_top = vld1q_u8((uint8_t *)&top[x]);
- v_tl = vextq_u8(v_tl, v_top, 12);
- uint8x16_t v_diff = vabdq_u8(v_top, v_tl);
- uint8x16_t v_neg = vcltq_u8(v_top, v_tl);
- uint8x16x4_t v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- uint8x16_t v_add = vqaddq_u8(v_left, v_diff);
- uint8x16_t v_sub = vqsubq_u8(v_left, v_diff);
- uint8x16_t v_pred = vbslq_u8(v_neg, v_sub, v_add);
- v_pixel4.val[i] = vaddq_u8(v_pixel, v_pred);
- v_left = vextq_u8(v_pixel4.val[i], v_pixel4.val[i], 12);
- })
- v_pixel = jebp__neon_flatten_px4(v_pixel4);
- vst1q_u8((uint8_t *)&pixel[x], v_pixel);
- v_tl = v_top;
- }
-#endif
- for (; x < width; x += 1) {
- pixel[x].r +=
- JEBP__CLAMP_UBYTE(pixel[x - 1].r + top[x].r - top[x - 1].r);
- pixel[x].g +=
- JEBP__CLAMP_UBYTE(pixel[x - 1].g + top[x].g - top[x - 1].g);
- pixel[x].b +=
- JEBP__CLAMP_UBYTE(pixel[x - 1].b + top[x].b - top[x - 1].b);
- pixel[x].a +=
- JEBP__CLAMP_UBYTE(pixel[x - 1].a + top[x].a - top[x - 1].a);
- }
-}
-
-static void jebp__vp8l_pred13(jebp_color_t *pixel, jebp_color_t *top,
- jebp_int width) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- __m128i v_mask = _mm_set1_epi8(0x7f);
- __m128i v_left;
- __m128i v_tl;
- if (width >= 4) {
- v_left = _mm_cvtsi32_si128(*(int *)&pixel[-1]);
- v_tl = _mm_cvtsi32_si128(*(int *)&top[-1]);
- }
- for (; x + 4 <= width; x += 4) {
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- __m128i v_top = _mm_loadu_si128((__m128i *)&top[x]);
- __m128i v_rot = _mm_shuffle_epi32(v_top, _MM_SHUFFLE(2, 1, 0, 3));
- v_tl = jebp__sse_move_px1(v_rot, v_tl);
- jebp__m128x4i v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- __m128i v_avg = jebp__sse_avg_u8x16(v_left, v_top);
- __m128i v_max = _mm_max_epu8(v_avg, v_tl);
- __m128i v_min = _mm_min_epu8(v_avg, v_tl);
- __m128i v_diff = _mm_sub_epi8(v_max, v_min);
- v_diff = _mm_srli_epi16(v_diff, 1);
- v_diff = _mm_and_si128(v_diff, v_mask);
- __m128i v_pos = _mm_cmpeq_epi8(v_max, v_avg);
- __m128i v_add = _mm_adds_epu8(v_avg, v_diff);
- __m128i v_sub = _mm_subs_epu8(v_avg, v_diff);
- v_pixel4.v[i] = jebp__sse_bsela_u8x16(v_pixel, v_pos, v_add, v_sub);
- v_left = _mm_shuffle_epi32(v_pixel4.v[i], _MM_SHUFFLE(2, 1, 0, 3));
- })
- v_pixel = jebp__sse_flatten_px4(v_pixel4);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- v_tl = v_rot;
- }
-#elif defined(JEBP__SIMD_NEON)
- uint8x16_t v_left;
- uint8x16_t v_tl;
- if (width >= 4) {
- v_left = jebp__neon_load_px1(&pixel[-1]);
- v_tl = jebp__neon_load_px1(&top[-1]);
- }
- for (; x + 4 <= width; x += 4) {
- uint8x16_t v_pixel = vld1q_u8((uint8_t *)&pixel[x]);
- uint8x16_t v_top = vld1q_u8((uint8_t *)&top[x]);
- v_tl = vextq_u8(v_tl, v_top, 12);
- uint8x16x4_t v_pixel4;
- JEBP__UNROLL4(jebp_int i, {
- uint8x16_t v_avg = vhaddq_u8(v_left, v_top);
- uint8x16_t v_diff = vabdq_u8(v_avg, v_tl);
- v_diff = vshrq_n_u8(v_diff, 1);
- uint8x16_t v_neg = vcltq_u8(v_avg, v_tl);
- uint8x16_t v_add = vqaddq_u8(v_avg, v_diff);
- uint8x16_t v_sub = vqsubq_u8(v_avg, v_diff);
- uint8x16_t v_pred = vbslq_u8(v_neg, v_sub, v_add);
- v_pixel4.val[i] = vaddq_u8(v_pixel, v_pred);
- v_left = vextq_u8(v_pixel4.val[i], v_pixel4.val[i], 12);
- })
- v_pixel = jebp__neon_flatten_px4(v_pixel4);
- vst1q_u8((uint8_t *)&pixel[x], v_pixel);
- v_tl = v_top;
- }
-#endif
- for (; x < width; x += 1) {
- jebp_color_t avg = {JEBP__AVG(pixel[x - 1].r, top[x].r),
- JEBP__AVG(pixel[x - 1].g, top[x].g),
- JEBP__AVG(pixel[x - 1].b, top[x].b),
- JEBP__AVG(pixel[x - 1].a, top[x].a)};
- pixel[x].r += JEBP__CLAMP_UBYTE(avg.r + (avg.r - top[x - 1].r) / 2);
- pixel[x].g += JEBP__CLAMP_UBYTE(avg.g + (avg.g - top[x - 1].g) / 2);
- pixel[x].b += JEBP__CLAMP_UBYTE(avg.b + (avg.b - top[x - 1].b) / 2);
- pixel[x].a += JEBP__CLAMP_UBYTE(avg.a + (avg.a - top[x - 1].a) / 2);
- }
-}
-
-static const jebp__vp8l_pred_t jebp__vp8l_preds[JEBP__NB_VP8L_PRED_TYPES] = {
- jebp__vp8l_pred0, jebp__vp8l_pred1, jebp__vp8l_pred2, jebp__vp8l_pred3,
- jebp__vp8l_pred4, jebp__vp8l_pred5, jebp__vp8l_pred6, jebp__vp8l_pred7,
- jebp__vp8l_pred8, jebp__vp8l_pred9, jebp__vp8l_pred10, jebp__vp8l_pred11,
- jebp__vp8l_pred12, jebp__vp8l_pred13};
-
-/**
- * VP8L transforms
- */
-typedef enum jebp__transform_type_t {
- JEBP__TRANSFORM_PREDICT,
- JEBP__TRANSFORM_COLOR,
- JEBP__TRANSFORM_GREEN,
- JEBP__TRANSFORM_PALETTE,
- JEBP__NB_TRANSFORMS
-} jebp__transform_type_t;
-
-typedef struct jebp__transform_t {
- jebp__transform_type_t type;
- jebp__subimage_t image;
-} jebp__transform_t;
-
-static jebp_error_t jebp__read_transform(jebp__transform_t *transform,
- jebp__bit_reader_t *bits,
- jebp_image_t *image) {
- jebp_error_t err = JEBP_OK;
- transform->type = jebp__read_bits(bits, 2, &err);
- if (err != JEBP_OK) {
- return err;
- }
- if (transform->type == JEBP__TRANSFORM_PALETTE) {
- // TODO: support palette images
- return JEBP_ERROR_NOSUP_PALETTE;
- } else if (transform->type != JEBP__TRANSFORM_GREEN) {
- err = jebp__read_subimage(&transform->image, bits, image);
- }
- return err;
-}
-
-static void jebp__free_transform(jebp__transform_t *transform) {
- if (transform->type != JEBP__TRANSFORM_GREEN) {
- jebp_free_image((jebp_image_t *)&transform->image);
- }
-}
-
-JEBP__INLINE jebp_error_t jebp__apply_predict_row(jebp_color_t *pixel,
- jebp_color_t *top,
- jebp_int width,
- jebp_color_t *predict_pixel) {
- if (predict_pixel->g >= JEBP__NB_VP8L_PRED_TYPES) {
- return JEBP_ERROR_INVDATA;
- }
- jebp__vp8l_preds[predict_pixel->g](pixel, top, width);
- return JEBP_OK;
-}
-
-JEBP__INLINE jebp_error_t jebp__apply_predict_transform(
- jebp_image_t *image, jebp__subimage_t *predict_image) {
- jebp_error_t err;
- jebp_color_t *pixel = image->pixels;
- jebp_color_t *top = pixel;
- jebp_int predict_width = predict_image->width - 1;
- jebp_int block_size = 1 << predict_image->block_bits;
- jebp_int end_size =
- image->width - (predict_width << predict_image->block_bits);
- if (predict_width == 0) {
- // Special case: if there is only one block the first block which is
- // shortened by one pixel (due to the left prediction)
- // needs to be `end_size` and the proper end block then
- // needs to be skipped.
- block_size = end_size;
- end_size = 0;
- }
- // Use opaque-black prediction for the top-left pixel
- jebp__vp8l_pred_black(pixel, 1);
- // Use left prediction for the top row
- jebp__vp8l_pred_left(pixel + 1, image->width - 1);
- pixel += image->width;
- for (jebp_int y = 1; y < image->height; y += 1) {
- jebp_color_t *predict_row =
- &predict_image->pixels[(y >> predict_image->block_bits) *
- predict_image->width];
- // Use top prediction for the left column
- jebp__vp8l_pred_top(pixel, top, 1);
- // Finish the rest of the first block
- if ((err = jebp__apply_predict_row(pixel + 1, top + 1, block_size - 1,
- predict_row)) != JEBP_OK) {
- return err;
- }
- pixel += block_size;
- top += block_size;
- for (jebp_int x = 1; x < predict_width; x += 1) {
- if ((err = jebp__apply_predict_row(pixel, top, block_size,
- &predict_row[x])) != JEBP_OK) {
- return err;
- }
- pixel += block_size;
- top += block_size;
- }
- jebp__apply_predict_row(pixel, top, end_size,
- &predict_row[predict_width]);
- pixel += end_size;
- top += end_size;
- }
- return JEBP_OK;
-}
-
-JEBP__INLINE void jebp__apply_color_row(jebp_color_t *pixel, jebp_int width,
- jebp_color_t *color_pixel) {
- jebp_int x = 0;
-#if defined(JEBP__SIMD_SSE2)
- jebp_ushort color_r = ((jebp_short)(color_pixel->r << 8) >> 5);
- jebp_ushort color_g = ((jebp_short)(color_pixel->g << 8) >> 5);
- jebp_ushort color_b = ((jebp_short)(color_pixel->b << 8) >> 5);
- __m128i v_color_bg = _mm_set1_epi32(color_b | ((jebp_uint)color_g << 16));
- __m128i v_color_r = _mm_set1_epi32(color_r);
- __m128i v_masklo = _mm_set1_epi16((short)0x00ff);
- __m128i v_maskhi = _mm_set1_epi16((short)0xff00);
- for (; x + 4 <= width; x += 4) {
- __m128i v_pixel = _mm_loadu_si128((__m128i *)&pixel[x]);
- __m128i v_green = _mm_and_si128(v_pixel, v_maskhi);
- v_green = _mm_shufflelo_epi16(v_green, _MM_SHUFFLE(2, 2, 0, 0));
- v_green = _mm_shufflehi_epi16(v_green, _MM_SHUFFLE(2, 2, 0, 0));
- __m128i v_bg = _mm_mulhi_epi16(v_green, v_color_bg);
- v_bg = _mm_and_si128(v_bg, v_masklo);
- v_pixel = _mm_add_epi8(v_pixel, v_bg);
- __m128i v_red = _mm_slli_epi16(v_pixel, 8);
- v_red = _mm_mulhi_epi16(v_red, v_color_r);
- v_red = _mm_and_si128(v_red, v_masklo);
- v_red = _mm_slli_epi32(v_red, 16);
- v_pixel = _mm_add_epi8(v_pixel, v_red);
- _mm_storeu_si128((__m128i *)&pixel[x], v_pixel);
- }
-#elif defined(JEBP__SIMD_NEON)
- int8x8x3_t v_color_pixel = vld3_dup_s8((jebp_byte *)color_pixel);
- for (; x + 8 <= width; x += 8) {
- int16x8_t v_mul;
- int8x8_t v_shr;
- int8x8x4_t v_pixel = vld4_s8((jebp_byte *)&pixel[x]);
- v_mul = vmull_s8(v_pixel.val[1], v_color_pixel.val[2]);
- v_shr = vshrn_n_s16(v_mul, 5);
- v_pixel.val[0] = vadd_s8(v_pixel.val[0], v_shr);
- v_mul = vmull_s8(v_pixel.val[1], v_color_pixel.val[1]);
- v_shr = vshrn_n_s16(v_mul, 5);
- v_pixel.val[2] = vadd_s8(v_pixel.val[2], v_shr);
- v_mul = vmull_s8(v_pixel.val[0], v_color_pixel.val[0]);
- v_shr = vshrn_n_s16(v_mul, 5);
- v_pixel.val[2] = vadd_s8(v_pixel.val[2], v_shr);
- vst4_s8((jebp_byte *)&pixel[x], v_pixel);
- }
-#endif
- for (; x < width; x += 1) {
- pixel[x].r += ((jebp_byte)pixel[x].g * (jebp_byte)color_pixel->b) >> 5;
- pixel[x].b += ((jebp_byte)pixel[x].g * (jebp_byte)color_pixel->g) >> 5;
- pixel[x].b += ((jebp_byte)pixel[x].r * (jebp_byte)color_pixel->r) >> 5;
- }
-}
-
-JEBP__INLINE jebp_error_t jebp__apply_color_transform(
- jebp_image_t *image, jebp__subimage_t *color_image) {
- jebp_color_t *pixel = image->pixels;
- jebp_int color_width = color_image->width - 1;
- jebp_int block_size = 1 << color_image->block_bits;
- jebp_int end_size = image->width - (color_width << color_image->block_bits);
- for (jebp_int y = 0; y < image->height; y += 1) {
- jebp_color_t *color_row =
- &color_image
- ->pixels[(y >> color_image->block_bits) * color_image->width];
- for (jebp_int x = 0; x < color_width; x += 1) {
- jebp__apply_color_row(pixel, block_size, &color_row[x]);
- pixel += block_size;
- }
- jebp__apply_color_row(pixel, end_size, &color_row[color_width]);
- pixel += end_size;
- }
- return JEBP_OK;
-}
-
-JEBP__INLINE jebp_error_t jebp__apply_green_transform(jebp_image_t *image) {
- jebp_int size = image->width * image->height;
- jebp_int i = 0;
-#if defined(JEBP__SIMD_SSE2)
- for (; i + 4 <= size; i += 4) {
- __m128i *pixel = (__m128i *)&image->pixels[i];
- __m128i v_pixel = _mm_loadu_si128(pixel);
- __m128i v_green = _mm_srli_epi16(v_pixel, 8);
- v_green = _mm_shufflelo_epi16(v_green, _MM_SHUFFLE(2, 2, 0, 0));
- v_green = _mm_shufflehi_epi16(v_green, _MM_SHUFFLE(2, 2, 0, 0));
- v_pixel = _mm_add_epi8(v_pixel, v_green);
- _mm_storeu_si128(pixel, v_pixel);
- }
-#elif defined(JEBP__SIMD_NEON)
- for (; i + 16 <= size; i += 16) {
- jebp_ubyte *pixel = (jebp_ubyte *)&image->pixels[i];
- uint8x16x4_t v_pixel = vld4q_u8(pixel);
- v_pixel.val[0] = vaddq_u8(v_pixel.val[0], v_pixel.val[1]);
- v_pixel.val[2] = vaddq_u8(v_pixel.val[2], v_pixel.val[1]);
- vst4q_u8(pixel, v_pixel);
- }
-#endif
- for (; i < size; i += 1) {
- jebp_color_t *pixel = &image->pixels[i];
- pixel->r += pixel->g;
- pixel->b += pixel->g;
- }
- return JEBP_OK;
-}
-
-static jebp_error_t jebp__apply_transform(jebp__transform_t *transform,
- jebp_image_t *image) {
- switch (transform->type) {
- case JEBP__TRANSFORM_PREDICT:
- return jebp__apply_predict_transform(image, &transform->image);
- case JEBP__TRANSFORM_COLOR:
- return jebp__apply_color_transform(image, &transform->image);
- case JEBP__TRANSFORM_GREEN:
- return jebp__apply_green_transform(image);
- default:
- return JEBP_ERROR_NOSUP;
- }
-}
-
-/**
- * VP8L lossless codec
- */
-#define JEBP__VP8L_TAG 0x4c385056
-#define JEBP__VP8L_MAGIC 0x2f
-
-static jebp_error_t jebp__read_vp8l_header(jebp_image_t *image,
- jebp__reader_t *reader,
- jebp__bit_reader_t *bits,
- jebp__chunk_t *chunk) {
- jebp_error_t err = JEBP_OK;
- if (chunk->size < 5) {
- return JEBP_ERROR_INVDATA_HEADER;
- }
- if (jebp__read_uint8(reader, &err) != JEBP__VP8L_MAGIC) {
- return jebp__error(&err, JEBP_ERROR_INVDATA_HEADER);
- }
- jepb__init_bit_reader(bits, reader, chunk->size - 1);
- image->width = jebp__read_bits(bits, 14, &err) + 1;
- image->height = jebp__read_bits(bits, 14, &err) + 1;
- jebp__read_bits(bits, 1, &err); // alpha does not impact decoding
- if (jebp__read_bits(bits, 3, &err) != 0) {
- // version must be 0
- return jebp__error(&err, JEBP_ERROR_NOSUP);
- }
- return err;
-}
-
-static jebp_error_t jebp__read_vp8l_size(jebp_image_t *image,
- jebp__reader_t *reader,
- jebp__chunk_t *chunk) {
- jebp__bit_reader_t bits;
- return jebp__read_vp8l_header(image, reader, &bits, chunk);
-}
-
-static jebp_error_t jebp__read_vp8l_nohead(jebp_image_t *image,
- jebp__bit_reader_t *bits) {
- jebp_error_t err = JEBP_OK;
- jebp__transform_t transforms[4];
- jebp_int nb_transforms = 0;
- for (; nb_transforms <= JEBP__NB_TRANSFORMS; nb_transforms += 1) {
- if (!jebp__read_bits(bits, 1, &err)) {
- // no more transforms to read
- break;
- }
- if (err != JEBP_OK || nb_transforms == JEBP__NB_TRANSFORMS) {
- // too many transforms
- jebp__error(&err, JEBP_ERROR_INVDATA);
- goto free_transforms;
- }
- if ((err = jebp__read_transform(&transforms[nb_transforms], bits,
- image)) != JEBP_OK) {
- goto free_transforms;
- }
- }
- if (err != JEBP_OK) {
- goto free_transforms;
- }
-
- jebp__colcache_t colcache;
- if ((err = jebp__read_colcache(&colcache, bits)) != JEBP_OK) {
- goto free_transforms;
- }
- jebp__subimage_t *huffman_image = &(jebp__subimage_t){0};
- if (!jebp__read_bits(bits, 1, &err)) {
- // there is no huffman image
- huffman_image = NULL;
- }
- if (err != JEBP_OK) {
- jebp__free_colcache(&colcache);
- goto free_transforms;
- }
- if (huffman_image != NULL) {
- if ((err = jebp__read_subimage(huffman_image, bits, image)) !=
- JEBP_OK) {
- jebp__free_colcache(&colcache);
- goto free_transforms;
- }
- }
- err = jebp__read_vp8l_image(image, bits, &colcache, huffman_image);
- jebp__free_colcache(&colcache);
- jebp_free_image((jebp_image_t *)huffman_image);
-
-free_transforms:
- for (nb_transforms -= 1; nb_transforms >= 0; nb_transforms -= 1) {
- if (err == JEBP_OK) {
- err = jebp__apply_transform(&transforms[nb_transforms], image);
- }
- jebp__free_transform(&transforms[nb_transforms]);
- }
- return err;
-}
-
-static jebp_error_t jebp__read_vp8l(jebp_image_t *image, jebp__reader_t *reader,
- jebp__chunk_t *chunk) {
- jebp_error_t err;
- jebp__bit_reader_t bits;
- if ((err = jebp__read_vp8l_header(image, reader, &bits, chunk)) !=
- JEBP_OK) {
- return err;
- }
- if ((err = jebp__read_vp8l_nohead(image, &bits)) != JEBP_OK) {
- return err;
- }
- return JEBP_OK;
-}
-#endif // JEBP_NO_VP8L
-
-/**
- * Public API
- */
-static const char *const jebp__error_strings[JEBP_NB_ERRORS];
-
-const char *jebp_error_string(jebp_error_t err) {
- if (err < 0 || err >= JEBP_NB_ERRORS) {
- err = JEBP_ERROR_UNKNOWN;
- }
- return jebp__error_strings[err];
-}
-
-void jebp_free_image(jebp_image_t *image) {
- if (image != NULL) {
- JEBP_FREE(image->pixels);
- JEBP__CLEAR(image, sizeof(jebp_image_t));
- }
-}
-
-static jebp_error_t jebp__read_size(jebp_image_t *image,
- jebp__reader_t *reader) {
- jebp_error_t err;
- jebp__riff_reader_t riff;
- JEBP__CLEAR(image, sizeof(jebp_image_t));
- if ((err = jebp__read_riff_header(&riff, reader)) != JEBP_OK) {
- return err;
- }
- jebp__chunk_t chunk;
- if ((err = jebp__read_riff_chunk(&riff, &chunk)) != JEBP_OK) {
- return err;
- }
-
- switch (chunk.tag) {
-#ifndef JEBP_NO_VP8L
- case JEBP__VP8L_TAG:
- return jebp__read_vp8l_size(image, reader, &chunk);
-#endif // JEBP_NO_VP8L
- default:
- return JEBP_ERROR_NOSUP_CODEC;
- }
-}
-
-jebp_error_t jebp_decode_size(jebp_image_t *image, size_t size,
- const void *data) {
- if (image == NULL || data == NULL) {
- return JEBP_ERROR_INVAL;
- }
- jebp__reader_t reader;
- jebp__init_memory(&reader, size, data);
- return jebp__read_size(image, &reader);
-}
-
-static jebp_error_t jebp__read(jebp_image_t *image, jebp__reader_t *reader) {
- jebp_error_t err;
- jebp__riff_reader_t riff;
- JEBP__CLEAR(image, sizeof(jebp_image_t));
- if ((err = jebp__read_riff_header(&riff, reader)) != JEBP_OK) {
- return err;
- }
- jebp__chunk_t chunk;
- if ((err = jebp__read_riff_chunk(&riff, &chunk)) != JEBP_OK) {
- return err;
- }
-
- switch (chunk.tag) {
-#ifndef JEBP_NO_VP8L
- case JEBP__VP8L_TAG:
- return jebp__read_vp8l(image, reader, &chunk);
-#endif // JEBP_NO_VP8L
- default:
- return JEBP_ERROR_NOSUP_CODEC;
- }
-}
-
-jebp_error_t jebp_decode(jebp_image_t *image, size_t size, const void *data) {
- if (image == NULL || data == NULL) {
- return JEBP_ERROR_INVAL;
- }
- jebp__reader_t reader;
- jebp__init_memory(&reader, size, data);
- return jebp__read(image, &reader);
-}
-
-#ifndef JEBP_NO_STDIO
-jebp_error_t jebp_read_size(jebp_image_t *image, const char *path) {
- jebp_error_t err;
- if (image == NULL || path == NULL) {
- return JEBP_ERROR_INVAL;
- }
- jebp__reader_t reader;
- if ((err = jebp__open_file(&reader, path)) != JEBP_OK) {
- return err;
- }
- err = jebp__read_size(image, &reader);
- jebp__close_file(&reader);
- return err;
-}
-
-jebp_error_t jebp_read(jebp_image_t *image, const char *path) {
- jebp_error_t err;
- if (image == NULL || path == NULL) {
- return JEBP_ERROR_INVAL;
- }
- jebp__reader_t reader;
- if ((err = jebp__open_file(&reader, path)) != JEBP_OK) {
- return err;
- }
- err = jebp__read(image, &reader);
- jebp__close_file(&reader);
- return err;
-}
-#endif // JEBP_NO_STDIO
-
-/**
- * Lookup tables
- */
-// These are moved to the end of the file since some of them are very large and
-// putting them in the middle of the code would disrupt the flow of reading.
-// Especially since in most situations the values in these tables are
-// unimportant to the developer.
-#ifndef JEBP_NO_VP8L
-// The order that meta lengths are read
-static const jebp_byte jebp__meta_length_order[JEBP__NB_META_SYMBOLS] = {
- 17, 18, 0, 1, 2, 3, 4, 5, 16, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
-
-// {X, Y} offsets from the pixel when decoding short distance codes
-static const jebp_byte jebp__vp8l_offsets[JEBP__NB_VP8L_OFFSETS][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}};
-#endif // JEBP_NO_VP8L
-
-// Error strings to return from jebp_error_string
-static const char *const jebp__error_strings[JEBP_NB_ERRORS] = {
- "Ok",
- "Invalid value or argument",
- "Invalid data or corrupted file",
- "Invalid WebP header or corrupted file",
- "End of file",
- "Feature not supported",
- "Codec not supported",
- "Color-indexing or palettes are not supported",
- "Not enough memory",
- "I/O error",
- "Unknown error"};
-#endif // JEBP_IMPLEMENTATION