diff --git a/3rdparty/dav1d/LICENSE b/3rdparty/dav1d/LICENSE new file mode 100644 index 0000000..8d4d641 --- /dev/null +++ b/3rdparty/dav1d/LICENSE @@ -0,0 +1,23 @@ +Copyright © 2018-2025, VideoLAN and dav1d authors +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. + +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 OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/dav1d/config.h b/3rdparty/dav1d/config.h new file mode 100644 index 0000000..099a28f --- /dev/null +++ b/3rdparty/dav1d/config.h @@ -0,0 +1,95 @@ +/* + * dav1d build configuration for bimg. + */ + +#ifndef DAV1D_CONFIG_H +#define DAV1D_CONFIG_H + +#define ARCH_AARCH64 0 +#define ARCH_ARM 0 +#define ARCH_LOONGARCH 0 +#define ARCH_LOONGARCH32 0 +#define ARCH_LOONGARCH64 0 +#define ARCH_PPC64LE 0 +#define ARCH_RISCV 0 +#define ARCH_RV32 0 +#define ARCH_RV64 0 +#define ARCH_X86 0 +#define ARCH_X86_32 0 +#define ARCH_X86_64 0 +#define HAVE_ASM 0 +#define CONFIG_8BPC 1 +#define CONFIG_16BPC 1 +#define CONFIG_LOG 1 +#define TRIM_DSP_FUNCTIONS 0 +#define ENDIANNESS_BIG 0 + +#if defined(_WIN32) +# define HAVE_ALIGNED_ALLOC 0 +# define HAVE_POSIX_MEMALIGN 0 +# define HAVE_MEMALIGN 0 +# define HAVE_CLOCK_GETTIME 0 +# define HAVE_SIGACTION 0 +# define HAVE_DLSYM 0 +# define HAVE_UNISTD_H 0 +# define HAVE_IO_H 1 +# define HAVE_SYS_TYPES_H 1 +# define HAVE_PTHREAD_GETAFFINITY_NP 0 +# define HAVE_PTHREAD_SETAFFINITY_NP 0 +# define HAVE_PTHREAD_SETNAME_NP 0 +# define HAVE_PTHREAD_SET_NAME_NP 0 +# define HAVE_PTHREAD_NP_H 0 +# define _WIN32_WINNT 0x0601 +#else +# define HAVE_ALIGNED_ALLOC 1 +# define HAVE_POSIX_MEMALIGN 1 +# define HAVE_MEMALIGN 0 +# define HAVE_CLOCK_GETTIME 1 +# define HAVE_SIGACTION 1 +# define HAVE_DLSYM 1 +# define HAVE_UNISTD_H 1 +# define HAVE_IO_H 0 +# define HAVE_SYS_TYPES_H 1 + +# if defined(__linux__) && !defined(__ANDROID__) +# define HAVE_PTHREAD_GETAFFINITY_NP 1 +# define HAVE_PTHREAD_SETAFFINITY_NP 1 +# else +# define HAVE_PTHREAD_GETAFFINITY_NP 0 +# define HAVE_PTHREAD_SETAFFINITY_NP 0 +# endif // + +# if defined(__linux__) || defined(__APPLE__) +# define HAVE_PTHREAD_SETNAME_NP 1 +# else +# define HAVE_PTHREAD_SETNAME_NP 0 +# endif // + +# define HAVE_PTHREAD_SET_NAME_NP 0 +# define HAVE_PTHREAD_NP_H 0 +#endif + +#define HAVE_GETAUXVAL 0 +#define HAVE_ELF_AUX_INFO 0 +#define HAVE_C11_GENERIC 0 + +#if defined(_WIN32) +# ifndef UNICODE +# define UNICODE 1 +# endif // UNICODE + +# ifndef _UNICODE +# define _UNICODE 1 +# endif // _UNICODE + +# if defined(_MSC_VER) +# define _CRT_DECLARE_NONSTDC_NAMES 1 +# define fseeko _fseeki64 +# define ftello _ftelli64 +# else +# define _FILE_OFFSET_BITS 64 +# define __USE_MINGW_ANSI_STDIO 1 +# endif // defined(_MSC_VER) +#endif // defined(_WIN32) + +#endif // DAV1D_CONFIG_H diff --git a/3rdparty/dav1d/dav1d-amalgamated.c b/3rdparty/dav1d/dav1d-amalgamated.c new file mode 100644 index 0000000..ab363a9 --- /dev/null +++ b/3rdparty/dav1d/dav1d-amalgamated.c @@ -0,0 +1,97 @@ +/* bimg: amalgamated dav1d core (bitdepth-agnostic) sources. */ + +/* Platform feature macros must be set before any system header is included. */ +#if !defined(_WIN32) +# ifndef _GNU_SOURCE +# define _GNU_SOURCE // posix_memalign, PTHREAD_STACK_MIN (glibc), etc. +# endif +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 // 64-bit off_t on 32-bit POSIX (e.g. Android) +# endif +#endif + +/* macOS defines a 1-arg ALIGN() that clashes with dav1d's 2-arg + * ALIGN(); include it first (header-guarded) and drop its macro so dav1d's wins. */ +#if defined(__APPLE__) +# include +# undef ALIGN +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4057) // warning C4057: indirection to slightly different base types +# pragma warning(disable: 4090) // warning C4090: different 'const' qualifiers +# pragma warning(disable: 4100) // warning C4100: unreferenced formal parameter +# pragma warning(disable: 4127) // warning C4127: conditional expression is constant +# pragma warning(disable: 4152) // warning C4152: function/data pointer conversion in expression +# pragma warning(disable: 4189) // warning C4189: local variable is initialized but not referenced +# pragma warning(disable: 4200) // warning C4200: zero-sized array in struct/union +# pragma warning(disable: 4201) // warning C4201: nonstandard extension: nameless struct/union +# pragma warning(disable: 4244) // warning C4244: conversion, possible loss of data +# pragma warning(disable: 4245) // warning C4245: conversion, signed/unsigned mismatch +# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier +# pragma warning(disable: 4389) // warning C4389: signed/unsigned mismatch +# pragma warning(disable: 4456) // warning C4456: declaration hides previous local declaration +# pragma warning(disable: 4457) // warning C4457: declaration hides function parameter +# pragma warning(disable: 4701) // warning C4701: potentially uninitialized local variable used +# pragma warning(disable: 4702) // warning C4702: unreachable code +# pragma warning(disable: 4703) // warning C4703: potentially uninitialized local pointer variable used +# pragma warning(disable: 5287) // warning C5287: operands are different enum types +#elif defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +# pragma clang diagnostic ignored "-Wunused-function" +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wmissing-field-initializers" +# pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#include "src/cdf.c" +#include "src/cpu.c" +#include "src/ctx.c" +#include "src/data.c" +#include "src/decode.c" +#include "src/dequant_tables.c" +#include "src/getbits.c" +#include "src/intra_edge.c" +#include "src/itx_1d.c" +#include "src/lf_mask.c" +#include "src/lib.c" +#include "src/log.c" +#include "src/mem.c" +#include "src/msac.c" +#include "src/obu.c" +#include "src/pal.c" +#include "src/picture.c" +#include "src/qm.c" +#include "src/ref.c" +#include "src/refmvs.c" +#include "src/scan.c" +#include "src/tables.c" +#include "src/thread_task.c" +#include "src/warpmv.c" +#include "src/wedge.c" + +#if defined(_WIN32) +#include "src/win32/thread.c" +#endif + +#if defined(_MSC_VER) +# pragma warning(pop) +#elif defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif diff --git a/3rdparty/dav1d/dav1d-bitdepth-16.c b/3rdparty/dav1d/dav1d-bitdepth-16.c new file mode 100644 index 0000000..9c3cf7a --- /dev/null +++ b/3rdparty/dav1d/dav1d-bitdepth-16.c @@ -0,0 +1,83 @@ +/* bimg: amalgamated dav1d 16-bit DSP templates (one translation unit per bitdepth). */ + +/* Platform feature macros must be set before any system header is included. */ +#if !defined(_WIN32) +# ifndef _GNU_SOURCE +# define _GNU_SOURCE // posix_memalign, PTHREAD_STACK_MIN (glibc), etc. +# endif +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 // 64-bit off_t on 32-bit POSIX (e.g. Android) +# endif +#endif + +/* macOS defines a 1-arg ALIGN() that clashes with dav1d's 2-arg + * ALIGN(); include it first (header-guarded) and drop its macro so dav1d's wins. */ +#if defined(__APPLE__) +# include +# undef ALIGN +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4057) // warning C4057: indirection to slightly different base types +# pragma warning(disable: 4090) // warning C4090: different 'const' qualifiers +# pragma warning(disable: 4100) // warning C4100: unreferenced formal parameter +# pragma warning(disable: 4127) // warning C4127: conditional expression is constant +# pragma warning(disable: 4152) // warning C4152: function/data pointer conversion in expression +# pragma warning(disable: 4189) // warning C4189: local variable is initialized but not referenced +# pragma warning(disable: 4200) // warning C4200: zero-sized array in struct/union +# pragma warning(disable: 4201) // warning C4201: nonstandard extension: nameless struct/union +# pragma warning(disable: 4244) // warning C4244: conversion, possible loss of data +# pragma warning(disable: 4245) // warning C4245: conversion, signed/unsigned mismatch +# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier +# pragma warning(disable: 4389) // warning C4389: signed/unsigned mismatch +# pragma warning(disable: 4456) // warning C4456: declaration hides previous local declaration +# pragma warning(disable: 4457) // warning C4457: declaration hides function parameter +# pragma warning(disable: 4701) // warning C4701: potentially uninitialized local variable used +# pragma warning(disable: 4702) // warning C4702: unreachable code +# pragma warning(disable: 4703) // warning C4703: potentially uninitialized local pointer variable used +# pragma warning(disable: 5287) // warning C5287: operands are different enum types +#elif defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +# pragma clang diagnostic ignored "-Wunused-function" +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wmissing-field-initializers" +# pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#define BITDEPTH 16 + +#include "src/cdef_apply_tmpl.c" +#include "src/cdef_tmpl.c" +#include "src/fg_apply_tmpl.c" +#include "src/filmgrain_tmpl.c" +#include "src/ipred_prepare_tmpl.c" +#include "src/ipred_tmpl.c" +#include "src/itx_tmpl.c" +#include "src/lf_apply_tmpl.c" +#include "src/loopfilter_tmpl.c" +#include "src/looprestoration_tmpl.c" +#include "src/lr_apply_tmpl.c" +#include "src/mc_tmpl.c" +#include "src/recon_tmpl.c" + +#if defined(_MSC_VER) +# pragma warning(pop) +#elif defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif diff --git a/3rdparty/dav1d/dav1d-bitdepth-8.c b/3rdparty/dav1d/dav1d-bitdepth-8.c new file mode 100644 index 0000000..d2bd944 --- /dev/null +++ b/3rdparty/dav1d/dav1d-bitdepth-8.c @@ -0,0 +1,83 @@ +/* bimg: amalgamated dav1d 8-bit DSP templates (one translation unit per bitdepth). */ + +/* Platform feature macros must be set before any system header is included. */ +#if !defined(_WIN32) +# ifndef _GNU_SOURCE +# define _GNU_SOURCE // posix_memalign, PTHREAD_STACK_MIN (glibc), etc. +# endif +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 // 64-bit off_t on 32-bit POSIX (e.g. Android) +# endif +#endif + +/* macOS defines a 1-arg ALIGN() that clashes with dav1d's 2-arg + * ALIGN(); include it first (header-guarded) and drop its macro so dav1d's wins. */ +#if defined(__APPLE__) +# include +# undef ALIGN +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4057) // warning C4057: indirection to slightly different base types +# pragma warning(disable: 4090) // warning C4090: different 'const' qualifiers +# pragma warning(disable: 4100) // warning C4100: unreferenced formal parameter +# pragma warning(disable: 4127) // warning C4127: conditional expression is constant +# pragma warning(disable: 4152) // warning C4152: function/data pointer conversion in expression +# pragma warning(disable: 4189) // warning C4189: local variable is initialized but not referenced +# pragma warning(disable: 4200) // warning C4200: zero-sized array in struct/union +# pragma warning(disable: 4201) // warning C4201: nonstandard extension: nameless struct/union +# pragma warning(disable: 4244) // warning C4244: conversion, possible loss of data +# pragma warning(disable: 4245) // warning C4245: conversion, signed/unsigned mismatch +# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier +# pragma warning(disable: 4389) // warning C4389: signed/unsigned mismatch +# pragma warning(disable: 4456) // warning C4456: declaration hides previous local declaration +# pragma warning(disable: 4457) // warning C4457: declaration hides function parameter +# pragma warning(disable: 4701) // warning C4701: potentially uninitialized local variable used +# pragma warning(disable: 4702) // warning C4702: unreachable code +# pragma warning(disable: 4703) // warning C4703: potentially uninitialized local pointer variable used +# pragma warning(disable: 5287) // warning C5287: operands are different enum types +#elif defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +# pragma clang diagnostic ignored "-Wunused-function" +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wmissing-field-initializers" +# pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#define BITDEPTH 8 + +#include "src/cdef_apply_tmpl.c" +#include "src/cdef_tmpl.c" +#include "src/fg_apply_tmpl.c" +#include "src/filmgrain_tmpl.c" +#include "src/ipred_prepare_tmpl.c" +#include "src/ipred_tmpl.c" +#include "src/itx_tmpl.c" +#include "src/lf_apply_tmpl.c" +#include "src/loopfilter_tmpl.c" +#include "src/looprestoration_tmpl.c" +#include "src/lr_apply_tmpl.c" +#include "src/mc_tmpl.c" +#include "src/recon_tmpl.c" + +#if defined(_MSC_VER) +# pragma warning(pop) +#elif defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif diff --git a/3rdparty/dav1d/include/common/attributes.h b/3rdparty/dav1d/include/common/attributes.h new file mode 100644 index 0000000..c6cecc7 --- /dev/null +++ b/3rdparty/dav1d/include/common/attributes.h @@ -0,0 +1,213 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_COMMON_ATTRIBUTES_H +#define DAV1D_COMMON_ATTRIBUTES_H + +#include "config.h" + +#include +#include + +#ifndef __has_attribute +#define __has_attribute(x) 0 +#endif + +#ifndef __has_feature +#define __has_feature(x) 0 +#endif + +#ifdef __GNUC__ +#define ATTR_ALIAS __attribute__((may_alias)) +#if defined(__MINGW32__) && !defined(__clang__) +#define ATTR_FORMAT_PRINTF(fmt, attr) __attribute__((__format__(__gnu_printf__, fmt, attr))) +#else +#define ATTR_FORMAT_PRINTF(fmt, attr) __attribute__((__format__(__printf__, fmt, attr))) +#endif +#define COLD __attribute__((cold)) +#else +#define ATTR_ALIAS +#define ATTR_FORMAT_PRINTF(fmt, attr) +#define COLD +#endif + +#if ARCH_X86_64 +/* x86-64 needs 32- and 64-byte alignment for AVX2 and AVX-512. */ +#define ALIGN_64_VAL 64 +#define ALIGN_32_VAL 32 +#define ALIGN_16_VAL 16 +#elif ARCH_AARCH64 || ARCH_ARM || ARCH_LOONGARCH || ARCH_PPC64LE || ARCH_X86_32 +/* ARM doesn't benefit from anything more than 16-byte alignment. */ +#define ALIGN_64_VAL 16 +#define ALIGN_32_VAL 16 +#define ALIGN_16_VAL 16 +#else +/* No need for extra alignment on platforms without assembly. */ +#define ALIGN_64_VAL 8 +#define ALIGN_32_VAL 8 +#define ALIGN_16_VAL 8 +#endif + +/* + * API for variables, struct members (ALIGN()) like: + * uint8_t var[1][2][3][4] + * becomes: + * ALIGN(uint8_t var[1][2][3][4], alignment). + */ +#ifdef _MSC_VER +#define ALIGN(ll, a) \ + __declspec(align(a)) ll +#else +#define ALIGN(line, align) \ + line __attribute__((aligned(align))) +#endif + +/* + * API for stack alignment (ALIGN_STK_$align()) of variables like: + * uint8_t var[1][2][3][4] + * becomes: + * ALIGN_STK_$align(uint8_t, var, 1, [2][3][4]) + */ +#define ALIGN_STK_64(type, var, sz1d, sznd) \ + ALIGN(type var[sz1d]sznd, ALIGN_64_VAL) +#define ALIGN_STK_32(type, var, sz1d, sznd) \ + ALIGN(type var[sz1d]sznd, ALIGN_32_VAL) +#define ALIGN_STK_16(type, var, sz1d, sznd) \ + ALIGN(type var[sz1d]sznd, ALIGN_16_VAL) + +/* + * Forbid inlining of a function: + * static NOINLINE void func() {} + */ +#ifdef _MSC_VER +#define NOINLINE __declspec(noinline) +#elif __has_attribute(noclone) +#define NOINLINE __attribute__((noinline, noclone)) +#else +#define NOINLINE __attribute__((noinline)) +#endif + +#ifdef _MSC_VER +#define ALWAYS_INLINE __forceinline +#else +#define ALWAYS_INLINE __attribute__((always_inline)) inline +#endif + +#if (defined(__ELF__) || defined(__MACH__) || (defined(_WIN32) && defined(__clang__))) && __has_attribute(visibility) +#define EXTERN extern __attribute__((visibility("hidden"))) +#else +#define EXTERN extern +#endif + +#if ARCH_X86_64 && __has_attribute(model) +#define ATTR_MCMODEL_SMALL __attribute__((model("small"))) +#else +#define ATTR_MCMODEL_SMALL +#endif + +#ifdef __clang__ +#define NO_SANITIZE(x) __attribute__((no_sanitize(x))) +#else +#define NO_SANITIZE(x) +#endif + +#if defined(NDEBUG) && (defined(__GNUC__) || defined(__clang__)) +#undef assert +#define assert(x) do { if (!(x)) __builtin_unreachable(); } while (0) +#elif defined(NDEBUG) && defined(_MSC_VER) +#undef assert +#define assert __assume +#endif + +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && !defined(__clang__) +# define dav1d_uninit(x) x=x +#else +# define dav1d_uninit(x) x +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +#include + +static inline int ctz(const unsigned int mask) { + unsigned long idx; + _BitScanForward(&idx, mask); + return idx; +} + +static inline int clz(const unsigned int mask) { + unsigned long leading_zero = 0; + _BitScanReverse(&leading_zero, mask); + return (31 - leading_zero); +} + +#ifdef _WIN64 +static inline int clzll(const unsigned long long mask) { + unsigned long leading_zero = 0; + _BitScanReverse64(&leading_zero, mask); + return (63 - leading_zero); +} +#else /* _WIN64 */ +static inline int clzll(const unsigned long long mask) { + if (mask >> 32) + return clz((unsigned)(mask >> 32)); + else + return clz((unsigned)mask) + 32; +} +#endif /* _WIN64 */ +#else /* !_MSC_VER */ +static inline int ctz(const unsigned int mask) { + return __builtin_ctz(mask); +} + +static inline int clz(const unsigned int mask) { + return __builtin_clz(mask); +} + +static inline int clzll(const unsigned long long mask) { + return __builtin_clzll(mask); +} +#endif /* !_MSC_VER */ + +#ifndef static_assert +#define CHECK_OFFSET(type, field, name) \ + struct check_##type##_##field { int x[(name == offsetof(type, field)) ? 1 : -1]; } +#define CHECK_SIZE(type, size) \ + struct check_##type##_size { int x[(size == sizeof(type)) ? 1 : -1]; } +#else +#define CHECK_OFFSET(type, field, name) \ + static_assert(name == offsetof(type, field), #field) +#define CHECK_SIZE(type, size) \ + static_assert(size == sizeof(type), #type) +#endif + +#ifdef _MSC_VER +#define PACKED(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop)) +#else +#define PACKED(...) __VA_ARGS__ __attribute__((__packed__)) +#endif + +#endif /* DAV1D_COMMON_ATTRIBUTES_H */ diff --git a/3rdparty/dav1d/include/common/bitdepth.h b/3rdparty/dav1d/include/common/bitdepth.h new file mode 100644 index 0000000..8a8f574 --- /dev/null +++ b/3rdparty/dav1d/include/common/bitdepth.h @@ -0,0 +1,93 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_COMMON_BITDEPTH_H +#define DAV1D_COMMON_BITDEPTH_H + +#include +#include + +#include "common/attributes.h" + +#if !defined(BITDEPTH) +typedef uint8_t pixel; /* can't be void due to pointer-to-array usage */ +typedef void coef; +#define HIGHBD_DECL_SUFFIX /* nothing */ +#define HIGHBD_CALL_SUFFIX /* nothing */ +#define HIGHBD_TAIL_SUFFIX /* nothing */ +#elif BITDEPTH == 8 +typedef uint8_t pixel; +typedef int16_t coef; +#define PIXEL_TYPE uint8_t +#define COEF_TYPE int16_t +#define pixel_copy memcpy +#define pixel_set memset +#define iclip_pixel iclip_u8 +#define PIX_HEX_FMT "%02x" +#define bitfn(x) x##_8bpc +#define BF(x, suffix) x##_8bpc_##suffix +#define PXSTRIDE(x) (x) +#define highbd_only(x) +#define HIGHBD_DECL_SUFFIX /* nothing */ +#define HIGHBD_CALL_SUFFIX /* nothing */ +#define HIGHBD_TAIL_SUFFIX /* nothing */ +#define bitdepth_from_max(x) 8 +#define BITDEPTH_MAX 0xff +#elif BITDEPTH == 16 +typedef uint16_t pixel; +typedef int32_t coef; +#define PIXEL_TYPE uint16_t +#define COEF_TYPE int32_t +#define pixel_copy(a, b, c) memcpy(a, b, (c) << 1) +static inline void pixel_set(pixel *const dst, const int val, const int num) { + for (int n = 0; n < num; n++) + dst[n] = val; +} +#define PIX_HEX_FMT "%03x" +#define iclip_pixel(x) iclip(x, 0, bitdepth_max) +#define HIGHBD_DECL_SUFFIX , const int bitdepth_max +#define HIGHBD_CALL_SUFFIX , f->bitdepth_max +#define HIGHBD_TAIL_SUFFIX , bitdepth_max +#define bitdepth_from_max(bitdepth_max) (32 - clz(bitdepth_max)) +#define BITDEPTH_MAX bitdepth_max +#define bitfn(x) x##_16bpc +#define BF(x, suffix) x##_16bpc_##suffix +static inline ptrdiff_t PXSTRIDE(const ptrdiff_t x) { + assert(!(x & 1)); + return x >> 1; +} +#define highbd_only(x) x +#else +#error invalid value for bitdepth +#endif +#define bytefn(x) bitfn(x) + +#define bitfn_decls(name, ...) \ +name##_8bpc(__VA_ARGS__); \ +name##_16bpc(__VA_ARGS__) + +#endif /* DAV1D_COMMON_BITDEPTH_H */ diff --git a/3rdparty/dav1d/include/common/dump.h b/3rdparty/dav1d/include/common/dump.h new file mode 100644 index 0000000..9ffab6a --- /dev/null +++ b/3rdparty/dav1d/include/common/dump.h @@ -0,0 +1,92 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_COMMON_DUMP_H +#define DAV1D_COMMON_DUMP_H + +#include +#include +#include + +#include "common/bitdepth.h" + +static inline void append_plane_to_file(const pixel *buf, ptrdiff_t stride, + int w, int h, const char *const file) +{ + FILE *const f = fopen(file, "ab"); + while (h--) { + fwrite(buf, w * sizeof(pixel), 1, f); + buf += PXSTRIDE(stride); + } + fclose(f); +} + +static inline void hex_fdump(FILE *out, const pixel *buf, ptrdiff_t stride, + int w, int h, const char *what) +{ + fprintf(out, "%s\n", what); + while (h--) { + int x; + for (x = 0; x < w; x++) + fprintf(out, " " PIX_HEX_FMT, buf[x]); + buf += PXSTRIDE(stride); + fprintf(out, "\n"); + } +} + +static inline void hex_dump(const pixel *buf, ptrdiff_t stride, + int w, int h, const char *what) +{ + hex_fdump(stdout, buf, stride, w, h, what); +} + +static inline void coef_dump(const coef *buf, const int w, const int h, + const int len, const char *what) +{ + int y; + printf("%s\n", what); + for (y = 0; y < h; y++) { + int x; + for (x = 0; x < w; x++) + printf(" %*d", len, buf[x]); + buf += w; + printf("\n"); + } +} + +static inline void ac_dump(const int16_t *buf, int w, int h, const char *what) +{ + printf("%s\n", what); + while (h--) { + for (int x = 0; x < w; x++) + printf(" %03d", buf[x]); + buf += w; + printf("\n"); + } +} + +#endif /* DAV1D_COMMON_DUMP_H */ diff --git a/3rdparty/dav1d/include/common/frame.h b/3rdparty/dav1d/include/common/frame.h new file mode 100644 index 0000000..767f7fd --- /dev/null +++ b/3rdparty/dav1d/include/common/frame.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2021, VideoLAN and dav1d authors + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_COMMON_FRAME_H +#define DAV1D_COMMON_FRAME_H + +/* + * Checks whether Dav1dFrameType == INTER || == SWITCH + * Both are defined as odd numbers {1, 3} and therefore have the LSB set. + * See also: AV1 spec 6.8.2 + */ +#define IS_INTER_OR_SWITCH(frame_header) \ + ((frame_header)->frame_type & 1) + +/* + * Checks whether Dav1dFrameType == KEY || == INTRA + * See also: AV1 spec 6.8.2 + */ +#define IS_KEY_OR_INTRA(frame_header) \ + (!IS_INTER_OR_SWITCH(frame_header)) + +#endif /* DAV1D_COMMON_FRAME_H */ diff --git a/3rdparty/dav1d/include/common/intops.h b/3rdparty/dav1d/include/common/intops.h new file mode 100644 index 0000000..089da5e --- /dev/null +++ b/3rdparty/dav1d/include/common/intops.h @@ -0,0 +1,84 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_COMMON_INTOPS_H +#define DAV1D_COMMON_INTOPS_H + +#include + +#include "common/attributes.h" + +static inline int imax(const int a, const int b) { + return a > b ? a : b; +} + +static inline int imin(const int a, const int b) { + return a < b ? a : b; +} + +static inline unsigned umax(const unsigned a, const unsigned b) { + return a > b ? a : b; +} + +static inline unsigned umin(const unsigned a, const unsigned b) { + return a < b ? a : b; +} + +static inline int iclip(const int v, const int min, const int max) { + return v < min ? min : v > max ? max : v; +} + +static inline int iclip_u8(const int v) { + return iclip(v, 0, 255); +} + +static inline int apply_sign(const int v, const int s) { + return s < 0 ? -v : v; +} + +static inline int apply_sign64(const int v, const int64_t s) { + return s < 0 ? -v : v; +} + +static inline int ulog2(const unsigned v) { + return 31 ^ clz(v); +} + +static inline int u64log2(const uint64_t v) { + return 63 ^ clzll(v); +} + +static inline unsigned inv_recenter(const unsigned r, const unsigned v) { + if (v > (r << 1)) + return v; + else if ((v & 1) == 0) + return (v >> 1) + r; + else + return r - ((v + 1) >> 1); +} + +#endif /* DAV1D_COMMON_INTOPS_H */ diff --git a/3rdparty/dav1d/include/common/validate.h b/3rdparty/dav1d/include/common/validate.h new file mode 100644 index 0000000..3aaed5b --- /dev/null +++ b/3rdparty/dav1d/include/common/validate.h @@ -0,0 +1,61 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_COMMON_VALIDATE_H +#define DAV1D_COMMON_VALIDATE_H + +#include +#include + +#if defined(NDEBUG) +#define debug_print(...) do {} while (0) +#define debug_abort() do {} while (0) +#else +#define debug_print(...) fprintf(stderr, __VA_ARGS__) +#define debug_abort abort +#endif + +#define validate_input_or_ret_with_msg(x, r, ...) \ + if (!(x)) { \ + debug_print("Input validation check \'%s\' failed in %s!\n", \ + #x, __func__); \ + debug_print(__VA_ARGS__); \ + debug_abort(); \ + return r; \ + } + +#define validate_input_or_ret(x, r) \ + if (!(x)) { \ + debug_print("Input validation check \'%s\' failed in %s!\n", \ + #x, __func__); \ + debug_abort(); \ + return r; \ + } + +#define validate_input(x) validate_input_or_ret(x, ) + +#endif /* DAV1D_COMMON_VALIDATE_H */ diff --git a/3rdparty/dav1d/include/compat/gcc/stdatomic.h b/3rdparty/dav1d/include/compat/gcc/stdatomic.h new file mode 100644 index 0000000..29f9063 --- /dev/null +++ b/3rdparty/dav1d/include/compat/gcc/stdatomic.h @@ -0,0 +1,51 @@ +/* +* Copyright © 2018, VideoLAN and dav1d authors +* 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. +* +* 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 OWNER 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. +*/ + +#ifndef GCCVER_STDATOMIC_H_ +#define GCCVER_STDATOMIC_H_ + +#if !defined(__cplusplus) + +typedef int atomic_int; +typedef unsigned int atomic_uint; + +#define memory_order_relaxed __ATOMIC_RELAXED +#define memory_order_acquire __ATOMIC_ACQUIRE + +#define atomic_init(p_a, v) do { *(p_a) = (v); } while(0) +#define atomic_store(p_a, v) __atomic_store_n(p_a, v, __ATOMIC_SEQ_CST) +#define atomic_load(p_a) __atomic_load_n(p_a, __ATOMIC_SEQ_CST) +#define atomic_load_explicit(p_a, mo) __atomic_load_n(p_a, mo) +#define atomic_fetch_add(p_a, inc) __atomic_fetch_add(p_a, inc, __ATOMIC_SEQ_CST) +#define atomic_fetch_add_explicit(p_a, inc, mo) __atomic_fetch_add(p_a, inc, mo) +#define atomic_fetch_sub(p_a, dec) __atomic_fetch_sub(p_a, dec, __ATOMIC_SEQ_CST) +#define atomic_exchange(p_a, v) __atomic_exchange_n(p_a, v, __ATOMIC_SEQ_CST) +#define atomic_fetch_or(p_a, v) __atomic_fetch_or(p_a, v, __ATOMIC_SEQ_CST) +#define atomic_compare_exchange_strong(p_a, expected, desired) __atomic_compare_exchange_n(p_a, expected, desired, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) + +#endif /* !defined(__cplusplus) */ + +#endif /* GCCVER_STDATOMIC_H_ */ diff --git a/3rdparty/dav1d/include/compat/msvc/stdatomic.h b/3rdparty/dav1d/include/compat/msvc/stdatomic.h new file mode 100644 index 0000000..618f0ee --- /dev/null +++ b/3rdparty/dav1d/include/compat/msvc/stdatomic.h @@ -0,0 +1,82 @@ +/* +* Copyright © 2018, VideoLAN and dav1d authors +* 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. +* +* 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 OWNER 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. +*/ + +#ifndef MSCVER_STDATOMIC_H_ +#define MSCVER_STDATOMIC_H_ + +#if !defined(__cplusplus) && defined(_MSC_VER) + +#pragma warning(push) +#pragma warning(disable:4067) /* newline for __has_include_next */ + +#if defined(__clang__) && __has_include_next() + /* use the clang stdatomic.h with clang-cl*/ +# include_next +#else /* ! stdatomic.h */ + +#include + +#include "common/attributes.h" + +typedef volatile LONG atomic_int; +typedef volatile ULONG atomic_uint; + +typedef enum { + memory_order_relaxed, + memory_order_acquire +} msvc_atomic_memory_order; + +#define atomic_init(p_a, v) do { *(p_a) = (v); } while(0) +#define atomic_store(p_a, v) InterlockedExchange((LONG*)p_a, v) +#define atomic_load(p_a) InterlockedCompareExchange((LONG*)p_a, 0, 0) +#define atomic_exchange(p_a, v) InterlockedExchange(p_a, v) +#define atomic_load_explicit(p_a, mo) atomic_load(p_a) + +static inline int atomic_compare_exchange_strong_int(LONG *obj, LONG *expected, + LONG desired) +{ + LONG orig = *expected; + *expected = InterlockedCompareExchange(obj, desired, orig); + return *expected == orig; +} +#define atomic_compare_exchange_strong(p_a, expected, desired) atomic_compare_exchange_strong_int((LONG *)p_a, (LONG *)expected, (LONG)desired) + +/* + * TODO use a special call to increment/decrement + * using InterlockedIncrement/InterlockedDecrement + */ +#define atomic_fetch_add(p_a, inc) InterlockedExchangeAdd(p_a, inc) +#define atomic_fetch_sub(p_a, dec) InterlockedExchangeAdd(p_a, -(dec)) +#define atomic_fetch_or(p_a, v) InterlockedOr(p_a, v) +#define atomic_fetch_add_explicit(p_a, inc, mo) atomic_fetch_add(p_a, inc) + +#endif /* ! stdatomic.h */ + +#pragma warning(pop) + +#endif /* !defined(__cplusplus) && defined(_MSC_VER) */ + +#endif /* MSCVER_STDATOMIC_H_ */ diff --git a/3rdparty/dav1d/include/dav1d/common.h b/3rdparty/dav1d/include/dav1d/common.h new file mode 100644 index 0000000..44764d0 --- /dev/null +++ b/3rdparty/dav1d/include/dav1d/common.h @@ -0,0 +1,96 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_COMMON_H +#define DAV1D_COMMON_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef DAV1D_API + #if defined _WIN32 + #if defined DAV1D_BUILDING_DLL + #define DAV1D_API __declspec(dllexport) + #else + #define DAV1D_API + #endif + #elif defined __OS2__ + #define DAV1D_API __declspec(dllexport) + #else + #if __GNUC__ >= 4 + #define DAV1D_API __attribute__ ((visibility ("default"))) + #else + #define DAV1D_API + #endif + #endif +#endif + +#if EPERM > 0 +#define DAV1D_ERR(e) (-(e)) ///< Negate POSIX error code. +#else +#define DAV1D_ERR(e) (e) +#endif + +/** + * A reference-counted object wrapper for a user-configurable pointer. + */ +typedef struct Dav1dUserData { + const uint8_t *data; ///< data pointer + struct Dav1dRef *ref; ///< allocation origin +} Dav1dUserData; + +/** + * Input packet metadata which are copied from the input data used to + * decode each image into the matching structure of the output image + * returned back to the user. Since these are metadata fields, they + * can be used for other purposes than the documented ones, they will + * still be passed from input data to output picture without being + * used internally. + */ +typedef struct Dav1dDataProps { + int64_t timestamp; ///< container timestamp of input data, INT64_MIN if unknown (default) + int64_t duration; ///< container duration of input data, 0 if unknown (default) + int64_t offset; ///< stream offset of input data, -1 if unknown (default) + size_t size; ///< packet size, default Dav1dData.sz + struct Dav1dUserData user_data; ///< user-configurable data, default NULL members +} Dav1dDataProps; + +/** + * Release reference to a Dav1dDataProps. + */ +DAV1D_API void dav1d_data_props_unref(Dav1dDataProps *props); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_COMMON_H */ diff --git a/3rdparty/dav1d/include/dav1d/data.h b/3rdparty/dav1d/include/dav1d/data.h new file mode 100644 index 0000000..e551ad6 --- /dev/null +++ b/3rdparty/dav1d/include/dav1d/data.h @@ -0,0 +1,117 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_DATA_H +#define DAV1D_DATA_H + +#include +#include + +#include "common.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Dav1dData { + const uint8_t *data; ///< data pointer + size_t sz; ///< data size + struct Dav1dRef *ref; ///< allocation origin + Dav1dDataProps m; ///< user provided metadata passed to the output picture +} Dav1dData; + +/** + * Allocate data. + * + * @param data Input context. + * @param sz Size of the data that should be allocated. + * + * @return Pointer to the allocated buffer on success. NULL on error. + */ +DAV1D_API uint8_t * dav1d_data_create(Dav1dData *data, size_t sz); + +/** + * Wrap an existing data array. + * + * @param data Input context. + * @param buf The data to be wrapped. + * @param sz Size of the data. + * @param free_callback Function to be called when we release our last + * reference to this data. In this callback, $buf will be + * the $buf argument to this function, and $cookie will + * be the $cookie input argument to this function. + * @param cookie Opaque parameter passed to free_callback(). + * + * @return 0 on success. A negative DAV1D_ERR value on error. + */ +DAV1D_API int dav1d_data_wrap(Dav1dData *data, const uint8_t *buf, size_t sz, + void (*free_callback)(const uint8_t *buf, void *cookie), + void *cookie); + +/** + * Wrap a user-provided data pointer into a reference counted object. + * + * data->m.user_data field will initialized to wrap the provided $user_data + * pointer. + * + * $free_callback will be called on the same thread that released the last + * reference. If frame threading is used, make sure $free_callback is + * thread-safe. + * + * @param data Input context. + * @param user_data The user data to be wrapped. + * @param free_callback Function to be called when we release our last + * reference to this data. In this callback, $user_data + * will be the $user_data argument to this function, and + * $cookie will be the $cookie input argument to this + * function. + * @param cookie Opaque parameter passed to $free_callback. + * + * @return 0 on success. A negative DAV1D_ERR value on error. + */ +DAV1D_API int dav1d_data_wrap_user_data(Dav1dData *data, + const uint8_t *user_data, + void (*free_callback)(const uint8_t *user_data, + void *cookie), + void *cookie); + +/** + * Free the data reference. + * + * The reference count for data->m.user_data will be decremented (if it has been + * initialized with dav1d_data_wrap_user_data). The $data object will be memset + * to 0. + * + * @param data Input context. + */ +DAV1D_API void dav1d_data_unref(Dav1dData *data); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_DATA_H */ diff --git a/3rdparty/dav1d/include/dav1d/dav1d.h b/3rdparty/dav1d/include/dav1d/dav1d.h new file mode 100644 index 0000000..e8f0705 --- /dev/null +++ b/3rdparty/dav1d/include/dav1d/dav1d.h @@ -0,0 +1,329 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_H +#define DAV1D_H + +#include +#include + +#include "common.h" +#include "picture.h" +#include "data.h" +#include "version.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct Dav1dContext Dav1dContext; +typedef struct Dav1dRef Dav1dRef; + +#define DAV1D_MAX_THREADS 256 +#define DAV1D_MAX_FRAME_DELAY 256 + +typedef struct Dav1dLogger { + void *cookie; ///< Custom data to pass to the callback. + /** + * Logger callback. May be NULL to disable logging. + * + * @param cookie Custom pointer passed to all calls. + * @param format The vprintf compatible format string. + * @param ap List of arguments referenced by the format string. + */ + void (*callback)(void *cookie, const char *format, va_list ap); +} Dav1dLogger; + +enum Dav1dInloopFilterType { + DAV1D_INLOOPFILTER_NONE = 0, + DAV1D_INLOOPFILTER_DEBLOCK = 1 << 0, + DAV1D_INLOOPFILTER_CDEF = 1 << 1, + DAV1D_INLOOPFILTER_RESTORATION = 1 << 2, + DAV1D_INLOOPFILTER_ALL = DAV1D_INLOOPFILTER_DEBLOCK | + DAV1D_INLOOPFILTER_CDEF | + DAV1D_INLOOPFILTER_RESTORATION, +}; + +enum Dav1dDecodeFrameType { + DAV1D_DECODEFRAMETYPE_ALL = 0, ///< decode and return all frames + DAV1D_DECODEFRAMETYPE_REFERENCE = 1,///< decode and return frames referenced by other frames only + DAV1D_DECODEFRAMETYPE_INTRA = 2, ///< decode and return intra frames only (includes keyframes) + DAV1D_DECODEFRAMETYPE_KEY = 3, ///< decode and return keyframes only +}; + +typedef struct Dav1dSettings { + int n_threads; ///< number of threads (0 = number of logical cores in host system, default 0) + int max_frame_delay; ///< Set to 1 for low-latency decoding (0 = ceil(sqrt(n_threads)), default 0) + int apply_grain; ///< whether to apply film grain on output frames (default 1) + int operating_point; ///< select an operating point for scalable AV1 bitstreams (0 - 31, default 0) + int all_layers; ///< output all spatial layers of a scalable AV1 biststream (default 1) + unsigned frame_size_limit; ///< maximum frame size, in pixels (0 = unlimited, default 0) + Dav1dPicAllocator allocator; ///< Picture allocator callback. + Dav1dLogger logger; ///< Logger callback. + int strict_std_compliance; ///< whether to abort decoding on standard compliance violations + ///< that don't affect actual bitstream decoding (e.g. inconsistent + ///< or invalid metadata, default 0) + int output_invisible_frames; ///< output invisibly coded frames (in coding order) in addition + ///< to all visible frames. Because of show-existing-frame, this + ///< means some frames may appear twice (once when coded, + ///< once when shown, default 0) + enum Dav1dInloopFilterType inloop_filters; ///< postfilters to enable during decoding (default + ///< DAV1D_INLOOPFILTER_ALL) + enum Dav1dDecodeFrameType decode_frame_type; ///< frame types to decode (default + ///< DAV1D_DECODEFRAMETYPE_ALL) + uint8_t reserved[16]; ///< reserved for future use +} Dav1dSettings; + +/** + * Get library version. + */ +DAV1D_API const char *dav1d_version(void); + +/** + * Get library API version. + * + * @return A value in the format 0x00XXYYZZ, where XX is the major version, + * YY the minor version, and ZZ the patch version. + * @see DAV1D_API_MAJOR, DAV1D_API_MINOR, DAV1D_API_PATCH + */ +DAV1D_API unsigned dav1d_version_api(void); + +/** + * Initialize settings to default values. + * + * @param s Input settings context. + */ +DAV1D_API void dav1d_default_settings(Dav1dSettings *s); + +/** + * Allocate and open a decoder instance. + * + * @param c_out The decoder instance to open. *c_out will be set to the + * allocated context. + * @param s Input settings context. + * + * @note The context must be freed using dav1d_close() when decoding is + * finished. + * + * @return 0 on success, or < 0 (a negative DAV1D_ERR code) on error. + */ +DAV1D_API int dav1d_open(Dav1dContext **c_out, const Dav1dSettings *s); + +/** + * Parse a Sequence Header OBU from bitstream data. + * + * @param out Output Sequence Header. + * @param buf The data to be parser. + * @param sz Size of the data. + * + * @return + * 0: Success, and out is filled with the parsed Sequence Header + * OBU parameters. + * DAV1D_ERR(ENOENT): No Sequence Header OBUs were found in the buffer. + * Other negative DAV1D_ERR codes: Invalid data in the buffer, invalid passed-in + * arguments, and other errors during parsing. + * + * @note It is safe to feed this function data containing other OBUs than a + * Sequence Header, as they will simply be ignored. If there is more than + * one Sequence Header OBU present, only the last will be returned. + */ +DAV1D_API int dav1d_parse_sequence_header(Dav1dSequenceHeader *out, + const uint8_t *buf, const size_t sz); + +/** + * Feed bitstream data to the decoder, in the form of one or multiple AV1 + * Open Bitstream Units (OBUs). + * + * @param c Input decoder instance. + * @param in Input bitstream data. On success, ownership of the reference is + * passed to the library. + * + * @return + * 0: Success, and the data was consumed. + * DAV1D_ERR(EAGAIN): The data can't be consumed. dav1d_get_picture() should + * be called to get one or more frames before the function + * can consume new data. + * Other negative DAV1D_ERR codes: Error during decoding or because of invalid + * passed-in arguments. The reference remains + * owned by the caller. + */ +DAV1D_API int dav1d_send_data(Dav1dContext *c, Dav1dData *in); + +/** + * Return a decoded picture. + * + * @param c Input decoder instance. + * @param out Output frame. The caller assumes ownership of the returned + * reference. + * + * @return + * 0: Success, and a frame is returned. + * DAV1D_ERR(EAGAIN): Not enough data to output a frame. dav1d_send_data() + * should be called with new input. + * Other negative DAV1D_ERR codes: Error during decoding or because of invalid + * passed-in arguments. + * + * @note To drain buffered frames from the decoder (i.e. on end of stream), + * call this function until it returns DAV1D_ERR(EAGAIN). + * + * @code{.c} + * Dav1dData data = { 0 }; + * Dav1dPicture p = { 0 }; + * int res; + * + * read_data(&data); + * do { + * res = dav1d_send_data(c, &data); + * // Keep going even if the function can't consume the current data + * packet. It eventually will after one or more frames have been + * returned in this loop. + * if (res < 0 && res != DAV1D_ERR(EAGAIN)) + * free_and_abort(); + * res = dav1d_get_picture(c, &p); + * if (res < 0) { + * if (res != DAV1D_ERR(EAGAIN)) + * free_and_abort(); + * } else + * output_and_unref_picture(&p); + * // Stay in the loop as long as there's data to consume. + * } while (data.sz || read_data(&data) == SUCCESS); + * + * // Handle EOS by draining all buffered frames. + * do { + * res = dav1d_get_picture(c, &p); + * if (res < 0) { + * if (res != DAV1D_ERR(EAGAIN)) + * free_and_abort(); + * } else + * output_and_unref_picture(&p); + * } while (res == 0); + * @endcode + */ +DAV1D_API int dav1d_get_picture(Dav1dContext *c, Dav1dPicture *out); + +/** + * Apply film grain to a previously decoded picture. If the picture contains no + * film grain metadata, then this function merely returns a new reference. + * + * @param c Input decoder instance. + * @param out Output frame. The caller assumes ownership of the returned + * reference. + * @param in Input frame. No ownership is transferred. + * + * @return + * 0: Success, and a frame is returned. + * Other negative DAV1D_ERR codes: Error due to lack of memory or because of + * invalid passed-in arguments. + * + * @note If `Dav1dSettings.apply_grain` is true, film grain was already applied + * by `dav1d_get_picture`, and so calling this function leads to double + * application of film grain. Users should only call this when needed. + */ +DAV1D_API int dav1d_apply_grain(Dav1dContext *c, Dav1dPicture *out, + const Dav1dPicture *in); + +/** + * Close a decoder instance and free all associated memory. + * + * @param c_out The decoder instance to close. *c_out will be set to NULL. + */ +DAV1D_API void dav1d_close(Dav1dContext **c_out); + +/** + * Flush all delayed frames in decoder and clear internal decoder state, + * to be used when seeking. + * + * @param c Input decoder instance. + * + * @note Decoding will start only after a valid sequence header OBU is + * delivered to dav1d_send_data(). + * + */ +DAV1D_API void dav1d_flush(Dav1dContext *c); + +enum Dav1dEventFlags { + /** + * The last returned picture contains a reference to a new Sequence Header, + * either because it's the start of a new coded sequence, or the decoder was + * flushed before it was generated. + */ + DAV1D_EVENT_FLAG_NEW_SEQUENCE = 1 << 0, + /** + * The last returned picture contains a reference to a Sequence Header with + * new operating parameters information for the current coded sequence. + */ + DAV1D_EVENT_FLAG_NEW_OP_PARAMS_INFO = 1 << 1, +}; + +/** + * Fetch a combination of DAV1D_EVENT_FLAG_* event flags generated by the decoding + * process. + * + * @param c Input decoder instance. + * @param flags Where to write the flags. + * + * @return 0 on success, or < 0 (a negative DAV1D_ERR code) on error. + * + * @note Calling this function will clear all the event flags currently stored in + * the decoder. + * + */ +DAV1D_API int dav1d_get_event_flags(Dav1dContext *c, enum Dav1dEventFlags *flags); + +/** + * Retrieve the user-provided metadata associated with the input data packet + * for the last decoding error reported to the user, i.e. a negative return + * value (not EAGAIN) from dav1d_send_data() or dav1d_get_picture(). + * + * @param c Input decoder instance. + * @param out Output Dav1dDataProps. On success, the caller assumes ownership of + * the returned reference. + * + * @return 0 on success, or < 0 (a negative DAV1D_ERR code) on error. + */ +DAV1D_API int dav1d_get_decode_error_data_props(Dav1dContext *c, Dav1dDataProps *out); + +/** + * Get the decoder delay, which is the number of internally buffered frames, not + * including reference frames. + * This value is guaranteed to be >= 1 and <= max_frame_delay. + * + * @param s Input settings context. + * + * @return Decoder frame delay on success, or < 0 (a negative DAV1D_ERR code) on + * error. + * + * @note The returned delay is valid only for a Dav1dContext initialized with the + * provided Dav1dSettings. + */ +DAV1D_API int dav1d_get_frame_delay(const Dav1dSettings *s); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_H */ diff --git a/3rdparty/dav1d/include/dav1d/headers.h b/3rdparty/dav1d/include/dav1d/headers.h new file mode 100644 index 0000000..0fab993 --- /dev/null +++ b/3rdparty/dav1d/include/dav1d/headers.h @@ -0,0 +1,440 @@ +/* + * Copyright © 2018-2020, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_HEADERS_H +#define DAV1D_HEADERS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// Constants from Section 3. "Symbols and abbreviated terms" +#define DAV1D_MAX_CDEF_STRENGTHS 8 +#define DAV1D_MAX_OPERATING_POINTS 32 +#define DAV1D_MAX_TILE_COLS 64 +#define DAV1D_MAX_TILE_ROWS 64 +#define DAV1D_MAX_SEGMENTS 8 +#define DAV1D_NUM_REF_FRAMES 8 +#define DAV1D_PRIMARY_REF_NONE 7 +#define DAV1D_REFS_PER_FRAME 7 +#define DAV1D_TOTAL_REFS_PER_FRAME (DAV1D_REFS_PER_FRAME + 1) + +enum Dav1dObuType { + DAV1D_OBU_SEQ_HDR = 1, + DAV1D_OBU_TD = 2, + DAV1D_OBU_FRAME_HDR = 3, + DAV1D_OBU_TILE_GRP = 4, + DAV1D_OBU_METADATA = 5, + DAV1D_OBU_FRAME = 6, + DAV1D_OBU_REDUNDANT_FRAME_HDR = 7, + DAV1D_OBU_PADDING = 15, +}; + +enum Dav1dTxfmMode { + DAV1D_TX_4X4_ONLY, + DAV1D_TX_LARGEST, + DAV1D_TX_SWITCHABLE, + DAV1D_N_TX_MODES, +}; + +enum Dav1dFilterMode { + DAV1D_FILTER_8TAP_REGULAR, + DAV1D_FILTER_8TAP_SMOOTH, + DAV1D_FILTER_8TAP_SHARP, + DAV1D_N_SWITCHABLE_FILTERS, + DAV1D_FILTER_BILINEAR = DAV1D_N_SWITCHABLE_FILTERS, + DAV1D_N_FILTERS, + DAV1D_FILTER_SWITCHABLE = DAV1D_N_FILTERS, +}; + +enum Dav1dAdaptiveBoolean { + DAV1D_OFF = 0, + DAV1D_ON = 1, + DAV1D_ADAPTIVE = 2, +}; + +enum Dav1dRestorationType { + DAV1D_RESTORATION_NONE, + DAV1D_RESTORATION_SWITCHABLE, + DAV1D_RESTORATION_WIENER, + DAV1D_RESTORATION_SGRPROJ, +}; + +enum Dav1dWarpedMotionType { + DAV1D_WM_TYPE_IDENTITY, + DAV1D_WM_TYPE_TRANSLATION, + DAV1D_WM_TYPE_ROT_ZOOM, + DAV1D_WM_TYPE_AFFINE, +}; + +typedef struct Dav1dWarpedMotionParams { + enum Dav1dWarpedMotionType type; + int32_t matrix[6]; + union { + struct { + int16_t alpha, beta, gamma, delta; + } p; + int16_t abcd[4]; + } u; +} Dav1dWarpedMotionParams; + +enum Dav1dPixelLayout { + DAV1D_PIXEL_LAYOUT_I400, ///< monochrome + DAV1D_PIXEL_LAYOUT_I420, ///< 4:2:0 planar + DAV1D_PIXEL_LAYOUT_I422, ///< 4:2:2 planar + DAV1D_PIXEL_LAYOUT_I444, ///< 4:4:4 planar +}; + +enum Dav1dFrameType { + DAV1D_FRAME_TYPE_KEY = 0, ///< Key Intra frame + DAV1D_FRAME_TYPE_INTER = 1, ///< Inter frame + DAV1D_FRAME_TYPE_INTRA = 2, ///< Non key Intra frame + DAV1D_FRAME_TYPE_SWITCH = 3, ///< Switch Inter frame +}; + +enum Dav1dColorPrimaries { + DAV1D_COLOR_PRI_BT709 = 1, + DAV1D_COLOR_PRI_UNKNOWN = 2, + DAV1D_COLOR_PRI_BT470M = 4, + DAV1D_COLOR_PRI_BT470BG = 5, + DAV1D_COLOR_PRI_BT601 = 6, + DAV1D_COLOR_PRI_SMPTE240 = 7, + DAV1D_COLOR_PRI_FILM = 8, + DAV1D_COLOR_PRI_BT2020 = 9, + DAV1D_COLOR_PRI_XYZ = 10, + DAV1D_COLOR_PRI_SMPTE431 = 11, + DAV1D_COLOR_PRI_SMPTE432 = 12, + DAV1D_COLOR_PRI_EBU3213 = 22, + DAV1D_COLOR_PRI_RESERVED = 255, +}; + +enum Dav1dTransferCharacteristics { + DAV1D_TRC_BT709 = 1, + DAV1D_TRC_UNKNOWN = 2, + DAV1D_TRC_BT470M = 4, + DAV1D_TRC_BT470BG = 5, + DAV1D_TRC_BT601 = 6, + DAV1D_TRC_SMPTE240 = 7, + DAV1D_TRC_LINEAR = 8, + DAV1D_TRC_LOG100 = 9, ///< logarithmic (100:1 range) + DAV1D_TRC_LOG100_SQRT10 = 10, ///< lograithmic (100*sqrt(10):1 range) + DAV1D_TRC_IEC61966 = 11, + DAV1D_TRC_BT1361 = 12, + DAV1D_TRC_SRGB = 13, + DAV1D_TRC_BT2020_10BIT = 14, + DAV1D_TRC_BT2020_12BIT = 15, + DAV1D_TRC_SMPTE2084 = 16, ///< PQ + DAV1D_TRC_SMPTE428 = 17, + DAV1D_TRC_HLG = 18, ///< hybrid log/gamma (BT.2100 / ARIB STD-B67) + DAV1D_TRC_RESERVED = 255, +}; + +enum Dav1dMatrixCoefficients { + DAV1D_MC_IDENTITY = 0, + DAV1D_MC_BT709 = 1, + DAV1D_MC_UNKNOWN = 2, + DAV1D_MC_FCC = 4, + DAV1D_MC_BT470BG = 5, + DAV1D_MC_BT601 = 6, + DAV1D_MC_SMPTE240 = 7, + DAV1D_MC_SMPTE_YCGCO = 8, + DAV1D_MC_BT2020_NCL = 9, + DAV1D_MC_BT2020_CL = 10, + DAV1D_MC_SMPTE2085 = 11, + DAV1D_MC_CHROMAT_NCL = 12, ///< Chromaticity-derived + DAV1D_MC_CHROMAT_CL = 13, + DAV1D_MC_ICTCP = 14, + DAV1D_MC_RESERVED = 255, +}; + +enum Dav1dChromaSamplePosition { + DAV1D_CHR_UNKNOWN = 0, + DAV1D_CHR_VERTICAL = 1, ///< Horizontally co-located with luma(0, 0) + ///< sample, between two vertical samples + DAV1D_CHR_COLOCATED = 2, ///< Co-located with luma(0, 0) sample +}; + +typedef struct Dav1dContentLightLevel { + uint16_t max_content_light_level; + uint16_t max_frame_average_light_level; +} Dav1dContentLightLevel; + +typedef struct Dav1dMasteringDisplay { + uint16_t primaries[3][2]; ///< 0.16 fixed point + uint16_t white_point[2]; ///< 0.16 fixed point + uint32_t max_luminance; ///< 24.8 fixed point + uint32_t min_luminance; ///< 18.14 fixed point +} Dav1dMasteringDisplay; + +typedef struct Dav1dITUTT35 { + uint8_t country_code; + uint8_t country_code_extension_byte; + size_t payload_size; + uint8_t *payload; +} Dav1dITUTT35; + +typedef struct Dav1dSequenceHeader { + /** + * Stream profile, 0 for 8-10 bits/component 4:2:0 or monochrome; + * 1 for 8-10 bits/component 4:4:4; 2 for 4:2:2 at any bits/component, + * or 12 bits/component at any chroma subsampling. + */ + uint8_t profile; + /** + * Maximum dimensions for this stream. In non-scalable streams, these + * are often the actual dimensions of the stream, although that is not + * a normative requirement. + */ + int max_width, max_height; + enum Dav1dPixelLayout layout; ///< format of the picture + enum Dav1dColorPrimaries pri; ///< color primaries (av1) + enum Dav1dTransferCharacteristics trc; ///< transfer characteristics (av1) + enum Dav1dMatrixCoefficients mtrx; ///< matrix coefficients (av1) + enum Dav1dChromaSamplePosition chr; ///< chroma sample position (av1) + /** + * 0, 1 and 2 mean 8, 10 or 12 bits/component, respectively. This is not + * exactly the same as 'hbd' from the spec; the spec's hbd distinguishes + * between 8 (0) and 10-12 (1) bits/component, and another element + * (twelve_bit) to distinguish between 10 and 12 bits/component. To get + * the spec's hbd, use !!our_hbd, and to get twelve_bit, use hbd == 2. + */ + uint8_t hbd; + /** + * Pixel data uses JPEG pixel range ([0,255] for 8bits) instead of + * MPEG pixel range ([16,235] for 8bits luma, [16,240] for 8bits chroma). + */ + uint8_t color_range; + + uint8_t num_operating_points; + struct Dav1dSequenceHeaderOperatingPoint { + uint8_t major_level, minor_level; + uint8_t initial_display_delay; + uint16_t idc; + uint8_t tier; + uint8_t decoder_model_param_present; + uint8_t display_model_param_present; + } operating_points[DAV1D_MAX_OPERATING_POINTS]; + + uint8_t still_picture; + uint8_t reduced_still_picture_header; + uint8_t timing_info_present; + uint32_t num_units_in_tick; + uint32_t time_scale; + uint8_t equal_picture_interval; + uint32_t num_ticks_per_picture; + uint8_t decoder_model_info_present; + uint8_t encoder_decoder_buffer_delay_length; + uint32_t num_units_in_decoding_tick; + uint8_t buffer_removal_delay_length; + uint8_t frame_presentation_delay_length; + uint8_t display_model_info_present; + uint8_t width_n_bits, height_n_bits; + uint8_t frame_id_numbers_present; + uint8_t delta_frame_id_n_bits; + uint8_t frame_id_n_bits; + uint8_t sb128; + uint8_t filter_intra; + uint8_t intra_edge_filter; + uint8_t inter_intra; + uint8_t masked_compound; + uint8_t warped_motion; + uint8_t dual_filter; + uint8_t order_hint; + uint8_t jnt_comp; + uint8_t ref_frame_mvs; + enum Dav1dAdaptiveBoolean screen_content_tools; + enum Dav1dAdaptiveBoolean force_integer_mv; + uint8_t order_hint_n_bits; + uint8_t super_res; + uint8_t cdef; + uint8_t restoration; + uint8_t ss_hor, ss_ver, monochrome; + uint8_t color_description_present; + uint8_t separate_uv_delta_q; + uint8_t film_grain_present; + + // Dav1dSequenceHeaders of the same sequence are required to be + // bit-identical until this offset. See 7.5 "Ordering of OBUs": + // Within a particular coded video sequence, the contents of + // sequence_header_obu must be bit-identical each time the + // sequence header appears except for the contents of + // operating_parameters_info. + struct Dav1dSequenceHeaderOperatingParameterInfo { + uint32_t decoder_buffer_delay; + uint32_t encoder_buffer_delay; + uint8_t low_delay_mode; + } operating_parameter_info[DAV1D_MAX_OPERATING_POINTS]; +} Dav1dSequenceHeader; + +typedef struct Dav1dSegmentationData { + int16_t delta_q; + int8_t delta_lf_y_v, delta_lf_y_h, delta_lf_u, delta_lf_v; + int8_t ref; + uint8_t skip; + uint8_t globalmv; +} Dav1dSegmentationData; + +typedef struct Dav1dSegmentationDataSet { + Dav1dSegmentationData d[DAV1D_MAX_SEGMENTS]; + uint8_t preskip; + int8_t last_active_segid; +} Dav1dSegmentationDataSet; + +typedef struct Dav1dLoopfilterModeRefDeltas { + int8_t mode_delta[2 /* is_zeromv */]; + int8_t ref_delta[DAV1D_TOTAL_REFS_PER_FRAME]; +} Dav1dLoopfilterModeRefDeltas; + +typedef struct Dav1dFilmGrainData { + unsigned seed; + int num_y_points; + uint8_t y_points[14][2 /* value, scaling */]; + int chroma_scaling_from_luma; + int num_uv_points[2]; + uint8_t uv_points[2][10][2 /* value, scaling */]; + int scaling_shift; + int ar_coeff_lag; + int8_t ar_coeffs_y[24]; + int8_t ar_coeffs_uv[2][25 + 3 /* padding for alignment purposes */]; + uint64_t ar_coeff_shift; + int grain_scale_shift; + int uv_mult[2]; + int uv_luma_mult[2]; + int uv_offset[2]; + int overlap_flag; + int clip_to_restricted_range; +} Dav1dFilmGrainData; + +typedef struct Dav1dFrameHeader { + struct { + Dav1dFilmGrainData data; + uint8_t present, update; + } film_grain; ///< film grain parameters + enum Dav1dFrameType frame_type; ///< type of the picture + int width[2 /* { coded_width, superresolution_upscaled_width } */], height; + uint8_t frame_offset; ///< frame number + uint8_t temporal_id; ///< temporal id of the frame for SVC + uint8_t spatial_id; ///< spatial id of the frame for SVC + + uint8_t show_existing_frame; + uint8_t existing_frame_idx; + uint32_t frame_id; + uint32_t frame_presentation_delay; + uint8_t show_frame; + uint8_t showable_frame; + uint8_t error_resilient_mode; + uint8_t disable_cdf_update; + uint8_t allow_screen_content_tools; + uint8_t force_integer_mv; + uint8_t frame_size_override; + uint8_t primary_ref_frame; + uint8_t buffer_removal_time_present; + struct Dav1dFrameHeaderOperatingPoint { + uint32_t buffer_removal_time; + } operating_points[DAV1D_MAX_OPERATING_POINTS]; + uint8_t refresh_frame_flags; + int render_width, render_height; + struct { + uint8_t width_scale_denominator; + uint8_t enabled; + } super_res; + uint8_t have_render_size; + uint8_t allow_intrabc; + uint8_t frame_ref_short_signaling; + int8_t refidx[DAV1D_REFS_PER_FRAME]; + uint8_t hp; + enum Dav1dFilterMode subpel_filter_mode; + uint8_t switchable_motion_mode; + uint8_t use_ref_frame_mvs; + uint8_t refresh_context; + struct { + uint8_t uniform; + uint8_t n_bytes; + uint8_t min_log2_cols, max_log2_cols, log2_cols, cols; + uint8_t min_log2_rows, max_log2_rows, log2_rows, rows; + uint16_t col_start_sb[DAV1D_MAX_TILE_COLS + 1]; + uint16_t row_start_sb[DAV1D_MAX_TILE_ROWS + 1]; + uint16_t update; + } tiling; + struct { + uint8_t yac; + int8_t ydc_delta; + int8_t udc_delta, uac_delta, vdc_delta, vac_delta; + uint8_t qm, qm_y, qm_u, qm_v; + } quant; + struct { + uint8_t enabled, update_map, temporal, update_data; + Dav1dSegmentationDataSet seg_data; + uint8_t lossless[DAV1D_MAX_SEGMENTS], qidx[DAV1D_MAX_SEGMENTS]; + } segmentation; + struct { + struct { + uint8_t present; + uint8_t res_log2; + } q; + struct { + uint8_t present; + uint8_t res_log2; + uint8_t multi; + } lf; + } delta; + uint8_t all_lossless; + struct { + uint8_t level_y[2 /* dir */]; + uint8_t level_u, level_v; + uint8_t mode_ref_delta_enabled; + uint8_t mode_ref_delta_update; + Dav1dLoopfilterModeRefDeltas mode_ref_deltas; + uint8_t sharpness; + } loopfilter; + struct { + uint8_t damping; + uint8_t n_bits; + uint8_t y_strength[DAV1D_MAX_CDEF_STRENGTHS]; + uint8_t uv_strength[DAV1D_MAX_CDEF_STRENGTHS]; + } cdef; + struct { + enum Dav1dRestorationType type[3 /* plane */]; + uint8_t unit_size[2 /* y, uv */]; + } restoration; + enum Dav1dTxfmMode txfm_mode; + uint8_t switchable_comp_refs; + uint8_t skip_mode_allowed, skip_mode_enabled; + int8_t skip_mode_refs[2]; + uint8_t warp_motion; + uint8_t reduced_txtp_set; + Dav1dWarpedMotionParams gmv[DAV1D_REFS_PER_FRAME]; +} Dav1dFrameHeader; + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_HEADERS_H */ diff --git a/3rdparty/dav1d/include/dav1d/picture.h b/3rdparty/dav1d/include/dav1d/picture.h new file mode 100644 index 0000000..cc291a4 --- /dev/null +++ b/3rdparty/dav1d/include/dav1d/picture.h @@ -0,0 +1,157 @@ +/* + * Copyright © 2018-2020, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_PICTURE_H +#define DAV1D_PICTURE_H + +#include +#include + +#include "common.h" +#include "headers.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Number of bytes to align AND pad picture memory buffers by, so that SIMD + * implementations can over-read by a few bytes, and use aligned read/write + * instructions. */ +#define DAV1D_PICTURE_ALIGNMENT 64 + +typedef struct Dav1dPictureParameters { + int w; ///< width (in pixels) + int h; ///< height (in pixels) + enum Dav1dPixelLayout layout; ///< format of the picture + int bpc; ///< bits per pixel component (8 or 10) +} Dav1dPictureParameters; + +typedef struct Dav1dPicture { + Dav1dSequenceHeader *seq_hdr; + Dav1dFrameHeader *frame_hdr; + + /** + * Pointers to planar image data (Y is [0], U is [1], V is [2]). The data + * should be bytes (for 8 bpc) or words (for 10 bpc). In case of words + * containing 10 bpc image data, the pixels should be located in the LSB + * bits, so that values range between [0, 1023]; the upper bits should be + * zero'ed out. + */ + void *data[3]; + + /** + * Number of bytes between 2 lines in data[] for luma [0] or chroma [1]. + */ + ptrdiff_t stride[2]; + + Dav1dPictureParameters p; + Dav1dDataProps m; + + /** + * High Dynamic Range Content Light Level metadata applying to this picture, + * as defined in section 5.8.3 and 6.7.3 + */ + Dav1dContentLightLevel *content_light; + /** + * High Dynamic Range Mastering Display Color Volume metadata applying to + * this picture, as defined in section 5.8.4 and 6.7.4 + */ + Dav1dMasteringDisplay *mastering_display; + /** + * Array of ITU-T T.35 metadata as defined in section 5.8.2 and 6.7.2 + */ + Dav1dITUTT35 *itut_t35; + + /** + * Number of ITU-T T35 metadata entries in the array + */ + size_t n_itut_t35; + + uintptr_t reserved[4]; ///< reserved for future use + + struct Dav1dRef *frame_hdr_ref; ///< Dav1dFrameHeader allocation origin + struct Dav1dRef *seq_hdr_ref; ///< Dav1dSequenceHeader allocation origin + struct Dav1dRef *content_light_ref; ///< Dav1dContentLightLevel allocation origin + struct Dav1dRef *mastering_display_ref; ///< Dav1dMasteringDisplay allocation origin + struct Dav1dRef *itut_t35_ref; ///< Dav1dITUTT35 allocation origin + uintptr_t reserved_ref[4]; ///< reserved for future use + struct Dav1dRef *ref; ///< Frame data allocation origin + + void *allocator_data; ///< pointer managed by the allocator +} Dav1dPicture; + +typedef struct Dav1dPicAllocator { + void *cookie; ///< custom data to pass to the allocator callbacks. + /** + * Allocate the picture buffer based on the Dav1dPictureParameters. + * + * The data[0], data[1] and data[2] must be DAV1D_PICTURE_ALIGNMENT byte + * aligned and with a pixel width/height multiple of 128 pixels. Any + * allocated memory area should also be padded by DAV1D_PICTURE_ALIGNMENT + * bytes. + * data[1] and data[2] must share the same stride[1]. + * + * This function will be called on the main thread (the thread which calls + * dav1d_get_picture()). + * + * @param pic The picture to allocate the buffer for. The callback needs to + * fill the picture data[0], data[1], data[2], stride[0] and + * stride[1]. + * The allocator can fill the pic allocator_data pointer with + * a custom pointer that will be passed to + * release_picture_callback(). + * @param cookie Custom pointer passed to all calls. + * + * @note No fields other than data, stride and allocator_data must be filled + * by this callback. + * @return 0 on success. A negative DAV1D_ERR value on error. + */ + int (*alloc_picture_callback)(Dav1dPicture *pic, void *cookie); + /** + * Release the picture buffer. + * + * If frame threading is used, this function may be called by the main + * thread (the thread which calls dav1d_get_picture()) or any of the frame + * threads and thus must be thread-safe. If frame threading is not used, + * this function will only be called on the main thread. + * + * @param pic The picture that was filled by alloc_picture_callback(). + * @param cookie Custom pointer passed to all calls. + */ + void (*release_picture_callback)(Dav1dPicture *pic, void *cookie); +} Dav1dPicAllocator; + +/** + * Release reference to a picture. + */ +DAV1D_API void dav1d_picture_unref(Dav1dPicture *p); + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_PICTURE_H */ diff --git a/3rdparty/dav1d/include/dav1d/version.h b/3rdparty/dav1d/include/dav1d/version.h new file mode 100644 index 0000000..43df603 --- /dev/null +++ b/3rdparty/dav1d/include/dav1d/version.h @@ -0,0 +1,50 @@ +/* + * Copyright © 2019-2024, VideoLAN and dav1d authors + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_VERSION_H +#define DAV1D_VERSION_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define DAV1D_API_VERSION_MAJOR 7 +#define DAV1D_API_VERSION_MINOR 0 +#define DAV1D_API_VERSION_PATCH 0 + +/** + * Extract version components from the value returned by + * dav1d_version_int() + */ +#define DAV1D_API_MAJOR(v) (((v) >> 16) & 0xFF) +#define DAV1D_API_MINOR(v) (((v) >> 8) & 0xFF) +#define DAV1D_API_PATCH(v) (((v) >> 0) & 0xFF) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* DAV1D_VERSION_H */ diff --git a/3rdparty/dav1d/src/cdef.h b/3rdparty/dav1d/src/cdef.h new file mode 100644 index 0000000..07c84d9 --- /dev/null +++ b/3rdparty/dav1d/src/cdef.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_CDEF_H +#define DAV1D_SRC_CDEF_H + +#include +#include + +#include "common/bitdepth.h" + +enum CdefEdgeFlags { + CDEF_HAVE_LEFT = 1 << 0, + CDEF_HAVE_RIGHT = 1 << 1, + CDEF_HAVE_TOP = 1 << 2, + CDEF_HAVE_BOTTOM = 1 << 3, +}; + +#ifdef BITDEPTH +typedef const pixel (*const_left_pixel_row_2px)[2]; +#else +typedef const void *const_left_pixel_row_2px; +#endif + +// CDEF operates entirely on pre-filter data; if bottom/right edges are +// present (according to $edges), then the pre-filter data is located in +// $dst. However, the edge pixels above $dst may be post-filter, so in +// order to get access to pre-filter top pixels, use $top. +#define decl_cdef_fn(name) \ +void (name)(pixel *dst, ptrdiff_t stride, const_left_pixel_row_2px left, \ + const pixel *top, const pixel *bottom, \ + int pri_strength, int sec_strength, \ + int dir, int damping, enum CdefEdgeFlags edges HIGHBD_DECL_SUFFIX) +typedef decl_cdef_fn(*cdef_fn); + +#define decl_cdef_dir_fn(name) \ +int (name)(const pixel *dst, ptrdiff_t dst_stride, unsigned *var HIGHBD_DECL_SUFFIX) +typedef decl_cdef_dir_fn(*cdef_dir_fn); + +typedef struct Dav1dCdefDSPContext { + cdef_dir_fn dir; + cdef_fn fb[3 /* 444/luma, 422, 420 */]; +} Dav1dCdefDSPContext; + +bitfn_decls(void dav1d_cdef_dsp_init, Dav1dCdefDSPContext *c); + +#endif /* DAV1D_SRC_CDEF_H */ diff --git a/3rdparty/dav1d/src/cdef_apply.h b/3rdparty/dav1d/src/cdef_apply.h new file mode 100644 index 0000000..a9748ee --- /dev/null +++ b/3rdparty/dav1d/src/cdef_apply.h @@ -0,0 +1,39 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_CDEF_APPLY_H +#define DAV1D_SRC_CDEF_APPLY_H + +#include "common/bitdepth.h" + +#include "src/internal.h" + +void bytefn(dav1d_cdef_brow)(Dav1dTaskContext *tc, pixel *const p[3], + const Av1Filter *lflvl, int by_start, int by_end, + int sbrow_start, int sby); + +#endif /* DAV1D_SRC_CDEF_APPLY_H */ diff --git a/3rdparty/dav1d/src/cdef_apply_tmpl.c b/3rdparty/dav1d/src/cdef_apply_tmpl.c new file mode 100644 index 0000000..9b09f19 --- /dev/null +++ b/3rdparty/dav1d/src/cdef_apply_tmpl.c @@ -0,0 +1,308 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/intops.h" + +#include "src/cdef_apply.h" + +enum Backup2x8Flags { + BACKUP_2X8_Y = 1 << 0, + BACKUP_2X8_UV = 1 << 1, +}; + +static void backup2lines(pixel *const dst[3], /*const*/ pixel *const src[3], + const ptrdiff_t stride[2], + const enum Dav1dPixelLayout layout) +{ + const ptrdiff_t y_stride = PXSTRIDE(stride[0]); + if (y_stride < 0) + pixel_copy(dst[0] + y_stride, src[0] + 7 * y_stride, -2 * y_stride); + else + pixel_copy(dst[0], src[0] + 6 * y_stride, 2 * y_stride); + + if (layout != DAV1D_PIXEL_LAYOUT_I400) { + const ptrdiff_t uv_stride = PXSTRIDE(stride[1]); + if (uv_stride < 0) { + const int uv_off = layout == DAV1D_PIXEL_LAYOUT_I420 ? 3 : 7; + pixel_copy(dst[1] + uv_stride, src[1] + uv_off * uv_stride, -2 * uv_stride); + pixel_copy(dst[2] + uv_stride, src[2] + uv_off * uv_stride, -2 * uv_stride); + } else { + const int uv_off = layout == DAV1D_PIXEL_LAYOUT_I420 ? 2 : 6; + pixel_copy(dst[1], src[1] + uv_off * uv_stride, 2 * uv_stride); + pixel_copy(dst[2], src[2] + uv_off * uv_stride, 2 * uv_stride); + } + } +} + +static void backup2x8(pixel dst[3][8][2], + /*const*/ pixel *const src[3], + const ptrdiff_t src_stride[2], int x_off, + const enum Dav1dPixelLayout layout, + const enum Backup2x8Flags flag) +{ + ptrdiff_t y_off = 0; + if (flag & BACKUP_2X8_Y) { + for (int y = 0; y < 8; y++, y_off += PXSTRIDE(src_stride[0])) + pixel_copy(dst[0][y], &src[0][y_off + x_off - 2], 2); + } + + if (layout == DAV1D_PIXEL_LAYOUT_I400 || !(flag & BACKUP_2X8_UV)) + return; + + const int ss_ver = layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = layout != DAV1D_PIXEL_LAYOUT_I444; + + x_off >>= ss_hor; + y_off = 0; + for (int y = 0; y < (8 >> ss_ver); y++, y_off += PXSTRIDE(src_stride[1])) { + pixel_copy(dst[1][y], &src[1][y_off + x_off - 2], 2); + pixel_copy(dst[2][y], &src[2][y_off + x_off - 2], 2); + } +} + +static int adjust_strength(const int strength, const unsigned var) { + if (!var) return 0; + const int i = var >> 6 ? imin(ulog2(var >> 6), 12) : 0; + return (strength * (4 + i) + 8) >> 4; +} + +void bytefn(dav1d_cdef_brow)(Dav1dTaskContext *const tc, + pixel *const p[3], + const Av1Filter *const lflvl, + const int by_start, const int by_end, + const int sbrow_start, const int sby) +{ + Dav1dFrameContext *const f = (Dav1dFrameContext *)tc->f; + const int bitdepth_min_8 = BITDEPTH == 8 ? 0 : f->cur.p.bpc - 8; + const Dav1dDSPContext *const dsp = f->dsp; + enum CdefEdgeFlags edges = CDEF_HAVE_BOTTOM | (by_start > 0 ? CDEF_HAVE_TOP : 0); + pixel *ptrs[3] = { p[0], p[1], p[2] }; + const int sbsz = 16; + const int sb64w = f->sb128w << 1; + const int damping = f->frame_hdr->cdef.damping + bitdepth_min_8; + const enum Dav1dPixelLayout layout = f->cur.p.layout; + const int uv_idx = DAV1D_PIXEL_LAYOUT_I444 - layout; + const int ss_ver = layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = layout != DAV1D_PIXEL_LAYOUT_I444; + static const uint8_t uv_dirs[2][8] = { { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 7, 0, 2, 4, 5, 6, 6, 6 } }; + const uint8_t *uv_dir = uv_dirs[layout == DAV1D_PIXEL_LAYOUT_I422]; + const int have_tt = f->c->n_tc > 1; + const int sb128 = f->seq_hdr->sb128; + const int resize = f->frame_hdr->width[0] != f->frame_hdr->width[1]; + const ptrdiff_t y_stride = PXSTRIDE(f->cur.stride[0]); + const ptrdiff_t uv_stride = PXSTRIDE(f->cur.stride[1]); + + for (int bit = 0, by = by_start; by < by_end; by += 2, edges |= CDEF_HAVE_TOP) { + const int tf = tc->top_pre_cdef_toggle; + const int by_idx = (by & 30) >> 1; + if (by + 2 >= f->bh) edges &= ~CDEF_HAVE_BOTTOM; + + if ((!have_tt || sbrow_start || by + 2 < by_end) && + edges & CDEF_HAVE_BOTTOM) + { + // backup pre-filter data for next iteration + pixel *const cdef_top_bak[3] = { + f->lf.cdef_line[!tf][0] + have_tt * sby * 4 * y_stride, + f->lf.cdef_line[!tf][1] + have_tt * sby * 8 * uv_stride, + f->lf.cdef_line[!tf][2] + have_tt * sby * 8 * uv_stride + }; + backup2lines(cdef_top_bak, ptrs, f->cur.stride, layout); + } + + ALIGN_STK_16(pixel, lr_bak, 2 /* idx */, [3 /* plane */][8 /* y */][2 /* x */]); + pixel *iptrs[3] = { ptrs[0], ptrs[1], ptrs[2] }; + edges &= ~CDEF_HAVE_LEFT; + edges |= CDEF_HAVE_RIGHT; + enum Backup2x8Flags prev_flag = 0; + for (int sbx = 0; sbx < sb64w; sbx++, edges |= CDEF_HAVE_LEFT) { + const int sb128x = sbx >> 1; + const int sb64_idx = ((by & sbsz) >> 3) + (sbx & 1); + const int cdef_idx = lflvl[sb128x].cdef_idx[sb64_idx]; + if (cdef_idx == -1 || + (!f->frame_hdr->cdef.y_strength[cdef_idx] && + !f->frame_hdr->cdef.uv_strength[cdef_idx])) + { + prev_flag = 0; + goto next_sb; + } + + // Create a complete 32-bit mask for the sb row ahead of time. + const uint16_t (*noskip_row)[2] = &lflvl[sb128x].noskip_mask[by_idx]; + const unsigned noskip_mask = (unsigned) noskip_row[0][1] << 16 | + noskip_row[0][0]; + + const int y_lvl = f->frame_hdr->cdef.y_strength[cdef_idx]; + const int uv_lvl = f->frame_hdr->cdef.uv_strength[cdef_idx]; + const enum Backup2x8Flags flag = !!y_lvl + (!!uv_lvl << 1); + + const int y_pri_lvl = (y_lvl >> 2) << bitdepth_min_8; + int y_sec_lvl = y_lvl & 3; + y_sec_lvl += y_sec_lvl == 3; + y_sec_lvl <<= bitdepth_min_8; + + const int uv_pri_lvl = (uv_lvl >> 2) << bitdepth_min_8; + int uv_sec_lvl = uv_lvl & 3; + uv_sec_lvl += uv_sec_lvl == 3; + uv_sec_lvl <<= bitdepth_min_8; + + pixel *bptrs[3] = { iptrs[0], iptrs[1], iptrs[2] }; + for (int bx = sbx * sbsz; bx < imin((sbx + 1) * sbsz, f->bw); + bx += 2, edges |= CDEF_HAVE_LEFT) + { + if (bx + 2 >= f->bw) edges &= ~CDEF_HAVE_RIGHT; + + // check if this 8x8 block had any coded coefficients; if not, + // go to the next block + const uint32_t bx_mask = 3U << (bx & 30); + if (!(noskip_mask & bx_mask)) { + prev_flag = 0; + goto next_b; + } + const enum Backup2x8Flags do_left = (prev_flag ^ flag) & flag; + prev_flag = flag; + if (do_left && edges & CDEF_HAVE_LEFT) { + // we didn't backup the prefilter data because it wasn't + // there, so do it here instead + backup2x8(lr_bak[bit], bptrs, f->cur.stride, 0, layout, do_left); + } + if (edges & CDEF_HAVE_RIGHT) { + // backup pre-filter data for next iteration + backup2x8(lr_bak[!bit], bptrs, f->cur.stride, 8, layout, flag); + } + + int dir; + unsigned variance; + if (y_pri_lvl || uv_pri_lvl) + dir = dsp->cdef.dir(bptrs[0], f->cur.stride[0], + &variance HIGHBD_CALL_SUFFIX); + + const pixel *top, *bot; + ptrdiff_t offset; + + if (!have_tt) goto st_y; + if (sbrow_start && by == by_start) { + if (resize) { + offset = (sby - 1) * 4 * y_stride + bx * 4; + top = &f->lf.cdef_lpf_line[0][offset]; + } else { + offset = (sby * (4 << sb128) - 4) * y_stride + bx * 4; + top = &f->lf.lr_lpf_line[0][offset]; + } + bot = bptrs[0] + 8 * y_stride; + } else if (!sbrow_start && by + 2 >= by_end) { + top = &f->lf.cdef_line[tf][0][sby * 4 * y_stride + bx * 4]; + if (resize) { + offset = (sby * 4 + 2) * y_stride + bx * 4; + bot = &f->lf.cdef_lpf_line[0][offset]; + } else { + const int line = sby * (4 << sb128) + 4 * sb128 + 2; + offset = line * y_stride + bx * 4; + bot = &f->lf.lr_lpf_line[0][offset]; + } + } else { + st_y:; + offset = sby * 4 * y_stride; + top = &f->lf.cdef_line[tf][0][have_tt * offset + bx * 4]; + bot = bptrs[0] + 8 * y_stride; + } + if (y_pri_lvl) { + const int adj_y_pri_lvl = adjust_strength(y_pri_lvl, variance); + if (adj_y_pri_lvl || y_sec_lvl) + dsp->cdef.fb[0](bptrs[0], f->cur.stride[0], lr_bak[bit][0], + top, bot, adj_y_pri_lvl, y_sec_lvl, + dir, damping, edges HIGHBD_CALL_SUFFIX); + } else if (y_sec_lvl) + dsp->cdef.fb[0](bptrs[0], f->cur.stride[0], lr_bak[bit][0], + top, bot, 0, y_sec_lvl, 0, damping, + edges HIGHBD_CALL_SUFFIX); + + if (!uv_lvl) goto skip_uv; + assert(layout != DAV1D_PIXEL_LAYOUT_I400); + + const int uvdir = uv_pri_lvl ? uv_dir[dir] : 0; + for (int pl = 1; pl <= 2; pl++) { + if (!have_tt) goto st_uv; + if (sbrow_start && by == by_start) { + if (resize) { + offset = (sby - 1) * 4 * uv_stride + (bx * 4 >> ss_hor); + top = &f->lf.cdef_lpf_line[pl][offset]; + } else { + const int line = sby * (4 << sb128) - 4; + offset = line * uv_stride + (bx * 4 >> ss_hor); + top = &f->lf.lr_lpf_line[pl][offset]; + } + bot = bptrs[pl] + (8 >> ss_ver) * uv_stride; + } else if (!sbrow_start && by + 2 >= by_end) { + const ptrdiff_t top_offset = sby * 8 * uv_stride + + (bx * 4 >> ss_hor); + top = &f->lf.cdef_line[tf][pl][top_offset]; + if (resize) { + offset = (sby * 4 + 2) * uv_stride + (bx * 4 >> ss_hor); + bot = &f->lf.cdef_lpf_line[pl][offset]; + } else { + const int line = sby * (4 << sb128) + 4 * sb128 + 2; + offset = line * uv_stride + (bx * 4 >> ss_hor); + bot = &f->lf.lr_lpf_line[pl][offset]; + } + } else { + st_uv:; + const ptrdiff_t offset = sby * 8 * uv_stride; + top = &f->lf.cdef_line[tf][pl][have_tt * offset + (bx * 4 >> ss_hor)]; + bot = bptrs[pl] + (8 >> ss_ver) * uv_stride; + } + dsp->cdef.fb[uv_idx](bptrs[pl], f->cur.stride[1], + lr_bak[bit][pl], top, bot, + uv_pri_lvl, uv_sec_lvl, uvdir, + damping - 1, edges HIGHBD_CALL_SUFFIX); + } + + skip_uv: + bit ^= 1; + + next_b: + bptrs[0] += 8; + bptrs[1] += 8 >> ss_hor; + bptrs[2] += 8 >> ss_hor; + } + + next_sb: + iptrs[0] += sbsz * 4; + iptrs[1] += sbsz * 4 >> ss_hor; + iptrs[2] += sbsz * 4 >> ss_hor; + } + + ptrs[0] += 8 * PXSTRIDE(f->cur.stride[0]); + ptrs[1] += 8 * PXSTRIDE(f->cur.stride[1]) >> ss_ver; + ptrs[2] += 8 * PXSTRIDE(f->cur.stride[1]) >> ss_ver; + tc->top_pre_cdef_toggle ^= 1; + } +} diff --git a/3rdparty/dav1d/src/cdef_tmpl.c b/3rdparty/dav1d/src/cdef_tmpl.c new file mode 100644 index 0000000..4efea45 --- /dev/null +++ b/3rdparty/dav1d/src/cdef_tmpl.c @@ -0,0 +1,339 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/intops.h" + +#include "src/cdef.h" +#include "src/tables.h" + +static inline int constrain(const int diff, const int threshold, + const int shift) +{ + const int adiff = abs(diff); + return apply_sign(imin(adiff, imax(0, threshold - (adiff >> shift))), diff); +} + +static inline void fill(int16_t *tmp, const ptrdiff_t stride, + const int w, const int h) +{ + /* Use a value that's a large positive number when interpreted as unsigned, + * and a large negative number when interpreted as signed. */ + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) + tmp[x] = INT16_MIN; + tmp += stride; + } +} + +static void padding(int16_t *tmp, const ptrdiff_t tmp_stride, + const pixel *src, const ptrdiff_t src_stride, + const pixel (*left)[2], + const pixel *top, const pixel *bottom, + const int w, const int h, const enum CdefEdgeFlags edges) +{ + // fill extended input buffer + int x_start = -2, x_end = w + 2, y_start = -2, y_end = h + 2; + if (!(edges & CDEF_HAVE_TOP)) { + fill(tmp - 2 - 2 * tmp_stride, tmp_stride, w + 4, 2); + y_start = 0; + } + if (!(edges & CDEF_HAVE_BOTTOM)) { + fill(tmp + h * tmp_stride - 2, tmp_stride, w + 4, 2); + y_end -= 2; + } + if (!(edges & CDEF_HAVE_LEFT)) { + fill(tmp + y_start * tmp_stride - 2, tmp_stride, 2, y_end - y_start); + x_start = 0; + } + if (!(edges & CDEF_HAVE_RIGHT)) { + fill(tmp + y_start * tmp_stride + w, tmp_stride, 2, y_end - y_start); + x_end -= 2; + } + + for (int y = y_start; y < 0; y++) { + for (int x = x_start; x < x_end; x++) + tmp[x + y * tmp_stride] = top[x]; + top += PXSTRIDE(src_stride); + } + for (int y = 0; y < h; y++) + for (int x = x_start; x < 0; x++) + tmp[x + y * tmp_stride] = left[y][2 + x]; + for (int y = 0; y < h; y++) { + for (int x = (y < h) ? 0 : x_start; x < x_end; x++) + tmp[x] = src[x]; + src += PXSTRIDE(src_stride); + tmp += tmp_stride; + } + for (int y = h; y < y_end; y++) { + for (int x = x_start; x < x_end; x++) + tmp[x] = bottom[x]; + bottom += PXSTRIDE(src_stride); + tmp += tmp_stride; + } + +} + +static NOINLINE void +cdef_filter_block_c(pixel *dst, const ptrdiff_t dst_stride, + const pixel (*left)[2], + const pixel *const top, const pixel *const bottom, + const int pri_strength, const int sec_strength, + const int dir, const int damping, const int w, int h, + const enum CdefEdgeFlags edges HIGHBD_DECL_SUFFIX) +{ + const ptrdiff_t tmp_stride = 12; + assert((w == 4 || w == 8) && (h == 4 || h == 8)); + int16_t tmp_buf[144]; // 12*12 is the maximum value of tmp_stride * (h + 4) + int16_t *tmp = tmp_buf + 2 * tmp_stride + 2; + + padding(tmp, tmp_stride, dst, dst_stride, left, top, bottom, w, h, edges); + + if (pri_strength) { + const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8; + const int pri_tap = 4 - ((pri_strength >> bitdepth_min_8) & 1); + const int pri_shift = imax(0, damping - ulog2(pri_strength)); + if (sec_strength) { + const int sec_shift = damping - ulog2(sec_strength); + do { + for (int x = 0; x < w; x++) { + const int px = dst[x]; + int sum = 0; + int max = px, min = px; + int pri_tap_k = pri_tap; + for (int k = 0; k < 2; k++) { + const int off1 = dav1d_cdef_directions[dir + 2][k]; // dir + const int p0 = tmp[x + off1]; + const int p1 = tmp[x - off1]; + sum += pri_tap_k * constrain(p0 - px, pri_strength, pri_shift); + sum += pri_tap_k * constrain(p1 - px, pri_strength, pri_shift); + // if pri_tap_k == 4 then it becomes 2 else it remains 3 + pri_tap_k = (pri_tap_k & 3) | 2; + min = umin(p0, min); + max = imax(p0, max); + min = umin(p1, min); + max = imax(p1, max); + const int off2 = dav1d_cdef_directions[dir + 4][k]; // dir + 2 + const int off3 = dav1d_cdef_directions[dir + 0][k]; // dir - 2 + const int s0 = tmp[x + off2]; + const int s1 = tmp[x - off2]; + const int s2 = tmp[x + off3]; + const int s3 = tmp[x - off3]; + // sec_tap starts at 2 and becomes 1 + const int sec_tap = 2 - k; + sum += sec_tap * constrain(s0 - px, sec_strength, sec_shift); + sum += sec_tap * constrain(s1 - px, sec_strength, sec_shift); + sum += sec_tap * constrain(s2 - px, sec_strength, sec_shift); + sum += sec_tap * constrain(s3 - px, sec_strength, sec_shift); + min = umin(s0, min); + max = imax(s0, max); + min = umin(s1, min); + max = imax(s1, max); + min = umin(s2, min); + max = imax(s2, max); + min = umin(s3, min); + max = imax(s3, max); + } + dst[x] = iclip(px + ((sum - (sum < 0) + 8) >> 4), min, max); + } + dst += PXSTRIDE(dst_stride); + tmp += tmp_stride; + } while (--h); + } else { // pri_strength only + do { + for (int x = 0; x < w; x++) { + const int px = dst[x]; + int sum = 0; + int pri_tap_k = pri_tap; + for (int k = 0; k < 2; k++) { + const int off = dav1d_cdef_directions[dir + 2][k]; // dir + const int p0 = tmp[x + off]; + const int p1 = tmp[x - off]; + sum += pri_tap_k * constrain(p0 - px, pri_strength, pri_shift); + sum += pri_tap_k * constrain(p1 - px, pri_strength, pri_shift); + pri_tap_k = (pri_tap_k & 3) | 2; + } + dst[x] = px + ((sum - (sum < 0) + 8) >> 4); + } + dst += PXSTRIDE(dst_stride); + tmp += tmp_stride; + } while (--h); + } + } else { // sec_strength only + assert(sec_strength); + const int sec_shift = damping - ulog2(sec_strength); + do { + for (int x = 0; x < w; x++) { + const int px = dst[x]; + int sum = 0; + for (int k = 0; k < 2; k++) { + const int off1 = dav1d_cdef_directions[dir + 4][k]; // dir + 2 + const int off2 = dav1d_cdef_directions[dir + 0][k]; // dir - 2 + const int s0 = tmp[x + off1]; + const int s1 = tmp[x - off1]; + const int s2 = tmp[x + off2]; + const int s3 = tmp[x - off2]; + const int sec_tap = 2 - k; + sum += sec_tap * constrain(s0 - px, sec_strength, sec_shift); + sum += sec_tap * constrain(s1 - px, sec_strength, sec_shift); + sum += sec_tap * constrain(s2 - px, sec_strength, sec_shift); + sum += sec_tap * constrain(s3 - px, sec_strength, sec_shift); + } + dst[x] = px + ((sum - (sum < 0) + 8) >> 4); + } + dst += PXSTRIDE(dst_stride); + tmp += tmp_stride; + } while (--h); + } +} + +#define cdef_fn(w, h) \ +static void cdef_filter_block_##w##x##h##_c(pixel *const dst, \ + const ptrdiff_t stride, \ + const pixel (*left)[2], \ + const pixel *const top, \ + const pixel *const bottom, \ + const int pri_strength, \ + const int sec_strength, \ + const int dir, \ + const int damping, \ + const enum CdefEdgeFlags edges \ + HIGHBD_DECL_SUFFIX) \ +{ \ + cdef_filter_block_c(dst, stride, left, top, bottom, \ + pri_strength, sec_strength, dir, damping, w, h, edges HIGHBD_TAIL_SUFFIX); \ +} + +cdef_fn(4, 4); +cdef_fn(4, 8); +cdef_fn(8, 8); + +static int cdef_find_dir_c(const pixel *img, const ptrdiff_t stride, + unsigned *const var HIGHBD_DECL_SUFFIX) +{ + const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8; + int partial_sum_hv[2][8] = { { 0 } }; + int partial_sum_diag[2][15] = { { 0 } }; + int partial_sum_alt[4][11] = { { 0 } }; + + for (int y = 0; y < 8; y++) { + for (int x = 0; x < 8; x++) { + const int px = (img[x] >> bitdepth_min_8) - 128; + + partial_sum_diag[0][ y + x ] += px; + partial_sum_alt [0][ y + (x >> 1)] += px; + partial_sum_hv [0][ y ] += px; + partial_sum_alt [1][3 + y - (x >> 1)] += px; + partial_sum_diag[1][7 + y - x ] += px; + partial_sum_alt [2][3 - (y >> 1) + x ] += px; + partial_sum_hv [1][ x ] += px; + partial_sum_alt [3][ (y >> 1) + x ] += px; + } + img += PXSTRIDE(stride); + } + + unsigned cost[8] = { 0 }; + for (int n = 0; n < 8; n++) { + cost[2] += partial_sum_hv[0][n] * partial_sum_hv[0][n]; + cost[6] += partial_sum_hv[1][n] * partial_sum_hv[1][n]; + } + cost[2] *= 105; + cost[6] *= 105; + + static const uint16_t div_table[7] = { 840, 420, 280, 210, 168, 140, 120 }; + for (int n = 0; n < 7; n++) { + const int d = div_table[n]; + cost[0] += (partial_sum_diag[0][n] * partial_sum_diag[0][n] + + partial_sum_diag[0][14 - n] * partial_sum_diag[0][14 - n]) * d; + cost[4] += (partial_sum_diag[1][n] * partial_sum_diag[1][n] + + partial_sum_diag[1][14 - n] * partial_sum_diag[1][14 - n]) * d; + } + cost[0] += partial_sum_diag[0][7] * partial_sum_diag[0][7] * 105; + cost[4] += partial_sum_diag[1][7] * partial_sum_diag[1][7] * 105; + + for (int n = 0; n < 4; n++) { + unsigned *const cost_ptr = &cost[n * 2 + 1]; + for (int m = 0; m < 5; m++) + *cost_ptr += partial_sum_alt[n][3 + m] * partial_sum_alt[n][3 + m]; + *cost_ptr *= 105; + for (int m = 0; m < 3; m++) { + const int d = div_table[2 * m + 1]; + *cost_ptr += (partial_sum_alt[n][m] * partial_sum_alt[n][m] + + partial_sum_alt[n][10 - m] * partial_sum_alt[n][10 - m]) * d; + } + } + + int best_dir = 0; + unsigned best_cost = cost[0]; + for (int n = 1; n < 8; n++) { + if (cost[n] > best_cost) { + best_cost = cost[n]; + best_dir = n; + } + } + + *var = (best_cost - (cost[best_dir ^ 4])) >> 10; + return best_dir; +} + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM +#include "src/arm/cdef.h" +#elif ARCH_PPC64LE +#include "src/ppc/cdef.h" +#elif ARCH_RISCV +#include "src/riscv/cdef.h" +#elif ARCH_X86 +#include "src/x86/cdef.h" +#elif ARCH_LOONGARCH64 +#include "src/loongarch/cdef.h" +#endif +#endif + +COLD void bitfn(dav1d_cdef_dsp_init)(Dav1dCdefDSPContext *const c) { + c->dir = cdef_find_dir_c; + c->fb[0] = cdef_filter_block_8x8_c; + c->fb[1] = cdef_filter_block_4x8_c; + c->fb[2] = cdef_filter_block_4x4_c; + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM + cdef_dsp_init_arm(c); +#elif ARCH_PPC64LE + cdef_dsp_init_ppc(c); +#elif ARCH_RISCV + cdef_dsp_init_riscv(c); +#elif ARCH_X86 + cdef_dsp_init_x86(c); +#elif ARCH_LOONGARCH64 + cdef_dsp_init_loongarch(c); +#endif +#endif +} diff --git a/3rdparty/dav1d/src/cdf.c b/3rdparty/dav1d/src/cdf.c new file mode 100644 index 0000000..b17b8df --- /dev/null +++ b/3rdparty/dav1d/src/cdf.c @@ -0,0 +1,4065 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/frame.h" + +#include "src/internal.h" +#include "src/tables.h" + +#define CDF1(x) (32768-(x)) + +#define CDF2(a,b) \ + CDF1(a), CDF1(b) +#define CDF3(a,b,c) \ + CDF1(a), CDF2(b,c) +#define CDF4(a,b,c,d) \ + CDF1(a), CDF3(b,c,d) +#define CDF5(a,b,c,d,e) \ + CDF1(a), CDF4(b,c,d,e) +#define CDF6(a,b,c,d,e,f) \ + CDF1(a), CDF5(b,c,d,e,f) +#define CDF7(a,b,c,d,e,f,g) \ + CDF1(a), CDF6(b,c,d,e,f,g) +#define CDF8(a,b,c,d,e,f,g,h) \ + CDF1(a), CDF7(b,c,d,e,f,g,h) +#define CDF9(a,b,c,d,e,f,g,h,i) \ + CDF1(a), CDF8(b,c,d,e,f,g,h,i) +#define CDF10(a,b,c,d,e,f,g,h,i,j) \ + CDF1(a), CDF9(b,c,d,e,f,g,h,i,j) +#define CDF11(a,b,c,d,e,f,g,h,i,j,k) \ + CDF1(a), CDF10(b,c,d,e,f,g,h,i,j,k) +#define CDF12(a,b,c,d,e,f,g,h,i,j,k,l) \ + CDF1(a), CDF11(b,c,d,e,f,g,h,i,j,k,l) +#define CDF13(a,b,c,d,e,f,g,h,i,j,k,l,m) \ + CDF1(a), CDF12(b,c,d,e,f,g,h,i,j,k,l,m) +#define CDF14(a,b,c,d,e,f,g,h,i,j,k,l,m,n) \ + CDF1(a), CDF13(b,c,d,e,f,g,h,i,j,k,l,m,n) +#define CDF15(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) \ + CDF1(a), CDF14(b,c,d,e,f,g,h,i,j,k,l,m,n,o) + +typedef struct CdfDefaultContext { + CdfModeContext m; + struct { + CdfMvComponent comp; + ALIGN(uint16_t joint[N_MV_JOINTS], 8); + } mv; + ALIGN(uint16_t kfym[5][5][N_INTRA_PRED_MODES + 3], 32); +} CdfDefaultContext; + +static const CdfDefaultContext default_cdf = { + .m = { + .y_mode = { + { CDF12(22801, 23489, 24293, 24756, 25601, 26123, + 26606, 27418, 27945, 29228, 29685, 30349) }, + { CDF12(18673, 19845, 22631, 23318, 23950, 24649, + 25527, 27364, 28152, 29701, 29984, 30852) }, + { CDF12(19770, 20979, 23396, 23939, 24241, 24654, + 25136, 27073, 27830, 29360, 29730, 30659) }, + { CDF12(20155, 21301, 22838, 23178, 23261, 23533, + 23703, 24804, 25352, 26575, 27016, 28049) }, + }, .use_filter_intra = { + [BS_4x4] = { CDF1( 4621) }, + [BS_4x8] = { CDF1( 6743) }, + [BS_8x4] = { CDF1( 5893) }, + [BS_8x8] = { CDF1( 7866) }, + [BS_8x16] = { CDF1(12551) }, + [BS_16x8] = { CDF1( 9394) }, + [BS_16x16] = { CDF1(12408) }, + [BS_16x32] = { CDF1(14301) }, + [BS_32x16] = { CDF1(12756) }, + [BS_32x32] = { CDF1(22343) }, + [BS_32x64] = { CDF1(16384) }, + [BS_64x32] = { CDF1(16384) }, + [BS_64x64] = { CDF1(16384) }, + [BS_64x128] = { CDF1(16384) }, + [BS_128x64] = { CDF1(16384) }, + [BS_128x128] = { CDF1(16384) }, + [BS_4x16] = { CDF1(12770) }, + [BS_16x4] = { CDF1(10368) }, + [BS_8x32] = { CDF1(20229) }, + [BS_32x8] = { CDF1(18101) }, + [BS_16x64] = { CDF1(16384) }, + [BS_64x16] = { CDF1(16384) }, + }, .filter_intra = { + CDF4(8949, 12776, 17211, 29558), + }, .uv_mode = { + { + { CDF12(22631, 24152, 25378, 25661, 25986, 26520, + 27055, 27923, 28244, 30059, 30941, 31961) }, + { CDF12( 9513, 26881, 26973, 27046, 27118, 27664, + 27739, 27824, 28359, 29505, 29800, 31796) }, + { CDF12( 9845, 9915, 28663, 28704, 28757, 28780, + 29198, 29822, 29854, 30764, 31777, 32029) }, + { CDF12(13639, 13897, 14171, 25331, 25606, 25727, + 25953, 27148, 28577, 30612, 31355, 32493) }, + { CDF12( 9764, 9835, 9930, 9954, 25386, 27053, + 27958, 28148, 28243, 31101, 31744, 32363) }, + { CDF12(11825, 13589, 13677, 13720, 15048, 29213, + 29301, 29458, 29711, 31161, 31441, 32550) }, + { CDF12(14175, 14399, 16608, 16821, 17718, 17775, + 28551, 30200, 30245, 31837, 32342, 32667) }, + { CDF12(12885, 13038, 14978, 15590, 15673, 15748, + 16176, 29128, 29267, 30643, 31961, 32461) }, + { CDF12(12026, 13661, 13874, 15305, 15490, 15726, + 15995, 16273, 28443, 30388, 30767, 32416) }, + { CDF12(19052, 19840, 20579, 20916, 21150, 21467, + 21885, 22719, 23174, 28861, 30379, 32175) }, + { CDF12(18627, 19649, 20974, 21219, 21492, 21816, + 22199, 23119, 23527, 27053, 31397, 32148) }, + { CDF12(17026, 19004, 19997, 20339, 20586, 21103, + 21349, 21907, 22482, 25896, 26541, 31819) }, + { CDF12(12124, 13759, 14959, 14992, 15007, 15051, + 15078, 15166, 15255, 15753, 16039, 16606) }, + }, { + { CDF13(10407, 11208, 12900, 13181, 13823, 14175, 14899, + 15656, 15986, 20086, 20995, 22455, 24212) }, + { CDF13( 4532, 19780, 20057, 20215, 20428, 21071, 21199, + 21451, 22099, 24228, 24693, 27032, 29472) }, + { CDF13( 5273, 5379, 20177, 20270, 20385, 20439, 20949, + 21695, 21774, 23138, 24256, 24703, 26679) }, + { CDF13( 6740, 7167, 7662, 14152, 14536, 14785, 15034, + 16741, 18371, 21520, 22206, 23389, 24182) }, + { CDF13( 4987, 5368, 5928, 6068, 19114, 20315, 21857, + 22253, 22411, 24911, 25380, 26027, 26376) }, + { CDF13( 5370, 6889, 7247, 7393, 9498, 21114, 21402, + 21753, 21981, 24780, 25386, 26517, 27176) }, + { CDF13( 4816, 4961, 7204, 7326, 8765, 8930, 20169, + 20682, 20803, 23188, 23763, 24455, 24940) }, + { CDF13( 6608, 6740, 8529, 9049, 9257, 9356, 9735, + 18827, 19059, 22336, 23204, 23964, 24793) }, + { CDF13( 5998, 7419, 7781, 8933, 9255, 9549, 9753, + 10417, 18898, 22494, 23139, 24764, 25989) }, + { CDF13(10660, 11298, 12550, 12957, 13322, 13624, 14040, + 15004, 15534, 20714, 21789, 23443, 24861) }, + { CDF13(10522, 11530, 12552, 12963, 13378, 13779, 14245, + 15235, 15902, 20102, 22696, 23774, 25838) }, + { CDF13(10099, 10691, 12639, 13049, 13386, 13665, 14125, + 15163, 15636, 19676, 20474, 23519, 25208) }, + { CDF13( 3144, 5087, 7382, 7504, 7593, 7690, 7801, + 8064, 8232, 9248, 9875, 10521, 29048) }, + }, + }, .angle_delta = { + { CDF6( 2180, 5032, 7567, 22776, 26989, 30217) }, + { CDF6( 2301, 5608, 8801, 23487, 26974, 30330) }, + { CDF6( 3780, 11018, 13699, 19354, 23083, 31286) }, + { CDF6( 4581, 11226, 15147, 17138, 21834, 28397) }, + { CDF6( 1737, 10927, 14509, 19588, 22745, 28823) }, + { CDF6( 2664, 10176, 12485, 17650, 21600, 30495) }, + { CDF6( 2240, 11096, 15453, 20341, 22561, 28917) }, + { CDF6( 3605, 10428, 12459, 17676, 21244, 30655) }, + }, .filter = { + { + { CDF2(31935, 32720) }, { CDF2( 5568, 32719) }, + { CDF2( 422, 2938) }, { CDF2(28244, 32608) }, + { CDF2(31206, 31953) }, { CDF2( 4862, 32121) }, + { CDF2( 770, 1152) }, { CDF2(20889, 25637) }, + }, { + { CDF2(31910, 32724) }, { CDF2( 4120, 32712) }, + { CDF2( 305, 2247) }, { CDF2(27403, 32636) }, + { CDF2(31022, 32009) }, { CDF2( 2963, 32093) }, + { CDF2( 601, 943) }, { CDF2(14969, 21398) }, + }, + }, .newmv_mode = { + { CDF1(24035) }, { CDF1(16630) }, { CDF1(15339) }, + { CDF1( 8386) }, { CDF1(12222) }, { CDF1( 4676) }, + }, .globalmv_mode = { + { CDF1( 2175) }, { CDF1( 1054) }, + }, .refmv_mode = { + { CDF1(23974) }, { CDF1(24188) }, { CDF1(17848) }, + { CDF1(28622) }, { CDF1(24312) }, { CDF1(19923) }, + }, .drl_bit = { + { CDF1(13104) }, { CDF1(24560) }, { CDF1(18945) }, + }, .comp_inter_mode = { + { CDF7( 7760, 13823, 15808, 17641, 19156, 20666, 26891) }, + { CDF7(10730, 19452, 21145, 22749, 24039, 25131, 28724) }, + { CDF7(10664, 20221, 21588, 22906, 24295, 25387, 28436) }, + { CDF7(13298, 16984, 20471, 24182, 25067, 25736, 26422) }, + { CDF7(18904, 23325, 25242, 27432, 27898, 28258, 30758) }, + { CDF7(10725, 17454, 20124, 22820, 24195, 25168, 26046) }, + { CDF7(17125, 24273, 25814, 27492, 28214, 28704, 30592) }, + { CDF7(13046, 23214, 24505, 25942, 27435, 28442, 29330) }, + }, .intra = { + { CDF1( 806) }, { CDF1(16662) }, { CDF1(20186) }, + { CDF1(26538) }, + }, .comp = { + { CDF1(26828) }, { CDF1(24035) }, { CDF1(12031) }, + { CDF1(10640) }, { CDF1( 2901) }, + }, .comp_dir = { + { CDF1( 1198) }, { CDF1( 2070) }, { CDF1( 9166) }, + { CDF1( 7499) }, { CDF1(22475) }, + }, .jnt_comp = { + { CDF1(18244) }, { CDF1(12865) }, { CDF1( 7053) }, + { CDF1(13259) }, { CDF1( 9334) }, { CDF1( 4644) }, + }, .mask_comp = { + { CDF1(26607) }, { CDF1(22891) }, { CDF1(18840) }, + { CDF1(24594) }, { CDF1(19934) }, { CDF1(22674) }, + }, .wedge_comp = { + { CDF1(23431) }, { CDF1(13171) }, { CDF1(11470) }, + { CDF1( 9770) }, { CDF1( 9100) }, { CDF1( 8233) }, + { CDF1( 6172) }, { CDF1(11820) }, { CDF1( 7701) }, + }, .wedge_idx = { + { CDF15( 2438, 4440, 6599, 8663, 11005, 12874, 15751, 18094, + 20359, 22362, 24127, 25702, 27752, 29450, 31171) }, + { CDF15( 806, 3266, 6005, 6738, 7218, 7367, 7771, 14588, + 16323, 17367, 18452, 19422, 22839, 26127, 29629) }, + { CDF15( 2779, 3738, 4683, 7213, 7775, 8017, 8655, 14357, + 17939, 21332, 24520, 27470, 29456, 30529, 31656) }, + { CDF15( 1684, 3625, 5675, 7108, 9302, 11274, 14429, 17144, + 19163, 20961, 22884, 24471, 26719, 28714, 30877) }, + { CDF15( 1142, 3491, 6277, 7314, 8089, 8355, 9023, 13624, + 15369, 16730, 18114, 19313, 22521, 26012, 29550) }, + { CDF15( 2742, 4195, 5727, 8035, 8980, 9336, 10146, 14124, + 17270, 20533, 23434, 25972, 27944, 29570, 31416) }, + { CDF15( 1727, 3948, 6101, 7796, 9841, 12344, 15766, 18944, + 20638, 22038, 23963, 25311, 26988, 28766, 31012) }, + { CDF15( 154, 987, 1925, 2051, 2088, 2111, 2151, 23033, + 23703, 24284, 24985, 25684, 27259, 28883, 30911) }, + { CDF15( 1135, 1322, 1493, 2635, 2696, 2737, 2770, 21016, + 22935, 25057, 27251, 29173, 30089, 30960, 31933) }, + }, .interintra = { + { CDF1(16384) }, { CDF1(26887) }, { CDF1(27597) }, + { CDF1(30237) }, + }, .interintra_mode = { + { CDF3(8192, 16384, 24576) }, + { CDF3(1875, 11082, 27332) }, + { CDF3(2473, 9996, 26388) }, + { CDF3(4238, 11537, 25926) }, + }, .interintra_wedge = { + { CDF1(20036) }, { CDF1(24957) }, { CDF1(26704) }, + { CDF1(27530) }, { CDF1(29564) }, { CDF1(29444) }, + { CDF1(26872) }, + }, .ref = { + { { CDF1( 4897) }, { CDF1(16973) }, { CDF1(29744) } }, + { { CDF1( 1555) }, { CDF1(16751) }, { CDF1(30279) } }, + { { CDF1( 4236) }, { CDF1(19647) }, { CDF1(31194) } }, + { { CDF1( 8650) }, { CDF1(24773) }, { CDF1(31895) } }, + { { CDF1( 904) }, { CDF1(11014) }, { CDF1(26875) } }, + { { CDF1( 1444) }, { CDF1(15087) }, { CDF1(30304) } }, + }, .comp_fwd_ref = { + { { CDF1( 4946) }, { CDF1(19891) }, { CDF1(30731) } }, + { { CDF1( 9468) }, { CDF1(22441) }, { CDF1(31059) } }, + { { CDF1( 1503) }, { CDF1(15160) }, { CDF1(27544) } }, + }, .comp_bwd_ref = { + { { CDF1( 2235) }, { CDF1(17182) }, { CDF1(30606) } }, + { { CDF1( 1423) }, { CDF1(15175) }, { CDF1(30489) } }, + }, .comp_uni_ref = { + { { CDF1( 5284) }, { CDF1(23152) }, { CDF1(31774) } }, + { { CDF1( 3865) }, { CDF1(14173) }, { CDF1(25120) } }, + { { CDF1( 3128) }, { CDF1(15270) }, { CDF1(26710) } }, + }, .txsz = { + { + { CDF1(19968) }, { CDF1(19968) }, { CDF1(24320) }, + }, { + { CDF2(12272, 30172) }, { CDF2(12272, 30172) }, + { CDF2(18677, 30848) }, + }, { + { CDF2(12986, 15180) }, { CDF2(12986, 15180) }, + { CDF2(24302, 25602) }, + }, { + { CDF2( 5782, 11475) }, { CDF2( 5782, 11475) }, + { CDF2(16803, 22759) }, + }, + }, .txpart = { + { { CDF1(28581) }, { CDF1(23846) }, { CDF1(20847) } }, + { { CDF1(24315) }, { CDF1(18196) }, { CDF1(12133) } }, + { { CDF1(18791) }, { CDF1(10887) }, { CDF1(11005) } }, + { { CDF1(27179) }, { CDF1(20004) }, { CDF1(11281) } }, + { { CDF1(26549) }, { CDF1(19308) }, { CDF1(14224) } }, + { { CDF1(28015) }, { CDF1(21546) }, { CDF1(14400) } }, + { { CDF1(28165) }, { CDF1(22401) }, { CDF1(16088) } }, + }, .txtp_inter1 = { + { CDF15( 4458, 5560, 7695, 9709, 13330, 14789, 17537, 20266, + 21504, 22848, 23934, 25474, 27727, 28915, 30631) }, + { CDF15( 1645, 2573, 4778, 5711, 7807, 8622, 10522, 15357, + 17674, 20408, 22517, 25010, 27116, 28856, 30749) }, + }, .txtp_inter2 = { + CDF11( 770, 2421, 5225, 12907, 15819, 18927, + 21561, 24089, 26595, 28526, 30529) + }, .txtp_inter3 = { + { CDF1(16384) }, { CDF1( 4167) }, { CDF1( 1998) }, { CDF1( 748) }, + }, .txtp_intra1 = { + { + { CDF6( 1535, 8035, 9461, 12751, 23467, 27825) }, + { CDF6( 564, 3335, 9709, 10870, 18143, 28094) }, + { CDF6( 672, 3247, 3676, 11982, 19415, 23127) }, + { CDF6( 5279, 13885, 15487, 18044, 23527, 30252) }, + { CDF6( 4423, 6074, 7985, 10416, 25693, 29298) }, + { CDF6( 1486, 4241, 9460, 10662, 16456, 27694) }, + { CDF6( 439, 2838, 3522, 6737, 18058, 23754) }, + { CDF6( 1190, 4233, 4855, 11670, 20281, 24377) }, + { CDF6( 1045, 4312, 8647, 10159, 18644, 29335) }, + { CDF6( 202, 3734, 4747, 7298, 17127, 24016) }, + { CDF6( 447, 4312, 6819, 8884, 16010, 23858) }, + { CDF6( 277, 4369, 5255, 8905, 16465, 22271) }, + { CDF6( 3409, 5436, 10599, 15599, 19687, 24040) }, + }, { + { CDF6( 1870, 13742, 14530, 16498, 23770, 27698) }, + { CDF6( 326, 8796, 14632, 15079, 19272, 27486) }, + { CDF6( 484, 7576, 7712, 14443, 19159, 22591) }, + { CDF6( 1126, 15340, 15895, 17023, 20896, 30279) }, + { CDF6( 655, 4854, 5249, 5913, 22099, 27138) }, + { CDF6( 1299, 6458, 8885, 9290, 14851, 25497) }, + { CDF6( 311, 5295, 5552, 6885, 16107, 22672) }, + { CDF6( 883, 8059, 8270, 11258, 17289, 21549) }, + { CDF6( 741, 7580, 9318, 10345, 16688, 29046) }, + { CDF6( 110, 7406, 7915, 9195, 16041, 23329) }, + { CDF6( 363, 7974, 9357, 10673, 15629, 24474) }, + { CDF6( 153, 7647, 8112, 9936, 15307, 19996) }, + { CDF6( 3511, 6332, 11165, 15335, 19323, 23594) }, + }, + }, .txtp_intra2 = { + { + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + }, { + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + { CDF4( 6554, 13107, 19661, 26214) }, + }, { + { CDF4( 1127, 12814, 22772, 27483) }, + { CDF4( 145, 6761, 11980, 26667) }, + { CDF4( 362, 5887, 11678, 16725) }, + { CDF4( 385, 15213, 18587, 30693) }, + { CDF4( 25, 2914, 23134, 27903) }, + { CDF4( 60, 4470, 11749, 23991) }, + { CDF4( 37, 3332, 14511, 21448) }, + { CDF4( 157, 6320, 13036, 17439) }, + { CDF4( 119, 6719, 12906, 29396) }, + { CDF4( 47, 5537, 12576, 21499) }, + { CDF4( 269, 6076, 11258, 23115) }, + { CDF4( 83, 5615, 12001, 17228) }, + { CDF4( 1968, 5556, 12023, 18547) }, + }, + }, .skip = { + { CDF1(31671) }, { CDF1(16515) }, { CDF1( 4576) }, + }, .skip_mode = { + { CDF1(32621) }, { CDF1(20708) }, { CDF1( 8127) }, + }, .partition = { + { + // 128x128 -> 64x64 + { CDF7(27899, 28219, 28529, 32484, 32539, 32619, 32639) }, + { CDF7( 6607, 6990, 8268, 32060, 32219, 32338, 32371) }, + { CDF7( 5429, 6676, 7122, 32027, 32227, 32531, 32582) }, + { CDF7( 711, 966, 1172, 32448, 32538, 32617, 32664) }, + }, { + // 64x64 -> 32x32 + { CDF9(20137, 21547, 23078, 29566, 29837, + 30261, 30524, 30892, 31724) }, + { CDF9( 6732, 7490, 9497, 27944, 28250, + 28515, 28969, 29630, 30104) }, + { CDF9( 5945, 7663, 8348, 28683, 29117, + 29749, 30064, 30298, 32238) }, + { CDF9( 870, 1212, 1487, 31198, 31394, + 31574, 31743, 31881, 32332) }, + }, { + // 32x32 -> 16x16 + { CDF9(18462, 20920, 23124, 27647, 28227, + 29049, 29519, 30178, 31544) }, + { CDF9( 7689, 9060, 12056, 24992, 25660, + 26182, 26951, 28041, 29052) }, + { CDF9( 6015, 9009, 10062, 24544, 25409, + 26545, 27071, 27526, 32047) }, + { CDF9( 1394, 2208, 2796, 28614, 29061, + 29466, 29840, 30185, 31899) }, + }, { + // 16x16 -> 8x8 + { CDF9(15597, 20929, 24571, 26706, 27664, + 28821, 29601, 30571, 31902) }, + { CDF9( 7925, 11043, 16785, 22470, 23971, + 25043, 26651, 28701, 29834) }, + { CDF9( 5414, 13269, 15111, 20488, 22360, + 24500, 25537, 26336, 32117) }, + { CDF9( 2662, 6362, 8614, 20860, 23053, + 24778, 26436, 27829, 31171) }, + }, { + // 8x8 -> 4x4 only supports the four legacy partition types + { CDF3(19132, 25510, 30392) }, + { CDF3(13928, 19855, 28540) }, + { CDF3(12522, 23679, 28629) }, + { CDF3( 9896, 18783, 25853) }, + }, + }, .seg_pred = { + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, .seg_id = { + { CDF7( 5622, 7893, 16093, 18233, 27809, 28373, 32533) }, + { CDF7(14274, 18230, 22557, 24935, 29980, 30851, 32344) }, + { CDF7(27527, 28487, 28723, 28890, 32397, 32647, 32679) }, + }, .cfl_sign = { + CDF7( 1418, 2123, 13340, 18405, 26972, 28343, 32294) + }, .cfl_alpha = { + { CDF15( 7637, 20719, 31401, 32481, 32657, 32688, 32692, 32696, + 32700, 32704, 32708, 32712, 32716, 32720, 32724) }, + { CDF15(14365, 23603, 28135, 31168, 32167, 32395, 32487, 32573, + 32620, 32647, 32668, 32672, 32676, 32680, 32684) }, + { CDF15(11532, 22380, 28445, 31360, 32349, 32523, 32584, 32649, + 32673, 32677, 32681, 32685, 32689, 32693, 32697) }, + { CDF15(26990, 31402, 32282, 32571, 32692, 32696, 32700, 32704, + 32708, 32712, 32716, 32720, 32724, 32728, 32732) }, + { CDF15(17248, 26058, 28904, 30608, 31305, 31877, 32126, 32321, + 32394, 32464, 32516, 32560, 32576, 32593, 32622) }, + { CDF15(14738, 21678, 25779, 27901, 29024, 30302, 30980, 31843, + 32144, 32413, 32520, 32594, 32622, 32656, 32660) }, + }, .restore_wiener = { + CDF1(11570) + }, .restore_sgrproj = { + CDF1(16855) + }, .restore_switchable = { + CDF2( 9413, 22581) + }, .delta_q = { + CDF3(28160, 32120, 32677) + }, .delta_lf = { + { CDF3(28160, 32120, 32677) }, + { CDF3(28160, 32120, 32677) }, + { CDF3(28160, 32120, 32677) }, + { CDF3(28160, 32120, 32677) }, + { CDF3(28160, 32120, 32677) }, + }, .motion_mode = { + [BS_8x8] = { CDF2( 7651, 24760) }, + [BS_8x16] = { CDF2( 4738, 24765) }, + [BS_8x32] = { CDF2(28799, 31390) }, + [BS_16x8] = { CDF2( 5391, 25528) }, + [BS_16x16] = { CDF2(19419, 26810) }, + [BS_16x32] = { CDF2( 5123, 23606) }, + [BS_16x64] = { CDF2(28973, 31594) }, + [BS_32x8] = { CDF2(26431, 30774) }, + [BS_32x16] = { CDF2(11606, 24308) }, + [BS_32x32] = { CDF2(26260, 29116) }, + [BS_32x64] = { CDF2(20360, 28062) }, + [BS_64x16] = { CDF2(29742, 31203) }, + [BS_64x32] = { CDF2(21679, 26830) }, + [BS_64x64] = { CDF2(29516, 30701) }, + [BS_64x128] = { CDF2(28898, 30397) }, + [BS_128x64] = { CDF2(30878, 31335) }, + [BS_128x128] = { CDF2(32507, 32558) }, + }, .obmc = { + [BS_8x8] = { CDF1(10437) }, + [BS_8x16] = { CDF1( 9371) }, + [BS_8x32] = { CDF1(23664) }, + [BS_16x8] = { CDF1( 9301) }, + [BS_16x16] = { CDF1(17432) }, + [BS_16x32] = { CDF1(14423) }, + [BS_16x64] = { CDF1(24008) }, + [BS_32x8] = { CDF1(20901) }, + [BS_32x16] = { CDF1(15142) }, + [BS_32x32] = { CDF1(25817) }, + [BS_32x64] = { CDF1(22823) }, + [BS_64x16] = { CDF1(26879) }, + [BS_64x32] = { CDF1(22083) }, + [BS_64x64] = { CDF1(30128) }, + [BS_64x128] = { CDF1(31014) }, + [BS_128x64] = { CDF1(31560) }, + [BS_128x128] = { CDF1(32638) }, + }, .pal_y = { + { { CDF1(31676) }, { CDF1( 3419) }, { CDF1( 1261) } }, + { { CDF1(31912) }, { CDF1( 2859) }, { CDF1( 980) } }, + { { CDF1(31823) }, { CDF1( 3400) }, { CDF1( 781) } }, + { { CDF1(32030) }, { CDF1( 3561) }, { CDF1( 904) } }, + { { CDF1(32309) }, { CDF1( 7337) }, { CDF1( 1462) } }, + { { CDF1(32265) }, { CDF1( 4015) }, { CDF1( 1521) } }, + { { CDF1(32450) }, { CDF1( 7946) }, { CDF1( 129) } }, + }, .pal_sz = { + { + { CDF6( 7952, 13000, 18149, 21478, 25527, 29241) }, + { CDF6( 7139, 11421, 16195, 19544, 23666, 28073) }, + { CDF6( 7788, 12741, 17325, 20500, 24315, 28530) }, + { CDF6( 8271, 14064, 18246, 21564, 25071, 28533) }, + { CDF6(12725, 19180, 21863, 24839, 27535, 30120) }, + { CDF6( 9711, 14888, 16923, 21052, 25661, 27875) }, + { CDF6(14940, 20797, 21678, 24186, 27033, 28999) }, + }, { + { CDF6( 8713, 19979, 27128, 29609, 31331, 32272) }, + { CDF6( 5839, 15573, 23581, 26947, 29848, 31700) }, + { CDF6( 4426, 11260, 17999, 21483, 25863, 29430) }, + { CDF6( 3228, 9464, 14993, 18089, 22523, 27420) }, + { CDF6( 3768, 8886, 13091, 17852, 22495, 27207) }, + { CDF6( 2464, 8451, 12861, 21632, 25525, 28555) }, + { CDF6( 1269, 5435, 10433, 18963, 21700, 25865) }, + }, + }, .pal_uv = { + { CDF1(32461) }, { CDF1(21488) }, + }, .color_map = { + { /* y */ + { + { CDF1(28710) }, { CDF1(16384) }, { CDF1(10553) }, + { CDF1(27036) }, { CDF1(31603) }, + }, { + { CDF2(27877, 30490) }, { CDF2(11532, 25697) }, + { CDF2( 6544, 30234) }, { CDF2(23018, 28072) }, + { CDF2(31915, 32385) }, + }, { + { CDF3(25572, 28046, 30045) }, + { CDF3( 9478, 21590, 27256) }, + { CDF3( 7248, 26837, 29824) }, + { CDF3(19167, 24486, 28349) }, + { CDF3(31400, 31825, 32250) }, + }, { + { CDF4(24779, 26955, 28576, 30282) }, + { CDF4( 8669, 20364, 24073, 28093) }, + { CDF4( 4255, 27565, 29377, 31067) }, + { CDF4(19864, 23674, 26716, 29530) }, + { CDF4(31646, 31893, 32147, 32426) }, + }, { + { CDF5(23132, 25407, 26970, 28435, 30073) }, + { CDF5( 7443, 17242, 20717, 24762, 27982) }, + { CDF5( 6300, 24862, 26944, 28784, 30671) }, + { CDF5(18916, 22895, 25267, 27435, 29652) }, + { CDF5(31270, 31550, 31808, 32059, 32353) }, + }, { + { CDF6(23105, 25199, 26464, 27684, 28931, 30318) }, + { CDF6( 6950, 15447, 18952, 22681, 25567, 28563) }, + { CDF6( 7560, 23474, 25490, 27203, 28921, 30708) }, + { CDF6(18544, 22373, 24457, 26195, 28119, 30045) }, + { CDF6(31198, 31451, 31670, 31882, 32123, 32391) }, + }, { + { CDF7(21689, 23883, 25163, 26352, 27506, 28827, 30195) }, + { CDF7( 6892, 15385, 17840, 21606, 24287, 26753, 29204) }, + { CDF7( 5651, 23182, 25042, 26518, 27982, 29392, 30900) }, + { CDF7(19349, 22578, 24418, 25994, 27524, 29031, 30448) }, + { CDF7(31028, 31270, 31504, 31705, 31927, 32153, 32392) }, + }, + }, { /* uv */ + { + { CDF1(29089) }, { CDF1(16384) }, { CDF1( 8713) }, + { CDF1(29257) }, { CDF1(31610) }, + }, { + { CDF2(25257, 29145) }, { CDF2(12287, 27293) }, + { CDF2( 7033, 27960) }, { CDF2(20145, 25405) }, + { CDF2(30608, 31639) }, + }, { + { CDF3(24210, 27175, 29903) }, + { CDF3( 9888, 22386, 27214) }, + { CDF3( 5901, 26053, 29293) }, + { CDF3(18318, 22152, 28333) }, + { CDF3(30459, 31136, 31926) }, + }, { + { CDF4(22980, 25479, 27781, 29986) }, + { CDF4( 8413, 21408, 24859, 28874) }, + { CDF4( 2257, 29449, 30594, 31598) }, + { CDF4(19189, 21202, 25915, 28620) }, + { CDF4(31844, 32044, 32281, 32518) }, + }, { + { CDF5(22217, 24567, 26637, 28683, 30548) }, + { CDF5( 7307, 16406, 19636, 24632, 28424) }, + { CDF5( 4441, 25064, 26879, 28942, 30919) }, + { CDF5(17210, 20528, 23319, 26750, 29582) }, + { CDF5(30674, 30953, 31396, 31735, 32207) }, + }, { + { CDF6(21239, 23168, 25044, 26962, 28705, 30506) }, + { CDF6( 6545, 15012, 18004, 21817, 25503, 28701) }, + { CDF6( 3448, 26295, 27437, 28704, 30126, 31442) }, + { CDF6(15889, 18323, 21704, 24698, 26976, 29690) }, + { CDF6(30988, 31204, 31479, 31734, 31983, 32325) }, + }, { + { CDF7(21442, 23288, 24758, 26246, 27649, 28980, 30563) }, + { CDF7( 5863, 14933, 17552, 20668, 23683, 26411, 29273) }, + { CDF7( 3415, 25810, 26877, 27990, 29223, 30394, 31618) }, + { CDF7(17965, 20084, 22232, 23974, 26274, 28402, 30390) }, + { CDF7(31190, 31329, 31516, 31679, 31825, 32026, 32322) }, + }, + }, + }, .intrabc = { + CDF1(30531) + }, + }, .mv = { + .comp = { + .classes = { + CDF10(28672, 30976, 31858, 32320, 32551, + 32656, 32740, 32757, 32762, 32767) + }, .class0 = { + CDF1(27648) + }, .classN = { + { CDF1(17408) }, { CDF1(17920) }, { CDF1(18944) }, + { CDF1(20480) }, { CDF1(22528) }, { CDF1(24576) }, + { CDF1(28672) }, { CDF1(29952) }, { CDF1(29952) }, + { CDF1(30720) }, + }, .class0_fp = { + { CDF3(16384, 24576, 26624) }, + { CDF3(12288, 21248, 24128) }, + }, .classN_fp = { + CDF3( 8192, 17408, 21248) + }, .class0_hp = { + CDF1(20480) + }, .classN_hp = { + CDF1(16384) + }, .sign = { + CDF1(16384) + }, + }, .joint = { + CDF3( 4096, 11264, 19328) + }, + }, .kfym = { + { + { CDF12(15588, 17027, 19338, 20218, 20682, 21110, + 21825, 23244, 24189, 28165, 29093, 30466) }, + { CDF12(12016, 18066, 19516, 20303, 20719, 21444, + 21888, 23032, 24434, 28658, 30172, 31409) }, + { CDF12(10052, 10771, 22296, 22788, 23055, 23239, + 24133, 25620, 26160, 29336, 29929, 31567) }, + { CDF12(14091, 15406, 16442, 18808, 19136, 19546, + 19998, 22096, 24746, 29585, 30958, 32462) }, + { CDF12(12122, 13265, 15603, 16501, 18609, 20033, + 22391, 25583, 26437, 30261, 31073, 32475) }, + }, { + { CDF12(10023, 19585, 20848, 21440, 21832, 22760, + 23089, 24023, 25381, 29014, 30482, 31436) }, + { CDF12( 5983, 24099, 24560, 24886, 25066, 25795, + 25913, 26423, 27610, 29905, 31276, 31794) }, + { CDF12( 7444, 12781, 20177, 20728, 21077, 21607, + 22170, 23405, 24469, 27915, 29090, 30492) }, + { CDF12( 8537, 14689, 15432, 17087, 17408, 18172, + 18408, 19825, 24649, 29153, 31096, 32210) }, + { CDF12( 7543, 14231, 15496, 16195, 17905, 20717, + 21984, 24516, 26001, 29675, 30981, 31994) }, + }, { + { CDF12(12613, 13591, 21383, 22004, 22312, 22577, + 23401, 25055, 25729, 29538, 30305, 32077) }, + { CDF12( 9687, 13470, 18506, 19230, 19604, 20147, + 20695, 22062, 23219, 27743, 29211, 30907) }, + { CDF12( 6183, 6505, 26024, 26252, 26366, 26434, + 27082, 28354, 28555, 30467, 30794, 32086) }, + { CDF12(10718, 11734, 14954, 17224, 17565, 17924, + 18561, 21523, 23878, 28975, 30287, 32252) }, + { CDF12( 9194, 9858, 16501, 17263, 18424, 19171, + 21563, 25961, 26561, 30072, 30737, 32463) }, + }, { + { CDF12(12602, 14399, 15488, 18381, 18778, 19315, + 19724, 21419, 25060, 29696, 30917, 32409) }, + { CDF12( 8203, 13821, 14524, 17105, 17439, 18131, + 18404, 19468, 25225, 29485, 31158, 32342) }, + { CDF12( 8451, 9731, 15004, 17643, 18012, 18425, + 19070, 21538, 24605, 29118, 30078, 32018) }, + { CDF12( 7714, 9048, 9516, 16667, 16817, 16994, + 17153, 18767, 26743, 30389, 31536, 32528) }, + { CDF12( 8843, 10280, 11496, 15317, 16652, 17943, + 19108, 22718, 25769, 29953, 30983, 32485) }, + }, { + { CDF12(12578, 13671, 15979, 16834, 19075, 20913, + 22989, 25449, 26219, 30214, 31150, 32477) }, + { CDF12( 9563, 13626, 15080, 15892, 17756, 20863, + 22207, 24236, 25380, 29653, 31143, 32277) }, + { CDF12( 8356, 8901, 17616, 18256, 19350, 20106, + 22598, 25947, 26466, 29900, 30523, 32261) }, + { CDF12(10835, 11815, 13124, 16042, 17018, 18039, + 18947, 22753, 24615, 29489, 30883, 32482) }, + { CDF12( 7618, 8288, 9859, 10509, 15386, 18657, + 22903, 28776, 29180, 31355, 31802, 32593) }, + }, + }, +}; + +static const CdfCoefContext default_coef_cdf[4] = { + [0] = { + .skip = { + { + { CDF1(31849) }, { CDF1( 5892) }, { CDF1(12112) }, + { CDF1(21935) }, { CDF1(20289) }, { CDF1(27473) }, + { CDF1(32487) }, { CDF1( 7654) }, { CDF1(19473) }, + { CDF1(29984) }, { CDF1( 9961) }, { CDF1(30242) }, + { CDF1(32117) }, + }, { + { CDF1(31548) }, { CDF1( 1549) }, { CDF1(10130) }, + { CDF1(16656) }, { CDF1(18591) }, { CDF1(26308) }, + { CDF1(32537) }, { CDF1( 5403) }, { CDF1(18096) }, + { CDF1(30003) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(29957) }, { CDF1( 5391) }, { CDF1(18039) }, + { CDF1(23566) }, { CDF1(22431) }, { CDF1(25822) }, + { CDF1(32197) }, { CDF1( 3778) }, { CDF1(15336) }, + { CDF1(28981) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(17920) }, { CDF1( 1818) }, { CDF1( 7282) }, + { CDF1(25273) }, { CDF1(10923) }, { CDF1(31554) }, + { CDF1(32624) }, { CDF1( 1366) }, { CDF1(15628) }, + { CDF1(30462) }, { CDF1( 146) }, { CDF1( 5132) }, + { CDF1(31657) }, + }, { + { CDF1( 6308) }, { CDF1( 117) }, { CDF1( 1638) }, + { CDF1( 2161) }, { CDF1(16384) }, { CDF1(10923) }, + { CDF1(30247) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, + }, .eob_bin_16 = { + { + { CDF4( 840, 1039, 1980, 4895) }, + { CDF4( 370, 671, 1883, 4471) }, + }, { + { CDF4( 3247, 4950, 9688, 14563) }, + { CDF4( 1904, 3354, 7763, 14647) }, + }, + }, .eob_bin_32 = { + { + { CDF5( 400, 520, 977, 2102, 6542) }, + { CDF5( 210, 405, 1315, 3326, 7537) }, + }, { + { CDF5( 2636, 4273, 7588, 11794, 20401) }, + { CDF5( 1786, 3179, 6902, 11357, 19054) }, + }, + }, .eob_bin_64 = { + { + { CDF6( 329, 498, 1101, 1784, 3265, 7758) }, + { CDF6( 335, 730, 1459, 5494, 8755, 12997) }, + }, { + { CDF6( 3505, 5304, 10086, 13814, 17684, 23370) }, + { CDF6( 1563, 2700, 4876, 10911, 14706, 22480) }, + }, + }, .eob_bin_128 = { + { + { CDF7( 219, 482, 1140, 2091, 3680, 6028, 12586) }, + { CDF7( 371, 699, 1254, 4830, 9479, 12562, 17497) }, + }, { + { CDF7( 5245, 7456, 12880, 15852, 20033, 23932, 27608) }, + { CDF7( 2054, 3472, 5869, 14232, 18242, 20590, 26752) }, + }, + }, .eob_bin_256 = { + { + { CDF8( 310, 584, 1887, 3589, + 6168, 8611, 11352, 15652) }, + { CDF8( 998, 1850, 2998, 5604, + 17341, 19888, 22899, 25583) }, + }, { + { CDF8( 2520, 3240, 5952, 8870, + 12577, 17558, 19954, 24168) }, + { CDF8( 2203, 4130, 7435, 10739, + 20652, 23681, 25609, 27261) }, + }, + }, .eob_bin_512 = { + { CDF9( 641, 983, 3707, 5430, 10234, + 14958, 18788, 23412, 26061) }, + { CDF9( 5095, 6446, 9996, 13354, 16017, + 17986, 20919, 26129, 29140) }, + }, .eob_bin_1024 = { + { CDF10( 393, 421, 751, 1623, 3160, + 6352, 13345, 18047, 22571, 25830) }, + { CDF10( 1865, 1988, 2930, 4242, 10533, + 16538, 21354, 27255, 28546, 31784) }, + }, .eob_hi_bit = { + { + { + { CDF1(16961) }, { CDF1(17223) }, { CDF1( 7621) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(19069) }, { CDF1(22525) }, { CDF1(13377) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(20401) }, { CDF1(17025) }, { CDF1(12845) }, + { CDF1(12873) }, { CDF1(14094) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(20681) }, { CDF1(20701) }, { CDF1(15250) }, + { CDF1(15017) }, { CDF1(14928) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(23905) }, { CDF1(17194) }, { CDF1(16170) }, + { CDF1(17695) }, { CDF1(13826) }, { CDF1(15810) }, + { CDF1(12036) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(23959) }, { CDF1(20799) }, { CDF1(19021) }, + { CDF1(16203) }, { CDF1(17886) }, { CDF1(14144) }, + { CDF1(12010) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(27399) }, { CDF1(16327) }, { CDF1(18071) }, + { CDF1(19584) }, { CDF1(20721) }, { CDF1(18432) }, + { CDF1(19560) }, { CDF1(10150) }, { CDF1( 8805) }, + }, { + { CDF1(24932) }, { CDF1(20833) }, { CDF1(12027) }, + { CDF1(16670) }, { CDF1(19914) }, { CDF1(15106) }, + { CDF1(17662) }, { CDF1(13783) }, { CDF1(28756) }, + }, + }, { + { + { CDF1(23406) }, { CDF1(21845) }, { CDF1(18432) }, + { CDF1(16384) }, { CDF1(17096) }, { CDF1(12561) }, + { CDF1(17320) }, { CDF1(22395) }, { CDF1(21370) }, + }, { + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, + }, .eob_base_tok = { + { + { + { CDF2(17837, 29055) }, { CDF2(29600, 31446) }, + { CDF2(30844, 31878) }, { CDF2(24926, 28948) }, + }, { + { CDF2(21365, 30026) }, { CDF2(30512, 32423) }, + { CDF2(31658, 32621) }, { CDF2(29630, 31881) }, + }, + }, { + { + { CDF2( 5717, 26477) }, { CDF2(30491, 31703) }, + { CDF2(31550, 32158) }, { CDF2(29648, 31491) }, + }, { + { CDF2(12608, 27820) }, { CDF2(30680, 32225) }, + { CDF2(30809, 32335) }, { CDF2(31299, 32423) }, + }, + }, { + { + { CDF2( 1786, 12612) }, { CDF2(30663, 31625) }, + { CDF2(32339, 32468) }, { CDF2(31148, 31833) }, + }, { + { CDF2(18857, 23865) }, { CDF2(31428, 32428) }, + { CDF2(31744, 32373) }, { CDF2(31775, 32526) }, + }, + }, { + { + { CDF2( 1787, 2532) }, { CDF2(30832, 31662) }, + { CDF2(31824, 32682) }, { CDF2(32133, 32569) }, + }, { + { CDF2(13751, 22235) }, { CDF2(32089, 32409) }, + { CDF2(27084, 27920) }, { CDF2(29291, 32594) }, + }, + }, { + { + { CDF2( 1725, 3449) }, { CDF2(31102, 31935) }, + { CDF2(32457, 32613) }, { CDF2(32412, 32649) }, + }, { + { CDF2(10923, 21845) }, { CDF2(10923, 21845) }, + { CDF2(10923, 21845) }, { CDF2(10923, 21845) }, + }, + }, + }, .base_tok = { + { + { + { CDF3( 4034, 8930, 12727) }, + { CDF3(18082, 29741, 31877) }, + { CDF3(12596, 26124, 30493) }, + { CDF3( 9446, 21118, 27005) }, + { CDF3( 6308, 15141, 21279) }, + { CDF3( 2463, 6357, 9783) }, + { CDF3(20667, 30546, 31929) }, + { CDF3(13043, 26123, 30134) }, + { CDF3( 8151, 18757, 24778) }, + { CDF3( 5255, 12839, 18632) }, + { CDF3( 2820, 7206, 11161) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3(15736, 27553, 30604) }, + { CDF3(11210, 23794, 28787) }, + { CDF3( 5947, 13874, 19701) }, + { CDF3( 4215, 9323, 13891) }, + { CDF3( 2833, 6462, 10059) }, + { CDF3(19605, 30393, 31582) }, + { CDF3(13523, 26252, 30248) }, + { CDF3( 8446, 18622, 24512) }, + { CDF3( 3818, 10343, 15974) }, + { CDF3( 1481, 4117, 6796) }, + { CDF3(22649, 31302, 32190) }, + { CDF3(14829, 27127, 30449) }, + { CDF3( 8313, 17702, 23304) }, + { CDF3( 3022, 8301, 12786) }, + { CDF3( 1536, 4412, 7184) }, + { CDF3(22354, 29774, 31372) }, + { CDF3(14723, 25472, 29214) }, + { CDF3( 6673, 13745, 18662) }, + { CDF3( 2068, 5766, 9322) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3( 6302, 16444, 21761) }, + { CDF3(23040, 31538, 32475) }, + { CDF3(15196, 28452, 31496) }, + { CDF3(10020, 22946, 28514) }, + { CDF3( 6533, 16862, 23501) }, + { CDF3( 3538, 9816, 15076) }, + { CDF3(24444, 31875, 32525) }, + { CDF3(15881, 28924, 31635) }, + { CDF3( 9922, 22873, 28466) }, + { CDF3( 6527, 16966, 23691) }, + { CDF3( 4114, 11303, 17220) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3(20201, 30770, 32209) }, + { CDF3(14754, 28071, 31258) }, + { CDF3( 8378, 20186, 26517) }, + { CDF3( 5916, 15299, 21978) }, + { CDF3( 4268, 11583, 17901) }, + { CDF3(24361, 32025, 32581) }, + { CDF3(18673, 30105, 31943) }, + { CDF3(10196, 22244, 27576) }, + { CDF3( 5495, 14349, 20417) }, + { CDF3( 2676, 7415, 11498) }, + { CDF3(24678, 31958, 32585) }, + { CDF3(18629, 29906, 31831) }, + { CDF3( 9364, 20724, 26315) }, + { CDF3( 4641, 12318, 18094) }, + { CDF3( 2758, 7387, 11579) }, + { CDF3(25433, 31842, 32469) }, + { CDF3(18795, 29289, 31411) }, + { CDF3( 7644, 17584, 23592) }, + { CDF3( 3408, 9014, 15047) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, { + { + { CDF3( 4536, 10072, 14001) }, + { CDF3(25459, 31416, 32206) }, + { CDF3(16605, 28048, 30818) }, + { CDF3(11008, 22857, 27719) }, + { CDF3( 6915, 16268, 22315) }, + { CDF3( 2625, 6812, 10537) }, + { CDF3(24257, 31788, 32499) }, + { CDF3(16880, 29454, 31879) }, + { CDF3(11958, 25054, 29778) }, + { CDF3( 7916, 18718, 25084) }, + { CDF3( 3383, 8777, 13446) }, + { CDF3(22720, 31603, 32393) }, + { CDF3(14960, 28125, 31335) }, + { CDF3( 9731, 22210, 27928) }, + { CDF3( 6304, 15832, 22277) }, + { CDF3( 2910, 7818, 12166) }, + { CDF3(20375, 30627, 32131) }, + { CDF3(13904, 27284, 30887) }, + { CDF3( 9368, 21558, 27144) }, + { CDF3( 5937, 14966, 21119) }, + { CDF3( 2667, 7225, 11319) }, + { CDF3(23970, 31470, 32378) }, + { CDF3(17173, 29734, 32018) }, + { CDF3(12795, 25441, 29965) }, + { CDF3( 8981, 19680, 25893) }, + { CDF3( 4728, 11372, 16902) }, + { CDF3(24287, 31797, 32439) }, + { CDF3(16703, 29145, 31696) }, + { CDF3(10833, 23554, 28725) }, + { CDF3( 6468, 16566, 23057) }, + { CDF3( 2415, 6562, 10278) }, + { CDF3(26610, 32395, 32659) }, + { CDF3(18590, 30498, 32117) }, + { CDF3(12420, 25756, 29950) }, + { CDF3( 7639, 18746, 24710) }, + { CDF3( 3001, 8086, 12347) }, + { CDF3(25076, 32064, 32580) }, + { CDF3(17946, 30128, 32028) }, + { CDF3(12024, 24985, 29378) }, + { CDF3( 7517, 18390, 24304) }, + { CDF3( 3243, 8781, 13331) }, + }, { + { CDF3( 6037, 16771, 21957) }, + { CDF3(24774, 31704, 32426) }, + { CDF3(16830, 28589, 31056) }, + { CDF3(10602, 22828, 27760) }, + { CDF3( 6733, 16829, 23071) }, + { CDF3( 3250, 8914, 13556) }, + { CDF3(25582, 32220, 32668) }, + { CDF3(18659, 30342, 32223) }, + { CDF3(12546, 26149, 30515) }, + { CDF3( 8420, 20451, 26801) }, + { CDF3( 4636, 12420, 18344) }, + { CDF3(27581, 32362, 32639) }, + { CDF3(18987, 30083, 31978) }, + { CDF3(11327, 24248, 29084) }, + { CDF3( 7264, 17719, 24120) }, + { CDF3( 3995, 10768, 16169) }, + { CDF3(25893, 31831, 32487) }, + { CDF3(16577, 28587, 31379) }, + { CDF3(10189, 22748, 28182) }, + { CDF3( 6832, 17094, 23556) }, + { CDF3( 3708, 10110, 15334) }, + { CDF3(25904, 32282, 32656) }, + { CDF3(19721, 30792, 32276) }, + { CDF3(12819, 26243, 30411) }, + { CDF3( 8572, 20614, 26891) }, + { CDF3( 5364, 14059, 20467) }, + { CDF3(26580, 32438, 32677) }, + { CDF3(20852, 31225, 32340) }, + { CDF3(12435, 25700, 29967) }, + { CDF3( 8691, 20825, 26976) }, + { CDF3( 4446, 12209, 17269) }, + { CDF3(27350, 32429, 32696) }, + { CDF3(21372, 30977, 32272) }, + { CDF3(12673, 25270, 29853) }, + { CDF3( 9208, 20925, 26640) }, + { CDF3( 5018, 13351, 18732) }, + { CDF3(27351, 32479, 32713) }, + { CDF3(21398, 31209, 32387) }, + { CDF3(12162, 25047, 29842) }, + { CDF3( 7896, 18691, 25319) }, + { CDF3( 4670, 12882, 18881) }, + }, + }, { + { + { CDF3( 5487, 10460, 13708) }, + { CDF3(21597, 28303, 30674) }, + { CDF3(11037, 21953, 26476) }, + { CDF3( 8147, 17962, 22952) }, + { CDF3( 5242, 13061, 18532) }, + { CDF3( 1889, 5208, 8182) }, + { CDF3(26774, 32133, 32590) }, + { CDF3(17844, 29564, 31767) }, + { CDF3(11690, 24438, 29171) }, + { CDF3( 7542, 18215, 24459) }, + { CDF3( 2993, 8050, 12319) }, + { CDF3(28023, 32328, 32591) }, + { CDF3(18651, 30126, 31954) }, + { CDF3(12164, 25146, 29589) }, + { CDF3( 7762, 18530, 24771) }, + { CDF3( 3492, 9183, 13920) }, + { CDF3(27591, 32008, 32491) }, + { CDF3(17149, 28853, 31510) }, + { CDF3(11485, 24003, 28860) }, + { CDF3( 7697, 18086, 24210) }, + { CDF3( 3075, 7999, 12218) }, + { CDF3(28268, 32482, 32654) }, + { CDF3(19631, 31051, 32404) }, + { CDF3(13860, 27260, 31020) }, + { CDF3( 9605, 21613, 27594) }, + { CDF3( 4876, 12162, 17908) }, + { CDF3(27248, 32316, 32576) }, + { CDF3(18955, 30457, 32075) }, + { CDF3(11824, 23997, 28795) }, + { CDF3( 7346, 18196, 24647) }, + { CDF3( 3403, 9247, 14111) }, + { CDF3(29711, 32655, 32735) }, + { CDF3(21169, 31394, 32417) }, + { CDF3(13487, 27198, 30957) }, + { CDF3( 8828, 21683, 27614) }, + { CDF3( 4270, 11451, 17038) }, + { CDF3(28708, 32578, 32731) }, + { CDF3(20120, 31241, 32482) }, + { CDF3(13692, 27550, 31321) }, + { CDF3( 9418, 22514, 28439) }, + { CDF3( 4999, 13283, 19462) }, + }, { + { CDF3( 5673, 14302, 19711) }, + { CDF3(26251, 30701, 31834) }, + { CDF3(12782, 23783, 27803) }, + { CDF3( 9127, 20657, 25808) }, + { CDF3( 6368, 16208, 21462) }, + { CDF3( 2465, 7177, 10822) }, + { CDF3(29961, 32563, 32719) }, + { CDF3(18318, 29891, 31949) }, + { CDF3(11361, 24514, 29357) }, + { CDF3( 7900, 19603, 25607) }, + { CDF3( 4002, 10590, 15546) }, + { CDF3(29637, 32310, 32595) }, + { CDF3(18296, 29913, 31809) }, + { CDF3(10144, 21515, 26871) }, + { CDF3( 5358, 14322, 20394) }, + { CDF3( 3067, 8362, 13346) }, + { CDF3(28652, 32470, 32676) }, + { CDF3(17538, 30771, 32209) }, + { CDF3(13924, 26882, 30494) }, + { CDF3(10496, 22837, 27869) }, + { CDF3( 7236, 16396, 21621) }, + { CDF3(30743, 32687, 32746) }, + { CDF3(23006, 31676, 32489) }, + { CDF3(14494, 27828, 31120) }, + { CDF3(10174, 22801, 28352) }, + { CDF3( 6242, 15281, 21043) }, + { CDF3(25817, 32243, 32720) }, + { CDF3(18618, 31367, 32325) }, + { CDF3(13997, 28318, 31878) }, + { CDF3(12255, 26534, 31383) }, + { CDF3( 9561, 21588, 28450) }, + { CDF3(28188, 32635, 32724) }, + { CDF3(22060, 32365, 32728) }, + { CDF3(18102, 30690, 32528) }, + { CDF3(14196, 28864, 31999) }, + { CDF3(12262, 25792, 30865) }, + { CDF3(24176, 32109, 32628) }, + { CDF3(18280, 29681, 31963) }, + { CDF3(10205, 23703, 29664) }, + { CDF3( 7889, 20025, 27676) }, + { CDF3( 6060, 16743, 23970) }, + }, + }, { + { + { CDF3( 5141, 7096, 8260) }, + { CDF3(27186, 29022, 29789) }, + { CDF3( 6668, 12568, 15682) }, + { CDF3( 2172, 6181, 8638) }, + { CDF3( 1126, 3379, 4531) }, + { CDF3( 443, 1361, 2254) }, + { CDF3(26083, 31153, 32436) }, + { CDF3(13486, 24603, 28483) }, + { CDF3( 6508, 14840, 19910) }, + { CDF3( 3386, 8800, 13286) }, + { CDF3( 1530, 4322, 7054) }, + { CDF3(29639, 32080, 32548) }, + { CDF3(15897, 27552, 30290) }, + { CDF3( 8588, 20047, 25383) }, + { CDF3( 4889, 13339, 19269) }, + { CDF3( 2240, 6871, 10498) }, + { CDF3(28165, 32197, 32517) }, + { CDF3(20735, 30427, 31568) }, + { CDF3(14325, 24671, 27692) }, + { CDF3( 5119, 12554, 17805) }, + { CDF3( 1810, 5441, 8261) }, + { CDF3(31212, 32724, 32748) }, + { CDF3(23352, 31766, 32545) }, + { CDF3(14669, 27570, 31059) }, + { CDF3( 8492, 20894, 27272) }, + { CDF3( 3644, 10194, 15204) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3( 2461, 7013, 9371) }, + { CDF3(24749, 29600, 30986) }, + { CDF3( 9466, 19037, 22417) }, + { CDF3( 3584, 9280, 14400) }, + { CDF3( 1505, 3929, 5433) }, + { CDF3( 677, 1500, 2736) }, + { CDF3(23987, 30702, 32117) }, + { CDF3(13554, 24571, 29263) }, + { CDF3( 6211, 14556, 21155) }, + { CDF3( 3135, 10972, 15625) }, + { CDF3( 2435, 7127, 11427) }, + { CDF3(31300, 32532, 32550) }, + { CDF3(14757, 30365, 31954) }, + { CDF3( 4405, 11612, 18553) }, + { CDF3( 580, 4132, 7322) }, + { CDF3( 1695, 10169, 14124) }, + { CDF3(30008, 32282, 32591) }, + { CDF3(19244, 30108, 31748) }, + { CDF3(11180, 24158, 29555) }, + { CDF3( 5650, 14972, 19209) }, + { CDF3( 2114, 5109, 8456) }, + { CDF3(31856, 32716, 32748) }, + { CDF3(23012, 31664, 32572) }, + { CDF3(13694, 26656, 30636) }, + { CDF3( 8142, 19508, 26093) }, + { CDF3( 4253, 10955, 16724) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, { + { + { CDF3( 601, 983, 1311) }, + { CDF3(18725, 23406, 28087) }, + { CDF3( 5461, 8192, 10923) }, + { CDF3( 3781, 15124, 21425) }, + { CDF3( 2587, 7761, 12072) }, + { CDF3( 106, 458, 810) }, + { CDF3(22282, 29710, 31894) }, + { CDF3( 8508, 20926, 25984) }, + { CDF3( 3726, 12713, 18083) }, + { CDF3( 1620, 7112, 10893) }, + { CDF3( 729, 2236, 3495) }, + { CDF3(30163, 32474, 32684) }, + { CDF3(18304, 30464, 32000) }, + { CDF3(11443, 26526, 29647) }, + { CDF3( 6007, 15292, 21299) }, + { CDF3( 2234, 6703, 8937) }, + { CDF3(30954, 32177, 32571) }, + { CDF3(17363, 29562, 31076) }, + { CDF3( 9686, 22464, 27410) }, + { CDF3( 8192, 16384, 21390) }, + { CDF3( 1755, 8046, 11264) }, + { CDF3(31168, 32734, 32748) }, + { CDF3(22486, 31441, 32471) }, + { CDF3(12833, 25627, 29738) }, + { CDF3( 6980, 17379, 23122) }, + { CDF3( 3111, 8887, 13479) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, + }, .dc_sign = { + { { CDF1(16000) }, { CDF1(13056) }, { CDF1(18816) } }, + { { CDF1(15232) }, { CDF1(12928) }, { CDF1(17280) } }, + }, .br_tok = { + { + { + { CDF3(14298, 20718, 24174) }, + { CDF3(12536, 19601, 23789) }, + { CDF3( 8712, 15051, 19503) }, + { CDF3( 6170, 11327, 15434) }, + { CDF3( 4742, 8926, 12538) }, + { CDF3( 3803, 7317, 10546) }, + { CDF3( 1696, 3317, 4871) }, + { CDF3(14392, 19951, 22756) }, + { CDF3(15978, 23218, 26818) }, + { CDF3(12187, 19474, 23889) }, + { CDF3( 9176, 15640, 20259) }, + { CDF3( 7068, 12655, 17028) }, + { CDF3( 5656, 10442, 14472) }, + { CDF3( 2580, 4992, 7244) }, + { CDF3(12136, 18049, 21426) }, + { CDF3(13784, 20721, 24481) }, + { CDF3(10836, 17621, 21900) }, + { CDF3( 8372, 14444, 18847) }, + { CDF3( 6523, 11779, 16000) }, + { CDF3( 5337, 9898, 13760) }, + { CDF3( 3034, 5860, 8462) }, + }, { + { CDF3(15967, 22905, 26286) }, + { CDF3(13534, 20654, 24579) }, + { CDF3( 9504, 16092, 20535) }, + { CDF3( 6975, 12568, 16903) }, + { CDF3( 5364, 10091, 14020) }, + { CDF3( 4357, 8370, 11857) }, + { CDF3( 2506, 4934, 7218) }, + { CDF3(23032, 28815, 30936) }, + { CDF3(19540, 26704, 29719) }, + { CDF3(15158, 22969, 27097) }, + { CDF3(11408, 18865, 23650) }, + { CDF3( 8885, 15448, 20250) }, + { CDF3( 7108, 12853, 17416) }, + { CDF3( 4231, 8041, 11480) }, + { CDF3(19823, 26490, 29156) }, + { CDF3(18890, 25929, 28932) }, + { CDF3(15660, 23491, 27433) }, + { CDF3(12147, 19776, 24488) }, + { CDF3( 9728, 16774, 21649) }, + { CDF3( 7919, 14277, 19066) }, + { CDF3( 5440, 10170, 14185) }, + }, + }, { + { + { CDF3(14406, 20862, 24414) }, + { CDF3(11824, 18907, 23109) }, + { CDF3( 8257, 14393, 18803) }, + { CDF3( 5860, 10747, 14778) }, + { CDF3( 4475, 8486, 11984) }, + { CDF3( 3606, 6954, 10043) }, + { CDF3( 1736, 3410, 5048) }, + { CDF3(14430, 20046, 22882) }, + { CDF3(15593, 22899, 26709) }, + { CDF3(12102, 19368, 23811) }, + { CDF3( 9059, 15584, 20262) }, + { CDF3( 6999, 12603, 17048) }, + { CDF3( 5684, 10497, 14553) }, + { CDF3( 2822, 5438, 7862) }, + { CDF3(15785, 21585, 24359) }, + { CDF3(18347, 25229, 28266) }, + { CDF3(14974, 22487, 26389) }, + { CDF3(11423, 18681, 23271) }, + { CDF3( 8863, 15350, 20008) }, + { CDF3( 7153, 12852, 17278) }, + { CDF3( 3707, 7036, 9982) }, + }, { + { CDF3(15460, 21696, 25469) }, + { CDF3(12170, 19249, 23191) }, + { CDF3( 8723, 15027, 19332) }, + { CDF3( 6428, 11704, 15874) }, + { CDF3( 4922, 9292, 13052) }, + { CDF3( 4139, 7695, 11010) }, + { CDF3( 2291, 4508, 6598) }, + { CDF3(19856, 26920, 29828) }, + { CDF3(17923, 25289, 28792) }, + { CDF3(14278, 21968, 26297) }, + { CDF3(10910, 18136, 22950) }, + { CDF3( 8423, 14815, 19627) }, + { CDF3( 6771, 12283, 16774) }, + { CDF3( 4074, 7750, 11081) }, + { CDF3(19852, 26074, 28672) }, + { CDF3(19371, 26110, 28989) }, + { CDF3(16265, 23873, 27663) }, + { CDF3(12758, 20378, 24952) }, + { CDF3(10095, 17098, 21961) }, + { CDF3( 8250, 14628, 19451) }, + { CDF3( 5205, 9745, 13622) }, + }, + }, { + { + { CDF3(10563, 16233, 19763) }, + { CDF3( 9794, 16022, 19804) }, + { CDF3( 6750, 11945, 15759) }, + { CDF3( 4963, 9186, 12752) }, + { CDF3( 3845, 7435, 10627) }, + { CDF3( 3051, 6085, 8834) }, + { CDF3( 1311, 2596, 3830) }, + { CDF3(11246, 16404, 19689) }, + { CDF3(12315, 18911, 22731) }, + { CDF3(10557, 17095, 21289) }, + { CDF3( 8136, 14006, 18249) }, + { CDF3( 6348, 11474, 15565) }, + { CDF3( 5196, 9655, 13400) }, + { CDF3( 2349, 4526, 6587) }, + { CDF3(13337, 18730, 21569) }, + { CDF3(19306, 26071, 28882) }, + { CDF3(15952, 23540, 27254) }, + { CDF3(12409, 19934, 24430) }, + { CDF3( 9760, 16706, 21389) }, + { CDF3( 8004, 14220, 18818) }, + { CDF3( 4138, 7794, 10961) }, + }, { + { CDF3(10870, 16684, 20949) }, + { CDF3( 9664, 15230, 18680) }, + { CDF3( 6886, 12109, 15408) }, + { CDF3( 4825, 8900, 12305) }, + { CDF3( 3630, 7162, 10314) }, + { CDF3( 3036, 6429, 9387) }, + { CDF3( 1671, 3296, 4940) }, + { CDF3(13819, 19159, 23026) }, + { CDF3(11984, 19108, 23120) }, + { CDF3(10690, 17210, 21663) }, + { CDF3( 7984, 14154, 18333) }, + { CDF3( 6868, 12294, 16124) }, + { CDF3( 5274, 8994, 12868) }, + { CDF3( 2988, 5771, 8424) }, + { CDF3(19736, 26647, 29141) }, + { CDF3(18933, 26070, 28984) }, + { CDF3(15779, 23048, 27200) }, + { CDF3(12638, 20061, 24532) }, + { CDF3(10692, 17545, 22220) }, + { CDF3( 9217, 15251, 20054) }, + { CDF3( 5078, 9284, 12594) }, + }, + }, { + { + { CDF3( 2331, 3662, 5244) }, + { CDF3( 2891, 4771, 6145) }, + { CDF3( 4598, 7623, 9729) }, + { CDF3( 3520, 6845, 9199) }, + { CDF3( 3417, 6119, 9324) }, + { CDF3( 2601, 5412, 7385) }, + { CDF3( 600, 1173, 1744) }, + { CDF3( 7672, 13286, 17469) }, + { CDF3( 4232, 7792, 10793) }, + { CDF3( 2915, 5317, 7397) }, + { CDF3( 2318, 4356, 6152) }, + { CDF3( 2127, 4000, 5554) }, + { CDF3( 1850, 3478, 5275) }, + { CDF3( 977, 1933, 2843) }, + { CDF3(18280, 24387, 27989) }, + { CDF3(15852, 22671, 26185) }, + { CDF3(13845, 20951, 24789) }, + { CDF3(11055, 17966, 22129) }, + { CDF3( 9138, 15422, 19801) }, + { CDF3( 7454, 13145, 17456) }, + { CDF3( 3370, 6393, 9013) }, + }, { + { CDF3( 5842, 9229, 10838) }, + { CDF3( 2313, 3491, 4276) }, + { CDF3( 2998, 6104, 7496) }, + { CDF3( 2420, 7447, 9868) }, + { CDF3( 3034, 8495, 10923) }, + { CDF3( 4076, 8937, 10975) }, + { CDF3( 1086, 2370, 3299) }, + { CDF3( 9714, 17254, 20444) }, + { CDF3( 8543, 13698, 17123) }, + { CDF3( 4918, 9007, 11910) }, + { CDF3( 4129, 7532, 10553) }, + { CDF3( 2364, 5533, 8058) }, + { CDF3( 1834, 3546, 5563) }, + { CDF3( 1473, 2908, 4133) }, + { CDF3(15405, 21193, 25619) }, + { CDF3(15691, 21952, 26561) }, + { CDF3(12962, 19194, 24165) }, + { CDF3(10272, 17855, 22129) }, + { CDF3( 8588, 15270, 20718) }, + { CDF3( 8682, 14669, 19500) }, + { CDF3( 4870, 9636, 13205) }, + }, + }, + }, + }, [1] = { + .skip = { + { + { CDF1(30371) }, { CDF1( 7570) }, { CDF1(13155) }, + { CDF1(20751) }, { CDF1(20969) }, { CDF1(27067) }, + { CDF1(32013) }, { CDF1( 5495) }, { CDF1(17942) }, + { CDF1(28280) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(31782) }, { CDF1( 1836) }, { CDF1(10689) }, + { CDF1(17604) }, { CDF1(21622) }, { CDF1(27518) }, + { CDF1(32399) }, { CDF1( 4419) }, { CDF1(16294) }, + { CDF1(28345) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(31901) }, { CDF1(10311) }, { CDF1(18047) }, + { CDF1(24806) }, { CDF1(23288) }, { CDF1(27914) }, + { CDF1(32296) }, { CDF1( 4215) }, { CDF1(15756) }, + { CDF1(28341) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(26726) }, { CDF1( 1045) }, { CDF1(11703) }, + { CDF1(20590) }, { CDF1(18554) }, { CDF1(25970) }, + { CDF1(31938) }, { CDF1( 5583) }, { CDF1(21313) }, + { CDF1(29390) }, { CDF1( 641) }, { CDF1(22265) }, + { CDF1(31452) }, + }, { + { CDF1(26584) }, { CDF1( 188) }, { CDF1( 8847) }, + { CDF1(24519) }, { CDF1(22938) }, { CDF1(30583) }, + { CDF1(32608) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, + }, .eob_bin_16 = { + { + { CDF4( 2125, 2551, 5165, 8946) }, + { CDF4( 513, 765, 1859, 6339) }, + }, { + { CDF4( 7637, 9498, 14259, 19108) }, + { CDF4( 2497, 4096, 8866, 16993) }, + }, + }, .eob_bin_32 = { + { + { CDF5( 989, 1249, 2019, 4151, 10785) }, + { CDF5( 313, 441, 1099, 2917, 8562) }, + }, { + { CDF5( 8394, 10352, 13932, 18855, 26014) }, + { CDF5( 2578, 4124, 8181, 13670, 24234) }, + }, + }, .eob_bin_64 = { + { + { CDF6( 1260, 1446, 2253, 3712, 6652, 13369) }, + { CDF6( 401, 605, 1029, 2563, 5845, 12626) }, + }, { + { CDF6( 8609, 10612, 14624, 18714, 22614, 29024) }, + { CDF6( 1923, 3127, 5867, 9703, 14277, 27100) }, + }, + }, .eob_bin_128 = { + { + { CDF7( 685, 933, 1488, 2714, 4766, 8562, 19254) }, + { CDF7( 217, 352, 618, 2303, 5261, 9969, 17472) }, + }, { + { CDF7( 8045, 11200, 15497, 19595, 23948, 27408, 30938) }, + { CDF7( 2310, 4160, 7471, 14997, 17931, 20768, 30240) }, + }, + }, .eob_bin_256 = { + { + { CDF8( 1448, 2109, 4151, 6263, + 9329, 13260, 17944, 23300) }, + { CDF8( 399, 1019, 1749, 3038, + 10444, 15546, 22739, 27294) }, + }, { + { CDF8( 6402, 8148, 12623, 15072, + 18728, 22847, 26447, 29377) }, + { CDF8( 1674, 3252, 5734, 10159, + 22397, 23802, 24821, 30940) }, + }, + }, .eob_bin_512 = { + { CDF9( 1230, 2278, 5035, 7776, 11871, + 15346, 19590, 24584, 28749) }, + { CDF9( 7265, 9979, 15819, 19250, 21780, + 23846, 26478, 28396, 31811) }, + }, .eob_bin_1024 = { + { CDF10( 696, 948, 3145, 5702, 9706, + 13217, 17851, 21856, 25692, 28034) }, + { CDF10( 2672, 3591, 9330, 17084, 22725, + 24284, 26527, 28027, 28377, 30876) }, + }, .eob_hi_bit = { + { + { + { CDF1(17471) }, { CDF1(20223) }, { CDF1(11357) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(20335) }, { CDF1(21667) }, { CDF1(14818) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(20430) }, { CDF1(20662) }, { CDF1(15367) }, + { CDF1(16970) }, { CDF1(14657) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(22117) }, { CDF1(22028) }, { CDF1(18650) }, + { CDF1(16042) }, { CDF1(15885) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(22409) }, { CDF1(21012) }, { CDF1(15650) }, + { CDF1(17395) }, { CDF1(15469) }, { CDF1(20205) }, + { CDF1(19511) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(24220) }, { CDF1(22480) }, { CDF1(17737) }, + { CDF1(18916) }, { CDF1(19268) }, { CDF1(18412) }, + { CDF1(18844) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(25991) }, { CDF1(20314) }, { CDF1(17731) }, + { CDF1(19678) }, { CDF1(18649) }, { CDF1(17307) }, + { CDF1(21798) }, { CDF1(17549) }, { CDF1(15630) }, + }, { + { CDF1(26585) }, { CDF1(21469) }, { CDF1(20432) }, + { CDF1(17735) }, { CDF1(19280) }, { CDF1(15235) }, + { CDF1(20297) }, { CDF1(22471) }, { CDF1(28997) }, + }, + }, { + { + { CDF1(26605) }, { CDF1(11304) }, { CDF1(16726) }, + { CDF1(16560) }, { CDF1(20866) }, { CDF1(23524) }, + { CDF1(19878) }, { CDF1(13469) }, { CDF1(23084) }, + }, { + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, + }, .eob_base_tok = { + { + { + { CDF2(17560, 29888) }, { CDF2(29671, 31549) }, + { CDF2(31007, 32056) }, { CDF2(27286, 30006) }, + }, { + { CDF2(26594, 31212) }, { CDF2(31208, 32582) }, + { CDF2(31835, 32637) }, { CDF2(30595, 32206) }, + }, + }, { + { + { CDF2(15239, 29932) }, { CDF2(31315, 32095) }, + { CDF2(32130, 32434) }, { CDF2(30864, 31996) }, + }, { + { CDF2(26279, 30968) }, { CDF2(31142, 32495) }, + { CDF2(31713, 32540) }, { CDF2(31929, 32594) }, + }, + }, { + { + { CDF2( 2644, 25198) }, { CDF2(32038, 32451) }, + { CDF2(32639, 32695) }, { CDF2(32166, 32518) }, + }, { + { CDF2(17187, 27668) }, { CDF2(31714, 32550) }, + { CDF2(32283, 32678) }, { CDF2(31930, 32563) }, + }, + }, { + { + { CDF2( 1044, 2257) }, { CDF2(30755, 31923) }, + { CDF2(32208, 32693) }, { CDF2(32244, 32615) }, + }, { + { CDF2(21317, 26207) }, { CDF2(29133, 30868) }, + { CDF2(29311, 31231) }, { CDF2(29657, 31087) }, + }, + }, { + { + { CDF2( 478, 1834) }, { CDF2(31005, 31987) }, + { CDF2(32317, 32724) }, { CDF2(30865, 32648) }, + }, { + { CDF2(10923, 21845) }, { CDF2(10923, 21845) }, + { CDF2(10923, 21845) }, { CDF2(10923, 21845) }, + }, + }, + }, .base_tok = { + { + { + { CDF3( 6041, 11854, 15927) }, + { CDF3(20326, 30905, 32251) }, + { CDF3(14164, 26831, 30725) }, + { CDF3( 9760, 20647, 26585) }, + { CDF3( 6416, 14953, 21219) }, + { CDF3( 2966, 7151, 10891) }, + { CDF3(23567, 31374, 32254) }, + { CDF3(14978, 27416, 30946) }, + { CDF3( 9434, 20225, 26254) }, + { CDF3( 6658, 14558, 20535) }, + { CDF3( 3916, 8677, 12989) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3(18088, 29545, 31587) }, + { CDF3(13062, 25843, 30073) }, + { CDF3( 8940, 16827, 22251) }, + { CDF3( 7654, 13220, 17973) }, + { CDF3( 5733, 10316, 14456) }, + { CDF3(22879, 31388, 32114) }, + { CDF3(15215, 27993, 30955) }, + { CDF3( 9397, 19445, 24978) }, + { CDF3( 3442, 9813, 15344) }, + { CDF3( 1368, 3936, 6532) }, + { CDF3(25494, 32033, 32406) }, + { CDF3(16772, 27963, 30718) }, + { CDF3( 9419, 18165, 23260) }, + { CDF3( 2677, 7501, 11797) }, + { CDF3( 1516, 4344, 7170) }, + { CDF3(26556, 31454, 32101) }, + { CDF3(17128, 27035, 30108) }, + { CDF3( 8324, 15344, 20249) }, + { CDF3( 1903, 5696, 9469) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3( 8455, 19003, 24368) }, + { CDF3(23563, 32021, 32604) }, + { CDF3(16237, 29446, 31935) }, + { CDF3(10724, 23999, 29358) }, + { CDF3( 6725, 17528, 24416) }, + { CDF3( 3927, 10927, 16825) }, + { CDF3(26313, 32288, 32634) }, + { CDF3(17430, 30095, 32095) }, + { CDF3(11116, 24606, 29679) }, + { CDF3( 7195, 18384, 25269) }, + { CDF3( 4726, 12852, 19315) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3(22822, 31648, 32483) }, + { CDF3(16724, 29633, 31929) }, + { CDF3(10261, 23033, 28725) }, + { CDF3( 7029, 17840, 24528) }, + { CDF3( 4867, 13886, 21502) }, + { CDF3(25298, 31892, 32491) }, + { CDF3(17809, 29330, 31512) }, + { CDF3( 9668, 21329, 26579) }, + { CDF3( 4774, 12956, 18976) }, + { CDF3( 2322, 7030, 11540) }, + { CDF3(25472, 31920, 32543) }, + { CDF3(17957, 29387, 31632) }, + { CDF3( 9196, 20593, 26400) }, + { CDF3( 4680, 12705, 19202) }, + { CDF3( 2917, 8456, 13436) }, + { CDF3(26471, 32059, 32574) }, + { CDF3(18458, 29783, 31909) }, + { CDF3( 8400, 19464, 25956) }, + { CDF3( 3812, 10973, 17206) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, { + { + { CDF3( 6779, 13743, 17678) }, + { CDF3(24806, 31797, 32457) }, + { CDF3(17616, 29047, 31372) }, + { CDF3(11063, 23175, 28003) }, + { CDF3( 6521, 16110, 22324) }, + { CDF3( 2764, 7504, 11654) }, + { CDF3(25266, 32367, 32637) }, + { CDF3(19054, 30553, 32175) }, + { CDF3(12139, 25212, 29807) }, + { CDF3( 7311, 18162, 24704) }, + { CDF3( 3397, 9164, 14074) }, + { CDF3(25988, 32208, 32522) }, + { CDF3(16253, 28912, 31526) }, + { CDF3( 9151, 21387, 27372) }, + { CDF3( 5688, 14915, 21496) }, + { CDF3( 2717, 7627, 12004) }, + { CDF3(23144, 31855, 32443) }, + { CDF3(16070, 28491, 31325) }, + { CDF3( 8702, 20467, 26517) }, + { CDF3( 5243, 13956, 20367) }, + { CDF3( 2621, 7335, 11567) }, + { CDF3(26636, 32340, 32630) }, + { CDF3(19990, 31050, 32341) }, + { CDF3(13243, 26105, 30315) }, + { CDF3( 8588, 19521, 25918) }, + { CDF3( 4717, 11585, 17304) }, + { CDF3(25844, 32292, 32582) }, + { CDF3(19090, 30635, 32097) }, + { CDF3(11963, 24546, 28939) }, + { CDF3( 6218, 16087, 22354) }, + { CDF3( 2340, 6608, 10426) }, + { CDF3(28046, 32576, 32694) }, + { CDF3(21178, 31313, 32296) }, + { CDF3(13486, 26184, 29870) }, + { CDF3( 7149, 17871, 23723) }, + { CDF3( 2833, 7958, 12259) }, + { CDF3(27710, 32528, 32686) }, + { CDF3(20674, 31076, 32268) }, + { CDF3(12413, 24955, 29243) }, + { CDF3( 6676, 16927, 23097) }, + { CDF3( 2966, 8333, 12919) }, + }, { + { CDF3( 8639, 19339, 24429) }, + { CDF3(24404, 31837, 32525) }, + { CDF3(16997, 29425, 31784) }, + { CDF3(11253, 24234, 29149) }, + { CDF3( 6751, 17394, 24028) }, + { CDF3( 3490, 9830, 15191) }, + { CDF3(26283, 32471, 32714) }, + { CDF3(19599, 31168, 32442) }, + { CDF3(13146, 26954, 30893) }, + { CDF3( 8214, 20588, 26890) }, + { CDF3( 4699, 13081, 19300) }, + { CDF3(28212, 32458, 32669) }, + { CDF3(18594, 30316, 32100) }, + { CDF3(11219, 24408, 29234) }, + { CDF3( 6865, 17656, 24149) }, + { CDF3( 3678, 10362, 16006) }, + { CDF3(25825, 32136, 32616) }, + { CDF3(17313, 29853, 32021) }, + { CDF3(11197, 24471, 29472) }, + { CDF3( 6947, 17781, 24405) }, + { CDF3( 3768, 10660, 16261) }, + { CDF3(27352, 32500, 32706) }, + { CDF3(20850, 31468, 32469) }, + { CDF3(14021, 27707, 31133) }, + { CDF3( 8964, 21748, 27838) }, + { CDF3( 5437, 14665, 21187) }, + { CDF3(26304, 32492, 32698) }, + { CDF3(20409, 31380, 32385) }, + { CDF3(13682, 27222, 30632) }, + { CDF3( 8974, 21236, 26685) }, + { CDF3( 4234, 11665, 16934) }, + { CDF3(26273, 32357, 32711) }, + { CDF3(20672, 31242, 32441) }, + { CDF3(14172, 27254, 30902) }, + { CDF3( 9870, 21898, 27275) }, + { CDF3( 5164, 13506, 19270) }, + { CDF3(26725, 32459, 32728) }, + { CDF3(20991, 31442, 32527) }, + { CDF3(13071, 26434, 30811) }, + { CDF3( 8184, 20090, 26742) }, + { CDF3( 4803, 13255, 19895) }, + }, + }, { + { + { CDF3( 7555, 14942, 18501) }, + { CDF3(24410, 31178, 32287) }, + { CDF3(14394, 26738, 30253) }, + { CDF3( 8413, 19554, 25195) }, + { CDF3( 4766, 12924, 18785) }, + { CDF3( 2029, 5806, 9207) }, + { CDF3(26776, 32364, 32663) }, + { CDF3(18732, 29967, 31931) }, + { CDF3(11005, 23786, 28852) }, + { CDF3( 6466, 16909, 23510) }, + { CDF3( 3044, 8638, 13419) }, + { CDF3(29208, 32582, 32704) }, + { CDF3(20068, 30857, 32208) }, + { CDF3(12003, 25085, 29595) }, + { CDF3( 6947, 17750, 24189) }, + { CDF3( 3245, 9103, 14007) }, + { CDF3(27359, 32465, 32669) }, + { CDF3(19421, 30614, 32174) }, + { CDF3(11915, 25010, 29579) }, + { CDF3( 6950, 17676, 24074) }, + { CDF3( 3007, 8473, 13096) }, + { CDF3(29002, 32676, 32735) }, + { CDF3(22102, 31849, 32576) }, + { CDF3(14408, 28009, 31405) }, + { CDF3( 9027, 21679, 27931) }, + { CDF3( 4694, 12678, 18748) }, + { CDF3(28216, 32528, 32682) }, + { CDF3(20849, 31264, 32318) }, + { CDF3(12756, 25815, 29751) }, + { CDF3( 7565, 18801, 24923) }, + { CDF3( 3509, 9533, 14477) }, + { CDF3(30133, 32687, 32739) }, + { CDF3(23063, 31910, 32515) }, + { CDF3(14588, 28051, 31132) }, + { CDF3( 9085, 21649, 27457) }, + { CDF3( 4261, 11654, 17264) }, + { CDF3(29518, 32691, 32748) }, + { CDF3(22451, 31959, 32613) }, + { CDF3(14864, 28722, 31700) }, + { CDF3( 9695, 22964, 28716) }, + { CDF3( 4932, 13358, 19502) }, + }, { + { CDF3( 6465, 16958, 21688) }, + { CDF3(25199, 31514, 32360) }, + { CDF3(14774, 27149, 30607) }, + { CDF3( 9257, 21438, 26972) }, + { CDF3( 5723, 15183, 21882) }, + { CDF3( 3150, 8879, 13731) }, + { CDF3(26989, 32262, 32682) }, + { CDF3(17396, 29937, 32085) }, + { CDF3(11387, 24901, 29784) }, + { CDF3( 7289, 18821, 25548) }, + { CDF3( 3734, 10577, 16086) }, + { CDF3(29728, 32501, 32695) }, + { CDF3(17431, 29701, 31903) }, + { CDF3( 9921, 22826, 28300) }, + { CDF3( 5896, 15434, 22068) }, + { CDF3( 3430, 9646, 14757) }, + { CDF3(28614, 32511, 32705) }, + { CDF3(19364, 30638, 32263) }, + { CDF3(13129, 26254, 30402) }, + { CDF3( 8754, 20484, 26440) }, + { CDF3( 4378, 11607, 17110) }, + { CDF3(30292, 32671, 32744) }, + { CDF3(21780, 31603, 32501) }, + { CDF3(14314, 27829, 31291) }, + { CDF3( 9611, 22327, 28263) }, + { CDF3( 4890, 13087, 19065) }, + { CDF3(25862, 32567, 32733) }, + { CDF3(20794, 32050, 32567) }, + { CDF3(17243, 30625, 32254) }, + { CDF3(13283, 27628, 31474) }, + { CDF3( 9669, 22532, 28918) }, + { CDF3(27435, 32697, 32748) }, + { CDF3(24922, 32390, 32714) }, + { CDF3(21449, 31504, 32536) }, + { CDF3(16392, 29729, 31832) }, + { CDF3(11692, 24884, 29076) }, + { CDF3(24193, 32290, 32735) }, + { CDF3(18909, 31104, 32563) }, + { CDF3(12236, 26841, 31403) }, + { CDF3( 8171, 21840, 29082) }, + { CDF3( 7224, 17280, 25275) }, + }, + }, { + { + { CDF3( 3078, 6839, 9890) }, + { CDF3(13837, 20450, 24479) }, + { CDF3( 5914, 14222, 19328) }, + { CDF3( 3866, 10267, 14762) }, + { CDF3( 2612, 7208, 11042) }, + { CDF3( 1067, 2991, 4776) }, + { CDF3(25817, 31646, 32529) }, + { CDF3(13708, 26338, 30385) }, + { CDF3( 7328, 18585, 24870) }, + { CDF3( 4691, 13080, 19276) }, + { CDF3( 1825, 5253, 8352) }, + { CDF3(29386, 32315, 32624) }, + { CDF3(17160, 29001, 31360) }, + { CDF3( 9602, 21862, 27396) }, + { CDF3( 5915, 15772, 22148) }, + { CDF3( 2786, 7779, 12047) }, + { CDF3(29246, 32450, 32663) }, + { CDF3(18696, 29929, 31818) }, + { CDF3(10510, 23369, 28560) }, + { CDF3( 6229, 16499, 23125) }, + { CDF3( 2608, 7448, 11705) }, + { CDF3(30753, 32710, 32748) }, + { CDF3(21638, 31487, 32503) }, + { CDF3(12937, 26854, 30870) }, + { CDF3( 8182, 20596, 26970) }, + { CDF3( 3637, 10269, 15497) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3( 5244, 12150, 16906) }, + { CDF3(20486, 26858, 29701) }, + { CDF3( 7756, 18317, 23735) }, + { CDF3( 3452, 9256, 13146) }, + { CDF3( 2020, 5206, 8229) }, + { CDF3( 1801, 4993, 7903) }, + { CDF3(27051, 31858, 32531) }, + { CDF3(15988, 27531, 30619) }, + { CDF3( 9188, 21484, 26719) }, + { CDF3( 6273, 17186, 23800) }, + { CDF3( 3108, 9355, 14764) }, + { CDF3(31076, 32520, 32680) }, + { CDF3(18119, 30037, 31850) }, + { CDF3(10244, 22969, 27472) }, + { CDF3( 4692, 14077, 19273) }, + { CDF3( 3694, 11677, 17556) }, + { CDF3(30060, 32581, 32720) }, + { CDF3(21011, 30775, 32120) }, + { CDF3(11931, 24820, 29289) }, + { CDF3( 7119, 17662, 24356) }, + { CDF3( 3833, 10706, 16304) }, + { CDF3(31954, 32731, 32748) }, + { CDF3(23913, 31724, 32489) }, + { CDF3(15520, 28060, 31286) }, + { CDF3(11517, 23008, 28571) }, + { CDF3( 6193, 14508, 20629) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, { + { + { CDF3( 1035, 2807, 4156) }, + { CDF3(13162, 18138, 20939) }, + { CDF3( 2696, 6633, 8755) }, + { CDF3( 1373, 4161, 6853) }, + { CDF3( 1099, 2746, 4716) }, + { CDF3( 340, 1021, 1599) }, + { CDF3(22826, 30419, 32135) }, + { CDF3(10395, 21762, 26942) }, + { CDF3( 4726, 12407, 17361) }, + { CDF3( 2447, 7080, 10593) }, + { CDF3( 1227, 3717, 6011) }, + { CDF3(28156, 31424, 31934) }, + { CDF3(16915, 27754, 30373) }, + { CDF3( 9148, 20990, 26431) }, + { CDF3( 5950, 15515, 21148) }, + { CDF3( 2492, 7327, 11526) }, + { CDF3(30602, 32477, 32670) }, + { CDF3(20026, 29955, 31568) }, + { CDF3(11220, 23628, 28105) }, + { CDF3( 6652, 17019, 22973) }, + { CDF3( 3064, 8536, 13043) }, + { CDF3(31769, 32724, 32748) }, + { CDF3(22230, 30887, 32373) }, + { CDF3(12234, 25079, 29731) }, + { CDF3( 7326, 18816, 25353) }, + { CDF3( 3933, 10907, 16616) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, + }, .dc_sign = { + { { CDF1(16000) }, { CDF1(13056) }, { CDF1(18816) } }, + { { CDF1(15232) }, { CDF1(12928) }, { CDF1(17280) } }, + }, .br_tok = { + { + { + { CDF3(14995, 21341, 24749) }, + { CDF3(13158, 20289, 24601) }, + { CDF3( 8941, 15326, 19876) }, + { CDF3( 6297, 11541, 15807) }, + { CDF3( 4817, 9029, 12776) }, + { CDF3( 3731, 7273, 10627) }, + { CDF3( 1847, 3617, 5354) }, + { CDF3(14472, 19659, 22343) }, + { CDF3(16806, 24162, 27533) }, + { CDF3(12900, 20404, 24713) }, + { CDF3( 9411, 16112, 20797) }, + { CDF3( 7056, 12697, 17148) }, + { CDF3( 5544, 10339, 14460) }, + { CDF3( 2954, 5704, 8319) }, + { CDF3(12464, 18071, 21354) }, + { CDF3(15482, 22528, 26034) }, + { CDF3(12070, 19269, 23624) }, + { CDF3( 8953, 15406, 20106) }, + { CDF3( 7027, 12730, 17220) }, + { CDF3( 5887, 10913, 15140) }, + { CDF3( 3793, 7278, 10447) }, + }, { + { CDF3(15571, 22232, 25749) }, + { CDF3(14506, 21575, 25374) }, + { CDF3(10189, 17089, 21569) }, + { CDF3( 7316, 13301, 17915) }, + { CDF3( 5783, 10912, 15190) }, + { CDF3( 4760, 9155, 13088) }, + { CDF3( 2993, 5966, 8774) }, + { CDF3(23424, 28903, 30778) }, + { CDF3(20775, 27666, 30290) }, + { CDF3(16474, 24410, 28299) }, + { CDF3(12471, 20180, 24987) }, + { CDF3( 9410, 16487, 21439) }, + { CDF3( 7536, 13614, 18529) }, + { CDF3( 5048, 9586, 13549) }, + { CDF3(21090, 27290, 29756) }, + { CDF3(20796, 27402, 30026) }, + { CDF3(17819, 25485, 28969) }, + { CDF3(13860, 21909, 26462) }, + { CDF3(11002, 18494, 23529) }, + { CDF3( 8953, 15929, 20897) }, + { CDF3( 6448, 11918, 16454) }, + }, + }, { + { + { CDF3(15999, 22208, 25449) }, + { CDF3(13050, 19988, 24122) }, + { CDF3( 8594, 14864, 19378) }, + { CDF3( 6033, 11079, 15238) }, + { CDF3( 4554, 8683, 12347) }, + { CDF3( 3672, 7139, 10337) }, + { CDF3( 1900, 3771, 5576) }, + { CDF3(15788, 21340, 23949) }, + { CDF3(16825, 24235, 27758) }, + { CDF3(12873, 20402, 24810) }, + { CDF3( 9590, 16363, 21094) }, + { CDF3( 7352, 13209, 17733) }, + { CDF3( 5960, 10989, 15184) }, + { CDF3( 3232, 6234, 9007) }, + { CDF3(15761, 20716, 23224) }, + { CDF3(19318, 25989, 28759) }, + { CDF3(15529, 23094, 26929) }, + { CDF3(11662, 18989, 23641) }, + { CDF3( 8955, 15568, 20366) }, + { CDF3( 7281, 13106, 17708) }, + { CDF3( 4248, 8059, 11440) }, + }, { + { CDF3(14899, 21217, 24503) }, + { CDF3(13519, 20283, 24047) }, + { CDF3( 9429, 15966, 20365) }, + { CDF3( 6700, 12355, 16652) }, + { CDF3( 5088, 9704, 13716) }, + { CDF3( 4243, 8154, 11731) }, + { CDF3( 2702, 5364, 7861) }, + { CDF3(22745, 28388, 30454) }, + { CDF3(20235, 27146, 29922) }, + { CDF3(15896, 23715, 27637) }, + { CDF3(11840, 19350, 24131) }, + { CDF3( 9122, 15932, 20880) }, + { CDF3( 7488, 13581, 18362) }, + { CDF3( 5114, 9568, 13370) }, + { CDF3(20845, 26553, 28932) }, + { CDF3(20981, 27372, 29884) }, + { CDF3(17781, 25335, 28785) }, + { CDF3(13760, 21708, 26297) }, + { CDF3(10975, 18415, 23365) }, + { CDF3( 9045, 15789, 20686) }, + { CDF3( 6130, 11199, 15423) }, + }, + }, { + { + { CDF3(13549, 19724, 23158) }, + { CDF3(11844, 18382, 22246) }, + { CDF3( 7919, 13619, 17773) }, + { CDF3( 5486, 10143, 13946) }, + { CDF3( 4166, 7983, 11324) }, + { CDF3( 3364, 6506, 9427) }, + { CDF3( 1598, 3160, 4674) }, + { CDF3(15281, 20979, 23781) }, + { CDF3(14939, 22119, 25952) }, + { CDF3(11363, 18407, 22812) }, + { CDF3( 8609, 14857, 19370) }, + { CDF3( 6737, 12184, 16480) }, + { CDF3( 5506, 10263, 14262) }, + { CDF3( 2990, 5786, 8380) }, + { CDF3(20249, 25253, 27417) }, + { CDF3(21070, 27518, 30001) }, + { CDF3(16854, 24469, 28074) }, + { CDF3(12864, 20486, 25000) }, + { CDF3( 9962, 16978, 21778) }, + { CDF3( 8074, 14338, 19048) }, + { CDF3( 4494, 8479, 11906) }, + }, { + { CDF3(13960, 19617, 22829) }, + { CDF3(11150, 17341, 21228) }, + { CDF3( 7150, 12964, 17190) }, + { CDF3( 5331, 10002, 13867) }, + { CDF3( 4167, 7744, 11057) }, + { CDF3( 3480, 6629, 9646) }, + { CDF3( 1883, 3784, 5686) }, + { CDF3(18752, 25660, 28912) }, + { CDF3(16968, 24586, 28030) }, + { CDF3(13520, 21055, 25313) }, + { CDF3(10453, 17626, 22280) }, + { CDF3( 8386, 14505, 19116) }, + { CDF3( 6742, 12595, 17008) }, + { CDF3( 4273, 8140, 11499) }, + { CDF3(22120, 27827, 30233) }, + { CDF3(20563, 27358, 29895) }, + { CDF3(17076, 24644, 28153) }, + { CDF3(13362, 20942, 25309) }, + { CDF3(10794, 17965, 22695) }, + { CDF3( 9014, 15652, 20319) }, + { CDF3( 5708, 10512, 14497) }, + }, + }, { + { + { CDF3( 5705, 10930, 15725) }, + { CDF3( 7946, 12765, 16115) }, + { CDF3( 6801, 12123, 16226) }, + { CDF3( 5462, 10135, 14200) }, + { CDF3( 4189, 8011, 11507) }, + { CDF3( 3191, 6229, 9408) }, + { CDF3( 1057, 2137, 3212) }, + { CDF3(10018, 17067, 21491) }, + { CDF3( 7380, 12582, 16453) }, + { CDF3( 6068, 10845, 14339) }, + { CDF3( 5098, 9198, 12555) }, + { CDF3( 4312, 8010, 11119) }, + { CDF3( 3700, 6966, 9781) }, + { CDF3( 1693, 3326, 4887) }, + { CDF3(18757, 24930, 27774) }, + { CDF3(17648, 24596, 27817) }, + { CDF3(14707, 22052, 26026) }, + { CDF3(11720, 18852, 23292) }, + { CDF3( 9357, 15952, 20525) }, + { CDF3( 7810, 13753, 18210) }, + { CDF3( 3879, 7333, 10328) }, + }, { + { CDF3( 8278, 13242, 15922) }, + { CDF3(10547, 15867, 18919) }, + { CDF3( 9106, 15842, 20609) }, + { CDF3( 6833, 13007, 17218) }, + { CDF3( 4811, 9712, 13923) }, + { CDF3( 3985, 7352, 11128) }, + { CDF3( 1688, 3458, 5262) }, + { CDF3(12951, 21861, 26510) }, + { CDF3( 9788, 16044, 20276) }, + { CDF3( 6309, 11244, 14870) }, + { CDF3( 5183, 9349, 12566) }, + { CDF3( 4389, 8229, 11492) }, + { CDF3( 3633, 6945, 10620) }, + { CDF3( 3600, 6847, 9907) }, + { CDF3(21748, 28137, 30255) }, + { CDF3(19436, 26581, 29560) }, + { CDF3(16359, 24201, 27953) }, + { CDF3(13961, 21693, 25871) }, + { CDF3(11544, 18686, 23322) }, + { CDF3( 9372, 16462, 20952) }, + { CDF3( 6138, 11210, 15390) }, + }, + }, + }, + }, [2] = { + .skip = { + { + { CDF1(29614) }, { CDF1( 9068) }, { CDF1(12924) }, + { CDF1(19538) }, { CDF1(17737) }, { CDF1(24619) }, + { CDF1(30642) }, { CDF1( 4119) }, { CDF1(16026) }, + { CDF1(25657) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(31957) }, { CDF1( 3230) }, { CDF1(11153) }, + { CDF1(18123) }, { CDF1(20143) }, { CDF1(26536) }, + { CDF1(31986) }, { CDF1( 3050) }, { CDF1(14603) }, + { CDF1(25155) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(32363) }, { CDF1(10692) }, { CDF1(19090) }, + { CDF1(24357) }, { CDF1(24442) }, { CDF1(28312) }, + { CDF1(32169) }, { CDF1( 3648) }, { CDF1(15690) }, + { CDF1(26815) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(30669) }, { CDF1( 3832) }, { CDF1(11663) }, + { CDF1(18889) }, { CDF1(19782) }, { CDF1(23313) }, + { CDF1(31330) }, { CDF1( 5124) }, { CDF1(18719) }, + { CDF1(28468) }, { CDF1( 3082) }, { CDF1(20982) }, + { CDF1(29443) }, + }, { + { CDF1(28573) }, { CDF1( 3183) }, { CDF1(17802) }, + { CDF1(25977) }, { CDF1(26677) }, { CDF1(27832) }, + { CDF1(32387) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, + }, .eob_bin_16 = { + { + { CDF4( 4016, 4897, 8881, 14968) }, + { CDF4( 716, 1105, 2646, 10056) }, + }, { + { CDF4(11139, 13270, 18241, 23566) }, + { CDF4( 3192, 5032, 10297, 19755) }, + }, + }, .eob_bin_32 = { + { + { CDF5( 2515, 3003, 4452, 8162, 16041) }, + { CDF5( 574, 821, 1836, 5089, 13128) }, + }, { + { CDF5(13468, 16303, 20361, 25105, 29281) }, + { CDF5( 3542, 5502, 10415, 16760, 25644) }, + }, + }, .eob_bin_64 = { + { + { CDF6( 2374, 2772, 4583, 7276, 12288, 19706) }, + { CDF6( 497, 810, 1315, 3000, 7004, 15641) }, + }, { + { CDF6(15050, 17126, 21410, 24886, 28156, 30726) }, + { CDF6( 4034, 6290, 10235, 14982, 21214, 28491) }, + }, + }, .eob_bin_128 = { + { + { CDF7( 1366, 1738, 2527, 5016, 9355, 15797, 24643) }, + { CDF7( 354, 558, 944, 2760, 7287, 14037, 21779) }, + }, { + { CDF7(13627, 16246, 20173, 24429, 27948, 30415, 31863) }, + { CDF7( 6275, 9889, 14769, 23164, 27988, 30493, 32272) }, + }, + }, .eob_bin_256 = { + { + { CDF8( 3089, 3920, 6038, 9460, + 14266, 19881, 25766, 29176) }, + { CDF8( 1084, 2358, 3488, 5122, + 11483, 18103, 26023, 29799) }, + }, { + { CDF8(11514, 13794, 17480, 20754, + 24361, 27378, 29492, 31277) }, + { CDF8( 6571, 9610, 15516, 21826, + 29092, 30829, 31842, 32708) }, + }, + }, .eob_bin_512 = { + { CDF9( 2624, 3936, 6480, 9686, 13979, + 17726, 23267, 28410, 31078) }, + { CDF9(12015, 14769, 19588, 22052, 24222, + 25812, 27300, 29219, 32114) }, + }, .eob_bin_1024 = { + { CDF10( 2784, 3831, 7041, 10521, 14847, + 18844, 23155, 26682, 29229, 31045) }, + { CDF10( 9577, 12466, 17739, 20750, 22061, + 23215, 24601, 25483, 25843, 32056) }, + }, .eob_hi_bit = { + { + { + { CDF1(18983) }, { CDF1(20512) }, { CDF1(14885) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(20090) }, { CDF1(19444) }, { CDF1(17286) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(19139) }, { CDF1(21487) }, { CDF1(18959) }, + { CDF1(20910) }, { CDF1(19089) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(20536) }, { CDF1(20664) }, { CDF1(20625) }, + { CDF1(19123) }, { CDF1(14862) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(19833) }, { CDF1(21502) }, { CDF1(17485) }, + { CDF1(20267) }, { CDF1(18353) }, { CDF1(23329) }, + { CDF1(21478) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(22041) }, { CDF1(23434) }, { CDF1(20001) }, + { CDF1(20554) }, { CDF1(20951) }, { CDF1(20145) }, + { CDF1(15562) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(23312) }, { CDF1(21607) }, { CDF1(16526) }, + { CDF1(18957) }, { CDF1(18034) }, { CDF1(18934) }, + { CDF1(24247) }, { CDF1(16921) }, { CDF1(17080) }, + }, { + { CDF1(26579) }, { CDF1(24910) }, { CDF1(18637) }, + { CDF1(19800) }, { CDF1(20388) }, { CDF1( 9887) }, + { CDF1(15642) }, { CDF1(30198) }, { CDF1(24721) }, + }, + }, { + { + { CDF1(26998) }, { CDF1(16737) }, { CDF1(17838) }, + { CDF1(18922) }, { CDF1(19515) }, { CDF1(18636) }, + { CDF1(17333) }, { CDF1(15776) }, { CDF1(22658) }, + }, { + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, + }, .eob_base_tok = { + { + { + { CDF2(20092, 30774) }, { CDF2(30695, 32020) }, + { CDF2(31131, 32103) }, { CDF2(28666, 30870) }, + }, { + { CDF2(27258, 31095) }, { CDF2(31804, 32623) }, + { CDF2(31763, 32528) }, { CDF2(31438, 32506) }, + }, + }, { + { + { CDF2(18049, 30489) }, { CDF2(31706, 32286) }, + { CDF2(32163, 32473) }, { CDF2(31550, 32184) }, + }, { + { CDF2(27116, 30842) }, { CDF2(31971, 32598) }, + { CDF2(32088, 32576) }, { CDF2(32067, 32664) }, + }, + }, { + { + { CDF2(12854, 29093) }, { CDF2(32272, 32558) }, + { CDF2(32667, 32729) }, { CDF2(32306, 32585) }, + }, { + { CDF2(25476, 30366) }, { CDF2(32169, 32687) }, + { CDF2(32479, 32689) }, { CDF2(31673, 32634) }, + }, + }, { + { + { CDF2( 2809, 19301) }, { CDF2(32205, 32622) }, + { CDF2(32338, 32730) }, { CDF2(31786, 32616) }, + }, { + { CDF2(22737, 29105) }, { CDF2(30810, 32362) }, + { CDF2(30014, 32627) }, { CDF2(30528, 32574) }, + }, + }, { + { + { CDF2( 935, 3382) }, { CDF2(30789, 31909) }, + { CDF2(32466, 32756) }, { CDF2(30860, 32513) }, + }, { + { CDF2(10923, 21845) }, { CDF2(10923, 21845) }, + { CDF2(10923, 21845) }, { CDF2(10923, 21845) }, + }, + }, + }, .base_tok = { + { + { + { CDF3( 8896, 16227, 20630) }, + { CDF3(23629, 31782, 32527) }, + { CDF3(15173, 27755, 31321) }, + { CDF3(10158, 21233, 27382) }, + { CDF3( 6420, 14857, 21558) }, + { CDF3( 3269, 8155, 12646) }, + { CDF3(24835, 32009, 32496) }, + { CDF3(16509, 28421, 31579) }, + { CDF3(10957, 21514, 27418) }, + { CDF3( 7881, 15930, 22096) }, + { CDF3( 5388, 10960, 15918) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3(20745, 30773, 32093) }, + { CDF3(15200, 27221, 30861) }, + { CDF3(13032, 20873, 25667) }, + { CDF3(12285, 18663, 23494) }, + { CDF3(11563, 17481, 21489) }, + { CDF3(26260, 31982, 32320) }, + { CDF3(15397, 28083, 31100) }, + { CDF3( 9742, 19217, 24824) }, + { CDF3( 3261, 9629, 15362) }, + { CDF3( 1480, 4322, 7499) }, + { CDF3(27599, 32256, 32460) }, + { CDF3(16857, 27659, 30774) }, + { CDF3( 9551, 18290, 23748) }, + { CDF3( 3052, 8933, 14103) }, + { CDF3( 2021, 5910, 9787) }, + { CDF3(29005, 32015, 32392) }, + { CDF3(17677, 27694, 30863) }, + { CDF3( 9204, 17356, 23219) }, + { CDF3( 2403, 7516, 12814) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3(10808, 22056, 26896) }, + { CDF3(25739, 32313, 32676) }, + { CDF3(17288, 30203, 32221) }, + { CDF3(11359, 24878, 29896) }, + { CDF3( 6949, 17767, 24893) }, + { CDF3( 4287, 11796, 18071) }, + { CDF3(27880, 32521, 32705) }, + { CDF3(19038, 31004, 32414) }, + { CDF3(12564, 26345, 30768) }, + { CDF3( 8269, 19947, 26779) }, + { CDF3( 5674, 14657, 21674) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3(25742, 32319, 32671) }, + { CDF3(19557, 31164, 32454) }, + { CDF3(13381, 26381, 30755) }, + { CDF3(10101, 21466, 26722) }, + { CDF3( 9209, 19650, 26825) }, + { CDF3(27107, 31917, 32432) }, + { CDF3(18056, 28893, 31203) }, + { CDF3(10200, 21434, 26764) }, + { CDF3( 4660, 12913, 19502) }, + { CDF3( 2368, 6930, 12504) }, + { CDF3(26960, 32158, 32613) }, + { CDF3(18628, 30005, 32031) }, + { CDF3(10233, 22442, 28232) }, + { CDF3( 5471, 14630, 21516) }, + { CDF3( 3235, 10767, 17109) }, + { CDF3(27696, 32440, 32692) }, + { CDF3(20032, 31167, 32438) }, + { CDF3( 8700, 21341, 28442) }, + { CDF3( 5662, 14831, 21795) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, { + { + { CDF3( 9704, 17294, 21132) }, + { CDF3(26762, 32278, 32633) }, + { CDF3(18382, 29620, 31819) }, + { CDF3(10891, 23475, 28723) }, + { CDF3( 6358, 16583, 23309) }, + { CDF3( 3248, 9118, 14141) }, + { CDF3(27204, 32573, 32699) }, + { CDF3(19818, 30824, 32329) }, + { CDF3(11772, 25120, 30041) }, + { CDF3( 6995, 18033, 25039) }, + { CDF3( 3752, 10442, 16098) }, + { CDF3(27222, 32256, 32559) }, + { CDF3(15356, 28399, 31475) }, + { CDF3( 8821, 20635, 27057) }, + { CDF3( 5511, 14404, 21239) }, + { CDF3( 2935, 8222, 13051) }, + { CDF3(24875, 32120, 32529) }, + { CDF3(15233, 28265, 31445) }, + { CDF3( 8605, 20570, 26932) }, + { CDF3( 5431, 14413, 21196) }, + { CDF3( 2994, 8341, 13223) }, + { CDF3(28201, 32604, 32700) }, + { CDF3(21041, 31446, 32456) }, + { CDF3(13221, 26213, 30475) }, + { CDF3( 8255, 19385, 26037) }, + { CDF3( 4930, 12585, 18830) }, + { CDF3(28768, 32448, 32627) }, + { CDF3(19705, 30561, 32021) }, + { CDF3(11572, 23589, 28220) }, + { CDF3( 5532, 15034, 21446) }, + { CDF3( 2460, 7150, 11456) }, + { CDF3(29874, 32619, 32699) }, + { CDF3(21621, 31071, 32201) }, + { CDF3(12511, 24747, 28992) }, + { CDF3( 6281, 16395, 22748) }, + { CDF3( 3246, 9278, 14497) }, + { CDF3(29715, 32625, 32712) }, + { CDF3(20958, 31011, 32283) }, + { CDF3(11233, 23671, 28806) }, + { CDF3( 6012, 16128, 22868) }, + { CDF3( 3427, 9851, 15414) }, + }, { + { CDF3(11016, 22111, 26794) }, + { CDF3(25946, 32357, 32677) }, + { CDF3(17890, 30452, 32252) }, + { CDF3(11678, 25142, 29816) }, + { CDF3( 6720, 17534, 24584) }, + { CDF3( 4230, 11665, 17820) }, + { CDF3(28400, 32623, 32747) }, + { CDF3(21164, 31668, 32575) }, + { CDF3(13572, 27388, 31182) }, + { CDF3( 8234, 20750, 27358) }, + { CDF3( 5065, 14055, 20897) }, + { CDF3(28981, 32547, 32705) }, + { CDF3(18681, 30543, 32239) }, + { CDF3(10919, 24075, 29286) }, + { CDF3( 6431, 17199, 24077) }, + { CDF3( 3819, 10464, 16618) }, + { CDF3(26870, 32467, 32693) }, + { CDF3(19041, 30831, 32347) }, + { CDF3(11794, 25211, 30016) }, + { CDF3( 6888, 18019, 24970) }, + { CDF3( 4370, 12363, 18992) }, + { CDF3(29578, 32670, 32744) }, + { CDF3(23159, 32007, 32613) }, + { CDF3(15315, 28669, 31676) }, + { CDF3( 9298, 22607, 28782) }, + { CDF3( 6144, 15913, 22968) }, + { CDF3(28110, 32499, 32669) }, + { CDF3(21574, 30937, 32015) }, + { CDF3(12759, 24818, 28727) }, + { CDF3( 6545, 16761, 23042) }, + { CDF3( 3649, 10597, 16833) }, + { CDF3(28163, 32552, 32728) }, + { CDF3(22101, 31469, 32464) }, + { CDF3(13160, 25472, 30143) }, + { CDF3( 7303, 18684, 25468) }, + { CDF3( 5241, 13975, 20955) }, + { CDF3(28400, 32631, 32744) }, + { CDF3(22104, 31793, 32603) }, + { CDF3(13557, 26571, 30846) }, + { CDF3( 7749, 19861, 26675) }, + { CDF3( 4873, 14030, 21234) }, + }, + }, { + { + { CDF3( 9800, 17635, 21073) }, + { CDF3(26153, 31885, 32527) }, + { CDF3(15038, 27852, 31006) }, + { CDF3( 8718, 20564, 26486) }, + { CDF3( 5128, 14076, 20514) }, + { CDF3( 2636, 7566, 11925) }, + { CDF3(27551, 32504, 32701) }, + { CDF3(18310, 30054, 32100) }, + { CDF3(10211, 23420, 29082) }, + { CDF3( 6222, 16876, 23916) }, + { CDF3( 3462, 9954, 15498) }, + { CDF3(29991, 32633, 32721) }, + { CDF3(19883, 30751, 32201) }, + { CDF3(11141, 24184, 29285) }, + { CDF3( 6420, 16940, 23774) }, + { CDF3( 3392, 9753, 15118) }, + { CDF3(28465, 32616, 32712) }, + { CDF3(19850, 30702, 32244) }, + { CDF3(10983, 24024, 29223) }, + { CDF3( 6294, 16770, 23582) }, + { CDF3( 3244, 9283, 14509) }, + { CDF3(30023, 32717, 32748) }, + { CDF3(22940, 32032, 32626) }, + { CDF3(14282, 27928, 31473) }, + { CDF3( 8562, 21327, 27914) }, + { CDF3( 4846, 13393, 19919) }, + { CDF3(29981, 32590, 32695) }, + { CDF3(20465, 30963, 32166) }, + { CDF3(11479, 23579, 28195) }, + { CDF3( 5916, 15648, 22073) }, + { CDF3( 3031, 8605, 13398) }, + { CDF3(31146, 32691, 32739) }, + { CDF3(23106, 31724, 32444) }, + { CDF3(13783, 26738, 30439) }, + { CDF3( 7852, 19468, 25807) }, + { CDF3( 3860, 11124, 16853) }, + { CDF3(31014, 32724, 32748) }, + { CDF3(23629, 32109, 32628) }, + { CDF3(14747, 28115, 31403) }, + { CDF3( 8545, 21242, 27478) }, + { CDF3( 4574, 12781, 19067) }, + }, { + { CDF3( 9185, 19694, 24688) }, + { CDF3(26081, 31985, 32621) }, + { CDF3(16015, 29000, 31787) }, + { CDF3(10542, 23690, 29206) }, + { CDF3( 6732, 17945, 24677) }, + { CDF3( 3916, 11039, 16722) }, + { CDF3(28224, 32566, 32744) }, + { CDF3(19100, 31138, 32485) }, + { CDF3(12528, 26620, 30879) }, + { CDF3( 7741, 20277, 26885) }, + { CDF3( 4566, 12845, 18990) }, + { CDF3(29933, 32593, 32718) }, + { CDF3(17670, 30333, 32155) }, + { CDF3(10385, 23600, 28909) }, + { CDF3( 6243, 16236, 22407) }, + { CDF3( 3976, 10389, 16017) }, + { CDF3(28377, 32561, 32738) }, + { CDF3(19366, 31175, 32482) }, + { CDF3(13327, 27175, 31094) }, + { CDF3( 8258, 20769, 27143) }, + { CDF3( 4703, 13198, 19527) }, + { CDF3(31086, 32706, 32748) }, + { CDF3(22853, 31902, 32583) }, + { CDF3(14759, 28186, 31419) }, + { CDF3( 9284, 22382, 28348) }, + { CDF3( 5585, 15192, 21868) }, + { CDF3(28291, 32652, 32746) }, + { CDF3(19849, 32107, 32571) }, + { CDF3(14834, 26818, 29214) }, + { CDF3(10306, 22594, 28672) }, + { CDF3( 6615, 17384, 23384) }, + { CDF3(28947, 32604, 32745) }, + { CDF3(25625, 32289, 32646) }, + { CDF3(18758, 28672, 31403) }, + { CDF3(10017, 23430, 28523) }, + { CDF3( 6862, 15269, 22131) }, + { CDF3(23933, 32509, 32739) }, + { CDF3(19927, 31495, 32631) }, + { CDF3(11903, 26023, 30621) }, + { CDF3( 7026, 20094, 27252) }, + { CDF3( 5998, 18106, 24437) }, + }, + }, { + { + { CDF3( 4456, 11274, 15533) }, + { CDF3(21219, 29079, 31616) }, + { CDF3(11173, 23774, 28567) }, + { CDF3( 7282, 18293, 24263) }, + { CDF3( 4890, 13286, 19115) }, + { CDF3( 1890, 5508, 8659) }, + { CDF3(26651, 32136, 32647) }, + { CDF3(14630, 28254, 31455) }, + { CDF3( 8716, 21287, 27395) }, + { CDF3( 5615, 15331, 22008) }, + { CDF3( 2675, 7700, 12150) }, + { CDF3(29954, 32526, 32690) }, + { CDF3(16126, 28982, 31633) }, + { CDF3( 9030, 21361, 27352) }, + { CDF3( 5411, 14793, 21271) }, + { CDF3( 2943, 8422, 13163) }, + { CDF3(29539, 32601, 32730) }, + { CDF3(18125, 30385, 32201) }, + { CDF3(10422, 24090, 29468) }, + { CDF3( 6468, 17487, 24438) }, + { CDF3( 2970, 8653, 13531) }, + { CDF3(30912, 32715, 32748) }, + { CDF3(20666, 31373, 32497) }, + { CDF3(12509, 26640, 30917) }, + { CDF3( 8058, 20629, 27290) }, + { CDF3( 4231, 12006, 18052) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3(10202, 20633, 25484) }, + { CDF3(27336, 31445, 32352) }, + { CDF3(12420, 24384, 28552) }, + { CDF3( 7648, 18115, 23856) }, + { CDF3( 5662, 14341, 19902) }, + { CDF3( 3611, 10328, 15390) }, + { CDF3(30945, 32616, 32736) }, + { CDF3(18682, 30505, 32253) }, + { CDF3(11513, 25336, 30203) }, + { CDF3( 7449, 19452, 26148) }, + { CDF3( 4482, 13051, 18886) }, + { CDF3(32022, 32690, 32747) }, + { CDF3(18578, 30501, 32146) }, + { CDF3(11249, 23368, 28631) }, + { CDF3( 5645, 16958, 22158) }, + { CDF3( 5009, 11444, 16637) }, + { CDF3(31357, 32710, 32748) }, + { CDF3(21552, 31494, 32504) }, + { CDF3(13891, 27677, 31340) }, + { CDF3( 9051, 22098, 28172) }, + { CDF3( 5190, 13377, 19486) }, + { CDF3(32364, 32740, 32748) }, + { CDF3(24839, 31907, 32551) }, + { CDF3(17160, 28779, 31696) }, + { CDF3(12452, 24137, 29602) }, + { CDF3( 6165, 15389, 22477) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, { + { + { CDF3( 2575, 7281, 11077) }, + { CDF3(14002, 20866, 25402) }, + { CDF3( 6343, 15056, 19658) }, + { CDF3( 4474, 11858, 17041) }, + { CDF3( 2865, 8299, 12534) }, + { CDF3( 1344, 3949, 6391) }, + { CDF3(24720, 31239, 32459) }, + { CDF3(12585, 25356, 29968) }, + { CDF3( 7181, 18246, 24444) }, + { CDF3( 5025, 13667, 19885) }, + { CDF3( 2521, 7304, 11605) }, + { CDF3(29908, 32252, 32584) }, + { CDF3(17421, 29156, 31575) }, + { CDF3( 9889, 22188, 27782) }, + { CDF3( 5878, 15647, 22123) }, + { CDF3( 2814, 8665, 13323) }, + { CDF3(30183, 32568, 32713) }, + { CDF3(18528, 30195, 32049) }, + { CDF3(10982, 24606, 29657) }, + { CDF3( 6957, 18165, 25231) }, + { CDF3( 3508, 10118, 15468) }, + { CDF3(31761, 32736, 32748) }, + { CDF3(21041, 31328, 32546) }, + { CDF3(12568, 26732, 31166) }, + { CDF3( 8052, 20720, 27733) }, + { CDF3( 4336, 12192, 18396) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, + }, .dc_sign = { + { { CDF1(16000) }, { CDF1(13056) }, { CDF1(18816) } }, + { { CDF1(15232) }, { CDF1(12928) }, { CDF1(17280) } }, + }, .br_tok = { + { + { + { CDF3(16138, 22223, 25509) }, + { CDF3(15347, 22430, 26332) }, + { CDF3( 9614, 16736, 21332) }, + { CDF3( 6600, 12275, 16907) }, + { CDF3( 4811, 9424, 13547) }, + { CDF3( 3748, 7809, 11420) }, + { CDF3( 2254, 4587, 6890) }, + { CDF3(15196, 20284, 23177) }, + { CDF3(18317, 25469, 28451) }, + { CDF3(13918, 21651, 25842) }, + { CDF3(10052, 17150, 21995) }, + { CDF3( 7499, 13630, 18587) }, + { CDF3( 6158, 11417, 16003) }, + { CDF3( 4014, 7785, 11252) }, + { CDF3(15048, 21067, 24384) }, + { CDF3(18202, 25346, 28553) }, + { CDF3(14302, 22019, 26356) }, + { CDF3(10839, 18139, 23166) }, + { CDF3( 8715, 15744, 20806) }, + { CDF3( 7536, 13576, 18544) }, + { CDF3( 5413, 10335, 14498) }, + }, { + { CDF3(17394, 24501, 27895) }, + { CDF3(15889, 23420, 27185) }, + { CDF3(11561, 19133, 23870) }, + { CDF3( 8285, 14812, 19844) }, + { CDF3( 6496, 12043, 16550) }, + { CDF3( 4771, 9574, 13677) }, + { CDF3( 3603, 6830, 10144) }, + { CDF3(21656, 27704, 30200) }, + { CDF3(21324, 27915, 30511) }, + { CDF3(17327, 25336, 28997) }, + { CDF3(13417, 21381, 26033) }, + { CDF3(10132, 17425, 22338) }, + { CDF3( 8580, 15016, 19633) }, + { CDF3( 5694, 11477, 16411) }, + { CDF3(24116, 29780, 31450) }, + { CDF3(23853, 29695, 31591) }, + { CDF3(20085, 27614, 30428) }, + { CDF3(15326, 24335, 28575) }, + { CDF3(11814, 19472, 24810) }, + { CDF3(10221, 18611, 24767) }, + { CDF3( 7689, 14558, 20321) }, + }, + }, { + { + { CDF3(16214, 22380, 25770) }, + { CDF3(14213, 21304, 25295) }, + { CDF3( 9213, 15823, 20455) }, + { CDF3( 6395, 11758, 16139) }, + { CDF3( 4779, 9187, 13066) }, + { CDF3( 3821, 7501, 10953) }, + { CDF3( 2293, 4567, 6795) }, + { CDF3(15859, 21283, 23820) }, + { CDF3(18404, 25602, 28726) }, + { CDF3(14325, 21980, 26206) }, + { CDF3(10669, 17937, 22720) }, + { CDF3( 8297, 14642, 19447) }, + { CDF3( 6746, 12389, 16893) }, + { CDF3( 4324, 8251, 11770) }, + { CDF3(16532, 21631, 24475) }, + { CDF3(20667, 27150, 29668) }, + { CDF3(16728, 24510, 28175) }, + { CDF3(12861, 20645, 25332) }, + { CDF3(10076, 17361, 22417) }, + { CDF3( 8395, 14940, 19963) }, + { CDF3( 5731, 10683, 14912) }, + }, { + { CDF3(14433, 21155, 24938) }, + { CDF3(14658, 21716, 25545) }, + { CDF3( 9923, 16824, 21557) }, + { CDF3( 6982, 13052, 17721) }, + { CDF3( 5419, 10503, 15050) }, + { CDF3( 4852, 9162, 13014) }, + { CDF3( 3271, 6395, 9630) }, + { CDF3(22210, 27833, 30109) }, + { CDF3(20750, 27368, 29821) }, + { CDF3(16894, 24828, 28573) }, + { CDF3(13247, 21276, 25757) }, + { CDF3(10038, 17265, 22563) }, + { CDF3( 8587, 14947, 20327) }, + { CDF3( 5645, 11371, 15252) }, + { CDF3(22027, 27526, 29714) }, + { CDF3(23098, 29146, 31221) }, + { CDF3(19886, 27341, 30272) }, + { CDF3(15609, 23747, 28046) }, + { CDF3(11993, 20065, 24939) }, + { CDF3( 9637, 18267, 23671) }, + { CDF3( 7625, 13801, 19144) }, + }, + }, { + { + { CDF3(14438, 20798, 24089) }, + { CDF3(12621, 19203, 23097) }, + { CDF3( 8177, 14125, 18402) }, + { CDF3( 5674, 10501, 14456) }, + { CDF3( 4236, 8239, 11733) }, + { CDF3( 3447, 6750, 9806) }, + { CDF3( 1986, 3950, 5864) }, + { CDF3(16208, 22099, 24930) }, + { CDF3(16537, 24025, 27585) }, + { CDF3(12780, 20381, 24867) }, + { CDF3( 9767, 16612, 21416) }, + { CDF3( 7686, 13738, 18398) }, + { CDF3( 6333, 11614, 15964) }, + { CDF3( 3941, 7571, 10836) }, + { CDF3(22819, 27422, 29202) }, + { CDF3(22224, 28514, 30721) }, + { CDF3(17660, 25433, 28913) }, + { CDF3(13574, 21482, 26002) }, + { CDF3(10629, 17977, 22938) }, + { CDF3( 8612, 15298, 20265) }, + { CDF3( 5607, 10491, 14596) }, + }, { + { CDF3(13569, 19800, 23206) }, + { CDF3(13128, 19924, 23869) }, + { CDF3( 8329, 14841, 19403) }, + { CDF3( 6130, 10976, 15057) }, + { CDF3( 4682, 8839, 12518) }, + { CDF3( 3656, 7409, 10588) }, + { CDF3( 2577, 5099, 7412) }, + { CDF3(22427, 28684, 30585) }, + { CDF3(20913, 27750, 30139) }, + { CDF3(15840, 24109, 27834) }, + { CDF3(12308, 20029, 24569) }, + { CDF3(10216, 16785, 21458) }, + { CDF3( 8309, 14203, 19113) }, + { CDF3( 6043, 11168, 15307) }, + { CDF3(23166, 28901, 30998) }, + { CDF3(21899, 28405, 30751) }, + { CDF3(18413, 26091, 29443) }, + { CDF3(15233, 23114, 27352) }, + { CDF3(12683, 20472, 25288) }, + { CDF3(10702, 18259, 23409) }, + { CDF3( 8125, 14464, 19226) }, + }, + }, { + { + { CDF3( 9040, 14786, 18360) }, + { CDF3( 9979, 15718, 19415) }, + { CDF3( 7913, 13918, 18311) }, + { CDF3( 5859, 10889, 15184) }, + { CDF3( 4593, 8677, 12510) }, + { CDF3( 3820, 7396, 10791) }, + { CDF3( 1730, 3471, 5192) }, + { CDF3(11803, 18365, 22709) }, + { CDF3(11419, 18058, 22225) }, + { CDF3( 9418, 15774, 20243) }, + { CDF3( 7539, 13325, 17657) }, + { CDF3( 6233, 11317, 15384) }, + { CDF3( 5137, 9656, 13545) }, + { CDF3( 2977, 5774, 8349) }, + { CDF3(21207, 27246, 29640) }, + { CDF3(19547, 26578, 29497) }, + { CDF3(16169, 23871, 27690) }, + { CDF3(12820, 20458, 25018) }, + { CDF3(10224, 17332, 22214) }, + { CDF3( 8526, 15048, 19884) }, + { CDF3( 5037, 9410, 13118) }, + }, { + { CDF3(12339, 17329, 20140) }, + { CDF3(13505, 19895, 23225) }, + { CDF3( 9847, 16944, 21564) }, + { CDF3( 7280, 13256, 18348) }, + { CDF3( 4712, 10009, 14454) }, + { CDF3( 4361, 7914, 12477) }, + { CDF3( 2870, 5628, 7995) }, + { CDF3(20061, 25504, 28526) }, + { CDF3(15235, 22878, 26145) }, + { CDF3(12985, 19958, 24155) }, + { CDF3( 9782, 16641, 21403) }, + { CDF3( 9456, 16360, 20760) }, + { CDF3( 6855, 12940, 18557) }, + { CDF3( 5661, 10564, 15002) }, + { CDF3(25656, 30602, 31894) }, + { CDF3(22570, 29107, 31092) }, + { CDF3(18917, 26423, 29541) }, + { CDF3(15940, 23649, 27754) }, + { CDF3(12803, 20581, 25219) }, + { CDF3(11082, 18695, 23376) }, + { CDF3( 7939, 14373, 19005) }, + }, + }, + }, + }, [3] = { + .skip = { + { + { CDF1(26887) }, { CDF1( 6729) }, { CDF1(10361) }, + { CDF1(17442) }, { CDF1(15045) }, { CDF1(22478) }, + { CDF1(29072) }, { CDF1( 2713) }, { CDF1(11861) }, + { CDF1(20773) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(31903) }, { CDF1( 2044) }, { CDF1( 7528) }, + { CDF1(14618) }, { CDF1(16182) }, { CDF1(24168) }, + { CDF1(31037) }, { CDF1( 2786) }, { CDF1(11194) }, + { CDF1(20155) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(32510) }, { CDF1( 8430) }, { CDF1(17318) }, + { CDF1(24154) }, { CDF1(23674) }, { CDF1(28789) }, + { CDF1(32139) }, { CDF1( 3440) }, { CDF1(13117) }, + { CDF1(22702) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, { + { CDF1(31671) }, { CDF1( 2056) }, { CDF1(11746) }, + { CDF1(16852) }, { CDF1(18635) }, { CDF1(24715) }, + { CDF1(31484) }, { CDF1( 4656) }, { CDF1(16074) }, + { CDF1(24704) }, { CDF1( 1806) }, { CDF1(14645) }, + { CDF1(25336) }, + }, { + { CDF1(31539) }, { CDF1( 8433) }, { CDF1(20576) }, + { CDF1(27904) }, { CDF1(27852) }, { CDF1(30026) }, + { CDF1(32441) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, + }, + }, .eob_bin_16 = { + { + { CDF4( 6708, 8958, 14746, 22133) }, + { CDF4( 1222, 2074, 4783, 15410) }, + }, { + { CDF4(19575, 21766, 26044, 29709) }, + { CDF4( 7297, 10767, 19273, 28194) }, + }, + }, .eob_bin_32 = { + { + { CDF5( 4617, 5709, 8446, 13584, 23135) }, + { CDF5( 1156, 1702, 3675, 9274, 20539) }, + }, { + { CDF5(22086, 24282, 27010, 29770, 31743) }, + { CDF5( 7699, 10897, 20891, 26926, 31628) }, + }, + }, .eob_bin_64 = { + { + { CDF6( 6307, 7541, 12060, 16358, 22553, 27865) }, + { CDF6( 1289, 2320, 3971, 7926, 14153, 24291) }, + }, { + { CDF6(24212, 25708, 28268, 30035, 31307, 32049) }, + { CDF6( 8726, 12378, 19409, 26450, 30038, 32462) }, + }, + }, .eob_bin_128 = { + { + { CDF7( 3472, 4885, 7489, 12481, 18517, 24536, 29635) }, + { CDF7( 886, 1731, 3271, 8469, 15569, 22126, 28383) }, + }, { + { CDF7(24313, 26062, 28385, 30107, 31217, 31898, 32345) }, + { CDF7( 9165, 13282, 21150, 30286, 31894, 32571, 32712) }, + }, + }, .eob_bin_256 = { + { + { CDF8( 5348, 7113, 11820, 15924, + 22106, 26777, 30334, 31757) }, + { CDF8( 2453, 4474, 6307, 8777, + 16474, 22975, 29000, 31547) }, + }, { + { CDF8(23110, 24597, 27140, 28894, + 30167, 30927, 31392, 32094) }, + { CDF8( 9998, 17661, 25178, 28097, + 31308, 32038, 32403, 32695) }, + }, + }, .eob_bin_512 = { + { CDF9( 5927, 7809, 10923, 14597, 19439, + 24135, 28456, 31142, 32060) }, + { CDF9(21093, 23043, 25742, 27658, 29097, + 29716, 30073, 30820, 31956) }, + }, .eob_bin_1024 = { + { CDF10( 6698, 8334, 11961, 15762, 20186, + 23862, 27434, 29326, 31082, 32050) }, + { CDF10(20569, 22426, 25569, 26859, 28053, + 28913, 29486, 29724, 29807, 32570) }, + }, .eob_hi_bit = { + { + { + { CDF1(20177) }, { CDF1(20789) }, { CDF1(20262) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(21416) }, { CDF1(20855) }, { CDF1(23410) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(20238) }, { CDF1(21057) }, { CDF1(19159) }, + { CDF1(22337) }, { CDF1(20159) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(20125) }, { CDF1(20559) }, { CDF1(21707) }, + { CDF1(22296) }, { CDF1(17333) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(19941) }, { CDF1(20527) }, { CDF1(21470) }, + { CDF1(22487) }, { CDF1(19558) }, { CDF1(22354) }, + { CDF1(20331) }, { CDF1(16384) }, { CDF1(16384) }, + }, { + { CDF1(22752) }, { CDF1(25006) }, { CDF1(22075) }, + { CDF1(21576) }, { CDF1(17740) }, { CDF1(21690) }, + { CDF1(19211) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, { + { + { CDF1(21442) }, { CDF1(22358) }, { CDF1(18503) }, + { CDF1(20291) }, { CDF1(19945) }, { CDF1(21294) }, + { CDF1(21178) }, { CDF1(19400) }, { CDF1(10556) }, + }, { + { CDF1(24648) }, { CDF1(24949) }, { CDF1(20708) }, + { CDF1(23905) }, { CDF1(20501) }, { CDF1( 9558) }, + { CDF1( 9423) }, { CDF1(30365) }, { CDF1(19253) }, + }, + }, { + { + { CDF1(26064) }, { CDF1(22098) }, { CDF1(19613) }, + { CDF1(20525) }, { CDF1(17595) }, { CDF1(16618) }, + { CDF1(20497) }, { CDF1(18989) }, { CDF1(15513) }, + }, { + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + { CDF1(16384) }, { CDF1(16384) }, { CDF1(16384) }, + }, + }, + }, .eob_base_tok = { + { + { + { CDF2(22497, 31198) }, { CDF2(31715, 32495) }, + { CDF2(31606, 32337) }, { CDF2(30388, 31990) }, + }, { + { CDF2(27877, 31584) }, { CDF2(32170, 32728) }, + { CDF2(32155, 32688) }, { CDF2(32219, 32702) }, + }, + }, { + { + { CDF2(21457, 31043) }, { CDF2(31951, 32483) }, + { CDF2(32153, 32562) }, { CDF2(31473, 32215) }, + }, { + { CDF2(27558, 31151) }, { CDF2(32020, 32640) }, + { CDF2(32097, 32575) }, { CDF2(32242, 32719) }, + }, + }, { + { + { CDF2(19980, 30591) }, { CDF2(32219, 32597) }, + { CDF2(32581, 32706) }, { CDF2(31803, 32287) }, + }, { + { CDF2(26473, 30507) }, { CDF2(32431, 32723) }, + { CDF2(32196, 32611) }, { CDF2(31588, 32528) }, + }, + }, { + { + { CDF2(24647, 30463) }, { CDF2(32412, 32695) }, + { CDF2(32468, 32720) }, { CDF2(31269, 32523) }, + }, { + { CDF2(28482, 31505) }, { CDF2(32152, 32701) }, + { CDF2(31732, 32598) }, { CDF2(31767, 32712) }, + }, + }, { + { + { CDF2(12358, 24977) }, { CDF2(31331, 32385) }, + { CDF2(32634, 32756) }, { CDF2(30411, 32548) }, + }, { + { CDF2(10923, 21845) }, { CDF2(10923, 21845) }, + { CDF2(10923, 21845) }, { CDF2(10923, 21845) }, + }, + }, + }, .base_tok = { + { + { + { CDF3( 7062, 16472, 22319) }, + { CDF3(24538, 32261, 32674) }, + { CDF3(13675, 28041, 31779) }, + { CDF3( 8590, 20674, 27631) }, + { CDF3( 5685, 14675, 22013) }, + { CDF3( 3655, 9898, 15731) }, + { CDF3(26493, 32418, 32658) }, + { CDF3(16376, 29342, 32090) }, + { CDF3(10594, 22649, 28970) }, + { CDF3( 8176, 17170, 24303) }, + { CDF3( 5605, 12694, 19139) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3(23888, 31902, 32542) }, + { CDF3(18612, 29687, 31987) }, + { CDF3(16245, 24852, 29249) }, + { CDF3(15765, 22608, 27559) }, + { CDF3(19895, 24699, 27510) }, + { CDF3(28401, 32212, 32457) }, + { CDF3(15274, 27825, 30980) }, + { CDF3( 9364, 18128, 24332) }, + { CDF3( 2283, 8193, 15082) }, + { CDF3( 1228, 3972, 7881) }, + { CDF3(29455, 32469, 32620) }, + { CDF3(17981, 28245, 31388) }, + { CDF3(10921, 20098, 26240) }, + { CDF3( 3743, 11829, 18657) }, + { CDF3( 2374, 9593, 15715) }, + { CDF3(31068, 32466, 32635) }, + { CDF3(20321, 29572, 31971) }, + { CDF3(10771, 20255, 27119) }, + { CDF3( 2795, 10410, 17361) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3( 9320, 22102, 27840) }, + { CDF3(27057, 32464, 32724) }, + { CDF3(16331, 30268, 32309) }, + { CDF3(10319, 23935, 29720) }, + { CDF3( 6189, 16448, 24106) }, + { CDF3( 3589, 10884, 18808) }, + { CDF3(29026, 32624, 32748) }, + { CDF3(19226, 31507, 32587) }, + { CDF3(12692, 26921, 31203) }, + { CDF3( 7049, 19532, 27635) }, + { CDF3( 7727, 15669, 23252) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3(28056, 32625, 32748) }, + { CDF3(22383, 32075, 32669) }, + { CDF3(15417, 27098, 31749) }, + { CDF3(18127, 26493, 27190) }, + { CDF3( 5461, 16384, 21845) }, + { CDF3(27982, 32091, 32584) }, + { CDF3(19045, 29868, 31972) }, + { CDF3(10397, 22266, 27932) }, + { CDF3( 5990, 13697, 21500) }, + { CDF3( 1792, 6912, 15104) }, + { CDF3(28198, 32501, 32718) }, + { CDF3(21534, 31521, 32569) }, + { CDF3(11109, 25217, 30017) }, + { CDF3( 5671, 15124, 26151) }, + { CDF3( 4681, 14043, 18725) }, + { CDF3(28688, 32580, 32741) }, + { CDF3(22576, 32079, 32661) }, + { CDF3(10627, 22141, 28340) }, + { CDF3( 9362, 14043, 28087) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, { + { + { CDF3( 7754, 16948, 22142) }, + { CDF3(25670, 32330, 32691) }, + { CDF3(15663, 29225, 31994) }, + { CDF3( 9878, 23288, 29158) }, + { CDF3( 6419, 17088, 24336) }, + { CDF3( 3859, 11003, 17039) }, + { CDF3(27562, 32595, 32725) }, + { CDF3(17575, 30588, 32399) }, + { CDF3(10819, 24838, 30309) }, + { CDF3( 7124, 18686, 25916) }, + { CDF3( 4479, 12688, 19340) }, + { CDF3(28385, 32476, 32673) }, + { CDF3(15306, 29005, 31938) }, + { CDF3( 8937, 21615, 28322) }, + { CDF3( 5982, 15603, 22786) }, + { CDF3( 3620, 10267, 16136) }, + { CDF3(27280, 32464, 32667) }, + { CDF3(15607, 29160, 32004) }, + { CDF3( 9091, 22135, 28740) }, + { CDF3( 6232, 16632, 24020) }, + { CDF3( 4047, 11377, 17672) }, + { CDF3(29220, 32630, 32718) }, + { CDF3(19650, 31220, 32462) }, + { CDF3(13050, 26312, 30827) }, + { CDF3( 9228, 20870, 27468) }, + { CDF3( 6146, 15149, 21971) }, + { CDF3(30169, 32481, 32623) }, + { CDF3(17212, 29311, 31554) }, + { CDF3( 9911, 21311, 26882) }, + { CDF3( 4487, 13314, 20372) }, + { CDF3( 2570, 7772, 12889) }, + { CDF3(30924, 32613, 32708) }, + { CDF3(19490, 30206, 32107) }, + { CDF3(11232, 23998, 29276) }, + { CDF3( 6769, 17955, 25035) }, + { CDF3( 4398, 12623, 19214) }, + { CDF3(30609, 32627, 32722) }, + { CDF3(19370, 30582, 32287) }, + { CDF3(10457, 23619, 29409) }, + { CDF3( 6443, 17637, 24834) }, + { CDF3( 4645, 13236, 20106) }, + }, { + { CDF3( 8626, 20271, 26216) }, + { CDF3(26707, 32406, 32711) }, + { CDF3(16999, 30329, 32286) }, + { CDF3(11445, 25123, 30286) }, + { CDF3( 6411, 18828, 25601) }, + { CDF3( 6801, 12458, 20248) }, + { CDF3(29918, 32682, 32748) }, + { CDF3(20649, 31739, 32618) }, + { CDF3(12879, 27773, 31581) }, + { CDF3( 7896, 21751, 28244) }, + { CDF3( 5260, 14870, 23698) }, + { CDF3(29252, 32593, 32731) }, + { CDF3(17072, 30460, 32294) }, + { CDF3(10653, 24143, 29365) }, + { CDF3( 6536, 17490, 23983) }, + { CDF3( 4929, 13170, 20085) }, + { CDF3(28137, 32518, 32715) }, + { CDF3(18171, 30784, 32407) }, + { CDF3(11437, 25436, 30459) }, + { CDF3( 7252, 18534, 26176) }, + { CDF3( 4126, 13353, 20978) }, + { CDF3(31162, 32726, 32748) }, + { CDF3(23017, 32222, 32701) }, + { CDF3(15629, 29233, 32046) }, + { CDF3( 9387, 22621, 29480) }, + { CDF3( 6922, 17616, 25010) }, + { CDF3(28838, 32265, 32614) }, + { CDF3(19701, 30206, 31920) }, + { CDF3(11214, 22410, 27933) }, + { CDF3( 5320, 14177, 23034) }, + { CDF3( 5049, 12881, 17827) }, + { CDF3(27484, 32471, 32734) }, + { CDF3(21076, 31526, 32561) }, + { CDF3(12707, 26303, 31211) }, + { CDF3( 8169, 21722, 28219) }, + { CDF3( 6045, 19406, 27042) }, + { CDF3(27753, 32572, 32745) }, + { CDF3(20832, 31878, 32653) }, + { CDF3(13250, 27356, 31674) }, + { CDF3( 7718, 21508, 29858) }, + { CDF3( 7209, 18350, 25559) }, + }, + }, { + { + { CDF3( 7876, 16901, 21741) }, + { CDF3(24001, 31898, 32625) }, + { CDF3(14529, 27959, 31451) }, + { CDF3( 8273, 20818, 27258) }, + { CDF3( 5278, 14673, 21510) }, + { CDF3( 2983, 8843, 14039) }, + { CDF3(28016, 32574, 32732) }, + { CDF3(17471, 30306, 32301) }, + { CDF3(10224, 24063, 29728) }, + { CDF3( 6602, 17954, 25052) }, + { CDF3( 4002, 11585, 17759) }, + { CDF3(30190, 32634, 32739) }, + { CDF3(17497, 30282, 32270) }, + { CDF3(10229, 23729, 29538) }, + { CDF3( 6344, 17211, 24440) }, + { CDF3( 3849, 11189, 17108) }, + { CDF3(28570, 32583, 32726) }, + { CDF3(17521, 30161, 32238) }, + { CDF3(10153, 23565, 29378) }, + { CDF3( 6455, 17341, 24443) }, + { CDF3( 3907, 11042, 17024) }, + { CDF3(30689, 32715, 32748) }, + { CDF3(21546, 31840, 32610) }, + { CDF3(13547, 27581, 31459) }, + { CDF3( 8912, 21757, 28309) }, + { CDF3( 5548, 15080, 22046) }, + { CDF3(30783, 32540, 32685) }, + { CDF3(17540, 29528, 31668) }, + { CDF3(10160, 21468, 26783) }, + { CDF3( 4724, 13393, 20054) }, + { CDF3( 2702, 8174, 13102) }, + { CDF3(31648, 32686, 32742) }, + { CDF3(20954, 31094, 32337) }, + { CDF3(12420, 25698, 30179) }, + { CDF3( 7304, 19320, 26248) }, + { CDF3( 4366, 12261, 18864) }, + { CDF3(31581, 32723, 32748) }, + { CDF3(21373, 31586, 32525) }, + { CDF3(12744, 26625, 30885) }, + { CDF3( 7431, 20322, 26950) }, + { CDF3( 4692, 13323, 20111) }, + }, { + { CDF3( 7833, 18369, 24095) }, + { CDF3(26650, 32273, 32702) }, + { CDF3(16371, 29961, 32191) }, + { CDF3(11055, 24082, 29629) }, + { CDF3( 6892, 18644, 25400) }, + { CDF3( 5006, 13057, 19240) }, + { CDF3(29834, 32666, 32748) }, + { CDF3(19577, 31335, 32570) }, + { CDF3(12253, 26509, 31122) }, + { CDF3( 7991, 20772, 27711) }, + { CDF3( 5677, 15910, 23059) }, + { CDF3(30109, 32532, 32720) }, + { CDF3(16747, 30166, 32252) }, + { CDF3(10134, 23542, 29184) }, + { CDF3( 5791, 16176, 23556) }, + { CDF3( 4362, 10414, 17284) }, + { CDF3(29492, 32626, 32748) }, + { CDF3(19894, 31402, 32525) }, + { CDF3(12942, 27071, 30869) }, + { CDF3( 8346, 21216, 27405) }, + { CDF3( 6572, 17087, 23859) }, + { CDF3(32035, 32735, 32748) }, + { CDF3(22957, 31838, 32618) }, + { CDF3(14724, 28572, 31772) }, + { CDF3(10364, 23999, 29553) }, + { CDF3( 7004, 18433, 25655) }, + { CDF3(27528, 32277, 32681) }, + { CDF3(16959, 31171, 32096) }, + { CDF3(10486, 23593, 27962) }, + { CDF3( 8192, 16384, 23211) }, + { CDF3( 8937, 17873, 20852) }, + { CDF3(27715, 32002, 32615) }, + { CDF3(15073, 29491, 31676) }, + { CDF3(11264, 24576, 28672) }, + { CDF3( 2341, 18725, 23406) }, + { CDF3( 7282, 18204, 25486) }, + { CDF3(28547, 32213, 32657) }, + { CDF3(20788, 29773, 32239) }, + { CDF3( 6780, 21469, 30508) }, + { CDF3( 5958, 14895, 23831) }, + { CDF3(16384, 21845, 27307) }, + }, + }, { + { + { CDF3( 5992, 14304, 19765) }, + { CDF3(22612, 31238, 32456) }, + { CDF3(13456, 27162, 31087) }, + { CDF3( 8001, 20062, 26504) }, + { CDF3( 5168, 14105, 20764) }, + { CDF3( 2632, 7771, 12385) }, + { CDF3(27034, 32344, 32709) }, + { CDF3(15850, 29415, 31997) }, + { CDF3( 9494, 22776, 28841) }, + { CDF3( 6151, 16830, 23969) }, + { CDF3( 3461, 10039, 15722) }, + { CDF3(30134, 32569, 32731) }, + { CDF3(15638, 29422, 31945) }, + { CDF3( 9150, 21865, 28218) }, + { CDF3( 5647, 15719, 22676) }, + { CDF3( 3402, 9772, 15477) }, + { CDF3(28530, 32586, 32735) }, + { CDF3(17139, 30298, 32292) }, + { CDF3(10200, 24039, 29685) }, + { CDF3( 6419, 17674, 24786) }, + { CDF3( 3544, 10225, 15824) }, + { CDF3(31333, 32726, 32748) }, + { CDF3(20618, 31487, 32544) }, + { CDF3(12901, 27217, 31232) }, + { CDF3( 8624, 21734, 28171) }, + { CDF3( 5104, 14191, 20748) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3(11206, 21090, 26561) }, + { CDF3(28759, 32279, 32671) }, + { CDF3(14171, 27952, 31569) }, + { CDF3( 9743, 22907, 29141) }, + { CDF3( 6871, 17886, 24868) }, + { CDF3( 4960, 13152, 19315) }, + { CDF3(31077, 32661, 32748) }, + { CDF3(19400, 31195, 32515) }, + { CDF3(12752, 26858, 31040) }, + { CDF3( 8370, 22098, 28591) }, + { CDF3( 5457, 15373, 22298) }, + { CDF3(31697, 32706, 32748) }, + { CDF3(17860, 30657, 32333) }, + { CDF3(12510, 24812, 29261) }, + { CDF3( 6180, 19124, 24722) }, + { CDF3( 5041, 13548, 17959) }, + { CDF3(31552, 32716, 32748) }, + { CDF3(21908, 31769, 32623) }, + { CDF3(14470, 28201, 31565) }, + { CDF3( 9493, 22982, 28608) }, + { CDF3( 6858, 17240, 24137) }, + { CDF3(32543, 32752, 32756) }, + { CDF3(24286, 32097, 32666) }, + { CDF3(15958, 29217, 32024) }, + { CDF3(10207, 24234, 29958) }, + { CDF3( 6929, 18305, 25652) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, { + { + { CDF3( 4137, 10847, 15682) }, + { CDF3(17824, 27001, 30058) }, + { CDF3(10204, 22796, 28291) }, + { CDF3( 6076, 15935, 22125) }, + { CDF3( 3852, 10937, 16816) }, + { CDF3( 2252, 6324, 10131) }, + { CDF3(25840, 32016, 32662) }, + { CDF3(15109, 28268, 31531) }, + { CDF3( 9385, 22231, 28340) }, + { CDF3( 6082, 16672, 23479) }, + { CDF3( 3318, 9427, 14681) }, + { CDF3(30594, 32574, 32718) }, + { CDF3(16836, 29552, 31859) }, + { CDF3( 9556, 22542, 28356) }, + { CDF3( 6305, 16725, 23540) }, + { CDF3( 3376, 9895, 15184) }, + { CDF3(29383, 32617, 32745) }, + { CDF3(18891, 30809, 32401) }, + { CDF3(11688, 25942, 30687) }, + { CDF3( 7468, 19469, 26651) }, + { CDF3( 3909, 11358, 17012) }, + { CDF3(31564, 32736, 32748) }, + { CDF3(20906, 31611, 32600) }, + { CDF3(13191, 27621, 31537) }, + { CDF3( 8768, 22029, 28676) }, + { CDF3( 5079, 14109, 20906) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, { + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + { CDF3( 8192, 16384, 24576) }, + }, + }, + }, .dc_sign = { + { { CDF1(16000) }, { CDF1(13056) }, { CDF1(18816) } }, + { { CDF1(15232) }, { CDF1(12928) }, { CDF1(17280) } }, + }, .br_tok = { + { + { + { CDF3(18315, 24289, 27551) }, + { CDF3(16854, 24068, 27835) }, + { CDF3(10140, 17927, 23173) }, + { CDF3( 6722, 12982, 18267) }, + { CDF3( 4661, 9826, 14706) }, + { CDF3( 3832, 8165, 12294) }, + { CDF3( 2795, 6098, 9245) }, + { CDF3(17145, 23326, 26672) }, + { CDF3(20733, 27680, 30308) }, + { CDF3(16032, 24461, 28546) }, + { CDF3(11653, 20093, 25081) }, + { CDF3( 9290, 16429, 22086) }, + { CDF3( 7796, 14598, 19982) }, + { CDF3( 6502, 12378, 17441) }, + { CDF3(21681, 27732, 30320) }, + { CDF3(22389, 29044, 31261) }, + { CDF3(19027, 26731, 30087) }, + { CDF3(14739, 23755, 28624) }, + { CDF3(11358, 20778, 25511) }, + { CDF3(10995, 18073, 24190) }, + { CDF3( 9162, 14990, 20617) }, + }, { + { CDF3(21425, 27952, 30388) }, + { CDF3(18062, 25838, 29034) }, + { CDF3(11956, 19881, 24808) }, + { CDF3( 7718, 15000, 20980) }, + { CDF3( 5702, 11254, 16143) }, + { CDF3( 4898, 9088, 16864) }, + { CDF3( 3679, 6776, 11907) }, + { CDF3(23294, 30160, 31663) }, + { CDF3(24397, 29896, 31836) }, + { CDF3(19245, 27128, 30593) }, + { CDF3(13202, 19825, 26404) }, + { CDF3(11578, 19297, 23957) }, + { CDF3( 8073, 13297, 21370) }, + { CDF3( 5461, 10923, 19745) }, + { CDF3(27367, 30521, 31934) }, + { CDF3(24904, 30671, 31940) }, + { CDF3(23075, 28460, 31299) }, + { CDF3(14400, 23658, 30417) }, + { CDF3(13885, 23882, 28325) }, + { CDF3(14746, 22938, 27853) }, + { CDF3( 5461, 16384, 27307) }, + }, + }, { + { + { CDF3(18274, 24813, 27890) }, + { CDF3(15537, 23149, 27003) }, + { CDF3( 9449, 16740, 21827) }, + { CDF3( 6700, 12498, 17261) }, + { CDF3( 4988, 9866, 14198) }, + { CDF3( 4236, 8147, 11902) }, + { CDF3( 2867, 5860, 8654) }, + { CDF3(17124, 23171, 26101) }, + { CDF3(20396, 27477, 30148) }, + { CDF3(16573, 24629, 28492) }, + { CDF3(12749, 20846, 25674) }, + { CDF3(10233, 17878, 22818) }, + { CDF3( 8525, 15332, 20363) }, + { CDF3( 6283, 11632, 16255) }, + { CDF3(20466, 26511, 29286) }, + { CDF3(23059, 29174, 31191) }, + { CDF3(19481, 27263, 30241) }, + { CDF3(15458, 23631, 28137) }, + { CDF3(12416, 20608, 25693) }, + { CDF3(10261, 18011, 23261) }, + { CDF3( 8016, 14655, 19666) }, + }, { + { CDF3(17616, 24586, 28112) }, + { CDF3(15809, 23299, 27155) }, + { CDF3(10767, 18890, 23793) }, + { CDF3( 7727, 14255, 18865) }, + { CDF3( 6129, 11926, 16882) }, + { CDF3( 4482, 9704, 14861) }, + { CDF3( 3277, 7452, 11522) }, + { CDF3(22956, 28551, 30730) }, + { CDF3(22724, 28937, 30961) }, + { CDF3(18467, 26324, 29580) }, + { CDF3(13234, 20713, 25649) }, + { CDF3(11181, 17592, 22481) }, + { CDF3( 8291, 18358, 24576) }, + { CDF3( 7568, 11881, 14984) }, + { CDF3(24948, 29001, 31147) }, + { CDF3(25674, 30619, 32151) }, + { CDF3(20841, 26793, 29603) }, + { CDF3(14669, 24356, 28666) }, + { CDF3(11334, 23593, 28219) }, + { CDF3( 8922, 14762, 22873) }, + { CDF3( 8301, 13544, 20535) }, + }, + }, { + { + { CDF3(17113, 23733, 27081) }, + { CDF3(14139, 21406, 25452) }, + { CDF3( 8552, 15002, 19776) }, + { CDF3( 5871, 11120, 15378) }, + { CDF3( 4455, 8616, 12253) }, + { CDF3( 3469, 6910, 10386) }, + { CDF3( 2255, 4553, 6782) }, + { CDF3(18224, 24376, 27053) }, + { CDF3(19290, 26710, 29614) }, + { CDF3(14936, 22991, 27184) }, + { CDF3(11238, 18951, 23762) }, + { CDF3( 8786, 15617, 20588) }, + { CDF3( 7317, 13228, 18003) }, + { CDF3( 5101, 9512, 13493) }, + { CDF3(22639, 28222, 30210) }, + { CDF3(23216, 29331, 31307) }, + { CDF3(19075, 26762, 29895) }, + { CDF3(15014, 23113, 27457) }, + { CDF3(11938, 19857, 24752) }, + { CDF3( 9942, 17280, 22282) }, + { CDF3( 7167, 13144, 17752) }, + }, { + { CDF3(15820, 22738, 26488) }, + { CDF3(13530, 20885, 25216) }, + { CDF3( 8395, 15530, 20452) }, + { CDF3( 6574, 12321, 16380) }, + { CDF3( 5353, 10419, 14568) }, + { CDF3( 4613, 8446, 12381) }, + { CDF3( 3440, 7158, 9903) }, + { CDF3(24247, 29051, 31224) }, + { CDF3(22118, 28058, 30369) }, + { CDF3(16498, 24768, 28389) }, + { CDF3(12920, 21175, 26137) }, + { CDF3(10730, 18619, 25352) }, + { CDF3(10187, 16279, 22791) }, + { CDF3( 9310, 14631, 22127) }, + { CDF3(24970, 30558, 32057) }, + { CDF3(24801, 29942, 31698) }, + { CDF3(22432, 28453, 30855) }, + { CDF3(19054, 25680, 29580) }, + { CDF3(14392, 23036, 28109) }, + { CDF3(12495, 20947, 26650) }, + { CDF3(12442, 20326, 26214) }, + }, + }, { + { + { CDF3(12162, 18785, 22648) }, + { CDF3(12749, 19697, 23806) }, + { CDF3( 8580, 15297, 20346) }, + { CDF3( 6169, 11749, 16543) }, + { CDF3( 4836, 9391, 13448) }, + { CDF3( 3821, 7711, 11613) }, + { CDF3( 2228, 4601, 7070) }, + { CDF3(16319, 24725, 28280) }, + { CDF3(15698, 23277, 27168) }, + { CDF3(12726, 20368, 25047) }, + { CDF3( 9912, 17015, 21976) }, + { CDF3( 7888, 14220, 19179) }, + { CDF3( 6777, 12284, 17018) }, + { CDF3( 4492, 8590, 12252) }, + { CDF3(23249, 28904, 30947) }, + { CDF3(21050, 27908, 30512) }, + { CDF3(17440, 25340, 28949) }, + { CDF3(14059, 22018, 26541) }, + { CDF3(11288, 18903, 23898) }, + { CDF3( 9411, 16342, 21428) }, + { CDF3( 6278, 11588, 15944) }, + }, { + { CDF3(13981, 20067, 23226) }, + { CDF3(16922, 23580, 26783) }, + { CDF3(11005, 19039, 24487) }, + { CDF3( 7389, 14218, 19798) }, + { CDF3( 5598, 11505, 17206) }, + { CDF3( 6090, 11213, 15659) }, + { CDF3( 3820, 7371, 10119) }, + { CDF3(21082, 26925, 29675) }, + { CDF3(21262, 28627, 31128) }, + { CDF3(18392, 26454, 30437) }, + { CDF3(14870, 22910, 27096) }, + { CDF3(12620, 19484, 24908) }, + { CDF3( 9290, 16553, 22802) }, + { CDF3( 6668, 14288, 20004) }, + { CDF3(27704, 31055, 31949) }, + { CDF3(24709, 29978, 31788) }, + { CDF3(21668, 29264, 31657) }, + { CDF3(18295, 26968, 30074) }, + { CDF3(16399, 24422, 29313) }, + { CDF3(14347, 23026, 28104) }, + { CDF3(12370, 19806, 24477) }, + }, + }, + }, + } +}; + +void dav1d_cdf_thread_update(const Dav1dFrameHeader *const hdr, + CdfContext *const dst, + const CdfContext *const src) +{ +#define update_cdf_1d(n1d, name) \ + do { \ + dst->name[n1d] = 0; \ + } while (0) +#define update_cdf_2d(n1d, n2d, name) \ + for (int j = 0; j < (n1d); j++) update_cdf_1d(n2d, name[j]) +#define update_cdf_3d(n1d, n2d, n3d, name) \ + for (int k = 0; k < (n1d); k++) update_cdf_2d(n2d, n3d, name[k]) +#define update_cdf_4d(n1d, n2d, n3d, n4d, name) \ + for (int l = 0; l < (n1d); l++) update_cdf_3d(n2d, n3d, n4d, name[l]) + + memcpy(dst, src, offsetof(CdfContext, m.intrabc)); + + update_cdf_3d(2, 2, 4, coef.eob_bin_16); + update_cdf_3d(2, 2, 5, coef.eob_bin_32); + update_cdf_3d(2, 2, 6, coef.eob_bin_64); + update_cdf_3d(2, 2, 7, coef.eob_bin_128); + update_cdf_3d(2, 2, 8, coef.eob_bin_256); + update_cdf_2d(2, 9, coef.eob_bin_512); + update_cdf_2d(2, 10, coef.eob_bin_1024); + update_cdf_4d(N_TX_SIZES, 2, 4, 2, coef.eob_base_tok); + update_cdf_4d(N_TX_SIZES, 2, 41 /*42*/, 3, coef.base_tok); + update_cdf_4d(4, 2, 21, 3, coef.br_tok); + update_cdf_4d(N_TX_SIZES, 2, 9, 1, coef.eob_hi_bit); + update_cdf_3d(N_TX_SIZES, 13, 1, coef.skip); + update_cdf_3d(2, 3, 1, coef.dc_sign); + + update_cdf_3d(2, N_INTRA_PRED_MODES, N_UV_INTRA_PRED_MODES - 1 - !k, m.uv_mode); + update_cdf_2d(4, N_PARTITIONS - 3, m.partition[BL_128X128]); + for (int k = BL_64X64; k < BL_8X8; k++) + update_cdf_2d(4, N_PARTITIONS - 1, m.partition[k]); + update_cdf_2d(4, N_SUB8X8_PARTITIONS - 1, m.partition[BL_8X8]); + update_cdf_2d(6, 15, m.cfl_alpha); + update_cdf_2d(2, 15, m.txtp_inter1); + update_cdf_1d(11, m.txtp_inter2); + update_cdf_3d(2, N_INTRA_PRED_MODES, 6, m.txtp_intra1); + update_cdf_3d(3, N_INTRA_PRED_MODES, 4, m.txtp_intra2); + update_cdf_1d(7, m.cfl_sign); + update_cdf_2d(8, 6, m.angle_delta); + update_cdf_1d(4, m.filter_intra); + update_cdf_2d(3, DAV1D_MAX_SEGMENTS - 1, m.seg_id); + update_cdf_3d(2, 7, 6, m.pal_sz); + update_cdf_4d(2, 7, 5, k + 1, m.color_map); + update_cdf_3d(N_TX_SIZES - 1, 3, imin(k + 1, 2), m.txsz); + update_cdf_1d(3, m.delta_q); + update_cdf_2d(5, 3, m.delta_lf); + update_cdf_1d(2, m.restore_switchable); + update_cdf_1d(1, m.restore_wiener); + update_cdf_1d(1, m.restore_sgrproj); + update_cdf_2d(4, 1, m.txtp_inter3); + update_cdf_2d(N_BS_SIZES, 1, m.use_filter_intra); + update_cdf_3d(7, 3, 1, m.txpart); + update_cdf_2d(3, 1, m.skip); + update_cdf_3d(7, 3, 1, m.pal_y); + update_cdf_2d(2, 1, m.pal_uv); + + if (IS_KEY_OR_INTRA(hdr)) + return; + + memcpy(dst->m.y_mode, src->m.y_mode, + offsetof(CdfContext, kfym) - offsetof(CdfContext, m.y_mode)); + + update_cdf_2d(4, N_INTRA_PRED_MODES - 1, m.y_mode); + update_cdf_2d(9, 15, m.wedge_idx); + update_cdf_2d(8, N_COMP_INTER_PRED_MODES - 1, m.comp_inter_mode); + update_cdf_3d(2, 8, DAV1D_N_SWITCHABLE_FILTERS - 1, m.filter); + update_cdf_2d(4, 3, m.interintra_mode); + update_cdf_2d(N_BS_SIZES, 2, m.motion_mode); + update_cdf_2d(3, 1, m.skip_mode); + update_cdf_2d(6, 1, m.newmv_mode); + update_cdf_2d(2, 1, m.globalmv_mode); + update_cdf_2d(6, 1, m.refmv_mode); + update_cdf_2d(3, 1, m.drl_bit); + update_cdf_2d(4, 1, m.intra); + update_cdf_2d(5, 1, m.comp); + update_cdf_2d(5, 1, m.comp_dir); + update_cdf_2d(6, 1, m.jnt_comp); + update_cdf_2d(6, 1, m.mask_comp); + update_cdf_2d(9, 1, m.wedge_comp); + update_cdf_3d(6, 3, 1, m.ref); + update_cdf_3d(3, 3, 1, m.comp_fwd_ref); + update_cdf_3d(2, 3, 1, m.comp_bwd_ref); + update_cdf_3d(3, 3, 1, m.comp_uni_ref); + update_cdf_2d(3, 1, m.seg_pred); + update_cdf_2d(4, 1, m.interintra); + update_cdf_2d(7, 1, m.interintra_wedge); + update_cdf_2d(N_BS_SIZES, 1, m.obmc); + + for (int k = 0; k < 2; k++) { + update_cdf_1d(10, mv.comp[k].classes); + update_cdf_1d(1, mv.comp[k].sign); + update_cdf_1d(1, mv.comp[k].class0); + update_cdf_2d(2, 3, mv.comp[k].class0_fp); + update_cdf_1d(1, mv.comp[k].class0_hp); + update_cdf_2d(10, 1, mv.comp[k].classN); + update_cdf_1d(3, mv.comp[k].classN_fp); + update_cdf_1d(1, mv.comp[k].classN_hp); + } + update_cdf_1d(N_MV_JOINTS - 1, mv.joint); +} + +/* + * CDF threading wrappers. + */ +void dav1d_cdf_thread_init_static(CdfThreadContext *const cdf, const unsigned qidx) { + cdf->ref = NULL; + cdf->data.qcat = (qidx > 20) + (qidx > 60) + (qidx > 120); +} + +void dav1d_cdf_thread_copy(CdfContext *const dst, const CdfThreadContext *const src) { + if (src->ref) { + memcpy(dst, src->data.cdf, sizeof(*dst)); + } else { + dst->coef = default_coef_cdf[src->data.qcat]; + memcpy(&dst->m, &default_cdf.m, + offsetof(CdfDefaultContext, mv.joint)); + memcpy(&dst->mv.comp[1], &default_cdf.mv.comp, + sizeof(default_cdf) - offsetof(CdfDefaultContext, mv.comp)); + } +} + +int dav1d_cdf_thread_alloc(Dav1dContext *const c, CdfThreadContext *const cdf, + const int have_frame_mt) +{ + cdf->ref = dav1d_ref_create_using_pool(c->cdf_pool, + sizeof(CdfContext) + sizeof(atomic_uint)); + if (!cdf->ref) return DAV1D_ERR(ENOMEM); + cdf->data.cdf = cdf->ref->data; + if (have_frame_mt) { + cdf->progress = (atomic_uint *) &cdf->data.cdf[1]; + atomic_init(cdf->progress, 0); + } + return 0; +} + +void dav1d_cdf_thread_ref(CdfThreadContext *const dst, + CdfThreadContext *const src) +{ + *dst = *src; + if (src->ref) + dav1d_ref_inc(src->ref); +} + +void dav1d_cdf_thread_unref(CdfThreadContext *const cdf) { + memset(&cdf->data, 0, sizeof(*cdf) - offsetof(CdfThreadContext, data)); + dav1d_ref_dec(&cdf->ref); +} diff --git a/3rdparty/dav1d/src/cdf.h b/3rdparty/dav1d/src/cdf.h new file mode 100644 index 0000000..db83710 --- /dev/null +++ b/3rdparty/dav1d/src/cdf.h @@ -0,0 +1,154 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_CDF_H +#define DAV1D_SRC_CDF_H + +#include + +#include "src/levels.h" +#include "src/ref.h" +#include "src/thread_data.h" + +/* Buffers padded to [4]/[8]/[16] for SIMD where needed. */ + +typedef struct CdfModeContext { + ALIGN(uint16_t uv_mode[2][N_INTRA_PRED_MODES][N_UV_INTRA_PRED_MODES + 2], 32); + ALIGN(uint16_t partition[N_BL_LEVELS][4][N_PARTITIONS + 6], 32); + ALIGN(uint16_t cfl_alpha[6][16], 32); + ALIGN(uint16_t txtp_inter1[2][16], 32); + ALIGN(uint16_t txtp_inter2[12 + 4], 32); + ALIGN(uint16_t txtp_intra1[2][N_INTRA_PRED_MODES][7 + 1], 16); + ALIGN(uint16_t txtp_intra2[3][N_INTRA_PRED_MODES][5 + 3], 16); + ALIGN(uint16_t cfl_sign[8], 16); + ALIGN(uint16_t angle_delta[8][8], 16); + ALIGN(uint16_t filter_intra[5 + 3], 16); + ALIGN(uint16_t seg_id[3][DAV1D_MAX_SEGMENTS], 16); + ALIGN(uint16_t pal_sz[2][7][7 + 1], 16); + ALIGN(uint16_t color_map[2][7][5][8], 16); + ALIGN(uint16_t txsz[N_TX_SIZES - 1][3][4], 8); + ALIGN(uint16_t delta_q[4], 8); + ALIGN(uint16_t delta_lf[5][4], 8); + ALIGN(uint16_t restore_switchable[3 + 1], 8); + ALIGN(uint16_t restore_wiener[2], 4); + ALIGN(uint16_t restore_sgrproj[2], 4); + ALIGN(uint16_t txtp_inter3[4][2], 4); + ALIGN(uint16_t use_filter_intra[N_BS_SIZES][2], 4); + ALIGN(uint16_t txpart[7][3][2], 4); + ALIGN(uint16_t skip[3][2], 4); + ALIGN(uint16_t pal_y[7][3][2], 4); + ALIGN(uint16_t pal_uv[2][2], 4); + + /* key/intra */ + ALIGN(uint16_t intrabc[2], 4); + + /* inter/switch */ + ALIGN(uint16_t y_mode[4][N_INTRA_PRED_MODES + 3], 32); + ALIGN(uint16_t wedge_idx[9][16], 32); + ALIGN(uint16_t comp_inter_mode[8][N_COMP_INTER_PRED_MODES], 16); + ALIGN(uint16_t filter[2][8][DAV1D_N_SWITCHABLE_FILTERS + 1], 8); + ALIGN(uint16_t interintra_mode[4][4], 8); + ALIGN(uint16_t motion_mode[N_BS_SIZES][3 + 1], 8); + ALIGN(uint16_t skip_mode[3][2], 4); + ALIGN(uint16_t newmv_mode[6][2], 4); + ALIGN(uint16_t globalmv_mode[2][2], 4); + ALIGN(uint16_t refmv_mode[6][2], 4); + ALIGN(uint16_t drl_bit[3][2], 4); + ALIGN(uint16_t intra[4][2], 4); + ALIGN(uint16_t comp[5][2], 4); + ALIGN(uint16_t comp_dir[5][2], 4); + ALIGN(uint16_t jnt_comp[6][2], 4); + ALIGN(uint16_t mask_comp[6][2], 4); + ALIGN(uint16_t wedge_comp[9][2], 4); + ALIGN(uint16_t ref[6][3][2], 4); + ALIGN(uint16_t comp_fwd_ref[3][3][2], 4); + ALIGN(uint16_t comp_bwd_ref[2][3][2], 4); + ALIGN(uint16_t comp_uni_ref[3][3][2], 4); + ALIGN(uint16_t seg_pred[3][2], 4); + ALIGN(uint16_t interintra[7][2], 4); + ALIGN(uint16_t interintra_wedge[7][2], 4); + ALIGN(uint16_t obmc[N_BS_SIZES][2], 4); +} CdfModeContext; + +typedef struct CdfCoefContext { + ALIGN(uint16_t eob_bin_16[2][2][5 + 3], 16); + ALIGN(uint16_t eob_bin_32[2][2][6 + 2], 16); + ALIGN(uint16_t eob_bin_64[2][2][7 + 1], 16); + ALIGN(uint16_t eob_bin_128[2][2][8 + 0], 16); + ALIGN(uint16_t eob_bin_256[2][2][9 + 7], 32); + ALIGN(uint16_t eob_bin_512[2][10 + 6], 32); + ALIGN(uint16_t eob_bin_1024[2][11 + 5], 32); + ALIGN(uint16_t eob_base_tok[N_TX_SIZES][2][4][4], 8); + ALIGN(uint16_t base_tok[N_TX_SIZES][2][41][4], 8); + ALIGN(uint16_t br_tok[4 /*5*/][2][21][4], 8); + ALIGN(uint16_t eob_hi_bit[N_TX_SIZES][2][9][2], 4); + ALIGN(uint16_t skip[N_TX_SIZES][13][2], 4); + ALIGN(uint16_t dc_sign[2][3][2], 4); +} CdfCoefContext; + +typedef struct CdfMvComponent { + ALIGN(uint16_t classes[11 + 5], 32); + ALIGN(uint16_t sign[2], 4); + ALIGN(uint16_t class0[2], 4); + ALIGN(uint16_t class0_fp[2][4], 8); + ALIGN(uint16_t class0_hp[2], 4); + ALIGN(uint16_t classN[10][2], 4); + ALIGN(uint16_t classN_fp[4], 8); + ALIGN(uint16_t classN_hp[2], 4); +} CdfMvComponent; + +typedef struct CdfMvContext { + CdfMvComponent comp[2]; + ALIGN(uint16_t joint[N_MV_JOINTS], 8); +} CdfMvContext; + +typedef struct CdfContext { + CdfCoefContext coef; + CdfModeContext m; + CdfMvContext mv; + ALIGN(uint16_t kfym[5][5][N_INTRA_PRED_MODES + 3], 32); +} CdfContext; + +typedef struct CdfThreadContext { + Dav1dRef *ref; ///< allocation origin + union { + CdfContext *cdf; // if ref != NULL + unsigned qcat; // if ref == NULL, from static CDF tables + } data; + atomic_uint *progress; +} CdfThreadContext; + +void dav1d_cdf_thread_init_static(CdfThreadContext *cdf, unsigned qidx); +int dav1d_cdf_thread_alloc(Dav1dContext *c, CdfThreadContext *cdf, + const int have_frame_mt); +void dav1d_cdf_thread_copy(CdfContext *dst, const CdfThreadContext *src); +void dav1d_cdf_thread_ref(CdfThreadContext *dst, CdfThreadContext *src); +void dav1d_cdf_thread_unref(CdfThreadContext *cdf); +void dav1d_cdf_thread_update(const Dav1dFrameHeader *hdr, CdfContext *dst, + const CdfContext *src); + +#endif /* DAV1D_SRC_CDF_H */ diff --git a/3rdparty/dav1d/src/cpu.c b/3rdparty/dav1d/src/cpu.c new file mode 100644 index 0000000..6b57fe4 --- /dev/null +++ b/3rdparty/dav1d/src/cpu.c @@ -0,0 +1,129 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ +#include "config.h" + +#include +#include + +#include "src/cpu.h" +#include "src/log.h" + +#ifdef _WIN32 +#include +#endif +#ifdef __APPLE__ +#include +#include +#endif +#if HAVE_UNISTD_H +#include +#endif + +#if HAVE_PTHREAD_GETAFFINITY_NP +#include +#if HAVE_PTHREAD_NP_H +#include +#endif +#if defined(__FreeBSD__) +#define cpu_set_t cpuset_t +#endif +#endif + +#if HAVE_GETAUXVAL || HAVE_ELF_AUX_INFO +#include +#endif + +unsigned dav1d_cpu_flags = 0U; +unsigned dav1d_cpu_flags_mask = ~0U; + +COLD void dav1d_init_cpu(void) { +#if HAVE_ASM && !__has_feature(memory_sanitizer) +// memory sanitizer is inherently incompatible with asm +#if ARCH_AARCH64 || ARCH_ARM + dav1d_cpu_flags = dav1d_get_cpu_flags_arm(); +#elif ARCH_LOONGARCH + dav1d_cpu_flags = dav1d_get_cpu_flags_loongarch(); +#elif ARCH_PPC64LE + dav1d_cpu_flags = dav1d_get_cpu_flags_ppc(); +#elif ARCH_RISCV + dav1d_cpu_flags = dav1d_get_cpu_flags_riscv(); +#elif ARCH_X86 + dav1d_cpu_flags = dav1d_get_cpu_flags_x86(); +#endif +#endif +} + +COLD void dav1d_set_cpu_flags_mask(const unsigned mask) { + dav1d_cpu_flags_mask = mask; +} + +COLD int dav1d_num_logical_processors(Dav1dContext *const c) { +#ifdef _WIN32 +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + GROUP_AFFINITY affinity; + if (GetThreadGroupAffinity(GetCurrentThread(), &affinity)) { + int num_processors = 1; + while (affinity.Mask &= affinity.Mask - 1) + num_processors++; + return num_processors; + } +#else + SYSTEM_INFO system_info; + GetNativeSystemInfo(&system_info); + return system_info.dwNumberOfProcessors; +#endif +#elif HAVE_PTHREAD_GETAFFINITY_NP && defined(CPU_COUNT) + cpu_set_t affinity; + if (!pthread_getaffinity_np(pthread_self(), sizeof(affinity), &affinity)) + return CPU_COUNT(&affinity); +#elif defined(__APPLE__) + int num_processors; + size_t length = sizeof(num_processors); + if (!sysctlbyname("hw.logicalcpu", &num_processors, &length, NULL, 0)) + return num_processors; +#elif defined(_SC_NPROCESSORS_ONLN) + return (int)sysconf(_SC_NPROCESSORS_ONLN); +#endif + if (c) + dav1d_log(c, "Unable to detect thread count, defaulting to single-threaded mode\n"); + return 1; +} + +COLD unsigned long dav1d_getauxval(unsigned long type) { +#if HAVE_GETAUXVAL + return getauxval(type); +#elif HAVE_ELF_AUX_INFO + unsigned long aux = 0; + int ret = elf_aux_info(type, &aux, sizeof(aux)); + if (ret != 0) + errno = ret; + return aux; +#else + errno = ENOSYS; + return 0; +#endif +} diff --git a/3rdparty/dav1d/src/cpu.h b/3rdparty/dav1d/src/cpu.h new file mode 100644 index 0000000..5d712bb --- /dev/null +++ b/3rdparty/dav1d/src/cpu.h @@ -0,0 +1,136 @@ +/* + * Copyright © 2018-2022, VideoLAN and dav1d authors + * Copyright © 2018-2022, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_CPU_H +#define DAV1D_SRC_CPU_H + +#include "config.h" + +#include "common/attributes.h" + +#include "dav1d/common.h" +#include "dav1d/dav1d.h" + +#if ARCH_AARCH64 || ARCH_ARM +#include "src/arm/cpu.h" +#elif ARCH_LOONGARCH +#include "src/loongarch/cpu.h" +#elif ARCH_PPC64LE +#include "src/ppc/cpu.h" +#elif ARCH_RISCV +#include "src/riscv/cpu.h" +#elif ARCH_X86 +#include "src/x86/cpu.h" +#endif + +EXTERN unsigned dav1d_cpu_flags; +EXTERN unsigned dav1d_cpu_flags_mask; + +void dav1d_init_cpu(void); +DAV1D_API void dav1d_set_cpu_flags_mask(unsigned mask); +int dav1d_num_logical_processors(Dav1dContext *c); +unsigned long dav1d_getauxval(unsigned long); + +static ALWAYS_INLINE unsigned dav1d_get_default_cpu_flags(void) { + unsigned flags = 0; + +#if ARCH_AARCH64 || ARCH_ARM +#if defined(__ARM_NEON) || defined(__APPLE__) || defined(_WIN32) || ARCH_AARCH64 + flags |= DAV1D_ARM_CPU_FLAG_NEON; +#endif +#ifdef __ARM_FEATURE_DOTPROD + flags |= DAV1D_ARM_CPU_FLAG_DOTPROD; +#endif +#ifdef __ARM_FEATURE_MATMUL_INT8 + flags |= DAV1D_ARM_CPU_FLAG_I8MM; +#endif +#if ARCH_AARCH64 +#ifdef __ARM_FEATURE_SVE + flags |= DAV1D_ARM_CPU_FLAG_SVE; +#endif +#ifdef __ARM_FEATURE_SVE2 + flags |= DAV1D_ARM_CPU_FLAG_SVE2; +#endif +#endif /* ARCH_AARCH64 */ +#elif ARCH_PPC64LE +#if defined(__VSX__) + flags |= DAV1D_PPC_CPU_FLAG_VSX; +#endif +#if defined(__POWER9_VECTOR__) + flags |= DAV1D_PPC_CPU_FLAG_PWR9; +#endif +#elif ARCH_RISCV +#if defined(__riscv_v) + flags |= DAV1D_RISCV_CPU_FLAG_V; +#endif +#elif ARCH_X86 +#if defined(__AVX512F__) && defined(__AVX512CD__) && \ + defined(__AVX512BW__) && defined(__AVX512DQ__) && \ + defined(__AVX512VL__) && defined(__AVX512VNNI__) && \ + defined(__AVX512IFMA__) && defined(__AVX512VBMI__) && \ + defined(__AVX512VBMI2__) && defined(__AVX512VPOPCNTDQ__) && \ + defined(__AVX512BITALG__) && defined(__GFNI__) && \ + defined(__VAES__) && defined(__VPCLMULQDQ__) + flags |= DAV1D_X86_CPU_FLAG_AVX512ICL | + DAV1D_X86_CPU_FLAG_AVX2 | + DAV1D_X86_CPU_FLAG_SSE41 | + DAV1D_X86_CPU_FLAG_SSSE3 | + DAV1D_X86_CPU_FLAG_SSE2; +#elif defined(__AVX2__) + flags |= DAV1D_X86_CPU_FLAG_AVX2 | + DAV1D_X86_CPU_FLAG_SSE41 | + DAV1D_X86_CPU_FLAG_SSSE3 | + DAV1D_X86_CPU_FLAG_SSE2; +#elif defined(__SSE4_1__) || defined(__AVX__) + flags |= DAV1D_X86_CPU_FLAG_SSE41 | + DAV1D_X86_CPU_FLAG_SSSE3 | + DAV1D_X86_CPU_FLAG_SSE2; +#elif defined(__SSSE3__) + flags |= DAV1D_X86_CPU_FLAG_SSSE3 | + DAV1D_X86_CPU_FLAG_SSE2; +#elif ARCH_X86_64 || defined(__SSE2__) || \ + (defined(_M_IX86_FP) && _M_IX86_FP >= 2) + flags |= DAV1D_X86_CPU_FLAG_SSE2; +#endif +#endif + + return flags; +} + +static ALWAYS_INLINE unsigned dav1d_get_cpu_flags(void) { + unsigned flags = dav1d_cpu_flags & dav1d_cpu_flags_mask; + +#if TRIM_DSP_FUNCTIONS +/* Since this function is inlined, unconditionally setting a flag here will + * enable dead code elimination in the calling function. */ + flags |= dav1d_get_default_cpu_flags(); +#endif + + return flags; +} + +#endif /* DAV1D_SRC_CPU_H */ diff --git a/3rdparty/dav1d/src/ctx.c b/3rdparty/dav1d/src/ctx.c new file mode 100644 index 0000000..0a0fe54 --- /dev/null +++ b/3rdparty/dav1d/src/ctx.c @@ -0,0 +1,65 @@ +/* + * Copyright © 2024, VideoLAN and dav1d authors + * Copyright © 2024, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "ctx.h" + +static void memset_w1(void *const ptr, const int value) { + set_ctx1((uint8_t *) ptr, 0, value); +} + +static void memset_w2(void *const ptr, const int value) { + set_ctx2((uint8_t *) ptr, 0, value); +} + +static void memset_w4(void *const ptr, const int value) { + set_ctx4((uint8_t *) ptr, 0, value); +} + +static void memset_w8(void *const ptr, const int value) { + set_ctx8((uint8_t *) ptr, 0, value); +} + +static void memset_w16(void *const ptr, const int value) { + set_ctx16((uint8_t *) ptr, 0, value); +} + +static void memset_w32(void *const ptr, const int value) { + set_ctx32((uint8_t *) ptr, 0, value); +} + +const dav1d_memset_pow2_fn dav1d_memset_pow2[6] = { + memset_w1, + memset_w2, + memset_w4, + memset_w8, + memset_w16, + memset_w32 +}; diff --git a/3rdparty/dav1d/src/ctx.h b/3rdparty/dav1d/src/ctx.h new file mode 100644 index 0000000..7dea8b6 --- /dev/null +++ b/3rdparty/dav1d/src/ctx.h @@ -0,0 +1,89 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_CTX_H +#define DAV1D_SRC_CTX_H + +#include + +#include "common/attributes.h" +#include "common/intops.h" + +union alias64 { uint64_t u64; uint8_t u8[8]; } ATTR_ALIAS; +union alias32 { uint32_t u32; uint8_t u8[4]; } ATTR_ALIAS; +union alias16 { uint16_t u16; uint8_t u8[2]; } ATTR_ALIAS; +union alias8 { uint8_t u8; } ATTR_ALIAS; + +typedef void (*dav1d_memset_pow2_fn)(void *ptr, int value); +EXTERN const dav1d_memset_pow2_fn dav1d_memset_pow2[6]; + +static inline void dav1d_memset_likely_pow2(void *const ptr, const int value, const int n) { + assert(n >= 1 && n <= 32); + if ((n&(n-1)) == 0) { + dav1d_memset_pow2[ulog2(n)](ptr, value); + } else { + memset(ptr, value, n); + } +} + +// For smaller sizes use multiplication to broadcast bytes. memset misbehaves on the smaller sizes. +// For the larger sizes, we want to use memset to get access to vector operations. +#define set_ctx1(var, off, val) \ + ((union alias8 *) &(var)[off])->u8 = (val) * 0x01 +#define set_ctx2(var, off, val) \ + ((union alias16 *) &(var)[off])->u16 = (val) * 0x0101 +#define set_ctx4(var, off, val) \ + ((union alias32 *) &(var)[off])->u32 = (val) * 0x01010101U +#define set_ctx8(var, off, val) \ + ((union alias64 *) &(var)[off])->u64 = (val) * 0x0101010101010101ULL +#define set_ctx16(var, off, val) do { \ + memset(&(var)[off], val, 16); \ + } while (0) +#define set_ctx32(var, off, val) do { \ + memset(&(var)[off], val, 32); \ + } while (0) +#define case_set(var) \ + switch (var) { \ + case 0: set_ctx(set_ctx1); break; \ + case 1: set_ctx(set_ctx2); break; \ + case 2: set_ctx(set_ctx4); break; \ + case 3: set_ctx(set_ctx8); break; \ + case 4: set_ctx(set_ctx16); break; \ + case 5: set_ctx(set_ctx32); break; \ + default: assert(0); \ + } +#define case_set_upto16(var) \ + switch (var) { \ + case 0: set_ctx(set_ctx1); break; \ + case 1: set_ctx(set_ctx2); break; \ + case 2: set_ctx(set_ctx4); break; \ + case 3: set_ctx(set_ctx8); break; \ + case 4: set_ctx(set_ctx16); break; \ + default: assert(0); \ + } + +#endif /* DAV1D_SRC_CTX_H */ diff --git a/3rdparty/dav1d/src/data.c b/3rdparty/dav1d/src/data.c new file mode 100644 index 0000000..bbbe02e --- /dev/null +++ b/3rdparty/dav1d/src/data.c @@ -0,0 +1,149 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "dav1d/data.h" + +#include "common/attributes.h" +#include "common/validate.h" + +#include "src/data.h" +#include "src/ref.h" + +uint8_t *dav1d_data_create_internal(Dav1dData *const buf, const size_t sz) { + validate_input_or_ret(buf != NULL, NULL); + + if (sz > SIZE_MAX / 2) return NULL; + buf->ref = dav1d_ref_create(ALLOC_DAV1DDATA, sz); + if (!buf->ref) return NULL; + buf->data = buf->ref->const_data; + buf->sz = sz; + dav1d_data_props_set_defaults(&buf->m); + buf->m.size = sz; + + return buf->ref->data; +} + +int dav1d_data_wrap_internal(Dav1dData *const buf, const uint8_t *const ptr, + const size_t sz, + void (*const free_callback)(const uint8_t *data, + void *cookie), + void *const cookie) +{ + validate_input_or_ret(buf != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(ptr != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(free_callback != NULL, DAV1D_ERR(EINVAL)); + + if (sz > SIZE_MAX / 2) return DAV1D_ERR(EINVAL); + Dav1dRef *const ref = dav1d_malloc(ALLOC_DAV1DDATA, sizeof(Dav1dRef)); + if (!ref) return DAV1D_ERR(ENOMEM); + + buf->ref = dav1d_ref_init(ref, ptr, free_callback, cookie, 1); + buf->data = ptr; + buf->sz = sz; + dav1d_data_props_set_defaults(&buf->m); + buf->m.size = sz; + + return 0; +} + +int dav1d_data_wrap_user_data_internal(Dav1dData *const buf, + const uint8_t *const user_data, + void (*const free_callback)(const uint8_t *user_data, + void *cookie), + void *const cookie) +{ + validate_input_or_ret(buf != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(free_callback != NULL, DAV1D_ERR(EINVAL)); + + Dav1dRef *const ref = dav1d_malloc(ALLOC_DAV1DDATA, sizeof(Dav1dRef)); + if (!ref) return DAV1D_ERR(ENOMEM); + + buf->m.user_data.ref = dav1d_ref_init(ref, user_data, free_callback, cookie, 1); + buf->m.user_data.data = user_data; + + return 0; +} + +void dav1d_data_ref(Dav1dData *const dst, const Dav1dData *const src) { + assert(dst != NULL); + assert(dst->data == NULL); + assert(src != NULL); + + if (src->ref) { + assert(src->data != NULL); + dav1d_ref_inc(src->ref); + } + if (src->m.user_data.ref) dav1d_ref_inc(src->m.user_data.ref); + *dst = *src; +} + +void dav1d_data_props_copy(Dav1dDataProps *const dst, + const Dav1dDataProps *const src) +{ + assert(dst != NULL); + assert(src != NULL); + + dav1d_ref_dec(&dst->user_data.ref); + *dst = *src; + if (dst->user_data.ref) dav1d_ref_inc(dst->user_data.ref); +} + +void dav1d_data_props_set_defaults(Dav1dDataProps *const props) { + assert(props != NULL); + + memset(props, 0, sizeof(*props)); + props->timestamp = INT64_MIN; + props->offset = -1; +} + +void dav1d_data_props_unref_internal(Dav1dDataProps *const props) { + validate_input(props != NULL); + + struct Dav1dRef *user_data_ref = props->user_data.ref; + dav1d_data_props_set_defaults(props); + dav1d_ref_dec(&user_data_ref); +} + +void dav1d_data_unref_internal(Dav1dData *const buf) { + validate_input(buf != NULL); + + struct Dav1dRef *user_data_ref = buf->m.user_data.ref; + if (buf->ref) { + validate_input(buf->data != NULL); + dav1d_ref_dec(&buf->ref); + } + memset(buf, 0, sizeof(*buf)); + dav1d_data_props_set_defaults(&buf->m); + dav1d_ref_dec(&user_data_ref); +} diff --git a/3rdparty/dav1d/src/data.h b/3rdparty/dav1d/src/data.h new file mode 100644 index 0000000..b34c1db --- /dev/null +++ b/3rdparty/dav1d/src/data.h @@ -0,0 +1,56 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_DATA_H +#define DAV1D_SRC_DATA_H + +#include "dav1d/data.h" + +void dav1d_data_ref(Dav1dData *dst, const Dav1dData *src); + +/** + * Copy the source properties to the destination and increase the + * user_data's reference count (if it's not NULL). + */ +void dav1d_data_props_copy(Dav1dDataProps *dst, const Dav1dDataProps *src); + +void dav1d_data_props_set_defaults(Dav1dDataProps *props); + +uint8_t *dav1d_data_create_internal(Dav1dData *buf, size_t sz); +int dav1d_data_wrap_internal(Dav1dData *buf, const uint8_t *ptr, size_t sz, + void (*free_callback)(const uint8_t *data, + void *user_data), + void *user_data); +int dav1d_data_wrap_user_data_internal(Dav1dData *buf, + const uint8_t *user_data, + void (*free_callback)(const uint8_t *user_data, + void *cookie), + void *cookie); +void dav1d_data_unref_internal(Dav1dData *buf); +void dav1d_data_props_unref_internal(Dav1dDataProps *props); + +#endif /* DAV1D_SRC_DATA_H */ diff --git a/3rdparty/dav1d/src/decode.c b/3rdparty/dav1d/src/decode.c new file mode 100644 index 0000000..fb5d23c --- /dev/null +++ b/3rdparty/dav1d/src/decode.c @@ -0,0 +1,3746 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "dav1d/data.h" + +#include "common/frame.h" +#include "common/intops.h" + +#include "src/ctx.h" +#include "src/decode.h" +#include "src/dequant_tables.h" +#include "src/env.h" +#include "src/filmgrain.h" +#include "src/log.h" +#include "src/qm.h" +#include "src/recon.h" +#include "src/ref.h" +#include "src/tables.h" +#include "src/thread_task.h" +#include "src/warpmv.h" + +static void init_quant_tables(const Dav1dSequenceHeader *const seq_hdr, + const Dav1dFrameHeader *const frame_hdr, + const int qidx, uint16_t (*dq)[3][2]) +{ + for (int i = 0; i < (frame_hdr->segmentation.enabled ? 8 : 1); i++) { + const int yac = frame_hdr->segmentation.enabled ? + iclip_u8(qidx + frame_hdr->segmentation.seg_data.d[i].delta_q) : qidx; + const int ydc = iclip_u8(yac + frame_hdr->quant.ydc_delta); + const int uac = iclip_u8(yac + frame_hdr->quant.uac_delta); + const int udc = iclip_u8(yac + frame_hdr->quant.udc_delta); + const int vac = iclip_u8(yac + frame_hdr->quant.vac_delta); + const int vdc = iclip_u8(yac + frame_hdr->quant.vdc_delta); + + dq[i][0][0] = dav1d_dq_tbl[seq_hdr->hbd][ydc][0]; + dq[i][0][1] = dav1d_dq_tbl[seq_hdr->hbd][yac][1]; + dq[i][1][0] = dav1d_dq_tbl[seq_hdr->hbd][udc][0]; + dq[i][1][1] = dav1d_dq_tbl[seq_hdr->hbd][uac][1]; + dq[i][2][0] = dav1d_dq_tbl[seq_hdr->hbd][vdc][0]; + dq[i][2][1] = dav1d_dq_tbl[seq_hdr->hbd][vac][1]; + } +} + +static int read_mv_component_diff(MsacContext *const msac, + CdfMvComponent *const mv_comp, + const int mv_prec) +{ + const int sign = dav1d_msac_decode_bool_adapt(msac, mv_comp->sign); + const int cl = dav1d_msac_decode_symbol_adapt16(msac, mv_comp->classes, 10); + int up, fp = 3, hp = 1; + + if (!cl) { + up = dav1d_msac_decode_bool_adapt(msac, mv_comp->class0); + if (mv_prec >= 0) { // !force_integer_mv + fp = dav1d_msac_decode_symbol_adapt4(msac, mv_comp->class0_fp[up], 3); + if (mv_prec > 0) // allow_high_precision_mv + hp = dav1d_msac_decode_bool_adapt(msac, mv_comp->class0_hp); + } + } else { + up = 1 << cl; + for (int n = 0; n < cl; n++) + up |= dav1d_msac_decode_bool_adapt(msac, mv_comp->classN[n]) << n; + if (mv_prec >= 0) { // !force_integer_mv + fp = dav1d_msac_decode_symbol_adapt4(msac, mv_comp->classN_fp, 3); + if (mv_prec > 0) // allow_high_precision_mv + hp = dav1d_msac_decode_bool_adapt(msac, mv_comp->classN_hp); + } + } + + const int diff = ((up << 3) | (fp << 1) | hp) + 1; + + return sign ? -diff : diff; +} + +static void read_mv_residual(Dav1dTileState *const ts, mv *const ref_mv, + const int mv_prec) +{ + MsacContext *const msac = &ts->msac; + const enum MVJoint mv_joint = + dav1d_msac_decode_symbol_adapt4(msac, ts->cdf.mv.joint, N_MV_JOINTS - 1); + if (mv_joint & MV_JOINT_V) + ref_mv->y += read_mv_component_diff(msac, &ts->cdf.mv.comp[0], mv_prec); + if (mv_joint & MV_JOINT_H) + ref_mv->x += read_mv_component_diff(msac, &ts->cdf.mv.comp[1], mv_prec); +} + +static void read_tx_tree(Dav1dTaskContext *const t, + const enum RectTxfmSize from, + const int depth, uint16_t *const masks, + const int x_off, const int y_off) +{ + const Dav1dFrameContext *const f = t->f; + const int bx4 = t->bx & 31, by4 = t->by & 31; + const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[from]; + const int txw = t_dim->lw, txh = t_dim->lh; + int is_split; + + if (depth < 2 && from > (int) TX_4X4) { + const int cat = 2 * (TX_64X64 - t_dim->max) - depth; + const int a = t->a->tx[bx4] < txw; + const int l = t->l.tx[by4] < txh; + + is_split = dav1d_msac_decode_bool_adapt(&t->ts->msac, + t->ts->cdf.m.txpart[cat][a + l]); + if (is_split) + masks[depth] |= 1 << (y_off * 4 + x_off); + } else { + is_split = 0; + } + + if (is_split && t_dim->max > TX_8X8) { + const enum RectTxfmSize sub = t_dim->sub; + const TxfmInfo *const sub_t_dim = &dav1d_txfm_dimensions[sub]; + const int txsw = sub_t_dim->w, txsh = sub_t_dim->h; + + read_tx_tree(t, sub, depth + 1, masks, x_off * 2 + 0, y_off * 2 + 0); + t->bx += txsw; + if (txw >= txh && t->bx < f->bw) + read_tx_tree(t, sub, depth + 1, masks, x_off * 2 + 1, y_off * 2 + 0); + t->bx -= txsw; + t->by += txsh; + if (txh >= txw && t->by < f->bh) { + read_tx_tree(t, sub, depth + 1, masks, x_off * 2 + 0, y_off * 2 + 1); + t->bx += txsw; + if (txw >= txh && t->bx < f->bw) + read_tx_tree(t, sub, depth + 1, masks, + x_off * 2 + 1, y_off * 2 + 1); + t->bx -= txsw; + } + t->by -= txsh; + } else { + dav1d_memset_pow2[t_dim->lw](&t->a->tx[bx4], is_split ? TX_4X4 : txw); + dav1d_memset_pow2[t_dim->lh](&t->l.tx[by4], is_split ? TX_4X4 : txh); + } +} + +static int neg_deinterleave(int diff, int ref, int max) { + if (!ref) return diff; + if (ref >= (max - 1)) return max - diff - 1; + if (2 * ref < max) { + if (diff <= 2 * ref) { + if (diff & 1) + return ref + ((diff + 1) >> 1); + else + return ref - (diff >> 1); + } + return diff; + } else { + if (diff <= 2 * (max - ref - 1)) { + if (diff & 1) + return ref + ((diff + 1) >> 1); + else + return ref - (diff >> 1); + } + return max - (diff + 1); + } +} + +static void find_matching_ref(const Dav1dTaskContext *const t, + const enum EdgeFlags intra_edge_flags, + const int bw4, const int bh4, + const int w4, const int h4, + const int have_left, const int have_top, + const int ref, uint64_t masks[2]) +{ + /*const*/ refmvs_block *const *r = &t->rt.r[(t->by & 31) + 5]; + int count = 0; + int have_topleft = have_top && have_left; + int have_topright = imax(bw4, bh4) < 32 && + have_top && t->bx + bw4 < t->ts->tiling.col_end && + (intra_edge_flags & EDGE_I444_TOP_HAS_RIGHT); + +#define bs(rp) dav1d_block_dimensions[(rp)->bs] +#define matches(rp) ((rp)->ref.ref[0] == ref + 1 && (rp)->ref.ref[1] == -1) + + if (have_top) { + const refmvs_block *r2 = &r[-1][t->bx]; + if (matches(r2)) { + masks[0] |= 1; + count = 1; + } + int aw4 = bs(r2)[0]; + if (aw4 >= bw4) { + const int off = t->bx & (aw4 - 1); + if (off) have_topleft = 0; + if (aw4 - off > bw4) have_topright = 0; + } else { + unsigned mask = 1 << aw4; + for (int x = aw4; x < w4; x += aw4) { + r2 += aw4; + if (matches(r2)) { + masks[0] |= mask; + if (++count >= 8) return; + } + aw4 = bs(r2)[0]; + mask <<= aw4; + } + } + } + if (have_left) { + /*const*/ refmvs_block *const *r2 = r; + if (matches(&r2[0][t->bx - 1])) { + masks[1] |= 1; + if (++count >= 8) return; + } + int lh4 = bs(&r2[0][t->bx - 1])[1]; + if (lh4 >= bh4) { + if (t->by & (lh4 - 1)) have_topleft = 0; + } else { + unsigned mask = 1 << lh4; + for (int y = lh4; y < h4; y += lh4) { + r2 += lh4; + if (matches(&r2[0][t->bx - 1])) { + masks[1] |= mask; + if (++count >= 8) return; + } + lh4 = bs(&r2[0][t->bx - 1])[1]; + mask <<= lh4; + } + } + } + if (have_topleft && matches(&r[-1][t->bx - 1])) { + masks[1] |= 1ULL << 32; + if (++count >= 8) return; + } + if (have_topright && matches(&r[-1][t->bx + bw4])) { + masks[0] |= 1ULL << 32; + } +#undef matches +} + +static void derive_warpmv(const Dav1dTaskContext *const t, + const int bw4, const int bh4, + const uint64_t masks[2], const union mv mv, + Dav1dWarpedMotionParams *const wmp) +{ + int pts[8][2 /* in, out */][2 /* x, y */], np = 0; + /*const*/ refmvs_block *const *r = &t->rt.r[(t->by & 31) + 5]; + +#define add_sample(dx, dy, sx, sy, rp) do { \ + pts[np][0][0] = 16 * (2 * dx + sx * bs(rp)[0]) - 8; \ + pts[np][0][1] = 16 * (2 * dy + sy * bs(rp)[1]) - 8; \ + pts[np][1][0] = pts[np][0][0] + (rp)->mv.mv[0].x; \ + pts[np][1][1] = pts[np][0][1] + (rp)->mv.mv[0].y; \ + np++; \ +} while (0) + + // use masks[] to find the projectable motion vectors in the edges + if ((unsigned) masks[0] == 1 && !(masks[1] >> 32)) { + const int off = t->bx & (bs(&r[-1][t->bx])[0] - 1); + add_sample(-off, 0, 1, -1, &r[-1][t->bx]); + } else for (unsigned off = 0, xmask = (uint32_t) masks[0]; np < 8 && xmask;) { // top + const int tz = ctz(xmask); + off += tz; + xmask >>= tz; + add_sample(off, 0, 1, -1, &r[-1][t->bx + off]); + xmask &= ~1; + } + if (np < 8 && masks[1] == 1) { + const int off = t->by & (bs(&r[0][t->bx - 1])[1] - 1); + add_sample(0, -off, -1, 1, &r[-off][t->bx - 1]); + } else for (unsigned off = 0, ymask = (uint32_t) masks[1]; np < 8 && ymask;) { // left + const int tz = ctz(ymask); + off += tz; + ymask >>= tz; + add_sample(0, off, -1, 1, &r[off][t->bx - 1]); + ymask &= ~1; + } + if (np < 8 && masks[1] >> 32) // top/left + add_sample(0, 0, -1, -1, &r[-1][t->bx - 1]); + if (np < 8 && masks[0] >> 32) // top/right + add_sample(bw4, 0, 1, -1, &r[-1][t->bx + bw4]); + assert(np > 0 && np <= 8); +#undef bs + + // select according to motion vector difference against a threshold + int mvd[8], ret = 0; + const int thresh = 4 * iclip(imax(bw4, bh4), 4, 28); + for (int i = 0; i < np; i++) { + mvd[i] = abs(pts[i][1][0] - pts[i][0][0] - mv.x) + + abs(pts[i][1][1] - pts[i][0][1] - mv.y); + if (mvd[i] > thresh) + mvd[i] = -1; + else + ret++; + } + if (!ret) { + ret = 1; + } else for (int i = 0, j = np - 1, k = 0; k < np - ret; k++, i++, j--) { + while (mvd[i] != -1) i++; + while (mvd[j] == -1) j--; + assert(i != j); + if (i > j) break; + // replace the discarded samples; + mvd[i] = mvd[j]; + memcpy(pts[i], pts[j], sizeof(*pts)); + } + + if (!dav1d_find_affine_int(pts, ret, bw4, bh4, mv, wmp, t->bx, t->by) && + !dav1d_get_shear_params(wmp)) + { + wmp->type = DAV1D_WM_TYPE_AFFINE; + } else + wmp->type = DAV1D_WM_TYPE_IDENTITY; +} + +static inline int findoddzero(const uint8_t *buf, int len) { + for (int n = 0; n < len; n++) + if (!buf[n * 2]) return 1; + return 0; +} + +// meant to be SIMD'able, so that theoretical complexity of this function +// times block size goes from w4*h4 to w4+h4-1 +// a and b are previous two lines containing (a) top/left entries or (b) +// top/left entries, with a[0] being either the first top or first left entry, +// depending on top_offset being 1 or 0, and b being the first top/left entry +// for whichever has one. left_offset indicates whether the (len-1)th entry +// has a left neighbour. +// output is order[] and ctx for each member of this diagonal. +static void order_palette(const uint8_t *pal_idx, const ptrdiff_t stride, + const int i, const int first, const int last, + uint8_t (*const order)[8], uint8_t *const ctx) +{ + int have_top = i > first; + + assert(pal_idx); + pal_idx += first + (i - first) * stride; + for (int j = first, n = 0; j >= last; have_top = 1, j--, n++, pal_idx += stride - 1) { + const int have_left = j > 0; + + assert(have_left || have_top); + +#define add(v_in) do { \ + const int v = v_in; \ + assert((unsigned)v < 8U); \ + order[n][o_idx++] = v; \ + mask |= 1 << v; \ + } while (0) + + unsigned mask = 0; + int o_idx = 0; + if (!have_left) { + ctx[n] = 0; + add(pal_idx[-stride]); + } else if (!have_top) { + ctx[n] = 0; + add(pal_idx[-1]); + } else { + const int l = pal_idx[-1], t = pal_idx[-stride], tl = pal_idx[-(stride + 1)]; + const int same_t_l = t == l; + const int same_t_tl = t == tl; + const int same_l_tl = l == tl; + const int same_all = same_t_l & same_t_tl & same_l_tl; + + if (same_all) { + ctx[n] = 4; + add(t); + } else if (same_t_l) { + ctx[n] = 3; + add(t); + add(tl); + } else if (same_t_tl | same_l_tl) { + ctx[n] = 2; + add(tl); + add(same_t_tl ? l : t); + } else { + ctx[n] = 1; + add(imin(t, l)); + add(imax(t, l)); + add(tl); + } + } + for (unsigned m = 1, bit = 0; m < 0x100; m <<= 1, bit++) + if (!(mask & m)) + order[n][o_idx++] = bit; + assert(o_idx == 8); +#undef add + } +} + +static void read_pal_indices(Dav1dTaskContext *const t, + uint8_t *const pal_idx, + const int pal_sz, const int pl, + const int w4, const int h4, + const int bw4, const int bh4) +{ + Dav1dTileState *const ts = t->ts; + const ptrdiff_t stride = bw4 * 4; + assert(pal_idx); + uint8_t *const pal_tmp = t->scratch.pal_idx_uv; + pal_tmp[0] = dav1d_msac_decode_uniform(&ts->msac, pal_sz); + uint16_t (*const color_map_cdf)[8] = + ts->cdf.m.color_map[pl][pal_sz - 2]; + uint8_t (*const order)[8] = t->scratch.pal_order; + uint8_t *const ctx = t->scratch.pal_ctx; + for (int i = 1; i < 4 * (w4 + h4) - 1; i++) { + // top/left-to-bottom/right diagonals ("wave-front") + const int first = imin(i, w4 * 4 - 1); + const int last = imax(0, i - h4 * 4 + 1); + order_palette(pal_tmp, stride, i, first, last, order, ctx); + for (int j = first, m = 0; j >= last; j--, m++) { + const int color_idx = dav1d_msac_decode_symbol_adapt8(&ts->msac, + color_map_cdf[ctx[m]], pal_sz - 1); + pal_tmp[(i - j) * stride + j] = order[m][color_idx]; + } + } + + t->c->pal_dsp.pal_idx_finish(pal_idx, pal_tmp, bw4 * 4, bh4 * 4, + w4 * 4, h4 * 4); +} + +static void read_vartx_tree(Dav1dTaskContext *const t, + Av1Block *const b, const enum BlockSize bs, + const int bx4, const int by4) +{ + const Dav1dFrameContext *const f = t->f; + const uint8_t *const b_dim = dav1d_block_dimensions[bs]; + const int bw4 = b_dim[0], bh4 = b_dim[1]; + + // var-tx tree coding + uint16_t tx_split[2] = { 0 }; + b->max_ytx = dav1d_max_txfm_size_for_bs[bs][0]; + if (!b->skip && (f->frame_hdr->segmentation.lossless[b->seg_id] || + b->max_ytx == TX_4X4)) + { + b->max_ytx = b->uvtx = TX_4X4; + if (f->frame_hdr->txfm_mode == DAV1D_TX_SWITCHABLE) { + dav1d_memset_pow2[b_dim[2]](&t->a->tx[bx4], TX_4X4); + dav1d_memset_pow2[b_dim[3]](&t->l.tx[by4], TX_4X4); + } + } else if (f->frame_hdr->txfm_mode != DAV1D_TX_SWITCHABLE || b->skip) { + if (f->frame_hdr->txfm_mode == DAV1D_TX_SWITCHABLE) { + dav1d_memset_pow2[b_dim[2]](&t->a->tx[bx4], b_dim[2 + 0]); + dav1d_memset_pow2[b_dim[3]](&t->l.tx[by4], b_dim[2 + 1]); + } + b->uvtx = dav1d_max_txfm_size_for_bs[bs][f->cur.p.layout]; + } else { + assert(bw4 <= 16 || bh4 <= 16 || b->max_ytx == TX_64X64); + int y, x, y_off, x_off; + const TxfmInfo *const ytx = &dav1d_txfm_dimensions[b->max_ytx]; + for (y = 0, y_off = 0; y < bh4; y += ytx->h, y_off++) { + for (x = 0, x_off = 0; x < bw4; x += ytx->w, x_off++) { + read_tx_tree(t, b->max_ytx, 0, tx_split, x_off, y_off); + // contexts are updated inside read_tx_tree() + t->bx += ytx->w; + } + t->bx -= x; + t->by += ytx->h; + } + t->by -= y; + if (DEBUG_BLOCK_INFO) + printf("Post-vartxtree[%x/%x]: r=%d\n", + tx_split[0], tx_split[1], t->ts->msac.rng); + b->uvtx = dav1d_max_txfm_size_for_bs[bs][f->cur.p.layout]; + } + assert(!(tx_split[0] & ~0x33)); + b->tx_split0 = (uint8_t)tx_split[0]; + b->tx_split1 = tx_split[1]; +} + +static inline unsigned get_prev_frame_segid(const Dav1dFrameContext *const f, + const int by, const int bx, + const int w4, int h4, + const uint8_t *ref_seg_map, + const ptrdiff_t stride) +{ + assert(f->frame_hdr->primary_ref_frame != DAV1D_PRIMARY_REF_NONE); + + unsigned seg_id = 8; + ref_seg_map += by * stride + bx; + do { + for (int x = 0; x < w4; x++) + seg_id = imin(seg_id, ref_seg_map[x]); + ref_seg_map += stride; + } while (--h4 > 0 && seg_id); + assert(seg_id < 8); + + return seg_id; +} + +static inline void splat_oneref_mv(const Dav1dContext *const c, + Dav1dTaskContext *const t, + const enum BlockSize bs, + const Av1Block *const b, + const int bw4, const int bh4) +{ + const enum InterPredMode mode = b->inter_mode; + const refmvs_block ALIGN(tmpl, 16) = (refmvs_block) { + .ref.ref = { b->ref[0] + 1, b->interintra_type ? 0 : -1 }, + .mv.mv[0] = b->mv[0], + .bs = bs, + .mf = (mode == GLOBALMV && imin(bw4, bh4) >= 2) | ((mode == NEWMV) * 2), + }; + c->refmvs_dsp.splat_mv(&t->rt.r[(t->by & 31) + 5], &tmpl, t->bx, bw4, bh4); +} + +static inline void splat_intrabc_mv(const Dav1dContext *const c, + Dav1dTaskContext *const t, + const enum BlockSize bs, + const Av1Block *const b, + const int bw4, const int bh4) +{ + const refmvs_block ALIGN(tmpl, 16) = (refmvs_block) { + .ref.ref = { 0, -1 }, + .mv.mv[0] = b->mv[0], + .bs = bs, + .mf = 0, + }; + c->refmvs_dsp.splat_mv(&t->rt.r[(t->by & 31) + 5], &tmpl, t->bx, bw4, bh4); +} + +static inline void splat_tworef_mv(const Dav1dContext *const c, + Dav1dTaskContext *const t, + const enum BlockSize bs, + const Av1Block *const b, + const int bw4, const int bh4) +{ + assert(bw4 >= 2 && bh4 >= 2); + const enum CompInterPredMode mode = b->inter_mode; + const refmvs_block ALIGN(tmpl, 16) = (refmvs_block) { + .ref.ref = { b->ref[0] + 1, b->ref[1] + 1 }, + .mv.mv = { b->mv[0], b->mv[1] }, + .bs = bs, + .mf = (mode == GLOBALMV_GLOBALMV) | !!((1 << mode) & (0xbc)) * 2, + }; + c->refmvs_dsp.splat_mv(&t->rt.r[(t->by & 31) + 5], &tmpl, t->bx, bw4, bh4); +} + +static inline void splat_intraref(const Dav1dContext *const c, + Dav1dTaskContext *const t, + const enum BlockSize bs, + const int bw4, const int bh4) +{ + const refmvs_block ALIGN(tmpl, 16) = (refmvs_block) { + .ref.ref = { 0, -1 }, + .mv.mv[0].n = INVALID_MV, + .bs = bs, + .mf = 0, + }; + c->refmvs_dsp.splat_mv(&t->rt.r[(t->by & 31) + 5], &tmpl, t->bx, bw4, bh4); +} + +static void mc_lowest_px(int *const dst, const int by4, const int bh4, + const int mvy, const int ss_ver, + const struct ScalableMotionParams *const smp) +{ + const int v_mul = 4 >> ss_ver; + if (!smp->scale) { + const int my = mvy >> (3 + ss_ver), dy = mvy & (15 >> !ss_ver); + *dst = imax(*dst, (by4 + bh4) * v_mul + my + 4 * !!dy); + } else { + int y = (by4 * v_mul << 4) + mvy * (1 << !ss_ver); + const int64_t tmp = (int64_t)(y) * smp->scale + (smp->scale - 0x4000) * 8; + y = apply_sign64((int)((llabs(tmp) + 128) >> 8), tmp) + 32; + const int bottom = ((y + (bh4 * v_mul - 1) * smp->step) >> 10) + 1 + 4; + *dst = imax(*dst, bottom); + } +} + +static ALWAYS_INLINE void affine_lowest_px(Dav1dTaskContext *const t, int *const dst, + const uint8_t *const b_dim, + const Dav1dWarpedMotionParams *const wmp, + const int ss_ver, const int ss_hor) +{ + const int h_mul = 4 >> ss_hor, v_mul = 4 >> ss_ver; + assert(!((b_dim[0] * h_mul) & 7) && !((b_dim[1] * v_mul) & 7)); + const int32_t *const mat = wmp->matrix; + const int y = b_dim[1] * v_mul - 8; // lowest y + + const int src_y = t->by * 4 + ((y + 4) << ss_ver); + const int64_t mat5_y = (int64_t) mat[5] * src_y + mat[1]; + // check left- and right-most blocks + for (int x = 0; x < b_dim[0] * h_mul; x += imax(8, b_dim[0] * h_mul - 8)) { + // calculate transformation relative to center of 8x8 block in + // luma pixel units + const int src_x = t->bx * 4 + ((x + 4) << ss_hor); + const int64_t mvy = ((int64_t) mat[4] * src_x + mat5_y) >> ss_ver; + const int dy = (int) (mvy >> 16) - 4; + *dst = imax(*dst, dy + 4 + 8); + } +} + +static NOINLINE void affine_lowest_px_luma(Dav1dTaskContext *const t, int *const dst, + const uint8_t *const b_dim, + const Dav1dWarpedMotionParams *const wmp) +{ + affine_lowest_px(t, dst, b_dim, wmp, 0, 0); +} + +static NOINLINE void affine_lowest_px_chroma(Dav1dTaskContext *const t, int *const dst, + const uint8_t *const b_dim, + const Dav1dWarpedMotionParams *const wmp) +{ + const Dav1dFrameContext *const f = t->f; + assert(f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400); + if (f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I444) + affine_lowest_px_luma(t, dst, b_dim, wmp); + else + affine_lowest_px(t, dst, b_dim, wmp, f->cur.p.layout & DAV1D_PIXEL_LAYOUT_I420, 1); +} + +static void obmc_lowest_px(Dav1dTaskContext *const t, + int (*const dst)[2], const int is_chroma, + const uint8_t *const b_dim, + const int bx4, const int by4, const int w4, const int h4) +{ + assert(!(t->bx & 1) && !(t->by & 1)); + const Dav1dFrameContext *const f = t->f; + /*const*/ refmvs_block **r = &t->rt.r[(t->by & 31) + 5]; + const int ss_ver = is_chroma && f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = is_chroma && f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int h_mul = 4 >> ss_hor, v_mul = 4 >> ss_ver; + + if (t->by > t->ts->tiling.row_start && + (!is_chroma || b_dim[0] * h_mul + b_dim[1] * v_mul >= 16)) + { + for (int i = 0, x = 0; x < w4 && i < imin(b_dim[2], 4); ) { + // only odd blocks are considered for overlap handling, hence +1 + const refmvs_block *const a_r = &r[-1][t->bx + x + 1]; + const uint8_t *const a_b_dim = dav1d_block_dimensions[a_r->bs]; + + if (a_r->ref.ref[0] > 0) { + const int oh4 = imin(b_dim[1], 16) >> 1; + mc_lowest_px(&dst[a_r->ref.ref[0] - 1][is_chroma], t->by, + (oh4 * 3 + 3) >> 2, a_r->mv.mv[0].y, ss_ver, + &f->svc[a_r->ref.ref[0] - 1][1]); + i++; + } + x += imax(a_b_dim[0], 2); + } + } + + if (t->bx > t->ts->tiling.col_start) + for (int i = 0, y = 0; y < h4 && i < imin(b_dim[3], 4); ) { + // only odd blocks are considered for overlap handling, hence +1 + const refmvs_block *const l_r = &r[y + 1][t->bx - 1]; + const uint8_t *const l_b_dim = dav1d_block_dimensions[l_r->bs]; + + if (l_r->ref.ref[0] > 0) { + const int oh4 = iclip(l_b_dim[1], 2, b_dim[1]); + mc_lowest_px(&dst[l_r->ref.ref[0] - 1][is_chroma], + t->by + y, oh4, l_r->mv.mv[0].y, ss_ver, + &f->svc[l_r->ref.ref[0] - 1][1]); + i++; + } + y += imax(l_b_dim[1], 2); + } +} + +static int decode_b(Dav1dTaskContext *const t, + const enum BlockLevel bl, + const enum BlockSize bs, + const enum BlockPartition bp, + const enum EdgeFlags intra_edge_flags) { + Dav1dTileState *const ts = t->ts; + const Dav1dFrameContext *const f = t->f; + Av1Block b_mem, *const b = t->frame_thread.pass ? + &f->frame_thread.b[t->by * f->b4_stride + t->bx] : &b_mem; + const uint8_t *const b_dim = dav1d_block_dimensions[bs]; + const int bx4 = t->bx & 31, by4 = t->by & 31; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int cbx4 = bx4 >> ss_hor, cby4 = by4 >> ss_ver; + const int bw4 = b_dim[0], bh4 = b_dim[1]; + const int w4 = imin(bw4, f->bw - t->bx), h4 = imin(bh4, f->bh - t->by); + const int cbw4 = (bw4 + ss_hor) >> ss_hor, cbh4 = (bh4 + ss_ver) >> ss_ver; + const int have_left = t->bx > ts->tiling.col_start; + const int have_top = t->by > ts->tiling.row_start; + const int has_chroma = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400 && + (bw4 > ss_hor || t->bx & 1) && + (bh4 > ss_ver || t->by & 1); + + if (t->frame_thread.pass == 2) { + if (b->intra) { + f->bd_fn.recon_b_intra(t, bs, intra_edge_flags, b); + + const enum IntraPredMode y_mode_nofilt = + b->y_mode == FILTER_PRED ? DC_PRED : b->y_mode; +#define set_ctx(rep_macro) \ + rep_macro(edge->mode, off, y_mode_nofilt); \ + rep_macro(edge->intra, off, 1) + BlockContext *edge = t->a; + for (int i = 0, off = bx4; i < 2; i++, off = by4, edge = &t->l) { + case_set(b_dim[2 + i]); + } +#undef set_ctx + if (IS_INTER_OR_SWITCH(f->frame_hdr)) { + refmvs_block *const r = &t->rt.r[(t->by & 31) + 5 + bh4 - 1][t->bx]; + for (int x = 0; x < bw4; x++) { + r[x].ref.ref[0] = 0; + r[x].bs = bs; + } + refmvs_block *const *rr = &t->rt.r[(t->by & 31) + 5]; + for (int y = 0; y < bh4 - 1; y++) { + rr[y][t->bx + bw4 - 1].ref.ref[0] = 0; + rr[y][t->bx + bw4 - 1].bs = bs; + } + } + + if (has_chroma) { + uint8_t uv_mode = b->uv_mode; + dav1d_memset_pow2[ulog2(cbw4)](&t->a->uvmode[cbx4], uv_mode); + dav1d_memset_pow2[ulog2(cbh4)](&t->l.uvmode[cby4], uv_mode); + } + } else { + if (IS_INTER_OR_SWITCH(f->frame_hdr) /* not intrabc */ && + b->comp_type == COMP_INTER_NONE && b->motion_mode == MM_WARP) + { + if (b->matrix[0] == INT16_MIN) { + t->warpmv.type = DAV1D_WM_TYPE_IDENTITY; + } else { + t->warpmv.type = DAV1D_WM_TYPE_AFFINE; + t->warpmv.matrix[2] = b->matrix[0] + 0x10000; + t->warpmv.matrix[3] = b->matrix[1]; + t->warpmv.matrix[4] = b->matrix[2]; + t->warpmv.matrix[5] = b->matrix[3] + 0x10000; + dav1d_set_affine_mv2d(bw4, bh4, b->mv2d, &t->warpmv, + t->bx, t->by); + dav1d_get_shear_params(&t->warpmv); +#define signabs(v) v < 0 ? '-' : ' ', abs(v) + if (DEBUG_BLOCK_INFO) + printf("[ %c%x %c%x %c%x\n %c%x %c%x %c%x ]\n" + "alpha=%c%x, beta=%c%x, gamma=%c%x, delta=%c%x, mv=y:%d,x:%d\n", + signabs(t->warpmv.matrix[0]), + signabs(t->warpmv.matrix[1]), + signabs(t->warpmv.matrix[2]), + signabs(t->warpmv.matrix[3]), + signabs(t->warpmv.matrix[4]), + signabs(t->warpmv.matrix[5]), + signabs(t->warpmv.u.p.alpha), + signabs(t->warpmv.u.p.beta), + signabs(t->warpmv.u.p.gamma), + signabs(t->warpmv.u.p.delta), + b->mv2d.y, b->mv2d.x); +#undef signabs + } + } + if (f->bd_fn.recon_b_inter(t, bs, b)) return -1; + + const uint8_t *const filter = dav1d_filter_dir[b->filter2d]; + BlockContext *edge = t->a; + for (int i = 0, off = bx4; i < 2; i++, off = by4, edge = &t->l) { +#define set_ctx(rep_macro) \ + rep_macro(edge->filter[0], off, filter[0]); \ + rep_macro(edge->filter[1], off, filter[1]); \ + rep_macro(edge->intra, off, 0) + case_set(b_dim[2 + i]); +#undef set_ctx + } + + if (IS_INTER_OR_SWITCH(f->frame_hdr)) { + refmvs_block *const r = &t->rt.r[(t->by & 31) + 5 + bh4 - 1][t->bx]; + for (int x = 0; x < bw4; x++) { + r[x].ref.ref[0] = b->ref[0] + 1; + r[x].mv.mv[0] = b->mv[0]; + r[x].bs = bs; + } + refmvs_block *const *rr = &t->rt.r[(t->by & 31) + 5]; + for (int y = 0; y < bh4 - 1; y++) { + rr[y][t->bx + bw4 - 1].ref.ref[0] = b->ref[0] + 1; + rr[y][t->bx + bw4 - 1].mv.mv[0] = b->mv[0]; + rr[y][t->bx + bw4 - 1].bs = bs; + } + } + + if (has_chroma) { + dav1d_memset_pow2[ulog2(cbw4)](&t->a->uvmode[cbx4], DC_PRED); + dav1d_memset_pow2[ulog2(cbh4)](&t->l.uvmode[cby4], DC_PRED); + } + } + return 0; + } + + const int cw4 = (w4 + ss_hor) >> ss_hor, ch4 = (h4 + ss_ver) >> ss_ver; + + b->bl = bl; + b->bp = bp; + b->bs = bs; + + const Dav1dSegmentationData *seg = NULL; + + // segment_id (if seg_feature for skip/ref/gmv is enabled) + int seg_pred = 0; + if (f->frame_hdr->segmentation.enabled) { + if (!f->frame_hdr->segmentation.update_map) { + if (f->prev_segmap) { + unsigned seg_id = get_prev_frame_segid(f, t->by, t->bx, w4, h4, + f->prev_segmap, + f->b4_stride); + if (seg_id >= 8) return -1; + b->seg_id = seg_id; + } else { + b->seg_id = 0; + } + seg = &f->frame_hdr->segmentation.seg_data.d[b->seg_id]; + } else if (f->frame_hdr->segmentation.seg_data.preskip) { + if (f->frame_hdr->segmentation.temporal && + (seg_pred = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.seg_pred[t->a->seg_pred[bx4] + + t->l.seg_pred[by4]]))) + { + // temporal predicted seg_id + if (f->prev_segmap) { + unsigned seg_id = get_prev_frame_segid(f, t->by, t->bx, + w4, h4, + f->prev_segmap, + f->b4_stride); + if (seg_id >= 8) return -1; + b->seg_id = seg_id; + } else { + b->seg_id = 0; + } + } else { + int seg_ctx; + const unsigned pred_seg_id = + get_cur_frame_segid(t->by, t->bx, have_top, have_left, + &seg_ctx, f->cur_segmap, f->b4_stride); + const unsigned diff = dav1d_msac_decode_symbol_adapt8(&ts->msac, + ts->cdf.m.seg_id[seg_ctx], + DAV1D_MAX_SEGMENTS - 1); + const unsigned last_active_seg_id = + f->frame_hdr->segmentation.seg_data.last_active_segid; + b->seg_id = neg_deinterleave(diff, pred_seg_id, + last_active_seg_id + 1); + if (b->seg_id > last_active_seg_id) b->seg_id = 0; // error? + if (b->seg_id >= DAV1D_MAX_SEGMENTS) b->seg_id = 0; // error? + } + + if (DEBUG_BLOCK_INFO) + printf("Post-segid[preskip;%d]: r=%d\n", + b->seg_id, ts->msac.rng); + + seg = &f->frame_hdr->segmentation.seg_data.d[b->seg_id]; + } + } else { + b->seg_id = 0; + } + + // skip_mode + if ((!seg || (!seg->globalmv && seg->ref == -1 && !seg->skip)) && + f->frame_hdr->skip_mode_enabled && imin(bw4, bh4) > 1) + { + const int smctx = t->a->skip_mode[bx4] + t->l.skip_mode[by4]; + b->skip_mode = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.skip_mode[smctx]); + if (DEBUG_BLOCK_INFO) + printf("Post-skipmode[%d]: r=%d\n", b->skip_mode, ts->msac.rng); + } else { + b->skip_mode = 0; + } + + // skip + if (b->skip_mode || (seg && seg->skip)) { + b->skip = 1; + } else { + const int sctx = t->a->skip[bx4] + t->l.skip[by4]; + b->skip = dav1d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.skip[sctx]); + if (DEBUG_BLOCK_INFO) + printf("Post-skip[%d]: r=%d\n", b->skip, ts->msac.rng); + } + + // segment_id + if (f->frame_hdr->segmentation.enabled && + f->frame_hdr->segmentation.update_map && + !f->frame_hdr->segmentation.seg_data.preskip) + { + if (!b->skip && f->frame_hdr->segmentation.temporal && + (seg_pred = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.seg_pred[t->a->seg_pred[bx4] + + t->l.seg_pred[by4]]))) + { + // temporal predicted seg_id + if (f->prev_segmap) { + unsigned seg_id = get_prev_frame_segid(f, t->by, t->bx, w4, h4, + f->prev_segmap, + f->b4_stride); + if (seg_id >= 8) return -1; + b->seg_id = seg_id; + } else { + b->seg_id = 0; + } + } else { + int seg_ctx; + const unsigned pred_seg_id = + get_cur_frame_segid(t->by, t->bx, have_top, have_left, + &seg_ctx, f->cur_segmap, f->b4_stride); + if (b->skip) { + b->seg_id = pred_seg_id; + } else { + const unsigned diff = dav1d_msac_decode_symbol_adapt8(&ts->msac, + ts->cdf.m.seg_id[seg_ctx], + DAV1D_MAX_SEGMENTS - 1); + const unsigned last_active_seg_id = + f->frame_hdr->segmentation.seg_data.last_active_segid; + b->seg_id = neg_deinterleave(diff, pred_seg_id, + last_active_seg_id + 1); + if (b->seg_id > last_active_seg_id) b->seg_id = 0; // error? + } + if (b->seg_id >= DAV1D_MAX_SEGMENTS) b->seg_id = 0; // error? + } + + seg = &f->frame_hdr->segmentation.seg_data.d[b->seg_id]; + + if (DEBUG_BLOCK_INFO) + printf("Post-segid[postskip;%d]: r=%d\n", + b->seg_id, ts->msac.rng); + } + + // cdef index + if (!b->skip) { + const int idx = f->seq_hdr->sb128 ? ((t->bx & 16) >> 4) + + ((t->by & 16) >> 3) : 0; + if (t->cur_sb_cdef_idx_ptr[idx] == -1) { + const int v = dav1d_msac_decode_bools(&ts->msac, + f->frame_hdr->cdef.n_bits); + t->cur_sb_cdef_idx_ptr[idx] = v; + if (bw4 > 16) t->cur_sb_cdef_idx_ptr[idx + 1] = v; + if (bh4 > 16) t->cur_sb_cdef_idx_ptr[idx + 2] = v; + if (bw4 == 32 && bh4 == 32) t->cur_sb_cdef_idx_ptr[idx + 3] = v; + + if (DEBUG_BLOCK_INFO) + printf("Post-cdef_idx[%d]: r=%d\n", + *t->cur_sb_cdef_idx_ptr, ts->msac.rng); + } + } + + // delta-q/lf + if (!((t->bx | t->by) & (31 >> !f->seq_hdr->sb128))) { + const int prev_qidx = ts->last_qidx; + const int have_delta_q = f->frame_hdr->delta.q.present && + (bs != (f->seq_hdr->sb128 ? BS_128x128 : BS_64x64) || !b->skip); + + uint32_t prev_delta_lf = ts->last_delta_lf.u32; + + if (have_delta_q) { + int delta_q = dav1d_msac_decode_symbol_adapt4(&ts->msac, + ts->cdf.m.delta_q, 3); + if (delta_q == 3) { + const int n_bits = 1 + dav1d_msac_decode_bools(&ts->msac, 3); + delta_q = dav1d_msac_decode_bools(&ts->msac, n_bits) + + 1 + (1 << n_bits); + } + if (delta_q) { + if (dav1d_msac_decode_bool_equi(&ts->msac)) delta_q = -delta_q; + delta_q *= 1 << f->frame_hdr->delta.q.res_log2; + } + ts->last_qidx = iclip(ts->last_qidx + delta_q, 1, 255); + if (have_delta_q && DEBUG_BLOCK_INFO) + printf("Post-delta_q[%d->%d]: r=%d\n", + delta_q, ts->last_qidx, ts->msac.rng); + + if (f->frame_hdr->delta.lf.present) { + const int n_lfs = f->frame_hdr->delta.lf.multi ? + f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400 ? 4 : 2 : 1; + + for (int i = 0; i < n_lfs; i++) { + int delta_lf = dav1d_msac_decode_symbol_adapt4(&ts->msac, + ts->cdf.m.delta_lf[i + f->frame_hdr->delta.lf.multi], 3); + if (delta_lf == 3) { + const int n_bits = 1 + dav1d_msac_decode_bools(&ts->msac, 3); + delta_lf = dav1d_msac_decode_bools(&ts->msac, n_bits) + + 1 + (1 << n_bits); + } + if (delta_lf) { + if (dav1d_msac_decode_bool_equi(&ts->msac)) + delta_lf = -delta_lf; + delta_lf *= 1 << f->frame_hdr->delta.lf.res_log2; + } + ts->last_delta_lf.i8[i] = + iclip(ts->last_delta_lf.i8[i] + delta_lf, -63, 63); + if (have_delta_q && DEBUG_BLOCK_INFO) + printf("Post-delta_lf[%d:%d]: r=%d\n", i, delta_lf, + ts->msac.rng); + } + } + } + if (ts->last_qidx == f->frame_hdr->quant.yac) { + // assign frame-wide q values to this sb + ts->dq = f->dq; + } else if (ts->last_qidx != prev_qidx) { + // find sb-specific quant parameters + init_quant_tables(f->seq_hdr, f->frame_hdr, ts->last_qidx, ts->dqmem); + ts->dq = ts->dqmem; + } + if (!ts->last_delta_lf.u32) { + // assign frame-wide lf values to this sb + ts->lflvl = f->lf.lvl; + } else if (ts->last_delta_lf.u32 != prev_delta_lf) { + // find sb-specific lf lvl parameters + ts->lflvl = ts->lflvlmem; + dav1d_calc_lf_values(ts->lflvlmem, f->frame_hdr, ts->last_delta_lf.i8); + } + } + + if (b->skip_mode) { + b->intra = 0; + } else if (IS_INTER_OR_SWITCH(f->frame_hdr)) { + if (seg && (seg->ref >= 0 || seg->globalmv)) { + b->intra = !seg->ref; + } else { + const int ictx = get_intra_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + b->intra = !dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.intra[ictx]); + if (DEBUG_BLOCK_INFO) + printf("Post-intra[%d]: r=%d\n", b->intra, ts->msac.rng); + } + } else if (f->frame_hdr->allow_intrabc) { + b->intra = !dav1d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.intrabc); + if (DEBUG_BLOCK_INFO) + printf("Post-intrabcflag[%d]: r=%d\n", b->intra, ts->msac.rng); + } else { + b->intra = 1; + } + + // intra/inter-specific stuff + if (b->intra) { + uint16_t *const ymode_cdf = IS_INTER_OR_SWITCH(f->frame_hdr) ? + ts->cdf.m.y_mode[dav1d_ymode_size_context[bs]] : + ts->cdf.kfym[dav1d_intra_mode_context[t->a->mode[bx4]]] + [dav1d_intra_mode_context[t->l.mode[by4]]]; + b->y_mode = dav1d_msac_decode_symbol_adapt16(&ts->msac, ymode_cdf, + N_INTRA_PRED_MODES - 1); + if (DEBUG_BLOCK_INFO) + printf("Post-ymode[%d]: r=%d\n", b->y_mode, ts->msac.rng); + + // angle delta + if (b_dim[2] + b_dim[3] >= 2 && b->y_mode >= VERT_PRED && + b->y_mode <= VERT_LEFT_PRED) + { + uint16_t *const acdf = ts->cdf.m.angle_delta[b->y_mode - VERT_PRED]; + const int angle = dav1d_msac_decode_symbol_adapt8(&ts->msac, acdf, 6); + b->y_angle = angle - 3; + } else { + b->y_angle = 0; + } + + if (has_chroma) { + const int cfl_allowed = f->frame_hdr->segmentation.lossless[b->seg_id] ? + cbw4 == 1 && cbh4 == 1 : !!(cfl_allowed_mask & (1 << bs)); + uint16_t *const uvmode_cdf = ts->cdf.m.uv_mode[cfl_allowed][b->y_mode]; + b->uv_mode = dav1d_msac_decode_symbol_adapt16(&ts->msac, uvmode_cdf, + N_UV_INTRA_PRED_MODES - 1 - !cfl_allowed); + if (DEBUG_BLOCK_INFO) + printf("Post-uvmode[%d]: r=%d\n", b->uv_mode, ts->msac.rng); + + b->uv_angle = 0; + if (b->uv_mode == CFL_PRED) { +#define SIGN(a) (!!(a) + ((a) > 0)) + const int sign = dav1d_msac_decode_symbol_adapt8(&ts->msac, + ts->cdf.m.cfl_sign, 7) + 1; + const int sign_u = sign * 0x56 >> 8, sign_v = sign - sign_u * 3; + assert(sign_u == sign / 3); + if (sign_u) { + const int ctx = (sign_u == 2) * 3 + sign_v; + b->cfl_alpha[0] = dav1d_msac_decode_symbol_adapt16(&ts->msac, + ts->cdf.m.cfl_alpha[ctx], 15) + 1; + if (sign_u == 1) b->cfl_alpha[0] = -b->cfl_alpha[0]; + } else { + b->cfl_alpha[0] = 0; + } + if (sign_v) { + const int ctx = (sign_v == 2) * 3 + sign_u; + b->cfl_alpha[1] = dav1d_msac_decode_symbol_adapt16(&ts->msac, + ts->cdf.m.cfl_alpha[ctx], 15) + 1; + if (sign_v == 1) b->cfl_alpha[1] = -b->cfl_alpha[1]; + } else { + b->cfl_alpha[1] = 0; + } +#undef SIGN + if (DEBUG_BLOCK_INFO) + printf("Post-uvalphas[%d/%d]: r=%d\n", + b->cfl_alpha[0], b->cfl_alpha[1], ts->msac.rng); + } else if (b_dim[2] + b_dim[3] >= 2 && b->uv_mode >= VERT_PRED && + b->uv_mode <= VERT_LEFT_PRED) + { + uint16_t *const acdf = ts->cdf.m.angle_delta[b->uv_mode - VERT_PRED]; + const int angle = dav1d_msac_decode_symbol_adapt8(&ts->msac, acdf, 6); + b->uv_angle = angle - 3; + } + } + + b->pal_sz[0] = b->pal_sz[1] = 0; + if (f->frame_hdr->allow_screen_content_tools && + imax(bw4, bh4) <= 16 && bw4 + bh4 >= 4) + { + const int sz_ctx = b_dim[2] + b_dim[3] - 2; + if (b->y_mode == DC_PRED) { + const int pal_ctx = (t->a->pal_sz[bx4] > 0) + (t->l.pal_sz[by4] > 0); + const int use_y_pal = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.pal_y[sz_ctx][pal_ctx]); + if (DEBUG_BLOCK_INFO) + printf("Post-y_pal[%d]: r=%d\n", use_y_pal, ts->msac.rng); + if (use_y_pal) + f->bd_fn.read_pal_plane(t, b, 0, sz_ctx, bx4, by4); + } + + if (has_chroma && b->uv_mode == DC_PRED) { + const int pal_ctx = b->pal_sz[0] > 0; + const int use_uv_pal = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.pal_uv[pal_ctx]); + if (DEBUG_BLOCK_INFO) + printf("Post-uv_pal[%d]: r=%d\n", use_uv_pal, ts->msac.rng); + if (use_uv_pal) // see aomedia bug 2183 for why we use luma coordinates + f->bd_fn.read_pal_uv(t, b, sz_ctx, bx4, by4); + } + } + + if (b->y_mode == DC_PRED && !b->pal_sz[0] && + imax(b_dim[2], b_dim[3]) <= 3 && f->seq_hdr->filter_intra) + { + const int is_filter = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.use_filter_intra[bs]); + if (is_filter) { + b->y_mode = FILTER_PRED; + b->y_angle = dav1d_msac_decode_symbol_adapt8(&ts->msac, + ts->cdf.m.filter_intra, 4); + } + if (DEBUG_BLOCK_INFO) + printf("Post-filterintramode[%d/%d]: r=%d\n", + b->y_mode, b->y_angle, ts->msac.rng); + } + + if (b->pal_sz[0]) { + uint8_t *pal_idx; + if (t->frame_thread.pass) { + const int p = t->frame_thread.pass & 1; + assert(ts->frame_thread[p].pal_idx); + pal_idx = ts->frame_thread[p].pal_idx; + ts->frame_thread[p].pal_idx += bw4 * bh4 * 8; + } else + pal_idx = t->scratch.pal_idx_y; + read_pal_indices(t, pal_idx, b->pal_sz[0], 0, w4, h4, bw4, bh4); + if (DEBUG_BLOCK_INFO) + printf("Post-y-pal-indices: r=%d\n", ts->msac.rng); + } + + if (has_chroma && b->pal_sz[1]) { + uint8_t *pal_idx; + if (t->frame_thread.pass) { + const int p = t->frame_thread.pass & 1; + assert(ts->frame_thread[p].pal_idx); + pal_idx = ts->frame_thread[p].pal_idx; + ts->frame_thread[p].pal_idx += cbw4 * cbh4 * 8; + } else + pal_idx = t->scratch.pal_idx_uv; + read_pal_indices(t, pal_idx, b->pal_sz[1], 1, cw4, ch4, cbw4, cbh4); + if (DEBUG_BLOCK_INFO) + printf("Post-uv-pal-indices: r=%d\n", ts->msac.rng); + } + + const TxfmInfo *t_dim; + if (f->frame_hdr->segmentation.lossless[b->seg_id]) { + b->tx = b->uvtx = (int) TX_4X4; + t_dim = &dav1d_txfm_dimensions[TX_4X4]; + } else { + b->tx = dav1d_max_txfm_size_for_bs[bs][0]; + b->uvtx = dav1d_max_txfm_size_for_bs[bs][f->cur.p.layout]; + t_dim = &dav1d_txfm_dimensions[b->tx]; + if (f->frame_hdr->txfm_mode == DAV1D_TX_SWITCHABLE && t_dim->max > TX_4X4) { + const int tctx = get_tx_ctx(t->a, &t->l, t_dim, by4, bx4); + uint16_t *const tx_cdf = ts->cdf.m.txsz[t_dim->max - 1][tctx]; + int depth = dav1d_msac_decode_symbol_adapt4(&ts->msac, tx_cdf, + imin(t_dim->max, 2)); + + while (depth--) { + b->tx = t_dim->sub; + t_dim = &dav1d_txfm_dimensions[b->tx]; + } + } + if (DEBUG_BLOCK_INFO) + printf("Post-tx[%d]: r=%d\n", b->tx, ts->msac.rng); + } + + // reconstruction + if (t->frame_thread.pass == 1) { + f->bd_fn.read_coef_blocks(t, bs, b); + } else { + f->bd_fn.recon_b_intra(t, bs, intra_edge_flags, b); + } + + if (f->frame_hdr->loopfilter.level_y[0] || + f->frame_hdr->loopfilter.level_y[1]) + { + dav1d_create_lf_mask_intra(t->lf_mask, f->lf.level, f->b4_stride, + (const uint8_t (*)[8][2]) + &ts->lflvl[b->seg_id][0][0][0], + t->bx, t->by, f->w4, f->h4, bs, + b->tx, b->uvtx, f->cur.p.layout, + &t->a->tx_lpf_y[bx4], &t->l.tx_lpf_y[by4], + has_chroma ? &t->a->tx_lpf_uv[cbx4] : NULL, + has_chroma ? &t->l.tx_lpf_uv[cby4] : NULL); + } + // update contexts + const enum IntraPredMode y_mode_nofilt = + b->y_mode == FILTER_PRED ? DC_PRED : b->y_mode; + BlockContext *edge = t->a; + for (int i = 0, off = bx4; i < 2; i++, off = by4, edge = &t->l) { + int t_lsz = ((uint8_t *) &t_dim->lw)[i]; // lw then lh +#define set_ctx(rep_macro) \ + rep_macro(edge->tx_intra, off, t_lsz); \ + rep_macro(edge->tx, off, t_lsz); \ + rep_macro(edge->mode, off, y_mode_nofilt); \ + rep_macro(edge->pal_sz, off, b->pal_sz[0]); \ + rep_macro(edge->seg_pred, off, seg_pred); \ + rep_macro(edge->skip_mode, off, 0); \ + rep_macro(edge->intra, off, 1); \ + rep_macro(edge->skip, off, b->skip); \ + /* see aomedia bug 2183 for why we use luma coordinates here */ \ + rep_macro(t->pal_sz_uv[i], off, (has_chroma ? b->pal_sz[1] : 0)); \ + if (IS_INTER_OR_SWITCH(f->frame_hdr)) { \ + rep_macro(edge->comp_type, off, COMP_INTER_NONE); \ + rep_macro(edge->ref[0], off, ((uint8_t) -1)); \ + rep_macro(edge->ref[1], off, ((uint8_t) -1)); \ + rep_macro(edge->filter[0], off, DAV1D_N_SWITCHABLE_FILTERS); \ + rep_macro(edge->filter[1], off, DAV1D_N_SWITCHABLE_FILTERS); \ + } + case_set(b_dim[2 + i]); +#undef set_ctx + } + if (b->pal_sz[0]) + f->bd_fn.copy_pal_block_y(t, bx4, by4, bw4, bh4); + if (has_chroma) { + uint8_t uv_mode = b->uv_mode; + dav1d_memset_pow2[ulog2(cbw4)](&t->a->uvmode[cbx4], uv_mode); + dav1d_memset_pow2[ulog2(cbh4)](&t->l.uvmode[cby4], uv_mode); + if (b->pal_sz[1]) + f->bd_fn.copy_pal_block_uv(t, bx4, by4, bw4, bh4); + } + if (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc) + splat_intraref(f->c, t, bs, bw4, bh4); + } else if (IS_KEY_OR_INTRA(f->frame_hdr)) { + // intra block copy + refmvs_candidate mvstack[8]; + int n_mvs, ctx; + dav1d_refmvs_find(&t->rt, mvstack, &n_mvs, &ctx, + (union refmvs_refpair) { .ref = { 0, -1 }}, + bs, intra_edge_flags, t->by, t->bx); + + if (mvstack[0].mv.mv[0].n) + b->mv[0] = mvstack[0].mv.mv[0]; + else if (mvstack[1].mv.mv[0].n) + b->mv[0] = mvstack[1].mv.mv[0]; + else { + if (t->by - (16 << f->seq_hdr->sb128) < ts->tiling.row_start) { + b->mv[0].y = 0; + b->mv[0].x = -(512 << f->seq_hdr->sb128) - 2048; + } else { + b->mv[0].y = -(512 << f->seq_hdr->sb128); + b->mv[0].x = 0; + } + } + + const union mv ref = b->mv[0]; + read_mv_residual(ts, &b->mv[0], -1); + + // clip intrabc motion vector to decoded parts of current tile + int border_left = ts->tiling.col_start * 4; + int border_top = ts->tiling.row_start * 4; + if (has_chroma) { + if (bw4 < 2 && ss_hor) + border_left += 4; + if (bh4 < 2 && ss_ver) + border_top += 4; + } + int src_left = t->bx * 4 + (b->mv[0].x >> 3); + int src_top = t->by * 4 + (b->mv[0].y >> 3); + int src_right = src_left + bw4 * 4; + int src_bottom = src_top + bh4 * 4; + const int border_right = ((ts->tiling.col_end + (bw4 - 1)) & ~(bw4 - 1)) * 4; + + // check against left or right tile boundary and adjust if necessary + if (src_left < border_left) { + src_right += border_left - src_left; + src_left += border_left - src_left; + } else if (src_right > border_right) { + src_left -= src_right - border_right; + src_right -= src_right - border_right; + } + // check against top tile boundary and adjust if necessary + if (src_top < border_top) { + src_bottom += border_top - src_top; + src_top += border_top - src_top; + } + + const int sbx = (t->bx >> (4 + f->seq_hdr->sb128)) << (6 + f->seq_hdr->sb128); + const int sby = (t->by >> (4 + f->seq_hdr->sb128)) << (6 + f->seq_hdr->sb128); + const int sb_size = 1 << (6 + f->seq_hdr->sb128); + // check for overlap with current superblock + if (src_bottom > sby && src_right > sbx) { + if (src_top - border_top >= src_bottom - sby) { + // if possible move src up into the previous suberblock row + src_top -= src_bottom - sby; + src_bottom -= src_bottom - sby; + } else if (src_left - border_left >= src_right - sbx) { + // if possible move src left into the previous suberblock + src_left -= src_right - sbx; + src_right -= src_right - sbx; + } + } + // move src up if it is below current superblock row + if (src_bottom > sby + sb_size) { + src_top -= src_bottom - (sby + sb_size); + src_bottom -= src_bottom - (sby + sb_size); + } + // error out if mv still overlaps with the current superblock + if (src_bottom > sby && src_right > sbx) + return -1; + + b->mv[0].x = (src_left - t->bx * 4) * 8; + b->mv[0].y = (src_top - t->by * 4) * 8; + + if (DEBUG_BLOCK_INFO) + printf("Post-dmv[%d/%d,ref=%d/%d|%d/%d]: r=%d\n", + b->mv[0].y, b->mv[0].x, ref.y, ref.x, + mvstack[0].mv.mv[0].y, mvstack[0].mv.mv[0].x, ts->msac.rng); + read_vartx_tree(t, b, bs, bx4, by4); + + // reconstruction + if (t->frame_thread.pass == 1) { + f->bd_fn.read_coef_blocks(t, bs, b); + b->filter2d = FILTER_2D_BILINEAR; + } else { + if (f->bd_fn.recon_b_inter(t, bs, b)) return -1; + } + + splat_intrabc_mv(f->c, t, bs, b, bw4, bh4); + BlockContext *edge = t->a; + for (int i = 0, off = bx4; i < 2; i++, off = by4, edge = &t->l) { +#define set_ctx(rep_macro) \ + rep_macro(edge->tx_intra, off, b_dim[2 + i]); \ + rep_macro(edge->mode, off, DC_PRED); \ + rep_macro(edge->pal_sz, off, 0); \ + /* see aomedia bug 2183 for why this is outside if (has_chroma) */ \ + rep_macro(t->pal_sz_uv[i], off, 0); \ + rep_macro(edge->seg_pred, off, seg_pred); \ + rep_macro(edge->skip_mode, off, 0); \ + rep_macro(edge->intra, off, 0); \ + rep_macro(edge->skip, off, b->skip) + case_set(b_dim[2 + i]); +#undef set_ctx + } + if (has_chroma) { + dav1d_memset_pow2[ulog2(cbw4)](&t->a->uvmode[cbx4], DC_PRED); + dav1d_memset_pow2[ulog2(cbh4)](&t->l.uvmode[cby4], DC_PRED); + } + } else { + // inter-specific mode/mv coding + int is_comp, has_subpel_filter; + + if (b->skip_mode) { + is_comp = 1; + } else if ((!seg || (seg->ref == -1 && !seg->globalmv && !seg->skip)) && + f->frame_hdr->switchable_comp_refs && imin(bw4, bh4) > 1) + { + const int ctx = get_comp_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + is_comp = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.comp[ctx]); + if (DEBUG_BLOCK_INFO) + printf("Post-compflag[%d]: r=%d\n", is_comp, ts->msac.rng); + } else { + is_comp = 0; + } + + if (b->skip_mode) { + b->ref[0] = f->frame_hdr->skip_mode_refs[0]; + b->ref[1] = f->frame_hdr->skip_mode_refs[1]; + b->comp_type = COMP_INTER_AVG; + b->inter_mode = NEARESTMV_NEARESTMV; + b->drl_idx = NEAREST_DRL; + has_subpel_filter = 0; + + refmvs_candidate mvstack[8]; + int n_mvs, ctx; + dav1d_refmvs_find(&t->rt, mvstack, &n_mvs, &ctx, + (union refmvs_refpair) { .ref = { + b->ref[0] + 1, b->ref[1] + 1 }}, + bs, intra_edge_flags, t->by, t->bx); + + b->mv[0] = mvstack[0].mv.mv[0]; + b->mv[1] = mvstack[0].mv.mv[1]; + fix_mv_precision(f->frame_hdr, &b->mv[0]); + fix_mv_precision(f->frame_hdr, &b->mv[1]); + if (DEBUG_BLOCK_INFO) + printf("Post-skipmodeblock[mv=1:y=%d,x=%d,2:y=%d,x=%d,refs=%d+%d\n", + b->mv[0].y, b->mv[0].x, b->mv[1].y, b->mv[1].x, + b->ref[0], b->ref[1]); + } else if (is_comp) { + const int dir_ctx = get_comp_dir_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + if (dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.comp_dir[dir_ctx])) + { + // bidir - first reference (fw) + const int ctx1 = av1_get_fwd_ref_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + if (dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.comp_fwd_ref[0][ctx1])) + { + const int ctx2 = av1_get_fwd_ref_2_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + b->ref[0] = 2 + dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.comp_fwd_ref[2][ctx2]); + } else { + const int ctx2 = av1_get_fwd_ref_1_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + b->ref[0] = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.comp_fwd_ref[1][ctx2]); + } + + // second reference (bw) + const int ctx3 = av1_get_bwd_ref_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + if (dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.comp_bwd_ref[0][ctx3])) + { + b->ref[1] = 6; + } else { + const int ctx4 = av1_get_bwd_ref_1_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + b->ref[1] = 4 + dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.comp_bwd_ref[1][ctx4]); + } + } else { + // unidir + const int uctx_p = av1_get_uni_p_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + if (dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.comp_uni_ref[0][uctx_p])) + { + b->ref[0] = 4; + b->ref[1] = 6; + } else { + const int uctx_p1 = av1_get_uni_p1_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + b->ref[0] = 0; + b->ref[1] = 1 + dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.comp_uni_ref[1][uctx_p1]); + if (b->ref[1] == 2) { + const int uctx_p2 = av1_get_uni_p2_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + b->ref[1] += dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.comp_uni_ref[2][uctx_p2]); + } + } + } + if (DEBUG_BLOCK_INFO) + printf("Post-refs[%d/%d]: r=%d\n", + b->ref[0], b->ref[1], ts->msac.rng); + + refmvs_candidate mvstack[8]; + int n_mvs, ctx; + dav1d_refmvs_find(&t->rt, mvstack, &n_mvs, &ctx, + (union refmvs_refpair) { .ref = { + b->ref[0] + 1, b->ref[1] + 1 }}, + bs, intra_edge_flags, t->by, t->bx); + + b->inter_mode = dav1d_msac_decode_symbol_adapt8(&ts->msac, + ts->cdf.m.comp_inter_mode[ctx], + N_COMP_INTER_PRED_MODES - 1); + if (DEBUG_BLOCK_INFO) + printf("Post-compintermode[%d,ctx=%d,n_mvs=%d]: r=%d\n", + b->inter_mode, ctx, n_mvs, ts->msac.rng); + + const uint8_t *const im = dav1d_comp_inter_pred_modes[b->inter_mode]; + b->drl_idx = NEAREST_DRL; + if (b->inter_mode == NEWMV_NEWMV) { + if (n_mvs > 1) { // NEARER, NEAR or NEARISH + const int drl_ctx_v1 = get_drl_context(mvstack, 0); + b->drl_idx += dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.drl_bit[drl_ctx_v1]); + if (b->drl_idx == NEARER_DRL && n_mvs > 2) { + const int drl_ctx_v2 = get_drl_context(mvstack, 1); + b->drl_idx += dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.drl_bit[drl_ctx_v2]); + } + if (DEBUG_BLOCK_INFO) + printf("Post-drlidx[%d,n_mvs=%d]: r=%d\n", + b->drl_idx, n_mvs, ts->msac.rng); + } + } else if (im[0] == NEARMV || im[1] == NEARMV) { + b->drl_idx = NEARER_DRL; + if (n_mvs > 2) { // NEAR or NEARISH + const int drl_ctx_v2 = get_drl_context(mvstack, 1); + b->drl_idx += dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.drl_bit[drl_ctx_v2]); + if (b->drl_idx == NEAR_DRL && n_mvs > 3) { + const int drl_ctx_v3 = get_drl_context(mvstack, 2); + b->drl_idx += dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.drl_bit[drl_ctx_v3]); + } + if (DEBUG_BLOCK_INFO) + printf("Post-drlidx[%d,n_mvs=%d]: r=%d\n", + b->drl_idx, n_mvs, ts->msac.rng); + } + } + assert(b->drl_idx >= NEAREST_DRL && b->drl_idx <= NEARISH_DRL); + +#define assign_comp_mv(idx) \ + switch (im[idx]) { \ + case NEARMV: \ + case NEARESTMV: \ + b->mv[idx] = mvstack[b->drl_idx].mv.mv[idx]; \ + fix_mv_precision(f->frame_hdr, &b->mv[idx]); \ + break; \ + case GLOBALMV: \ + has_subpel_filter |= \ + f->frame_hdr->gmv[b->ref[idx]].type == DAV1D_WM_TYPE_TRANSLATION; \ + b->mv[idx] = get_gmv_2d(&f->frame_hdr->gmv[b->ref[idx]], \ + t->bx, t->by, bw4, bh4, f->frame_hdr); \ + break; \ + case NEWMV: \ + b->mv[idx] = mvstack[b->drl_idx].mv.mv[idx]; \ + const int mv_prec = f->frame_hdr->hp - f->frame_hdr->force_integer_mv; \ + read_mv_residual(ts, &b->mv[idx], mv_prec); \ + break; \ + } + has_subpel_filter = imin(bw4, bh4) == 1 || + b->inter_mode != GLOBALMV_GLOBALMV; + assign_comp_mv(0); + assign_comp_mv(1); +#undef assign_comp_mv + if (DEBUG_BLOCK_INFO) + printf("Post-residual_mv[1:y=%d,x=%d,2:y=%d,x=%d]: r=%d\n", + b->mv[0].y, b->mv[0].x, b->mv[1].y, b->mv[1].x, + ts->msac.rng); + + // jnt_comp vs. seg vs. wedge + int is_segwedge = 0; + if (f->seq_hdr->masked_compound) { + const int mask_ctx = get_mask_comp_ctx(t->a, &t->l, by4, bx4); + + is_segwedge = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.mask_comp[mask_ctx]); + if (DEBUG_BLOCK_INFO) + printf("Post-segwedge_vs_jntavg[%d,ctx=%d]: r=%d\n", + is_segwedge, mask_ctx, ts->msac.rng); + } + + if (!is_segwedge) { + if (f->seq_hdr->jnt_comp) { + const int jnt_ctx = + get_jnt_comp_ctx(f->seq_hdr->order_hint_n_bits, + f->cur.frame_hdr->frame_offset, + f->refp[b->ref[0]].p.frame_hdr->frame_offset, + f->refp[b->ref[1]].p.frame_hdr->frame_offset, + t->a, &t->l, by4, bx4); + b->comp_type = COMP_INTER_WEIGHTED_AVG + + dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.jnt_comp[jnt_ctx]); + if (DEBUG_BLOCK_INFO) + printf("Post-jnt_comp[%d,ctx=%d[ac:%d,ar:%d,lc:%d,lr:%d]]: r=%d\n", + b->comp_type == COMP_INTER_AVG, + jnt_ctx, t->a->comp_type[bx4], t->a->ref[0][bx4], + t->l.comp_type[by4], t->l.ref[0][by4], + ts->msac.rng); + } else { + b->comp_type = COMP_INTER_AVG; + } + } else { + if (wedge_allowed_mask & (1 << bs)) { + const int ctx = dav1d_wedge_ctx_lut[bs]; + b->comp_type = COMP_INTER_WEDGE - + dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.wedge_comp[ctx]); + if (b->comp_type == COMP_INTER_WEDGE) + b->wedge_idx = dav1d_msac_decode_symbol_adapt16(&ts->msac, + ts->cdf.m.wedge_idx[ctx], 15); + } else { + b->comp_type = COMP_INTER_SEG; + } + b->mask_sign = dav1d_msac_decode_bool_equi(&ts->msac); + if (DEBUG_BLOCK_INFO) + printf("Post-seg/wedge[%d,wedge_idx=%d,sign=%d]: r=%d\n", + b->comp_type == COMP_INTER_WEDGE, + b->wedge_idx, b->mask_sign, ts->msac.rng); + } + } else { + b->comp_type = COMP_INTER_NONE; + + // ref + if (seg && seg->ref > 0) { + b->ref[0] = seg->ref - 1; + } else if (seg && (seg->globalmv || seg->skip)) { + b->ref[0] = 0; + } else { + const int ctx1 = av1_get_ref_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + if (dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.ref[0][ctx1])) + { + const int ctx2 = av1_get_ref_2_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + if (dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.ref[1][ctx2])) + { + b->ref[0] = 6; + } else { + const int ctx3 = av1_get_ref_6_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + b->ref[0] = 4 + dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.ref[5][ctx3]); + } + } else { + const int ctx2 = av1_get_ref_3_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + if (dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.ref[2][ctx2])) + { + const int ctx3 = av1_get_ref_5_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + b->ref[0] = 2 + dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.ref[4][ctx3]); + } else { + const int ctx3 = av1_get_ref_4_ctx(t->a, &t->l, by4, bx4, + have_top, have_left); + b->ref[0] = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.ref[3][ctx3]); + } + } + if (DEBUG_BLOCK_INFO) + printf("Post-ref[%d]: r=%d\n", b->ref[0], ts->msac.rng); + } + b->ref[1] = -1; + + refmvs_candidate mvstack[8]; + int n_mvs, ctx; + dav1d_refmvs_find(&t->rt, mvstack, &n_mvs, &ctx, + (union refmvs_refpair) { .ref = { b->ref[0] + 1, -1 }}, + bs, intra_edge_flags, t->by, t->bx); + + // mode parsing and mv derivation from ref_mvs + if ((seg && (seg->skip || seg->globalmv)) || + dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.newmv_mode[ctx & 7])) + { + if ((seg && (seg->skip || seg->globalmv)) || + !dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.globalmv_mode[(ctx >> 3) & 1])) + { + b->inter_mode = GLOBALMV; + b->mv[0] = get_gmv_2d(&f->frame_hdr->gmv[b->ref[0]], + t->bx, t->by, bw4, bh4, f->frame_hdr); + has_subpel_filter = imin(bw4, bh4) == 1 || + f->frame_hdr->gmv[b->ref[0]].type == DAV1D_WM_TYPE_TRANSLATION; + } else { + has_subpel_filter = 1; + if (dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.refmv_mode[(ctx >> 4) & 15])) + { // NEAREST, NEARER, NEAR or NEARISH + b->inter_mode = NEARMV; + b->drl_idx = NEARER_DRL; + if (n_mvs > 2) { // NEARER, NEAR or NEARISH + const int drl_ctx_v2 = get_drl_context(mvstack, 1); + b->drl_idx += dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.drl_bit[drl_ctx_v2]); + if (b->drl_idx == NEAR_DRL && n_mvs > 3) { // NEAR or NEARISH + const int drl_ctx_v3 = + get_drl_context(mvstack, 2); + b->drl_idx += dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.drl_bit[drl_ctx_v3]); + } + } + } else { + b->inter_mode = NEARESTMV; + b->drl_idx = NEAREST_DRL; + } + assert(b->drl_idx >= NEAREST_DRL && b->drl_idx <= NEARISH_DRL); + b->mv[0] = mvstack[b->drl_idx].mv.mv[0]; + if (b->drl_idx < NEAR_DRL) + fix_mv_precision(f->frame_hdr, &b->mv[0]); + } + + if (DEBUG_BLOCK_INFO) + printf("Post-intermode[%d,drl=%d,mv=y:%d,x:%d,n_mvs=%d]: r=%d\n", + b->inter_mode, b->drl_idx, b->mv[0].y, b->mv[0].x, n_mvs, + ts->msac.rng); + } else { + has_subpel_filter = 1; + b->inter_mode = NEWMV; + b->drl_idx = NEAREST_DRL; + if (n_mvs > 1) { // NEARER, NEAR or NEARISH + const int drl_ctx_v1 = get_drl_context(mvstack, 0); + b->drl_idx += dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.drl_bit[drl_ctx_v1]); + if (b->drl_idx == NEARER_DRL && n_mvs > 2) { // NEAR or NEARISH + const int drl_ctx_v2 = get_drl_context(mvstack, 1); + b->drl_idx += dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.drl_bit[drl_ctx_v2]); + } + } + assert(b->drl_idx >= NEAREST_DRL && b->drl_idx <= NEARISH_DRL); + if (n_mvs > 1) { + b->mv[0] = mvstack[b->drl_idx].mv.mv[0]; + } else { + assert(!b->drl_idx); + b->mv[0] = mvstack[0].mv.mv[0]; + fix_mv_precision(f->frame_hdr, &b->mv[0]); + } + if (DEBUG_BLOCK_INFO) + printf("Post-intermode[%d,drl=%d]: r=%d\n", + b->inter_mode, b->drl_idx, ts->msac.rng); + const int mv_prec = f->frame_hdr->hp - f->frame_hdr->force_integer_mv; + read_mv_residual(ts, &b->mv[0], mv_prec); + if (DEBUG_BLOCK_INFO) + printf("Post-residualmv[mv=y:%d,x:%d]: r=%d\n", + b->mv[0].y, b->mv[0].x, ts->msac.rng); + } + + // interintra flags + const int ii_sz_grp = dav1d_ymode_size_context[bs]; + if (f->seq_hdr->inter_intra && + interintra_allowed_mask & (1 << bs) && + dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.interintra[ii_sz_grp])) + { + b->interintra_mode = dav1d_msac_decode_symbol_adapt4(&ts->msac, + ts->cdf.m.interintra_mode[ii_sz_grp], + N_INTER_INTRA_PRED_MODES - 1); + const int wedge_ctx = dav1d_wedge_ctx_lut[bs]; + b->interintra_type = INTER_INTRA_BLEND + + dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.interintra_wedge[wedge_ctx]); + if (b->interintra_type == INTER_INTRA_WEDGE) + b->wedge_idx = dav1d_msac_decode_symbol_adapt16(&ts->msac, + ts->cdf.m.wedge_idx[wedge_ctx], 15); + } else { + b->interintra_type = INTER_INTRA_NONE; + } + if (DEBUG_BLOCK_INFO && f->seq_hdr->inter_intra && + interintra_allowed_mask & (1 << bs)) + { + printf("Post-interintra[t=%d,m=%d,w=%d]: r=%d\n", + b->interintra_type, b->interintra_mode, + b->wedge_idx, ts->msac.rng); + } + + // motion variation + if (f->frame_hdr->switchable_motion_mode && + b->interintra_type == INTER_INTRA_NONE && imin(bw4, bh4) >= 2 && + // is not warped global motion + !(!f->frame_hdr->force_integer_mv && b->inter_mode == GLOBALMV && + f->frame_hdr->gmv[b->ref[0]].type > DAV1D_WM_TYPE_TRANSLATION) && + // has overlappable neighbours + ((have_left && findoddzero(&t->l.intra[by4 + 1], h4 >> 1)) || + (have_top && findoddzero(&t->a->intra[bx4 + 1], w4 >> 1)))) + { + // reaching here means the block allows obmc - check warp by + // finding matching-ref blocks in top/left edges + uint64_t mask[2] = { 0, 0 }; + find_matching_ref(t, intra_edge_flags, bw4, bh4, w4, h4, + have_left, have_top, b->ref[0], mask); + const int allow_warp = !f->svc[b->ref[0]][0].scale && + !f->frame_hdr->force_integer_mv && + f->frame_hdr->warp_motion && (mask[0] | mask[1]); + + b->motion_mode = allow_warp ? + dav1d_msac_decode_symbol_adapt4(&ts->msac, + ts->cdf.m.motion_mode[bs], 2) : + dav1d_msac_decode_bool_adapt(&ts->msac, ts->cdf.m.obmc[bs]); + if (b->motion_mode == MM_WARP) { + has_subpel_filter = 0; + derive_warpmv(t, bw4, bh4, mask, b->mv[0], &t->warpmv); +#define signabs(v) v < 0 ? '-' : ' ', abs(v) + if (DEBUG_BLOCK_INFO) + printf("[ %c%x %c%x %c%x\n %c%x %c%x %c%x ]\n" + "alpha=%c%x, beta=%c%x, gamma=%c%x, delta=%c%x, " + "mv=y:%d,x:%d\n", + signabs(t->warpmv.matrix[0]), + signabs(t->warpmv.matrix[1]), + signabs(t->warpmv.matrix[2]), + signabs(t->warpmv.matrix[3]), + signabs(t->warpmv.matrix[4]), + signabs(t->warpmv.matrix[5]), + signabs(t->warpmv.u.p.alpha), + signabs(t->warpmv.u.p.beta), + signabs(t->warpmv.u.p.gamma), + signabs(t->warpmv.u.p.delta), + b->mv[0].y, b->mv[0].x); +#undef signabs + if (t->frame_thread.pass) { + if (t->warpmv.type == DAV1D_WM_TYPE_AFFINE) { + b->matrix[0] = t->warpmv.matrix[2] - 0x10000; + b->matrix[1] = t->warpmv.matrix[3]; + b->matrix[2] = t->warpmv.matrix[4]; + b->matrix[3] = t->warpmv.matrix[5] - 0x10000; + } else { + b->matrix[0] = INT16_MIN; + } + } + } + + if (DEBUG_BLOCK_INFO) + printf("Post-motionmode[%d]: r=%d [mask: 0x%" PRIx64 "/0x%" + PRIx64 "]\n", b->motion_mode, ts->msac.rng, mask[0], + mask[1]); + } else { + b->motion_mode = MM_TRANSLATION; + } + } + + // subpel filter + enum Dav1dFilterMode filter[2]; + if (f->frame_hdr->subpel_filter_mode == DAV1D_FILTER_SWITCHABLE) { + if (has_subpel_filter) { + const int comp = b->comp_type != COMP_INTER_NONE; + const int ctx1 = get_filter_ctx(t->a, &t->l, comp, 0, b->ref[0], + by4, bx4); + filter[0] = dav1d_msac_decode_symbol_adapt4(&ts->msac, + ts->cdf.m.filter[0][ctx1], + DAV1D_N_SWITCHABLE_FILTERS - 1); + if (f->seq_hdr->dual_filter) { + const int ctx2 = get_filter_ctx(t->a, &t->l, comp, 1, + b->ref[0], by4, bx4); + if (DEBUG_BLOCK_INFO) + printf("Post-subpel_filter1[%d,ctx=%d]: r=%d\n", + filter[0], ctx1, ts->msac.rng); + filter[1] = dav1d_msac_decode_symbol_adapt4(&ts->msac, + ts->cdf.m.filter[1][ctx2], + DAV1D_N_SWITCHABLE_FILTERS - 1); + if (DEBUG_BLOCK_INFO) + printf("Post-subpel_filter2[%d,ctx=%d]: r=%d\n", + filter[1], ctx2, ts->msac.rng); + } else { + filter[1] = filter[0]; + if (DEBUG_BLOCK_INFO) + printf("Post-subpel_filter[%d,ctx=%d]: r=%d\n", + filter[0], ctx1, ts->msac.rng); + } + } else { + filter[0] = filter[1] = DAV1D_FILTER_8TAP_REGULAR; + } + } else { + filter[0] = filter[1] = f->frame_hdr->subpel_filter_mode; + } + b->filter2d = dav1d_filter_2d[filter[1]][filter[0]]; + + read_vartx_tree(t, b, bs, bx4, by4); + + // reconstruction + if (t->frame_thread.pass == 1) { + f->bd_fn.read_coef_blocks(t, bs, b); + } else { + if (f->bd_fn.recon_b_inter(t, bs, b)) return -1; + } + + if (f->frame_hdr->loopfilter.level_y[0] || + f->frame_hdr->loopfilter.level_y[1]) + { + const int is_globalmv = + b->inter_mode == (is_comp ? GLOBALMV_GLOBALMV : GLOBALMV); + const uint8_t (*const lf_lvls)[8][2] = (const uint8_t (*)[8][2]) + &ts->lflvl[b->seg_id][0][b->ref[0] + 1][!is_globalmv]; + const uint16_t tx_split[2] = { b->tx_split0, b->tx_split1 }; + enum RectTxfmSize ytx = b->max_ytx, uvtx = b->uvtx; + if (f->frame_hdr->segmentation.lossless[b->seg_id]) { + ytx = (enum RectTxfmSize) TX_4X4; + uvtx = (enum RectTxfmSize) TX_4X4; + } + dav1d_create_lf_mask_inter(t->lf_mask, f->lf.level, f->b4_stride, lf_lvls, + t->bx, t->by, f->w4, f->h4, b->skip, bs, + ytx, tx_split, uvtx, f->cur.p.layout, + &t->a->tx_lpf_y[bx4], &t->l.tx_lpf_y[by4], + has_chroma ? &t->a->tx_lpf_uv[cbx4] : NULL, + has_chroma ? &t->l.tx_lpf_uv[cby4] : NULL); + } + + // context updates + if (is_comp) + splat_tworef_mv(f->c, t, bs, b, bw4, bh4); + else + splat_oneref_mv(f->c, t, bs, b, bw4, bh4); + BlockContext *edge = t->a; + for (int i = 0, off = bx4; i < 2; i++, off = by4, edge = &t->l) { +#define set_ctx(rep_macro) \ + rep_macro(edge->seg_pred, off, seg_pred); \ + rep_macro(edge->skip_mode, off, b->skip_mode); \ + rep_macro(edge->intra, off, 0); \ + rep_macro(edge->skip, off, b->skip); \ + rep_macro(edge->pal_sz, off, 0); \ + /* see aomedia bug 2183 for why this is outside if (has_chroma) */ \ + rep_macro(t->pal_sz_uv[i], off, 0); \ + rep_macro(edge->tx_intra, off, b_dim[2 + i]); \ + rep_macro(edge->comp_type, off, b->comp_type); \ + rep_macro(edge->filter[0], off, filter[0]); \ + rep_macro(edge->filter[1], off, filter[1]); \ + rep_macro(edge->mode, off, b->inter_mode); \ + rep_macro(edge->ref[0], off, b->ref[0]); \ + rep_macro(edge->ref[1], off, ((uint8_t) b->ref[1])) + case_set(b_dim[2 + i]); +#undef set_ctx + } + if (has_chroma) { + dav1d_memset_pow2[ulog2(cbw4)](&t->a->uvmode[cbx4], DC_PRED); + dav1d_memset_pow2[ulog2(cbh4)](&t->l.uvmode[cby4], DC_PRED); + } + } + + // update contexts + if (f->frame_hdr->segmentation.enabled && + f->frame_hdr->segmentation.update_map) + { + uint8_t *seg_ptr = &f->cur_segmap[t->by * f->b4_stride + t->bx]; +#define set_ctx(rep_macro) \ + for (int y = 0; y < bh4; y++) { \ + rep_macro(seg_ptr, 0, b->seg_id); \ + seg_ptr += f->b4_stride; \ + } + case_set(b_dim[2]); +#undef set_ctx + } + if (!b->skip) { + uint16_t (*noskip_mask)[2] = &t->lf_mask->noskip_mask[by4 >> 1]; + const unsigned mask = (~0U >> (32 - bw4)) << (bx4 & 15); + const int bx_idx = (bx4 & 16) >> 4; + for (int y = 0; y < bh4; y += 2, noskip_mask++) { + (*noskip_mask)[bx_idx] |= mask; + if (bw4 == 32) // this should be mask >> 16, but it's 0xffffffff anyway + (*noskip_mask)[1] |= mask; + } + } + + if (t->frame_thread.pass == 1 && !b->intra && IS_INTER_OR_SWITCH(f->frame_hdr)) { + const int sby = (t->by - ts->tiling.row_start) >> f->sb_shift; + int (*const lowest_px)[2] = ts->lowest_pixel[sby]; + + // keep track of motion vectors for each reference + if (b->comp_type == COMP_INTER_NONE) { + // y + if (imin(bw4, bh4) > 1 && + ((b->inter_mode == GLOBALMV && f->gmv_warp_allowed[b->ref[0]]) || + (b->motion_mode == MM_WARP && t->warpmv.type > DAV1D_WM_TYPE_TRANSLATION))) + { + affine_lowest_px_luma(t, &lowest_px[b->ref[0]][0], b_dim, + b->motion_mode == MM_WARP ? &t->warpmv : + &f->frame_hdr->gmv[b->ref[0]]); + } else { + mc_lowest_px(&lowest_px[b->ref[0]][0], t->by, bh4, b->mv[0].y, + 0, &f->svc[b->ref[0]][1]); + if (b->motion_mode == MM_OBMC) { + obmc_lowest_px(t, lowest_px, 0, b_dim, bx4, by4, w4, h4); + } + } + + // uv + if (has_chroma) { + // sub8x8 derivation + int is_sub8x8 = bw4 == ss_hor || bh4 == ss_ver; + refmvs_block *const *r; + if (is_sub8x8) { + assert(ss_hor == 1); + r = &t->rt.r[(t->by & 31) + 5]; + if (bw4 == 1) is_sub8x8 &= r[0][t->bx - 1].ref.ref[0] > 0; + if (bh4 == ss_ver) is_sub8x8 &= r[-1][t->bx].ref.ref[0] > 0; + if (bw4 == 1 && bh4 == ss_ver) + is_sub8x8 &= r[-1][t->bx - 1].ref.ref[0] > 0; + } + + // chroma prediction + if (is_sub8x8) { + assert(ss_hor == 1); + if (bw4 == 1 && bh4 == ss_ver) { + const refmvs_block *const rr = &r[-1][t->bx - 1]; + mc_lowest_px(&lowest_px[rr->ref.ref[0] - 1][1], + t->by - 1, bh4, rr->mv.mv[0].y, ss_ver, + &f->svc[rr->ref.ref[0] - 1][1]); + } + if (bw4 == 1) { + const refmvs_block *const rr = &r[0][t->bx - 1]; + mc_lowest_px(&lowest_px[rr->ref.ref[0] - 1][1], + t->by, bh4, rr->mv.mv[0].y, ss_ver, + &f->svc[rr->ref.ref[0] - 1][1]); + } + if (bh4 == ss_ver) { + const refmvs_block *const rr = &r[-1][t->bx]; + mc_lowest_px(&lowest_px[rr->ref.ref[0] - 1][1], + t->by - 1, bh4, rr->mv.mv[0].y, ss_ver, + &f->svc[rr->ref.ref[0] - 1][1]); + } + mc_lowest_px(&lowest_px[b->ref[0]][1], t->by, bh4, + b->mv[0].y, ss_ver, &f->svc[b->ref[0]][1]); + } else { + if (imin(cbw4, cbh4) > 1 && + ((b->inter_mode == GLOBALMV && f->gmv_warp_allowed[b->ref[0]]) || + (b->motion_mode == MM_WARP && t->warpmv.type > DAV1D_WM_TYPE_TRANSLATION))) + { + affine_lowest_px_chroma(t, &lowest_px[b->ref[0]][1], b_dim, + b->motion_mode == MM_WARP ? &t->warpmv : + &f->frame_hdr->gmv[b->ref[0]]); + } else { + mc_lowest_px(&lowest_px[b->ref[0]][1], + t->by & ~ss_ver, bh4 << (bh4 == ss_ver), + b->mv[0].y, ss_ver, &f->svc[b->ref[0]][1]); + if (b->motion_mode == MM_OBMC) { + obmc_lowest_px(t, lowest_px, 1, b_dim, bx4, by4, w4, h4); + } + } + } + } + } else { + // y + for (int i = 0; i < 2; i++) { + if (b->inter_mode == GLOBALMV_GLOBALMV && f->gmv_warp_allowed[b->ref[i]]) { + affine_lowest_px_luma(t, &lowest_px[b->ref[i]][0], b_dim, + &f->frame_hdr->gmv[b->ref[i]]); + } else { + mc_lowest_px(&lowest_px[b->ref[i]][0], t->by, bh4, + b->mv[i].y, 0, &f->svc[b->ref[i]][1]); + } + } + + // uv + if (has_chroma) for (int i = 0; i < 2; i++) { + if (b->inter_mode == GLOBALMV_GLOBALMV && + imin(cbw4, cbh4) > 1 && f->gmv_warp_allowed[b->ref[i]]) + { + affine_lowest_px_chroma(t, &lowest_px[b->ref[i]][1], b_dim, + &f->frame_hdr->gmv[b->ref[i]]); + } else { + mc_lowest_px(&lowest_px[b->ref[i]][1], t->by, bh4, + b->mv[i].y, ss_ver, &f->svc[b->ref[i]][1]); + } + } + } + } + + return 0; +} + +#if __has_feature(memory_sanitizer) + +#include + +static int checked_decode_b(Dav1dTaskContext *const t, + const enum BlockLevel bl, + const enum BlockSize bs, + const enum BlockPartition bp, + const enum EdgeFlags intra_edge_flags) +{ + const Dav1dFrameContext *const f = t->f; + const int err = decode_b(t, bl, bs, bp, intra_edge_flags); + + if (err == 0 && !(t->frame_thread.pass & 1)) { + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const uint8_t *const b_dim = dav1d_block_dimensions[bs]; + const int bw4 = b_dim[0], bh4 = b_dim[1]; + const int w4 = imin(bw4, f->bw - t->bx), h4 = imin(bh4, f->bh - t->by); + const int has_chroma = f->seq_hdr->layout != DAV1D_PIXEL_LAYOUT_I400 && + (bw4 > ss_hor || t->bx & 1) && + (bh4 > ss_ver || t->by & 1); + + for (int p = 0; p < 1 + 2 * has_chroma; p++) { + const int ss_ver = p && f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = p && f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const ptrdiff_t stride = f->cur.stride[!!p]; + const int bx = t->bx & ~ss_hor; + const int by = t->by & ~ss_ver; + const int width = w4 << (2 - ss_hor + (bw4 == ss_hor)); + const int height = h4 << (2 - ss_ver + (bh4 == ss_ver)); + + const uint8_t *data = f->cur.data[p] + (by << (2 - ss_ver)) * stride + + (bx << (2 - ss_hor + !!f->seq_hdr->hbd)); + + for (int y = 0; y < height; data += stride, y++) { + const size_t line_sz = width << !!f->seq_hdr->hbd; + if (__msan_test_shadow(data, line_sz) != -1) { + fprintf(stderr, "B[%d](%d, %d) w4:%d, h4:%d, row:%d\n", + p, bx, by, w4, h4, y); + __msan_check_mem_is_initialized(data, line_sz); + } + } + } + } + + return err; +} + +#define decode_b checked_decode_b + +#endif /* defined(__has_feature) */ + +static int decode_sb(Dav1dTaskContext *const t, const enum BlockLevel bl, + const EdgeNode *const node) +{ + const Dav1dFrameContext *const f = t->f; + Dav1dTileState *const ts = t->ts; + const int hsz = 16 >> bl; + const int have_h_split = f->bw > t->bx + hsz; + const int have_v_split = f->bh > t->by + hsz; + + if (!have_h_split && !have_v_split) { + assert(bl < BL_8X8); + return decode_sb(t, bl + 1, INTRA_EDGE_SPLIT(node, 0)); + } + + uint16_t *pc; + enum BlockPartition bp; + int ctx, bx8, by8; + if (t->frame_thread.pass != 2) { + if (0 && bl == BL_64X64) + printf("poc=%d,y=%d,x=%d,bl=%d,r=%d\n", + f->frame_hdr->frame_offset, t->by, t->bx, bl, ts->msac.rng); + bx8 = (t->bx & 31) >> 1; + by8 = (t->by & 31) >> 1; + ctx = get_partition_ctx(t->a, &t->l, bl, by8, bx8); + pc = ts->cdf.m.partition[bl][ctx]; + } + + if (have_h_split && have_v_split) { + if (t->frame_thread.pass == 2) { + const Av1Block *const b = &f->frame_thread.b[t->by * f->b4_stride + t->bx]; + bp = b->bl == bl ? b->bp : PARTITION_SPLIT; + } else { + bp = dav1d_msac_decode_symbol_adapt16(&ts->msac, pc, + dav1d_partition_type_count[bl]); + if (f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I422 && + (bp == PARTITION_V || bp == PARTITION_V4 || + bp == PARTITION_T_LEFT_SPLIT || bp == PARTITION_T_RIGHT_SPLIT)) + { + return 1; + } + if (DEBUG_BLOCK_INFO) + printf("poc=%d,y=%d,x=%d,bl=%d,ctx=%d,bp=%d: r=%d\n", + f->frame_hdr->frame_offset, t->by, t->bx, bl, ctx, bp, + ts->msac.rng); + } + const uint8_t *const b = dav1d_block_sizes[bl][bp]; + + switch (bp) { + case PARTITION_NONE: + if (decode_b(t, bl, b[0], PARTITION_NONE, node->o)) + return -1; + break; + case PARTITION_H: + if (decode_b(t, bl, b[0], PARTITION_H, node->h[0])) + return -1; + t->by += hsz; + if (decode_b(t, bl, b[0], PARTITION_H, node->h[1])) + return -1; + t->by -= hsz; + break; + case PARTITION_V: + if (decode_b(t, bl, b[0], PARTITION_V, node->v[0])) + return -1; + t->bx += hsz; + if (decode_b(t, bl, b[0], PARTITION_V, node->v[1])) + return -1; + t->bx -= hsz; + break; + case PARTITION_SPLIT: + if (bl == BL_8X8) { + const EdgeTip *const tip = (const EdgeTip *) node; + assert(hsz == 1); + if (decode_b(t, bl, BS_4x4, PARTITION_SPLIT, EDGE_ALL_TR_AND_BL)) + return -1; + const enum Filter2d tl_filter = t->tl_4x4_filter; + t->bx++; + if (decode_b(t, bl, BS_4x4, PARTITION_SPLIT, tip->split[0])) + return -1; + t->bx--; + t->by++; + if (decode_b(t, bl, BS_4x4, PARTITION_SPLIT, tip->split[1])) + return -1; + t->bx++; + t->tl_4x4_filter = tl_filter; + if (decode_b(t, bl, BS_4x4, PARTITION_SPLIT, tip->split[2])) + return -1; + t->bx--; + t->by--; +#if ARCH_X86_64 + if (t->frame_thread.pass) { + /* In 8-bit mode with 2-pass decoding the coefficient buffer + * can end up misaligned due to skips here. Work around + * the issue by explicitly realigning the buffer. */ + const int p = t->frame_thread.pass & 1; + ts->frame_thread[p].cf = + (void*)(((uintptr_t)ts->frame_thread[p].cf + 63) & ~63); + } +#endif + } else { + if (decode_sb(t, bl + 1, INTRA_EDGE_SPLIT(node, 0))) + return 1; + t->bx += hsz; + if (decode_sb(t, bl + 1, INTRA_EDGE_SPLIT(node, 1))) + return 1; + t->bx -= hsz; + t->by += hsz; + if (decode_sb(t, bl + 1, INTRA_EDGE_SPLIT(node, 2))) + return 1; + t->bx += hsz; + if (decode_sb(t, bl + 1, INTRA_EDGE_SPLIT(node, 3))) + return 1; + t->bx -= hsz; + t->by -= hsz; + } + break; + case PARTITION_T_TOP_SPLIT: { + if (decode_b(t, bl, b[0], PARTITION_T_TOP_SPLIT, EDGE_ALL_TR_AND_BL)) + return -1; + t->bx += hsz; + if (decode_b(t, bl, b[0], PARTITION_T_TOP_SPLIT, node->v[1])) + return -1; + t->bx -= hsz; + t->by += hsz; + if (decode_b(t, bl, b[1], PARTITION_T_TOP_SPLIT, node->h[1])) + return -1; + t->by -= hsz; + break; + } + case PARTITION_T_BOTTOM_SPLIT: { + if (decode_b(t, bl, b[0], PARTITION_T_BOTTOM_SPLIT, node->h[0])) + return -1; + t->by += hsz; + if (decode_b(t, bl, b[1], PARTITION_T_BOTTOM_SPLIT, node->v[0])) + return -1; + t->bx += hsz; + if (decode_b(t, bl, b[1], PARTITION_T_BOTTOM_SPLIT, 0)) + return -1; + t->bx -= hsz; + t->by -= hsz; + break; + } + case PARTITION_T_LEFT_SPLIT: { + if (decode_b(t, bl, b[0], PARTITION_T_LEFT_SPLIT, EDGE_ALL_TR_AND_BL)) + return -1; + t->by += hsz; + if (decode_b(t, bl, b[0], PARTITION_T_LEFT_SPLIT, node->h[1])) + return -1; + t->by -= hsz; + t->bx += hsz; + if (decode_b(t, bl, b[1], PARTITION_T_LEFT_SPLIT, node->v[1])) + return -1; + t->bx -= hsz; + break; + } + case PARTITION_T_RIGHT_SPLIT: { + if (decode_b(t, bl, b[0], PARTITION_T_RIGHT_SPLIT, node->v[0])) + return -1; + t->bx += hsz; + if (decode_b(t, bl, b[1], PARTITION_T_RIGHT_SPLIT, node->h[0])) + return -1; + t->by += hsz; + if (decode_b(t, bl, b[1], PARTITION_T_RIGHT_SPLIT, 0)) + return -1; + t->by -= hsz; + t->bx -= hsz; + break; + } + case PARTITION_H4: { + const EdgeBranch *const branch = (const EdgeBranch *) node; + if (decode_b(t, bl, b[0], PARTITION_H4, node->h[0])) + return -1; + t->by += hsz >> 1; + if (decode_b(t, bl, b[0], PARTITION_H4, branch->h4)) + return -1; + t->by += hsz >> 1; + if (decode_b(t, bl, b[0], PARTITION_H4, EDGE_ALL_LEFT_HAS_BOTTOM)) + return -1; + t->by += hsz >> 1; + if (t->by < f->bh) + if (decode_b(t, bl, b[0], PARTITION_H4, node->h[1])) + return -1; + t->by -= hsz * 3 >> 1; + break; + } + case PARTITION_V4: { + const EdgeBranch *const branch = (const EdgeBranch *) node; + if (decode_b(t, bl, b[0], PARTITION_V4, node->v[0])) + return -1; + t->bx += hsz >> 1; + if (decode_b(t, bl, b[0], PARTITION_V4, branch->v4)) + return -1; + t->bx += hsz >> 1; + if (decode_b(t, bl, b[0], PARTITION_V4, EDGE_ALL_TOP_HAS_RIGHT)) + return -1; + t->bx += hsz >> 1; + if (t->bx < f->bw) + if (decode_b(t, bl, b[0], PARTITION_V4, node->v[1])) + return -1; + t->bx -= hsz * 3 >> 1; + break; + } + default: assert(0); + } + } else if (have_h_split) { + unsigned is_split; + if (t->frame_thread.pass == 2) { + const Av1Block *const b = &f->frame_thread.b[t->by * f->b4_stride + t->bx]; + is_split = b->bl != bl; + } else { + is_split = dav1d_msac_decode_bool(&ts->msac, + gather_top_partition_prob(pc, bl)); + if (DEBUG_BLOCK_INFO) + printf("poc=%d,y=%d,x=%d,bl=%d,ctx=%d,bp=%d: r=%d\n", + f->frame_hdr->frame_offset, t->by, t->bx, bl, ctx, + is_split ? PARTITION_SPLIT : PARTITION_H, ts->msac.rng); + } + + assert(bl < BL_8X8); + if (is_split) { + bp = PARTITION_SPLIT; + if (decode_sb(t, bl + 1, INTRA_EDGE_SPLIT(node, 0))) return 1; + t->bx += hsz; + if (decode_sb(t, bl + 1, INTRA_EDGE_SPLIT(node, 1))) return 1; + t->bx -= hsz; + } else { + bp = PARTITION_H; + if (decode_b(t, bl, dav1d_block_sizes[bl][PARTITION_H][0], + PARTITION_H, node->h[0])) + return -1; + } + } else { + assert(have_v_split); + unsigned is_split; + if (t->frame_thread.pass == 2) { + const Av1Block *const b = &f->frame_thread.b[t->by * f->b4_stride + t->bx]; + is_split = b->bl != bl; + } else { + is_split = dav1d_msac_decode_bool(&ts->msac, + gather_left_partition_prob(pc, bl)); + if (f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I422 && !is_split) + return 1; + if (DEBUG_BLOCK_INFO) + printf("poc=%d,y=%d,x=%d,bl=%d,ctx=%d,bp=%d: r=%d\n", + f->frame_hdr->frame_offset, t->by, t->bx, bl, ctx, + is_split ? PARTITION_SPLIT : PARTITION_V, ts->msac.rng); + } + + assert(bl < BL_8X8); + if (is_split) { + bp = PARTITION_SPLIT; + if (decode_sb(t, bl + 1, INTRA_EDGE_SPLIT(node, 0))) return 1; + t->by += hsz; + if (decode_sb(t, bl + 1, INTRA_EDGE_SPLIT(node, 2))) return 1; + t->by -= hsz; + } else { + bp = PARTITION_V; + if (decode_b(t, bl, dav1d_block_sizes[bl][PARTITION_V][0], + PARTITION_V, node->v[0])) + return -1; + } + } + + if (t->frame_thread.pass != 2 && (bp != PARTITION_SPLIT || bl == BL_8X8)) { +#define set_ctx(rep_macro) \ + rep_macro(t->a->partition, bx8, dav1d_al_part_ctx[0][bl][bp]); \ + rep_macro(t->l.partition, by8, dav1d_al_part_ctx[1][bl][bp]) + case_set_upto16(ulog2(hsz)); +#undef set_ctx + } + + return 0; +} + +static void reset_context(BlockContext *const ctx, const int keyframe, const int pass) { + memset(ctx->intra, keyframe, sizeof(ctx->intra)); + memset(ctx->uvmode, DC_PRED, sizeof(ctx->uvmode)); + if (keyframe) + memset(ctx->mode, DC_PRED, sizeof(ctx->mode)); + + if (pass == 2) return; + + memset(ctx->partition, 0, sizeof(ctx->partition)); + memset(ctx->skip, 0, sizeof(ctx->skip)); + memset(ctx->skip_mode, 0, sizeof(ctx->skip_mode)); + memset(ctx->tx_lpf_y, 2, sizeof(ctx->tx_lpf_y)); + memset(ctx->tx_lpf_uv, 1, sizeof(ctx->tx_lpf_uv)); + memset(ctx->tx_intra, -1, sizeof(ctx->tx_intra)); + memset(ctx->tx, TX_64X64, sizeof(ctx->tx)); + if (!keyframe) { + memset(ctx->ref, -1, sizeof(ctx->ref)); + memset(ctx->comp_type, 0, sizeof(ctx->comp_type)); + memset(ctx->mode, NEARESTMV, sizeof(ctx->mode)); + } + memset(ctx->lcoef, 0x40, sizeof(ctx->lcoef)); + memset(ctx->ccoef, 0x40, sizeof(ctx->ccoef)); + memset(ctx->filter, DAV1D_N_SWITCHABLE_FILTERS, sizeof(ctx->filter)); + memset(ctx->seg_pred, 0, sizeof(ctx->seg_pred)); + memset(ctx->pal_sz, 0, sizeof(ctx->pal_sz)); +} + +// { Y+U+V, Y+U } * 4 +static const uint8_t ss_size_mul[4][2] = { + [DAV1D_PIXEL_LAYOUT_I400] = { 4, 4 }, + [DAV1D_PIXEL_LAYOUT_I420] = { 6, 5 }, + [DAV1D_PIXEL_LAYOUT_I422] = { 8, 6 }, + [DAV1D_PIXEL_LAYOUT_I444] = { 12, 8 }, +}; + +static void setup_tile(Dav1dTileState *const ts, + const Dav1dFrameContext *const f, + const uint8_t *const data, const size_t sz, + const int tile_row, const int tile_col, + const unsigned tile_start_off) +{ + const int col_sb_start = f->frame_hdr->tiling.col_start_sb[tile_col]; + const int col_sb128_start = col_sb_start >> !f->seq_hdr->sb128; + const int col_sb_end = f->frame_hdr->tiling.col_start_sb[tile_col + 1]; + const int row_sb_start = f->frame_hdr->tiling.row_start_sb[tile_row]; + const int row_sb_end = f->frame_hdr->tiling.row_start_sb[tile_row + 1]; + const int sb_shift = f->sb_shift; + + const uint8_t *const size_mul = ss_size_mul[f->cur.p.layout]; + for (int p = 0; p < 2; p++) { + ts->frame_thread[p].pal_idx = f->frame_thread.pal_idx ? + &f->frame_thread.pal_idx[(size_t)tile_start_off * size_mul[1] / 8] : + NULL; + ts->frame_thread[p].cbi = f->frame_thread.cbi ? + &f->frame_thread.cbi[(size_t)tile_start_off * size_mul[0] / 64] : + NULL; + ts->frame_thread[p].cf = f->frame_thread.cf ? + (uint8_t*)f->frame_thread.cf + + (((size_t)tile_start_off * size_mul[0]) >> !f->seq_hdr->hbd) : + NULL; + } + + dav1d_cdf_thread_copy(&ts->cdf, &f->in_cdf); + ts->last_qidx = f->frame_hdr->quant.yac; + ts->last_delta_lf.u32 = 0; + + dav1d_msac_init(&ts->msac, data, sz, f->frame_hdr->disable_cdf_update); + + ts->tiling.row = tile_row; + ts->tiling.col = tile_col; + ts->tiling.col_start = col_sb_start << sb_shift; + ts->tiling.col_end = imin(col_sb_end << sb_shift, f->bw); + ts->tiling.row_start = row_sb_start << sb_shift; + ts->tiling.row_end = imin(row_sb_end << sb_shift, f->bh); + + // Reference Restoration Unit (used for exp coding) + int sb_idx, unit_idx; + if (f->frame_hdr->width[0] != f->frame_hdr->width[1]) { + // vertical components only + sb_idx = (ts->tiling.row_start >> 5) * f->sr_sb128w; + unit_idx = (ts->tiling.row_start & 16) >> 3; + } else { + sb_idx = (ts->tiling.row_start >> 5) * f->sb128w + col_sb128_start; + unit_idx = ((ts->tiling.row_start & 16) >> 3) + + ((ts->tiling.col_start & 16) >> 4); + } + for (int p = 0; p < 3; p++) { + if (!((f->lf.restore_planes >> p) & 1U)) + continue; + + if (f->frame_hdr->width[0] != f->frame_hdr->width[1]) { + const int ss_hor = p && f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int d = f->frame_hdr->super_res.width_scale_denominator; + const int unit_size_log2 = f->frame_hdr->restoration.unit_size[!!p]; + const int rnd = (8 << unit_size_log2) - 1, shift = unit_size_log2 + 3; + const int x = ((4 * ts->tiling.col_start * d >> ss_hor) + rnd) >> shift; + const int px_x = x << (unit_size_log2 + ss_hor); + const int u_idx = unit_idx + ((px_x & 64) >> 6); + const int sb128x = px_x >> 7; + if (sb128x >= f->sr_sb128w) continue; + ts->lr_ref[p] = &f->lf.lr_mask[sb_idx + sb128x].lr[p][u_idx]; + } else { + ts->lr_ref[p] = &f->lf.lr_mask[sb_idx].lr[p][unit_idx]; + } + + ts->lr_ref[p]->filter_v[0] = 3; + ts->lr_ref[p]->filter_v[1] = -7; + ts->lr_ref[p]->filter_v[2] = 15; + ts->lr_ref[p]->filter_h[0] = 3; + ts->lr_ref[p]->filter_h[1] = -7; + ts->lr_ref[p]->filter_h[2] = 15; + ts->lr_ref[p]->sgr_weights[0] = -32; + ts->lr_ref[p]->sgr_weights[1] = 31; + } + + if (f->c->n_tc > 1) { + for (int p = 0; p < 2; p++) + atomic_init(&ts->progress[p], row_sb_start); + } +} + +static void read_restoration_info(Dav1dTaskContext *const t, + Av1RestorationUnit *const lr, const int p, + const enum Dav1dRestorationType frame_type) +{ + const Dav1dFrameContext *const f = t->f; + Dav1dTileState *const ts = t->ts; + + if (frame_type == DAV1D_RESTORATION_SWITCHABLE) { + const int filter = dav1d_msac_decode_symbol_adapt4(&ts->msac, + ts->cdf.m.restore_switchable, 2); + lr->type = filter + !!filter; /* NONE/WIENER/SGRPROJ */ + } else { + const unsigned type = + dav1d_msac_decode_bool_adapt(&ts->msac, + frame_type == DAV1D_RESTORATION_WIENER ? + ts->cdf.m.restore_wiener : ts->cdf.m.restore_sgrproj); + lr->type = type ? frame_type : DAV1D_RESTORATION_NONE; + } + + if (lr->type == DAV1D_RESTORATION_WIENER) { + lr->filter_v[0] = p ? 0 : + dav1d_msac_decode_subexp(&ts->msac, + ts->lr_ref[p]->filter_v[0] + 5, 16, 1) - 5; + lr->filter_v[1] = + dav1d_msac_decode_subexp(&ts->msac, + ts->lr_ref[p]->filter_v[1] + 23, 32, 2) - 23; + lr->filter_v[2] = + dav1d_msac_decode_subexp(&ts->msac, + ts->lr_ref[p]->filter_v[2] + 17, 64, 3) - 17; + + lr->filter_h[0] = p ? 0 : + dav1d_msac_decode_subexp(&ts->msac, + ts->lr_ref[p]->filter_h[0] + 5, 16, 1) - 5; + lr->filter_h[1] = + dav1d_msac_decode_subexp(&ts->msac, + ts->lr_ref[p]->filter_h[1] + 23, 32, 2) - 23; + lr->filter_h[2] = + dav1d_msac_decode_subexp(&ts->msac, + ts->lr_ref[p]->filter_h[2] + 17, 64, 3) - 17; + memcpy(lr->sgr_weights, ts->lr_ref[p]->sgr_weights, sizeof(lr->sgr_weights)); + ts->lr_ref[p] = lr; + if (DEBUG_BLOCK_INFO) + printf("Post-lr_wiener[pl=%d,v[%d,%d,%d],h[%d,%d,%d]]: r=%d\n", + p, lr->filter_v[0], lr->filter_v[1], + lr->filter_v[2], lr->filter_h[0], + lr->filter_h[1], lr->filter_h[2], ts->msac.rng); + } else if (lr->type == DAV1D_RESTORATION_SGRPROJ) { + const unsigned idx = dav1d_msac_decode_bools(&ts->msac, 4); + const uint16_t *const sgr_params = dav1d_sgr_params[idx]; + lr->type += idx; + lr->sgr_weights[0] = sgr_params[0] ? dav1d_msac_decode_subexp(&ts->msac, + ts->lr_ref[p]->sgr_weights[0] + 96, 128, 4) - 96 : 0; + lr->sgr_weights[1] = sgr_params[1] ? dav1d_msac_decode_subexp(&ts->msac, + ts->lr_ref[p]->sgr_weights[1] + 32, 128, 4) - 32 : 95; + memcpy(lr->filter_v, ts->lr_ref[p]->filter_v, sizeof(lr->filter_v)); + memcpy(lr->filter_h, ts->lr_ref[p]->filter_h, sizeof(lr->filter_h)); + ts->lr_ref[p] = lr; + if (DEBUG_BLOCK_INFO) + printf("Post-lr_sgrproj[pl=%d,idx=%d,w[%d,%d]]: r=%d\n", + p, idx, lr->sgr_weights[0], + lr->sgr_weights[1], ts->msac.rng); + } +} + +// modeled after the equivalent function in aomdec:decodeframe.c +static int check_trailing_bits_after_symbol_coder(const MsacContext *const msac) { + // check marker bit (single 1), followed by zeroes + const int n_bits = -(msac->cnt + 14); + assert(n_bits <= 0); // this assumes we errored out when cnt <= -15 in caller + const int n_bytes = (n_bits + 7) >> 3; + const uint8_t *p = &msac->buf_pos[n_bytes]; + const int pattern = 128 >> ((n_bits - 1) & 7); + if ((p[-1] & (2 * pattern - 1)) != pattern) + return 1; + + // check remainder zero bytes + for (; p < msac->buf_end; p++) + if (*p) + return 1; + + return 0; +} + +int dav1d_decode_tile_sbrow(Dav1dTaskContext *const t) { + const Dav1dFrameContext *const f = t->f; + const enum BlockLevel root_bl = f->seq_hdr->sb128 ? BL_128X128 : BL_64X64; + Dav1dTileState *const ts = t->ts; + const Dav1dContext *const c = f->c; + const int sb_step = f->sb_step; + const int tile_row = ts->tiling.row, tile_col = ts->tiling.col; + const int col_sb_start = f->frame_hdr->tiling.col_start_sb[tile_col]; + const int col_sb128_start = col_sb_start >> !f->seq_hdr->sb128; + + if (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc) { + dav1d_refmvs_tile_sbrow_init(&t->rt, &f->rf, ts->tiling.col_start, + ts->tiling.col_end, ts->tiling.row_start, + ts->tiling.row_end, t->by >> f->sb_shift, + ts->tiling.row, t->frame_thread.pass); + } + + if (IS_INTER_OR_SWITCH(f->frame_hdr) && c->n_fc > 1) { + const int sby = (t->by - ts->tiling.row_start) >> f->sb_shift; + int (*const lowest_px)[2] = ts->lowest_pixel[sby]; + for (int n = 0; n < 7; n++) + for (int m = 0; m < 2; m++) + lowest_px[n][m] = INT_MIN; + } + + reset_context(&t->l, IS_KEY_OR_INTRA(f->frame_hdr), t->frame_thread.pass); + if (t->frame_thread.pass == 2) { + const int off_2pass = c->n_tc > 1 ? f->sb128w * f->frame_hdr->tiling.rows : 0; + for (t->bx = ts->tiling.col_start, + t->a = f->a + off_2pass + col_sb128_start + tile_row * f->sb128w; + t->bx < ts->tiling.col_end; t->bx += sb_step) + { + if (atomic_load_explicit(c->flush, memory_order_acquire)) + return 1; + if (decode_sb(t, root_bl, dav1d_intra_edge_tree[root_bl])) + return 1; + if (t->bx & 16 || f->seq_hdr->sb128) + t->a++; + } + f->bd_fn.backup_ipred_edge(t); + return 0; + } + + if (f->c->n_tc > 1 && f->frame_hdr->use_ref_frame_mvs) { + f->c->refmvs_dsp.load_tmvs(&f->rf, ts->tiling.row, + ts->tiling.col_start >> 1, ts->tiling.col_end >> 1, + t->by >> 1, (t->by + sb_step) >> 1); + } + memset(t->pal_sz_uv[1], 0, sizeof(*t->pal_sz_uv)); + const int sb128y = t->by >> 5; + for (t->bx = ts->tiling.col_start, t->a = f->a + col_sb128_start + tile_row * f->sb128w, + t->lf_mask = f->lf.mask + sb128y * f->sb128w + col_sb128_start; + t->bx < ts->tiling.col_end; t->bx += sb_step) + { + if (atomic_load_explicit(c->flush, memory_order_acquire)) + return 1; + if (root_bl == BL_128X128) { + t->cur_sb_cdef_idx_ptr = t->lf_mask->cdef_idx; + t->cur_sb_cdef_idx_ptr[0] = -1; + t->cur_sb_cdef_idx_ptr[1] = -1; + t->cur_sb_cdef_idx_ptr[2] = -1; + t->cur_sb_cdef_idx_ptr[3] = -1; + } else { + t->cur_sb_cdef_idx_ptr = + &t->lf_mask->cdef_idx[((t->bx & 16) >> 4) + + ((t->by & 16) >> 3)]; + t->cur_sb_cdef_idx_ptr[0] = -1; + } + // Restoration filter + for (int p = 0; p < 3; p++) { + if (!((f->lf.restore_planes >> p) & 1U)) + continue; + + const int ss_ver = p && f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = p && f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int unit_size_log2 = f->frame_hdr->restoration.unit_size[!!p]; + const int y = t->by * 4 >> ss_ver; + const int h = (f->cur.p.h + ss_ver) >> ss_ver; + + const int unit_size = 1 << unit_size_log2; + const unsigned mask = unit_size - 1; + if (y & mask) continue; + const int half_unit = unit_size >> 1; + // Round half up at frame boundaries, if there's more than one + // restoration unit + if (y && y + half_unit > h) continue; + + const enum Dav1dRestorationType frame_type = f->frame_hdr->restoration.type[p]; + + if (f->frame_hdr->width[0] != f->frame_hdr->width[1]) { + const int w = (f->sr_cur.p.p.w + ss_hor) >> ss_hor; + const int n_units = imax(1, (w + half_unit) >> unit_size_log2); + + const int d = f->frame_hdr->super_res.width_scale_denominator; + const int rnd = unit_size * 8 - 1, shift = unit_size_log2 + 3; + const int x0 = ((4 * t->bx * d >> ss_hor) + rnd) >> shift; + const int x1 = ((4 * (t->bx + sb_step) * d >> ss_hor) + rnd) >> shift; + + for (int x = x0; x < imin(x1, n_units); x++) { + const int px_x = x << (unit_size_log2 + ss_hor); + const int sb_idx = (t->by >> 5) * f->sr_sb128w + (px_x >> 7); + const int unit_idx = ((t->by & 16) >> 3) + ((px_x & 64) >> 6); + Av1RestorationUnit *const lr = &f->lf.lr_mask[sb_idx].lr[p][unit_idx]; + + read_restoration_info(t, lr, p, frame_type); + } + } else { + const int x = 4 * t->bx >> ss_hor; + if (x & mask) continue; + const int w = (f->cur.p.w + ss_hor) >> ss_hor; + // Round half up at frame boundaries, if there's more than one + // restoration unit + if (x && x + half_unit > w) continue; + const int sb_idx = (t->by >> 5) * f->sr_sb128w + (t->bx >> 5); + const int unit_idx = ((t->by & 16) >> 3) + ((t->bx & 16) >> 4); + Av1RestorationUnit *const lr = &f->lf.lr_mask[sb_idx].lr[p][unit_idx]; + + read_restoration_info(t, lr, p, frame_type); + } + } + if (decode_sb(t, root_bl, dav1d_intra_edge_tree[root_bl])) + return 1; + if (t->bx & 16 || f->seq_hdr->sb128) { + t->a++; + t->lf_mask++; + } + } + + if (f->seq_hdr->ref_frame_mvs && f->c->n_tc > 1 && IS_INTER_OR_SWITCH(f->frame_hdr)) { + dav1d_refmvs_save_tmvs(&f->c->refmvs_dsp, &t->rt, + ts->tiling.col_start >> 1, ts->tiling.col_end >> 1, + t->by >> 1, (t->by + sb_step) >> 1); + } + + // backup pre-loopfilter pixels for intra prediction of the next sbrow + if (t->frame_thread.pass != 1) + f->bd_fn.backup_ipred_edge(t); + + // backup t->a/l.tx_lpf_y/uv at tile boundaries to use them to "fix" + // up the initial value in neighbour tiles when running the loopfilter + int align_h = (f->bh + 31) & ~31; + memcpy(&f->lf.tx_lpf_right_edge[0][align_h * tile_col + t->by], + &t->l.tx_lpf_y[t->by & 16], sb_step); + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + align_h >>= ss_ver; + memcpy(&f->lf.tx_lpf_right_edge[1][align_h * tile_col + (t->by >> ss_ver)], + &t->l.tx_lpf_uv[(t->by & 16) >> ss_ver], sb_step >> ss_ver); + + // error out on symbol decoder overread + if (ts->msac.cnt <= -15) return 1; + + return c->strict_std_compliance && + (t->by >> f->sb_shift) + 1 >= f->frame_hdr->tiling.row_start_sb[tile_row + 1] && + check_trailing_bits_after_symbol_coder(&ts->msac); +} + +int dav1d_decode_frame_init(Dav1dFrameContext *const f) { + const Dav1dContext *const c = f->c; + int retval = DAV1D_ERR(ENOMEM); + + if (f->sbh > f->lf.start_of_tile_row_sz) { + dav1d_free(f->lf.start_of_tile_row); + f->lf.start_of_tile_row = dav1d_malloc(ALLOC_TILE, f->sbh * sizeof(uint8_t)); + if (!f->lf.start_of_tile_row) { + f->lf.start_of_tile_row_sz = 0; + goto error; + } + f->lf.start_of_tile_row_sz = f->sbh; + } + int sby = 0; + for (int tile_row = 0; tile_row < f->frame_hdr->tiling.rows; tile_row++) { + f->lf.start_of_tile_row[sby++] = tile_row; + while (sby < f->frame_hdr->tiling.row_start_sb[tile_row + 1]) + f->lf.start_of_tile_row[sby++] = 0; + } + + const int n_ts = f->frame_hdr->tiling.cols * f->frame_hdr->tiling.rows; + if (n_ts != f->n_ts) { + if (c->n_fc > 1) { + dav1d_free(f->frame_thread.tile_start_off); + f->frame_thread.tile_start_off = + dav1d_malloc(ALLOC_TILE, sizeof(*f->frame_thread.tile_start_off) * n_ts); + if (!f->frame_thread.tile_start_off) { + f->n_ts = 0; + goto error; + } + } + dav1d_free_aligned(f->ts); + f->ts = dav1d_alloc_aligned(ALLOC_TILE, sizeof(*f->ts) * n_ts, 32); + if (!f->ts) goto error; + f->n_ts = n_ts; + } + + const int a_sz = f->sb128w * f->frame_hdr->tiling.rows * (1 + (c->n_fc > 1 && c->n_tc > 1)); + if (a_sz != f->a_sz) { + dav1d_free(f->a); + f->a = dav1d_malloc(ALLOC_TILE, sizeof(*f->a) * a_sz); + if (!f->a) { + f->a_sz = 0; + goto error; + } + f->a_sz = a_sz; + } + + const int num_sb128 = f->sb128w * f->sb128h; + const uint8_t *const size_mul = ss_size_mul[f->cur.p.layout]; + const int hbd = !!f->seq_hdr->hbd; + if (c->n_fc > 1) { + const unsigned sb_step4 = f->sb_step * 4; + int tile_idx = 0; + for (int tile_row = 0; tile_row < f->frame_hdr->tiling.rows; tile_row++) { + const unsigned row_off = f->frame_hdr->tiling.row_start_sb[tile_row] * + sb_step4 * f->sb128w * 128; + const unsigned b_diff = (f->frame_hdr->tiling.row_start_sb[tile_row + 1] - + f->frame_hdr->tiling.row_start_sb[tile_row]) * sb_step4; + for (int tile_col = 0; tile_col < f->frame_hdr->tiling.cols; tile_col++) { + f->frame_thread.tile_start_off[tile_idx++] = row_off + b_diff * + f->frame_hdr->tiling.col_start_sb[tile_col] * sb_step4; + } + } + + const int lowest_pixel_mem_sz = f->frame_hdr->tiling.cols * f->sbh; + if (lowest_pixel_mem_sz != f->tile_thread.lowest_pixel_mem_sz) { + dav1d_free(f->tile_thread.lowest_pixel_mem); + f->tile_thread.lowest_pixel_mem = + dav1d_malloc(ALLOC_TILE, lowest_pixel_mem_sz * + sizeof(*f->tile_thread.lowest_pixel_mem)); + if (!f->tile_thread.lowest_pixel_mem) { + f->tile_thread.lowest_pixel_mem_sz = 0; + goto error; + } + f->tile_thread.lowest_pixel_mem_sz = lowest_pixel_mem_sz; + } + int (*lowest_pixel_ptr)[7][2] = f->tile_thread.lowest_pixel_mem; + for (int tile_row = 0, tile_row_base = 0; tile_row < f->frame_hdr->tiling.rows; + tile_row++, tile_row_base += f->frame_hdr->tiling.cols) + { + const int tile_row_sb_h = f->frame_hdr->tiling.row_start_sb[tile_row + 1] - + f->frame_hdr->tiling.row_start_sb[tile_row]; + for (int tile_col = 0; tile_col < f->frame_hdr->tiling.cols; tile_col++) { + f->ts[tile_row_base + tile_col].lowest_pixel = lowest_pixel_ptr; + lowest_pixel_ptr += tile_row_sb_h; + } + } + + const int cbi_sz = num_sb128 * size_mul[0]; + if (cbi_sz != f->frame_thread.cbi_sz) { + dav1d_free_aligned(f->frame_thread.cbi); + f->frame_thread.cbi = + dav1d_alloc_aligned(ALLOC_BLOCK, sizeof(*f->frame_thread.cbi) * + cbi_sz * 32 * 32 / 4, 64); + if (!f->frame_thread.cbi) { + f->frame_thread.cbi_sz = 0; + goto error; + } + f->frame_thread.cbi_sz = cbi_sz; + } + + const int cf_sz = (num_sb128 * size_mul[0]) << hbd; + if (cf_sz != f->frame_thread.cf_sz) { + dav1d_free_aligned(f->frame_thread.cf); + f->frame_thread.cf = + dav1d_alloc_aligned(ALLOC_COEF, (size_t)cf_sz * 128 * 128 / 2, 64); + if (!f->frame_thread.cf) { + f->frame_thread.cf_sz = 0; + goto error; + } + memset(f->frame_thread.cf, 0, (size_t)cf_sz * 128 * 128 / 2); + f->frame_thread.cf_sz = cf_sz; + } + + if (f->frame_hdr->allow_screen_content_tools) { + const int pal_sz = num_sb128 << hbd; + if (pal_sz != f->frame_thread.pal_sz) { + dav1d_free_aligned(f->frame_thread.pal); + f->frame_thread.pal = + dav1d_alloc_aligned(ALLOC_PAL, sizeof(*f->frame_thread.pal) * + pal_sz * 16 * 16, 64); + if (!f->frame_thread.pal) { + f->frame_thread.pal_sz = 0; + goto error; + } + f->frame_thread.pal_sz = pal_sz; + } + + const int pal_idx_sz = num_sb128 * size_mul[1]; + if (pal_idx_sz != f->frame_thread.pal_idx_sz) { + dav1d_free_aligned(f->frame_thread.pal_idx); + f->frame_thread.pal_idx = + dav1d_alloc_aligned(ALLOC_PAL, sizeof(*f->frame_thread.pal_idx) * + pal_idx_sz * 128 * 128 / 8, 64); + if (!f->frame_thread.pal_idx) { + f->frame_thread.pal_idx_sz = 0; + goto error; + } + f->frame_thread.pal_idx_sz = pal_idx_sz; + } + } else if (f->frame_thread.pal) { + dav1d_freep_aligned(&f->frame_thread.pal); + dav1d_freep_aligned(&f->frame_thread.pal_idx); + f->frame_thread.pal_sz = f->frame_thread.pal_idx_sz = 0; + } + } + + // update allocation of block contexts for above + ptrdiff_t y_stride = f->cur.stride[0], uv_stride = f->cur.stride[1]; + const int has_resize = f->frame_hdr->width[0] != f->frame_hdr->width[1]; + const int need_cdef_lpf_copy = c->n_tc > 1 && has_resize; + if (y_stride * f->sbh * 4 != f->lf.cdef_buf_plane_sz[0] || + uv_stride * f->sbh * 8 != f->lf.cdef_buf_plane_sz[1] || + need_cdef_lpf_copy != f->lf.need_cdef_lpf_copy || + f->sbh != f->lf.cdef_buf_sbh) + { + dav1d_free_aligned(f->lf.cdef_line_buf); + size_t alloc_sz = 64; + alloc_sz += (size_t)llabs(y_stride) * 4 * f->sbh << need_cdef_lpf_copy; + alloc_sz += (size_t)llabs(uv_stride) * 8 * f->sbh << need_cdef_lpf_copy; + uint8_t *ptr = f->lf.cdef_line_buf = dav1d_alloc_aligned(ALLOC_CDEF, alloc_sz, 32); + if (!ptr) { + f->lf.cdef_buf_plane_sz[0] = f->lf.cdef_buf_plane_sz[1] = 0; + goto error; + } + + ptr += 32; + if (y_stride < 0) { + f->lf.cdef_line[0][0] = ptr - y_stride * (f->sbh * 4 - 1); + f->lf.cdef_line[1][0] = ptr - y_stride * (f->sbh * 4 - 3); + } else { + f->lf.cdef_line[0][0] = ptr + y_stride * 0; + f->lf.cdef_line[1][0] = ptr + y_stride * 2; + } + ptr += llabs(y_stride) * f->sbh * 4; + if (uv_stride < 0) { + f->lf.cdef_line[0][1] = ptr - uv_stride * (f->sbh * 8 - 1); + f->lf.cdef_line[0][2] = ptr - uv_stride * (f->sbh * 8 - 3); + f->lf.cdef_line[1][1] = ptr - uv_stride * (f->sbh * 8 - 5); + f->lf.cdef_line[1][2] = ptr - uv_stride * (f->sbh * 8 - 7); + } else { + f->lf.cdef_line[0][1] = ptr + uv_stride * 0; + f->lf.cdef_line[0][2] = ptr + uv_stride * 2; + f->lf.cdef_line[1][1] = ptr + uv_stride * 4; + f->lf.cdef_line[1][2] = ptr + uv_stride * 6; + } + + if (need_cdef_lpf_copy) { + ptr += llabs(uv_stride) * f->sbh * 8; + if (y_stride < 0) + f->lf.cdef_lpf_line[0] = ptr - y_stride * (f->sbh * 4 - 1); + else + f->lf.cdef_lpf_line[0] = ptr; + ptr += llabs(y_stride) * f->sbh * 4; + if (uv_stride < 0) { + f->lf.cdef_lpf_line[1] = ptr - uv_stride * (f->sbh * 4 - 1); + f->lf.cdef_lpf_line[2] = ptr - uv_stride * (f->sbh * 8 - 1); + } else { + f->lf.cdef_lpf_line[1] = ptr; + f->lf.cdef_lpf_line[2] = ptr + uv_stride * f->sbh * 4; + } + } + + f->lf.cdef_buf_plane_sz[0] = (int) y_stride * f->sbh * 4; + f->lf.cdef_buf_plane_sz[1] = (int) uv_stride * f->sbh * 8; + f->lf.need_cdef_lpf_copy = need_cdef_lpf_copy; + f->lf.cdef_buf_sbh = f->sbh; + } + + const int sb128 = f->seq_hdr->sb128; + const int num_lines = c->n_tc > 1 ? f->sbh * 4 << sb128 : 12; + y_stride = f->sr_cur.p.stride[0], uv_stride = f->sr_cur.p.stride[1]; + if (y_stride * num_lines != f->lf.lr_buf_plane_sz[0] || + uv_stride * num_lines * 2 != f->lf.lr_buf_plane_sz[1]) + { + dav1d_free_aligned(f->lf.lr_line_buf); + // lr simd may overread the input, so slightly over-allocate the lpf buffer + size_t alloc_sz = 128; + alloc_sz += (size_t)llabs(y_stride) * num_lines; + alloc_sz += (size_t)llabs(uv_stride) * num_lines * 2; + uint8_t *ptr = f->lf.lr_line_buf = dav1d_alloc_aligned(ALLOC_LR, alloc_sz, 64); + if (!ptr) { + f->lf.lr_buf_plane_sz[0] = f->lf.lr_buf_plane_sz[1] = 0; + goto error; + } + + ptr += 64; + if (y_stride < 0) + f->lf.lr_lpf_line[0] = ptr - y_stride * (num_lines - 1); + else + f->lf.lr_lpf_line[0] = ptr; + ptr += llabs(y_stride) * num_lines; + if (uv_stride < 0) { + f->lf.lr_lpf_line[1] = ptr - uv_stride * (num_lines * 1 - 1); + f->lf.lr_lpf_line[2] = ptr - uv_stride * (num_lines * 2 - 1); + } else { + f->lf.lr_lpf_line[1] = ptr; + f->lf.lr_lpf_line[2] = ptr + uv_stride * num_lines; + } + + f->lf.lr_buf_plane_sz[0] = (int) y_stride * num_lines; + f->lf.lr_buf_plane_sz[1] = (int) uv_stride * num_lines * 2; + } + + // update allocation for loopfilter masks + if (num_sb128 != f->lf.mask_sz) { + dav1d_free(f->lf.mask); + dav1d_free(f->lf.level); + f->lf.mask = dav1d_malloc(ALLOC_LF, sizeof(*f->lf.mask) * num_sb128); + // over-allocate by 3 bytes since some of the SIMD implementations + // index this from the level type and can thus over-read by up to 3 + f->lf.level = dav1d_malloc(ALLOC_LF, sizeof(*f->lf.level) * num_sb128 * 32 * 32 + 3); + if (!f->lf.mask || !f->lf.level) { + f->lf.mask_sz = 0; + goto error; + } + if (c->n_fc > 1) { + dav1d_free(f->frame_thread.b); + f->frame_thread.b = dav1d_malloc(ALLOC_BLOCK, sizeof(*f->frame_thread.b) * + num_sb128 * 32 * 32); + if (!f->frame_thread.b) { + f->lf.mask_sz = 0; + goto error; + } + } + f->lf.mask_sz = num_sb128; + } + + f->sr_sb128w = (f->sr_cur.p.p.w + 127) >> 7; + const int lr_mask_sz = f->sr_sb128w * f->sb128h; + if (lr_mask_sz != f->lf.lr_mask_sz) { + dav1d_free(f->lf.lr_mask); + f->lf.lr_mask = dav1d_malloc(ALLOC_LR, sizeof(*f->lf.lr_mask) * lr_mask_sz); + if (!f->lf.lr_mask) { + f->lf.lr_mask_sz = 0; + goto error; + } + f->lf.lr_mask_sz = lr_mask_sz; + } + f->lf.restore_planes = + ((f->frame_hdr->restoration.type[0] != DAV1D_RESTORATION_NONE) << 0) + + ((f->frame_hdr->restoration.type[1] != DAV1D_RESTORATION_NONE) << 1) + + ((f->frame_hdr->restoration.type[2] != DAV1D_RESTORATION_NONE) << 2); + if (f->frame_hdr->loopfilter.sharpness != f->lf.last_sharpness) { + dav1d_calc_eih(&f->lf.lim_lut, f->frame_hdr->loopfilter.sharpness); + f->lf.last_sharpness = f->frame_hdr->loopfilter.sharpness; + } + dav1d_calc_lf_values(f->lf.lvl, f->frame_hdr, (int8_t[4]) { 0, 0, 0, 0 }); + memset(f->lf.mask, 0, sizeof(*f->lf.mask) * num_sb128); + + const int ipred_edge_sz = f->sbh * f->sb128w << hbd; + if (ipred_edge_sz != f->ipred_edge_sz) { + dav1d_free_aligned(f->ipred_edge[0]); + uint8_t *ptr = f->ipred_edge[0] = + dav1d_alloc_aligned(ALLOC_IPRED, ipred_edge_sz * 128 * 3, 64); + if (!ptr) { + f->ipred_edge_sz = 0; + goto error; + } + f->ipred_edge[1] = ptr + ipred_edge_sz * 128 * 1; + f->ipred_edge[2] = ptr + ipred_edge_sz * 128 * 2; + f->ipred_edge_sz = ipred_edge_sz; + } + + const int re_sz = f->sb128h * f->frame_hdr->tiling.cols; + if (re_sz != f->lf.re_sz) { + dav1d_free(f->lf.tx_lpf_right_edge[0]); + f->lf.tx_lpf_right_edge[0] = dav1d_malloc(ALLOC_LF, re_sz * 32 * 2); + if (!f->lf.tx_lpf_right_edge[0]) { + f->lf.re_sz = 0; + goto error; + } + f->lf.tx_lpf_right_edge[1] = f->lf.tx_lpf_right_edge[0] + re_sz * 32; + f->lf.re_sz = re_sz; + } + + // init ref mvs + if (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc) { + const int ret = + dav1d_refmvs_init_frame(&f->rf, f->seq_hdr, f->frame_hdr, + f->refpoc, f->mvs, f->refrefpoc, f->ref_mvs, + f->c->n_tc, f->c->n_fc); + if (ret < 0) goto error; + } + + // setup dequant tables + init_quant_tables(f->seq_hdr, f->frame_hdr, f->frame_hdr->quant.yac, f->dq); + if (f->frame_hdr->quant.qm) + for (int i = 0; i < N_RECT_TX_SIZES; i++) { + f->qm[i][0] = dav1d_qm_tbl[f->frame_hdr->quant.qm_y][0][i]; + f->qm[i][1] = dav1d_qm_tbl[f->frame_hdr->quant.qm_u][1][i]; + f->qm[i][2] = dav1d_qm_tbl[f->frame_hdr->quant.qm_v][1][i]; + } + else + memset(f->qm, 0, sizeof(f->qm)); + + // setup jnt_comp weights + if (f->frame_hdr->switchable_comp_refs) { + for (int i = 0; i < 7; i++) { + const unsigned ref0poc = f->refp[i].p.frame_hdr->frame_offset; + + for (int j = i + 1; j < 7; j++) { + const unsigned ref1poc = f->refp[j].p.frame_hdr->frame_offset; + + const unsigned d1 = + imin(abs(get_poc_diff(f->seq_hdr->order_hint_n_bits, ref0poc, + f->cur.frame_hdr->frame_offset)), 31); + const unsigned d0 = + imin(abs(get_poc_diff(f->seq_hdr->order_hint_n_bits, ref1poc, + f->cur.frame_hdr->frame_offset)), 31); + const int order = d0 <= d1; + + static const uint8_t quant_dist_weight[3][2] = { + { 2, 3 }, { 2, 5 }, { 2, 7 } + }; + static const uint8_t quant_dist_lookup_table[4][2] = { + { 9, 7 }, { 11, 5 }, { 12, 4 }, { 13, 3 } + }; + + int k; + for (k = 0; k < 3; k++) { + const int c0 = quant_dist_weight[k][order]; + const int c1 = quant_dist_weight[k][!order]; + const int d0_c0 = d0 * c0; + const int d1_c1 = d1 * c1; + if ((d0 > d1 && d0_c0 < d1_c1) || (d0 <= d1 && d0_c0 > d1_c1)) break; + } + + f->jnt_weights[i][j] = quant_dist_lookup_table[k][order]; + } + } + } + + /* Init loopfilter pointers. Increasing NULL pointers is technically UB, + * so just point the chroma pointers in 4:0:0 to the luma plane here to + * avoid having additional in-loop branches in various places. We never + * dereference those pointers so it doesn't really matter what they + * point at, as long as the pointers are valid. */ + const int has_chroma = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400; + f->lf.p[0] = f->cur.data[0]; + f->lf.p[1] = f->cur.data[has_chroma ? 1 : 0]; + f->lf.p[2] = f->cur.data[has_chroma ? 2 : 0]; + f->lf.sr_p[0] = f->sr_cur.p.data[0]; + f->lf.sr_p[1] = f->sr_cur.p.data[has_chroma ? 1 : 0]; + f->lf.sr_p[2] = f->sr_cur.p.data[has_chroma ? 2 : 0]; + + retval = 0; +error: + return retval; +} + +int dav1d_decode_frame_init_cdf(Dav1dFrameContext *const f) { + const Dav1dContext *const c = f->c; + int retval = DAV1D_ERR(EINVAL); + + if (f->frame_hdr->refresh_context) + dav1d_cdf_thread_copy(f->out_cdf.data.cdf, &f->in_cdf); + + // parse individual tiles per tile group + int tile_row = 0, tile_col = 0; + f->task_thread.update_set = 0; + for (int i = 0; i < f->n_tile_data; i++) { + const uint8_t *data = f->tile[i].data.data; + size_t size = f->tile[i].data.sz; + + for (int j = f->tile[i].start; j <= f->tile[i].end; j++) { + size_t tile_sz; + if (j == f->tile[i].end) { + tile_sz = size; + } else { + if (f->frame_hdr->tiling.n_bytes > size) goto error; + tile_sz = 0; + for (unsigned k = 0; k < f->frame_hdr->tiling.n_bytes; k++) + tile_sz |= (unsigned)*data++ << (k * 8); + tile_sz++; + size -= f->frame_hdr->tiling.n_bytes; + if (tile_sz > size) goto error; + } + + setup_tile(&f->ts[j], f, data, tile_sz, tile_row, tile_col++, + c->n_fc > 1 ? f->frame_thread.tile_start_off[j] : 0); + + if (tile_col == f->frame_hdr->tiling.cols) { + tile_col = 0; + tile_row++; + } + if (j == f->frame_hdr->tiling.update && f->frame_hdr->refresh_context) + f->task_thread.update_set = 1; + data += tile_sz; + size -= tile_sz; + } + } + + if (c->n_tc > 1) { + const int uses_2pass = c->n_fc > 1; + for (int n = 0; n < f->sb128w * f->frame_hdr->tiling.rows * (1 + uses_2pass); n++) + reset_context(&f->a[n], IS_KEY_OR_INTRA(f->frame_hdr), + uses_2pass ? 1 + (n >= f->sb128w * f->frame_hdr->tiling.rows) : 0); + } + + retval = 0; +error: + return retval; +} + +int dav1d_decode_frame_main(Dav1dFrameContext *const f) { + const Dav1dContext *const c = f->c; + int retval = DAV1D_ERR(EINVAL); + + assert(f->c->n_tc == 1); + + Dav1dTaskContext *const t = &c->tc[f - c->fc]; + t->f = f; + t->frame_thread.pass = 0; + + for (int n = 0; n < f->sb128w * f->frame_hdr->tiling.rows; n++) + reset_context(&f->a[n], IS_KEY_OR_INTRA(f->frame_hdr), 0); + + // no threading - we explicitly interleave tile/sbrow decoding + // and post-filtering, so that the full process runs in-line + for (int tile_row = 0; tile_row < f->frame_hdr->tiling.rows; tile_row++) { + const int sbh_end = + imin(f->frame_hdr->tiling.row_start_sb[tile_row + 1], f->sbh); + for (int sby = f->frame_hdr->tiling.row_start_sb[tile_row]; + sby < sbh_end; sby++) + { + t->by = sby << (4 + f->seq_hdr->sb128); + const int by_end = (t->by + f->sb_step) >> 1; + if (f->frame_hdr->use_ref_frame_mvs) { + f->c->refmvs_dsp.load_tmvs(&f->rf, tile_row, + 0, f->bw >> 1, t->by >> 1, by_end); + } + for (int tile_col = 0; tile_col < f->frame_hdr->tiling.cols; tile_col++) { + t->ts = &f->ts[tile_row * f->frame_hdr->tiling.cols + tile_col]; + if (dav1d_decode_tile_sbrow(t)) goto error; + } + if (IS_INTER_OR_SWITCH(f->frame_hdr)) { + dav1d_refmvs_save_tmvs(&f->c->refmvs_dsp, &t->rt, + 0, f->bw >> 1, t->by >> 1, by_end); + } + + // loopfilter + cdef + restoration + f->bd_fn.filter_sbrow(f, sby); + } + } + + retval = 0; +error: + return retval; +} + +void dav1d_decode_frame_exit(Dav1dFrameContext *const f, int retval) { + const Dav1dContext *const c = f->c; + + if (f->sr_cur.p.data[0]) + atomic_init(&f->task_thread.error, 0); + + if (c->n_fc > 1 && retval && f->frame_thread.cf) { + memset(f->frame_thread.cf, 0, + (size_t)f->frame_thread.cf_sz * 128 * 128 / 2); + } + for (int i = 0; i < 7; i++) { + if (f->refp[i].p.frame_hdr) { + if (!retval && c->n_fc > 1 && c->strict_std_compliance && + atomic_load(&f->refp[i].progress[1]) == FRAME_ERROR) + { + retval = DAV1D_ERR(EINVAL); + atomic_store(&f->task_thread.error, 1); + atomic_store(&f->sr_cur.progress[1], FRAME_ERROR); + } + dav1d_thread_picture_unref(&f->refp[i]); + } + dav1d_ref_dec(&f->ref_mvs_ref[i]); + } + + dav1d_picture_unref_internal(&f->cur); + dav1d_thread_picture_unref(&f->sr_cur); + dav1d_cdf_thread_unref(&f->in_cdf); + if (f->frame_hdr && f->frame_hdr->refresh_context) { + if (f->out_cdf.progress) + atomic_store(f->out_cdf.progress, retval == 0 ? 1 : TILE_ERROR); + dav1d_cdf_thread_unref(&f->out_cdf); + } + dav1d_ref_dec(&f->cur_segmap_ref); + dav1d_ref_dec(&f->prev_segmap_ref); + dav1d_ref_dec(&f->mvs_ref); + dav1d_ref_dec(&f->seq_hdr_ref); + dav1d_ref_dec(&f->frame_hdr_ref); + + for (int i = 0; i < f->n_tile_data; i++) + dav1d_data_unref_internal(&f->tile[i].data); + f->task_thread.retval = retval; +} + +int dav1d_decode_frame(Dav1dFrameContext *const f) { + assert(f->c->n_fc == 1); + // if n_tc > 1 (but n_fc == 1), we could run init/exit in the task + // threads also. Not sure it makes a measurable difference. + int res = dav1d_decode_frame_init(f); + if (!res) res = dav1d_decode_frame_init_cdf(f); + // wait until all threads have completed + if (!res) { + if (f->c->n_tc > 1) { + res = dav1d_task_create_tile_sbrow(f, 0, 1); + pthread_mutex_lock(&f->task_thread.ttd->lock); + pthread_cond_signal(&f->task_thread.ttd->cond); + if (!res) { + while (!f->task_thread.done[0] || + atomic_load(&f->task_thread.task_counter) > 0) + { + pthread_cond_wait(&f->task_thread.cond, + &f->task_thread.ttd->lock); + } + } + pthread_mutex_unlock(&f->task_thread.ttd->lock); + res = f->task_thread.retval; + } else { + res = dav1d_decode_frame_main(f); + if (!res && f->frame_hdr->refresh_context && f->task_thread.update_set) { + dav1d_cdf_thread_update(f->frame_hdr, f->out_cdf.data.cdf, + &f->ts[f->frame_hdr->tiling.update].cdf); + } + } + } + dav1d_decode_frame_exit(f, res); + res = f->task_thread.retval; + f->n_tile_data = 0; + return res; +} + +static int get_upscale_x0(const int in_w, const int out_w, const int step) { + const int err = out_w * step - (in_w << 14); + const int x0 = (-((out_w - in_w) << 13) + (out_w >> 1)) / out_w + 128 - (err / 2); + return x0 & 0x3fff; +} + +int dav1d_submit_frame(Dav1dContext *const c) { + Dav1dFrameContext *f; + int res = -1; + + // wait for c->out_delayed[next] and move into c->out if visible + Dav1dThreadPicture *out_delayed; + if (c->n_fc > 1) { + pthread_mutex_lock(&c->task_thread.lock); + const unsigned next = c->frame_thread.next++; + if (c->frame_thread.next == c->n_fc) + c->frame_thread.next = 0; + + f = &c->fc[next]; + while (f->n_tile_data > 0) + pthread_cond_wait(&f->task_thread.cond, + &c->task_thread.lock); + out_delayed = &c->frame_thread.out_delayed[next]; + if (out_delayed->p.data[0] || atomic_load(&f->task_thread.error)) { + unsigned first = atomic_load(&c->task_thread.first); + if (first + 1U < c->n_fc) + atomic_fetch_add(&c->task_thread.first, 1U); + else + atomic_store(&c->task_thread.first, 0); + atomic_compare_exchange_strong(&c->task_thread.reset_task_cur, + &first, UINT_MAX); + if (c->task_thread.cur && c->task_thread.cur < c->n_fc) + c->task_thread.cur--; + } + const int error = f->task_thread.retval; + if (error) { + f->task_thread.retval = 0; + c->cached_error = error; + dav1d_data_props_copy(&c->cached_error_props, &out_delayed->p.m); + dav1d_thread_picture_unref(out_delayed); + } else if (out_delayed->p.data[0]) { + const unsigned progress = atomic_load_explicit(&out_delayed->progress[1], + memory_order_relaxed); + if ((out_delayed->visible || c->output_invisible_frames) && + progress != FRAME_ERROR) + { + dav1d_thread_picture_ref(&c->out, out_delayed); + c->event_flags |= dav1d_picture_get_event_flags(out_delayed); + } + dav1d_thread_picture_unref(out_delayed); + } + } else { + f = c->fc; + } + + f->seq_hdr = c->seq_hdr; + f->seq_hdr_ref = c->seq_hdr_ref; + dav1d_ref_inc(f->seq_hdr_ref); + f->frame_hdr = c->frame_hdr; + f->frame_hdr_ref = c->frame_hdr_ref; + c->frame_hdr = NULL; + c->frame_hdr_ref = NULL; + f->dsp = &c->dsp[f->seq_hdr->hbd]; + + const int bpc = 8 + 2 * f->seq_hdr->hbd; + + if (!f->dsp->ipred.intra_pred[DC_PRED]) { + Dav1dDSPContext *const dsp = &c->dsp[f->seq_hdr->hbd]; + + switch (bpc) { +#define assign_bitdepth_case(bd) \ + dav1d_cdef_dsp_init_##bd##bpc(&dsp->cdef); \ + dav1d_intra_pred_dsp_init_##bd##bpc(&dsp->ipred); \ + dav1d_itx_dsp_init_##bd##bpc(&dsp->itx, bpc); \ + dav1d_loop_filter_dsp_init_##bd##bpc(&dsp->lf); \ + dav1d_loop_restoration_dsp_init_##bd##bpc(&dsp->lr, bpc); \ + dav1d_mc_dsp_init_##bd##bpc(&dsp->mc); \ + dav1d_film_grain_dsp_init_##bd##bpc(&dsp->fg); \ + break +#if CONFIG_8BPC + case 8: + assign_bitdepth_case(8); +#endif +#if CONFIG_16BPC + case 10: + case 12: + assign_bitdepth_case(16); +#endif +#undef assign_bitdepth_case + default: + dav1d_log(c, "Compiled without support for %d-bit decoding\n", + 8 + 2 * f->seq_hdr->hbd); + res = DAV1D_ERR(ENOPROTOOPT); + goto error; + } + } + +#define assign_bitdepth_case(bd) \ + f->bd_fn.recon_b_inter = dav1d_recon_b_inter_##bd##bpc; \ + f->bd_fn.recon_b_intra = dav1d_recon_b_intra_##bd##bpc; \ + f->bd_fn.filter_sbrow = dav1d_filter_sbrow_##bd##bpc; \ + f->bd_fn.filter_sbrow_deblock_cols = dav1d_filter_sbrow_deblock_cols_##bd##bpc; \ + f->bd_fn.filter_sbrow_deblock_rows = dav1d_filter_sbrow_deblock_rows_##bd##bpc; \ + f->bd_fn.filter_sbrow_cdef = dav1d_filter_sbrow_cdef_##bd##bpc; \ + f->bd_fn.filter_sbrow_resize = dav1d_filter_sbrow_resize_##bd##bpc; \ + f->bd_fn.filter_sbrow_lr = dav1d_filter_sbrow_lr_##bd##bpc; \ + f->bd_fn.backup_ipred_edge = dav1d_backup_ipred_edge_##bd##bpc; \ + f->bd_fn.read_coef_blocks = dav1d_read_coef_blocks_##bd##bpc; \ + f->bd_fn.copy_pal_block_y = dav1d_copy_pal_block_y_##bd##bpc; \ + f->bd_fn.copy_pal_block_uv = dav1d_copy_pal_block_uv_##bd##bpc; \ + f->bd_fn.read_pal_plane = dav1d_read_pal_plane_##bd##bpc; \ + f->bd_fn.read_pal_uv = dav1d_read_pal_uv_##bd##bpc + if (!f->seq_hdr->hbd) { +#if CONFIG_8BPC + assign_bitdepth_case(8); +#endif + } else { +#if CONFIG_16BPC + assign_bitdepth_case(16); +#endif + } +#undef assign_bitdepth_case + + int ref_coded_width[7]; + if (IS_INTER_OR_SWITCH(f->frame_hdr)) { + if (f->frame_hdr->primary_ref_frame != DAV1D_PRIMARY_REF_NONE) { + const int pri_ref = f->frame_hdr->refidx[f->frame_hdr->primary_ref_frame]; + if (!c->refs[pri_ref].p.p.data[0]) { + res = DAV1D_ERR(EINVAL); + goto error; + } + } + for (int i = 0; i < 7; i++) { + const int refidx = f->frame_hdr->refidx[i]; + if (!c->refs[refidx].p.p.data[0] || + f->frame_hdr->width[0] * 2 < c->refs[refidx].p.p.p.w || + f->frame_hdr->height * 2 < c->refs[refidx].p.p.p.h || + f->frame_hdr->width[0] > c->refs[refidx].p.p.p.w * 16 || + f->frame_hdr->height > c->refs[refidx].p.p.p.h * 16 || + f->seq_hdr->layout != c->refs[refidx].p.p.p.layout || + bpc != c->refs[refidx].p.p.p.bpc) + { + for (int j = 0; j < i; j++) + dav1d_thread_picture_unref(&f->refp[j]); + res = DAV1D_ERR(EINVAL); + goto error; + } + dav1d_thread_picture_ref(&f->refp[i], &c->refs[refidx].p); + ref_coded_width[i] = c->refs[refidx].p.p.frame_hdr->width[0]; + if (f->frame_hdr->width[0] != c->refs[refidx].p.p.p.w || + f->frame_hdr->height != c->refs[refidx].p.p.p.h) + { +#define scale_fac(ref_sz, this_sz) \ + ((((ref_sz) << 14) + ((this_sz) >> 1)) / (this_sz)) + f->svc[i][0].scale = scale_fac(c->refs[refidx].p.p.p.w, + f->frame_hdr->width[0]); + f->svc[i][1].scale = scale_fac(c->refs[refidx].p.p.p.h, + f->frame_hdr->height); + f->svc[i][0].step = (f->svc[i][0].scale + 8) >> 4; + f->svc[i][1].step = (f->svc[i][1].scale + 8) >> 4; + } else { + f->svc[i][0].scale = f->svc[i][1].scale = 0; + } + f->gmv_warp_allowed[i] = f->frame_hdr->gmv[i].type > DAV1D_WM_TYPE_TRANSLATION && + !f->frame_hdr->force_integer_mv && + !dav1d_get_shear_params(&f->frame_hdr->gmv[i]) && + !f->svc[i][0].scale; + } + } + + // setup entropy + if (f->frame_hdr->primary_ref_frame == DAV1D_PRIMARY_REF_NONE) { + dav1d_cdf_thread_init_static(&f->in_cdf, f->frame_hdr->quant.yac); + } else { + const int pri_ref = f->frame_hdr->refidx[f->frame_hdr->primary_ref_frame]; + dav1d_cdf_thread_ref(&f->in_cdf, &c->cdf[pri_ref]); + } + if (f->frame_hdr->refresh_context) { + res = dav1d_cdf_thread_alloc(c, &f->out_cdf, c->n_fc > 1); + if (res < 0) goto error; + } + + // FIXME qsort so tiles are in order (for frame threading) + if (f->n_tile_data_alloc < c->n_tile_data) { + dav1d_free(f->tile); + assert(c->n_tile_data < INT_MAX / (int)sizeof(*f->tile)); + f->tile = dav1d_malloc(ALLOC_TILE, c->n_tile_data * sizeof(*f->tile)); + if (!f->tile) { + f->n_tile_data_alloc = f->n_tile_data = 0; + res = DAV1D_ERR(ENOMEM); + goto error; + } + f->n_tile_data_alloc = c->n_tile_data; + } + memcpy(f->tile, c->tile, c->n_tile_data * sizeof(*f->tile)); + memset(c->tile, 0, c->n_tile_data * sizeof(*c->tile)); + f->n_tile_data = c->n_tile_data; + c->n_tile_data = 0; + + // allocate frame + res = dav1d_thread_picture_alloc(c, f, bpc); + if (res < 0) goto error; + + if (f->frame_hdr->width[0] != f->frame_hdr->width[1]) { + res = dav1d_picture_alloc_copy(c, &f->cur, f->frame_hdr->width[0], &f->sr_cur.p); + if (res < 0) goto error; + } else { + dav1d_picture_ref(&f->cur, &f->sr_cur.p); + } + + if (f->frame_hdr->width[0] != f->frame_hdr->width[1]) { + f->resize_step[0] = scale_fac(f->cur.p.w, f->sr_cur.p.p.w); + const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int in_cw = (f->cur.p.w + ss_hor) >> ss_hor; + const int out_cw = (f->sr_cur.p.p.w + ss_hor) >> ss_hor; + f->resize_step[1] = scale_fac(in_cw, out_cw); +#undef scale_fac + f->resize_start[0] = get_upscale_x0(f->cur.p.w, f->sr_cur.p.p.w, f->resize_step[0]); + f->resize_start[1] = get_upscale_x0(in_cw, out_cw, f->resize_step[1]); + } + + // move f->cur into output queue + if (c->n_fc == 1) { + if (f->frame_hdr->show_frame || c->output_invisible_frames) { + dav1d_thread_picture_ref(&c->out, &f->sr_cur); + c->event_flags |= dav1d_picture_get_event_flags(&f->sr_cur); + } + } else { + dav1d_thread_picture_ref(out_delayed, &f->sr_cur); + } + + f->w4 = (f->frame_hdr->width[0] + 3) >> 2; + f->h4 = (f->frame_hdr->height + 3) >> 2; + f->bw = ((f->frame_hdr->width[0] + 7) >> 3) << 1; + f->bh = ((f->frame_hdr->height + 7) >> 3) << 1; + f->sb128w = (f->bw + 31) >> 5; + f->sb128h = (f->bh + 31) >> 5; + f->sb_shift = 4 + f->seq_hdr->sb128; + f->sb_step = 16 << f->seq_hdr->sb128; + f->sbh = (f->bh + f->sb_step - 1) >> f->sb_shift; + f->b4_stride = (f->bw + 31) & ~31; + f->bitdepth_max = (1 << f->cur.p.bpc) - 1; + atomic_init(&f->task_thread.error, 0); + const int uses_2pass = c->n_fc > 1; + const int cols = f->frame_hdr->tiling.cols; + const int rows = f->frame_hdr->tiling.rows; + atomic_store(&f->task_thread.task_counter, + (cols * rows + f->sbh) << uses_2pass); + + // ref_mvs + if (IS_INTER_OR_SWITCH(f->frame_hdr) || f->frame_hdr->allow_intrabc) { + f->mvs_ref = dav1d_ref_create_using_pool(c->refmvs_pool, + sizeof(*f->mvs) * f->sb128h * 16 * (f->b4_stride >> 1)); + if (!f->mvs_ref) { + res = DAV1D_ERR(ENOMEM); + goto error; + } + f->mvs = f->mvs_ref->data; + if (!f->frame_hdr->allow_intrabc) { + for (int i = 0; i < 7; i++) + f->refpoc[i] = f->refp[i].p.frame_hdr->frame_offset; + } else { + memset(f->refpoc, 0, sizeof(f->refpoc)); + } + if (f->frame_hdr->use_ref_frame_mvs) { + for (int i = 0; i < 7; i++) { + const int refidx = f->frame_hdr->refidx[i]; + const int ref_w = ((ref_coded_width[i] + 7) >> 3) << 1; + const int ref_h = ((f->refp[i].p.p.h + 7) >> 3) << 1; + if (c->refs[refidx].refmvs != NULL && + ref_w == f->bw && ref_h == f->bh) + { + f->ref_mvs_ref[i] = c->refs[refidx].refmvs; + dav1d_ref_inc(f->ref_mvs_ref[i]); + f->ref_mvs[i] = c->refs[refidx].refmvs->data; + } else { + f->ref_mvs[i] = NULL; + f->ref_mvs_ref[i] = NULL; + } + memcpy(f->refrefpoc[i], c->refs[refidx].refpoc, + sizeof(*f->refrefpoc)); + } + } else { + memset(f->ref_mvs_ref, 0, sizeof(f->ref_mvs_ref)); + } + } else { + f->mvs_ref = NULL; + memset(f->ref_mvs_ref, 0, sizeof(f->ref_mvs_ref)); + } + + // segmap + if (f->frame_hdr->segmentation.enabled) { + // By default, the previous segmentation map is not initialised. + f->prev_segmap_ref = NULL; + f->prev_segmap = NULL; + + // We might need a previous frame's segmentation map. This + // happens if there is either no update or a temporal update. + if (f->frame_hdr->segmentation.temporal || !f->frame_hdr->segmentation.update_map) { + const int pri_ref = f->frame_hdr->primary_ref_frame; + assert(pri_ref != DAV1D_PRIMARY_REF_NONE); + const int ref_w = ((ref_coded_width[pri_ref] + 7) >> 3) << 1; + const int ref_h = ((f->refp[pri_ref].p.p.h + 7) >> 3) << 1; + if (ref_w == f->bw && ref_h == f->bh) { + f->prev_segmap_ref = c->refs[f->frame_hdr->refidx[pri_ref]].segmap; + if (f->prev_segmap_ref) { + dav1d_ref_inc(f->prev_segmap_ref); + f->prev_segmap = f->prev_segmap_ref->data; + } + } + } + + if (f->frame_hdr->segmentation.update_map) { + // We're updating an existing map, but need somewhere to + // put the new values. Allocate them here (the data + // actually gets set elsewhere) + f->cur_segmap_ref = dav1d_ref_create_using_pool(c->segmap_pool, + sizeof(*f->cur_segmap) * f->b4_stride * 32 * f->sb128h); + if (!f->cur_segmap_ref) { + dav1d_ref_dec(&f->prev_segmap_ref); + res = DAV1D_ERR(ENOMEM); + goto error; + } + f->cur_segmap = f->cur_segmap_ref->data; + } else if (f->prev_segmap_ref) { + // We're not updating an existing map, and we have a valid + // reference. Use that. + f->cur_segmap_ref = f->prev_segmap_ref; + dav1d_ref_inc(f->cur_segmap_ref); + f->cur_segmap = f->prev_segmap_ref->data; + } else { + // We need to make a new map. Allocate one here and zero it out. + const size_t segmap_size = sizeof(*f->cur_segmap) * f->b4_stride * 32 * f->sb128h; + f->cur_segmap_ref = dav1d_ref_create_using_pool(c->segmap_pool, segmap_size); + if (!f->cur_segmap_ref) { + res = DAV1D_ERR(ENOMEM); + goto error; + } + f->cur_segmap = f->cur_segmap_ref->data; + memset(f->cur_segmap, 0, segmap_size); + } + } else { + f->cur_segmap = NULL; + f->cur_segmap_ref = NULL; + f->prev_segmap_ref = NULL; + } + + // update references etc. + const unsigned refresh_frame_flags = f->frame_hdr->refresh_frame_flags; + for (int i = 0; i < 8; i++) { + if (refresh_frame_flags & (1 << i)) { + if (c->refs[i].p.p.frame_hdr) + dav1d_thread_picture_unref(&c->refs[i].p); + dav1d_thread_picture_ref(&c->refs[i].p, &f->sr_cur); + + dav1d_cdf_thread_unref(&c->cdf[i]); + if (f->frame_hdr->refresh_context) { + dav1d_cdf_thread_ref(&c->cdf[i], &f->out_cdf); + } else { + dav1d_cdf_thread_ref(&c->cdf[i], &f->in_cdf); + } + + dav1d_ref_dec(&c->refs[i].segmap); + c->refs[i].segmap = f->cur_segmap_ref; + if (f->cur_segmap_ref) + dav1d_ref_inc(f->cur_segmap_ref); + dav1d_ref_dec(&c->refs[i].refmvs); + if (!f->frame_hdr->allow_intrabc) { + c->refs[i].refmvs = f->mvs_ref; + if (f->mvs_ref) + dav1d_ref_inc(f->mvs_ref); + } + memcpy(c->refs[i].refpoc, f->refpoc, sizeof(f->refpoc)); + } + } + + if (c->n_fc == 1) { + if ((res = dav1d_decode_frame(f)) < 0) { + dav1d_thread_picture_unref(&c->out); + for (int i = 0; i < 8; i++) { + if (refresh_frame_flags & (1 << i)) { + if (c->refs[i].p.p.frame_hdr) + dav1d_thread_picture_unref(&c->refs[i].p); + dav1d_cdf_thread_unref(&c->cdf[i]); + dav1d_ref_dec(&c->refs[i].segmap); + dav1d_ref_dec(&c->refs[i].refmvs); + } + } + goto error; + } + } else { + dav1d_task_frame_init(f); + pthread_mutex_unlock(&c->task_thread.lock); + } + + return 0; +error: + atomic_init(&f->task_thread.error, 1); + dav1d_cdf_thread_unref(&f->in_cdf); + if (f->frame_hdr->refresh_context) + dav1d_cdf_thread_unref(&f->out_cdf); + for (int i = 0; i < 7; i++) { + if (f->refp[i].p.frame_hdr) + dav1d_thread_picture_unref(&f->refp[i]); + dav1d_ref_dec(&f->ref_mvs_ref[i]); + } + if (c->n_fc == 1) + dav1d_thread_picture_unref(&c->out); + else + dav1d_thread_picture_unref(out_delayed); + dav1d_picture_unref_internal(&f->cur); + dav1d_thread_picture_unref(&f->sr_cur); + dav1d_ref_dec(&f->mvs_ref); + dav1d_ref_dec(&f->seq_hdr_ref); + dav1d_ref_dec(&f->frame_hdr_ref); + dav1d_data_props_copy(&c->cached_error_props, &c->in.m); + + for (int i = 0; i < f->n_tile_data; i++) + dav1d_data_unref_internal(&f->tile[i].data); + f->n_tile_data = 0; + + if (c->n_fc > 1) + pthread_mutex_unlock(&c->task_thread.lock); + + return res; +} diff --git a/3rdparty/dav1d/src/decode.h b/3rdparty/dav1d/src/decode.h new file mode 100644 index 0000000..1eae585 --- /dev/null +++ b/3rdparty/dav1d/src/decode.h @@ -0,0 +1,35 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_DECODE_H +#define DAV1D_SRC_DECODE_H + +#include "src/internal.h" + +int dav1d_submit_frame(Dav1dContext *c); + +#endif /* DAV1D_SRC_DECODE_H */ diff --git a/3rdparty/dav1d/src/dequant_tables.c b/3rdparty/dav1d/src/dequant_tables.c new file mode 100644 index 0000000..520d727 --- /dev/null +++ b/3rdparty/dav1d/src/dequant_tables.c @@ -0,0 +1,229 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include "src/dequant_tables.h" + +const uint16_t dav1d_dq_tbl[3][QINDEX_RANGE][2] = { + { + { 4, 4, }, { 8, 8, }, { 8, 9, }, { 9, 10, }, + { 10, 11, }, { 11, 12, }, { 12, 13, }, { 12, 14, }, + { 13, 15, }, { 14, 16, }, { 15, 17, }, { 16, 18, }, + { 17, 19, }, { 18, 20, }, { 19, 21, }, { 19, 22, }, + { 20, 23, }, { 21, 24, }, { 22, 25, }, { 23, 26, }, + { 24, 27, }, { 25, 28, }, { 26, 29, }, { 26, 30, }, + { 27, 31, }, { 28, 32, }, { 29, 33, }, { 30, 34, }, + { 31, 35, }, { 32, 36, }, { 32, 37, }, { 33, 38, }, + { 34, 39, }, { 35, 40, }, { 36, 41, }, { 37, 42, }, + { 38, 43, }, { 38, 44, }, { 39, 45, }, { 40, 46, }, + { 41, 47, }, { 42, 48, }, { 43, 49, }, { 43, 50, }, + { 44, 51, }, { 45, 52, }, { 46, 53, }, { 47, 54, }, + { 48, 55, }, { 48, 56, }, { 49, 57, }, { 50, 58, }, + { 51, 59, }, { 52, 60, }, { 53, 61, }, { 53, 62, }, + { 54, 63, }, { 55, 64, }, { 56, 65, }, { 57, 66, }, + { 57, 67, }, { 58, 68, }, { 59, 69, }, { 60, 70, }, + { 61, 71, }, { 62, 72, }, { 62, 73, }, { 63, 74, }, + { 64, 75, }, { 65, 76, }, { 66, 77, }, { 66, 78, }, + { 67, 79, }, { 68, 80, }, { 69, 81, }, { 70, 82, }, + { 70, 83, }, { 71, 84, }, { 72, 85, }, { 73, 86, }, + { 74, 87, }, { 74, 88, }, { 75, 89, }, { 76, 90, }, + { 77, 91, }, { 78, 92, }, { 78, 93, }, { 79, 94, }, + { 80, 95, }, { 81, 96, }, { 81, 97, }, { 82, 98, }, + { 83, 99, }, { 84, 100, }, { 85, 101, }, { 85, 102, }, + { 87, 104, }, { 88, 106, }, { 90, 108, }, { 92, 110, }, + { 93, 112, }, { 95, 114, }, { 96, 116, }, { 98, 118, }, + { 99, 120, }, { 101, 122, }, { 102, 124, }, { 104, 126, }, + { 105, 128, }, { 107, 130, }, { 108, 132, }, { 110, 134, }, + { 111, 136, }, { 113, 138, }, { 114, 140, }, { 116, 142, }, + { 117, 144, }, { 118, 146, }, { 120, 148, }, { 121, 150, }, + { 123, 152, }, { 125, 155, }, { 127, 158, }, { 129, 161, }, + { 131, 164, }, { 134, 167, }, { 136, 170, }, { 138, 173, }, + { 140, 176, }, { 142, 179, }, { 144, 182, }, { 146, 185, }, + { 148, 188, }, { 150, 191, }, { 152, 194, }, { 154, 197, }, + { 156, 200, }, { 158, 203, }, { 161, 207, }, { 164, 211, }, + { 166, 215, }, { 169, 219, }, { 172, 223, }, { 174, 227, }, + { 177, 231, }, { 180, 235, }, { 182, 239, }, { 185, 243, }, + { 187, 247, }, { 190, 251, }, { 192, 255, }, { 195, 260, }, + { 199, 265, }, { 202, 270, }, { 205, 275, }, { 208, 280, }, + { 211, 285, }, { 214, 290, }, { 217, 295, }, { 220, 300, }, + { 223, 305, }, { 226, 311, }, { 230, 317, }, { 233, 323, }, + { 237, 329, }, { 240, 335, }, { 243, 341, }, { 247, 347, }, + { 250, 353, }, { 253, 359, }, { 257, 366, }, { 261, 373, }, + { 265, 380, }, { 269, 387, }, { 272, 394, }, { 276, 401, }, + { 280, 408, }, { 284, 416, }, { 288, 424, }, { 292, 432, }, + { 296, 440, }, { 300, 448, }, { 304, 456, }, { 309, 465, }, + { 313, 474, }, { 317, 483, }, { 322, 492, }, { 326, 501, }, + { 330, 510, }, { 335, 520, }, { 340, 530, }, { 344, 540, }, + { 349, 550, }, { 354, 560, }, { 359, 571, }, { 364, 582, }, + { 369, 593, }, { 374, 604, }, { 379, 615, }, { 384, 627, }, + { 389, 639, }, { 395, 651, }, { 400, 663, }, { 406, 676, }, + { 411, 689, }, { 417, 702, }, { 423, 715, }, { 429, 729, }, + { 435, 743, }, { 441, 757, }, { 447, 771, }, { 454, 786, }, + { 461, 801, }, { 467, 816, }, { 475, 832, }, { 482, 848, }, + { 489, 864, }, { 497, 881, }, { 505, 898, }, { 513, 915, }, + { 522, 933, }, { 530, 951, }, { 539, 969, }, { 549, 988, }, + { 559, 1007, }, { 569, 1026, }, { 579, 1046, }, { 590, 1066, }, + { 602, 1087, }, { 614, 1108, }, { 626, 1129, }, { 640, 1151, }, + { 654, 1173, }, { 668, 1196, }, { 684, 1219, }, { 700, 1243, }, + { 717, 1267, }, { 736, 1292, }, { 755, 1317, }, { 775, 1343, }, + { 796, 1369, }, { 819, 1396, }, { 843, 1423, }, { 869, 1451, }, + { 896, 1479, }, { 925, 1508, }, { 955, 1537, }, { 988, 1567, }, + { 1022, 1597, }, { 1058, 1628, }, { 1098, 1660, }, { 1139, 1692, }, + { 1184, 1725, }, { 1232, 1759, }, { 1282, 1793, }, { 1336, 1828, }, + }, { + { 4, 4, }, { 9, 9, }, { 10, 11, }, { 13, 13, }, + { 15, 16, }, { 17, 18, }, { 20, 21, }, { 22, 24, }, + { 25, 27, }, { 28, 30, }, { 31, 33, }, { 34, 37, }, + { 37, 40, }, { 40, 44, }, { 43, 48, }, { 47, 51, }, + { 50, 55, }, { 53, 59, }, { 57, 63, }, { 60, 67, }, + { 64, 71, }, { 68, 75, }, { 71, 79, }, { 75, 83, }, + { 78, 88, }, { 82, 92, }, { 86, 96, }, { 90, 100, }, + { 93, 105, }, { 97, 109, }, { 101, 114, }, { 105, 118, }, + { 109, 122, }, { 113, 127, }, { 116, 131, }, { 120, 136, }, + { 124, 140, }, { 128, 145, }, { 132, 149, }, { 136, 154, }, + { 140, 158, }, { 143, 163, }, { 147, 168, }, { 151, 172, }, + { 155, 177, }, { 159, 181, }, { 163, 186, }, { 166, 190, }, + { 170, 195, }, { 174, 199, }, { 178, 204, }, { 182, 208, }, + { 185, 213, }, { 189, 217, }, { 193, 222, }, { 197, 226, }, + { 200, 231, }, { 204, 235, }, { 208, 240, }, { 212, 244, }, + { 215, 249, }, { 219, 253, }, { 223, 258, }, { 226, 262, }, + { 230, 267, }, { 233, 271, }, { 237, 275, }, { 241, 280, }, + { 244, 284, }, { 248, 289, }, { 251, 293, }, { 255, 297, }, + { 259, 302, }, { 262, 306, }, { 266, 311, }, { 269, 315, }, + { 273, 319, }, { 276, 324, }, { 280, 328, }, { 283, 332, }, + { 287, 337, }, { 290, 341, }, { 293, 345, }, { 297, 349, }, + { 300, 354, }, { 304, 358, }, { 307, 362, }, { 310, 367, }, + { 314, 371, }, { 317, 375, }, { 321, 379, }, { 324, 384, }, + { 327, 388, }, { 331, 392, }, { 334, 396, }, { 337, 401, }, + { 343, 409, }, { 350, 417, }, { 356, 425, }, { 362, 433, }, + { 369, 441, }, { 375, 449, }, { 381, 458, }, { 387, 466, }, + { 394, 474, }, { 400, 482, }, { 406, 490, }, { 412, 498, }, + { 418, 506, }, { 424, 514, }, { 430, 523, }, { 436, 531, }, + { 442, 539, }, { 448, 547, }, { 454, 555, }, { 460, 563, }, + { 466, 571, }, { 472, 579, }, { 478, 588, }, { 484, 596, }, + { 490, 604, }, { 499, 616, }, { 507, 628, }, { 516, 640, }, + { 525, 652, }, { 533, 664, }, { 542, 676, }, { 550, 688, }, + { 559, 700, }, { 567, 713, }, { 576, 725, }, { 584, 737, }, + { 592, 749, }, { 601, 761, }, { 609, 773, }, { 617, 785, }, + { 625, 797, }, { 634, 809, }, { 644, 825, }, { 655, 841, }, + { 666, 857, }, { 676, 873, }, { 687, 889, }, { 698, 905, }, + { 708, 922, }, { 718, 938, }, { 729, 954, }, { 739, 970, }, + { 749, 986, }, { 759, 1002, }, { 770, 1018, }, { 782, 1038, }, + { 795, 1058, }, { 807, 1078, }, { 819, 1098, }, { 831, 1118, }, + { 844, 1138, }, { 856, 1158, }, { 868, 1178, }, { 880, 1198, }, + { 891, 1218, }, { 906, 1242, }, { 920, 1266, }, { 933, 1290, }, + { 947, 1314, }, { 961, 1338, }, { 975, 1362, }, { 988, 1386, }, + { 1001, 1411, }, { 1015, 1435, }, { 1030, 1463, }, { 1045, 1491, }, + { 1061, 1519, }, { 1076, 1547, }, { 1090, 1575, }, { 1105, 1603, }, + { 1120, 1631, }, { 1137, 1663, }, { 1153, 1695, }, { 1170, 1727, }, + { 1186, 1759, }, { 1202, 1791, }, { 1218, 1823, }, { 1236, 1859, }, + { 1253, 1895, }, { 1271, 1931, }, { 1288, 1967, }, { 1306, 2003, }, + { 1323, 2039, }, { 1342, 2079, }, { 1361, 2119, }, { 1379, 2159, }, + { 1398, 2199, }, { 1416, 2239, }, { 1436, 2283, }, { 1456, 2327, }, + { 1476, 2371, }, { 1496, 2415, }, { 1516, 2459, }, { 1537, 2507, }, + { 1559, 2555, }, { 1580, 2603, }, { 1601, 2651, }, { 1624, 2703, }, + { 1647, 2755, }, { 1670, 2807, }, { 1692, 2859, }, { 1717, 2915, }, + { 1741, 2971, }, { 1766, 3027, }, { 1791, 3083, }, { 1817, 3143, }, + { 1844, 3203, }, { 1871, 3263, }, { 1900, 3327, }, { 1929, 3391, }, + { 1958, 3455, }, { 1990, 3523, }, { 2021, 3591, }, { 2054, 3659, }, + { 2088, 3731, }, { 2123, 3803, }, { 2159, 3876, }, { 2197, 3952, }, + { 2236, 4028, }, { 2276, 4104, }, { 2319, 4184, }, { 2363, 4264, }, + { 2410, 4348, }, { 2458, 4432, }, { 2508, 4516, }, { 2561, 4604, }, + { 2616, 4692, }, { 2675, 4784, }, { 2737, 4876, }, { 2802, 4972, }, + { 2871, 5068, }, { 2944, 5168, }, { 3020, 5268, }, { 3102, 5372, }, + { 3188, 5476, }, { 3280, 5584, }, { 3375, 5692, }, { 3478, 5804, }, + { 3586, 5916, }, { 3702, 6032, }, { 3823, 6148, }, { 3953, 6268, }, + { 4089, 6388, }, { 4236, 6512, }, { 4394, 6640, }, { 4559, 6768, }, + { 4737, 6900, }, { 4929, 7036, }, { 5130, 7172, }, { 5347, 7312, }, + }, { + { 4, 4 }, { 12, 13 }, { 18, 19 }, { 25, 27 }, + { 33, 35 }, { 41, 44 }, { 50, 54 }, { 60, 64 }, + { 70, 75 }, { 80, 87 }, { 91, 99 }, { 103, 112 }, + { 115, 126 }, { 127, 139 }, { 140, 154 }, { 153, 168 }, + { 166, 183 }, { 180, 199 }, { 194, 214 }, { 208, 230 }, + { 222, 247 }, { 237, 263 }, { 251, 280 }, { 266, 297 }, + { 281, 314 }, { 296, 331 }, { 312, 349 }, { 327, 366 }, + { 343, 384 }, { 358, 402 }, { 374, 420 }, { 390, 438 }, + { 405, 456 }, { 421, 475 }, { 437, 493 }, { 453, 511 }, + { 469, 530 }, { 484, 548 }, { 500, 567 }, { 516, 586 }, + { 532, 604 }, { 548, 623 }, { 564, 642 }, { 580, 660 }, + { 596, 679 }, { 611, 698 }, { 627, 716 }, { 643, 735 }, + { 659, 753 }, { 674, 772 }, { 690, 791 }, { 706, 809 }, + { 721, 828 }, { 737, 846 }, { 752, 865 }, { 768, 884 }, + { 783, 902 }, { 798, 920 }, { 814, 939 }, { 829, 957 }, + { 844, 976 }, { 859, 994 }, { 874, 1012 }, { 889, 1030 }, + { 904, 1049 }, { 919, 1067 }, { 934, 1085 }, { 949, 1103 }, + { 964, 1121 }, { 978, 1139 }, { 993, 1157 }, { 1008, 1175 }, + { 1022, 1193 }, { 1037, 1211 }, { 1051, 1229 }, { 1065, 1246 }, + { 1080, 1264 }, { 1094, 1282 }, { 1108, 1299 }, { 1122, 1317 }, + { 1136, 1335 }, { 1151, 1352 }, { 1165, 1370 }, { 1179, 1387 }, + { 1192, 1405 }, { 1206, 1422 }, { 1220, 1440 }, { 1234, 1457 }, + { 1248, 1474 }, { 1261, 1491 }, { 1275, 1509 }, { 1288, 1526 }, + { 1302, 1543 }, { 1315, 1560 }, { 1329, 1577 }, { 1342, 1595 }, + { 1368, 1627 }, { 1393, 1660 }, { 1419, 1693 }, { 1444, 1725 }, + { 1469, 1758 }, { 1494, 1791 }, { 1519, 1824 }, { 1544, 1856 }, + { 1569, 1889 }, { 1594, 1922 }, { 1618, 1954 }, { 1643, 1987 }, + { 1668, 2020 }, { 1692, 2052 }, { 1717, 2085 }, { 1741, 2118 }, + { 1765, 2150 }, { 1789, 2183 }, { 1814, 2216 }, { 1838, 2248 }, + { 1862, 2281 }, { 1885, 2313 }, { 1909, 2346 }, { 1933, 2378 }, + { 1957, 2411 }, { 1992, 2459 }, { 2027, 2508 }, { 2061, 2556 }, + { 2096, 2605 }, { 2130, 2653 }, { 2165, 2701 }, { 2199, 2750 }, + { 2233, 2798 }, { 2267, 2847 }, { 2300, 2895 }, { 2334, 2943 }, + { 2367, 2992 }, { 2400, 3040 }, { 2434, 3088 }, { 2467, 3137 }, + { 2499, 3185 }, { 2532, 3234 }, { 2575, 3298 }, { 2618, 3362 }, + { 2661, 3426 }, { 2704, 3491 }, { 2746, 3555 }, { 2788, 3619 }, + { 2830, 3684 }, { 2872, 3748 }, { 2913, 3812 }, { 2954, 3876 }, + { 2995, 3941 }, { 3036, 4005 }, { 3076, 4069 }, { 3127, 4149 }, + { 3177, 4230 }, { 3226, 4310 }, { 3275, 4390 }, { 3324, 4470 }, + { 3373, 4550 }, { 3421, 4631 }, { 3469, 4711 }, { 3517, 4791 }, + { 3565, 4871 }, { 3621, 4967 }, { 3677, 5064 }, { 3733, 5160 }, + { 3788, 5256 }, { 3843, 5352 }, { 3897, 5448 }, { 3951, 5544 }, + { 4005, 5641 }, { 4058, 5737 }, { 4119, 5849 }, { 4181, 5961 }, + { 4241, 6073 }, { 4301, 6185 }, { 4361, 6297 }, { 4420, 6410 }, + { 4479, 6522 }, { 4546, 6650 }, { 4612, 6778 }, { 4677, 6906 }, + { 4742, 7034 }, { 4807, 7162 }, { 4871, 7290 }, { 4942, 7435 }, + { 5013, 7579 }, { 5083, 7723 }, { 5153, 7867 }, { 5222, 8011 }, + { 5291, 8155 }, { 5367, 8315 }, { 5442, 8475 }, { 5517, 8635 }, + { 5591, 8795 }, { 5665, 8956 }, { 5745, 9132 }, { 5825, 9308 }, + { 5905, 9484 }, { 5984, 9660 }, { 6063, 9836 }, { 6149, 10028 }, + { 6234, 10220 }, { 6319, 10412 }, { 6404, 10604 }, { 6495, 10812 }, + { 6587, 11020 }, { 6678, 11228 }, { 6769, 11437 }, { 6867, 11661 }, + { 6966, 11885 }, { 7064, 12109 }, { 7163, 12333 }, { 7269, 12573 }, + { 7376, 12813 }, { 7483, 13053 }, { 7599, 13309 }, { 7715, 13565 }, + { 7832, 13821 }, { 7958, 14093 }, { 8085, 14365 }, { 8214, 14637 }, + { 8352, 14925 }, { 8492, 15213 }, { 8635, 15502 }, { 8788, 15806 }, + { 8945, 16110 }, { 9104, 16414 }, { 9275, 16734 }, { 9450, 17054 }, + { 9639, 17390 }, { 9832, 17726 }, { 10031, 18062 }, { 10245, 18414 }, + { 10465, 18766 }, { 10702, 19134 }, { 10946, 19502 }, { 11210, 19886 }, + { 11482, 20270 }, { 11776, 20670 }, { 12081, 21070 }, { 12409, 21486 }, + { 12750, 21902 }, { 13118, 22334 }, { 13501, 22766 }, { 13913, 23214 }, + { 14343, 23662 }, { 14807, 24126 }, { 15290, 24590 }, { 15812, 25070 }, + { 16356, 25551 }, { 16943, 26047 }, { 17575, 26559 }, { 18237, 27071 }, + { 18949, 27599 }, { 19718, 28143 }, { 20521, 28687 }, { 21387, 29247 }, + } +}; diff --git a/3rdparty/dav1d/src/dequant_tables.h b/3rdparty/dav1d/src/dequant_tables.h new file mode 100644 index 0000000..1776337 --- /dev/null +++ b/3rdparty/dav1d/src/dequant_tables.h @@ -0,0 +1,37 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_DEQUANT_TABLES_H +#define DAV1D_SRC_DEQUANT_TABLES_H + +#include + +#include "src/levels.h" + +EXTERN const uint16_t dav1d_dq_tbl[3][QINDEX_RANGE][2]; + +#endif /* DAV1D_SRC_DEQUANT_TABLES_H */ diff --git a/3rdparty/dav1d/src/env.h b/3rdparty/dav1d/src/env.h new file mode 100644 index 0000000..7c22f82 --- /dev/null +++ b/3rdparty/dav1d/src/env.h @@ -0,0 +1,520 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_ENV_H +#define DAV1D_SRC_ENV_H + +#include +#include +#include + +#include "src/levels.h" +#include "src/refmvs.h" +#include "src/tables.h" + +typedef struct BlockContext { + uint8_t ALIGN(mode[32], 8); + uint8_t ALIGN(lcoef[32], 8); + uint8_t ALIGN(ccoef[2][32], 8); + uint8_t ALIGN(seg_pred[32], 8); + uint8_t ALIGN(skip[32], 8); + uint8_t ALIGN(skip_mode[32], 8); + uint8_t ALIGN(intra[32], 8); + uint8_t ALIGN(comp_type[32], 8); + int8_t ALIGN(ref[2][32], 8); // -1 means intra + uint8_t ALIGN(filter[2][32], 8); // 3 means unset + int8_t ALIGN(tx_intra[32], 8); + int8_t ALIGN(tx[32], 8); + uint8_t ALIGN(tx_lpf_y[32], 8); + uint8_t ALIGN(tx_lpf_uv[32], 8); + uint8_t ALIGN(partition[16], 8); + uint8_t ALIGN(uvmode[32], 8); + uint8_t ALIGN(pal_sz[32], 8); +} BlockContext; + +static inline int get_intra_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4, + const int have_top, const int have_left) +{ + if (have_left) { + if (have_top) { + const int ctx = l->intra[yb4] + a->intra[xb4]; + return ctx + (ctx == 2); + } else + return l->intra[yb4] * 2; + } else { + return have_top ? a->intra[xb4] * 2 : 0; + } +} + +static inline int get_tx_ctx(const BlockContext *const a, + const BlockContext *const l, + const TxfmInfo *const max_tx, + const int yb4, const int xb4) +{ + return (l->tx_intra[yb4] >= max_tx->lh) + (a->tx_intra[xb4] >= max_tx->lw); +} + +static inline int get_partition_ctx(const BlockContext *const a, + const BlockContext *const l, + const enum BlockLevel bl, + const int yb8, const int xb8) +{ + return ((a->partition[xb8] >> (4 - bl)) & 1) + + (((l->partition[yb8] >> (4 - bl)) & 1) << 1); +} + +static inline unsigned gather_left_partition_prob(const uint16_t *const in, + const enum BlockLevel bl) +{ + unsigned out = in[PARTITION_H - 1] - in[PARTITION_H]; + // Exploit the fact that cdfs for PARTITION_SPLIT, PARTITION_T_TOP_SPLIT, + // PARTITION_T_BOTTOM_SPLIT and PARTITION_T_LEFT_SPLIT are neighbors. + out += in[PARTITION_SPLIT - 1] - in[PARTITION_T_LEFT_SPLIT]; + if (bl != BL_128X128) + out += in[PARTITION_H4 - 1] - in[PARTITION_H4]; + return out; +} + +static inline unsigned gather_top_partition_prob(const uint16_t *const in, + const enum BlockLevel bl) +{ + // Exploit the fact that cdfs for PARTITION_V, PARTITION_SPLIT and + // PARTITION_T_TOP_SPLIT are neighbors. + unsigned out = in[PARTITION_V - 1] - in[PARTITION_T_TOP_SPLIT]; + // Exploit the facts that cdfs for PARTITION_T_LEFT_SPLIT and + // PARTITION_T_RIGHT_SPLIT are neighbors, the probability for + // PARTITION_V4 is always zero, and the probability for + // PARTITION_T_RIGHT_SPLIT is zero in 128x128 blocks. + out += in[PARTITION_T_LEFT_SPLIT - 1]; + if (bl != BL_128X128) + out += in[PARTITION_V4 - 1] - in[PARTITION_T_RIGHT_SPLIT]; + return out; +} + +static inline enum TxfmType get_uv_inter_txtp(const TxfmInfo *const uvt_dim, + const enum TxfmType ytxtp) +{ + if (uvt_dim->max == TX_32X32) + return ytxtp == IDTX ? IDTX : DCT_DCT; + if (uvt_dim->min == TX_16X16 && + ((1 << ytxtp) & ((1 << H_FLIPADST) | (1 << V_FLIPADST) | + (1 << H_ADST) | (1 << V_ADST)))) + { + return DCT_DCT; + } + + return ytxtp; +} + +static inline int get_filter_ctx(const BlockContext *const a, + const BlockContext *const l, + const int comp, const int dir, const int ref, + const int yb4, const int xb4) +{ + const int a_filter = (a->ref[0][xb4] == ref || a->ref[1][xb4] == ref) ? + a->filter[dir][xb4] : DAV1D_N_SWITCHABLE_FILTERS; + const int l_filter = (l->ref[0][yb4] == ref || l->ref[1][yb4] == ref) ? + l->filter[dir][yb4] : DAV1D_N_SWITCHABLE_FILTERS; + + if (a_filter == l_filter) { + return comp * 4 + a_filter; + } else if (a_filter == DAV1D_N_SWITCHABLE_FILTERS) { + return comp * 4 + l_filter; + } else if (l_filter == DAV1D_N_SWITCHABLE_FILTERS) { + return comp * 4 + a_filter; + } else { + return comp * 4 + DAV1D_N_SWITCHABLE_FILTERS; + } +} + +static inline int get_comp_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4, + const int have_top, const int have_left) +{ + if (have_top) { + if (have_left) { + if (a->comp_type[xb4]) { + if (l->comp_type[yb4]) { + return 4; + } else { + // 4U means intra (-1) or bwd (>= 4) + return 2 + ((unsigned)l->ref[0][yb4] >= 4U); + } + } else if (l->comp_type[yb4]) { + // 4U means intra (-1) or bwd (>= 4) + return 2 + ((unsigned)a->ref[0][xb4] >= 4U); + } else { + return (l->ref[0][yb4] >= 4) ^ (a->ref[0][xb4] >= 4); + } + } else { + return a->comp_type[xb4] ? 3 : a->ref[0][xb4] >= 4; + } + } else if (have_left) { + return l->comp_type[yb4] ? 3 : l->ref[0][yb4] >= 4; + } else { + return 1; + } +} + +static inline int get_comp_dir_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4, + const int have_top, const int have_left) +{ +#define has_uni_comp(edge, off) \ + ((edge->ref[0][off] < 4) == (edge->ref[1][off] < 4)) + + if (have_top && have_left) { + const int a_intra = a->intra[xb4], l_intra = l->intra[yb4]; + + if (a_intra && l_intra) return 2; + if (a_intra || l_intra) { + const BlockContext *const edge = a_intra ? l : a; + const int off = a_intra ? yb4 : xb4; + + if (edge->comp_type[off] == COMP_INTER_NONE) return 2; + return 1 + 2 * has_uni_comp(edge, off); + } + + const int a_comp = a->comp_type[xb4] != COMP_INTER_NONE; + const int l_comp = l->comp_type[yb4] != COMP_INTER_NONE; + const int a_ref0 = a->ref[0][xb4], l_ref0 = l->ref[0][yb4]; + + if (!a_comp && !l_comp) { + return 1 + 2 * ((a_ref0 >= 4) == (l_ref0 >= 4)); + } else if (!a_comp || !l_comp) { + const BlockContext *const edge = a_comp ? a : l; + const int off = a_comp ? xb4 : yb4; + + if (!has_uni_comp(edge, off)) return 1; + return 3 + ((a_ref0 >= 4) == (l_ref0 >= 4)); + } else { + const int a_uni = has_uni_comp(a, xb4), l_uni = has_uni_comp(l, yb4); + + if (!a_uni && !l_uni) return 0; + if (!a_uni || !l_uni) return 2; + return 3 + ((a_ref0 == 4) == (l_ref0 == 4)); + } + } else if (have_top || have_left) { + const BlockContext *const edge = have_left ? l : a; + const int off = have_left ? yb4 : xb4; + + if (edge->intra[off]) return 2; + if (edge->comp_type[off] == COMP_INTER_NONE) return 2; + return 4 * has_uni_comp(edge, off); + } else { + return 2; + } +} + +static inline int get_poc_diff(const int order_hint_n_bits, + const int poc0, const int poc1) +{ + if (!order_hint_n_bits) return 0; + const int mask = 1 << (order_hint_n_bits - 1); + const int diff = poc0 - poc1; + return (diff & (mask - 1)) - (diff & mask); +} + +static inline int get_jnt_comp_ctx(const int order_hint_n_bits, const int poc, + const int ref0poc, const int ref1poc, + const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4) +{ + const int d0 = abs(get_poc_diff(order_hint_n_bits, ref0poc, poc)); + const int d1 = abs(get_poc_diff(order_hint_n_bits, poc, ref1poc)); + const int offset = d0 == d1; + const int a_ctx = a->comp_type[xb4] >= COMP_INTER_AVG || + a->ref[0][xb4] == 6; + const int l_ctx = l->comp_type[yb4] >= COMP_INTER_AVG || + l->ref[0][yb4] == 6; + + return 3 * offset + a_ctx + l_ctx; +} + +static inline int get_mask_comp_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4) +{ + const int a_ctx = a->comp_type[xb4] >= COMP_INTER_SEG ? 1 : + a->ref[0][xb4] == 6 ? 3 : 0; + const int l_ctx = l->comp_type[yb4] >= COMP_INTER_SEG ? 1 : + l->ref[0][yb4] == 6 ? 3 : 0; + + return imin(a_ctx + l_ctx, 5); +} + +#define av1_get_ref_2_ctx av1_get_bwd_ref_ctx +#define av1_get_ref_3_ctx av1_get_fwd_ref_ctx +#define av1_get_ref_4_ctx av1_get_fwd_ref_1_ctx +#define av1_get_ref_5_ctx av1_get_fwd_ref_2_ctx +#define av1_get_ref_6_ctx av1_get_bwd_ref_1_ctx +#define av1_get_uni_p_ctx av1_get_ref_ctx +#define av1_get_uni_p2_ctx av1_get_fwd_ref_2_ctx + +static inline int av1_get_ref_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4, + int have_top, int have_left) +{ + int cnt[2] = { 0 }; + + if (have_top && !a->intra[xb4]) { + cnt[a->ref[0][xb4] >= 4]++; + if (a->comp_type[xb4]) cnt[a->ref[1][xb4] >= 4]++; + } + + if (have_left && !l->intra[yb4]) { + cnt[l->ref[0][yb4] >= 4]++; + if (l->comp_type[yb4]) cnt[l->ref[1][yb4] >= 4]++; + } + + return cnt[0] == cnt[1] ? 1 : cnt[0] < cnt[1] ? 0 : 2; +} + +static inline int av1_get_fwd_ref_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4, + const int have_top, const int have_left) +{ + int cnt[4] = { 0 }; + + if (have_top && !a->intra[xb4]) { + if (a->ref[0][xb4] < 4) cnt[a->ref[0][xb4]]++; + if (a->comp_type[xb4] && a->ref[1][xb4] < 4) cnt[a->ref[1][xb4]]++; + } + + if (have_left && !l->intra[yb4]) { + if (l->ref[0][yb4] < 4) cnt[l->ref[0][yb4]]++; + if (l->comp_type[yb4] && l->ref[1][yb4] < 4) cnt[l->ref[1][yb4]]++; + } + + cnt[0] += cnt[1]; + cnt[2] += cnt[3]; + + return cnt[0] == cnt[2] ? 1 : cnt[0] < cnt[2] ? 0 : 2; +} + +static inline int av1_get_fwd_ref_1_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4, + const int have_top, const int have_left) +{ + int cnt[2] = { 0 }; + + if (have_top && !a->intra[xb4]) { + if (a->ref[0][xb4] < 2) cnt[a->ref[0][xb4]]++; + if (a->comp_type[xb4] && a->ref[1][xb4] < 2) cnt[a->ref[1][xb4]]++; + } + + if (have_left && !l->intra[yb4]) { + if (l->ref[0][yb4] < 2) cnt[l->ref[0][yb4]]++; + if (l->comp_type[yb4] && l->ref[1][yb4] < 2) cnt[l->ref[1][yb4]]++; + } + + return cnt[0] == cnt[1] ? 1 : cnt[0] < cnt[1] ? 0 : 2; +} + +static inline int av1_get_fwd_ref_2_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4, + const int have_top, const int have_left) +{ + int cnt[2] = { 0 }; + + if (have_top && !a->intra[xb4]) { + if ((a->ref[0][xb4] ^ 2U) < 2) cnt[a->ref[0][xb4] - 2]++; + if (a->comp_type[xb4] && (a->ref[1][xb4] ^ 2U) < 2) cnt[a->ref[1][xb4] - 2]++; + } + + if (have_left && !l->intra[yb4]) { + if ((l->ref[0][yb4] ^ 2U) < 2) cnt[l->ref[0][yb4] - 2]++; + if (l->comp_type[yb4] && (l->ref[1][yb4] ^ 2U) < 2) cnt[l->ref[1][yb4] - 2]++; + } + + return cnt[0] == cnt[1] ? 1 : cnt[0] < cnt[1] ? 0 : 2; +} + +static inline int av1_get_bwd_ref_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4, + const int have_top, const int have_left) +{ + int cnt[3] = { 0 }; + + if (have_top && !a->intra[xb4]) { + if (a->ref[0][xb4] >= 4) cnt[a->ref[0][xb4] - 4]++; + if (a->comp_type[xb4] && a->ref[1][xb4] >= 4) cnt[a->ref[1][xb4] - 4]++; + } + + if (have_left && !l->intra[yb4]) { + if (l->ref[0][yb4] >= 4) cnt[l->ref[0][yb4] - 4]++; + if (l->comp_type[yb4] && l->ref[1][yb4] >= 4) cnt[l->ref[1][yb4] - 4]++; + } + + cnt[1] += cnt[0]; + + return cnt[2] == cnt[1] ? 1 : cnt[1] < cnt[2] ? 0 : 2; +} + +static inline int av1_get_bwd_ref_1_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4, + const int have_top, const int have_left) +{ + int cnt[3] = { 0 }; + + if (have_top && !a->intra[xb4]) { + if (a->ref[0][xb4] >= 4) cnt[a->ref[0][xb4] - 4]++; + if (a->comp_type[xb4] && a->ref[1][xb4] >= 4) cnt[a->ref[1][xb4] - 4]++; + } + + if (have_left && !l->intra[yb4]) { + if (l->ref[0][yb4] >= 4) cnt[l->ref[0][yb4] - 4]++; + if (l->comp_type[yb4] && l->ref[1][yb4] >= 4) cnt[l->ref[1][yb4] - 4]++; + } + + return cnt[0] == cnt[1] ? 1 : cnt[0] < cnt[1] ? 0 : 2; +} + +static inline int av1_get_uni_p1_ctx(const BlockContext *const a, + const BlockContext *const l, + const int yb4, const int xb4, + const int have_top, const int have_left) +{ + int cnt[3] = { 0 }; + + if (have_top && !a->intra[xb4]) { + if (a->ref[0][xb4] - 1U < 3) cnt[a->ref[0][xb4] - 1]++; + if (a->comp_type[xb4] && a->ref[1][xb4] - 1U < 3) cnt[a->ref[1][xb4] - 1]++; + } + + if (have_left && !l->intra[yb4]) { + if (l->ref[0][yb4] - 1U < 3) cnt[l->ref[0][yb4] - 1]++; + if (l->comp_type[yb4] && l->ref[1][yb4] - 1U < 3) cnt[l->ref[1][yb4] - 1]++; + } + + cnt[1] += cnt[2]; + + return cnt[0] == cnt[1] ? 1 : cnt[0] < cnt[1] ? 0 : 2; +} + +static inline int get_drl_context(const refmvs_candidate *const ref_mv_stack, + const int ref_idx) +{ + if (ref_mv_stack[ref_idx].weight >= 640) + return ref_mv_stack[ref_idx + 1].weight < 640; + + return ref_mv_stack[ref_idx + 1].weight < 640 ? 2 : 0; +} + +static inline unsigned get_cur_frame_segid(const int by, const int bx, + const int have_top, + const int have_left, + int *const seg_ctx, + const uint8_t *cur_seg_map, + const ptrdiff_t stride) +{ + cur_seg_map += bx + by * stride; + if (have_left && have_top) { + const int l = cur_seg_map[-1]; + const int a = cur_seg_map[-stride]; + const int al = cur_seg_map[-(stride + 1)]; + + if (l == a && al == l) *seg_ctx = 2; + else if (l == a || al == l || a == al) *seg_ctx = 1; + else *seg_ctx = 0; + return a == al ? a : l; + } else { + *seg_ctx = 0; + return have_left ? cur_seg_map[-1] : have_top ? cur_seg_map[-stride] : 0; + } +} + +static inline void fix_int_mv_precision(mv *const mv) { + mv->x = (mv->x - (mv->x >> 15) + 3) & ~7U; + mv->y = (mv->y - (mv->y >> 15) + 3) & ~7U; +} + +static inline void fix_mv_precision(const Dav1dFrameHeader *const hdr, + mv *const mv) +{ + if (hdr->force_integer_mv) { + fix_int_mv_precision(mv); + } else if (!hdr->hp) { + mv->x = (mv->x - (mv->x >> 15)) & ~1U; + mv->y = (mv->y - (mv->y >> 15)) & ~1U; + } +} + +static inline mv get_gmv_2d(const Dav1dWarpedMotionParams *const gmv, + const int bx4, const int by4, + const int bw4, const int bh4, + const Dav1dFrameHeader *const hdr) +{ + switch (gmv->type) { + case DAV1D_WM_TYPE_ROT_ZOOM: + assert(gmv->matrix[5] == gmv->matrix[2]); + assert(gmv->matrix[4] == -gmv->matrix[3]); + // fall-through + default: + case DAV1D_WM_TYPE_AFFINE: { + const int x = bx4 * 4 + bw4 * 2 - 1; + const int y = by4 * 4 + bh4 * 2 - 1; + const int xc = (gmv->matrix[2] - (1 << 16)) * x + + gmv->matrix[3] * y + gmv->matrix[0]; + const int yc = (gmv->matrix[5] - (1 << 16)) * y + + gmv->matrix[4] * x + gmv->matrix[1]; + const int shift = 16 - (3 - !hdr->hp); + const int round = (1 << shift) >> 1; + mv res = (mv) { + .y = apply_sign(((abs(yc) + round) >> shift) << !hdr->hp, yc), + .x = apply_sign(((abs(xc) + round) >> shift) << !hdr->hp, xc), + }; + if (hdr->force_integer_mv) + fix_int_mv_precision(&res); + return res; + } + case DAV1D_WM_TYPE_TRANSLATION: { + mv res = (mv) { + .y = gmv->matrix[0] >> 13, + .x = gmv->matrix[1] >> 13, + }; + if (hdr->force_integer_mv) + fix_int_mv_precision(&res); + return res; + } + case DAV1D_WM_TYPE_IDENTITY: + return (mv) { .x = 0, .y = 0 }; + } +} + +#endif /* DAV1D_SRC_ENV_H */ diff --git a/3rdparty/dav1d/src/fg_apply.h b/3rdparty/dav1d/src/fg_apply.h new file mode 100644 index 0000000..be6685d --- /dev/null +++ b/3rdparty/dav1d/src/fg_apply.h @@ -0,0 +1,58 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_FG_APPLY_H +#define DAV1D_SRC_FG_APPLY_H + +#include "dav1d/picture.h" + +#include "common/bitdepth.h" + +#include "src/filmgrain.h" + +#ifdef BITDEPTH +# define array_decl(type, name, sz) type name sz +#else +# define array_decl(type, name, sz) void *name +#endif + +bitfn_decls(void dav1d_apply_grain, + const Dav1dFilmGrainDSPContext *const dsp, + Dav1dPicture *const out, const Dav1dPicture *const in); +bitfn_decls(void dav1d_prep_grain, + const Dav1dFilmGrainDSPContext *const dsp, + Dav1dPicture *const out, const Dav1dPicture *const in, + array_decl(uint8_t, scaling, [3][SCALING_SIZE]), + array_decl(entry, grain_lut, [3][GRAIN_HEIGHT+1][GRAIN_WIDTH])); +bitfn_decls(void dav1d_apply_grain_row, + const Dav1dFilmGrainDSPContext *const dsp, + Dav1dPicture *const out, const Dav1dPicture *const in, + array_decl(const uint8_t, scaling, [3][SCALING_SIZE]), + array_decl(const entry, grain_lut, [3][GRAIN_HEIGHT+1][GRAIN_WIDTH]), + const int row); + +#endif /* DAV1D_SRC_FG_APPLY_H */ diff --git a/3rdparty/dav1d/src/fg_apply_tmpl.c b/3rdparty/dav1d/src/fg_apply_tmpl.c new file mode 100644 index 0000000..044e257 --- /dev/null +++ b/3rdparty/dav1d/src/fg_apply_tmpl.c @@ -0,0 +1,241 @@ +/* + * Copyright © 2018, Niklas Haas + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "dav1d/common.h" +#include "dav1d/picture.h" + +#include "common/intops.h" +#include "common/bitdepth.h" + +#include "src/fg_apply.h" + +static void generate_scaling(const int bitdepth, + const uint8_t points[][2], const int num, + uint8_t scaling[SCALING_SIZE]) +{ +#if BITDEPTH == 8 + const int shift_x = 0; + const int scaling_size = SCALING_SIZE; +#else + assert(bitdepth > 8); + const int shift_x = bitdepth - 8; + const int scaling_size = 1 << bitdepth; +#endif + + if (num == 0) { + memset(scaling, 0, scaling_size); + return; + } + + // Fill up the preceding entries with the initial value + memset(scaling, points[0][1], points[0][0] << shift_x); + + // Linearly interpolate the values in the middle + for (int i = 0; i < num - 1; i++) { + const int bx = points[i][0]; + const int by = points[i][1]; + const int ex = points[i+1][0]; + const int ey = points[i+1][1]; + const int dx = ex - bx; + const int dy = ey - by; + assert(dx > 0); + const int delta = dy * ((0x10000 + (dx >> 1)) / dx); + for (int x = 0, d = 0x8000; x < dx; x++) { + scaling[(bx + x) << shift_x] = by + (d >> 16); + d += delta; + } + } + + // Fill up the remaining entries with the final value + const int n = points[num - 1][0] << shift_x; + memset(&scaling[n], points[num - 1][1], scaling_size - n); + +#if BITDEPTH != 8 + const int pad = 1 << shift_x, rnd = pad >> 1; + for (int i = 0; i < num - 1; i++) { + const int bx = points[i][0] << shift_x; + const int ex = points[i+1][0] << shift_x; + const int dx = ex - bx; + for (int x = 0; x < dx; x += pad) { + const int range = scaling[bx + x + pad] - scaling[bx + x]; + for (int n = 1, r = rnd; n < pad; n++) { + r += range; + scaling[bx + x + n] = scaling[bx + x] + (r >> shift_x); + } + } + } +#endif +} + +#ifndef UNIT_TEST +void bitfn(dav1d_prep_grain)(const Dav1dFilmGrainDSPContext *const dsp, + Dav1dPicture *const out, + const Dav1dPicture *const in, + uint8_t scaling[3][SCALING_SIZE], + entry grain_lut[3][GRAIN_HEIGHT+1][GRAIN_WIDTH]) +{ + const Dav1dFilmGrainData *const data = &out->frame_hdr->film_grain.data; +#if BITDEPTH != 8 + const int bitdepth_max = (1 << out->p.bpc) - 1; +#endif + + // Generate grain LUTs as needed + dsp->generate_grain_y(grain_lut[0], data HIGHBD_TAIL_SUFFIX); // always needed + if (data->num_uv_points[0] || data->chroma_scaling_from_luma) + dsp->generate_grain_uv[in->p.layout - 1](grain_lut[1], grain_lut[0], + data, 0 HIGHBD_TAIL_SUFFIX); + if (data->num_uv_points[1] || data->chroma_scaling_from_luma) + dsp->generate_grain_uv[in->p.layout - 1](grain_lut[2], grain_lut[0], + data, 1 HIGHBD_TAIL_SUFFIX); + + // Generate scaling LUTs as needed + if (data->num_y_points || data->chroma_scaling_from_luma) + generate_scaling(in->p.bpc, data->y_points, data->num_y_points, scaling[0]); + if (data->num_uv_points[0]) + generate_scaling(in->p.bpc, data->uv_points[0], data->num_uv_points[0], scaling[1]); + if (data->num_uv_points[1]) + generate_scaling(in->p.bpc, data->uv_points[1], data->num_uv_points[1], scaling[2]); + + // Copy over the non-modified planes + assert(out->stride[0] == in->stride[0]); + if (!data->num_y_points) { + const ptrdiff_t stride = out->stride[0]; + const ptrdiff_t sz = out->p.h * stride; + if (sz < 0) + memcpy((uint8_t*) out->data[0] + sz - stride, + (uint8_t*) in->data[0] + sz - stride, -sz); + else + memcpy(out->data[0], in->data[0], sz); + } + + if (in->p.layout != DAV1D_PIXEL_LAYOUT_I400 && !data->chroma_scaling_from_luma) { + assert(out->stride[1] == in->stride[1]); + const int ss_ver = in->p.layout == DAV1D_PIXEL_LAYOUT_I420; + const ptrdiff_t stride = out->stride[1]; + const ptrdiff_t sz = ((out->p.h + ss_ver) >> ss_ver) * stride; + if (sz < 0) { + if (!data->num_uv_points[0]) + memcpy((uint8_t*) out->data[1] + sz - stride, + (uint8_t*) in->data[1] + sz - stride, -sz); + if (!data->num_uv_points[1]) + memcpy((uint8_t*) out->data[2] + sz - stride, + (uint8_t*) in->data[2] + sz - stride, -sz); + } else { + if (!data->num_uv_points[0]) + memcpy(out->data[1], in->data[1], sz); + if (!data->num_uv_points[1]) + memcpy(out->data[2], in->data[2], sz); + } + } +} + +void bitfn(dav1d_apply_grain_row)(const Dav1dFilmGrainDSPContext *const dsp, + Dav1dPicture *const out, + const Dav1dPicture *const in, + const uint8_t scaling[3][SCALING_SIZE], + const entry grain_lut[3][GRAIN_HEIGHT+1][GRAIN_WIDTH], + const int row) +{ + // Synthesize grain for the affected planes + const Dav1dFilmGrainData *const data = &out->frame_hdr->film_grain.data; + const int ss_y = in->p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_x = in->p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int cpw = (out->p.w + ss_x) >> ss_x; + const int is_id = out->seq_hdr->mtrx == DAV1D_MC_IDENTITY; + pixel *const luma_src = + ((pixel *) in->data[0]) + row * FG_BLOCK_SIZE * PXSTRIDE(in->stride[0]); +#if BITDEPTH != 8 + const int bitdepth_max = (1 << out->p.bpc) - 1; +#endif + + if (data->num_y_points) { + const int bh = imin(out->p.h - row * FG_BLOCK_SIZE, FG_BLOCK_SIZE); + dsp->fgy_32x32xn(((pixel *) out->data[0]) + row * FG_BLOCK_SIZE * PXSTRIDE(out->stride[0]), + luma_src, out->stride[0], data, + out->p.w, scaling[0], grain_lut[0], bh, row HIGHBD_TAIL_SUFFIX); + } + + if (!data->num_uv_points[0] && !data->num_uv_points[1] && + !data->chroma_scaling_from_luma) + { + return; + } + + const int bh = (imin(out->p.h - row * FG_BLOCK_SIZE, FG_BLOCK_SIZE) + ss_y) >> ss_y; + + // extend padding pixels + if (out->p.w & ss_x) { + pixel *ptr = luma_src; + for (int y = 0; y < bh; y++) { + ptr[out->p.w] = ptr[out->p.w - 1]; + ptr += PXSTRIDE(in->stride[0]) << ss_y; + } + } + + const ptrdiff_t uv_off = row * FG_BLOCK_SIZE * PXSTRIDE(out->stride[1]) >> ss_y; + if (data->chroma_scaling_from_luma) { + for (int pl = 0; pl < 2; pl++) + dsp->fguv_32x32xn[in->p.layout - 1](((pixel *) out->data[1 + pl]) + uv_off, + ((const pixel *) in->data[1 + pl]) + uv_off, + in->stride[1], data, cpw, + scaling[0], grain_lut[1 + pl], + bh, row, luma_src, in->stride[0], + pl, is_id HIGHBD_TAIL_SUFFIX); + } else { + for (int pl = 0; pl < 2; pl++) + if (data->num_uv_points[pl]) + dsp->fguv_32x32xn[in->p.layout - 1](((pixel *) out->data[1 + pl]) + uv_off, + ((const pixel *) in->data[1 + pl]) + uv_off, + in->stride[1], data, cpw, + scaling[1 + pl], grain_lut[1 + pl], + bh, row, luma_src, in->stride[0], + pl, is_id HIGHBD_TAIL_SUFFIX); + } +} + +void bitfn(dav1d_apply_grain)(const Dav1dFilmGrainDSPContext *const dsp, + Dav1dPicture *const out, + const Dav1dPicture *const in) +{ + ALIGN_STK_16(entry, grain_lut, 3,[GRAIN_HEIGHT + 1][GRAIN_WIDTH]); +#if ARCH_X86_64 && BITDEPTH == 8 + ALIGN_STK_64(uint8_t, scaling, 3,[SCALING_SIZE]); +#else + uint8_t scaling[3][SCALING_SIZE]; +#endif + const int rows = (out->p.h + FG_BLOCK_SIZE - 1) / FG_BLOCK_SIZE; + + bitfn(dav1d_prep_grain)(dsp, out, in, scaling, grain_lut); + for (int row = 0; row < rows; row++) + bitfn(dav1d_apply_grain_row)(dsp, out, in, scaling, grain_lut, row); +} +#endif diff --git a/3rdparty/dav1d/src/filmgrain.h b/3rdparty/dav1d/src/filmgrain.h new file mode 100644 index 0000000..1509bb6 --- /dev/null +++ b/3rdparty/dav1d/src/filmgrain.h @@ -0,0 +1,84 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_FILM_GRAIN_H +#define DAV1D_SRC_FILM_GRAIN_H + +#include "common/bitdepth.h" + +#include "src/levels.h" + +#define GRAIN_WIDTH 82 +#define GRAIN_HEIGHT 73 +#define FG_BLOCK_SIZE 32 +#if !defined(BITDEPTH) || BITDEPTH == 8 +#define SCALING_SIZE 256 +typedef int8_t entry; +#else +#define SCALING_SIZE 4096 +typedef int16_t entry; +#endif + +#define decl_generate_grain_y_fn(name) \ +void (name)(entry buf[][GRAIN_WIDTH], \ + const Dav1dFilmGrainData *const data HIGHBD_DECL_SUFFIX) +typedef decl_generate_grain_y_fn(*generate_grain_y_fn); + +#define decl_generate_grain_uv_fn(name) \ +void (name)(entry buf[][GRAIN_WIDTH], \ + const entry buf_y[][GRAIN_WIDTH], \ + const Dav1dFilmGrainData *const data, const intptr_t uv HIGHBD_DECL_SUFFIX) +typedef decl_generate_grain_uv_fn(*generate_grain_uv_fn); + +#define decl_fgy_32x32xn_fn(name) \ +void (name)(pixel *dst_row, const pixel *src_row, ptrdiff_t stride, \ + const Dav1dFilmGrainData *data, \ + size_t pw, const uint8_t scaling[SCALING_SIZE], \ + const entry grain_lut[][GRAIN_WIDTH], \ + int bh, int row_num HIGHBD_DECL_SUFFIX) +typedef decl_fgy_32x32xn_fn(*fgy_32x32xn_fn); + +#define decl_fguv_32x32xn_fn(name) \ +void (name)(pixel *dst_row, const pixel *src_row, ptrdiff_t stride, \ + const Dav1dFilmGrainData *data, size_t pw, \ + const uint8_t scaling[SCALING_SIZE], \ + const entry grain_lut[][GRAIN_WIDTH], int bh, int row_num, \ + const pixel *luma_row, ptrdiff_t luma_stride, \ + int uv_pl, int is_id HIGHBD_DECL_SUFFIX) +typedef decl_fguv_32x32xn_fn(*fguv_32x32xn_fn); + +typedef struct Dav1dFilmGrainDSPContext { + generate_grain_y_fn generate_grain_y; + generate_grain_uv_fn generate_grain_uv[3]; + + fgy_32x32xn_fn fgy_32x32xn; + fguv_32x32xn_fn fguv_32x32xn[3]; +} Dav1dFilmGrainDSPContext; + +bitfn_decls(void dav1d_film_grain_dsp_init, Dav1dFilmGrainDSPContext *c); + +#endif /* DAV1D_SRC_FILM_GRAIN_H */ diff --git a/3rdparty/dav1d/src/filmgrain_tmpl.c b/3rdparty/dav1d/src/filmgrain_tmpl.c new file mode 100644 index 0000000..12e91dd --- /dev/null +++ b/3rdparty/dav1d/src/filmgrain_tmpl.c @@ -0,0 +1,441 @@ +/* + * Copyright © 2018, Niklas Haas + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "common/attributes.h" +#include "common/intops.h" + +#include "src/filmgrain.h" +#include "src/tables.h" + +#define SUB_GRAIN_WIDTH 44 +#define SUB_GRAIN_HEIGHT 38 + +static inline int get_random_number(const int bits, unsigned *const state) { + const int r = *state; + unsigned bit = ((r >> 0) ^ (r >> 1) ^ (r >> 3) ^ (r >> 12)) & 1; + *state = (r >> 1) | (bit << 15); + + return (*state >> (16 - bits)) & ((1 << bits) - 1); +} + +static inline int round2(const int x, const uint64_t shift) { + return (x + ((1 << shift) >> 1)) >> shift; +} + +static void generate_grain_y_c(entry buf[][GRAIN_WIDTH], + const Dav1dFilmGrainData *const data + HIGHBD_DECL_SUFFIX) +{ + const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8; + unsigned seed = data->seed; + const int shift = 4 - bitdepth_min_8 + data->grain_scale_shift; + const int grain_ctr = 128 << bitdepth_min_8; + const int grain_min = -grain_ctr, grain_max = grain_ctr - 1; + + for (int y = 0; y < GRAIN_HEIGHT; y++) { + for (int x = 0; x < GRAIN_WIDTH; x++) { + const int value = get_random_number(11, &seed); + buf[y][x] = round2(dav1d_gaussian_sequence[ value ], shift); + } + } + + const int ar_pad = 3; + const int ar_lag = data->ar_coeff_lag; + + for (int y = ar_pad; y < GRAIN_HEIGHT; y++) { + for (int x = ar_pad; x < GRAIN_WIDTH - ar_pad; x++) { + const int8_t *coeff = data->ar_coeffs_y; + int sum = 0; + for (int dy = -ar_lag; dy <= 0; dy++) { + for (int dx = -ar_lag; dx <= ar_lag; dx++) { + if (!dx && !dy) + break; + sum += *(coeff++) * buf[y + dy][x + dx]; + } + } + + const int grain = buf[y][x] + round2(sum, data->ar_coeff_shift); + buf[y][x] = iclip(grain, grain_min, grain_max); + } + } +} + +static NOINLINE void +generate_grain_uv_c(entry buf[][GRAIN_WIDTH], + const entry buf_y[][GRAIN_WIDTH], + const Dav1dFilmGrainData *const data, const intptr_t uv, + const int subx, const int suby HIGHBD_DECL_SUFFIX) +{ + const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8; + unsigned seed = data->seed ^ (uv ? 0x49d8 : 0xb524); + const int shift = 4 - bitdepth_min_8 + data->grain_scale_shift; + const int grain_ctr = 128 << bitdepth_min_8; + const int grain_min = -grain_ctr, grain_max = grain_ctr - 1; + + const int chromaW = subx ? SUB_GRAIN_WIDTH : GRAIN_WIDTH; + const int chromaH = suby ? SUB_GRAIN_HEIGHT : GRAIN_HEIGHT; + + for (int y = 0; y < chromaH; y++) { + for (int x = 0; x < chromaW; x++) { + const int value = get_random_number(11, &seed); + buf[y][x] = round2(dav1d_gaussian_sequence[ value ], shift); + } + } + + const int ar_pad = 3; + const int ar_lag = data->ar_coeff_lag; + + for (int y = ar_pad; y < chromaH; y++) { + for (int x = ar_pad; x < chromaW - ar_pad; x++) { + const int8_t *coeff = data->ar_coeffs_uv[uv]; + int sum = 0; + for (int dy = -ar_lag; dy <= 0; dy++) { + for (int dx = -ar_lag; dx <= ar_lag; dx++) { + // For the final (current) pixel, we need to add in the + // contribution from the luma grain texture + if (!dx && !dy) { + if (!data->num_y_points) + break; + int luma = 0; + const int lumaX = ((x - ar_pad) << subx) + ar_pad; + const int lumaY = ((y - ar_pad) << suby) + ar_pad; + for (int i = 0; i <= suby; i++) { + for (int j = 0; j <= subx; j++) { + luma += buf_y[lumaY + i][lumaX + j]; + } + } + luma = round2(luma, subx + suby); + sum += luma * (*coeff); + break; + } + + sum += *(coeff++) * buf[y + dy][x + dx]; + } + } + + const int grain = buf[y][x] + round2(sum, data->ar_coeff_shift); + buf[y][x] = iclip(grain, grain_min, grain_max); + } + } +} + +#define gnuv_ss_fn(nm, ss_x, ss_y) \ +static decl_generate_grain_uv_fn(generate_grain_uv_##nm##_c) { \ + generate_grain_uv_c(buf, buf_y, data, uv, ss_x, ss_y HIGHBD_TAIL_SUFFIX); \ +} + +gnuv_ss_fn(420, 1, 1); +gnuv_ss_fn(422, 1, 0); +gnuv_ss_fn(444, 0, 0); + +// samples from the correct block of a grain LUT, while taking into account the +// offsets provided by the offsets cache +static inline entry sample_lut(const entry grain_lut[][GRAIN_WIDTH], + const int offsets[2][2], const int subx, const int suby, + const int bx, const int by, const int x, const int y) +{ + const int randval = offsets[bx][by]; + const int offx = 3 + (2 >> subx) * (3 + (randval >> 4)); + const int offy = 3 + (2 >> suby) * (3 + (randval & 0xF)); + return grain_lut[offy + y + (FG_BLOCK_SIZE >> suby) * by] + [offx + x + (FG_BLOCK_SIZE >> subx) * bx]; +} + +static void fgy_32x32xn_c(pixel *const dst_row, const pixel *const src_row, + const ptrdiff_t stride, + const Dav1dFilmGrainData *const data, const size_t pw, + const uint8_t scaling[SCALING_SIZE], + const entry grain_lut[][GRAIN_WIDTH], + const int bh, const int row_num HIGHBD_DECL_SUFFIX) +{ + const int rows = 1 + (data->overlap_flag && row_num > 0); + const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8; + const int grain_ctr = 128 << bitdepth_min_8; + const int grain_min = -grain_ctr, grain_max = grain_ctr - 1; + + int min_value, max_value; + if (data->clip_to_restricted_range) { + min_value = 16 << bitdepth_min_8; + max_value = 235 << bitdepth_min_8; + } else { + min_value = 0; + max_value = BITDEPTH_MAX; + } + + // seed[0] contains the current row, seed[1] contains the previous + unsigned seed[2]; + for (int i = 0; i < rows; i++) { + seed[i] = data->seed; + seed[i] ^= (((row_num - i) * 37 + 178) & 0xFF) << 8; + seed[i] ^= (((row_num - i) * 173 + 105) & 0xFF); + } + + assert(stride % (FG_BLOCK_SIZE * sizeof(pixel)) == 0); + + int offsets[2 /* col offset */][2 /* row offset */]; + + // process this row in FG_BLOCK_SIZE^2 blocks + for (unsigned bx = 0; bx < pw; bx += FG_BLOCK_SIZE) { + const int bw = imin(FG_BLOCK_SIZE, (int) pw - bx); + + if (data->overlap_flag && bx) { + // shift previous offsets left + for (int i = 0; i < rows; i++) + offsets[1][i] = offsets[0][i]; + } + + // update current offsets + for (int i = 0; i < rows; i++) + offsets[0][i] = get_random_number(8, &seed[i]); + + // x/y block offsets to compensate for overlapped regions + const int ystart = data->overlap_flag && row_num ? imin(2, bh) : 0; + const int xstart = data->overlap_flag && bx ? imin(2, bw) : 0; + + static const int w[2][2] = { { 27, 17 }, { 17, 27 } }; + +#define add_noise_y(x, y, grain) \ + const pixel *const src = src_row + (y) * PXSTRIDE(stride) + (x) + bx; \ + pixel *const dst = dst_row + (y) * PXSTRIDE(stride) + (x) + bx; \ + const int noise = round2(scaling[ *src ] * (grain), data->scaling_shift); \ + *dst = iclip(*src + noise, min_value, max_value); + + for (int y = ystart; y < bh; y++) { + // Non-overlapped image region (straightforward) + for (int x = xstart; x < bw; x++) { + int grain = sample_lut(grain_lut, offsets, 0, 0, 0, 0, x, y); + add_noise_y(x, y, grain); + } + + // Special case for overlapped column + for (int x = 0; x < xstart; x++) { + int grain = sample_lut(grain_lut, offsets, 0, 0, 0, 0, x, y); + int old = sample_lut(grain_lut, offsets, 0, 0, 1, 0, x, y); + grain = round2(old * w[x][0] + grain * w[x][1], 5); + grain = iclip(grain, grain_min, grain_max); + add_noise_y(x, y, grain); + } + } + + for (int y = 0; y < ystart; y++) { + // Special case for overlapped row (sans corner) + for (int x = xstart; x < bw; x++) { + int grain = sample_lut(grain_lut, offsets, 0, 0, 0, 0, x, y); + int old = sample_lut(grain_lut, offsets, 0, 0, 0, 1, x, y); + grain = round2(old * w[y][0] + grain * w[y][1], 5); + grain = iclip(grain, grain_min, grain_max); + add_noise_y(x, y, grain); + } + + // Special case for doubly-overlapped corner + for (int x = 0; x < xstart; x++) { + // Blend the top pixel with the top left block + int top = sample_lut(grain_lut, offsets, 0, 0, 0, 1, x, y); + int old = sample_lut(grain_lut, offsets, 0, 0, 1, 1, x, y); + top = round2(old * w[x][0] + top * w[x][1], 5); + top = iclip(top, grain_min, grain_max); + + // Blend the current pixel with the left block + int grain = sample_lut(grain_lut, offsets, 0, 0, 0, 0, x, y); + old = sample_lut(grain_lut, offsets, 0, 0, 1, 0, x, y); + grain = round2(old * w[x][0] + grain * w[x][1], 5); + grain = iclip(grain, grain_min, grain_max); + + // Mix the row rows together and apply grain + grain = round2(top * w[y][0] + grain * w[y][1], 5); + grain = iclip(grain, grain_min, grain_max); + add_noise_y(x, y, grain); + } + } + } +} + +static NOINLINE void +fguv_32x32xn_c(pixel *const dst_row, const pixel *const src_row, + const ptrdiff_t stride, const Dav1dFilmGrainData *const data, + const size_t pw, const uint8_t scaling[SCALING_SIZE], + const entry grain_lut[][GRAIN_WIDTH], const int bh, + const int row_num, const pixel *const luma_row, + const ptrdiff_t luma_stride, const int uv, const int is_id, + const int sx, const int sy HIGHBD_DECL_SUFFIX) +{ + const int rows = 1 + (data->overlap_flag && row_num > 0); + const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8; + const int grain_ctr = 128 << bitdepth_min_8; + const int grain_min = -grain_ctr, grain_max = grain_ctr - 1; + + int min_value, max_value; + if (data->clip_to_restricted_range) { + min_value = 16 << bitdepth_min_8; + max_value = (is_id ? 235 : 240) << bitdepth_min_8; + } else { + min_value = 0; + max_value = BITDEPTH_MAX; + } + + // seed[0] contains the current row, seed[1] contains the previous + unsigned seed[2]; + for (int i = 0; i < rows; i++) { + seed[i] = data->seed; + seed[i] ^= (((row_num - i) * 37 + 178) & 0xFF) << 8; + seed[i] ^= (((row_num - i) * 173 + 105) & 0xFF); + } + + assert(stride % (FG_BLOCK_SIZE * sizeof(pixel)) == 0); + + int offsets[2 /* col offset */][2 /* row offset */]; + + // process this row in FG_BLOCK_SIZE^2 blocks (subsampled) + for (unsigned bx = 0; bx < pw; bx += FG_BLOCK_SIZE >> sx) { + const int bw = imin(FG_BLOCK_SIZE >> sx, (int)(pw - bx)); + if (data->overlap_flag && bx) { + // shift previous offsets left + for (int i = 0; i < rows; i++) + offsets[1][i] = offsets[0][i]; + } + + // update current offsets + for (int i = 0; i < rows; i++) + offsets[0][i] = get_random_number(8, &seed[i]); + + // x/y block offsets to compensate for overlapped regions + const int ystart = data->overlap_flag && row_num ? imin(2 >> sy, bh) : 0; + const int xstart = data->overlap_flag && bx ? imin(2 >> sx, bw) : 0; + + static const int w[2 /* sub */][2 /* off */][2] = { + { { 27, 17 }, { 17, 27 } }, + { { 23, 22 } }, + }; + +#define add_noise_uv(x, y, grain) \ + const int lx = (bx + x) << sx; \ + const int ly = y << sy; \ + const pixel *const luma = luma_row + ly * PXSTRIDE(luma_stride) + lx; \ + pixel avg = luma[0]; \ + if (sx) \ + avg = (avg + luma[1] + 1) >> 1; \ + const pixel *const src = src_row + (y) * PXSTRIDE(stride) + (bx + (x)); \ + pixel *const dst = dst_row + (y) * PXSTRIDE(stride) + (bx + (x)); \ + int val = avg; \ + if (!data->chroma_scaling_from_luma) { \ + const int combined = avg * data->uv_luma_mult[uv] + \ + *src * data->uv_mult[uv]; \ + val = iclip_pixel( (combined >> 6) + \ + (data->uv_offset[uv] * (1 << bitdepth_min_8)) ); \ + } \ + const int noise = round2(scaling[ val ] * (grain), data->scaling_shift); \ + *dst = iclip(*src + noise, min_value, max_value); + + for (int y = ystart; y < bh; y++) { + // Non-overlapped image region (straightforward) + for (int x = xstart; x < bw; x++) { + int grain = sample_lut(grain_lut, offsets, sx, sy, 0, 0, x, y); + add_noise_uv(x, y, grain); + } + + // Special case for overlapped column + for (int x = 0; x < xstart; x++) { + int grain = sample_lut(grain_lut, offsets, sx, sy, 0, 0, x, y); + int old = sample_lut(grain_lut, offsets, sx, sy, 1, 0, x, y); + grain = round2(old * w[sx][x][0] + grain * w[sx][x][1], 5); + grain = iclip(grain, grain_min, grain_max); + add_noise_uv(x, y, grain); + } + } + + for (int y = 0; y < ystart; y++) { + // Special case for overlapped row (sans corner) + for (int x = xstart; x < bw; x++) { + int grain = sample_lut(grain_lut, offsets, sx, sy, 0, 0, x, y); + int old = sample_lut(grain_lut, offsets, sx, sy, 0, 1, x, y); + grain = round2(old * w[sy][y][0] + grain * w[sy][y][1], 5); + grain = iclip(grain, grain_min, grain_max); + add_noise_uv(x, y, grain); + } + + // Special case for doubly-overlapped corner + for (int x = 0; x < xstart; x++) { + // Blend the top pixel with the top left block + int top = sample_lut(grain_lut, offsets, sx, sy, 0, 1, x, y); + int old = sample_lut(grain_lut, offsets, sx, sy, 1, 1, x, y); + top = round2(old * w[sx][x][0] + top * w[sx][x][1], 5); + top = iclip(top, grain_min, grain_max); + + // Blend the current pixel with the left block + int grain = sample_lut(grain_lut, offsets, sx, sy, 0, 0, x, y); + old = sample_lut(grain_lut, offsets, sx, sy, 1, 0, x, y); + grain = round2(old * w[sx][x][0] + grain * w[sx][x][1], 5); + grain = iclip(grain, grain_min, grain_max); + + // Mix the row rows together and apply to image + grain = round2(top * w[sy][y][0] + grain * w[sy][y][1], 5); + grain = iclip(grain, grain_min, grain_max); + add_noise_uv(x, y, grain); + } + } + } +} + +#define fguv_ss_fn(nm, ss_x, ss_y) \ +static decl_fguv_32x32xn_fn(fguv_32x32xn_##nm##_c) { \ + fguv_32x32xn_c(dst_row, src_row, stride, data, pw, scaling, grain_lut, bh, \ + row_num, luma_row, luma_stride, uv_pl, is_id, ss_x, ss_y \ + HIGHBD_TAIL_SUFFIX); \ +} + +fguv_ss_fn(420, 1, 1); +fguv_ss_fn(422, 1, 0); +fguv_ss_fn(444, 0, 0); + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM +#include "src/arm/filmgrain.h" +#elif ARCH_X86 +#include "src/x86/filmgrain.h" +#endif +#endif + +COLD void bitfn(dav1d_film_grain_dsp_init)(Dav1dFilmGrainDSPContext *const c) { + c->generate_grain_y = generate_grain_y_c; + c->generate_grain_uv[DAV1D_PIXEL_LAYOUT_I420 - 1] = generate_grain_uv_420_c; + c->generate_grain_uv[DAV1D_PIXEL_LAYOUT_I422 - 1] = generate_grain_uv_422_c; + c->generate_grain_uv[DAV1D_PIXEL_LAYOUT_I444 - 1] = generate_grain_uv_444_c; + + c->fgy_32x32xn = fgy_32x32xn_c; + c->fguv_32x32xn[DAV1D_PIXEL_LAYOUT_I420 - 1] = fguv_32x32xn_420_c; + c->fguv_32x32xn[DAV1D_PIXEL_LAYOUT_I422 - 1] = fguv_32x32xn_422_c; + c->fguv_32x32xn[DAV1D_PIXEL_LAYOUT_I444 - 1] = fguv_32x32xn_444_c; + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM + film_grain_dsp_init_arm(c); +#elif ARCH_X86 + film_grain_dsp_init_x86(c); +#endif +#endif +} diff --git a/3rdparty/dav1d/src/getbits.c b/3rdparty/dav1d/src/getbits.c new file mode 100644 index 0000000..13e7783 --- /dev/null +++ b/3rdparty/dav1d/src/getbits.c @@ -0,0 +1,164 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/intops.h" + +#include "src/getbits.h" + +void dav1d_init_get_bits(GetBits *const c, const uint8_t *const data, + const size_t sz) +{ + assert(sz); + c->ptr = c->ptr_start = data; + c->ptr_end = &c->ptr_start[sz]; + c->state = 0; + c->bits_left = 0; + c->error = 0; +} + +unsigned dav1d_get_bit(GetBits *const c) { + if (!c->bits_left) { + if (c->ptr >= c->ptr_end) { + c->error = 1; + } else { + const unsigned state = *c->ptr++; + c->bits_left = 7; + c->state = (uint64_t) state << 57; + return state >> 7; + } + } + + const uint64_t state = c->state; + c->bits_left--; + c->state = state << 1; + return (unsigned) (state >> 63); +} + +static inline void refill(GetBits *const c, const int n) { + assert(c->bits_left >= 0 && c->bits_left < 32); + unsigned state = 0; + do { + if (c->ptr >= c->ptr_end) { + c->error = 1; + if (state) break; + return; + } + state = (state << 8) | *c->ptr++; + c->bits_left += 8; + } while (n > c->bits_left); + c->state |= (uint64_t) state << (64 - c->bits_left); +} + +#define GET_BITS(name, type, type64) \ +type name(GetBits *const c, const int n) { \ + assert(n > 0 && n <= 32); \ + /* Unsigned cast avoids refill after eob */ \ + if ((unsigned) n > (unsigned) c->bits_left) \ + refill(c, n); \ + const uint64_t state = c->state; \ + c->bits_left -= n; \ + c->state = state << n; \ + return (type) ((type64) state >> (64 - n)); \ +} + +GET_BITS(dav1d_get_bits, unsigned, uint64_t) +GET_BITS(dav1d_get_sbits, int, int64_t) + +unsigned dav1d_get_uleb128(GetBits *const c) { + uint64_t val = 0; + unsigned i = 0, more; + + do { + const int v = dav1d_get_bits(c, 8); + more = v & 0x80; + val |= ((uint64_t) (v & 0x7F)) << i; + i += 7; + } while (more && i < 56); + + if (val > UINT32_MAX || more) { + c->error = 1; + return 0; + } + + return (unsigned) val; +} + +unsigned dav1d_get_uniform(GetBits *const c, const unsigned max) { + // Output in range [0..max-1] + // max must be > 1, or else nothing is read from the bitstream + assert(max > 1); + const int l = ulog2(max) + 1; + assert(l > 1); + const unsigned m = (1U << l) - max; + const unsigned v = dav1d_get_bits(c, l - 1); + return v < m ? v : (v << 1) - m + dav1d_get_bit(c); +} + +unsigned dav1d_get_vlc(GetBits *const c) { + if (dav1d_get_bit(c)) + return 0; + + int n_bits = 0; + do { + if (++n_bits == 32) + return UINT32_MAX; + } while (!dav1d_get_bit(c)); + + return ((1U << n_bits) - 1) + dav1d_get_bits(c, n_bits); +} + +static unsigned get_bits_subexp_u(GetBits *const c, const unsigned ref, + const unsigned n) +{ + unsigned v = 0; + + for (int i = 0;; i++) { + const int b = i ? 3 + i - 1 : 3; + + if (n < v + 3 * (1 << b)) { + v += dav1d_get_uniform(c, n - v + 1); + break; + } + + if (!dav1d_get_bit(c)) { + v += dav1d_get_bits(c, b); + break; + } + + v += 1 << b; + } + + return ref * 2 <= n ? inv_recenter(ref, v) : n - inv_recenter(n - ref, v); +} + +int dav1d_get_bits_subexp(GetBits *const c, const int ref, const unsigned n) { + return (int) get_bits_subexp_u(c, ref + (1 << n), 2 << n) - (1 << n); +} diff --git a/3rdparty/dav1d/src/getbits.h b/3rdparty/dav1d/src/getbits.h new file mode 100644 index 0000000..6792594 --- /dev/null +++ b/3rdparty/dav1d/src/getbits.h @@ -0,0 +1,71 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_GETBITS_H +#define DAV1D_SRC_GETBITS_H + +#include +#include + +typedef struct GetBits { + uint64_t state; + int bits_left, error; + const uint8_t *ptr, *ptr_start, *ptr_end; +} GetBits; + +void dav1d_init_get_bits(GetBits *c, const uint8_t *data, size_t sz); +unsigned dav1d_get_bit(GetBits *c); +unsigned dav1d_get_bits(GetBits *c, int n); +int dav1d_get_sbits(GetBits *c, int n); +unsigned dav1d_get_uleb128(GetBits *c); + +// Output in range 0..max-1 +unsigned dav1d_get_uniform(GetBits *c, unsigned max); +unsigned dav1d_get_vlc(GetBits *c); +int dav1d_get_bits_subexp(GetBits *c, int ref, unsigned n); + +// Discard bits from the buffer until we're next byte-aligned. +static inline void dav1d_bytealign_get_bits(GetBits *c) { + // bits_left is never more than 7, because it is only incremented + // by refill(), called by dav1d_get_bits and that never reads more + // than 7 bits more than it needs. + // + // If this wasn't true, we would need to work out how many bits to + // discard (bits_left % 8), subtract that from bits_left and then + // shift state right by that amount. + assert(c->bits_left <= 7); + + c->bits_left = 0; + c->state = 0; +} + +// Return the current bit position relative to the start of the buffer. +static inline unsigned dav1d_get_bits_pos(const GetBits *c) { + return (unsigned) (c->ptr - c->ptr_start) * 8 - c->bits_left; +} + +#endif /* DAV1D_SRC_GETBITS_H */ diff --git a/3rdparty/dav1d/src/internal.h b/3rdparty/dav1d/src/internal.h new file mode 100644 index 0000000..b1770ed --- /dev/null +++ b/3rdparty/dav1d/src/internal.h @@ -0,0 +1,471 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_INTERNAL_H +#define DAV1D_SRC_INTERNAL_H + +#include + +#include "dav1d/data.h" + +typedef struct Dav1dFrameContext Dav1dFrameContext; +typedef struct Dav1dTileState Dav1dTileState; +typedef struct Dav1dTaskContext Dav1dTaskContext; +typedef struct Dav1dTask Dav1dTask; + +#include "common/attributes.h" + +#include "src/cdef.h" +#include "src/cdf.h" +#include "src/data.h" +#include "src/env.h" +#include "src/filmgrain.h" +#include "src/intra_edge.h" +#include "src/ipred.h" +#include "src/itx.h" +#include "src/levels.h" +#include "src/lf_mask.h" +#include "src/loopfilter.h" +#include "src/looprestoration.h" +#include "src/mc.h" +#include "src/msac.h" +#include "src/pal.h" +#include "src/picture.h" +#include "src/recon.h" +#include "src/refmvs.h" +#include "src/thread.h" + +typedef struct Dav1dDSPContext { + Dav1dFilmGrainDSPContext fg; + Dav1dIntraPredDSPContext ipred; + Dav1dMCDSPContext mc; + Dav1dInvTxfmDSPContext itx; + Dav1dLoopFilterDSPContext lf; + Dav1dCdefDSPContext cdef; + Dav1dLoopRestorationDSPContext lr; +} Dav1dDSPContext; + +struct Dav1dTileGroup { + Dav1dData data; + int start, end; +}; + +enum TaskType { + DAV1D_TASK_TYPE_INIT, + DAV1D_TASK_TYPE_INIT_CDF, + DAV1D_TASK_TYPE_TILE_ENTROPY, + DAV1D_TASK_TYPE_ENTROPY_PROGRESS, + DAV1D_TASK_TYPE_TILE_RECONSTRUCTION, + DAV1D_TASK_TYPE_DEBLOCK_COLS, + DAV1D_TASK_TYPE_DEBLOCK_ROWS, + DAV1D_TASK_TYPE_CDEF, + DAV1D_TASK_TYPE_SUPER_RESOLUTION, + DAV1D_TASK_TYPE_LOOP_RESTORATION, + DAV1D_TASK_TYPE_RECONSTRUCTION_PROGRESS, + DAV1D_TASK_TYPE_FG_PREP, + DAV1D_TASK_TYPE_FG_APPLY, +}; + +struct Dav1dContext { + Dav1dFrameContext *fc; + unsigned n_fc; + + Dav1dTaskContext *tc; + unsigned n_tc; + + // cache of OBUs that make up a single frame before we submit them + // to a frame worker to be decoded + struct Dav1dTileGroup *tile; + int n_tile_data_alloc; + int n_tile_data; + int n_tiles; + Dav1dMemPool *seq_hdr_pool; + Dav1dRef *seq_hdr_ref; + Dav1dSequenceHeader *seq_hdr; + Dav1dMemPool *frame_hdr_pool; + Dav1dRef *frame_hdr_ref; + Dav1dFrameHeader *frame_hdr; + + Dav1dRef *content_light_ref; + Dav1dContentLightLevel *content_light; + Dav1dRef *mastering_display_ref; + Dav1dMasteringDisplay *mastering_display; + Dav1dRef *itut_t35_ref; + Dav1dITUTT35 *itut_t35; + int n_itut_t35; + + // decoded output picture queue + Dav1dData in; + Dav1dThreadPicture out, cache; + // dummy is a pointer to prevent compiler errors about atomic_load() + // not taking const arguments + atomic_int flush_mem, *flush; + struct { + Dav1dThreadPicture *out_delayed; + unsigned next; + } frame_thread; + + // task threading (refer to tc[] for per_thread thingies) + struct TaskThreadData { + pthread_mutex_t lock; + pthread_cond_t cond; + atomic_uint first; + unsigned cur; + // This is used for delayed reset of the task cur pointer when + // such operation is needed but the thread doesn't enter a critical + // section (typically when executing the next sbrow task locklessly). + // See src/thread_task.c:reset_task_cur(). + atomic_uint reset_task_cur; + atomic_int cond_signaled; + struct { + int exec, finished; + pthread_cond_t cond; + const Dav1dPicture *in; + Dav1dPicture *out; + enum TaskType type; + atomic_int progress[2]; /* [0]=started, [1]=completed */ + union { + struct { + ALIGN(int8_t grain_lut_8bpc[3][GRAIN_HEIGHT + 1][GRAIN_WIDTH], 16); + ALIGN(uint8_t scaling_8bpc[3][256], 64); + }; + struct { + ALIGN(int16_t grain_lut_16bpc[3][GRAIN_HEIGHT + 1][GRAIN_WIDTH], 16); + ALIGN(uint8_t scaling_16bpc[3][4096], 64); + }; + }; + } delayed_fg; + int inited; + } task_thread; + + // reference/entropy state + Dav1dMemPool *segmap_pool; + Dav1dMemPool *refmvs_pool; + struct { + Dav1dThreadPicture p; + Dav1dRef *segmap; + Dav1dRef *refmvs; + uint8_t refpoc[7]; + } refs[8]; + Dav1dMemPool *cdf_pool; + CdfThreadContext cdf[8]; + + Dav1dDSPContext dsp[3 /* 8, 10, 12 bits/component */]; + Dav1dPalDSPContext pal_dsp; + Dav1dRefmvsDSPContext refmvs_dsp; + + Dav1dPicAllocator allocator; + int apply_grain; + int operating_point; + unsigned operating_point_idc; + int all_layers; + int max_spatial_id; + unsigned frame_size_limit; + int strict_std_compliance; + int output_invisible_frames; + enum Dav1dInloopFilterType inloop_filters; + enum Dav1dDecodeFrameType decode_frame_type; + int drain; + enum PictureFlags frame_flags; + enum Dav1dEventFlags event_flags; + Dav1dDataProps cached_error_props; + int cached_error; + + Dav1dLogger logger; + + Dav1dMemPool *picture_pool; + Dav1dMemPool *pic_ctx_pool; +}; + +struct Dav1dTask { + unsigned frame_idx; // frame thread id + enum TaskType type; // task work + int sby; // sbrow + + // task dependencies + int recon_progress, deblock_progress; + int deps_skip; + struct Dav1dTask *next; // only used in task queue +}; + +struct Dav1dFrameContext { + Dav1dRef *seq_hdr_ref; + Dav1dSequenceHeader *seq_hdr; + Dav1dRef *frame_hdr_ref; + Dav1dFrameHeader *frame_hdr; + Dav1dThreadPicture refp[7]; + Dav1dPicture cur; // during block coding / reconstruction + Dav1dThreadPicture sr_cur; // after super-resolution upscaling + Dav1dRef *mvs_ref; + refmvs_temporal_block *mvs, *ref_mvs[7]; + Dav1dRef *ref_mvs_ref[7]; + Dav1dRef *cur_segmap_ref, *prev_segmap_ref; + uint8_t *cur_segmap; + const uint8_t *prev_segmap; + uint8_t refpoc[7], refrefpoc[7][7]; + uint8_t gmv_warp_allowed[7]; + CdfThreadContext in_cdf, out_cdf; + struct Dav1dTileGroup *tile; + int n_tile_data_alloc; + int n_tile_data; + + // for scalable references + struct ScalableMotionParams { + int scale; // if no scaling, this is 0 + int step; + } svc[7][2 /* x, y */]; + int resize_step[2 /* y, uv */], resize_start[2 /* y, uv */]; + + const Dav1dContext *c; + Dav1dTileState *ts; + int n_ts; + const Dav1dDSPContext *dsp; + struct { + recon_b_intra_fn recon_b_intra; + recon_b_inter_fn recon_b_inter; + filter_sbrow_fn filter_sbrow; + filter_sbrow_fn filter_sbrow_deblock_cols; + filter_sbrow_fn filter_sbrow_deblock_rows; + void (*filter_sbrow_cdef)(Dav1dTaskContext *tc, int sby); + filter_sbrow_fn filter_sbrow_resize; + filter_sbrow_fn filter_sbrow_lr; + backup_ipred_edge_fn backup_ipred_edge; + read_coef_blocks_fn read_coef_blocks; + copy_pal_block_fn copy_pal_block_y; + copy_pal_block_fn copy_pal_block_uv; + read_pal_plane_fn read_pal_plane; + read_pal_uv_fn read_pal_uv; + } bd_fn; + + int ipred_edge_sz; + pixel *ipred_edge[3]; + ptrdiff_t b4_stride; + int w4, h4, bw, bh, sb128w, sb128h, sbh, sb_shift, sb_step, sr_sb128w; + uint16_t dq[DAV1D_MAX_SEGMENTS][3 /* plane */][2 /* dc/ac */]; + const uint8_t *qm[N_RECT_TX_SIZES][3 /* plane */]; + BlockContext *a; + int a_sz /* w*tile_rows */; + refmvs_frame rf; + uint8_t jnt_weights[7][7]; + int bitdepth_max; + + struct { + int next_tile_row[2 /* 0: reconstruction, 1: entropy */]; + atomic_int entropy_progress; + atomic_int deblock_progress; // in sby units + atomic_uint *frame_progress, *copy_lpf_progress; + // indexed using t->by * f->b4_stride + t->bx + Av1Block *b; + int16_t *cbi; /* bits 0-4: txtp, bits 5-15: eob */ + // indexed using (t->by >> 1) * (f->b4_stride >> 1) + (t->bx >> 1) + pixel (*pal)[3 /* plane */][8 /* idx */]; + // iterated over inside tile state + uint8_t *pal_idx; + coef *cf; + int prog_sz; + int cbi_sz, pal_sz, pal_idx_sz, cf_sz; + // start offsets per tile + unsigned *tile_start_off; + } frame_thread; + + // loopfilter + struct { + uint8_t (*level)[4]; + Av1Filter *mask; + Av1Restoration *lr_mask; + int mask_sz /* w*h */, lr_mask_sz; + int cdef_buf_plane_sz[2]; /* stride*sbh*4 */ + int cdef_buf_sbh; + int lr_buf_plane_sz[2]; /* (stride*sbh*4) << sb128 if n_tc > 1, else stride*4 */ + int re_sz /* h */; + Av1FilterLUT lim_lut; + ALIGN(uint8_t lvl[8 /* seg_id */][4 /* dir */][8 /* ref */][2 /* is_gmv */], 16); + int last_sharpness; + uint8_t *tx_lpf_right_edge[2]; + uint8_t *cdef_line_buf, *lr_line_buf; + pixel *cdef_line[2 /* pre, post */][3 /* plane */]; + pixel *cdef_lpf_line[3 /* plane */]; + pixel *lr_lpf_line[3 /* plane */]; + + // in-loop filter per-frame state keeping + uint8_t *start_of_tile_row; + int start_of_tile_row_sz; + int need_cdef_lpf_copy; + pixel *p[3], *sr_p[3]; + int restore_planes; // enum LrRestorePlanes + } lf; + + struct { + pthread_mutex_t lock; + pthread_cond_t cond; + struct TaskThreadData *ttd; + struct Dav1dTask *tasks, *tile_tasks[2], init_task; + int num_tasks, num_tile_tasks; + atomic_int init_done; + atomic_int done[2]; + int retval; + int update_set; // whether we need to update CDF reference + atomic_int error; + atomic_int task_counter; + struct Dav1dTask *task_head, *task_tail; + // Points to the task directly before the cur pointer in the queue. + // This cur pointer is theoretical here, we actually keep track of the + // "prev_t" variable. This is needed to not loose the tasks in + // [head;cur-1] when picking one for execution. + struct Dav1dTask *task_cur_prev; + struct { // async task insertion + atomic_int merge; + pthread_mutex_t lock; + Dav1dTask *head, *tail; + } pending_tasks; + } task_thread; + + // threading (refer to tc[] for per-thread things) + struct FrameTileThreadData { + int (*lowest_pixel_mem)[7][2]; + int lowest_pixel_mem_sz; + } tile_thread; +}; + +struct Dav1dTileState { + CdfContext cdf; + MsacContext msac; + + struct { + int col_start, col_end, row_start, row_end; // in 4px units + int col, row; // in tile units + } tiling; + + // in sby units, TILE_ERROR after a decoding error + atomic_int progress[2 /* 0: reconstruction, 1: entropy */]; + struct { + uint8_t *pal_idx; + int16_t *cbi; + coef *cf; + } frame_thread[2 /* 0: reconstruction, 1: entropy */]; + + // in fullpel units, [0] = Y, [1] = UV, used for progress requirements + // each entry is one tile-sbrow; middle index is refidx + int (*lowest_pixel)[7][2]; + + uint16_t dqmem[DAV1D_MAX_SEGMENTS][3 /* plane */][2 /* dc/ac */]; + const uint16_t (*dq)[3][2]; + int last_qidx; + + union { + int8_t i8[4]; + uint32_t u32; + } last_delta_lf; + ALIGN(uint8_t lflvlmem[8 /* seg_id */][4 /* dir */][8 /* ref */][2 /* is_gmv */], 16); + const uint8_t (*lflvl)[4][8][2]; + + Av1RestorationUnit *lr_ref[3]; +}; + +struct Dav1dTaskContext { + const Dav1dContext *c; + const Dav1dFrameContext *f; + Dav1dTileState *ts; + int bx, by; + BlockContext l, *a; + refmvs_tile rt; + ALIGN(union, 64) { + int16_t cf_8bpc [32 * 32]; + int32_t cf_16bpc[32 * 32]; + }; + union { + uint8_t al_pal_8bpc [2 /* a/l */][32 /* bx/y4 */][3 /* plane */][8 /* palette_idx */]; + uint16_t al_pal_16bpc[2 /* a/l */][32 /* bx/y4 */][3 /* plane */][8 /* palette_idx */]; + }; + uint8_t pal_sz_uv[2 /* a/l */][32 /* bx4/by4 */]; + ALIGN(union, 64) { + struct { + union { + uint8_t lap_8bpc [128 * 32]; + uint16_t lap_16bpc[128 * 32]; + struct { + int16_t compinter[2][128 * 128]; + uint8_t seg_mask[128 * 128]; + }; + }; + union { + // stride=192 for non-SVC, or 320 for SVC + uint8_t emu_edge_8bpc [320 * (256 + 7)]; + uint16_t emu_edge_16bpc[320 * (256 + 7)]; + }; + }; + struct { + union { + uint8_t levels[32 * 34]; + struct { + uint8_t pal_order[64][8]; + uint8_t pal_ctx[64]; + }; + }; + union { + int16_t ac[32 * 32]; // intra-only + uint8_t txtp_map[32 * 32]; // inter-only + }; + uint8_t pal_idx_y[32 * 64]; + uint8_t pal_idx_uv[64 * 64]; /* also used as pre-pack scratch buffer */ + union { + struct { + uint8_t interintra_8bpc[64 * 64]; + uint8_t edge_8bpc[257]; + ALIGN(uint8_t pal_8bpc[3 /* plane */][8 /* palette_idx */], 8); + }; + struct { + uint16_t interintra_16bpc[64 * 64]; + uint16_t edge_16bpc[257]; + ALIGN(uint16_t pal_16bpc[3 /* plane */][8 /* palette_idx */], 16); + }; + }; + }; + } scratch; + + Dav1dWarpedMotionParams warpmv; + Av1Filter *lf_mask; + int top_pre_cdef_toggle; + int8_t *cur_sb_cdef_idx_ptr; + // for chroma sub8x8, we need to know the filter for all 4 subblocks in + // a 4x4 area, but the top/left one can go out of cache already, so this + // keeps it accessible + enum Filter2d tl_4x4_filter; + + struct { + int pass; + } frame_thread; + struct { + struct thread_data td; + struct TaskThreadData *ttd; + struct FrameTileThreadData *fttd; + int flushed; + int die; + } task_thread; +}; + +#endif /* DAV1D_SRC_INTERNAL_H */ diff --git a/3rdparty/dav1d/src/intra_edge.c b/3rdparty/dav1d/src/intra_edge.c new file mode 100644 index 0000000..e9261e6 --- /dev/null +++ b/3rdparty/dav1d/src/intra_edge.c @@ -0,0 +1,148 @@ +/* + * Copyright © 2018-2023, VideoLAN and dav1d authors + * Copyright © 2018-2023, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/attributes.h" + +#include "src/intra_edge.h" +#include "src/levels.h" + +struct ModeSelMem { + EdgeBranch *nwc[3 /* 64x64, 32x32, 16x16 */]; + EdgeTip *nt; +}; + +/* Because we're using 16-bit offsets to refer to other nodes those arrays + * are placed in a struct to ensure they're consecutive in memory. */ +static struct { + EdgeBranch branch_sb128[1 + 4 + 16 + 64]; + EdgeTip tip_sb128[256]; + EdgeBranch branch_sb64[1 + 4 + 16]; + EdgeTip tip_sb64[64]; +} ALIGN(nodes, 16); + +const EdgeNode *dav1d_intra_edge_tree[2] = { + (EdgeNode*)nodes.branch_sb128, (EdgeNode*)nodes.branch_sb64 +}; + +static COLD void init_edges(EdgeNode *const node, + const enum BlockLevel bl, + const enum EdgeFlags edge_flags) +{ + node->o = edge_flags; + node->h[0] = edge_flags | EDGE_ALL_LEFT_HAS_BOTTOM; + node->v[0] = edge_flags | EDGE_ALL_TOP_HAS_RIGHT; + + if (bl == BL_8X8) { + EdgeTip *const nt = (EdgeTip *) node; + + node->h[1] = edge_flags & (EDGE_ALL_LEFT_HAS_BOTTOM | + EDGE_I420_TOP_HAS_RIGHT); + node->v[1] = edge_flags & (EDGE_ALL_TOP_HAS_RIGHT | + EDGE_I420_LEFT_HAS_BOTTOM | + EDGE_I422_LEFT_HAS_BOTTOM); + + nt->split[0] = (edge_flags & EDGE_ALL_TOP_HAS_RIGHT) | + EDGE_I422_LEFT_HAS_BOTTOM; + nt->split[1] = edge_flags | EDGE_I444_TOP_HAS_RIGHT; + nt->split[2] = edge_flags & (EDGE_I420_TOP_HAS_RIGHT | + EDGE_I420_LEFT_HAS_BOTTOM | + EDGE_I422_LEFT_HAS_BOTTOM); + } else { + EdgeBranch *const nwc = (EdgeBranch *) node; + + node->h[1] = edge_flags & EDGE_ALL_LEFT_HAS_BOTTOM; + node->v[1] = edge_flags & EDGE_ALL_TOP_HAS_RIGHT; + + nwc->h4 = EDGE_ALL_LEFT_HAS_BOTTOM; + nwc->v4 = EDGE_ALL_TOP_HAS_RIGHT; + if (bl == BL_16X16) { + nwc->h4 |= edge_flags & EDGE_I420_TOP_HAS_RIGHT; + nwc->v4 |= edge_flags & (EDGE_I420_LEFT_HAS_BOTTOM | + EDGE_I422_LEFT_HAS_BOTTOM); + } + } +} + +#define PTR_OFFSET(a, b) ((uint16_t)((uintptr_t)(b) - (uintptr_t)(a))) + +static COLD void init_mode_node(EdgeBranch *const nwc, + const enum BlockLevel bl, + struct ModeSelMem *const mem, + const int top_has_right, + const int left_has_bottom) +{ + init_edges(&nwc->node, bl, + (top_has_right ? EDGE_ALL_TOP_HAS_RIGHT : 0) | + (left_has_bottom ? EDGE_ALL_LEFT_HAS_BOTTOM : 0)); + if (bl == BL_16X16) { + for (int n = 0; n < 4; n++) { + EdgeTip *const nt = mem->nt++; + nwc->split_offset[n] = PTR_OFFSET(nwc, nt); + init_edges(&nt->node, bl + 1, + ((n == 3 || (n == 1 && !top_has_right)) ? 0 : + EDGE_ALL_TOP_HAS_RIGHT) | + (!(n == 0 || (n == 2 && left_has_bottom)) ? 0 : + EDGE_ALL_LEFT_HAS_BOTTOM)); + } + } else { + for (int n = 0; n < 4; n++) { + EdgeBranch *const nwc_child = mem->nwc[bl]++; + nwc->split_offset[n] = PTR_OFFSET(nwc, nwc_child); + init_mode_node(nwc_child, bl + 1, mem, + !(n == 3 || (n == 1 && !top_has_right)), + n == 0 || (n == 2 && left_has_bottom)); + } + } +} + +COLD void dav1d_init_intra_edge_tree(void) { + // This function is guaranteed to be called only once + struct ModeSelMem mem; + + mem.nwc[BL_128X128] = &nodes.branch_sb128[1]; + mem.nwc[BL_64X64] = &nodes.branch_sb128[1 + 4]; + mem.nwc[BL_32X32] = &nodes.branch_sb128[1 + 4 + 16]; + mem.nt = nodes.tip_sb128; + init_mode_node(nodes.branch_sb128, BL_128X128, &mem, 1, 0); + assert(mem.nwc[BL_128X128] == &nodes.branch_sb128[1 + 4]); + assert(mem.nwc[BL_64X64] == &nodes.branch_sb128[1 + 4 + 16]); + assert(mem.nwc[BL_32X32] == &nodes.branch_sb128[1 + 4 + 16 + 64]); + assert(mem.nt == &nodes.tip_sb128[256]); + + mem.nwc[BL_128X128] = NULL; + mem.nwc[BL_64X64] = &nodes.branch_sb64[1]; + mem.nwc[BL_32X32] = &nodes.branch_sb64[1 + 4]; + mem.nt = nodes.tip_sb64; + init_mode_node(nodes.branch_sb64, BL_64X64, &mem, 1, 0); + assert(mem.nwc[BL_64X64] == &nodes.branch_sb64[1 + 4]); + assert(mem.nwc[BL_32X32] == &nodes.branch_sb64[1 + 4 + 16]); + assert(mem.nt == &nodes.tip_sb64[64]); +} diff --git a/3rdparty/dav1d/src/intra_edge.h b/3rdparty/dav1d/src/intra_edge.h new file mode 100644 index 0000000..ecfb3de --- /dev/null +++ b/3rdparty/dav1d/src/intra_edge.h @@ -0,0 +1,73 @@ +/* + * Copyright © 2018-2023, VideoLAN and dav1d authors + * Copyright © 2018-2023, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_INTRA_EDGE_H +#define DAV1D_SRC_INTRA_EDGE_H + +#include + +enum EdgeFlags { + EDGE_I444_TOP_HAS_RIGHT = 1 << 0, + EDGE_I422_TOP_HAS_RIGHT = 1 << 1, + EDGE_I420_TOP_HAS_RIGHT = 1 << 2, + EDGE_I444_LEFT_HAS_BOTTOM = 1 << 3, + EDGE_I422_LEFT_HAS_BOTTOM = 1 << 4, + EDGE_I420_LEFT_HAS_BOTTOM = 1 << 5, + EDGE_ALL_TOP_HAS_RIGHT = EDGE_I444_TOP_HAS_RIGHT | + EDGE_I422_TOP_HAS_RIGHT | + EDGE_I420_TOP_HAS_RIGHT, + EDGE_ALL_LEFT_HAS_BOTTOM = EDGE_I444_LEFT_HAS_BOTTOM | + EDGE_I422_LEFT_HAS_BOTTOM | + EDGE_I420_LEFT_HAS_BOTTOM, + EDGE_ALL_TR_AND_BL = EDGE_ALL_TOP_HAS_RIGHT | + EDGE_ALL_LEFT_HAS_BOTTOM, +}; + +#define INTRA_EDGE_SPLIT(n, i) \ + ((const EdgeNode*)((uintptr_t)(n) + ((const EdgeBranch*)(n))->split_offset[i])) + +typedef struct EdgeNode { + uint8_t /* enum EdgeFlags */ o, h[2], v[2]; +} EdgeNode; + +typedef struct EdgeTip { + EdgeNode node; + uint8_t /* enum EdgeFlags */ split[3]; +} EdgeTip; + +typedef struct EdgeBranch { + EdgeNode node; + uint8_t /* enum EdgeFlags */ h4, v4; + uint16_t split_offset[4]; /* relative to the address of this node */ +} EdgeBranch; + +/* Tree to keep track of which edges are available. */ +EXTERN const EdgeNode *dav1d_intra_edge_tree[2 /* BL_128X128, BL_64X64 */]; + +void dav1d_init_intra_edge_tree(void); + +#endif /* DAV1D_SRC_INTRA_EDGE_H */ diff --git a/3rdparty/dav1d/src/ipred.h b/3rdparty/dav1d/src/ipred.h new file mode 100644 index 0000000..35adb02 --- /dev/null +++ b/3rdparty/dav1d/src/ipred.h @@ -0,0 +1,94 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_IPRED_H +#define DAV1D_SRC_IPRED_H + +#include + +#include "common/bitdepth.h" + +#include "src/levels.h" + +/* + * Intra prediction. + * - a is the angle (in degrees) for directional intra predictors. For other + * modes, it is ignored; + * - topleft is the same as the argument given to dav1d_prepare_intra_edges(), + * see ipred_prepare.h for more detailed documentation. + */ +#define decl_angular_ipred_fn(name) \ +void (name)(pixel *dst, ptrdiff_t stride, const pixel *topleft, \ + int width, int height, int angle, int max_width, int max_height \ + HIGHBD_DECL_SUFFIX) +typedef decl_angular_ipred_fn(*angular_ipred_fn); + +/* + * Create a subsampled Y plane with the DC subtracted. + * - w/h_pad is the edge of the width/height that extends outside the visible + * portion of the frame in 4px units; + * - ac has a stride of 16. + */ +#define decl_cfl_ac_fn(name) \ +void (name)(int16_t *ac, const pixel *y, ptrdiff_t stride, \ + int w_pad, int h_pad, int cw, int ch) +typedef decl_cfl_ac_fn(*cfl_ac_fn); + +/* + * dst[x,y] += alpha * ac[x,y] + * - alpha contains a q3 scalar in [-16,16] range; + */ +#define decl_cfl_pred_fn(name) \ +void (name)(pixel *dst, ptrdiff_t stride, const pixel *topleft, \ + int width, int height, const int16_t *ac, int alpha \ + HIGHBD_DECL_SUFFIX) +typedef decl_cfl_pred_fn(*cfl_pred_fn); + +/* + * dst[x,y] = pal[idx[x,y]] + * - palette indices are [0-7] + * - only 16-byte alignment is guaranteed for idx. + */ +#define decl_pal_pred_fn(name) \ +void (name)(pixel *dst, ptrdiff_t stride, const pixel *pal, \ + const uint8_t *idx, int w, int h) +typedef decl_pal_pred_fn(*pal_pred_fn); + +typedef struct Dav1dIntraPredDSPContext { + angular_ipred_fn intra_pred[N_IMPL_INTRA_PRED_MODES]; + + // chroma-from-luma + cfl_ac_fn cfl_ac[3 /* 420, 422, 444 */]; + cfl_pred_fn cfl_pred[DC_128_PRED + 1]; + + // palette + pal_pred_fn pal_pred; +} Dav1dIntraPredDSPContext; + +bitfn_decls(void dav1d_intra_pred_dsp_init, Dav1dIntraPredDSPContext *c); + +#endif /* DAV1D_SRC_IPRED_H */ diff --git a/3rdparty/dav1d/src/ipred_prepare.h b/3rdparty/dav1d/src/ipred_prepare.h new file mode 100644 index 0000000..6a7efeb --- /dev/null +++ b/3rdparty/dav1d/src/ipred_prepare.h @@ -0,0 +1,108 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_IPRED_PREPARE_H +#define DAV1D_SRC_IPRED_PREPARE_H + +#include +#include + +#include "common/bitdepth.h" + +#include "src/env.h" +#include "src/intra_edge.h" +#include "src/levels.h" + +/* + * Luma intra edge preparation. + * + * x/y/start/w/h are in luma block (4px) units: + * - x and y are the absolute block positions in the image; + * - start/w/h are the *dependent tile* boundary positions. In practice, start + * is the horizontal tile start, w is the horizontal tile end, the vertical + * tile start is assumed to be 0 and h is the vertical image end. + * + * edge_flags signals which edges are available for this transform-block inside + * the given partition, as well as for the partition inside the superblock + * structure. + * + * dst and stride are pointers to the top/left position of the current block, + * and can be used to locate the top, left, top/left, top/right and bottom/left + * edge pointers also. + * + * angle is the angle_delta [-3..3] on input, and the absolute angle on output. + * + * mode is the intra prediction mode as coded in the bitstream. The return value + * is this same mode, converted to an index in the DSP functions. + * + * tw/th are the size of the transform block in block (4px) units. + * + * topleft_out is a pointer to scratch memory that will be filled with the edge + * pixels. The memory array should have space to be indexed in the [-2*w,2*w] + * range, in the following order: + * - [0] will be the top/left edge pixel; + * - [1..w] will be the top edge pixels (1 being left-most, w being right-most); + * - [w+1..2*w] will be the top/right edge pixels; + * - [-1..-w] will be the left edge pixels (-1 being top-most, -w being bottom- + * most); + * - [-w-1..-2*w] will be the bottom/left edge pixels. + * Each edge may remain uninitialized if it is not used by the returned mode + * index. If edges are not available (because the edge position is outside the + * tile dimensions or because edge_flags indicates lack of edge availability), + * they will be extended from nearby edges as defined by the av1 spec. + */ +enum IntraPredMode + bytefn(dav1d_prepare_intra_edges)(int x, int have_left, int y, int have_top, + int w, int h, enum EdgeFlags edge_flags, + const pixel *dst, ptrdiff_t stride, + const pixel *prefilter_toplevel_sb_edge, + enum IntraPredMode mode, int *angle, + int tw, int th, int filter_edge, + pixel *topleft_out HIGHBD_DECL_SUFFIX); + +// These flags are OR'd with the angle argument into intra predictors. +// ANGLE_USE_EDGE_FILTER_FLAG signals that edges should be convolved +// with a filter before using them to predict values in a block. +// ANGLE_SMOOTH_EDGE_FLAG means that edges are smooth and should use +// reduced filter strength. +#define ANGLE_USE_EDGE_FILTER_FLAG 1024 +#define ANGLE_SMOOTH_EDGE_FLAG 512 + +static inline int sm_flag(const BlockContext *const b, const int idx) { + if (!b->intra[idx]) return 0; + const enum IntraPredMode m = b->mode[idx]; + return (m == SMOOTH_PRED || m == SMOOTH_H_PRED || + m == SMOOTH_V_PRED) ? ANGLE_SMOOTH_EDGE_FLAG : 0; +} + +static inline int sm_uv_flag(const BlockContext *const b, const int idx) { + const enum IntraPredMode m = b->uvmode[idx]; + return (m == SMOOTH_PRED || m == SMOOTH_H_PRED || + m == SMOOTH_V_PRED) ? ANGLE_SMOOTH_EDGE_FLAG : 0; +} + +#endif /* DAV1D_SRC_IPRED_PREPARE_H */ diff --git a/3rdparty/dav1d/src/ipred_prepare_tmpl.c b/3rdparty/dav1d/src/ipred_prepare_tmpl.c new file mode 100644 index 0000000..0bf9de9 --- /dev/null +++ b/3rdparty/dav1d/src/ipred_prepare_tmpl.c @@ -0,0 +1,204 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include + +#include "common/intops.h" + +#include "src/ipred_prepare.h" + +static const uint8_t av1_mode_conv[N_INTRA_PRED_MODES] + [2 /* have_left */][2 /* have_top */] = +{ + [DC_PRED] = { { DC_128_PRED, TOP_DC_PRED }, + { LEFT_DC_PRED, DC_PRED } }, + [PAETH_PRED] = { { DC_128_PRED, VERT_PRED }, + { HOR_PRED, PAETH_PRED } }, +}; + +static const uint8_t av1_mode_to_angle_map[8] = { + 90, 180, 45, 135, 113, 157, 203, 67 +}; + +static const struct { + uint8_t needs_left:1; + uint8_t needs_top:1; + uint8_t needs_topleft:1; + uint8_t needs_topright:1; + uint8_t needs_bottomleft:1; +} av1_intra_prediction_edges[N_IMPL_INTRA_PRED_MODES] = { + [DC_PRED] = { .needs_top = 1, .needs_left = 1 }, + [VERT_PRED] = { .needs_top = 1 }, + [HOR_PRED] = { .needs_left = 1 }, + [LEFT_DC_PRED] = { .needs_left = 1 }, + [TOP_DC_PRED] = { .needs_top = 1 }, + [DC_128_PRED] = { 0 }, + [Z1_PRED] = { .needs_top = 1, .needs_topright = 1, + .needs_topleft = 1 }, + [Z2_PRED] = { .needs_left = 1, .needs_top = 1, .needs_topleft = 1 }, + [Z3_PRED] = { .needs_left = 1, .needs_bottomleft = 1, + .needs_topleft = 1 }, + [SMOOTH_PRED] = { .needs_left = 1, .needs_top = 1 }, + [SMOOTH_V_PRED] = { .needs_left = 1, .needs_top = 1 }, + [SMOOTH_H_PRED] = { .needs_left = 1, .needs_top = 1 }, + [PAETH_PRED] = { .needs_left = 1, .needs_top = 1, .needs_topleft = 1 }, + [FILTER_PRED] = { .needs_left = 1, .needs_top = 1, .needs_topleft = 1 }, +}; + +enum IntraPredMode +bytefn(dav1d_prepare_intra_edges)(const int x, const int have_left, + const int y, const int have_top, + const int w, const int h, + const enum EdgeFlags edge_flags, + const pixel *const dst, + const ptrdiff_t stride, + const pixel *prefilter_toplevel_sb_edge, + enum IntraPredMode mode, int *const angle, + const int tw, const int th, const int filter_edge, + pixel *const topleft_out HIGHBD_DECL_SUFFIX) +{ + const int bitdepth = bitdepth_from_max(bitdepth_max); + assert(y < h && x < w); + + switch (mode) { + case VERT_PRED: + case HOR_PRED: + case DIAG_DOWN_LEFT_PRED: + case DIAG_DOWN_RIGHT_PRED: + case VERT_RIGHT_PRED: + case HOR_DOWN_PRED: + case HOR_UP_PRED: + case VERT_LEFT_PRED: { + *angle = av1_mode_to_angle_map[mode - VERT_PRED] + 3 * *angle; + + if (*angle <= 90) + mode = *angle < 90 && have_top ? Z1_PRED : VERT_PRED; + else if (*angle < 180) + mode = Z2_PRED; + else + mode = *angle > 180 && have_left ? Z3_PRED : HOR_PRED; + break; + } + case DC_PRED: + case PAETH_PRED: + mode = av1_mode_conv[mode][have_left][have_top]; + break; + default: + break; + } + + const pixel *dst_top; + if (have_top && + (av1_intra_prediction_edges[mode].needs_top || + av1_intra_prediction_edges[mode].needs_topleft || + (av1_intra_prediction_edges[mode].needs_left && !have_left))) + { + if (prefilter_toplevel_sb_edge) { + dst_top = &prefilter_toplevel_sb_edge[x * 4]; + } else { + dst_top = &dst[-PXSTRIDE(stride)]; + } + } + + if (av1_intra_prediction_edges[mode].needs_left) { + const int sz = th << 2; + pixel *const left = &topleft_out[-sz]; + + if (have_left) { + const int px_have = imin(sz, (h - y) << 2); + + for (int i = 0; i < px_have; i++) + left[sz - 1 - i] = dst[PXSTRIDE(stride) * i - 1]; + if (px_have < sz) + pixel_set(left, left[sz - px_have], sz - px_have); + } else { + pixel_set(left, have_top ? *dst_top : ((1 << bitdepth) >> 1) + 1, sz); + } + + if (av1_intra_prediction_edges[mode].needs_bottomleft) { + const int have_bottomleft = (!have_left || y + th >= h) ? 0 : + (edge_flags & EDGE_I444_LEFT_HAS_BOTTOM); + + if (have_bottomleft) { + const int px_have = imin(sz, (h - y - th) << 2); + + for (int i = 0; i < px_have; i++) + left[-(i + 1)] = dst[(sz + i) * PXSTRIDE(stride) - 1]; + if (px_have < sz) + pixel_set(left - sz, left[-px_have], sz - px_have); + } else { + pixel_set(left - sz, left[0], sz); + } + } + } + + if (av1_intra_prediction_edges[mode].needs_top) { + const int sz = tw << 2; + pixel *const top = &topleft_out[1]; + + if (have_top) { + const int px_have = imin(sz, (w - x) << 2); + pixel_copy(top, dst_top, px_have); + if (px_have < sz) + pixel_set(top + px_have, top[px_have - 1], sz - px_have); + } else { + pixel_set(top, have_left ? dst[-1] : ((1 << bitdepth) >> 1) - 1, sz); + } + + if (av1_intra_prediction_edges[mode].needs_topright) { + const int have_topright = (!have_top || x + tw >= w) ? 0 : + (edge_flags & EDGE_I444_TOP_HAS_RIGHT); + + if (have_topright) { + const int px_have = imin(sz, (w - x - tw) << 2); + + pixel_copy(top + sz, &dst_top[sz], px_have); + if (px_have < sz) + pixel_set(top + sz + px_have, top[sz + px_have - 1], + sz - px_have); + } else { + pixel_set(top + sz, top[sz - 1], sz); + } + } + } + + if (av1_intra_prediction_edges[mode].needs_topleft) { + if (have_left) + *topleft_out = have_top ? dst_top[-1] : dst[-1]; + else + *topleft_out = have_top ? *dst_top : (1 << bitdepth) >> 1; + + if (mode == Z2_PRED && tw + th >= 6 && filter_edge) + *topleft_out = ((topleft_out[-1] + topleft_out[1]) * 5 + + topleft_out[0] * 6 + 8) >> 4; + } + + return mode; +} diff --git a/3rdparty/dav1d/src/ipred_tmpl.c b/3rdparty/dav1d/src/ipred_tmpl.c new file mode 100644 index 0000000..9cad40e --- /dev/null +++ b/3rdparty/dav1d/src/ipred_tmpl.c @@ -0,0 +1,782 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include + +#include "common/attributes.h" +#include "common/intops.h" + +#include "src/ipred.h" +#include "src/tables.h" + +static NOINLINE void +splat_dc(pixel *dst, const ptrdiff_t stride, + const int width, const int height, const int dc HIGHBD_DECL_SUFFIX) +{ +#if BITDEPTH == 8 + assert(dc <= 0xff); + if (width > 4) { + const uint64_t dcN = dc * 0x0101010101010101ULL; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x += sizeof(dcN)) + *((uint64_t *) &dst[x]) = dcN; + dst += PXSTRIDE(stride); + } + } else { + const unsigned dcN = dc * 0x01010101U; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x += sizeof(dcN)) + *((unsigned *) &dst[x]) = dcN; + dst += PXSTRIDE(stride); + } + } +#else + assert(dc <= bitdepth_max); + const uint64_t dcN = dc * 0x0001000100010001ULL; + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x += sizeof(dcN) >> 1) + *((uint64_t *) &dst[x]) = dcN; + dst += PXSTRIDE(stride); + } +#endif +} + +static NOINLINE void +cfl_pred(pixel *dst, const ptrdiff_t stride, + const int width, const int height, const int dc, + const int16_t *ac, const int alpha HIGHBD_DECL_SUFFIX) +{ + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int diff = alpha * ac[x]; + dst[x] = iclip_pixel(dc + apply_sign((abs(diff) + 32) >> 6, diff)); + } + ac += width; + dst += PXSTRIDE(stride); + } +} + +static unsigned dc_gen_top(const pixel *const topleft, const int width) { + unsigned dc = width >> 1; + for (int i = 0; i < width; i++) + dc += topleft[1 + i]; + return dc >> ctz(width); +} + +static void ipred_dc_top_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, const int a, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + splat_dc(dst, stride, width, height, dc_gen_top(topleft, width) + HIGHBD_TAIL_SUFFIX); +} + +static void ipred_cfl_top_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, + const int16_t *ac, const int alpha + HIGHBD_DECL_SUFFIX) +{ + cfl_pred(dst, stride, width, height, dc_gen_top(topleft, width), ac, alpha + HIGHBD_TAIL_SUFFIX); +} + +static unsigned dc_gen_left(const pixel *const topleft, const int height) { + unsigned dc = height >> 1; + for (int i = 0; i < height; i++) + dc += topleft[-(1 + i)]; + return dc >> ctz(height); +} + +static void ipred_dc_left_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, const int a, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + splat_dc(dst, stride, width, height, dc_gen_left(topleft, height) + HIGHBD_TAIL_SUFFIX); +} + +static void ipred_cfl_left_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, + const int16_t *ac, const int alpha + HIGHBD_DECL_SUFFIX) +{ + const unsigned dc = dc_gen_left(topleft, height); + cfl_pred(dst, stride, width, height, dc, ac, alpha HIGHBD_TAIL_SUFFIX); +} + +#if BITDEPTH == 8 +#define MULTIPLIER_1x2 0x5556 +#define MULTIPLIER_1x4 0x3334 +#define BASE_SHIFT 16 +#else +#define MULTIPLIER_1x2 0xAAAB +#define MULTIPLIER_1x4 0x6667 +#define BASE_SHIFT 17 +#endif + +static unsigned dc_gen(const pixel *const topleft, + const int width, const int height) +{ + unsigned dc = (width + height) >> 1; + for (int i = 0; i < width; i++) + dc += topleft[i + 1]; + for (int i = 0; i < height; i++) + dc += topleft[-(i + 1)]; + dc >>= ctz(width + height); + + if (width != height) { + dc *= (width > height * 2 || height > width * 2) ? MULTIPLIER_1x4 : + MULTIPLIER_1x2; + dc >>= BASE_SHIFT; + } + return dc; +} + +static void ipred_dc_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, const int a, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + splat_dc(dst, stride, width, height, dc_gen(topleft, width, height) + HIGHBD_TAIL_SUFFIX); +} + +static void ipred_cfl_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, + const int16_t *ac, const int alpha + HIGHBD_DECL_SUFFIX) +{ + unsigned dc = dc_gen(topleft, width, height); + cfl_pred(dst, stride, width, height, dc, ac, alpha HIGHBD_TAIL_SUFFIX); +} + +#undef MULTIPLIER_1x2 +#undef MULTIPLIER_1x4 +#undef BASE_SHIFT + +static void ipred_dc_128_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, const int a, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ +#if BITDEPTH == 16 + const int dc = (bitdepth_max + 1) >> 1; +#else + const int dc = 128; +#endif + splat_dc(dst, stride, width, height, dc HIGHBD_TAIL_SUFFIX); +} + +static void ipred_cfl_128_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, + const int16_t *ac, const int alpha + HIGHBD_DECL_SUFFIX) +{ +#if BITDEPTH == 16 + const int dc = (bitdepth_max + 1) >> 1; +#else + const int dc = 128; +#endif + cfl_pred(dst, stride, width, height, dc, ac, alpha HIGHBD_TAIL_SUFFIX); +} + +static void ipred_v_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, const int a, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + for (int y = 0; y < height; y++) { + pixel_copy(dst, topleft + 1, width); + dst += PXSTRIDE(stride); + } +} + +static void ipred_h_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, const int a, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + for (int y = 0; y < height; y++) { + pixel_set(dst, topleft[-(1 + y)], width); + dst += PXSTRIDE(stride); + } +} + +static void ipred_paeth_c(pixel *dst, const ptrdiff_t stride, + const pixel *const tl_ptr, + const int width, const int height, const int a, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + const int topleft = tl_ptr[0]; + for (int y = 0; y < height; y++) { + const int left = tl_ptr[-(y + 1)]; + for (int x = 0; x < width; x++) { + const int top = tl_ptr[1 + x]; + const int base = left + top - topleft; + const int ldiff = abs(left - base); + const int tdiff = abs(top - base); + const int tldiff = abs(topleft - base); + + dst[x] = ldiff <= tdiff && ldiff <= tldiff ? left : + tdiff <= tldiff ? top : topleft; + } + dst += PXSTRIDE(stride); + } +} + +static void ipred_smooth_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, const int a, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + const uint8_t *const weights_hor = &dav1d_sm_weights[width]; + const uint8_t *const weights_ver = &dav1d_sm_weights[height]; + const int right = topleft[width], bottom = topleft[-height]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int pred = weights_ver[y] * topleft[1 + x] + + (256 - weights_ver[y]) * bottom + + weights_hor[x] * topleft[-(1 + y)] + + (256 - weights_hor[x]) * right; + dst[x] = (pred + 256) >> 9; + } + dst += PXSTRIDE(stride); + } +} + +static void ipred_smooth_v_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, const int a, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + const uint8_t *const weights_ver = &dav1d_sm_weights[height]; + const int bottom = topleft[-height]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int pred = weights_ver[y] * topleft[1 + x] + + (256 - weights_ver[y]) * bottom; + dst[x] = (pred + 128) >> 8; + } + dst += PXSTRIDE(stride); + } +} + +static void ipred_smooth_h_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft, + const int width, const int height, const int a, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + const uint8_t *const weights_hor = &dav1d_sm_weights[width]; + const int right = topleft[width]; + + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + const int pred = weights_hor[x] * topleft[-(y + 1)] + + (256 - weights_hor[x]) * right; + dst[x] = (pred + 128) >> 8; + } + dst += PXSTRIDE(stride); + } +} + +static NOINLINE int get_filter_strength(const int wh, const int angle, + const int is_sm) +{ + if (is_sm) { + if (wh <= 8) { + if (angle >= 64) return 2; + if (angle >= 40) return 1; + } else if (wh <= 16) { + if (angle >= 48) return 2; + if (angle >= 20) return 1; + } else if (wh <= 24) { + if (angle >= 4) return 3; + } else { + return 3; + } + } else { + if (wh <= 8) { + if (angle >= 56) return 1; + } else if (wh <= 16) { + if (angle >= 40) return 1; + } else if (wh <= 24) { + if (angle >= 32) return 3; + if (angle >= 16) return 2; + if (angle >= 8) return 1; + } else if (wh <= 32) { + if (angle >= 32) return 3; + if (angle >= 4) return 2; + return 1; + } else { + return 3; + } + } + return 0; +} + +static NOINLINE void filter_edge(pixel *const out, const int sz, + const int lim_from, const int lim_to, + const pixel *const in, const int from, + const int to, const int strength) +{ + static const uint8_t kernel[3][5] = { + { 0, 4, 8, 4, 0 }, + { 0, 5, 6, 5, 0 }, + { 2, 4, 4, 4, 2 } + }; + + assert(strength > 0); + int i = 0; + for (; i < imin(sz, lim_from); i++) + out[i] = in[iclip(i, from, to - 1)]; + for (; i < imin(lim_to, sz); i++) { + int s = 0; + for (int j = 0; j < 5; j++) + s += in[iclip(i - 2 + j, from, to - 1)] * kernel[strength - 1][j]; + out[i] = (s + 8) >> 4; + } + for (; i < sz; i++) + out[i] = in[iclip(i, from, to - 1)]; +} + +static inline int get_upsample(const int wh, const int angle, const int is_sm) { + return angle < 40 && wh <= 16 >> is_sm; +} + +static NOINLINE void upsample_edge(pixel *const out, const int hsz, + const pixel *const in, const int from, + const int to HIGHBD_DECL_SUFFIX) +{ + static const int8_t kernel[4] = { -1, 9, 9, -1 }; + int i; + for (i = 0; i < hsz - 1; i++) { + out[i * 2] = in[iclip(i, from, to - 1)]; + + int s = 0; + for (int j = 0; j < 4; j++) + s += in[iclip(i + j - 1, from, to - 1)] * kernel[j]; + out[i * 2 + 1] = iclip_pixel((s + 8) >> 4); + } + out[i * 2] = in[iclip(i, from, to - 1)]; +} + +static void ipred_z1_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft_in, + const int width, const int height, int angle, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + const int is_sm = (angle >> 9) & 0x1; + const int enable_intra_edge_filter = angle >> 10; + angle &= 511; + assert(angle < 90); + int dx = dav1d_dr_intra_derivative[angle >> 1]; + pixel top_out[64 + 64]; + const pixel *top; + int max_base_x; + const int upsample_above = enable_intra_edge_filter ? + get_upsample(width + height, 90 - angle, is_sm) : 0; + if (upsample_above) { + upsample_edge(top_out, width + height, &topleft_in[1], -1, + width + imin(width, height) HIGHBD_TAIL_SUFFIX); + top = top_out; + max_base_x = 2 * (width + height) - 2; + dx <<= 1; + } else { + const int filter_strength = enable_intra_edge_filter ? + get_filter_strength(width + height, 90 - angle, is_sm) : 0; + if (filter_strength) { + filter_edge(top_out, width + height, 0, width + height, + &topleft_in[1], -1, width + imin(width, height), + filter_strength); + top = top_out; + max_base_x = width + height - 1; + } else { + top = &topleft_in[1]; + max_base_x = width + imin(width, height) - 1; + } + } + const int base_inc = 1 + upsample_above; + for (int y = 0, xpos = dx; y < height; + y++, dst += PXSTRIDE(stride), xpos += dx) + { + const int frac = xpos & 0x3E; + + for (int x = 0, base = xpos >> 6; x < width; x++, base += base_inc) { + if (base < max_base_x) { + const int v = top[base] * (64 - frac) + top[base + 1] * frac; + dst[x] = (v + 32) >> 6; + } else { + pixel_set(&dst[x], top[max_base_x], width - x); + break; + } + } + } +} + +static void ipred_z2_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft_in, + const int width, const int height, int angle, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + const int is_sm = (angle >> 9) & 0x1; + const int enable_intra_edge_filter = angle >> 10; + angle &= 511; + assert(angle > 90 && angle < 180); + int dy = dav1d_dr_intra_derivative[(angle - 90) >> 1]; + int dx = dav1d_dr_intra_derivative[(180 - angle) >> 1]; + const int upsample_left = enable_intra_edge_filter ? + get_upsample(width + height, 180 - angle, is_sm) : 0; + const int upsample_above = enable_intra_edge_filter ? + get_upsample(width + height, angle - 90, is_sm) : 0; + pixel edge[64 + 64 + 1]; + pixel *const topleft = &edge[64]; + + if (upsample_above) { + upsample_edge(topleft, width + 1, topleft_in, 0, width + 1 + HIGHBD_TAIL_SUFFIX); + dx <<= 1; + } else { + const int filter_strength = enable_intra_edge_filter ? + get_filter_strength(width + height, angle - 90, is_sm) : 0; + + if (filter_strength) { + filter_edge(&topleft[1], width, 0, max_width, + &topleft_in[1], -1, width, + filter_strength); + } else { + pixel_copy(&topleft[1], &topleft_in[1], width); + } + } + if (upsample_left) { + upsample_edge(&topleft[-height * 2], height + 1, &topleft_in[-height], + 0, height + 1 HIGHBD_TAIL_SUFFIX); + dy <<= 1; + } else { + const int filter_strength = enable_intra_edge_filter ? + get_filter_strength(width + height, 180 - angle, is_sm) : 0; + + if (filter_strength) { + filter_edge(&topleft[-height], height, height - max_height, height, + &topleft_in[-height], + 0, height + 1, filter_strength); + } else { + pixel_copy(&topleft[-height], &topleft_in[-height], height); + } + } + *topleft = *topleft_in; + + const int base_inc_x = 1 + upsample_above; + const pixel *const left = &topleft[-(1 + upsample_left)]; + for (int y = 0, xpos = ((1 + upsample_above) << 6) - dx; y < height; + y++, xpos -= dx, dst += PXSTRIDE(stride)) + { + int base_x = xpos >> 6; + const int frac_x = xpos & 0x3E; + + for (int x = 0, ypos = (y << (6 + upsample_left)) - dy; x < width; + x++, base_x += base_inc_x, ypos -= dy) + { + int v; + if (base_x >= 0) { + v = topleft[base_x] * (64 - frac_x) + + topleft[base_x + 1] * frac_x; + } else { + const int base_y = ypos >> 6; + assert(base_y >= -(1 + upsample_left)); + const int frac_y = ypos & 0x3E; + v = left[-base_y] * (64 - frac_y) + + left[-(base_y + 1)] * frac_y; + } + dst[x] = (v + 32) >> 6; + } + } +} + +static void ipred_z3_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft_in, + const int width, const int height, int angle, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + const int is_sm = (angle >> 9) & 0x1; + const int enable_intra_edge_filter = angle >> 10; + angle &= 511; + assert(angle > 180); + int dy = dav1d_dr_intra_derivative[(270 - angle) >> 1]; + pixel left_out[64 + 64]; + const pixel *left; + int max_base_y; + const int upsample_left = enable_intra_edge_filter ? + get_upsample(width + height, angle - 180, is_sm) : 0; + if (upsample_left) { + upsample_edge(left_out, width + height, + &topleft_in[-(width + height)], + imax(width - height, 0), width + height + 1 + HIGHBD_TAIL_SUFFIX); + left = &left_out[2 * (width + height) - 2]; + max_base_y = 2 * (width + height) - 2; + dy <<= 1; + } else { + const int filter_strength = enable_intra_edge_filter ? + get_filter_strength(width + height, angle - 180, is_sm) : 0; + + if (filter_strength) { + filter_edge(left_out, width + height, 0, width + height, + &topleft_in[-(width + height)], + imax(width - height, 0), width + height + 1, + filter_strength); + left = &left_out[width + height - 1]; + max_base_y = width + height - 1; + } else { + left = &topleft_in[-1]; + max_base_y = height + imin(width, height) - 1; + } + } + const int base_inc = 1 + upsample_left; + for (int x = 0, ypos = dy; x < width; x++, ypos += dy) { + const int frac = ypos & 0x3E; + + for (int y = 0, base = ypos >> 6; y < height; y++, base += base_inc) { + if (base < max_base_y) { + const int v = left[-base] * (64 - frac) + + left[-(base + 1)] * frac; + dst[y * PXSTRIDE(stride) + x] = (v + 32) >> 6; + } else { + do { + dst[y * PXSTRIDE(stride) + x] = left[-max_base_y]; + } while (++y < height); + break; + } + } + } +} + +#if ARCH_X86 +#define FILTER(flt_ptr, p0, p1, p2, p3, p4, p5, p6) \ + flt_ptr[ 0] * p0 + flt_ptr[ 1] * p1 + \ + flt_ptr[16] * p2 + flt_ptr[17] * p3 + \ + flt_ptr[32] * p4 + flt_ptr[33] * p5 + \ + flt_ptr[48] * p6 +#define FLT_INCR 2 +#else +#define FILTER(flt_ptr, p0, p1, p2, p3, p4, p5, p6) \ + flt_ptr[ 0] * p0 + flt_ptr[ 8] * p1 + \ + flt_ptr[16] * p2 + flt_ptr[24] * p3 + \ + flt_ptr[32] * p4 + flt_ptr[40] * p5 + \ + flt_ptr[48] * p6 +#define FLT_INCR 1 +#endif + +/* Up to 32x32 only */ +static void ipred_filter_c(pixel *dst, const ptrdiff_t stride, + const pixel *const topleft_in, + const int width, const int height, int filt_idx, + const int max_width, const int max_height + HIGHBD_DECL_SUFFIX) +{ + filt_idx &= 511; + assert(filt_idx < 5); + + const int8_t *const filter = dav1d_filter_intra_taps[filt_idx]; + const pixel *top = &topleft_in[1]; + for (int y = 0; y < height; y += 2) { + const pixel *topleft = &topleft_in[-y]; + const pixel *left = &topleft[-1]; + ptrdiff_t left_stride = -1; + for (int x = 0; x < width; x += 4) { + const int p0 = *topleft; + const int p1 = top[0], p2 = top[1], p3 = top[2], p4 = top[3]; + const int p5 = left[0 * left_stride], p6 = left[1 * left_stride]; + pixel *ptr = &dst[x]; + const int8_t *flt_ptr = filter; + + for (int yy = 0; yy < 2; yy++) { + for (int xx = 0; xx < 4; xx++, flt_ptr += FLT_INCR) { + const int acc = FILTER(flt_ptr, p0, p1, p2, p3, p4, p5, p6); + ptr[xx] = iclip_pixel((acc + 8) >> 4); + } + ptr += PXSTRIDE(stride); + } + left = &dst[x + 4 - 1]; + left_stride = PXSTRIDE(stride); + top += 4; + topleft = &top[-1]; + } + top = &dst[PXSTRIDE(stride)]; + dst = &dst[PXSTRIDE(stride) * 2]; + } +} + +static NOINLINE void +cfl_ac_c(int16_t *ac, const pixel *ypx, const ptrdiff_t stride, + const int w_pad, const int h_pad, const int width, const int height, + const int ss_hor, const int ss_ver) +{ + int y, x; + int16_t *const ac_orig = ac; + + assert(w_pad >= 0 && w_pad * 4 < width); + assert(h_pad >= 0 && h_pad * 4 < height); + + for (y = 0; y < height - 4 * h_pad; y++) { + for (x = 0; x < width - 4 * w_pad; x++) { + int ac_sum = ypx[x << ss_hor]; + if (ss_hor) ac_sum += ypx[x * 2 + 1]; + if (ss_ver) { + ac_sum += ypx[(x << ss_hor) + PXSTRIDE(stride)]; + if (ss_hor) ac_sum += ypx[x * 2 + 1 + PXSTRIDE(stride)]; + } + ac[x] = ac_sum << (1 + !ss_ver + !ss_hor); + } + for (; x < width; x++) + ac[x] = ac[x - 1]; + ac += width; + ypx += PXSTRIDE(stride) << ss_ver; + } + for (; y < height; y++) { + memcpy(ac, &ac[-width], width * sizeof(*ac)); + ac += width; + } + + const int log2sz = ctz(width) + ctz(height); + int sum = (1 << log2sz) >> 1; + for (ac = ac_orig, y = 0; y < height; y++) { + for (x = 0; x < width; x++) + sum += ac[x]; + ac += width; + } + sum >>= log2sz; + + // subtract DC + for (ac = ac_orig, y = 0; y < height; y++) { + for (x = 0; x < width; x++) + ac[x] -= sum; + ac += width; + } +} + +#define cfl_ac_fn(fmt, ss_hor, ss_ver) \ +static void cfl_ac_##fmt##_c(int16_t *const ac, const pixel *const ypx, \ + const ptrdiff_t stride, const int w_pad, \ + const int h_pad, const int cw, const int ch) \ +{ \ + cfl_ac_c(ac, ypx, stride, w_pad, h_pad, cw, ch, ss_hor, ss_ver); \ +} + +cfl_ac_fn(420, 1, 1) +cfl_ac_fn(422, 1, 0) +cfl_ac_fn(444, 0, 0) + +static void pal_pred_c(pixel *dst, const ptrdiff_t stride, + const pixel *const pal, const uint8_t *idx, + const int w, const int h) +{ + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x += 2) { + const int i = *idx++; + assert(!(i & 0x88)); + dst[x + 0] = pal[i & 7]; + dst[x + 1] = pal[i >> 4]; + } + dst += PXSTRIDE(stride); + } +} + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM +#include "src/arm/ipred.h" +#elif ARCH_RISCV +#include "src/riscv/ipred.h" +#elif ARCH_X86 +#include "src/x86/ipred.h" +#elif ARCH_LOONGARCH64 +#include "src/loongarch/ipred.h" +#endif +#endif + +COLD void bitfn(dav1d_intra_pred_dsp_init)(Dav1dIntraPredDSPContext *const c) { + c->intra_pred[DC_PRED ] = ipred_dc_c; + c->intra_pred[DC_128_PRED ] = ipred_dc_128_c; + c->intra_pred[TOP_DC_PRED ] = ipred_dc_top_c; + c->intra_pred[LEFT_DC_PRED ] = ipred_dc_left_c; + c->intra_pred[HOR_PRED ] = ipred_h_c; + c->intra_pred[VERT_PRED ] = ipred_v_c; + c->intra_pred[PAETH_PRED ] = ipred_paeth_c; + c->intra_pred[SMOOTH_PRED ] = ipred_smooth_c; + c->intra_pred[SMOOTH_V_PRED] = ipred_smooth_v_c; + c->intra_pred[SMOOTH_H_PRED] = ipred_smooth_h_c; + c->intra_pred[Z1_PRED ] = ipred_z1_c; + c->intra_pred[Z2_PRED ] = ipred_z2_c; + c->intra_pred[Z3_PRED ] = ipred_z3_c; + c->intra_pred[FILTER_PRED ] = ipred_filter_c; + + c->cfl_ac[DAV1D_PIXEL_LAYOUT_I420 - 1] = cfl_ac_420_c; + c->cfl_ac[DAV1D_PIXEL_LAYOUT_I422 - 1] = cfl_ac_422_c; + c->cfl_ac[DAV1D_PIXEL_LAYOUT_I444 - 1] = cfl_ac_444_c; + + c->cfl_pred[DC_PRED ] = ipred_cfl_c; + c->cfl_pred[DC_128_PRED ] = ipred_cfl_128_c; + c->cfl_pred[TOP_DC_PRED ] = ipred_cfl_top_c; + c->cfl_pred[LEFT_DC_PRED] = ipred_cfl_left_c; + + c->pal_pred = pal_pred_c; + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM + intra_pred_dsp_init_arm(c); +#elif ARCH_RISCV + intra_pred_dsp_init_riscv(c); +#elif ARCH_X86 + intra_pred_dsp_init_x86(c); +#elif ARCH_LOONGARCH64 + intra_pred_dsp_init_loongarch(c); +#endif +#endif +} diff --git a/3rdparty/dav1d/src/itx.h b/3rdparty/dav1d/src/itx.h new file mode 100644 index 0000000..8ef4f4d --- /dev/null +++ b/3rdparty/dav1d/src/itx.h @@ -0,0 +1,111 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_ITX_H +#define DAV1D_SRC_ITX_H + +#include + +#include "common/bitdepth.h" + +#include "src/levels.h" + +#define decl_itx_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, coef *coeff, int eob \ + HIGHBD_DECL_SUFFIX) +typedef decl_itx_fn(*itxfm_fn); + +#define decl_itx2_fns(w, h, opt) \ +decl_itx_fn(BF(dav1d_inv_txfm_add_dct_dct_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_identity_identity_##w##x##h, opt)) + +#define decl_itx12_fns(w, h, opt) \ +decl_itx2_fns(w, h, opt); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_dct_adst_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_dct_flipadst_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_dct_identity_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_adst_dct_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_adst_adst_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_adst_flipadst_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_flipadst_dct_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_flipadst_adst_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_flipadst_flipadst_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_identity_dct_##w##x##h, opt)) + +#define decl_itx16_fns(w, h, opt) \ +decl_itx12_fns(w, h, opt); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_adst_identity_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_flipadst_identity_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_identity_adst_##w##x##h, opt)); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_identity_flipadst_##w##x##h, opt)) + +#define decl_itx17_fns(w, h, opt) \ +decl_itx16_fns(w, h, opt); \ +decl_itx_fn(BF(dav1d_inv_txfm_add_wht_wht_##w##x##h, opt)) + +typedef struct Dav1dInvTxfmDSPContext { + itxfm_fn itxfm_add[N_RECT_TX_SIZES][N_TX_TYPES_PLUS_LL]; +} Dav1dInvTxfmDSPContext; + +bitfn_decls(void dav1d_itx_dsp_init, Dav1dInvTxfmDSPContext *c, int bpc); + +#define assign_itx_fn(pfx, w, h, type, type_enum, ext) \ + c->itxfm_add[pfx##TX_##w##X##h][type_enum] = \ + BF(dav1d_inv_txfm_add_##type##_##w##x##h, ext) + +#define assign_itx1_fn(pfx, w, h, ext) \ + assign_itx_fn(pfx, w, h, dct_dct, DCT_DCT, ext) + +#define assign_itx2_fn(pfx, w, h, ext) \ + assign_itx1_fn(pfx, w, h, ext); \ + assign_itx_fn(pfx, w, h, identity_identity, IDTX, ext) + +#define assign_itx12_fn(pfx, w, h, ext) \ + assign_itx2_fn(pfx, w, h, ext); \ + assign_itx_fn(pfx, w, h, dct_adst, ADST_DCT, ext); \ + assign_itx_fn(pfx, w, h, dct_flipadst, FLIPADST_DCT, ext); \ + assign_itx_fn(pfx, w, h, dct_identity, H_DCT, ext); \ + assign_itx_fn(pfx, w, h, adst_dct, DCT_ADST, ext); \ + assign_itx_fn(pfx, w, h, adst_adst, ADST_ADST, ext); \ + assign_itx_fn(pfx, w, h, adst_flipadst, FLIPADST_ADST, ext); \ + assign_itx_fn(pfx, w, h, flipadst_dct, DCT_FLIPADST, ext); \ + assign_itx_fn(pfx, w, h, flipadst_adst, ADST_FLIPADST, ext); \ + assign_itx_fn(pfx, w, h, flipadst_flipadst, FLIPADST_FLIPADST, ext); \ + assign_itx_fn(pfx, w, h, identity_dct, V_DCT, ext) + +#define assign_itx16_fn(pfx, w, h, ext) \ + assign_itx12_fn(pfx, w, h, ext); \ + assign_itx_fn(pfx, w, h, adst_identity, H_ADST, ext); \ + assign_itx_fn(pfx, w, h, flipadst_identity, H_FLIPADST, ext); \ + assign_itx_fn(pfx, w, h, identity_adst, V_ADST, ext); \ + assign_itx_fn(pfx, w, h, identity_flipadst, V_FLIPADST, ext) + +#define assign_itx17_fn(pfx, w, h, ext) \ + assign_itx16_fn(pfx, w, h, ext); \ + assign_itx_fn(pfx, w, h, wht_wht, WHT_WHT, ext) + +#endif /* DAV1D_SRC_ITX_H */ diff --git a/3rdparty/dav1d/src/itx_1d.c b/3rdparty/dav1d/src/itx_1d.c new file mode 100644 index 0000000..14e89ca --- /dev/null +++ b/3rdparty/dav1d/src/itx_1d.c @@ -0,0 +1,1082 @@ +/* + * Copyright © 2018-2019, VideoLAN and dav1d authors + * Copyright © 2018-2019, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include + +#include "common/intops.h" + +#include "src/itx_1d.h" + +#define CLIP(a) iclip(a, min, max) + +/* + * In some places, we use the pattern like this: + * t2 = ((in1 * 1567 - in3 * (3784 - 4096) + 2048) >> 12) - in3; + * even though the reference code might use something like: + * t2 = (in1 * 1567 - in3 * 3784 + 2048) >> 12; + * + * The reason for this is that for 12 bits/component bitstreams (corrupt/ + * invalid ones, but they are codable nonetheless), each coefficient or + * input can be 19(+sign) bits, and therefore if the combination of the + * two multipliers (each 12 bits) is >= 4096, the result of the add/sub + * after the pair of multiplies will exceed the 31+sign bit range. Signed + * integer overflows are UB in C, and we'd like to prevent that. + * + * To workaround this, we invert one of the two coefficients (or, if both are + * multiples of 2, we reduce their magnitude by one bit). It should be noted + * that SIMD implementations do not have to follow this exact behaviour. The + * AV1 spec clearly states that the result of the multiply/add pairs should + * fit in 31+sign bit intermediates, and that streams violating this convention + * are not AV1-compliant. So, as long as we don't trigger UB (which some people + * would consider a security vulnerability), we're fine. So, SIMD can simply + * use the faster implementation, even if that might in some cases result in + * integer overflows, since these are not considered valid AV1 anyway, and in + * e.g. x86 assembly, integer overflows are not considered UB, but they merely + * wrap around. + */ + +static NOINLINE void +inv_dct4_1d_internal_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max, const int tx64) +{ + assert(stride > 0); + const int in0 = c[0 * stride], in1 = c[1 * stride]; + + int t0, t1, t2, t3; + if (tx64) { + t0 = t1 = (in0 * 181 + 128) >> 8; + t2 = (in1 * 1567 + 2048) >> 12; + t3 = (in1 * 3784 + 2048) >> 12; + } else { + const int in2 = c[2 * stride], in3 = c[3 * stride]; + + t0 = ((in0 + in2) * 181 + 128) >> 8; + t1 = ((in0 - in2) * 181 + 128) >> 8; + t2 = ((in1 * 1567 - in3 * (3784 - 4096) + 2048) >> 12) - in3; + t3 = ((in1 * (3784 - 4096) + in3 * 1567 + 2048) >> 12) + in1; + } + + c[0 * stride] = CLIP(t0 + t3); + c[1 * stride] = CLIP(t1 + t2); + c[2 * stride] = CLIP(t1 - t2); + c[3 * stride] = CLIP(t0 - t3); +} + +static void inv_dct4_1d_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max) +{ + inv_dct4_1d_internal_c(c, stride, min, max, 0); +} + +static NOINLINE void +inv_dct8_1d_internal_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max, const int tx64) +{ + assert(stride > 0); + inv_dct4_1d_internal_c(c, stride << 1, min, max, tx64); + + const int in1 = c[1 * stride], in3 = c[3 * stride]; + + int t4a, t5a, t6a, t7a; + if (tx64) { + t4a = (in1 * 799 + 2048) >> 12; + t5a = (in3 * -2276 + 2048) >> 12; + t6a = (in3 * 3406 + 2048) >> 12; + t7a = (in1 * 4017 + 2048) >> 12; + } else { + const int in5 = c[5 * stride], in7 = c[7 * stride]; + + t4a = ((in1 * 799 - in7 * (4017 - 4096) + 2048) >> 12) - in7; + t5a = (in5 * 1703 - in3 * 1138 + 1024) >> 11; + t6a = (in5 * 1138 + in3 * 1703 + 1024) >> 11; + t7a = ((in1 * (4017 - 4096) + in7 * 799 + 2048) >> 12) + in1; + } + + const int t4 = CLIP(t4a + t5a); + t5a = CLIP(t4a - t5a); + const int t7 = CLIP(t7a + t6a); + t6a = CLIP(t7a - t6a); + + const int t5 = ((t6a - t5a) * 181 + 128) >> 8; + const int t6 = ((t6a + t5a) * 181 + 128) >> 8; + + const int t0 = c[0 * stride]; + const int t1 = c[2 * stride]; + const int t2 = c[4 * stride]; + const int t3 = c[6 * stride]; + + c[0 * stride] = CLIP(t0 + t7); + c[1 * stride] = CLIP(t1 + t6); + c[2 * stride] = CLIP(t2 + t5); + c[3 * stride] = CLIP(t3 + t4); + c[4 * stride] = CLIP(t3 - t4); + c[5 * stride] = CLIP(t2 - t5); + c[6 * stride] = CLIP(t1 - t6); + c[7 * stride] = CLIP(t0 - t7); +} + +static void inv_dct8_1d_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max) +{ + inv_dct8_1d_internal_c(c, stride, min, max, 0); +} + +static NOINLINE void +inv_dct16_1d_internal_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max, int tx64) +{ + assert(stride > 0); + inv_dct8_1d_internal_c(c, stride << 1, min, max, tx64); + + const int in1 = c[1 * stride], in3 = c[3 * stride]; + const int in5 = c[5 * stride], in7 = c[7 * stride]; + + int t8a, t9a, t10a, t11a, t12a, t13a, t14a, t15a; + if (tx64) { + t8a = (in1 * 401 + 2048) >> 12; + t9a = (in7 * -2598 + 2048) >> 12; + t10a = (in5 * 1931 + 2048) >> 12; + t11a = (in3 * -1189 + 2048) >> 12; + t12a = (in3 * 3920 + 2048) >> 12; + t13a = (in5 * 3612 + 2048) >> 12; + t14a = (in7 * 3166 + 2048) >> 12; + t15a = (in1 * 4076 + 2048) >> 12; + } else { + const int in9 = c[ 9 * stride], in11 = c[11 * stride]; + const int in13 = c[13 * stride], in15 = c[15 * stride]; + + t8a = ((in1 * 401 - in15 * (4076 - 4096) + 2048) >> 12) - in15; + t9a = (in9 * 1583 - in7 * 1299 + 1024) >> 11; + t10a = ((in5 * 1931 - in11 * (3612 - 4096) + 2048) >> 12) - in11; + t11a = ((in13 * (3920 - 4096) - in3 * 1189 + 2048) >> 12) + in13; + t12a = ((in13 * 1189 + in3 * (3920 - 4096) + 2048) >> 12) + in3; + t13a = ((in5 * (3612 - 4096) + in11 * 1931 + 2048) >> 12) + in5; + t14a = (in9 * 1299 + in7 * 1583 + 1024) >> 11; + t15a = ((in1 * (4076 - 4096) + in15 * 401 + 2048) >> 12) + in1; + } + + int t8 = CLIP(t8a + t9a); + int t9 = CLIP(t8a - t9a); + int t10 = CLIP(t11a - t10a); + int t11 = CLIP(t11a + t10a); + int t12 = CLIP(t12a + t13a); + int t13 = CLIP(t12a - t13a); + int t14 = CLIP(t15a - t14a); + int t15 = CLIP(t15a + t14a); + + t9a = (( t14 * 1567 - t9 * (3784 - 4096) + 2048) >> 12) - t9; + t14a = (( t14 * (3784 - 4096) + t9 * 1567 + 2048) >> 12) + t14; + t10a = ((-(t13 * (3784 - 4096) + t10 * 1567) + 2048) >> 12) - t13; + t13a = (( t13 * 1567 - t10 * (3784 - 4096) + 2048) >> 12) - t10; + + t8a = CLIP(t8 + t11); + t9 = CLIP(t9a + t10a); + t10 = CLIP(t9a - t10a); + t11a = CLIP(t8 - t11); + t12a = CLIP(t15 - t12); + t13 = CLIP(t14a - t13a); + t14 = CLIP(t14a + t13a); + t15a = CLIP(t15 + t12); + + t10a = ((t13 - t10) * 181 + 128) >> 8; + t13a = ((t13 + t10) * 181 + 128) >> 8; + t11 = ((t12a - t11a) * 181 + 128) >> 8; + t12 = ((t12a + t11a) * 181 + 128) >> 8; + + const int t0 = c[ 0 * stride]; + const int t1 = c[ 2 * stride]; + const int t2 = c[ 4 * stride]; + const int t3 = c[ 6 * stride]; + const int t4 = c[ 8 * stride]; + const int t5 = c[10 * stride]; + const int t6 = c[12 * stride]; + const int t7 = c[14 * stride]; + + c[ 0 * stride] = CLIP(t0 + t15a); + c[ 1 * stride] = CLIP(t1 + t14); + c[ 2 * stride] = CLIP(t2 + t13a); + c[ 3 * stride] = CLIP(t3 + t12); + c[ 4 * stride] = CLIP(t4 + t11); + c[ 5 * stride] = CLIP(t5 + t10a); + c[ 6 * stride] = CLIP(t6 + t9); + c[ 7 * stride] = CLIP(t7 + t8a); + c[ 8 * stride] = CLIP(t7 - t8a); + c[ 9 * stride] = CLIP(t6 - t9); + c[10 * stride] = CLIP(t5 - t10a); + c[11 * stride] = CLIP(t4 - t11); + c[12 * stride] = CLIP(t3 - t12); + c[13 * stride] = CLIP(t2 - t13a); + c[14 * stride] = CLIP(t1 - t14); + c[15 * stride] = CLIP(t0 - t15a); +} + +static void inv_dct16_1d_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max) +{ + inv_dct16_1d_internal_c(c, stride, min, max, 0); +} + +static NOINLINE void +inv_dct32_1d_internal_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max, const int tx64) +{ + assert(stride > 0); + inv_dct16_1d_internal_c(c, stride << 1, min, max, tx64); + + const int in1 = c[ 1 * stride], in3 = c[ 3 * stride]; + const int in5 = c[ 5 * stride], in7 = c[ 7 * stride]; + const int in9 = c[ 9 * stride], in11 = c[11 * stride]; + const int in13 = c[13 * stride], in15 = c[15 * stride]; + + int t16a, t17a, t18a, t19a, t20a, t21a, t22a, t23a; + int t24a, t25a, t26a, t27a, t28a, t29a, t30a, t31a; + if (tx64) { + t16a = (in1 * 201 + 2048) >> 12; + t17a = (in15 * -2751 + 2048) >> 12; + t18a = (in9 * 1751 + 2048) >> 12; + t19a = (in7 * -1380 + 2048) >> 12; + t20a = (in5 * 995 + 2048) >> 12; + t21a = (in11 * -2106 + 2048) >> 12; + t22a = (in13 * 2440 + 2048) >> 12; + t23a = (in3 * -601 + 2048) >> 12; + t24a = (in3 * 4052 + 2048) >> 12; + t25a = (in13 * 3290 + 2048) >> 12; + t26a = (in11 * 3513 + 2048) >> 12; + t27a = (in5 * 3973 + 2048) >> 12; + t28a = (in7 * 3857 + 2048) >> 12; + t29a = (in9 * 3703 + 2048) >> 12; + t30a = (in15 * 3035 + 2048) >> 12; + t31a = (in1 * 4091 + 2048) >> 12; + } else { + const int in17 = c[17 * stride], in19 = c[19 * stride]; + const int in21 = c[21 * stride], in23 = c[23 * stride]; + const int in25 = c[25 * stride], in27 = c[27 * stride]; + const int in29 = c[29 * stride], in31 = c[31 * stride]; + + t16a = ((in1 * 201 - in31 * (4091 - 4096) + 2048) >> 12) - in31; + t17a = ((in17 * (3035 - 4096) - in15 * 2751 + 2048) >> 12) + in17; + t18a = ((in9 * 1751 - in23 * (3703 - 4096) + 2048) >> 12) - in23; + t19a = ((in25 * (3857 - 4096) - in7 * 1380 + 2048) >> 12) + in25; + t20a = ((in5 * 995 - in27 * (3973 - 4096) + 2048) >> 12) - in27; + t21a = ((in21 * (3513 - 4096) - in11 * 2106 + 2048) >> 12) + in21; + t22a = (in13 * 1220 - in19 * 1645 + 1024) >> 11; + t23a = ((in29 * (4052 - 4096) - in3 * 601 + 2048) >> 12) + in29; + t24a = ((in29 * 601 + in3 * (4052 - 4096) + 2048) >> 12) + in3; + t25a = (in13 * 1645 + in19 * 1220 + 1024) >> 11; + t26a = ((in21 * 2106 + in11 * (3513 - 4096) + 2048) >> 12) + in11; + t27a = ((in5 * (3973 - 4096) + in27 * 995 + 2048) >> 12) + in5; + t28a = ((in25 * 1380 + in7 * (3857 - 4096) + 2048) >> 12) + in7; + t29a = ((in9 * (3703 - 4096) + in23 * 1751 + 2048) >> 12) + in9; + t30a = ((in17 * 2751 + in15 * (3035 - 4096) + 2048) >> 12) + in15; + t31a = ((in1 * (4091 - 4096) + in31 * 201 + 2048) >> 12) + in1; + } + + int t16 = CLIP(t16a + t17a); + int t17 = CLIP(t16a - t17a); + int t18 = CLIP(t19a - t18a); + int t19 = CLIP(t19a + t18a); + int t20 = CLIP(t20a + t21a); + int t21 = CLIP(t20a - t21a); + int t22 = CLIP(t23a - t22a); + int t23 = CLIP(t23a + t22a); + int t24 = CLIP(t24a + t25a); + int t25 = CLIP(t24a - t25a); + int t26 = CLIP(t27a - t26a); + int t27 = CLIP(t27a + t26a); + int t28 = CLIP(t28a + t29a); + int t29 = CLIP(t28a - t29a); + int t30 = CLIP(t31a - t30a); + int t31 = CLIP(t31a + t30a); + + t17a = (( t30 * 799 - t17 * (4017 - 4096) + 2048) >> 12) - t17; + t30a = (( t30 * (4017 - 4096) + t17 * 799 + 2048) >> 12) + t30; + t18a = ((-(t29 * (4017 - 4096) + t18 * 799) + 2048) >> 12) - t29; + t29a = (( t29 * 799 - t18 * (4017 - 4096) + 2048) >> 12) - t18; + t21a = ( t26 * 1703 - t21 * 1138 + 1024) >> 11; + t26a = ( t26 * 1138 + t21 * 1703 + 1024) >> 11; + t22a = (-(t25 * 1138 + t22 * 1703 ) + 1024) >> 11; + t25a = ( t25 * 1703 - t22 * 1138 + 1024) >> 11; + + t16a = CLIP(t16 + t19); + t17 = CLIP(t17a + t18a); + t18 = CLIP(t17a - t18a); + t19a = CLIP(t16 - t19); + t20a = CLIP(t23 - t20); + t21 = CLIP(t22a - t21a); + t22 = CLIP(t22a + t21a); + t23a = CLIP(t23 + t20); + t24a = CLIP(t24 + t27); + t25 = CLIP(t25a + t26a); + t26 = CLIP(t25a - t26a); + t27a = CLIP(t24 - t27); + t28a = CLIP(t31 - t28); + t29 = CLIP(t30a - t29a); + t30 = CLIP(t30a + t29a); + t31a = CLIP(t31 + t28); + + t18a = (( t29 * 1567 - t18 * (3784 - 4096) + 2048) >> 12) - t18; + t29a = (( t29 * (3784 - 4096) + t18 * 1567 + 2048) >> 12) + t29; + t19 = (( t28a * 1567 - t19a * (3784 - 4096) + 2048) >> 12) - t19a; + t28 = (( t28a * (3784 - 4096) + t19a * 1567 + 2048) >> 12) + t28a; + t20 = ((-(t27a * (3784 - 4096) + t20a * 1567) + 2048) >> 12) - t27a; + t27 = (( t27a * 1567 - t20a * (3784 - 4096) + 2048) >> 12) - t20a; + t21a = ((-(t26 * (3784 - 4096) + t21 * 1567) + 2048) >> 12) - t26; + t26a = (( t26 * 1567 - t21 * (3784 - 4096) + 2048) >> 12) - t21; + + t16 = CLIP(t16a + t23a); + t17a = CLIP(t17 + t22); + t18 = CLIP(t18a + t21a); + t19a = CLIP(t19 + t20); + t20a = CLIP(t19 - t20); + t21 = CLIP(t18a - t21a); + t22a = CLIP(t17 - t22); + t23 = CLIP(t16a - t23a); + t24 = CLIP(t31a - t24a); + t25a = CLIP(t30 - t25); + t26 = CLIP(t29a - t26a); + t27a = CLIP(t28 - t27); + t28a = CLIP(t28 + t27); + t29 = CLIP(t29a + t26a); + t30a = CLIP(t30 + t25); + t31 = CLIP(t31a + t24a); + + t20 = ((t27a - t20a) * 181 + 128) >> 8; + t27 = ((t27a + t20a) * 181 + 128) >> 8; + t21a = ((t26 - t21 ) * 181 + 128) >> 8; + t26a = ((t26 + t21 ) * 181 + 128) >> 8; + t22 = ((t25a - t22a) * 181 + 128) >> 8; + t25 = ((t25a + t22a) * 181 + 128) >> 8; + t23a = ((t24 - t23 ) * 181 + 128) >> 8; + t24a = ((t24 + t23 ) * 181 + 128) >> 8; + + const int t0 = c[ 0 * stride]; + const int t1 = c[ 2 * stride]; + const int t2 = c[ 4 * stride]; + const int t3 = c[ 6 * stride]; + const int t4 = c[ 8 * stride]; + const int t5 = c[10 * stride]; + const int t6 = c[12 * stride]; + const int t7 = c[14 * stride]; + const int t8 = c[16 * stride]; + const int t9 = c[18 * stride]; + const int t10 = c[20 * stride]; + const int t11 = c[22 * stride]; + const int t12 = c[24 * stride]; + const int t13 = c[26 * stride]; + const int t14 = c[28 * stride]; + const int t15 = c[30 * stride]; + + c[ 0 * stride] = CLIP(t0 + t31); + c[ 1 * stride] = CLIP(t1 + t30a); + c[ 2 * stride] = CLIP(t2 + t29); + c[ 3 * stride] = CLIP(t3 + t28a); + c[ 4 * stride] = CLIP(t4 + t27); + c[ 5 * stride] = CLIP(t5 + t26a); + c[ 6 * stride] = CLIP(t6 + t25); + c[ 7 * stride] = CLIP(t7 + t24a); + c[ 8 * stride] = CLIP(t8 + t23a); + c[ 9 * stride] = CLIP(t9 + t22); + c[10 * stride] = CLIP(t10 + t21a); + c[11 * stride] = CLIP(t11 + t20); + c[12 * stride] = CLIP(t12 + t19a); + c[13 * stride] = CLIP(t13 + t18); + c[14 * stride] = CLIP(t14 + t17a); + c[15 * stride] = CLIP(t15 + t16); + c[16 * stride] = CLIP(t15 - t16); + c[17 * stride] = CLIP(t14 - t17a); + c[18 * stride] = CLIP(t13 - t18); + c[19 * stride] = CLIP(t12 - t19a); + c[20 * stride] = CLIP(t11 - t20); + c[21 * stride] = CLIP(t10 - t21a); + c[22 * stride] = CLIP(t9 - t22); + c[23 * stride] = CLIP(t8 - t23a); + c[24 * stride] = CLIP(t7 - t24a); + c[25 * stride] = CLIP(t6 - t25); + c[26 * stride] = CLIP(t5 - t26a); + c[27 * stride] = CLIP(t4 - t27); + c[28 * stride] = CLIP(t3 - t28a); + c[29 * stride] = CLIP(t2 - t29); + c[30 * stride] = CLIP(t1 - t30a); + c[31 * stride] = CLIP(t0 - t31); +} + +static void inv_dct32_1d_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max) +{ + inv_dct32_1d_internal_c(c, stride, min, max, 0); +} + +static void inv_dct64_1d_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max) +{ + assert(stride > 0); + inv_dct32_1d_internal_c(c, stride << 1, min, max, 1); + + const int in1 = c[ 1 * stride], in3 = c[ 3 * stride]; + const int in5 = c[ 5 * stride], in7 = c[ 7 * stride]; + const int in9 = c[ 9 * stride], in11 = c[11 * stride]; + const int in13 = c[13 * stride], in15 = c[15 * stride]; + const int in17 = c[17 * stride], in19 = c[19 * stride]; + const int in21 = c[21 * stride], in23 = c[23 * stride]; + const int in25 = c[25 * stride], in27 = c[27 * stride]; + const int in29 = c[29 * stride], in31 = c[31 * stride]; + + int t32a = (in1 * 101 + 2048) >> 12; + int t33a = (in31 * -2824 + 2048) >> 12; + int t34a = (in17 * 1660 + 2048) >> 12; + int t35a = (in15 * -1474 + 2048) >> 12; + int t36a = (in9 * 897 + 2048) >> 12; + int t37a = (in23 * -2191 + 2048) >> 12; + int t38a = (in25 * 2359 + 2048) >> 12; + int t39a = (in7 * -700 + 2048) >> 12; + int t40a = (in5 * 501 + 2048) >> 12; + int t41a = (in27 * -2520 + 2048) >> 12; + int t42a = (in21 * 2019 + 2048) >> 12; + int t43a = (in11 * -1092 + 2048) >> 12; + int t44a = (in13 * 1285 + 2048) >> 12; + int t45a = (in19 * -1842 + 2048) >> 12; + int t46a = (in29 * 2675 + 2048) >> 12; + int t47a = (in3 * -301 + 2048) >> 12; + int t48a = (in3 * 4085 + 2048) >> 12; + int t49a = (in29 * 3102 + 2048) >> 12; + int t50a = (in19 * 3659 + 2048) >> 12; + int t51a = (in13 * 3889 + 2048) >> 12; + int t52a = (in11 * 3948 + 2048) >> 12; + int t53a = (in21 * 3564 + 2048) >> 12; + int t54a = (in27 * 3229 + 2048) >> 12; + int t55a = (in5 * 4065 + 2048) >> 12; + int t56a = (in7 * 4036 + 2048) >> 12; + int t57a = (in25 * 3349 + 2048) >> 12; + int t58a = (in23 * 3461 + 2048) >> 12; + int t59a = (in9 * 3996 + 2048) >> 12; + int t60a = (in15 * 3822 + 2048) >> 12; + int t61a = (in17 * 3745 + 2048) >> 12; + int t62a = (in31 * 2967 + 2048) >> 12; + int t63a = (in1 * 4095 + 2048) >> 12; + + int t32 = CLIP(t32a + t33a); + int t33 = CLIP(t32a - t33a); + int t34 = CLIP(t35a - t34a); + int t35 = CLIP(t35a + t34a); + int t36 = CLIP(t36a + t37a); + int t37 = CLIP(t36a - t37a); + int t38 = CLIP(t39a - t38a); + int t39 = CLIP(t39a + t38a); + int t40 = CLIP(t40a + t41a); + int t41 = CLIP(t40a - t41a); + int t42 = CLIP(t43a - t42a); + int t43 = CLIP(t43a + t42a); + int t44 = CLIP(t44a + t45a); + int t45 = CLIP(t44a - t45a); + int t46 = CLIP(t47a - t46a); + int t47 = CLIP(t47a + t46a); + int t48 = CLIP(t48a + t49a); + int t49 = CLIP(t48a - t49a); + int t50 = CLIP(t51a - t50a); + int t51 = CLIP(t51a + t50a); + int t52 = CLIP(t52a + t53a); + int t53 = CLIP(t52a - t53a); + int t54 = CLIP(t55a - t54a); + int t55 = CLIP(t55a + t54a); + int t56 = CLIP(t56a + t57a); + int t57 = CLIP(t56a - t57a); + int t58 = CLIP(t59a - t58a); + int t59 = CLIP(t59a + t58a); + int t60 = CLIP(t60a + t61a); + int t61 = CLIP(t60a - t61a); + int t62 = CLIP(t63a - t62a); + int t63 = CLIP(t63a + t62a); + + t33a = ((t33 * (4096 - 4076) + t62 * 401 + 2048) >> 12) - t33; + t34a = ((t34 * -401 + t61 * (4096 - 4076) + 2048) >> 12) - t61; + t37a = (t37 * -1299 + t58 * 1583 + 1024) >> 11; + t38a = (t38 * -1583 + t57 * -1299 + 1024) >> 11; + t41a = ((t41 * (4096 - 3612) + t54 * 1931 + 2048) >> 12) - t41; + t42a = ((t42 * -1931 + t53 * (4096 - 3612) + 2048) >> 12) - t53; + t45a = ((t45 * -1189 + t50 * (3920 - 4096) + 2048) >> 12) + t50; + t46a = ((t46 * (4096 - 3920) + t49 * -1189 + 2048) >> 12) - t46; + t49a = ((t46 * -1189 + t49 * (3920 - 4096) + 2048) >> 12) + t49; + t50a = ((t45 * (3920 - 4096) + t50 * 1189 + 2048) >> 12) + t45; + t53a = ((t42 * (4096 - 3612) + t53 * 1931 + 2048) >> 12) - t42; + t54a = ((t41 * 1931 + t54 * (3612 - 4096) + 2048) >> 12) + t54; + t57a = (t38 * -1299 + t57 * 1583 + 1024) >> 11; + t58a = (t37 * 1583 + t58 * 1299 + 1024) >> 11; + t61a = ((t34 * (4096 - 4076) + t61 * 401 + 2048) >> 12) - t34; + t62a = ((t33 * 401 + t62 * (4076 - 4096) + 2048) >> 12) + t62; + + t32a = CLIP(t32 + t35); + t33 = CLIP(t33a + t34a); + t34 = CLIP(t33a - t34a); + t35a = CLIP(t32 - t35); + t36a = CLIP(t39 - t36); + t37 = CLIP(t38a - t37a); + t38 = CLIP(t38a + t37a); + t39a = CLIP(t39 + t36); + t40a = CLIP(t40 + t43); + t41 = CLIP(t41a + t42a); + t42 = CLIP(t41a - t42a); + t43a = CLIP(t40 - t43); + t44a = CLIP(t47 - t44); + t45 = CLIP(t46a - t45a); + t46 = CLIP(t46a + t45a); + t47a = CLIP(t47 + t44); + t48a = CLIP(t48 + t51); + t49 = CLIP(t49a + t50a); + t50 = CLIP(t49a - t50a); + t51a = CLIP(t48 - t51); + t52a = CLIP(t55 - t52); + t53 = CLIP(t54a - t53a); + t54 = CLIP(t54a + t53a); + t55a = CLIP(t55 + t52); + t56a = CLIP(t56 + t59); + t57 = CLIP(t57a + t58a); + t58 = CLIP(t57a - t58a); + t59a = CLIP(t56 - t59); + t60a = CLIP(t63 - t60); + t61 = CLIP(t62a - t61a); + t62 = CLIP(t62a + t61a); + t63a = CLIP(t63 + t60); + + t34a = ((t34 * (4096 - 4017) + t61 * 799 + 2048) >> 12) - t34; + t35 = ((t35a * (4096 - 4017) + t60a * 799 + 2048) >> 12) - t35a; + t36 = ((t36a * -799 + t59a * (4096 - 4017) + 2048) >> 12) - t59a; + t37a = ((t37 * -799 + t58 * (4096 - 4017) + 2048) >> 12) - t58; + t42a = (t42 * -1138 + t53 * 1703 + 1024) >> 11; + t43 = (t43a * -1138 + t52a * 1703 + 1024) >> 11; + t44 = (t44a * -1703 + t51a * -1138 + 1024) >> 11; + t45a = (t45 * -1703 + t50 * -1138 + 1024) >> 11; + t50a = (t45 * -1138 + t50 * 1703 + 1024) >> 11; + t51 = (t44a * -1138 + t51a * 1703 + 1024) >> 11; + t52 = (t43a * 1703 + t52a * 1138 + 1024) >> 11; + t53a = (t42 * 1703 + t53 * 1138 + 1024) >> 11; + t58a = ((t37 * (4096 - 4017) + t58 * 799 + 2048) >> 12) - t37; + t59 = ((t36a * (4096 - 4017) + t59a * 799 + 2048) >> 12) - t36a; + t60 = ((t35a * 799 + t60a * (4017 - 4096) + 2048) >> 12) + t60a; + t61a = ((t34 * 799 + t61 * (4017 - 4096) + 2048) >> 12) + t61; + + t32 = CLIP(t32a + t39a); + t33a = CLIP(t33 + t38); + t34 = CLIP(t34a + t37a); + t35a = CLIP(t35 + t36); + t36a = CLIP(t35 - t36); + t37 = CLIP(t34a - t37a); + t38a = CLIP(t33 - t38); + t39 = CLIP(t32a - t39a); + t40 = CLIP(t47a - t40a); + t41a = CLIP(t46 - t41); + t42 = CLIP(t45a - t42a); + t43a = CLIP(t44 - t43); + t44a = CLIP(t44 + t43); + t45 = CLIP(t45a + t42a); + t46a = CLIP(t46 + t41); + t47 = CLIP(t47a + t40a); + t48 = CLIP(t48a + t55a); + t49a = CLIP(t49 + t54); + t50 = CLIP(t50a + t53a); + t51a = CLIP(t51 + t52); + t52a = CLIP(t51 - t52); + t53 = CLIP(t50a - t53a); + t54a = CLIP(t49 - t54); + t55 = CLIP(t48a - t55a); + t56 = CLIP(t63a - t56a); + t57a = CLIP(t62 - t57); + t58 = CLIP(t61a - t58a); + t59a = CLIP(t60 - t59); + t60a = CLIP(t60 + t59); + t61 = CLIP(t61a + t58a); + t62a = CLIP(t62 + t57); + t63 = CLIP(t63a + t56a); + + t36 = ((t36a * (4096 - 3784) + t59a * 1567 + 2048) >> 12) - t36a; + t37a = ((t37 * (4096 - 3784) + t58 * 1567 + 2048) >> 12) - t37; + t38 = ((t38a * (4096 - 3784) + t57a * 1567 + 2048) >> 12) - t38a; + t39a = ((t39 * (4096 - 3784) + t56 * 1567 + 2048) >> 12) - t39; + t40a = ((t40 * -1567 + t55 * (4096 - 3784) + 2048) >> 12) - t55; + t41 = ((t41a * -1567 + t54a * (4096 - 3784) + 2048) >> 12) - t54a; + t42a = ((t42 * -1567 + t53 * (4096 - 3784) + 2048) >> 12) - t53; + t43 = ((t43a * -1567 + t52a * (4096 - 3784) + 2048) >> 12) - t52a; + t52 = ((t43a * (4096 - 3784) + t52a * 1567 + 2048) >> 12) - t43a; + t53a = ((t42 * (4096 - 3784) + t53 * 1567 + 2048) >> 12) - t42; + t54 = ((t41a * (4096 - 3784) + t54a * 1567 + 2048) >> 12) - t41a; + t55a = ((t40 * (4096 - 3784) + t55 * 1567 + 2048) >> 12) - t40; + t56a = ((t39 * 1567 + t56 * (3784 - 4096) + 2048) >> 12) + t56; + t57 = ((t38a * 1567 + t57a * (3784 - 4096) + 2048) >> 12) + t57a; + t58a = ((t37 * 1567 + t58 * (3784 - 4096) + 2048) >> 12) + t58; + t59 = ((t36a * 1567 + t59a * (3784 - 4096) + 2048) >> 12) + t59a; + + t32a = CLIP(t32 + t47); + t33 = CLIP(t33a + t46a); + t34a = CLIP(t34 + t45); + t35 = CLIP(t35a + t44a); + t36a = CLIP(t36 + t43); + t37 = CLIP(t37a + t42a); + t38a = CLIP(t38 + t41); + t39 = CLIP(t39a + t40a); + t40 = CLIP(t39a - t40a); + t41a = CLIP(t38 - t41); + t42 = CLIP(t37a - t42a); + t43a = CLIP(t36 - t43); + t44 = CLIP(t35a - t44a); + t45a = CLIP(t34 - t45); + t46 = CLIP(t33a - t46a); + t47a = CLIP(t32 - t47); + t48a = CLIP(t63 - t48); + t49 = CLIP(t62a - t49a); + t50a = CLIP(t61 - t50); + t51 = CLIP(t60a - t51a); + t52a = CLIP(t59 - t52); + t53 = CLIP(t58a - t53a); + t54a = CLIP(t57 - t54); + t55 = CLIP(t56a - t55a); + t56 = CLIP(t56a + t55a); + t57a = CLIP(t57 + t54); + t58 = CLIP(t58a + t53a); + t59a = CLIP(t59 + t52); + t60 = CLIP(t60a + t51a); + t61a = CLIP(t61 + t50); + t62 = CLIP(t62a + t49a); + t63a = CLIP(t63 + t48); + + t40a = ((t55 - t40 ) * 181 + 128) >> 8; + t41 = ((t54a - t41a) * 181 + 128) >> 8; + t42a = ((t53 - t42 ) * 181 + 128) >> 8; + t43 = ((t52a - t43a) * 181 + 128) >> 8; + t44a = ((t51 - t44 ) * 181 + 128) >> 8; + t45 = ((t50a - t45a) * 181 + 128) >> 8; + t46a = ((t49 - t46 ) * 181 + 128) >> 8; + t47 = ((t48a - t47a) * 181 + 128) >> 8; + t48 = ((t47a + t48a) * 181 + 128) >> 8; + t49a = ((t46 + t49 ) * 181 + 128) >> 8; + t50 = ((t45a + t50a) * 181 + 128) >> 8; + t51a = ((t44 + t51 ) * 181 + 128) >> 8; + t52 = ((t43a + t52a) * 181 + 128) >> 8; + t53a = ((t42 + t53 ) * 181 + 128) >> 8; + t54 = ((t41a + t54a) * 181 + 128) >> 8; + t55a = ((t40 + t55 ) * 181 + 128) >> 8; + + const int t0 = c[ 0 * stride]; + const int t1 = c[ 2 * stride]; + const int t2 = c[ 4 * stride]; + const int t3 = c[ 6 * stride]; + const int t4 = c[ 8 * stride]; + const int t5 = c[10 * stride]; + const int t6 = c[12 * stride]; + const int t7 = c[14 * stride]; + const int t8 = c[16 * stride]; + const int t9 = c[18 * stride]; + const int t10 = c[20 * stride]; + const int t11 = c[22 * stride]; + const int t12 = c[24 * stride]; + const int t13 = c[26 * stride]; + const int t14 = c[28 * stride]; + const int t15 = c[30 * stride]; + const int t16 = c[32 * stride]; + const int t17 = c[34 * stride]; + const int t18 = c[36 * stride]; + const int t19 = c[38 * stride]; + const int t20 = c[40 * stride]; + const int t21 = c[42 * stride]; + const int t22 = c[44 * stride]; + const int t23 = c[46 * stride]; + const int t24 = c[48 * stride]; + const int t25 = c[50 * stride]; + const int t26 = c[52 * stride]; + const int t27 = c[54 * stride]; + const int t28 = c[56 * stride]; + const int t29 = c[58 * stride]; + const int t30 = c[60 * stride]; + const int t31 = c[62 * stride]; + + c[ 0 * stride] = CLIP(t0 + t63a); + c[ 1 * stride] = CLIP(t1 + t62); + c[ 2 * stride] = CLIP(t2 + t61a); + c[ 3 * stride] = CLIP(t3 + t60); + c[ 4 * stride] = CLIP(t4 + t59a); + c[ 5 * stride] = CLIP(t5 + t58); + c[ 6 * stride] = CLIP(t6 + t57a); + c[ 7 * stride] = CLIP(t7 + t56); + c[ 8 * stride] = CLIP(t8 + t55a); + c[ 9 * stride] = CLIP(t9 + t54); + c[10 * stride] = CLIP(t10 + t53a); + c[11 * stride] = CLIP(t11 + t52); + c[12 * stride] = CLIP(t12 + t51a); + c[13 * stride] = CLIP(t13 + t50); + c[14 * stride] = CLIP(t14 + t49a); + c[15 * stride] = CLIP(t15 + t48); + c[16 * stride] = CLIP(t16 + t47); + c[17 * stride] = CLIP(t17 + t46a); + c[18 * stride] = CLIP(t18 + t45); + c[19 * stride] = CLIP(t19 + t44a); + c[20 * stride] = CLIP(t20 + t43); + c[21 * stride] = CLIP(t21 + t42a); + c[22 * stride] = CLIP(t22 + t41); + c[23 * stride] = CLIP(t23 + t40a); + c[24 * stride] = CLIP(t24 + t39); + c[25 * stride] = CLIP(t25 + t38a); + c[26 * stride] = CLIP(t26 + t37); + c[27 * stride] = CLIP(t27 + t36a); + c[28 * stride] = CLIP(t28 + t35); + c[29 * stride] = CLIP(t29 + t34a); + c[30 * stride] = CLIP(t30 + t33); + c[31 * stride] = CLIP(t31 + t32a); + c[32 * stride] = CLIP(t31 - t32a); + c[33 * stride] = CLIP(t30 - t33); + c[34 * stride] = CLIP(t29 - t34a); + c[35 * stride] = CLIP(t28 - t35); + c[36 * stride] = CLIP(t27 - t36a); + c[37 * stride] = CLIP(t26 - t37); + c[38 * stride] = CLIP(t25 - t38a); + c[39 * stride] = CLIP(t24 - t39); + c[40 * stride] = CLIP(t23 - t40a); + c[41 * stride] = CLIP(t22 - t41); + c[42 * stride] = CLIP(t21 - t42a); + c[43 * stride] = CLIP(t20 - t43); + c[44 * stride] = CLIP(t19 - t44a); + c[45 * stride] = CLIP(t18 - t45); + c[46 * stride] = CLIP(t17 - t46a); + c[47 * stride] = CLIP(t16 - t47); + c[48 * stride] = CLIP(t15 - t48); + c[49 * stride] = CLIP(t14 - t49a); + c[50 * stride] = CLIP(t13 - t50); + c[51 * stride] = CLIP(t12 - t51a); + c[52 * stride] = CLIP(t11 - t52); + c[53 * stride] = CLIP(t10 - t53a); + c[54 * stride] = CLIP(t9 - t54); + c[55 * stride] = CLIP(t8 - t55a); + c[56 * stride] = CLIP(t7 - t56); + c[57 * stride] = CLIP(t6 - t57a); + c[58 * stride] = CLIP(t5 - t58); + c[59 * stride] = CLIP(t4 - t59a); + c[60 * stride] = CLIP(t3 - t60); + c[61 * stride] = CLIP(t2 - t61a); + c[62 * stride] = CLIP(t1 - t62); + c[63 * stride] = CLIP(t0 - t63a); +} + +static NOINLINE void +inv_adst4_1d_internal_c(const int32_t *const in, const ptrdiff_t in_s, + const int min, const int max, + int32_t *const out, const ptrdiff_t out_s) +{ + assert(in_s > 0 && out_s != 0); + const int in0 = in[0 * in_s], in1 = in[1 * in_s]; + const int in2 = in[2 * in_s], in3 = in[3 * in_s]; + + out[0 * out_s] = (( 1321 * in0 + (3803 - 4096) * in2 + + (2482 - 4096) * in3 + (3344 - 4096) * in1 + 2048) >> 12) + + in2 + in3 + in1; + out[1 * out_s] = (((2482 - 4096) * in0 - 1321 * in2 - + (3803 - 4096) * in3 + (3344 - 4096) * in1 + 2048) >> 12) + + in0 - in3 + in1; + out[2 * out_s] = (209 * (in0 - in2 + in3) + 128) >> 8; + out[3 * out_s] = (((3803 - 4096) * in0 + (2482 - 4096) * in2 - + 1321 * in3 - (3344 - 4096) * in1 + 2048) >> 12) + + in0 + in2 - in1; +} + +static NOINLINE void +inv_adst8_1d_internal_c(const int32_t *const in, const ptrdiff_t in_s, + const int min, const int max, + int32_t *const out, const ptrdiff_t out_s) +{ + assert(in_s > 0 && out_s != 0); + const int in0 = in[0 * in_s], in1 = in[1 * in_s]; + const int in2 = in[2 * in_s], in3 = in[3 * in_s]; + const int in4 = in[4 * in_s], in5 = in[5 * in_s]; + const int in6 = in[6 * in_s], in7 = in[7 * in_s]; + + const int t0a = (((4076 - 4096) * in7 + 401 * in0 + 2048) >> 12) + in7; + const int t1a = (( 401 * in7 - (4076 - 4096) * in0 + 2048) >> 12) - in0; + const int t2a = (((3612 - 4096) * in5 + 1931 * in2 + 2048) >> 12) + in5; + const int t3a = (( 1931 * in5 - (3612 - 4096) * in2 + 2048) >> 12) - in2; + int t4a = ( 1299 * in3 + 1583 * in4 + 1024) >> 11; + int t5a = ( 1583 * in3 - 1299 * in4 + 1024) >> 11; + int t6a = (( 1189 * in1 + (3920 - 4096) * in6 + 2048) >> 12) + in6; + int t7a = (((3920 - 4096) * in1 - 1189 * in6 + 2048) >> 12) + in1; + + const int t0 = CLIP(t0a + t4a); + const int t1 = CLIP(t1a + t5a); + int t2 = CLIP(t2a + t6a); + int t3 = CLIP(t3a + t7a); + const int t4 = CLIP(t0a - t4a); + const int t5 = CLIP(t1a - t5a); + int t6 = CLIP(t2a - t6a); + int t7 = CLIP(t3a - t7a); + + t4a = (((3784 - 4096) * t4 + 1567 * t5 + 2048) >> 12) + t4; + t5a = (( 1567 * t4 - (3784 - 4096) * t5 + 2048) >> 12) - t5; + t6a = (((3784 - 4096) * t7 - 1567 * t6 + 2048) >> 12) + t7; + t7a = (( 1567 * t7 + (3784 - 4096) * t6 + 2048) >> 12) + t6; + + out[0 * out_s] = CLIP(t0 + t2 ); + out[7 * out_s] = -CLIP(t1 + t3 ); + t2 = CLIP(t0 - t2 ); + t3 = CLIP(t1 - t3 ); + out[1 * out_s] = -CLIP(t4a + t6a); + out[6 * out_s] = CLIP(t5a + t7a); + t6 = CLIP(t4a - t6a); + t7 = CLIP(t5a - t7a); + + out[3 * out_s] = -(((t2 + t3) * 181 + 128) >> 8); + out[4 * out_s] = ((t2 - t3) * 181 + 128) >> 8; + out[2 * out_s] = ((t6 + t7) * 181 + 128) >> 8; + out[5 * out_s] = -(((t6 - t7) * 181 + 128) >> 8); +} + +static NOINLINE void +inv_adst16_1d_internal_c(const int32_t *const in, const ptrdiff_t in_s, + const int min, const int max, + int32_t *const out, const ptrdiff_t out_s) +{ + assert(in_s > 0 && out_s != 0); + const int in0 = in[ 0 * in_s], in1 = in[ 1 * in_s]; + const int in2 = in[ 2 * in_s], in3 = in[ 3 * in_s]; + const int in4 = in[ 4 * in_s], in5 = in[ 5 * in_s]; + const int in6 = in[ 6 * in_s], in7 = in[ 7 * in_s]; + const int in8 = in[ 8 * in_s], in9 = in[ 9 * in_s]; + const int in10 = in[10 * in_s], in11 = in[11 * in_s]; + const int in12 = in[12 * in_s], in13 = in[13 * in_s]; + const int in14 = in[14 * in_s], in15 = in[15 * in_s]; + + int t0 = ((in15 * (4091 - 4096) + in0 * 201 + 2048) >> 12) + in15; + int t1 = ((in15 * 201 - in0 * (4091 - 4096) + 2048) >> 12) - in0; + int t2 = ((in13 * (3973 - 4096) + in2 * 995 + 2048) >> 12) + in13; + int t3 = ((in13 * 995 - in2 * (3973 - 4096) + 2048) >> 12) - in2; + int t4 = ((in11 * (3703 - 4096) + in4 * 1751 + 2048) >> 12) + in11; + int t5 = ((in11 * 1751 - in4 * (3703 - 4096) + 2048) >> 12) - in4; + int t6 = (in9 * 1645 + in6 * 1220 + 1024) >> 11; + int t7 = (in9 * 1220 - in6 * 1645 + 1024) >> 11; + int t8 = ((in7 * 2751 + in8 * (3035 - 4096) + 2048) >> 12) + in8; + int t9 = ((in7 * (3035 - 4096) - in8 * 2751 + 2048) >> 12) + in7; + int t10 = ((in5 * 2106 + in10 * (3513 - 4096) + 2048) >> 12) + in10; + int t11 = ((in5 * (3513 - 4096) - in10 * 2106 + 2048) >> 12) + in5; + int t12 = ((in3 * 1380 + in12 * (3857 - 4096) + 2048) >> 12) + in12; + int t13 = ((in3 * (3857 - 4096) - in12 * 1380 + 2048) >> 12) + in3; + int t14 = ((in1 * 601 + in14 * (4052 - 4096) + 2048) >> 12) + in14; + int t15 = ((in1 * (4052 - 4096) - in14 * 601 + 2048) >> 12) + in1; + + int t0a = CLIP(t0 + t8 ); + int t1a = CLIP(t1 + t9 ); + int t2a = CLIP(t2 + t10); + int t3a = CLIP(t3 + t11); + int t4a = CLIP(t4 + t12); + int t5a = CLIP(t5 + t13); + int t6a = CLIP(t6 + t14); + int t7a = CLIP(t7 + t15); + int t8a = CLIP(t0 - t8 ); + int t9a = CLIP(t1 - t9 ); + int t10a = CLIP(t2 - t10); + int t11a = CLIP(t3 - t11); + int t12a = CLIP(t4 - t12); + int t13a = CLIP(t5 - t13); + int t14a = CLIP(t6 - t14); + int t15a = CLIP(t7 - t15); + + t8 = ((t8a * (4017 - 4096) + t9a * 799 + 2048) >> 12) + t8a; + t9 = ((t8a * 799 - t9a * (4017 - 4096) + 2048) >> 12) - t9a; + t10 = ((t10a * 2276 + t11a * (3406 - 4096) + 2048) >> 12) + t11a; + t11 = ((t10a * (3406 - 4096) - t11a * 2276 + 2048) >> 12) + t10a; + t12 = ((t13a * (4017 - 4096) - t12a * 799 + 2048) >> 12) + t13a; + t13 = ((t13a * 799 + t12a * (4017 - 4096) + 2048) >> 12) + t12a; + t14 = ((t15a * 2276 - t14a * (3406 - 4096) + 2048) >> 12) - t14a; + t15 = ((t15a * (3406 - 4096) + t14a * 2276 + 2048) >> 12) + t15a; + + t0 = CLIP(t0a + t4a); + t1 = CLIP(t1a + t5a); + t2 = CLIP(t2a + t6a); + t3 = CLIP(t3a + t7a); + t4 = CLIP(t0a - t4a); + t5 = CLIP(t1a - t5a); + t6 = CLIP(t2a - t6a); + t7 = CLIP(t3a - t7a); + t8a = CLIP(t8 + t12); + t9a = CLIP(t9 + t13); + t10a = CLIP(t10 + t14); + t11a = CLIP(t11 + t15); + t12a = CLIP(t8 - t12); + t13a = CLIP(t9 - t13); + t14a = CLIP(t10 - t14); + t15a = CLIP(t11 - t15); + + t4a = ((t4 * (3784 - 4096) + t5 * 1567 + 2048) >> 12) + t4; + t5a = ((t4 * 1567 - t5 * (3784 - 4096) + 2048) >> 12) - t5; + t6a = ((t7 * (3784 - 4096) - t6 * 1567 + 2048) >> 12) + t7; + t7a = ((t7 * 1567 + t6 * (3784 - 4096) + 2048) >> 12) + t6; + t12 = ((t12a * (3784 - 4096) + t13a * 1567 + 2048) >> 12) + t12a; + t13 = ((t12a * 1567 - t13a * (3784 - 4096) + 2048) >> 12) - t13a; + t14 = ((t15a * (3784 - 4096) - t14a * 1567 + 2048) >> 12) + t15a; + t15 = ((t15a * 1567 + t14a * (3784 - 4096) + 2048) >> 12) + t14a; + + out[ 0 * out_s] = CLIP(t0 + t2 ); + out[15 * out_s] = -CLIP(t1 + t3 ); + t2a = CLIP(t0 - t2 ); + t3a = CLIP(t1 - t3 ); + out[ 3 * out_s] = -CLIP(t4a + t6a ); + out[12 * out_s] = CLIP(t5a + t7a ); + t6 = CLIP(t4a - t6a ); + t7 = CLIP(t5a - t7a ); + out[ 1 * out_s] = -CLIP(t8a + t10a); + out[14 * out_s] = CLIP(t9a + t11a); + t10 = CLIP(t8a - t10a); + t11 = CLIP(t9a - t11a); + out[ 2 * out_s] = CLIP(t12 + t14 ); + out[13 * out_s] = -CLIP(t13 + t15 ); + t14a = CLIP(t12 - t14 ); + t15a = CLIP(t13 - t15 ); + + out[ 7 * out_s] = -(((t2a + t3a) * 181 + 128) >> 8); + out[ 8 * out_s] = ((t2a - t3a) * 181 + 128) >> 8; + out[ 4 * out_s] = ((t6 + t7) * 181 + 128) >> 8; + out[11 * out_s] = -(((t6 - t7) * 181 + 128) >> 8); + out[ 6 * out_s] = ((t10 + t11) * 181 + 128) >> 8; + out[ 9 * out_s] = -(((t10 - t11) * 181 + 128) >> 8); + out[ 5 * out_s] = -(((t14a + t15a) * 181 + 128) >> 8); + out[10 * out_s] = ((t14a - t15a) * 181 + 128) >> 8; +} + +#define inv_adst_1d(sz) \ +static void inv_adst##sz##_1d_c(int32_t *const c, const ptrdiff_t stride, \ + const int min, const int max) \ +{ \ + inv_adst##sz##_1d_internal_c(c, stride, min, max, c, stride); \ +} \ +static void inv_flipadst##sz##_1d_c(int32_t *const c, const ptrdiff_t stride, \ + const int min, const int max) \ +{ \ + inv_adst##sz##_1d_internal_c(c, stride, min, max, \ + &c[(sz - 1) * stride], -stride); \ +} + +inv_adst_1d( 4) +inv_adst_1d( 8) +inv_adst_1d(16) + +#undef inv_adst_1d + +static void inv_identity4_1d_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max) +{ + assert(stride > 0); + for (int i = 0; i < 4; i++) { + const int in = c[stride * i]; + c[stride * i] = in + ((in * 1697 + 2048) >> 12); + } +} + +static void inv_identity8_1d_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max) +{ + assert(stride > 0); + for (int i = 0; i < 8; i++) + c[stride * i] *= 2; +} + +static void inv_identity16_1d_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max) +{ + assert(stride > 0); + for (int i = 0; i < 16; i++) { + const int in = c[stride * i]; + c[stride * i] = 2 * in + ((in * 1697 + 1024) >> 11); + } +} + +static void inv_identity32_1d_c(int32_t *const c, const ptrdiff_t stride, + const int min, const int max) +{ + assert(stride > 0); + for (int i = 0; i < 32; i++) + c[stride * i] *= 4; +} + +const itx_1d_fn dav1d_tx1d_fns[N_TX_SIZES][N_TX_1D_TYPES] = { + [TX_4X4] = { + [DCT] = inv_dct4_1d_c, + [ADST] = inv_adst4_1d_c, + [FLIPADST] = inv_flipadst4_1d_c, + [IDENTITY] = inv_identity4_1d_c, + }, [TX_8X8] = { + [DCT] = inv_dct8_1d_c, + [ADST] = inv_adst8_1d_c, + [FLIPADST] = inv_flipadst8_1d_c, + [IDENTITY] = inv_identity8_1d_c, + }, [TX_16X16] = { + [DCT] = inv_dct16_1d_c, + [ADST] = inv_adst16_1d_c, + [FLIPADST] = inv_flipadst16_1d_c, + [IDENTITY] = inv_identity16_1d_c, + }, [TX_32X32] = { + [DCT] = inv_dct32_1d_c, + [IDENTITY] = inv_identity32_1d_c, + }, [TX_64X64] = { + [DCT] = inv_dct64_1d_c, + }, +}; + +const uint8_t /* enum Tx1dType */ dav1d_tx1d_types[N_TX_TYPES][2] = { + [DCT_DCT] = { DCT, DCT }, + [ADST_DCT] = { ADST, DCT }, + [DCT_ADST] = { DCT, ADST }, + [ADST_ADST] = { ADST, ADST }, + [FLIPADST_DCT] = { FLIPADST, DCT }, + [DCT_FLIPADST] = { DCT, FLIPADST }, + [FLIPADST_FLIPADST] = { FLIPADST, FLIPADST }, + [ADST_FLIPADST] = { ADST, FLIPADST }, + [FLIPADST_ADST] = { FLIPADST, ADST }, + [IDTX] = { IDENTITY, IDENTITY }, + [V_DCT] = { DCT, IDENTITY }, + [H_DCT] = { IDENTITY, DCT }, + [V_ADST] = { ADST, IDENTITY }, + [H_ADST] = { IDENTITY, ADST }, + [V_FLIPADST] = { FLIPADST, IDENTITY }, + [H_FLIPADST] = { IDENTITY, FLIPADST }, +}; + +#if !(HAVE_ASM && TRIM_DSP_FUNCTIONS && ( \ + ARCH_AARCH64 || \ + (ARCH_ARM && (defined(__ARM_NEON) || defined(__APPLE__) || defined(_WIN32))) \ +)) +void dav1d_inv_wht4_1d_c(int32_t *const c, const ptrdiff_t stride) { + assert(stride > 0); + const int in0 = c[0 * stride], in1 = c[1 * stride]; + const int in2 = c[2 * stride], in3 = c[3 * stride]; + + const int t0 = in0 + in1; + const int t2 = in2 - in3; + const int t4 = (t0 - t2) >> 1; + const int t3 = t4 - in3; + const int t1 = t4 - in1; + + c[0 * stride] = t0 - t3; + c[1 * stride] = t3; + c[2 * stride] = t1; + c[3 * stride] = t2 + t1; +} +#endif diff --git a/3rdparty/dav1d/src/itx_1d.h b/3rdparty/dav1d/src/itx_1d.h new file mode 100644 index 0000000..880ac99 --- /dev/null +++ b/3rdparty/dav1d/src/itx_1d.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2018-2019, VideoLAN and dav1d authors + * Copyright © 2018-2019, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include +#include + +#include "src/levels.h" + +#ifndef DAV1D_SRC_ITX_1D_H +#define DAV1D_SRC_ITX_1D_H + +enum Tx1dType { + DCT, + ADST, + IDENTITY, + FLIPADST, + N_TX_1D_TYPES, +}; + +#define decl_itx_1d_fn(name) \ +void (name)(int32_t *c, ptrdiff_t stride, int min, int max) +typedef decl_itx_1d_fn(*itx_1d_fn); + +EXTERN const itx_1d_fn dav1d_tx1d_fns[N_TX_SIZES][N_TX_1D_TYPES]; +EXTERN const uint8_t /* enum Tx1dType */ dav1d_tx1d_types[N_TX_TYPES][2]; + +void dav1d_inv_wht4_1d_c(int32_t *c, ptrdiff_t stride); + +#endif /* DAV1D_SRC_ITX_1D_H */ diff --git a/3rdparty/dav1d/src/itx_tmpl.c b/3rdparty/dav1d/src/itx_tmpl.c new file mode 100644 index 0000000..bafe0a8 --- /dev/null +++ b/3rdparty/dav1d/src/itx_tmpl.c @@ -0,0 +1,311 @@ +/* + * Copyright © 2018-2019, VideoLAN and dav1d authors + * Copyright © 2018-2019, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "common/attributes.h" +#include "common/intops.h" + +#include "src/itx.h" +#include "src/itx_1d.h" +#include "src/scan.h" +#include "src/tables.h" + +static NOINLINE void +inv_txfm_add_c(pixel *dst, const ptrdiff_t stride, coef *const coeff, + const int eob, const /*enum RectTxfmSize*/ int tx, const int shift, + const enum TxfmType txtp HIGHBD_DECL_SUFFIX) +{ + const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[tx]; + const int w = 4 * t_dim->w, h = 4 * t_dim->h; + const int has_dconly = txtp == DCT_DCT; + assert(w >= 4 && w <= 64); + assert(h >= 4 && h <= 64); + assert(eob >= 0); + + const int is_rect2 = w * 2 == h || h * 2 == w; + const int rnd = (1 << shift) >> 1; + + if (eob < has_dconly) { + int dc = coeff[0]; + coeff[0] = 0; + if (is_rect2) + dc = (dc * 181 + 128) >> 8; + dc = (dc * 181 + 128) >> 8; + dc = (dc + rnd) >> shift; + dc = (dc * 181 + 128 + 2048) >> 12; + for (int y = 0; y < h; y++, dst += PXSTRIDE(stride)) + for (int x = 0; x < w; x++) + dst[x] = iclip_pixel(dst[x] + dc); + return; + } + + const uint8_t *const txtps = dav1d_tx1d_types[txtp]; + const itx_1d_fn first_1d_fn = dav1d_tx1d_fns[t_dim->lw][txtps[0]]; + const itx_1d_fn second_1d_fn = dav1d_tx1d_fns[t_dim->lh][txtps[1]]; + const int sh = imin(h, 32), sw = imin(w, 32); +#if BITDEPTH == 8 + const int row_clip_min = INT16_MIN; + const int col_clip_min = INT16_MIN; +#else + const int row_clip_min = (int) ((unsigned) ~bitdepth_max << 7); + const int col_clip_min = (int) ((unsigned) ~bitdepth_max << 5); +#endif + const int row_clip_max = ~row_clip_min; + const int col_clip_max = ~col_clip_min; + + int32_t tmp[64 * 64], *c = tmp; + int last_nonzero_col; // in first 1d itx + if (txtps[1] == IDENTITY && txtps[0] != IDENTITY) { + last_nonzero_col = imin(sh - 1, eob); + } else if (txtps[0] == IDENTITY && txtps[1] != IDENTITY) { + last_nonzero_col = eob >> (t_dim->lw + 2); + } else { + last_nonzero_col = dav1d_last_nonzero_col_from_eob[tx][eob]; + } + assert(last_nonzero_col < sh); + for (int y = 0; y <= last_nonzero_col; y++, c += w) { + if (is_rect2) + for (int x = 0; x < sw; x++) + c[x] = (coeff[y + x * sh] * 181 + 128) >> 8; + else + for (int x = 0; x < sw; x++) + c[x] = coeff[y + x * sh]; + first_1d_fn(c, 1, row_clip_min, row_clip_max); + } + if (last_nonzero_col + 1 < sh) + memset(c, 0, sizeof(*c) * (sh - last_nonzero_col - 1) * w); + + memset(coeff, 0, sizeof(*coeff) * sw * sh); + for (int i = 0; i < w * sh; i++) + tmp[i] = iclip((tmp[i] + rnd) >> shift, col_clip_min, col_clip_max); + + for (int x = 0; x < w; x++) + second_1d_fn(&tmp[x], w, col_clip_min, col_clip_max); + + c = tmp; + for (int y = 0; y < h; y++, dst += PXSTRIDE(stride)) + for (int x = 0; x < w; x++) + dst[x] = iclip_pixel(dst[x] + ((*c++ + 8) >> 4)); +} + +#define inv_txfm_fn(type1, type2, type, pfx, w, h, shift) \ +static void \ +inv_txfm_add_##type1##_##type2##_##w##x##h##_c(pixel *dst, \ + const ptrdiff_t stride, \ + coef *const coeff, \ + const int eob \ + HIGHBD_DECL_SUFFIX) \ +{ \ + inv_txfm_add_c(dst, stride, coeff, eob, pfx##TX_##w##X##h, shift, type \ + HIGHBD_TAIL_SUFFIX); \ +} + +#define inv_txfm_fn64(pfx, w, h, shift) \ +inv_txfm_fn(dct, dct, DCT_DCT, pfx, w, h, shift) + +#define inv_txfm_fn32(pfx, w, h, shift) \ +inv_txfm_fn64(pfx, w, h, shift) \ +inv_txfm_fn(identity, identity, IDTX, pfx, w, h, shift) + +#define inv_txfm_fn16(pfx, w, h, shift) \ +inv_txfm_fn32(pfx, w, h, shift) \ +inv_txfm_fn(adst, dct, ADST_DCT, pfx, w, h, shift) \ +inv_txfm_fn(dct, adst, DCT_ADST, pfx, w, h, shift) \ +inv_txfm_fn(adst, adst, ADST_ADST, pfx, w, h, shift) \ +inv_txfm_fn(dct, flipadst, DCT_FLIPADST, pfx, w, h, shift) \ +inv_txfm_fn(flipadst, dct, FLIPADST_DCT, pfx, w, h, shift) \ +inv_txfm_fn(adst, flipadst, ADST_FLIPADST, pfx, w, h, shift) \ +inv_txfm_fn(flipadst, adst, FLIPADST_ADST, pfx, w, h, shift) \ +inv_txfm_fn(flipadst, flipadst, FLIPADST_FLIPADST, pfx, w, h, shift) \ +inv_txfm_fn(identity, dct, H_DCT, pfx, w, h, shift) \ +inv_txfm_fn(dct, identity, V_DCT, pfx, w, h, shift) \ + +#define inv_txfm_fn84(pfx, w, h, shift) \ +inv_txfm_fn16(pfx, w, h, shift) \ +inv_txfm_fn(identity, flipadst, H_FLIPADST, pfx, w, h, shift) \ +inv_txfm_fn(flipadst, identity, V_FLIPADST, pfx, w, h, shift) \ +inv_txfm_fn(identity, adst, H_ADST, pfx, w, h, shift) \ +inv_txfm_fn(adst, identity, V_ADST, pfx, w, h, shift) \ + +inv_txfm_fn84( , 4, 4, 0) +inv_txfm_fn84(R, 4, 8, 0) +inv_txfm_fn84(R, 4, 16, 1) +inv_txfm_fn84(R, 8, 4, 0) +inv_txfm_fn84( , 8, 8, 1) +inv_txfm_fn84(R, 8, 16, 1) +inv_txfm_fn32(R, 8, 32, 2) +inv_txfm_fn84(R, 16, 4, 1) +inv_txfm_fn84(R, 16, 8, 1) +inv_txfm_fn16( , 16, 16, 2) +inv_txfm_fn32(R, 16, 32, 1) +inv_txfm_fn64(R, 16, 64, 2) +inv_txfm_fn32(R, 32, 8, 2) +inv_txfm_fn32(R, 32, 16, 1) +inv_txfm_fn32( , 32, 32, 2) +inv_txfm_fn64(R, 32, 64, 1) +inv_txfm_fn64(R, 64, 16, 2) +inv_txfm_fn64(R, 64, 32, 1) +inv_txfm_fn64( , 64, 64, 2) + +#if !(HAVE_ASM && TRIM_DSP_FUNCTIONS && ( \ + ARCH_AARCH64 || \ + (ARCH_ARM && (defined(__ARM_NEON) || defined(__APPLE__) || defined(_WIN32))) \ +)) +static void inv_txfm_add_wht_wht_4x4_c(pixel *dst, const ptrdiff_t stride, + coef *const coeff, const int eob + HIGHBD_DECL_SUFFIX) +{ + int32_t tmp[4 * 4], *c = tmp; + for (int y = 0; y < 4; y++, c += 4) { + for (int x = 0; x < 4; x++) + c[x] = coeff[y + x * 4] >> 2; + dav1d_inv_wht4_1d_c(c, 1); + } + memset(coeff, 0, sizeof(*coeff) * 4 * 4); + + for (int x = 0; x < 4; x++) + dav1d_inv_wht4_1d_c(&tmp[x], 4); + + c = tmp; + for (int y = 0; y < 4; y++, dst += PXSTRIDE(stride)) + for (int x = 0; x < 4; x++) + dst[x] = iclip_pixel(dst[x] + *c++); +} +#endif + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM +#include "src/arm/itx.h" +#elif ARCH_LOONGARCH64 +#include "src/loongarch/itx.h" +#elif ARCH_PPC64LE +#include "src/ppc/itx.h" +#elif ARCH_RISCV +#include "src/riscv/itx.h" +#elif ARCH_X86 +#include "src/x86/itx.h" +#endif +#endif + +COLD void bitfn(dav1d_itx_dsp_init)(Dav1dInvTxfmDSPContext *const c, int bpc) { +#define assign_itx_all_fn64(w, h, pfx) \ + c->itxfm_add[pfx##TX_##w##X##h][DCT_DCT ] = \ + inv_txfm_add_dct_dct_##w##x##h##_c + +#define assign_itx_all_fn32(w, h, pfx) \ + assign_itx_all_fn64(w, h, pfx); \ + c->itxfm_add[pfx##TX_##w##X##h][IDTX] = \ + inv_txfm_add_identity_identity_##w##x##h##_c + +#define assign_itx_all_fn16(w, h, pfx) \ + assign_itx_all_fn32(w, h, pfx); \ + c->itxfm_add[pfx##TX_##w##X##h][DCT_ADST ] = \ + inv_txfm_add_adst_dct_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][ADST_DCT ] = \ + inv_txfm_add_dct_adst_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][ADST_ADST] = \ + inv_txfm_add_adst_adst_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][ADST_FLIPADST] = \ + inv_txfm_add_flipadst_adst_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][FLIPADST_ADST] = \ + inv_txfm_add_adst_flipadst_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][DCT_FLIPADST] = \ + inv_txfm_add_flipadst_dct_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][FLIPADST_DCT] = \ + inv_txfm_add_dct_flipadst_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][FLIPADST_FLIPADST] = \ + inv_txfm_add_flipadst_flipadst_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][H_DCT] = \ + inv_txfm_add_dct_identity_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][V_DCT] = \ + inv_txfm_add_identity_dct_##w##x##h##_c + +#define assign_itx_all_fn84(w, h, pfx) \ + assign_itx_all_fn16(w, h, pfx); \ + c->itxfm_add[pfx##TX_##w##X##h][H_FLIPADST] = \ + inv_txfm_add_flipadst_identity_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][V_FLIPADST] = \ + inv_txfm_add_identity_flipadst_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][H_ADST] = \ + inv_txfm_add_adst_identity_##w##x##h##_c; \ + c->itxfm_add[pfx##TX_##w##X##h][V_ADST] = \ + inv_txfm_add_identity_adst_##w##x##h##_c; \ + +#if !(HAVE_ASM && TRIM_DSP_FUNCTIONS && ( \ + ARCH_AARCH64 || \ + (ARCH_ARM && (defined(__ARM_NEON) || defined(__APPLE__) || defined(_WIN32))) \ +)) + c->itxfm_add[TX_4X4][WHT_WHT] = inv_txfm_add_wht_wht_4x4_c; +#endif + assign_itx_all_fn84( 4, 4, ); + assign_itx_all_fn84( 4, 8, R); + assign_itx_all_fn84( 4, 16, R); + assign_itx_all_fn84( 8, 4, R); + assign_itx_all_fn84( 8, 8, ); + assign_itx_all_fn84( 8, 16, R); + assign_itx_all_fn32( 8, 32, R); + assign_itx_all_fn84(16, 4, R); + assign_itx_all_fn84(16, 8, R); + assign_itx_all_fn16(16, 16, ); + assign_itx_all_fn32(16, 32, R); + assign_itx_all_fn64(16, 64, R); + assign_itx_all_fn32(32, 8, R); + assign_itx_all_fn32(32, 16, R); + assign_itx_all_fn32(32, 32, ); + assign_itx_all_fn64(32, 64, R); + assign_itx_all_fn64(64, 16, R); + assign_itx_all_fn64(64, 32, R); + assign_itx_all_fn64(64, 64, ); + + int all_simd = 0; +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM + itx_dsp_init_arm(c, bpc, &all_simd); +#endif +#if ARCH_LOONGARCH64 + itx_dsp_init_loongarch(c, bpc); +#endif +#if ARCH_PPC64LE + itx_dsp_init_ppc(c, bpc); +#endif +#if ARCH_RISCV + itx_dsp_init_riscv(c, bpc); +#endif +#if ARCH_X86 + itx_dsp_init_x86(c, bpc, &all_simd); +#endif +#endif + + if (!all_simd) + dav1d_init_last_nonzero_col_from_eob_tables(); +} diff --git a/3rdparty/dav1d/src/levels.h b/3rdparty/dav1d/src/levels.h new file mode 100644 index 0000000..0f510e9 --- /dev/null +++ b/3rdparty/dav1d/src/levels.h @@ -0,0 +1,289 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_LEVELS_H +#define DAV1D_SRC_LEVELS_H + +#include + +#include "dav1d/headers.h" +#include "common/attributes.h" + +enum ObuMetaType { + OBU_META_HDR_CLL = 1, + OBU_META_HDR_MDCV = 2, + OBU_META_SCALABILITY = 3, + OBU_META_ITUT_T35 = 4, + OBU_META_TIMECODE = 5, +}; + +enum TxfmSize { + TX_4X4, + TX_8X8, + TX_16X16, + TX_32X32, + TX_64X64, + N_TX_SIZES, +}; + +enum BlockLevel { + BL_128X128, + BL_64X64, + BL_32X32, + BL_16X16, + BL_8X8, + N_BL_LEVELS, +}; + +enum RectTxfmSize { + RTX_4X8 = N_TX_SIZES, + RTX_8X4, + RTX_8X16, + RTX_16X8, + RTX_16X32, + RTX_32X16, + RTX_32X64, + RTX_64X32, + RTX_4X16, + RTX_16X4, + RTX_8X32, + RTX_32X8, + RTX_16X64, + RTX_64X16, + N_RECT_TX_SIZES +}; + +enum TxfmType { + DCT_DCT, // DCT in both horizontal and vertical + ADST_DCT, // ADST in vertical, DCT in horizontal + DCT_ADST, // DCT in vertical, ADST in horizontal + ADST_ADST, // ADST in both directions + FLIPADST_DCT, + DCT_FLIPADST, + FLIPADST_FLIPADST, + ADST_FLIPADST, + FLIPADST_ADST, + IDTX, + V_DCT, + H_DCT, + V_ADST, + H_ADST, + V_FLIPADST, + H_FLIPADST, + N_TX_TYPES, + WHT_WHT = N_TX_TYPES, + N_TX_TYPES_PLUS_LL, +}; + +enum TxClass { + TX_CLASS_2D, + TX_CLASS_H, + TX_CLASS_V, +}; + +enum IntraPredMode { + DC_PRED, + VERT_PRED, + HOR_PRED, + DIAG_DOWN_LEFT_PRED, + DIAG_DOWN_RIGHT_PRED, + VERT_RIGHT_PRED, + HOR_DOWN_PRED, + HOR_UP_PRED, + VERT_LEFT_PRED, + SMOOTH_PRED, + SMOOTH_V_PRED, + SMOOTH_H_PRED, + PAETH_PRED, + N_INTRA_PRED_MODES, + CFL_PRED = N_INTRA_PRED_MODES, + N_UV_INTRA_PRED_MODES, + N_IMPL_INTRA_PRED_MODES = N_UV_INTRA_PRED_MODES, + LEFT_DC_PRED = DIAG_DOWN_LEFT_PRED, + TOP_DC_PRED, + DC_128_PRED, + Z1_PRED, + Z2_PRED, + Z3_PRED, + FILTER_PRED = N_INTRA_PRED_MODES, +}; + +enum InterIntraPredMode { + II_DC_PRED, + II_VERT_PRED, + II_HOR_PRED, + II_SMOOTH_PRED, + N_INTER_INTRA_PRED_MODES, +}; + +enum BlockPartition { + PARTITION_NONE, // [ ] <-. + PARTITION_H, // [-] | + PARTITION_V, // [|] | + PARTITION_SPLIT, // [+] --' + PARTITION_T_TOP_SPLIT, // [⊥] i.e. split top, H bottom + PARTITION_T_BOTTOM_SPLIT, // [т] i.e. H top, split bottom + PARTITION_T_LEFT_SPLIT, // [-|] i.e. split left, V right + PARTITION_T_RIGHT_SPLIT, // [|-] i.e. V left, split right + PARTITION_H4, // [Ⲷ] + PARTITION_V4, // [Ⲽ] + N_PARTITIONS, + N_SUB8X8_PARTITIONS = PARTITION_T_TOP_SPLIT, +}; + +enum BlockSize { + BS_128x128, + BS_128x64, + BS_64x128, + BS_64x64, + BS_64x32, + BS_64x16, + BS_32x64, + BS_32x32, + BS_32x16, + BS_32x8, + BS_16x64, + BS_16x32, + BS_16x16, + BS_16x8, + BS_16x4, + BS_8x32, + BS_8x16, + BS_8x8, + BS_8x4, + BS_4x16, + BS_4x8, + BS_4x4, + N_BS_SIZES, +}; + +enum Filter2d { // order is horizontal, vertical + FILTER_2D_8TAP_REGULAR, + FILTER_2D_8TAP_REGULAR_SMOOTH, + FILTER_2D_8TAP_REGULAR_SHARP, + FILTER_2D_8TAP_SHARP_REGULAR, + FILTER_2D_8TAP_SHARP_SMOOTH, + FILTER_2D_8TAP_SHARP, + FILTER_2D_8TAP_SMOOTH_REGULAR, + FILTER_2D_8TAP_SMOOTH, + FILTER_2D_8TAP_SMOOTH_SHARP, + FILTER_2D_BILINEAR, + N_2D_FILTERS, +}; + +enum MVJoint { + MV_JOINT_ZERO, + MV_JOINT_H, + MV_JOINT_V, + MV_JOINT_HV, + N_MV_JOINTS, +}; + +enum InterPredMode { + NEARESTMV, + NEARMV, + GLOBALMV, + NEWMV, + N_INTER_PRED_MODES, +}; + +enum DRL_PROXIMITY { + NEAREST_DRL, + NEARER_DRL, + NEAR_DRL, + NEARISH_DRL +}; + +enum CompInterPredMode { + NEARESTMV_NEARESTMV, + NEARMV_NEARMV, + NEARESTMV_NEWMV, + NEWMV_NEARESTMV, + NEARMV_NEWMV, + NEWMV_NEARMV, + GLOBALMV_GLOBALMV, + NEWMV_NEWMV, + N_COMP_INTER_PRED_MODES, +}; + +enum CompInterType { + COMP_INTER_NONE, + COMP_INTER_WEIGHTED_AVG, + COMP_INTER_AVG, + COMP_INTER_SEG, + COMP_INTER_WEDGE, +}; + +enum InterIntraType { + INTER_INTRA_NONE, + INTER_INTRA_BLEND, + INTER_INTRA_WEDGE, +}; + +typedef union mv { + struct { + int16_t y, x; + }; + uint32_t n; +} mv; + +enum MotionMode { + MM_TRANSLATION, + MM_OBMC, + MM_WARP, +}; + +#define QINDEX_RANGE 256 + +typedef struct Av1Block { + uint8_t bl, bs, bp; + uint8_t intra, seg_id, skip_mode, skip, uvtx; + union { + struct { + uint8_t y_mode, uv_mode, tx, pal_sz[2]; + int8_t y_angle, uv_angle, cfl_alpha[2]; + }; // intra + struct { + union { + struct { + union mv mv[2]; + uint8_t wedge_idx, mask_sign, interintra_mode; + }; + struct { + union mv mv2d; + int16_t matrix[4]; + }; + }; + uint8_t comp_type, inter_mode, motion_mode, drl_idx; + int8_t ref[2]; + uint8_t max_ytx, filter2d, interintra_type, tx_split0; + uint16_t tx_split1; + }; // inter + }; +} Av1Block; + +#endif /* DAV1D_SRC_LEVELS_H */ diff --git a/3rdparty/dav1d/src/lf_apply.h b/3rdparty/dav1d/src/lf_apply.h new file mode 100644 index 0000000..cf4c898 --- /dev/null +++ b/3rdparty/dav1d/src/lf_apply.h @@ -0,0 +1,48 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_LF_APPLY_H +#define DAV1D_SRC_LF_APPLY_H + +#include + +#include "common/bitdepth.h" + +#include "src/internal.h" +#include "src/levels.h" + +void bytefn(dav1d_loopfilter_sbrow_cols)(const Dav1dFrameContext *f, + pixel *const p[3], Av1Filter *lflvl, + int sby, int start_of_tile_row); +void bytefn(dav1d_loopfilter_sbrow_rows)(const Dav1dFrameContext *f, + pixel *const p[3], Av1Filter *lflvl, + int sby); + +void bytefn(dav1d_copy_lpf)(Dav1dFrameContext *const f, + /*const*/ pixel *const src[3], int sby); + +#endif /* DAV1D_SRC_LF_APPLY_H */ diff --git a/3rdparty/dav1d/src/lf_apply_tmpl.c b/3rdparty/dav1d/src/lf_apply_tmpl.c new file mode 100644 index 0000000..4ef3bec --- /dev/null +++ b/3rdparty/dav1d/src/lf_apply_tmpl.c @@ -0,0 +1,466 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/intops.h" + +#include "src/lf_apply.h" +#include "src/lr_apply.h" + +// The loop filter buffer stores 12 rows of pixels. A superblock block will +// contain at most 2 stripes. Each stripe requires 4 rows pixels (2 above +// and 2 below) the final 4 rows are used to swap the bottom of the last +// stripe with the top of the next super block row. +static void backup_lpf(const Dav1dFrameContext *const f, + pixel *dst, const ptrdiff_t dst_stride, + const pixel *src, const ptrdiff_t src_stride, + const int ss_ver, const int sb128, + int row, const int row_h, const int src_w, + const int h, const int ss_hor, const int lr_backup) +{ + const int cdef_backup = !lr_backup; + const int dst_w = f->frame_hdr->super_res.enabled ? + (f->frame_hdr->width[1] + ss_hor) >> ss_hor : src_w; + + // The first stripe of the frame is shorter by 8 luma pixel rows. + int stripe_h = ((64 << (cdef_backup & sb128)) - 8 * !row) >> ss_ver; + src += (stripe_h - 2) * PXSTRIDE(src_stride); + + if (f->c->n_tc == 1) { + if (row) { + const int top = 4 << sb128; + // Copy the top part of the stored loop filtered pixels from the + // previous sb row needed above the first stripe of this sb row. + pixel_copy(&dst[PXSTRIDE(dst_stride) * 0], + &dst[PXSTRIDE(dst_stride) * top], dst_w); + pixel_copy(&dst[PXSTRIDE(dst_stride) * 1], + &dst[PXSTRIDE(dst_stride) * (top + 1)], dst_w); + pixel_copy(&dst[PXSTRIDE(dst_stride) * 2], + &dst[PXSTRIDE(dst_stride) * (top + 2)], dst_w); + pixel_copy(&dst[PXSTRIDE(dst_stride) * 3], + &dst[PXSTRIDE(dst_stride) * (top + 3)], dst_w); + } + dst += 4 * PXSTRIDE(dst_stride); + } + + if (lr_backup && (f->frame_hdr->width[0] != f->frame_hdr->width[1])) { + while (row + stripe_h <= row_h) { + const int n_lines = 4 - (row + stripe_h + 1 == h); + f->dsp->mc.resize(dst, dst_stride, src, src_stride, + dst_w, n_lines, src_w, f->resize_step[ss_hor], + f->resize_start[ss_hor] HIGHBD_CALL_SUFFIX); + row += stripe_h; // unmodified stripe_h for the 1st stripe + stripe_h = 64 >> ss_ver; + src += stripe_h * PXSTRIDE(src_stride); + dst += n_lines * PXSTRIDE(dst_stride); + if (n_lines == 3) { + pixel_copy(dst, &dst[-PXSTRIDE(dst_stride)], dst_w); + dst += PXSTRIDE(dst_stride); + } + } + } else { + while (row + stripe_h <= row_h) { + const int n_lines = 4 - (row + stripe_h + 1 == h); + for (int i = 0; i < 4; i++) { + pixel_copy(dst, i == n_lines ? &dst[-PXSTRIDE(dst_stride)] : + src, src_w); + dst += PXSTRIDE(dst_stride); + src += PXSTRIDE(src_stride); + } + row += stripe_h; // unmodified stripe_h for the 1st stripe + stripe_h = 64 >> ss_ver; + src += (stripe_h - 4) * PXSTRIDE(src_stride); + } + } +} + +void bytefn(dav1d_copy_lpf)(Dav1dFrameContext *const f, + /*const*/ pixel *const src[3], const int sby) +{ + const int have_tt = f->c->n_tc > 1; + const int resize = f->frame_hdr->width[0] != f->frame_hdr->width[1]; + const int offset = 8 * !!sby; + const ptrdiff_t *const src_stride = f->cur.stride; + const ptrdiff_t *const lr_stride = f->sr_cur.p.stride; + const int tt_off = have_tt * sby * (4 << f->seq_hdr->sb128); + pixel *const dst[3] = { + f->lf.lr_lpf_line[0] + tt_off * PXSTRIDE(lr_stride[0]), + f->lf.lr_lpf_line[1] + tt_off * PXSTRIDE(lr_stride[1]), + f->lf.lr_lpf_line[2] + tt_off * PXSTRIDE(lr_stride[1]) + }; + + // TODO Also check block level restore type to reduce copying. + const int restore_planes = f->lf.restore_planes; + + if (f->seq_hdr->cdef || restore_planes & LR_RESTORE_Y) { + const int h = f->cur.p.h; + const int w = f->bw << 2; + const int row_h = imin((sby + 1) << (6 + f->seq_hdr->sb128), h - 1); + const int y_stripe = (sby << (6 + f->seq_hdr->sb128)) - offset; + if (restore_planes & LR_RESTORE_Y || !resize) + backup_lpf(f, dst[0], lr_stride[0], + src[0] - offset * PXSTRIDE(src_stride[0]), src_stride[0], + 0, f->seq_hdr->sb128, y_stripe, row_h, w, h, 0, 1); + if (have_tt && resize) { + const ptrdiff_t cdef_off_y = sby * 4 * PXSTRIDE(src_stride[0]); + backup_lpf(f, f->lf.cdef_lpf_line[0] + cdef_off_y, src_stride[0], + src[0] - offset * PXSTRIDE(src_stride[0]), src_stride[0], + 0, f->seq_hdr->sb128, y_stripe, row_h, w, h, 0, 0); + } + } + if ((f->seq_hdr->cdef || restore_planes & (LR_RESTORE_U | LR_RESTORE_V)) && + f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400) + { + const int ss_ver = f->sr_cur.p.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = f->sr_cur.p.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int h = (f->cur.p.h + ss_ver) >> ss_ver; + const int w = f->bw << (2 - ss_hor); + const int row_h = imin((sby + 1) << ((6 - ss_ver) + f->seq_hdr->sb128), h - 1); + const int offset_uv = offset >> ss_ver; + const int y_stripe = (sby << ((6 - ss_ver) + f->seq_hdr->sb128)) - offset_uv; + const ptrdiff_t cdef_off_uv = sby * 4 * PXSTRIDE(src_stride[1]); + if (f->seq_hdr->cdef || restore_planes & LR_RESTORE_U) { + if (restore_planes & LR_RESTORE_U || !resize) + backup_lpf(f, dst[1], lr_stride[1], + src[1] - offset_uv * PXSTRIDE(src_stride[1]), + src_stride[1], ss_ver, f->seq_hdr->sb128, y_stripe, + row_h, w, h, ss_hor, 1); + if (have_tt && resize) + backup_lpf(f, f->lf.cdef_lpf_line[1] + cdef_off_uv, src_stride[1], + src[1] - offset_uv * PXSTRIDE(src_stride[1]), + src_stride[1], ss_ver, f->seq_hdr->sb128, y_stripe, + row_h, w, h, ss_hor, 0); + } + if (f->seq_hdr->cdef || restore_planes & LR_RESTORE_V) { + if (restore_planes & LR_RESTORE_V || !resize) + backup_lpf(f, dst[2], lr_stride[1], + src[2] - offset_uv * PXSTRIDE(src_stride[1]), + src_stride[1], ss_ver, f->seq_hdr->sb128, y_stripe, + row_h, w, h, ss_hor, 1); + if (have_tt && resize) + backup_lpf(f, f->lf.cdef_lpf_line[2] + cdef_off_uv, src_stride[1], + src[2] - offset_uv * PXSTRIDE(src_stride[1]), + src_stride[1], ss_ver, f->seq_hdr->sb128, y_stripe, + row_h, w, h, ss_hor, 0); + } + } +} + +static inline void filter_plane_cols_y(const Dav1dFrameContext *const f, + const int have_left, + const uint8_t (*lvl)[4], + const ptrdiff_t b4_stride, + const uint16_t (*const mask)[3][2], + pixel *dst, const ptrdiff_t ls, + const int w, + const int starty4, const int endy4) +{ + const Dav1dDSPContext *const dsp = f->dsp; + + // filter edges between columns (e.g. block1 | block2) + for (int x = 0; x < w; x++) { + if (!have_left && !x) continue; + uint32_t hmask[4]; + if (!starty4) { + hmask[0] = mask[x][0][0]; + hmask[1] = mask[x][1][0]; + hmask[2] = mask[x][2][0]; + if (endy4 > 16) { + hmask[0] |= (unsigned) mask[x][0][1] << 16; + hmask[1] |= (unsigned) mask[x][1][1] << 16; + hmask[2] |= (unsigned) mask[x][2][1] << 16; + } + } else { + hmask[0] = mask[x][0][1]; + hmask[1] = mask[x][1][1]; + hmask[2] = mask[x][2][1]; + } + hmask[3] = 0; + dsp->lf.loop_filter_sb[0][0](&dst[x * 4], ls, hmask, + (const uint8_t(*)[4]) &lvl[x][0], b4_stride, + &f->lf.lim_lut, endy4 - starty4 HIGHBD_CALL_SUFFIX); + } +} + +static inline void filter_plane_rows_y(const Dav1dFrameContext *const f, + const int have_top, + const uint8_t (*lvl)[4], + const ptrdiff_t b4_stride, + const uint16_t (*const mask)[3][2], + pixel *dst, const ptrdiff_t ls, + const int w, + const int starty4, const int endy4) +{ + const Dav1dDSPContext *const dsp = f->dsp; + + // block1 + // filter edges between rows (e.g. ------) + // block2 + for (int y = starty4; y < endy4; + y++, dst += 4 * PXSTRIDE(ls), lvl += b4_stride) + { + if (!have_top && !y) continue; + const uint32_t vmask[4] = { + mask[y][0][0] | ((unsigned) mask[y][0][1] << 16), + mask[y][1][0] | ((unsigned) mask[y][1][1] << 16), + mask[y][2][0] | ((unsigned) mask[y][2][1] << 16), + 0, + }; + dsp->lf.loop_filter_sb[0][1](dst, ls, vmask, + (const uint8_t(*)[4]) &lvl[0][1], b4_stride, + &f->lf.lim_lut, w HIGHBD_CALL_SUFFIX); + } +} + +static inline void filter_plane_cols_uv(const Dav1dFrameContext *const f, + const int have_left, + const uint8_t (*lvl)[4], + const ptrdiff_t b4_stride, + const uint16_t (*const mask)[2][2], + pixel *const u, pixel *const v, + const ptrdiff_t ls, const int w, + const int starty4, const int endy4, + const int ss_ver) +{ + const Dav1dDSPContext *const dsp = f->dsp; + + // filter edges between columns (e.g. block1 | block2) + for (int x = 0; x < w; x++) { + if (!have_left && !x) continue; + uint32_t hmask[3]; + if (!starty4) { + hmask[0] = mask[x][0][0]; + hmask[1] = mask[x][1][0]; + if (endy4 > (16 >> ss_ver)) { + hmask[0] |= (unsigned) mask[x][0][1] << (16 >> ss_ver); + hmask[1] |= (unsigned) mask[x][1][1] << (16 >> ss_ver); + } + } else { + hmask[0] = mask[x][0][1]; + hmask[1] = mask[x][1][1]; + } + hmask[2] = 0; + dsp->lf.loop_filter_sb[1][0](&u[x * 4], ls, hmask, + (const uint8_t(*)[4]) &lvl[x][2], b4_stride, + &f->lf.lim_lut, endy4 - starty4 HIGHBD_CALL_SUFFIX); + dsp->lf.loop_filter_sb[1][0](&v[x * 4], ls, hmask, + (const uint8_t(*)[4]) &lvl[x][3], b4_stride, + &f->lf.lim_lut, endy4 - starty4 HIGHBD_CALL_SUFFIX); + } +} + +static inline void filter_plane_rows_uv(const Dav1dFrameContext *const f, + const int have_top, + const uint8_t (*lvl)[4], + const ptrdiff_t b4_stride, + const uint16_t (*const mask)[2][2], + pixel *const u, pixel *const v, + const ptrdiff_t ls, const int w, + const int starty4, const int endy4, + const int ss_hor) +{ + const Dav1dDSPContext *const dsp = f->dsp; + ptrdiff_t off_l = 0; + + // block1 + // filter edges between rows (e.g. ------) + // block2 + for (int y = starty4; y < endy4; + y++, off_l += 4 * PXSTRIDE(ls), lvl += b4_stride) + { + if (!have_top && !y) continue; + const uint32_t vmask[3] = { + mask[y][0][0] | ((unsigned) mask[y][0][1] << (16 >> ss_hor)), + mask[y][1][0] | ((unsigned) mask[y][1][1] << (16 >> ss_hor)), + 0, + }; + dsp->lf.loop_filter_sb[1][1](&u[off_l], ls, vmask, + (const uint8_t(*)[4]) &lvl[0][2], b4_stride, + &f->lf.lim_lut, w HIGHBD_CALL_SUFFIX); + dsp->lf.loop_filter_sb[1][1](&v[off_l], ls, vmask, + (const uint8_t(*)[4]) &lvl[0][3], b4_stride, + &f->lf.lim_lut, w HIGHBD_CALL_SUFFIX); + } +} + +void bytefn(dav1d_loopfilter_sbrow_cols)(const Dav1dFrameContext *const f, + pixel *const p[3], Av1Filter *const lflvl, + int sby, const int start_of_tile_row) +{ + int x, have_left; + // Don't filter outside the frame + const int is_sb64 = !f->seq_hdr->sb128; + const int starty4 = (sby & is_sb64) << 4; + const int sbsz = 32 >> is_sb64; + const int sbl2 = 5 - is_sb64; + const int halign = (f->bh + 31) & ~31; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int vmask = 16 >> ss_ver, hmask = 16 >> ss_hor; + const unsigned vmax = 1U << vmask, hmax = 1U << hmask; + const unsigned endy4 = starty4 + imin(f->h4 - sby * sbsz, sbsz); + const unsigned uv_endy4 = (endy4 + ss_ver) >> ss_ver; + + // fix lpf strength at tile col boundaries + const uint8_t *lpf_y = &f->lf.tx_lpf_right_edge[0][sby << sbl2]; + const uint8_t *lpf_uv = &f->lf.tx_lpf_right_edge[1][sby << (sbl2 - ss_ver)]; + for (int tile_col = 1;; tile_col++) { + x = f->frame_hdr->tiling.col_start_sb[tile_col]; + if ((x << sbl2) >= f->bw) break; + const int bx4 = x & is_sb64 ? 16 : 0, cbx4 = bx4 >> ss_hor; + x >>= is_sb64; + + uint16_t (*const y_hmask)[2] = lflvl[x].filter_y[0][bx4]; + for (unsigned y = starty4, mask = 1 << y; y < endy4; y++, mask <<= 1) { + const int sidx = mask >= 0x10000U; + const unsigned smask = mask >> (sidx << 4); + const int idx = 2 * !!(y_hmask[2][sidx] & smask) + + !!(y_hmask[1][sidx] & smask); + y_hmask[2][sidx] &= ~smask; + y_hmask[1][sidx] &= ~smask; + y_hmask[0][sidx] &= ~smask; + y_hmask[imin(idx, lpf_y[y - starty4])][sidx] |= smask; + } + + if (f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400) { + uint16_t (*const uv_hmask)[2] = lflvl[x].filter_uv[0][cbx4]; + for (unsigned y = starty4 >> ss_ver, uv_mask = 1 << y; y < uv_endy4; + y++, uv_mask <<= 1) + { + const int sidx = uv_mask >= vmax; + const unsigned smask = uv_mask >> (sidx << (4 - ss_ver)); + const int idx = !!(uv_hmask[1][sidx] & smask); + uv_hmask[1][sidx] &= ~smask; + uv_hmask[0][sidx] &= ~smask; + uv_hmask[imin(idx, lpf_uv[y - (starty4 >> ss_ver)])][sidx] |= smask; + } + } + lpf_y += halign; + lpf_uv += halign >> ss_ver; + } + + // fix lpf strength at tile row boundaries + if (start_of_tile_row) { + const BlockContext *a; + for (x = 0, a = &f->a[f->sb128w * (start_of_tile_row - 1)]; + x < f->sb128w; x++, a++) + { + uint16_t (*const y_vmask)[2] = lflvl[x].filter_y[1][starty4]; + const unsigned w = imin(32, f->w4 - (x << 5)); + for (unsigned mask = 1, i = 0; i < w; mask <<= 1, i++) { + const int sidx = mask >= 0x10000U; + const unsigned smask = mask >> (sidx << 4); + const int idx = 2 * !!(y_vmask[2][sidx] & smask) + + !!(y_vmask[1][sidx] & smask); + y_vmask[2][sidx] &= ~smask; + y_vmask[1][sidx] &= ~smask; + y_vmask[0][sidx] &= ~smask; + y_vmask[imin(idx, a->tx_lpf_y[i])][sidx] |= smask; + } + + if (f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400) { + const unsigned cw = (w + ss_hor) >> ss_hor; + uint16_t (*const uv_vmask)[2] = lflvl[x].filter_uv[1][starty4 >> ss_ver]; + for (unsigned uv_mask = 1, i = 0; i < cw; uv_mask <<= 1, i++) { + const int sidx = uv_mask >= hmax; + const unsigned smask = uv_mask >> (sidx << (4 - ss_hor)); + const int idx = !!(uv_vmask[1][sidx] & smask); + uv_vmask[1][sidx] &= ~smask; + uv_vmask[0][sidx] &= ~smask; + uv_vmask[imin(idx, a->tx_lpf_uv[i])][sidx] |= smask; + } + } + } + } + + pixel *ptr; + uint8_t (*level_ptr)[4] = f->lf.level + f->b4_stride * sby * sbsz; + for (ptr = p[0], have_left = 0, x = 0; x < f->sb128w; + x++, have_left = 1, ptr += 128, level_ptr += 32) + { + filter_plane_cols_y(f, have_left, level_ptr, f->b4_stride, + lflvl[x].filter_y[0], ptr, f->cur.stride[0], + imin(32, f->w4 - x * 32), starty4, endy4); + } + + if (!f->frame_hdr->loopfilter.level_u && !f->frame_hdr->loopfilter.level_v) + return; + + ptrdiff_t uv_off; + level_ptr = f->lf.level + f->b4_stride * (sby * sbsz >> ss_ver); + for (uv_off = 0, have_left = 0, x = 0; x < f->sb128w; + x++, have_left = 1, uv_off += 128 >> ss_hor, level_ptr += 32 >> ss_hor) + { + filter_plane_cols_uv(f, have_left, level_ptr, f->b4_stride, + lflvl[x].filter_uv[0], + &p[1][uv_off], &p[2][uv_off], f->cur.stride[1], + (imin(32, f->w4 - x * 32) + ss_hor) >> ss_hor, + starty4 >> ss_ver, uv_endy4, ss_ver); + } +} + +void bytefn(dav1d_loopfilter_sbrow_rows)(const Dav1dFrameContext *const f, + pixel *const p[3], Av1Filter *const lflvl, + int sby) +{ + int x; + // Don't filter outside the frame + const int have_top = sby > 0; + const int is_sb64 = !f->seq_hdr->sb128; + const int starty4 = (sby & is_sb64) << 4; + const int sbsz = 32 >> is_sb64; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const unsigned endy4 = starty4 + imin(f->h4 - sby * sbsz, sbsz); + const unsigned uv_endy4 = (endy4 + ss_ver) >> ss_ver; + + pixel *ptr; + uint8_t (*level_ptr)[4] = f->lf.level + f->b4_stride * sby * sbsz; + for (ptr = p[0], x = 0; x < f->sb128w; x++, ptr += 128, level_ptr += 32) { + filter_plane_rows_y(f, have_top, level_ptr, f->b4_stride, + lflvl[x].filter_y[1], ptr, f->cur.stride[0], + imin(32, f->w4 - x * 32), starty4, endy4); + } + + if (!f->frame_hdr->loopfilter.level_u && !f->frame_hdr->loopfilter.level_v) + return; + + ptrdiff_t uv_off; + level_ptr = f->lf.level + f->b4_stride * (sby * sbsz >> ss_ver); + for (uv_off = 0, x = 0; x < f->sb128w; + x++, uv_off += 128 >> ss_hor, level_ptr += 32 >> ss_hor) + { + filter_plane_rows_uv(f, have_top, level_ptr, f->b4_stride, + lflvl[x].filter_uv[1], + &p[1][uv_off], &p[2][uv_off], f->cur.stride[1], + (imin(32, f->w4 - x * 32) + ss_hor) >> ss_hor, + starty4 >> ss_ver, uv_endy4, ss_hor); + } +} diff --git a/3rdparty/dav1d/src/lf_mask.c b/3rdparty/dav1d/src/lf_mask.c new file mode 100644 index 0000000..c81bd9b --- /dev/null +++ b/3rdparty/dav1d/src/lf_mask.c @@ -0,0 +1,468 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/intops.h" + +#include "src/ctx.h" +#include "src/levels.h" +#include "src/lf_mask.h" +#include "src/tables.h" + +static void decomp_tx(uint8_t (*const txa)[2 /* txsz, step */][32 /* y */][32 /* x */], + const enum RectTxfmSize from, + const int depth, + const int y_off, const int x_off, + const uint16_t *const tx_masks) +{ + const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[from]; + const int is_split = (from == (int) TX_4X4 || depth > 1) ? 0 : + (tx_masks[depth] >> (y_off * 4 + x_off)) & 1; + + if (is_split) { + const enum RectTxfmSize sub = t_dim->sub; + const int htw4 = t_dim->w >> 1, hth4 = t_dim->h >> 1; + + decomp_tx(txa, sub, depth + 1, y_off * 2 + 0, x_off * 2 + 0, tx_masks); + if (t_dim->w >= t_dim->h) + decomp_tx((uint8_t(*)[2][32][32]) &txa[0][0][0][htw4], + sub, depth + 1, y_off * 2 + 0, x_off * 2 + 1, tx_masks); + if (t_dim->h >= t_dim->w) { + decomp_tx((uint8_t(*)[2][32][32]) &txa[0][0][hth4][0], + sub, depth + 1, y_off * 2 + 1, x_off * 2 + 0, tx_masks); + if (t_dim->w >= t_dim->h) + decomp_tx((uint8_t(*)[2][32][32]) &txa[0][0][hth4][htw4], + sub, depth + 1, y_off * 2 + 1, x_off * 2 + 1, tx_masks); + } + } else { + const int lw = imin(2, t_dim->lw), lh = imin(2, t_dim->lh); + +#define set_ctx(rep_macro) \ + for (int y = 0; y < t_dim->h; y++) { \ + rep_macro(txa[0][0][y], 0, lw); \ + rep_macro(txa[1][0][y], 0, lh); \ + txa[0][1][y][0] = t_dim->w; \ + } + case_set_upto16(t_dim->lw); +#undef set_ctx + dav1d_memset_pow2[t_dim->lw](txa[1][1][0], t_dim->h); + } +} + +static inline void mask_edges_inter(uint16_t (*const masks)[32][3][2], + const int by4, const int bx4, + const int w4, const int h4, const int skip, + const enum RectTxfmSize max_tx, + const uint16_t *const tx_masks, + uint8_t *const a, uint8_t *const l) +{ + const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[max_tx]; + int y, x; + + ALIGN_STK_16(uint8_t, txa, 2 /* edge */, [2 /* txsz, step */][32 /* y */][32 /* x */]); + for (int y_off = 0, y = 0; y < h4; y += t_dim->h, y_off++) + for (int x_off = 0, x = 0; x < w4; x += t_dim->w, x_off++) + decomp_tx((uint8_t(*)[2][32][32]) &txa[0][0][y][x], + max_tx, 0, y_off, x_off, tx_masks); + + // left block edge + unsigned mask = 1U << by4; + for (y = 0; y < h4; y++, mask <<= 1) { + const int sidx = mask >= 0x10000; + const unsigned smask = mask >> (sidx << 4); + masks[0][bx4][imin(txa[0][0][y][0], l[y])][sidx] |= smask; + } + + // top block edge + for (x = 0, mask = 1U << bx4; x < w4; x++, mask <<= 1) { + const int sidx = mask >= 0x10000; + const unsigned smask = mask >> (sidx << 4); + masks[1][by4][imin(txa[1][0][0][x], a[x])][sidx] |= smask; + } + + if (!skip) { + // inner (tx) left|right edges + for (y = 0, mask = 1U << by4; y < h4; y++, mask <<= 1) { + const int sidx = mask >= 0x10000U; + const unsigned smask = mask >> (sidx << 4); + int ltx = txa[0][0][y][0]; + int step = txa[0][1][y][0]; + for (x = step; x < w4; x += step) { + const int rtx = txa[0][0][y][x]; + masks[0][bx4 + x][imin(rtx, ltx)][sidx] |= smask; + ltx = rtx; + step = txa[0][1][y][x]; + } + } + + // top + // inner (tx) --- edges + // bottom + for (x = 0, mask = 1U << bx4; x < w4; x++, mask <<= 1) { + const int sidx = mask >= 0x10000U; + const unsigned smask = mask >> (sidx << 4); + int ttx = txa[1][0][0][x]; + int step = txa[1][1][0][x]; + for (y = step; y < h4; y += step) { + const int btx = txa[1][0][y][x]; + masks[1][by4 + y][imin(ttx, btx)][sidx] |= smask; + ttx = btx; + step = txa[1][1][y][x]; + } + } + } + + for (y = 0; y < h4; y++) + l[y] = txa[0][0][y][w4 - 1]; + memcpy(a, txa[1][0][h4 - 1], w4); +} + +static inline void mask_edges_intra(uint16_t (*const masks)[32][3][2], + const int by4, const int bx4, + const int w4, const int h4, + const enum RectTxfmSize tx, + uint8_t *const a, uint8_t *const l) +{ + const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[tx]; + const int twl4 = t_dim->lw, thl4 = t_dim->lh; + const int twl4c = imin(2, twl4), thl4c = imin(2, thl4); + int y, x; + + // left block edge + unsigned mask = 1U << by4; + for (y = 0; y < h4; y++, mask <<= 1) { + const int sidx = mask >= 0x10000; + const unsigned smask = mask >> (sidx << 4); + masks[0][bx4][imin(twl4c, l[y])][sidx] |= smask; + } + + // top block edge + for (x = 0, mask = 1U << bx4; x < w4; x++, mask <<= 1) { + const int sidx = mask >= 0x10000; + const unsigned smask = mask >> (sidx << 4); + masks[1][by4][imin(thl4c, a[x])][sidx] |= smask; + } + + // inner (tx) left|right edges + const int hstep = t_dim->w; + unsigned t = 1U << by4; + unsigned inner = (unsigned) ((((uint64_t) t) << h4) - t); + unsigned inner1 = inner & 0xffff, inner2 = inner >> 16; + for (x = hstep; x < w4; x += hstep) { + if (inner1) masks[0][bx4 + x][twl4c][0] |= inner1; + if (inner2) masks[0][bx4 + x][twl4c][1] |= inner2; + } + + // top + // inner (tx) --- edges + // bottom + const int vstep = t_dim->h; + t = 1U << bx4; + inner = (unsigned) ((((uint64_t) t) << w4) - t); + inner1 = inner & 0xffff; + inner2 = inner >> 16; + for (y = vstep; y < h4; y += vstep) { + if (inner1) masks[1][by4 + y][thl4c][0] |= inner1; + if (inner2) masks[1][by4 + y][thl4c][1] |= inner2; + } + + dav1d_memset_likely_pow2(a, thl4c, w4); + dav1d_memset_likely_pow2(l, twl4c, h4); +} + +static void mask_edges_chroma(uint16_t (*const masks)[32][2][2], + const int cby4, const int cbx4, + const int cw4, const int ch4, + const int skip_inter, + const enum RectTxfmSize tx, + uint8_t *const a, uint8_t *const l, + const int ss_hor, const int ss_ver) +{ + const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[tx]; + const int twl4 = t_dim->lw, thl4 = t_dim->lh; + const int twl4c = !!twl4, thl4c = !!thl4; + int y, x; + const int vbits = 4 - ss_ver, hbits = 4 - ss_hor; + const int vmask = 16 >> ss_ver, hmask = 16 >> ss_hor; + const unsigned vmax = 1 << vmask, hmax = 1 << hmask; + + // left block edge + unsigned mask = 1U << cby4; + for (y = 0; y < ch4; y++, mask <<= 1) { + const int sidx = mask >= vmax; + const unsigned smask = mask >> (sidx << vbits); + masks[0][cbx4][imin(twl4c, l[y])][sidx] |= smask; + } + + // top block edge + for (x = 0, mask = 1U << cbx4; x < cw4; x++, mask <<= 1) { + const int sidx = mask >= hmax; + const unsigned smask = mask >> (sidx << hbits); + masks[1][cby4][imin(thl4c, a[x])][sidx] |= smask; + } + + if (!skip_inter) { + // inner (tx) left|right edges + const int hstep = t_dim->w; + unsigned t = 1U << cby4; + unsigned inner = (unsigned) ((((uint64_t) t) << ch4) - t); + unsigned inner1 = inner & ((1 << vmask) - 1), inner2 = inner >> vmask; + for (x = hstep; x < cw4; x += hstep) { + if (inner1) masks[0][cbx4 + x][twl4c][0] |= inner1; + if (inner2) masks[0][cbx4 + x][twl4c][1] |= inner2; + } + + // top + // inner (tx) --- edges + // bottom + const int vstep = t_dim->h; + t = 1U << cbx4; + inner = (unsigned) ((((uint64_t) t) << cw4) - t); + inner1 = inner & ((1 << hmask) - 1), inner2 = inner >> hmask; + for (y = vstep; y < ch4; y += vstep) { + if (inner1) masks[1][cby4 + y][thl4c][0] |= inner1; + if (inner2) masks[1][cby4 + y][thl4c][1] |= inner2; + } + } + + dav1d_memset_likely_pow2(a, thl4c, cw4); + dav1d_memset_likely_pow2(l, twl4c, ch4); +} + +void dav1d_create_lf_mask_intra(Av1Filter *const lflvl, + uint8_t (*const level_cache)[4], + const ptrdiff_t b4_stride, + const uint8_t (*filter_level)[8][2], + const int bx, const int by, + const int iw, const int ih, + const enum BlockSize bs, + const enum RectTxfmSize ytx, + const enum RectTxfmSize uvtx, + const enum Dav1dPixelLayout layout, + uint8_t *const ay, uint8_t *const ly, + uint8_t *const auv, uint8_t *const luv) +{ + const uint8_t *const b_dim = dav1d_block_dimensions[bs]; + const int bw4 = imin(iw - bx, b_dim[0]); + const int bh4 = imin(ih - by, b_dim[1]); + const int bx4 = bx & 31; + const int by4 = by & 31; + assert(bw4 >= 0 && bh4 >= 0); + + if (bw4 && bh4) { + uint8_t (*level_cache_ptr)[4] = level_cache + by * b4_stride + bx; + for (int y = 0; y < bh4; y++) { + for (int x = 0; x < bw4; x++) { + level_cache_ptr[x][0] = filter_level[0][0][0]; + level_cache_ptr[x][1] = filter_level[1][0][0]; + } + level_cache_ptr += b4_stride; + } + + mask_edges_intra(lflvl->filter_y, by4, bx4, bw4, bh4, ytx, ay, ly); + } + + if (!auv) return; + + const int ss_ver = layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = layout != DAV1D_PIXEL_LAYOUT_I444; + const int cbw4 = imin(((iw + ss_hor) >> ss_hor) - (bx >> ss_hor), + (b_dim[0] + ss_hor) >> ss_hor); + const int cbh4 = imin(((ih + ss_ver) >> ss_ver) - (by >> ss_ver), + (b_dim[1] + ss_ver) >> ss_ver); + assert(cbw4 >= 0 && cbh4 >= 0); + + if (!cbw4 || !cbh4) return; + + const int cbx4 = bx4 >> ss_hor; + const int cby4 = by4 >> ss_ver; + + uint8_t (*level_cache_ptr)[4] = + level_cache + (by >> ss_ver) * b4_stride + (bx >> ss_hor); + for (int y = 0; y < cbh4; y++) { + for (int x = 0; x < cbw4; x++) { + level_cache_ptr[x][2] = filter_level[2][0][0]; + level_cache_ptr[x][3] = filter_level[3][0][0]; + } + level_cache_ptr += b4_stride; + } + + mask_edges_chroma(lflvl->filter_uv, cby4, cbx4, cbw4, cbh4, 0, uvtx, + auv, luv, ss_hor, ss_ver); +} + +void dav1d_create_lf_mask_inter(Av1Filter *const lflvl, + uint8_t (*const level_cache)[4], + const ptrdiff_t b4_stride, + const uint8_t (*filter_level)[8][2], + const int bx, const int by, + const int iw, const int ih, + const int skip, const enum BlockSize bs, + const enum RectTxfmSize max_ytx, + const uint16_t *const tx_masks, + const enum RectTxfmSize uvtx, + const enum Dav1dPixelLayout layout, + uint8_t *const ay, uint8_t *const ly, + uint8_t *const auv, uint8_t *const luv) +{ + const uint8_t *const b_dim = dav1d_block_dimensions[bs]; + const int bw4 = imin(iw - bx, b_dim[0]); + const int bh4 = imin(ih - by, b_dim[1]); + const int bx4 = bx & 31; + const int by4 = by & 31; + assert(bw4 >= 0 && bh4 >= 0); + + if (bw4 && bh4) { + uint8_t (*level_cache_ptr)[4] = level_cache + by * b4_stride + bx; + for (int y = 0; y < bh4; y++) { + for (int x = 0; x < bw4; x++) { + level_cache_ptr[x][0] = filter_level[0][0][0]; + level_cache_ptr[x][1] = filter_level[1][0][0]; + } + level_cache_ptr += b4_stride; + } + + mask_edges_inter(lflvl->filter_y, by4, bx4, bw4, bh4, skip, + max_ytx, tx_masks, ay, ly); + } + + if (!auv) return; + + const int ss_ver = layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = layout != DAV1D_PIXEL_LAYOUT_I444; + const int cbw4 = imin(((iw + ss_hor) >> ss_hor) - (bx >> ss_hor), + (b_dim[0] + ss_hor) >> ss_hor); + const int cbh4 = imin(((ih + ss_ver) >> ss_ver) - (by >> ss_ver), + (b_dim[1] + ss_ver) >> ss_ver); + assert(cbw4 >= 0 && cbh4 >= 0); + + if (!cbw4 || !cbh4) return; + + const int cbx4 = bx4 >> ss_hor; + const int cby4 = by4 >> ss_ver; + + uint8_t (*level_cache_ptr)[4] = + level_cache + (by >> ss_ver) * b4_stride + (bx >> ss_hor); + for (int y = 0; y < cbh4; y++) { + for (int x = 0; x < cbw4; x++) { + level_cache_ptr[x][2] = filter_level[2][0][0]; + level_cache_ptr[x][3] = filter_level[3][0][0]; + } + level_cache_ptr += b4_stride; + } + + mask_edges_chroma(lflvl->filter_uv, cby4, cbx4, cbw4, cbh4, skip, uvtx, + auv, luv, ss_hor, ss_ver); +} + +void dav1d_calc_eih(Av1FilterLUT *const lim_lut, const int filter_sharpness) { + // set E/I/H values from loopfilter level + const int sharp = filter_sharpness; + for (int level = 0; level < 64; level++) { + int limit = level; + + if (sharp > 0) { + limit >>= (sharp + 3) >> 2; + limit = imin(limit, 9 - sharp); + } + limit = imax(limit, 1); + + lim_lut->i[level] = limit; + lim_lut->e[level] = 2 * (level + 2) + limit; + } + lim_lut->sharp[0] = (sharp + 3) >> 2; + lim_lut->sharp[1] = sharp ? 9 - sharp : 0xff; +} + +static void calc_lf_value(uint8_t (*const lflvl_values)[2], + const int base_lvl, const int lf_delta, + const int seg_delta, + const Dav1dLoopfilterModeRefDeltas *const mr_delta) +{ + const int base = iclip(iclip(base_lvl + lf_delta, 0, 63) + seg_delta, 0, 63); + + if (!mr_delta) { + memset(lflvl_values, base, sizeof(*lflvl_values) * 8); + } else { + const int sh = base >= 32; + lflvl_values[0][0] = lflvl_values[0][1] = + iclip(base + (mr_delta->ref_delta[0] * (1 << sh)), 0, 63); + for (int r = 1; r < 8; r++) { + for (int m = 0; m < 2; m++) { + const int delta = + mr_delta->mode_delta[m] + mr_delta->ref_delta[r]; + lflvl_values[r][m] = iclip(base + (delta * (1 << sh)), 0, 63); + } + } + } +} + +static inline void calc_lf_value_chroma(uint8_t (*const lflvl_values)[2], + const int base_lvl, const int lf_delta, + const int seg_delta, + const Dav1dLoopfilterModeRefDeltas *const mr_delta) +{ + if (!base_lvl) + memset(lflvl_values, 0, sizeof(*lflvl_values) * 8); + else + calc_lf_value(lflvl_values, base_lvl, lf_delta, seg_delta, mr_delta); +} + +void dav1d_calc_lf_values(uint8_t (*const lflvl_values)[4][8][2], + const Dav1dFrameHeader *const hdr, + const int8_t lf_delta[4]) +{ + const int n_seg = hdr->segmentation.enabled ? 8 : 1; + + if (!hdr->loopfilter.level_y[0] && !hdr->loopfilter.level_y[1]) { + memset(lflvl_values, 0, sizeof(*lflvl_values) * n_seg); + return; + } + + const Dav1dLoopfilterModeRefDeltas *const mr_deltas = + hdr->loopfilter.mode_ref_delta_enabled ? + &hdr->loopfilter.mode_ref_deltas : NULL; + for (int s = 0; s < n_seg; s++) { + const Dav1dSegmentationData *const segd = + hdr->segmentation.enabled ? &hdr->segmentation.seg_data.d[s] : NULL; + + calc_lf_value(lflvl_values[s][0], hdr->loopfilter.level_y[0], + lf_delta[0], segd ? segd->delta_lf_y_v : 0, mr_deltas); + calc_lf_value(lflvl_values[s][1], hdr->loopfilter.level_y[1], + lf_delta[hdr->delta.lf.multi ? 1 : 0], + segd ? segd->delta_lf_y_h : 0, mr_deltas); + calc_lf_value_chroma(lflvl_values[s][2], hdr->loopfilter.level_u, + lf_delta[hdr->delta.lf.multi ? 2 : 0], + segd ? segd->delta_lf_u : 0, mr_deltas); + calc_lf_value_chroma(lflvl_values[s][3], hdr->loopfilter.level_v, + lf_delta[hdr->delta.lf.multi ? 3 : 0], + segd ? segd->delta_lf_v : 0, mr_deltas); + } +} diff --git a/3rdparty/dav1d/src/lf_mask.h b/3rdparty/dav1d/src/lf_mask.h new file mode 100644 index 0000000..01ab2cf --- /dev/null +++ b/3rdparty/dav1d/src/lf_mask.h @@ -0,0 +1,83 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_LF_MASK_H +#define DAV1D_SRC_LF_MASK_H + +#include +#include + +#include "src/levels.h" + +typedef struct Av1FilterLUT { + ALIGN(uint8_t e[64], 16); + ALIGN(uint8_t i[64], 16); + ALIGN(uint64_t sharp[2], 16); +} Av1FilterLUT; + +typedef struct Av1RestorationUnit { + /* SGR: type = DAV1D_RESTORATION_SGRPROJ + sgr_idx */ + uint8_t /* enum Dav1dRestorationType */ type; + int8_t filter_h[3]; + int8_t filter_v[3]; + int8_t sgr_weights[2]; +} Av1RestorationUnit; + +// each struct describes one 128x128 area (1 or 4 SBs), pre-superres-scaling +typedef struct Av1Filter { + // each bit is 1 col + uint16_t filter_y[2 /* 0=col, 1=row */][32][3][2]; + uint16_t filter_uv[2 /* 0=col, 1=row */][32][2][2]; + int8_t cdef_idx[4]; // -1 means "unset" + uint16_t noskip_mask[16][2]; // for 8x8 blocks, but stored on a 4x8 basis +} Av1Filter; + +// each struct describes one 128x128 area (1 or 4 SBs), post-superres-scaling +typedef struct Av1Restoration { + Av1RestorationUnit lr[3][4]; +} Av1Restoration; + +void dav1d_create_lf_mask_intra(Av1Filter *lflvl, uint8_t (*level_cache)[4], + const ptrdiff_t b4_stride, + const uint8_t (*level)[8][2], int bx, int by, + int iw, int ih, enum BlockSize bs, + enum RectTxfmSize ytx, enum RectTxfmSize uvtx, + enum Dav1dPixelLayout layout, uint8_t *ay, + uint8_t *ly, uint8_t *auv, uint8_t *luv); +void dav1d_create_lf_mask_inter(Av1Filter *lflvl, uint8_t (*level_cache)[4], + const ptrdiff_t b4_stride, + const uint8_t (*level)[8][2], int bx, int by, + int iw, int ih, int skip_inter, + enum BlockSize bs, enum RectTxfmSize max_ytx, + const uint16_t *tx_mask, enum RectTxfmSize uvtx, + enum Dav1dPixelLayout layout, uint8_t *ay, + uint8_t *ly, uint8_t *auv, uint8_t *luv); +void dav1d_calc_eih(Av1FilterLUT *lim_lut, int filter_sharpness); +void dav1d_calc_lf_values(uint8_t (*values)[4][8][2], const Dav1dFrameHeader *hdr, + const int8_t lf_delta[4]); + +#endif /* DAV1D_SRC_LF_MASK_H */ diff --git a/3rdparty/dav1d/src/lib.c b/3rdparty/dav1d/src/lib.c new file mode 100644 index 0000000..4f52f34 --- /dev/null +++ b/3rdparty/dav1d/src/lib.c @@ -0,0 +1,762 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" +#include "vcs_version.h" + +#include +#include + +#if defined(__linux__) && HAVE_DLSYM +#include +#endif + +#include "dav1d/dav1d.h" +#include "dav1d/data.h" + +#include "common/validate.h" + +#include "src/cpu.h" +#include "src/fg_apply.h" +#include "src/internal.h" +#include "src/log.h" +#include "src/obu.h" +#include "src/qm.h" +#include "src/ref.h" +#include "src/thread_task.h" +#include "src/wedge.h" + +static COLD void init_internal(void) { + dav1d_init_cpu(); + dav1d_init_ii_wedge_masks(); + dav1d_init_intra_edge_tree(); + dav1d_init_qm_tables(); + dav1d_init_thread(); +} + +COLD const char *dav1d_version(void) { + return DAV1D_VERSION; +} + +COLD unsigned dav1d_version_api(void) { + return (DAV1D_API_VERSION_MAJOR << 16) | + (DAV1D_API_VERSION_MINOR << 8) | + (DAV1D_API_VERSION_PATCH << 0); +} + +COLD void dav1d_default_settings(Dav1dSettings *const s) { + s->n_threads = 0; + s->max_frame_delay = 0; + s->apply_grain = 1; + s->allocator.cookie = NULL; + s->allocator.alloc_picture_callback = dav1d_default_picture_alloc; + s->allocator.release_picture_callback = dav1d_default_picture_release; + s->logger.cookie = NULL; + s->logger.callback = dav1d_log_default_callback; + s->operating_point = 0; + s->all_layers = 1; // just until the tests are adjusted + s->frame_size_limit = 0; + s->strict_std_compliance = 0; + s->output_invisible_frames = 0; + s->inloop_filters = DAV1D_INLOOPFILTER_ALL; + s->decode_frame_type = DAV1D_DECODEFRAMETYPE_ALL; +} + +static void close_internal(Dav1dContext **const c_out, int flush); + +#if defined(__linux__) && HAVE_DLSYM && defined(__GLIBC__) +NO_SANITIZE("cfi-icall") // CFI is broken with dlsym() +static COLD size_t get_stack_size_internal(const pthread_attr_t *const thread_attr) { + /* glibc has an issue where the size of the TLS is subtracted from the stack + * size instead of allocated separately. As a result the specified stack + * size may be insufficient when used in an application with large amounts + * of TLS data. The following is a workaround to compensate for that. + * See https://sourceware.org/bugzilla/show_bug.cgi?id=11787 */ + size_t (*const get_minstack)(const pthread_attr_t*) = + dlsym(RTLD_DEFAULT, "__pthread_get_minstack"); + if (get_minstack) + return get_minstack(thread_attr) - PTHREAD_STACK_MIN; + return 0; +} +#else +#define get_stack_size_internal(attr) (0) +#endif + +static COLD void get_num_threads(Dav1dContext *const c, const Dav1dSettings *const s, + unsigned *n_tc, unsigned *n_fc) +{ + /* ceil(sqrt(n)) */ + static const uint8_t fc_lut[49] = { + 1, /* 1 */ + 2, 2, 2, /* 2- 4 */ + 3, 3, 3, 3, 3, /* 5- 9 */ + 4, 4, 4, 4, 4, 4, 4, /* 10-16 */ + 5, 5, 5, 5, 5, 5, 5, 5, 5, /* 17-25 */ + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, /* 26-36 */ + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, /* 37-49 */ + }; + *n_tc = s->n_threads ? s->n_threads : + iclip(dav1d_num_logical_processors(c), 1, DAV1D_MAX_THREADS); + *n_fc = s->max_frame_delay ? umin(s->max_frame_delay, *n_tc) : + *n_tc < 50 ? fc_lut[*n_tc - 1] : 8; // min(8, ceil(sqrt(n))) +} + +COLD int dav1d_get_frame_delay(const Dav1dSettings *const s) { + unsigned n_tc, n_fc; + validate_input_or_ret(s != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->n_threads >= 0 && + s->n_threads <= DAV1D_MAX_THREADS, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->max_frame_delay >= 0 && + s->max_frame_delay <= DAV1D_MAX_FRAME_DELAY, DAV1D_ERR(EINVAL)); + + get_num_threads(NULL, s, &n_tc, &n_fc); + return n_fc; +} + +COLD int dav1d_open(Dav1dContext **const c_out, const Dav1dSettings *const s) { + static pthread_once_t initted = PTHREAD_ONCE_INIT; + pthread_once(&initted, init_internal); + + validate_input_or_ret(c_out != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->n_threads >= 0 && + s->n_threads <= DAV1D_MAX_THREADS, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->max_frame_delay >= 0 && + s->max_frame_delay <= DAV1D_MAX_FRAME_DELAY, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->allocator.alloc_picture_callback != NULL, + DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->allocator.release_picture_callback != NULL, + DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->operating_point >= 0 && + s->operating_point <= 31, DAV1D_ERR(EINVAL)); + validate_input_or_ret(s->decode_frame_type >= DAV1D_DECODEFRAMETYPE_ALL && + s->decode_frame_type <= DAV1D_DECODEFRAMETYPE_KEY, DAV1D_ERR(EINVAL)); + + pthread_attr_t thread_attr; + if (pthread_attr_init(&thread_attr)) return DAV1D_ERR(ENOMEM); + size_t stack_size = 1024 * 1024 + get_stack_size_internal(&thread_attr); + + pthread_attr_setstacksize(&thread_attr, stack_size); + + Dav1dContext *const c = *c_out = dav1d_alloc_aligned(ALLOC_COMMON_CTX, sizeof(*c), 64); + if (!c) goto error; + memset(c, 0, sizeof(*c)); + + c->allocator = s->allocator; + c->logger = s->logger; + c->apply_grain = s->apply_grain; + c->operating_point = s->operating_point; + c->all_layers = s->all_layers; + c->frame_size_limit = s->frame_size_limit; + c->strict_std_compliance = s->strict_std_compliance; + c->output_invisible_frames = s->output_invisible_frames; + c->inloop_filters = s->inloop_filters; + c->decode_frame_type = s->decode_frame_type; + + dav1d_data_props_set_defaults(&c->cached_error_props); + + if (dav1d_mem_pool_init(ALLOC_OBU_HDR, &c->seq_hdr_pool) || + dav1d_mem_pool_init(ALLOC_OBU_HDR, &c->frame_hdr_pool) || + dav1d_mem_pool_init(ALLOC_SEGMAP, &c->segmap_pool) || + dav1d_mem_pool_init(ALLOC_REFMVS, &c->refmvs_pool) || + dav1d_mem_pool_init(ALLOC_PIC_CTX, &c->pic_ctx_pool) || + dav1d_mem_pool_init(ALLOC_CDF, &c->cdf_pool)) + { + goto error; + } + + if (c->allocator.alloc_picture_callback == dav1d_default_picture_alloc && + c->allocator.release_picture_callback == dav1d_default_picture_release) + { + if (c->allocator.cookie) goto error; + if (dav1d_mem_pool_init(ALLOC_PIC, &c->picture_pool)) goto error; + c->allocator.cookie = c->picture_pool; + } else if (c->allocator.alloc_picture_callback == dav1d_default_picture_alloc || + c->allocator.release_picture_callback == dav1d_default_picture_release) + { + goto error; + } + + /* On 32-bit systems extremely large frame sizes can cause overflows in + * dav1d_decode_frame() malloc size calculations. Prevent that from occuring + * by enforcing a maximum frame size limit, chosen to roughly correspond to + * the largest size possible to decode without exhausting virtual memory. */ + if (sizeof(size_t) < 8 && s->frame_size_limit - 1 >= 8192 * 8192) { + c->frame_size_limit = 8192 * 8192; + if (s->frame_size_limit) + dav1d_log(c, "Frame size limit reduced from %u to %u.\n", + s->frame_size_limit, c->frame_size_limit); + } + + c->flush = &c->flush_mem; + atomic_init(c->flush, 0); + + get_num_threads(c, s, &c->n_tc, &c->n_fc); + + c->fc = dav1d_alloc_aligned(ALLOC_THREAD_CTX, sizeof(*c->fc) * c->n_fc, 32); + if (!c->fc) goto error; + memset(c->fc, 0, sizeof(*c->fc) * c->n_fc); + + c->tc = dav1d_alloc_aligned(ALLOC_THREAD_CTX, sizeof(*c->tc) * c->n_tc, 64); + if (!c->tc) goto error; + memset(c->tc, 0, sizeof(*c->tc) * c->n_tc); + if (c->n_tc > 1) { + if (pthread_mutex_init(&c->task_thread.lock, NULL)) goto error; + if (pthread_cond_init(&c->task_thread.cond, NULL)) { + pthread_mutex_destroy(&c->task_thread.lock); + goto error; + } + if (pthread_cond_init(&c->task_thread.delayed_fg.cond, NULL)) { + pthread_cond_destroy(&c->task_thread.cond); + pthread_mutex_destroy(&c->task_thread.lock); + goto error; + } + c->task_thread.cur = c->n_fc; + atomic_init(&c->task_thread.reset_task_cur, UINT_MAX); + atomic_init(&c->task_thread.cond_signaled, 0); + c->task_thread.inited = 1; + } + + if (c->n_fc > 1) { + const size_t out_delayed_sz = sizeof(*c->frame_thread.out_delayed) * c->n_fc; + c->frame_thread.out_delayed = + dav1d_malloc(ALLOC_THREAD_CTX, out_delayed_sz); + if (!c->frame_thread.out_delayed) goto error; + memset(c->frame_thread.out_delayed, 0, out_delayed_sz); + } + for (unsigned n = 0; n < c->n_fc; n++) { + Dav1dFrameContext *const f = &c->fc[n]; + if (c->n_tc > 1) { + if (pthread_mutex_init(&f->task_thread.lock, NULL)) goto error; + if (pthread_cond_init(&f->task_thread.cond, NULL)) { + pthread_mutex_destroy(&f->task_thread.lock); + goto error; + } + if (pthread_mutex_init(&f->task_thread.pending_tasks.lock, NULL)) { + pthread_cond_destroy(&f->task_thread.cond); + pthread_mutex_destroy(&f->task_thread.lock); + goto error; + } + } + f->c = c; + f->task_thread.ttd = &c->task_thread; + f->lf.last_sharpness = -1; + } + + for (unsigned m = 0; m < c->n_tc; m++) { + Dav1dTaskContext *const t = &c->tc[m]; + t->f = &c->fc[0]; + t->task_thread.ttd = &c->task_thread; + t->c = c; + memset(t->cf_16bpc, 0, sizeof(t->cf_16bpc)); + if (c->n_tc > 1) { + if (pthread_mutex_init(&t->task_thread.td.lock, NULL)) goto error; + if (pthread_cond_init(&t->task_thread.td.cond, NULL)) { + pthread_mutex_destroy(&t->task_thread.td.lock); + goto error; + } + if (pthread_create(&t->task_thread.td.thread, &thread_attr, dav1d_worker_task, t)) { + pthread_cond_destroy(&t->task_thread.td.cond); + pthread_mutex_destroy(&t->task_thread.td.lock); + goto error; + } + t->task_thread.td.inited = 1; + } + } + dav1d_pal_dsp_init(&c->pal_dsp); + dav1d_refmvs_dsp_init(&c->refmvs_dsp); + + pthread_attr_destroy(&thread_attr); + + return 0; + +error: + if (c) close_internal(c_out, 0); + pthread_attr_destroy(&thread_attr); + return DAV1D_ERR(ENOMEM); +} + +static int has_grain(const Dav1dPicture *const pic) +{ + const Dav1dFilmGrainData *fgdata = &pic->frame_hdr->film_grain.data; + return fgdata->num_y_points || fgdata->num_uv_points[0] || + fgdata->num_uv_points[1] || (fgdata->clip_to_restricted_range && + fgdata->chroma_scaling_from_luma); +} + +static int output_image(Dav1dContext *const c, Dav1dPicture *const out) +{ + int res = 0; + + Dav1dThreadPicture *const in = (c->all_layers || !c->max_spatial_id) + ? &c->out : &c->cache; + if (!c->apply_grain || !has_grain(&in->p)) { + dav1d_picture_move_ref(out, &in->p); + dav1d_thread_picture_unref(in); + goto end; + } + + res = dav1d_apply_grain(c, out, &in->p); + dav1d_thread_picture_unref(in); +end: + if (!c->all_layers && c->max_spatial_id && c->out.p.data[0]) { + dav1d_thread_picture_move_ref(in, &c->out); + } + return res; +} + +static int output_picture_ready(Dav1dContext *const c, const int drain) { + if (c->cached_error) return 1; + if (!c->all_layers && c->max_spatial_id) { + if (c->out.p.data[0] && c->cache.p.data[0]) { + if (c->max_spatial_id == c->cache.p.frame_hdr->spatial_id || + c->out.flags & PICTURE_FLAG_NEW_TEMPORAL_UNIT) + return 1; + dav1d_thread_picture_unref(&c->cache); + dav1d_thread_picture_move_ref(&c->cache, &c->out); + return 0; + } else if (c->cache.p.data[0] && drain) { + return 1; + } else if (c->out.p.data[0]) { + dav1d_thread_picture_move_ref(&c->cache, &c->out); + return 0; + } + } + + return !!c->out.p.data[0]; +} + +static int drain_picture(Dav1dContext *const c, Dav1dPicture *const out) { + unsigned drain_count = 0; + int drained = 0; + do { + const unsigned next = c->frame_thread.next; + Dav1dFrameContext *const f = &c->fc[next]; + pthread_mutex_lock(&c->task_thread.lock); + while (f->n_tile_data > 0) + pthread_cond_wait(&f->task_thread.cond, + &f->task_thread.ttd->lock); + Dav1dThreadPicture *const out_delayed = + &c->frame_thread.out_delayed[next]; + if (out_delayed->p.data[0] || atomic_load(&f->task_thread.error)) { + unsigned first = atomic_load(&c->task_thread.first); + if (first + 1U < c->n_fc) + atomic_fetch_add(&c->task_thread.first, 1U); + else + atomic_store(&c->task_thread.first, 0); + atomic_compare_exchange_strong(&c->task_thread.reset_task_cur, + &first, UINT_MAX); + if (c->task_thread.cur && c->task_thread.cur < c->n_fc) + c->task_thread.cur--; + drained = 1; + } else if (drained) { + pthread_mutex_unlock(&c->task_thread.lock); + break; + } + if (++c->frame_thread.next == c->n_fc) + c->frame_thread.next = 0; + pthread_mutex_unlock(&c->task_thread.lock); + const int error = f->task_thread.retval; + if (error) { + f->task_thread.retval = 0; + dav1d_data_props_copy(&c->cached_error_props, &out_delayed->p.m); + dav1d_thread_picture_unref(out_delayed); + return error; + } + if (out_delayed->p.data[0]) { + const unsigned progress = + atomic_load_explicit(&out_delayed->progress[1], + memory_order_relaxed); + if ((out_delayed->visible || c->output_invisible_frames) && + progress != FRAME_ERROR) + { + dav1d_thread_picture_ref(&c->out, out_delayed); + c->event_flags |= dav1d_picture_get_event_flags(out_delayed); + } + dav1d_thread_picture_unref(out_delayed); + if (output_picture_ready(c, 0)) + return output_image(c, out); + } + } while (++drain_count < c->n_fc); + + if (output_picture_ready(c, 1)) + return output_image(c, out); + + return DAV1D_ERR(EAGAIN); +} + +static int gen_picture(Dav1dContext *const c) +{ + Dav1dData *const in = &c->in; + + if (output_picture_ready(c, 0)) + return 0; + + while (in->sz > 0) { + const ptrdiff_t res = dav1d_parse_obus(c, in); + if (res < 0) { + dav1d_data_unref_internal(in); + } else { + assert((size_t)res <= in->sz); + in->sz -= res; + in->data += res; + if (!in->sz) dav1d_data_unref_internal(in); + } + if (output_picture_ready(c, 0)) + break; + if (res < 0) + return (int)res; + } + + return 0; +} + +int dav1d_send_data(Dav1dContext *const c, Dav1dData *const in) +{ + validate_input_or_ret(c != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(in != NULL, DAV1D_ERR(EINVAL)); + + if (in->data) { + validate_input_or_ret(in->sz > 0 && in->sz <= SIZE_MAX / 2, DAV1D_ERR(EINVAL)); + c->drain = 0; + } + if (c->in.data) + return DAV1D_ERR(EAGAIN); + dav1d_data_ref(&c->in, in); + + int res = gen_picture(c); + if (!res) + dav1d_data_unref_internal(in); + + return res; +} + +int dav1d_get_picture(Dav1dContext *const c, Dav1dPicture *const out) +{ + validate_input_or_ret(c != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL)); + + const int drain = c->drain; + c->drain = 1; + + int res = gen_picture(c); + if (res < 0) + return res; + + if (c->cached_error) { + const int res = c->cached_error; + c->cached_error = 0; + return res; + } + + if (output_picture_ready(c, c->n_fc == 1)) + return output_image(c, out); + + if (c->n_fc > 1 && drain) + return drain_picture(c, out); + + return DAV1D_ERR(EAGAIN); +} + +int dav1d_apply_grain(Dav1dContext *const c, Dav1dPicture *const out, + const Dav1dPicture *const in) +{ + validate_input_or_ret(c != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(in != NULL, DAV1D_ERR(EINVAL)); + + if (!has_grain(in)) { + dav1d_picture_ref(out, in); + return 0; + } + + int res = dav1d_picture_alloc_copy(c, out, in->p.w, in); + if (res < 0) goto error; + + if (c->n_tc > 1) { + dav1d_task_delayed_fg(c, out, in); + } else { + switch (out->p.bpc) { +#if CONFIG_8BPC + case 8: + dav1d_apply_grain_8bpc(&c->dsp[0].fg, out, in); + break; +#endif +#if CONFIG_16BPC + case 10: + case 12: + dav1d_apply_grain_16bpc(&c->dsp[(out->p.bpc >> 1) - 4].fg, out, in); + break; +#endif + default: abort(); + } + } + + return 0; + +error: + dav1d_picture_unref_internal(out); + return res; +} + +void dav1d_flush(Dav1dContext *const c) { + dav1d_data_unref_internal(&c->in); + if (c->out.p.frame_hdr) + dav1d_thread_picture_unref(&c->out); + if (c->cache.p.frame_hdr) + dav1d_thread_picture_unref(&c->cache); + + c->drain = 0; + c->cached_error = 0; + + for (int i = 0; i < 8; i++) { + if (c->refs[i].p.p.frame_hdr) + dav1d_thread_picture_unref(&c->refs[i].p); + dav1d_ref_dec(&c->refs[i].segmap); + dav1d_ref_dec(&c->refs[i].refmvs); + dav1d_cdf_thread_unref(&c->cdf[i]); + } + c->frame_hdr = NULL; + c->seq_hdr = NULL; + dav1d_ref_dec(&c->seq_hdr_ref); + + c->mastering_display = NULL; + c->content_light = NULL; + c->itut_t35 = NULL; + c->n_itut_t35 = 0; + dav1d_ref_dec(&c->mastering_display_ref); + dav1d_ref_dec(&c->content_light_ref); + dav1d_ref_dec(&c->itut_t35_ref); + + dav1d_data_props_unref_internal(&c->cached_error_props); + + if (c->n_fc == 1 && c->n_tc == 1) return; + atomic_store(c->flush, 1); + + if (c->n_tc > 1) { + pthread_mutex_lock(&c->task_thread.lock); + // stop running tasks in worker threads + for (unsigned i = 0; i < c->n_tc; i++) { + Dav1dTaskContext *const tc = &c->tc[i]; + while (!tc->task_thread.flushed) { + pthread_cond_wait(&tc->task_thread.td.cond, &c->task_thread.lock); + } + } + for (unsigned i = 0; i < c->n_fc; i++) { + c->fc[i].task_thread.task_head = NULL; + c->fc[i].task_thread.task_tail = NULL; + c->fc[i].task_thread.task_cur_prev = NULL; + c->fc[i].task_thread.pending_tasks.head = NULL; + c->fc[i].task_thread.pending_tasks.tail = NULL; + atomic_init(&c->fc[i].task_thread.pending_tasks.merge, 0); + } + atomic_init(&c->task_thread.first, 0); + c->task_thread.cur = c->n_fc; + atomic_store(&c->task_thread.reset_task_cur, UINT_MAX); + atomic_store(&c->task_thread.cond_signaled, 0); + pthread_mutex_unlock(&c->task_thread.lock); + } + + if (c->n_fc > 1) { + for (unsigned n = 0, next = c->frame_thread.next; n < c->n_fc; n++, next++) { + if (next == c->n_fc) next = 0; + Dav1dFrameContext *const f = &c->fc[next]; + dav1d_decode_frame_exit(f, -1); + f->n_tile_data = 0; + f->task_thread.retval = 0; + f->task_thread.error = 0; + Dav1dThreadPicture *out_delayed = &c->frame_thread.out_delayed[next]; + if (out_delayed->p.frame_hdr) { + dav1d_thread_picture_unref(out_delayed); + } + } + c->frame_thread.next = 0; + } + atomic_store(c->flush, 0); +} + +COLD void dav1d_close(Dav1dContext **const c_out) { + validate_input(c_out != NULL); +#if TRACK_HEAP_ALLOCATIONS + dav1d_log_alloc_stats(*c_out); +#endif + close_internal(c_out, 1); +} + +static COLD void close_internal(Dav1dContext **const c_out, int flush) { + Dav1dContext *const c = *c_out; + if (!c) return; + + if (flush) dav1d_flush(c); + + if (c->tc) { + struct TaskThreadData *ttd = &c->task_thread; + if (ttd->inited) { + pthread_mutex_lock(&ttd->lock); + for (unsigned n = 0; n < c->n_tc && c->tc[n].task_thread.td.inited; n++) + c->tc[n].task_thread.die = 1; + pthread_cond_broadcast(&ttd->cond); + pthread_mutex_unlock(&ttd->lock); + for (unsigned n = 0; n < c->n_tc; n++) { + Dav1dTaskContext *const pf = &c->tc[n]; + if (!pf->task_thread.td.inited) break; + pthread_join(pf->task_thread.td.thread, NULL); + pthread_cond_destroy(&pf->task_thread.td.cond); + pthread_mutex_destroy(&pf->task_thread.td.lock); + } + pthread_cond_destroy(&ttd->delayed_fg.cond); + pthread_cond_destroy(&ttd->cond); + pthread_mutex_destroy(&ttd->lock); + } + dav1d_free_aligned(c->tc); + } + + for (unsigned n = 0; c->fc && n < c->n_fc; n++) { + Dav1dFrameContext *const f = &c->fc[n]; + + // clean-up threading stuff + if (c->n_fc > 1) { + dav1d_free(f->tile_thread.lowest_pixel_mem); + dav1d_free(f->frame_thread.b); + dav1d_free_aligned(f->frame_thread.cbi); + dav1d_free_aligned(f->frame_thread.pal_idx); + dav1d_free_aligned(f->frame_thread.cf); + dav1d_free(f->frame_thread.tile_start_off); + dav1d_free_aligned(f->frame_thread.pal); + } + if (c->n_tc > 1) { + pthread_mutex_destroy(&f->task_thread.pending_tasks.lock); + pthread_cond_destroy(&f->task_thread.cond); + pthread_mutex_destroy(&f->task_thread.lock); + } + dav1d_free(f->frame_thread.frame_progress); + dav1d_free(f->task_thread.tasks); + dav1d_free(f->task_thread.tile_tasks[0]); + dav1d_free_aligned(f->ts); + dav1d_free_aligned(f->ipred_edge[0]); + dav1d_free(f->a); + dav1d_free(f->tile); + dav1d_free(f->lf.mask); + dav1d_free(f->lf.level); + dav1d_free(f->lf.lr_mask); + dav1d_free(f->lf.tx_lpf_right_edge[0]); + dav1d_free(f->lf.start_of_tile_row); + dav1d_free_aligned(f->rf.r); + dav1d_free_aligned(f->lf.cdef_line_buf); + dav1d_free_aligned(f->lf.lr_line_buf); + } + dav1d_free_aligned(c->fc); + if (c->n_fc > 1 && c->frame_thread.out_delayed) { + for (unsigned n = 0; n < c->n_fc; n++) + if (c->frame_thread.out_delayed[n].p.frame_hdr) + dav1d_thread_picture_unref(&c->frame_thread.out_delayed[n]); + dav1d_free(c->frame_thread.out_delayed); + } + for (int n = 0; n < c->n_tile_data; n++) + dav1d_data_unref_internal(&c->tile[n].data); + dav1d_free(c->tile); + for (int n = 0; n < 8; n++) { + dav1d_cdf_thread_unref(&c->cdf[n]); + if (c->refs[n].p.p.frame_hdr) + dav1d_thread_picture_unref(&c->refs[n].p); + dav1d_ref_dec(&c->refs[n].refmvs); + dav1d_ref_dec(&c->refs[n].segmap); + } + dav1d_ref_dec(&c->seq_hdr_ref); + dav1d_ref_dec(&c->frame_hdr_ref); + + dav1d_ref_dec(&c->mastering_display_ref); + dav1d_ref_dec(&c->content_light_ref); + dav1d_ref_dec(&c->itut_t35_ref); + + dav1d_mem_pool_end(c->seq_hdr_pool); + dav1d_mem_pool_end(c->frame_hdr_pool); + dav1d_mem_pool_end(c->segmap_pool); + dav1d_mem_pool_end(c->refmvs_pool); + dav1d_mem_pool_end(c->cdf_pool); + dav1d_mem_pool_end(c->picture_pool); + dav1d_mem_pool_end(c->pic_ctx_pool); + + dav1d_freep_aligned(c_out); +} + +int dav1d_get_event_flags(Dav1dContext *const c, enum Dav1dEventFlags *const flags) { + validate_input_or_ret(c != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(flags != NULL, DAV1D_ERR(EINVAL)); + + *flags = c->event_flags; + c->event_flags = 0; + return 0; +} + +int dav1d_get_decode_error_data_props(Dav1dContext *const c, Dav1dDataProps *const out) { + validate_input_or_ret(c != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL)); + + dav1d_data_props_unref_internal(out); + *out = c->cached_error_props; + dav1d_data_props_set_defaults(&c->cached_error_props); + + return 0; +} + +void dav1d_picture_unref(Dav1dPicture *const p) { + dav1d_picture_unref_internal(p); +} + +uint8_t *dav1d_data_create(Dav1dData *const buf, const size_t sz) { + return dav1d_data_create_internal(buf, sz); +} + +int dav1d_data_wrap(Dav1dData *const buf, const uint8_t *const ptr, + const size_t sz, + void (*const free_callback)(const uint8_t *data, + void *user_data), + void *const user_data) +{ + return dav1d_data_wrap_internal(buf, ptr, sz, free_callback, user_data); +} + +int dav1d_data_wrap_user_data(Dav1dData *const buf, + const uint8_t *const user_data, + void (*const free_callback)(const uint8_t *user_data, + void *cookie), + void *const cookie) +{ + return dav1d_data_wrap_user_data_internal(buf, + user_data, + free_callback, + cookie); +} + +void dav1d_data_unref(Dav1dData *const buf) { + dav1d_data_unref_internal(buf); +} + +void dav1d_data_props_unref(Dav1dDataProps *const props) { + dav1d_data_props_unref_internal(props); +} diff --git a/3rdparty/dav1d/src/log.c b/3rdparty/dav1d/src/log.c new file mode 100644 index 0000000..a08f6eb --- /dev/null +++ b/3rdparty/dav1d/src/log.c @@ -0,0 +1,57 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include + +#include "dav1d/dav1d.h" + +#include "common/validate.h" + +#include "src/internal.h" +#include "src/log.h" + +#if CONFIG_LOG +COLD void dav1d_log_default_callback(void *const cookie, + const char *const format, va_list ap) +{ + vfprintf(stderr, format, ap); +} + +COLD void dav1d_log(Dav1dContext *const c, const char *const format, ...) { + assert(c != NULL); + + if (!c->logger.callback) + return; + + va_list ap; + va_start(ap, format); + c->logger.callback(c->logger.cookie, format, ap); + va_end(ap); +} +#endif diff --git a/3rdparty/dav1d/src/log.h b/3rdparty/dav1d/src/log.h new file mode 100644 index 0000000..df32de7 --- /dev/null +++ b/3rdparty/dav1d/src/log.h @@ -0,0 +1,47 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_LOG_H +#define DAV1D_SRC_LOG_H + +#include "config.h" + +#include + +#include "dav1d/dav1d.h" + +#include "common/attributes.h" + +#if CONFIG_LOG +#define dav1d_log dav1d_log +void dav1d_log_default_callback(void *cookie, const char *format, va_list ap); +void dav1d_log(Dav1dContext *c, const char *format, ...) ATTR_FORMAT_PRINTF(2, 3); +#else +#define dav1d_log_default_callback NULL +#define dav1d_log(...) do { } while(0) +#endif + +#endif /* DAV1D_SRC_LOG_H */ diff --git a/3rdparty/dav1d/src/loopfilter.h b/3rdparty/dav1d/src/loopfilter.h new file mode 100644 index 0000000..a0f78c9 --- /dev/null +++ b/3rdparty/dav1d/src/loopfilter.h @@ -0,0 +1,57 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_LOOPFILTER_H +#define DAV1D_SRC_LOOPFILTER_H + +#include +#include + +#include "common/bitdepth.h" + +#include "src/levels.h" +#include "src/lf_mask.h" + +#define decl_loopfilter_sb_fn(name) \ +void (name)(pixel *dst, ptrdiff_t stride, const uint32_t *mask, \ + const uint8_t (*lvl)[4], ptrdiff_t lvl_stride, \ + const Av1FilterLUT *lut, int w HIGHBD_DECL_SUFFIX) +typedef decl_loopfilter_sb_fn(*loopfilter_sb_fn); + +typedef struct Dav1dLoopFilterDSPContext { + /* + * dimension 1: plane (0=luma, 1=chroma) + * dimension 2: 0=col-edge filter (h), 1=row-edge filter (v) + * + * dst/stride are aligned by 32 + */ + loopfilter_sb_fn loop_filter_sb[2][2]; +} Dav1dLoopFilterDSPContext; + +bitfn_decls(void dav1d_loop_filter_dsp_init, Dav1dLoopFilterDSPContext *c); + +#endif /* DAV1D_SRC_LOOPFILTER_H */ diff --git a/3rdparty/dav1d/src/loopfilter_tmpl.c b/3rdparty/dav1d/src/loopfilter_tmpl.c new file mode 100644 index 0000000..0a2baf1 --- /dev/null +++ b/3rdparty/dav1d/src/loopfilter_tmpl.c @@ -0,0 +1,276 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/attributes.h" +#include "common/intops.h" + +#include "src/loopfilter.h" + +static NOINLINE void +loop_filter(pixel *dst, int E, int I, int H, + const ptrdiff_t stridea, const ptrdiff_t strideb, const int wd + HIGHBD_DECL_SUFFIX) +{ + const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8; + const int F = 1 << bitdepth_min_8; + E <<= bitdepth_min_8; + I <<= bitdepth_min_8; + H <<= bitdepth_min_8; + + for (int i = 0; i < 4; i++, dst += stridea) { + int p6, p5, p4, p3, p2; + int p1 = dst[strideb * -2], p0 = dst[strideb * -1]; + int q0 = dst[strideb * +0], q1 = dst[strideb * +1]; + int q2, q3, q4, q5, q6; + int fm, flat8out, flat8in; + + fm = abs(p1 - p0) <= I && abs(q1 - q0) <= I && + abs(p0 - q0) * 2 + (abs(p1 - q1) >> 1) <= E; + + if (wd > 4) { + p2 = dst[strideb * -3]; + q2 = dst[strideb * +2]; + + fm &= abs(p2 - p1) <= I && abs(q2 - q1) <= I; + + if (wd > 6) { + p3 = dst[strideb * -4]; + q3 = dst[strideb * +3]; + + fm &= abs(p3 - p2) <= I && abs(q3 - q2) <= I; + } + } + if (!fm) continue; + + if (wd >= 16) { + p6 = dst[strideb * -7]; + p5 = dst[strideb * -6]; + p4 = dst[strideb * -5]; + q4 = dst[strideb * +4]; + q5 = dst[strideb * +5]; + q6 = dst[strideb * +6]; + + flat8out = abs(p6 - p0) <= F && abs(p5 - p0) <= F && + abs(p4 - p0) <= F && abs(q4 - q0) <= F && + abs(q5 - q0) <= F && abs(q6 - q0) <= F; + } + + if (wd >= 6) + flat8in = abs(p2 - p0) <= F && abs(p1 - p0) <= F && + abs(q1 - q0) <= F && abs(q2 - q0) <= F; + + if (wd >= 8) + flat8in &= abs(p3 - p0) <= F && abs(q3 - q0) <= F; + + if (wd >= 16 && (flat8out & flat8in)) { + dst[strideb * -6] = (p6 + p6 + p6 + p6 + p6 + p6 * 2 + p5 * 2 + + p4 * 2 + p3 + p2 + p1 + p0 + q0 + 8) >> 4; + dst[strideb * -5] = (p6 + p6 + p6 + p6 + p6 + p5 * 2 + p4 * 2 + + p3 * 2 + p2 + p1 + p0 + q0 + q1 + 8) >> 4; + dst[strideb * -4] = (p6 + p6 + p6 + p6 + p5 + p4 * 2 + p3 * 2 + + p2 * 2 + p1 + p0 + q0 + q1 + q2 + 8) >> 4; + dst[strideb * -3] = (p6 + p6 + p6 + p5 + p4 + p3 * 2 + p2 * 2 + + p1 * 2 + p0 + q0 + q1 + q2 + q3 + 8) >> 4; + dst[strideb * -2] = (p6 + p6 + p5 + p4 + p3 + p2 * 2 + p1 * 2 + + p0 * 2 + q0 + q1 + q2 + q3 + q4 + 8) >> 4; + dst[strideb * -1] = (p6 + p5 + p4 + p3 + p2 + p1 * 2 + p0 * 2 + + q0 * 2 + q1 + q2 + q3 + q4 + q5 + 8) >> 4; + dst[strideb * +0] = (p5 + p4 + p3 + p2 + p1 + p0 * 2 + q0 * 2 + + q1 * 2 + q2 + q3 + q4 + q5 + q6 + 8) >> 4; + dst[strideb * +1] = (p4 + p3 + p2 + p1 + p0 + q0 * 2 + q1 * 2 + + q2 * 2 + q3 + q4 + q5 + q6 + q6 + 8) >> 4; + dst[strideb * +2] = (p3 + p2 + p1 + p0 + q0 + q1 * 2 + q2 * 2 + + q3 * 2 + q4 + q5 + q6 + q6 + q6 + 8) >> 4; + dst[strideb * +3] = (p2 + p1 + p0 + q0 + q1 + q2 * 2 + q3 * 2 + + q4 * 2 + q5 + q6 + q6 + q6 + q6 + 8) >> 4; + dst[strideb * +4] = (p1 + p0 + q0 + q1 + q2 + q3 * 2 + q4 * 2 + + q5 * 2 + q6 + q6 + q6 + q6 + q6 + 8) >> 4; + dst[strideb * +5] = (p0 + q0 + q1 + q2 + q3 + q4 * 2 + q5 * 2 + + q6 * 2 + q6 + q6 + q6 + q6 + q6 + 8) >> 4; + } else if (wd >= 8 && flat8in) { + dst[strideb * -3] = (p3 + p3 + p3 + 2 * p2 + p1 + p0 + q0 + 4) >> 3; + dst[strideb * -2] = (p3 + p3 + p2 + 2 * p1 + p0 + q0 + q1 + 4) >> 3; + dst[strideb * -1] = (p3 + p2 + p1 + 2 * p0 + q0 + q1 + q2 + 4) >> 3; + dst[strideb * +0] = (p2 + p1 + p0 + 2 * q0 + q1 + q2 + q3 + 4) >> 3; + dst[strideb * +1] = (p1 + p0 + q0 + 2 * q1 + q2 + q3 + q3 + 4) >> 3; + dst[strideb * +2] = (p0 + q0 + q1 + 2 * q2 + q3 + q3 + q3 + 4) >> 3; + } else if (wd == 6 && flat8in) { + dst[strideb * -2] = (p2 + 2 * p2 + 2 * p1 + 2 * p0 + q0 + 4) >> 3; + dst[strideb * -1] = (p2 + 2 * p1 + 2 * p0 + 2 * q0 + q1 + 4) >> 3; + dst[strideb * +0] = (p1 + 2 * p0 + 2 * q0 + 2 * q1 + q2 + 4) >> 3; + dst[strideb * +1] = (p0 + 2 * q0 + 2 * q1 + 2 * q2 + q2 + 4) >> 3; + } else { + const int hev = abs(p1 - p0) > H || abs(q1 - q0) > H; + +#define iclip_diff(v) iclip(v, -128 * (1 << bitdepth_min_8), \ + 128 * (1 << bitdepth_min_8) - 1) + + if (hev) { + int f = iclip_diff(p1 - q1), f1, f2; + f = iclip_diff(3 * (q0 - p0) + f); + + f1 = imin(f + 4, (128 << bitdepth_min_8) - 1) >> 3; + f2 = imin(f + 3, (128 << bitdepth_min_8) - 1) >> 3; + + dst[strideb * -1] = iclip_pixel(p0 + f2); + dst[strideb * +0] = iclip_pixel(q0 - f1); + } else { + int f = iclip_diff(3 * (q0 - p0)), f1, f2; + + f1 = imin(f + 4, (128 << bitdepth_min_8) - 1) >> 3; + f2 = imin(f + 3, (128 << bitdepth_min_8) - 1) >> 3; + + dst[strideb * -1] = iclip_pixel(p0 + f2); + dst[strideb * +0] = iclip_pixel(q0 - f1); + + f = (f1 + 1) >> 1; + dst[strideb * -2] = iclip_pixel(p1 + f); + dst[strideb * +1] = iclip_pixel(q1 - f); + } +#undef iclip_diff + } + } +} + +static void loop_filter_h_sb128y_c(pixel *dst, const ptrdiff_t stride, + const uint32_t *const vmask, + const uint8_t (*l)[4], ptrdiff_t b4_stride, + const Av1FilterLUT *lut, const int h + HIGHBD_DECL_SUFFIX) +{ + const unsigned vm = vmask[0] | vmask[1] | vmask[2]; + for (unsigned y = 1; vm & ~(y - 1); + y <<= 1, dst += 4 * PXSTRIDE(stride), l += b4_stride) + { + if (vm & y) { + const int L = l[0][0] ? l[0][0] : l[-1][0]; + if (!L) continue; + const int H = L >> 4; + const int E = lut->e[L], I = lut->i[L]; + const int idx = (vmask[2] & y) ? 2 : !!(vmask[1] & y); + loop_filter(dst, E, I, H, PXSTRIDE(stride), 1, 4 << idx + HIGHBD_TAIL_SUFFIX); + } + } +} + +static void loop_filter_v_sb128y_c(pixel *dst, const ptrdiff_t stride, + const uint32_t *const vmask, + const uint8_t (*l)[4], ptrdiff_t b4_stride, + const Av1FilterLUT *lut, const int w + HIGHBD_DECL_SUFFIX) +{ + const unsigned vm = vmask[0] | vmask[1] | vmask[2]; + for (unsigned x = 1; vm & ~(x - 1); x <<= 1, dst += 4, l++) { + if (vm & x) { + const int L = l[0][0] ? l[0][0] : l[-b4_stride][0]; + if (!L) continue; + const int H = L >> 4; + const int E = lut->e[L], I = lut->i[L]; + const int idx = (vmask[2] & x) ? 2 : !!(vmask[1] & x); + loop_filter(dst, E, I, H, 1, PXSTRIDE(stride), 4 << idx + HIGHBD_TAIL_SUFFIX); + } + } +} + +static void loop_filter_h_sb128uv_c(pixel *dst, const ptrdiff_t stride, + const uint32_t *const vmask, + const uint8_t (*l)[4], ptrdiff_t b4_stride, + const Av1FilterLUT *lut, const int h + HIGHBD_DECL_SUFFIX) +{ + const unsigned vm = vmask[0] | vmask[1]; + for (unsigned y = 1; vm & ~(y - 1); + y <<= 1, dst += 4 * PXSTRIDE(stride), l += b4_stride) + { + if (vm & y) { + const int L = l[0][0] ? l[0][0] : l[-1][0]; + if (!L) continue; + const int H = L >> 4; + const int E = lut->e[L], I = lut->i[L]; + const int idx = !!(vmask[1] & y); + loop_filter(dst, E, I, H, PXSTRIDE(stride), 1, 4 + 2 * idx + HIGHBD_TAIL_SUFFIX); + } + } +} + +static void loop_filter_v_sb128uv_c(pixel *dst, const ptrdiff_t stride, + const uint32_t *const vmask, + const uint8_t (*l)[4], ptrdiff_t b4_stride, + const Av1FilterLUT *lut, const int w + HIGHBD_DECL_SUFFIX) +{ + const unsigned vm = vmask[0] | vmask[1]; + for (unsigned x = 1; vm & ~(x - 1); x <<= 1, dst += 4, l++) { + if (vm & x) { + const int L = l[0][0] ? l[0][0] : l[-b4_stride][0]; + if (!L) continue; + const int H = L >> 4; + const int E = lut->e[L], I = lut->i[L]; + const int idx = !!(vmask[1] & x); + loop_filter(dst, E, I, H, 1, PXSTRIDE(stride), 4 + 2 * idx + HIGHBD_TAIL_SUFFIX); + } + } +} + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM +#include "src/arm/loopfilter.h" +#elif ARCH_LOONGARCH64 +#include "src/loongarch/loopfilter.h" +#elif ARCH_PPC64LE +#include "src/ppc/loopfilter.h" +#elif ARCH_X86 +#include "src/x86/loopfilter.h" +#endif +#endif + +COLD void bitfn(dav1d_loop_filter_dsp_init)(Dav1dLoopFilterDSPContext *const c) { + c->loop_filter_sb[0][0] = loop_filter_h_sb128y_c; + c->loop_filter_sb[0][1] = loop_filter_v_sb128y_c; + c->loop_filter_sb[1][0] = loop_filter_h_sb128uv_c; + c->loop_filter_sb[1][1] = loop_filter_v_sb128uv_c; + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM + loop_filter_dsp_init_arm(c); +#elif ARCH_LOONGARCH64 + loop_filter_dsp_init_loongarch(c); +#elif ARCH_PPC64LE + loop_filter_dsp_init_ppc(c); +#elif ARCH_X86 + loop_filter_dsp_init_x86(c); +#endif +#endif +} diff --git a/3rdparty/dav1d/src/looprestoration.h b/3rdparty/dav1d/src/looprestoration.h new file mode 100644 index 0000000..f55dd31 --- /dev/null +++ b/3rdparty/dav1d/src/looprestoration.h @@ -0,0 +1,79 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_LOOPRESTORATION_H +#define DAV1D_SRC_LOOPRESTORATION_H + +#include +#include + +#include "common/bitdepth.h" + +enum LrEdgeFlags { + LR_HAVE_LEFT = 1 << 0, + LR_HAVE_RIGHT = 1 << 1, + LR_HAVE_TOP = 1 << 2, + LR_HAVE_BOTTOM = 1 << 3, +}; + +#ifdef BITDEPTH +typedef const pixel (*const_left_pixel_row)[4]; +#else +typedef const void *const_left_pixel_row; +#endif + +typedef union LooprestorationParams { + ALIGN(int16_t filter[2][8], 16); + struct { + uint32_t s0, s1; + int16_t w0, w1; + } sgr; +} LooprestorationParams; + +// Although the spec applies restoration filters over 4x4 blocks, +// they can be applied to a bigger surface. +// * w is constrained by the restoration unit size (w <= 256) +// * h is constrained by the stripe height (h <= 64) +// The filter functions are allowed to do aligned writes past the right +// edge of the buffer, aligned up to the minimum loop restoration unit size +// (which is 32 pixels for subsampled chroma and 64 pixels for luma). +#define decl_lr_filter_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, \ + const_left_pixel_row left, \ + const pixel *lpf, int w, int h, \ + const LooprestorationParams *params, \ + enum LrEdgeFlags edges HIGHBD_DECL_SUFFIX) +typedef decl_lr_filter_fn(*looprestorationfilter_fn); + +typedef struct Dav1dLoopRestorationDSPContext { + looprestorationfilter_fn wiener[2]; /* 7-tap, 5-tap */ + looprestorationfilter_fn sgr[3]; /* 5x5, 3x3, mix */ +} Dav1dLoopRestorationDSPContext; + +bitfn_decls(void dav1d_loop_restoration_dsp_init, Dav1dLoopRestorationDSPContext *c, int bpc); + +#endif /* DAV1D_SRC_LOOPRESTORATION_H */ diff --git a/3rdparty/dav1d/src/looprestoration_tmpl.c b/3rdparty/dav1d/src/looprestoration_tmpl.c new file mode 100644 index 0000000..c172462 --- /dev/null +++ b/3rdparty/dav1d/src/looprestoration_tmpl.c @@ -0,0 +1,1384 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include +#include + +#include "common/attributes.h" +#include "common/bitdepth.h" +#include "common/intops.h" + +#include "src/looprestoration.h" +#include "src/tables.h" + +// 256 * 1.5 + 3 + 3 = 390 +#define REST_UNIT_STRIDE (390) + +static void wiener_filter_h(uint16_t *dst, const pixel (*left)[4], + const pixel *src, const int16_t fh[8], + const int w, const enum LrEdgeFlags edges + HIGHBD_DECL_SUFFIX) +{ + const int bitdepth = bitdepth_from_max(bitdepth_max); + const int round_bits_h = 3 + (bitdepth == 12) * 2; + const int rounding_off_h = 1 << (round_bits_h - 1); + const int clip_limit = 1 << (bitdepth + 1 + 7 - round_bits_h); + + if (w < 6) { + // For small widths, do the fully conditional loop with + // conditions on each access. + for (int x = 0; x < w; x++) { + int sum = (1 << (bitdepth + 6)); +#if BITDEPTH == 8 + sum += src[x] * 128; +#endif + for (int i = 0; i < 7; i++) { + int idx = x + i - 3; + if (idx < 0) { + if (!(edges & LR_HAVE_LEFT)) + sum += src[0] * fh[i]; + else if (left) + sum += left[0][4 + idx] * fh[i]; + else + sum += src[idx] * fh[i]; + } else if (idx >= w && !(edges & LR_HAVE_RIGHT)) { + sum += src[w - 1] * fh[i]; + } else + sum += src[idx] * fh[i]; + } + sum = iclip((sum + rounding_off_h) >> round_bits_h, 0, clip_limit - 1); + dst[x] = sum; + } + + return; + } + + // For larger widths, do separate loops with less conditions; first + // handle the start of the row. + int start = 3; + if (!(edges & LR_HAVE_LEFT)) { + // If there's no left edge, pad using the leftmost pixel. + for (int x = 0; x < 3; x++) { + int sum = (1 << (bitdepth + 6)); +#if BITDEPTH == 8 + sum += src[x] * 128; +#endif + for (int i = 0; i < 7; i++) { + int idx = x + i - 3; + if (idx < 0) + sum += src[0] * fh[i]; + else + sum += src[idx] * fh[i]; + } + sum = iclip((sum + rounding_off_h) >> round_bits_h, 0, clip_limit - 1); + dst[x] = sum; + } + } else if (left) { + // If we have the left edge and a separate left buffer, pad using that. + for (int x = 0; x < 3; x++) { + int sum = (1 << (bitdepth + 6)); +#if BITDEPTH == 8 + sum += src[x] * 128; +#endif + for (int i = 0; i < 7; i++) { + int idx = x + i - 3; + if (idx < 0) + sum += left[0][4 + idx] * fh[i]; + else + sum += src[idx] * fh[i]; + } + sum = iclip((sum + rounding_off_h) >> round_bits_h, 0, clip_limit - 1); + dst[x] = sum; + } + } else { + // If we have the left edge, but no separate left buffer, we're in the + // top/bottom area (lpf) with the left edge existing in the same + // buffer; just do the regular loop from the start. + start = 0; + } + int end = w - 3; + if (edges & LR_HAVE_RIGHT) + end = w; + + // Do a condititon free loop for the bulk of the row. + for (int x = start; x < end; x++) { + int sum = (1 << (bitdepth + 6)); +#if BITDEPTH == 8 + sum += src[x] * 128; +#endif + for (int i = 0; i < 7; i++) { + int idx = x + i - 3; + sum += src[idx] * fh[i]; + } + sum = iclip((sum + rounding_off_h) >> round_bits_h, 0, clip_limit - 1); + dst[x] = sum; + } + + // If we need to, calculate the end of the row with a condition for + // right edge padding. + for (int x = end; x < w; x++) { + int sum = (1 << (bitdepth + 6)); +#if BITDEPTH == 8 + sum += src[x] * 128; +#endif + for (int i = 0; i < 7; i++) { + int idx = x + i - 3; + if (idx >= w) + sum += src[w - 1] * fh[i]; + else + sum += src[idx] * fh[i]; + } + sum = iclip((sum + rounding_off_h) >> round_bits_h, 0, clip_limit - 1); + dst[x] = sum; + } +} + +static void wiener_filter_v(pixel *p, uint16_t **ptrs, const int16_t fv[8], + const int w HIGHBD_DECL_SUFFIX) +{ + const int bitdepth = bitdepth_from_max(bitdepth_max); + + const int round_bits_v = 11 - (bitdepth == 12) * 2; + const int rounding_off_v = 1 << (round_bits_v - 1); + const int round_offset = 1 << (bitdepth + (round_bits_v - 1)); + + for (int i = 0; i < w; i++) { + int sum = -round_offset; + + // Only filter using 6 input rows. The 7th row is assumed to be + // identical to the last one. + // + // This function is assumed to only be called at the end, when doing + // padding at the bottom. + for (int k = 0; k < 6; k++) + sum += ptrs[k][i] * fv[k]; + sum += ptrs[5][i] * fv[6]; + + p[i] = iclip_pixel((sum + rounding_off_v) >> round_bits_v); + } + + // Shift the pointers, but only update the first 5; the 6th pointer is kept + // as it was before (and the 7th is implicitly identical to the 6th). + for (int i = 0; i < 5; i++) + ptrs[i] = ptrs[i + 1]; +} + +static void wiener_filter_hv(pixel *p, uint16_t **ptrs, const pixel (*left)[4], + const pixel *src, const int16_t filter[2][8], + const int w, const enum LrEdgeFlags edges + HIGHBD_DECL_SUFFIX) +{ + const int bitdepth = bitdepth_from_max(bitdepth_max); + + const int round_bits_v = 11 - (bitdepth == 12) * 2; + const int rounding_off_v = 1 << (round_bits_v - 1); + const int round_offset = 1 << (bitdepth + (round_bits_v - 1)); + + const int16_t *fh = filter[0]; + const int16_t *fv = filter[1]; + + // Do combined horziontal and vertical filtering; doing horizontal + // filtering of one row, combined with vertical filtering of 6 + // preexisting rows and the newly filtered row. + + // For simplicity in the C implementation, just do a separate call + // of the horizontal filter, into a temporary buffer. + uint16_t tmp[REST_UNIT_STRIDE]; + wiener_filter_h(tmp, left, src, fh, w, edges HIGHBD_TAIL_SUFFIX); + + for (int i = 0; i < w; i++) { + int sum = -round_offset; + + // Filter using the 6 stored preexisting rows, and the newly + // filtered one in tmp[]. + for (int k = 0; k < 6; k++) + sum += ptrs[k][i] * fv[k]; + sum += tmp[i] * fv[6]; + // At this point, after having read all inputs at point [i], we + // could overwrite [i] with the newly filtered data. + + p[i] = iclip_pixel((sum + rounding_off_v) >> round_bits_v); + } + + // For simplicity in the C implementation, just memcpy the newly + // filtered row into ptrs[6]. Normally, in steady state filtering, + // this output row, ptrs[6], is equal to ptrs[0]. However at startup, + // at the top of the filtered area, we may have ptrs[0] equal to ptrs[1], + // so we can't assume we can write into ptrs[0] but we need to keep + // a separate pointer for the next row to write into. + memcpy(ptrs[6], tmp, sizeof(uint16_t) * REST_UNIT_STRIDE); + + // Rotate the window of pointers. Shift the 6 pointers downwards one step. + for (int i = 0; i < 6; i++) + ptrs[i] = ptrs[i + 1]; + // The topmost pointer, ptrs[6], which isn't used as input, is set to + // ptrs[0], which will be used as output for the next _hv call. + // At the start of the filtering, the caller may set ptrs[6] to the + // right next buffer to fill in, instead. + ptrs[6] = ptrs[0]; +} + +// FIXME Could split into luma and chroma specific functions, +// (since first and last tops are always 0 for chroma) +static void wiener_c(pixel *p, const ptrdiff_t stride, + const pixel (*left)[4], + const pixel *lpf, const int w, int h, + const LooprestorationParams *const params, + const enum LrEdgeFlags edges HIGHBD_DECL_SUFFIX) +{ + // Values stored between horizontal and vertical filtering don't + // fit in a uint8_t. + uint16_t hor[6 * REST_UNIT_STRIDE]; + uint16_t *ptrs[7], *rows[6]; + for (int i = 0; i < 6; i++) + rows[i] = &hor[i * REST_UNIT_STRIDE]; + const int16_t (*const filter)[8] = params->filter; + const int16_t *fh = params->filter[0]; + const int16_t *fv = params->filter[1]; + const pixel *lpf_bottom = lpf + 6*PXSTRIDE(stride); + + const pixel *src = p; + if (edges & LR_HAVE_TOP) { + ptrs[0] = rows[0]; + ptrs[1] = rows[0]; + ptrs[2] = rows[1]; + ptrs[3] = rows[2]; + ptrs[4] = rows[2]; + ptrs[5] = rows[2]; + + wiener_filter_h(rows[0], NULL, lpf, fh, w, edges HIGHBD_TAIL_SUFFIX); + lpf += PXSTRIDE(stride); + wiener_filter_h(rows[1], NULL, lpf, fh, w, edges HIGHBD_TAIL_SUFFIX); + + wiener_filter_h(rows[2], left, src, fh, w, edges HIGHBD_TAIL_SUFFIX); + left++; + src += PXSTRIDE(stride); + + if (--h <= 0) + goto v1; + + ptrs[4] = ptrs[5] = rows[3]; + wiener_filter_h(rows[3], left, src, fh, w, edges HIGHBD_TAIL_SUFFIX); + left++; + src += PXSTRIDE(stride); + + if (--h <= 0) + goto v2; + + ptrs[5] = rows[4]; + wiener_filter_h(rows[4], left, src, fh, w, edges HIGHBD_TAIL_SUFFIX); + left++; + src += PXSTRIDE(stride); + + if (--h <= 0) + goto v3; + } else { + ptrs[0] = rows[0]; + ptrs[1] = rows[0]; + ptrs[2] = rows[0]; + ptrs[3] = rows[0]; + ptrs[4] = rows[0]; + ptrs[5] = rows[0]; + + wiener_filter_h(rows[0], left, src, fh, w, edges HIGHBD_TAIL_SUFFIX); + left++; + src += PXSTRIDE(stride); + + if (--h <= 0) + goto v1; + + ptrs[4] = ptrs[5] = rows[1]; + wiener_filter_h(rows[1], left, src, fh, w, edges HIGHBD_TAIL_SUFFIX); + left++; + src += PXSTRIDE(stride); + + if (--h <= 0) + goto v2; + + ptrs[5] = rows[2]; + wiener_filter_h(rows[2], left, src, fh, w, edges HIGHBD_TAIL_SUFFIX); + left++; + src += PXSTRIDE(stride); + + if (--h <= 0) + goto v3; + + ptrs[6] = rows[3]; + wiener_filter_hv(p, ptrs, left, src, filter, w, edges + HIGHBD_TAIL_SUFFIX); + left++; + src += PXSTRIDE(stride); + p += PXSTRIDE(stride); + + if (--h <= 0) + goto v3; + + ptrs[6] = rows[4]; + wiener_filter_hv(p, ptrs, left, src, filter, w, edges + HIGHBD_TAIL_SUFFIX); + left++; + src += PXSTRIDE(stride); + p += PXSTRIDE(stride); + + if (--h <= 0) + goto v3; + } + + ptrs[6] = ptrs[5] + REST_UNIT_STRIDE; + do { + wiener_filter_hv(p, ptrs, left, src, filter, w, edges + HIGHBD_TAIL_SUFFIX); + left++; + src += PXSTRIDE(stride); + p += PXSTRIDE(stride); + } while (--h > 0); + + if (!(edges & LR_HAVE_BOTTOM)) + goto v3; + + wiener_filter_hv(p, ptrs, NULL, lpf_bottom, filter, w, edges + HIGHBD_TAIL_SUFFIX); + lpf_bottom += PXSTRIDE(stride); + p += PXSTRIDE(stride); + + wiener_filter_hv(p, ptrs, NULL, lpf_bottom, filter, w, edges + HIGHBD_TAIL_SUFFIX); + p += PXSTRIDE(stride); +v1: + wiener_filter_v(p, ptrs, fv, w HIGHBD_TAIL_SUFFIX); + + return; + +v3: + wiener_filter_v(p, ptrs, fv, w HIGHBD_TAIL_SUFFIX); + p += PXSTRIDE(stride); +v2: + wiener_filter_v(p, ptrs, fv, w HIGHBD_TAIL_SUFFIX); + p += PXSTRIDE(stride); + goto v1; +} + +// SGR +static NOINLINE void rotate(int32_t **sumsq_ptrs, coef **sum_ptrs, int n) +{ + int32_t *tmp32 = sumsq_ptrs[0]; + coef *tmpc = sum_ptrs[0]; + for (int i = 0; i < n - 1; i++) { + sumsq_ptrs[i] = sumsq_ptrs[i + 1]; + sum_ptrs[i] = sum_ptrs[i + 1]; + } + sumsq_ptrs[n - 1] = tmp32; + sum_ptrs[n - 1] = tmpc; +} + +static NOINLINE void rotate5_x2(int32_t **sumsq_ptrs, coef **sum_ptrs) +{ + int32_t *tmp32[2]; + coef *tmpc[2]; + for (int i = 0; i < 2; i++) { + tmp32[i] = sumsq_ptrs[i]; + tmpc[i] = sum_ptrs[i]; + } + for (int i = 0; i < 3; i++) { + sumsq_ptrs[i] = sumsq_ptrs[i + 2]; + sum_ptrs[i] = sum_ptrs[i + 2]; + } + for (int i = 0; i < 2; i++) { + sumsq_ptrs[3 + i] = tmp32[i]; + sum_ptrs[3 + i] = tmpc[i]; + } +} + +static NOINLINE void sgr_box3_row_h(int32_t *sumsq, coef *sum, + const pixel (*left)[4], + const pixel *src, const int w, + const enum LrEdgeFlags edges) +{ + sumsq++; + sum++; + int a = edges & LR_HAVE_LEFT ? (left ? left[0][2] : src[-2]) : src[0]; + int b = edges & LR_HAVE_LEFT ? (left ? left[0][3] : src[-1]) : src[0]; + for (int x = -1; x < w + 1; x++) { + int c = (x + 1 < w || (edges & LR_HAVE_RIGHT)) ? src[x + 1] : src[w - 1]; + sum[x] = a + b + c; + sumsq[x] = a * a + b * b + c * c; + a = b; + b = c; + } +} + +static NOINLINE void sgr_box5_row_h(int32_t *sumsq, coef *sum, + const pixel (*left)[4], + const pixel *src, const int w, + const enum LrEdgeFlags edges) +{ + sumsq++; + sum++; + int a = edges & LR_HAVE_LEFT ? (left ? left[0][1] : src[-3]) : src[0]; + int b = edges & LR_HAVE_LEFT ? (left ? left[0][2] : src[-2]) : src[0]; + int c = edges & LR_HAVE_LEFT ? (left ? left[0][3] : src[-1]) : src[0]; + int d = src[0]; + for (int x = -1; x < w + 1; x++) { + int e = (x + 2 < w || (edges & LR_HAVE_RIGHT)) ? src[x + 2] : src[w - 1]; + sum[x] = a + b + c + d + e; + sumsq[x] = a * a + b * b + c * c + d * d + e * e; + a = b; + b = c; + c = d; + d = e; + } +} + +static void sgr_box35_row_h(int32_t *sumsq3, coef *sum3, + int32_t *sumsq5, coef *sum5, + const pixel (*left)[4], + const pixel *src, const int w, + const enum LrEdgeFlags edges) +{ + sgr_box3_row_h(sumsq3, sum3, left, src, w, edges); + sgr_box5_row_h(sumsq5, sum5, left, src, w, edges); +} + +static NOINLINE void sgr_box3_row_v(int32_t **sumsq, coef **sum, + int32_t *sumsq_out, coef *sum_out, + const int w) +{ + for (int x = 0; x < w + 2; x++) { + int sq_a = sumsq[0][x]; + int sq_b = sumsq[1][x]; + int sq_c = sumsq[2][x]; + int s_a = sum[0][x]; + int s_b = sum[1][x]; + int s_c = sum[2][x]; + sumsq_out[x] = sq_a + sq_b + sq_c; + sum_out[x] = s_a + s_b + s_c; + } +} + +static NOINLINE void sgr_box5_row_v(int32_t **sumsq, coef **sum, + int32_t *sumsq_out, coef *sum_out, + const int w) +{ + for (int x = 0; x < w + 2; x++) { + int sq_a = sumsq[0][x]; + int sq_b = sumsq[1][x]; + int sq_c = sumsq[2][x]; + int sq_d = sumsq[3][x]; + int sq_e = sumsq[4][x]; + int s_a = sum[0][x]; + int s_b = sum[1][x]; + int s_c = sum[2][x]; + int s_d = sum[3][x]; + int s_e = sum[4][x]; + sumsq_out[x] = sq_a + sq_b + sq_c + sq_d + sq_e; + sum_out[x] = s_a + s_b + s_c + s_d + s_e; + } +} + +static NOINLINE void sgr_calc_row_ab(int32_t *AA, coef *BB, int w, int s, + int bitdepth_max, int n, int sgr_one_by_x) +{ + const int bitdepth_min_8 = bitdepth_from_max(bitdepth_max) - 8; + for (int i = 0; i < w + 2; i++) { + const int a = + (AA[i] + ((1 << (2 * bitdepth_min_8)) >> 1)) >> (2 * bitdepth_min_8); + const int b = + (BB[i] + ((1 << bitdepth_min_8) >> 1)) >> bitdepth_min_8; + + const unsigned p = imax(a * n - b * b, 0); + const unsigned z = (p * s + (1 << 19)) >> 20; + const unsigned x = dav1d_sgr_x_by_x[umin(z, 255)]; + + // This is where we invert A and B, so that B is of size coef. + AA[i] = (x * BB[i] * sgr_one_by_x + (1 << 11)) >> 12; + BB[i] = x; + } +} + +static void sgr_box3_vert(int32_t **sumsq, coef **sum, + int32_t *sumsq_out, coef *sum_out, + const int w, const int s, const int bitdepth_max) +{ + sgr_box3_row_v(sumsq, sum, sumsq_out, sum_out, w); + sgr_calc_row_ab(sumsq_out, sum_out, w, s, bitdepth_max, 9, 455); + rotate(sumsq, sum, 3); +} + +static void sgr_box5_vert(int32_t **sumsq, coef **sum, + int32_t *sumsq_out, coef *sum_out, + const int w, const int s, const int bitdepth_max) +{ + sgr_box5_row_v(sumsq, sum, sumsq_out, sum_out, w); + sgr_calc_row_ab(sumsq_out, sum_out, w, s, bitdepth_max, 25, 164); + rotate5_x2(sumsq, sum); +} + +static void sgr_box3_hv(int32_t **sumsq, coef **sum, + int32_t *AA, coef *BB, + const pixel (*left)[4], + const pixel *src, const int w, + const int s, + const enum LrEdgeFlags edges, + const int bitdepth_max) +{ + sgr_box3_row_h(sumsq[2], sum[2], left, src, w, edges); + sgr_box3_vert(sumsq, sum, AA, BB, w, s, bitdepth_max); +} + +static NOINLINE void sgr_finish_filter_row1(coef *tmp, + const pixel *src, + int32_t **A_ptrs, coef **B_ptrs, + const int w) +{ +#define EIGHT_NEIGHBORS(P, i)\ + ((P[1][i] + P[1][i - 1] + P[1][i + 1] + P[0][i] + P[2][i]) * 4 + \ + (P[0][i - 1] + P[2][i - 1] + \ + P[0][i + 1] + P[2][i + 1]) * 3) + for (int i = 0; i < w; i++) { + const int a = EIGHT_NEIGHBORS(B_ptrs, i + 1); + const int b = EIGHT_NEIGHBORS(A_ptrs, i + 1); + tmp[i] = (b - a * src[i] + (1 << 8)) >> 9; + } +#undef EIGHT_NEIGHBORS +} + +#define FILTER_OUT_STRIDE (384) + +static NOINLINE void sgr_finish_filter2(coef *tmp, + const pixel *src, + const ptrdiff_t src_stride, + int32_t **A_ptrs, coef **B_ptrs, + const int w, const int h) +{ +#define SIX_NEIGHBORS(P, i)\ + ((P[0][i] + P[1][i]) * 6 + \ + (P[0][i - 1] + P[1][i - 1] + \ + P[0][i + 1] + P[1][i + 1]) * 5) + for (int i = 0; i < w; i++) { + const int a = SIX_NEIGHBORS(B_ptrs, i + 1); + const int b = SIX_NEIGHBORS(A_ptrs, i + 1); + tmp[i] = (b - a * src[i] + (1 << 8)) >> 9; + } + if (h <= 1) + return; + tmp += FILTER_OUT_STRIDE; + src += PXSTRIDE(src_stride); + const int32_t *A = &A_ptrs[1][1]; + const coef *B = &B_ptrs[1][1]; + for (int i = 0; i < w; i++) { + const int a = B[i] * 6 + (B[i - 1] + B[i + 1]) * 5; + const int b = A[i] * 6 + (A[i - 1] + A[i + 1]) * 5; + tmp[i] = (b - a * src[i] + (1 << 7)) >> 8; + } +#undef SIX_NEIGHBORS +} + +static NOINLINE void sgr_weighted_row1(pixel *dst, const coef *t1, + const int w, const int w1 HIGHBD_DECL_SUFFIX) +{ + for (int i = 0; i < w; i++) { + const int v = w1 * t1[i]; + dst[i] = iclip_pixel(dst[i] + ((v + (1 << 10)) >> 11)); + } +} + +static NOINLINE void sgr_weighted2(pixel *dst, const ptrdiff_t dst_stride, + const coef *t1, const coef *t2, + const int w, const int h, + const int w0, const int w1 HIGHBD_DECL_SUFFIX) +{ + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + const int v = w0 * t1[i] + w1 * t2[i]; + dst[i] = iclip_pixel(dst[i] + ((v + (1 << 10)) >> 11)); + } + dst += PXSTRIDE(dst_stride); + t1 += FILTER_OUT_STRIDE; + t2 += FILTER_OUT_STRIDE; + } +} + +static NOINLINE void sgr_finish1(pixel **dst, const ptrdiff_t stride, + int32_t **A_ptrs, coef **B_ptrs, const int w, + const int w1 HIGHBD_DECL_SUFFIX) +{ + // Only one single row, no stride needed + ALIGN_STK_16(coef, tmp, 384,); + + sgr_finish_filter_row1(tmp, *dst, A_ptrs, B_ptrs, w); + sgr_weighted_row1(*dst, tmp, w, w1 HIGHBD_TAIL_SUFFIX); + *dst += PXSTRIDE(stride); + rotate(A_ptrs, B_ptrs, 3); +} + +static NOINLINE void sgr_finish2(pixel **dst, const ptrdiff_t stride, + int32_t **A_ptrs, coef **B_ptrs, + const int w, const int h, const int w1 + HIGHBD_DECL_SUFFIX) +{ + ALIGN_STK_16(coef, tmp, 2*FILTER_OUT_STRIDE,); + + sgr_finish_filter2(tmp, *dst, stride, A_ptrs, B_ptrs, w, h); + sgr_weighted_row1(*dst, tmp, w, w1 HIGHBD_TAIL_SUFFIX); + *dst += PXSTRIDE(stride); + if (h > 1) { + sgr_weighted_row1(*dst, tmp + FILTER_OUT_STRIDE, w, w1 HIGHBD_TAIL_SUFFIX); + *dst += PXSTRIDE(stride); + } + rotate(A_ptrs, B_ptrs, 2); +} + +static NOINLINE void sgr_finish_mix(pixel **dst, const ptrdiff_t stride, + int32_t **A5_ptrs, coef **B5_ptrs, + int32_t **A3_ptrs, coef **B3_ptrs, + const int w, const int h, + const int w0, const int w1 HIGHBD_DECL_SUFFIX) +{ + ALIGN_STK_16(coef, tmp5, 2*FILTER_OUT_STRIDE,); + ALIGN_STK_16(coef, tmp3, 2*FILTER_OUT_STRIDE,); + + sgr_finish_filter2(tmp5, *dst, stride, A5_ptrs, B5_ptrs, w, h); + sgr_finish_filter_row1(tmp3, *dst, A3_ptrs, B3_ptrs, w); + if (h > 1) + sgr_finish_filter_row1(tmp3 + FILTER_OUT_STRIDE, *dst + PXSTRIDE(stride), + &A3_ptrs[1], &B3_ptrs[1], w); + sgr_weighted2(*dst, stride, tmp5, tmp3, w, h, w0, w1 HIGHBD_TAIL_SUFFIX); + *dst += h*PXSTRIDE(stride); + rotate(A5_ptrs, B5_ptrs, 2); + rotate(A3_ptrs, B3_ptrs, 4); +} + + +static void sgr_3x3_c(pixel *dst, const ptrdiff_t stride, + const pixel (*left)[4], const pixel *lpf, + const int w, int h, + const LooprestorationParams *const params, + const enum LrEdgeFlags edges HIGHBD_DECL_SUFFIX) +{ +#define BUF_STRIDE (384 + 16) + ALIGN_STK_16(int32_t, sumsq_buf, BUF_STRIDE * 3 + 16,); + ALIGN_STK_16(coef, sum_buf, BUF_STRIDE * 3 + 16,); + int32_t *sumsq_ptrs[3], *sumsq_rows[3]; + coef *sum_ptrs[3], *sum_rows[3]; + for (int i = 0; i < 3; i++) { + sumsq_rows[i] = &sumsq_buf[i * BUF_STRIDE]; + sum_rows[i] = &sum_buf[i * BUF_STRIDE]; + } + + ALIGN_STK_16(int32_t, A_buf, BUF_STRIDE * 3 + 16,); + ALIGN_STK_16(coef, B_buf, BUF_STRIDE * 3 + 16,); + int32_t *A_ptrs[3]; + coef *B_ptrs[3]; + for (int i = 0; i < 3; i++) { + A_ptrs[i] = &A_buf[i * BUF_STRIDE]; + B_ptrs[i] = &B_buf[i * BUF_STRIDE]; + } + const pixel *src = dst; + const pixel *lpf_bottom = lpf + 6*PXSTRIDE(stride); + + if (edges & LR_HAVE_TOP) { + sumsq_ptrs[0] = sumsq_rows[0]; + sumsq_ptrs[1] = sumsq_rows[1]; + sumsq_ptrs[2] = sumsq_rows[2]; + sum_ptrs[0] = sum_rows[0]; + sum_ptrs[1] = sum_rows[1]; + sum_ptrs[2] = sum_rows[2]; + + sgr_box3_row_h(sumsq_rows[0], sum_rows[0], NULL, lpf, w, edges); + lpf += PXSTRIDE(stride); + sgr_box3_row_h(sumsq_rows[1], sum_rows[1], NULL, lpf, w, edges); + + sgr_box3_hv(sumsq_ptrs, sum_ptrs, A_ptrs[2], B_ptrs[2], + left, src, w, params->sgr.s1, edges, BITDEPTH_MAX); + left++; + src += PXSTRIDE(stride); + rotate(A_ptrs, B_ptrs, 3); + + if (--h <= 0) + goto vert_1; + + sgr_box3_hv(sumsq_ptrs, sum_ptrs, A_ptrs[2], B_ptrs[2], + left, src, w, params->sgr.s1, edges, BITDEPTH_MAX); + left++; + src += PXSTRIDE(stride); + rotate(A_ptrs, B_ptrs, 3); + + if (--h <= 0) + goto vert_2; + } else { + sumsq_ptrs[0] = sumsq_rows[0]; + sumsq_ptrs[1] = sumsq_rows[0]; + sumsq_ptrs[2] = sumsq_rows[0]; + sum_ptrs[0] = sum_rows[0]; + sum_ptrs[1] = sum_rows[0]; + sum_ptrs[2] = sum_rows[0]; + + sgr_box3_row_h(sumsq_rows[0], sum_rows[0], left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box3_vert(sumsq_ptrs, sum_ptrs, A_ptrs[2], B_ptrs[2], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A_ptrs, B_ptrs, 3); + + if (--h <= 0) + goto vert_1; + + sumsq_ptrs[2] = sumsq_rows[1]; + sum_ptrs[2] = sum_rows[1]; + + sgr_box3_hv(sumsq_ptrs, sum_ptrs, A_ptrs[2], B_ptrs[2], + left, src, w, params->sgr.s1, edges, BITDEPTH_MAX); + left++; + src += PXSTRIDE(stride); + rotate(A_ptrs, B_ptrs, 3); + + if (--h <= 0) + goto vert_2; + + sumsq_ptrs[2] = sumsq_rows[2]; + sum_ptrs[2] = sum_rows[2]; + } + + do { + sgr_box3_hv(sumsq_ptrs, sum_ptrs, A_ptrs[2], B_ptrs[2], + left, src, w, params->sgr.s1, edges, BITDEPTH_MAX); + left++; + src += PXSTRIDE(stride); + + sgr_finish1(&dst, stride, A_ptrs, B_ptrs, + w, params->sgr.w1 HIGHBD_TAIL_SUFFIX); + } while (--h > 0); + + if (!(edges & LR_HAVE_BOTTOM)) + goto vert_2; + + sgr_box3_hv(sumsq_ptrs, sum_ptrs, A_ptrs[2], B_ptrs[2], + NULL, lpf_bottom, w, params->sgr.s1, edges, BITDEPTH_MAX); + lpf_bottom += PXSTRIDE(stride); + + sgr_finish1(&dst, stride, A_ptrs, B_ptrs, + w, params->sgr.w1 HIGHBD_TAIL_SUFFIX); + + sgr_box3_hv(sumsq_ptrs, sum_ptrs, A_ptrs[2], B_ptrs[2], + NULL, lpf_bottom, w, params->sgr.s1, edges, BITDEPTH_MAX); + + sgr_finish1(&dst, stride, A_ptrs, B_ptrs, + w, params->sgr.w1 HIGHBD_TAIL_SUFFIX); + return; + +vert_2: + sumsq_ptrs[2] = sumsq_ptrs[1]; + sum_ptrs[2] = sum_ptrs[1]; + sgr_box3_vert(sumsq_ptrs, sum_ptrs, A_ptrs[2], B_ptrs[2], + w, params->sgr.s1, BITDEPTH_MAX); + + sgr_finish1(&dst, stride, A_ptrs, B_ptrs, + w, params->sgr.w1 HIGHBD_TAIL_SUFFIX); + +output_1: + sumsq_ptrs[2] = sumsq_ptrs[1]; + sum_ptrs[2] = sum_ptrs[1]; + sgr_box3_vert(sumsq_ptrs, sum_ptrs, A_ptrs[2], B_ptrs[2], + w, params->sgr.s1, BITDEPTH_MAX); + + sgr_finish1(&dst, stride, A_ptrs, B_ptrs, + w, params->sgr.w1 HIGHBD_TAIL_SUFFIX); + return; + +vert_1: + sumsq_ptrs[2] = sumsq_ptrs[1]; + sum_ptrs[2] = sum_ptrs[1]; + sgr_box3_vert(sumsq_ptrs, sum_ptrs, A_ptrs[2], B_ptrs[2], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A_ptrs, B_ptrs, 3); + goto output_1; +} + +static void sgr_5x5_c(pixel *dst, const ptrdiff_t stride, + const pixel (*left)[4], const pixel *lpf, + const int w, int h, + const LooprestorationParams *const params, + const enum LrEdgeFlags edges HIGHBD_DECL_SUFFIX) +{ + ALIGN_STK_16(int32_t, sumsq_buf, BUF_STRIDE * 5 + 16,); + ALIGN_STK_16(coef, sum_buf, BUF_STRIDE * 5 + 16,); + int32_t *sumsq_ptrs[5], *sumsq_rows[5]; + coef *sum_ptrs[5], *sum_rows[5]; + for (int i = 0; i < 5; i++) { + sumsq_rows[i] = &sumsq_buf[i * BUF_STRIDE]; + sum_rows[i] = &sum_buf[i * BUF_STRIDE]; + } + + ALIGN_STK_16(int32_t, A_buf, BUF_STRIDE * 2 + 16,); + ALIGN_STK_16(coef, B_buf, BUF_STRIDE * 2 + 16,); + int32_t *A_ptrs[2]; + coef *B_ptrs[2]; + for (int i = 0; i < 2; i++) { + A_ptrs[i] = &A_buf[i * BUF_STRIDE]; + B_ptrs[i] = &B_buf[i * BUF_STRIDE]; + } + const pixel *src = dst; + const pixel *lpf_bottom = lpf + 6*PXSTRIDE(stride); + + if (edges & LR_HAVE_TOP) { + sumsq_ptrs[0] = sumsq_rows[0]; + sumsq_ptrs[1] = sumsq_rows[0]; + sumsq_ptrs[2] = sumsq_rows[1]; + sumsq_ptrs[3] = sumsq_rows[2]; + sumsq_ptrs[4] = sumsq_rows[3]; + sum_ptrs[0] = sum_rows[0]; + sum_ptrs[1] = sum_rows[0]; + sum_ptrs[2] = sum_rows[1]; + sum_ptrs[3] = sum_rows[2]; + sum_ptrs[4] = sum_rows[3]; + + sgr_box5_row_h(sumsq_rows[0], sum_rows[0], NULL, lpf, w, edges); + lpf += PXSTRIDE(stride); + sgr_box5_row_h(sumsq_rows[1], sum_rows[1], NULL, lpf, w, edges); + + sgr_box5_row_h(sumsq_rows[2], sum_rows[2], left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + if (--h <= 0) + goto vert_1; + + sgr_box5_row_h(sumsq_rows[3], sum_rows[3], left, src, w, edges); + left++; + src += PXSTRIDE(stride); + sgr_box5_vert(sumsq_ptrs, sum_ptrs, A_ptrs[1], B_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + rotate(A_ptrs, B_ptrs, 2); + + if (--h <= 0) + goto vert_2; + + // ptrs are rotated by 2; both [3] and [4] now point at rows[0]; set + // one of them to point at the previously unused rows[4]. + sumsq_ptrs[3] = sumsq_rows[4]; + sum_ptrs[3] = sum_rows[4]; + } else { + sumsq_ptrs[0] = sumsq_rows[0]; + sumsq_ptrs[1] = sumsq_rows[0]; + sumsq_ptrs[2] = sumsq_rows[0]; + sumsq_ptrs[3] = sumsq_rows[0]; + sumsq_ptrs[4] = sumsq_rows[0]; + sum_ptrs[0] = sum_rows[0]; + sum_ptrs[1] = sum_rows[0]; + sum_ptrs[2] = sum_rows[0]; + sum_ptrs[3] = sum_rows[0]; + sum_ptrs[4] = sum_rows[0]; + + sgr_box5_row_h(sumsq_rows[0], sum_rows[0], left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + if (--h <= 0) + goto vert_1; + + sumsq_ptrs[4] = sumsq_rows[1]; + sum_ptrs[4] = sum_rows[1]; + + sgr_box5_row_h(sumsq_rows[1], sum_rows[1], left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box5_vert(sumsq_ptrs, sum_ptrs, A_ptrs[1], B_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + rotate(A_ptrs, B_ptrs, 2); + + if (--h <= 0) + goto vert_2; + + sumsq_ptrs[3] = sumsq_rows[2]; + sumsq_ptrs[4] = sumsq_rows[3]; + sum_ptrs[3] = sum_rows[2]; + sum_ptrs[4] = sum_rows[3]; + + sgr_box5_row_h(sumsq_rows[2], sum_rows[2], left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + if (--h <= 0) + goto odd; + + sgr_box5_row_h(sumsq_rows[3], sum_rows[3], left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box5_vert(sumsq_ptrs, sum_ptrs, A_ptrs[1], B_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + sgr_finish2(&dst, stride, A_ptrs, B_ptrs, + w, 2, params->sgr.w0 HIGHBD_TAIL_SUFFIX); + + if (--h <= 0) + goto vert_2; + + // ptrs are rotated by 2; both [3] and [4] now point at rows[0]; set + // one of them to point at the previously unused rows[4]. + sumsq_ptrs[3] = sumsq_rows[4]; + sum_ptrs[3] = sum_rows[4]; + } + + do { + sgr_box5_row_h(sumsq_ptrs[3], sum_ptrs[3], left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + if (--h <= 0) + goto odd; + + sgr_box5_row_h(sumsq_ptrs[4], sum_ptrs[4], left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box5_vert(sumsq_ptrs, sum_ptrs, A_ptrs[1], B_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + sgr_finish2(&dst, stride, A_ptrs, B_ptrs, + w, 2, params->sgr.w0 HIGHBD_TAIL_SUFFIX); + } while (--h > 0); + + if (!(edges & LR_HAVE_BOTTOM)) + goto vert_2; + + sgr_box5_row_h(sumsq_ptrs[3], sum_ptrs[3], NULL, lpf_bottom, w, edges); + lpf_bottom += PXSTRIDE(stride); + sgr_box5_row_h(sumsq_ptrs[4], sum_ptrs[4], NULL, lpf_bottom, w, edges); + +output_2: + sgr_box5_vert(sumsq_ptrs, sum_ptrs, A_ptrs[1], B_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + sgr_finish2(&dst, stride, A_ptrs, B_ptrs, + w, 2, params->sgr.w0 HIGHBD_TAIL_SUFFIX); + return; + +vert_2: + // Duplicate the last row twice more + sumsq_ptrs[3] = sumsq_ptrs[2]; + sumsq_ptrs[4] = sumsq_ptrs[2]; + sum_ptrs[3] = sum_ptrs[2]; + sum_ptrs[4] = sum_ptrs[2]; + goto output_2; + +odd: + // Copy the last row as padding once + sumsq_ptrs[4] = sumsq_ptrs[3]; + sum_ptrs[4] = sum_ptrs[3]; + + sgr_box5_vert(sumsq_ptrs, sum_ptrs, A_ptrs[1], B_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + sgr_finish2(&dst, stride, A_ptrs, B_ptrs, + w, 2, params->sgr.w0 HIGHBD_TAIL_SUFFIX); + +output_1: + // Duplicate the last row twice more + sumsq_ptrs[3] = sumsq_ptrs[2]; + sumsq_ptrs[4] = sumsq_ptrs[2]; + sum_ptrs[3] = sum_ptrs[2]; + sum_ptrs[4] = sum_ptrs[2]; + + sgr_box5_vert(sumsq_ptrs, sum_ptrs, A_ptrs[1], B_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + // Output only one row + sgr_finish2(&dst, stride, A_ptrs, B_ptrs, + w, 1, params->sgr.w0 HIGHBD_TAIL_SUFFIX); + return; + +vert_1: + // Copy the last row as padding once + sumsq_ptrs[4] = sumsq_ptrs[3]; + sum_ptrs[4] = sum_ptrs[3]; + + sgr_box5_vert(sumsq_ptrs, sum_ptrs, A_ptrs[1], B_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + rotate(A_ptrs, B_ptrs, 2); + + goto output_1; +} + +static void sgr_mix_c(pixel *dst, const ptrdiff_t stride, + const pixel (*left)[4], const pixel *lpf, + const int w, int h, + const LooprestorationParams *const params, + const enum LrEdgeFlags edges HIGHBD_DECL_SUFFIX) +{ + ALIGN_STK_16(int32_t, sumsq5_buf, BUF_STRIDE * 5 + 16,); + ALIGN_STK_16(coef, sum5_buf, BUF_STRIDE * 5 + 16,); + int32_t *sumsq5_ptrs[5], *sumsq5_rows[5]; + coef *sum5_ptrs[5], *sum5_rows[5]; + for (int i = 0; i < 5; i++) { + sumsq5_rows[i] = &sumsq5_buf[i * BUF_STRIDE]; + sum5_rows[i] = &sum5_buf[i * BUF_STRIDE]; + } + ALIGN_STK_16(int32_t, sumsq3_buf, BUF_STRIDE * 3 + 16,); + ALIGN_STK_16(coef, sum3_buf, BUF_STRIDE * 3 + 16,); + int32_t *sumsq3_ptrs[3], *sumsq3_rows[3]; + coef *sum3_ptrs[3], *sum3_rows[3]; + for (int i = 0; i < 3; i++) { + sumsq3_rows[i] = &sumsq3_buf[i * BUF_STRIDE]; + sum3_rows[i] = &sum3_buf[i * BUF_STRIDE]; + } + + ALIGN_STK_16(int32_t, A5_buf, BUF_STRIDE * 2 + 16,); + ALIGN_STK_16(coef, B5_buf, BUF_STRIDE * 2 + 16,); + int32_t *A5_ptrs[2]; + coef *B5_ptrs[2]; + for (int i = 0; i < 2; i++) { + A5_ptrs[i] = &A5_buf[i * BUF_STRIDE]; + B5_ptrs[i] = &B5_buf[i * BUF_STRIDE]; + } + ALIGN_STK_16(int32_t, A3_buf, BUF_STRIDE * 4 + 16,); + ALIGN_STK_16(coef, B3_buf, BUF_STRIDE * 4 + 16,); + int32_t *A3_ptrs[4]; + coef *B3_ptrs[4]; + for (int i = 0; i < 4; i++) { + A3_ptrs[i] = &A3_buf[i * BUF_STRIDE]; + B3_ptrs[i] = &B3_buf[i * BUF_STRIDE]; + } + const pixel *src = dst; + const pixel *lpf_bottom = lpf + 6*PXSTRIDE(stride); + + if (edges & LR_HAVE_TOP) { + sumsq5_ptrs[0] = sumsq5_rows[0]; + sumsq5_ptrs[1] = sumsq5_rows[0]; + sumsq5_ptrs[2] = sumsq5_rows[1]; + sumsq5_ptrs[3] = sumsq5_rows[2]; + sumsq5_ptrs[4] = sumsq5_rows[3]; + sum5_ptrs[0] = sum5_rows[0]; + sum5_ptrs[1] = sum5_rows[0]; + sum5_ptrs[2] = sum5_rows[1]; + sum5_ptrs[3] = sum5_rows[2]; + sum5_ptrs[4] = sum5_rows[3]; + + sumsq3_ptrs[0] = sumsq3_rows[0]; + sumsq3_ptrs[1] = sumsq3_rows[1]; + sumsq3_ptrs[2] = sumsq3_rows[2]; + sum3_ptrs[0] = sum3_rows[0]; + sum3_ptrs[1] = sum3_rows[1]; + sum3_ptrs[2] = sum3_rows[2]; + + sgr_box35_row_h(sumsq3_rows[0], sum3_rows[0], + sumsq5_rows[0], sum5_rows[0], + NULL, lpf, w, edges); + lpf += PXSTRIDE(stride); + sgr_box35_row_h(sumsq3_rows[1], sum3_rows[1], + sumsq5_rows[1], sum5_rows[1], + NULL, lpf, w, edges); + + sgr_box35_row_h(sumsq3_rows[2], sum3_rows[2], + sumsq5_rows[2], sum5_rows[2], + left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A3_ptrs, B3_ptrs, 4); + + if (--h <= 0) + goto vert_1; + + sgr_box35_row_h(sumsq3_ptrs[2], sum3_ptrs[2], + sumsq5_rows[3], sum5_rows[3], + left, src, w, edges); + left++; + src += PXSTRIDE(stride); + sgr_box5_vert(sumsq5_ptrs, sum5_ptrs, A5_ptrs[1], B5_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + rotate(A5_ptrs, B5_ptrs, 2); + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A3_ptrs, B3_ptrs, 4); + + if (--h <= 0) + goto vert_2; + + // ptrs are rotated by 2; both [3] and [4] now point at rows[0]; set + // one of them to point at the previously unused rows[4]. + sumsq5_ptrs[3] = sumsq5_rows[4]; + sum5_ptrs[3] = sum5_rows[4]; + } else { + sumsq5_ptrs[0] = sumsq5_rows[0]; + sumsq5_ptrs[1] = sumsq5_rows[0]; + sumsq5_ptrs[2] = sumsq5_rows[0]; + sumsq5_ptrs[3] = sumsq5_rows[0]; + sumsq5_ptrs[4] = sumsq5_rows[0]; + sum5_ptrs[0] = sum5_rows[0]; + sum5_ptrs[1] = sum5_rows[0]; + sum5_ptrs[2] = sum5_rows[0]; + sum5_ptrs[3] = sum5_rows[0]; + sum5_ptrs[4] = sum5_rows[0]; + + sumsq3_ptrs[0] = sumsq3_rows[0]; + sumsq3_ptrs[1] = sumsq3_rows[0]; + sumsq3_ptrs[2] = sumsq3_rows[0]; + sum3_ptrs[0] = sum3_rows[0]; + sum3_ptrs[1] = sum3_rows[0]; + sum3_ptrs[2] = sum3_rows[0]; + + sgr_box35_row_h(sumsq3_rows[0], sum3_rows[0], + sumsq5_rows[0], sum5_rows[0], + left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A3_ptrs, B3_ptrs, 4); + + if (--h <= 0) + goto vert_1; + + sumsq5_ptrs[4] = sumsq5_rows[1]; + sum5_ptrs[4] = sum5_rows[1]; + + sumsq3_ptrs[2] = sumsq3_rows[1]; + sum3_ptrs[2] = sum3_rows[1]; + + sgr_box35_row_h(sumsq3_rows[1], sum3_rows[1], + sumsq5_rows[1], sum5_rows[1], + left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box5_vert(sumsq5_ptrs, sum5_ptrs, A5_ptrs[1], B5_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + rotate(A5_ptrs, B5_ptrs, 2); + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A3_ptrs, B3_ptrs, 4); + + if (--h <= 0) + goto vert_2; + + sumsq5_ptrs[3] = sumsq5_rows[2]; + sumsq5_ptrs[4] = sumsq5_rows[3]; + sum5_ptrs[3] = sum5_rows[2]; + sum5_ptrs[4] = sum5_rows[3]; + + sumsq3_ptrs[2] = sumsq3_rows[2]; + sum3_ptrs[2] = sum3_rows[2]; + + sgr_box35_row_h(sumsq3_rows[2], sum3_rows[2], + sumsq5_rows[2], sum5_rows[2], + left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A3_ptrs, B3_ptrs, 4); + + if (--h <= 0) + goto odd; + + sgr_box35_row_h(sumsq3_ptrs[2], sum3_ptrs[2], + sumsq5_rows[3], sum5_rows[3], + left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box5_vert(sumsq5_ptrs, sum5_ptrs, A5_ptrs[1], B5_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + sgr_finish_mix(&dst, stride, A5_ptrs, B5_ptrs, A3_ptrs, B3_ptrs, + w, 2, params->sgr.w0, params->sgr.w1 + HIGHBD_TAIL_SUFFIX); + + if (--h <= 0) + goto vert_2; + + // ptrs are rotated by 2; both [3] and [4] now point at rows[0]; set + // one of them to point at the previously unused rows[4]. + sumsq5_ptrs[3] = sumsq5_rows[4]; + sum5_ptrs[3] = sum5_rows[4]; + } + + do { + sgr_box35_row_h(sumsq3_ptrs[2], sum3_ptrs[2], + sumsq5_ptrs[3], sum5_ptrs[3], + left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A3_ptrs, B3_ptrs, 4); + + if (--h <= 0) + goto odd; + + sgr_box35_row_h(sumsq3_ptrs[2], sum3_ptrs[2], + sumsq5_ptrs[4], sum5_ptrs[4], + left, src, w, edges); + left++; + src += PXSTRIDE(stride); + + sgr_box5_vert(sumsq5_ptrs, sum5_ptrs, A5_ptrs[1], B5_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + sgr_finish_mix(&dst, stride, A5_ptrs, B5_ptrs, A3_ptrs, B3_ptrs, + w, 2, params->sgr.w0, params->sgr.w1 + HIGHBD_TAIL_SUFFIX); + } while (--h > 0); + + if (!(edges & LR_HAVE_BOTTOM)) + goto vert_2; + + sgr_box35_row_h(sumsq3_ptrs[2], sum3_ptrs[2], + sumsq5_ptrs[3], sum5_ptrs[3], + NULL, lpf_bottom, w, edges); + lpf_bottom += PXSTRIDE(stride); + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A3_ptrs, B3_ptrs, 4); + + sgr_box35_row_h(sumsq3_ptrs[2], sum3_ptrs[2], + sumsq5_ptrs[4], sum5_ptrs[4], + NULL, lpf_bottom, w, edges); + +output_2: + sgr_box5_vert(sumsq5_ptrs, sum5_ptrs, A5_ptrs[1], B5_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + sgr_finish_mix(&dst, stride, A5_ptrs, B5_ptrs, A3_ptrs, B3_ptrs, + w, 2, params->sgr.w0, params->sgr.w1 + HIGHBD_TAIL_SUFFIX); + return; + +vert_2: + // Duplicate the last row twice more + sumsq5_ptrs[3] = sumsq5_ptrs[2]; + sumsq5_ptrs[4] = sumsq5_ptrs[2]; + sum5_ptrs[3] = sum5_ptrs[2]; + sum5_ptrs[4] = sum5_ptrs[2]; + + sumsq3_ptrs[2] = sumsq3_ptrs[1]; + sum3_ptrs[2] = sum3_ptrs[1]; + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A3_ptrs, B3_ptrs, 4); + + sumsq3_ptrs[2] = sumsq3_ptrs[1]; + sum3_ptrs[2] = sum3_ptrs[1]; + + goto output_2; + +odd: + // Copy the last row as padding once + sumsq5_ptrs[4] = sumsq5_ptrs[3]; + sum5_ptrs[4] = sum5_ptrs[3]; + + sumsq3_ptrs[2] = sumsq3_ptrs[1]; + sum3_ptrs[2] = sum3_ptrs[1]; + + sgr_box5_vert(sumsq5_ptrs, sum5_ptrs, A5_ptrs[1], B5_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + sgr_finish_mix(&dst, stride, A5_ptrs, B5_ptrs, A3_ptrs, B3_ptrs, + w, 2, params->sgr.w0, params->sgr.w1 + HIGHBD_TAIL_SUFFIX); + +output_1: + // Duplicate the last row twice more + sumsq5_ptrs[3] = sumsq5_ptrs[2]; + sumsq5_ptrs[4] = sumsq5_ptrs[2]; + sum5_ptrs[3] = sum5_ptrs[2]; + sum5_ptrs[4] = sum5_ptrs[2]; + + sumsq3_ptrs[2] = sumsq3_ptrs[1]; + sum3_ptrs[2] = sum3_ptrs[1]; + + sgr_box5_vert(sumsq5_ptrs, sum5_ptrs, A5_ptrs[1], B5_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A3_ptrs, B3_ptrs, 4); + // Output only one row + sgr_finish_mix(&dst, stride, A5_ptrs, B5_ptrs, A3_ptrs, B3_ptrs, + w, 1, params->sgr.w0, params->sgr.w1 + HIGHBD_TAIL_SUFFIX); + return; + +vert_1: + // Copy the last row as padding once + sumsq5_ptrs[4] = sumsq5_ptrs[3]; + sum5_ptrs[4] = sum5_ptrs[3]; + + sumsq3_ptrs[2] = sumsq3_ptrs[1]; + sum3_ptrs[2] = sum3_ptrs[1]; + + sgr_box5_vert(sumsq5_ptrs, sum5_ptrs, A5_ptrs[1], B5_ptrs[1], + w, params->sgr.s0, BITDEPTH_MAX); + rotate(A5_ptrs, B5_ptrs, 2); + sgr_box3_vert(sumsq3_ptrs, sum3_ptrs, A3_ptrs[3], B3_ptrs[3], + w, params->sgr.s1, BITDEPTH_MAX); + rotate(A3_ptrs, B3_ptrs, 4); + + goto output_1; +} + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM +#include "src/arm/looprestoration.h" +#elif ARCH_LOONGARCH64 +#include "src/loongarch/looprestoration.h" +#elif ARCH_PPC64LE +#include "src/ppc/looprestoration.h" +#elif ARCH_X86 +#include "src/x86/looprestoration.h" +#endif +#endif + +COLD void bitfn(dav1d_loop_restoration_dsp_init)(Dav1dLoopRestorationDSPContext *const c, + const int bpc) +{ + c->wiener[0] = c->wiener[1] = wiener_c; + c->sgr[0] = sgr_5x5_c; + c->sgr[1] = sgr_3x3_c; + c->sgr[2] = sgr_mix_c; + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM + loop_restoration_dsp_init_arm(c, bpc); +#elif ARCH_LOONGARCH64 + loop_restoration_dsp_init_loongarch(c, bpc); +#elif ARCH_PPC64LE + loop_restoration_dsp_init_ppc(c, bpc); +#elif ARCH_X86 + loop_restoration_dsp_init_x86(c, bpc); +#endif +#endif +} diff --git a/3rdparty/dav1d/src/lr_apply.h b/3rdparty/dav1d/src/lr_apply.h new file mode 100644 index 0000000..2815367 --- /dev/null +++ b/3rdparty/dav1d/src/lr_apply.h @@ -0,0 +1,47 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_LR_APPLY_H +#define DAV1D_SRC_LR_APPLY_H + +#include +#include + +#include "common/bitdepth.h" + +#include "src/internal.h" + +enum LrRestorePlanes { + LR_RESTORE_Y = 1 << 0, + LR_RESTORE_U = 1 << 1, + LR_RESTORE_V = 1 << 2, +}; + +void bytefn(dav1d_lr_sbrow)(Dav1dFrameContext *const f, pixel *const dst[3], + int sby); + +#endif /* DAV1D_SRC_LR_APPLY_H */ diff --git a/3rdparty/dav1d/src/lr_apply_tmpl.c b/3rdparty/dav1d/src/lr_apply_tmpl.c new file mode 100644 index 0000000..ec0acdf --- /dev/null +++ b/3rdparty/dav1d/src/lr_apply_tmpl.c @@ -0,0 +1,202 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/intops.h" + +#include "src/lr_apply.h" + +static void lr_stripe(const Dav1dFrameContext *const f, pixel *p, + const pixel (*left)[4], int x, int y, + const int plane, const int unit_w, const int row_h, + const Av1RestorationUnit *const lr, enum LrEdgeFlags edges) +{ + const Dav1dDSPContext *const dsp = f->dsp; + const int chroma = !!plane; + const int ss_ver = chroma & (f->sr_cur.p.p.layout == DAV1D_PIXEL_LAYOUT_I420); + const ptrdiff_t stride = f->sr_cur.p.stride[chroma]; + const int sby = (y + (y ? 8 << ss_ver : 0)) >> (6 - ss_ver + f->seq_hdr->sb128); + const int have_tt = f->c->n_tc > 1; + const pixel *lpf = f->lf.lr_lpf_line[plane] + + have_tt * (sby * (4 << f->seq_hdr->sb128) - 4) * PXSTRIDE(stride) + x; + + // The first stripe of the frame is shorter by 8 luma pixel rows. + int stripe_h = imin((64 - 8 * !y) >> ss_ver, row_h - y); + + looprestorationfilter_fn lr_fn; + LooprestorationParams params; + if (lr->type == DAV1D_RESTORATION_WIENER) { + int16_t (*const filter)[8] = params.filter; + filter[0][0] = filter[0][6] = lr->filter_h[0]; + filter[0][1] = filter[0][5] = lr->filter_h[1]; + filter[0][2] = filter[0][4] = lr->filter_h[2]; + filter[0][3] = -(filter[0][0] + filter[0][1] + filter[0][2]) * 2; +#if BITDEPTH != 8 + /* For 8-bit SIMD it's beneficial to handle the +128 separately + * in order to avoid overflows. */ + filter[0][3] += 128; +#endif + + filter[1][0] = filter[1][6] = lr->filter_v[0]; + filter[1][1] = filter[1][5] = lr->filter_v[1]; + filter[1][2] = filter[1][4] = lr->filter_v[2]; + filter[1][3] = 128 - (filter[1][0] + filter[1][1] + filter[1][2]) * 2; + + lr_fn = dsp->lr.wiener[!(filter[0][0] | filter[1][0])]; + } else { + assert(lr->type >= DAV1D_RESTORATION_SGRPROJ); + const int sgr_idx = lr->type - DAV1D_RESTORATION_SGRPROJ; + const uint16_t *const sgr_params = dav1d_sgr_params[sgr_idx]; + params.sgr.s0 = sgr_params[0]; + params.sgr.s1 = sgr_params[1]; + params.sgr.w0 = lr->sgr_weights[0]; + params.sgr.w1 = 128 - (lr->sgr_weights[0] + lr->sgr_weights[1]); + + lr_fn = dsp->lr.sgr[!!sgr_params[0] + !!sgr_params[1] * 2 - 1]; + } + + while (y + stripe_h <= row_h) { + // Change the HAVE_BOTTOM bit in edges to (sby + 1 != f->sbh || y + stripe_h != row_h) + edges ^= (-(sby + 1 != f->sbh || y + stripe_h != row_h) ^ edges) & LR_HAVE_BOTTOM; + lr_fn(p, stride, left, lpf, unit_w, stripe_h, ¶ms, edges HIGHBD_CALL_SUFFIX); + + left += stripe_h; + y += stripe_h; + p += stripe_h * PXSTRIDE(stride); + edges |= LR_HAVE_TOP; + stripe_h = imin(64 >> ss_ver, row_h - y); + if (stripe_h == 0) break; + lpf += 4 * PXSTRIDE(stride); + } +} + +static void backup4xU(pixel (*dst)[4], const pixel *src, const ptrdiff_t src_stride, + int u) +{ + for (; u > 0; u--, dst++, src += PXSTRIDE(src_stride)) + pixel_copy(dst, src, 4); +} + +static void lr_sbrow(const Dav1dFrameContext *const f, pixel *p, const int y, + const int w, const int h, const int row_h, const int plane) +{ + const int chroma = !!plane; + const int ss_ver = chroma & (f->sr_cur.p.p.layout == DAV1D_PIXEL_LAYOUT_I420); + const int ss_hor = chroma & (f->sr_cur.p.p.layout != DAV1D_PIXEL_LAYOUT_I444); + const ptrdiff_t p_stride = f->sr_cur.p.stride[chroma]; + + const int unit_size_log2 = f->frame_hdr->restoration.unit_size[!!plane]; + const int unit_size = 1 << unit_size_log2; + const int half_unit_size = unit_size >> 1; + const int max_unit_size = unit_size + half_unit_size; + + // Y coordinate of the sbrow (y is 8 luma pixel rows above row_y) + const int row_y = y + ((8 >> ss_ver) * !!y); + + // FIXME This is an ugly hack to lookup the proper AV1Filter unit for + // chroma planes. Question: For Multithreaded decoding, is it better + // to store the chroma LR information with collocated Luma information? + // In other words. For a chroma restoration unit locate at 128,128 and + // with a 4:2:0 chroma subsampling, do we store the filter information at + // the AV1Filter unit located at (128,128) or (256,256) + // TODO Support chroma subsampling. + const int shift_hor = 7 - ss_hor; + + /* maximum sbrow height is 128 + 8 rows offset */ + ALIGN_STK_16(pixel, pre_lr_border, 2, [128 + 8][4]); + const Av1RestorationUnit *lr[2]; + + enum LrEdgeFlags edges = (y > 0 ? LR_HAVE_TOP : 0) | LR_HAVE_RIGHT; + + int aligned_unit_pos = row_y & ~(unit_size - 1); + if (aligned_unit_pos && aligned_unit_pos + half_unit_size > h) + aligned_unit_pos -= unit_size; + aligned_unit_pos <<= ss_ver; + const int sb_idx = (aligned_unit_pos >> 7) * f->sr_sb128w; + const int unit_idx = ((aligned_unit_pos >> 6) & 1) << 1; + lr[0] = &f->lf.lr_mask[sb_idx].lr[plane][unit_idx]; + int restore = lr[0]->type != DAV1D_RESTORATION_NONE; + int x = 0, bit = 0; + for (; x + max_unit_size <= w; p += unit_size, edges |= LR_HAVE_LEFT, bit ^= 1) { + const int next_x = x + unit_size; + const int next_u_idx = unit_idx + ((next_x >> (shift_hor - 1)) & 1); + lr[!bit] = + &f->lf.lr_mask[sb_idx + (next_x >> shift_hor)].lr[plane][next_u_idx]; + const int restore_next = lr[!bit]->type != DAV1D_RESTORATION_NONE; + if (restore_next) + backup4xU(pre_lr_border[bit], p + unit_size - 4, p_stride, row_h - y); + if (restore) + lr_stripe(f, p, pre_lr_border[!bit], x, y, plane, unit_size, row_h, + lr[bit], edges); + x = next_x; + restore = restore_next; + } + if (restore) { + edges &= ~LR_HAVE_RIGHT; + const int unit_w = w - x; + lr_stripe(f, p, pre_lr_border[!bit], x, y, plane, unit_w, row_h, lr[bit], edges); + } +} + +void bytefn(dav1d_lr_sbrow)(Dav1dFrameContext *const f, pixel *const dst[3], + const int sby) +{ + const int offset_y = 8 * !!sby; + const ptrdiff_t *const dst_stride = f->sr_cur.p.stride; + const int restore_planes = f->lf.restore_planes; + const int not_last = sby + 1 < f->sbh; + + if (restore_planes & LR_RESTORE_Y) { + const int h = f->sr_cur.p.p.h; + const int w = f->sr_cur.p.p.w; + const int next_row_y = (sby + 1) << (6 + f->seq_hdr->sb128); + const int row_h = imin(next_row_y - 8 * not_last, h); + const int y_stripe = (sby << (6 + f->seq_hdr->sb128)) - offset_y; + lr_sbrow(f, dst[0] - offset_y * PXSTRIDE(dst_stride[0]), y_stripe, w, + h, row_h, 0); + } + if (restore_planes & (LR_RESTORE_U | LR_RESTORE_V)) { + const int ss_ver = f->sr_cur.p.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = f->sr_cur.p.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int h = (f->sr_cur.p.p.h + ss_ver) >> ss_ver; + const int w = (f->sr_cur.p.p.w + ss_hor) >> ss_hor; + const int next_row_y = (sby + 1) << ((6 - ss_ver) + f->seq_hdr->sb128); + const int row_h = imin(next_row_y - (8 >> ss_ver) * not_last, h); + const int offset_uv = offset_y >> ss_ver; + const int y_stripe = (sby << ((6 - ss_ver) + f->seq_hdr->sb128)) - offset_uv; + if (restore_planes & LR_RESTORE_U) + lr_sbrow(f, dst[1] - offset_uv * PXSTRIDE(dst_stride[1]), y_stripe, + w, h, row_h, 1); + + if (restore_planes & LR_RESTORE_V) + lr_sbrow(f, dst[2] - offset_uv * PXSTRIDE(dst_stride[1]), y_stripe, + w, h, row_h, 2); + } +} diff --git a/3rdparty/dav1d/src/mc.h b/3rdparty/dav1d/src/mc.h new file mode 100644 index 0000000..587b908 --- /dev/null +++ b/3rdparty/dav1d/src/mc.h @@ -0,0 +1,166 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_MC_H +#define DAV1D_SRC_MC_H + +#include +#include + +#include "common/bitdepth.h" + +#include "src/levels.h" + +#define decl_mc_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, \ + const pixel *src, ptrdiff_t src_stride, \ + int w, int h, int mx, int my HIGHBD_DECL_SUFFIX) +typedef decl_mc_fn(*mc_fn); + +#define decl_mc_scaled_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, \ + const pixel *src, ptrdiff_t src_stride, \ + int w, int h, int mx, int my, int dx, int dy HIGHBD_DECL_SUFFIX) +typedef decl_mc_scaled_fn(*mc_scaled_fn); + +#define decl_warp8x8_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, \ + const pixel *src, ptrdiff_t src_stride, \ + const int16_t *abcd, int mx, int my HIGHBD_DECL_SUFFIX) +typedef decl_warp8x8_fn(*warp8x8_fn); + +#define decl_mct_fn(name) \ +void (name)(int16_t *tmp, const pixel *src, ptrdiff_t src_stride, \ + int w, int h, int mx, int my HIGHBD_DECL_SUFFIX) +typedef decl_mct_fn(*mct_fn); + +#define decl_mct_scaled_fn(name) \ +void (name)(int16_t *tmp, const pixel *src, ptrdiff_t src_stride, \ + int w, int h, int mx, int my, int dx, int dy HIGHBD_DECL_SUFFIX) +typedef decl_mct_scaled_fn(*mct_scaled_fn); + +#define decl_warp8x8t_fn(name) \ +void (name)(int16_t *tmp, const ptrdiff_t tmp_stride, \ + const pixel *src, ptrdiff_t src_stride, \ + const int16_t *abcd, int mx, int my HIGHBD_DECL_SUFFIX) +typedef decl_warp8x8t_fn(*warp8x8t_fn); + +#define decl_avg_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, \ + const int16_t *tmp1, const int16_t *tmp2, int w, int h \ + HIGHBD_DECL_SUFFIX) +typedef decl_avg_fn(*avg_fn); + +#define decl_w_avg_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, \ + const int16_t *tmp1, const int16_t *tmp2, int w, int h, int weight \ + HIGHBD_DECL_SUFFIX) +typedef decl_w_avg_fn(*w_avg_fn); + +#define decl_mask_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, \ + const int16_t *tmp1, const int16_t *tmp2, int w, int h, \ + const uint8_t *mask HIGHBD_DECL_SUFFIX) +typedef decl_mask_fn(*mask_fn); + +#define decl_w_mask_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, \ + const int16_t *tmp1, const int16_t *tmp2, int w, int h, \ + uint8_t *mask, int sign HIGHBD_DECL_SUFFIX) +typedef decl_w_mask_fn(*w_mask_fn); + +#define decl_blend_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, const pixel *tmp, \ + int w, int h, const uint8_t *mask) +typedef decl_blend_fn(*blend_fn); + +#define decl_blend_dir_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, const pixel *tmp, int w, int h) +typedef decl_blend_dir_fn(*blend_dir_fn); + +#define decl_emu_edge_fn(name) \ +void (name)(intptr_t bw, intptr_t bh, intptr_t iw, intptr_t ih, intptr_t x, intptr_t y, \ + pixel *dst, ptrdiff_t dst_stride, const pixel *src, ptrdiff_t src_stride) +typedef decl_emu_edge_fn(*emu_edge_fn); + +#define decl_resize_fn(name) \ +void (name)(pixel *dst, ptrdiff_t dst_stride, \ + const pixel *src, ptrdiff_t src_stride, \ + int dst_w, int h, int src_w, int dx, int mx HIGHBD_DECL_SUFFIX) +typedef decl_resize_fn(*resize_fn); + +#define decl_8tap_gen(decl_name, fn_name, opt) \ + decl_##decl_name##_fn(BF(dav1d_##fn_name##_8tap_regular, opt)); \ + decl_##decl_name##_fn(BF(dav1d_##fn_name##_8tap_regular_smooth, opt)); \ + decl_##decl_name##_fn(BF(dav1d_##fn_name##_8tap_regular_sharp, opt)); \ + decl_##decl_name##_fn(BF(dav1d_##fn_name##_8tap_smooth_regular, opt)); \ + decl_##decl_name##_fn(BF(dav1d_##fn_name##_8tap_smooth, opt)); \ + decl_##decl_name##_fn(BF(dav1d_##fn_name##_8tap_smooth_sharp, opt)); \ + decl_##decl_name##_fn(BF(dav1d_##fn_name##_8tap_sharp_regular, opt)); \ + decl_##decl_name##_fn(BF(dav1d_##fn_name##_8tap_sharp_smooth, opt)); \ + decl_##decl_name##_fn(BF(dav1d_##fn_name##_8tap_sharp, opt)) + +#define decl_8tap_fns(opt) \ + decl_8tap_gen(mc, put, opt); \ + decl_8tap_gen(mct, prep, opt) + +#define init_8tap_gen(name, opt) \ + init_##name##_fn(FILTER_2D_8TAP_REGULAR, 8tap_regular, opt); \ + init_##name##_fn(FILTER_2D_8TAP_REGULAR_SMOOTH, 8tap_regular_smooth, opt); \ + init_##name##_fn(FILTER_2D_8TAP_REGULAR_SHARP, 8tap_regular_sharp, opt); \ + init_##name##_fn(FILTER_2D_8TAP_SMOOTH_REGULAR, 8tap_smooth_regular, opt); \ + init_##name##_fn(FILTER_2D_8TAP_SMOOTH, 8tap_smooth, opt); \ + init_##name##_fn(FILTER_2D_8TAP_SMOOTH_SHARP, 8tap_smooth_sharp, opt); \ + init_##name##_fn(FILTER_2D_8TAP_SHARP_REGULAR, 8tap_sharp_regular, opt); \ + init_##name##_fn(FILTER_2D_8TAP_SHARP_SMOOTH, 8tap_sharp_smooth, opt); \ + init_##name##_fn(FILTER_2D_8TAP_SHARP, 8tap_sharp, opt) + +#define init_8tap_fns(opt) \ + init_8tap_gen(mc, opt); \ + init_8tap_gen(mct, opt) + +typedef struct Dav1dMCDSPContext { + mc_fn mc[N_2D_FILTERS]; + mc_scaled_fn mc_scaled[N_2D_FILTERS]; + mct_fn mct[N_2D_FILTERS]; + mct_scaled_fn mct_scaled[N_2D_FILTERS]; + avg_fn avg; + w_avg_fn w_avg; + mask_fn mask; + w_mask_fn w_mask[3 /* 444, 422, 420 */]; + blend_fn blend; + blend_dir_fn blend_v; + blend_dir_fn blend_h; + warp8x8_fn warp8x8; + warp8x8t_fn warp8x8t; + emu_edge_fn emu_edge; + resize_fn resize; +} Dav1dMCDSPContext; + +bitfn_decls(void dav1d_mc_dsp_init, Dav1dMCDSPContext *c); + +#endif /* DAV1D_SRC_MC_H */ diff --git a/3rdparty/dav1d/src/mc_tmpl.c b/3rdparty/dav1d/src/mc_tmpl.c new file mode 100644 index 0000000..b936d94 --- /dev/null +++ b/3rdparty/dav1d/src/mc_tmpl.c @@ -0,0 +1,1006 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include + +#include "common/attributes.h" +#include "common/intops.h" + +#include "src/mc.h" +#include "src/tables.h" + +#if BITDEPTH == 8 +#define get_intermediate_bits(bitdepth_max) 4 +// Output in interval [-5132, 9212], fits in int16_t as is +#define PREP_BIAS 0 +#else +// 4 for 10 bits/component, 2 for 12 bits/component +#define get_intermediate_bits(bitdepth_max) (14 - bitdepth_from_max(bitdepth_max)) +// Output in interval [-20588, 36956] (10-bit), [-20602, 36983] (12-bit) +// Subtract a bias to ensure the output fits in int16_t +#define PREP_BIAS 8192 +#endif + +static NOINLINE void +put_c(pixel *dst, const ptrdiff_t dst_stride, + const pixel *src, const ptrdiff_t src_stride, const int w, int h) +{ + do { + pixel_copy(dst, src, w); + + dst += dst_stride; + src += src_stride; + } while (--h); +} + +static NOINLINE void +prep_c(int16_t *tmp, const pixel *src, const ptrdiff_t src_stride, + const int w, int h HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + do { + for (int x = 0; x < w; x++) + tmp[x] = (src[x] << intermediate_bits) - PREP_BIAS; + + tmp += w; + src += src_stride; + } while (--h); +} + +#define FILTER_8TAP(src, x, F, stride) \ + (F[0] * src[x + -3 * stride] + \ + F[1] * src[x + -2 * stride] + \ + F[2] * src[x + -1 * stride] + \ + F[3] * src[x + +0 * stride] + \ + F[4] * src[x + +1 * stride] + \ + F[5] * src[x + +2 * stride] + \ + F[6] * src[x + +3 * stride] + \ + F[7] * src[x + +4 * stride]) + +#define FILTER_8TAP2(src, x, F) \ + (F[0] * src[0][x] + \ + F[1] * src[1][x] + \ + F[2] * src[2][x] + \ + F[3] * src[3][x] + \ + F[4] * src[4][x] + \ + F[5] * src[5][x] + \ + F[6] * src[6][x] + \ + F[7] * src[7][x]) + +#define DAV1D_FILTER_8TAP_RND(src, x, F, stride, sh) \ + ((FILTER_8TAP(src, x, F, stride) + ((1 << (sh)) >> 1)) >> (sh)) + +#define DAV1D_FILTER_8TAP_RND2(src, x, F, stride, rnd, sh) \ + ((FILTER_8TAP(src, x, F, stride) + (rnd)) >> (sh)) + +#define DAV1D_FILTER_8TAP_RND3(src, x, F, sh) \ + ((FILTER_8TAP2(src, x, F) + ((1 << (sh)) >> 1)) >> (sh)) + +#define DAV1D_FILTER_8TAP_CLIP(src, x, F, stride, sh) \ + iclip_pixel(DAV1D_FILTER_8TAP_RND(src, x, F, stride, sh)) + +#define DAV1D_FILTER_8TAP_CLIP2(src, x, F, stride, rnd, sh) \ + iclip_pixel(DAV1D_FILTER_8TAP_RND2(src, x, F, stride, rnd, sh)) + +#define DAV1D_FILTER_8TAP_CLIP3(src, x, F, sh) \ + iclip_pixel(DAV1D_FILTER_8TAP_RND3(src, x, F, sh)) + +#define GET_H_FILTER(mx) \ + const int8_t *const fh = !(mx) ? NULL : w > 4 ? \ + dav1d_mc_subpel_filters[filter_type & 3][(mx) - 1] : \ + dav1d_mc_subpel_filters[3 + (filter_type & 1)][(mx) - 1] + +#define GET_V_FILTER(my) \ + const int8_t *const fv = !(my) ? NULL : h > 4 ? \ + dav1d_mc_subpel_filters[filter_type >> 2][(my) - 1] : \ + dav1d_mc_subpel_filters[3 + ((filter_type >> 2) & 1)][(my) - 1] + +#define GET_FILTERS() \ + GET_H_FILTER(mx); \ + GET_V_FILTER(my) + +static NOINLINE void +put_8tap_c(pixel *dst, ptrdiff_t dst_stride, + const pixel *src, ptrdiff_t src_stride, + const int w, int h, const int mx, const int my, + const int filter_type HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + const int intermediate_rnd = 32 + ((1 << (6 - intermediate_bits)) >> 1); + + GET_FILTERS(); + dst_stride = PXSTRIDE(dst_stride); + src_stride = PXSTRIDE(src_stride); + + if (fh) { + if (fv) { + int tmp_h = h + 7; + int16_t mid[128 * 135], *mid_ptr = mid; + + src -= src_stride * 3; + do { + for (int x = 0; x < w; x++) + mid_ptr[x] = DAV1D_FILTER_8TAP_RND(src, x, fh, 1, + 6 - intermediate_bits); + + mid_ptr += 128; + src += src_stride; + } while (--tmp_h); + + mid_ptr = mid + 128 * 3; + do { + for (int x = 0; x < w; x++) + dst[x] = DAV1D_FILTER_8TAP_CLIP(mid_ptr, x, fv, 128, + 6 + intermediate_bits); + + mid_ptr += 128; + dst += dst_stride; + } while (--h); + } else { + do { + for (int x = 0; x < w; x++) { + dst[x] = DAV1D_FILTER_8TAP_CLIP2(src, x, fh, 1, + intermediate_rnd, 6); + } + + dst += dst_stride; + src += src_stride; + } while (--h); + } + } else if (fv) { + do { + for (int x = 0; x < w; x++) + dst[x] = DAV1D_FILTER_8TAP_CLIP(src, x, fv, src_stride, 6); + + dst += dst_stride; + src += src_stride; + } while (--h); + } else + put_c(dst, dst_stride, src, src_stride, w, h); +} + +static NOINLINE void +put_8tap_scaled_c(pixel *dst, const ptrdiff_t dst_stride, + const pixel *src, ptrdiff_t src_stride, + const int w, int h, const int mx, int my, + const int dx, const int dy, const int filter_type + HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + const int intermediate_rnd = (1 << intermediate_bits) >> 1; + int16_t mid[128 * 8]; + int16_t *mid_ptrs[8]; + int in_y = -8; + src_stride = PXSTRIDE(src_stride); + + for (int i = 0; i < 8; i++) + mid_ptrs[i] = &mid[128 * i]; + + src -= src_stride * 3; + + for (int y = 0; y < h; y++) { + int x; + int src_y = my >> 10; + GET_V_FILTER((my & 0x3ff) >> 6); + + while (in_y < src_y) { + int imx = mx, ioff = 0; + int16_t *mid_ptr = mid_ptrs[0]; + + for (int i = 0; i < 7; i++) + mid_ptrs[i] = mid_ptrs[i + 1]; + mid_ptrs[7] = mid_ptr; + + for (x = 0; x < w; x++) { + GET_H_FILTER(imx >> 6); + mid_ptr[x] = fh ? DAV1D_FILTER_8TAP_RND(src, ioff, fh, 1, + 6 - intermediate_bits) : + src[ioff] << intermediate_bits; + imx += dx; + ioff += imx >> 10; + imx &= 0x3ff; + } + + src += src_stride; + in_y++; + } + + for (x = 0; x < w; x++) + dst[x] = fv ? DAV1D_FILTER_8TAP_CLIP3(mid_ptrs, x, fv, + 6 + intermediate_bits) : + iclip_pixel((mid_ptrs[3][x] + intermediate_rnd) >> + intermediate_bits); + + my += dy; + dst += PXSTRIDE(dst_stride); + } +} + +static NOINLINE void +prep_8tap_c(int16_t *tmp, const pixel *src, ptrdiff_t src_stride, + const int w, int h, const int mx, const int my, + const int filter_type HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + GET_FILTERS(); + src_stride = PXSTRIDE(src_stride); + + if (fh) { + if (fv) { + int tmp_h = h + 7; + int16_t mid[128 * 135], *mid_ptr = mid; + + src -= src_stride * 3; + do { + for (int x = 0; x < w; x++) + mid_ptr[x] = DAV1D_FILTER_8TAP_RND(src, x, fh, 1, + 6 - intermediate_bits); + + mid_ptr += 128; + src += src_stride; + } while (--tmp_h); + + mid_ptr = mid + 128 * 3; + do { + for (int x = 0; x < w; x++) { + int t = DAV1D_FILTER_8TAP_RND(mid_ptr, x, fv, 128, 6) - + PREP_BIAS; + assert(t >= INT16_MIN && t <= INT16_MAX); + tmp[x] = t; + } + + mid_ptr += 128; + tmp += w; + } while (--h); + } else { + do { + for (int x = 0; x < w; x++) + tmp[x] = DAV1D_FILTER_8TAP_RND(src, x, fh, 1, + 6 - intermediate_bits) - + PREP_BIAS; + + tmp += w; + src += src_stride; + } while (--h); + } + } else if (fv) { + do { + for (int x = 0; x < w; x++) + tmp[x] = DAV1D_FILTER_8TAP_RND(src, x, fv, src_stride, + 6 - intermediate_bits) - + PREP_BIAS; + + tmp += w; + src += src_stride; + } while (--h); + } else + prep_c(tmp, src, src_stride, w, h HIGHBD_TAIL_SUFFIX); +} + +static NOINLINE void +prep_8tap_scaled_c(int16_t *tmp, const pixel *src, ptrdiff_t src_stride, + const int w, int h, const int mx, int my, + const int dx, const int dy, const int filter_type + HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + int16_t mid[128 * 8]; + int16_t *mid_ptrs[8]; + int in_y = -8; + src_stride = PXSTRIDE(src_stride); + + for (int i = 0; i < 8; i++) + mid_ptrs[i] = &mid[128 * i]; + + src -= src_stride * 3; + + for (int y = 0; y < h; y++) { + int x; + int src_y = my >> 10; + GET_V_FILTER((my & 0x3ff) >> 6); + + while (in_y < src_y) { + int imx = mx, ioff = 0; + int16_t *mid_ptr = mid_ptrs[0]; + + for (int i = 0; i < 7; i++) + mid_ptrs[i] = mid_ptrs[i + 1]; + mid_ptrs[7] = mid_ptr; + + for (x = 0; x < w; x++) { + GET_H_FILTER(imx >> 6); + mid_ptr[x] = fh ? DAV1D_FILTER_8TAP_RND(src, ioff, fh, 1, + 6 - intermediate_bits) : + src[ioff] << intermediate_bits; + imx += dx; + ioff += imx >> 10; + imx &= 0x3ff; + } + + src += src_stride; + in_y++; + } + + for (x = 0; x < w; x++) + tmp[x] = (fv ? DAV1D_FILTER_8TAP_RND3(mid_ptrs, x, fv, 6) + : mid_ptrs[3][x]) - PREP_BIAS; + + my += dy; + tmp += w; + } +} + +#define filter_fns(type, type_h, type_v) \ +static void put_8tap_##type##_c(pixel *const dst, \ + const ptrdiff_t dst_stride, \ + const pixel *const src, \ + const ptrdiff_t src_stride, \ + const int w, const int h, \ + const int mx, const int my \ + HIGHBD_DECL_SUFFIX) \ +{ \ + put_8tap_c(dst, dst_stride, src, src_stride, w, h, mx, my, \ + type_h | (type_v << 2) HIGHBD_TAIL_SUFFIX); \ +} \ +static void put_8tap_##type##_scaled_c(pixel *const dst, \ + const ptrdiff_t dst_stride, \ + const pixel *const src, \ + const ptrdiff_t src_stride, \ + const int w, const int h, \ + const int mx, const int my, \ + const int dx, const int dy \ + HIGHBD_DECL_SUFFIX) \ +{ \ + put_8tap_scaled_c(dst, dst_stride, src, src_stride, w, h, mx, my, dx, dy, \ + type_h | (type_v << 2) HIGHBD_TAIL_SUFFIX); \ +} \ +static void prep_8tap_##type##_c(int16_t *const tmp, \ + const pixel *const src, \ + const ptrdiff_t src_stride, \ + const int w, const int h, \ + const int mx, const int my \ + HIGHBD_DECL_SUFFIX) \ +{ \ + prep_8tap_c(tmp, src, src_stride, w, h, mx, my, \ + type_h | (type_v << 2) HIGHBD_TAIL_SUFFIX); \ +} \ +static void prep_8tap_##type##_scaled_c(int16_t *const tmp, \ + const pixel *const src, \ + const ptrdiff_t src_stride, \ + const int w, const int h, \ + const int mx, const int my, \ + const int dx, const int dy \ + HIGHBD_DECL_SUFFIX) \ +{ \ + prep_8tap_scaled_c(tmp, src, src_stride, w, h, mx, my, dx, dy, \ + type_h | (type_v << 2) HIGHBD_TAIL_SUFFIX); \ +} + +filter_fns(regular, DAV1D_FILTER_8TAP_REGULAR, DAV1D_FILTER_8TAP_REGULAR) +filter_fns(regular_sharp, DAV1D_FILTER_8TAP_REGULAR, DAV1D_FILTER_8TAP_SHARP) +filter_fns(regular_smooth, DAV1D_FILTER_8TAP_REGULAR, DAV1D_FILTER_8TAP_SMOOTH) +filter_fns(smooth, DAV1D_FILTER_8TAP_SMOOTH, DAV1D_FILTER_8TAP_SMOOTH) +filter_fns(smooth_regular, DAV1D_FILTER_8TAP_SMOOTH, DAV1D_FILTER_8TAP_REGULAR) +filter_fns(smooth_sharp, DAV1D_FILTER_8TAP_SMOOTH, DAV1D_FILTER_8TAP_SHARP) +filter_fns(sharp, DAV1D_FILTER_8TAP_SHARP, DAV1D_FILTER_8TAP_SHARP) +filter_fns(sharp_regular, DAV1D_FILTER_8TAP_SHARP, DAV1D_FILTER_8TAP_REGULAR) +filter_fns(sharp_smooth, DAV1D_FILTER_8TAP_SHARP, DAV1D_FILTER_8TAP_SMOOTH) + +#define FILTER_BILIN(src, x, mxy, stride) \ + (16 * src[x] + ((mxy) * (src[x + stride] - src[x]))) + +#define FILTER_BILIN_RND(src, x, mxy, stride, sh) \ + ((FILTER_BILIN(src, x, mxy, stride) + ((1 << (sh)) >> 1)) >> (sh)) + +#define FILTER_BILIN_CLIP(src, x, mxy, stride, sh) \ + iclip_pixel(FILTER_BILIN_RND(src, x, mxy, stride, sh)) + +#define FILTER_BILIN2(src1, src2, x, mxy) \ + (16 * src1[x] + ((mxy) * (src2[x] - src1[x]))) + +#define FILTER_BILIN_RND2(src1, src2, x, mxy, sh) \ + ((FILTER_BILIN2(src1, src2, x, mxy) + ((1 << (sh)) >> 1)) >> (sh)) + +#define FILTER_BILIN_CLIP2(src1, src2, x, mxy, sh) \ + iclip_pixel(FILTER_BILIN_RND2(src1, src2, x, mxy, sh)) + +static void put_bilin_c(pixel *dst, ptrdiff_t dst_stride, + const pixel *src, ptrdiff_t src_stride, + const int w, int h, const int mx, const int my + HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + const int intermediate_rnd = (1 << intermediate_bits) >> 1; + dst_stride = PXSTRIDE(dst_stride); + src_stride = PXSTRIDE(src_stride); + + if (mx) { + if (my) { + int16_t mid[128 * 129], *mid_ptr = mid; + int tmp_h = h + 1; + + do { + for (int x = 0; x < w; x++) + mid_ptr[x] = FILTER_BILIN_RND(src, x, mx, 1, + 4 - intermediate_bits); + + mid_ptr += 128; + src += src_stride; + } while (--tmp_h); + + mid_ptr = mid; + do { + for (int x = 0; x < w; x++) + dst[x] = FILTER_BILIN_CLIP(mid_ptr, x, my, 128, + 4 + intermediate_bits); + + mid_ptr += 128; + dst += dst_stride; + } while (--h); + } else { + do { + for (int x = 0; x < w; x++) { + const int px = FILTER_BILIN_RND(src, x, mx, 1, + 4 - intermediate_bits); + dst[x] = iclip_pixel((px + intermediate_rnd) >> intermediate_bits); + } + + dst += dst_stride; + src += src_stride; + } while (--h); + } + } else if (my) { + do { + for (int x = 0; x < w; x++) + dst[x] = FILTER_BILIN_CLIP(src, x, my, src_stride, 4); + + dst += dst_stride; + src += src_stride; + } while (--h); + } else + put_c(dst, dst_stride, src, src_stride, w, h); +} + +static void put_bilin_scaled_c(pixel *dst, ptrdiff_t dst_stride, + const pixel *src, ptrdiff_t src_stride, + const int w, int h, const int mx, int my, + const int dx, const int dy + HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + int16_t mid[128 * 2]; + int in_y = -2; + + do { + int x; + int y = my >> 10; + int16_t *mid1 = &mid[(y & 1) * 128]; + int16_t *mid2 = &mid[((y + 1) & 1) * 128]; + int dmy = my & 0x3ff; + + while (in_y < y) { + int imx = mx, ioff = 0; + int16_t *mid_ptr = &mid[(in_y & 1) * 128]; + + for (x = 0; x < w; x++) { + mid_ptr[x] = FILTER_BILIN_RND(src, ioff, imx >> 6, 1, + 4 - intermediate_bits); + imx += dx; + ioff += imx >> 10; + imx &= 0x3ff; + } + + src += PXSTRIDE(src_stride); + in_y++; + } + + for (x = 0; x < w; x++) + dst[x] = FILTER_BILIN_CLIP2(mid1, mid2, x, dmy >> 6, + 4 + intermediate_bits); + + my += dy; + dst += PXSTRIDE(dst_stride); + } while (--h); +} + +static void prep_bilin_c(int16_t *tmp, + const pixel *src, ptrdiff_t src_stride, + const int w, int h, const int mx, const int my + HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + src_stride = PXSTRIDE(src_stride); + + if (mx) { + if (my) { + int16_t mid[128 * 129], *mid_ptr = mid; + int tmp_h = h + 1; + + do { + for (int x = 0; x < w; x++) + mid_ptr[x] = FILTER_BILIN_RND(src, x, mx, 1, + 4 - intermediate_bits); + + mid_ptr += 128; + src += src_stride; + } while (--tmp_h); + + mid_ptr = mid; + do { + for (int x = 0; x < w; x++) + tmp[x] = FILTER_BILIN_RND(mid_ptr, x, my, 128, 4) - + PREP_BIAS; + + mid_ptr += 128; + tmp += w; + } while (--h); + } else { + do { + for (int x = 0; x < w; x++) + tmp[x] = FILTER_BILIN_RND(src, x, mx, 1, + 4 - intermediate_bits) - + PREP_BIAS; + + tmp += w; + src += src_stride; + } while (--h); + } + } else if (my) { + do { + for (int x = 0; x < w; x++) + tmp[x] = FILTER_BILIN_RND(src, x, my, src_stride, + 4 - intermediate_bits) - PREP_BIAS; + + tmp += w; + src += src_stride; + } while (--h); + } else + prep_c(tmp, src, src_stride, w, h HIGHBD_TAIL_SUFFIX); +} + +static void prep_bilin_scaled_c(int16_t *tmp, + const pixel *src, ptrdiff_t src_stride, + const int w, int h, const int mx, int my, + const int dx, const int dy HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + int16_t mid[128 * 2]; + int in_y = -2; + + do { + int x; + int y = my >> 10; + int16_t *mid1 = &mid[(y & 1) * 128]; + int16_t *mid2 = &mid[((y + 1) & 1) * 128]; + int dmy = my & 0x3ff; + + while (in_y < y) { + int imx = mx, ioff = 0; + int16_t *mid_ptr = &mid[(in_y & 1) * 128]; + + for (x = 0; x < w; x++) { + mid_ptr[x] = FILTER_BILIN_RND(src, ioff, imx >> 6, 1, + 4 - intermediate_bits); + imx += dx; + ioff += imx >> 10; + imx &= 0x3ff; + } + + src += PXSTRIDE(src_stride); + in_y++; + } + + for (x = 0; x < w; x++) + tmp[x] = FILTER_BILIN_RND2(mid1, mid2, x, dmy >> 6, 4) - PREP_BIAS; + + my += dy; + tmp += w; + } while (--h); +} + +static void avg_c(pixel *dst, const ptrdiff_t dst_stride, + const int16_t *tmp1, const int16_t *tmp2, const int w, int h + HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + const int sh = intermediate_bits + 1; + const int rnd = (1 << intermediate_bits) + PREP_BIAS * 2; + do { + for (int x = 0; x < w; x++) + dst[x] = iclip_pixel((tmp1[x] + tmp2[x] + rnd) >> sh); + + tmp1 += w; + tmp2 += w; + dst += PXSTRIDE(dst_stride); + } while (--h); +} + +static void w_avg_c(pixel *dst, const ptrdiff_t dst_stride, + const int16_t *tmp1, const int16_t *tmp2, const int w, int h, + const int weight HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + const int sh = intermediate_bits + 4; + const int rnd = (8 << intermediate_bits) + PREP_BIAS * 16; + do { + for (int x = 0; x < w; x++) + dst[x] = iclip_pixel((tmp1[x] * weight + + tmp2[x] * (16 - weight) + rnd) >> sh); + + tmp1 += w; + tmp2 += w; + dst += PXSTRIDE(dst_stride); + } while (--h); +} + +static void mask_c(pixel *dst, const ptrdiff_t dst_stride, + const int16_t *tmp1, const int16_t *tmp2, const int w, int h, + const uint8_t *mask HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + const int sh = intermediate_bits + 6; + const int rnd = (32 << intermediate_bits) + PREP_BIAS * 64; + do { + for (int x = 0; x < w; x++) + dst[x] = iclip_pixel((tmp1[x] * mask[x] + + tmp2[x] * (64 - mask[x]) + rnd) >> sh); + + tmp1 += w; + tmp2 += w; + mask += w; + dst += PXSTRIDE(dst_stride); + } while (--h); +} + +#define blend_px(a, b, m) (((a * (64 - m) + b * m) + 32) >> 6) +static void blend_c(pixel *dst, const ptrdiff_t dst_stride, const pixel *tmp, + const int w, int h, const uint8_t *mask) +{ + do { + for (int x = 0; x < w; x++) { + dst[x] = blend_px(dst[x], tmp[x], mask[x]); + } + dst += PXSTRIDE(dst_stride); + tmp += w; + mask += w; + } while (--h); +} + +static void blend_v_c(pixel *dst, const ptrdiff_t dst_stride, const pixel *tmp, + const int w, int h) +{ + const uint8_t *const mask = &dav1d_obmc_masks[w]; + do { + for (int x = 0; x < (w * 3) >> 2; x++) { + dst[x] = blend_px(dst[x], tmp[x], mask[x]); + } + dst += PXSTRIDE(dst_stride); + tmp += w; + } while (--h); +} + +static void blend_h_c(pixel *dst, const ptrdiff_t dst_stride, const pixel *tmp, + const int w, int h) +{ + const uint8_t *mask = &dav1d_obmc_masks[h]; + h = (h * 3) >> 2; + do { + const int m = *mask++; + for (int x = 0; x < w; x++) { + dst[x] = blend_px(dst[x], tmp[x], m); + } + dst += PXSTRIDE(dst_stride); + tmp += w; + } while (--h); +} + +static void w_mask_c(pixel *dst, const ptrdiff_t dst_stride, + const int16_t *tmp1, const int16_t *tmp2, const int w, int h, + uint8_t *mask, const int sign, + const int ss_hor, const int ss_ver HIGHBD_DECL_SUFFIX) +{ + // store mask at 2x2 resolution, i.e. store 2x1 sum for even rows, + // and then load this intermediate to calculate final value for odd rows + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + const int bitdepth = bitdepth_from_max(bitdepth_max); + const int sh = intermediate_bits + 6; + const int rnd = (32 << intermediate_bits) + PREP_BIAS * 64; + const int mask_sh = bitdepth + intermediate_bits - 4; + const int mask_rnd = 1 << (mask_sh - 5); + do { + for (int x = 0; x < w; x++) { + const int tmpdiff = tmp1[x] - tmp2[x]; + const int m = imin(38 + ((abs(tmpdiff) + mask_rnd) >> mask_sh), 64); + dst[x] = iclip_pixel((tmpdiff * m + tmp2[x] * 64 + rnd) >> sh); + + if (ss_hor) { + x++; + + const int tmpdiff = tmp1[x] - tmp2[x]; + const int n = imin(38 + ((abs(tmpdiff) + mask_rnd) >> mask_sh), 64); + dst[x] = iclip_pixel((tmpdiff * n + tmp2[x] * 64 + rnd) >> sh); + + if (h & ss_ver) { + mask[x >> 1] = (m + n + mask[x >> 1] + 2 - sign) >> 2; + } else if (ss_ver) { + mask[x >> 1] = m + n; + } else { + mask[x >> 1] = (m + n + 1 - sign) >> 1; + } + } else { + mask[x] = m; + } + } + + tmp1 += w; + tmp2 += w; + dst += PXSTRIDE(dst_stride); + if (!ss_ver || (h & 1)) mask += w >> ss_hor; + } while (--h); +} + +#define w_mask_fns(ssn, ss_hor, ss_ver) \ +static void w_mask_##ssn##_c(pixel *const dst, const ptrdiff_t dst_stride, \ + const int16_t *const tmp1, const int16_t *const tmp2, \ + const int w, const int h, uint8_t *mask, \ + const int sign HIGHBD_DECL_SUFFIX) \ +{ \ + w_mask_c(dst, dst_stride, tmp1, tmp2, w, h, mask, sign, ss_hor, ss_ver \ + HIGHBD_TAIL_SUFFIX); \ +} + +w_mask_fns(444, 0, 0); +w_mask_fns(422, 1, 0); +w_mask_fns(420, 1, 1); + +#undef w_mask_fns + +#define FILTER_WARP_RND(src, x, F, stride, sh) \ + ((F[0] * src[x - 3 * stride] + \ + F[1] * src[x - 2 * stride] + \ + F[2] * src[x - 1 * stride] + \ + F[3] * src[x + 0 * stride] + \ + F[4] * src[x + 1 * stride] + \ + F[5] * src[x + 2 * stride] + \ + F[6] * src[x + 3 * stride] + \ + F[7] * src[x + 4 * stride] + \ + ((1 << (sh)) >> 1)) >> (sh)) + +#define FILTER_WARP_CLIP(src, x, F, stride, sh) \ + iclip_pixel(FILTER_WARP_RND(src, x, F, stride, sh)) + +static void warp_affine_8x8_c(pixel *dst, const ptrdiff_t dst_stride, + const pixel *src, const ptrdiff_t src_stride, + const int16_t *const abcd, int mx, int my + HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + int16_t mid[15 * 8], *mid_ptr = mid; + + src -= 3 * PXSTRIDE(src_stride); + for (int y = 0; y < 15; y++, mx += abcd[1]) { + for (int x = 0, tmx = mx; x < 8; x++, tmx += abcd[0]) { + const int8_t *const filter = + dav1d_mc_warp_filter[64 + ((tmx + 512) >> 10)]; + + mid_ptr[x] = FILTER_WARP_RND(src, x, filter, 1, + 7 - intermediate_bits); + } + src += PXSTRIDE(src_stride); + mid_ptr += 8; + } + + mid_ptr = &mid[3 * 8]; + for (int y = 0; y < 8; y++, my += abcd[3]) { + for (int x = 0, tmy = my; x < 8; x++, tmy += abcd[2]) { + const int8_t *const filter = + dav1d_mc_warp_filter[64 + ((tmy + 512) >> 10)]; + + dst[x] = FILTER_WARP_CLIP(mid_ptr, x, filter, 8, + 7 + intermediate_bits); + } + mid_ptr += 8; + dst += PXSTRIDE(dst_stride); + } +} + +static void warp_affine_8x8t_c(int16_t *tmp, const ptrdiff_t tmp_stride, + const pixel *src, const ptrdiff_t src_stride, + const int16_t *const abcd, int mx, int my + HIGHBD_DECL_SUFFIX) +{ + const int intermediate_bits = get_intermediate_bits(bitdepth_max); + int16_t mid[15 * 8], *mid_ptr = mid; + + src -= 3 * PXSTRIDE(src_stride); + for (int y = 0; y < 15; y++, mx += abcd[1]) { + for (int x = 0, tmx = mx; x < 8; x++, tmx += abcd[0]) { + const int8_t *const filter = + dav1d_mc_warp_filter[64 + ((tmx + 512) >> 10)]; + + mid_ptr[x] = FILTER_WARP_RND(src, x, filter, 1, + 7 - intermediate_bits); + } + src += PXSTRIDE(src_stride); + mid_ptr += 8; + } + + mid_ptr = &mid[3 * 8]; + for (int y = 0; y < 8; y++, my += abcd[3]) { + for (int x = 0, tmy = my; x < 8; x++, tmy += abcd[2]) { + const int8_t *const filter = + dav1d_mc_warp_filter[64 + ((tmy + 512) >> 10)]; + + tmp[x] = FILTER_WARP_RND(mid_ptr, x, filter, 8, 7) - PREP_BIAS; + } + mid_ptr += 8; + tmp += tmp_stride; + } +} + +static void emu_edge_c(const intptr_t bw, const intptr_t bh, + const intptr_t iw, const intptr_t ih, + const intptr_t x, const intptr_t y, + pixel *dst, const ptrdiff_t dst_stride, + const pixel *ref, const ptrdiff_t ref_stride) +{ + // find offset in reference of visible block to copy + ref += iclip((int) y, 0, (int) ih - 1) * PXSTRIDE(ref_stride) + + iclip((int) x, 0, (int) iw - 1); + + // number of pixels to extend (left, right, top, bottom) + const int left_ext = iclip((int) -x, 0, (int) bw - 1); + const int right_ext = iclip((int) (x + bw - iw), 0, (int) bw - 1); + assert(left_ext + right_ext < bw); + const int top_ext = iclip((int) -y, 0, (int) bh - 1); + const int bottom_ext = iclip((int) (y + bh - ih), 0, (int) bh - 1); + assert(top_ext + bottom_ext < bh); + + // copy visible portion first + pixel *blk = dst + top_ext * PXSTRIDE(dst_stride); + const int center_w = (int) (bw - left_ext - right_ext); + const int center_h = (int) (bh - top_ext - bottom_ext); + for (int y = 0; y < center_h; y++) { + pixel_copy(blk + left_ext, ref, center_w); + // extend left edge for this line + if (left_ext) + pixel_set(blk, blk[left_ext], left_ext); + // extend right edge for this line + if (right_ext) + pixel_set(blk + left_ext + center_w, blk[left_ext + center_w - 1], + right_ext); + ref += PXSTRIDE(ref_stride); + blk += PXSTRIDE(dst_stride); + } + + // copy top + blk = dst + top_ext * PXSTRIDE(dst_stride); + for (int y = 0; y < top_ext; y++) { + pixel_copy(dst, blk, bw); + dst += PXSTRIDE(dst_stride); + } + + // copy bottom + dst += center_h * PXSTRIDE(dst_stride); + for (int y = 0; y < bottom_ext; y++) { + pixel_copy(dst, &dst[-PXSTRIDE(dst_stride)], bw); + dst += PXSTRIDE(dst_stride); + } +} + +static void resize_c(pixel *dst, const ptrdiff_t dst_stride, + const pixel *src, const ptrdiff_t src_stride, + const int dst_w, int h, const int src_w, + const int dx, const int mx0 HIGHBD_DECL_SUFFIX) +{ + do { + int mx = mx0, src_x = -1; + for (int x = 0; x < dst_w; x++) { + const int8_t *const F = dav1d_resize_filter[mx >> 8]; + dst[x] = iclip_pixel((-(F[0] * src[iclip(src_x - 3, 0, src_w - 1)] + + F[1] * src[iclip(src_x - 2, 0, src_w - 1)] + + F[2] * src[iclip(src_x - 1, 0, src_w - 1)] + + F[3] * src[iclip(src_x + 0, 0, src_w - 1)] + + F[4] * src[iclip(src_x + 1, 0, src_w - 1)] + + F[5] * src[iclip(src_x + 2, 0, src_w - 1)] + + F[6] * src[iclip(src_x + 3, 0, src_w - 1)] + + F[7] * src[iclip(src_x + 4, 0, src_w - 1)]) + + 64) >> 7); + mx += dx; + src_x += mx >> 14; + mx &= 0x3fff; + } + + dst += PXSTRIDE(dst_stride); + src += PXSTRIDE(src_stride); + } while (--h); +} + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM +#include "src/arm/mc.h" +#elif ARCH_LOONGARCH64 +#include "src/loongarch/mc.h" +#elif ARCH_PPC64LE +#include "src/ppc/mc.h" +#elif ARCH_RISCV +#include "src/riscv/mc.h" +#elif ARCH_X86 +#include "src/x86/mc.h" +#endif +#endif + +COLD void bitfn(dav1d_mc_dsp_init)(Dav1dMCDSPContext *const c) { +#define init_mc_fns(type, name) do { \ + c->mc [type] = put_##name##_c; \ + c->mc_scaled [type] = put_##name##_scaled_c; \ + c->mct [type] = prep_##name##_c; \ + c->mct_scaled[type] = prep_##name##_scaled_c; \ +} while (0) + + init_mc_fns(FILTER_2D_8TAP_REGULAR, 8tap_regular); + init_mc_fns(FILTER_2D_8TAP_REGULAR_SMOOTH, 8tap_regular_smooth); + init_mc_fns(FILTER_2D_8TAP_REGULAR_SHARP, 8tap_regular_sharp); + init_mc_fns(FILTER_2D_8TAP_SHARP_REGULAR, 8tap_sharp_regular); + init_mc_fns(FILTER_2D_8TAP_SHARP_SMOOTH, 8tap_sharp_smooth); + init_mc_fns(FILTER_2D_8TAP_SHARP, 8tap_sharp); + init_mc_fns(FILTER_2D_8TAP_SMOOTH_REGULAR, 8tap_smooth_regular); + init_mc_fns(FILTER_2D_8TAP_SMOOTH, 8tap_smooth); + init_mc_fns(FILTER_2D_8TAP_SMOOTH_SHARP, 8tap_smooth_sharp); + init_mc_fns(FILTER_2D_BILINEAR, bilin); + + c->avg = avg_c; + c->w_avg = w_avg_c; + c->mask = mask_c; + c->blend = blend_c; + c->blend_v = blend_v_c; + c->blend_h = blend_h_c; + c->w_mask[0] = w_mask_444_c; + c->w_mask[1] = w_mask_422_c; + c->w_mask[2] = w_mask_420_c; + c->warp8x8 = warp_affine_8x8_c; + c->warp8x8t = warp_affine_8x8t_c; + c->emu_edge = emu_edge_c; + c->resize = resize_c; + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM + mc_dsp_init_arm(c); +#elif ARCH_LOONGARCH64 + mc_dsp_init_loongarch(c); +#elif ARCH_PPC64LE + mc_dsp_init_ppc(c); +#elif ARCH_RISCV + mc_dsp_init_riscv(c); +#elif ARCH_X86 + mc_dsp_init_x86(c); +#endif +#endif +} diff --git a/3rdparty/dav1d/src/mem.c b/3rdparty/dav1d/src/mem.c new file mode 100644 index 0000000..0b6b662 --- /dev/null +++ b/3rdparty/dav1d/src/mem.c @@ -0,0 +1,311 @@ +/* + * Copyright © 2020, VideoLAN and dav1d authors + * Copyright © 2020, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "src/internal.h" + +#if TRACK_HEAP_ALLOCATIONS +#include + +#include "src/log.h" + +#define DEFAULT_ALIGN 16 + +typedef struct { + size_t sz; + unsigned align; + enum AllocationType type; +} Dav1dAllocationData; + +typedef struct { + size_t curr_sz; + size_t peak_sz; + unsigned num_allocs; + unsigned num_reuses; +} AllocStats; + +static AllocStats tracked_allocs[N_ALLOC_TYPES]; +static size_t curr_total_sz; +static size_t peak_total_sz; +static pthread_mutex_t track_alloc_mutex = PTHREAD_MUTEX_INITIALIZER; + +static void *track_alloc(const enum AllocationType type, char *ptr, + const size_t sz, const size_t align) +{ + assert(align >= sizeof(Dav1dAllocationData)); + if (ptr) { + ptr += align; + Dav1dAllocationData *const d = &((Dav1dAllocationData*)ptr)[-1]; + AllocStats *const s = &tracked_allocs[type]; + + d->sz = sz; + d->align = (unsigned)align; + d->type = type; + + pthread_mutex_lock(&track_alloc_mutex); + s->num_allocs++; + s->curr_sz += sz; + if (s->curr_sz > s->peak_sz) + s->peak_sz = s->curr_sz; + + curr_total_sz += sz; + if (curr_total_sz > peak_total_sz) + peak_total_sz = curr_total_sz; + pthread_mutex_unlock(&track_alloc_mutex); + } + return ptr; +} + +static void *track_free(char *const ptr) { + const Dav1dAllocationData *const d = &((Dav1dAllocationData*)ptr)[-1]; + const size_t sz = d->sz; + + pthread_mutex_lock(&track_alloc_mutex); + tracked_allocs[d->type].curr_sz -= sz; + curr_total_sz -= sz; + pthread_mutex_unlock(&track_alloc_mutex); + + return ptr - d->align; +} + +static void dav1d_track_reuse(const enum AllocationType type) { + pthread_mutex_lock(&track_alloc_mutex); + tracked_allocs[type].num_reuses++; + pthread_mutex_unlock(&track_alloc_mutex); +} + +void *dav1d_malloc(const enum AllocationType type, const size_t sz) { + void *const ptr = malloc(sz + DEFAULT_ALIGN); + return track_alloc(type, ptr, sz, DEFAULT_ALIGN); +} + +void *dav1d_alloc_aligned(const enum AllocationType type, + const size_t sz, const size_t align) +{ + void *const ptr = dav1d_alloc_aligned_internal(sz + align, align); + return track_alloc(type, ptr, sz, align); +} + +void *dav1d_realloc(const enum AllocationType type, + void *ptr, const size_t sz) +{ + if (!ptr) + return dav1d_malloc(type, sz); + ptr = realloc((char*)ptr - DEFAULT_ALIGN, sz + DEFAULT_ALIGN); + if (ptr) + ptr = track_free((char*)ptr + DEFAULT_ALIGN); + return track_alloc(type, ptr, sz, DEFAULT_ALIGN); +} + +void dav1d_free(void *ptr) { + if (ptr) + free(track_free(ptr)); +} + +void dav1d_free_aligned(void *ptr) { + if (ptr) { + dav1d_free_aligned_internal(track_free(ptr)); + } +} + +static COLD int cmp_stats(const void *const a, const void *const b) { + const size_t a_sz = ((const AllocStats*)a)->peak_sz; + const size_t b_sz = ((const AllocStats*)b)->peak_sz; + return a_sz < b_sz ? -1 : a_sz > b_sz; +} + +/* Insert spaces as thousands separators for better readability */ +static COLD int format_tsep(char *const s, const size_t n, const size_t value) { + if (value < 1000) + return snprintf(s, n, "%u", (unsigned)value); + + const int len = format_tsep(s, n, value / 1000); + assert((size_t)len < n); + return len + snprintf(s + len, n - len, " %03u", (unsigned)(value % 1000)); +} + +COLD void dav1d_log_alloc_stats(Dav1dContext *const c) { + static const char *const type_names[N_ALLOC_TYPES] = { + [ALLOC_BLOCK ] = "Block data", + [ALLOC_CDEF ] = "CDEF line buffers", + [ALLOC_CDF ] = "CDF contexts", + [ALLOC_COEF ] = "Coefficient data", + [ALLOC_COMMON_CTX] = "Common context data", + [ALLOC_DAV1DDATA ] = "Dav1dData", + [ALLOC_IPRED ] = "Intra pred edges", + [ALLOC_LF ] = "Loopfilter data", + [ALLOC_LR ] = "Looprestoration data", + [ALLOC_OBU_HDR ] = "OBU headers", + [ALLOC_OBU_META ] = "OBU metadata", + [ALLOC_PAL ] = "Palette data", + [ALLOC_PIC ] = "Picture buffers", + [ALLOC_PIC_CTX ] = "Picture context data", + [ALLOC_REFMVS ] = "Reference mv data", + [ALLOC_SEGMAP ] = "Segmentation maps", + [ALLOC_THREAD_CTX] = "Thread context data", + [ALLOC_TILE ] = "Tile data", + }; + + struct { + AllocStats stats; + enum AllocationType type; + } data[N_ALLOC_TYPES]; + unsigned total_allocs = 0; + unsigned total_reuses = 0; + + pthread_mutex_lock(&track_alloc_mutex); + for (int i = 0; i < N_ALLOC_TYPES; i++) { + AllocStats *const s = &data[i].stats; + *s = tracked_allocs[i]; + data[i].type = i; + total_allocs += s->num_allocs; + total_reuses += s->num_reuses; + } + size_t total_sz = peak_total_sz; + pthread_mutex_unlock(&track_alloc_mutex); + + /* Sort types by memory usage */ + qsort(&data, N_ALLOC_TYPES, sizeof(*data), cmp_stats); + + const double inv_total_share = 100.0 / total_sz; + char total_sz_buf[32]; + const int sz_len = 4 + format_tsep(total_sz_buf, sizeof(total_sz_buf), total_sz); + + dav1d_log(c, "\n Type Allocs Reuses Share Peak size\n" + "---------------------------------------------------------------------\n"); + for (int i = N_ALLOC_TYPES - 1; i >= 0; i--) { + const AllocStats *const s = &data[i].stats; + if (s->num_allocs) { + const double share = s->peak_sz * inv_total_share; + char sz_buf[32]; + format_tsep(sz_buf, sizeof(sz_buf), s->peak_sz); + dav1d_log(c, " %-20s%10u%10u%8.1f%%%*s\n", type_names[data[i].type], + s->num_allocs, s->num_reuses, share, sz_len, sz_buf); + } + } + dav1d_log(c, "---------------------------------------------------------------------\n" + "%31u%10u %s\n", + total_allocs, total_reuses, total_sz_buf); +} +#endif /* TRACK_HEAP_ALLOCATIONS */ + +static COLD void mem_pool_destroy(Dav1dMemPool *const pool) { + pthread_mutex_destroy(&pool->lock); + dav1d_free(pool); +} + +void dav1d_mem_pool_push(Dav1dMemPool *const pool, void *const ptr) { + pthread_mutex_lock(&pool->lock); + Dav1dMemPoolBuffer *const buf = (Dav1dMemPoolBuffer*)((uintptr_t)ptr - 64); + const int ref_cnt = --pool->ref_cnt; + if (!pool->end) { + buf->next = pool->buf; + pool->buf = buf; + pthread_mutex_unlock(&pool->lock); + assert(ref_cnt > 0); + } else { + pthread_mutex_unlock(&pool->lock); + dav1d_free_aligned(buf); + if (!ref_cnt) mem_pool_destroy(pool); + } +} + +void *dav1d_mem_pool_pop(Dav1dMemPool *const pool, const size_t size) { + pthread_mutex_lock(&pool->lock); + Dav1dMemPoolBuffer *buf = pool->buf; + pool->ref_cnt++; + + if (buf) { + pool->buf = buf->next; + pthread_mutex_unlock(&pool->lock); + if (buf->size != size) { + /* Reallocate if the size has changed */ + dav1d_free_aligned(buf); + goto alloc; + } +#if TRACK_HEAP_ALLOCATIONS + dav1d_track_reuse(pool->type); +#endif + } else { + pthread_mutex_unlock(&pool->lock); +alloc: + buf = dav1d_alloc_aligned(pool->type, size + 64, 64); + if (!buf) { + pthread_mutex_lock(&pool->lock); + const int ref_cnt = --pool->ref_cnt; + pthread_mutex_unlock(&pool->lock); + if (!ref_cnt) mem_pool_destroy(pool); + return NULL; + } + buf->size = size; + } + + return (void*)((uintptr_t)buf + 64); +} + +COLD int dav1d_mem_pool_init(const enum AllocationType type, + Dav1dMemPool **const ppool) +{ + Dav1dMemPool *const pool = dav1d_malloc(ALLOC_COMMON_CTX, + sizeof(Dav1dMemPool)); + if (pool) { + if (!pthread_mutex_init(&pool->lock, NULL)) { + pool->buf = NULL; + pool->ref_cnt = 1; + pool->end = 0; +#if TRACK_HEAP_ALLOCATIONS + pool->type = type; +#endif + *ppool = pool; + return 0; + } + dav1d_free(pool); + } + *ppool = NULL; + return DAV1D_ERR(ENOMEM); +} + +COLD void dav1d_mem_pool_end(Dav1dMemPool *const pool) { + if (pool) { + pthread_mutex_lock(&pool->lock); + Dav1dMemPoolBuffer *buf = pool->buf; + const int ref_cnt = --pool->ref_cnt; + pool->buf = NULL; + pool->end = 1; + pthread_mutex_unlock(&pool->lock); + + while (buf) { + void *const ptr = buf; + buf = buf->next; + dav1d_free_aligned(ptr); + } + if (!ref_cnt) mem_pool_destroy(pool); + } +} diff --git a/3rdparty/dav1d/src/mem.h b/3rdparty/dav1d/src/mem.h new file mode 100644 index 0000000..f092558 --- /dev/null +++ b/3rdparty/dav1d/src/mem.h @@ -0,0 +1,152 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_MEM_H +#define DAV1D_SRC_MEM_H + +#define TRACK_HEAP_ALLOCATIONS 0 + +#include + +#if defined(_WIN32) || HAVE_MEMALIGN +#include +#endif + +#include "dav1d/dav1d.h" + +#include "common/attributes.h" + +#include "src/thread.h" + +enum AllocationType { + ALLOC_BLOCK, + ALLOC_CDEF, + ALLOC_CDF, + ALLOC_COEF, + ALLOC_COMMON_CTX, + ALLOC_DAV1DDATA, + ALLOC_IPRED, + ALLOC_LF, + ALLOC_LR, + ALLOC_OBU_HDR, + ALLOC_OBU_META, + ALLOC_PAL, + ALLOC_PIC, + ALLOC_PIC_CTX, + ALLOC_REFMVS, + ALLOC_SEGMAP, + ALLOC_THREAD_CTX, + ALLOC_TILE, + N_ALLOC_TYPES, +}; + +typedef struct Dav1dMemPoolBuffer { + struct Dav1dMemPoolBuffer *next; + size_t size; +} Dav1dMemPoolBuffer; + +typedef struct Dav1dMemPool { + pthread_mutex_t lock; + Dav1dMemPoolBuffer *buf; + int ref_cnt; + int end; +#if TRACK_HEAP_ALLOCATIONS + enum AllocationType type; +#endif +} Dav1dMemPool; + +// TODO: Move this to a common location? +#define ROUND_UP(x,a) (((x)+((a)-1)) & ~((a)-1)) + +/* + * Allocate align-byte aligned memory. The return value can be released + * by calling the dav1d_free_aligned() function. + */ +static inline void *dav1d_alloc_aligned_internal(const size_t sz, const size_t align) { + assert(!(align & (align - 1))); +#ifdef _WIN32 + return _aligned_malloc(sz, align); +#elif HAVE_POSIX_MEMALIGN + void *ptr; + if (posix_memalign(&ptr, align, sz)) return NULL; + return ptr; +#elif HAVE_MEMALIGN + return memalign(align, sz); +#elif HAVE_ALIGNED_ALLOC + // The C11 standard specifies that the size parameter + // must be an integral multiple of alignment. + return aligned_alloc(align, ROUND_UP(sz, align)); +#else + void *const buf = malloc(sz + align + sizeof(void *)); + if (!buf) return NULL; + + void *const ptr = (void *)(((uintptr_t)buf + sizeof(void *) + align - 1) & ~(align - 1)); + ((void **)ptr)[-1] = buf; + return ptr; +#endif +} + +static inline void dav1d_free_aligned_internal(void *ptr) { +#ifdef _WIN32 + _aligned_free(ptr); +#elif HAVE_POSIX_MEMALIGN || HAVE_MEMALIGN || HAVE_ALIGNED_ALLOC + free(ptr); +#else + if (ptr) free(((void **)ptr)[-1]); +#endif +} + +#if TRACK_HEAP_ALLOCATIONS +void *dav1d_malloc(enum AllocationType type, size_t sz); +void *dav1d_realloc(enum AllocationType type, void *ptr, size_t sz); +void *dav1d_alloc_aligned(enum AllocationType type, size_t sz, size_t align); +void dav1d_free(void *ptr); +void dav1d_free_aligned(void *ptr); +void dav1d_log_alloc_stats(Dav1dContext *c); +#else +#define dav1d_mem_pool_init(type, pool) dav1d_mem_pool_init(pool) +#define dav1d_malloc(type, sz) malloc(sz) +#define dav1d_realloc(type, ptr, sz) realloc(ptr, sz) +#define dav1d_alloc_aligned(type, sz, align) dav1d_alloc_aligned_internal(sz, align) +#define dav1d_free(ptr) free(ptr) +#define dav1d_free_aligned(ptr) dav1d_free_aligned_internal(ptr) +#endif /* TRACK_HEAP_ALLOCATIONS */ + +void dav1d_mem_pool_push(Dav1dMemPool *pool, void *ptr); +void *dav1d_mem_pool_pop(Dav1dMemPool *pool, size_t size); +int dav1d_mem_pool_init(enum AllocationType type, Dav1dMemPool **pool); +void dav1d_mem_pool_end(Dav1dMemPool *pool); + +static inline void dav1d_freep_aligned(void *ptr) { + void **mem = (void **) ptr; + if (*mem) { + dav1d_free_aligned(*mem); + *mem = NULL; + } +} + +#endif /* DAV1D_SRC_MEM_H */ diff --git a/3rdparty/dav1d/src/msac.c b/3rdparty/dav1d/src/msac.c new file mode 100644 index 0000000..971ba85 --- /dev/null +++ b/3rdparty/dav1d/src/msac.c @@ -0,0 +1,220 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/intops.h" + +#include "src/msac.h" + +#define EC_PROB_SHIFT 6 +#define EC_MIN_PROB 4 // must be <= (1<buf_pos; + const uint8_t *buf_end = s->buf_end; + int c = EC_WIN_SIZE - s->cnt - 24; + ec_win dif = s->dif; + do { + if (buf_pos >= buf_end) { + // set remaining bits to 1; + dif |= ~(~(ec_win)0xff << c); + break; + } + dif |= (ec_win)(*buf_pos++ ^ 0xff) << c; + c -= 8; + } while (c >= 0); + s->dif = dif; + s->cnt = EC_WIN_SIZE - c - 24; + s->buf_pos = buf_pos; +} + +int dav1d_msac_decode_subexp(MsacContext *const s, const int ref, + const int n, unsigned k) +{ + assert(n >> k == 8); + + unsigned a = 0; + if (dav1d_msac_decode_bool_equi(s)) { + if (dav1d_msac_decode_bool_equi(s)) + k += dav1d_msac_decode_bool_equi(s) + 1; + a = 1 << k; + } + const unsigned v = dav1d_msac_decode_bools(s, k) + a; + return ref * 2 <= n ? inv_recenter(ref, v) : + n - 1 - inv_recenter(n - 1 - ref, v); +} + +#if !(HAVE_ASM && TRIM_DSP_FUNCTIONS && ( \ + ARCH_AARCH64 || \ + (ARCH_ARM && (defined(__ARM_NEON) || defined(__APPLE__) || defined(_WIN32))) \ +)) +/* Takes updated dif and range values, renormalizes them so that + * 32768 <= rng < 65536 (reading more bytes from the stream into dif if + * necessary), and stores them back in the decoder context. + * dif: The new value of dif. + * rng: The new value of the range. */ +static inline void ctx_norm(MsacContext *const s, const ec_win dif, + const unsigned rng) +{ + const int d = 15 ^ (31 ^ clz(rng)); + const int cnt = s->cnt; + assert(rng <= 65535U); + s->dif = dif << d; + s->rng = rng << d; + s->cnt = cnt - d; + // unsigned compare avoids redundant refills at eob + if ((unsigned)cnt < (unsigned)d) + ctx_refill(s); +} + +unsigned dav1d_msac_decode_bool_equi_c(MsacContext *const s) { + const unsigned r = s->rng; + ec_win dif = s->dif; + assert((dif >> (EC_WIN_SIZE - 16)) < r); + // When the probability is 1/2, f = 16384 >> EC_PROB_SHIFT = 256 and we can + // replace the multiply with a simple shift. + unsigned v = ((r >> 8) << 7) + EC_MIN_PROB; + const ec_win vw = (ec_win)v << (EC_WIN_SIZE - 16); + const unsigned ret = dif >= vw; + dif -= ret * vw; + v += ret * (r - 2 * v); + ctx_norm(s, dif, v); + return !ret; +} + +/* Decode a single binary value. + * f: The probability that the bit is one + * Return: The value decoded (0 or 1). */ +unsigned dav1d_msac_decode_bool_c(MsacContext *const s, const unsigned f) { + const unsigned r = s->rng; + ec_win dif = s->dif; + assert((dif >> (EC_WIN_SIZE - 16)) < r); + unsigned v = ((r >> 8) * (f >> EC_PROB_SHIFT) >> (7 - EC_PROB_SHIFT)) + EC_MIN_PROB; + const ec_win vw = (ec_win)v << (EC_WIN_SIZE - 16); + const unsigned ret = dif >= vw; + dif -= ret * vw; + v += ret * (r - 2 * v); + ctx_norm(s, dif, v); + return !ret; +} + +/* Decodes a symbol given an inverse cumulative distribution function (CDF) + * table in Q15. */ +unsigned dav1d_msac_decode_symbol_adapt_c(MsacContext *const s, + uint16_t *const cdf, + const size_t n_symbols) +{ + const unsigned c = s->dif >> (EC_WIN_SIZE - 16), r = s->rng >> 8; + unsigned u, v = s->rng, val = -1; + + assert(n_symbols <= 15); + assert(cdf[n_symbols] <= 32); + + do { + val++; + u = v; + v = r * (cdf[val] >> EC_PROB_SHIFT); + v >>= 7 - EC_PROB_SHIFT; + v += EC_MIN_PROB * ((unsigned)n_symbols - val); + } while (c < v); + + assert(u <= s->rng); + + ctx_norm(s, s->dif - ((ec_win)v << (EC_WIN_SIZE - 16)), u - v); + + if (s->allow_update_cdf) { + const unsigned count = cdf[n_symbols]; + const unsigned rate = 4 + (count >> 4) + (n_symbols > 2); + unsigned i; + for (i = 0; i < val; i++) + cdf[i] += (32768 - cdf[i]) >> rate; + for (; i < n_symbols; i++) + cdf[i] -= cdf[i] >> rate; + cdf[n_symbols] = count + (count < 32); + } + + return val; +} + +unsigned dav1d_msac_decode_bool_adapt_c(MsacContext *const s, + uint16_t *const cdf) +{ + const unsigned bit = dav1d_msac_decode_bool(s, *cdf); + + if (s->allow_update_cdf) { + // update_cdf() specialized for boolean CDFs + const unsigned count = cdf[1]; + const int rate = 4 + (count >> 4); + if (bit) + cdf[0] += (32768 - cdf[0]) >> rate; + else + cdf[0] -= cdf[0] >> rate; + cdf[1] = count + (count < 32); + } + + return bit; +} + +unsigned dav1d_msac_decode_hi_tok_c(MsacContext *const s, uint16_t *const cdf) { + unsigned tok_br = dav1d_msac_decode_symbol_adapt4(s, cdf, 3); + unsigned tok = 3 + tok_br; + if (tok_br == 3) { + tok_br = dav1d_msac_decode_symbol_adapt4(s, cdf, 3); + tok = 6 + tok_br; + if (tok_br == 3) { + tok_br = dav1d_msac_decode_symbol_adapt4(s, cdf, 3); + tok = 9 + tok_br; + if (tok_br == 3) + tok = 12 + dav1d_msac_decode_symbol_adapt4(s, cdf, 3); + } + } + return tok; +} +#endif + +void dav1d_msac_init(MsacContext *const s, const uint8_t *const data, + const size_t sz, const int disable_cdf_update_flag) +{ + s->buf_pos = data; + s->buf_end = data + sz; + s->dif = 0; + s->rng = 0x8000; + s->cnt = -15; + s->allow_update_cdf = !disable_cdf_update_flag; + ctx_refill(s); + +#if ARCH_X86_64 && HAVE_ASM + s->symbol_adapt16 = dav1d_msac_decode_symbol_adapt_c; + + msac_init_x86(s); +#endif +} diff --git a/3rdparty/dav1d/src/msac.h b/3rdparty/dav1d/src/msac.h new file mode 100644 index 0000000..eb97650 --- /dev/null +++ b/3rdparty/dav1d/src/msac.h @@ -0,0 +1,110 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_MSAC_H +#define DAV1D_SRC_MSAC_H + +#include +#include + +#include "common/intops.h" + +typedef size_t ec_win; + +typedef struct MsacContext { + const uint8_t *buf_pos; + const uint8_t *buf_end; + ec_win dif; + unsigned rng; + int cnt; + int allow_update_cdf; + +#if ARCH_X86_64 && HAVE_ASM + unsigned (*symbol_adapt16)(struct MsacContext *s, uint16_t *cdf, size_t n_symbols); +#endif +} MsacContext; + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM +#include "src/arm/msac.h" +#elif ARCH_LOONGARCH64 +#include "src/loongarch/msac.h" +#elif ARCH_X86 +#include "src/x86/msac.h" +#endif +#endif + +void dav1d_msac_init(MsacContext *s, const uint8_t *data, size_t sz, + int disable_cdf_update_flag); +unsigned dav1d_msac_decode_symbol_adapt_c(MsacContext *s, uint16_t *cdf, + size_t n_symbols); +unsigned dav1d_msac_decode_bool_adapt_c(MsacContext *s, uint16_t *cdf); +unsigned dav1d_msac_decode_bool_equi_c(MsacContext *s); +unsigned dav1d_msac_decode_bool_c(MsacContext *s, unsigned f); +unsigned dav1d_msac_decode_hi_tok_c(MsacContext *s, uint16_t *cdf); +int dav1d_msac_decode_subexp(MsacContext *s, int ref, int n, unsigned k); + +/* Supported n_symbols ranges: adapt4: 1-3, adapt8: 1-7, adapt16: 3-15 */ +#ifndef dav1d_msac_decode_symbol_adapt4 +#define dav1d_msac_decode_symbol_adapt4 dav1d_msac_decode_symbol_adapt_c +#endif +#ifndef dav1d_msac_decode_symbol_adapt8 +#define dav1d_msac_decode_symbol_adapt8 dav1d_msac_decode_symbol_adapt_c +#endif +#ifndef dav1d_msac_decode_symbol_adapt16 +#define dav1d_msac_decode_symbol_adapt16 dav1d_msac_decode_symbol_adapt_c +#endif +#ifndef dav1d_msac_decode_bool_adapt +#define dav1d_msac_decode_bool_adapt dav1d_msac_decode_bool_adapt_c +#endif +#ifndef dav1d_msac_decode_bool_equi +#define dav1d_msac_decode_bool_equi dav1d_msac_decode_bool_equi_c +#endif +#ifndef dav1d_msac_decode_bool +#define dav1d_msac_decode_bool dav1d_msac_decode_bool_c +#endif +#ifndef dav1d_msac_decode_hi_tok +#define dav1d_msac_decode_hi_tok dav1d_msac_decode_hi_tok_c +#endif + +static inline unsigned dav1d_msac_decode_bools(MsacContext *const s, unsigned n) { + unsigned v = 0; + while (n--) + v = (v << 1) | dav1d_msac_decode_bool_equi(s); + return v; +} + +static inline int dav1d_msac_decode_uniform(MsacContext *const s, const unsigned n) { + assert(n > 0); + const int l = ulog2(n) + 1; + assert(l > 1); + const unsigned m = (1 << l) - n; + const unsigned v = dav1d_msac_decode_bools(s, l - 1); + return v < m ? v : (v << 1) - m + dav1d_msac_decode_bool_equi(s); +} + +#endif /* DAV1D_SRC_MSAC_H */ diff --git a/3rdparty/dav1d/src/obu.c b/3rdparty/dav1d/src/obu.c new file mode 100644 index 0000000..da5002a --- /dev/null +++ b/3rdparty/dav1d/src/obu.c @@ -0,0 +1,1695 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include +#include + +#include "dav1d/data.h" + +#include "common/frame.h" +#include "common/intops.h" +#include "common/validate.h" + +#include "src/decode.h" +#include "src/getbits.h" +#include "src/levels.h" +#include "src/log.h" +#include "src/obu.h" +#include "src/ref.h" +#include "src/thread_task.h" + +static int check_trailing_bits(GetBits *const gb, + const int strict_std_compliance) +{ + const int trailing_one_bit = dav1d_get_bit(gb); + + if (gb->error) + return DAV1D_ERR(EINVAL); + + if (!strict_std_compliance) + return 0; + + if (!trailing_one_bit || gb->state) + return DAV1D_ERR(EINVAL); + + ptrdiff_t size = gb->ptr_end - gb->ptr; + while (size > 0 && gb->ptr[size - 1] == 0) + size--; + + if (size) + return DAV1D_ERR(EINVAL); + + return 0; +} + +static NOINLINE int parse_seq_hdr(Dav1dSequenceHeader *const hdr, + GetBits *const gb, + const int strict_std_compliance) +{ +#define DEBUG_SEQ_HDR 0 + +#if DEBUG_SEQ_HDR + const unsigned init_bit_pos = dav1d_get_bits_pos(gb); +#endif + + memset(hdr, 0, sizeof(*hdr)); + hdr->profile = dav1d_get_bits(gb, 3); + if (hdr->profile > 2) goto error; +#if DEBUG_SEQ_HDR + printf("SEQHDR: post-profile: off=%u\n", + dav1d_get_bits_pos(gb) - init_bit_pos); +#endif + + hdr->still_picture = dav1d_get_bit(gb); + hdr->reduced_still_picture_header = dav1d_get_bit(gb); + if (hdr->reduced_still_picture_header && !hdr->still_picture) goto error; +#if DEBUG_SEQ_HDR + printf("SEQHDR: post-stillpicture_flags: off=%u\n", + dav1d_get_bits_pos(gb) - init_bit_pos); +#endif + + if (hdr->reduced_still_picture_header) { + hdr->num_operating_points = 1; + hdr->operating_points[0].major_level = dav1d_get_bits(gb, 3); + hdr->operating_points[0].minor_level = dav1d_get_bits(gb, 2); + hdr->operating_points[0].initial_display_delay = 10; + } else { + hdr->timing_info_present = dav1d_get_bit(gb); + if (hdr->timing_info_present) { + hdr->num_units_in_tick = dav1d_get_bits(gb, 32); + hdr->time_scale = dav1d_get_bits(gb, 32); + if (strict_std_compliance && (!hdr->num_units_in_tick || !hdr->time_scale)) + goto error; + hdr->equal_picture_interval = dav1d_get_bit(gb); + if (hdr->equal_picture_interval) { + const unsigned num_ticks_per_picture = dav1d_get_vlc(gb); + if (num_ticks_per_picture == UINT32_MAX) + goto error; + hdr->num_ticks_per_picture = num_ticks_per_picture + 1; + } + + hdr->decoder_model_info_present = dav1d_get_bit(gb); + if (hdr->decoder_model_info_present) { + hdr->encoder_decoder_buffer_delay_length = dav1d_get_bits(gb, 5) + 1; + hdr->num_units_in_decoding_tick = dav1d_get_bits(gb, 32); + if (strict_std_compliance && !hdr->num_units_in_decoding_tick) + goto error; + hdr->buffer_removal_delay_length = dav1d_get_bits(gb, 5) + 1; + hdr->frame_presentation_delay_length = dav1d_get_bits(gb, 5) + 1; + } + } +#if DEBUG_SEQ_HDR + printf("SEQHDR: post-timinginfo: off=%u\n", + dav1d_get_bits_pos(gb) - init_bit_pos); +#endif + + hdr->display_model_info_present = dav1d_get_bit(gb); + hdr->num_operating_points = dav1d_get_bits(gb, 5) + 1; + for (int i = 0; i < hdr->num_operating_points; i++) { + struct Dav1dSequenceHeaderOperatingPoint *const op = + &hdr->operating_points[i]; + op->idc = dav1d_get_bits(gb, 12); + if (op->idc && (!(op->idc & 0xff) || !(op->idc & 0xf00))) + goto error; + op->major_level = 2 + dav1d_get_bits(gb, 3); + op->minor_level = dav1d_get_bits(gb, 2); + if (op->major_level > 3) + op->tier = dav1d_get_bit(gb); + if (hdr->decoder_model_info_present) { + op->decoder_model_param_present = dav1d_get_bit(gb); + if (op->decoder_model_param_present) { + struct Dav1dSequenceHeaderOperatingParameterInfo *const opi = + &hdr->operating_parameter_info[i]; + opi->decoder_buffer_delay = + dav1d_get_bits(gb, hdr->encoder_decoder_buffer_delay_length); + opi->encoder_buffer_delay = + dav1d_get_bits(gb, hdr->encoder_decoder_buffer_delay_length); + opi->low_delay_mode = dav1d_get_bit(gb); + } + } + if (hdr->display_model_info_present) + op->display_model_param_present = dav1d_get_bit(gb); + op->initial_display_delay = + op->display_model_param_present ? dav1d_get_bits(gb, 4) + 1 : 10; + } +#if DEBUG_SEQ_HDR + printf("SEQHDR: post-operating-points: off=%u\n", + dav1d_get_bits_pos(gb) - init_bit_pos); +#endif + } + + hdr->width_n_bits = dav1d_get_bits(gb, 4) + 1; + hdr->height_n_bits = dav1d_get_bits(gb, 4) + 1; + hdr->max_width = dav1d_get_bits(gb, hdr->width_n_bits) + 1; + hdr->max_height = dav1d_get_bits(gb, hdr->height_n_bits) + 1; +#if DEBUG_SEQ_HDR + printf("SEQHDR: post-size: off=%u\n", + dav1d_get_bits_pos(gb) - init_bit_pos); +#endif + if (!hdr->reduced_still_picture_header) { + hdr->frame_id_numbers_present = dav1d_get_bit(gb); + if (hdr->frame_id_numbers_present) { + hdr->delta_frame_id_n_bits = dav1d_get_bits(gb, 4) + 2; + hdr->frame_id_n_bits = dav1d_get_bits(gb, 3) + hdr->delta_frame_id_n_bits + 1; + } + } +#if DEBUG_SEQ_HDR + printf("SEQHDR: post-frame-id-numbers-present: off=%u\n", + dav1d_get_bits_pos(gb) - init_bit_pos); +#endif + + hdr->sb128 = dav1d_get_bit(gb); + hdr->filter_intra = dav1d_get_bit(gb); + hdr->intra_edge_filter = dav1d_get_bit(gb); + if (hdr->reduced_still_picture_header) { + hdr->screen_content_tools = DAV1D_ADAPTIVE; + hdr->force_integer_mv = DAV1D_ADAPTIVE; + } else { + hdr->inter_intra = dav1d_get_bit(gb); + hdr->masked_compound = dav1d_get_bit(gb); + hdr->warped_motion = dav1d_get_bit(gb); + hdr->dual_filter = dav1d_get_bit(gb); + hdr->order_hint = dav1d_get_bit(gb); + if (hdr->order_hint) { + hdr->jnt_comp = dav1d_get_bit(gb); + hdr->ref_frame_mvs = dav1d_get_bit(gb); + } + hdr->screen_content_tools = dav1d_get_bit(gb) ? DAV1D_ADAPTIVE : dav1d_get_bit(gb); + #if DEBUG_SEQ_HDR + printf("SEQHDR: post-screentools: off=%u\n", + dav1d_get_bits_pos(gb) - init_bit_pos); + #endif + hdr->force_integer_mv = hdr->screen_content_tools ? + dav1d_get_bit(gb) ? DAV1D_ADAPTIVE : dav1d_get_bit(gb) : 2; + if (hdr->order_hint) + hdr->order_hint_n_bits = dav1d_get_bits(gb, 3) + 1; + } + hdr->super_res = dav1d_get_bit(gb); + hdr->cdef = dav1d_get_bit(gb); + hdr->restoration = dav1d_get_bit(gb); +#if DEBUG_SEQ_HDR + printf("SEQHDR: post-featurebits: off=%u\n", + dav1d_get_bits_pos(gb) - init_bit_pos); +#endif + + hdr->hbd = dav1d_get_bit(gb); + if (hdr->profile == 2 && hdr->hbd) + hdr->hbd += dav1d_get_bit(gb); + if (hdr->profile != 1) + hdr->monochrome = dav1d_get_bit(gb); + hdr->color_description_present = dav1d_get_bit(gb); + if (hdr->color_description_present) { + hdr->pri = dav1d_get_bits(gb, 8); + hdr->trc = dav1d_get_bits(gb, 8); + hdr->mtrx = dav1d_get_bits(gb, 8); + } else { + hdr->pri = DAV1D_COLOR_PRI_UNKNOWN; + hdr->trc = DAV1D_TRC_UNKNOWN; + hdr->mtrx = DAV1D_MC_UNKNOWN; + } + if (hdr->monochrome) { + hdr->color_range = dav1d_get_bit(gb); + hdr->layout = DAV1D_PIXEL_LAYOUT_I400; + hdr->ss_hor = hdr->ss_ver = 1; + hdr->chr = DAV1D_CHR_UNKNOWN; + } else if (hdr->pri == DAV1D_COLOR_PRI_BT709 && + hdr->trc == DAV1D_TRC_SRGB && + hdr->mtrx == DAV1D_MC_IDENTITY) + { + hdr->layout = DAV1D_PIXEL_LAYOUT_I444; + hdr->color_range = 1; + if (hdr->profile != 1 && !(hdr->profile == 2 && hdr->hbd == 2)) + goto error; + } else { + hdr->color_range = dav1d_get_bit(gb); + switch (hdr->profile) { + case 0: hdr->layout = DAV1D_PIXEL_LAYOUT_I420; + hdr->ss_hor = hdr->ss_ver = 1; + break; + case 1: hdr->layout = DAV1D_PIXEL_LAYOUT_I444; + break; + case 2: + if (hdr->hbd == 2) { + hdr->ss_hor = dav1d_get_bit(gb); + if (hdr->ss_hor) + hdr->ss_ver = dav1d_get_bit(gb); + } else + hdr->ss_hor = 1; + hdr->layout = hdr->ss_hor ? + hdr->ss_ver ? DAV1D_PIXEL_LAYOUT_I420 : + DAV1D_PIXEL_LAYOUT_I422 : + DAV1D_PIXEL_LAYOUT_I444; + break; + } + hdr->chr = (hdr->ss_hor & hdr->ss_ver) ? + dav1d_get_bits(gb, 2) : DAV1D_CHR_UNKNOWN; + } + if (strict_std_compliance && + hdr->mtrx == DAV1D_MC_IDENTITY && hdr->layout != DAV1D_PIXEL_LAYOUT_I444) + { + goto error; + } + if (!hdr->monochrome) + hdr->separate_uv_delta_q = dav1d_get_bit(gb); +#if DEBUG_SEQ_HDR + printf("SEQHDR: post-colorinfo: off=%u\n", + dav1d_get_bits_pos(gb) - init_bit_pos); +#endif + + hdr->film_grain_present = dav1d_get_bit(gb); +#if DEBUG_SEQ_HDR + printf("SEQHDR: post-filmgrain: off=%u\n", + dav1d_get_bits_pos(gb) - init_bit_pos); +#endif + + // We needn't bother flushing the OBU here: we'll check we didn't + // overrun in the caller and will then discard gb, so there's no + // point in setting its position properly. + + return check_trailing_bits(gb, strict_std_compliance); + +error: + return DAV1D_ERR(EINVAL); +} + +int dav1d_parse_sequence_header(Dav1dSequenceHeader *const out, + const uint8_t *const ptr, const size_t sz) +{ + validate_input_or_ret(out != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(ptr != NULL, DAV1D_ERR(EINVAL)); + validate_input_or_ret(sz > 0 && sz <= SIZE_MAX / 2, DAV1D_ERR(EINVAL)); + + GetBits gb; + dav1d_init_get_bits(&gb, ptr, sz); + int res = DAV1D_ERR(ENOENT); + + do { + dav1d_get_bit(&gb); // obu_forbidden_bit + const enum Dav1dObuType type = dav1d_get_bits(&gb, 4); + const int has_extension = dav1d_get_bit(&gb); + const int has_length_field = dav1d_get_bit(&gb); + dav1d_get_bits(&gb, 1 + 8 * has_extension); // ignore + + const uint8_t *obu_end = gb.ptr_end; + if (has_length_field) { + const size_t len = dav1d_get_uleb128(&gb); + if (len > (size_t)(obu_end - gb.ptr)) return DAV1D_ERR(EINVAL); + obu_end = gb.ptr + len; + } + + if (type == DAV1D_OBU_SEQ_HDR) { + if ((res = parse_seq_hdr(out, &gb, 0)) < 0) return res; + if (gb.ptr > obu_end) return DAV1D_ERR(EINVAL); + dav1d_bytealign_get_bits(&gb); + } + + if (gb.error) return DAV1D_ERR(EINVAL); + assert(gb.state == 0 && gb.bits_left == 0); + gb.ptr = obu_end; + } while (gb.ptr < gb.ptr_end); + + return res; +} + +static int read_frame_size(Dav1dContext *const c, GetBits *const gb, + const int use_ref) +{ + const Dav1dSequenceHeader *const seqhdr = c->seq_hdr; + Dav1dFrameHeader *const hdr = c->frame_hdr; + + if (use_ref) { + for (int i = 0; i < 7; i++) { + if (dav1d_get_bit(gb)) { + const Dav1dThreadPicture *const ref = + &c->refs[c->frame_hdr->refidx[i]].p; + if (!ref->p.frame_hdr) return -1; + hdr->width[1] = ref->p.frame_hdr->width[1]; + hdr->height = ref->p.frame_hdr->height; + hdr->render_width = ref->p.frame_hdr->render_width; + hdr->render_height = ref->p.frame_hdr->render_height; + hdr->super_res.enabled = seqhdr->super_res && dav1d_get_bit(gb); + if (hdr->super_res.enabled) { + const int d = hdr->super_res.width_scale_denominator = + 9 + dav1d_get_bits(gb, 3); + hdr->width[0] = imax((hdr->width[1] * 8 + (d >> 1)) / d, + imin(16, hdr->width[1])); + } else { + hdr->super_res.width_scale_denominator = 8; + hdr->width[0] = hdr->width[1]; + } + return 0; + } + } + } + + if (hdr->frame_size_override) { + hdr->width[1] = dav1d_get_bits(gb, seqhdr->width_n_bits) + 1; + hdr->height = dav1d_get_bits(gb, seqhdr->height_n_bits) + 1; + } else { + hdr->width[1] = seqhdr->max_width; + hdr->height = seqhdr->max_height; + } + hdr->super_res.enabled = seqhdr->super_res && dav1d_get_bit(gb); + if (hdr->super_res.enabled) { + const int d = hdr->super_res.width_scale_denominator = 9 + dav1d_get_bits(gb, 3); + hdr->width[0] = imax((hdr->width[1] * 8 + (d >> 1)) / d, imin(16, hdr->width[1])); + } else { + hdr->super_res.width_scale_denominator = 8; + hdr->width[0] = hdr->width[1]; + } + hdr->have_render_size = dav1d_get_bit(gb); + if (hdr->have_render_size) { + hdr->render_width = dav1d_get_bits(gb, 16) + 1; + hdr->render_height = dav1d_get_bits(gb, 16) + 1; + } else { + hdr->render_width = hdr->width[1]; + hdr->render_height = hdr->height; + } + return 0; +} + +static inline int tile_log2(const int sz, const int tgt) { + int k; + for (k = 0; (sz << k) < tgt; k++) ; + return k; +} + +static const Dav1dLoopfilterModeRefDeltas default_mode_ref_deltas = { + .mode_delta = { 0, 0 }, + .ref_delta = { 1, 0, 0, 0, -1, 0, -1, -1 }, +}; + +static int parse_frame_hdr(Dav1dContext *const c, GetBits *const gb) { +#define DEBUG_FRAME_HDR 0 + +#if DEBUG_FRAME_HDR + const uint8_t *const init_ptr = gb->ptr; +#endif + const Dav1dSequenceHeader *const seqhdr = c->seq_hdr; + Dav1dFrameHeader *const hdr = c->frame_hdr; + + if (!seqhdr->reduced_still_picture_header) + hdr->show_existing_frame = dav1d_get_bit(gb); +#if DEBUG_FRAME_HDR + printf("HDR: post-show_existing_frame: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + if (hdr->show_existing_frame) { + hdr->existing_frame_idx = dav1d_get_bits(gb, 3); + if (seqhdr->decoder_model_info_present && !seqhdr->equal_picture_interval) + hdr->frame_presentation_delay = dav1d_get_bits(gb, seqhdr->frame_presentation_delay_length); + if (seqhdr->frame_id_numbers_present) { + hdr->frame_id = dav1d_get_bits(gb, seqhdr->frame_id_n_bits); + Dav1dFrameHeader *const ref_frame_hdr = c->refs[hdr->existing_frame_idx].p.p.frame_hdr; + if (!ref_frame_hdr || ref_frame_hdr->frame_id != hdr->frame_id) goto error; + } + return 0; + } + + if (seqhdr->reduced_still_picture_header) { + hdr->frame_type = DAV1D_FRAME_TYPE_KEY; + hdr->show_frame = 1; + } else { + hdr->frame_type = dav1d_get_bits(gb, 2); + hdr->show_frame = dav1d_get_bit(gb); + } + if (hdr->show_frame) { + if (seqhdr->decoder_model_info_present && !seqhdr->equal_picture_interval) + hdr->frame_presentation_delay = dav1d_get_bits(gb, seqhdr->frame_presentation_delay_length); + hdr->showable_frame = hdr->frame_type != DAV1D_FRAME_TYPE_KEY; + } else + hdr->showable_frame = dav1d_get_bit(gb); + hdr->error_resilient_mode = + (hdr->frame_type == DAV1D_FRAME_TYPE_KEY && hdr->show_frame) || + hdr->frame_type == DAV1D_FRAME_TYPE_SWITCH || + seqhdr->reduced_still_picture_header || dav1d_get_bit(gb); +#if DEBUG_FRAME_HDR + printf("HDR: post-frametype_bits: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + hdr->disable_cdf_update = dav1d_get_bit(gb); + hdr->allow_screen_content_tools = seqhdr->screen_content_tools == DAV1D_ADAPTIVE ? + dav1d_get_bit(gb) : seqhdr->screen_content_tools; + if (hdr->allow_screen_content_tools) + hdr->force_integer_mv = seqhdr->force_integer_mv == DAV1D_ADAPTIVE ? + dav1d_get_bit(gb) : seqhdr->force_integer_mv; + + if (IS_KEY_OR_INTRA(hdr)) + hdr->force_integer_mv = 1; + + if (seqhdr->frame_id_numbers_present) + hdr->frame_id = dav1d_get_bits(gb, seqhdr->frame_id_n_bits); + + if (!seqhdr->reduced_still_picture_header) + hdr->frame_size_override = hdr->frame_type == DAV1D_FRAME_TYPE_SWITCH ? 1 : dav1d_get_bit(gb); +#if DEBUG_FRAME_HDR + printf("HDR: post-frame_size_override_flag: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + if (seqhdr->order_hint) + hdr->frame_offset = dav1d_get_bits(gb, seqhdr->order_hint_n_bits); + hdr->primary_ref_frame = !hdr->error_resilient_mode && IS_INTER_OR_SWITCH(hdr) ? + dav1d_get_bits(gb, 3) : DAV1D_PRIMARY_REF_NONE; + + if (seqhdr->decoder_model_info_present) { + hdr->buffer_removal_time_present = dav1d_get_bit(gb); + if (hdr->buffer_removal_time_present) { + for (int i = 0; i < c->seq_hdr->num_operating_points; i++) { + const struct Dav1dSequenceHeaderOperatingPoint *const seqop = &seqhdr->operating_points[i]; + struct Dav1dFrameHeaderOperatingPoint *const op = &hdr->operating_points[i]; + if (seqop->decoder_model_param_present) { + int in_temporal_layer = (seqop->idc >> hdr->temporal_id) & 1; + int in_spatial_layer = (seqop->idc >> (hdr->spatial_id + 8)) & 1; + if (!seqop->idc || (in_temporal_layer && in_spatial_layer)) + op->buffer_removal_time = dav1d_get_bits(gb, seqhdr->buffer_removal_delay_length); + } + } + } + } + + if (IS_KEY_OR_INTRA(hdr)) { + hdr->refresh_frame_flags = (hdr->frame_type == DAV1D_FRAME_TYPE_KEY && + hdr->show_frame) ? 0xff : dav1d_get_bits(gb, 8); + if (hdr->refresh_frame_flags != 0xff && hdr->error_resilient_mode && seqhdr->order_hint) + for (int i = 0; i < 8; i++) + dav1d_get_bits(gb, seqhdr->order_hint_n_bits); + if (c->strict_std_compliance && + hdr->frame_type == DAV1D_FRAME_TYPE_INTRA && hdr->refresh_frame_flags == 0xff) + { + goto error; + } + if (read_frame_size(c, gb, 0) < 0) goto error; + if (hdr->allow_screen_content_tools && !hdr->super_res.enabled) + hdr->allow_intrabc = dav1d_get_bit(gb); + } else { + hdr->refresh_frame_flags = hdr->frame_type == DAV1D_FRAME_TYPE_SWITCH ? 0xff : + dav1d_get_bits(gb, 8); + if (hdr->error_resilient_mode && seqhdr->order_hint) + for (int i = 0; i < 8; i++) + dav1d_get_bits(gb, seqhdr->order_hint_n_bits); + if (seqhdr->order_hint) { + hdr->frame_ref_short_signaling = dav1d_get_bit(gb); + if (hdr->frame_ref_short_signaling) { + hdr->refidx[0] = dav1d_get_bits(gb, 3); + hdr->refidx[1] = hdr->refidx[2] = -1; + hdr->refidx[3] = dav1d_get_bits(gb, 3); + + /* +1 allows for unconditional stores, as unused + * values can be dumped into frame_offset[-1]. */ + int frame_offset_mem[8+1]; + int *const frame_offset = &frame_offset_mem[1]; + int earliest_ref = -1; + for (int i = 0, earliest_offset = INT_MAX; i < 8; i++) { + const Dav1dFrameHeader *const refhdr = c->refs[i].p.p.frame_hdr; + if (!refhdr) goto error; + const int diff = get_poc_diff(seqhdr->order_hint_n_bits, + refhdr->frame_offset, + hdr->frame_offset); + frame_offset[i] = diff; + if (diff < earliest_offset) { + earliest_offset = diff; + earliest_ref = i; + } + } + frame_offset[hdr->refidx[0]] = INT_MIN; // = reference frame is used + frame_offset[hdr->refidx[3]] = INT_MIN; + assert(earliest_ref >= 0); + + int refidx = -1; + for (int i = 0, latest_offset = 0; i < 8; i++) { + const int hint = frame_offset[i]; + if (hint >= latest_offset) { + latest_offset = hint; + refidx = i; + } + } + frame_offset[refidx] = INT_MIN; + hdr->refidx[6] = refidx; + + for (int i = 4; i < 6; i++) { + /* Unsigned compares to handle negative values. */ + unsigned earliest_offset = UINT8_MAX; + refidx = -1; + for (int j = 0; j < 8; j++) { + const unsigned hint = frame_offset[j]; + if (hint < earliest_offset) { + earliest_offset = hint; + refidx = j; + } + } + frame_offset[refidx] = INT_MIN; + hdr->refidx[i] = refidx; + } + + for (int i = 1; i < 7; i++) { + refidx = hdr->refidx[i]; + if (refidx < 0) { + unsigned latest_offset = ~UINT8_MAX; + for (int j = 0; j < 8; j++) { + const unsigned hint = frame_offset[j]; + if (hint >= latest_offset) { + latest_offset = hint; + refidx = j; + } + } + frame_offset[refidx] = INT_MIN; + hdr->refidx[i] = refidx >= 0 ? refidx : earliest_ref; + } + } + } + } + for (int i = 0; i < 7; i++) { + if (!hdr->frame_ref_short_signaling) + hdr->refidx[i] = dav1d_get_bits(gb, 3); + if (seqhdr->frame_id_numbers_present) { + const unsigned delta_ref_frame_id = dav1d_get_bits(gb, seqhdr->delta_frame_id_n_bits) + 1; + const unsigned ref_frame_id = (hdr->frame_id + (1 << seqhdr->frame_id_n_bits) - delta_ref_frame_id) & ((1 << seqhdr->frame_id_n_bits) - 1); + Dav1dFrameHeader *const ref_frame_hdr = c->refs[hdr->refidx[i]].p.p.frame_hdr; + if (!ref_frame_hdr || ref_frame_hdr->frame_id != ref_frame_id) goto error; + } + } + const int use_ref = !hdr->error_resilient_mode && + hdr->frame_size_override; + if (read_frame_size(c, gb, use_ref) < 0) goto error; + if (!hdr->force_integer_mv) + hdr->hp = dav1d_get_bit(gb); + hdr->subpel_filter_mode = dav1d_get_bit(gb) ? DAV1D_FILTER_SWITCHABLE : + dav1d_get_bits(gb, 2); + hdr->switchable_motion_mode = dav1d_get_bit(gb); + if (!hdr->error_resilient_mode && seqhdr->ref_frame_mvs && + seqhdr->order_hint && IS_INTER_OR_SWITCH(hdr)) + { + hdr->use_ref_frame_mvs = dav1d_get_bit(gb); + } + } +#if DEBUG_FRAME_HDR + printf("HDR: post-frametype-specific-bits: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + if (!seqhdr->reduced_still_picture_header && !hdr->disable_cdf_update) + hdr->refresh_context = !dav1d_get_bit(gb); +#if DEBUG_FRAME_HDR + printf("HDR: post-refresh_context: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + // tile data + hdr->tiling.uniform = dav1d_get_bit(gb); + const int sbsz_min1 = (64 << seqhdr->sb128) - 1; + const int sbsz_log2 = 6 + seqhdr->sb128; + const int sbw = (hdr->width[0] + sbsz_min1) >> sbsz_log2; + const int sbh = (hdr->height + sbsz_min1) >> sbsz_log2; + const int max_tile_width_sb = 4096 >> sbsz_log2; + const int max_tile_area_sb = 4096 * 2304 >> (2 * sbsz_log2); + hdr->tiling.min_log2_cols = tile_log2(max_tile_width_sb, sbw); + hdr->tiling.max_log2_cols = tile_log2(1, imin(sbw, DAV1D_MAX_TILE_COLS)); + hdr->tiling.max_log2_rows = tile_log2(1, imin(sbh, DAV1D_MAX_TILE_ROWS)); + const int min_log2_tiles = imax(tile_log2(max_tile_area_sb, sbw * sbh), + hdr->tiling.min_log2_cols); + if (hdr->tiling.uniform) { + for (hdr->tiling.log2_cols = hdr->tiling.min_log2_cols; + hdr->tiling.log2_cols < hdr->tiling.max_log2_cols && dav1d_get_bit(gb); + hdr->tiling.log2_cols++) ; + const int tile_w = 1 + ((sbw - 1) >> hdr->tiling.log2_cols); + hdr->tiling.cols = 0; + for (int sbx = 0; sbx < sbw; sbx += tile_w, hdr->tiling.cols++) + hdr->tiling.col_start_sb[hdr->tiling.cols] = sbx; + hdr->tiling.min_log2_rows = + imax(min_log2_tiles - hdr->tiling.log2_cols, 0); + + for (hdr->tiling.log2_rows = hdr->tiling.min_log2_rows; + hdr->tiling.log2_rows < hdr->tiling.max_log2_rows && dav1d_get_bit(gb); + hdr->tiling.log2_rows++) ; + const int tile_h = 1 + ((sbh - 1) >> hdr->tiling.log2_rows); + hdr->tiling.rows = 0; + for (int sby = 0; sby < sbh; sby += tile_h, hdr->tiling.rows++) + hdr->tiling.row_start_sb[hdr->tiling.rows] = sby; + } else { + hdr->tiling.cols = 0; + int widest_tile = 0, max_tile_area_sb = sbw * sbh; + for (int sbx = 0; sbx < sbw && hdr->tiling.cols < DAV1D_MAX_TILE_COLS; hdr->tiling.cols++) { + const int tile_width_sb = imin(sbw - sbx, max_tile_width_sb); + const int tile_w = (tile_width_sb > 1) ? 1 + dav1d_get_uniform(gb, tile_width_sb) : 1; + hdr->tiling.col_start_sb[hdr->tiling.cols] = sbx; + sbx += tile_w; + widest_tile = imax(widest_tile, tile_w); + } + hdr->tiling.log2_cols = tile_log2(1, hdr->tiling.cols); + if (min_log2_tiles) max_tile_area_sb >>= min_log2_tiles + 1; + const int max_tile_height_sb = imax(max_tile_area_sb / widest_tile, 1); + + hdr->tiling.rows = 0; + for (int sby = 0; sby < sbh && hdr->tiling.rows < DAV1D_MAX_TILE_ROWS; hdr->tiling.rows++) { + const int tile_height_sb = imin(sbh - sby, max_tile_height_sb); + const int tile_h = (tile_height_sb > 1) ? 1 + dav1d_get_uniform(gb, tile_height_sb) : 1; + hdr->tiling.row_start_sb[hdr->tiling.rows] = sby; + sby += tile_h; + } + hdr->tiling.log2_rows = tile_log2(1, hdr->tiling.rows); + } + hdr->tiling.col_start_sb[hdr->tiling.cols] = sbw; + hdr->tiling.row_start_sb[hdr->tiling.rows] = sbh; + if (hdr->tiling.log2_cols || hdr->tiling.log2_rows) { + hdr->tiling.update = dav1d_get_bits(gb, hdr->tiling.log2_cols + hdr->tiling.log2_rows); + if (hdr->tiling.update >= hdr->tiling.cols * hdr->tiling.rows) + goto error; + hdr->tiling.n_bytes = dav1d_get_bits(gb, 2) + 1; + } +#if DEBUG_FRAME_HDR + printf("HDR: post-tiling: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + // quant data + hdr->quant.yac = dav1d_get_bits(gb, 8); + if (dav1d_get_bit(gb)) + hdr->quant.ydc_delta = dav1d_get_sbits(gb, 7); + if (!seqhdr->monochrome) { + // If the sequence header says that delta_q might be different + // for U, V, we must check whether it actually is for this + // frame. + const int diff_uv_delta = seqhdr->separate_uv_delta_q ? dav1d_get_bit(gb) : 0; + if (dav1d_get_bit(gb)) + hdr->quant.udc_delta = dav1d_get_sbits(gb, 7); + if (dav1d_get_bit(gb)) + hdr->quant.uac_delta = dav1d_get_sbits(gb, 7); + if (diff_uv_delta) { + if (dav1d_get_bit(gb)) + hdr->quant.vdc_delta = dav1d_get_sbits(gb, 7); + if (dav1d_get_bit(gb)) + hdr->quant.vac_delta = dav1d_get_sbits(gb, 7); + } else { + hdr->quant.vdc_delta = hdr->quant.udc_delta; + hdr->quant.vac_delta = hdr->quant.uac_delta; + } + } +#if DEBUG_FRAME_HDR + printf("HDR: post-quant: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + hdr->quant.qm = dav1d_get_bit(gb); + if (hdr->quant.qm) { + hdr->quant.qm_y = dav1d_get_bits(gb, 4); + hdr->quant.qm_u = dav1d_get_bits(gb, 4); + hdr->quant.qm_v = seqhdr->separate_uv_delta_q ? dav1d_get_bits(gb, 4) : + hdr->quant.qm_u; + } +#if DEBUG_FRAME_HDR + printf("HDR: post-qm: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + // segmentation data + hdr->segmentation.enabled = dav1d_get_bit(gb); + if (hdr->segmentation.enabled) { + if (hdr->primary_ref_frame == DAV1D_PRIMARY_REF_NONE) { + hdr->segmentation.update_map = 1; + hdr->segmentation.update_data = 1; + } else { + hdr->segmentation.update_map = dav1d_get_bit(gb); + if (hdr->segmentation.update_map) + hdr->segmentation.temporal = dav1d_get_bit(gb); + hdr->segmentation.update_data = dav1d_get_bit(gb); + } + + if (hdr->segmentation.update_data) { + hdr->segmentation.seg_data.last_active_segid = -1; + for (int i = 0; i < DAV1D_MAX_SEGMENTS; i++) { + Dav1dSegmentationData *const seg = + &hdr->segmentation.seg_data.d[i]; + if (dav1d_get_bit(gb)) { + seg->delta_q = dav1d_get_sbits(gb, 9); + hdr->segmentation.seg_data.last_active_segid = i; + } + if (dav1d_get_bit(gb)) { + seg->delta_lf_y_v = dav1d_get_sbits(gb, 7); + hdr->segmentation.seg_data.last_active_segid = i; + } + if (dav1d_get_bit(gb)) { + seg->delta_lf_y_h = dav1d_get_sbits(gb, 7); + hdr->segmentation.seg_data.last_active_segid = i; + } + if (dav1d_get_bit(gb)) { + seg->delta_lf_u = dav1d_get_sbits(gb, 7); + hdr->segmentation.seg_data.last_active_segid = i; + } + if (dav1d_get_bit(gb)) { + seg->delta_lf_v = dav1d_get_sbits(gb, 7); + hdr->segmentation.seg_data.last_active_segid = i; + } + if (dav1d_get_bit(gb)) { + seg->ref = dav1d_get_bits(gb, 3); + hdr->segmentation.seg_data.last_active_segid = i; + hdr->segmentation.seg_data.preskip = 1; + } else { + seg->ref = -1; + } + if ((seg->skip = dav1d_get_bit(gb))) { + hdr->segmentation.seg_data.last_active_segid = i; + hdr->segmentation.seg_data.preskip = 1; + } + if ((seg->globalmv = dav1d_get_bit(gb))) { + hdr->segmentation.seg_data.last_active_segid = i; + hdr->segmentation.seg_data.preskip = 1; + } + } + } else { + // segmentation.update_data was false so we should copy + // segmentation data from the reference frame. + assert(hdr->primary_ref_frame != DAV1D_PRIMARY_REF_NONE); + const int pri_ref = hdr->refidx[hdr->primary_ref_frame]; + if (!c->refs[pri_ref].p.p.frame_hdr) goto error; + hdr->segmentation.seg_data = + c->refs[pri_ref].p.p.frame_hdr->segmentation.seg_data; + } + } else { + for (int i = 0; i < DAV1D_MAX_SEGMENTS; i++) + hdr->segmentation.seg_data.d[i].ref = -1; + } +#if DEBUG_FRAME_HDR + printf("HDR: post-segmentation: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + // delta q + if (hdr->quant.yac) { + hdr->delta.q.present = dav1d_get_bit(gb); + if (hdr->delta.q.present) { + hdr->delta.q.res_log2 = dav1d_get_bits(gb, 2); + if (!hdr->allow_intrabc) { + hdr->delta.lf.present = dav1d_get_bit(gb); + if (hdr->delta.lf.present) { + hdr->delta.lf.res_log2 = dav1d_get_bits(gb, 2); + hdr->delta.lf.multi = dav1d_get_bit(gb); + } + } + } + } +#if DEBUG_FRAME_HDR + printf("HDR: post-delta_q_lf_flags: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + // derive lossless flags + const int delta_lossless = !hdr->quant.ydc_delta && !hdr->quant.udc_delta && + !hdr->quant.uac_delta && !hdr->quant.vdc_delta && !hdr->quant.vac_delta; + hdr->all_lossless = 1; + for (int i = 0; i < DAV1D_MAX_SEGMENTS; i++) { + hdr->segmentation.qidx[i] = hdr->segmentation.enabled ? + iclip_u8(hdr->quant.yac + hdr->segmentation.seg_data.d[i].delta_q) : + hdr->quant.yac; + hdr->segmentation.lossless[i] = + !hdr->segmentation.qidx[i] && delta_lossless; + hdr->all_lossless &= hdr->segmentation.lossless[i]; + } + + // loopfilter + if (hdr->all_lossless || hdr->allow_intrabc) { + hdr->loopfilter.mode_ref_delta_enabled = 1; + hdr->loopfilter.mode_ref_delta_update = 1; + hdr->loopfilter.mode_ref_deltas = default_mode_ref_deltas; + } else { + hdr->loopfilter.level_y[0] = dav1d_get_bits(gb, 6); + hdr->loopfilter.level_y[1] = dav1d_get_bits(gb, 6); + if (!seqhdr->monochrome && + (hdr->loopfilter.level_y[0] || hdr->loopfilter.level_y[1])) + { + hdr->loopfilter.level_u = dav1d_get_bits(gb, 6); + hdr->loopfilter.level_v = dav1d_get_bits(gb, 6); + } + hdr->loopfilter.sharpness = dav1d_get_bits(gb, 3); + + if (hdr->primary_ref_frame == DAV1D_PRIMARY_REF_NONE) { + hdr->loopfilter.mode_ref_deltas = default_mode_ref_deltas; + } else { + const int ref = hdr->refidx[hdr->primary_ref_frame]; + if (!c->refs[ref].p.p.frame_hdr) goto error; + hdr->loopfilter.mode_ref_deltas = + c->refs[ref].p.p.frame_hdr->loopfilter.mode_ref_deltas; + } + hdr->loopfilter.mode_ref_delta_enabled = dav1d_get_bit(gb); + if (hdr->loopfilter.mode_ref_delta_enabled) { + hdr->loopfilter.mode_ref_delta_update = dav1d_get_bit(gb); + if (hdr->loopfilter.mode_ref_delta_update) { + for (int i = 0; i < 8; i++) + if (dav1d_get_bit(gb)) + hdr->loopfilter.mode_ref_deltas.ref_delta[i] = + dav1d_get_sbits(gb, 7); + for (int i = 0; i < 2; i++) + if (dav1d_get_bit(gb)) + hdr->loopfilter.mode_ref_deltas.mode_delta[i] = + dav1d_get_sbits(gb, 7); + } + } + } +#if DEBUG_FRAME_HDR + printf("HDR: post-lpf: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + // cdef + if (!hdr->all_lossless && seqhdr->cdef && !hdr->allow_intrabc) { + hdr->cdef.damping = dav1d_get_bits(gb, 2) + 3; + hdr->cdef.n_bits = dav1d_get_bits(gb, 2); + for (int i = 0; i < (1 << hdr->cdef.n_bits); i++) { + hdr->cdef.y_strength[i] = dav1d_get_bits(gb, 6); + if (!seqhdr->monochrome) + hdr->cdef.uv_strength[i] = dav1d_get_bits(gb, 6); + } + } +#if DEBUG_FRAME_HDR + printf("HDR: post-cdef: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + // restoration + if ((!hdr->all_lossless || hdr->super_res.enabled) && + seqhdr->restoration && !hdr->allow_intrabc) + { + hdr->restoration.type[0] = dav1d_get_bits(gb, 2); + if (!seqhdr->monochrome) { + hdr->restoration.type[1] = dav1d_get_bits(gb, 2); + hdr->restoration.type[2] = dav1d_get_bits(gb, 2); + } + + if (hdr->restoration.type[0] || hdr->restoration.type[1] || + hdr->restoration.type[2]) + { + // Log2 of the restoration unit size. + hdr->restoration.unit_size[0] = 6 + seqhdr->sb128; + if (dav1d_get_bit(gb)) { + hdr->restoration.unit_size[0]++; + if (!seqhdr->sb128) + hdr->restoration.unit_size[0] += dav1d_get_bit(gb); + } + hdr->restoration.unit_size[1] = hdr->restoration.unit_size[0]; + if ((hdr->restoration.type[1] || hdr->restoration.type[2]) && + seqhdr->ss_hor == 1 && seqhdr->ss_ver == 1) + { + hdr->restoration.unit_size[1] -= dav1d_get_bit(gb); + } + } else { + hdr->restoration.unit_size[0] = 8; + } + } +#if DEBUG_FRAME_HDR + printf("HDR: post-restoration: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + if (!hdr->all_lossless) + hdr->txfm_mode = dav1d_get_bit(gb) ? DAV1D_TX_SWITCHABLE : DAV1D_TX_LARGEST; +#if DEBUG_FRAME_HDR + printf("HDR: post-txfmmode: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + if (IS_INTER_OR_SWITCH(hdr)) + hdr->switchable_comp_refs = dav1d_get_bit(gb); +#if DEBUG_FRAME_HDR + printf("HDR: post-refmode: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + if (hdr->switchable_comp_refs && IS_INTER_OR_SWITCH(hdr) && seqhdr->order_hint) { + const int poc = hdr->frame_offset; + int off_before = -1, off_after = -1; + int off_before_idx, off_after_idx; + for (int i = 0; i < 7; i++) { + if (!c->refs[hdr->refidx[i]].p.p.frame_hdr) goto error; + const int refpoc = c->refs[hdr->refidx[i]].p.p.frame_hdr->frame_offset; + + const int diff = get_poc_diff(seqhdr->order_hint_n_bits, refpoc, poc); + if (diff > 0) { + if (off_after < 0 || get_poc_diff(seqhdr->order_hint_n_bits, + off_after, refpoc) > 0) + { + off_after = refpoc; + off_after_idx = i; + } + } else if (diff < 0 && (off_before < 0 || + get_poc_diff(seqhdr->order_hint_n_bits, + refpoc, off_before) > 0)) + { + off_before = refpoc; + off_before_idx = i; + } + } + + if ((off_before | off_after) >= 0) { + hdr->skip_mode_refs[0] = imin(off_before_idx, off_after_idx); + hdr->skip_mode_refs[1] = imax(off_before_idx, off_after_idx); + hdr->skip_mode_allowed = 1; + } else if (off_before >= 0) { + int off_before2 = -1; + int off_before2_idx; + for (int i = 0; i < 7; i++) { + if (!c->refs[hdr->refidx[i]].p.p.frame_hdr) goto error; + const int refpoc = c->refs[hdr->refidx[i]].p.p.frame_hdr->frame_offset; + if (get_poc_diff(seqhdr->order_hint_n_bits, + refpoc, off_before) < 0) { + if (off_before2 < 0 || get_poc_diff(seqhdr->order_hint_n_bits, + refpoc, off_before2) > 0) + { + off_before2 = refpoc; + off_before2_idx = i; + } + } + } + + if (off_before2 >= 0) { + hdr->skip_mode_refs[0] = imin(off_before_idx, off_before2_idx); + hdr->skip_mode_refs[1] = imax(off_before_idx, off_before2_idx); + hdr->skip_mode_allowed = 1; + } + } + } + if (hdr->skip_mode_allowed) + hdr->skip_mode_enabled = dav1d_get_bit(gb); +#if DEBUG_FRAME_HDR + printf("HDR: post-extskip: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + if (!hdr->error_resilient_mode && IS_INTER_OR_SWITCH(hdr) && seqhdr->warped_motion) + hdr->warp_motion = dav1d_get_bit(gb); +#if DEBUG_FRAME_HDR + printf("HDR: post-warpmotionbit: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + hdr->reduced_txtp_set = dav1d_get_bit(gb); +#if DEBUG_FRAME_HDR + printf("HDR: post-reducedtxtpset: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + for (int i = 0; i < 7; i++) + hdr->gmv[i] = dav1d_default_wm_params; + + if (IS_INTER_OR_SWITCH(hdr)) { + for (int i = 0; i < 7; i++) { + hdr->gmv[i].type = !dav1d_get_bit(gb) ? DAV1D_WM_TYPE_IDENTITY : + dav1d_get_bit(gb) ? DAV1D_WM_TYPE_ROT_ZOOM : + dav1d_get_bit(gb) ? DAV1D_WM_TYPE_TRANSLATION : + DAV1D_WM_TYPE_AFFINE; + + if (hdr->gmv[i].type == DAV1D_WM_TYPE_IDENTITY) continue; + + const Dav1dWarpedMotionParams *ref_gmv; + if (hdr->primary_ref_frame == DAV1D_PRIMARY_REF_NONE) { + ref_gmv = &dav1d_default_wm_params; + } else { + const int pri_ref = hdr->refidx[hdr->primary_ref_frame]; + if (!c->refs[pri_ref].p.p.frame_hdr) goto error; + ref_gmv = &c->refs[pri_ref].p.p.frame_hdr->gmv[i]; + } + int32_t *const mat = hdr->gmv[i].matrix; + const int32_t *const ref_mat = ref_gmv->matrix; + int bits, shift; + + if (hdr->gmv[i].type >= DAV1D_WM_TYPE_ROT_ZOOM) { + mat[2] = (1 << 16) + 2 * + dav1d_get_bits_subexp(gb, (ref_mat[2] - (1 << 16)) >> 1, 12); + mat[3] = 2 * dav1d_get_bits_subexp(gb, ref_mat[3] >> 1, 12); + + bits = 12; + shift = 10; + } else { + bits = 9 - !hdr->hp; + shift = 13 + !hdr->hp; + } + + if (hdr->gmv[i].type == DAV1D_WM_TYPE_AFFINE) { + mat[4] = 2 * dav1d_get_bits_subexp(gb, ref_mat[4] >> 1, 12); + mat[5] = (1 << 16) + 2 * + dav1d_get_bits_subexp(gb, (ref_mat[5] - (1 << 16)) >> 1, 12); + } else { + mat[4] = -mat[3]; + mat[5] = mat[2]; + } + + mat[0] = dav1d_get_bits_subexp(gb, ref_mat[0] >> shift, bits) * (1 << shift); + mat[1] = dav1d_get_bits_subexp(gb, ref_mat[1] >> shift, bits) * (1 << shift); + } + } +#if DEBUG_FRAME_HDR + printf("HDR: post-gmv: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + if (seqhdr->film_grain_present && (hdr->show_frame || hdr->showable_frame)) { + hdr->film_grain.present = dav1d_get_bit(gb); + if (hdr->film_grain.present) { + const unsigned seed = dav1d_get_bits(gb, 16); + hdr->film_grain.update = hdr->frame_type != DAV1D_FRAME_TYPE_INTER || dav1d_get_bit(gb); + if (!hdr->film_grain.update) { + const int refidx = dav1d_get_bits(gb, 3); + int i; + for (i = 0; i < 7; i++) + if (hdr->refidx[i] == refidx) + break; + if (i == 7 || !c->refs[refidx].p.p.frame_hdr) goto error; + hdr->film_grain.data = c->refs[refidx].p.p.frame_hdr->film_grain.data; + hdr->film_grain.data.seed = seed; + } else { + Dav1dFilmGrainData *const fgd = &hdr->film_grain.data; + fgd->seed = seed; + + fgd->num_y_points = dav1d_get_bits(gb, 4); + if (fgd->num_y_points > 14) goto error; + for (int i = 0; i < fgd->num_y_points; i++) { + fgd->y_points[i][0] = dav1d_get_bits(gb, 8); + if (i && fgd->y_points[i - 1][0] >= fgd->y_points[i][0]) + goto error; + fgd->y_points[i][1] = dav1d_get_bits(gb, 8); + } + + if (!seqhdr->monochrome) + fgd->chroma_scaling_from_luma = dav1d_get_bit(gb); + if (seqhdr->monochrome || fgd->chroma_scaling_from_luma || + (seqhdr->ss_ver == 1 && seqhdr->ss_hor == 1 && !fgd->num_y_points)) + { + fgd->num_uv_points[0] = fgd->num_uv_points[1] = 0; + } else for (int pl = 0; pl < 2; pl++) { + fgd->num_uv_points[pl] = dav1d_get_bits(gb, 4); + if (fgd->num_uv_points[pl] > 10) goto error; + for (int i = 0; i < fgd->num_uv_points[pl]; i++) { + fgd->uv_points[pl][i][0] = dav1d_get_bits(gb, 8); + if (i && fgd->uv_points[pl][i - 1][0] >= fgd->uv_points[pl][i][0]) + goto error; + fgd->uv_points[pl][i][1] = dav1d_get_bits(gb, 8); + } + } + + if (seqhdr->ss_hor == 1 && seqhdr->ss_ver == 1 && + !!fgd->num_uv_points[0] != !!fgd->num_uv_points[1]) + { + goto error; + } + + fgd->scaling_shift = dav1d_get_bits(gb, 2) + 8; + fgd->ar_coeff_lag = dav1d_get_bits(gb, 2); + const int num_y_pos = 2 * fgd->ar_coeff_lag * (fgd->ar_coeff_lag + 1); + if (fgd->num_y_points) + for (int i = 0; i < num_y_pos; i++) + fgd->ar_coeffs_y[i] = dav1d_get_bits(gb, 8) - 128; + for (int pl = 0; pl < 2; pl++) + if (fgd->num_uv_points[pl] || fgd->chroma_scaling_from_luma) { + const int num_uv_pos = num_y_pos + !!fgd->num_y_points; + for (int i = 0; i < num_uv_pos; i++) + fgd->ar_coeffs_uv[pl][i] = dav1d_get_bits(gb, 8) - 128; + if (!fgd->num_y_points) + fgd->ar_coeffs_uv[pl][num_uv_pos] = 0; + } + fgd->ar_coeff_shift = dav1d_get_bits(gb, 2) + 6; + fgd->grain_scale_shift = dav1d_get_bits(gb, 2); + for (int pl = 0; pl < 2; pl++) + if (fgd->num_uv_points[pl]) { + fgd->uv_mult[pl] = dav1d_get_bits(gb, 8) - 128; + fgd->uv_luma_mult[pl] = dav1d_get_bits(gb, 8) - 128; + fgd->uv_offset[pl] = dav1d_get_bits(gb, 9) - 256; + } + fgd->overlap_flag = dav1d_get_bit(gb); + fgd->clip_to_restricted_range = dav1d_get_bit(gb); + } + } + } +#if DEBUG_FRAME_HDR + printf("HDR: post-filmgrain: off=%td\n", + (gb->ptr - init_ptr) * 8 - gb->bits_left); +#endif + + return 0; + +error: + dav1d_log(c, "Error parsing frame header\n"); + return DAV1D_ERR(EINVAL); +} + +static void parse_tile_hdr(Dav1dContext *const c, GetBits *const gb) { + const int n_tiles = c->frame_hdr->tiling.cols * c->frame_hdr->tiling.rows; + const int have_tile_pos = n_tiles > 1 ? dav1d_get_bit(gb) : 0; + + if (have_tile_pos) { + const int n_bits = c->frame_hdr->tiling.log2_cols + + c->frame_hdr->tiling.log2_rows; + c->tile[c->n_tile_data].start = dav1d_get_bits(gb, n_bits); + c->tile[c->n_tile_data].end = dav1d_get_bits(gb, n_bits); + } else { + c->tile[c->n_tile_data].start = 0; + c->tile[c->n_tile_data].end = n_tiles - 1; + } +} + +ptrdiff_t dav1d_parse_obus(Dav1dContext *const c, Dav1dData *const in) { + GetBits gb; + int res; + + dav1d_init_get_bits(&gb, in->data, in->sz); + + // obu header + const int obu_forbidden_bit = dav1d_get_bit(&gb); + if (c->strict_std_compliance && obu_forbidden_bit) goto error; + const enum Dav1dObuType type = dav1d_get_bits(&gb, 4); + const int has_extension = dav1d_get_bit(&gb); + const int has_length_field = dav1d_get_bit(&gb); + dav1d_get_bit(&gb); // reserved + + int temporal_id = 0, spatial_id = 0; + if (has_extension) { + temporal_id = dav1d_get_bits(&gb, 3); + spatial_id = dav1d_get_bits(&gb, 2); + dav1d_get_bits(&gb, 3); // reserved + } + + if (has_length_field) { + const size_t len = dav1d_get_uleb128(&gb); + if (len > (size_t)(gb.ptr_end - gb.ptr)) goto error; + gb.ptr_end = gb.ptr + len; + } + if (gb.error) goto error; + + // We must have read a whole number of bytes at this point (1 byte + // for the header and whole bytes at a time when reading the + // leb128 length field). + assert(gb.bits_left == 0); + + // skip obu not belonging to the selected temporal/spatial layer + if (type != DAV1D_OBU_SEQ_HDR && type != DAV1D_OBU_TD && + has_extension && c->operating_point_idc != 0) + { + const int in_temporal_layer = (c->operating_point_idc >> temporal_id) & 1; + const int in_spatial_layer = (c->operating_point_idc >> (spatial_id + 8)) & 1; + if (!in_temporal_layer || !in_spatial_layer) + return gb.ptr_end - gb.ptr_start; + } + + switch (type) { + case DAV1D_OBU_SEQ_HDR: { + Dav1dRef *ref = dav1d_ref_create_using_pool(c->seq_hdr_pool, + sizeof(Dav1dSequenceHeader)); + if (!ref) return DAV1D_ERR(ENOMEM); + Dav1dSequenceHeader *seq_hdr = ref->data; + if ((res = parse_seq_hdr(seq_hdr, &gb, c->strict_std_compliance)) < 0) { + dav1d_log(c, "Error parsing sequence header\n"); + dav1d_ref_dec(&ref); + goto error; + } + + const int op_idx = + c->operating_point < seq_hdr->num_operating_points ? c->operating_point : 0; + c->operating_point_idc = seq_hdr->operating_points[op_idx].idc; + const unsigned spatial_mask = c->operating_point_idc >> 8; + c->max_spatial_id = spatial_mask ? ulog2(spatial_mask) : 0; + + // If we have read a sequence header which is different from + // the old one, this is a new video sequence and can't use any + // previous state. Free that state. + + if (!c->seq_hdr) { + c->frame_hdr = NULL; + c->frame_flags |= PICTURE_FLAG_NEW_SEQUENCE; + // see 7.5, operating_parameter_info is allowed to change in + // sequence headers of a single sequence + } else if (memcmp(seq_hdr, c->seq_hdr, offsetof(Dav1dSequenceHeader, operating_parameter_info))) { + c->frame_hdr = NULL; + c->mastering_display = NULL; + c->content_light = NULL; + dav1d_ref_dec(&c->mastering_display_ref); + dav1d_ref_dec(&c->content_light_ref); + for (int i = 0; i < 8; i++) { + if (c->refs[i].p.p.frame_hdr) + dav1d_thread_picture_unref(&c->refs[i].p); + dav1d_ref_dec(&c->refs[i].segmap); + dav1d_ref_dec(&c->refs[i].refmvs); + dav1d_cdf_thread_unref(&c->cdf[i]); + } + c->frame_flags |= PICTURE_FLAG_NEW_SEQUENCE; + // If operating_parameter_info changed, signal it + } else if (memcmp(seq_hdr->operating_parameter_info, c->seq_hdr->operating_parameter_info, + sizeof(seq_hdr->operating_parameter_info))) + { + c->frame_flags |= PICTURE_FLAG_NEW_OP_PARAMS_INFO; + } + dav1d_ref_dec(&c->seq_hdr_ref); + c->seq_hdr_ref = ref; + c->seq_hdr = seq_hdr; + break; + } + case DAV1D_OBU_REDUNDANT_FRAME_HDR: + if (c->frame_hdr) break; + // fall-through + case DAV1D_OBU_FRAME: + case DAV1D_OBU_FRAME_HDR: + if (!c->seq_hdr) goto error; + if (!c->frame_hdr_ref) { + c->frame_hdr_ref = dav1d_ref_create_using_pool(c->frame_hdr_pool, + sizeof(Dav1dFrameHeader)); + if (!c->frame_hdr_ref) return DAV1D_ERR(ENOMEM); + } +#ifndef NDEBUG + // ensure that the reference is writable + assert(dav1d_ref_is_writable(c->frame_hdr_ref)); +#endif + c->frame_hdr = c->frame_hdr_ref->data; + memset(c->frame_hdr, 0, sizeof(*c->frame_hdr)); + c->frame_hdr->temporal_id = temporal_id; + c->frame_hdr->spatial_id = spatial_id; + if ((res = parse_frame_hdr(c, &gb)) < 0) { + c->frame_hdr = NULL; + goto error; + } + for (int n = 0; n < c->n_tile_data; n++) + dav1d_data_unref_internal(&c->tile[n].data); + c->n_tile_data = 0; + c->n_tiles = 0; + if (type != DAV1D_OBU_FRAME) { + // This is actually a frame header OBU so read the + // trailing bit and check for overrun. + if (check_trailing_bits(&gb, c->strict_std_compliance) < 0) { + c->frame_hdr = NULL; + goto error; + } + } + + if (c->frame_size_limit && (int64_t)c->frame_hdr->width[1] * + c->frame_hdr->height > c->frame_size_limit) + { + dav1d_log(c, "Frame size %dx%d exceeds limit %u\n", c->frame_hdr->width[1], + c->frame_hdr->height, c->frame_size_limit); + c->frame_hdr = NULL; + return DAV1D_ERR(ERANGE); + } + + if (type != DAV1D_OBU_FRAME) + break; + // OBU_FRAMEs shouldn't be signaled with show_existing_frame + if (c->frame_hdr->show_existing_frame) { + c->frame_hdr = NULL; + goto error; + } + + // This is the frame header at the start of a frame OBU. + // There's no trailing bit at the end to skip, but we do need + // to align to the next byte. + dav1d_bytealign_get_bits(&gb); + // fall-through + case DAV1D_OBU_TILE_GRP: { + if (!c->frame_hdr) goto error; + if (c->n_tile_data_alloc < c->n_tile_data + 1) { + if ((c->n_tile_data + 1) > INT_MAX / (int)sizeof(*c->tile)) goto error; + struct Dav1dTileGroup *tile = dav1d_realloc(ALLOC_TILE, c->tile, + (c->n_tile_data + 1) * sizeof(*c->tile)); + if (!tile) goto error; + c->tile = tile; + memset(c->tile + c->n_tile_data, 0, sizeof(*c->tile)); + c->n_tile_data_alloc = c->n_tile_data + 1; + } + parse_tile_hdr(c, &gb); + // Align to the next byte boundary and check for overrun. + dav1d_bytealign_get_bits(&gb); + if (gb.error) goto error; + + dav1d_data_ref(&c->tile[c->n_tile_data].data, in); + c->tile[c->n_tile_data].data.data = gb.ptr; + c->tile[c->n_tile_data].data.sz = (size_t)(gb.ptr_end - gb.ptr); + // ensure tile groups are in order and sane, see 6.10.1 + if (c->tile[c->n_tile_data].start > c->tile[c->n_tile_data].end || + c->tile[c->n_tile_data].start != c->n_tiles) + { + for (int i = 0; i <= c->n_tile_data; i++) + dav1d_data_unref_internal(&c->tile[i].data); + c->n_tile_data = 0; + c->n_tiles = 0; + goto error; + } + c->n_tiles += 1 + c->tile[c->n_tile_data].end - + c->tile[c->n_tile_data].start; + c->n_tile_data++; + break; + } + case DAV1D_OBU_METADATA: { +#define DEBUG_OBU_METADATA 0 +#if DEBUG_OBU_METADATA + const uint8_t *const init_ptr = gb.ptr; +#endif + // obu metadta type field + const enum ObuMetaType meta_type = dav1d_get_uleb128(&gb); + if (gb.error) goto error; + + switch (meta_type) { + case OBU_META_HDR_CLL: { + Dav1dRef *ref = dav1d_ref_create(ALLOC_OBU_META, + sizeof(Dav1dContentLightLevel)); + if (!ref) return DAV1D_ERR(ENOMEM); + Dav1dContentLightLevel *const content_light = ref->data; + + content_light->max_content_light_level = dav1d_get_bits(&gb, 16); +#if DEBUG_OBU_METADATA + printf("CLLOBU: max-content-light-level: %d [off=%td]\n", + content_light->max_content_light_level, + (gb.ptr - init_ptr) * 8 - gb.bits_left); +#endif + content_light->max_frame_average_light_level = dav1d_get_bits(&gb, 16); +#if DEBUG_OBU_METADATA + printf("CLLOBU: max-frame-average-light-level: %d [off=%td]\n", + content_light->max_frame_average_light_level, + (gb.ptr - init_ptr) * 8 - gb.bits_left); +#endif + + if (check_trailing_bits(&gb, c->strict_std_compliance) < 0) { + dav1d_ref_dec(&ref); + goto error; + } + + dav1d_ref_dec(&c->content_light_ref); + c->content_light = content_light; + c->content_light_ref = ref; + break; + } + case OBU_META_HDR_MDCV: { + Dav1dRef *ref = dav1d_ref_create(ALLOC_OBU_META, + sizeof(Dav1dMasteringDisplay)); + if (!ref) return DAV1D_ERR(ENOMEM); + Dav1dMasteringDisplay *const mastering_display = ref->data; + + for (int i = 0; i < 3; i++) { + mastering_display->primaries[i][0] = dav1d_get_bits(&gb, 16); + mastering_display->primaries[i][1] = dav1d_get_bits(&gb, 16); +#if DEBUG_OBU_METADATA + printf("MDCVOBU: primaries[%d]: (%d, %d) [off=%td]\n", i, + mastering_display->primaries[i][0], + mastering_display->primaries[i][1], + (gb.ptr - init_ptr) * 8 - gb.bits_left); +#endif + } + mastering_display->white_point[0] = dav1d_get_bits(&gb, 16); +#if DEBUG_OBU_METADATA + printf("MDCVOBU: white-point-x: %d [off=%td]\n", + mastering_display->white_point[0], + (gb.ptr - init_ptr) * 8 - gb.bits_left); +#endif + mastering_display->white_point[1] = dav1d_get_bits(&gb, 16); +#if DEBUG_OBU_METADATA + printf("MDCVOBU: white-point-y: %d [off=%td]\n", + mastering_display->white_point[1], + (gb.ptr - init_ptr) * 8 - gb.bits_left); +#endif + mastering_display->max_luminance = dav1d_get_bits(&gb, 32); +#if DEBUG_OBU_METADATA + printf("MDCVOBU: max-luminance: %d [off=%td]\n", + mastering_display->max_luminance, + (gb.ptr - init_ptr) * 8 - gb.bits_left); +#endif + mastering_display->min_luminance = dav1d_get_bits(&gb, 32); +#if DEBUG_OBU_METADATA + printf("MDCVOBU: min-luminance: %d [off=%td]\n", + mastering_display->min_luminance, + (gb.ptr - init_ptr) * 8 - gb.bits_left); +#endif + if (check_trailing_bits(&gb, c->strict_std_compliance) < 0) { + dav1d_ref_dec(&ref); + goto error; + } + + dav1d_ref_dec(&c->mastering_display_ref); + c->mastering_display = mastering_display; + c->mastering_display_ref = ref; + break; + } + case OBU_META_ITUT_T35: { + ptrdiff_t payload_size = gb.ptr_end - gb.ptr; + // Don't take into account all the trailing bits for payload_size + while (payload_size > 0 && !gb.ptr[payload_size - 1]) + payload_size--; // trailing_zero_bit x 8 + payload_size--; // trailing_one_bit + trailing_zero_bit x 7 + + int country_code_extension_byte = 0; + const int country_code = dav1d_get_bits(&gb, 8); + payload_size--; + if (country_code == 0xFF) { + country_code_extension_byte = dav1d_get_bits(&gb, 8); + payload_size--; + } + + if (payload_size <= 0 || gb.ptr[payload_size] != 0x80) { + dav1d_log(c, "Malformed ITU-T T.35 metadata message format\n"); + break; + } + + if ((c->n_itut_t35 + 1) > INT_MAX / (int)sizeof(*c->itut_t35)) goto error; + struct Dav1dITUTT35 *itut_t35 = dav1d_realloc(ALLOC_OBU_META, c->itut_t35, + (c->n_itut_t35 + 1) * sizeof(*c->itut_t35)); + if (!itut_t35) goto error; + c->itut_t35 = itut_t35; + memset(c->itut_t35 + c->n_itut_t35, 0, sizeof(*c->itut_t35)); + + struct itut_t35_ctx_context *itut_t35_ctx; + if (!c->n_itut_t35) { + assert(!c->itut_t35_ref); + itut_t35_ctx = dav1d_malloc(ALLOC_OBU_META, sizeof(struct itut_t35_ctx_context)); + if (!itut_t35_ctx) goto error; + c->itut_t35_ref = dav1d_ref_init(&itut_t35_ctx->ref, c->itut_t35, + dav1d_picture_free_itut_t35, itut_t35_ctx, 0); + } else { + assert(c->itut_t35_ref && atomic_load(&c->itut_t35_ref->ref_cnt) == 1); + itut_t35_ctx = c->itut_t35_ref->user_data; + c->itut_t35_ref->const_data = (uint8_t *)c->itut_t35; + } + itut_t35_ctx->itut_t35 = c->itut_t35; + itut_t35_ctx->n_itut_t35 = c->n_itut_t35 + 1; + + Dav1dITUTT35 *const itut_t35_metadata = &c->itut_t35[c->n_itut_t35]; + itut_t35_metadata->payload = dav1d_malloc(ALLOC_OBU_META, payload_size); + if (!itut_t35_metadata->payload) goto error; + + itut_t35_metadata->country_code = country_code; + itut_t35_metadata->country_code_extension_byte = country_code_extension_byte; + itut_t35_metadata->payload_size = payload_size; + + // We know that we've read a whole number of bytes and that the + // payload is within the OBU boundaries, so just use memcpy() + assert(gb.bits_left == 0); + memcpy(itut_t35_metadata->payload, gb.ptr, payload_size); + + c->n_itut_t35++; + break; + } + case OBU_META_SCALABILITY: + case OBU_META_TIMECODE: + // ignore metadata OBUs we don't care about + break; + default: + // print a warning but don't fail for unknown types + if (meta_type > 31) // Types 6 to 31 are "Unregistered user private", so ignore them. + dav1d_log(c, "Unknown Metadata OBU type %d\n", meta_type); + break; + } + + break; + } + case DAV1D_OBU_TD: + c->frame_flags |= PICTURE_FLAG_NEW_TEMPORAL_UNIT; + break; + case DAV1D_OBU_PADDING: + // ignore OBUs we don't care about + break; + default: + // print a warning but don't fail for unknown types + dav1d_log(c, "Unknown OBU type %d of size %td\n", type, gb.ptr_end - gb.ptr); + break; + } + + if (c->seq_hdr && c->frame_hdr) { + if (c->frame_hdr->show_existing_frame) { + if (!c->refs[c->frame_hdr->existing_frame_idx].p.p.frame_hdr) goto error; + switch (c->refs[c->frame_hdr->existing_frame_idx].p.p.frame_hdr->frame_type) { + case DAV1D_FRAME_TYPE_INTER: + case DAV1D_FRAME_TYPE_SWITCH: + if (c->decode_frame_type > DAV1D_DECODEFRAMETYPE_REFERENCE) + goto skip; + break; + case DAV1D_FRAME_TYPE_INTRA: + if (c->decode_frame_type > DAV1D_DECODEFRAMETYPE_INTRA) + goto skip; + // fall-through + default: + break; + } + if (!c->refs[c->frame_hdr->existing_frame_idx].p.p.data[0]) goto error; + if (c->strict_std_compliance && + !c->refs[c->frame_hdr->existing_frame_idx].p.showable) + { + goto error; + } + if (c->n_fc == 1) { + dav1d_thread_picture_ref(&c->out, + &c->refs[c->frame_hdr->existing_frame_idx].p); + dav1d_picture_copy_props(&c->out.p, + c->content_light, c->content_light_ref, + c->mastering_display, c->mastering_display_ref, + c->itut_t35, c->itut_t35_ref, c->n_itut_t35, + &in->m); + // Must be removed from the context after being attached to the frame + dav1d_ref_dec(&c->itut_t35_ref); + c->itut_t35 = NULL; + c->n_itut_t35 = 0; + c->event_flags |= dav1d_picture_get_event_flags(&c->refs[c->frame_hdr->existing_frame_idx].p); + } else { + pthread_mutex_lock(&c->task_thread.lock); + // need to append this to the frame output queue + const unsigned next = c->frame_thread.next++; + if (c->frame_thread.next == c->n_fc) + c->frame_thread.next = 0; + + Dav1dFrameContext *const f = &c->fc[next]; + while (f->n_tile_data > 0) + pthread_cond_wait(&f->task_thread.cond, + &f->task_thread.ttd->lock); + Dav1dThreadPicture *const out_delayed = + &c->frame_thread.out_delayed[next]; + if (out_delayed->p.data[0] || atomic_load(&f->task_thread.error)) { + unsigned first = atomic_load(&c->task_thread.first); + if (first + 1U < c->n_fc) + atomic_fetch_add(&c->task_thread.first, 1U); + else + atomic_store(&c->task_thread.first, 0); + atomic_compare_exchange_strong(&c->task_thread.reset_task_cur, + &first, UINT_MAX); + if (c->task_thread.cur && c->task_thread.cur < c->n_fc) + c->task_thread.cur--; + } + const int error = f->task_thread.retval; + if (error) { + c->cached_error = error; + f->task_thread.retval = 0; + dav1d_data_props_copy(&c->cached_error_props, &out_delayed->p.m); + dav1d_thread_picture_unref(out_delayed); + } else if (out_delayed->p.data[0]) { + const unsigned progress = atomic_load_explicit(&out_delayed->progress[1], + memory_order_relaxed); + if ((out_delayed->visible || c->output_invisible_frames) && + progress != FRAME_ERROR) + { + dav1d_thread_picture_ref(&c->out, out_delayed); + c->event_flags |= dav1d_picture_get_event_flags(out_delayed); + } + dav1d_thread_picture_unref(out_delayed); + } + dav1d_thread_picture_ref(out_delayed, + &c->refs[c->frame_hdr->existing_frame_idx].p); + out_delayed->visible = 1; + dav1d_picture_copy_props(&out_delayed->p, + c->content_light, c->content_light_ref, + c->mastering_display, c->mastering_display_ref, + c->itut_t35, c->itut_t35_ref, c->n_itut_t35, + &in->m); + // Must be removed from the context after being attached to the frame + dav1d_ref_dec(&c->itut_t35_ref); + c->itut_t35 = NULL; + c->n_itut_t35 = 0; + + pthread_mutex_unlock(&c->task_thread.lock); + } + if (c->refs[c->frame_hdr->existing_frame_idx].p.p.frame_hdr->frame_type == DAV1D_FRAME_TYPE_KEY) { + const int r = c->frame_hdr->existing_frame_idx; + c->refs[r].p.showable = 0; + for (int i = 0; i < 8; i++) { + if (i == r) continue; + + if (c->refs[i].p.p.frame_hdr) + dav1d_thread_picture_unref(&c->refs[i].p); + dav1d_thread_picture_ref(&c->refs[i].p, &c->refs[r].p); + + dav1d_cdf_thread_unref(&c->cdf[i]); + dav1d_cdf_thread_ref(&c->cdf[i], &c->cdf[r]); + + dav1d_ref_dec(&c->refs[i].segmap); + c->refs[i].segmap = c->refs[r].segmap; + if (c->refs[r].segmap) + dav1d_ref_inc(c->refs[r].segmap); + dav1d_ref_dec(&c->refs[i].refmvs); + } + } + c->frame_hdr = NULL; + } else if (c->n_tiles == c->frame_hdr->tiling.cols * c->frame_hdr->tiling.rows) { + switch (c->frame_hdr->frame_type) { + case DAV1D_FRAME_TYPE_INTER: + case DAV1D_FRAME_TYPE_SWITCH: + if (c->decode_frame_type > DAV1D_DECODEFRAMETYPE_REFERENCE || + (c->decode_frame_type == DAV1D_DECODEFRAMETYPE_REFERENCE && + !c->frame_hdr->refresh_frame_flags)) + goto skip; + break; + case DAV1D_FRAME_TYPE_INTRA: + if (c->decode_frame_type > DAV1D_DECODEFRAMETYPE_INTRA || + (c->decode_frame_type == DAV1D_DECODEFRAMETYPE_REFERENCE && + !c->frame_hdr->refresh_frame_flags)) + goto skip; + // fall-through + default: + break; + } + if (!c->n_tile_data) + goto error; + if ((res = dav1d_submit_frame(c)) < 0) + return res; + assert(!c->n_tile_data); + c->frame_hdr = NULL; + c->n_tiles = 0; + } + } + + return gb.ptr_end - gb.ptr_start; + +skip: + // update refs with only the headers in case we skip the frame + for (int i = 0; i < 8; i++) { + if (c->frame_hdr->refresh_frame_flags & (1 << i)) { + dav1d_thread_picture_unref(&c->refs[i].p); + c->refs[i].p.p.frame_hdr = c->frame_hdr; + c->refs[i].p.p.seq_hdr = c->seq_hdr; + c->refs[i].p.p.frame_hdr_ref = c->frame_hdr_ref; + c->refs[i].p.p.seq_hdr_ref = c->seq_hdr_ref; + dav1d_ref_inc(c->frame_hdr_ref); + dav1d_ref_inc(c->seq_hdr_ref); + } + } + + dav1d_ref_dec(&c->frame_hdr_ref); + c->frame_hdr = NULL; + c->n_tiles = 0; + + return gb.ptr_end - gb.ptr_start; + +error: + dav1d_data_props_copy(&c->cached_error_props, &in->m); + dav1d_log(c, gb.error ? "Overrun in OBU bit buffer\n" : + "Error parsing OBU data\n"); + return DAV1D_ERR(EINVAL); +} diff --git a/3rdparty/dav1d/src/obu.h b/3rdparty/dav1d/src/obu.h new file mode 100644 index 0000000..22901f0 --- /dev/null +++ b/3rdparty/dav1d/src/obu.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_OBU_H +#define DAV1D_SRC_OBU_H + +#include "dav1d/data.h" +#include "src/internal.h" + +ptrdiff_t dav1d_parse_obus(Dav1dContext *c, Dav1dData *in); + +#endif /* DAV1D_SRC_OBU_H */ diff --git a/3rdparty/dav1d/src/pal.c b/3rdparty/dav1d/src/pal.c new file mode 100644 index 0000000..8e2cdec --- /dev/null +++ b/3rdparty/dav1d/src/pal.c @@ -0,0 +1,81 @@ +/* + * Copyright © 2023, VideoLAN and dav1d authors + * Copyright © 2023, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/attributes.h" + +#include "src/pal.h" + +// fill invisible edges and pack to 4-bit (2 pixels per byte) +static void pal_idx_finish_c(uint8_t *dst, const uint8_t *src, + const int bw, const int bh, + const int w, const int h) +{ + assert(bw >= 4 && bw <= 64 && !(bw & (bw - 1))); + assert(bh >= 4 && bh <= 64 && !(bh & (bh - 1))); + assert(w >= 4 && w <= bw && !(w & 3)); + assert(h >= 4 && h <= bh && !(h & 3)); + + const int dst_w = w / 2; + const int dst_bw = bw / 2; + + for (int y = 0; y < h; y++, src += bw, dst += dst_bw) { + for (int x = 0; x < dst_w; x++) + dst[x] = src[x * 2 + 0] | (src[x * 2 + 1] << 4); + if (dst_w < dst_bw) + memset(dst + dst_w, src[w - 1] * 0x11, dst_bw - dst_w); + } + + if (h < bh) { + const uint8_t *const last_row = &dst[-dst_bw]; + for (int y = h; y < bh; y++, dst += dst_bw) + memcpy(dst, last_row, dst_bw); + } +} + +#if HAVE_ASM +#if ARCH_RISCV +#include "riscv/pal.h" +#elif ARCH_X86 +#include "x86/pal.h" +#endif +#endif + +COLD void dav1d_pal_dsp_init(Dav1dPalDSPContext *const c) { + c->pal_idx_finish = pal_idx_finish_c; + +#if HAVE_ASM +#if ARCH_RISCV + pal_dsp_init_riscv(c); +#elif ARCH_X86 + pal_dsp_init_x86(c); +#endif +#endif +} diff --git a/3rdparty/dav1d/src/pal.h b/3rdparty/dav1d/src/pal.h new file mode 100644 index 0000000..6a6d729 --- /dev/null +++ b/3rdparty/dav1d/src/pal.h @@ -0,0 +1,43 @@ +/* + * Copyright © 2023, VideoLAN and dav1d authors + * Copyright © 2023, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_PAL_H +#define DAV1D_SRC_PAL_H + +#include + +#define decl_pal_idx_finish_fn(name) \ +void (name)(uint8_t *dst, const uint8_t *src, int bw, int bh, int w, int h) +typedef decl_pal_idx_finish_fn(*pal_idx_finish_fn); + +typedef struct Dav1dPalDSPContext { + pal_idx_finish_fn pal_idx_finish; +} Dav1dPalDSPContext; + +void dav1d_pal_dsp_init(Dav1dPalDSPContext *dsp); + +#endif /* DAV1D_SRC_PAL_H */ diff --git a/3rdparty/dav1d/src/picture.c b/3rdparty/dav1d/src/picture.c new file mode 100644 index 0000000..68f4569 --- /dev/null +++ b/3rdparty/dav1d/src/picture.c @@ -0,0 +1,333 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +#include "common/intops.h" +#include "common/validate.h" + +#include "src/internal.h" +#include "src/log.h" +#include "src/picture.h" +#include "src/ref.h" +#include "src/thread.h" +#include "src/thread_task.h" + +int dav1d_default_picture_alloc(Dav1dPicture *const p, void *const cookie) { + const int hbd = p->p.bpc > 8; + const int aligned_w = (p->p.w + 127) & ~127; + const int aligned_h = (p->p.h + 127) & ~127; + const int has_chroma = p->p.layout != DAV1D_PIXEL_LAYOUT_I400; + const int ss_ver = p->p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = p->p.layout != DAV1D_PIXEL_LAYOUT_I444; + ptrdiff_t y_stride = aligned_w << hbd; + ptrdiff_t uv_stride = has_chroma ? y_stride >> ss_hor : 0; + /* Due to how mapping of addresses to sets works in most L1 and L2 cache + * implementations, strides of multiples of certain power-of-two numbers + * may cause multiple rows of the same superblock to map to the same set, + * causing evictions of previous rows resulting in a reduction in cache + * hit rate. Avoid that by slightly padding the stride when necessary. */ + if (!(y_stride & 1023)) + y_stride += DAV1D_PICTURE_ALIGNMENT; + if (!(uv_stride & 1023) && has_chroma) + uv_stride += DAV1D_PICTURE_ALIGNMENT; + p->stride[0] = y_stride; + p->stride[1] = uv_stride; + const size_t y_sz = y_stride * aligned_h; + const size_t uv_sz = uv_stride * (aligned_h >> ss_ver); + const size_t pic_size = y_sz + 2 * uv_sz; + + uint8_t *const buf = dav1d_mem_pool_pop(cookie, pic_size + DAV1D_PICTURE_ALIGNMENT); + if (!buf) return DAV1D_ERR(ENOMEM); + p->allocator_data = buf; + p->data[0] = buf; + p->data[1] = has_chroma ? buf + y_sz : NULL; + p->data[2] = has_chroma ? buf + y_sz + uv_sz : NULL; + + return 0; +} + +void dav1d_default_picture_release(Dav1dPicture *const p, void *const cookie) { + dav1d_mem_pool_push(cookie, p->allocator_data); +} + +struct pic_ctx_context { + Dav1dPicAllocator allocator; + Dav1dPicture pic; + Dav1dRef ref; + void *extra_data[]; +}; + +static void free_buffer(const uint8_t *const data, void *const user_data) { + struct pic_ctx_context *pic_ctx = (struct pic_ctx_context*)data; + + pic_ctx->allocator.release_picture_callback(&pic_ctx->pic, + pic_ctx->allocator.cookie); + dav1d_mem_pool_push(user_data, pic_ctx); +} + +void dav1d_picture_free_itut_t35(const uint8_t *const data, void *const user_data) { + struct itut_t35_ctx_context *itut_t35_ctx = user_data; + + for (size_t i = 0; i < itut_t35_ctx->n_itut_t35; i++) + dav1d_free(itut_t35_ctx->itut_t35[i].payload); + dav1d_free(itut_t35_ctx->itut_t35); + dav1d_free(itut_t35_ctx); +} + +static int picture_alloc(Dav1dContext *const c, + Dav1dPicture *const p, + const int w, const int h, + Dav1dSequenceHeader *const seq_hdr, Dav1dRef *const seq_hdr_ref, + Dav1dFrameHeader *const frame_hdr, Dav1dRef *const frame_hdr_ref, + const int bpc, + const Dav1dDataProps *const props, + Dav1dPicAllocator *const p_allocator, + void **const extra_ptr) +{ + if (p->data[0]) { + dav1d_log(c, "Picture already allocated!\n"); + return -1; + } + assert(bpc > 0 && bpc <= 16); + + size_t extra = c->n_fc > 1 ? sizeof(atomic_int) * 2 : 0; + struct pic_ctx_context *pic_ctx = dav1d_mem_pool_pop(c->pic_ctx_pool, extra + + sizeof(struct pic_ctx_context)); + if (!pic_ctx) + return DAV1D_ERR(ENOMEM); + + p->p.w = w; + p->p.h = h; + p->seq_hdr = seq_hdr; + p->frame_hdr = frame_hdr; + p->p.layout = seq_hdr->layout; + p->p.bpc = bpc; + dav1d_data_props_set_defaults(&p->m); + const int res = p_allocator->alloc_picture_callback(p, p_allocator->cookie); + if (res < 0) { + dav1d_mem_pool_push(c->pic_ctx_pool, pic_ctx); + return res; + } + + pic_ctx->allocator = *p_allocator; + pic_ctx->pic = *p; + p->ref = dav1d_ref_init(&pic_ctx->ref, pic_ctx, free_buffer, c->pic_ctx_pool, 0); + + p->seq_hdr_ref = seq_hdr_ref; + if (seq_hdr_ref) dav1d_ref_inc(seq_hdr_ref); + + p->frame_hdr_ref = frame_hdr_ref; + if (frame_hdr_ref) dav1d_ref_inc(frame_hdr_ref); + + if (extra && extra_ptr) + *extra_ptr = &pic_ctx->extra_data; + + return 0; +} + +void dav1d_picture_copy_props(Dav1dPicture *const p, + Dav1dContentLightLevel *const content_light, Dav1dRef *const content_light_ref, + Dav1dMasteringDisplay *const mastering_display, Dav1dRef *const mastering_display_ref, + Dav1dITUTT35 *const itut_t35, Dav1dRef *itut_t35_ref, size_t n_itut_t35, + const Dav1dDataProps *const props) +{ + dav1d_data_props_copy(&p->m, props); + + dav1d_ref_dec(&p->content_light_ref); + p->content_light_ref = content_light_ref; + p->content_light = content_light; + if (content_light_ref) dav1d_ref_inc(content_light_ref); + + dav1d_ref_dec(&p->mastering_display_ref); + p->mastering_display_ref = mastering_display_ref; + p->mastering_display = mastering_display; + if (mastering_display_ref) dav1d_ref_inc(mastering_display_ref); + + dav1d_ref_dec(&p->itut_t35_ref); + p->itut_t35_ref = itut_t35_ref; + p->itut_t35 = itut_t35; + p->n_itut_t35 = n_itut_t35; + if (itut_t35_ref) dav1d_ref_inc(itut_t35_ref); +} + +int dav1d_thread_picture_alloc(Dav1dContext *const c, Dav1dFrameContext *const f, + const int bpc) +{ + Dav1dThreadPicture *const p = &f->sr_cur; + + const int res = picture_alloc(c, &p->p, f->frame_hdr->width[1], f->frame_hdr->height, + f->seq_hdr, f->seq_hdr_ref, + f->frame_hdr, f->frame_hdr_ref, + bpc, &f->tile[0].data.m, &c->allocator, + (void **) &p->progress); + if (res) return res; + + // Don't clear these flags from c->frame_flags if the frame is not going to be output. + // This way they will be added to the next visible frame too. + const int flags_mask = ((f->frame_hdr->show_frame || c->output_invisible_frames) && + c->max_spatial_id == f->frame_hdr->spatial_id) + ? 0 : (PICTURE_FLAG_NEW_SEQUENCE | PICTURE_FLAG_NEW_OP_PARAMS_INFO); + p->flags = c->frame_flags; + c->frame_flags &= flags_mask; + + p->visible = f->frame_hdr->show_frame; + p->showable = f->frame_hdr->showable_frame; + + if (p->visible) { + // Only add HDR10+ and T35 metadata when show frame flag is enabled + dav1d_picture_copy_props(&p->p, c->content_light, c->content_light_ref, + c->mastering_display, c->mastering_display_ref, + c->itut_t35, c->itut_t35_ref, c->n_itut_t35, + &f->tile[0].data.m); + + // Must be removed from the context after being attached to the frame + dav1d_ref_dec(&c->itut_t35_ref); + c->itut_t35 = NULL; + c->n_itut_t35 = 0; + } else { + dav1d_data_props_copy(&p->p.m, &f->tile[0].data.m); + } + + if (c->n_fc > 1) { + atomic_init(&p->progress[0], 0); + atomic_init(&p->progress[1], 0); + } + return res; +} + +int dav1d_picture_alloc_copy(Dav1dContext *const c, Dav1dPicture *const dst, const int w, + const Dav1dPicture *const src) +{ + struct pic_ctx_context *const pic_ctx = (struct pic_ctx_context*)src->ref->const_data; + const int res = picture_alloc(c, dst, w, src->p.h, + src->seq_hdr, src->seq_hdr_ref, + src->frame_hdr, src->frame_hdr_ref, + src->p.bpc, &src->m, &pic_ctx->allocator, + NULL); + if (res) return res; + + dav1d_picture_copy_props(dst, src->content_light, src->content_light_ref, + src->mastering_display, src->mastering_display_ref, + src->itut_t35, src->itut_t35_ref, src->n_itut_t35, + &src->m); + + return 0; +} + +void dav1d_picture_ref(Dav1dPicture *const dst, const Dav1dPicture *const src) { + assert(dst != NULL); + assert(dst->data[0] == NULL); + assert(src != NULL); + + if (src->ref) { + assert(src->data[0] != NULL); + dav1d_ref_inc(src->ref); + } + if (src->frame_hdr_ref) dav1d_ref_inc(src->frame_hdr_ref); + if (src->seq_hdr_ref) dav1d_ref_inc(src->seq_hdr_ref); + if (src->m.user_data.ref) dav1d_ref_inc(src->m.user_data.ref); + if (src->content_light_ref) dav1d_ref_inc(src->content_light_ref); + if (src->mastering_display_ref) dav1d_ref_inc(src->mastering_display_ref); + if (src->itut_t35_ref) dav1d_ref_inc(src->itut_t35_ref); + *dst = *src; +} + +void dav1d_picture_move_ref(Dav1dPicture *const dst, Dav1dPicture *const src) { + assert(dst != NULL); + assert(dst->data[0] == NULL); + assert(src != NULL); + + if (src->ref) + assert(src->data[0] != NULL); + + *dst = *src; + memset(src, 0, sizeof(*src)); +} + +void dav1d_thread_picture_ref(Dav1dThreadPicture *const dst, + const Dav1dThreadPicture *const src) +{ + dav1d_picture_ref(&dst->p, &src->p); + dst->visible = src->visible; + dst->showable = src->showable; + dst->progress = src->progress; + dst->flags = src->flags; +} + +void dav1d_thread_picture_move_ref(Dav1dThreadPicture *const dst, + Dav1dThreadPicture *const src) +{ + dav1d_picture_move_ref(&dst->p, &src->p); + dst->visible = src->visible; + dst->showable = src->showable; + dst->progress = src->progress; + dst->flags = src->flags; + memset(src, 0, sizeof(*src)); +} + +void dav1d_picture_unref_internal(Dav1dPicture *const p) { + validate_input(p != NULL); + + if (p->ref) { + validate_input(p->data[0] != NULL); + dav1d_ref_dec(&p->ref); + } + dav1d_ref_dec(&p->seq_hdr_ref); + dav1d_ref_dec(&p->frame_hdr_ref); + dav1d_ref_dec(&p->m.user_data.ref); + dav1d_ref_dec(&p->content_light_ref); + dav1d_ref_dec(&p->mastering_display_ref); + dav1d_ref_dec(&p->itut_t35_ref); + memset(p, 0, sizeof(*p)); + dav1d_data_props_set_defaults(&p->m); +} + +void dav1d_thread_picture_unref(Dav1dThreadPicture *const p) { + dav1d_picture_unref_internal(&p->p); + + p->progress = NULL; +} + +enum Dav1dEventFlags dav1d_picture_get_event_flags(const Dav1dThreadPicture *const p) { + if (!p->flags) + return 0; + + enum Dav1dEventFlags flags = 0; + if (p->flags & PICTURE_FLAG_NEW_SEQUENCE) + flags |= DAV1D_EVENT_FLAG_NEW_SEQUENCE; + if (p->flags & PICTURE_FLAG_NEW_OP_PARAMS_INFO) + flags |= DAV1D_EVENT_FLAG_NEW_OP_PARAMS_INFO; + + return flags; +} diff --git a/3rdparty/dav1d/src/picture.h b/3rdparty/dav1d/src/picture.h new file mode 100644 index 0000000..88aee08 --- /dev/null +++ b/3rdparty/dav1d/src/picture.h @@ -0,0 +1,122 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_PICTURE_H +#define DAV1D_SRC_PICTURE_H + +#include + +#include "src/thread.h" +#include "dav1d/picture.h" + +#include "src/thread_data.h" +#include "src/ref.h" + +enum PlaneType { + PLANE_TYPE_Y, + PLANE_TYPE_UV, + PLANE_TYPE_BLOCK, + PLANE_TYPE_ALL, +}; + +enum PictureFlags { + PICTURE_FLAG_NEW_SEQUENCE = 1 << 0, + PICTURE_FLAG_NEW_OP_PARAMS_INFO = 1 << 1, + PICTURE_FLAG_NEW_TEMPORAL_UNIT = 1 << 2, +}; + +typedef struct Dav1dThreadPicture { + Dav1dPicture p; + int visible; + // This can be set for inter frames, non-key intra frames, or for invisible + // keyframes that have not yet been made visible using the show-existing-frame + // mechanism. + int showable; + enum PictureFlags flags; + // [0] block data (including segmentation map and motion vectors) + // [1] pixel data + atomic_uint *progress; +} Dav1dThreadPicture; + +typedef struct Dav1dPictureBuffer { + void *data; + struct Dav1dPictureBuffer *next; +} Dav1dPictureBuffer; + +/* + * Allocate a picture with custom border size. + */ +int dav1d_thread_picture_alloc(Dav1dContext *c, Dav1dFrameContext *f, const int bpc); + +/** + * Allocate a picture with identical metadata to an existing picture. + * The width is a separate argument so this function can be used for + * super-res, where the width changes, but everything else is the same. + * For the more typical use case of allocating a new image of the same + * dimensions, use src->p.w as width. + */ +int dav1d_picture_alloc_copy(Dav1dContext *c, Dav1dPicture *dst, const int w, + const Dav1dPicture *src); + +/** + * Create a copy of a picture. + */ +void dav1d_picture_ref(Dav1dPicture *dst, const Dav1dPicture *src); +void dav1d_thread_picture_ref(Dav1dThreadPicture *dst, + const Dav1dThreadPicture *src); +void dav1d_thread_picture_move_ref(Dav1dThreadPicture *dst, + Dav1dThreadPicture *src); +void dav1d_thread_picture_unref(Dav1dThreadPicture *p); + +/** + * Move a picture reference. + */ +void dav1d_picture_move_ref(Dav1dPicture *dst, Dav1dPicture *src); + +int dav1d_default_picture_alloc(Dav1dPicture *p, void *cookie); +void dav1d_default_picture_release(Dav1dPicture *p, void *cookie); +void dav1d_picture_unref_internal(Dav1dPicture *p); + +struct itut_t35_ctx_context { + Dav1dITUTT35 *itut_t35; + size_t n_itut_t35; + Dav1dRef ref; +}; + +void dav1d_picture_free_itut_t35(const uint8_t *data, void *user_data); +void dav1d_picture_copy_props(Dav1dPicture *p, + Dav1dContentLightLevel *content_light, Dav1dRef *content_light_ref, + Dav1dMasteringDisplay *mastering_display, Dav1dRef *mastering_display_ref, + Dav1dITUTT35 *itut_t35, Dav1dRef *itut_t35_ref, size_t n_itut_t35, + const Dav1dDataProps *props); + +/** + * Get event flags from picture flags. + */ +enum Dav1dEventFlags dav1d_picture_get_event_flags(const Dav1dThreadPicture *p); + +#endif /* DAV1D_SRC_PICTURE_H */ diff --git a/3rdparty/dav1d/src/qm.c b/3rdparty/dav1d/src/qm.c new file mode 100644 index 0000000..987782a --- /dev/null +++ b/3rdparty/dav1d/src/qm.c @@ -0,0 +1,4715 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + + +#include "common/attributes.h" + +#include "src/qm.h" + +static const uint8_t qm_tbl_4x4[15][2][16] = { + { + { + 32, 43, 73, 97, 43, 67, 94, 110, 73, 94, 137, 150, 97, 110, 150, 200, + }, { + 35, 46, 57, 66, 46, 60, 69, 71, 57, 69, 90, 90, 66, 71, 90, 109, + }, + }, { + { + 32, 41, 69, 92, 41, 63, 88, 103, 69, 88, 127, 140, 92, 103, 140, 184, + }, { + 33, 45, 56, 64, 45, 58, 66, 69, 56, 66, 86, 87, 64, 69, 87, 105, + }, + }, { + { + 32, 38, 63, 86, 38, 56, 78, 97, 63, 78, 113, 130, 86, 97, 130, 169, + }, { + 32, 45, 53, 63, 45, 55, 62, 67, 53, 62, 80, 84, 63, 67, 84, 101, + }, + }, { + { + 32, 37, 58, 81, 37, 54, 72, 91, 58, 72, 102, 121, 81, 91, 121, 156, + }, { + 32, 45, 51, 61, 45, 54, 59, 65, 51, 59, 75, 81, 61, 65, 81, 97, + }, + }, { + { + 32, 34, 53, 75, 34, 49, 64, 81, 53, 64, 91, 112, 75, 81, 112, 140, + }, { + 32, 46, 49, 58, 46, 53, 55, 62, 49, 55, 70, 78, 58, 62, 78, 91, + }, + }, { + { + 32, 34, 49, 72, 34, 48, 60, 79, 49, 60, 82, 104, 72, 79, 104, 134, + }, { + 32, 46, 47, 57, 46, 53, 54, 60, 47, 54, 66, 75, 57, 60, 75, 89, + }, + }, { + { + 32, 33, 45, 62, 33, 39, 51, 64, 45, 51, 71, 87, 62, 64, 87, 108, + }, { + 31, 42, 47, 53, 42, 48, 50, 54, 47, 50, 61, 67, 53, 54, 67, 78, + }, + }, { + { + 32, 33, 42, 55, 33, 38, 46, 57, 42, 46, 63, 75, 55, 57, 75, 92, + }, { + 31, 41, 46, 51, 41, 48, 48, 51, 46, 48, 58, 62, 51, 51, 62, 71, + }, + }, { + { + 32, 32, 38, 51, 32, 35, 40, 49, 38, 40, 54, 64, 51, 49, 64, 81, + }, { + 31, 38, 47, 49, 38, 47, 46, 46, 47, 46, 54, 57, 49, 46, 57, 66, + }, + }, { + { + 32, 32, 35, 43, 32, 34, 37, 43, 35, 37, 48, 54, 43, 43, 54, 65, + }, { + 31, 37, 47, 47, 37, 44, 47, 45, 47, 47, 53, 53, 47, 45, 53, 59, + }, + }, { + { + 32, 32, 34, 38, 32, 33, 35, 39, 34, 35, 39, 45, 38, 39, 45, 54, + }, { + 31, 34, 42, 47, 34, 39, 45, 46, 42, 45, 48, 49, 47, 46, 49, 54, + }, + }, { + { + 32, 32, 32, 35, 32, 32, 33, 35, 32, 33, 35, 38, 35, 35, 38, 46, + }, { + 31, 32, 38, 46, 32, 34, 41, 46, 38, 41, 47, 47, 46, 46, 47, 52, + }, + }, { + { + 31, 32, 32, 32, 32, 32, 32, 33, 32, 32, 33, 34, 32, 33, 34, 35, + }, { + 31, 31, 34, 38, 31, 32, 35, 40, 34, 35, 39, 43, 38, 40, 43, 47, + }, + }, { + { + 31, 31, 31, 32, 31, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 33, + }, { + 31, 31, 31, 34, 31, 31, 31, 35, 31, 31, 32, 35, 34, 35, 35, 39, + }, + }, { + { + 31, 31, 31, 31, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, + }, { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + }, + }, +}; + +static const uint8_t qm_tbl_4x8[15][2][32] = { + { + { + 32, 42, 75, 91, 33, 42, 69, 86, 37, 58, 84, 91, 49, 71, 103, 110, 65, 84, 125, 128, 80, 97, 142, 152, 91, 100, 145, 178, 104, 112, 146, 190, + }, { + 31, 47, 60, 66, 40, 45, 54, 61, 46, 56, 64, 64, 48, 61, 75, 73, 54, 65, 85, 82, 61, 69, 92, 92, 64, 68, 90, 102, 68, 71, 87, 105, + }, + }, { + { + 32, 42, 69, 88, 33, 42, 64, 83, 36, 56, 77, 88, 46, 67, 93, 105, 60, 79, 112, 122, 75, 92, 130, 144, 86, 95, 136, 167, 98, 105, 136, 177, + }, { + 31, 47, 57, 65, 40, 45, 52, 61, 46, 55, 61, 63, 47, 60, 70, 72, 52, 64, 79, 81, 59, 68, 87, 90, 63, 66, 88, 99, 66, 69, 85, 102, + }, + }, { + { + 32, 38, 62, 86, 32, 40, 58, 80, 34, 51, 68, 85, 44, 61, 85, 101, 54, 69, 98, 117, 72, 84, 118, 136, 82, 89, 129, 157, 92, 98, 127, 165, + }, { + 31, 47, 54, 64, 38, 46, 50, 60, 46, 53, 57, 62, 46, 56, 66, 71, 50, 59, 74, 79, 57, 64, 82, 88, 61, 65, 85, 97, 65, 67, 82, 99, + }, + }, { + { + 32, 35, 59, 83, 32, 36, 57, 78, 34, 47, 65, 82, 41, 53, 78, 97, 51, 61, 92, 111, 65, 73, 108, 129, 75, 81, 117, 148, 86, 92, 119, 154, + }, { + 31, 47, 53, 63, 36, 47, 50, 59, 46, 52, 55, 61, 45, 53, 63, 70, 49, 55, 71, 77, 54, 58, 77, 86, 59, 61, 81, 94, 63, 65, 80, 95, + }, + }, { + { + 32, 35, 51, 77, 32, 36, 50, 72, 34, 42, 54, 75, 38, 51, 67, 87, 48, 59, 80, 103, 60, 68, 92, 119, 72, 79, 104, 135, 81, 86, 112, 144, + }, { + 31, 47, 50, 61, 36, 47, 47, 57, 43, 50, 50, 58, 45, 53, 58, 65, 47, 54, 66, 74, 52, 56, 70, 82, 57, 60, 75, 90, 61, 63, 77, 93, + }, + }, { + { + 32, 35, 51, 75, 32, 36, 50, 71, 34, 42, 54, 73, 37, 50, 65, 84, 45, 56, 76, 96, 54, 63, 87, 110, 65, 73, 97, 125, 75, 81, 106, 136, + }, { + 31, 47, 50, 60, 36, 47, 47, 56, 43, 50, 50, 57, 46, 53, 57, 64, 46, 54, 64, 71, 50, 55, 68, 78, 54, 58, 72, 85, 59, 61, 75, 90, + }, + }, { + { + 32, 34, 43, 62, 32, 34, 42, 59, 33, 37, 44, 58, 35, 43, 54, 68, 41, 48, 64, 79, 49, 54, 71, 91, 57, 60, 78, 101, 66, 68, 86, 111, + }, { + 31, 42, 47, 54, 33, 44, 45, 51, 40, 47, 46, 50, 47, 50, 54, 57, 45, 49, 59, 64, 48, 50, 61, 70, 51, 52, 63, 75, 55, 55, 66, 79, + }, + }, { + { + 32, 32, 42, 56, 32, 33, 41, 53, 32, 35, 42, 52, 34, 37, 50, 59, 38, 40, 58, 68, 44, 45, 66, 78, 50, 50, 71, 86, 61, 58, 79, 97, + }, { + 31, 38, 47, 52, 32, 40, 45, 49, 39, 47, 45, 48, 44, 47, 51, 53, 46, 47, 56, 58, 47, 46, 59, 64, 48, 47, 61, 68, 53, 50, 64, 73, + }, + }, { + { + 32, 32, 37, 52, 32, 33, 36, 49, 32, 34, 38, 49, 34, 37, 44, 54, 35, 38, 49, 60, 40, 42, 55, 69, 46, 46, 59, 76, 52, 51, 64, 83, + }, { + 31, 38, 47, 50, 31, 40, 46, 48, 36, 44, 47, 47, 42, 47, 50, 50, 47, 48, 53, 54, 46, 46, 54, 60, 48, 46, 55, 64, 50, 48, 56, 67, + }, + }, { + { + 31, 32, 35, 43, 32, 33, 34, 41, 32, 34, 36, 42, 32, 35, 38, 42, 34, 37, 43, 49, 37, 40, 49, 56, 42, 43, 53, 63, 46, 46, 56, 67, + }, { + 31, 38, 47, 48, 31, 40, 46, 45, 35, 43, 47, 46, 39, 47, 47, 45, 43, 47, 50, 50, 47, 47, 53, 55, 46, 46, 53, 58, 48, 46, 54, 59, + }, + }, { + { + 31, 32, 34, 39, 32, 32, 34, 38, 32, 33, 34, 38, 32, 33, 36, 40, 33, 34, 38, 42, 34, 36, 41, 47, 37, 38, 44, 52, 40, 40, 46, 56, + }, { + 31, 34, 42, 48, 31, 35, 42, 46, 33, 37, 44, 46, 36, 41, 46, 46, 40, 44, 48, 48, 45, 46, 49, 51, 47, 47, 50, 54, 47, 46, 49, 55, + }, + }, { + { + 31, 31, 32, 35, 32, 32, 32, 35, 32, 32, 33, 34, 32, 32, 34, 36, 32, 33, 35, 38, 33, 33, 36, 40, 34, 34, 37, 42, 35, 34, 38, 48, + }, { + 31, 31, 37, 48, 31, 31, 38, 47, 31, 32, 40, 46, 34, 36, 43, 47, 37, 39, 46, 47, 39, 41, 47, 48, 42, 43, 47, 50, 48, 46, 48, 53, + }, + }, { + { + 31, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 32, 32, 33, 34, 32, 32, 34, 34, 32, 33, 34, 35, 33, 33, 35, 36, + }, { + 31, 31, 35, 37, 31, 31, 36, 38, 31, 32, 37, 39, 31, 32, 37, 40, 34, 36, 40, 43, 35, 37, 42, 44, 38, 40, 45, 47, 41, 42, 45, 47, + }, + }, { + { + 31, 31, 31, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 33, 32, 32, 32, 33, 32, 32, 32, 33, + }, { + 31, 31, 31, 34, 31, 31, 31, 35, 31, 31, 31, 35, 31, 32, 32, 36, 31, 32, 32, 36, 31, 33, 33, 37, 34, 36, 36, 40, 34, 36, 36, 40, + }, + }, { + { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, + }, { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 30, 31, 32, 32, + }, + }, +}; + +static const uint8_t qm_tbl_4x16[15][2][64] = { + { + { + 31, 44, 79, 96, 32, 41, 72, 90, 32, 42, 71, 86, 34, 48, 73, 83, + 34, 54, 78, 89, 41, 63, 90, 95, 45, 67, 96, 102, 54, 75, 110, 111, + 60, 79, 118, 123, 72, 90, 133, 135, 75, 92, 136, 149, 83, 100, 142, 160, + 88, 100, 140, 173, 94, 101, 144, 180, 101, 108, 141, 188, 108, 115, 151, 197, + }, { + 31, 49, 63, 69, 32, 45, 57, 65, 36, 46, 56, 62, 43, 49, 57, 60, + 46, 53, 60, 63, 45, 58, 67, 66, 46, 59, 71, 70, 50, 62, 78, 74, + 52, 64, 82, 80, 57, 67, 89, 85, 59, 68, 90, 91, 62, 71, 91, 96, + 63, 69, 89, 101, 65, 68, 89, 103, 67, 70, 86, 105, 69, 72, 88, 107, + }, + }, { + { + 31, 44, 73, 93, 32, 41, 67, 87, 32, 42, 65, 83, 33, 44, 66, 81, + 34, 54, 74, 86, 37, 58, 79, 92, 44, 66, 90, 98, 49, 71, 99, 107, + 56, 77, 107, 117, 65, 84, 119, 129, 72, 90, 127, 141, 78, 95, 133, 151, + 84, 95, 132, 163, 89, 95, 136, 169, 95, 101, 132, 175, 101, 108, 141, 183, + }, { + 31, 49, 61, 69, 32, 45, 55, 64, 36, 46, 54, 61, 41, 47, 54, 59, + 46, 53, 59, 62, 46, 56, 62, 65, 46, 59, 68, 68, 48, 61, 73, 73, + 51, 63, 77, 78, 54, 65, 82, 84, 57, 67, 86, 89, 60, 69, 88, 93, + 62, 67, 86, 98, 64, 66, 87, 100, 65, 68, 83, 102, 67, 70, 86, 103, + }, + }, { + { + 31, 39, 65, 90, 32, 38, 60, 84, 32, 39, 59, 81, 33, 40, 58, 78, + 34, 47, 65, 83, 37, 54, 73, 89, 41, 58, 79, 94, 46, 62, 86, 102, + 53, 68, 97, 112, 60, 73, 105, 123, 65, 78, 111, 134, 74, 85, 120, 143, + 79, 90, 125, 154, 84, 90, 128, 158, 89, 95, 124, 164, 94, 101, 131, 170, + }, { + 31, 48, 57, 68, 32, 46, 53, 63, 36, 46, 51, 60, 40, 46, 50, 58, + 44, 51, 54, 61, 46, 54, 60, 64, 45, 56, 64, 67, 47, 57, 68, 71, + 49, 58, 73, 77, 52, 60, 76, 82, 54, 62, 79, 87, 58, 64, 82, 91, + 60, 66, 84, 95, 62, 64, 84, 97, 64, 66, 81, 99, 65, 68, 83, 100, + }, + }, { + { + 31, 36, 62, 88, 32, 35, 58, 82, 32, 36, 57, 79, 33, 38, 56, 76, + 34, 42, 61, 81, 34, 48, 66, 85, 39, 51, 74, 91, 44, 56, 82, 98, + 49, 60, 90, 107, 54, 63, 95, 117, 60, 68, 102, 127, 68, 75, 110, 135, + 75, 81, 117, 145, 79, 85, 120, 148, 84, 89, 116, 153, 88, 94, 123, 159, + }, { + 31, 48, 56, 67, 32, 46, 52, 62, 35, 47, 50, 60, 40, 47, 49, 57, + 43, 50, 53, 60, 46, 53, 56, 63, 45, 53, 61, 66, 46, 54, 65, 70, + 48, 54, 70, 75, 50, 55, 72, 80, 52, 56, 75, 85, 56, 59, 79, 89, + 58, 61, 81, 93, 60, 63, 82, 94, 62, 64, 79, 96, 63, 66, 81, 97, + }, + }, { + { + 31, 36, 53, 81, 32, 35, 51, 76, 32, 35, 49, 73, 32, 37, 49, 71, + 33, 41, 53, 74, 34, 48, 60, 80, 37, 50, 65, 85, 41, 53, 71, 91, + 45, 56, 76, 98, 49, 60, 82, 105, 54, 63, 87, 112, 61, 69, 93, 121, + 68, 75, 100, 130, 74, 80, 105, 137, 78, 84, 109, 142, 83, 88, 114, 148, + }, { + 31, 48, 52, 64, 31, 47, 49, 60, 33, 46, 48, 57, 38, 47, 47, 56, + 42, 49, 50, 57, 46, 53, 54, 61, 46, 53, 57, 64, 45, 53, 61, 68, + 46, 54, 64, 71, 48, 54, 66, 75, 50, 55, 68, 78, 52, 57, 71, 83, + 56, 59, 73, 87, 58, 61, 75, 90, 60, 62, 76, 92, 62, 64, 78, 94, + }, + }, { + { + 31, 36, 53, 79, 32, 35, 51, 75, 32, 34, 49, 72, 32, 36, 50, 71, + 33, 38, 49, 69, 34, 42, 54, 73, 34, 48, 60, 78, 37, 50, 65, 84, + 41, 53, 71, 90, 45, 56, 76, 96, 49, 60, 82, 103, 54, 63, 87, 110, + 60, 68, 92, 118, 65, 73, 97, 125, 72, 79, 104, 133, 75, 81, 106, 136, + }, { + 31, 48, 52, 63, 31, 47, 50, 60, 32, 46, 48, 57, 36, 47, 47, 56, + 40, 47, 47, 54, 43, 50, 50, 57, 46, 53, 54, 60, 46, 53, 57, 64, + 45, 53, 61, 67, 46, 54, 64, 71, 48, 54, 66, 75, 50, 55, 68, 78, + 52, 56, 70, 82, 54, 58, 72, 85, 57, 60, 75, 89, 59, 61, 75, 90, + }, + }, { + { + 31, 34, 44, 65, 32, 34, 43, 62, 32, 33, 41, 59, 32, 35, 43, 59, + 32, 37, 43, 58, 34, 39, 48, 63, 34, 42, 53, 67, 36, 44, 57, 71, + 39, 46, 60, 76, 42, 48, 64, 81, 45, 51, 67, 85, 50, 54, 72, 92, + 54, 58, 76, 98, 60, 63, 80, 105, 66, 68, 85, 111, 73, 74, 91, 118, + }, { + 31, 42, 49, 57, 31, 42, 47, 54, 32, 42, 45, 52, 35, 45, 46, 51, + 40, 47, 46, 50, 43, 48, 49, 53, 46, 50, 53, 56, 46, 50, 55, 58, + 46, 49, 57, 61, 46, 49, 59, 64, 47, 50, 60, 67, 48, 50, 61, 71, + 50, 52, 63, 73, 52, 53, 64, 76, 55, 55, 66, 79, 58, 58, 68, 82, + }, + }, { + { + 31, 32, 44, 58, 32, 32, 42, 55, 32, 33, 41, 53, 32, 34, 42, 53, + 32, 34, 42, 53, 32, 35, 42, 52, 34, 37, 48, 57, 35, 38, 54, 63, + 37, 40, 57, 67, 39, 41, 60, 70, 41, 43, 63, 74, 45, 46, 67, 79, + 50, 50, 71, 86, 54, 53, 74, 90, 57, 56, 77, 93, 61, 58, 79, 97, + }, { + 31, 37, 49, 54, 31, 38, 47, 51, 32, 40, 45, 49, 34, 42, 45, 49, + 37, 44, 45, 48, 39, 47, 45, 48, 42, 47, 49, 51, 47, 48, 53, 55, + 46, 47, 55, 58, 46, 46, 57, 60, 46, 46, 58, 62, 47, 46, 59, 65, + 48, 47, 61, 68, 50, 48, 62, 70, 51, 49, 63, 71, 53, 50, 64, 73, + }, + }, { + { + 31, 32, 38, 53, 32, 32, 37, 51, 32, 32, 36, 49, 32, 33, 36, 49, + 32, 34, 38, 50, 32, 35, 39, 49, 33, 36, 41, 51, 34, 37, 44, 54, + 35, 38, 49, 60, 37, 40, 51, 63, 38, 40, 52, 65, 42, 43, 56, 71, + 45, 45, 58, 75, 47, 47, 60, 77, 51, 50, 63, 82, 55, 54, 67, 87, + }, { + 31, 37, 48, 52, 31, 38, 47, 50, 31, 39, 46, 48, 32, 40, 46, 48, + 35, 43, 46, 47, 39, 47, 47, 47, 40, 47, 48, 48, 42, 47, 50, 50, + 47, 48, 53, 54, 47, 47, 53, 56, 46, 47, 54, 57, 46, 46, 55, 61, + 47, 46, 55, 63, 48, 47, 55, 64, 49, 47, 56, 66, 51, 49, 57, 68, + }, + }, { + { + 31, 32, 36, 44, 32, 32, 35, 42, 32, 32, 35, 41, 32, 33, 34, 41, + 32, 34, 36, 42, 32, 34, 36, 42, 32, 35, 38, 42, 33, 36, 40, 44, + 34, 37, 42, 48, 35, 38, 47, 52, 35, 38, 48, 54, 38, 40, 50, 58, + 40, 41, 51, 60, 42, 43, 53, 63, 45, 45, 56, 66, 46, 46, 56, 67, + }, { + 31, 37, 48, 49, 31, 38, 47, 47, 31, 39, 46, 46, 31, 40, 46, 45, + 34, 42, 47, 45, 35, 43, 47, 46, 39, 47, 47, 45, 40, 47, 48, 47, + 42, 47, 50, 49, 46, 48, 52, 53, 47, 48, 53, 53, 47, 47, 53, 56, + 47, 46, 53, 57, 46, 46, 53, 58, 48, 46, 54, 59, 48, 46, 54, 59, + }, + }, { + { + 31, 32, 34, 39, 32, 32, 34, 38, 32, 32, 34, 38, 32, 32, 33, 37, + 32, 32, 33, 37, 32, 33, 35, 39, 32, 33, 35, 39, 32, 34, 37, 40, + 32, 34, 37, 40, 34, 35, 39, 45, 34, 35, 39, 45, 35, 36, 43, 51, + 35, 36, 43, 51, 38, 39, 45, 54, 38, 39, 45, 54, 42, 42, 48, 58, + }, { + 31, 33, 42, 48, 31, 34, 42, 47, 31, 34, 42, 47, 31, 35, 42, 45, + 31, 35, 42, 45, 34, 39, 45, 46, 34, 39, 45, 46, 38, 43, 47, 46, + 38, 43, 47, 46, 42, 45, 48, 50, 42, 45, 48, 50, 48, 47, 50, 53, + 48, 47, 50, 53, 47, 46, 50, 54, 47, 46, 50, 54, 47, 45, 49, 56, + }, + }, { + { + 31, 31, 32, 36, 31, 32, 32, 35, 32, 32, 32, 35, 32, 32, 32, 35, + 32, 32, 33, 34, 32, 32, 33, 34, 32, 32, 34, 36, 32, 32, 34, 36, + 32, 32, 34, 37, 32, 33, 35, 38, 32, 33, 35, 38, 33, 33, 36, 41, + 34, 34, 37, 42, 34, 34, 37, 44, 35, 34, 38, 48, 35, 34, 38, 48, + }, { + 31, 31, 37, 48, 31, 31, 38, 47, 31, 31, 38, 47, 31, 32, 39, 46, + 31, 32, 40, 46, 31, 32, 40, 46, 34, 35, 42, 47, 34, 36, 43, 47, + 36, 37, 44, 47, 38, 40, 47, 47, 38, 40, 47, 47, 41, 42, 47, 49, + 42, 43, 47, 50, 44, 44, 47, 51, 48, 46, 48, 53, 48, 46, 48, 53, + }, + }, { + { + 31, 31, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 33, 32, 32, 32, 33, 32, 32, 33, 33, + 32, 32, 33, 34, 32, 32, 33, 34, 32, 32, 33, 34, 32, 32, 34, 35, + 32, 33, 34, 35, 32, 33, 34, 35, 33, 33, 35, 36, 34, 34, 36, 37, + }, { + 31, 31, 35, 37, 31, 31, 35, 38, 31, 31, 36, 38, 31, 31, 36, 38, + 31, 32, 36, 39, 31, 32, 37, 40, 31, 32, 37, 40, 31, 33, 38, 40, + 33, 35, 40, 42, 34, 36, 40, 43, 34, 36, 40, 43, 36, 38, 43, 45, + 38, 40, 45, 47, 38, 40, 45, 47, 39, 41, 45, 47, 42, 43, 46, 47, + }, + }, { + { + 31, 31, 31, 32, 31, 31, 31, 32, 31, 32, 32, 32, 31, 32, 32, 32, + 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, + 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 33, + 32, 32, 32, 33, 32, 32, 32, 33, 32, 32, 32, 33, 32, 32, 32, 33, + }, { + 31, 31, 31, 34, 31, 31, 31, 34, 31, 31, 31, 35, 31, 31, 31, 35, + 31, 31, 31, 35, 31, 31, 31, 35, 31, 32, 32, 36, 31, 32, 32, 36, + 31, 32, 32, 36, 31, 32, 32, 36, 31, 32, 32, 36, 32, 33, 33, 37, + 33, 35, 35, 39, 34, 36, 36, 40, 34, 36, 36, 40, 34, 36, 36, 40, + }, + }, { + { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, + 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, + 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, + 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, 31, 32, 32, 32, + }, { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 32, 31, 31, 32, 32, 31, 31, 32, 32, 30, 31, 32, 32, + }, + }, +}; + +static const uint8_t qm_tbl_8x4[15][2][32] = { + { + { + 32, 33, 37, 49, 65, 80, 91, 104, 42, 42, 58, 71, 84, 97, 100, 112, 75, 69, 84, 103, 125, 142, 145, 146, 91, 86, 91, 110, 128, 152, 178, 190, + }, { + 31, 40, 46, 48, 54, 61, 64, 68, 47, 45, 56, 61, 65, 69, 68, 71, 60, 54, 64, 75, 85, 92, 90, 87, 66, 61, 64, 73, 82, 92, 102, 105, + }, + }, { + { + 32, 33, 36, 46, 60, 75, 86, 98, 42, 42, 56, 67, 79, 92, 95, 105, 69, 64, 77, 93, 112, 130, 136, 136, 88, 83, 88, 105, 122, 144, 167, 177, + }, { + 31, 40, 46, 47, 52, 59, 63, 66, 47, 45, 55, 60, 64, 68, 66, 69, 57, 52, 61, 70, 79, 87, 88, 85, 65, 61, 63, 72, 81, 90, 99, 102, + }, + }, { + { + 32, 32, 34, 44, 54, 72, 82, 92, 38, 40, 51, 61, 69, 84, 89, 98, 62, 58, 68, 85, 98, 118, 129, 127, 86, 80, 85, 101, 117, 136, 157, 165, + }, { + 31, 38, 46, 46, 50, 57, 61, 65, 47, 46, 53, 56, 59, 64, 65, 67, 54, 50, 57, 66, 74, 82, 85, 82, 64, 60, 62, 71, 79, 88, 97, 99, + }, + }, { + { + 32, 32, 34, 41, 51, 65, 75, 86, 35, 36, 47, 53, 61, 73, 81, 92, 59, 57, 65, 78, 92, 108, 117, 119, 83, 78, 82, 97, 111, 129, 148, 154, + }, { + 31, 36, 46, 45, 49, 54, 59, 63, 47, 47, 52, 53, 55, 58, 61, 65, 53, 50, 55, 63, 71, 77, 81, 80, 63, 59, 61, 70, 77, 86, 94, 95, + }, + }, { + { + 32, 32, 34, 38, 48, 60, 72, 81, 35, 36, 42, 51, 59, 68, 79, 86, 51, 50, 54, 67, 80, 92, 104, 112, 77, 72, 75, 87, 103, 119, 135, 144, + }, { + 31, 36, 43, 45, 47, 52, 57, 61, 47, 47, 50, 53, 54, 56, 60, 63, 50, 47, 50, 58, 66, 70, 75, 77, 61, 57, 58, 65, 74, 82, 90, 93, + }, + }, { + { + 32, 32, 34, 37, 45, 54, 65, 75, 35, 36, 42, 50, 56, 63, 73, 81, 51, 50, 54, 65, 76, 87, 97, 106, 75, 71, 73, 84, 96, 110, 125, 136, + }, { + 31, 36, 43, 46, 46, 50, 54, 59, 47, 47, 50, 53, 54, 55, 58, 61, 50, 47, 50, 57, 64, 68, 72, 75, 60, 56, 57, 64, 71, 78, 85, 90, + }, + }, { + { + 32, 32, 33, 35, 41, 49, 57, 66, 34, 34, 37, 43, 48, 54, 60, 68, 43, 42, 44, 54, 64, 71, 78, 86, 62, 59, 58, 68, 79, 91, 101, 111, + }, { + 31, 33, 40, 47, 45, 48, 51, 55, 42, 44, 47, 50, 49, 50, 52, 55, 47, 45, 46, 54, 59, 61, 63, 66, 54, 51, 50, 57, 64, 70, 75, 79, + }, + }, { + { + 32, 32, 32, 34, 38, 44, 50, 61, 32, 33, 35, 37, 40, 45, 50, 58, 42, 41, 42, 50, 58, 66, 71, 79, 56, 53, 52, 59, 68, 78, 86, 97, + }, { + 31, 32, 39, 44, 46, 47, 48, 53, 38, 40, 47, 47, 47, 46, 47, 50, 47, 45, 45, 51, 56, 59, 61, 64, 52, 49, 48, 53, 58, 64, 68, 73, + }, + }, { + { + 32, 32, 32, 34, 35, 40, 46, 52, 32, 33, 34, 37, 38, 42, 46, 51, 37, 36, 38, 44, 49, 55, 59, 64, 52, 49, 49, 54, 60, 69, 76, 83, + }, { + 31, 31, 36, 42, 47, 46, 48, 50, 38, 40, 44, 47, 48, 46, 46, 48, 47, 46, 47, 50, 53, 54, 55, 56, 50, 48, 47, 50, 54, 60, 64, 67, + }, + }, { + { + 31, 32, 32, 32, 34, 37, 42, 46, 32, 33, 34, 35, 37, 40, 43, 46, 35, 34, 36, 38, 43, 49, 53, 56, 43, 41, 42, 42, 49, 56, 63, 67, + }, { + 31, 31, 35, 39, 43, 47, 46, 48, 38, 40, 43, 47, 47, 47, 46, 46, 47, 46, 47, 47, 50, 53, 53, 54, 48, 45, 46, 45, 50, 55, 58, 59, + }, + }, { + { + 31, 32, 32, 32, 33, 34, 37, 40, 32, 32, 33, 33, 34, 36, 38, 40, 34, 34, 34, 36, 38, 41, 44, 46, 39, 38, 38, 40, 42, 47, 52, 56, + }, { + 31, 31, 33, 36, 40, 45, 47, 47, 34, 35, 37, 41, 44, 46, 47, 46, 42, 42, 44, 46, 48, 49, 50, 49, 48, 46, 46, 46, 48, 51, 54, 55, + }, + }, { + { + 31, 32, 32, 32, 32, 33, 34, 35, 31, 32, 32, 32, 33, 33, 34, 34, 32, 32, 33, 34, 35, 36, 37, 38, 35, 35, 34, 36, 38, 40, 42, 48, + }, { + 31, 31, 31, 34, 37, 39, 42, 48, 31, 31, 32, 36, 39, 41, 43, 46, 37, 38, 40, 43, 46, 47, 47, 48, 48, 47, 46, 47, 47, 48, 50, 53, + }, + }, { + { + 31, 31, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 33, 33, 32, 32, 32, 32, 33, 34, 34, 35, 32, 32, 32, 33, 34, 34, 35, 36, + }, { + 31, 31, 31, 31, 34, 35, 38, 41, 31, 31, 32, 32, 36, 37, 40, 42, 35, 36, 37, 37, 40, 42, 45, 45, 37, 38, 39, 40, 43, 44, 47, 47, + }, + }, { + { + 31, 31, 31, 31, 31, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, + }, { + 31, 31, 31, 31, 31, 31, 34, 34, 31, 31, 31, 32, 32, 33, 36, 36, 31, 31, 31, 32, 32, 33, 36, 36, 34, 35, 35, 36, 36, 37, 40, 40, + }, + }, { + { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + }, { + 31, 31, 31, 31, 31, 31, 31, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, + }, + }, +}; + +static const uint8_t qm_tbl_8x8[15][2][64] = { + { + { + 32, 32, 38, 51, 68, 84, 95, 109, 32, 35, 40, 49, 63, 76, 89, 102, 38, 40, 54, 65, 78, 91, 98, 106, 51, 49, 65, 82, 97, 111, 113, 121, + 68, 63, 78, 97, 117, 134, 138, 142, 84, 76, 91, 111, 134, 152, 159, 168, 95, 89, 98, 113, 138, 159, 183, 199, 109, 102, 106, 121, 142, 168, 199, 220, + }, { + 31, 38, 47, 50, 57, 63, 67, 71, 38, 47, 46, 47, 52, 57, 62, 67, 47, 46, 54, 57, 61, 66, 67, 68, 50, 47, 57, 66, 72, 77, 75, 75, + 57, 52, 61, 72, 82, 88, 86, 84, 63, 57, 66, 77, 88, 96, 95, 95, 67, 62, 67, 75, 86, 95, 104, 107, 71, 67, 68, 75, 84, 95, 107, 113, + }, + }, { + { + 32, 32, 37, 47, 62, 78, 90, 102, 32, 35, 39, 46, 58, 72, 84, 96, 37, 39, 51, 60, 71, 84, 93, 100, 47, 46, 60, 73, 87, 100, 106, 113, + 62, 58, 71, 87, 105, 121, 129, 132, 78, 72, 84, 100, 121, 140, 148, 155, 90, 84, 93, 106, 129, 148, 169, 183, 102, 96, 100, 113, 132, 155, 183, 201, + }, { + 31, 38, 47, 48, 54, 61, 66, 69, 38, 47, 47, 46, 50, 55, 61, 65, 47, 47, 53, 55, 58, 63, 65, 66, 48, 46, 55, 62, 67, 72, 73, 73, + 54, 50, 58, 67, 76, 83, 84, 82, 61, 55, 63, 72, 83, 91, 92, 92, 66, 61, 65, 73, 84, 92, 101, 103, 69, 65, 66, 73, 82, 92, 103, 109, + }, + }, { + { + 32, 32, 35, 46, 57, 76, 85, 96, 32, 34, 37, 45, 54, 70, 79, 90, 35, 37, 48, 56, 64, 79, 87, 93, 46, 45, 56, 70, 80, 96, 100, 105, + 57, 54, 64, 80, 93, 111, 121, 122, 76, 70, 79, 96, 111, 134, 138, 144, 85, 79, 87, 100, 121, 138, 156, 168, 96, 90, 93, 105, 122, 144, 168, 184, + }, { + 31, 36, 47, 48, 52, 60, 64, 67, 36, 43, 47, 46, 49, 55, 59, 63, 47, 47, 53, 54, 55, 60, 63, 64, 48, 46, 54, 61, 65, 70, 71, 71, + 52, 49, 55, 65, 71, 78, 81, 79, 60, 55, 60, 70, 78, 89, 89, 89, 64, 59, 63, 71, 81, 89, 97, 99, 67, 63, 64, 71, 79, 89, 99, 104, + }, + }, { + { + 32, 32, 35, 42, 53, 68, 78, 90, 32, 33, 36, 42, 51, 64, 74, 84, 35, 36, 46, 52, 60, 72, 80, 87, 42, 42, 52, 63, 73, 84, 92, 98, + 53, 51, 60, 73, 86, 100, 109, 114, 68, 64, 72, 84, 100, 117, 128, 133, 78, 74, 80, 92, 109, 128, 140, 155, 90, 84, 87, 98, 114, 133, 155, 168, + }, { + 31, 34, 46, 47, 50, 57, 61, 65, 34, 39, 47, 45, 48, 53, 57, 61, 46, 47, 52, 52, 54, 58, 61, 62, 47, 45, 52, 58, 62, 65, 68, 68, + 50, 48, 54, 62, 68, 73, 77, 76, 57, 53, 58, 65, 73, 82, 86, 86, 61, 57, 61, 68, 77, 86, 91, 95, 65, 61, 62, 68, 76, 86, 95, 100, + }, + }, { + { + 32, 32, 34, 39, 50, 62, 76, 84, 32, 33, 35, 40, 48, 59, 71, 79, 34, 35, 39, 46, 53, 63, 74, 81, 39, 40, 46, 56, 65, 75, 86, 92, + 50, 48, 53, 65, 78, 90, 101, 106, 62, 59, 63, 75, 90, 105, 118, 123, 76, 71, 74, 86, 101, 118, 134, 142, 84, 79, 81, 92, 106, 123, 142, 153, + }, { + 31, 34, 42, 47, 49, 54, 60, 64, 34, 39, 45, 46, 47, 51, 56, 59, 42, 45, 48, 49, 50, 53, 57, 60, 47, 46, 49, 55, 58, 61, 65, 66, + 49, 47, 50, 58, 65, 69, 73, 74, 54, 51, 53, 61, 69, 76, 82, 83, 60, 56, 57, 65, 73, 82, 89, 92, 64, 59, 60, 66, 74, 83, 92, 96, + }, + }, { + { + 32, 32, 34, 38, 46, 56, 68, 78, 32, 33, 35, 39, 45, 54, 64, 74, 34, 35, 39, 45, 51, 58, 68, 76, 38, 39, 45, 54, 61, 69, 78, 86, + 46, 45, 51, 61, 71, 80, 90, 99, 56, 54, 58, 69, 80, 92, 103, 113, 68, 64, 68, 78, 90, 103, 117, 128, 78, 74, 76, 86, 99, 113, 128, 140, + }, { + 31, 34, 42, 47, 48, 52, 57, 61, 34, 39, 45, 46, 46, 49, 53, 57, 42, 45, 48, 49, 50, 52, 55, 58, 47, 46, 49, 54, 56, 58, 61, 64, + 48, 46, 50, 56, 61, 65, 68, 71, 52, 49, 52, 58, 65, 71, 75, 79, 57, 53, 55, 61, 68, 75, 82, 86, 61, 57, 58, 64, 71, 79, 86, 91, + }, + }, { + { + 31, 32, 32, 35, 42, 51, 59, 69, 32, 32, 33, 35, 41, 49, 56, 65, 32, 33, 35, 38, 43, 49, 56, 64, 35, 35, 38, 48, 54, 59, 66, 73, + 42, 41, 43, 54, 63, 71, 77, 85, 51, 49, 49, 59, 71, 81, 89, 97, 59, 56, 56, 66, 77, 89, 98, 108, 69, 65, 64, 73, 85, 97, 108, 119, + }, { + 31, 32, 38, 48, 47, 50, 53, 57, 32, 35, 42, 47, 45, 47, 50, 54, 38, 42, 47, 48, 45, 47, 49, 52, 48, 47, 48, 53, 53, 54, 56, 58, + 47, 45, 45, 53, 58, 61, 63, 65, 50, 47, 47, 54, 61, 66, 69, 72, 53, 50, 49, 56, 63, 69, 73, 77, 57, 54, 52, 58, 65, 72, 77, 82, + }, + }, { + { + 31, 32, 32, 34, 38, 46, 52, 63, 32, 32, 32, 34, 37, 44, 49, 59, 32, 32, 35, 37, 40, 45, 49, 58, 34, 34, 37, 42, 47, 52, 56, 65, + 38, 37, 40, 47, 54, 60, 65, 73, 46, 44, 45, 52, 60, 69, 75, 84, 52, 49, 49, 56, 65, 75, 82, 92, 63, 59, 58, 65, 73, 84, 92, 105, + }, { + 31, 31, 38, 44, 47, 48, 50, 55, 31, 32, 40, 44, 45, 46, 47, 52, 38, 40, 47, 47, 46, 46, 47, 50, 44, 44, 47, 50, 51, 51, 52, 54, + 47, 45, 46, 51, 54, 56, 57, 60, 48, 46, 46, 51, 56, 61, 63, 66, 50, 47, 47, 52, 57, 63, 66, 70, 55, 52, 50, 54, 60, 66, 70, 76, + }, + }, { + { + 31, 32, 32, 34, 35, 41, 47, 53, 32, 32, 32, 33, 34, 40, 44, 50, 32, 32, 34, 35, 37, 41, 45, 51, 34, 33, 35, 39, 42, 47, 51, 55, + 35, 34, 37, 42, 48, 53, 57, 61, 41, 40, 41, 47, 53, 60, 65, 70, 47, 44, 45, 51, 57, 65, 71, 77, 53, 50, 51, 55, 61, 70, 77, 85, + }, { + 31, 31, 35, 42, 48, 47, 49, 51, 31, 32, 36, 42, 46, 45, 46, 48, 35, 36, 41, 45, 47, 45, 46, 48, 42, 42, 45, 48, 50, 49, 50, 51, + 48, 46, 47, 50, 53, 53, 54, 54, 47, 45, 45, 49, 53, 57, 59, 60, 49, 46, 46, 50, 54, 59, 61, 64, 51, 48, 48, 51, 54, 60, 64, 68, + }, + }, { + { + 31, 31, 32, 32, 34, 37, 43, 47, 31, 32, 32, 32, 34, 36, 41, 44, 32, 32, 33, 34, 35, 38, 42, 45, 32, 32, 34, 35, 37, 39, 42, 46, + 34, 34, 35, 37, 41, 45, 49, 52, 37, 36, 38, 39, 45, 51, 56, 59, 43, 41, 42, 42, 49, 56, 63, 67, 47, 44, 45, 46, 52, 59, 67, 71, + }, { + 31, 31, 34, 37, 43, 48, 47, 49, 31, 32, 35, 40, 43, 46, 45, 46, 34, 35, 39, 43, 45, 46, 45, 46, 37, 40, 43, 47, 47, 47, 45, 46, + 43, 43, 45, 47, 49, 50, 50, 50, 48, 46, 46, 47, 50, 53, 55, 55, 47, 45, 45, 45, 50, 55, 58, 60, 49, 46, 46, 46, 50, 55, 60, 61, + }, + }, { + { + 31, 31, 32, 32, 33, 34, 37, 41, 31, 32, 32, 32, 33, 34, 36, 39, 32, 32, 32, 33, 34, 35, 37, 40, 32, 32, 33, 34, 35, 36, 38, 41, + 33, 33, 34, 35, 37, 39, 41, 44, 34, 34, 35, 36, 39, 43, 46, 49, 37, 36, 37, 38, 41, 46, 51, 54, 41, 39, 40, 41, 44, 49, 54, 58, + }, { + 31, 31, 32, 35, 39, 45, 48, 48, 31, 31, 33, 37, 41, 44, 46, 46, 32, 33, 35, 39, 42, 45, 46, 45, 35, 37, 39, 43, 45, 47, 47, 46, + 39, 41, 42, 45, 47, 48, 48, 47, 45, 44, 45, 47, 48, 50, 51, 51, 48, 46, 46, 47, 48, 51, 53, 54, 48, 46, 45, 46, 47, 51, 54, 56, + }, + }, { + { + 31, 31, 31, 32, 32, 32, 34, 35, 31, 32, 32, 32, 32, 33, 34, 35, 31, 32, 32, 32, 32, 33, 33, 34, 32, 32, 32, 33, 34, 34, 35, 36, + 32, 32, 32, 34, 35, 35, 36, 38, 32, 33, 33, 34, 35, 36, 38, 40, 34, 34, 33, 35, 36, 38, 39, 42, 35, 35, 34, 36, 38, 40, 42, 48, + }, { + 31, 31, 30, 34, 36, 39, 42, 48, 31, 31, 31, 34, 37, 40, 42, 47, 30, 31, 32, 35, 39, 41, 42, 46, 34, 34, 35, 39, 42, 44, 45, 47, + 36, 37, 39, 42, 46, 47, 47, 47, 39, 40, 41, 44, 47, 47, 48, 49, 42, 42, 42, 45, 47, 48, 48, 50, 48, 47, 46, 47, 47, 49, 50, 53, + }, + }, { + { + 31, 31, 31, 31, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, + 32, 32, 32, 32, 33, 33, 34, 35, 32, 32, 32, 32, 33, 34, 34, 35, 32, 32, 32, 32, 34, 34, 35, 36, 33, 33, 33, 33, 35, 35, 36, 38, + }, { + 31, 31, 31, 30, 34, 35, 37, 40, 31, 31, 31, 31, 34, 35, 38, 41, 31, 31, 31, 31, 35, 36, 39, 41, 30, 31, 31, 32, 35, 36, 40, 42, + 34, 34, 35, 35, 39, 40, 43, 44, 35, 35, 36, 36, 40, 41, 44, 45, 37, 38, 39, 40, 43, 44, 47, 47, 40, 41, 41, 42, 44, 45, 47, 48, + }, + }, { + { + 31, 31, 31, 31, 31, 31, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 32, 32, 32, 32, 32, 32, 33, 33, + }, { + 31, 31, 31, 31, 30, 31, 33, 33, 31, 31, 31, 31, 31, 32, 34, 34, 31, 31, 31, 31, 31, 32, 34, 34, 31, 31, 31, 31, 31, 32, 35, 35, + 30, 31, 31, 31, 32, 32, 35, 35, 31, 32, 32, 32, 32, 33, 36, 36, 33, 34, 34, 35, 35, 36, 39, 39, 33, 34, 34, 35, 35, 36, 39, 39, + }, + }, { + { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + }, { + 31, 31, 31, 31, 31, 31, 31, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 31, 31, 31, 31, 31, 31, 31, + }, + }, +}; + +static const uint8_t qm_tbl_8x16[15][2][128] = { + { + { + 32, 32, 36, 53, 65, 87, 93, 99, 31, 33, 34, 49, 59, 78, 86, 93, + 32, 34, 36, 50, 59, 77, 82, 89, 34, 37, 42, 54, 63, 79, 80, 88, + 36, 38, 48, 60, 68, 84, 86, 90, 44, 43, 53, 71, 79, 95, 94, 97, + 48, 46, 56, 76, 85, 102, 105, 105, 58, 54, 63, 87, 98, 116, 112, 115, + 65, 58, 68, 92, 105, 124, 122, 124, 79, 70, 79, 104, 118, 141, 135, 135, + 82, 72, 81, 106, 121, 144, 149, 146, 91, 80, 88, 106, 130, 148, 162, 159, + 97, 86, 94, 107, 128, 157, 167, 171, 103, 93, 98, 114, 131, 150, 174, 186, + 110, 100, 101, 117, 138, 161, 183, 193, 118, 107, 105, 118, 136, 157, 182, 203, + }, { + 32, 37, 48, 52, 57, 66, 68, 71, 30, 40, 46, 48, 52, 60, 63, 66, + 33, 43, 47, 47, 51, 59, 60, 63, 42, 47, 50, 50, 53, 60, 59, 62, + 49, 48, 53, 54, 57, 62, 62, 62, 49, 46, 53, 61, 64, 69, 66, 66, + 50, 46, 54, 64, 67, 73, 72, 70, 54, 49, 55, 68, 73, 80, 76, 75, + 57, 50, 56, 70, 76, 84, 80, 79, 63, 55, 60, 75, 82, 92, 87, 84, + 64, 56, 61, 75, 83, 93, 93, 89, 68, 59, 64, 74, 86, 94, 98, 94, + 70, 62, 66, 73, 83, 96, 99, 98, 72, 64, 66, 75, 83, 92, 101, 104, + 74, 67, 66, 74, 84, 94, 103, 106, 76, 69, 67, 73, 82, 91, 101, 109, + }, + }, { + { + 32, 32, 36, 47, 65, 79, 90, 96, 31, 32, 35, 44, 60, 72, 84, 90, + 32, 34, 36, 45, 59, 71, 80, 87, 32, 35, 40, 47, 60, 71, 78, 85, + 36, 37, 48, 56, 68, 78, 83, 87, 39, 40, 50, 60, 73, 84, 91, 94, + 47, 45, 56, 69, 84, 95, 101, 101, 53, 50, 60, 75, 92, 103, 108, 110, + 61, 56, 65, 81, 100, 113, 116, 118, 71, 64, 73, 89, 111, 125, 129, 129, + 79, 70, 79, 95, 118, 133, 142, 138, 86, 76, 84, 100, 124, 140, 153, 150, + 92, 82, 89, 101, 121, 148, 157, 161, 98, 88, 93, 108, 124, 141, 163, 174, + 104, 94, 95, 110, 129, 151, 171, 181, 110, 100, 98, 111, 127, 147, 169, 188, + }, { + 32, 35, 48, 50, 57, 63, 68, 70, 30, 38, 46, 46, 52, 58, 63, 65, + 33, 41, 47, 46, 51, 56, 60, 63, 39, 46, 48, 47, 51, 55, 58, 61, + 49, 48, 53, 54, 57, 60, 61, 61, 48, 46, 53, 56, 60, 64, 65, 65, + 50, 46, 54, 61, 66, 70, 71, 69, 52, 47, 54, 63, 71, 75, 75, 74, + 55, 49, 56, 65, 74, 79, 79, 78, 60, 53, 58, 68, 79, 85, 85, 82, + 63, 55, 60, 70, 82, 89, 91, 87, 66, 58, 62, 72, 84, 91, 95, 91, + 68, 60, 64, 71, 81, 94, 97, 96, 70, 62, 65, 73, 81, 89, 98, 101, + 72, 65, 65, 72, 82, 92, 100, 103, 74, 67, 65, 71, 79, 89, 98, 105, + }, + }, { + { + 32, 32, 36, 44, 58, 79, 88, 93, 31, 32, 35, 41, 54, 73, 81, 88, + 32, 33, 36, 42, 53, 71, 78, 84, 32, 34, 38, 42, 52, 69, 76, 82, + 34, 36, 44, 50, 59, 75, 81, 84, 39, 39, 50, 58, 68, 84, 88, 90, + 44, 42, 53, 63, 74, 90, 97, 97, 49, 46, 57, 67, 81, 97, 104, 105, + 57, 53, 63, 74, 90, 108, 111, 113, 65, 59, 68, 79, 97, 118, 123, 122, + 71, 64, 73, 84, 102, 125, 135, 131, 81, 72, 80, 91, 110, 135, 145, 141, + 87, 77, 85, 96, 114, 140, 148, 151, 92, 83, 88, 102, 117, 133, 153, 163, + 98, 88, 89, 103, 121, 141, 160, 169, 103, 94, 92, 103, 119, 137, 158, 175, + }, { + 32, 34, 48, 49, 54, 63, 67, 69, 31, 36, 46, 46, 50, 58, 62, 65, + 33, 40, 47, 46, 49, 56, 59, 62, 37, 44, 47, 45, 48, 54, 57, 60, + 44, 46, 51, 51, 53, 59, 60, 61, 48, 46, 53, 56, 58, 64, 64, 64, + 49, 45, 53, 58, 62, 67, 70, 68, 51, 47, 54, 60, 65, 71, 73, 72, + 54, 49, 55, 62, 70, 77, 77, 76, 57, 51, 56, 64, 73, 82, 83, 81, + 60, 53, 58, 65, 75, 85, 89, 85, 64, 57, 61, 68, 78, 89, 93, 89, + 66, 59, 63, 69, 79, 91, 94, 93, 68, 61, 63, 71, 79, 87, 96, 98, + 70, 63, 63, 70, 80, 89, 97, 100, 72, 65, 63, 69, 77, 86, 95, 102, + }, + }, { + { + 32, 31, 35, 44, 53, 65, 82, 90, 31, 32, 34, 41, 50, 61, 76, 85, + 31, 33, 35, 42, 49, 59, 73, 81, 32, 34, 37, 42, 49, 58, 71, 79, + 34, 35, 41, 48, 54, 63, 76, 81, 36, 36, 46, 54, 60, 68, 80, 87, + 41, 40, 49, 60, 67, 76, 88, 93, 47, 44, 53, 66, 75, 84, 97, 101, + 53, 50, 57, 71, 82, 92, 106, 108, 58, 54, 61, 75, 87, 98, 112, 116, + 65, 59, 66, 79, 92, 105, 120, 124, 74, 67, 73, 86, 100, 113, 131, 134, + 82, 73, 79, 92, 105, 120, 139, 142, 87, 78, 83, 96, 110, 125, 144, 153, + 92, 83, 84, 97, 114, 132, 150, 157, 97, 88, 86, 97, 111, 128, 147, 163, + }, { + 32, 33, 45, 49, 52, 57, 64, 68, 31, 34, 45, 46, 49, 53, 60, 64, + 33, 37, 46, 45, 47, 51, 57, 61, 37, 43, 47, 45, 47, 50, 55, 59, + 42, 44, 49, 49, 50, 53, 58, 60, 49, 47, 52, 53, 54, 57, 61, 63, + 48, 46, 51, 57, 59, 61, 66, 67, 50, 46, 52, 59, 63, 66, 71, 71, + 52, 47, 53, 61, 66, 71, 75, 74, 54, 49, 54, 62, 68, 73, 79, 79, + 57, 51, 55, 64, 70, 76, 83, 83, 61, 55, 58, 66, 73, 80, 87, 87, + 64, 57, 60, 68, 75, 83, 91, 91, 66, 59, 61, 69, 77, 84, 93, 95, + 68, 61, 61, 68, 77, 86, 94, 97, 70, 63, 61, 67, 75, 83, 92, 98, + }, + }, { + { + 32, 31, 33, 40, 51, 65, 79, 87, 31, 32, 33, 39, 49, 61, 74, 82, + 31, 32, 34, 38, 47, 59, 71, 79, 32, 33, 36, 40, 48, 58, 69, 77, + 33, 34, 38, 44, 52, 62, 72, 78, 36, 35, 42, 51, 58, 68, 78, 84, + 39, 38, 44, 54, 63, 73, 84, 89, 44, 41, 46, 59, 69, 79, 90, 96, + 48, 45, 50, 62, 74, 85, 96, 103, 53, 49, 53, 66, 79, 92, 103, 111, + 58, 54, 57, 70, 84, 98, 110, 118, 66, 60, 63, 75, 90, 106, 119, 126, + 74, 67, 69, 81, 97, 113, 128, 134, 81, 73, 75, 86, 102, 120, 135, 143, + 86, 78, 78, 90, 106, 124, 140, 147, 91, 82, 80, 90, 103, 119, 137, 151, + }, { + 32, 32, 40, 49, 51, 57, 63, 67, 31, 33, 41, 47, 49, 54, 59, 63, + 31, 35, 43, 46, 47, 51, 57, 60, 35, 39, 46, 46, 47, 50, 55, 58, + 41, 43, 48, 49, 49, 52, 57, 59, 49, 47, 50, 53, 54, 57, 60, 62, + 48, 46, 49, 54, 57, 60, 64, 65, 49, 45, 48, 56, 61, 64, 67, 69, + 50, 46, 49, 57, 63, 67, 71, 73, 52, 48, 50, 58, 65, 71, 75, 77, + 54, 50, 51, 59, 67, 73, 78, 81, 57, 52, 53, 61, 69, 77, 82, 85, + 61, 55, 56, 63, 72, 80, 86, 88, 64, 58, 58, 65, 73, 82, 89, 92, + 66, 59, 59, 66, 75, 84, 91, 94, 68, 61, 59, 65, 72, 81, 89, 95, + }, + }, { + { + 32, 31, 32, 36, 44, 53, 65, 79, 31, 32, 32, 35, 42, 51, 62, 75, + 31, 32, 33, 34, 41, 49, 59, 72, 32, 32, 34, 36, 42, 50, 59, 71, + 32, 33, 35, 38, 42, 49, 58, 69, 34, 34, 37, 42, 48, 54, 63, 73, + 36, 34, 38, 48, 54, 60, 68, 78, 39, 37, 40, 50, 58, 65, 73, 84, + 44, 41, 43, 53, 63, 71, 79, 90, 48, 45, 46, 56, 67, 76, 85, 96, + 53, 49, 50, 60, 71, 82, 92, 103, 58, 54, 54, 63, 75, 87, 98, 110, + 65, 60, 58, 68, 79, 92, 105, 118, 71, 65, 63, 73, 84, 97, 111, 125, + 79, 72, 70, 79, 90, 104, 118, 133, 82, 75, 72, 81, 92, 106, 121, 136, + }, { + 32, 31, 37, 48, 49, 52, 57, 63, 31, 31, 38, 47, 47, 50, 54, 60, + 30, 32, 40, 46, 45, 48, 52, 57, 33, 36, 43, 47, 46, 47, 51, 56, + 37, 40, 47, 47, 45, 47, 50, 54, 42, 43, 47, 50, 49, 50, 53, 57, + 49, 46, 48, 53, 53, 54, 57, 60, 48, 46, 47, 53, 56, 57, 60, 64, + 49, 45, 46, 53, 58, 61, 64, 67, 50, 46, 46, 54, 59, 64, 67, 71, + 52, 48, 47, 54, 61, 66, 71, 75, 54, 50, 49, 55, 62, 68, 73, 78, + 57, 52, 50, 56, 64, 70, 76, 82, 60, 54, 52, 58, 65, 72, 79, 85, + 63, 57, 55, 60, 67, 75, 82, 89, 64, 59, 56, 61, 68, 75, 83, 90, + }, + }, { + { + 32, 31, 32, 36, 44, 53, 62, 73, 31, 32, 32, 35, 42, 51, 59, 69, + 31, 32, 33, 34, 41, 49, 57, 66, 32, 32, 34, 36, 42, 50, 57, 65, + 32, 33, 35, 38, 42, 49, 56, 64, 34, 34, 37, 42, 48, 54, 61, 69, + 35, 34, 38, 47, 52, 59, 65, 73, 38, 36, 40, 49, 56, 63, 69, 77, + 41, 39, 41, 51, 60, 67, 74, 81, 44, 42, 43, 54, 64, 72, 79, 86, + 48, 45, 46, 56, 67, 76, 83, 91, 53, 49, 50, 60, 71, 82, 90, 99, + 58, 54, 54, 63, 75, 87, 95, 105, 65, 60, 58, 68, 79, 92, 102, 112, + 71, 65, 63, 73, 84, 97, 108, 119, 79, 72, 70, 79, 90, 104, 115, 127, + }, { + 32, 31, 37, 48, 49, 52, 56, 61, 31, 31, 38, 47, 47, 50, 53, 57, + 30, 32, 40, 46, 45, 48, 51, 55, 33, 36, 43, 47, 46, 47, 50, 54, + 37, 40, 47, 47, 45, 47, 49, 52, 42, 43, 47, 50, 49, 50, 53, 56, + 47, 46, 48, 52, 53, 53, 55, 58, 48, 46, 47, 53, 55, 56, 58, 61, + 48, 45, 46, 53, 57, 59, 61, 63, 49, 45, 46, 53, 58, 62, 64, 66, + 50, 46, 46, 54, 59, 64, 66, 69, 52, 48, 47, 54, 61, 66, 70, 73, + 54, 50, 49, 55, 62, 68, 72, 76, 57, 52, 50, 56, 64, 70, 75, 79, + 60, 54, 52, 58, 65, 72, 77, 82, 63, 57, 55, 60, 67, 75, 80, 86, + }, + }, { + { + 32, 31, 32, 35, 39, 44, 53, 65, 31, 32, 32, 35, 38, 42, 51, 62, + 31, 32, 33, 34, 37, 41, 49, 59, 31, 32, 34, 35, 38, 42, 49, 59, + 32, 32, 34, 36, 39, 42, 49, 58, 32, 33, 35, 37, 40, 42, 49, 58, + 34, 34, 37, 41, 44, 48, 54, 63, 36, 34, 38, 46, 50, 54, 60, 68, + 38, 37, 40, 47, 52, 57, 64, 72, 41, 39, 41, 49, 54, 60, 67, 76, + 44, 41, 43, 51, 57, 63, 71, 79, 48, 45, 46, 54, 60, 67, 76, 85, + 53, 49, 50, 57, 64, 71, 82, 92, 57, 53, 53, 60, 67, 74, 86, 97, + 61, 56, 56, 63, 69, 77, 89, 100, 65, 60, 58, 66, 72, 79, 92, 105, + }, { + 32, 31, 37, 45, 48, 49, 52, 57, 31, 31, 38, 45, 47, 47, 50, 54, + 30, 32, 40, 44, 45, 45, 48, 52, 33, 35, 42, 46, 46, 45, 47, 51, + 35, 37, 44, 46, 46, 45, 47, 51, 37, 40, 47, 47, 47, 45, 47, 50, + 42, 43, 47, 49, 50, 49, 50, 53, 49, 46, 48, 52, 53, 53, 54, 57, + 48, 46, 47, 51, 54, 55, 57, 59, 48, 45, 46, 51, 54, 57, 59, 61, + 49, 45, 46, 51, 55, 58, 61, 64, 50, 46, 46, 52, 56, 59, 64, 67, + 52, 48, 47, 53, 57, 61, 66, 71, 54, 49, 48, 54, 58, 62, 68, 73, + 55, 51, 49, 54, 58, 63, 69, 74, 57, 52, 50, 55, 59, 64, 70, 76, + }, + }, { + { + 32, 31, 32, 32, 36, 44, 47, 53, 31, 32, 32, 33, 35, 42, 45, 51, + 31, 32, 32, 33, 35, 41, 44, 49, 31, 32, 33, 33, 35, 41, 44, 49, + 32, 32, 34, 34, 36, 42, 45, 50, 32, 33, 35, 36, 38, 42, 45, 49, + 32, 33, 35, 36, 40, 44, 47, 51, 34, 34, 36, 38, 42, 48, 50, 54, + 36, 34, 37, 40, 48, 54, 56, 60, 38, 36, 39, 41, 49, 56, 58, 63, + 39, 37, 40, 42, 50, 58, 60, 65, 44, 41, 42, 45, 53, 63, 66, 71, + 47, 44, 45, 47, 56, 66, 69, 75, 49, 46, 47, 48, 57, 67, 71, 77, + 53, 49, 50, 51, 60, 71, 75, 82, 58, 54, 54, 55, 63, 75, 79, 87, + }, { + 32, 31, 35, 38, 48, 49, 50, 52, 31, 31, 37, 40, 47, 47, 48, 50, + 30, 32, 38, 40, 46, 45, 46, 48, 31, 33, 38, 41, 46, 45, 46, 48, + 33, 36, 41, 44, 47, 46, 46, 47, 37, 40, 45, 47, 47, 45, 46, 47, + 39, 41, 46, 47, 48, 47, 47, 48, 42, 43, 46, 48, 50, 49, 50, 50, + 49, 46, 48, 49, 53, 53, 54, 54, 48, 46, 47, 48, 53, 55, 55, 56, + 48, 46, 46, 48, 53, 56, 56, 57, 49, 45, 45, 47, 53, 58, 59, 61, + 50, 46, 46, 48, 54, 59, 61, 63, 51, 47, 47, 48, 54, 60, 61, 64, + 52, 48, 47, 48, 54, 61, 63, 66, 54, 50, 49, 50, 55, 62, 65, 68, + }, + }, { + { + 32, 31, 31, 32, 35, 36, 44, 47, 31, 32, 32, 32, 35, 35, 42, 45, + 31, 32, 32, 32, 34, 35, 41, 45, 31, 32, 32, 33, 34, 34, 41, 44, + 31, 32, 33, 34, 35, 36, 42, 44, 32, 32, 33, 34, 36, 36, 42, 45, + 32, 33, 34, 35, 37, 38, 42, 45, 32, 33, 34, 36, 39, 40, 44, 47, + 34, 34, 35, 37, 41, 42, 48, 50, 35, 34, 36, 38, 45, 47, 52, 55, + 36, 34, 36, 38, 46, 48, 54, 56, 39, 37, 39, 40, 48, 50, 58, 60, + 41, 39, 40, 41, 49, 51, 60, 62, 44, 41, 42, 43, 51, 53, 63, 66, + 47, 44, 44, 45, 53, 56, 66, 69, 48, 45, 45, 46, 54, 56, 67, 70, + }, { + 32, 31, 33, 37, 45, 48, 49, 50, 31, 31, 34, 38, 45, 47, 47, 48, + 31, 32, 34, 39, 45, 46, 46, 47, 30, 32, 35, 40, 44, 46, 45, 46, + 33, 35, 37, 42, 46, 47, 45, 46, 33, 36, 38, 43, 46, 47, 46, 46, + 37, 40, 43, 47, 47, 47, 45, 46, 39, 41, 43, 47, 48, 48, 47, 47, + 42, 43, 44, 47, 49, 50, 49, 50, 47, 46, 46, 48, 51, 52, 53, 53, + 49, 46, 47, 48, 52, 53, 53, 54, 48, 46, 46, 47, 51, 53, 56, 56, + 48, 45, 46, 46, 51, 53, 57, 57, 49, 45, 45, 46, 51, 53, 58, 59, + 50, 46, 46, 46, 52, 54, 59, 61, 50, 46, 46, 46, 52, 54, 59, 61, + }, + }, { + { + 32, 31, 31, 32, 32, 36, 36, 44, 31, 32, 32, 32, 32, 35, 35, 42, + 31, 32, 32, 32, 32, 35, 35, 42, 31, 32, 32, 33, 33, 34, 34, 41, + 31, 32, 32, 33, 33, 34, 34, 41, 32, 32, 32, 34, 34, 36, 36, 42, + 32, 32, 32, 34, 34, 36, 36, 42, 32, 33, 33, 35, 35, 38, 38, 42, + 32, 33, 33, 35, 35, 38, 38, 42, 34, 34, 34, 37, 37, 42, 42, 48, + 34, 34, 34, 37, 37, 42, 42, 48, 36, 34, 34, 38, 38, 48, 48, 54, + 36, 34, 34, 38, 38, 48, 48, 54, 39, 37, 37, 40, 40, 50, 50, 58, + 39, 37, 37, 40, 40, 50, 50, 58, 44, 41, 41, 43, 43, 53, 53, 63, + }, { + 32, 31, 31, 37, 37, 48, 48, 49, 31, 31, 31, 38, 38, 47, 47, 47, + 31, 31, 31, 38, 38, 47, 47, 47, 30, 32, 32, 40, 40, 46, 46, 45, + 30, 32, 32, 40, 40, 46, 46, 45, 33, 36, 36, 43, 43, 47, 47, 46, + 33, 36, 36, 43, 43, 47, 47, 46, 37, 40, 40, 47, 47, 47, 47, 45, + 37, 40, 40, 47, 47, 47, 47, 45, 42, 43, 43, 47, 47, 50, 50, 49, + 42, 43, 43, 47, 47, 50, 50, 49, 49, 46, 46, 48, 48, 53, 53, 53, + 49, 46, 46, 48, 48, 53, 53, 53, 48, 46, 46, 47, 47, 53, 53, 56, + 48, 46, 46, 47, 47, 53, 53, 56, 49, 45, 45, 46, 46, 53, 53, 58, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 35, 36, 31, 32, 32, 32, 32, 32, 35, 35, + 31, 32, 32, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 34, 35, + 31, 32, 32, 32, 33, 33, 34, 34, 31, 32, 32, 32, 33, 33, 34, 34, + 31, 32, 32, 33, 34, 34, 35, 36, 32, 32, 32, 33, 34, 34, 36, 36, + 32, 32, 32, 33, 34, 34, 36, 37, 32, 32, 33, 34, 35, 35, 37, 38, + 32, 32, 33, 34, 35, 35, 37, 38, 33, 33, 33, 35, 36, 36, 40, 41, + 34, 34, 34, 35, 37, 37, 41, 42, 34, 34, 34, 35, 37, 37, 43, 44, + 36, 35, 34, 36, 38, 38, 46, 48, 36, 35, 34, 36, 38, 38, 46, 48, + }, { + 32, 31, 31, 33, 37, 37, 45, 48, 31, 31, 31, 34, 38, 38, 45, 47, + 31, 31, 31, 34, 38, 38, 45, 47, 31, 31, 32, 34, 39, 39, 45, 46, + 30, 32, 32, 35, 40, 40, 44, 46, 30, 32, 32, 35, 40, 40, 44, 46, + 33, 34, 35, 37, 42, 42, 46, 47, 33, 35, 36, 38, 43, 43, 46, 47, + 35, 37, 37, 40, 44, 44, 46, 47, 37, 39, 40, 43, 47, 47, 47, 47, + 37, 39, 40, 43, 47, 47, 47, 47, 41, 42, 42, 44, 47, 47, 49, 49, + 42, 42, 43, 44, 47, 47, 49, 50, 44, 44, 44, 45, 47, 47, 50, 51, + 49, 47, 46, 47, 48, 48, 52, 53, 49, 47, 46, 47, 48, 48, 52, 53, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 33, + 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, + 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 33, 33, 33, + 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 32, 33, 33, 33, + 31, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 34, 34, 34, + 32, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 35, 35, 35, + 32, 32, 33, 33, 34, 35, 35, 36, 32, 32, 33, 33, 34, 35, 35, 36, + 32, 33, 33, 33, 34, 36, 36, 36, 34, 34, 34, 34, 35, 37, 37, 38, + }, { + 32, 31, 31, 31, 33, 37, 37, 38, 31, 31, 31, 31, 33, 38, 38, 39, + 31, 31, 31, 31, 34, 38, 38, 40, 31, 31, 31, 31, 34, 38, 38, 40, + 31, 31, 32, 32, 34, 39, 39, 40, 30, 31, 32, 32, 35, 40, 40, 41, + 30, 31, 32, 32, 35, 40, 40, 41, 31, 32, 33, 33, 35, 40, 40, 41, + 33, 34, 35, 35, 37, 42, 42, 43, 33, 35, 36, 36, 38, 43, 43, 44, + 33, 35, 36, 36, 38, 43, 43, 44, 35, 37, 38, 38, 41, 45, 45, 46, + 37, 39, 40, 40, 43, 47, 47, 47, 37, 39, 40, 40, 43, 47, 47, 47, + 39, 40, 41, 41, 43, 47, 47, 47, 42, 42, 43, 43, 44, 47, 47, 48, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 33, 33, + 31, 32, 32, 32, 32, 32, 33, 33, 32, 32, 32, 32, 32, 32, 33, 34, + 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, + }, { + 32, 31, 31, 31, 31, 31, 33, 35, 31, 31, 31, 31, 31, 31, 33, 36, + 31, 31, 31, 31, 31, 31, 34, 36, 31, 31, 31, 31, 31, 31, 34, 37, + 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, + 31, 31, 31, 32, 32, 32, 34, 37, 30, 31, 31, 32, 32, 32, 34, 38, + 30, 31, 32, 32, 32, 32, 35, 38, 30, 31, 32, 32, 32, 32, 35, 38, + 30, 31, 32, 32, 32, 32, 35, 38, 31, 32, 33, 33, 33, 33, 36, 39, + 33, 34, 34, 35, 35, 35, 37, 40, 33, 34, 35, 36, 36, 36, 38, 41, + 33, 34, 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, + 30, 31, 31, 31, 31, 32, 32, 32, 30, 31, 31, 31, 32, 32, 32, 32, + }, + }, +}; + +static const uint8_t qm_tbl_8x32[15][2][256] = { + { + { + 32, 32, 36, 53, 65, 87, 93, 99, 31, 32, 35, 51, 62, 82, 88, 94, 31, 33, 34, 49, 59, 78, 86, 93, 31, 33, 35, 49, 59, 78, 84, 90, + 32, 34, 36, 50, 59, 77, 82, 89, 32, 35, 38, 49, 58, 75, 82, 89, 34, 37, 42, 54, 63, 79, 80, 88, 35, 37, 45, 57, 65, 82, 84, 87, + 36, 38, 48, 60, 68, 84, 86, 90, 39, 40, 50, 65, 73, 89, 91, 93, 44, 43, 53, 71, 79, 95, 94, 97, 46, 44, 55, 73, 82, 98, 98, 99, + 48, 46, 56, 76, 85, 102, 105, 105, 53, 50, 60, 82, 92, 109, 107, 107, 58, 54, 63, 87, 98, 116, 112, 115, 61, 56, 66, 89, 101, 120, 119, 116, + 65, 58, 68, 92, 105, 124, 122, 124, 71, 63, 73, 97, 111, 132, 130, 127, 79, 70, 79, 104, 118, 141, 135, 135, 81, 71, 80, 105, 119, 142, 140, 139, + 82, 72, 81, 106, 121, 144, 149, 146, 88, 77, 85, 108, 126, 149, 153, 152, 91, 80, 88, 106, 130, 148, 162, 159, 94, 83, 91, 105, 131, 153, 165, 166, + 97, 86, 94, 107, 128, 157, 167, 171, 100, 89, 97, 111, 127, 152, 173, 182, 103, 93, 98, 114, 131, 150, 174, 186, 107, 96, 100, 117, 136, 155, 177, 191, + 110, 100, 101, 117, 138, 161, 183, 193, 114, 104, 103, 117, 137, 159, 185, 201, 118, 107, 105, 118, 136, 157, 182, 203, 122, 111, 107, 119, 136, 156, 179, 204, + }, { + 32, 37, 48, 52, 57, 66, 68, 71, 31, 38, 47, 50, 54, 63, 65, 67, 30, 40, 46, 48, 52, 60, 63, 66, 32, 41, 46, 48, 51, 59, 62, 64, + 33, 43, 47, 47, 51, 59, 60, 63, 37, 47, 47, 47, 50, 57, 60, 62, 42, 47, 50, 50, 53, 60, 59, 62, 45, 47, 51, 52, 55, 61, 61, 61, + 49, 48, 53, 54, 57, 62, 62, 62, 48, 47, 53, 57, 60, 66, 65, 64, 49, 46, 53, 61, 64, 69, 66, 66, 49, 46, 53, 62, 65, 71, 68, 67, + 50, 46, 54, 64, 67, 73, 72, 70, 52, 47, 54, 66, 71, 77, 73, 71, 54, 49, 55, 68, 73, 80, 76, 75, 55, 49, 56, 69, 75, 82, 79, 76, + 57, 50, 56, 70, 76, 84, 80, 79, 60, 52, 58, 72, 79, 88, 84, 81, 63, 55, 60, 75, 82, 92, 87, 84, 64, 55, 61, 75, 82, 92, 89, 86, + 64, 56, 61, 75, 83, 93, 93, 89, 67, 58, 63, 76, 85, 95, 94, 91, 68, 59, 64, 74, 86, 94, 98, 94, 69, 60, 65, 72, 85, 95, 99, 97, + 70, 62, 66, 73, 83, 96, 99, 98, 71, 63, 67, 74, 82, 93, 102, 102, 72, 64, 66, 75, 83, 92, 101, 104, 73, 65, 66, 75, 84, 93, 102, 106, + 74, 67, 66, 74, 84, 94, 103, 106, 75, 68, 66, 74, 83, 93, 103, 109, 76, 69, 67, 73, 82, 91, 101, 109, 77, 70, 67, 73, 81, 90, 99, 108, + }, + }, { + { + 32, 32, 36, 47, 65, 79, 90, 96, 31, 32, 35, 45, 62, 75, 86, 91, 31, 32, 35, 44, 60, 72, 84, 90, 31, 33, 35, 44, 59, 71, 82, 87, + 32, 34, 36, 45, 59, 71, 80, 87, 32, 35, 38, 45, 58, 69, 80, 86, 32, 35, 40, 47, 60, 71, 78, 85, 34, 36, 42, 50, 63, 73, 82, 84, + 36, 37, 48, 56, 68, 78, 83, 87, 38, 39, 49, 58, 71, 81, 88, 90, 39, 40, 50, 60, 73, 84, 91, 94, 44, 42, 53, 66, 79, 90, 94, 96, + 47, 45, 56, 69, 84, 95, 101, 101, 49, 47, 57, 71, 86, 97, 103, 102, 53, 50, 60, 75, 92, 103, 108, 110, 58, 54, 63, 79, 98, 110, 114, 111, + 61, 56, 65, 81, 100, 113, 116, 118, 65, 59, 68, 84, 105, 118, 124, 121, 71, 64, 73, 89, 111, 125, 129, 129, 76, 68, 76, 92, 115, 130, 134, 132, + 79, 70, 79, 95, 118, 133, 142, 138, 82, 73, 81, 97, 121, 136, 145, 144, 86, 76, 84, 100, 124, 140, 153, 150, 89, 79, 87, 99, 124, 145, 156, 156, + 92, 82, 89, 101, 121, 148, 157, 161, 95, 85, 92, 105, 120, 143, 163, 171, 98, 88, 93, 108, 124, 141, 163, 174, 101, 91, 94, 110, 128, 146, 166, 179, + 104, 94, 95, 110, 129, 151, 171, 181, 107, 97, 96, 110, 128, 149, 173, 188, 110, 100, 98, 111, 127, 147, 169, 188, 114, 104, 100, 111, 127, 145, 166, 190, + }, { + 32, 35, 48, 50, 57, 63, 68, 70, 31, 37, 47, 48, 54, 60, 64, 66, 30, 38, 46, 46, 52, 58, 63, 65, 31, 38, 46, 46, 52, 57, 61, 63, + 33, 41, 47, 46, 51, 56, 60, 63, 37, 45, 47, 46, 50, 54, 59, 62, 39, 46, 48, 47, 51, 55, 58, 61, 42, 46, 50, 50, 53, 57, 60, 60, + 49, 48, 53, 54, 57, 60, 61, 61, 48, 47, 53, 55, 58, 62, 64, 63, 48, 46, 53, 56, 60, 64, 65, 65, 49, 45, 53, 59, 64, 67, 67, 66, + 50, 46, 54, 61, 66, 70, 71, 69, 51, 47, 54, 61, 68, 71, 72, 70, 52, 47, 54, 63, 71, 75, 75, 74, 54, 49, 55, 65, 73, 78, 78, 74, + 55, 49, 56, 65, 74, 79, 79, 78, 57, 50, 56, 66, 76, 82, 83, 79, 60, 53, 58, 68, 79, 85, 85, 82, 62, 54, 60, 69, 81, 87, 87, 84, + 63, 55, 60, 70, 82, 89, 91, 87, 64, 56, 61, 71, 83, 90, 92, 89, 66, 58, 62, 72, 84, 91, 95, 91, 67, 59, 63, 71, 83, 93, 96, 94, + 68, 60, 64, 71, 81, 94, 97, 96, 69, 61, 65, 72, 80, 91, 99, 100, 70, 62, 65, 73, 81, 89, 98, 101, 71, 64, 65, 73, 82, 90, 99, 103, + 72, 65, 65, 72, 82, 92, 100, 103, 73, 66, 65, 72, 81, 90, 100, 105, 74, 67, 65, 71, 79, 89, 98, 105, 75, 68, 65, 71, 78, 87, 96, 105, + }, + }, { + { + 32, 32, 36, 44, 58, 79, 88, 93, 31, 32, 35, 42, 55, 75, 83, 88, 31, 32, 35, 41, 54, 73, 81, 88, 31, 32, 34, 41, 53, 72, 79, 84, + 32, 33, 36, 42, 53, 71, 78, 84, 32, 34, 37, 42, 53, 70, 77, 83, 32, 34, 38, 42, 52, 69, 76, 82, 34, 35, 42, 48, 57, 73, 79, 81, + 34, 36, 44, 50, 59, 75, 81, 84, 36, 37, 48, 54, 63, 78, 85, 86, 39, 39, 50, 58, 68, 84, 88, 90, 40, 40, 51, 59, 70, 85, 91, 92, + 44, 42, 53, 63, 74, 90, 97, 97, 47, 45, 56, 66, 79, 95, 99, 98, 49, 46, 57, 67, 81, 97, 104, 105, 53, 50, 60, 71, 86, 103, 109, 106, + 57, 53, 63, 74, 90, 108, 111, 113, 59, 54, 64, 75, 91, 111, 119, 115, 65, 59, 68, 79, 97, 118, 123, 122, 69, 62, 71, 83, 100, 122, 127, 125, + 71, 64, 73, 84, 102, 125, 135, 131, 79, 71, 79, 90, 109, 133, 137, 136, 81, 72, 80, 91, 110, 135, 145, 141, 82, 73, 81, 92, 111, 136, 147, 147, + 87, 77, 85, 96, 114, 140, 148, 151, 90, 80, 87, 99, 113, 135, 153, 160, 92, 83, 88, 102, 117, 133, 153, 163, 95, 85, 88, 103, 120, 137, 155, 168, + 98, 88, 89, 103, 121, 141, 160, 169, 100, 91, 90, 103, 120, 139, 161, 175, 103, 94, 92, 103, 119, 137, 158, 175, 106, 97, 93, 104, 118, 135, 155, 176, + }, { + 32, 34, 48, 49, 54, 63, 67, 69, 31, 35, 47, 47, 51, 60, 63, 65, 31, 36, 46, 46, 50, 58, 62, 65, 30, 36, 46, 45, 49, 57, 60, 62, + 33, 40, 47, 46, 49, 56, 59, 62, 35, 42, 47, 45, 48, 55, 58, 61, 37, 44, 47, 45, 48, 54, 57, 60, 42, 45, 50, 49, 51, 57, 59, 59, + 44, 46, 51, 51, 53, 59, 60, 61, 49, 47, 53, 53, 55, 60, 63, 62, 48, 46, 53, 56, 58, 64, 64, 64, 48, 46, 53, 56, 59, 65, 66, 65, + 49, 45, 53, 58, 62, 67, 70, 68, 50, 46, 54, 59, 65, 70, 70, 68, 51, 47, 54, 60, 65, 71, 73, 72, 52, 47, 54, 61, 68, 75, 76, 73, + 54, 49, 55, 62, 70, 77, 77, 76, 54, 49, 55, 62, 70, 78, 81, 77, 57, 51, 56, 64, 73, 82, 83, 81, 59, 52, 58, 65, 74, 84, 85, 82, + 60, 53, 58, 65, 75, 85, 89, 85, 63, 56, 60, 67, 77, 89, 90, 87, 64, 57, 61, 68, 78, 89, 93, 89, 64, 57, 61, 68, 78, 90, 94, 92, + 66, 59, 63, 69, 79, 91, 94, 93, 67, 60, 63, 70, 78, 88, 96, 97, 68, 61, 63, 71, 79, 87, 96, 98, 69, 62, 63, 71, 80, 88, 96, 100, + 70, 63, 63, 70, 80, 89, 97, 100, 71, 64, 63, 70, 78, 88, 97, 102, 72, 65, 63, 69, 77, 86, 95, 102, 73, 66, 63, 69, 76, 84, 93, 101, + }, + }, { + { + 32, 31, 35, 44, 53, 65, 82, 90, 31, 32, 35, 42, 51, 62, 78, 86, 31, 32, 34, 41, 50, 61, 76, 85, 31, 32, 34, 41, 49, 59, 74, 82, + 31, 33, 35, 42, 49, 59, 73, 81, 32, 33, 36, 42, 50, 59, 73, 80, 32, 34, 37, 42, 49, 58, 71, 79, 32, 34, 39, 44, 51, 60, 73, 78, + 34, 35, 41, 48, 54, 63, 76, 81, 35, 36, 45, 52, 59, 67, 79, 83, 36, 36, 46, 54, 60, 68, 80, 87, 39, 39, 48, 58, 65, 73, 86, 88, + 41, 40, 49, 60, 67, 76, 88, 93, 44, 42, 51, 63, 71, 79, 92, 94, 47, 44, 53, 66, 75, 84, 97, 101, 48, 45, 54, 67, 76, 85, 98, 101, + 53, 50, 57, 71, 82, 92, 106, 108, 55, 51, 59, 72, 84, 94, 108, 110, 58, 54, 61, 75, 87, 98, 112, 116, 63, 58, 65, 78, 91, 103, 118, 119, + 65, 59, 66, 79, 92, 105, 120, 124, 71, 64, 71, 84, 97, 111, 127, 129, 74, 67, 73, 86, 100, 113, 131, 134, 79, 71, 77, 90, 104, 118, 136, 139, + 82, 73, 79, 92, 105, 120, 139, 142, 82, 74, 79, 92, 106, 121, 139, 150, 87, 78, 83, 96, 110, 125, 144, 153, 89, 81, 83, 97, 113, 128, 145, 157, + 92, 83, 84, 97, 114, 132, 150, 157, 94, 85, 85, 97, 112, 130, 151, 163, 97, 88, 86, 97, 111, 128, 147, 163, 99, 91, 87, 97, 110, 126, 144, 163, + }, { + 32, 33, 45, 49, 52, 57, 64, 68, 31, 34, 45, 47, 50, 54, 61, 64, 31, 34, 45, 46, 49, 53, 60, 64, 30, 35, 44, 45, 48, 52, 58, 61, + 33, 37, 46, 45, 47, 51, 57, 61, 33, 38, 46, 46, 47, 51, 57, 60, 37, 43, 47, 45, 47, 50, 55, 59, 39, 43, 48, 47, 48, 51, 56, 58, + 42, 44, 49, 49, 50, 53, 58, 60, 47, 46, 51, 53, 53, 56, 61, 61, 49, 47, 52, 53, 54, 57, 61, 63, 48, 46, 51, 56, 57, 60, 64, 64, + 48, 46, 51, 57, 59, 61, 66, 67, 49, 45, 51, 58, 61, 64, 68, 67, 50, 46, 52, 59, 63, 66, 71, 71, 50, 46, 52, 59, 64, 67, 71, 71, + 52, 47, 53, 61, 66, 71, 75, 74, 53, 48, 53, 61, 67, 72, 77, 75, 54, 49, 54, 62, 68, 73, 79, 79, 56, 51, 55, 63, 70, 76, 82, 80, + 57, 51, 55, 64, 70, 76, 83, 83, 60, 54, 57, 65, 72, 79, 86, 85, 61, 55, 58, 66, 73, 80, 87, 87, 63, 56, 59, 67, 75, 82, 90, 89, + 64, 57, 60, 68, 75, 83, 91, 91, 64, 58, 60, 68, 75, 83, 91, 94, 66, 59, 61, 69, 77, 84, 93, 95, 67, 60, 61, 69, 78, 85, 93, 97, + 68, 61, 61, 68, 77, 86, 94, 97, 69, 62, 61, 68, 76, 85, 94, 99, 70, 63, 61, 67, 75, 83, 92, 98, 70, 64, 61, 67, 74, 82, 90, 98, + }, + }, { + { + 32, 31, 33, 40, 51, 65, 79, 87, 31, 32, 33, 39, 49, 62, 75, 83, 31, 32, 33, 39, 49, 61, 74, 82, 31, 32, 33, 38, 47, 59, 72, 79, + 31, 32, 34, 38, 47, 59, 71, 79, 32, 33, 35, 39, 48, 59, 71, 78, 32, 33, 36, 40, 48, 58, 69, 77, 32, 33, 36, 41, 48, 58, 69, 75, + 33, 34, 38, 44, 52, 62, 72, 78, 34, 34, 39, 45, 53, 63, 73, 80, 36, 35, 42, 51, 58, 68, 78, 84, 36, 35, 42, 51, 59, 68, 79, 85, + 39, 38, 44, 54, 63, 73, 84, 89, 40, 39, 45, 56, 65, 75, 85, 90, 44, 41, 46, 59, 69, 79, 90, 96, 46, 43, 48, 60, 72, 82, 93, 97, + 48, 45, 50, 62, 74, 85, 96, 103, 52, 48, 52, 65, 78, 90, 101, 105, 53, 49, 53, 66, 79, 92, 103, 111, 58, 53, 57, 69, 83, 97, 109, 113, + 58, 54, 57, 70, 84, 98, 110, 118, 65, 59, 62, 74, 89, 105, 118, 122, 66, 60, 63, 75, 90, 106, 119, 126, 71, 65, 67, 79, 94, 111, 125, 131, + 74, 67, 69, 81, 97, 113, 128, 134, 79, 72, 73, 85, 101, 118, 133, 141, 81, 73, 75, 86, 102, 120, 135, 143, 82, 74, 75, 87, 103, 121, 136, 147, + 86, 78, 78, 90, 106, 124, 140, 147, 88, 80, 80, 90, 105, 122, 140, 152, 91, 82, 80, 90, 103, 119, 137, 151, 93, 85, 81, 90, 103, 117, 134, 152, + }, { + 32, 32, 40, 49, 51, 57, 63, 67, 31, 33, 41, 47, 49, 54, 60, 63, 31, 33, 41, 47, 49, 54, 59, 63, 30, 33, 42, 45, 47, 52, 57, 60, + 31, 35, 43, 46, 47, 51, 57, 60, 33, 37, 44, 46, 47, 51, 56, 59, 35, 39, 46, 46, 47, 50, 55, 58, 37, 41, 47, 46, 46, 50, 54, 57, + 41, 43, 48, 49, 49, 52, 57, 59, 42, 43, 48, 49, 50, 53, 57, 60, 49, 47, 50, 53, 54, 57, 60, 62, 49, 47, 50, 53, 54, 57, 61, 63, + 48, 46, 49, 54, 57, 60, 64, 65, 48, 46, 49, 55, 58, 61, 65, 66, 49, 45, 48, 56, 61, 64, 67, 69, 49, 46, 49, 57, 62, 65, 69, 70, + 50, 46, 49, 57, 63, 67, 71, 73, 51, 47, 49, 58, 64, 69, 73, 74, 52, 48, 50, 58, 65, 71, 75, 77, 54, 49, 51, 59, 67, 73, 77, 78, + 54, 50, 51, 59, 67, 73, 78, 81, 57, 52, 52, 60, 69, 76, 82, 83, 57, 52, 53, 61, 69, 77, 82, 85, 60, 54, 55, 62, 71, 79, 85, 87, + 61, 55, 56, 63, 72, 80, 86, 88, 63, 57, 57, 64, 73, 82, 89, 92, 64, 58, 58, 65, 73, 82, 89, 92, 64, 58, 58, 65, 74, 83, 90, 94, + 66, 59, 59, 66, 75, 84, 91, 94, 67, 60, 59, 66, 74, 82, 91, 96, 68, 61, 59, 65, 72, 81, 89, 95, 68, 62, 59, 65, 71, 79, 87, 95, + }, + }, { + { + 32, 31, 32, 36, 44, 53, 65, 79, 31, 32, 32, 35, 42, 51, 62, 75, 31, 32, 32, 35, 42, 51, 62, 75, 31, 32, 33, 34, 41, 49, 59, 72, + 31, 32, 33, 34, 41, 49, 59, 72, 32, 32, 34, 36, 42, 50, 59, 71, 32, 32, 34, 36, 42, 50, 59, 71, 32, 33, 35, 38, 42, 49, 58, 69, + 32, 33, 35, 38, 42, 49, 58, 69, 34, 34, 37, 42, 48, 54, 63, 73, 34, 34, 37, 42, 48, 54, 63, 73, 36, 34, 38, 48, 54, 60, 68, 78, + 36, 34, 38, 48, 54, 60, 68, 78, 39, 37, 40, 50, 58, 65, 73, 84, 39, 37, 40, 50, 58, 65, 73, 84, 44, 41, 43, 53, 63, 71, 79, 90, + 44, 41, 43, 53, 63, 71, 79, 90, 48, 45, 46, 56, 67, 76, 85, 96, 48, 45, 46, 56, 67, 76, 85, 96, 53, 49, 50, 60, 71, 82, 92, 103, + 53, 49, 50, 60, 71, 82, 92, 103, 58, 54, 54, 63, 75, 87, 98, 110, 58, 54, 54, 63, 75, 87, 98, 110, 65, 60, 58, 68, 79, 92, 105, 118, + 65, 60, 58, 68, 79, 92, 105, 118, 71, 65, 63, 73, 84, 97, 111, 125, 71, 65, 63, 73, 84, 97, 111, 125, 79, 72, 70, 79, 90, 104, 118, 133, + 79, 72, 70, 79, 90, 104, 118, 133, 82, 75, 72, 81, 92, 106, 121, 136, 82, 75, 72, 81, 92, 106, 121, 136, 87, 79, 76, 84, 96, 109, 124, 141, + }, { + 32, 31, 37, 48, 49, 52, 57, 63, 31, 31, 38, 47, 47, 50, 54, 60, 31, 31, 38, 47, 47, 50, 54, 60, 30, 32, 40, 46, 45, 48, 52, 57, + 30, 32, 40, 46, 45, 48, 52, 57, 33, 36, 43, 47, 46, 47, 51, 56, 33, 36, 43, 47, 46, 47, 51, 56, 37, 40, 47, 47, 45, 47, 50, 54, + 37, 40, 47, 47, 45, 47, 50, 54, 42, 43, 47, 50, 49, 50, 53, 57, 42, 43, 47, 50, 49, 50, 53, 57, 49, 46, 48, 53, 53, 54, 57, 60, + 49, 46, 48, 53, 53, 54, 57, 60, 48, 46, 47, 53, 56, 57, 60, 64, 48, 46, 47, 53, 56, 57, 60, 64, 49, 45, 46, 53, 58, 61, 64, 67, + 49, 45, 46, 53, 58, 61, 64, 67, 50, 46, 46, 54, 59, 64, 67, 71, 50, 46, 46, 54, 59, 64, 67, 71, 52, 48, 47, 54, 61, 66, 71, 75, + 52, 48, 47, 54, 61, 66, 71, 75, 54, 50, 49, 55, 62, 68, 73, 78, 54, 50, 49, 55, 62, 68, 73, 78, 57, 52, 50, 56, 64, 70, 76, 82, + 57, 52, 50, 56, 64, 70, 76, 82, 60, 54, 52, 58, 65, 72, 79, 85, 60, 54, 52, 58, 65, 72, 79, 85, 63, 57, 55, 60, 67, 75, 82, 89, + 63, 57, 55, 60, 67, 75, 82, 89, 64, 59, 56, 61, 68, 75, 83, 90, 64, 59, 56, 61, 68, 75, 83, 90, 66, 60, 57, 63, 69, 77, 84, 92, + }, + }, { + { + 32, 31, 32, 36, 44, 53, 62, 73, 31, 32, 32, 35, 42, 51, 60, 70, 31, 32, 32, 35, 42, 51, 59, 69, 31, 32, 32, 35, 41, 50, 58, 67, + 31, 32, 33, 34, 41, 49, 57, 66, 31, 32, 33, 35, 41, 49, 57, 66, 32, 32, 34, 36, 42, 50, 57, 65, 32, 32, 34, 37, 42, 49, 56, 65, + 32, 33, 35, 38, 42, 49, 56, 64, 32, 33, 35, 39, 43, 50, 56, 64, 34, 34, 37, 42, 48, 54, 61, 69, 34, 34, 37, 42, 48, 54, 61, 69, + 35, 34, 38, 47, 52, 59, 65, 73, 36, 34, 38, 48, 54, 60, 66, 74, 38, 36, 40, 49, 56, 63, 69, 77, 39, 37, 40, 50, 58, 65, 71, 79, + 41, 39, 41, 51, 60, 67, 74, 81, 44, 41, 43, 53, 63, 71, 78, 85, 44, 42, 43, 54, 64, 72, 79, 86, 48, 45, 46, 56, 67, 76, 83, 91, + 48, 45, 46, 56, 67, 76, 83, 91, 53, 49, 49, 59, 71, 81, 89, 98, 53, 49, 50, 60, 71, 82, 90, 99, 57, 52, 52, 62, 74, 85, 94, 103, + 58, 54, 54, 63, 75, 87, 95, 105, 61, 57, 56, 66, 77, 89, 98, 108, 65, 60, 58, 68, 79, 92, 102, 112, 67, 61, 60, 69, 81, 94, 103, 114, + 71, 65, 63, 73, 84, 97, 108, 119, 72, 66, 64, 73, 85, 98, 108, 119, 79, 72, 70, 79, 90, 104, 115, 127, 79, 72, 70, 79, 90, 104, 115, 127, + }, { + 32, 31, 37, 48, 49, 52, 56, 61, 31, 31, 38, 47, 47, 50, 54, 58, 31, 31, 38, 47, 47, 50, 53, 57, 30, 32, 39, 46, 46, 48, 52, 56, + 30, 32, 40, 46, 45, 48, 51, 55, 32, 34, 41, 46, 45, 48, 51, 54, 33, 36, 43, 47, 46, 47, 50, 54, 34, 37, 44, 47, 45, 47, 50, 53, + 37, 40, 47, 47, 45, 47, 49, 52, 37, 40, 47, 48, 46, 47, 49, 53, 42, 43, 47, 50, 49, 50, 53, 56, 42, 43, 47, 50, 49, 50, 53, 56, + 47, 46, 48, 52, 53, 53, 55, 58, 49, 46, 48, 53, 53, 54, 56, 59, 48, 46, 47, 53, 55, 56, 58, 61, 48, 46, 47, 53, 56, 57, 59, 62, + 48, 45, 46, 53, 57, 59, 61, 63, 49, 45, 46, 53, 58, 61, 63, 66, 49, 45, 46, 53, 58, 62, 64, 66, 50, 46, 46, 54, 59, 64, 66, 69, + 50, 46, 46, 54, 59, 64, 66, 69, 52, 48, 47, 54, 61, 66, 69, 72, 52, 48, 47, 54, 61, 66, 70, 73, 53, 49, 48, 55, 62, 68, 71, 75, + 54, 50, 49, 55, 62, 68, 72, 76, 55, 51, 49, 56, 63, 69, 74, 78, 57, 52, 50, 56, 64, 70, 75, 79, 58, 53, 51, 57, 64, 71, 76, 80, + 60, 54, 52, 58, 65, 72, 77, 82, 60, 55, 53, 59, 65, 73, 78, 83, 63, 57, 55, 60, 67, 75, 80, 86, 63, 57, 55, 60, 67, 75, 80, 86, + }, + }, { + { + 32, 31, 32, 35, 39, 44, 53, 65, 31, 32, 32, 35, 38, 42, 52, 63, 31, 32, 32, 35, 38, 42, 51, 62, 31, 32, 32, 34, 37, 41, 50, 61, + 31, 32, 33, 34, 37, 41, 49, 59, 31, 32, 33, 34, 37, 41, 49, 59, 31, 32, 34, 35, 38, 42, 49, 59, 32, 32, 34, 36, 38, 42, 50, 59, + 32, 32, 34, 36, 39, 42, 49, 58, 32, 33, 35, 37, 40, 42, 49, 58, 32, 33, 35, 37, 40, 42, 49, 58, 33, 33, 36, 40, 43, 46, 53, 62, + 34, 34, 37, 41, 44, 48, 54, 63, 34, 34, 37, 43, 46, 50, 56, 65, 36, 34, 38, 46, 50, 54, 60, 68, 36, 34, 38, 46, 50, 54, 60, 68, + 38, 37, 40, 47, 52, 57, 64, 72, 39, 37, 40, 48, 53, 58, 65, 73, 41, 39, 41, 49, 54, 60, 67, 76, 44, 41, 43, 51, 57, 63, 71, 79, + 44, 41, 43, 51, 57, 63, 71, 79, 47, 44, 45, 53, 59, 66, 75, 84, 48, 45, 46, 54, 60, 67, 76, 85, 50, 46, 47, 55, 61, 68, 78, 88, + 53, 49, 50, 57, 64, 71, 82, 92, 53, 49, 50, 57, 64, 71, 82, 92, 57, 53, 53, 60, 67, 74, 86, 97, 58, 54, 54, 61, 68, 75, 87, 98, + 61, 56, 56, 63, 69, 77, 89, 100, 65, 60, 58, 66, 72, 79, 92, 105, 65, 60, 58, 66, 72, 79, 92, 105, 70, 64, 62, 70, 76, 83, 96, 109, + }, { + 32, 31, 37, 45, 48, 49, 52, 57, 31, 31, 38, 45, 47, 47, 50, 55, 31, 31, 38, 45, 47, 47, 50, 54, 31, 32, 39, 45, 46, 46, 49, 53, + 30, 32, 40, 44, 45, 45, 48, 52, 30, 32, 40, 44, 45, 45, 48, 52, 33, 35, 42, 46, 46, 45, 47, 51, 33, 36, 43, 46, 46, 46, 47, 51, + 35, 37, 44, 46, 46, 45, 47, 51, 37, 40, 47, 47, 47, 45, 47, 50, 37, 40, 47, 47, 47, 45, 47, 50, 41, 42, 47, 49, 49, 48, 50, 52, + 42, 43, 47, 49, 50, 49, 50, 53, 44, 44, 47, 50, 51, 51, 52, 54, 49, 46, 48, 52, 53, 53, 54, 57, 49, 46, 48, 52, 53, 53, 54, 57, + 48, 46, 47, 51, 54, 55, 57, 59, 48, 46, 47, 51, 54, 56, 57, 60, 48, 45, 46, 51, 54, 57, 59, 61, 49, 45, 46, 51, 55, 58, 61, 64, + 49, 45, 46, 51, 55, 58, 61, 64, 50, 46, 46, 52, 56, 59, 63, 66, 50, 46, 46, 52, 56, 59, 64, 67, 51, 47, 47, 52, 56, 60, 65, 68, + 52, 48, 47, 53, 57, 61, 66, 71, 52, 48, 47, 53, 57, 61, 66, 71, 54, 49, 48, 54, 58, 62, 68, 73, 54, 50, 49, 54, 58, 62, 68, 73, + 55, 51, 49, 54, 58, 63, 69, 74, 57, 52, 50, 55, 59, 64, 70, 76, 57, 52, 50, 55, 59, 64, 70, 76, 59, 54, 52, 57, 61, 65, 72, 78, + }, + }, { + { + 32, 31, 32, 32, 36, 44, 47, 53, 31, 32, 32, 33, 35, 43, 46, 52, 31, 32, 32, 33, 35, 42, 45, 51, 31, 32, 32, 33, 35, 42, 45, 51, + 31, 32, 32, 33, 35, 41, 44, 49, 31, 32, 32, 33, 34, 41, 44, 49, 31, 32, 33, 33, 35, 41, 44, 49, 32, 32, 33, 34, 36, 42, 45, 49, + 32, 32, 34, 34, 36, 42, 45, 50, 32, 32, 34, 35, 37, 42, 45, 49, 32, 33, 35, 36, 38, 42, 45, 49, 32, 33, 35, 36, 38, 42, 45, 49, + 32, 33, 35, 36, 40, 44, 47, 51, 34, 34, 36, 38, 42, 48, 50, 54, 34, 34, 36, 38, 42, 48, 50, 54, 35, 34, 37, 39, 45, 50, 53, 57, + 36, 34, 37, 40, 48, 54, 56, 60, 36, 34, 37, 40, 48, 54, 56, 60, 38, 36, 39, 41, 49, 56, 58, 63, 39, 37, 40, 42, 50, 58, 60, 65, + 39, 37, 40, 42, 50, 58, 60, 65, 42, 40, 42, 44, 52, 61, 64, 69, 44, 41, 42, 45, 53, 63, 66, 71, 44, 41, 43, 45, 54, 63, 66, 72, + 47, 44, 45, 47, 56, 66, 69, 75, 48, 45, 46, 48, 56, 67, 70, 76, 49, 46, 47, 48, 57, 67, 71, 77, 53, 49, 49, 51, 59, 71, 74, 81, + 53, 49, 50, 51, 60, 71, 75, 82, 55, 51, 51, 53, 61, 72, 76, 83, 58, 54, 54, 55, 63, 75, 79, 87, 58, 54, 54, 55, 63, 75, 79, 87, + }, { + 32, 31, 35, 38, 48, 49, 50, 52, 31, 31, 36, 39, 47, 48, 49, 50, 31, 31, 37, 40, 47, 47, 48, 50, 31, 31, 37, 40, 47, 47, 48, 50, + 30, 32, 38, 40, 46, 45, 46, 48, 30, 32, 38, 41, 46, 45, 46, 48, 31, 33, 38, 41, 46, 45, 46, 48, 33, 35, 41, 43, 47, 45, 46, 47, + 33, 36, 41, 44, 47, 46, 46, 47, 34, 37, 42, 45, 47, 45, 46, 47, 37, 40, 45, 47, 47, 45, 46, 47, 37, 40, 45, 47, 47, 45, 46, 47, + 39, 41, 46, 47, 48, 47, 47, 48, 42, 43, 46, 48, 50, 49, 50, 50, 42, 43, 46, 48, 50, 49, 50, 50, 45, 44, 47, 48, 51, 51, 52, 52, + 49, 46, 48, 49, 53, 53, 54, 54, 49, 46, 48, 49, 53, 53, 54, 54, 48, 46, 47, 48, 53, 55, 55, 56, 48, 46, 46, 48, 53, 56, 56, 57, + 48, 46, 46, 48, 53, 56, 56, 57, 49, 45, 46, 47, 53, 57, 58, 60, 49, 45, 45, 47, 53, 58, 59, 61, 49, 45, 46, 47, 53, 58, 60, 61, + 50, 46, 46, 48, 54, 59, 61, 63, 50, 46, 46, 48, 54, 59, 61, 64, 51, 47, 47, 48, 54, 60, 61, 64, 52, 48, 47, 48, 54, 61, 63, 66, + 52, 48, 47, 48, 54, 61, 63, 66, 53, 48, 48, 49, 54, 61, 63, 67, 54, 50, 49, 50, 55, 62, 65, 68, 54, 50, 49, 50, 55, 62, 65, 68, + }, + }, { + { + 32, 31, 31, 32, 35, 36, 44, 47, 31, 32, 32, 32, 35, 35, 43, 46, 31, 32, 32, 32, 35, 35, 42, 45, 31, 32, 32, 32, 35, 35, 42, 45, + 31, 32, 32, 32, 34, 35, 41, 45, 31, 32, 32, 33, 34, 34, 41, 44, 31, 32, 32, 33, 34, 34, 41, 44, 31, 32, 32, 33, 34, 35, 41, 44, + 31, 32, 33, 34, 35, 36, 42, 44, 32, 32, 33, 34, 36, 36, 42, 45, 32, 32, 33, 34, 36, 36, 42, 45, 32, 32, 33, 35, 37, 37, 42, 45, + 32, 33, 34, 35, 37, 38, 42, 45, 32, 33, 34, 35, 37, 38, 42, 45, 32, 33, 34, 36, 39, 40, 44, 47, 34, 34, 35, 37, 41, 42, 48, 50, + 34, 34, 35, 37, 41, 42, 48, 50, 34, 34, 35, 37, 42, 43, 49, 51, 35, 34, 36, 38, 45, 47, 52, 55, 36, 34, 36, 38, 46, 48, 54, 56, + 36, 34, 36, 38, 46, 48, 54, 56, 38, 36, 37, 40, 47, 49, 56, 58, 39, 37, 39, 40, 48, 50, 58, 60, 39, 37, 39, 40, 48, 50, 58, 60, + 41, 39, 40, 41, 49, 51, 60, 62, 44, 41, 42, 43, 51, 53, 63, 66, 44, 41, 42, 43, 51, 53, 63, 66, 44, 42, 42, 43, 51, 54, 64, 67, + 47, 44, 44, 45, 53, 56, 66, 69, 48, 45, 45, 46, 54, 56, 67, 70, 48, 45, 45, 46, 54, 56, 67, 70, 51, 47, 48, 48, 56, 58, 69, 73, + }, { + 32, 31, 33, 37, 45, 48, 49, 50, 31, 31, 33, 38, 45, 47, 48, 49, 31, 31, 34, 38, 45, 47, 47, 48, 31, 31, 34, 38, 45, 47, 47, 48, + 31, 32, 34, 39, 45, 46, 46, 47, 30, 32, 35, 40, 44, 46, 45, 46, 30, 32, 35, 40, 44, 46, 45, 46, 31, 33, 35, 40, 45, 46, 45, 46, + 33, 35, 37, 42, 46, 47, 45, 46, 33, 36, 38, 43, 46, 47, 46, 46, 33, 36, 38, 43, 46, 47, 46, 46, 35, 38, 41, 45, 47, 47, 45, 46, + 37, 40, 43, 47, 47, 47, 45, 46, 37, 40, 43, 47, 47, 47, 45, 46, 39, 41, 43, 47, 48, 48, 47, 47, 42, 43, 44, 47, 49, 50, 49, 50, + 42, 43, 44, 47, 49, 50, 49, 50, 43, 43, 45, 47, 50, 50, 50, 50, 47, 46, 46, 48, 51, 52, 53, 53, 49, 46, 47, 48, 52, 53, 53, 54, + 49, 46, 47, 48, 52, 53, 53, 54, 48, 46, 46, 47, 52, 53, 55, 55, 48, 46, 46, 47, 51, 53, 56, 56, 48, 46, 46, 47, 51, 53, 56, 56, + 48, 45, 46, 46, 51, 53, 57, 57, 49, 45, 45, 46, 51, 53, 58, 59, 49, 45, 45, 46, 51, 53, 58, 59, 49, 45, 45, 46, 52, 53, 58, 60, + 50, 46, 46, 46, 52, 54, 59, 61, 50, 46, 46, 46, 52, 54, 59, 61, 50, 46, 46, 46, 52, 54, 59, 61, 51, 47, 47, 47, 52, 54, 60, 62, + }, + }, { + { + 32, 31, 31, 32, 32, 36, 36, 44, 31, 31, 31, 32, 32, 35, 35, 43, 31, 32, 32, 32, 32, 35, 35, 42, 31, 32, 32, 32, 32, 35, 35, 42, + 31, 32, 32, 32, 32, 35, 35, 42, 31, 32, 32, 32, 32, 35, 35, 41, 31, 32, 32, 33, 33, 34, 34, 41, 31, 32, 32, 33, 33, 34, 34, 41, + 31, 32, 32, 33, 33, 34, 34, 41, 31, 32, 32, 33, 33, 35, 35, 41, 32, 32, 32, 34, 34, 36, 36, 42, 32, 32, 32, 34, 34, 36, 36, 42, + 32, 32, 32, 34, 34, 36, 36, 42, 32, 32, 32, 34, 34, 37, 37, 42, 32, 33, 33, 35, 35, 38, 38, 42, 32, 33, 33, 35, 35, 38, 38, 42, + 32, 33, 33, 35, 35, 38, 38, 42, 33, 33, 33, 36, 36, 40, 40, 45, 34, 34, 34, 37, 37, 42, 42, 48, 34, 34, 34, 37, 37, 42, 42, 48, + 34, 34, 34, 37, 37, 42, 42, 48, 35, 34, 34, 37, 37, 45, 45, 50, 36, 34, 34, 38, 38, 48, 48, 54, 36, 34, 34, 38, 38, 48, 48, 54, + 36, 34, 34, 38, 38, 48, 48, 54, 37, 36, 36, 39, 39, 49, 49, 56, 39, 37, 37, 40, 40, 50, 50, 58, 39, 37, 37, 40, 40, 50, 50, 58, + 39, 37, 37, 40, 40, 50, 50, 58, 41, 39, 39, 42, 42, 52, 52, 60, 44, 41, 41, 43, 43, 53, 53, 63, 44, 41, 41, 43, 43, 53, 53, 63, + }, { + 32, 31, 31, 37, 37, 48, 48, 49, 31, 31, 31, 37, 37, 47, 47, 48, 31, 31, 31, 38, 38, 47, 47, 47, 31, 31, 31, 38, 38, 47, 47, 47, + 31, 31, 31, 38, 38, 47, 47, 47, 31, 32, 32, 39, 39, 46, 46, 46, 30, 32, 32, 40, 40, 46, 46, 45, 30, 32, 32, 40, 40, 46, 46, 45, + 30, 32, 32, 40, 40, 46, 46, 45, 32, 34, 34, 41, 41, 46, 46, 45, 33, 36, 36, 43, 43, 47, 47, 46, 33, 36, 36, 43, 43, 47, 47, 46, + 33, 36, 36, 43, 43, 47, 47, 46, 35, 38, 38, 45, 45, 47, 47, 45, 37, 40, 40, 47, 47, 47, 47, 45, 37, 40, 40, 47, 47, 47, 47, 45, + 37, 40, 40, 47, 47, 47, 47, 45, 39, 41, 41, 47, 47, 49, 49, 47, 42, 43, 43, 47, 47, 50, 50, 49, 42, 43, 43, 47, 47, 50, 50, 49, + 42, 43, 43, 47, 47, 50, 50, 49, 45, 44, 44, 47, 47, 51, 51, 51, 49, 46, 46, 48, 48, 53, 53, 53, 49, 46, 46, 48, 48, 53, 53, 53, + 49, 46, 46, 48, 48, 53, 53, 53, 48, 46, 46, 47, 47, 53, 53, 54, 48, 46, 46, 47, 47, 53, 53, 56, 48, 46, 46, 47, 47, 53, 53, 56, + 48, 46, 46, 47, 47, 53, 53, 56, 48, 45, 45, 46, 46, 53, 53, 57, 49, 45, 45, 46, 46, 53, 53, 58, 49, 45, 45, 46, 46, 53, 53, 58, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 35, 36, 31, 31, 31, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 35, 35, + 31, 32, 32, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 35, 35, 31, 32, 32, 32, 32, 32, 34, 35, 31, 32, 32, 32, 32, 32, 34, 35, + 31, 32, 32, 32, 33, 33, 34, 34, 31, 32, 32, 32, 33, 33, 34, 34, 31, 32, 32, 32, 33, 33, 34, 34, 31, 32, 32, 33, 33, 33, 35, 35, + 31, 32, 32, 33, 34, 34, 35, 36, 32, 32, 32, 33, 34, 34, 36, 36, 32, 32, 32, 33, 34, 34, 36, 36, 32, 32, 32, 33, 34, 34, 36, 36, + 32, 32, 32, 33, 34, 34, 36, 37, 32, 32, 33, 33, 35, 35, 37, 38, 32, 32, 33, 34, 35, 35, 37, 38, 32, 32, 33, 34, 35, 35, 37, 38, + 32, 32, 33, 34, 35, 35, 37, 38, 32, 33, 33, 34, 36, 36, 39, 40, 33, 33, 33, 35, 36, 36, 40, 41, 34, 34, 34, 35, 37, 37, 41, 42, + 34, 34, 34, 35, 37, 37, 41, 42, 34, 34, 34, 35, 37, 37, 41, 42, 34, 34, 34, 35, 37, 37, 43, 44, 35, 34, 34, 36, 38, 38, 45, 47, + 36, 35, 34, 36, 38, 38, 46, 48, 36, 35, 34, 36, 38, 38, 46, 48, 36, 35, 34, 36, 38, 38, 46, 48, 37, 36, 36, 37, 39, 39, 46, 49, + }, { + 32, 31, 31, 33, 37, 37, 45, 48, 31, 31, 31, 33, 37, 37, 45, 48, 31, 31, 31, 34, 38, 38, 45, 47, 31, 31, 31, 34, 38, 38, 45, 47, + 31, 31, 31, 34, 38, 38, 45, 47, 31, 31, 31, 34, 38, 38, 45, 47, 31, 31, 32, 34, 39, 39, 45, 46, 30, 31, 32, 34, 39, 39, 44, 46, + 30, 32, 32, 35, 40, 40, 44, 46, 30, 32, 32, 35, 40, 40, 44, 46, 30, 32, 32, 35, 40, 40, 44, 46, 31, 33, 33, 36, 41, 41, 45, 46, + 33, 34, 35, 37, 42, 42, 46, 47, 33, 35, 36, 38, 43, 43, 46, 47, 33, 35, 36, 38, 43, 43, 46, 47, 33, 35, 36, 38, 43, 43, 46, 47, + 35, 37, 37, 40, 44, 44, 46, 47, 36, 38, 39, 42, 46, 46, 47, 47, 37, 39, 40, 43, 47, 47, 47, 47, 37, 39, 40, 43, 47, 47, 47, 47, + 37, 39, 40, 43, 47, 47, 47, 47, 39, 40, 41, 43, 47, 47, 48, 48, 41, 42, 42, 44, 47, 47, 49, 49, 42, 42, 43, 44, 47, 47, 49, 50, + 42, 42, 43, 44, 47, 47, 49, 50, 42, 42, 43, 44, 47, 47, 49, 50, 44, 44, 44, 45, 47, 47, 50, 51, 47, 46, 46, 46, 48, 48, 51, 52, + 49, 47, 46, 47, 48, 48, 52, 53, 49, 47, 46, 47, 48, 48, 52, 53, 49, 47, 46, 47, 48, 48, 52, 53, 49, 47, 46, 47, 47, 47, 52, 53, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 33, 31, 31, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, + 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, + 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 32, 33, 33, 33, + 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 32, 33, 33, 33, 31, 32, 32, 32, 33, 33, 33, 34, + 31, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 34, 34, 34, + 32, 32, 32, 32, 33, 34, 34, 34, 32, 32, 32, 32, 33, 34, 34, 35, 32, 32, 32, 32, 33, 35, 35, 35, 32, 32, 33, 33, 33, 35, 35, 36, + 32, 32, 33, 33, 34, 35, 35, 36, 32, 32, 33, 33, 34, 35, 35, 36, 32, 32, 33, 33, 34, 35, 35, 36, 32, 32, 33, 33, 34, 35, 35, 36, + 32, 33, 33, 33, 34, 36, 36, 36, 33, 33, 33, 33, 34, 36, 36, 37, 34, 34, 34, 34, 35, 37, 37, 38, 34, 34, 34, 34, 35, 37, 37, 38, + }, { + 32, 31, 31, 31, 33, 37, 37, 38, 31, 31, 31, 31, 33, 37, 37, 39, 31, 31, 31, 31, 33, 38, 38, 39, 31, 31, 31, 31, 34, 38, 38, 40, + 31, 31, 31, 31, 34, 38, 38, 40, 31, 31, 31, 31, 34, 38, 38, 40, 31, 31, 31, 31, 34, 38, 38, 40, 31, 31, 31, 31, 34, 38, 38, 40, + 31, 31, 32, 32, 34, 39, 39, 40, 30, 31, 32, 32, 34, 39, 39, 40, 30, 31, 32, 32, 35, 40, 40, 41, 30, 31, 32, 32, 35, 40, 40, 41, + 30, 31, 32, 32, 35, 40, 40, 41, 30, 31, 32, 32, 35, 40, 40, 41, 31, 32, 33, 33, 35, 40, 40, 41, 32, 33, 34, 34, 36, 41, 41, 42, + 33, 34, 35, 35, 37, 42, 42, 43, 33, 35, 36, 36, 38, 43, 43, 44, 33, 35, 36, 36, 38, 43, 43, 44, 33, 35, 36, 36, 38, 43, 43, 44, + 33, 35, 36, 36, 38, 43, 43, 44, 34, 36, 37, 37, 39, 44, 44, 45, 35, 37, 38, 38, 41, 45, 45, 46, 36, 38, 39, 39, 42, 47, 47, 47, + 37, 39, 40, 40, 43, 47, 47, 47, 37, 39, 40, 40, 43, 47, 47, 47, 37, 39, 40, 40, 43, 47, 47, 47, 37, 39, 40, 40, 43, 47, 47, 47, + 39, 40, 41, 41, 43, 47, 47, 47, 40, 41, 42, 42, 44, 47, 47, 47, 42, 42, 43, 43, 44, 47, 47, 48, 42, 42, 43, 43, 44, 47, 47, 48, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 31, 31, 31, 31, 31, 31, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 31, 32, 32, 32, 32, 32, 32, 33, 31, 32, 32, 32, 32, 32, 33, 33, 31, 32, 32, 32, 32, 32, 33, 33, + 31, 32, 32, 32, 32, 32, 33, 33, 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, + 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, 32, 32, 32, 32, 32, 32, 33, 34, + }, { + 32, 31, 31, 31, 31, 31, 33, 35, 31, 31, 31, 31, 31, 31, 33, 35, 31, 31, 31, 31, 31, 31, 33, 36, 31, 31, 31, 31, 31, 31, 33, 36, + 31, 31, 31, 31, 31, 31, 34, 36, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, + 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, 31, 31, 31, 31, 31, 31, 34, 37, + 31, 31, 31, 32, 32, 32, 34, 37, 31, 31, 31, 32, 32, 32, 34, 37, 30, 31, 31, 32, 32, 32, 34, 38, 30, 31, 32, 32, 32, 32, 35, 38, + 30, 31, 32, 32, 32, 32, 35, 38, 30, 31, 32, 32, 32, 32, 35, 38, 30, 31, 32, 32, 32, 32, 35, 38, 30, 31, 32, 32, 32, 32, 35, 38, + 30, 31, 32, 32, 32, 32, 35, 38, 31, 31, 32, 33, 33, 33, 35, 38, 31, 32, 33, 33, 33, 33, 36, 39, 32, 33, 34, 34, 34, 34, 37, 40, + 33, 34, 34, 35, 35, 35, 37, 40, 33, 34, 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, + 33, 34, 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, 33, 34, 35, 36, 36, 36, 38, 41, 34, 35, 36, 36, 36, 36, 39, 42, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, 31, 32, 32, 32, 30, 31, 31, 31, 31, 32, 32, 32, + 30, 31, 31, 31, 31, 32, 32, 32, 30, 31, 31, 31, 32, 32, 32, 32, 30, 31, 31, 31, 32, 32, 32, 32, 30, 31, 31, 31, 32, 32, 32, 32, + }, + }, +}; + +static const uint8_t qm_tbl_16x4[15][2][64] = { + { + { + 31, 32, 32, 34, 34, 41, 45, 54, 60, 72, 75, 83, 88, 94, 101, 108, + 44, 41, 42, 48, 54, 63, 67, 75, 79, 90, 92, 100, 100, 101, 108, 115, + 79, 72, 71, 73, 78, 90, 96, 110, 118, 133, 136, 142, 140, 144, 141, 151, + 96, 90, 86, 83, 89, 95, 102, 111, 123, 135, 149, 160, 173, 180, 188, 197, + }, { + 31, 32, 36, 43, 46, 45, 46, 50, 52, 57, 59, 62, 63, 65, 67, 69, + 49, 45, 46, 49, 53, 58, 59, 62, 64, 67, 68, 71, 69, 68, 70, 72, + 63, 57, 56, 57, 60, 67, 71, 78, 82, 89, 90, 91, 89, 89, 86, 88, + 69, 65, 62, 60, 63, 66, 70, 74, 80, 85, 91, 96, 101, 103, 105, 107, + }, + }, { + { + 31, 32, 32, 33, 34, 37, 44, 49, 56, 65, 72, 78, 84, 89, 95, 101, + 44, 41, 42, 44, 54, 58, 66, 71, 77, 84, 90, 95, 95, 95, 101, 108, + 73, 67, 65, 66, 74, 79, 90, 99, 107, 119, 127, 133, 132, 136, 132, 141, + 93, 87, 83, 81, 86, 92, 98, 107, 117, 129, 141, 151, 163, 169, 175, 183, + }, { + 31, 32, 36, 41, 46, 46, 46, 48, 51, 54, 57, 60, 62, 64, 65, 67, + 49, 45, 46, 47, 53, 56, 59, 61, 63, 65, 67, 69, 67, 66, 68, 70, + 61, 55, 54, 54, 59, 62, 68, 73, 77, 82, 86, 88, 86, 87, 83, 86, + 69, 64, 61, 59, 62, 65, 68, 73, 78, 84, 89, 93, 98, 100, 102, 103, + }, + }, { + { + 31, 32, 32, 33, 34, 37, 41, 46, 53, 60, 65, 74, 79, 84, 89, 94, + 39, 38, 39, 40, 47, 54, 58, 62, 68, 73, 78, 85, 90, 90, 95, 101, + 65, 60, 59, 58, 65, 73, 79, 86, 97, 105, 111, 120, 125, 128, 124, 131, + 90, 84, 81, 78, 83, 89, 94, 102, 112, 123, 134, 143, 154, 158, 164, 170, + }, { + 31, 32, 36, 40, 44, 46, 45, 47, 49, 52, 54, 58, 60, 62, 64, 65, + 48, 46, 46, 46, 51, 54, 56, 57, 58, 60, 62, 64, 66, 64, 66, 68, + 57, 53, 51, 50, 54, 60, 64, 68, 73, 76, 79, 82, 84, 84, 81, 83, + 68, 63, 60, 58, 61, 64, 67, 71, 77, 82, 87, 91, 95, 97, 99, 100, + }, + }, { + { + 31, 32, 32, 33, 34, 34, 39, 44, 49, 54, 60, 68, 75, 79, 84, 88, + 36, 35, 36, 38, 42, 48, 51, 56, 60, 63, 68, 75, 81, 85, 89, 94, + 62, 58, 57, 56, 61, 66, 74, 82, 90, 95, 102, 110, 117, 120, 116, 123, + 88, 82, 79, 76, 81, 85, 91, 98, 107, 117, 127, 135, 145, 148, 153, 159, + }, { + 31, 32, 35, 40, 43, 46, 45, 46, 48, 50, 52, 56, 58, 60, 62, 63, + 48, 46, 47, 47, 50, 53, 53, 54, 54, 55, 56, 59, 61, 63, 64, 66, + 56, 52, 50, 49, 53, 56, 61, 65, 70, 72, 75, 79, 81, 82, 79, 81, + 67, 62, 60, 57, 60, 63, 66, 70, 75, 80, 85, 89, 93, 94, 96, 97, + }, + }, { + { + 31, 32, 32, 32, 33, 34, 37, 41, 45, 49, 54, 61, 68, 74, 78, 83, + 36, 35, 35, 37, 41, 48, 50, 53, 56, 60, 63, 69, 75, 80, 84, 88, + 53, 51, 49, 49, 53, 60, 65, 71, 76, 82, 87, 93, 100, 105, 109, 114, + 81, 76, 73, 71, 74, 80, 85, 91, 98, 105, 112, 121, 130, 137, 142, 148, + }, { + 31, 31, 33, 38, 42, 46, 46, 45, 46, 48, 50, 52, 56, 58, 60, 62, + 48, 47, 46, 47, 49, 53, 53, 53, 54, 54, 55, 57, 59, 61, 62, 64, + 52, 49, 48, 47, 50, 54, 57, 61, 64, 66, 68, 71, 73, 75, 76, 78, + 64, 60, 57, 56, 57, 61, 64, 68, 71, 75, 78, 83, 87, 90, 92, 94, + }, + }, { + { + 31, 32, 32, 32, 33, 34, 34, 37, 41, 45, 49, 54, 60, 65, 72, 75, + 36, 35, 34, 36, 38, 42, 48, 50, 53, 56, 60, 63, 68, 73, 79, 81, + 53, 51, 49, 50, 49, 54, 60, 65, 71, 76, 82, 87, 92, 97, 104, 106, + 79, 75, 72, 71, 69, 73, 78, 84, 90, 96, 103, 110, 118, 125, 133, 136, + }, { + 31, 31, 32, 36, 40, 43, 46, 46, 45, 46, 48, 50, 52, 54, 57, 59, + 48, 47, 46, 47, 47, 50, 53, 53, 53, 54, 54, 55, 56, 58, 60, 61, + 52, 50, 48, 47, 47, 50, 54, 57, 61, 64, 66, 68, 70, 72, 75, 75, + 63, 60, 57, 56, 54, 57, 60, 64, 67, 71, 75, 78, 82, 85, 89, 90, + }, + }, { + { + 31, 32, 32, 32, 32, 34, 34, 36, 39, 42, 45, 50, 54, 60, 66, 73, + 34, 34, 33, 35, 37, 39, 42, 44, 46, 48, 51, 54, 58, 63, 68, 74, + 44, 43, 41, 43, 43, 48, 53, 57, 60, 64, 67, 72, 76, 80, 85, 91, + 65, 62, 59, 59, 58, 63, 67, 71, 76, 81, 85, 92, 98, 105, 111, 118, + }, { + 31, 31, 32, 35, 40, 43, 46, 46, 46, 46, 47, 48, 50, 52, 55, 58, + 42, 42, 42, 45, 47, 48, 50, 50, 49, 49, 50, 50, 52, 53, 55, 58, + 49, 47, 45, 46, 46, 49, 53, 55, 57, 59, 60, 61, 63, 64, 66, 68, + 57, 54, 52, 51, 50, 53, 56, 58, 61, 64, 67, 71, 73, 76, 79, 82, + }, + }, { + { + 31, 32, 32, 32, 32, 32, 34, 35, 37, 39, 41, 45, 50, 54, 57, 61, + 32, 32, 33, 34, 34, 35, 37, 38, 40, 41, 43, 46, 50, 53, 56, 58, + 44, 42, 41, 42, 42, 42, 48, 54, 57, 60, 63, 67, 71, 74, 77, 79, + 58, 55, 53, 53, 53, 52, 57, 63, 67, 70, 74, 79, 86, 90, 93, 97, + }, { + 31, 31, 32, 34, 37, 39, 42, 47, 46, 46, 46, 47, 48, 50, 51, 53, + 37, 38, 40, 42, 44, 47, 47, 48, 47, 46, 46, 46, 47, 48, 49, 50, + 49, 47, 45, 45, 45, 45, 49, 53, 55, 57, 58, 59, 61, 62, 63, 64, + 54, 51, 49, 49, 48, 48, 51, 55, 58, 60, 62, 65, 68, 70, 71, 73, + }, + }, { + { + 31, 32, 32, 32, 32, 32, 33, 34, 35, 37, 38, 42, 45, 47, 51, 55, + 32, 32, 32, 33, 34, 35, 36, 37, 38, 40, 40, 43, 45, 47, 50, 54, + 38, 37, 36, 36, 38, 39, 41, 44, 49, 51, 52, 56, 58, 60, 63, 67, + 53, 51, 49, 49, 50, 49, 51, 54, 60, 63, 65, 71, 75, 77, 82, 87, + }, { + 31, 31, 31, 32, 35, 39, 40, 42, 47, 47, 46, 46, 47, 48, 49, 51, + 37, 38, 39, 40, 43, 47, 47, 47, 48, 47, 47, 46, 46, 47, 47, 49, + 48, 47, 46, 46, 46, 47, 48, 50, 53, 53, 54, 55, 55, 55, 56, 57, + 52, 50, 48, 48, 47, 47, 48, 50, 54, 56, 57, 61, 63, 64, 66, 68, + }, + }, { + { + 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 38, 40, 42, 45, 46, + 32, 32, 32, 33, 34, 34, 35, 36, 37, 38, 38, 40, 41, 43, 45, 46, + 36, 35, 35, 34, 36, 36, 38, 40, 42, 47, 48, 50, 51, 53, 56, 56, + 44, 42, 41, 41, 42, 42, 42, 44, 48, 52, 54, 58, 60, 63, 66, 67, + }, { + 31, 31, 31, 31, 34, 35, 39, 40, 42, 46, 47, 47, 47, 46, 48, 48, + 37, 38, 39, 40, 42, 43, 47, 47, 47, 48, 48, 47, 46, 46, 46, 46, + 48, 47, 46, 46, 47, 47, 47, 48, 50, 52, 53, 53, 53, 53, 54, 54, + 49, 47, 46, 45, 45, 46, 45, 47, 49, 53, 53, 56, 57, 58, 59, 59, + }, + }, { + { + 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 38, 38, 42, + 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 39, 39, 42, + 34, 34, 34, 33, 33, 35, 35, 37, 37, 39, 39, 43, 43, 45, 45, 48, + 39, 38, 38, 37, 37, 39, 39, 40, 40, 45, 45, 51, 51, 54, 54, 58, + }, { + 31, 31, 31, 31, 31, 34, 34, 38, 38, 42, 42, 48, 48, 47, 47, 47, + 33, 34, 34, 35, 35, 39, 39, 43, 43, 45, 45, 47, 47, 46, 46, 45, + 42, 42, 42, 42, 42, 45, 45, 47, 47, 48, 48, 50, 50, 50, 50, 49, + 48, 47, 47, 45, 45, 46, 46, 46, 46, 50, 50, 53, 53, 54, 54, 56, + }, + }, { + { + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, + 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, + 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 41, 42, 44, 48, 48, + }, { + 31, 31, 31, 31, 31, 31, 34, 34, 36, 38, 38, 41, 42, 44, 48, 48, + 31, 31, 31, 32, 32, 32, 35, 36, 37, 40, 40, 42, 43, 44, 46, 46, + 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, + 48, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 49, 50, 51, 53, 53, + }, + }, { + { + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 36, + 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, + }, { + 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 34, 36, 38, 38, 39, 42, + 31, 31, 31, 31, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, + 35, 35, 36, 36, 36, 37, 37, 38, 40, 40, 40, 43, 45, 45, 45, 46, + 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, + }, + }, { + { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + }, { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, + 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 36, 37, 39, 40, 40, 40, + }, + }, { + { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + }, { + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + }, + }, +}; + +static const uint8_t qm_tbl_16x8[15][2][128] = { + { + { + 32, 31, 32, 34, 36, 44, 48, 58, 65, 79, 82, 91, 97, 103, 110, 118, + 32, 33, 34, 37, 38, 43, 46, 54, 58, 70, 72, 80, 86, 93, 100, 107, + 36, 34, 36, 42, 48, 53, 56, 63, 68, 79, 81, 88, 94, 98, 101, 105, + 53, 49, 50, 54, 60, 71, 76, 87, 92, 104, 106, 106, 107, 114, 117, 118, + 65, 59, 59, 63, 68, 79, 85, 98, 105, 118, 121, 130, 128, 131, 138, 136, + 87, 78, 77, 79, 84, 95, 102, 116, 124, 141, 144, 148, 157, 150, 161, 157, + 93, 86, 82, 80, 86, 94, 105, 112, 122, 135, 149, 162, 167, 174, 183, 182, + 99, 93, 89, 88, 90, 97, 105, 115, 124, 135, 146, 159, 171, 186, 193, 203, + }, { + 32, 30, 33, 42, 49, 49, 50, 54, 57, 63, 64, 68, 70, 72, 74, 76, + 37, 40, 43, 47, 48, 46, 46, 49, 50, 55, 56, 59, 62, 64, 67, 69, + 48, 46, 47, 50, 53, 53, 54, 55, 56, 60, 61, 64, 66, 66, 66, 67, + 52, 48, 47, 50, 54, 61, 64, 68, 70, 75, 75, 74, 73, 75, 74, 73, + 57, 52, 51, 53, 57, 64, 67, 73, 76, 82, 83, 86, 83, 83, 84, 82, + 66, 60, 59, 60, 62, 69, 73, 80, 84, 92, 93, 94, 96, 92, 94, 91, + 68, 63, 60, 59, 62, 66, 72, 76, 80, 87, 93, 98, 99, 101, 103, 101, + 71, 66, 63, 62, 62, 66, 70, 75, 79, 84, 89, 94, 98, 104, 106, 109, + }, + }, { + { + 32, 31, 32, 32, 36, 39, 47, 53, 61, 71, 79, 86, 92, 98, 104, 110, + 32, 32, 34, 35, 37, 40, 45, 50, 56, 64, 70, 76, 82, 88, 94, 100, + 36, 35, 36, 40, 48, 50, 56, 60, 65, 73, 79, 84, 89, 93, 95, 98, + 47, 44, 45, 47, 56, 60, 69, 75, 81, 89, 95, 100, 101, 108, 110, 111, + 65, 60, 59, 60, 68, 73, 84, 92, 100, 111, 118, 124, 121, 124, 129, 127, + 79, 72, 71, 71, 78, 84, 95, 103, 113, 125, 133, 140, 148, 141, 151, 147, + 90, 84, 80, 78, 83, 91, 101, 108, 116, 129, 142, 153, 157, 163, 171, 169, + 96, 90, 87, 85, 87, 94, 101, 110, 118, 129, 138, 150, 161, 174, 181, 188, + }, { + 32, 30, 33, 39, 49, 48, 50, 52, 55, 60, 63, 66, 68, 70, 72, 74, + 35, 38, 41, 46, 48, 46, 46, 47, 49, 53, 55, 58, 60, 62, 65, 67, + 48, 46, 47, 48, 53, 53, 54, 54, 56, 58, 60, 62, 64, 65, 65, 65, + 50, 46, 46, 47, 54, 56, 61, 63, 65, 68, 70, 72, 71, 73, 72, 71, + 57, 52, 51, 51, 57, 60, 66, 71, 74, 79, 82, 84, 81, 81, 82, 79, + 63, 58, 56, 55, 60, 64, 70, 75, 79, 85, 89, 91, 94, 89, 92, 89, + 68, 63, 60, 58, 61, 65, 71, 75, 79, 85, 91, 95, 97, 98, 100, 98, + 70, 65, 63, 61, 61, 65, 69, 74, 78, 82, 87, 91, 96, 101, 103, 105, + }, + }, { + { + 32, 31, 32, 32, 34, 39, 44, 49, 57, 65, 71, 81, 87, 92, 98, 103, + 32, 32, 33, 34, 36, 39, 42, 46, 53, 59, 64, 72, 77, 83, 88, 94, + 36, 35, 36, 38, 44, 50, 53, 57, 63, 68, 73, 80, 85, 88, 89, 92, + 44, 41, 42, 42, 50, 58, 63, 67, 74, 79, 84, 91, 96, 102, 103, 103, + 58, 54, 53, 52, 59, 68, 74, 81, 90, 97, 102, 110, 114, 117, 121, 119, + 79, 73, 71, 69, 75, 84, 90, 97, 108, 118, 125, 135, 140, 133, 141, 137, + 88, 81, 78, 76, 81, 88, 97, 104, 111, 123, 135, 145, 148, 153, 160, 158, + 93, 88, 84, 82, 84, 90, 97, 105, 113, 122, 131, 141, 151, 163, 169, 175, + }, { + 32, 31, 33, 37, 44, 48, 49, 51, 54, 57, 60, 64, 66, 68, 70, 72, + 34, 36, 40, 44, 46, 46, 45, 47, 49, 51, 53, 57, 59, 61, 63, 65, + 48, 46, 47, 47, 51, 53, 53, 54, 55, 56, 58, 61, 63, 63, 63, 63, + 49, 46, 46, 45, 51, 56, 58, 60, 62, 64, 65, 68, 69, 71, 70, 69, + 54, 50, 49, 48, 53, 58, 62, 65, 70, 73, 75, 78, 79, 79, 80, 77, + 63, 58, 56, 54, 59, 64, 67, 71, 77, 82, 85, 89, 91, 87, 89, 86, + 67, 62, 59, 57, 60, 64, 70, 73, 77, 83, 89, 93, 94, 96, 97, 95, + 69, 65, 62, 60, 61, 64, 68, 72, 76, 81, 85, 89, 93, 98, 100, 102, + }, + }, { + { + 32, 31, 31, 32, 34, 36, 41, 47, 53, 58, 65, 74, 82, 87, 92, 97, + 31, 32, 33, 34, 35, 36, 40, 44, 50, 54, 59, 67, 73, 78, 83, 88, + 35, 34, 35, 37, 41, 46, 49, 53, 57, 61, 66, 73, 79, 83, 84, 86, + 44, 41, 42, 42, 48, 54, 60, 66, 71, 75, 79, 86, 92, 96, 97, 97, + 53, 50, 49, 49, 54, 60, 67, 75, 82, 87, 92, 100, 105, 110, 114, 111, + 65, 61, 59, 58, 63, 68, 76, 84, 92, 98, 105, 113, 120, 125, 132, 128, + 82, 76, 73, 71, 76, 80, 88, 97, 106, 112, 120, 131, 139, 144, 150, 147, + 90, 85, 81, 79, 81, 87, 93, 101, 108, 116, 124, 134, 142, 153, 157, 163, + }, { + 32, 31, 33, 37, 42, 49, 48, 50, 52, 54, 57, 61, 64, 66, 68, 70, + 33, 34, 37, 43, 44, 47, 46, 46, 47, 49, 51, 55, 57, 59, 61, 63, + 45, 45, 46, 47, 49, 52, 51, 52, 53, 54, 55, 58, 60, 61, 61, 61, + 49, 46, 45, 45, 49, 53, 57, 59, 61, 62, 64, 66, 68, 69, 68, 67, + 52, 49, 47, 47, 50, 54, 59, 63, 66, 68, 70, 73, 75, 77, 77, 75, + 57, 53, 51, 50, 53, 57, 61, 66, 71, 73, 76, 80, 83, 84, 86, 83, + 64, 60, 57, 55, 58, 61, 66, 71, 75, 79, 83, 87, 91, 93, 94, 92, + 68, 64, 61, 59, 60, 63, 67, 71, 74, 79, 83, 87, 91, 95, 97, 98, + }, + }, { + { + 32, 31, 31, 32, 33, 36, 39, 44, 48, 53, 58, 66, 74, 81, 86, 91, + 31, 32, 32, 33, 34, 35, 38, 41, 45, 49, 54, 60, 67, 73, 78, 82, + 33, 33, 34, 36, 38, 42, 44, 46, 50, 53, 57, 63, 69, 75, 78, 80, + 40, 39, 38, 40, 44, 51, 54, 59, 62, 66, 70, 75, 81, 86, 90, 90, + 51, 49, 47, 48, 52, 58, 63, 69, 74, 79, 84, 90, 97, 102, 106, 103, + 65, 61, 59, 58, 62, 68, 73, 79, 85, 92, 98, 106, 113, 120, 124, 119, + 79, 74, 71, 69, 72, 78, 84, 90, 96, 103, 110, 119, 128, 135, 140, 137, + 87, 82, 79, 77, 78, 84, 89, 96, 103, 111, 118, 126, 134, 143, 147, 151, + }, { + 32, 31, 31, 35, 41, 49, 48, 49, 50, 52, 54, 57, 61, 64, 66, 68, + 32, 33, 35, 39, 43, 47, 46, 45, 46, 48, 50, 52, 55, 58, 59, 61, + 40, 41, 43, 46, 48, 50, 49, 48, 49, 50, 51, 53, 56, 58, 59, 59, + 49, 47, 46, 46, 49, 53, 54, 56, 57, 58, 59, 61, 63, 65, 66, 65, + 51, 49, 47, 47, 49, 54, 57, 61, 63, 65, 67, 69, 72, 73, 75, 72, + 57, 54, 51, 50, 52, 57, 60, 64, 67, 71, 73, 77, 80, 82, 84, 81, + 63, 59, 57, 55, 57, 60, 64, 67, 71, 75, 78, 82, 86, 89, 91, 89, + 67, 63, 60, 58, 59, 62, 65, 69, 73, 77, 81, 85, 88, 92, 94, 95, + }, + }, { + { + 32, 31, 31, 32, 32, 34, 36, 39, 44, 48, 53, 58, 65, 71, 79, 82, + 31, 32, 32, 32, 33, 34, 34, 37, 41, 45, 49, 54, 60, 65, 72, 75, + 32, 32, 33, 34, 35, 37, 38, 40, 43, 46, 50, 54, 58, 63, 70, 72, + 36, 35, 34, 36, 38, 42, 48, 50, 53, 56, 60, 63, 68, 73, 79, 81, + 44, 42, 41, 42, 42, 48, 54, 58, 63, 67, 71, 75, 79, 84, 90, 92, + 53, 51, 49, 50, 49, 54, 60, 65, 71, 76, 82, 87, 92, 97, 104, 106, + 65, 62, 59, 59, 58, 63, 68, 73, 79, 85, 92, 98, 105, 111, 118, 121, + 79, 75, 72, 71, 69, 73, 78, 84, 90, 96, 103, 110, 118, 125, 133, 136, + }, { + 32, 31, 30, 33, 37, 42, 49, 48, 49, 50, 52, 54, 57, 60, 63, 64, + 31, 31, 32, 36, 40, 43, 46, 46, 45, 46, 48, 50, 52, 54, 57, 59, + 37, 38, 40, 43, 47, 47, 48, 47, 46, 46, 47, 49, 50, 52, 55, 56, + 48, 47, 46, 47, 47, 50, 53, 53, 53, 54, 54, 55, 56, 58, 60, 61, + 49, 47, 45, 46, 45, 49, 53, 56, 58, 59, 61, 62, 64, 65, 67, 68, + 52, 50, 48, 47, 47, 50, 54, 57, 61, 64, 66, 68, 70, 72, 75, 75, + 57, 54, 52, 51, 50, 53, 57, 60, 64, 67, 71, 73, 76, 79, 82, 83, + 63, 60, 57, 56, 54, 57, 60, 64, 67, 71, 75, 78, 82, 85, 89, 90, + }, + }, { + { + 32, 31, 31, 32, 32, 34, 35, 38, 41, 44, 48, 53, 58, 65, 71, 79, + 31, 32, 32, 32, 33, 34, 34, 36, 39, 42, 45, 49, 54, 60, 65, 72, + 32, 32, 33, 34, 35, 37, 38, 40, 41, 43, 46, 50, 54, 58, 63, 70, + 36, 35, 34, 36, 38, 42, 47, 49, 51, 54, 56, 60, 63, 68, 73, 79, + 44, 42, 41, 42, 42, 48, 52, 56, 60, 64, 67, 71, 75, 79, 84, 90, + 53, 51, 49, 50, 49, 54, 59, 63, 67, 72, 76, 82, 87, 92, 97, 104, + 62, 59, 57, 57, 56, 61, 65, 69, 74, 79, 83, 90, 95, 102, 108, 115, + 73, 69, 66, 65, 64, 69, 73, 77, 81, 86, 91, 99, 105, 112, 119, 127, + }, { + 32, 31, 30, 33, 37, 42, 47, 48, 48, 49, 50, 52, 54, 57, 60, 63, + 31, 31, 32, 36, 40, 43, 46, 46, 45, 45, 46, 48, 50, 52, 54, 57, + 37, 38, 40, 43, 47, 47, 48, 47, 46, 46, 46, 47, 49, 50, 52, 55, + 48, 47, 46, 47, 47, 50, 52, 53, 53, 53, 54, 54, 55, 56, 58, 60, + 49, 47, 45, 46, 45, 49, 53, 55, 57, 58, 59, 61, 62, 64, 65, 67, + 52, 50, 48, 47, 47, 50, 53, 56, 59, 62, 64, 66, 68, 70, 72, 75, + 56, 53, 51, 50, 49, 53, 55, 58, 61, 64, 66, 70, 72, 75, 77, 80, + 61, 57, 55, 54, 52, 56, 58, 61, 63, 66, 69, 73, 76, 79, 82, 86, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 34, 36, 38, 41, 44, 48, 53, 57, 61, 65, + 31, 32, 32, 32, 32, 33, 34, 34, 37, 39, 41, 45, 49, 53, 56, 60, + 32, 32, 33, 34, 34, 35, 37, 38, 40, 41, 43, 46, 50, 53, 56, 58, + 35, 35, 34, 35, 36, 37, 41, 46, 47, 49, 51, 54, 57, 60, 63, 66, + 39, 38, 37, 38, 39, 40, 44, 50, 52, 54, 57, 60, 64, 67, 69, 72, + 44, 42, 41, 42, 42, 42, 48, 54, 57, 60, 63, 67, 71, 74, 77, 79, + 53, 51, 49, 49, 49, 49, 54, 60, 64, 67, 71, 76, 82, 86, 89, 92, + 65, 62, 59, 59, 58, 58, 63, 68, 72, 76, 79, 85, 92, 97, 100, 105, + }, { + 32, 31, 30, 33, 35, 37, 42, 49, 48, 48, 49, 50, 52, 54, 55, 57, + 31, 31, 32, 35, 37, 40, 43, 46, 46, 45, 45, 46, 48, 49, 51, 52, + 37, 38, 40, 42, 44, 47, 47, 48, 47, 46, 46, 46, 47, 48, 49, 50, + 45, 45, 44, 46, 46, 47, 49, 52, 51, 51, 51, 52, 53, 54, 54, 55, + 48, 47, 45, 46, 46, 47, 50, 53, 54, 54, 55, 56, 57, 58, 58, 59, + 49, 47, 45, 45, 45, 45, 49, 53, 55, 57, 58, 59, 61, 62, 63, 64, + 52, 50, 48, 47, 47, 47, 50, 54, 57, 59, 61, 64, 66, 68, 69, 70, + 57, 54, 52, 51, 51, 50, 53, 57, 59, 61, 64, 67, 71, 73, 74, 76, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 32, 34, 36, 38, 39, 44, 47, 49, 53, 58, + 31, 32, 32, 32, 32, 33, 33, 34, 34, 36, 37, 41, 44, 46, 49, 54, + 32, 32, 32, 33, 34, 35, 35, 36, 37, 39, 40, 42, 45, 47, 50, 54, + 32, 33, 33, 33, 34, 36, 36, 38, 40, 41, 42, 45, 47, 48, 51, 55, + 36, 35, 35, 35, 36, 38, 40, 42, 48, 49, 50, 53, 56, 57, 60, 63, + 44, 42, 41, 41, 42, 42, 44, 48, 54, 56, 58, 63, 66, 67, 71, 75, + 47, 45, 44, 44, 45, 45, 47, 50, 56, 58, 60, 66, 69, 71, 75, 79, + 53, 51, 49, 49, 50, 49, 51, 54, 60, 63, 65, 71, 75, 77, 82, 87, + }, { + 32, 31, 30, 31, 33, 37, 39, 42, 49, 48, 48, 49, 50, 51, 52, 54, + 31, 31, 32, 33, 36, 40, 41, 43, 46, 46, 46, 45, 46, 47, 48, 50, + 35, 37, 38, 38, 41, 45, 46, 46, 48, 47, 46, 45, 46, 47, 47, 49, + 38, 40, 40, 41, 44, 47, 47, 48, 49, 48, 48, 47, 48, 48, 48, 50, + 48, 47, 46, 46, 47, 47, 48, 50, 53, 53, 53, 53, 54, 54, 54, 55, + 49, 47, 45, 45, 46, 45, 47, 49, 53, 55, 56, 58, 59, 60, 61, 62, + 50, 48, 46, 46, 46, 46, 47, 50, 54, 55, 56, 59, 61, 61, 63, 65, + 52, 50, 48, 48, 47, 47, 48, 50, 54, 56, 57, 61, 63, 64, 66, 68, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 34, 35, 36, 39, 41, 44, 47, 48, + 31, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 37, 39, 41, 44, 45, + 31, 32, 32, 32, 33, 33, 34, 34, 35, 36, 36, 39, 40, 42, 44, 45, + 32, 32, 32, 33, 34, 34, 35, 36, 37, 38, 38, 40, 41, 43, 45, 46, + 35, 35, 34, 34, 35, 36, 37, 39, 41, 45, 46, 48, 49, 51, 53, 54, + 36, 35, 35, 34, 36, 36, 38, 40, 42, 47, 48, 50, 51, 53, 56, 56, + 44, 42, 41, 41, 42, 42, 42, 44, 48, 52, 54, 58, 60, 63, 66, 67, + 47, 45, 45, 44, 44, 45, 45, 47, 50, 55, 56, 60, 62, 66, 69, 70, + }, { + 32, 31, 31, 30, 33, 33, 37, 39, 42, 47, 49, 48, 48, 49, 50, 50, + 31, 31, 32, 32, 35, 36, 40, 41, 43, 46, 46, 46, 45, 45, 46, 46, + 33, 34, 34, 35, 37, 38, 43, 43, 44, 46, 47, 46, 46, 45, 46, 46, + 37, 38, 39, 40, 42, 43, 47, 47, 47, 48, 48, 47, 46, 46, 46, 46, + 45, 45, 45, 44, 46, 46, 47, 48, 49, 51, 52, 51, 51, 51, 52, 52, + 48, 47, 46, 46, 47, 47, 47, 48, 50, 52, 53, 53, 53, 53, 54, 54, + 49, 47, 46, 45, 45, 46, 45, 47, 49, 53, 53, 56, 57, 58, 59, 59, + 50, 48, 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 57, 59, 61, 61, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 36, 36, 39, 39, 44, + 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 37, 37, 41, + 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 37, 37, 41, + 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 43, + 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 43, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 53, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 53, + 44, 42, 42, 41, 41, 42, 42, 42, 42, 48, 48, 54, 54, 58, 58, 63, + }, { + 32, 31, 31, 30, 30, 33, 33, 37, 37, 42, 42, 49, 49, 48, 48, 49, + 31, 31, 31, 32, 32, 36, 36, 40, 40, 43, 43, 46, 46, 46, 46, 45, + 31, 31, 31, 32, 32, 36, 36, 40, 40, 43, 43, 46, 46, 46, 46, 45, + 37, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 47, 47, 46, + 37, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 47, 47, 46, + 48, 47, 47, 46, 46, 47, 47, 47, 47, 50, 50, 53, 53, 53, 53, 53, + 48, 47, 47, 46, 46, 47, 47, 47, 47, 50, 50, 53, 53, 53, 53, 53, + 49, 47, 47, 45, 45, 46, 46, 45, 45, 49, 49, 53, 53, 56, 56, 58, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 33, 34, 34, 36, 36, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 35, 36, 36, + 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, + 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, + 35, 35, 35, 34, 34, 34, 35, 36, 36, 37, 37, 40, 41, 43, 46, 46, + 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 41, 42, 44, 48, 48, + }, { + 32, 31, 31, 31, 30, 30, 33, 33, 35, 37, 37, 41, 42, 44, 49, 49, + 31, 31, 31, 31, 32, 32, 34, 35, 37, 39, 39, 42, 42, 44, 47, 47, + 31, 31, 31, 32, 32, 32, 35, 36, 37, 40, 40, 42, 43, 44, 46, 46, + 33, 34, 34, 34, 35, 35, 37, 38, 40, 43, 43, 44, 44, 45, 47, 47, + 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, + 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, + 45, 45, 45, 45, 44, 44, 46, 46, 46, 47, 47, 49, 49, 50, 52, 52, + 48, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 49, 50, 51, 53, 53, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, + 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, + 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, + 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 36, 36, 36, 38, + }, { + 32, 31, 31, 31, 31, 30, 30, 31, 33, 33, 33, 35, 37, 37, 39, 42, + 31, 31, 31, 31, 31, 31, 31, 32, 34, 35, 35, 37, 39, 39, 40, 42, + 31, 31, 31, 31, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, + 31, 31, 31, 31, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, + 33, 33, 34, 34, 34, 35, 35, 35, 37, 38, 38, 41, 43, 43, 43, 44, + 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, + 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, + 38, 39, 40, 40, 40, 41, 41, 41, 43, 44, 44, 46, 47, 47, 47, 48, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, + }, { + 32, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 33, 33, 33, 33, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, + 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 37, 38, 38, 38, + 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 40, 41, 41, 41, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + }, + }, +}; + +static const uint8_t qm_tbl_16x16[15][2][256] = { + { + { + 32, 31, 31, 34, 36, 44, 48, 59, 65, 80, 83, 91, 97, 104, 111, 119, + 31, 32, 32, 33, 34, 41, 44, 54, 59, 72, 75, 83, 90, 97, 104, 112, + 31, 32, 33, 35, 36, 42, 45, 54, 59, 71, 74, 81, 86, 93, 100, 107, + 34, 33, 35, 39, 42, 47, 51, 58, 63, 74, 76, 81, 84, 90, 97, 105, + 36, 34, 36, 42, 48, 54, 57, 64, 68, 79, 81, 88, 91, 96, 102, 105, + 44, 41, 42, 47, 54, 63, 67, 75, 79, 90, 92, 95, 100, 102, 109, 112, + 48, 44, 45, 51, 57, 67, 71, 80, 85, 96, 99, 107, 108, 111, 117, 120, + 59, 54, 54, 58, 64, 75, 80, 92, 98, 110, 113, 115, 116, 122, 125, 130, + 65, 59, 59, 63, 68, 79, 85, 98, 105, 118, 121, 127, 130, 134, 135, 140, + 80, 72, 71, 74, 79, 90, 96, 110, 118, 134, 137, 140, 143, 144, 146, 152, + 83, 75, 74, 76, 81, 92, 99, 113, 121, 137, 140, 151, 152, 155, 158, 165, + 91, 83, 81, 81, 88, 95, 107, 115, 127, 140, 151, 159, 166, 169, 173, 179, + 97, 90, 86, 84, 91, 100, 108, 116, 130, 143, 152, 166, 174, 182, 189, 193, + 104, 97, 93, 90, 96, 102, 111, 122, 134, 144, 155, 169, 182, 191, 200, 210, + 111, 104, 100, 97, 102, 109, 117, 125, 135, 146, 158, 173, 189, 200, 210, 220, + 119, 112, 107, 105, 105, 112, 120, 130, 140, 152, 165, 179, 193, 210, 220, 231, + }, { + 32, 30, 33, 41, 49, 49, 50, 54, 57, 63, 65, 68, 70, 72, 74, 76, + 30, 32, 35, 42, 46, 45, 46, 49, 52, 57, 58, 62, 64, 67, 70, 72, + 33, 35, 39, 45, 47, 45, 46, 49, 51, 56, 57, 60, 62, 64, 66, 69, + 41, 42, 45, 48, 50, 49, 50, 52, 53, 57, 58, 59, 60, 61, 64, 67, + 49, 46, 47, 50, 53, 53, 54, 55, 56, 60, 61, 64, 64, 65, 66, 66, + 49, 45, 45, 49, 53, 58, 60, 62, 63, 67, 68, 67, 69, 68, 70, 70, + 50, 46, 46, 50, 54, 60, 61, 65, 67, 71, 71, 74, 73, 73, 74, 74, + 54, 49, 49, 52, 55, 62, 65, 71, 73, 78, 79, 78, 77, 78, 78, 78, + 57, 52, 51, 53, 56, 63, 67, 73, 76, 82, 83, 84, 84, 84, 82, 83, + 63, 57, 56, 57, 60, 67, 71, 78, 82, 89, 90, 90, 89, 88, 87, 88, + 65, 58, 57, 58, 61, 68, 71, 79, 83, 90, 91, 94, 93, 93, 92, 93, + 68, 62, 60, 59, 64, 67, 74, 78, 84, 90, 94, 98, 99, 98, 98, 98, + 70, 64, 62, 60, 64, 69, 73, 77, 84, 89, 93, 99, 102, 103, 104, 104, + 72, 67, 64, 61, 65, 68, 73, 78, 84, 88, 93, 98, 103, 106, 108, 109, + 74, 70, 66, 64, 66, 70, 74, 78, 82, 87, 92, 98, 104, 108, 111, 112, + 76, 72, 69, 67, 66, 70, 74, 78, 83, 88, 93, 98, 104, 109, 112, 116, + }, + }, { + { + 32, 31, 31, 32, 36, 39, 47, 54, 61, 71, 80, 86, 92, 98, 104, 111, + 31, 32, 32, 33, 34, 37, 44, 50, 56, 65, 73, 79, 85, 91, 98, 105, + 31, 32, 33, 34, 36, 39, 45, 50, 56, 64, 71, 77, 82, 88, 94, 100, + 32, 33, 34, 36, 40, 42, 47, 51, 57, 65, 71, 76, 80, 85, 91, 98, + 36, 34, 36, 40, 48, 50, 56, 60, 65, 73, 79, 84, 86, 90, 95, 98, + 39, 37, 39, 42, 50, 54, 60, 65, 70, 78, 84, 89, 95, 96, 102, 105, + 47, 44, 45, 47, 56, 60, 69, 75, 81, 89, 95, 100, 102, 104, 109, 112, + 54, 50, 50, 51, 60, 65, 75, 82, 89, 97, 104, 109, 110, 114, 117, 121, + 61, 56, 56, 57, 65, 70, 81, 89, 97, 106, 113, 119, 122, 126, 125, 130, + 71, 65, 64, 65, 73, 78, 89, 97, 106, 117, 125, 131, 134, 134, 136, 141, + 80, 73, 71, 71, 79, 84, 95, 104, 113, 125, 134, 140, 142, 145, 146, 152, + 86, 79, 77, 76, 84, 89, 100, 109, 119, 131, 140, 147, 154, 157, 160, 165, + 92, 85, 82, 80, 86, 95, 102, 110, 122, 134, 142, 154, 162, 168, 174, 178, + 98, 91, 88, 85, 90, 96, 104, 114, 126, 134, 145, 157, 168, 176, 184, 193, + 104, 98, 94, 91, 95, 102, 109, 117, 125, 136, 146, 160, 174, 184, 193, 201, + 111, 105, 100, 98, 98, 105, 112, 121, 130, 141, 152, 165, 178, 193, 201, 210, + }, { + 32, 30, 33, 38, 49, 48, 50, 52, 55, 60, 63, 66, 68, 70, 72, 74, + 30, 31, 35, 41, 46, 46, 46, 48, 51, 55, 58, 60, 63, 65, 68, 70, + 33, 35, 39, 44, 47, 46, 46, 47, 50, 53, 56, 58, 60, 62, 65, 67, + 38, 41, 44, 47, 49, 48, 47, 48, 50, 53, 55, 58, 58, 60, 62, 65, + 49, 46, 47, 49, 53, 53, 54, 54, 56, 58, 60, 62, 62, 63, 64, 64, + 48, 46, 46, 48, 53, 54, 56, 57, 59, 61, 63, 65, 67, 66, 68, 68, + 50, 46, 46, 47, 54, 56, 61, 63, 65, 68, 70, 72, 71, 71, 72, 72, + 52, 48, 47, 48, 54, 57, 63, 66, 69, 72, 75, 76, 75, 76, 76, 76, + 55, 51, 50, 50, 56, 59, 65, 69, 73, 77, 79, 81, 81, 81, 80, 80, + 60, 55, 53, 53, 58, 61, 68, 72, 77, 82, 85, 87, 87, 85, 84, 85, + 63, 58, 56, 55, 60, 63, 70, 75, 79, 85, 89, 91, 91, 90, 89, 90, + 66, 60, 58, 58, 62, 65, 72, 76, 81, 87, 91, 94, 96, 95, 95, 95, + 68, 63, 60, 58, 62, 67, 71, 75, 81, 87, 91, 96, 99, 100, 100, 100, + 70, 65, 62, 60, 63, 66, 71, 76, 81, 85, 90, 95, 100, 103, 104, 105, + 72, 68, 65, 62, 64, 68, 72, 76, 80, 84, 89, 95, 100, 104, 107, 108, + 74, 70, 67, 65, 64, 68, 72, 76, 80, 85, 90, 95, 100, 105, 108, 111, + }, + }, { + { + 32, 31, 31, 32, 34, 39, 44, 49, 58, 65, 71, 81, 87, 93, 98, 104, + 31, 32, 32, 32, 34, 38, 41, 46, 54, 60, 66, 75, 81, 86, 92, 98, + 31, 32, 33, 34, 36, 39, 42, 46, 53, 59, 64, 73, 78, 83, 88, 94, + 32, 32, 34, 35, 37, 40, 42, 46, 52, 58, 63, 71, 75, 80, 86, 92, + 34, 34, 36, 37, 42, 47, 50, 53, 59, 65, 70, 77, 82, 85, 89, 92, + 39, 38, 39, 40, 47, 54, 58, 62, 68, 73, 78, 85, 90, 90, 96, 98, + 44, 41, 42, 42, 50, 58, 63, 68, 74, 79, 84, 91, 96, 98, 102, 104, + 49, 46, 46, 46, 53, 62, 68, 73, 81, 87, 92, 99, 103, 107, 109, 112, + 58, 54, 53, 52, 59, 68, 74, 81, 90, 97, 102, 110, 114, 118, 117, 121, + 65, 60, 59, 58, 65, 73, 79, 87, 97, 105, 111, 120, 125, 125, 126, 130, + 71, 66, 64, 63, 70, 78, 84, 92, 102, 111, 117, 127, 133, 134, 136, 141, + 81, 75, 73, 71, 77, 85, 91, 99, 110, 120, 127, 137, 143, 145, 148, 152, + 87, 81, 78, 75, 82, 90, 96, 103, 114, 125, 133, 143, 150, 156, 160, 163, + 93, 86, 83, 80, 85, 90, 98, 107, 118, 125, 134, 145, 156, 163, 169, 177, + 98, 92, 88, 86, 89, 96, 102, 109, 117, 126, 136, 148, 160, 169, 176, 184, + 104, 98, 94, 92, 92, 98, 104, 112, 121, 130, 141, 152, 163, 177, 184, 191, + }, { + 32, 30, 33, 36, 44, 48, 49, 51, 54, 57, 60, 64, 67, 68, 70, 72, + 30, 31, 35, 39, 44, 46, 46, 47, 50, 53, 55, 59, 61, 64, 66, 68, + 33, 35, 39, 43, 46, 46, 45, 47, 49, 51, 53, 57, 59, 61, 63, 65, + 36, 39, 43, 47, 47, 46, 45, 46, 48, 50, 52, 55, 57, 58, 61, 63, + 44, 44, 46, 47, 50, 51, 51, 51, 53, 54, 56, 59, 61, 61, 63, 62, + 48, 46, 46, 46, 51, 54, 55, 56, 58, 60, 61, 64, 65, 64, 66, 66, + 49, 46, 45, 45, 51, 55, 58, 60, 62, 63, 65, 68, 69, 69, 69, 69, + 51, 47, 47, 46, 51, 56, 60, 62, 65, 67, 69, 72, 73, 74, 73, 73, + 54, 50, 49, 48, 53, 58, 62, 65, 70, 73, 75, 78, 79, 79, 77, 77, + 57, 53, 51, 50, 54, 60, 63, 67, 73, 76, 79, 82, 84, 83, 82, 82, + 60, 55, 53, 52, 56, 61, 65, 69, 75, 79, 82, 86, 88, 87, 86, 87, + 64, 59, 57, 55, 59, 64, 68, 72, 78, 82, 86, 90, 93, 92, 91, 92, + 67, 61, 59, 57, 61, 65, 69, 73, 79, 84, 88, 93, 95, 96, 96, 96, + 68, 64, 61, 58, 61, 64, 69, 74, 79, 83, 87, 92, 96, 99, 100, 101, + 70, 66, 63, 61, 63, 66, 69, 73, 77, 82, 86, 91, 96, 100, 103, 104, + 72, 68, 65, 63, 62, 66, 69, 73, 77, 82, 87, 92, 96, 101, 104, 106, + }, + }, { + { + 32, 31, 31, 32, 34, 36, 41, 47, 54, 59, 65, 74, 82, 87, 92, 97, + 31, 32, 32, 32, 34, 35, 39, 45, 50, 55, 61, 69, 76, 81, 87, 92, + 31, 32, 33, 33, 35, 36, 40, 44, 49, 54, 59, 67, 73, 78, 83, 88, + 32, 32, 33, 35, 37, 38, 41, 45, 49, 53, 58, 65, 71, 75, 80, 86, + 34, 34, 35, 37, 39, 42, 46, 50, 54, 58, 63, 70, 76, 80, 84, 85, + 36, 35, 36, 38, 42, 48, 52, 56, 60, 64, 68, 75, 80, 85, 90, 91, + 41, 39, 40, 41, 46, 52, 57, 62, 67, 71, 75, 83, 88, 92, 95, 97, + 47, 45, 44, 45, 50, 56, 62, 69, 75, 79, 84, 91, 97, 100, 102, 104, + 54, 50, 49, 49, 54, 60, 67, 75, 82, 87, 92, 100, 106, 110, 109, 112, + 59, 55, 54, 53, 58, 64, 71, 79, 87, 92, 98, 106, 112, 117, 117, 121, + 65, 61, 59, 58, 63, 68, 75, 84, 92, 98, 105, 114, 120, 125, 126, 130, + 74, 69, 67, 65, 70, 75, 83, 91, 100, 106, 114, 123, 131, 135, 137, 140, + 82, 76, 73, 71, 76, 80, 88, 97, 106, 112, 120, 131, 139, 144, 148, 150, + 87, 81, 78, 75, 80, 85, 92, 100, 110, 117, 125, 135, 144, 150, 155, 162, + 92, 87, 83, 80, 84, 90, 95, 102, 109, 117, 126, 137, 148, 155, 162, 168, + 97, 92, 88, 86, 85, 91, 97, 104, 112, 121, 130, 140, 150, 162, 168, 174, + }, { + 32, 31, 33, 36, 41, 49, 49, 50, 52, 54, 57, 61, 64, 67, 68, 70, + 31, 31, 34, 39, 42, 47, 46, 47, 49, 51, 53, 57, 60, 62, 64, 66, + 33, 34, 37, 42, 44, 47, 46, 46, 47, 49, 51, 55, 57, 59, 61, 63, + 36, 39, 42, 47, 47, 48, 46, 46, 47, 48, 50, 53, 55, 57, 59, 61, + 41, 42, 44, 47, 48, 50, 49, 50, 50, 52, 53, 56, 58, 60, 61, 60, + 49, 47, 47, 48, 50, 53, 53, 54, 54, 55, 56, 59, 61, 63, 64, 64, + 49, 46, 46, 46, 49, 53, 55, 57, 59, 60, 61, 64, 66, 67, 67, 67, + 50, 47, 46, 46, 50, 54, 57, 61, 63, 64, 66, 69, 70, 72, 71, 71, + 52, 49, 47, 47, 50, 54, 59, 63, 66, 68, 70, 73, 75, 77, 75, 75, + 54, 51, 49, 48, 52, 55, 60, 64, 68, 71, 73, 76, 79, 80, 79, 79, + 57, 53, 51, 50, 53, 56, 61, 66, 70, 73, 76, 80, 82, 84, 83, 84, + 61, 57, 55, 53, 56, 59, 64, 69, 73, 76, 80, 84, 87, 89, 88, 88, + 64, 60, 57, 55, 58, 61, 66, 70, 75, 79, 82, 87, 91, 93, 93, 93, + 67, 62, 59, 57, 60, 63, 67, 72, 77, 80, 84, 89, 93, 95, 96, 97, + 68, 64, 61, 59, 61, 64, 67, 71, 75, 79, 83, 88, 93, 96, 99, 100, + 70, 66, 63, 61, 60, 64, 67, 71, 75, 79, 84, 88, 93, 97, 100, 102, + }, + }, { + { + 32, 31, 31, 32, 33, 36, 39, 44, 48, 54, 59, 66, 74, 81, 86, 91, + 31, 32, 32, 32, 33, 35, 38, 42, 46, 51, 56, 63, 70, 77, 81, 86, + 31, 32, 32, 33, 34, 35, 38, 41, 45, 49, 54, 60, 67, 73, 77, 82, + 32, 32, 33, 34, 36, 37, 40, 42, 45, 49, 53, 59, 66, 71, 75, 80, + 33, 33, 34, 36, 38, 42, 44, 46, 50, 53, 57, 63, 69, 74, 78, 80, + 36, 35, 35, 37, 42, 48, 50, 54, 57, 60, 64, 69, 75, 80, 84, 85, + 39, 38, 38, 40, 44, 50, 54, 58, 61, 65, 69, 74, 80, 85, 89, 91, + 44, 42, 41, 42, 46, 54, 58, 63, 67, 71, 75, 80, 86, 91, 95, 97, + 48, 46, 45, 45, 50, 57, 61, 67, 71, 76, 80, 86, 93, 98, 101, 104, + 54, 51, 49, 49, 53, 60, 65, 71, 76, 82, 87, 93, 100, 105, 109, 112, + 59, 56, 54, 53, 57, 64, 69, 75, 80, 87, 92, 99, 106, 112, 116, 120, + 66, 63, 60, 59, 63, 69, 74, 80, 86, 93, 99, 107, 115, 121, 125, 129, + 74, 70, 67, 66, 69, 75, 80, 86, 93, 100, 106, 115, 123, 130, 135, 138, + 81, 77, 73, 71, 74, 80, 85, 91, 98, 105, 112, 121, 130, 137, 142, 148, + 86, 81, 77, 75, 78, 84, 89, 95, 101, 109, 116, 125, 135, 142, 147, 153, + 91, 86, 82, 80, 80, 85, 91, 97, 104, 112, 120, 129, 138, 148, 153, 159, + }, { + 32, 31, 31, 35, 40, 49, 48, 49, 50, 52, 54, 57, 61, 64, 66, 68, + 31, 31, 32, 37, 41, 47, 47, 46, 48, 49, 51, 54, 57, 60, 62, 64, + 31, 32, 34, 39, 43, 46, 46, 45, 46, 47, 49, 52, 55, 57, 59, 61, + 35, 37, 39, 44, 46, 47, 46, 45, 46, 47, 48, 51, 53, 56, 57, 59, + 40, 41, 43, 46, 48, 50, 49, 48, 49, 49, 51, 53, 55, 57, 59, 59, + 49, 47, 46, 47, 50, 53, 53, 53, 54, 54, 55, 57, 59, 61, 62, 62, + 48, 47, 46, 46, 49, 53, 54, 55, 56, 57, 58, 60, 62, 64, 65, 65, + 49, 46, 45, 45, 48, 53, 55, 58, 60, 61, 62, 64, 66, 68, 69, 69, + 50, 48, 46, 46, 49, 54, 56, 60, 61, 63, 65, 67, 69, 71, 72, 72, + 52, 49, 47, 47, 49, 54, 57, 61, 63, 66, 68, 71, 73, 75, 76, 77, + 54, 51, 49, 48, 51, 55, 58, 62, 65, 68, 71, 74, 76, 78, 80, 81, + 57, 54, 52, 51, 53, 57, 60, 64, 67, 71, 74, 77, 80, 83, 84, 85, + 61, 57, 55, 53, 55, 59, 62, 66, 69, 73, 76, 80, 84, 87, 89, 89, + 64, 60, 57, 56, 57, 61, 64, 68, 71, 75, 78, 83, 87, 90, 92, 94, + 66, 62, 59, 57, 59, 62, 65, 69, 72, 76, 80, 84, 89, 92, 94, 96, + 68, 64, 61, 59, 59, 62, 65, 69, 72, 77, 81, 85, 89, 94, 96, 98, + }, + }, { + { + 32, 31, 31, 31, 32, 34, 36, 39, 44, 48, 54, 59, 65, 71, 80, 83, + 31, 32, 32, 32, 32, 34, 35, 38, 42, 46, 51, 56, 62, 68, 76, 78, + 31, 32, 32, 32, 32, 33, 34, 37, 41, 44, 49, 54, 59, 65, 72, 75, + 31, 32, 32, 33, 34, 35, 36, 39, 42, 45, 50, 54, 59, 64, 71, 74, + 32, 32, 32, 34, 35, 37, 38, 40, 42, 46, 49, 53, 58, 63, 69, 72, + 34, 34, 33, 35, 37, 39, 42, 45, 47, 51, 54, 58, 63, 68, 74, 76, + 36, 35, 34, 36, 38, 42, 48, 50, 54, 57, 60, 64, 68, 73, 79, 81, + 39, 38, 37, 39, 40, 45, 50, 54, 58, 61, 65, 69, 73, 78, 84, 86, + 44, 42, 41, 42, 42, 47, 54, 58, 63, 67, 71, 75, 79, 84, 90, 92, + 48, 46, 44, 45, 46, 51, 57, 61, 67, 71, 76, 80, 85, 90, 96, 99, + 54, 51, 49, 50, 49, 54, 60, 65, 71, 76, 82, 87, 92, 97, 104, 106, + 59, 56, 54, 54, 53, 58, 64, 69, 75, 80, 87, 92, 98, 103, 110, 113, + 65, 62, 59, 59, 58, 63, 68, 73, 79, 85, 92, 98, 105, 111, 118, 121, + 71, 68, 65, 64, 63, 68, 73, 78, 84, 90, 97, 103, 111, 117, 125, 128, + 80, 76, 72, 71, 69, 74, 79, 84, 90, 96, 104, 110, 118, 125, 134, 137, + 83, 78, 75, 74, 72, 76, 81, 86, 92, 99, 106, 113, 121, 128, 137, 140, + }, { + 32, 31, 30, 33, 36, 41, 49, 48, 49, 50, 52, 54, 57, 60, 63, 65, + 31, 31, 31, 34, 38, 42, 47, 47, 47, 48, 50, 52, 54, 57, 60, 61, + 30, 31, 32, 35, 40, 42, 46, 45, 45, 46, 47, 49, 52, 54, 57, 58, + 33, 34, 35, 39, 43, 45, 47, 46, 45, 46, 47, 49, 51, 53, 56, 57, + 36, 38, 40, 43, 47, 47, 48, 46, 45, 46, 47, 48, 50, 52, 54, 55, + 41, 42, 42, 45, 47, 48, 50, 49, 49, 50, 50, 52, 53, 55, 57, 58, + 49, 47, 46, 47, 48, 50, 53, 53, 53, 54, 54, 55, 56, 58, 60, 61, + 48, 47, 45, 46, 46, 49, 53, 54, 55, 56, 57, 58, 60, 61, 63, 64, + 49, 47, 45, 45, 45, 49, 53, 55, 58, 60, 61, 62, 63, 65, 67, 68, + 50, 48, 46, 46, 46, 50, 54, 56, 60, 61, 63, 65, 67, 68, 71, 71, + 52, 50, 47, 47, 47, 50, 54, 57, 61, 63, 66, 68, 70, 72, 75, 75, + 54, 52, 49, 49, 48, 52, 55, 58, 62, 65, 68, 71, 73, 75, 78, 79, + 57, 54, 52, 51, 50, 53, 56, 60, 63, 67, 70, 73, 76, 79, 82, 83, + 60, 57, 54, 53, 52, 55, 58, 61, 65, 68, 72, 75, 79, 82, 85, 86, + 63, 60, 57, 56, 54, 57, 60, 63, 67, 71, 75, 78, 82, 85, 89, 90, + 65, 61, 58, 57, 55, 58, 61, 64, 68, 71, 75, 79, 83, 86, 90, 91, + }, + }, { + { + 32, 31, 31, 31, 32, 34, 35, 38, 41, 45, 48, 54, 59, 65, 71, 80, + 31, 32, 32, 32, 32, 34, 35, 37, 40, 43, 46, 51, 56, 62, 68, 76, + 31, 32, 32, 32, 32, 33, 34, 36, 38, 41, 44, 49, 54, 59, 65, 72, + 31, 32, 32, 33, 34, 35, 36, 38, 40, 42, 45, 50, 54, 59, 64, 71, + 32, 32, 32, 34, 35, 37, 38, 39, 41, 43, 46, 49, 53, 58, 63, 69, + 34, 34, 33, 35, 37, 39, 42, 44, 46, 48, 51, 54, 58, 63, 68, 74, + 35, 35, 34, 36, 38, 42, 46, 48, 50, 53, 55, 59, 62, 67, 72, 78, + 38, 37, 36, 38, 39, 44, 48, 51, 54, 57, 59, 63, 67, 71, 76, 82, + 41, 40, 38, 40, 41, 46, 50, 54, 57, 60, 63, 67, 71, 75, 80, 86, + 45, 43, 41, 42, 43, 48, 53, 57, 60, 65, 68, 72, 76, 81, 85, 91, + 48, 46, 44, 45, 46, 51, 55, 59, 63, 68, 71, 76, 80, 85, 90, 96, + 54, 51, 49, 50, 49, 54, 59, 63, 67, 72, 76, 82, 87, 92, 97, 104, + 59, 56, 54, 54, 53, 58, 62, 67, 71, 76, 80, 87, 92, 98, 103, 110, + 65, 62, 59, 59, 58, 63, 67, 71, 75, 81, 85, 92, 98, 105, 111, 118, + 71, 68, 65, 64, 63, 68, 72, 76, 80, 85, 90, 97, 103, 111, 117, 125, + 80, 76, 72, 71, 69, 74, 78, 82, 86, 91, 96, 104, 110, 118, 125, 134, + }, { + 32, 31, 30, 33, 36, 41, 47, 49, 49, 49, 50, 52, 54, 57, 60, 63, + 31, 31, 31, 34, 38, 42, 46, 47, 47, 47, 48, 50, 52, 54, 57, 60, + 30, 31, 32, 35, 40, 42, 45, 46, 45, 45, 46, 47, 49, 52, 54, 57, + 33, 34, 35, 39, 43, 45, 47, 46, 46, 45, 46, 47, 49, 51, 53, 56, + 36, 38, 40, 43, 47, 47, 47, 47, 46, 45, 46, 47, 48, 50, 52, 54, + 41, 42, 42, 45, 47, 48, 50, 50, 49, 49, 50, 50, 52, 53, 55, 57, + 47, 46, 45, 47, 47, 50, 52, 52, 52, 52, 53, 53, 55, 56, 58, 60, + 49, 47, 46, 46, 47, 50, 52, 53, 54, 55, 55, 56, 57, 58, 60, 62, + 49, 47, 45, 46, 46, 49, 52, 54, 55, 57, 58, 59, 60, 61, 63, 65, + 49, 47, 45, 45, 45, 49, 52, 55, 57, 59, 60, 61, 63, 64, 66, 68, + 50, 48, 46, 46, 46, 50, 53, 55, 58, 60, 61, 63, 65, 67, 68, 71, + 52, 50, 47, 47, 47, 50, 53, 56, 59, 61, 63, 66, 68, 70, 72, 75, + 54, 52, 49, 49, 48, 52, 55, 57, 60, 63, 65, 68, 71, 73, 75, 78, + 57, 54, 52, 51, 50, 53, 56, 58, 61, 64, 67, 70, 73, 76, 79, 82, + 60, 57, 54, 53, 52, 55, 58, 60, 63, 66, 68, 72, 75, 79, 82, 85, + 63, 60, 57, 56, 54, 57, 60, 62, 65, 68, 71, 75, 78, 82, 85, 89, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 34, 36, 38, 41, 44, 48, 54, 58, 61, 65, + 31, 32, 32, 32, 32, 32, 34, 35, 38, 40, 42, 46, 51, 55, 58, 62, + 31, 32, 32, 32, 32, 32, 33, 34, 37, 38, 41, 44, 49, 53, 56, 59, + 31, 32, 32, 33, 33, 33, 35, 36, 38, 40, 42, 45, 49, 53, 56, 59, + 32, 32, 32, 33, 34, 34, 36, 37, 39, 40, 42, 45, 49, 53, 55, 59, + 32, 32, 32, 33, 34, 35, 37, 38, 40, 41, 42, 46, 49, 52, 55, 58, + 34, 34, 33, 35, 36, 37, 39, 42, 44, 46, 47, 51, 54, 57, 60, 63, + 36, 35, 34, 36, 37, 38, 42, 48, 50, 52, 54, 57, 60, 63, 65, 68, + 38, 38, 37, 38, 39, 40, 44, 50, 52, 54, 57, 60, 64, 67, 69, 72, + 41, 40, 38, 40, 40, 41, 46, 52, 54, 57, 60, 63, 67, 70, 73, 75, + 44, 42, 41, 42, 42, 42, 47, 54, 57, 60, 63, 67, 71, 74, 77, 79, + 48, 46, 44, 45, 45, 46, 51, 57, 60, 63, 67, 71, 76, 79, 82, 85, + 54, 51, 49, 49, 49, 49, 54, 60, 64, 67, 71, 76, 82, 86, 89, 92, + 58, 55, 53, 53, 53, 52, 57, 63, 67, 70, 74, 79, 86, 90, 93, 97, + 61, 58, 56, 56, 55, 55, 60, 65, 69, 73, 77, 82, 89, 93, 97, 101, + 65, 62, 59, 59, 59, 58, 63, 68, 72, 75, 79, 85, 92, 97, 101, 105, + }, { + 32, 31, 30, 33, 34, 36, 41, 49, 48, 49, 49, 50, 52, 54, 55, 57, + 31, 31, 31, 34, 36, 38, 42, 47, 47, 47, 47, 48, 50, 51, 53, 54, + 30, 31, 32, 34, 37, 40, 42, 46, 45, 45, 45, 46, 47, 49, 50, 52, + 33, 34, 34, 37, 40, 42, 44, 47, 46, 46, 45, 46, 47, 49, 50, 51, + 34, 36, 37, 40, 42, 45, 46, 47, 46, 46, 45, 46, 47, 48, 49, 50, + 36, 38, 40, 42, 45, 47, 47, 48, 47, 46, 45, 46, 47, 48, 49, 50, + 41, 42, 42, 44, 46, 47, 48, 50, 50, 49, 49, 50, 50, 51, 52, 53, + 49, 47, 46, 47, 47, 48, 50, 53, 53, 53, 53, 54, 54, 55, 56, 56, + 48, 47, 45, 46, 46, 47, 50, 53, 54, 54, 55, 56, 57, 58, 58, 59, + 49, 47, 45, 46, 46, 46, 49, 53, 54, 55, 57, 58, 59, 60, 60, 61, + 49, 47, 45, 45, 45, 45, 49, 53, 55, 57, 58, 60, 61, 62, 63, 63, + 50, 48, 46, 46, 46, 46, 50, 54, 56, 58, 60, 61, 63, 65, 66, 67, + 52, 50, 47, 47, 47, 47, 50, 54, 57, 59, 61, 63, 66, 68, 69, 70, + 54, 51, 49, 49, 48, 48, 51, 55, 58, 60, 62, 65, 68, 70, 71, 73, + 55, 53, 50, 50, 49, 49, 52, 56, 58, 60, 63, 66, 69, 71, 73, 74, + 57, 54, 52, 51, 50, 50, 53, 56, 59, 61, 63, 67, 70, 73, 74, 76, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 34, 36, 38, 39, 44, 47, 49, 54, 59, + 31, 32, 32, 32, 32, 32, 33, 34, 35, 37, 38, 42, 45, 47, 51, 56, + 31, 32, 32, 32, 32, 32, 33, 33, 34, 36, 37, 41, 44, 46, 50, 54, + 31, 32, 32, 32, 32, 33, 33, 34, 35, 36, 38, 41, 44, 45, 49, 54, + 31, 32, 32, 32, 33, 34, 34, 35, 36, 38, 39, 42, 45, 46, 50, 54, + 32, 32, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 45, 46, 49, 53, + 32, 33, 33, 33, 34, 36, 36, 38, 40, 41, 42, 44, 47, 48, 51, 55, + 34, 34, 33, 34, 35, 37, 38, 39, 42, 44, 45, 47, 50, 51, 54, 58, + 36, 35, 34, 35, 36, 38, 40, 42, 48, 50, 50, 54, 56, 57, 60, 64, + 38, 37, 36, 36, 38, 39, 41, 44, 50, 51, 52, 56, 58, 60, 63, 67, + 39, 38, 37, 38, 39, 40, 42, 45, 50, 52, 54, 58, 60, 62, 65, 69, + 44, 42, 41, 41, 42, 42, 44, 47, 54, 56, 58, 63, 66, 68, 71, 75, + 47, 45, 44, 44, 45, 45, 47, 50, 56, 58, 60, 66, 69, 71, 75, 79, + 49, 47, 46, 45, 46, 46, 48, 51, 57, 60, 62, 68, 71, 73, 77, 81, + 54, 51, 50, 49, 50, 49, 51, 54, 60, 63, 65, 71, 75, 77, 82, 87, + 59, 56, 54, 54, 54, 53, 55, 58, 64, 67, 69, 75, 79, 81, 87, 92, + }, { + 32, 31, 30, 31, 33, 36, 38, 41, 49, 49, 48, 49, 50, 51, 52, 54, + 31, 31, 31, 32, 34, 38, 40, 42, 47, 47, 47, 47, 48, 48, 50, 52, + 30, 31, 31, 32, 35, 39, 41, 42, 46, 46, 46, 45, 46, 47, 48, 50, + 31, 32, 32, 33, 36, 40, 41, 43, 46, 46, 45, 45, 46, 46, 47, 49, + 33, 34, 35, 36, 39, 43, 44, 45, 47, 46, 46, 45, 46, 47, 47, 49, + 36, 38, 39, 40, 43, 47, 47, 47, 48, 47, 46, 45, 46, 46, 47, 48, + 38, 40, 41, 41, 44, 47, 47, 48, 49, 48, 48, 47, 47, 47, 48, 49, + 41, 42, 42, 43, 45, 47, 48, 48, 50, 50, 49, 49, 50, 50, 50, 52, + 49, 47, 46, 46, 47, 48, 49, 50, 53, 53, 53, 53, 54, 54, 54, 55, + 49, 47, 46, 46, 46, 47, 48, 50, 53, 53, 54, 55, 55, 55, 56, 57, + 48, 47, 46, 45, 46, 46, 48, 49, 53, 54, 54, 55, 56, 56, 57, 58, + 49, 47, 45, 45, 45, 45, 47, 49, 53, 55, 55, 58, 59, 60, 61, 62, + 50, 48, 46, 46, 46, 46, 47, 50, 54, 55, 56, 59, 61, 61, 63, 64, + 51, 48, 47, 46, 47, 46, 47, 50, 54, 55, 56, 60, 61, 62, 64, 66, + 52, 50, 48, 47, 47, 47, 48, 50, 54, 56, 57, 61, 63, 64, 66, 68, + 54, 52, 50, 49, 49, 48, 49, 52, 55, 57, 58, 62, 64, 66, 68, 71, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 32, 32, 34, 35, 36, 39, 41, 44, 47, 48, + 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 38, 40, 42, 45, 46, + 31, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 38, 39, 42, 45, 45, + 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 38, 41, 44, 44, + 31, 32, 32, 32, 33, 33, 33, 34, 35, 36, 36, 39, 40, 42, 44, 45, + 31, 32, 32, 32, 33, 33, 34, 34, 35, 36, 36, 39, 40, 42, 45, 45, + 32, 32, 32, 32, 33, 34, 35, 36, 37, 38, 38, 40, 41, 42, 45, 46, + 32, 33, 33, 33, 34, 34, 36, 36, 38, 39, 40, 42, 43, 44, 47, 47, + 34, 34, 34, 33, 35, 35, 37, 38, 39, 42, 42, 45, 46, 47, 50, 51, + 35, 35, 34, 34, 36, 36, 38, 39, 42, 46, 47, 49, 50, 52, 55, 55, + 36, 35, 35, 34, 36, 36, 38, 40, 42, 47, 48, 50, 52, 54, 56, 57, + 39, 38, 38, 37, 39, 39, 40, 42, 45, 49, 50, 54, 55, 58, 60, 61, + 41, 40, 39, 38, 40, 40, 41, 43, 46, 50, 52, 55, 57, 60, 62, 63, + 44, 42, 42, 41, 42, 42, 42, 44, 47, 52, 54, 58, 60, 63, 66, 67, + 47, 45, 45, 44, 44, 45, 45, 47, 50, 55, 56, 60, 62, 66, 69, 70, + 48, 46, 45, 44, 45, 45, 46, 47, 51, 55, 57, 61, 63, 67, 70, 71, + }, { + 32, 31, 31, 30, 33, 33, 36, 38, 41, 47, 49, 48, 49, 49, 50, 50, + 31, 31, 31, 31, 34, 34, 38, 40, 42, 46, 47, 47, 47, 47, 48, 48, + 31, 31, 31, 31, 34, 35, 39, 40, 42, 46, 47, 46, 46, 46, 47, 47, + 30, 31, 31, 32, 34, 35, 40, 41, 42, 45, 46, 45, 45, 45, 46, 46, + 33, 34, 34, 34, 37, 38, 42, 43, 44, 46, 47, 46, 46, 45, 46, 46, + 33, 34, 35, 35, 38, 39, 43, 44, 45, 47, 47, 46, 46, 45, 46, 46, + 36, 38, 39, 40, 42, 43, 47, 47, 47, 47, 48, 46, 46, 45, 46, 46, + 38, 40, 40, 41, 43, 44, 47, 47, 48, 48, 49, 48, 47, 47, 47, 47, + 41, 42, 42, 42, 44, 45, 47, 48, 48, 50, 50, 49, 49, 49, 50, 50, + 47, 46, 46, 45, 46, 47, 47, 48, 50, 52, 52, 52, 52, 52, 53, 53, + 49, 47, 47, 46, 47, 47, 48, 49, 50, 52, 53, 53, 53, 53, 54, 54, + 48, 47, 46, 45, 46, 46, 46, 48, 49, 52, 53, 54, 55, 55, 56, 56, + 49, 47, 46, 45, 46, 46, 46, 47, 49, 52, 53, 55, 55, 57, 57, 58, + 49, 47, 46, 45, 45, 45, 45, 47, 49, 52, 53, 55, 57, 58, 59, 60, + 50, 48, 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 57, 59, 61, 61, + 50, 48, 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 58, 60, 61, 61, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 32, 34, 34, 36, 36, 39, 39, 44, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 38, 38, 42, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 38, 38, 42, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 37, 41, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 37, 41, + 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 39, 39, 42, + 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 39, 39, 42, + 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 42, + 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 42, + 34, 34, 34, 33, 33, 35, 35, 37, 37, 39, 39, 42, 42, 45, 45, 47, + 34, 34, 34, 33, 33, 35, 35, 37, 37, 39, 39, 42, 42, 45, 45, 47, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 54, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 54, + 39, 38, 38, 37, 37, 39, 39, 40, 40, 45, 45, 50, 50, 54, 54, 58, + 39, 38, 38, 37, 37, 39, 39, 40, 40, 45, 45, 50, 50, 54, 54, 58, + 44, 42, 42, 41, 41, 42, 42, 42, 42, 47, 47, 54, 54, 58, 58, 63, + }, { + 32, 31, 31, 30, 30, 33, 33, 36, 36, 41, 41, 49, 49, 48, 48, 49, + 31, 31, 31, 31, 31, 34, 34, 38, 38, 42, 42, 47, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 34, 34, 38, 38, 42, 42, 47, 47, 47, 47, 47, + 30, 31, 31, 32, 32, 35, 35, 40, 40, 42, 42, 46, 46, 45, 45, 45, + 30, 31, 31, 32, 32, 35, 35, 40, 40, 42, 42, 46, 46, 45, 45, 45, + 33, 34, 34, 35, 35, 39, 39, 43, 43, 45, 45, 47, 47, 46, 46, 45, + 33, 34, 34, 35, 35, 39, 39, 43, 43, 45, 45, 47, 47, 46, 46, 45, + 36, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 46, 46, 45, + 36, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 46, 46, 45, + 41, 42, 42, 42, 42, 45, 45, 47, 47, 48, 48, 50, 50, 49, 49, 49, + 41, 42, 42, 42, 42, 45, 45, 47, 47, 48, 48, 50, 50, 49, 49, 49, + 49, 47, 47, 46, 46, 47, 47, 48, 48, 50, 50, 53, 53, 53, 53, 53, + 49, 47, 47, 46, 46, 47, 47, 48, 48, 50, 50, 53, 53, 53, 53, 53, + 48, 47, 47, 45, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 54, 55, + 48, 47, 47, 45, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 54, 55, + 49, 47, 47, 45, 45, 45, 45, 45, 45, 49, 49, 53, 53, 55, 55, 58, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 34, 36, 36, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 35, 35, 36, 36, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 36, 36, 36, + 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 36, 36, 37, 37, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, 38, 38, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, 38, 38, + 33, 33, 33, 33, 33, 33, 34, 35, 35, 36, 36, 38, 39, 40, 42, 42, + 34, 34, 34, 34, 33, 33, 35, 35, 36, 37, 37, 39, 39, 41, 42, 42, + 34, 34, 34, 34, 34, 34, 35, 36, 36, 37, 37, 40, 41, 42, 45, 45, + 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 42, 42, 45, 48, 48, + 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 42, 42, 45, 48, 48, + }, { + 32, 31, 31, 31, 30, 30, 33, 33, 34, 36, 36, 40, 41, 44, 49, 49, + 31, 31, 31, 31, 31, 31, 33, 34, 36, 38, 38, 41, 42, 44, 48, 48, + 31, 31, 31, 31, 31, 31, 34, 34, 36, 38, 38, 41, 42, 44, 47, 47, + 31, 31, 31, 31, 31, 31, 34, 35, 36, 39, 39, 41, 42, 44, 47, 47, + 30, 31, 31, 31, 32, 32, 34, 35, 37, 40, 40, 42, 42, 44, 46, 46, + 30, 31, 31, 31, 32, 32, 34, 35, 37, 40, 40, 42, 42, 44, 46, 46, + 33, 33, 34, 34, 34, 34, 37, 38, 40, 42, 42, 44, 44, 45, 47, 47, + 33, 34, 34, 35, 35, 35, 38, 39, 40, 43, 43, 44, 45, 46, 47, 47, + 34, 36, 36, 36, 37, 37, 40, 40, 42, 45, 45, 45, 46, 46, 47, 47, + 36, 38, 38, 39, 40, 40, 42, 43, 45, 47, 47, 47, 47, 47, 48, 48, + 36, 38, 38, 39, 40, 40, 42, 43, 45, 47, 47, 47, 47, 47, 48, 48, + 40, 41, 41, 41, 42, 42, 44, 44, 45, 47, 47, 48, 48, 49, 50, 50, + 41, 42, 42, 42, 42, 42, 44, 45, 46, 47, 47, 48, 48, 49, 50, 50, + 44, 44, 44, 44, 44, 44, 45, 46, 46, 47, 47, 49, 49, 50, 51, 51, + 49, 48, 47, 47, 46, 46, 47, 47, 47, 48, 48, 50, 50, 51, 53, 53, + 49, 48, 47, 47, 46, 46, 47, 47, 47, 48, 48, 50, 50, 51, 53, 53, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 35, 35, 35, 36, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 37, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 37, + 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 36, 36, 36, 38, + 34, 34, 34, 34, 34, 33, 33, 34, 35, 35, 35, 36, 37, 37, 38, 39, + }, { + 32, 31, 31, 31, 31, 30, 30, 31, 33, 33, 33, 35, 36, 36, 38, 41, + 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 34, 36, 37, 37, 39, 42, + 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 37, 38, 38, 40, 42, + 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 37, 38, 38, 40, 42, + 31, 31, 31, 31, 31, 31, 31, 32, 34, 35, 35, 37, 39, 39, 40, 42, + 30, 31, 31, 31, 31, 32, 32, 32, 34, 35, 35, 38, 40, 40, 41, 42, + 30, 31, 31, 31, 31, 32, 32, 32, 34, 35, 35, 38, 40, 40, 41, 42, + 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, + 33, 33, 34, 34, 34, 34, 34, 35, 37, 38, 38, 41, 42, 42, 43, 44, + 33, 34, 34, 34, 35, 35, 35, 36, 38, 39, 39, 41, 43, 43, 44, 45, + 33, 34, 34, 34, 35, 35, 35, 36, 38, 39, 39, 41, 43, 43, 44, 45, + 35, 36, 37, 37, 37, 38, 38, 38, 41, 41, 41, 44, 46, 46, 46, 46, + 36, 37, 38, 38, 39, 40, 40, 40, 42, 43, 43, 46, 47, 47, 47, 47, + 36, 37, 38, 38, 39, 40, 40, 40, 42, 43, 43, 46, 47, 47, 47, 47, + 38, 39, 40, 40, 40, 41, 41, 41, 43, 44, 44, 46, 47, 47, 47, 48, + 41, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 46, 47, 47, 48, 48, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + }, { + 32, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 33, 33, 33, 33, + 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 32, 33, 34, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 35, 35, + 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 35, 35, + 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, + 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, + 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 36, 37, 37, 37, + 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 36, 37, 38, 38, 38, + 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, + 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, + 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, + }, + }, +}; + +static const uint8_t qm_tbl_16x32[15][2][512] = { + { + { + 32, 31, 32, 34, 36, 44, 53, 59, 65, 79, 87, 90, 93, 96, 99, 102, 31, 32, 32, 34, 35, 42, 51, 56, 62, 75, 82, 85, 88, 91, 94, 97, + 31, 32, 33, 33, 34, 41, 49, 54, 59, 72, 78, 82, 86, 90, 93, 97, 31, 32, 33, 34, 35, 41, 49, 54, 59, 71, 78, 81, 84, 87, 90, 93, + 32, 32, 34, 35, 36, 42, 50, 54, 59, 71, 77, 80, 82, 86, 89, 93, 32, 33, 35, 37, 38, 42, 49, 53, 58, 69, 75, 78, 82, 86, 89, 92, + 34, 34, 37, 39, 42, 48, 54, 58, 63, 73, 79, 78, 80, 83, 88, 92, 35, 34, 37, 41, 45, 50, 57, 61, 65, 76, 82, 83, 84, 84, 87, 90, + 36, 34, 38, 43, 48, 54, 60, 64, 68, 78, 84, 87, 86, 89, 90, 90, 39, 37, 40, 45, 50, 58, 65, 69, 73, 84, 89, 89, 91, 91, 93, 96, + 44, 41, 43, 48, 53, 63, 71, 75, 79, 90, 95, 93, 94, 95, 97, 97, 46, 43, 44, 49, 55, 65, 73, 78, 82, 93, 98, 100, 98, 100, 99, 103, + 48, 45, 46, 51, 56, 67, 76, 80, 85, 96, 102, 102, 105, 102, 105, 104, 53, 49, 50, 54, 60, 71, 82, 87, 92, 103, 109, 107, 107, 110, 107, 111, + 58, 54, 54, 58, 63, 75, 87, 92, 98, 110, 116, 115, 112, 111, 115, 112, 61, 57, 56, 60, 66, 77, 89, 95, 101, 114, 120, 118, 119, 118, 116, 120, + 65, 60, 58, 63, 68, 79, 92, 98, 105, 118, 124, 123, 122, 123, 124, 121, 71, 65, 63, 68, 73, 84, 97, 103, 111, 125, 132, 132, 130, 128, 127, 130, + 79, 72, 70, 74, 79, 90, 104, 110, 118, 133, 141, 136, 135, 135, 135, 131, 81, 74, 71, 75, 80, 91, 105, 112, 119, 135, 142, 140, 140, 138, 139, 142, + 82, 75, 72, 76, 81, 92, 106, 113, 121, 136, 144, 151, 149, 149, 146, 143, 88, 80, 77, 80, 85, 97, 108, 115, 126, 142, 149, 153, 153, 152, 152, 154, + 91, 83, 80, 81, 88, 100, 106, 114, 130, 142, 148, 155, 162, 160, 159, 155, 94, 85, 83, 82, 91, 100, 105, 118, 131, 137, 153, 160, 165, 167, 166, 168, + 97, 88, 86, 85, 94, 100, 107, 123, 128, 140, 157, 161, 167, 173, 171, 169, 100, 91, 89, 87, 97, 100, 111, 121, 127, 145, 152, 164, 173, 178, 182, 181, + 103, 94, 93, 90, 98, 101, 114, 120, 131, 144, 150, 170, 174, 180, 186, 183, 107, 97, 96, 93, 100, 104, 117, 119, 136, 142, 155, 168, 177, 187, 191, 198, + 110, 101, 100, 97, 101, 108, 117, 123, 138, 141, 161, 165, 183, 188, 193, 200, 114, 104, 104, 100, 103, 112, 117, 127, 137, 146, 159, 167, 185, 190, 201, 206, + 118, 108, 107, 103, 105, 115, 118, 131, 136, 151, 157, 172, 182, 197, 203, 208, 122, 111, 111, 107, 107, 119, 119, 136, 136, 156, 156, 178, 179, 203, 204, 217, + }, { + 32, 31, 37, 42, 48, 49, 52, 54, 57, 63, 66, 67, 68, 69, 71, 72, 31, 31, 38, 42, 47, 47, 50, 52, 54, 60, 63, 64, 65, 66, 67, 68, + 30, 32, 40, 42, 46, 45, 48, 50, 52, 57, 60, 62, 63, 65, 66, 68, 32, 34, 41, 44, 46, 45, 48, 49, 51, 57, 59, 61, 62, 63, 64, 65, + 33, 36, 43, 45, 47, 46, 47, 49, 51, 56, 59, 60, 60, 62, 63, 65, 37, 40, 47, 47, 47, 45, 47, 48, 50, 54, 57, 58, 60, 61, 62, 63, + 42, 43, 47, 48, 50, 49, 50, 52, 53, 57, 60, 58, 59, 60, 62, 63, 45, 44, 47, 49, 51, 51, 52, 54, 55, 59, 61, 61, 61, 60, 61, 61, + 49, 46, 48, 50, 53, 53, 54, 55, 57, 60, 62, 63, 62, 63, 62, 62, 48, 46, 47, 50, 53, 56, 57, 59, 60, 64, 66, 65, 65, 64, 64, 65, + 49, 45, 46, 49, 53, 58, 61, 62, 64, 67, 69, 67, 66, 66, 66, 65, 49, 46, 46, 49, 53, 59, 62, 64, 65, 69, 71, 70, 68, 68, 67, 68, + 50, 46, 46, 50, 54, 59, 64, 65, 67, 71, 73, 72, 72, 70, 70, 69, 52, 48, 47, 50, 54, 61, 66, 68, 71, 75, 77, 74, 73, 73, 71, 72, + 54, 50, 49, 52, 55, 62, 68, 71, 73, 78, 80, 78, 76, 74, 75, 73, 55, 51, 49, 52, 56, 63, 69, 72, 75, 80, 82, 80, 79, 78, 76, 77, + 57, 52, 50, 53, 56, 64, 70, 73, 76, 82, 84, 82, 80, 80, 79, 77, 60, 54, 52, 55, 58, 65, 72, 75, 79, 85, 88, 86, 84, 82, 81, 81, + 63, 57, 55, 58, 60, 67, 75, 78, 82, 89, 92, 88, 87, 85, 84, 81, 64, 58, 55, 58, 61, 68, 75, 78, 82, 89, 92, 90, 89, 87, 86, 86, + 64, 59, 56, 58, 61, 68, 75, 79, 83, 90, 93, 95, 93, 91, 89, 87, 67, 61, 58, 60, 63, 69, 76, 79, 85, 92, 95, 96, 94, 92, 91, 91, + 68, 62, 59, 60, 64, 71, 74, 78, 86, 91, 94, 96, 98, 96, 94, 91, 69, 62, 60, 60, 65, 70, 72, 79, 85, 88, 95, 98, 99, 98, 97, 96, + 70, 63, 62, 60, 66, 69, 73, 81, 83, 89, 96, 97, 99, 101, 98, 97, 71, 64, 63, 61, 67, 68, 74, 79, 82, 90, 93, 98, 102, 102, 102, 101, + 72, 65, 64, 62, 66, 68, 75, 78, 83, 89, 92, 100, 101, 103, 104, 102, 73, 66, 65, 63, 66, 69, 75, 76, 84, 87, 93, 98, 102, 105, 106, 107, + 74, 67, 67, 64, 66, 70, 74, 77, 84, 86, 94, 96, 103, 105, 106, 107, 75, 68, 68, 65, 66, 71, 74, 78, 83, 87, 93, 96, 103, 105, 109, 109, + 76, 69, 69, 66, 67, 72, 73, 80, 82, 88, 91, 97, 101, 107, 109, 110, 77, 70, 70, 67, 67, 73, 73, 81, 81, 90, 90, 99, 99, 108, 108, 113, + }, + }, { + { + 32, 31, 32, 32, 36, 44, 47, 53, 65, 73, 79, 87, 90, 93, 96, 99, 31, 32, 32, 33, 35, 42, 45, 51, 62, 69, 75, 83, 86, 88, 91, 94, + 31, 32, 32, 33, 35, 41, 44, 49, 60, 67, 72, 80, 84, 87, 90, 94, 31, 32, 33, 33, 35, 41, 44, 49, 59, 66, 71, 79, 82, 84, 87, 90, + 32, 32, 34, 34, 36, 42, 45, 50, 59, 65, 71, 78, 80, 83, 87, 90, 32, 33, 35, 36, 38, 42, 45, 49, 58, 64, 69, 76, 80, 83, 86, 88, + 32, 33, 35, 36, 40, 44, 47, 51, 60, 66, 71, 76, 78, 81, 85, 89, 34, 34, 36, 38, 42, 48, 50, 54, 63, 69, 73, 80, 82, 81, 84, 86, + 36, 34, 37, 40, 48, 54, 56, 60, 68, 74, 78, 84, 83, 86, 87, 87, 38, 36, 39, 41, 49, 56, 58, 63, 71, 77, 81, 86, 88, 88, 90, 93, + 39, 37, 40, 42, 50, 58, 60, 65, 73, 79, 84, 90, 91, 92, 94, 93, 44, 41, 42, 45, 53, 63, 66, 71, 79, 85, 90, 96, 94, 96, 96, 99, + 47, 44, 45, 47, 56, 66, 69, 75, 84, 90, 95, 99, 101, 98, 101, 99, 49, 46, 47, 48, 57, 67, 71, 77, 86, 93, 97, 103, 103, 105, 102, 106, + 53, 49, 50, 51, 60, 71, 75, 82, 92, 99, 103, 111, 108, 107, 110, 107, 58, 54, 54, 55, 63, 75, 79, 87, 98, 105, 110, 114, 114, 113, 111, 115, + 61, 56, 56, 57, 65, 77, 81, 89, 100, 107, 113, 118, 116, 117, 118, 116, 65, 60, 59, 60, 68, 79, 84, 92, 105, 112, 118, 126, 124, 122, 121, 124, + 71, 65, 64, 65, 73, 84, 89, 97, 111, 119, 125, 130, 129, 129, 129, 125, 76, 69, 68, 69, 76, 88, 92, 101, 115, 123, 130, 134, 134, 131, 132, 135, + 79, 72, 70, 71, 79, 90, 95, 104, 118, 127, 133, 143, 142, 141, 138, 136, 82, 75, 73, 74, 81, 92, 97, 106, 121, 130, 136, 146, 145, 144, 144, 145, + 86, 78, 76, 77, 84, 95, 100, 109, 124, 133, 140, 147, 153, 151, 150, 146, 89, 81, 79, 78, 87, 95, 99, 112, 124, 130, 145, 152, 156, 157, 156, 158, + 92, 84, 82, 80, 89, 95, 101, 116, 121, 132, 148, 151, 157, 163, 161, 159, 95, 86, 85, 83, 92, 95, 105, 114, 120, 136, 143, 155, 163, 167, 171, 170, + 98, 89, 88, 85, 93, 95, 108, 113, 124, 136, 141, 160, 163, 169, 174, 171, 101, 92, 91, 88, 94, 98, 110, 112, 128, 133, 146, 158, 166, 175, 179, 185, + 104, 95, 94, 91, 95, 101, 110, 115, 129, 132, 151, 154, 171, 175, 181, 186, 107, 98, 97, 94, 96, 105, 110, 119, 128, 136, 149, 156, 173, 177, 188, 192, + 110, 101, 100, 97, 98, 108, 111, 123, 127, 141, 147, 161, 169, 183, 188, 193, 114, 104, 104, 100, 100, 111, 111, 126, 127, 145, 145, 166, 166, 189, 190, 201, + }, { + 32, 31, 35, 38, 48, 49, 50, 52, 57, 61, 63, 67, 68, 69, 70, 71, 31, 31, 37, 40, 47, 47, 48, 50, 54, 57, 60, 63, 64, 65, 66, 67, + 30, 32, 38, 40, 46, 45, 46, 48, 52, 55, 58, 61, 63, 64, 65, 67, 31, 33, 38, 41, 46, 45, 46, 48, 52, 55, 57, 60, 61, 62, 63, 64, + 33, 36, 41, 44, 47, 46, 46, 47, 51, 54, 56, 59, 60, 61, 63, 64, 37, 40, 45, 47, 47, 45, 46, 47, 50, 52, 54, 57, 59, 61, 62, 62, + 39, 41, 46, 47, 48, 47, 47, 48, 51, 54, 55, 57, 58, 59, 61, 62, 42, 43, 46, 48, 50, 49, 50, 50, 53, 56, 57, 60, 60, 59, 60, 60, + 49, 46, 48, 49, 53, 53, 54, 54, 57, 59, 60, 63, 61, 62, 61, 61, 48, 46, 47, 48, 53, 55, 55, 56, 58, 61, 62, 64, 64, 63, 63, 64, + 48, 46, 46, 48, 53, 56, 56, 57, 60, 62, 64, 66, 65, 65, 65, 64, 49, 45, 45, 47, 53, 58, 59, 61, 64, 66, 67, 69, 67, 67, 66, 67, + 50, 46, 46, 48, 54, 59, 61, 63, 66, 68, 70, 71, 71, 68, 69, 67, 51, 47, 47, 48, 54, 60, 61, 64, 68, 70, 71, 73, 72, 72, 70, 71, + 52, 48, 47, 48, 54, 61, 63, 66, 71, 73, 75, 77, 75, 73, 74, 71, 54, 50, 49, 50, 55, 62, 65, 68, 73, 76, 78, 79, 78, 76, 74, 75, + 55, 51, 49, 50, 56, 63, 65, 69, 74, 77, 79, 81, 79, 78, 78, 75, 57, 52, 50, 51, 56, 64, 66, 70, 76, 79, 82, 85, 83, 81, 79, 79, + 60, 54, 53, 53, 58, 65, 68, 72, 79, 82, 85, 87, 85, 84, 82, 80, 62, 56, 54, 55, 60, 66, 69, 74, 81, 84, 87, 88, 87, 85, 84, 84, + 63, 57, 55, 56, 60, 67, 70, 75, 82, 86, 89, 92, 91, 89, 87, 84, 64, 59, 56, 57, 61, 68, 71, 75, 83, 87, 90, 93, 92, 90, 89, 89, + 66, 60, 58, 58, 62, 69, 72, 76, 84, 88, 91, 94, 95, 93, 91, 89, 67, 61, 59, 58, 63, 68, 71, 78, 83, 86, 93, 96, 96, 96, 94, 94, + 68, 62, 60, 59, 64, 67, 71, 79, 81, 86, 94, 95, 97, 98, 96, 94, 69, 63, 61, 60, 65, 66, 72, 77, 80, 88, 91, 96, 99, 99, 100, 98, + 70, 64, 62, 60, 65, 66, 73, 76, 81, 87, 89, 97, 98, 100, 101, 99, 71, 65, 64, 61, 65, 67, 73, 74, 82, 85, 90, 95, 99, 102, 103, 104, + 72, 65, 65, 62, 65, 68, 72, 75, 82, 83, 92, 93, 100, 102, 103, 104, 73, 66, 66, 63, 65, 69, 72, 76, 81, 85, 90, 93, 100, 102, 105, 106, + 74, 67, 67, 64, 65, 70, 71, 77, 79, 86, 89, 94, 98, 103, 105, 106, 75, 68, 68, 65, 65, 71, 71, 78, 78, 87, 87, 96, 96, 105, 105, 109, + }, + }, { + { + 32, 31, 32, 32, 36, 39, 44, 53, 58, 65, 79, 81, 88, 90, 93, 96, 31, 32, 32, 32, 35, 38, 42, 51, 55, 62, 75, 77, 83, 86, 88, 91, + 31, 32, 32, 32, 35, 38, 41, 50, 54, 60, 73, 75, 81, 84, 88, 91, 31, 32, 32, 33, 34, 37, 41, 49, 53, 59, 72, 74, 79, 82, 84, 87, + 32, 32, 33, 34, 36, 39, 42, 50, 53, 59, 71, 72, 78, 81, 84, 87, 32, 32, 34, 34, 37, 40, 42, 49, 53, 58, 70, 71, 77, 80, 83, 85, + 32, 33, 34, 35, 38, 40, 42, 49, 52, 58, 69, 70, 76, 78, 82, 86, 34, 34, 35, 37, 42, 45, 48, 54, 57, 63, 73, 75, 79, 79, 81, 83, + 34, 34, 36, 37, 44, 47, 50, 56, 59, 65, 75, 77, 81, 83, 84, 84, 36, 34, 37, 38, 48, 51, 54, 60, 63, 68, 78, 80, 85, 85, 86, 89, + 39, 37, 39, 40, 50, 54, 58, 65, 68, 73, 84, 85, 88, 89, 90, 89, 40, 38, 40, 41, 51, 55, 59, 67, 70, 75, 85, 87, 91, 92, 92, 95, + 44, 41, 42, 43, 53, 58, 63, 71, 74, 79, 90, 91, 97, 94, 97, 95, 47, 44, 45, 46, 56, 61, 66, 75, 79, 85, 95, 97, 99, 101, 98, 102, + 49, 46, 46, 47, 57, 62, 67, 77, 81, 86, 97, 99, 104, 102, 105, 102, 53, 49, 50, 50, 60, 65, 71, 82, 86, 92, 103, 105, 109, 108, 106, 110, + 57, 53, 53, 53, 63, 68, 74, 86, 90, 97, 108, 110, 111, 112, 113, 110, 59, 54, 54, 54, 64, 69, 75, 87, 91, 98, 111, 112, 119, 117, 115, 118, + 65, 60, 59, 58, 68, 73, 79, 92, 97, 105, 118, 119, 123, 123, 122, 119, 69, 63, 62, 62, 71, 76, 83, 96, 100, 109, 122, 124, 127, 125, 125, 128, + 71, 65, 64, 63, 73, 78, 84, 97, 102, 111, 125, 127, 135, 134, 131, 129, 79, 72, 71, 70, 79, 84, 90, 104, 109, 118, 133, 135, 137, 136, 136, 137, + 81, 74, 72, 71, 80, 85, 91, 105, 110, 120, 135, 137, 145, 143, 141, 138, 82, 75, 73, 72, 81, 86, 92, 106, 111, 121, 136, 139, 147, 148, 147, 149, + 87, 79, 77, 76, 85, 90, 96, 110, 114, 125, 140, 143, 148, 154, 151, 149, 90, 82, 80, 78, 87, 89, 99, 108, 113, 129, 135, 146, 153, 157, 160, 159, + 92, 84, 83, 81, 88, 90, 102, 106, 117, 128, 133, 150, 153, 158, 163, 160, 95, 87, 85, 83, 88, 92, 103, 105, 120, 125, 137, 148, 155, 164, 168, 173, + 98, 89, 88, 85, 89, 95, 103, 108, 121, 124, 141, 144, 160, 164, 169, 174, 100, 92, 91, 88, 90, 98, 103, 111, 120, 127, 139, 146, 161, 165, 175, 179, + 103, 94, 94, 90, 92, 101, 103, 114, 119, 131, 137, 150, 158, 170, 175, 180, 106, 97, 97, 93, 93, 104, 104, 118, 118, 135, 135, 154, 155, 175, 176, 187, + }, { + 32, 31, 34, 37, 48, 48, 49, 52, 54, 57, 63, 64, 67, 68, 69, 69, 31, 31, 35, 38, 47, 47, 47, 50, 51, 54, 60, 61, 63, 64, 65, 66, + 31, 32, 36, 39, 46, 46, 46, 48, 50, 53, 58, 59, 62, 63, 65, 66, 30, 32, 36, 40, 46, 45, 45, 48, 49, 52, 57, 58, 60, 61, 62, 63, + 33, 36, 40, 43, 47, 46, 46, 47, 49, 51, 56, 57, 59, 60, 62, 63, 35, 38, 42, 45, 47, 46, 45, 47, 48, 50, 55, 56, 58, 60, 61, 61, + 37, 40, 44, 47, 47, 46, 45, 47, 48, 50, 54, 55, 57, 58, 60, 61, 42, 43, 45, 47, 50, 50, 49, 50, 51, 53, 57, 58, 59, 58, 59, 59, + 44, 44, 46, 47, 51, 51, 51, 52, 53, 54, 59, 59, 60, 61, 61, 60, 49, 46, 47, 48, 53, 53, 53, 54, 55, 57, 60, 61, 63, 62, 62, 63, + 48, 46, 46, 47, 53, 54, 56, 57, 58, 60, 64, 64, 64, 64, 64, 63, 48, 45, 46, 46, 53, 55, 56, 58, 59, 61, 65, 65, 66, 66, 65, 66, + 49, 45, 45, 46, 53, 56, 58, 61, 62, 64, 67, 68, 70, 67, 68, 66, 50, 46, 46, 46, 54, 56, 59, 63, 65, 66, 70, 71, 70, 71, 68, 70, + 51, 47, 47, 47, 54, 57, 60, 64, 65, 68, 71, 72, 73, 71, 72, 70, 52, 48, 47, 47, 54, 57, 61, 66, 68, 71, 75, 75, 76, 75, 73, 73, + 54, 49, 49, 48, 55, 58, 62, 68, 70, 73, 77, 78, 77, 77, 76, 74, 54, 50, 49, 49, 55, 59, 62, 68, 70, 74, 78, 79, 81, 79, 77, 78, + 57, 52, 51, 50, 56, 60, 64, 70, 73, 76, 82, 82, 83, 82, 81, 78, 59, 54, 52, 52, 58, 61, 65, 72, 74, 78, 84, 85, 85, 83, 82, 82, + 60, 54, 53, 52, 58, 62, 65, 72, 75, 79, 85, 86, 89, 87, 85, 82, 63, 57, 56, 55, 60, 64, 67, 75, 77, 82, 89, 90, 90, 88, 87, 86, + 64, 58, 57, 55, 61, 64, 68, 75, 78, 82, 89, 90, 93, 91, 89, 87, 64, 59, 57, 56, 61, 65, 68, 75, 78, 83, 90, 91, 94, 93, 92, 91, + 66, 60, 59, 57, 63, 66, 69, 77, 79, 84, 91, 93, 94, 95, 93, 91, 67, 61, 60, 58, 63, 65, 70, 75, 78, 85, 88, 93, 96, 97, 97, 95, + 68, 62, 61, 59, 63, 64, 71, 74, 79, 84, 87, 94, 96, 97, 98, 96, 69, 63, 62, 60, 63, 65, 71, 72, 80, 82, 88, 93, 96, 99, 100, 101, + 70, 64, 63, 60, 63, 66, 70, 73, 80, 81, 89, 90, 97, 99, 100, 101, 71, 65, 64, 61, 63, 67, 70, 74, 78, 82, 88, 90, 97, 99, 102, 103, + 72, 65, 65, 62, 63, 68, 69, 75, 77, 83, 86, 92, 95, 100, 102, 103, 73, 66, 66, 63, 63, 69, 69, 76, 76, 84, 84, 93, 93, 101, 101, 105, + }, + }, { + { + 32, 31, 31, 32, 35, 36, 44, 47, 53, 62, 65, 79, 82, 88, 90, 93, 31, 32, 32, 32, 35, 35, 42, 45, 51, 59, 62, 75, 78, 83, 86, 88, + 31, 32, 32, 32, 34, 35, 41, 45, 50, 58, 61, 74, 76, 82, 85, 88, 31, 32, 32, 33, 34, 34, 41, 44, 49, 57, 59, 72, 74, 79, 82, 84, + 31, 32, 33, 34, 35, 36, 42, 44, 49, 57, 59, 71, 73, 79, 81, 84, 32, 32, 33, 34, 36, 36, 42, 45, 50, 57, 59, 71, 73, 78, 80, 82, + 32, 33, 34, 35, 37, 38, 42, 45, 49, 56, 58, 69, 71, 76, 79, 83, 32, 33, 34, 36, 39, 40, 44, 47, 51, 58, 60, 71, 73, 76, 78, 80, + 34, 34, 35, 37, 41, 42, 48, 50, 54, 61, 63, 73, 76, 81, 81, 80, 35, 34, 36, 38, 45, 47, 52, 55, 59, 65, 67, 77, 79, 82, 83, 86, + 36, 34, 36, 38, 46, 48, 54, 56, 60, 66, 68, 78, 80, 85, 87, 86, 39, 37, 39, 40, 48, 50, 58, 60, 65, 71, 73, 84, 86, 89, 88, 91, + 41, 39, 40, 41, 49, 51, 60, 62, 67, 74, 76, 86, 88, 91, 93, 91, 44, 41, 42, 43, 51, 53, 63, 66, 71, 78, 79, 90, 92, 97, 94, 97, + 47, 44, 44, 45, 53, 56, 66, 69, 75, 82, 84, 95, 97, 98, 101, 98, 48, 45, 45, 46, 54, 56, 67, 70, 76, 83, 85, 96, 98, 104, 101, 105, + 53, 49, 50, 50, 57, 60, 71, 75, 82, 90, 92, 103, 106, 107, 108, 105, 55, 51, 51, 51, 59, 61, 72, 77, 84, 92, 94, 106, 108, 111, 110, 112, + 58, 54, 54, 54, 61, 63, 75, 79, 87, 95, 98, 110, 112, 117, 116, 113, 63, 58, 58, 57, 65, 67, 78, 83, 91, 100, 103, 116, 118, 119, 119, 121, + 65, 60, 59, 58, 66, 68, 79, 84, 92, 102, 105, 118, 120, 127, 124, 122, 71, 65, 64, 63, 71, 73, 84, 89, 97, 108, 111, 125, 127, 129, 129, 130, + 74, 68, 67, 66, 73, 75, 86, 91, 100, 110, 113, 128, 131, 135, 134, 130, 79, 72, 71, 70, 77, 79, 90, 95, 104, 115, 118, 133, 136, 140, 139, 140, + 82, 75, 73, 72, 79, 81, 92, 97, 105, 117, 120, 136, 139, 145, 142, 140, 82, 75, 74, 72, 79, 81, 92, 97, 106, 117, 121, 136, 139, 148, 150, 149, + 87, 79, 78, 76, 83, 85, 96, 100, 110, 120, 125, 141, 144, 148, 153, 150, 89, 82, 81, 78, 83, 87, 97, 99, 113, 118, 128, 139, 145, 153, 157, 161, + 92, 84, 83, 80, 84, 89, 97, 101, 114, 116, 132, 135, 150, 153, 157, 162, 94, 86, 85, 82, 85, 92, 97, 104, 112, 119, 130, 136, 151, 154, 163, 166, + 97, 88, 88, 85, 86, 94, 97, 107, 111, 123, 128, 140, 147, 159, 163, 167, 99, 91, 91, 87, 87, 97, 97, 110, 110, 126, 126, 144, 144, 163, 163, 173, + }, { + 32, 31, 33, 37, 45, 48, 49, 50, 52, 56, 57, 63, 64, 67, 68, 68, 31, 31, 34, 38, 45, 47, 47, 48, 50, 53, 54, 60, 61, 63, 64, 65, + 31, 32, 34, 39, 45, 46, 46, 47, 49, 52, 53, 59, 60, 62, 64, 65, 30, 32, 35, 40, 44, 46, 45, 46, 48, 51, 52, 57, 58, 60, 61, 62, + 33, 35, 37, 42, 46, 47, 45, 46, 47, 50, 51, 56, 57, 60, 61, 62, 33, 36, 38, 43, 46, 47, 46, 46, 47, 50, 51, 56, 57, 59, 60, 60, + 37, 40, 43, 47, 47, 47, 45, 46, 47, 49, 50, 54, 55, 57, 59, 61, 39, 41, 43, 47, 48, 48, 47, 47, 48, 50, 51, 55, 56, 57, 58, 59, + 42, 43, 44, 47, 49, 50, 49, 50, 50, 53, 53, 57, 58, 60, 60, 59, 47, 46, 46, 48, 51, 52, 53, 53, 53, 55, 56, 60, 61, 61, 61, 62, + 49, 46, 47, 48, 52, 53, 53, 54, 54, 56, 57, 60, 61, 63, 63, 62, 48, 46, 46, 47, 51, 53, 56, 56, 57, 59, 60, 64, 64, 65, 64, 65, + 48, 45, 46, 46, 51, 53, 57, 57, 59, 61, 61, 65, 66, 66, 67, 65, 49, 45, 45, 46, 51, 53, 58, 59, 61, 63, 64, 67, 68, 70, 67, 68, + 50, 46, 46, 46, 52, 54, 59, 61, 63, 65, 66, 70, 71, 70, 71, 68, 50, 46, 46, 46, 52, 54, 59, 61, 64, 66, 67, 71, 71, 73, 71, 72, + 52, 48, 47, 47, 53, 54, 61, 63, 66, 70, 71, 75, 75, 75, 74, 72, 53, 49, 48, 48, 53, 55, 61, 64, 67, 71, 72, 76, 77, 77, 75, 76, + 54, 50, 49, 49, 54, 55, 62, 65, 68, 72, 73, 78, 79, 80, 79, 76, 56, 51, 51, 50, 55, 56, 63, 66, 70, 74, 76, 81, 82, 81, 80, 80, + 57, 52, 51, 50, 55, 56, 64, 66, 70, 75, 76, 82, 83, 85, 83, 80, 60, 54, 54, 52, 57, 58, 65, 68, 72, 77, 79, 85, 86, 86, 85, 84, + 61, 56, 55, 53, 58, 59, 66, 69, 73, 79, 80, 86, 87, 89, 87, 84, 63, 57, 56, 55, 59, 60, 67, 70, 75, 80, 82, 89, 90, 91, 89, 89, + 64, 58, 57, 56, 60, 61, 68, 71, 75, 81, 83, 90, 91, 93, 91, 89, 64, 59, 58, 56, 60, 61, 68, 71, 75, 81, 83, 90, 91, 94, 94, 93, + 66, 60, 59, 57, 61, 63, 69, 72, 77, 82, 84, 92, 93, 94, 95, 93, 67, 61, 60, 58, 61, 63, 69, 70, 78, 80, 85, 90, 93, 96, 97, 97, + 68, 62, 61, 59, 61, 64, 68, 71, 77, 79, 86, 88, 94, 96, 97, 98, 69, 63, 62, 59, 61, 65, 68, 72, 76, 80, 85, 88, 94, 95, 99, 99, + 70, 63, 63, 60, 61, 66, 67, 73, 75, 81, 83, 89, 92, 97, 98, 99, 70, 64, 64, 61, 61, 67, 67, 74, 74, 82, 82, 90, 90, 98, 98, 102, + }, + }, { + { + 32, 31, 31, 32, 33, 36, 40, 44, 51, 53, 65, 66, 79, 81, 87, 90, 31, 32, 32, 32, 33, 35, 39, 42, 49, 51, 62, 63, 75, 77, 83, 85, + 31, 32, 32, 32, 33, 35, 39, 42, 49, 51, 61, 62, 74, 76, 82, 85, 31, 32, 32, 33, 33, 34, 38, 41, 47, 49, 59, 60, 72, 74, 79, 81, + 31, 32, 32, 33, 34, 35, 38, 41, 47, 49, 59, 60, 71, 73, 79, 81, 32, 32, 33, 34, 35, 36, 39, 42, 48, 50, 59, 60, 71, 72, 78, 80, + 32, 32, 33, 35, 36, 37, 40, 42, 48, 49, 58, 59, 69, 71, 77, 80, 32, 33, 33, 35, 36, 38, 41, 42, 48, 49, 58, 59, 69, 70, 75, 77, + 33, 33, 34, 36, 38, 41, 44, 46, 52, 53, 62, 63, 72, 74, 78, 78, 34, 34, 34, 37, 39, 42, 45, 48, 53, 54, 63, 64, 73, 75, 80, 83, + 36, 34, 35, 38, 42, 48, 51, 54, 58, 60, 68, 69, 78, 80, 84, 83, 36, 35, 35, 38, 42, 48, 51, 54, 59, 60, 68, 69, 79, 80, 85, 87, + 39, 37, 38, 40, 44, 50, 54, 58, 63, 65, 73, 74, 84, 85, 89, 88, 40, 38, 39, 41, 45, 51, 56, 59, 65, 67, 75, 76, 85, 87, 90, 93, + 44, 41, 41, 43, 46, 53, 59, 63, 69, 71, 79, 80, 90, 91, 96, 93, 46, 43, 43, 44, 48, 55, 60, 65, 72, 73, 82, 83, 93, 94, 97, 100, + 48, 45, 45, 46, 50, 56, 62, 67, 74, 76, 85, 86, 96, 98, 103, 100, 52, 48, 48, 49, 52, 59, 65, 70, 78, 80, 90, 91, 101, 103, 105, 107, + 53, 49, 49, 50, 53, 60, 66, 71, 79, 82, 92, 93, 103, 105, 111, 107, 58, 53, 53, 53, 57, 63, 69, 74, 83, 86, 97, 98, 109, 111, 113, 115, + 58, 54, 54, 54, 57, 63, 70, 75, 84, 87, 98, 99, 110, 112, 118, 115, 65, 60, 59, 58, 62, 68, 74, 79, 89, 92, 105, 106, 118, 119, 122, 123, + 66, 61, 60, 59, 63, 69, 75, 80, 90, 93, 106, 107, 119, 121, 126, 123, 71, 65, 65, 63, 67, 73, 79, 84, 94, 97, 111, 112, 125, 127, 131, 132, + 74, 68, 67, 66, 69, 75, 81, 86, 97, 100, 113, 115, 128, 130, 134, 132, 79, 72, 72, 70, 73, 79, 85, 90, 101, 104, 118, 119, 133, 135, 141, 140, + 81, 74, 73, 71, 75, 80, 86, 91, 102, 105, 120, 121, 135, 137, 143, 140, 82, 75, 74, 72, 75, 81, 87, 92, 103, 106, 121, 122, 136, 139, 147, 151, + 86, 78, 78, 75, 78, 84, 90, 95, 106, 109, 124, 125, 140, 142, 147, 151, 88, 81, 80, 77, 80, 86, 90, 98, 105, 112, 122, 127, 140, 144, 152, 155, + 91, 83, 82, 79, 80, 88, 90, 100, 103, 114, 119, 130, 137, 148, 151, 155, 93, 85, 85, 81, 81, 90, 90, 102, 103, 117, 117, 134, 134, 151, 152, 160, + }, { + 32, 31, 32, 37, 40, 48, 49, 49, 51, 52, 57, 58, 63, 64, 67, 67, 31, 31, 33, 38, 41, 47, 47, 47, 49, 50, 54, 55, 60, 61, 63, 64, + 31, 31, 33, 38, 41, 47, 47, 47, 49, 49, 54, 54, 59, 60, 63, 64, 30, 32, 33, 40, 42, 46, 45, 45, 47, 48, 52, 52, 57, 58, 60, 61, + 31, 33, 35, 41, 43, 46, 46, 45, 47, 48, 51, 52, 57, 57, 60, 61, 33, 36, 37, 43, 44, 47, 46, 46, 47, 47, 51, 52, 56, 57, 59, 60, + 35, 38, 39, 45, 46, 47, 46, 45, 47, 47, 50, 51, 55, 56, 58, 60, 37, 40, 41, 47, 47, 47, 46, 45, 46, 47, 50, 50, 54, 55, 57, 58, + 41, 42, 43, 47, 48, 49, 49, 48, 49, 50, 52, 53, 57, 57, 59, 58, 42, 43, 43, 47, 48, 50, 49, 49, 50, 50, 53, 54, 57, 58, 60, 61, + 49, 46, 47, 48, 50, 53, 53, 53, 54, 54, 57, 57, 60, 61, 62, 61, 49, 46, 47, 48, 50, 53, 53, 54, 54, 55, 57, 57, 61, 61, 63, 64, + 48, 46, 46, 47, 49, 53, 54, 56, 57, 57, 60, 60, 64, 64, 65, 64, 48, 45, 46, 46, 49, 53, 55, 56, 58, 58, 61, 61, 65, 65, 66, 67, + 49, 45, 45, 46, 48, 53, 56, 58, 61, 61, 64, 64, 67, 68, 69, 67, 49, 46, 46, 46, 49, 53, 57, 59, 62, 62, 65, 66, 69, 69, 70, 70, + 50, 46, 46, 46, 49, 54, 57, 59, 63, 64, 67, 67, 71, 71, 73, 71, 51, 47, 47, 47, 49, 54, 58, 61, 64, 66, 69, 70, 73, 74, 74, 74, + 52, 48, 48, 47, 50, 54, 58, 61, 65, 66, 71, 71, 75, 75, 77, 74, 54, 50, 49, 48, 51, 55, 59, 62, 67, 68, 73, 73, 77, 78, 78, 78, + 54, 50, 50, 49, 51, 55, 59, 62, 67, 68, 73, 74, 78, 78, 81, 78, 57, 52, 52, 50, 52, 56, 60, 64, 69, 70, 76, 77, 82, 82, 83, 82, + 57, 52, 52, 51, 53, 57, 61, 64, 69, 71, 77, 77, 82, 83, 85, 82, 60, 54, 54, 52, 55, 58, 62, 65, 71, 72, 79, 79, 85, 86, 87, 86, + 61, 56, 55, 53, 56, 59, 63, 66, 72, 73, 80, 81, 86, 87, 88, 86, 63, 57, 57, 55, 57, 60, 64, 67, 73, 75, 82, 82, 89, 90, 92, 90, + 64, 58, 58, 55, 58, 61, 65, 68, 73, 75, 82, 83, 89, 90, 92, 90, 64, 59, 58, 56, 58, 61, 65, 68, 74, 75, 83, 83, 90, 91, 94, 95, + 66, 60, 59, 57, 59, 62, 66, 69, 75, 76, 84, 85, 91, 92, 94, 95, 67, 61, 60, 58, 59, 63, 66, 70, 74, 77, 82, 85, 91, 93, 96, 96, + 68, 62, 61, 58, 59, 64, 65, 71, 72, 78, 81, 86, 89, 94, 95, 96, 68, 62, 62, 59, 59, 65, 65, 71, 71, 79, 79, 87, 87, 95, 95, 98, + }, + }, { + { + 32, 31, 31, 32, 32, 36, 36, 44, 44, 53, 53, 65, 65, 79, 79, 87, 31, 32, 32, 32, 32, 35, 35, 42, 42, 51, 51, 62, 62, 75, 75, 82, + 31, 32, 32, 32, 32, 35, 35, 42, 42, 51, 51, 62, 62, 75, 75, 82, 31, 32, 32, 33, 33, 34, 34, 41, 41, 49, 49, 59, 59, 72, 72, 78, + 31, 32, 32, 33, 33, 34, 34, 41, 41, 49, 49, 59, 59, 72, 72, 78, 32, 32, 32, 34, 34, 36, 36, 42, 42, 50, 50, 59, 59, 71, 71, 77, + 32, 32, 32, 34, 34, 36, 36, 42, 42, 50, 50, 59, 59, 71, 71, 77, 32, 33, 33, 35, 35, 38, 38, 42, 42, 49, 49, 58, 58, 69, 69, 75, + 32, 33, 33, 35, 35, 38, 38, 42, 42, 49, 49, 58, 58, 69, 69, 75, 34, 34, 34, 37, 37, 42, 42, 48, 48, 54, 54, 63, 63, 73, 73, 79, + 34, 34, 34, 37, 37, 42, 42, 48, 48, 54, 54, 63, 63, 73, 73, 79, 36, 34, 34, 38, 38, 48, 48, 54, 54, 60, 60, 68, 68, 78, 78, 84, + 36, 34, 34, 38, 38, 48, 48, 54, 54, 60, 60, 68, 68, 78, 78, 84, 39, 37, 37, 40, 40, 50, 50, 58, 58, 65, 65, 73, 73, 84, 84, 89, + 39, 37, 37, 40, 40, 50, 50, 58, 58, 65, 65, 73, 73, 84, 84, 89, 44, 41, 41, 43, 43, 53, 53, 63, 63, 71, 71, 79, 79, 90, 90, 95, + 44, 41, 41, 43, 43, 53, 53, 63, 63, 71, 71, 79, 79, 90, 90, 95, 48, 45, 45, 46, 46, 56, 56, 67, 67, 76, 76, 85, 85, 96, 96, 102, + 48, 45, 45, 46, 46, 56, 56, 67, 67, 76, 76, 85, 85, 96, 96, 102, 53, 49, 49, 50, 50, 60, 60, 71, 71, 82, 82, 92, 92, 103, 103, 109, + 53, 49, 49, 50, 50, 60, 60, 71, 71, 82, 82, 92, 92, 103, 103, 109, 58, 54, 54, 54, 54, 63, 63, 75, 75, 87, 87, 98, 98, 110, 110, 116, + 58, 54, 54, 54, 54, 63, 63, 75, 75, 87, 87, 98, 98, 110, 110, 116, 65, 60, 60, 58, 58, 68, 68, 79, 79, 92, 92, 105, 105, 118, 118, 124, + 65, 60, 60, 58, 58, 68, 68, 79, 79, 92, 92, 105, 105, 118, 118, 124, 71, 65, 65, 63, 63, 73, 73, 84, 84, 97, 97, 111, 111, 125, 125, 132, + 71, 65, 65, 63, 63, 73, 73, 84, 84, 97, 97, 111, 111, 125, 125, 132, 79, 72, 72, 70, 70, 79, 79, 90, 90, 104, 104, 118, 118, 133, 133, 141, + 79, 72, 72, 70, 70, 79, 79, 90, 90, 104, 104, 118, 118, 133, 133, 141, 82, 75, 75, 72, 72, 81, 81, 92, 92, 106, 106, 121, 121, 136, 136, 144, + 82, 75, 75, 72, 72, 81, 81, 92, 92, 106, 106, 121, 121, 136, 136, 144, 87, 79, 79, 76, 76, 84, 84, 96, 96, 109, 109, 124, 124, 141, 141, 149, + }, { + 32, 31, 31, 37, 37, 48, 48, 49, 49, 52, 52, 57, 57, 63, 63, 66, 31, 31, 31, 38, 38, 47, 47, 47, 47, 50, 50, 54, 54, 60, 60, 63, + 31, 31, 31, 38, 38, 47, 47, 47, 47, 50, 50, 54, 54, 60, 60, 63, 30, 32, 32, 40, 40, 46, 46, 45, 45, 48, 48, 52, 52, 57, 57, 60, + 30, 32, 32, 40, 40, 46, 46, 45, 45, 48, 48, 52, 52, 57, 57, 60, 33, 36, 36, 43, 43, 47, 47, 46, 46, 47, 47, 51, 51, 56, 56, 59, + 33, 36, 36, 43, 43, 47, 47, 46, 46, 47, 47, 51, 51, 56, 56, 59, 37, 40, 40, 47, 47, 47, 47, 45, 45, 47, 47, 50, 50, 54, 54, 57, + 37, 40, 40, 47, 47, 47, 47, 45, 45, 47, 47, 50, 50, 54, 54, 57, 42, 43, 43, 47, 47, 50, 50, 49, 49, 50, 50, 53, 53, 57, 57, 60, + 42, 43, 43, 47, 47, 50, 50, 49, 49, 50, 50, 53, 53, 57, 57, 60, 49, 46, 46, 48, 48, 53, 53, 53, 53, 54, 54, 57, 57, 60, 60, 62, + 49, 46, 46, 48, 48, 53, 53, 53, 53, 54, 54, 57, 57, 60, 60, 62, 48, 46, 46, 47, 47, 53, 53, 56, 56, 57, 57, 60, 60, 64, 64, 66, + 48, 46, 46, 47, 47, 53, 53, 56, 56, 57, 57, 60, 60, 64, 64, 66, 49, 45, 45, 46, 46, 53, 53, 58, 58, 61, 61, 64, 64, 67, 67, 69, + 49, 45, 45, 46, 46, 53, 53, 58, 58, 61, 61, 64, 64, 67, 67, 69, 50, 46, 46, 46, 46, 54, 54, 59, 59, 64, 64, 67, 67, 71, 71, 73, + 50, 46, 46, 46, 46, 54, 54, 59, 59, 64, 64, 67, 67, 71, 71, 73, 52, 48, 48, 47, 47, 54, 54, 61, 61, 66, 66, 71, 71, 75, 75, 77, + 52, 48, 48, 47, 47, 54, 54, 61, 61, 66, 66, 71, 71, 75, 75, 77, 54, 50, 50, 49, 49, 55, 55, 62, 62, 68, 68, 73, 73, 78, 78, 80, + 54, 50, 50, 49, 49, 55, 55, 62, 62, 68, 68, 73, 73, 78, 78, 80, 57, 52, 52, 50, 50, 56, 56, 64, 64, 70, 70, 76, 76, 82, 82, 84, + 57, 52, 52, 50, 50, 56, 56, 64, 64, 70, 70, 76, 76, 82, 82, 84, 60, 54, 54, 52, 52, 58, 58, 65, 65, 72, 72, 79, 79, 85, 85, 88, + 60, 54, 54, 52, 52, 58, 58, 65, 65, 72, 72, 79, 79, 85, 85, 88, 63, 57, 57, 55, 55, 60, 60, 67, 67, 75, 75, 82, 82, 89, 89, 92, + 63, 57, 57, 55, 55, 60, 60, 67, 67, 75, 75, 82, 82, 89, 89, 92, 64, 59, 59, 56, 56, 61, 61, 68, 68, 75, 75, 83, 83, 90, 90, 93, + 64, 59, 59, 56, 56, 61, 61, 68, 68, 75, 75, 83, 83, 90, 90, 93, 66, 60, 60, 57, 57, 63, 63, 69, 69, 77, 77, 84, 84, 92, 92, 95, + }, + }, { + { + 32, 31, 31, 32, 32, 34, 36, 38, 44, 44, 53, 53, 62, 65, 73, 79, 31, 32, 32, 32, 32, 34, 35, 37, 42, 43, 51, 51, 60, 62, 70, 75, + 31, 32, 32, 32, 32, 34, 35, 37, 42, 43, 51, 51, 59, 62, 69, 75, 31, 32, 32, 32, 32, 33, 35, 36, 41, 42, 50, 50, 58, 60, 67, 73, + 31, 32, 32, 32, 33, 33, 34, 36, 41, 41, 49, 49, 57, 59, 66, 72, 31, 32, 32, 33, 33, 34, 35, 37, 41, 42, 49, 49, 57, 59, 66, 71, + 32, 32, 32, 33, 34, 35, 36, 38, 42, 43, 50, 50, 57, 59, 65, 71, 32, 32, 32, 34, 34, 35, 37, 38, 42, 43, 49, 49, 56, 59, 65, 70, + 32, 32, 33, 34, 35, 37, 38, 39, 42, 43, 49, 49, 56, 58, 64, 69, 32, 33, 33, 34, 35, 37, 39, 40, 43, 44, 50, 50, 56, 58, 64, 69, + 34, 34, 34, 36, 37, 39, 42, 44, 48, 48, 54, 54, 61, 63, 69, 73, 34, 34, 34, 36, 37, 39, 42, 44, 48, 48, 54, 54, 61, 63, 69, 73, + 35, 34, 34, 37, 38, 42, 47, 48, 52, 53, 59, 59, 65, 67, 73, 77, 36, 35, 34, 37, 38, 43, 48, 49, 54, 54, 60, 60, 66, 68, 74, 78, + 38, 36, 36, 38, 40, 44, 49, 51, 56, 57, 63, 63, 69, 71, 77, 81, 39, 38, 37, 40, 40, 45, 50, 52, 58, 58, 65, 65, 71, 73, 79, 84, + 41, 39, 39, 41, 41, 46, 51, 54, 60, 60, 67, 67, 74, 76, 81, 86, 44, 41, 41, 42, 43, 48, 53, 56, 63, 64, 71, 71, 78, 79, 85, 90, + 44, 42, 42, 43, 43, 48, 54, 56, 64, 64, 72, 72, 79, 81, 86, 91, 48, 45, 45, 46, 46, 51, 56, 59, 67, 67, 76, 76, 83, 85, 91, 96, + 48, 45, 45, 46, 46, 51, 56, 59, 67, 67, 76, 76, 83, 85, 91, 96, 53, 49, 49, 49, 49, 54, 59, 62, 71, 71, 81, 81, 89, 91, 98, 103, + 53, 50, 49, 50, 50, 54, 60, 63, 71, 72, 82, 82, 90, 92, 99, 103, 57, 53, 52, 52, 52, 57, 62, 65, 74, 75, 85, 85, 94, 96, 103, 108, + 58, 54, 54, 54, 54, 58, 63, 67, 75, 76, 87, 87, 95, 98, 105, 110, 61, 57, 57, 56, 56, 60, 66, 69, 77, 78, 89, 89, 98, 101, 108, 114, + 65, 60, 60, 59, 58, 63, 68, 71, 79, 80, 92, 92, 102, 105, 112, 118, 67, 62, 61, 60, 60, 64, 69, 72, 81, 82, 94, 94, 103, 106, 114, 120, + 71, 66, 65, 64, 63, 68, 73, 76, 84, 85, 97, 97, 108, 111, 119, 125, 72, 66, 66, 64, 64, 68, 73, 76, 85, 86, 98, 98, 108, 111, 119, 125, + 79, 73, 72, 71, 70, 74, 79, 82, 90, 91, 104, 104, 115, 118, 127, 133, 79, 73, 72, 71, 70, 74, 79, 82, 90, 91, 104, 104, 115, 118, 127, 133, + }, { + 32, 31, 31, 35, 37, 42, 48, 48, 49, 49, 52, 52, 56, 57, 61, 63, 31, 31, 31, 36, 38, 42, 47, 47, 47, 47, 50, 50, 54, 54, 58, 60, + 31, 31, 31, 36, 38, 42, 47, 47, 47, 47, 50, 50, 53, 54, 57, 60, 30, 32, 32, 37, 39, 42, 46, 46, 46, 46, 48, 48, 52, 52, 56, 58, + 30, 32, 32, 37, 40, 42, 46, 46, 45, 45, 48, 48, 51, 52, 55, 57, 32, 33, 34, 39, 41, 44, 46, 46, 45, 45, 48, 48, 51, 51, 54, 57, + 33, 35, 36, 40, 43, 45, 47, 46, 46, 46, 47, 47, 50, 51, 54, 56, 34, 37, 37, 42, 44, 45, 47, 47, 45, 46, 47, 47, 50, 51, 53, 55, + 37, 40, 40, 45, 47, 47, 47, 47, 45, 46, 47, 47, 49, 50, 52, 54, 37, 40, 40, 45, 47, 47, 48, 47, 46, 46, 47, 47, 49, 50, 53, 55, + 42, 43, 43, 46, 47, 48, 50, 50, 49, 49, 50, 50, 53, 53, 56, 57, 42, 43, 43, 46, 47, 48, 50, 50, 49, 49, 50, 50, 53, 53, 56, 57, + 47, 46, 46, 47, 48, 50, 52, 52, 53, 53, 53, 53, 55, 56, 58, 60, 49, 47, 46, 47, 48, 50, 53, 53, 53, 54, 54, 54, 56, 57, 59, 60, + 48, 46, 46, 47, 47, 50, 53, 53, 55, 55, 56, 56, 58, 58, 61, 62, 48, 46, 46, 46, 47, 50, 53, 54, 56, 56, 57, 57, 59, 60, 62, 64, + 48, 46, 45, 46, 46, 49, 53, 54, 57, 57, 59, 59, 61, 61, 63, 65, 49, 45, 45, 45, 46, 49, 53, 55, 58, 59, 61, 61, 63, 64, 66, 67, + 49, 46, 45, 46, 46, 49, 53, 55, 58, 59, 62, 62, 64, 64, 66, 68, 50, 47, 46, 46, 46, 50, 54, 55, 59, 60, 64, 64, 66, 67, 69, 71, + 50, 47, 46, 46, 46, 50, 54, 55, 59, 60, 64, 64, 66, 67, 69, 71, 52, 48, 48, 47, 47, 50, 54, 56, 61, 61, 66, 66, 69, 70, 72, 74, + 52, 48, 48, 47, 47, 50, 54, 56, 61, 61, 66, 66, 70, 71, 73, 75, 53, 50, 49, 48, 48, 51, 55, 57, 62, 62, 68, 68, 71, 72, 75, 77, + 54, 50, 50, 49, 49, 52, 55, 57, 62, 63, 68, 68, 72, 73, 76, 78, 55, 51, 51, 50, 49, 52, 56, 58, 63, 63, 69, 69, 74, 75, 78, 80, + 57, 52, 52, 51, 50, 53, 56, 58, 64, 64, 70, 70, 75, 76, 79, 82, 58, 53, 53, 51, 51, 54, 57, 59, 64, 65, 71, 71, 76, 77, 80, 83, + 60, 55, 54, 53, 52, 55, 58, 60, 65, 66, 72, 72, 77, 79, 82, 85, 60, 55, 55, 53, 53, 55, 59, 60, 65, 66, 73, 73, 78, 79, 83, 85, + 63, 58, 57, 56, 55, 58, 60, 62, 67, 68, 75, 75, 80, 82, 86, 89, 63, 58, 57, 56, 55, 58, 60, 62, 67, 68, 75, 75, 80, 82, 86, 89, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 35, 36, 39, 44, 44, 51, 53, 58, 65, 65, 31, 32, 32, 32, 32, 32, 35, 35, 38, 42, 42, 49, 52, 56, 63, 63, + 31, 32, 32, 32, 32, 32, 35, 35, 38, 42, 42, 49, 51, 55, 62, 62, 31, 32, 32, 32, 32, 32, 34, 35, 37, 41, 41, 48, 50, 54, 61, 61, + 31, 32, 32, 32, 33, 33, 34, 34, 37, 41, 41, 47, 49, 53, 59, 59, 31, 32, 32, 32, 33, 33, 34, 34, 37, 41, 41, 47, 49, 53, 59, 59, + 31, 32, 32, 33, 34, 34, 35, 36, 38, 42, 42, 48, 49, 53, 59, 59, 32, 32, 32, 33, 34, 34, 36, 36, 38, 42, 42, 48, 50, 53, 59, 59, + 32, 32, 32, 33, 34, 34, 36, 37, 39, 42, 42, 48, 49, 53, 58, 58, 32, 32, 33, 34, 35, 35, 37, 38, 40, 42, 42, 48, 49, 52, 58, 58, + 32, 32, 33, 34, 35, 35, 37, 38, 40, 42, 42, 48, 49, 52, 58, 58, 33, 33, 33, 35, 36, 36, 40, 41, 43, 46, 46, 52, 53, 56, 62, 62, + 34, 34, 34, 35, 37, 37, 41, 42, 44, 48, 48, 53, 54, 57, 63, 63, 34, 34, 34, 35, 37, 37, 43, 44, 46, 50, 50, 55, 56, 59, 65, 65, + 36, 35, 34, 36, 38, 38, 46, 48, 50, 54, 54, 58, 60, 63, 68, 68, 36, 35, 34, 36, 38, 38, 46, 48, 50, 54, 54, 58, 60, 63, 68, 68, + 38, 37, 37, 38, 40, 40, 47, 50, 52, 57, 57, 62, 64, 67, 72, 72, 39, 38, 37, 39, 40, 40, 48, 50, 53, 58, 58, 63, 65, 68, 73, 73, + 41, 39, 39, 40, 41, 41, 49, 51, 54, 60, 60, 66, 67, 70, 76, 76, 44, 41, 41, 42, 43, 43, 51, 53, 57, 63, 63, 69, 71, 74, 79, 79, + 44, 41, 41, 42, 43, 43, 51, 53, 57, 63, 63, 69, 71, 74, 79, 79, 47, 44, 44, 44, 45, 45, 53, 56, 59, 66, 66, 73, 75, 78, 84, 84, + 48, 45, 45, 45, 46, 46, 54, 56, 60, 67, 67, 74, 76, 79, 85, 85, 50, 47, 46, 47, 47, 47, 55, 58, 61, 68, 68, 76, 78, 82, 88, 88, + 53, 50, 49, 50, 50, 50, 57, 60, 64, 71, 71, 79, 82, 86, 92, 92, 53, 50, 49, 50, 50, 50, 57, 60, 64, 71, 71, 79, 82, 86, 92, 92, + 57, 54, 53, 53, 53, 53, 60, 63, 67, 74, 74, 83, 86, 90, 97, 97, 58, 55, 54, 54, 54, 54, 61, 63, 68, 75, 75, 84, 87, 91, 98, 98, + 61, 57, 56, 56, 56, 56, 63, 65, 69, 77, 77, 86, 89, 93, 100, 100, 65, 61, 60, 59, 58, 58, 66, 68, 72, 79, 79, 89, 92, 97, 105, 105, + 65, 61, 60, 59, 58, 58, 66, 68, 72, 79, 79, 89, 92, 97, 105, 105, 70, 65, 64, 63, 62, 62, 70, 72, 76, 83, 83, 93, 96, 101, 109, 109, + }, { + 32, 31, 31, 33, 37, 37, 45, 48, 48, 49, 49, 51, 52, 54, 57, 57, 31, 31, 31, 34, 38, 38, 45, 47, 47, 47, 47, 50, 50, 52, 55, 55, + 31, 31, 31, 34, 38, 38, 45, 47, 47, 47, 47, 49, 50, 51, 54, 54, 31, 31, 32, 34, 39, 39, 45, 46, 46, 46, 46, 48, 49, 51, 53, 53, + 30, 32, 32, 35, 40, 40, 44, 46, 45, 45, 45, 47, 48, 49, 52, 52, 30, 32, 32, 35, 40, 40, 44, 46, 45, 45, 45, 47, 48, 49, 52, 52, + 33, 34, 35, 37, 42, 42, 46, 47, 46, 45, 45, 47, 47, 49, 51, 51, 33, 35, 36, 38, 43, 43, 46, 47, 46, 46, 46, 47, 47, 49, 51, 51, + 35, 37, 37, 40, 44, 44, 46, 47, 46, 45, 45, 47, 47, 48, 51, 51, 37, 39, 40, 43, 47, 47, 47, 47, 47, 45, 45, 46, 47, 48, 50, 50, + 37, 39, 40, 43, 47, 47, 47, 47, 47, 45, 45, 46, 47, 48, 50, 50, 41, 42, 42, 44, 47, 47, 49, 49, 49, 48, 48, 49, 50, 51, 52, 52, + 42, 42, 43, 44, 47, 47, 49, 50, 50, 49, 49, 50, 50, 51, 53, 53, 44, 44, 44, 45, 47, 47, 50, 51, 51, 51, 51, 52, 52, 53, 54, 54, + 49, 47, 46, 47, 48, 48, 52, 53, 53, 53, 53, 54, 54, 55, 57, 57, 49, 47, 46, 47, 48, 48, 52, 53, 53, 53, 53, 54, 54, 55, 57, 57, + 48, 46, 46, 46, 47, 47, 51, 53, 54, 55, 55, 56, 57, 58, 59, 59, 48, 46, 46, 46, 47, 47, 51, 53, 54, 56, 56, 57, 57, 58, 60, 60, + 48, 46, 45, 46, 46, 46, 51, 53, 54, 57, 57, 58, 59, 60, 61, 61, 49, 46, 45, 45, 46, 46, 51, 53, 55, 58, 58, 61, 61, 62, 64, 64, + 49, 46, 45, 45, 46, 46, 51, 53, 55, 58, 58, 61, 61, 62, 64, 64, 50, 47, 46, 46, 46, 46, 52, 54, 56, 59, 59, 62, 63, 64, 66, 66, + 50, 47, 46, 46, 46, 46, 52, 54, 56, 59, 59, 63, 64, 65, 67, 67, 51, 48, 47, 47, 47, 47, 52, 54, 56, 60, 60, 64, 65, 66, 68, 68, + 52, 48, 48, 47, 47, 47, 53, 54, 57, 61, 61, 65, 66, 68, 71, 71, 52, 48, 48, 47, 47, 47, 53, 54, 57, 61, 61, 65, 66, 68, 71, 71, + 54, 50, 49, 49, 48, 48, 54, 55, 58, 62, 62, 67, 68, 70, 73, 73, 54, 51, 50, 49, 49, 49, 54, 55, 58, 62, 62, 67, 68, 70, 73, 73, + 55, 51, 51, 50, 49, 49, 54, 56, 58, 63, 63, 68, 69, 71, 74, 74, 57, 53, 52, 51, 50, 50, 55, 56, 59, 64, 64, 69, 70, 73, 76, 76, + 57, 53, 52, 51, 50, 50, 55, 56, 59, 64, 64, 69, 70, 73, 76, 76, 59, 55, 54, 53, 52, 52, 57, 58, 61, 65, 65, 70, 72, 74, 78, 78, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 32, 35, 36, 38, 44, 44, 47, 53, 53, 59, 31, 32, 32, 32, 32, 32, 33, 35, 35, 37, 43, 43, 46, 52, 52, 57, + 31, 32, 32, 32, 32, 32, 33, 35, 35, 37, 42, 42, 45, 51, 51, 56, 31, 32, 32, 32, 32, 32, 33, 35, 35, 37, 42, 42, 45, 51, 51, 56, + 31, 32, 32, 32, 32, 32, 33, 34, 35, 36, 41, 41, 44, 49, 49, 54, 31, 32, 32, 32, 32, 33, 33, 34, 34, 36, 41, 41, 44, 49, 49, 54, + 31, 32, 32, 32, 33, 33, 33, 35, 35, 36, 41, 41, 44, 49, 49, 54, 32, 32, 32, 32, 33, 34, 34, 36, 36, 38, 42, 42, 45, 49, 49, 54, + 32, 32, 32, 33, 34, 34, 34, 36, 36, 38, 42, 42, 45, 50, 50, 54, 32, 32, 32, 33, 34, 34, 35, 37, 37, 38, 42, 42, 45, 49, 49, 54, + 32, 32, 33, 33, 35, 35, 36, 38, 38, 39, 42, 42, 45, 49, 49, 53, 32, 32, 33, 33, 35, 35, 36, 38, 38, 39, 42, 42, 45, 49, 49, 53, + 32, 33, 33, 33, 35, 36, 36, 39, 40, 41, 44, 44, 47, 51, 51, 55, 34, 34, 34, 34, 36, 37, 38, 42, 42, 44, 48, 48, 50, 54, 54, 58, + 34, 34, 34, 34, 36, 37, 38, 42, 42, 44, 48, 48, 50, 54, 54, 58, 35, 34, 34, 34, 37, 37, 39, 44, 45, 46, 50, 50, 53, 57, 57, 61, + 36, 35, 34, 35, 37, 38, 40, 47, 48, 49, 54, 54, 56, 60, 60, 64, 36, 35, 34, 35, 37, 38, 40, 47, 48, 49, 54, 54, 56, 60, 60, 64, + 38, 37, 36, 37, 39, 40, 41, 48, 49, 51, 56, 56, 58, 63, 63, 67, 39, 38, 37, 38, 40, 40, 42, 49, 50, 52, 58, 58, 60, 65, 65, 69, + 39, 38, 37, 38, 40, 40, 42, 49, 50, 52, 58, 58, 60, 65, 65, 69, 42, 40, 40, 40, 42, 42, 44, 51, 52, 55, 61, 61, 64, 69, 69, 73, + 44, 42, 41, 41, 42, 43, 45, 52, 53, 56, 63, 63, 66, 71, 71, 75, 44, 42, 41, 41, 43, 43, 45, 52, 54, 56, 63, 63, 66, 72, 72, 76, + 47, 45, 44, 44, 45, 45, 47, 54, 56, 58, 66, 66, 69, 75, 75, 79, 48, 46, 45, 45, 46, 46, 48, 55, 56, 59, 67, 67, 70, 76, 76, 80, + 49, 47, 46, 46, 47, 47, 48, 56, 57, 60, 67, 67, 71, 77, 77, 81, 53, 50, 49, 49, 49, 49, 51, 58, 59, 62, 71, 71, 74, 81, 81, 86, + 53, 51, 49, 49, 50, 50, 51, 59, 60, 63, 71, 71, 75, 82, 82, 87, 55, 52, 51, 51, 51, 51, 53, 60, 61, 64, 72, 72, 76, 83, 83, 88, + 58, 55, 54, 54, 54, 54, 55, 62, 63, 67, 75, 75, 79, 87, 87, 92, 58, 55, 54, 54, 54, 54, 55, 62, 63, 67, 75, 75, 79, 87, 87, 92, + }, { + 32, 31, 31, 31, 35, 37, 38, 47, 48, 48, 49, 49, 50, 52, 52, 54, 31, 31, 31, 32, 36, 38, 39, 46, 47, 47, 48, 48, 49, 50, 50, 53, + 31, 31, 31, 32, 37, 38, 40, 46, 47, 47, 47, 47, 48, 50, 50, 52, 31, 31, 31, 32, 37, 38, 40, 46, 47, 47, 47, 47, 48, 50, 50, 52, + 30, 31, 32, 32, 38, 39, 40, 45, 46, 46, 45, 45, 46, 48, 48, 50, 30, 31, 32, 33, 38, 40, 41, 45, 46, 46, 45, 45, 46, 48, 48, 50, + 31, 32, 33, 33, 38, 40, 41, 45, 46, 46, 45, 45, 46, 48, 48, 50, 33, 35, 35, 36, 41, 43, 43, 46, 47, 46, 45, 45, 46, 47, 47, 49, + 33, 35, 36, 36, 41, 43, 44, 46, 47, 46, 46, 46, 46, 47, 47, 49, 34, 36, 37, 37, 42, 44, 45, 47, 47, 47, 45, 45, 46, 47, 47, 49, + 37, 39, 40, 41, 45, 47, 47, 47, 47, 47, 45, 45, 46, 47, 47, 48, 37, 39, 40, 41, 45, 47, 47, 47, 47, 47, 45, 45, 46, 47, 47, 48, + 39, 40, 41, 42, 46, 47, 47, 48, 48, 48, 47, 47, 47, 48, 48, 50, 42, 42, 43, 43, 46, 47, 48, 50, 50, 50, 49, 49, 50, 50, 50, 52, + 42, 42, 43, 43, 46, 47, 48, 50, 50, 50, 49, 49, 50, 50, 50, 52, 45, 45, 44, 45, 47, 47, 48, 51, 51, 51, 51, 51, 52, 52, 52, 54, + 49, 47, 46, 47, 48, 48, 49, 52, 53, 53, 53, 53, 54, 54, 54, 55, 49, 47, 46, 47, 48, 48, 49, 52, 53, 53, 53, 53, 54, 54, 54, 55, + 48, 47, 46, 46, 47, 47, 48, 52, 53, 53, 55, 55, 55, 56, 56, 57, 48, 46, 46, 46, 46, 47, 48, 52, 53, 54, 56, 56, 56, 57, 57, 59, + 48, 46, 46, 46, 46, 47, 48, 52, 53, 54, 56, 56, 56, 57, 57, 59, 49, 46, 45, 45, 46, 46, 47, 52, 53, 54, 57, 57, 58, 60, 60, 61, + 49, 46, 45, 45, 45, 46, 47, 52, 53, 55, 58, 58, 59, 61, 61, 62, 49, 46, 45, 45, 46, 46, 47, 52, 53, 55, 58, 58, 60, 61, 61, 63, + 50, 47, 46, 46, 46, 46, 48, 53, 54, 55, 59, 59, 61, 63, 63, 65, 50, 48, 46, 46, 46, 46, 48, 53, 54, 55, 59, 59, 61, 64, 64, 65, + 51, 48, 47, 47, 47, 47, 48, 53, 54, 55, 60, 60, 61, 64, 64, 66, 52, 49, 48, 48, 47, 47, 48, 53, 54, 56, 61, 61, 63, 66, 66, 68, + 52, 49, 48, 48, 47, 47, 48, 53, 54, 56, 61, 61, 63, 66, 66, 68, 53, 50, 48, 48, 48, 48, 49, 54, 54, 56, 61, 61, 63, 67, 67, 69, + 54, 51, 50, 50, 49, 49, 50, 55, 55, 57, 62, 62, 65, 68, 68, 71, 54, 51, 50, 50, 49, 49, 50, 55, 55, 57, 62, 62, 65, 68, 68, 71, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 35, 36, 36, 40, 44, 44, 47, 53, 31, 31, 32, 32, 32, 32, 32, 33, 35, 35, 35, 39, 43, 43, 46, 52, + 31, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 39, 42, 42, 45, 51, 31, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 39, 42, 42, 45, 51, + 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 39, 41, 41, 45, 50, 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 38, 41, 41, 44, 49, + 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 38, 41, 41, 44, 49, 31, 32, 32, 32, 32, 33, 33, 33, 34, 35, 35, 38, 41, 41, 44, 49, + 31, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 39, 42, 42, 44, 49, 32, 32, 32, 32, 33, 34, 34, 34, 36, 36, 36, 39, 42, 42, 45, 50, + 32, 32, 32, 32, 33, 34, 34, 34, 36, 36, 36, 39, 42, 42, 45, 50, 32, 32, 32, 32, 33, 35, 35, 35, 37, 37, 37, 40, 42, 42, 45, 49, + 32, 32, 33, 33, 34, 35, 35, 36, 37, 38, 38, 41, 42, 42, 45, 49, 32, 32, 33, 33, 34, 35, 35, 36, 37, 38, 38, 41, 42, 42, 45, 49, + 32, 33, 33, 33, 34, 36, 36, 36, 39, 40, 40, 42, 44, 44, 47, 51, 34, 34, 34, 34, 35, 37, 37, 38, 41, 42, 42, 45, 48, 48, 50, 54, + 34, 34, 34, 34, 35, 37, 37, 38, 41, 42, 42, 45, 48, 48, 50, 54, 34, 34, 34, 34, 35, 37, 37, 38, 42, 43, 43, 46, 49, 49, 51, 55, + 35, 35, 34, 34, 36, 38, 38, 39, 45, 47, 47, 50, 52, 52, 55, 59, 36, 35, 34, 34, 36, 38, 38, 40, 46, 48, 48, 51, 54, 54, 56, 60, + 36, 35, 34, 34, 36, 38, 38, 40, 46, 48, 48, 51, 54, 54, 56, 60, 38, 37, 36, 36, 37, 40, 40, 41, 47, 49, 49, 53, 56, 56, 58, 63, + 39, 38, 37, 37, 39, 40, 40, 42, 48, 50, 50, 54, 58, 58, 60, 65, 39, 38, 37, 37, 39, 40, 40, 42, 48, 50, 50, 54, 58, 58, 60, 65, + 41, 40, 39, 39, 40, 41, 41, 43, 49, 51, 51, 56, 60, 60, 62, 67, 44, 42, 41, 41, 42, 43, 43, 45, 51, 53, 53, 59, 63, 63, 66, 71, + 44, 42, 41, 41, 42, 43, 43, 45, 51, 53, 53, 59, 63, 63, 66, 71, 44, 43, 42, 42, 42, 43, 43, 45, 51, 54, 54, 59, 64, 64, 67, 72, + 47, 45, 44, 44, 44, 45, 45, 47, 53, 56, 56, 61, 66, 66, 69, 75, 48, 46, 45, 45, 45, 46, 46, 48, 54, 56, 56, 62, 67, 67, 70, 76, + 48, 46, 45, 45, 45, 46, 46, 48, 54, 56, 56, 62, 67, 67, 70, 76, 51, 49, 47, 47, 48, 48, 48, 50, 56, 58, 58, 64, 69, 69, 73, 79, + }, { + 32, 31, 31, 31, 33, 37, 37, 38, 45, 48, 48, 49, 49, 49, 50, 52, 31, 31, 31, 31, 33, 38, 38, 39, 45, 47, 47, 48, 48, 48, 49, 51, + 31, 31, 31, 31, 34, 38, 38, 40, 45, 47, 47, 47, 47, 47, 48, 50, 31, 31, 31, 31, 34, 38, 38, 40, 45, 47, 47, 47, 47, 47, 48, 50, + 31, 31, 32, 32, 34, 39, 39, 40, 45, 46, 46, 46, 46, 46, 47, 49, 30, 31, 32, 32, 35, 40, 40, 41, 44, 46, 46, 45, 45, 45, 46, 48, + 30, 31, 32, 32, 35, 40, 40, 41, 44, 46, 46, 45, 45, 45, 46, 48, 31, 32, 33, 33, 35, 40, 40, 41, 45, 46, 46, 45, 45, 45, 46, 48, + 33, 34, 35, 35, 37, 42, 42, 43, 46, 47, 47, 46, 45, 45, 46, 47, 33, 35, 36, 36, 38, 43, 43, 44, 46, 47, 47, 46, 46, 46, 46, 47, + 33, 35, 36, 36, 38, 43, 43, 44, 46, 47, 47, 46, 46, 46, 46, 47, 35, 37, 38, 38, 41, 45, 45, 46, 47, 47, 47, 46, 45, 45, 46, 47, + 37, 39, 40, 40, 43, 47, 47, 47, 47, 47, 47, 46, 45, 45, 46, 47, 37, 39, 40, 40, 43, 47, 47, 47, 47, 47, 47, 46, 45, 45, 46, 47, + 39, 40, 41, 41, 43, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 48, 42, 42, 43, 43, 44, 47, 47, 48, 49, 50, 50, 49, 49, 49, 50, 50, + 42, 42, 43, 43, 44, 47, 47, 48, 49, 50, 50, 49, 49, 49, 50, 50, 43, 43, 43, 43, 45, 47, 47, 48, 50, 50, 50, 50, 50, 50, 50, 51, + 47, 46, 46, 46, 46, 48, 48, 48, 51, 52, 52, 52, 53, 53, 53, 53, 49, 47, 46, 46, 47, 48, 48, 49, 52, 53, 53, 53, 53, 53, 54, 54, + 49, 47, 46, 46, 47, 48, 48, 49, 52, 53, 53, 53, 53, 53, 54, 54, 48, 47, 46, 46, 46, 47, 47, 48, 52, 53, 53, 54, 55, 55, 55, 56, + 48, 47, 46, 46, 46, 47, 47, 48, 51, 53, 53, 54, 56, 56, 56, 57, 48, 47, 46, 46, 46, 47, 47, 48, 51, 53, 53, 54, 56, 56, 56, 57, + 48, 47, 45, 45, 46, 46, 46, 47, 51, 53, 53, 55, 57, 57, 57, 59, 49, 46, 45, 45, 45, 46, 46, 47, 51, 53, 53, 56, 58, 58, 59, 61, + 49, 46, 45, 45, 45, 46, 46, 47, 51, 53, 53, 56, 58, 58, 59, 61, 49, 47, 45, 45, 45, 46, 46, 47, 52, 53, 53, 56, 58, 58, 60, 62, + 50, 48, 46, 46, 46, 46, 46, 48, 52, 54, 54, 57, 59, 59, 61, 63, 50, 48, 46, 46, 46, 46, 46, 48, 52, 54, 54, 57, 59, 59, 61, 64, + 50, 48, 46, 46, 46, 46, 46, 48, 52, 54, 54, 57, 59, 59, 61, 64, 51, 49, 47, 47, 47, 47, 47, 48, 52, 54, 54, 58, 60, 60, 62, 65, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 36, 36, 39, 44, 44, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 35, 35, 35, 39, 43, 43, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 35, 35, 35, 38, 42, 42, 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 35, 35, 35, 38, 42, 42, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 35, 35, 35, 38, 42, 42, 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 35, 35, 35, 38, 41, 41, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 37, 41, 41, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 37, 41, 41, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 37, 41, 41, 31, 32, 32, 32, 32, 33, 33, 33, 33, 34, 35, 35, 35, 38, 41, 41, + 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 39, 42, 42, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 39, 42, 42, + 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 39, 42, 42, 32, 32, 32, 32, 32, 33, 34, 34, 34, 36, 37, 37, 37, 40, 42, 42, + 32, 32, 33, 33, 33, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, 32, 32, 33, 33, 33, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, + 32, 32, 33, 33, 33, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, 33, 33, 33, 33, 33, 34, 36, 36, 36, 38, 40, 40, 40, 42, 45, 45, + 34, 34, 34, 34, 34, 35, 37, 37, 37, 39, 42, 42, 42, 45, 48, 48, 34, 34, 34, 34, 34, 35, 37, 37, 37, 39, 42, 42, 42, 45, 48, 48, + 34, 34, 34, 34, 34, 35, 37, 37, 37, 39, 42, 42, 42, 45, 48, 48, 35, 34, 34, 34, 34, 36, 37, 37, 37, 41, 45, 45, 45, 47, 50, 50, + 36, 35, 34, 34, 34, 36, 38, 38, 38, 43, 48, 48, 48, 51, 54, 54, 36, 35, 34, 34, 34, 36, 38, 38, 38, 43, 48, 48, 48, 51, 54, 54, + 36, 35, 34, 34, 34, 36, 38, 38, 38, 43, 48, 48, 48, 51, 54, 54, 37, 37, 36, 36, 36, 38, 39, 39, 39, 44, 49, 49, 49, 52, 56, 56, + 39, 38, 37, 37, 37, 39, 40, 40, 40, 45, 50, 50, 50, 54, 58, 58, 39, 38, 37, 37, 37, 39, 40, 40, 40, 45, 50, 50, 50, 54, 58, 58, + 39, 38, 37, 37, 37, 39, 40, 40, 40, 45, 50, 50, 50, 54, 58, 58, 41, 40, 39, 39, 39, 40, 42, 42, 42, 46, 52, 52, 52, 56, 60, 60, + 44, 42, 41, 41, 41, 42, 43, 43, 43, 48, 53, 53, 53, 58, 63, 63, 44, 42, 41, 41, 41, 42, 43, 43, 43, 48, 53, 53, 53, 58, 63, 63, + }, { + 32, 31, 31, 31, 31, 33, 37, 37, 37, 42, 48, 48, 48, 48, 49, 49, 31, 31, 31, 31, 31, 34, 37, 37, 37, 42, 47, 47, 47, 48, 48, 48, + 31, 31, 31, 31, 31, 34, 38, 38, 38, 42, 47, 47, 47, 47, 47, 47, 31, 31, 31, 31, 31, 34, 38, 38, 38, 42, 47, 47, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 34, 38, 38, 38, 42, 47, 47, 47, 47, 47, 47, 31, 31, 32, 32, 32, 35, 39, 39, 39, 42, 46, 46, 46, 46, 46, 46, + 30, 31, 32, 32, 32, 35, 40, 40, 40, 42, 46, 46, 46, 45, 45, 45, 30, 31, 32, 32, 32, 35, 40, 40, 40, 42, 46, 46, 46, 45, 45, 45, + 30, 31, 32, 32, 32, 35, 40, 40, 40, 42, 46, 46, 46, 45, 45, 45, 32, 33, 34, 34, 34, 37, 41, 41, 41, 44, 46, 46, 46, 46, 45, 45, + 33, 34, 36, 36, 36, 39, 43, 43, 43, 45, 47, 47, 47, 46, 46, 46, 33, 34, 36, 36, 36, 39, 43, 43, 43, 45, 47, 47, 47, 46, 46, 46, + 33, 34, 36, 36, 36, 39, 43, 43, 43, 45, 47, 47, 47, 46, 46, 46, 35, 36, 38, 38, 38, 41, 45, 45, 45, 46, 47, 47, 47, 46, 45, 45, + 37, 38, 40, 40, 40, 43, 47, 47, 47, 47, 47, 47, 47, 46, 45, 45, 37, 38, 40, 40, 40, 43, 47, 47, 47, 47, 47, 47, 47, 46, 45, 45, + 37, 38, 40, 40, 40, 43, 47, 47, 47, 47, 47, 47, 47, 46, 45, 45, 39, 40, 41, 41, 41, 44, 47, 47, 47, 48, 49, 49, 49, 48, 47, 47, + 42, 42, 43, 43, 43, 45, 47, 47, 47, 48, 50, 50, 50, 50, 49, 49, 42, 42, 43, 43, 43, 45, 47, 47, 47, 48, 50, 50, 50, 50, 49, 49, + 42, 42, 43, 43, 43, 45, 47, 47, 47, 48, 50, 50, 50, 50, 49, 49, 45, 45, 44, 44, 44, 46, 47, 47, 47, 49, 51, 51, 51, 51, 51, 51, + 49, 48, 46, 46, 46, 47, 48, 48, 48, 50, 53, 53, 53, 53, 53, 53, 49, 48, 46, 46, 46, 47, 48, 48, 48, 50, 53, 53, 53, 53, 53, 53, + 49, 48, 46, 46, 46, 47, 48, 48, 48, 50, 53, 53, 53, 53, 53, 53, 48, 47, 46, 46, 46, 47, 47, 47, 47, 50, 53, 53, 53, 54, 54, 54, + 48, 47, 46, 46, 46, 46, 47, 47, 47, 50, 53, 53, 53, 54, 56, 56, 48, 47, 46, 46, 46, 46, 47, 47, 47, 50, 53, 53, 53, 54, 56, 56, + 48, 47, 46, 46, 46, 46, 47, 47, 47, 50, 53, 53, 53, 54, 56, 56, 48, 47, 45, 45, 45, 46, 46, 46, 46, 49, 53, 53, 53, 55, 57, 57, + 49, 47, 45, 45, 45, 45, 46, 46, 46, 49, 53, 53, 53, 56, 58, 58, 49, 47, 45, 45, 45, 45, 46, 46, 46, 49, 53, 53, 53, 56, 58, 58, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 33, 35, 36, 36, 36, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 35, 35, 35, 35, + 31, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 36, 36, 36, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 36, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 36, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 36, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 37, 37, 37, 32, 32, 32, 33, 33, 33, 33, 34, 35, 35, 35, 36, 37, 38, 38, 38, + 32, 32, 32, 33, 33, 33, 34, 35, 35, 35, 35, 36, 37, 38, 38, 38, 32, 32, 32, 33, 33, 33, 34, 35, 35, 35, 35, 36, 37, 38, 38, 38, + 32, 32, 32, 33, 33, 33, 34, 35, 35, 35, 35, 36, 37, 38, 38, 38, 32, 33, 33, 33, 33, 33, 34, 35, 36, 36, 36, 37, 39, 40, 40, 40, + 33, 33, 33, 33, 33, 33, 35, 36, 36, 36, 36, 38, 40, 41, 41, 41, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 39, 41, 42, 42, 42, + 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 39, 41, 42, 42, 42, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 39, 41, 42, 42, 42, + 34, 34, 34, 34, 34, 34, 35, 37, 37, 37, 37, 40, 43, 44, 44, 44, 35, 35, 34, 34, 34, 34, 36, 37, 38, 38, 38, 41, 45, 47, 47, 47, + 36, 35, 35, 34, 34, 34, 36, 37, 38, 38, 38, 42, 46, 48, 48, 48, 36, 35, 35, 34, 34, 34, 36, 37, 38, 38, 38, 42, 46, 48, 48, 48, + 36, 35, 35, 34, 34, 34, 36, 37, 38, 38, 38, 42, 46, 48, 48, 48, 37, 36, 36, 36, 36, 36, 37, 38, 39, 39, 39, 42, 46, 49, 49, 49, + }, { + 32, 31, 31, 31, 31, 31, 33, 35, 37, 37, 37, 40, 45, 48, 48, 48, 31, 31, 31, 31, 31, 31, 33, 36, 37, 37, 37, 41, 45, 48, 48, 48, + 31, 31, 31, 31, 31, 31, 34, 36, 38, 38, 38, 41, 45, 47, 47, 47, 31, 31, 31, 31, 31, 31, 34, 37, 38, 38, 38, 41, 45, 47, 47, 47, + 31, 31, 31, 31, 31, 31, 34, 37, 38, 38, 38, 41, 45, 47, 47, 47, 31, 31, 31, 31, 31, 31, 34, 37, 38, 38, 38, 41, 45, 47, 47, 47, + 31, 31, 31, 32, 32, 32, 34, 37, 39, 39, 39, 41, 45, 46, 46, 46, 30, 31, 31, 32, 32, 32, 34, 38, 39, 39, 39, 42, 44, 46, 46, 46, + 30, 31, 32, 32, 32, 32, 35, 38, 40, 40, 40, 42, 44, 46, 46, 46, 30, 31, 32, 32, 32, 32, 35, 38, 40, 40, 40, 42, 44, 46, 46, 46, + 30, 31, 32, 32, 32, 32, 35, 38, 40, 40, 40, 42, 44, 46, 46, 46, 31, 32, 33, 33, 33, 33, 36, 39, 41, 41, 41, 43, 45, 46, 46, 46, + 33, 34, 34, 35, 35, 35, 37, 40, 42, 42, 42, 44, 46, 47, 47, 47, 33, 34, 35, 36, 36, 36, 38, 41, 43, 43, 43, 44, 46, 47, 47, 47, + 33, 34, 35, 36, 36, 36, 38, 41, 43, 43, 43, 44, 46, 47, 47, 47, 33, 34, 35, 36, 36, 36, 38, 41, 43, 43, 43, 44, 46, 47, 47, 47, + 35, 36, 37, 37, 37, 37, 40, 43, 44, 44, 44, 45, 46, 47, 47, 47, 36, 37, 38, 39, 39, 39, 42, 44, 46, 46, 46, 47, 47, 47, 47, 47, + 37, 38, 39, 40, 40, 40, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 37, 38, 39, 40, 40, 40, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, + 37, 38, 39, 40, 40, 40, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 39, 39, 40, 41, 41, 41, 43, 46, 47, 47, 47, 48, 48, 48, 48, 48, + 41, 41, 42, 42, 42, 42, 44, 46, 47, 47, 47, 48, 49, 49, 49, 49, 42, 42, 42, 43, 43, 43, 44, 46, 47, 47, 47, 48, 49, 50, 50, 50, + 42, 42, 42, 43, 43, 43, 44, 46, 47, 47, 47, 48, 49, 50, 50, 50, 42, 42, 42, 43, 43, 43, 44, 46, 47, 47, 47, 48, 49, 50, 50, 50, + 44, 44, 44, 44, 44, 44, 45, 47, 47, 47, 47, 49, 50, 51, 51, 51, 47, 46, 46, 46, 46, 46, 46, 47, 48, 48, 48, 49, 51, 52, 52, 52, + 49, 48, 47, 46, 46, 46, 47, 48, 48, 48, 48, 50, 52, 53, 53, 53, 49, 48, 47, 46, 46, 46, 47, 48, 48, 48, 48, 50, 52, 53, 53, 53, + 49, 48, 47, 46, 46, 46, 47, 48, 48, 48, 48, 50, 52, 53, 53, 53, 49, 48, 47, 46, 46, 46, 47, 47, 47, 47, 47, 49, 52, 53, 53, 53, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 34, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 35, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 35, 35, 35, 35, 36, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 35, 35, 35, 35, 36, 36, + 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, 37, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, 37, + 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, 37, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, 37, + 32, 33, 33, 33, 33, 33, 33, 33, 34, 35, 36, 36, 36, 36, 36, 38, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 36, 36, 36, 36, 37, 38, + 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 37, 38, 39, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 37, 38, 39, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 33, 35, 37, 37, 37, 37, 38, 42, 31, 31, 31, 31, 31, 31, 31, 31, 33, 35, 37, 37, 37, 37, 39, 42, + 31, 31, 31, 31, 31, 31, 31, 32, 33, 35, 38, 38, 38, 38, 39, 42, 31, 31, 31, 31, 31, 31, 31, 32, 34, 36, 38, 38, 38, 38, 40, 42, + 31, 31, 31, 31, 31, 31, 31, 32, 34, 36, 38, 38, 38, 38, 40, 42, 31, 31, 31, 31, 31, 31, 31, 32, 34, 36, 38, 38, 38, 38, 40, 42, + 31, 31, 31, 31, 31, 31, 31, 32, 34, 36, 38, 38, 38, 38, 40, 42, 31, 31, 31, 31, 31, 31, 31, 32, 34, 36, 38, 38, 38, 38, 40, 42, + 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 39, 39, 39, 39, 40, 42, 30, 31, 31, 32, 32, 32, 32, 32, 34, 37, 39, 39, 39, 39, 40, 42, + 30, 31, 31, 32, 32, 32, 32, 33, 35, 37, 40, 40, 40, 40, 41, 42, 30, 31, 31, 32, 32, 32, 32, 33, 35, 37, 40, 40, 40, 40, 41, 42, + 30, 31, 31, 32, 32, 32, 32, 33, 35, 37, 40, 40, 40, 40, 41, 42, 30, 31, 31, 32, 32, 32, 32, 33, 35, 37, 40, 40, 40, 40, 41, 42, + 31, 31, 32, 32, 33, 33, 33, 33, 35, 38, 40, 40, 40, 40, 41, 43, 32, 32, 33, 33, 34, 34, 34, 34, 36, 39, 41, 41, 41, 41, 42, 44, + 33, 33, 34, 35, 35, 35, 35, 35, 37, 40, 42, 42, 42, 42, 43, 44, 33, 34, 35, 35, 36, 36, 36, 36, 38, 40, 43, 43, 43, 43, 44, 45, + 33, 34, 35, 35, 36, 36, 36, 36, 38, 40, 43, 43, 43, 43, 44, 45, 33, 34, 35, 35, 36, 36, 36, 36, 38, 40, 43, 43, 43, 43, 44, 45, + 33, 34, 35, 35, 36, 36, 36, 36, 38, 40, 43, 43, 43, 43, 44, 45, 34, 35, 36, 37, 37, 37, 37, 37, 39, 42, 44, 44, 44, 44, 45, 45, + 35, 36, 37, 38, 38, 38, 38, 39, 41, 43, 45, 45, 45, 45, 46, 46, 36, 37, 38, 39, 39, 39, 39, 40, 42, 44, 47, 47, 47, 47, 47, 47, + 37, 38, 39, 40, 40, 40, 40, 41, 43, 45, 47, 47, 47, 47, 47, 47, 37, 38, 39, 40, 40, 40, 40, 41, 43, 45, 47, 47, 47, 47, 47, 47, + 37, 38, 39, 40, 40, 40, 40, 41, 43, 45, 47, 47, 47, 47, 47, 47, 37, 38, 39, 40, 40, 40, 40, 41, 43, 45, 47, 47, 47, 47, 47, 47, + 39, 39, 40, 41, 41, 41, 41, 42, 43, 45, 47, 47, 47, 47, 47, 48, 40, 41, 41, 42, 42, 42, 42, 42, 44, 45, 47, 47, 47, 47, 47, 48, + 42, 42, 42, 43, 43, 43, 43, 43, 44, 46, 47, 47, 47, 47, 48, 48, 42, 42, 42, 43, 43, 43, 43, 43, 44, 46, 47, 47, 47, 47, 48, 48, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 37, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 37, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 36, 37, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 35, 36, 38, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 35, 36, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 37, 38, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 37, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 37, 38, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 37, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 37, 38, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 37, 38, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 37, 38, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 36, 37, 39, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 36, 37, 39, + 30, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 36, 38, 39, 30, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, + 30, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, 30, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, + 30, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, 30, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, + 30, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 35, 36, 38, 40, 31, 31, 31, 32, 32, 33, 33, 33, 33, 33, 33, 34, 35, 37, 38, 40, + 31, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 35, 36, 37, 39, 41, 32, 32, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 37, 38, 40, 41, + 33, 33, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 37, 39, 40, 42, 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, 41, 43, + 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, 41, 43, 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, 41, 43, + 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, 41, 43, 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, 41, 43, + 33, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 40, 41, 43, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 36, 38, 39, 40, 42, 44, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + }, + }, +}; + +static const uint8_t qm_tbl_32x8[15][2][256] = { + { + { + 32, 31, 31, 31, 32, 32, 34, 35, 36, 39, 44, 46, 48, 53, 58, 61, 65, 71, 79, 81, 82, 88, 91, 94, 97, 100, 103, 107, 110, 114, 118, 122, + 32, 32, 33, 33, 34, 35, 37, 37, 38, 40, 43, 44, 46, 50, 54, 56, 58, 63, 70, 71, 72, 77, 80, 83, 86, 89, 93, 96, 100, 104, 107, 111, + 36, 35, 34, 35, 36, 38, 42, 45, 48, 50, 53, 55, 56, 60, 63, 66, 68, 73, 79, 80, 81, 85, 88, 91, 94, 97, 98, 100, 101, 103, 105, 107, + 53, 51, 49, 49, 50, 49, 54, 57, 60, 65, 71, 73, 76, 82, 87, 89, 92, 97, 104, 105, 106, 108, 106, 105, 107, 111, 114, 117, 117, 117, 118, 119, + 65, 62, 59, 59, 59, 58, 63, 65, 68, 73, 79, 82, 85, 92, 98, 101, 105, 111, 118, 119, 121, 126, 130, 131, 128, 127, 131, 136, 138, 137, 136, 136, + 87, 82, 78, 78, 77, 75, 79, 82, 84, 89, 95, 98, 102, 109, 116, 120, 124, 132, 141, 142, 144, 149, 148, 153, 157, 152, 150, 155, 161, 159, 157, 156, + 93, 88, 86, 84, 82, 82, 80, 84, 86, 91, 94, 98, 105, 107, 112, 119, 122, 130, 135, 140, 149, 153, 162, 165, 167, 173, 174, 177, 183, 185, 182, 179, + 99, 94, 93, 90, 89, 89, 88, 87, 90, 93, 97, 99, 105, 107, 115, 116, 124, 127, 135, 139, 146, 152, 159, 166, 171, 182, 186, 191, 193, 201, 203, 204, + }, { + 32, 31, 30, 32, 33, 37, 42, 45, 49, 48, 49, 49, 50, 52, 54, 55, 57, 60, 63, 64, 64, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 37, 38, 40, 41, 43, 47, 47, 47, 48, 47, 46, 46, 46, 47, 49, 49, 50, 52, 55, 55, 56, 58, 59, 60, 62, 63, 64, 65, 67, 68, 69, 70, + 48, 47, 46, 46, 47, 47, 50, 51, 53, 53, 53, 53, 54, 54, 55, 56, 56, 58, 60, 61, 61, 63, 64, 65, 66, 67, 66, 66, 66, 66, 67, 67, + 52, 50, 48, 48, 47, 47, 50, 52, 54, 57, 61, 62, 64, 66, 68, 69, 70, 72, 75, 75, 75, 76, 74, 72, 73, 74, 75, 75, 74, 74, 73, 73, + 57, 54, 52, 51, 51, 50, 53, 55, 57, 60, 64, 65, 67, 71, 73, 75, 76, 79, 82, 82, 83, 85, 86, 85, 83, 82, 83, 84, 84, 83, 82, 81, + 66, 63, 60, 59, 59, 57, 60, 61, 62, 66, 69, 71, 73, 77, 80, 82, 84, 88, 92, 92, 93, 95, 94, 95, 96, 93, 92, 93, 94, 93, 91, 90, + 68, 65, 63, 62, 60, 60, 59, 61, 62, 65, 66, 68, 72, 73, 76, 79, 80, 84, 87, 89, 93, 94, 98, 99, 99, 102, 101, 102, 103, 103, 101, 99, + 71, 67, 66, 64, 63, 62, 62, 61, 62, 64, 66, 67, 70, 71, 75, 76, 79, 81, 84, 86, 89, 91, 94, 97, 98, 102, 104, 106, 106, 109, 109, 108, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 32, 34, 36, 38, 39, 44, 47, 49, 53, 58, 61, 65, 71, 76, 79, 82, 86, 89, 92, 95, 98, 101, 104, 107, 110, 114, + 32, 32, 32, 33, 34, 35, 35, 36, 37, 39, 40, 42, 45, 47, 50, 54, 56, 59, 64, 68, 70, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 104, + 36, 35, 35, 35, 36, 38, 40, 42, 48, 49, 50, 53, 56, 57, 60, 63, 65, 68, 73, 76, 79, 81, 84, 87, 89, 92, 93, 94, 95, 96, 98, 100, + 47, 45, 44, 44, 45, 45, 47, 50, 56, 58, 60, 66, 69, 71, 75, 79, 81, 84, 89, 92, 95, 97, 100, 99, 101, 105, 108, 110, 110, 110, 111, 111, + 65, 62, 60, 59, 59, 58, 60, 63, 68, 71, 73, 79, 84, 86, 92, 98, 100, 105, 111, 115, 118, 121, 124, 124, 121, 120, 124, 128, 129, 128, 127, 127, + 79, 75, 72, 71, 71, 69, 71, 73, 78, 81, 84, 90, 95, 97, 103, 110, 113, 118, 125, 130, 133, 136, 140, 145, 148, 143, 141, 146, 151, 149, 147, 145, + 90, 86, 84, 82, 80, 80, 78, 82, 83, 88, 91, 94, 101, 103, 108, 114, 116, 124, 129, 134, 142, 145, 153, 156, 157, 163, 163, 166, 171, 173, 169, 166, + 96, 91, 90, 87, 87, 86, 85, 84, 87, 90, 94, 96, 101, 102, 110, 111, 118, 121, 129, 132, 138, 144, 150, 156, 161, 171, 174, 179, 181, 188, 188, 190, + }, { + 32, 31, 30, 31, 33, 37, 39, 42, 49, 48, 48, 49, 50, 51, 52, 54, 55, 57, 60, 62, 63, 64, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 35, 37, 38, 38, 41, 45, 46, 46, 48, 47, 46, 45, 46, 47, 47, 49, 49, 50, 53, 54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, + 48, 47, 46, 46, 47, 47, 48, 50, 53, 53, 53, 53, 54, 54, 54, 55, 56, 56, 58, 60, 60, 61, 62, 63, 64, 65, 65, 65, 65, 65, 65, 65, + 50, 48, 46, 46, 46, 46, 47, 50, 54, 55, 56, 59, 61, 61, 63, 65, 65, 66, 68, 69, 70, 71, 72, 71, 71, 72, 73, 73, 72, 72, 71, 71, + 57, 54, 52, 52, 51, 50, 51, 53, 57, 58, 60, 64, 66, 68, 71, 73, 74, 76, 79, 81, 82, 83, 84, 83, 81, 80, 81, 82, 82, 81, 79, 78, + 63, 60, 58, 57, 56, 54, 55, 57, 60, 62, 64, 67, 70, 71, 75, 78, 79, 82, 85, 87, 89, 90, 91, 93, 94, 91, 89, 90, 92, 90, 89, 87, + 68, 64, 63, 61, 60, 59, 58, 60, 61, 64, 65, 67, 71, 72, 75, 78, 79, 83, 85, 87, 91, 92, 95, 96, 97, 99, 98, 99, 100, 100, 98, 96, + 70, 66, 65, 63, 63, 62, 61, 60, 61, 63, 65, 66, 69, 70, 74, 74, 78, 79, 82, 84, 87, 89, 91, 94, 96, 100, 101, 103, 103, 105, 105, 105, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 32, 34, 34, 36, 39, 40, 44, 47, 49, 53, 57, 59, 65, 69, 71, 79, 81, 82, 87, 90, 92, 95, 98, 100, 103, 106, + 32, 32, 32, 32, 33, 34, 34, 35, 36, 37, 39, 40, 42, 45, 46, 50, 53, 54, 59, 62, 64, 71, 72, 73, 77, 80, 83, 85, 88, 91, 94, 97, + 36, 35, 35, 34, 36, 37, 38, 42, 44, 48, 50, 51, 53, 56, 57, 60, 63, 64, 68, 71, 73, 79, 80, 81, 85, 87, 88, 88, 89, 90, 92, 93, + 44, 42, 41, 41, 42, 42, 42, 48, 50, 54, 58, 59, 63, 66, 67, 71, 74, 75, 79, 83, 84, 90, 91, 92, 96, 99, 102, 103, 103, 103, 103, 104, + 58, 55, 54, 53, 53, 53, 52, 57, 59, 63, 68, 70, 74, 79, 81, 86, 90, 91, 97, 100, 102, 109, 110, 111, 114, 113, 117, 120, 121, 120, 119, 118, + 79, 75, 73, 72, 71, 70, 69, 73, 75, 78, 84, 85, 90, 95, 97, 103, 108, 111, 118, 122, 125, 133, 135, 136, 140, 135, 133, 137, 141, 139, 137, 135, + 88, 83, 81, 79, 78, 77, 76, 79, 81, 85, 88, 91, 97, 99, 104, 109, 111, 119, 123, 127, 135, 137, 145, 147, 148, 153, 153, 155, 160, 161, 158, 155, + 93, 88, 88, 84, 84, 83, 82, 81, 84, 86, 90, 92, 97, 98, 105, 106, 113, 115, 122, 125, 131, 136, 141, 147, 151, 160, 163, 168, 169, 175, 175, 176, + }, { + 32, 31, 31, 30, 33, 35, 37, 42, 44, 49, 48, 48, 49, 50, 51, 52, 54, 54, 57, 59, 60, 63, 64, 64, 66, 67, 68, 69, 70, 71, 72, 73, + 34, 35, 36, 36, 40, 42, 44, 45, 46, 47, 46, 46, 45, 46, 47, 47, 49, 49, 51, 52, 53, 56, 57, 57, 59, 60, 61, 62, 63, 64, 65, 66, + 48, 47, 46, 46, 47, 47, 47, 50, 51, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 58, 58, 60, 61, 61, 63, 63, 63, 63, 63, 63, 63, 63, + 49, 47, 46, 45, 46, 45, 45, 49, 51, 53, 56, 56, 58, 59, 60, 61, 62, 62, 64, 65, 65, 67, 68, 68, 69, 70, 71, 71, 70, 70, 69, 69, + 54, 51, 50, 49, 49, 48, 48, 51, 53, 55, 58, 59, 62, 65, 65, 68, 70, 70, 73, 74, 75, 77, 78, 78, 79, 78, 79, 80, 80, 78, 77, 76, + 63, 60, 58, 57, 56, 55, 54, 57, 59, 60, 64, 65, 67, 70, 71, 75, 77, 78, 82, 84, 85, 89, 89, 90, 91, 88, 87, 88, 89, 88, 86, 84, + 67, 63, 62, 60, 59, 58, 57, 59, 60, 63, 64, 66, 70, 70, 73, 76, 77, 81, 83, 85, 89, 90, 93, 94, 94, 96, 96, 96, 97, 97, 95, 93, + 69, 65, 65, 62, 62, 61, 60, 59, 61, 62, 64, 65, 68, 68, 72, 73, 76, 77, 81, 82, 85, 87, 89, 92, 93, 97, 98, 100, 100, 102, 102, 101, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 34, 35, 36, 39, 41, 44, 47, 48, 53, 55, 58, 63, 65, 71, 74, 79, 82, 82, 87, 89, 92, 94, 97, 99, + 31, 32, 32, 32, 33, 33, 34, 34, 35, 36, 36, 39, 40, 42, 44, 45, 50, 51, 54, 58, 59, 64, 67, 71, 73, 74, 78, 81, 83, 85, 88, 91, + 35, 35, 34, 34, 35, 36, 37, 39, 41, 45, 46, 48, 49, 51, 53, 54, 57, 59, 61, 65, 66, 71, 73, 77, 79, 79, 83, 83, 84, 85, 86, 87, + 44, 42, 41, 41, 42, 42, 42, 44, 48, 52, 54, 58, 60, 63, 66, 67, 71, 72, 75, 78, 79, 84, 86, 90, 92, 92, 96, 97, 97, 97, 97, 97, + 53, 51, 50, 49, 49, 50, 49, 51, 54, 59, 60, 65, 67, 71, 75, 76, 82, 84, 87, 91, 92, 97, 100, 104, 105, 106, 110, 113, 114, 112, 111, 110, + 65, 62, 61, 59, 59, 59, 58, 60, 63, 67, 68, 73, 76, 79, 84, 85, 92, 94, 98, 103, 105, 111, 113, 118, 120, 121, 125, 128, 132, 130, 128, 126, + 82, 78, 76, 74, 73, 73, 71, 73, 76, 79, 80, 86, 88, 92, 97, 98, 106, 108, 112, 118, 120, 127, 131, 136, 139, 139, 144, 145, 150, 151, 147, 144, + 90, 86, 85, 82, 81, 80, 79, 78, 81, 83, 87, 88, 93, 94, 101, 101, 108, 110, 116, 119, 124, 129, 134, 139, 142, 150, 153, 157, 157, 163, 163, 163, + }, { + 32, 31, 31, 30, 33, 33, 37, 39, 42, 47, 49, 48, 48, 49, 50, 50, 52, 53, 54, 56, 57, 60, 61, 63, 64, 64, 66, 67, 68, 69, 70, 70, + 33, 34, 34, 35, 37, 38, 43, 43, 44, 46, 47, 46, 46, 45, 46, 46, 47, 48, 49, 51, 51, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 45, 45, 45, 44, 46, 46, 47, 48, 49, 51, 52, 51, 51, 51, 52, 52, 53, 53, 54, 55, 55, 57, 58, 59, 60, 60, 61, 61, 61, 61, 61, 61, + 49, 47, 46, 45, 45, 46, 45, 47, 49, 53, 53, 56, 57, 58, 59, 59, 61, 61, 62, 63, 64, 65, 66, 67, 68, 68, 69, 69, 68, 68, 67, 67, + 52, 50, 49, 48, 47, 47, 47, 48, 50, 53, 54, 57, 59, 61, 63, 64, 66, 67, 68, 70, 70, 72, 73, 75, 75, 75, 77, 78, 77, 76, 75, 74, + 57, 54, 53, 52, 51, 51, 50, 51, 53, 56, 57, 60, 61, 64, 66, 67, 71, 72, 73, 76, 76, 79, 80, 82, 83, 83, 84, 85, 86, 85, 83, 82, + 64, 61, 60, 58, 57, 57, 55, 56, 58, 61, 61, 64, 66, 68, 71, 71, 75, 77, 79, 82, 83, 86, 87, 90, 91, 91, 93, 93, 94, 94, 92, 90, + 68, 64, 64, 61, 61, 60, 59, 58, 60, 61, 63, 64, 67, 67, 71, 71, 74, 75, 79, 80, 83, 85, 87, 89, 91, 94, 95, 97, 97, 99, 98, 98, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 33, 34, 36, 36, 39, 40, 44, 46, 48, 52, 53, 58, 58, 65, 66, 71, 74, 79, 81, 82, 86, 88, 91, 93, + 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 38, 39, 41, 43, 45, 48, 49, 53, 54, 59, 60, 65, 67, 72, 73, 74, 78, 80, 82, 85, + 33, 33, 33, 33, 34, 35, 36, 36, 38, 39, 42, 42, 44, 45, 46, 48, 50, 52, 53, 57, 57, 62, 63, 67, 69, 73, 75, 75, 78, 80, 80, 81, + 40, 39, 39, 38, 38, 39, 40, 41, 44, 45, 51, 51, 54, 56, 59, 60, 62, 65, 66, 69, 70, 74, 75, 79, 81, 85, 86, 87, 90, 90, 90, 90, + 51, 49, 49, 47, 47, 48, 48, 48, 52, 53, 58, 59, 63, 65, 69, 72, 74, 78, 79, 83, 84, 89, 90, 94, 97, 101, 102, 103, 106, 105, 103, 103, + 65, 62, 61, 59, 59, 59, 58, 58, 62, 63, 68, 68, 73, 75, 79, 82, 85, 90, 92, 97, 98, 105, 106, 111, 113, 118, 120, 121, 124, 122, 119, 117, + 79, 75, 74, 72, 71, 71, 69, 69, 72, 73, 78, 79, 84, 85, 90, 93, 96, 101, 103, 109, 110, 118, 119, 125, 128, 133, 135, 136, 140, 140, 137, 134, + 87, 83, 82, 79, 79, 78, 77, 75, 78, 80, 84, 85, 89, 90, 96, 97, 103, 105, 111, 113, 118, 122, 126, 131, 134, 141, 143, 147, 147, 152, 151, 152, + }, { + 32, 31, 31, 30, 31, 33, 35, 37, 41, 42, 49, 49, 48, 48, 49, 49, 50, 51, 52, 54, 54, 57, 57, 60, 61, 63, 64, 64, 66, 67, 68, 68, + 32, 33, 33, 33, 35, 37, 39, 41, 43, 43, 47, 47, 46, 46, 45, 46, 46, 47, 48, 49, 50, 52, 52, 54, 55, 57, 58, 58, 59, 60, 61, 62, + 40, 41, 41, 42, 43, 44, 46, 47, 48, 48, 50, 50, 49, 49, 48, 49, 49, 49, 50, 51, 51, 52, 53, 55, 56, 57, 58, 58, 59, 59, 59, 59, + 49, 47, 47, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 55, 56, 57, 57, 58, 58, 59, 59, 60, 61, 62, 63, 64, 65, 65, 66, 66, 65, 65, + 51, 49, 49, 47, 47, 47, 47, 46, 49, 50, 54, 54, 57, 58, 61, 62, 63, 64, 65, 67, 67, 69, 69, 71, 72, 73, 73, 74, 75, 74, 72, 71, + 57, 54, 54, 52, 51, 51, 50, 50, 52, 53, 57, 57, 60, 61, 64, 65, 67, 69, 71, 73, 73, 76, 77, 79, 80, 82, 82, 83, 84, 82, 81, 79, + 63, 60, 59, 57, 57, 56, 55, 54, 57, 57, 60, 61, 64, 65, 67, 69, 71, 73, 75, 77, 78, 82, 82, 85, 86, 89, 89, 90, 91, 91, 89, 87, + 67, 63, 63, 60, 60, 59, 58, 57, 59, 60, 62, 63, 65, 66, 69, 70, 73, 74, 77, 78, 81, 83, 85, 87, 88, 92, 92, 94, 94, 96, 95, 95, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 36, 36, 39, 39, 44, 44, 48, 48, 53, 53, 58, 58, 65, 65, 71, 71, 79, 79, 82, 82, 87, + 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 37, 37, 41, 41, 45, 45, 49, 49, 54, 54, 60, 60, 65, 65, 72, 72, 75, 75, 79, + 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 43, 43, 46, 46, 50, 50, 54, 54, 58, 58, 63, 63, 70, 70, 72, 72, 76, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 53, 53, 56, 56, 60, 60, 63, 63, 68, 68, 73, 73, 79, 79, 81, 81, 84, + 44, 42, 42, 41, 41, 42, 42, 42, 42, 48, 48, 54, 54, 58, 58, 63, 63, 67, 67, 71, 71, 75, 75, 79, 79, 84, 84, 90, 90, 92, 92, 96, + 53, 51, 51, 49, 49, 50, 50, 49, 49, 54, 54, 60, 60, 65, 65, 71, 71, 76, 76, 82, 82, 87, 87, 92, 92, 97, 97, 104, 104, 106, 106, 109, + 65, 62, 62, 59, 59, 59, 59, 58, 58, 63, 63, 68, 68, 73, 73, 79, 79, 85, 85, 92, 92, 98, 98, 105, 105, 111, 111, 118, 118, 121, 121, 124, + 79, 75, 75, 72, 72, 71, 71, 69, 69, 73, 73, 78, 78, 84, 84, 90, 90, 96, 96, 103, 103, 110, 110, 118, 118, 125, 125, 133, 133, 136, 136, 141, + }, { + 32, 31, 31, 30, 30, 33, 33, 37, 37, 42, 42, 49, 49, 48, 48, 49, 49, 50, 50, 52, 52, 54, 54, 57, 57, 60, 60, 63, 63, 64, 64, 66, + 31, 31, 31, 32, 32, 36, 36, 40, 40, 43, 43, 46, 46, 46, 46, 45, 45, 46, 46, 48, 48, 50, 50, 52, 52, 54, 54, 57, 57, 59, 59, 60, + 37, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 57, + 48, 47, 47, 46, 46, 47, 47, 47, 47, 50, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 58, 58, 60, 60, 61, 61, 63, + 49, 47, 47, 45, 45, 46, 46, 45, 45, 49, 49, 53, 53, 56, 56, 58, 58, 59, 59, 61, 61, 62, 62, 64, 64, 65, 65, 67, 67, 68, 68, 69, + 52, 50, 50, 48, 48, 47, 47, 47, 47, 50, 50, 54, 54, 57, 57, 61, 61, 64, 64, 66, 66, 68, 68, 70, 70, 72, 72, 75, 75, 75, 75, 77, + 57, 54, 54, 52, 52, 51, 51, 50, 50, 53, 53, 57, 57, 60, 60, 64, 64, 67, 67, 71, 71, 73, 73, 76, 76, 79, 79, 82, 82, 83, 83, 84, + 63, 60, 60, 57, 57, 56, 56, 54, 54, 57, 57, 60, 60, 64, 64, 67, 67, 71, 71, 75, 75, 78, 78, 82, 82, 85, 85, 89, 89, 90, 90, 92, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 35, 36, 38, 39, 41, 44, 44, 48, 48, 53, 53, 57, 58, 61, 65, 67, 71, 72, 79, 79, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 36, 37, 39, 41, 42, 45, 45, 49, 49, 52, 54, 57, 60, 61, 65, 66, 72, 72, + 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 41, 43, 43, 46, 46, 49, 50, 52, 54, 56, 58, 60, 63, 64, 70, 70, + 36, 35, 35, 35, 34, 35, 36, 37, 38, 39, 42, 42, 47, 48, 49, 50, 51, 53, 54, 56, 56, 59, 60, 62, 63, 66, 68, 69, 73, 73, 79, 79, + 44, 42, 42, 41, 41, 41, 42, 42, 42, 43, 48, 48, 52, 54, 56, 58, 60, 63, 64, 67, 67, 71, 71, 74, 75, 77, 79, 81, 84, 85, 90, 90, + 53, 51, 51, 50, 49, 49, 50, 49, 49, 50, 54, 54, 59, 60, 63, 65, 67, 71, 72, 76, 76, 81, 82, 85, 87, 89, 92, 94, 97, 98, 104, 104, + 62, 60, 59, 58, 57, 57, 57, 56, 56, 56, 61, 61, 65, 66, 69, 71, 74, 78, 79, 83, 83, 89, 90, 94, 95, 98, 102, 103, 108, 108, 115, 115, + 73, 70, 69, 67, 66, 66, 65, 65, 64, 64, 69, 69, 73, 74, 77, 79, 81, 85, 86, 91, 91, 98, 99, 103, 105, 108, 112, 114, 119, 119, 127, 127, + }, { + 32, 31, 31, 30, 30, 32, 33, 34, 37, 37, 42, 42, 47, 49, 48, 48, 48, 49, 49, 50, 50, 52, 52, 53, 54, 55, 57, 58, 60, 60, 63, 63, + 31, 31, 31, 32, 32, 34, 36, 37, 40, 40, 43, 43, 46, 46, 46, 46, 45, 45, 45, 46, 46, 48, 48, 49, 50, 51, 52, 53, 54, 55, 57, 57, + 37, 38, 38, 39, 40, 41, 43, 44, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 46, 47, 47, 48, 49, 49, 50, 51, 52, 53, 55, 55, + 48, 47, 47, 46, 46, 46, 47, 47, 47, 48, 50, 50, 52, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 57, 58, 59, 60, 60, + 49, 47, 47, 46, 45, 45, 46, 45, 45, 46, 49, 49, 53, 53, 55, 56, 57, 58, 58, 59, 59, 61, 61, 62, 62, 63, 64, 64, 65, 65, 67, 67, + 52, 50, 50, 48, 48, 48, 47, 47, 47, 47, 50, 50, 53, 54, 56, 57, 59, 61, 62, 64, 64, 66, 66, 68, 68, 69, 70, 71, 72, 73, 75, 75, + 56, 54, 53, 52, 51, 51, 50, 50, 49, 49, 53, 53, 55, 56, 58, 59, 61, 63, 64, 66, 66, 69, 70, 71, 72, 74, 75, 76, 77, 78, 80, 80, + 61, 58, 57, 56, 55, 54, 54, 53, 52, 53, 56, 56, 58, 59, 61, 62, 63, 66, 66, 69, 69, 72, 73, 75, 76, 78, 79, 80, 82, 83, 86, 86, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 33, 34, 34, 36, 36, 38, 39, 41, 44, 44, 47, 48, 50, 53, 53, 57, 58, 61, 65, 65, 70, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 37, 37, 39, 41, 41, 44, 45, 46, 49, 49, 53, 54, 56, 60, 60, 64, + 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, 40, 40, 41, 43, 43, 45, 46, 47, 50, 50, 53, 54, 56, 58, 58, 62, + 35, 35, 35, 34, 34, 34, 35, 36, 36, 37, 37, 40, 41, 43, 46, 46, 47, 48, 49, 51, 51, 53, 54, 55, 57, 57, 60, 61, 63, 66, 66, 70, + 39, 38, 38, 37, 37, 37, 38, 38, 39, 40, 40, 43, 44, 46, 50, 50, 52, 53, 54, 57, 57, 59, 60, 61, 64, 64, 67, 68, 69, 72, 72, 76, + 44, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 46, 48, 50, 54, 54, 57, 58, 60, 63, 63, 66, 67, 68, 71, 71, 74, 75, 77, 79, 79, 83, + 53, 52, 51, 50, 49, 49, 49, 50, 49, 49, 49, 53, 54, 56, 60, 60, 64, 65, 67, 71, 71, 75, 76, 78, 82, 82, 86, 87, 89, 92, 92, 96, + 65, 63, 62, 61, 59, 59, 59, 59, 58, 58, 58, 62, 63, 65, 68, 68, 72, 73, 76, 79, 79, 84, 85, 88, 92, 92, 97, 98, 100, 105, 105, 109, + }, { + 32, 31, 31, 31, 30, 30, 33, 33, 35, 37, 37, 41, 42, 44, 49, 49, 48, 48, 48, 49, 49, 50, 50, 51, 52, 52, 54, 54, 55, 57, 57, 59, + 31, 31, 31, 32, 32, 32, 35, 36, 37, 40, 40, 42, 43, 44, 46, 46, 46, 46, 45, 45, 45, 46, 46, 47, 48, 48, 49, 50, 51, 52, 52, 54, + 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 48, 49, 49, 50, 50, 52, + 45, 45, 45, 45, 44, 44, 46, 46, 46, 47, 47, 49, 49, 50, 52, 52, 51, 51, 51, 51, 51, 52, 52, 52, 53, 53, 54, 54, 54, 55, 55, 57, + 48, 47, 47, 46, 45, 45, 46, 46, 46, 47, 47, 49, 50, 51, 53, 53, 54, 54, 54, 55, 55, 56, 56, 56, 57, 57, 58, 58, 58, 59, 59, 61, + 49, 47, 47, 46, 45, 45, 45, 46, 45, 45, 45, 48, 49, 51, 53, 53, 55, 56, 57, 58, 58, 59, 59, 60, 61, 61, 62, 62, 63, 64, 64, 65, + 52, 50, 50, 49, 48, 48, 47, 47, 47, 47, 47, 50, 50, 52, 54, 54, 57, 57, 59, 61, 61, 63, 64, 65, 66, 66, 68, 68, 69, 70, 70, 72, + 57, 55, 54, 53, 52, 52, 51, 51, 51, 50, 50, 52, 53, 54, 57, 57, 59, 60, 61, 64, 64, 66, 67, 68, 71, 71, 73, 73, 74, 76, 76, 78, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 34, 34, 35, 36, 36, 38, 39, 39, 42, 44, 44, 47, 48, 49, 53, 53, 55, 58, 58, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 36, 37, 37, 40, 41, 41, 44, 45, 46, 49, 49, 51, 54, 54, + 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 36, 37, 37, 37, 39, 40, 40, 42, 42, 43, 45, 46, 47, 49, 50, 51, 54, 54, + 32, 33, 33, 33, 33, 33, 33, 34, 34, 35, 36, 36, 36, 38, 38, 39, 40, 40, 41, 42, 42, 44, 45, 45, 47, 48, 48, 51, 51, 53, 55, 55, + 36, 35, 35, 35, 35, 34, 35, 36, 36, 37, 38, 38, 40, 42, 42, 45, 48, 48, 49, 50, 50, 52, 53, 54, 56, 56, 57, 59, 60, 61, 63, 63, + 44, 43, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 44, 48, 48, 50, 54, 54, 56, 58, 58, 61, 63, 63, 66, 67, 67, 71, 71, 72, 75, 75, + 47, 46, 45, 45, 44, 44, 44, 45, 45, 45, 45, 45, 47, 50, 50, 53, 56, 56, 58, 60, 60, 64, 66, 66, 69, 70, 71, 74, 75, 76, 79, 79, + 53, 52, 51, 51, 49, 49, 49, 49, 50, 49, 49, 49, 51, 54, 54, 57, 60, 60, 63, 65, 65, 69, 71, 72, 75, 76, 77, 81, 82, 83, 87, 87, + }, { + 32, 31, 31, 31, 30, 30, 31, 33, 33, 34, 37, 37, 39, 42, 42, 45, 49, 49, 48, 48, 48, 49, 49, 49, 50, 50, 51, 52, 52, 53, 54, 54, + 31, 31, 31, 31, 32, 32, 33, 35, 36, 37, 40, 40, 41, 43, 43, 44, 46, 46, 46, 46, 46, 45, 45, 45, 46, 46, 47, 48, 48, 48, 50, 50, + 35, 36, 37, 37, 38, 38, 38, 41, 41, 42, 45, 45, 46, 46, 46, 47, 48, 48, 47, 46, 46, 46, 45, 46, 46, 46, 47, 47, 47, 48, 49, 49, + 38, 39, 40, 40, 40, 41, 41, 43, 44, 45, 47, 47, 47, 48, 48, 48, 49, 49, 48, 48, 48, 47, 47, 47, 48, 48, 48, 48, 48, 49, 50, 50, + 48, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 48, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, + 49, 48, 47, 47, 45, 45, 45, 45, 46, 45, 45, 45, 47, 49, 49, 51, 53, 53, 55, 56, 56, 57, 58, 58, 59, 59, 60, 61, 61, 61, 62, 62, + 50, 49, 48, 48, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 52, 54, 54, 55, 56, 56, 58, 59, 60, 61, 61, 61, 63, 63, 63, 65, 65, + 52, 50, 50, 50, 48, 48, 48, 47, 47, 47, 47, 47, 48, 50, 50, 52, 54, 54, 56, 57, 57, 60, 61, 61, 63, 64, 64, 66, 66, 67, 68, 68, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 34, 34, 34, 35, 36, 36, 38, 39, 39, 41, 44, 44, 44, 47, 48, 48, 51, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 34, 36, 37, 37, 39, 41, 41, 42, 44, 45, 45, 47, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 39, 39, 40, 42, 42, 42, 44, 45, 45, 48, + 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 38, 38, 38, 40, 40, 40, 41, 43, 43, 43, 45, 46, 46, 48, + 35, 35, 35, 35, 34, 34, 34, 34, 35, 36, 36, 37, 37, 37, 39, 41, 41, 42, 45, 46, 46, 47, 48, 48, 49, 51, 51, 51, 53, 54, 54, 56, + 36, 35, 35, 35, 35, 34, 34, 35, 36, 36, 36, 37, 38, 38, 40, 42, 42, 43, 47, 48, 48, 49, 50, 50, 51, 53, 53, 54, 56, 56, 56, 58, + 44, 43, 42, 42, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 44, 48, 48, 49, 52, 54, 54, 56, 58, 58, 60, 63, 63, 64, 66, 67, 67, 69, + 47, 46, 45, 45, 45, 44, 44, 44, 44, 45, 45, 45, 45, 45, 47, 50, 50, 51, 55, 56, 56, 58, 60, 60, 62, 66, 66, 67, 69, 70, 70, 73, + }, { + 32, 31, 31, 31, 31, 30, 30, 31, 33, 33, 33, 35, 37, 37, 39, 42, 42, 43, 47, 49, 49, 48, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, + 31, 31, 31, 31, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, 43, 43, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 46, 46, 46, 47, + 33, 33, 34, 34, 34, 35, 35, 35, 37, 38, 38, 41, 43, 43, 43, 44, 44, 45, 46, 47, 47, 46, 46, 46, 46, 45, 45, 45, 46, 46, 46, 47, + 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 47, + 45, 45, 45, 45, 45, 44, 44, 45, 46, 46, 46, 47, 47, 47, 48, 49, 49, 50, 51, 52, 52, 52, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, + 48, 47, 47, 47, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 50, 50, 50, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, + 49, 48, 47, 47, 46, 45, 45, 45, 45, 46, 46, 45, 45, 45, 47, 49, 49, 50, 53, 53, 53, 55, 56, 56, 57, 58, 58, 58, 59, 59, 59, 60, + 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 50, 53, 54, 54, 55, 56, 56, 57, 59, 59, 60, 61, 61, 61, 62, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 37, 39, 39, 39, 41, 44, 44, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, + 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 53, 53, + 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 53, 53, + 44, 43, 42, 42, 42, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 45, 48, 48, 48, 50, 54, 54, 54, 56, 58, 58, 58, 60, 63, 63, + }, { + 32, 31, 31, 31, 31, 31, 30, 30, 30, 32, 33, 33, 33, 35, 37, 37, 37, 39, 42, 42, 42, 45, 49, 49, 49, 48, 48, 48, 48, 48, 49, 49, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 36, 36, 38, 40, 40, 40, 41, 43, 43, 43, 44, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 36, 36, 38, 40, 40, 40, 41, 43, 43, 43, 44, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, + 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 46, 46, 46, + 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 46, 46, 46, + 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 46, 46, 46, 45, 45, 45, 45, 47, 49, 49, 49, 51, 53, 53, 53, 54, 56, 56, 56, 57, 58, 58, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 37, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 36, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 36, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, 36, 36, 37, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, + 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 39, 40, 41, 41, 41, 43, 45, 46, 46, 46, 46, + 36, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 41, 42, 42, 42, 44, 47, 48, 48, 48, 49, + }, { + 32, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 33, 33, 33, 33, 35, 36, 37, 37, 37, 39, 41, 42, 42, 42, 44, 47, 49, 49, 49, 49, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 37, 38, 39, 39, 39, 40, 42, 42, 42, 42, 44, 46, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 37, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 46, 46, 46, 46, + 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 37, 38, 38, 38, 40, 42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 46, 47, 47, 47, 47, + 37, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 47, + 37, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 47, + 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 45, 46, 46, 46, 46, 46, 47, 47, 47, 47, 48, 49, 49, 49, 49, 50, 51, 52, 52, 52, 52, + 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, 50, 50, 51, 52, 53, 53, 53, 53, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, + 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 38, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 31, 32, 33, 33, 33, 33, 33, 34, 35, 36, 37, 37, 37, 37, 39, 40, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 39, 40, 40, 40, 40, 41, 42, 43, 43, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 39, 40, 40, 40, 40, 41, 42, 43, 43, + 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 37, 38, 38, 38, 38, 39, 41, 42, 43, 43, 43, 43, 43, 44, 44, 44, + 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 38, 39, 39, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 43, 44, 44, 44, 44, 45, 46, 47, 47, 47, 47, 47, 47, 47, 48, 48, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 31, 31, 32, 33, 33, 33, 33, 33, 33, 33, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, + 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 38, 38, 38, 38, 38, 39, + 35, 35, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 39, 40, 40, 41, 41, 41, 41, 41, 41, 42, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + }, + }, +}; + +static const uint8_t qm_tbl_32x16[][2][512] = { + { + { + 32, 31, 31, 31, 32, 32, 34, 35, 36, 39, 44, 46, 48, 53, 58, 61, 65, 71, 79, 81, 82, 88, 91, 94, 97, 100, 103, 107, 110, 114, 118, 122, + 31, 32, 32, 32, 32, 33, 34, 34, 34, 37, 41, 43, 45, 49, 54, 57, 60, 65, 72, 74, 75, 80, 83, 85, 88, 91, 94, 97, 101, 104, 108, 111, + 32, 32, 33, 33, 34, 35, 37, 37, 38, 40, 43, 44, 46, 50, 54, 56, 58, 63, 70, 71, 72, 77, 80, 83, 86, 89, 93, 96, 100, 104, 107, 111, + 34, 34, 33, 34, 35, 37, 39, 41, 43, 45, 48, 49, 51, 54, 58, 60, 63, 68, 74, 75, 76, 80, 81, 82, 85, 87, 90, 93, 97, 100, 103, 107, + 36, 35, 34, 35, 36, 38, 42, 45, 48, 50, 53, 55, 56, 60, 63, 66, 68, 73, 79, 80, 81, 85, 88, 91, 94, 97, 98, 100, 101, 103, 105, 107, + 44, 42, 41, 41, 42, 42, 48, 50, 54, 58, 63, 65, 67, 71, 75, 77, 79, 84, 90, 91, 92, 97, 100, 100, 100, 100, 101, 104, 108, 112, 115, 119, + 53, 51, 49, 49, 50, 49, 54, 57, 60, 65, 71, 73, 76, 82, 87, 89, 92, 97, 104, 105, 106, 108, 106, 105, 107, 111, 114, 117, 117, 117, 118, 119, + 59, 56, 54, 54, 54, 53, 58, 61, 64, 69, 75, 78, 80, 87, 92, 95, 98, 103, 110, 112, 113, 115, 114, 118, 123, 121, 120, 119, 123, 127, 131, 136, + 65, 62, 59, 59, 59, 58, 63, 65, 68, 73, 79, 82, 85, 92, 98, 101, 105, 111, 118, 119, 121, 126, 130, 131, 128, 127, 131, 136, 138, 137, 136, 136, + 79, 75, 72, 71, 71, 69, 73, 76, 78, 84, 90, 93, 96, 103, 110, 114, 118, 125, 133, 135, 136, 142, 142, 137, 140, 145, 144, 142, 141, 146, 151, 156, + 87, 82, 78, 78, 77, 75, 79, 82, 84, 89, 95, 98, 102, 109, 116, 120, 124, 132, 141, 142, 144, 149, 148, 153, 157, 152, 150, 155, 161, 159, 157, 156, + 90, 85, 82, 81, 80, 78, 78, 83, 87, 89, 93, 100, 102, 107, 115, 118, 123, 132, 136, 140, 151, 153, 155, 160, 161, 164, 170, 168, 165, 167, 172, 178, + 93, 88, 86, 84, 82, 82, 80, 84, 86, 91, 94, 98, 105, 107, 112, 119, 122, 130, 135, 140, 149, 153, 162, 165, 167, 173, 174, 177, 183, 185, 182, 179, + 96, 91, 90, 87, 86, 86, 83, 84, 89, 91, 95, 100, 102, 110, 111, 118, 123, 128, 135, 138, 149, 152, 160, 167, 173, 178, 180, 187, 188, 190, 197, 203, + 99, 94, 93, 90, 89, 89, 88, 87, 90, 93, 97, 99, 105, 107, 115, 116, 124, 127, 135, 139, 146, 152, 159, 166, 171, 182, 186, 191, 193, 201, 203, 204, + 102, 97, 97, 93, 93, 92, 92, 90, 90, 96, 97, 103, 104, 111, 112, 120, 121, 130, 131, 142, 143, 154, 155, 168, 169, 181, 183, 198, 200, 206, 208, 217, + }, { + 32, 31, 30, 32, 33, 37, 42, 45, 49, 48, 49, 49, 50, 52, 54, 55, 57, 60, 63, 64, 64, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, + 31, 31, 32, 34, 36, 40, 43, 44, 46, 46, 45, 46, 46, 48, 50, 51, 52, 54, 57, 58, 59, 61, 62, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 37, 38, 40, 41, 43, 47, 47, 47, 48, 47, 46, 46, 46, 47, 49, 49, 50, 52, 55, 55, 56, 58, 59, 60, 62, 63, 64, 65, 67, 68, 69, 70, + 42, 42, 42, 44, 45, 47, 48, 49, 50, 50, 49, 49, 50, 50, 52, 52, 53, 55, 58, 58, 58, 60, 60, 60, 60, 61, 62, 63, 64, 65, 66, 67, + 48, 47, 46, 46, 47, 47, 50, 51, 53, 53, 53, 53, 54, 54, 55, 56, 56, 58, 60, 61, 61, 63, 64, 65, 66, 67, 66, 66, 66, 66, 67, 67, + 49, 47, 45, 45, 46, 45, 49, 51, 53, 56, 58, 59, 59, 61, 62, 63, 64, 65, 67, 68, 68, 69, 71, 70, 69, 68, 68, 69, 70, 71, 72, 73, + 52, 50, 48, 48, 47, 47, 50, 52, 54, 57, 61, 62, 64, 66, 68, 69, 70, 72, 75, 75, 75, 76, 74, 72, 73, 74, 75, 75, 74, 74, 73, 73, + 54, 52, 50, 49, 49, 48, 52, 54, 55, 59, 62, 64, 65, 68, 71, 72, 73, 75, 78, 78, 79, 79, 78, 79, 81, 79, 78, 76, 77, 78, 80, 81, + 57, 54, 52, 51, 51, 50, 53, 55, 57, 60, 64, 65, 67, 71, 73, 75, 76, 79, 82, 82, 83, 85, 86, 85, 83, 82, 83, 84, 84, 83, 82, 81, + 63, 60, 57, 57, 56, 54, 57, 59, 60, 64, 67, 69, 71, 75, 78, 80, 82, 85, 89, 89, 90, 92, 91, 88, 89, 90, 89, 87, 86, 87, 88, 90, + 66, 63, 60, 59, 59, 57, 60, 61, 62, 66, 69, 71, 73, 77, 80, 82, 84, 88, 92, 92, 93, 95, 94, 95, 96, 93, 92, 93, 94, 93, 91, 90, + 67, 64, 62, 61, 60, 58, 58, 61, 63, 65, 67, 70, 72, 74, 78, 80, 82, 86, 88, 90, 95, 96, 96, 98, 97, 98, 100, 98, 96, 96, 97, 99, + 68, 65, 63, 62, 60, 60, 59, 61, 62, 65, 66, 68, 72, 73, 76, 79, 80, 84, 87, 89, 93, 94, 98, 99, 99, 102, 101, 102, 103, 103, 101, 99, + 69, 66, 65, 63, 62, 61, 60, 60, 63, 64, 66, 68, 70, 73, 74, 78, 80, 82, 85, 87, 91, 92, 96, 98, 101, 102, 103, 105, 105, 105, 107, 108, + 71, 67, 66, 64, 63, 62, 62, 61, 62, 64, 66, 67, 70, 71, 75, 76, 79, 81, 84, 86, 89, 91, 94, 97, 98, 102, 104, 106, 106, 109, 109, 108, + 72, 68, 68, 65, 65, 63, 63, 61, 62, 65, 65, 68, 69, 72, 73, 77, 77, 81, 81, 86, 87, 91, 91, 96, 97, 101, 102, 107, 107, 109, 110, 113, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 32, 34, 36, 38, 39, 44, 47, 49, 53, 58, 61, 65, 71, 76, 79, 82, 86, 89, 92, 95, 98, 101, 104, 107, 110, 114, + 31, 32, 32, 32, 32, 33, 33, 34, 34, 36, 37, 41, 44, 46, 49, 54, 56, 60, 65, 69, 72, 75, 78, 81, 84, 86, 89, 92, 95, 98, 101, 104, + 32, 32, 32, 33, 34, 35, 35, 36, 37, 39, 40, 42, 45, 47, 50, 54, 56, 59, 64, 68, 70, 73, 76, 79, 82, 85, 88, 91, 94, 97, 100, 104, + 32, 33, 33, 33, 34, 36, 36, 38, 40, 41, 42, 45, 47, 48, 51, 55, 57, 60, 65, 69, 71, 74, 77, 78, 80, 83, 85, 88, 91, 94, 97, 100, + 36, 35, 35, 35, 36, 38, 40, 42, 48, 49, 50, 53, 56, 57, 60, 63, 65, 68, 73, 76, 79, 81, 84, 87, 89, 92, 93, 94, 95, 96, 98, 100, + 44, 42, 41, 41, 42, 42, 44, 48, 54, 56, 58, 63, 66, 67, 71, 75, 77, 79, 84, 88, 90, 92, 95, 95, 95, 95, 95, 98, 101, 105, 108, 111, + 47, 45, 44, 44, 45, 45, 47, 50, 56, 58, 60, 66, 69, 71, 75, 79, 81, 84, 89, 92, 95, 97, 100, 99, 101, 105, 108, 110, 110, 110, 111, 111, + 53, 51, 49, 49, 50, 49, 51, 54, 60, 63, 65, 71, 75, 77, 82, 87, 89, 92, 97, 101, 104, 106, 109, 112, 116, 114, 113, 112, 115, 119, 123, 126, + 65, 62, 60, 59, 59, 58, 60, 63, 68, 71, 73, 79, 84, 86, 92, 98, 100, 105, 111, 115, 118, 121, 124, 124, 121, 120, 124, 128, 129, 128, 127, 127, + 73, 69, 67, 66, 65, 64, 66, 69, 74, 77, 79, 85, 90, 93, 99, 105, 107, 112, 119, 123, 127, 130, 133, 130, 132, 136, 136, 133, 132, 136, 141, 145, + 79, 75, 72, 71, 71, 69, 71, 73, 78, 81, 84, 90, 95, 97, 103, 110, 113, 118, 125, 130, 133, 136, 140, 145, 148, 143, 141, 146, 151, 149, 147, 145, + 87, 83, 80, 79, 78, 76, 76, 80, 84, 86, 90, 96, 99, 103, 111, 114, 118, 126, 130, 134, 143, 146, 147, 152, 151, 155, 160, 158, 154, 156, 161, 166, + 90, 86, 84, 82, 80, 80, 78, 82, 83, 88, 91, 94, 101, 103, 108, 114, 116, 124, 129, 134, 142, 145, 153, 156, 157, 163, 163, 166, 171, 173, 169, 166, + 93, 88, 87, 84, 83, 83, 81, 81, 86, 88, 92, 96, 98, 105, 107, 113, 117, 122, 129, 131, 141, 144, 151, 157, 163, 167, 169, 175, 175, 177, 183, 189, + 96, 91, 90, 87, 87, 86, 85, 84, 87, 90, 94, 96, 101, 102, 110, 111, 118, 121, 129, 132, 138, 144, 150, 156, 161, 171, 174, 179, 181, 188, 188, 190, + 99, 94, 94, 90, 90, 88, 89, 86, 87, 93, 93, 99, 99, 106, 107, 115, 116, 124, 125, 135, 136, 145, 146, 158, 159, 170, 171, 185, 186, 192, 193, 201, + }, { + 32, 31, 30, 31, 33, 37, 39, 42, 49, 48, 48, 49, 50, 51, 52, 54, 55, 57, 60, 62, 63, 64, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 31, 31, 32, 33, 36, 40, 41, 43, 46, 46, 46, 45, 46, 47, 48, 50, 51, 52, 54, 56, 57, 59, 60, 61, 62, 63, 64, 65, 65, 66, 67, 68, + 35, 37, 38, 38, 41, 45, 46, 46, 48, 47, 46, 45, 46, 47, 47, 49, 49, 50, 53, 54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, + 38, 40, 40, 41, 44, 47, 47, 48, 49, 48, 48, 47, 48, 48, 48, 50, 50, 51, 53, 55, 56, 57, 58, 58, 59, 60, 60, 61, 62, 63, 64, 65, + 48, 47, 46, 46, 47, 47, 48, 50, 53, 53, 53, 53, 54, 54, 54, 55, 56, 56, 58, 60, 60, 61, 62, 63, 64, 65, 65, 65, 65, 65, 65, 65, + 49, 47, 45, 45, 46, 45, 47, 49, 53, 55, 56, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 68, 67, 66, 66, 67, 68, 69, 70, 71, + 50, 48, 46, 46, 46, 46, 47, 50, 54, 55, 56, 59, 61, 61, 63, 65, 65, 66, 68, 69, 70, 71, 72, 71, 71, 72, 73, 73, 72, 72, 71, 71, + 52, 50, 48, 48, 47, 47, 48, 50, 54, 56, 57, 61, 63, 64, 66, 68, 69, 70, 72, 74, 75, 75, 76, 78, 79, 77, 76, 74, 75, 76, 77, 78, + 57, 54, 52, 52, 51, 50, 51, 53, 57, 58, 60, 64, 66, 68, 71, 73, 74, 76, 79, 81, 82, 83, 84, 83, 81, 80, 81, 82, 82, 81, 79, 78, + 61, 57, 55, 55, 54, 52, 54, 56, 59, 61, 62, 66, 68, 70, 73, 76, 77, 79, 82, 84, 86, 87, 88, 86, 86, 88, 87, 85, 83, 85, 86, 87, + 63, 60, 58, 57, 56, 54, 55, 57, 60, 62, 64, 67, 70, 71, 75, 78, 79, 82, 85, 87, 89, 90, 91, 93, 94, 91, 89, 90, 92, 90, 89, 87, + 67, 63, 61, 60, 59, 57, 57, 60, 63, 64, 66, 69, 71, 73, 77, 79, 81, 85, 87, 88, 92, 93, 94, 96, 95, 96, 97, 95, 93, 93, 94, 96, + 68, 64, 63, 61, 60, 59, 58, 60, 61, 64, 65, 67, 71, 72, 75, 78, 79, 83, 85, 87, 91, 92, 95, 96, 97, 99, 98, 99, 100, 100, 98, 96, + 69, 65, 64, 62, 61, 61, 59, 59, 62, 63, 65, 67, 68, 72, 73, 76, 78, 81, 84, 85, 89, 90, 93, 96, 98, 99, 100, 102, 102, 102, 103, 105, + 70, 66, 65, 63, 63, 62, 61, 60, 61, 63, 65, 66, 69, 70, 74, 74, 78, 79, 82, 84, 87, 89, 91, 94, 96, 100, 101, 103, 103, 105, 105, 105, + 71, 67, 67, 64, 64, 62, 62, 60, 61, 64, 64, 67, 67, 71, 71, 75, 75, 79, 80, 84, 84, 89, 89, 94, 94, 98, 99, 104, 104, 106, 106, 109, + }, + }, { + { + 32, 31, 31, 31, 32, 32, 32, 34, 34, 36, 39, 40, 44, 47, 49, 53, 57, 59, 65, 69, 71, 79, 81, 82, 87, 90, 92, 95, 98, 100, 103, 106, + 31, 32, 32, 32, 32, 32, 33, 34, 34, 34, 37, 38, 41, 44, 46, 49, 53, 54, 60, 63, 65, 72, 74, 75, 79, 82, 84, 87, 89, 92, 94, 97, + 32, 32, 32, 32, 33, 34, 34, 35, 36, 37, 39, 40, 42, 45, 46, 50, 53, 54, 59, 62, 64, 71, 72, 73, 77, 80, 83, 85, 88, 91, 94, 97, + 32, 32, 32, 33, 34, 34, 35, 37, 37, 38, 40, 41, 43, 46, 47, 50, 53, 54, 58, 62, 63, 70, 71, 72, 76, 78, 81, 83, 85, 88, 90, 93, + 36, 35, 35, 34, 36, 37, 38, 42, 44, 48, 50, 51, 53, 56, 57, 60, 63, 64, 68, 71, 73, 79, 80, 81, 85, 87, 88, 88, 89, 90, 92, 93, + 39, 38, 38, 37, 39, 40, 40, 45, 47, 51, 54, 55, 58, 61, 62, 65, 68, 69, 73, 76, 78, 84, 85, 86, 90, 89, 90, 92, 95, 98, 101, 104, + 44, 42, 41, 41, 42, 42, 42, 48, 50, 54, 58, 59, 63, 66, 67, 71, 74, 75, 79, 83, 84, 90, 91, 92, 96, 99, 102, 103, 103, 103, 103, 104, + 53, 51, 50, 49, 50, 49, 49, 54, 56, 60, 65, 67, 71, 75, 77, 82, 86, 87, 92, 96, 97, 104, 105, 106, 110, 108, 106, 105, 108, 111, 114, 118, + 58, 55, 54, 53, 53, 53, 52, 57, 59, 63, 68, 70, 74, 79, 81, 86, 90, 91, 97, 100, 102, 109, 110, 111, 114, 113, 117, 120, 121, 120, 119, 118, + 65, 62, 60, 59, 59, 58, 58, 63, 65, 68, 73, 75, 79, 85, 86, 92, 97, 98, 105, 109, 111, 118, 120, 121, 125, 129, 128, 125, 124, 127, 131, 135, + 79, 75, 73, 72, 71, 70, 69, 73, 75, 78, 84, 85, 90, 95, 97, 103, 108, 111, 118, 122, 125, 133, 135, 136, 140, 135, 133, 137, 141, 139, 137, 135, + 81, 77, 75, 74, 72, 71, 70, 75, 77, 80, 85, 87, 91, 97, 99, 105, 110, 112, 119, 124, 127, 135, 137, 139, 143, 146, 150, 148, 144, 146, 150, 154, + 88, 83, 81, 79, 78, 77, 76, 79, 81, 85, 88, 91, 97, 99, 104, 109, 111, 119, 123, 127, 135, 137, 145, 147, 148, 153, 153, 155, 160, 161, 158, 155, + 90, 86, 84, 82, 81, 80, 78, 79, 83, 85, 89, 92, 94, 101, 102, 108, 112, 117, 123, 125, 134, 136, 143, 148, 154, 157, 158, 164, 164, 165, 170, 175, + 93, 88, 88, 84, 84, 83, 82, 81, 84, 86, 90, 92, 97, 98, 105, 106, 113, 115, 122, 125, 131, 136, 141, 147, 151, 160, 163, 168, 169, 175, 175, 176, + 96, 91, 91, 87, 87, 85, 86, 83, 84, 89, 89, 95, 95, 102, 102, 110, 110, 118, 119, 128, 129, 137, 138, 149, 149, 159, 160, 173, 174, 179, 180, 187, + }, { + 32, 31, 31, 30, 33, 35, 37, 42, 44, 49, 48, 48, 49, 50, 51, 52, 54, 54, 57, 59, 60, 63, 64, 64, 66, 67, 68, 69, 70, 71, 72, 73, + 31, 31, 32, 32, 36, 38, 40, 43, 44, 46, 46, 45, 45, 46, 47, 48, 49, 50, 52, 54, 54, 57, 58, 59, 60, 61, 62, 63, 64, 65, 65, 66, + 34, 35, 36, 36, 40, 42, 44, 45, 46, 47, 46, 46, 45, 46, 47, 47, 49, 49, 51, 52, 53, 56, 57, 57, 59, 60, 61, 62, 63, 64, 65, 66, + 37, 38, 39, 40, 43, 45, 47, 47, 47, 48, 47, 46, 46, 46, 47, 47, 48, 49, 50, 52, 52, 55, 55, 56, 57, 58, 59, 60, 60, 61, 62, 63, + 48, 47, 46, 46, 47, 47, 47, 50, 51, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 58, 58, 60, 61, 61, 63, 63, 63, 63, 63, 63, 63, 63, + 48, 47, 46, 45, 46, 46, 46, 50, 51, 53, 54, 55, 56, 56, 57, 57, 58, 59, 60, 61, 62, 64, 64, 65, 66, 65, 64, 65, 66, 67, 68, 69, + 49, 47, 46, 45, 46, 45, 45, 49, 51, 53, 56, 56, 58, 59, 60, 61, 62, 62, 64, 65, 65, 67, 68, 68, 69, 70, 71, 71, 70, 70, 69, 69, + 52, 50, 48, 48, 47, 47, 47, 50, 52, 54, 57, 58, 61, 63, 64, 66, 68, 68, 70, 72, 72, 75, 75, 75, 77, 75, 74, 72, 73, 74, 75, 76, + 54, 51, 50, 49, 49, 48, 48, 51, 53, 55, 58, 59, 62, 65, 65, 68, 70, 70, 73, 74, 75, 77, 78, 78, 79, 78, 79, 80, 80, 78, 77, 76, + 57, 54, 53, 52, 51, 50, 50, 53, 54, 57, 60, 61, 64, 66, 68, 71, 73, 74, 76, 78, 79, 82, 82, 83, 84, 85, 84, 82, 81, 82, 83, 84, + 63, 60, 58, 57, 56, 55, 54, 57, 59, 60, 64, 65, 67, 70, 71, 75, 77, 78, 82, 84, 85, 89, 89, 90, 91, 88, 87, 88, 89, 88, 86, 84, + 64, 61, 59, 58, 57, 56, 55, 58, 59, 61, 64, 65, 68, 71, 72, 75, 78, 79, 82, 85, 86, 90, 90, 91, 93, 93, 94, 93, 90, 90, 92, 93, + 67, 63, 62, 60, 59, 58, 57, 59, 60, 63, 64, 66, 70, 70, 73, 76, 77, 81, 83, 85, 89, 90, 93, 94, 94, 96, 96, 96, 97, 97, 95, 93, + 68, 64, 63, 61, 60, 60, 58, 58, 61, 62, 64, 66, 67, 71, 71, 75, 77, 79, 82, 83, 87, 88, 91, 93, 95, 97, 97, 99, 99, 99, 100, 101, + 69, 65, 65, 62, 62, 61, 60, 59, 61, 62, 64, 65, 68, 68, 72, 73, 76, 77, 81, 82, 85, 87, 89, 92, 93, 97, 98, 100, 100, 102, 102, 101, + 69, 66, 66, 63, 63, 61, 61, 59, 60, 63, 63, 66, 66, 70, 70, 73, 74, 78, 78, 82, 82, 86, 87, 91, 91, 95, 96, 101, 101, 103, 103, 105, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 34, 35, 36, 39, 41, 44, 47, 48, 53, 55, 58, 63, 65, 71, 74, 79, 82, 82, 87, 89, 92, 94, 97, 99, + 31, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 37, 39, 41, 44, 45, 49, 51, 54, 58, 60, 65, 68, 72, 75, 75, 79, 82, 84, 86, 88, 91, + 31, 32, 32, 32, 33, 33, 34, 34, 35, 36, 36, 39, 40, 42, 44, 45, 50, 51, 54, 58, 59, 64, 67, 71, 73, 74, 78, 81, 83, 85, 88, 91, + 32, 32, 32, 33, 34, 34, 35, 36, 37, 38, 38, 40, 41, 43, 45, 46, 50, 51, 54, 57, 58, 63, 66, 70, 72, 72, 76, 78, 80, 82, 85, 87, + 35, 35, 34, 34, 35, 36, 37, 39, 41, 45, 46, 48, 49, 51, 53, 54, 57, 59, 61, 65, 66, 71, 73, 77, 79, 79, 83, 83, 84, 85, 86, 87, + 36, 35, 35, 34, 36, 36, 38, 40, 42, 47, 48, 50, 51, 53, 56, 56, 60, 61, 63, 67, 68, 73, 75, 79, 81, 81, 85, 87, 89, 92, 94, 97, + 44, 42, 41, 41, 42, 42, 42, 44, 48, 52, 54, 58, 60, 63, 66, 67, 71, 72, 75, 78, 79, 84, 86, 90, 92, 92, 96, 97, 97, 97, 97, 97, + 47, 45, 45, 44, 44, 45, 45, 47, 50, 55, 56, 60, 62, 66, 69, 70, 75, 77, 79, 83, 84, 89, 91, 95, 97, 97, 100, 99, 101, 104, 107, 110, + 53, 51, 50, 49, 49, 50, 49, 51, 54, 59, 60, 65, 67, 71, 75, 76, 82, 84, 87, 91, 92, 97, 100, 104, 105, 106, 110, 113, 114, 112, 111, 110, + 62, 59, 58, 57, 57, 57, 56, 58, 61, 65, 66, 71, 74, 78, 82, 83, 90, 92, 95, 100, 102, 108, 110, 115, 117, 117, 120, 118, 116, 119, 123, 126, + 65, 62, 61, 59, 59, 59, 58, 60, 63, 67, 68, 73, 76, 79, 84, 85, 92, 94, 98, 103, 105, 111, 113, 118, 120, 121, 125, 128, 132, 130, 128, 126, + 79, 75, 74, 72, 71, 71, 69, 71, 73, 77, 78, 84, 86, 90, 95, 96, 103, 106, 110, 116, 118, 125, 128, 133, 136, 136, 141, 139, 135, 136, 140, 144, + 82, 78, 76, 74, 73, 73, 71, 73, 76, 79, 80, 86, 88, 92, 97, 98, 106, 108, 112, 118, 120, 127, 131, 136, 139, 139, 144, 145, 150, 151, 147, 144, + 88, 83, 82, 79, 79, 78, 76, 76, 81, 82, 85, 89, 91, 97, 98, 104, 107, 111, 117, 119, 127, 129, 135, 140, 145, 148, 148, 153, 153, 154, 159, 163, + 90, 86, 85, 82, 81, 80, 79, 78, 81, 83, 87, 88, 93, 94, 101, 101, 108, 110, 116, 119, 124, 129, 134, 139, 142, 150, 153, 157, 157, 163, 163, 163, + 93, 88, 88, 84, 84, 82, 83, 80, 80, 86, 86, 91, 91, 97, 98, 105, 105, 112, 113, 121, 122, 130, 130, 140, 140, 149, 150, 161, 162, 166, 167, 173, + }, { + 32, 31, 31, 30, 33, 33, 37, 39, 42, 47, 49, 48, 48, 49, 50, 50, 52, 53, 54, 56, 57, 60, 61, 63, 64, 64, 66, 67, 68, 69, 70, 70, + 31, 31, 32, 32, 35, 36, 40, 41, 43, 46, 46, 46, 45, 45, 46, 46, 48, 49, 50, 51, 52, 54, 56, 57, 58, 59, 60, 61, 62, 63, 63, 64, + 33, 34, 34, 35, 37, 38, 43, 43, 44, 46, 47, 46, 46, 45, 46, 46, 47, 48, 49, 51, 51, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 37, 38, 39, 40, 42, 43, 47, 47, 47, 48, 48, 47, 46, 46, 46, 46, 47, 48, 49, 50, 50, 52, 53, 55, 56, 56, 57, 58, 59, 59, 60, 61, + 45, 45, 45, 44, 46, 46, 47, 48, 49, 51, 52, 51, 51, 51, 52, 52, 53, 53, 54, 55, 55, 57, 58, 59, 60, 60, 61, 61, 61, 61, 61, 61, + 48, 47, 46, 46, 47, 47, 47, 48, 50, 52, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 56, 58, 59, 60, 61, 61, 63, 63, 64, 65, 66, 67, + 49, 47, 46, 45, 45, 46, 45, 47, 49, 53, 53, 56, 57, 58, 59, 59, 61, 61, 62, 63, 64, 65, 66, 67, 68, 68, 69, 69, 68, 68, 67, 67, + 50, 48, 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 57, 59, 61, 61, 63, 64, 65, 66, 66, 68, 69, 70, 71, 71, 72, 70, 71, 72, 73, 74, + 52, 50, 49, 48, 47, 47, 47, 48, 50, 53, 54, 57, 59, 61, 63, 64, 66, 67, 68, 70, 70, 72, 73, 75, 75, 75, 77, 78, 77, 76, 75, 74, + 56, 53, 52, 51, 50, 50, 49, 50, 53, 55, 56, 59, 61, 63, 65, 66, 70, 71, 72, 74, 75, 77, 79, 80, 81, 81, 82, 80, 79, 80, 81, 82, + 57, 54, 53, 52, 51, 51, 50, 51, 53, 56, 57, 60, 61, 64, 66, 67, 71, 72, 73, 76, 76, 79, 80, 82, 83, 83, 84, 85, 86, 85, 83, 82, + 63, 60, 59, 57, 56, 56, 54, 55, 57, 60, 60, 64, 65, 67, 70, 71, 75, 76, 78, 81, 82, 85, 86, 89, 90, 90, 92, 90, 88, 88, 89, 90, + 64, 61, 60, 58, 57, 57, 55, 56, 58, 61, 61, 64, 66, 68, 71, 71, 75, 77, 79, 82, 83, 86, 87, 90, 91, 91, 93, 93, 94, 94, 92, 90, + 67, 63, 62, 60, 60, 59, 57, 57, 60, 61, 63, 65, 66, 70, 70, 73, 75, 77, 80, 81, 85, 86, 89, 91, 93, 94, 94, 96, 96, 95, 97, 98, + 68, 64, 64, 61, 61, 60, 59, 58, 60, 61, 63, 64, 67, 67, 71, 71, 74, 75, 79, 80, 83, 85, 87, 89, 91, 94, 95, 97, 97, 99, 98, 98, + 68, 65, 65, 62, 62, 60, 61, 59, 59, 62, 62, 65, 65, 68, 68, 72, 72, 76, 76, 80, 80, 84, 84, 89, 89, 93, 93, 97, 98, 99, 99, 102, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 33, 34, 36, 36, 39, 40, 44, 46, 48, 52, 53, 58, 58, 65, 66, 71, 74, 79, 81, 82, 86, 88, 91, 93, + 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 37, 38, 41, 43, 45, 48, 49, 53, 54, 60, 61, 65, 68, 72, 74, 75, 78, 81, 83, 85, + 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 38, 39, 41, 43, 45, 48, 49, 53, 54, 59, 60, 65, 67, 72, 73, 74, 78, 80, 82, 85, + 32, 32, 32, 33, 33, 34, 35, 35, 36, 37, 38, 38, 40, 41, 43, 44, 46, 49, 50, 53, 54, 58, 59, 63, 66, 70, 71, 72, 75, 77, 79, 81, + 33, 33, 33, 33, 34, 35, 36, 36, 38, 39, 42, 42, 44, 45, 46, 48, 50, 52, 53, 57, 57, 62, 63, 67, 69, 73, 75, 75, 78, 80, 80, 81, + 36, 35, 35, 34, 35, 36, 37, 38, 41, 42, 48, 48, 50, 51, 53, 55, 56, 59, 60, 63, 63, 68, 69, 73, 75, 79, 80, 81, 84, 86, 88, 90, + 40, 39, 39, 38, 38, 39, 40, 41, 44, 45, 51, 51, 54, 56, 59, 60, 62, 65, 66, 69, 70, 74, 75, 79, 81, 85, 86, 87, 90, 90, 90, 90, + 44, 42, 42, 41, 41, 42, 42, 42, 46, 48, 54, 54, 58, 59, 63, 65, 67, 70, 71, 74, 75, 79, 80, 84, 86, 90, 91, 92, 95, 98, 100, 102, + 51, 49, 49, 47, 47, 48, 48, 48, 52, 53, 58, 59, 63, 65, 69, 72, 74, 78, 79, 83, 84, 89, 90, 94, 97, 101, 102, 103, 106, 105, 103, 103, + 53, 51, 51, 49, 49, 50, 49, 49, 53, 54, 60, 60, 65, 67, 71, 73, 76, 80, 82, 86, 87, 92, 93, 97, 100, 104, 105, 106, 109, 112, 114, 117, + 65, 62, 61, 59, 59, 59, 58, 58, 62, 63, 68, 68, 73, 75, 79, 82, 85, 90, 92, 97, 98, 105, 106, 111, 113, 118, 120, 121, 124, 122, 119, 117, + 66, 63, 62, 60, 60, 60, 59, 59, 63, 64, 69, 69, 74, 76, 80, 83, 86, 91, 93, 98, 99, 106, 107, 112, 115, 119, 121, 122, 125, 127, 130, 134, + 79, 75, 74, 72, 71, 71, 69, 69, 72, 73, 78, 79, 84, 85, 90, 93, 96, 101, 103, 109, 110, 118, 119, 125, 128, 133, 135, 136, 140, 140, 137, 134, + 81, 77, 76, 74, 73, 72, 71, 70, 74, 75, 80, 80, 85, 87, 91, 94, 98, 103, 105, 111, 112, 119, 121, 127, 130, 135, 137, 139, 142, 144, 148, 151, + 87, 83, 82, 79, 79, 78, 77, 75, 78, 80, 84, 85, 89, 90, 96, 97, 103, 105, 111, 113, 118, 122, 126, 131, 134, 141, 143, 147, 147, 152, 151, 152, + 90, 85, 85, 81, 81, 80, 80, 77, 78, 83, 83, 87, 88, 93, 93, 100, 100, 107, 107, 115, 115, 123, 123, 132, 132, 140, 140, 151, 151, 155, 155, 160, + }, { + 32, 31, 31, 30, 31, 33, 35, 37, 41, 42, 49, 49, 48, 48, 49, 49, 50, 51, 52, 54, 54, 57, 57, 60, 61, 63, 64, 64, 66, 67, 68, 68, + 31, 31, 31, 32, 33, 36, 38, 40, 42, 43, 46, 46, 46, 45, 45, 46, 46, 47, 48, 50, 50, 52, 52, 54, 56, 57, 58, 59, 60, 61, 62, 62, + 32, 33, 33, 33, 35, 37, 39, 41, 43, 43, 47, 47, 46, 46, 45, 46, 46, 47, 48, 49, 50, 52, 52, 54, 55, 57, 58, 58, 59, 60, 61, 62, + 37, 38, 38, 40, 41, 43, 45, 47, 47, 47, 48, 48, 47, 46, 46, 46, 46, 47, 47, 48, 49, 50, 51, 52, 53, 55, 55, 56, 57, 58, 58, 59, + 40, 41, 41, 42, 43, 44, 46, 47, 48, 48, 50, 50, 49, 49, 48, 49, 49, 49, 50, 51, 51, 52, 53, 55, 56, 57, 58, 58, 59, 59, 59, 59, + 48, 47, 47, 46, 46, 47, 47, 47, 49, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 57, 58, 59, 60, 61, 61, 62, 63, 64, 65, + 49, 47, 47, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 55, 56, 57, 57, 58, 58, 59, 59, 60, 61, 62, 63, 64, 65, 65, 66, 66, 65, 65, + 49, 47, 47, 45, 45, 46, 45, 45, 48, 49, 53, 54, 56, 56, 58, 59, 59, 61, 61, 62, 62, 64, 64, 65, 66, 67, 68, 68, 69, 70, 71, 71, + 51, 49, 49, 47, 47, 47, 47, 46, 49, 50, 54, 54, 57, 58, 61, 62, 63, 64, 65, 67, 67, 69, 69, 71, 72, 73, 73, 74, 75, 74, 72, 71, + 52, 50, 49, 48, 48, 47, 47, 47, 50, 50, 54, 55, 57, 58, 61, 62, 64, 66, 66, 68, 68, 70, 71, 72, 73, 75, 75, 75, 76, 77, 78, 79, + 57, 54, 54, 52, 51, 51, 50, 50, 52, 53, 57, 57, 60, 61, 64, 65, 67, 69, 71, 73, 73, 76, 77, 79, 80, 82, 82, 83, 84, 82, 81, 79, + 58, 55, 54, 52, 52, 52, 51, 50, 53, 54, 57, 57, 60, 61, 64, 66, 67, 70, 71, 73, 74, 77, 77, 79, 81, 82, 83, 83, 85, 85, 86, 87, + 63, 60, 59, 57, 57, 56, 55, 54, 57, 57, 60, 61, 64, 65, 67, 69, 71, 73, 75, 77, 78, 82, 82, 85, 86, 89, 89, 90, 91, 91, 89, 87, + 64, 61, 60, 58, 57, 57, 56, 55, 57, 58, 61, 61, 64, 65, 68, 69, 71, 74, 75, 78, 78, 82, 83, 86, 87, 90, 90, 91, 92, 93, 94, 95, + 67, 63, 63, 60, 60, 59, 58, 57, 59, 60, 62, 63, 65, 66, 69, 70, 73, 74, 77, 78, 81, 83, 85, 87, 88, 92, 92, 94, 94, 96, 95, 95, + 67, 64, 64, 61, 61, 60, 60, 58, 58, 61, 61, 64, 64, 67, 67, 70, 71, 74, 74, 78, 78, 82, 82, 86, 86, 90, 90, 95, 95, 96, 96, 98, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 36, 36, 39, 39, 44, 44, 48, 48, 53, 53, 58, 58, 65, 65, 71, 71, 79, 79, 82, 82, 87, + 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 37, 37, 41, 41, 45, 45, 49, 49, 54, 54, 60, 60, 65, 65, 72, 72, 75, 75, 79, + 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 37, 37, 41, 41, 45, 45, 49, 49, 54, 54, 60, 60, 65, 65, 72, 72, 75, 75, 79, + 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 43, 43, 46, 46, 50, 50, 54, 54, 58, 58, 63, 63, 70, 70, 72, 72, 76, + 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 43, 43, 46, 46, 50, 50, 54, 54, 58, 58, 63, 63, 70, 70, 72, 72, 76, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 53, 53, 56, 56, 60, 60, 63, 63, 68, 68, 73, 73, 79, 79, 81, 81, 84, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 53, 53, 56, 56, 60, 60, 63, 63, 68, 68, 73, 73, 79, 79, 81, 81, 84, + 44, 42, 42, 41, 41, 42, 42, 42, 42, 48, 48, 54, 54, 58, 58, 63, 63, 67, 67, 71, 71, 75, 75, 79, 79, 84, 84, 90, 90, 92, 92, 96, + 44, 42, 42, 41, 41, 42, 42, 42, 42, 48, 48, 54, 54, 58, 58, 63, 63, 67, 67, 71, 71, 75, 75, 79, 79, 84, 84, 90, 90, 92, 92, 96, + 53, 51, 51, 49, 49, 50, 50, 49, 49, 54, 54, 60, 60, 65, 65, 71, 71, 76, 76, 82, 82, 87, 87, 92, 92, 97, 97, 104, 104, 106, 106, 109, + 53, 51, 51, 49, 49, 50, 50, 49, 49, 54, 54, 60, 60, 65, 65, 71, 71, 76, 76, 82, 82, 87, 87, 92, 92, 97, 97, 104, 104, 106, 106, 109, + 65, 62, 62, 59, 59, 59, 59, 58, 58, 63, 63, 68, 68, 73, 73, 79, 79, 85, 85, 92, 92, 98, 98, 105, 105, 111, 111, 118, 118, 121, 121, 124, + 65, 62, 62, 59, 59, 59, 59, 58, 58, 63, 63, 68, 68, 73, 73, 79, 79, 85, 85, 92, 92, 98, 98, 105, 105, 111, 111, 118, 118, 121, 121, 124, + 79, 75, 75, 72, 72, 71, 71, 69, 69, 73, 73, 78, 78, 84, 84, 90, 90, 96, 96, 103, 103, 110, 110, 118, 118, 125, 125, 133, 133, 136, 136, 141, + 79, 75, 75, 72, 72, 71, 71, 69, 69, 73, 73, 78, 78, 84, 84, 90, 90, 96, 96, 103, 103, 110, 110, 118, 118, 125, 125, 133, 133, 136, 136, 141, + 87, 82, 82, 78, 78, 77, 77, 75, 75, 79, 79, 84, 84, 89, 89, 95, 95, 102, 102, 109, 109, 116, 116, 124, 124, 132, 132, 141, 141, 144, 144, 149, + }, { + 32, 31, 31, 30, 30, 33, 33, 37, 37, 42, 42, 49, 49, 48, 48, 49, 49, 50, 50, 52, 52, 54, 54, 57, 57, 60, 60, 63, 63, 64, 64, 66, + 31, 31, 31, 32, 32, 36, 36, 40, 40, 43, 43, 46, 46, 46, 46, 45, 45, 46, 46, 48, 48, 50, 50, 52, 52, 54, 54, 57, 57, 59, 59, 60, + 31, 31, 31, 32, 32, 36, 36, 40, 40, 43, 43, 46, 46, 46, 46, 45, 45, 46, 46, 48, 48, 50, 50, 52, 52, 54, 54, 57, 57, 59, 59, 60, + 37, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 57, + 37, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 47, 47, 49, 49, 50, 50, 52, 52, 55, 55, 56, 56, 57, + 48, 47, 47, 46, 46, 47, 47, 47, 47, 50, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 58, 58, 60, 60, 61, 61, 63, + 48, 47, 47, 46, 46, 47, 47, 47, 47, 50, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 58, 58, 60, 60, 61, 61, 63, + 49, 47, 47, 45, 45, 46, 46, 45, 45, 49, 49, 53, 53, 56, 56, 58, 58, 59, 59, 61, 61, 62, 62, 64, 64, 65, 65, 67, 67, 68, 68, 69, + 49, 47, 47, 45, 45, 46, 46, 45, 45, 49, 49, 53, 53, 56, 56, 58, 58, 59, 59, 61, 61, 62, 62, 64, 64, 65, 65, 67, 67, 68, 68, 69, + 52, 50, 50, 48, 48, 47, 47, 47, 47, 50, 50, 54, 54, 57, 57, 61, 61, 64, 64, 66, 66, 68, 68, 70, 70, 72, 72, 75, 75, 75, 75, 77, + 52, 50, 50, 48, 48, 47, 47, 47, 47, 50, 50, 54, 54, 57, 57, 61, 61, 64, 64, 66, 66, 68, 68, 70, 70, 72, 72, 75, 75, 75, 75, 77, + 57, 54, 54, 52, 52, 51, 51, 50, 50, 53, 53, 57, 57, 60, 60, 64, 64, 67, 67, 71, 71, 73, 73, 76, 76, 79, 79, 82, 82, 83, 83, 84, + 57, 54, 54, 52, 52, 51, 51, 50, 50, 53, 53, 57, 57, 60, 60, 64, 64, 67, 67, 71, 71, 73, 73, 76, 76, 79, 79, 82, 82, 83, 83, 84, + 63, 60, 60, 57, 57, 56, 56, 54, 54, 57, 57, 60, 60, 64, 64, 67, 67, 71, 71, 75, 75, 78, 78, 82, 82, 85, 85, 89, 89, 90, 90, 92, + 63, 60, 60, 57, 57, 56, 56, 54, 54, 57, 57, 60, 60, 64, 64, 67, 67, 71, 71, 75, 75, 78, 78, 82, 82, 85, 85, 89, 89, 90, 90, 92, + 66, 63, 63, 60, 60, 59, 59, 57, 57, 60, 60, 62, 62, 66, 66, 69, 69, 73, 73, 77, 77, 80, 80, 84, 84, 88, 88, 92, 92, 93, 93, 95, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 35, 36, 38, 39, 41, 44, 44, 48, 48, 53, 53, 57, 58, 61, 65, 67, 71, 72, 79, 79, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 38, 39, 41, 42, 45, 45, 49, 50, 53, 54, 57, 60, 62, 66, 66, 73, 73, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 36, 37, 39, 41, 42, 45, 45, 49, 49, 52, 54, 57, 60, 61, 65, 66, 72, 72, + 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 36, 36, 37, 37, 38, 40, 41, 42, 43, 46, 46, 49, 50, 52, 54, 56, 59, 60, 64, 64, 71, 71, + 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 41, 43, 43, 46, 46, 49, 50, 52, 54, 56, 58, 60, 63, 64, 70, 70, + 34, 34, 34, 33, 33, 34, 35, 35, 37, 37, 39, 39, 42, 43, 44, 45, 46, 48, 48, 51, 51, 54, 54, 57, 58, 60, 63, 64, 68, 68, 74, 74, + 36, 35, 35, 35, 34, 35, 36, 37, 38, 39, 42, 42, 47, 48, 49, 50, 51, 53, 54, 56, 56, 59, 60, 62, 63, 66, 68, 69, 73, 73, 79, 79, + 38, 37, 37, 36, 36, 37, 38, 38, 39, 40, 44, 44, 48, 49, 51, 52, 54, 56, 56, 59, 59, 62, 63, 65, 67, 69, 71, 72, 76, 76, 82, 82, + 44, 42, 42, 41, 41, 41, 42, 42, 42, 43, 48, 48, 52, 54, 56, 58, 60, 63, 64, 67, 67, 71, 71, 74, 75, 77, 79, 81, 84, 85, 90, 90, + 44, 43, 43, 42, 41, 42, 43, 43, 43, 44, 48, 48, 53, 54, 57, 58, 60, 64, 64, 67, 67, 71, 72, 75, 76, 78, 80, 82, 85, 86, 91, 91, + 53, 51, 51, 50, 49, 49, 50, 49, 49, 50, 54, 54, 59, 60, 63, 65, 67, 71, 72, 76, 76, 81, 82, 85, 87, 89, 92, 94, 97, 98, 104, 104, + 53, 51, 51, 50, 49, 49, 50, 49, 49, 50, 54, 54, 59, 60, 63, 65, 67, 71, 72, 76, 76, 81, 82, 85, 87, 89, 92, 94, 97, 98, 104, 104, + 62, 60, 59, 58, 57, 57, 57, 56, 56, 56, 61, 61, 65, 66, 69, 71, 74, 78, 79, 83, 83, 89, 90, 94, 95, 98, 102, 103, 108, 108, 115, 115, + 65, 62, 62, 60, 59, 59, 59, 59, 58, 58, 63, 63, 67, 68, 71, 73, 76, 79, 81, 85, 85, 91, 92, 96, 98, 101, 105, 106, 111, 111, 118, 118, + 73, 70, 69, 67, 66, 66, 65, 65, 64, 64, 69, 69, 73, 74, 77, 79, 81, 85, 86, 91, 91, 98, 99, 103, 105, 108, 112, 114, 119, 119, 127, 127, + 79, 75, 75, 73, 72, 71, 71, 70, 69, 69, 73, 73, 77, 78, 81, 84, 86, 90, 91, 96, 96, 103, 103, 108, 110, 114, 118, 120, 125, 125, 133, 133, + }, { + 32, 31, 31, 30, 30, 32, 33, 34, 37, 37, 42, 42, 47, 49, 48, 48, 48, 49, 49, 50, 50, 52, 52, 53, 54, 55, 57, 58, 60, 60, 63, 63, + 31, 31, 31, 32, 32, 33, 35, 37, 40, 40, 43, 43, 46, 47, 46, 46, 46, 45, 46, 47, 47, 48, 48, 50, 50, 51, 52, 53, 55, 55, 58, 58, + 31, 31, 31, 32, 32, 34, 36, 37, 40, 40, 43, 43, 46, 46, 46, 46, 45, 45, 45, 46, 46, 48, 48, 49, 50, 51, 52, 53, 54, 55, 57, 57, + 35, 36, 36, 37, 37, 39, 40, 42, 45, 45, 46, 46, 47, 47, 47, 46, 46, 45, 46, 46, 46, 47, 47, 48, 49, 50, 51, 51, 53, 53, 56, 56, + 37, 38, 38, 39, 40, 41, 43, 44, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 46, 47, 47, 48, 49, 49, 50, 51, 52, 53, 55, 55, + 42, 42, 42, 42, 42, 44, 45, 45, 47, 47, 48, 48, 50, 50, 50, 50, 49, 49, 49, 50, 50, 50, 50, 51, 52, 52, 53, 54, 55, 55, 58, 58, + 48, 47, 47, 46, 46, 46, 47, 47, 47, 48, 50, 50, 52, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 57, 58, 59, 60, 60, + 48, 47, 47, 46, 46, 46, 46, 47, 47, 47, 50, 50, 52, 53, 53, 54, 54, 55, 55, 55, 55, 56, 56, 57, 57, 58, 58, 59, 60, 60, 62, 62, + 49, 47, 47, 46, 45, 45, 46, 45, 45, 46, 49, 49, 53, 53, 55, 56, 57, 58, 58, 59, 59, 61, 61, 62, 62, 63, 64, 64, 65, 65, 67, 67, + 49, 47, 47, 46, 45, 45, 46, 46, 46, 46, 49, 49, 53, 54, 55, 56, 57, 59, 59, 60, 60, 61, 61, 62, 63, 63, 64, 65, 66, 66, 68, 68, + 52, 50, 50, 48, 48, 48, 47, 47, 47, 47, 50, 50, 53, 54, 56, 57, 59, 61, 62, 64, 64, 66, 66, 68, 68, 69, 70, 71, 72, 73, 75, 75, + 52, 50, 50, 48, 48, 48, 47, 47, 47, 47, 50, 50, 53, 54, 56, 57, 59, 61, 62, 64, 64, 66, 66, 68, 68, 69, 70, 71, 72, 73, 75, 75, + 56, 54, 53, 52, 51, 51, 50, 50, 49, 49, 53, 53, 55, 56, 58, 59, 61, 63, 64, 66, 66, 69, 70, 71, 72, 74, 75, 76, 77, 78, 80, 80, + 57, 54, 54, 52, 52, 51, 51, 51, 50, 50, 53, 53, 56, 57, 58, 60, 61, 64, 64, 67, 67, 70, 71, 72, 73, 75, 76, 77, 79, 79, 82, 82, + 61, 58, 57, 56, 55, 54, 54, 53, 52, 53, 56, 56, 58, 59, 61, 62, 63, 66, 66, 69, 69, 72, 73, 75, 76, 78, 79, 80, 82, 83, 86, 86, + 63, 60, 60, 58, 57, 57, 56, 55, 54, 55, 57, 57, 60, 60, 62, 64, 65, 67, 68, 71, 71, 74, 75, 77, 78, 80, 82, 83, 85, 85, 89, 89, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 33, 34, 34, 36, 36, 38, 39, 41, 44, 44, 47, 48, 50, 53, 53, 57, 58, 61, 65, 65, 70, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 37, 38, 39, 41, 41, 44, 45, 47, 50, 50, 54, 55, 57, 61, 61, 65, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 37, 37, 39, 41, 41, 44, 45, 46, 49, 49, 53, 54, 56, 60, 60, 64, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 35, 36, 36, 38, 39, 40, 42, 42, 44, 45, 47, 50, 50, 53, 54, 56, 59, 59, 63, + 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, 40, 40, 41, 43, 43, 45, 46, 47, 50, 50, 53, 54, 56, 58, 58, 62, + 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 38, 38, 40, 40, 41, 43, 43, 45, 46, 47, 50, 50, 53, 54, 56, 58, 58, 62, + 35, 35, 35, 34, 34, 34, 35, 36, 36, 37, 37, 40, 41, 43, 46, 46, 47, 48, 49, 51, 51, 53, 54, 55, 57, 57, 60, 61, 63, 66, 66, 70, + 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 41, 42, 44, 48, 48, 50, 50, 51, 53, 53, 56, 56, 58, 60, 60, 63, 63, 65, 68, 68, 72, + 39, 38, 38, 37, 37, 37, 38, 38, 39, 40, 40, 43, 44, 46, 50, 50, 52, 53, 54, 57, 57, 59, 60, 61, 64, 64, 67, 68, 69, 72, 72, 76, + 44, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 46, 48, 50, 54, 54, 57, 58, 60, 63, 63, 66, 67, 68, 71, 71, 74, 75, 77, 79, 79, 83, + 44, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 46, 48, 50, 54, 54, 57, 58, 60, 63, 63, 66, 67, 68, 71, 71, 74, 75, 77, 79, 79, 83, + 51, 49, 49, 48, 47, 47, 48, 48, 48, 48, 48, 52, 53, 55, 58, 58, 62, 63, 66, 69, 69, 73, 74, 76, 79, 79, 83, 84, 86, 89, 89, 93, + 53, 52, 51, 50, 49, 49, 49, 50, 49, 49, 49, 53, 54, 56, 60, 60, 64, 65, 67, 71, 71, 75, 76, 78, 82, 82, 86, 87, 89, 92, 92, 96, + 58, 56, 55, 54, 53, 53, 53, 53, 53, 52, 52, 56, 57, 59, 63, 63, 67, 68, 70, 74, 74, 78, 79, 82, 86, 86, 90, 91, 93, 97, 97, 101, + 65, 63, 62, 61, 59, 59, 59, 59, 58, 58, 58, 62, 63, 65, 68, 68, 72, 73, 76, 79, 79, 84, 85, 88, 92, 92, 97, 98, 100, 105, 105, 109, + 65, 63, 62, 61, 59, 59, 59, 59, 58, 58, 58, 62, 63, 65, 68, 68, 72, 73, 76, 79, 79, 84, 85, 88, 92, 92, 97, 98, 100, 105, 105, 109, + }, { + 32, 31, 31, 31, 30, 30, 33, 33, 35, 37, 37, 41, 42, 44, 49, 49, 48, 48, 48, 49, 49, 50, 50, 51, 52, 52, 54, 54, 55, 57, 57, 59, + 31, 31, 31, 31, 32, 32, 34, 35, 37, 39, 39, 42, 42, 44, 47, 47, 46, 46, 46, 46, 46, 47, 47, 48, 48, 48, 50, 51, 51, 53, 53, 55, + 31, 31, 31, 32, 32, 32, 35, 36, 37, 40, 40, 42, 43, 44, 46, 46, 46, 46, 45, 45, 45, 46, 46, 47, 48, 48, 49, 50, 51, 52, 52, 54, + 33, 34, 34, 34, 35, 35, 37, 38, 40, 43, 43, 44, 44, 45, 47, 47, 46, 46, 46, 45, 45, 46, 46, 47, 47, 47, 49, 49, 50, 51, 51, 53, + 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 48, 49, 49, 50, 50, 52, + 37, 38, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 47, 48, 48, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 48, 49, 49, 50, 50, 52, + 45, 45, 45, 45, 44, 44, 46, 46, 46, 47, 47, 49, 49, 50, 52, 52, 51, 51, 51, 51, 51, 52, 52, 52, 53, 53, 54, 54, 54, 55, 55, 57, + 48, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 49, 50, 51, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 56, 56, 56, 58, + 48, 47, 47, 46, 45, 45, 46, 46, 46, 47, 47, 49, 50, 51, 53, 53, 54, 54, 54, 55, 55, 56, 56, 56, 57, 57, 58, 58, 58, 59, 59, 61, + 49, 47, 47, 46, 45, 45, 45, 46, 45, 45, 45, 48, 49, 51, 53, 53, 55, 56, 57, 58, 58, 59, 59, 60, 61, 61, 62, 62, 63, 64, 64, 65, + 49, 47, 47, 46, 45, 45, 45, 46, 45, 45, 45, 48, 49, 51, 53, 53, 55, 56, 57, 58, 58, 59, 59, 60, 61, 61, 62, 62, 63, 64, 64, 65, + 51, 50, 49, 48, 47, 47, 47, 47, 47, 46, 46, 49, 50, 52, 54, 54, 56, 57, 58, 61, 61, 62, 63, 64, 65, 65, 67, 67, 68, 69, 69, 70, + 52, 50, 50, 49, 48, 48, 47, 47, 47, 47, 47, 50, 50, 52, 54, 54, 57, 57, 59, 61, 61, 63, 64, 65, 66, 66, 68, 68, 69, 70, 70, 72, + 54, 52, 51, 51, 49, 49, 49, 49, 48, 48, 48, 51, 51, 53, 55, 55, 58, 58, 60, 62, 62, 64, 65, 66, 68, 68, 70, 70, 71, 73, 73, 74, + 57, 55, 54, 53, 52, 52, 51, 51, 51, 50, 50, 52, 53, 54, 57, 57, 59, 60, 61, 64, 64, 66, 67, 68, 71, 71, 73, 73, 74, 76, 76, 78, + 57, 55, 54, 53, 52, 52, 51, 51, 51, 50, 50, 52, 53, 54, 57, 57, 59, 60, 61, 64, 64, 66, 67, 68, 71, 71, 73, 73, 74, 76, 76, 78, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 34, 34, 35, 36, 36, 38, 39, 39, 42, 44, 44, 47, 48, 49, 53, 53, 55, 58, 58, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 37, 38, 38, 40, 42, 42, 45, 46, 47, 50, 51, 52, 55, 55, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 36, 37, 37, 40, 41, 41, 44, 45, 46, 49, 49, 51, 54, 54, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 35, 35, 37, 38, 38, 40, 41, 41, 44, 45, 46, 49, 49, 51, 54, 54, + 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 36, 37, 37, 37, 39, 40, 40, 42, 42, 43, 45, 46, 47, 49, 50, 51, 54, 54, + 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 37, 37, 38, 38, 40, 40, 40, 42, 43, 43, 45, 46, 47, 49, 50, 51, 54, 54, + 32, 33, 33, 33, 33, 33, 33, 34, 34, 35, 36, 36, 36, 38, 38, 39, 40, 40, 41, 42, 42, 44, 45, 45, 47, 48, 48, 51, 51, 53, 55, 55, + 35, 35, 35, 35, 34, 34, 35, 36, 36, 37, 38, 38, 39, 42, 42, 44, 47, 47, 48, 49, 49, 51, 52, 52, 54, 55, 56, 58, 59, 60, 62, 62, + 36, 35, 35, 35, 35, 34, 35, 36, 36, 37, 38, 38, 40, 42, 42, 45, 48, 48, 49, 50, 50, 52, 53, 54, 56, 56, 57, 59, 60, 61, 63, 63, + 38, 37, 37, 37, 36, 36, 36, 38, 38, 38, 39, 39, 41, 44, 44, 46, 49, 49, 51, 52, 52, 55, 56, 56, 58, 59, 60, 62, 63, 64, 67, 67, + 44, 43, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 44, 48, 48, 50, 54, 54, 56, 58, 58, 61, 63, 63, 66, 67, 67, 71, 71, 72, 75, 75, + 44, 43, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 44, 48, 48, 50, 54, 54, 56, 58, 58, 61, 63, 63, 66, 67, 67, 71, 71, 72, 75, 75, + 47, 46, 45, 45, 44, 44, 44, 45, 45, 45, 45, 45, 47, 50, 50, 53, 56, 56, 58, 60, 60, 64, 66, 66, 69, 70, 71, 74, 75, 76, 79, 79, + 53, 52, 51, 51, 49, 49, 49, 49, 50, 49, 49, 49, 51, 54, 54, 57, 60, 60, 63, 65, 65, 69, 71, 72, 75, 76, 77, 81, 82, 83, 87, 87, + 53, 52, 51, 51, 49, 49, 49, 49, 50, 49, 49, 49, 51, 54, 54, 57, 60, 60, 63, 65, 65, 69, 71, 72, 75, 76, 77, 81, 82, 83, 87, 87, + 59, 57, 56, 56, 54, 54, 54, 54, 54, 54, 53, 53, 55, 58, 58, 61, 64, 64, 67, 69, 69, 73, 75, 76, 79, 80, 81, 86, 87, 88, 92, 92, + }, { + 32, 31, 31, 31, 30, 30, 31, 33, 33, 34, 37, 37, 39, 42, 42, 45, 49, 49, 48, 48, 48, 49, 49, 49, 50, 50, 51, 52, 52, 53, 54, 54, + 31, 31, 31, 31, 31, 31, 32, 35, 35, 36, 39, 39, 40, 42, 42, 45, 47, 47, 47, 46, 46, 46, 46, 46, 47, 48, 48, 49, 49, 50, 51, 51, + 31, 31, 31, 31, 32, 32, 33, 35, 36, 37, 40, 40, 41, 43, 43, 44, 46, 46, 46, 46, 46, 45, 45, 45, 46, 46, 47, 48, 48, 48, 50, 50, + 31, 32, 32, 32, 32, 33, 33, 36, 36, 37, 41, 41, 42, 43, 43, 45, 47, 47, 46, 46, 46, 45, 45, 45, 46, 46, 47, 48, 48, 48, 50, 50, + 35, 36, 37, 37, 38, 38, 38, 41, 41, 42, 45, 45, 46, 46, 46, 47, 48, 48, 47, 46, 46, 46, 45, 46, 46, 46, 47, 47, 47, 48, 49, 49, + 37, 38, 38, 38, 39, 40, 40, 43, 43, 44, 47, 47, 47, 47, 47, 47, 48, 48, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 48, 49, 49, + 38, 39, 40, 40, 40, 41, 41, 43, 44, 45, 47, 47, 47, 48, 48, 48, 49, 49, 48, 48, 48, 47, 47, 47, 48, 48, 48, 48, 48, 49, 50, 50, + 47, 46, 46, 46, 45, 45, 45, 46, 46, 47, 47, 47, 48, 50, 50, 51, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 53, 54, 55, 55, + 48, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 48, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, + 48, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 48, 50, 50, 51, 53, 53, 53, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56, 57, 57, + 49, 48, 47, 47, 45, 45, 45, 45, 46, 45, 45, 45, 47, 49, 49, 51, 53, 53, 55, 56, 56, 57, 58, 58, 59, 59, 60, 61, 61, 61, 62, 62, + 49, 48, 47, 47, 45, 45, 45, 45, 46, 45, 45, 45, 47, 49, 49, 51, 53, 53, 55, 56, 56, 57, 58, 58, 59, 59, 60, 61, 61, 61, 62, 62, + 50, 49, 48, 48, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 52, 54, 54, 55, 56, 56, 58, 59, 60, 61, 61, 61, 63, 63, 63, 65, 65, + 52, 50, 50, 50, 48, 48, 48, 47, 47, 47, 47, 47, 48, 50, 50, 52, 54, 54, 56, 57, 57, 60, 61, 61, 63, 64, 64, 66, 66, 67, 68, 68, + 52, 50, 50, 50, 48, 48, 48, 47, 47, 47, 47, 47, 48, 50, 50, 52, 54, 54, 56, 57, 57, 60, 61, 61, 63, 64, 64, 66, 66, 67, 68, 68, + 54, 53, 52, 52, 50, 50, 50, 49, 49, 49, 48, 48, 50, 52, 52, 54, 55, 55, 57, 59, 59, 61, 62, 63, 65, 65, 66, 68, 68, 69, 71, 71, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 34, 34, 34, 35, 36, 36, 38, 39, 39, 41, 44, 44, 44, 47, 48, 48, 51, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 35, 37, 38, 38, 40, 42, 42, 43, 45, 46, 46, 49, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 34, 36, 37, 37, 39, 41, 41, 42, 44, 45, 45, 47, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 34, 36, 37, 37, 39, 41, 41, 42, 44, 45, 45, 47, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 37, 39, 39, 40, 42, 42, 42, 44, 45, 45, 48, + 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 38, 38, 38, 40, 40, 40, 41, 43, 43, 43, 45, 46, 46, 48, + 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 38, 38, 38, 40, 40, 40, 41, 43, 43, 43, 45, 46, 46, 48, + 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 36, 36, 36, 38, 38, 38, 39, 40, 40, 41, 42, 42, 43, 45, 45, 45, 47, 48, 48, 50, + 35, 35, 35, 35, 34, 34, 34, 34, 35, 36, 36, 37, 37, 37, 39, 41, 41, 42, 45, 46, 46, 47, 48, 48, 49, 51, 51, 51, 53, 54, 54, 56, + 36, 35, 35, 35, 35, 34, 34, 35, 36, 36, 36, 37, 38, 38, 40, 42, 42, 43, 47, 48, 48, 49, 50, 50, 51, 53, 53, 54, 56, 56, 56, 58, + 36, 35, 35, 35, 35, 34, 34, 35, 36, 36, 36, 37, 38, 38, 40, 42, 42, 43, 47, 48, 48, 49, 50, 50, 51, 53, 53, 54, 56, 56, 56, 58, + 40, 39, 39, 39, 39, 38, 38, 38, 39, 39, 39, 40, 41, 41, 42, 45, 45, 46, 50, 51, 51, 53, 54, 54, 56, 59, 59, 59, 61, 62, 62, 64, + 44, 43, 42, 42, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 44, 48, 48, 49, 52, 54, 54, 56, 58, 58, 60, 63, 63, 64, 66, 67, 67, 69, + 44, 43, 42, 42, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 44, 48, 48, 49, 52, 54, 54, 56, 58, 58, 60, 63, 63, 64, 66, 67, 67, 69, + 47, 46, 45, 45, 45, 44, 44, 44, 44, 45, 45, 45, 45, 45, 47, 50, 50, 51, 55, 56, 56, 58, 60, 60, 62, 66, 66, 67, 69, 70, 70, 73, + 53, 52, 51, 51, 50, 49, 49, 49, 49, 50, 50, 49, 49, 49, 51, 54, 54, 55, 59, 60, 60, 63, 65, 65, 67, 71, 71, 72, 75, 76, 76, 79, + }, { + 32, 31, 31, 31, 31, 30, 30, 31, 33, 33, 33, 35, 37, 37, 39, 42, 42, 43, 47, 49, 49, 48, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, + 31, 31, 31, 31, 31, 31, 31, 32, 34, 35, 35, 37, 39, 39, 40, 42, 42, 43, 46, 47, 47, 47, 47, 47, 47, 46, 46, 47, 48, 48, 48, 49, + 31, 31, 31, 31, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, 43, 43, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 46, 46, 46, 47, + 31, 31, 31, 31, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, 43, 43, 46, 46, 46, 46, 46, 46, 45, 45, 45, 45, 46, 46, 46, 47, + 33, 33, 34, 34, 34, 35, 35, 35, 37, 38, 38, 41, 43, 43, 43, 44, 44, 45, 46, 47, 47, 46, 46, 46, 46, 45, 45, 45, 46, 46, 46, 47, + 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 47, + 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, 45, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, 47, + 38, 39, 40, 40, 40, 41, 41, 41, 43, 44, 44, 46, 47, 47, 47, 48, 48, 48, 48, 49, 49, 48, 48, 48, 47, 47, 47, 47, 48, 48, 48, 48, + 45, 45, 45, 45, 45, 44, 44, 45, 46, 46, 46, 47, 47, 47, 48, 49, 49, 50, 51, 52, 52, 52, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, + 48, 47, 47, 47, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 50, 50, 50, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, + 48, 47, 47, 47, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 50, 50, 50, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, + 49, 48, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, 49, 49, 50, 52, 53, 53, 54, 54, 54, 55, 56, 56, 56, 57, 57, 57, 58, + 49, 48, 47, 47, 46, 45, 45, 45, 45, 46, 46, 45, 45, 45, 47, 49, 49, 50, 53, 53, 53, 55, 56, 56, 57, 58, 58, 58, 59, 59, 59, 60, + 49, 48, 47, 47, 46, 45, 45, 45, 45, 46, 46, 45, 45, 45, 47, 49, 49, 50, 53, 53, 53, 55, 56, 56, 57, 58, 58, 58, 59, 59, 59, 60, + 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 50, 53, 54, 54, 55, 56, 56, 57, 59, 59, 60, 61, 61, 61, 62, + 52, 51, 50, 50, 49, 48, 48, 48, 47, 47, 47, 47, 47, 47, 48, 50, 50, 51, 53, 54, 54, 56, 57, 57, 59, 61, 61, 62, 63, 64, 64, 65, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 37, 39, 39, 39, 41, 44, 44, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 38, 39, 39, 39, 40, 42, 42, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 42, 43, 43, + 34, 34, 34, 34, 34, 34, 33, 33, 33, 34, 35, 35, 35, 36, 37, 37, 37, 38, 39, 39, 39, 41, 43, 43, 43, 44, 45, 45, 45, 46, 48, 48, + 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 53, 53, + 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 53, 53, + 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 53, 53, + 39, 39, 38, 38, 38, 38, 37, 37, 37, 38, 39, 39, 39, 40, 40, 40, 40, 42, 45, 45, 45, 47, 51, 51, 51, 52, 54, 54, 54, 56, 58, 58, + 44, 43, 42, 42, 42, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 45, 48, 48, 48, 50, 54, 54, 54, 56, 58, 58, 58, 60, 63, 63, + 44, 43, 42, 42, 42, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 45, 48, 48, 48, 50, 54, 54, 54, 56, 58, 58, 58, 60, 63, 63, + }, { + 32, 31, 31, 31, 31, 31, 30, 30, 30, 32, 33, 33, 33, 35, 37, 37, 37, 39, 42, 42, 42, 45, 49, 49, 49, 48, 48, 48, 48, 48, 49, 49, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 34, 34, 36, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 36, 36, 38, 40, 40, 40, 41, 43, 43, 43, 44, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 36, 36, 38, 40, 40, 40, 41, 43, 43, 43, 44, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 36, 36, 36, 38, 40, 40, 40, 41, 43, 43, 43, 44, 46, 46, 46, 46, 46, 46, 46, 45, 45, 45, + 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, 39, 39, 39, 41, 43, 43, 43, 44, 45, 45, 45, 46, 47, 47, 47, 47, 46, 46, 46, 46, 45, 45, + 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 46, 46, 46, + 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 46, 46, 46, + 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 47, 47, 47, 46, 46, 46, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 44, 45, 45, 45, 46, 47, 47, 47, 48, 48, 48, 48, 49, 50, 50, 50, 50, 50, 50, 50, 49, 49, 49, + 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 48, 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 48, 50, 50, 50, 51, 53, 53, 53, 54, 54, 54, 54, 55, 56, 56, + 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 46, 46, 46, 45, 45, 45, 45, 47, 49, 49, 49, 51, 53, 53, 53, 54, 56, 56, 56, 57, 58, 58, + 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 46, 46, 46, 45, 45, 45, 45, 47, 49, 49, 49, 51, 53, 53, 53, 54, 56, 56, 56, 57, 58, 58, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 37, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 36, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 36, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 36, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 36, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, 36, 36, 37, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 37, 38, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, + 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 38, 39, 39, 39, 40, 41, 42, 42, 42, 42, + 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 39, 40, 41, 41, 41, 43, 45, 46, 46, 46, 46, + 36, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 41, 42, 42, 42, 44, 47, 48, 48, 48, 49, + 36, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 41, 42, 42, 42, 44, 47, 48, 48, 48, 49, + 36, 35, 35, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 41, 42, 42, 42, 44, 47, 48, 48, 48, 49, + }, { + 32, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 33, 33, 33, 33, 35, 36, 37, 37, 37, 39, 41, 42, 42, 42, 44, 47, 49, 49, 49, 49, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, 36, 37, 38, 38, 38, 39, 41, 42, 42, 42, 44, 46, 48, 48, 48, 48, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 37, 38, 39, 39, 39, 40, 42, 42, 42, 42, 44, 46, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 37, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 46, 46, 46, 46, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 37, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 46, 46, 46, 46, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 36, 37, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 46, 46, 46, 46, + 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 37, 38, 38, 38, 40, 42, 43, 43, 43, 43, 44, 44, 44, 44, 45, 46, 47, 47, 47, 47, + 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, 40, 41, 41, 41, 43, 44, 45, 45, 45, 46, 46, 46, 46, 46, 47, 47, 48, 48, 48, 47, + 37, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 47, + 37, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 47, + 37, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 48, 47, + 40, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 43, 44, 44, 44, 44, 45, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 49, 50, 50, 50, 49, + 45, 45, 45, 45, 45, 45, 45, 44, 44, 44, 44, 45, 46, 46, 46, 46, 46, 47, 47, 47, 47, 48, 49, 49, 49, 49, 50, 51, 52, 52, 52, 52, + 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, 50, 50, 51, 52, 53, 53, 53, 53, + 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, 50, 50, 51, 52, 53, 53, 53, 53, + 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, 50, 50, 51, 52, 53, 53, 53, 53, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 36, 36, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, + 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 36, 36, 36, 36, 36, 36, 37, 38, 38, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 39, 39, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 31, 32, 33, 33, 33, 33, 33, 34, 35, 36, 37, 37, 37, 37, 39, 40, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 35, 36, 37, 38, 38, 38, 38, 39, 41, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 35, 35, 35, 35, 35, 37, 38, 39, 40, 40, 40, 40, 41, 42, 43, 43, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 39, 40, 40, 40, 40, 41, 42, 43, 43, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 39, 40, 40, 40, 40, 41, 42, 43, 43, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 39, 40, 40, 40, 40, 41, 42, 43, 43, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 35, 36, 36, 36, 36, 37, 39, 40, 41, 41, 41, 41, 42, 42, 43, 43, + 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 37, 38, 38, 38, 38, 39, 41, 42, 43, 43, 43, 43, 43, 44, 44, 44, + 35, 35, 35, 36, 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 38, 39, 40, 40, 40, 40, 40, 42, 43, 44, 45, 45, 45, 45, 45, 45, 46, 46, + 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 38, 39, 39, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 43, 44, 44, 44, 44, 45, 46, 47, 47, 47, 47, 47, 47, 47, 48, 48, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 44, 44, 45, 45, 45, 45, 45, 46, 47, 47, 47, 47, 47, 48, 48, 48, 48, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 31, 31, 32, 33, 33, 33, 33, 33, 33, 33, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33, 34, 35, 35, 35, 35, 35, 35, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 36, 36, 36, 36, 36, + 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 35, 35, 36, 37, 37, 37, 37, 37, 37, 38, + 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 38, 38, 38, 38, 38, 39, + 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 38, 39, 40, 40, 40, 40, 40, 40, 40, + 35, 35, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 39, 40, 40, 41, 41, 41, 41, 41, 41, 42, + 37, 37, 37, 38, 38, 38, 38, 38, 38, 38, 38, 38, 39, 39, 39, 40, 40, 40, 40, 40, 40, 40, 41, 41, 42, 43, 43, 43, 43, 43, 43, 44, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + }, + }, +}; + +static const uint8_t qm_tbl_32x32[15][2][1024] = { + { + { + 32, 31, 31, 31, 31, 32, 34, 35, 36, 39, 44, 46, 48, 54, 59, 62, 65, 71, 80, 81, 83, 88, 91, 94, 97, 101, 104, 107, 111, 115, 119, 123, + 31, 32, 32, 32, 32, 32, 34, 34, 35, 38, 42, 44, 46, 51, 56, 59, 62, 68, 76, 77, 78, 84, 86, 89, 92, 95, 99, 102, 105, 109, 113, 116, + 31, 32, 32, 32, 32, 32, 33, 34, 34, 37, 41, 42, 44, 49, 54, 56, 59, 65, 72, 73, 75, 80, 83, 86, 90, 93, 97, 101, 104, 108, 112, 116, + 31, 32, 32, 32, 33, 33, 34, 35, 35, 38, 41, 43, 45, 49, 54, 56, 59, 64, 72, 73, 74, 79, 82, 85, 88, 91, 94, 97, 101, 104, 107, 111, + 31, 32, 32, 33, 33, 34, 35, 36, 36, 39, 42, 44, 45, 50, 54, 56, 59, 64, 71, 72, 74, 78, 81, 84, 86, 89, 93, 96, 100, 104, 107, 111, + 32, 32, 32, 33, 34, 35, 37, 37, 38, 40, 42, 44, 46, 49, 53, 55, 58, 63, 69, 70, 72, 76, 79, 82, 85, 89, 93, 96, 99, 102, 106, 109, + 34, 34, 33, 34, 35, 37, 39, 41, 42, 45, 47, 49, 51, 54, 58, 60, 63, 68, 74, 75, 76, 80, 81, 82, 84, 87, 90, 93, 97, 101, 105, 110, + 35, 34, 34, 35, 36, 37, 41, 43, 45, 47, 50, 52, 53, 57, 61, 63, 65, 70, 76, 77, 79, 82, 84, 86, 89, 91, 92, 93, 96, 100, 103, 107, + 36, 35, 34, 35, 36, 38, 42, 45, 48, 50, 54, 55, 57, 60, 64, 66, 68, 73, 79, 80, 81, 85, 88, 90, 91, 93, 96, 99, 102, 103, 105, 107, + 39, 38, 37, 38, 39, 40, 45, 47, 50, 54, 58, 59, 61, 65, 69, 71, 73, 78, 84, 85, 86, 91, 92, 92, 95, 98, 100, 101, 103, 106, 110, 114, + 44, 42, 41, 41, 42, 42, 47, 50, 54, 58, 63, 65, 67, 71, 75, 77, 79, 84, 90, 91, 92, 95, 95, 97, 100, 101, 102, 105, 109, 111, 112, 114, + 46, 44, 42, 43, 44, 44, 49, 52, 55, 59, 65, 67, 69, 74, 78, 80, 82, 87, 93, 94, 95, 98, 100, 103, 102, 105, 108, 110, 111, 113, 117, 121, + 48, 46, 44, 45, 45, 46, 51, 53, 57, 61, 67, 69, 71, 76, 80, 83, 85, 90, 96, 97, 99, 103, 107, 105, 108, 111, 111, 113, 117, 119, 120, 122, + 54, 51, 49, 49, 50, 49, 54, 57, 60, 65, 71, 74, 76, 82, 87, 89, 92, 97, 104, 105, 106, 111, 110, 111, 114, 113, 116, 120, 120, 121, 125, 130, + 59, 56, 54, 54, 54, 53, 58, 61, 64, 69, 75, 78, 80, 87, 92, 95, 98, 103, 110, 111, 113, 115, 115, 119, 116, 120, 122, 122, 125, 129, 130, 130, + 62, 59, 56, 56, 56, 55, 60, 63, 66, 71, 77, 80, 83, 89, 95, 98, 101, 107, 114, 115, 117, 119, 123, 121, 125, 126, 125, 129, 131, 131, 135, 140, + 65, 62, 59, 59, 59, 58, 63, 65, 68, 73, 79, 82, 85, 92, 98, 101, 105, 111, 118, 119, 121, 126, 127, 128, 130, 130, 134, 133, 135, 140, 140, 140, + 71, 68, 65, 64, 64, 63, 68, 70, 73, 78, 84, 87, 90, 97, 103, 107, 111, 117, 125, 126, 128, 134, 132, 136, 133, 138, 137, 140, 143, 142, 145, 150, + 80, 76, 72, 72, 71, 69, 74, 76, 79, 84, 90, 93, 96, 104, 110, 114, 118, 125, 134, 135, 137, 139, 140, 139, 143, 142, 144, 146, 146, 151, 152, 151, + 81, 77, 73, 73, 72, 70, 75, 77, 80, 85, 91, 94, 97, 105, 111, 115, 119, 126, 135, 137, 138, 144, 147, 146, 148, 149, 151, 150, 156, 155, 157, 163, + 83, 78, 75, 74, 74, 72, 76, 79, 81, 86, 92, 95, 99, 106, 113, 117, 121, 128, 137, 138, 140, 147, 151, 156, 152, 157, 155, 161, 158, 162, 165, 164, + 88, 84, 80, 79, 78, 76, 80, 82, 85, 91, 95, 98, 103, 111, 115, 119, 126, 134, 139, 144, 147, 152, 154, 158, 163, 159, 165, 163, 168, 168, 169, 176, + 91, 86, 83, 82, 81, 79, 81, 84, 88, 92, 95, 100, 107, 110, 115, 123, 127, 132, 140, 147, 151, 154, 159, 161, 166, 171, 169, 173, 173, 176, 179, 177, + 94, 89, 86, 85, 84, 82, 82, 86, 90, 92, 97, 103, 105, 111, 119, 121, 128, 136, 139, 146, 156, 158, 161, 166, 168, 174, 179, 178, 180, 183, 183, 190, + 97, 92, 90, 88, 86, 85, 84, 89, 91, 95, 100, 102, 108, 114, 116, 125, 130, 133, 143, 148, 152, 163, 166, 168, 174, 176, 182, 187, 189, 188, 193, 191, + 101, 95, 93, 91, 89, 89, 87, 91, 93, 98, 101, 105, 111, 113, 120, 126, 130, 138, 142, 149, 157, 159, 171, 174, 176, 183, 184, 191, 195, 199, 197, 204, + 104, 99, 97, 94, 93, 93, 90, 92, 96, 100, 102, 108, 111, 116, 122, 125, 134, 137, 144, 151, 155, 165, 169, 179, 182, 184, 191, 193, 200, 204, 210, 206, + 107, 102, 101, 97, 96, 96, 93, 93, 99, 101, 105, 110, 113, 120, 122, 129, 133, 140, 146, 150, 161, 163, 173, 178, 187, 191, 193, 200, 202, 210, 214, 222, + 111, 105, 104, 101, 100, 99, 97, 96, 102, 103, 109, 111, 117, 120, 125, 131, 135, 143, 146, 156, 158, 168, 173, 180, 189, 195, 200, 202, 210, 212, 220, 224, + 115, 109, 108, 104, 104, 102, 101, 100, 103, 106, 111, 113, 119, 121, 129, 131, 140, 142, 151, 155, 162, 168, 176, 183, 188, 199, 204, 210, 212, 220, 222, 230, + 119, 113, 112, 107, 107, 106, 105, 103, 105, 110, 112, 117, 120, 125, 130, 135, 140, 145, 152, 157, 165, 169, 179, 183, 193, 197, 210, 214, 220, 222, 231, 232, + 123, 116, 116, 111, 111, 109, 110, 107, 107, 114, 114, 121, 122, 130, 130, 140, 140, 150, 151, 163, 164, 176, 177, 190, 191, 204, 206, 222, 224, 230, 232, 242, + }, { + 32, 31, 30, 32, 33, 36, 41, 45, 49, 48, 49, 50, 50, 52, 54, 56, 57, 60, 63, 64, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 78, + 31, 31, 31, 33, 34, 38, 42, 45, 47, 47, 47, 47, 48, 50, 52, 53, 54, 57, 60, 61, 61, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 74, + 30, 31, 32, 33, 35, 40, 42, 44, 46, 45, 45, 45, 46, 47, 49, 51, 52, 54, 57, 58, 58, 61, 62, 63, 64, 66, 67, 68, 70, 71, 72, 74, + 32, 33, 33, 35, 37, 41, 43, 45, 47, 46, 45, 46, 46, 47, 49, 50, 51, 54, 57, 57, 58, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, + 33, 34, 35, 37, 39, 43, 45, 46, 47, 46, 45, 46, 46, 47, 49, 50, 51, 53, 56, 57, 57, 59, 60, 61, 62, 63, 64, 65, 66, 68, 69, 70, + 36, 38, 40, 41, 43, 47, 47, 47, 48, 46, 45, 46, 46, 47, 48, 49, 50, 52, 54, 55, 55, 57, 58, 59, 61, 62, 64, 65, 66, 67, 68, 69, + 41, 42, 42, 43, 45, 47, 48, 49, 50, 49, 49, 49, 50, 50, 52, 52, 53, 55, 57, 58, 58, 60, 59, 59, 60, 61, 61, 63, 64, 66, 67, 69, + 45, 45, 44, 45, 46, 47, 49, 50, 51, 51, 51, 51, 52, 52, 53, 54, 55, 57, 59, 59, 60, 61, 61, 62, 63, 63, 63, 63, 63, 64, 65, 66, + 49, 47, 46, 47, 47, 48, 50, 51, 53, 53, 53, 54, 54, 54, 55, 56, 56, 58, 60, 61, 61, 63, 64, 64, 64, 64, 65, 66, 66, 66, 66, 66, + 48, 47, 45, 46, 46, 46, 49, 51, 53, 54, 55, 56, 56, 57, 58, 59, 60, 61, 63, 64, 64, 66, 66, 65, 66, 67, 67, 67, 67, 68, 69, 70, + 49, 47, 45, 45, 45, 45, 49, 51, 53, 55, 58, 59, 60, 61, 62, 63, 63, 65, 67, 67, 68, 69, 67, 68, 69, 68, 68, 69, 70, 70, 70, 70, + 50, 47, 45, 46, 46, 46, 49, 51, 54, 56, 59, 60, 60, 62, 64, 64, 65, 67, 69, 69, 70, 70, 71, 71, 70, 70, 71, 71, 71, 71, 72, 74, + 50, 48, 46, 46, 46, 46, 50, 52, 54, 56, 60, 60, 61, 63, 65, 66, 67, 68, 71, 71, 71, 73, 74, 72, 73, 74, 73, 73, 74, 74, 74, 74, + 52, 50, 47, 47, 47, 47, 50, 52, 54, 57, 61, 62, 63, 66, 68, 69, 70, 72, 75, 75, 75, 77, 75, 75, 76, 75, 75, 76, 75, 75, 76, 77, + 54, 52, 49, 49, 49, 48, 52, 53, 55, 58, 62, 64, 65, 68, 71, 72, 73, 75, 78, 78, 79, 79, 78, 79, 77, 78, 78, 77, 78, 79, 78, 78, + 56, 53, 51, 50, 50, 49, 52, 54, 56, 59, 63, 64, 66, 69, 72, 73, 75, 77, 80, 80, 81, 81, 82, 80, 81, 81, 79, 81, 80, 79, 81, 82, + 57, 54, 52, 51, 51, 50, 53, 55, 56, 60, 63, 65, 67, 70, 73, 75, 76, 79, 82, 82, 83, 85, 84, 83, 84, 83, 84, 82, 82, 84, 83, 82, + 60, 57, 54, 54, 53, 52, 55, 57, 58, 61, 65, 67, 68, 72, 75, 77, 79, 82, 85, 85, 86, 88, 86, 87, 85, 86, 85, 85, 86, 84, 85, 86, + 63, 60, 57, 57, 56, 54, 57, 59, 60, 63, 67, 69, 71, 75, 78, 80, 82, 85, 89, 89, 90, 90, 90, 89, 89, 88, 88, 88, 87, 88, 88, 87, + 64, 61, 58, 57, 57, 55, 58, 59, 61, 64, 67, 69, 71, 75, 78, 80, 82, 85, 89, 90, 91, 92, 93, 92, 92, 91, 91, 90, 91, 90, 90, 92, + 65, 61, 58, 58, 57, 55, 58, 60, 61, 64, 68, 70, 71, 75, 79, 81, 83, 86, 90, 91, 91, 94, 94, 96, 93, 94, 93, 94, 92, 93, 93, 92, + 67, 63, 61, 60, 59, 57, 60, 61, 63, 66, 69, 70, 73, 77, 79, 81, 85, 88, 90, 92, 94, 96, 96, 97, 98, 95, 97, 95, 96, 95, 95, 96, + 68, 64, 62, 61, 60, 58, 59, 61, 64, 66, 67, 71, 74, 75, 78, 82, 84, 86, 90, 93, 94, 96, 98, 98, 99, 100, 98, 99, 98, 98, 98, 97, + 69, 65, 63, 62, 61, 59, 59, 62, 64, 65, 68, 71, 72, 75, 79, 80, 83, 87, 89, 92, 96, 97, 98, 100, 100, 101, 102, 101, 101, 101, 100, 102, + 70, 66, 64, 63, 62, 61, 60, 63, 64, 66, 69, 70, 73, 76, 77, 81, 84, 85, 89, 92, 93, 98, 99, 100, 102, 102, 103, 104, 104, 103, 104, 102, + 71, 67, 66, 64, 63, 62, 61, 63, 64, 67, 68, 70, 74, 75, 78, 81, 83, 86, 88, 91, 94, 95, 100, 101, 102, 104, 104, 105, 106, 107, 105, 107, + 72, 68, 67, 65, 64, 64, 61, 63, 65, 67, 68, 71, 73, 75, 78, 79, 84, 85, 88, 91, 93, 97, 98, 102, 103, 104, 106, 106, 108, 108, 109, 107, + 73, 69, 68, 66, 65, 65, 63, 63, 66, 67, 69, 71, 73, 76, 77, 81, 82, 85, 88, 90, 94, 95, 99, 101, 104, 105, 106, 109, 108, 110, 111, 112, + 74, 70, 70, 67, 66, 66, 64, 63, 66, 67, 70, 71, 74, 75, 78, 80, 82, 86, 87, 91, 92, 96, 98, 101, 104, 106, 108, 108, 111, 111, 112, 113, + 75, 71, 71, 68, 68, 67, 66, 64, 66, 68, 70, 71, 74, 75, 79, 79, 84, 84, 88, 90, 93, 95, 98, 101, 103, 107, 108, 110, 111, 113, 113, 115, + 76, 72, 72, 69, 69, 68, 67, 65, 66, 69, 70, 72, 74, 76, 78, 81, 83, 85, 88, 90, 93, 95, 98, 100, 104, 105, 109, 111, 112, 113, 116, 115, + 78, 74, 74, 70, 70, 69, 69, 66, 66, 70, 70, 74, 74, 77, 78, 82, 82, 86, 87, 92, 92, 96, 97, 102, 102, 107, 107, 112, 113, 115, 115, 118, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 34, 36, 38, 39, 44, 47, 49, 54, 59, 61, 65, 71, 76, 80, 83, 86, 89, 92, 95, 98, 101, 104, 108, 111, 114, + 31, 32, 32, 32, 32, 32, 33, 34, 35, 37, 38, 42, 45, 47, 51, 56, 58, 62, 68, 72, 76, 78, 82, 85, 88, 90, 93, 96, 99, 102, 105, 109, + 31, 32, 32, 32, 32, 32, 33, 33, 34, 36, 37, 41, 44, 46, 50, 54, 56, 60, 65, 70, 73, 76, 79, 82, 85, 88, 91, 95, 98, 101, 105, 109, + 31, 32, 32, 32, 32, 33, 33, 34, 35, 36, 38, 41, 44, 45, 49, 54, 56, 59, 65, 69, 72, 75, 78, 81, 84, 86, 89, 92, 95, 98, 101, 104, + 31, 32, 32, 32, 33, 34, 34, 35, 36, 38, 39, 42, 45, 46, 50, 54, 56, 59, 64, 68, 71, 74, 77, 79, 82, 85, 88, 91, 94, 97, 100, 104, + 32, 32, 32, 33, 34, 35, 36, 37, 38, 39, 40, 42, 45, 46, 49, 53, 55, 58, 63, 66, 69, 72, 74, 78, 81, 84, 87, 90, 93, 96, 99, 102, + 32, 33, 33, 33, 34, 36, 36, 38, 40, 41, 42, 44, 47, 48, 51, 55, 57, 60, 65, 68, 71, 73, 76, 78, 80, 82, 85, 88, 91, 95, 98, 102, + 34, 34, 33, 34, 35, 37, 38, 39, 42, 44, 45, 47, 50, 51, 54, 58, 60, 63, 68, 71, 74, 76, 79, 82, 85, 86, 87, 88, 90, 93, 96, 99, + 36, 35, 34, 35, 36, 38, 40, 42, 48, 50, 50, 54, 56, 57, 60, 64, 65, 68, 73, 76, 79, 81, 84, 86, 86, 88, 90, 93, 95, 97, 98, 100, + 38, 37, 36, 36, 38, 39, 41, 44, 50, 51, 52, 56, 58, 60, 63, 67, 68, 71, 76, 79, 82, 84, 87, 87, 90, 93, 94, 95, 96, 100, 103, 106, + 39, 38, 37, 38, 39, 40, 42, 45, 50, 52, 54, 58, 60, 62, 65, 69, 70, 73, 78, 81, 84, 86, 89, 92, 95, 95, 96, 99, 102, 104, 105, 106, + 44, 42, 41, 41, 42, 42, 44, 47, 54, 56, 58, 63, 66, 68, 71, 75, 77, 79, 84, 88, 90, 92, 95, 97, 97, 99, 102, 103, 103, 106, 109, 113, + 47, 45, 44, 44, 45, 45, 47, 50, 56, 58, 60, 66, 69, 71, 75, 79, 81, 84, 89, 92, 95, 97, 100, 100, 102, 105, 104, 106, 109, 111, 112, 113, + 49, 47, 46, 45, 46, 46, 48, 51, 57, 60, 62, 68, 71, 73, 77, 81, 83, 87, 92, 95, 98, 100, 103, 105, 107, 106, 109, 112, 112, 113, 117, 120, + 54, 51, 50, 49, 50, 49, 51, 54, 60, 63, 65, 71, 75, 77, 82, 87, 89, 92, 97, 101, 104, 106, 109, 112, 110, 113, 114, 114, 117, 121, 121, 121, + 59, 56, 54, 54, 54, 53, 55, 58, 64, 67, 69, 75, 79, 81, 87, 92, 94, 98, 103, 107, 110, 113, 116, 114, 117, 118, 117, 121, 122, 122, 125, 129, + 61, 58, 56, 56, 56, 55, 57, 60, 65, 68, 70, 77, 81, 83, 89, 94, 97, 101, 106, 110, 113, 116, 119, 120, 122, 121, 126, 124, 125, 130, 130, 130, + 65, 62, 60, 59, 59, 58, 60, 63, 68, 71, 73, 79, 84, 87, 92, 98, 101, 105, 111, 115, 118, 121, 124, 128, 125, 129, 128, 131, 133, 132, 135, 139, + 71, 68, 65, 65, 64, 63, 65, 68, 73, 76, 78, 84, 89, 92, 97, 103, 106, 111, 117, 122, 125, 128, 131, 131, 134, 132, 134, 136, 136, 140, 141, 140, + 76, 72, 70, 69, 68, 66, 68, 71, 76, 79, 81, 88, 92, 95, 101, 107, 110, 115, 122, 127, 130, 133, 136, 136, 138, 139, 141, 140, 145, 143, 146, 151, + 80, 76, 73, 72, 71, 69, 71, 74, 79, 82, 84, 90, 95, 98, 104, 110, 113, 118, 125, 130, 134, 137, 140, 146, 142, 146, 145, 149, 146, 150, 152, 151, + 83, 78, 76, 75, 74, 72, 73, 76, 81, 84, 86, 92, 97, 100, 106, 113, 116, 121, 128, 133, 137, 140, 144, 147, 152, 148, 154, 151, 156, 155, 156, 162, + 86, 82, 79, 78, 77, 74, 76, 79, 84, 87, 89, 95, 100, 103, 109, 116, 119, 124, 131, 136, 140, 144, 147, 150, 154, 159, 157, 160, 160, 162, 165, 162, + 89, 85, 82, 81, 79, 78, 78, 82, 86, 87, 92, 97, 100, 105, 112, 114, 120, 128, 131, 136, 146, 147, 150, 155, 156, 161, 166, 165, 167, 169, 169, 175, + 92, 88, 85, 84, 82, 81, 80, 85, 86, 90, 95, 97, 102, 107, 110, 117, 122, 125, 134, 138, 142, 152, 154, 156, 162, 163, 168, 173, 174, 174, 178, 176, + 95, 90, 88, 86, 85, 84, 82, 86, 88, 93, 95, 99, 105, 106, 113, 118, 121, 129, 132, 139, 146, 148, 159, 161, 163, 169, 170, 176, 180, 183, 181, 187, + 98, 93, 91, 89, 88, 87, 85, 87, 90, 94, 96, 102, 104, 109, 114, 117, 126, 128, 134, 141, 145, 154, 157, 166, 168, 170, 176, 178, 184, 188, 193, 188, + 101, 96, 95, 92, 91, 90, 88, 88, 93, 95, 99, 103, 106, 112, 114, 121, 124, 131, 136, 140, 149, 151, 160, 165, 173, 176, 178, 184, 186, 192, 196, 203, + 104, 99, 98, 95, 94, 93, 91, 90, 95, 96, 102, 103, 109, 112, 117, 122, 125, 133, 136, 145, 146, 156, 160, 167, 174, 180, 184, 186, 193, 194, 201, 204, + 108, 102, 101, 98, 97, 96, 95, 93, 97, 100, 104, 106, 111, 113, 121, 122, 130, 132, 140, 143, 150, 155, 162, 169, 174, 183, 188, 192, 194, 201, 202, 210, + 111, 105, 105, 101, 100, 99, 98, 96, 98, 103, 105, 109, 112, 117, 121, 125, 130, 135, 141, 146, 152, 156, 165, 169, 178, 181, 193, 196, 201, 202, 210, 211, + 114, 109, 109, 104, 104, 102, 102, 99, 100, 106, 106, 113, 113, 120, 121, 129, 130, 139, 140, 151, 151, 162, 162, 175, 176, 187, 188, 203, 204, 210, 211, 219, + }, { + 32, 31, 30, 31, 33, 36, 38, 41, 49, 49, 48, 49, 50, 51, 52, 54, 55, 57, 60, 62, 63, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, + 31, 31, 31, 32, 34, 38, 40, 42, 47, 47, 47, 47, 48, 48, 50, 52, 53, 54, 57, 59, 60, 61, 63, 64, 65, 66, 67, 67, 68, 69, 70, 71, + 30, 31, 31, 32, 35, 39, 41, 42, 46, 46, 46, 45, 46, 47, 48, 50, 51, 52, 55, 57, 58, 59, 60, 62, 63, 64, 65, 67, 68, 69, 70, 71, + 31, 32, 32, 33, 36, 40, 41, 43, 46, 46, 45, 45, 46, 46, 47, 49, 50, 51, 54, 56, 57, 58, 59, 61, 62, 63, 63, 64, 65, 66, 67, 68, + 33, 34, 35, 36, 39, 43, 44, 45, 47, 46, 46, 45, 46, 47, 47, 49, 50, 51, 53, 55, 56, 57, 58, 59, 60, 61, 62, 63, 65, 66, 67, 68, + 36, 38, 39, 40, 43, 47, 47, 47, 48, 47, 46, 45, 46, 46, 47, 48, 49, 50, 52, 53, 54, 55, 56, 58, 59, 61, 62, 63, 64, 65, 66, 66, + 38, 40, 41, 41, 44, 47, 47, 48, 49, 48, 48, 47, 47, 47, 48, 49, 50, 51, 53, 54, 55, 56, 58, 58, 58, 59, 60, 61, 62, 64, 65, 66, + 41, 42, 42, 43, 45, 47, 48, 48, 50, 50, 49, 49, 50, 50, 50, 52, 52, 53, 55, 56, 57, 58, 59, 60, 61, 61, 61, 61, 62, 63, 63, 64, + 49, 47, 46, 46, 47, 48, 49, 50, 53, 53, 53, 53, 54, 54, 54, 55, 56, 56, 58, 59, 60, 61, 62, 63, 62, 62, 63, 64, 64, 64, 64, 64, + 49, 47, 46, 46, 46, 47, 48, 50, 53, 53, 54, 55, 55, 55, 56, 57, 58, 58, 60, 61, 62, 63, 64, 64, 64, 65, 65, 65, 65, 66, 67, 68, + 48, 47, 46, 45, 46, 46, 48, 49, 53, 54, 54, 55, 56, 56, 57, 58, 59, 60, 61, 63, 63, 64, 65, 66, 67, 66, 66, 67, 68, 68, 68, 68, + 49, 47, 45, 45, 45, 45, 47, 49, 53, 55, 55, 58, 59, 60, 61, 62, 63, 63, 65, 66, 67, 68, 69, 69, 68, 68, 69, 69, 69, 69, 70, 71, + 50, 48, 46, 46, 46, 46, 47, 50, 54, 55, 56, 59, 61, 61, 63, 64, 65, 66, 68, 69, 70, 71, 72, 71, 71, 72, 71, 71, 72, 72, 72, 71, + 51, 48, 47, 46, 47, 46, 47, 50, 54, 55, 56, 60, 61, 62, 64, 66, 66, 67, 69, 70, 71, 72, 73, 73, 74, 73, 73, 74, 73, 73, 74, 75, + 52, 50, 48, 47, 47, 47, 48, 50, 54, 56, 57, 61, 63, 64, 66, 68, 69, 70, 72, 74, 75, 75, 76, 77, 75, 76, 76, 75, 76, 77, 76, 75, + 54, 52, 50, 49, 49, 48, 49, 52, 55, 57, 58, 62, 64, 66, 68, 71, 72, 73, 75, 77, 78, 79, 80, 78, 79, 78, 77, 78, 78, 77, 78, 79, + 55, 53, 51, 50, 50, 49, 50, 52, 56, 58, 59, 63, 65, 66, 69, 72, 73, 74, 77, 78, 79, 80, 81, 81, 81, 80, 81, 80, 80, 81, 80, 79, + 57, 54, 52, 51, 51, 50, 51, 53, 56, 58, 60, 63, 66, 67, 70, 73, 74, 76, 79, 80, 82, 83, 84, 85, 83, 84, 83, 83, 83, 82, 82, 83, + 60, 57, 55, 54, 53, 52, 53, 55, 58, 60, 61, 65, 68, 69, 72, 75, 77, 79, 82, 84, 85, 86, 87, 86, 87, 85, 85, 85, 84, 86, 85, 84, + 62, 59, 57, 56, 55, 53, 54, 56, 59, 61, 63, 66, 69, 70, 74, 77, 78, 80, 84, 86, 87, 88, 90, 89, 89, 88, 88, 87, 88, 87, 87, 88, + 63, 60, 58, 57, 56, 54, 55, 57, 60, 62, 63, 67, 70, 71, 75, 78, 79, 82, 85, 87, 89, 90, 91, 93, 91, 91, 90, 91, 89, 90, 90, 89, + 65, 61, 59, 58, 57, 55, 56, 58, 61, 63, 64, 68, 71, 72, 75, 79, 80, 83, 86, 88, 90, 91, 93, 94, 95, 92, 94, 92, 93, 92, 91, 93, + 66, 63, 60, 59, 58, 56, 58, 59, 62, 64, 65, 69, 72, 73, 76, 80, 81, 84, 87, 90, 91, 93, 94, 95, 96, 97, 95, 95, 95, 95, 95, 93, + 67, 64, 62, 61, 59, 58, 58, 60, 63, 64, 66, 69, 71, 73, 77, 78, 81, 85, 86, 89, 93, 94, 95, 97, 97, 98, 99, 97, 97, 97, 96, 98, + 68, 65, 63, 62, 60, 59, 58, 61, 62, 64, 67, 68, 71, 74, 75, 79, 81, 83, 87, 89, 91, 95, 96, 97, 99, 98, 100, 100, 100, 99, 100, 98, + 69, 66, 64, 63, 61, 61, 59, 61, 62, 65, 66, 68, 72, 73, 76, 78, 80, 84, 85, 88, 91, 92, 97, 98, 98, 101, 100, 102, 102, 103, 101, 102, + 70, 67, 65, 63, 62, 62, 60, 61, 63, 65, 66, 69, 71, 73, 76, 77, 81, 83, 85, 88, 90, 94, 95, 99, 100, 100, 103, 102, 104, 104, 105, 103, + 71, 67, 67, 64, 63, 63, 61, 61, 64, 65, 67, 69, 71, 74, 75, 78, 80, 83, 85, 87, 91, 92, 95, 97, 100, 102, 102, 105, 104, 106, 106, 108, + 72, 68, 68, 65, 65, 64, 62, 62, 64, 65, 68, 69, 72, 73, 76, 78, 80, 83, 84, 88, 89, 93, 95, 97, 100, 102, 104, 104, 107, 106, 108, 108, + 73, 69, 69, 66, 66, 65, 64, 63, 64, 66, 68, 69, 72, 73, 77, 77, 81, 82, 86, 87, 90, 92, 95, 97, 99, 103, 104, 106, 106, 109, 108, 110, + 74, 70, 70, 67, 67, 66, 65, 63, 64, 67, 68, 70, 72, 74, 76, 78, 80, 82, 85, 87, 90, 91, 95, 96, 100, 101, 105, 106, 108, 108, 111, 110, + 75, 71, 71, 68, 68, 66, 66, 64, 64, 68, 68, 71, 71, 75, 75, 79, 79, 83, 84, 88, 89, 93, 93, 98, 98, 102, 103, 108, 108, 110, 110, 113, + }, + }, { + { + 32, 31, 31, 31, 31, 32, 32, 34, 34, 36, 39, 41, 44, 48, 49, 54, 58, 59, 65, 69, 71, 80, 81, 83, 87, 90, 93, 95, 98, 101, 104, 107, + 31, 32, 32, 32, 32, 32, 32, 34, 34, 35, 38, 39, 42, 46, 47, 51, 55, 57, 62, 66, 68, 76, 77, 78, 83, 85, 88, 90, 93, 96, 99, 101, + 31, 32, 32, 32, 32, 32, 32, 33, 34, 34, 38, 39, 41, 45, 46, 50, 54, 55, 60, 64, 66, 73, 75, 76, 81, 83, 86, 89, 92, 95, 98, 101, + 31, 32, 32, 32, 32, 32, 32, 33, 34, 34, 37, 38, 41, 44, 45, 49, 53, 54, 59, 63, 65, 72, 74, 75, 79, 81, 84, 86, 89, 91, 94, 97, + 31, 32, 32, 32, 33, 33, 34, 35, 36, 36, 39, 40, 42, 45, 46, 50, 53, 54, 59, 63, 64, 71, 73, 74, 78, 80, 83, 85, 88, 91, 94, 97, + 32, 32, 32, 32, 33, 34, 34, 36, 36, 37, 40, 40, 42, 45, 46, 49, 53, 54, 58, 62, 63, 70, 72, 73, 77, 79, 82, 85, 87, 90, 92, 95, + 32, 32, 32, 32, 34, 34, 35, 37, 37, 38, 40, 41, 42, 45, 46, 49, 52, 54, 58, 61, 63, 69, 71, 72, 75, 78, 80, 83, 86, 89, 92, 95, + 34, 34, 33, 33, 35, 36, 37, 39, 41, 42, 45, 46, 47, 50, 51, 54, 57, 59, 63, 66, 68, 74, 75, 76, 80, 81, 82, 83, 85, 87, 90, 93, + 34, 34, 34, 34, 36, 36, 37, 41, 42, 45, 47, 48, 50, 53, 53, 56, 59, 61, 65, 68, 70, 76, 77, 78, 82, 83, 85, 88, 89, 90, 92, 93, + 36, 35, 34, 34, 36, 37, 38, 42, 45, 48, 50, 51, 54, 56, 57, 60, 63, 64, 68, 71, 73, 79, 80, 81, 85, 87, 89, 89, 90, 93, 96, 99, + 39, 38, 38, 37, 39, 40, 40, 45, 47, 50, 54, 55, 58, 61, 62, 65, 68, 69, 73, 76, 78, 84, 85, 86, 90, 89, 90, 93, 96, 97, 98, 99, + 41, 39, 39, 38, 40, 40, 41, 46, 48, 51, 55, 56, 59, 62, 63, 67, 70, 71, 75, 78, 80, 86, 87, 88, 91, 93, 96, 97, 97, 99, 102, 105, + 44, 42, 41, 41, 42, 42, 42, 47, 50, 54, 58, 59, 63, 66, 68, 71, 74, 75, 79, 83, 84, 90, 91, 92, 96, 98, 98, 99, 102, 104, 104, 105, + 48, 46, 45, 44, 45, 45, 45, 50, 53, 56, 61, 62, 66, 70, 71, 76, 79, 80, 85, 88, 90, 96, 97, 98, 101, 100, 102, 105, 105, 105, 109, 112, + 49, 47, 46, 45, 46, 46, 46, 51, 53, 57, 62, 63, 68, 71, 73, 77, 81, 82, 87, 90, 92, 98, 99, 100, 103, 106, 107, 106, 109, 112, 112, 112, + 54, 51, 50, 49, 50, 49, 49, 54, 56, 60, 65, 67, 71, 76, 77, 82, 86, 87, 92, 96, 97, 104, 105, 106, 110, 110, 109, 113, 114, 113, 116, 120, + 58, 55, 54, 53, 53, 53, 52, 57, 59, 63, 68, 70, 74, 79, 81, 86, 90, 91, 97, 100, 102, 109, 110, 111, 114, 114, 118, 116, 117, 121, 121, 120, + 59, 57, 55, 54, 54, 54, 54, 59, 61, 64, 69, 71, 75, 80, 82, 87, 91, 93, 99, 102, 104, 111, 112, 113, 117, 121, 120, 122, 124, 122, 125, 129, + 65, 62, 60, 59, 59, 58, 58, 63, 65, 68, 73, 75, 79, 85, 87, 92, 97, 99, 105, 109, 111, 118, 120, 121, 125, 124, 125, 127, 126, 130, 130, 129, + 69, 66, 64, 63, 63, 62, 61, 66, 68, 71, 76, 78, 83, 88, 90, 96, 100, 102, 109, 113, 115, 123, 125, 126, 129, 130, 131, 130, 134, 133, 135, 139, + 71, 68, 66, 65, 64, 63, 63, 68, 70, 73, 78, 80, 84, 90, 92, 97, 102, 104, 111, 115, 117, 125, 127, 128, 133, 136, 134, 139, 136, 139, 141, 140, + 80, 76, 73, 72, 71, 70, 69, 74, 76, 79, 84, 86, 90, 96, 98, 104, 109, 111, 118, 123, 125, 134, 136, 137, 142, 138, 143, 140, 144, 144, 144, 149, + 81, 77, 75, 74, 73, 72, 71, 75, 77, 80, 85, 87, 91, 97, 99, 105, 110, 112, 120, 125, 127, 136, 137, 139, 143, 148, 145, 148, 148, 150, 152, 149, + 83, 78, 76, 75, 74, 73, 72, 76, 78, 81, 86, 88, 92, 98, 100, 106, 111, 113, 121, 126, 128, 137, 139, 140, 145, 149, 153, 153, 154, 155, 155, 161, + 87, 83, 81, 79, 78, 77, 75, 80, 82, 85, 90, 91, 96, 101, 103, 110, 114, 117, 125, 129, 133, 142, 143, 145, 150, 151, 156, 159, 160, 160, 163, 161, + 90, 85, 83, 81, 80, 79, 78, 81, 83, 87, 89, 93, 98, 100, 106, 110, 114, 121, 124, 130, 136, 138, 148, 149, 151, 156, 157, 162, 166, 168, 166, 172, + 93, 88, 86, 84, 83, 82, 80, 82, 85, 89, 90, 96, 98, 102, 107, 109, 118, 120, 125, 131, 134, 143, 145, 153, 156, 157, 163, 164, 169, 172, 177, 172, + 95, 90, 89, 86, 85, 85, 83, 83, 88, 89, 93, 97, 99, 105, 106, 113, 116, 122, 127, 130, 139, 140, 148, 153, 159, 162, 164, 169, 170, 176, 179, 185, + 98, 93, 92, 89, 88, 87, 86, 85, 89, 90, 96, 97, 102, 105, 109, 114, 117, 124, 126, 134, 136, 144, 148, 154, 160, 166, 169, 170, 176, 177, 184, 186, + 101, 96, 95, 91, 91, 90, 89, 87, 90, 93, 97, 99, 104, 105, 112, 113, 121, 122, 130, 133, 139, 144, 150, 155, 160, 168, 172, 176, 177, 184, 185, 191, + 104, 99, 98, 94, 94, 92, 92, 90, 92, 96, 98, 102, 104, 109, 112, 116, 121, 125, 130, 135, 141, 144, 152, 155, 163, 166, 177, 179, 184, 185, 191, 192, + 107, 101, 101, 97, 97, 95, 95, 93, 93, 99, 99, 105, 105, 112, 112, 120, 120, 129, 129, 139, 140, 149, 149, 161, 161, 172, 172, 185, 186, 191, 192, 199, + }, { + 32, 31, 30, 30, 33, 35, 36, 41, 44, 49, 48, 48, 49, 50, 51, 52, 54, 55, 57, 59, 60, 63, 64, 65, 67, 68, 68, 69, 70, 71, 72, 73, + 31, 31, 31, 31, 34, 36, 38, 42, 44, 47, 47, 47, 47, 48, 48, 50, 51, 52, 54, 56, 57, 60, 61, 61, 63, 64, 65, 66, 67, 67, 68, 69, + 30, 31, 31, 31, 35, 37, 39, 42, 44, 47, 46, 46, 46, 47, 47, 48, 50, 51, 53, 54, 55, 58, 59, 60, 61, 63, 64, 65, 66, 67, 68, 69, + 30, 31, 31, 32, 35, 37, 40, 42, 44, 46, 45, 45, 45, 46, 46, 47, 49, 50, 52, 53, 54, 57, 58, 58, 60, 61, 62, 63, 63, 64, 65, 66, + 33, 34, 35, 35, 39, 41, 43, 45, 46, 47, 46, 46, 45, 46, 47, 47, 49, 49, 51, 53, 53, 56, 57, 57, 59, 60, 61, 62, 63, 64, 65, 66, + 35, 36, 37, 37, 41, 43, 45, 46, 46, 47, 46, 46, 45, 46, 46, 47, 48, 49, 50, 52, 53, 55, 56, 56, 58, 59, 60, 61, 62, 63, 64, 64, + 36, 38, 39, 40, 43, 45, 47, 47, 47, 48, 46, 46, 45, 46, 46, 47, 48, 48, 50, 51, 52, 54, 55, 55, 57, 58, 58, 59, 61, 62, 63, 64, + 41, 42, 42, 42, 45, 46, 47, 48, 49, 50, 49, 49, 49, 50, 50, 50, 51, 52, 53, 54, 55, 57, 58, 58, 60, 60, 59, 59, 60, 61, 61, 62, + 44, 44, 44, 44, 46, 46, 47, 49, 50, 51, 51, 51, 51, 51, 51, 52, 53, 53, 54, 56, 56, 59, 59, 59, 61, 61, 61, 62, 63, 62, 62, 62, + 49, 47, 47, 46, 47, 47, 48, 50, 51, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 58, 58, 60, 61, 61, 63, 63, 64, 63, 63, 64, 65, 66, + 48, 47, 46, 45, 46, 46, 46, 49, 51, 53, 54, 54, 55, 56, 56, 57, 58, 59, 60, 61, 61, 63, 64, 64, 65, 65, 64, 65, 66, 66, 66, 66, + 48, 47, 46, 45, 46, 46, 46, 49, 51, 53, 54, 55, 56, 57, 57, 58, 59, 60, 61, 62, 63, 65, 65, 65, 66, 67, 68, 67, 67, 67, 68, 69, + 49, 47, 46, 45, 45, 45, 45, 49, 51, 53, 55, 56, 58, 59, 60, 61, 62, 62, 63, 65, 65, 67, 68, 68, 69, 70, 69, 69, 69, 70, 69, 69, + 50, 48, 47, 46, 46, 46, 46, 50, 51, 54, 56, 57, 59, 61, 62, 63, 64, 65, 66, 68, 68, 70, 71, 71, 72, 71, 71, 72, 71, 71, 71, 72, + 51, 48, 47, 46, 47, 46, 46, 50, 51, 54, 56, 57, 60, 62, 62, 64, 65, 66, 67, 69, 69, 71, 72, 72, 73, 74, 74, 72, 73, 74, 73, 73, + 52, 50, 48, 47, 47, 47, 47, 50, 52, 54, 57, 58, 61, 63, 64, 66, 68, 68, 70, 72, 72, 75, 75, 75, 77, 76, 75, 76, 76, 74, 75, 76, + 54, 51, 50, 49, 49, 48, 48, 51, 53, 55, 58, 59, 62, 64, 65, 68, 70, 70, 73, 74, 75, 77, 78, 78, 79, 78, 79, 78, 77, 78, 77, 77, + 55, 52, 51, 50, 49, 49, 48, 52, 53, 55, 59, 60, 62, 65, 66, 68, 70, 71, 73, 75, 76, 78, 79, 79, 80, 81, 80, 80, 81, 79, 79, 81, + 57, 54, 53, 52, 51, 50, 50, 53, 54, 56, 60, 61, 63, 66, 67, 70, 73, 73, 76, 78, 79, 82, 82, 83, 84, 83, 83, 83, 82, 83, 82, 81, + 59, 56, 54, 53, 53, 52, 51, 54, 56, 58, 61, 62, 65, 68, 69, 72, 74, 75, 78, 80, 81, 84, 85, 85, 86, 86, 86, 84, 85, 84, 84, 85, + 60, 57, 55, 54, 53, 53, 52, 55, 56, 58, 61, 63, 65, 68, 69, 72, 75, 76, 79, 81, 82, 85, 86, 86, 88, 88, 87, 88, 86, 87, 87, 85, + 63, 60, 58, 57, 56, 55, 54, 57, 59, 60, 63, 65, 67, 70, 71, 75, 77, 78, 82, 84, 85, 89, 89, 90, 92, 89, 91, 89, 90, 89, 88, 89, + 64, 61, 59, 58, 57, 56, 55, 58, 59, 61, 64, 65, 68, 71, 72, 75, 78, 79, 82, 85, 86, 89, 90, 91, 93, 94, 92, 92, 91, 91, 92, 90, + 65, 61, 60, 58, 57, 56, 55, 58, 59, 61, 64, 65, 68, 71, 72, 75, 78, 79, 83, 85, 86, 90, 91, 91, 93, 94, 95, 94, 94, 94, 93, 94, + 67, 63, 61, 60, 59, 58, 57, 60, 61, 63, 65, 66, 69, 72, 73, 77, 79, 80, 84, 86, 88, 92, 93, 93, 95, 95, 96, 97, 96, 95, 96, 94, + 68, 64, 63, 61, 60, 59, 58, 60, 61, 63, 65, 67, 70, 71, 74, 76, 78, 81, 83, 86, 88, 89, 94, 94, 95, 97, 97, 98, 99, 99, 97, 99, + 68, 65, 64, 62, 61, 60, 58, 59, 61, 64, 64, 68, 69, 71, 74, 75, 79, 80, 83, 86, 87, 91, 92, 95, 96, 97, 99, 99, 100, 100, 101, 99, + 69, 66, 65, 63, 62, 61, 59, 59, 62, 63, 65, 67, 69, 72, 72, 76, 78, 80, 83, 84, 88, 89, 92, 94, 97, 98, 99, 101, 100, 102, 102, 104, + 70, 67, 66, 63, 63, 62, 61, 60, 63, 63, 66, 67, 69, 71, 73, 76, 77, 81, 82, 85, 86, 90, 91, 94, 96, 99, 100, 100, 103, 102, 104, 104, + 71, 67, 67, 64, 64, 63, 62, 61, 62, 64, 66, 67, 70, 71, 74, 74, 78, 79, 83, 84, 87, 89, 91, 94, 95, 99, 100, 102, 102, 104, 104, 106, + 72, 68, 68, 65, 65, 64, 63, 61, 62, 65, 66, 68, 69, 71, 73, 75, 77, 79, 82, 84, 87, 88, 92, 93, 96, 97, 101, 102, 104, 104, 106, 106, + 73, 69, 69, 66, 66, 64, 64, 62, 62, 66, 66, 69, 69, 72, 73, 76, 77, 81, 81, 85, 85, 89, 90, 94, 94, 99, 99, 104, 104, 106, 106, 108, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 32, 32, 34, 35, 36, 39, 41, 44, 47, 48, 54, 56, 59, 64, 65, 71, 74, 80, 82, 83, 87, 90, 92, 95, 97, 100, + 31, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 38, 40, 42, 45, 46, 51, 53, 56, 61, 62, 68, 71, 76, 78, 78, 83, 85, 88, 90, 92, 95, + 31, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 38, 39, 42, 45, 45, 50, 52, 55, 60, 61, 67, 69, 74, 76, 77, 81, 84, 87, 89, 92, 95, + 31, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 38, 41, 44, 44, 49, 51, 54, 58, 59, 65, 68, 72, 74, 75, 79, 81, 84, 86, 88, 90, + 31, 32, 32, 32, 33, 33, 33, 34, 35, 36, 36, 39, 40, 42, 44, 45, 49, 51, 54, 58, 59, 64, 67, 71, 73, 74, 78, 80, 83, 85, 88, 90, + 31, 32, 32, 32, 33, 33, 34, 34, 35, 36, 36, 39, 40, 42, 45, 45, 50, 51, 54, 58, 59, 64, 67, 71, 73, 74, 78, 80, 82, 84, 86, 89, + 32, 32, 32, 32, 33, 34, 35, 36, 37, 38, 38, 40, 41, 42, 45, 46, 49, 51, 53, 57, 58, 63, 65, 69, 71, 72, 75, 78, 80, 83, 86, 89, + 32, 33, 33, 33, 34, 34, 36, 36, 38, 39, 40, 42, 43, 44, 47, 47, 51, 53, 55, 59, 60, 65, 67, 71, 73, 73, 77, 78, 80, 82, 84, 86, + 34, 34, 34, 33, 35, 35, 37, 38, 39, 42, 42, 45, 46, 47, 50, 51, 54, 56, 58, 62, 63, 68, 70, 74, 76, 76, 80, 82, 84, 85, 85, 86, + 35, 35, 34, 34, 36, 36, 38, 39, 42, 46, 47, 49, 50, 52, 55, 55, 59, 60, 62, 66, 67, 72, 74, 78, 79, 80, 83, 84, 85, 87, 90, 92, + 36, 35, 35, 34, 36, 36, 38, 40, 42, 47, 48, 50, 52, 54, 56, 57, 60, 61, 64, 67, 68, 73, 75, 79, 80, 81, 85, 87, 90, 91, 91, 92, + 39, 38, 38, 37, 39, 39, 40, 42, 45, 49, 50, 54, 55, 58, 60, 61, 65, 66, 69, 72, 73, 78, 80, 84, 86, 86, 90, 91, 91, 92, 95, 97, + 41, 40, 39, 38, 40, 40, 41, 43, 46, 50, 52, 55, 57, 60, 62, 63, 67, 69, 71, 75, 75, 80, 83, 86, 88, 89, 92, 93, 95, 97, 97, 98, + 44, 42, 42, 41, 42, 42, 42, 44, 47, 52, 54, 58, 60, 63, 66, 67, 71, 73, 75, 79, 79, 84, 86, 90, 92, 92, 96, 98, 98, 98, 101, 104, + 47, 45, 45, 44, 44, 45, 45, 47, 50, 55, 56, 60, 62, 66, 69, 70, 75, 77, 79, 83, 84, 89, 91, 95, 97, 97, 100, 99, 102, 105, 104, 104, + 48, 46, 45, 44, 45, 45, 46, 47, 51, 55, 57, 61, 63, 67, 70, 71, 76, 78, 80, 84, 85, 90, 93, 96, 98, 99, 102, 106, 106, 105, 108, 111, + 54, 51, 50, 49, 49, 50, 49, 51, 54, 59, 60, 65, 67, 71, 75, 76, 82, 84, 87, 91, 92, 97, 100, 104, 106, 106, 110, 108, 109, 112, 112, 111, + 56, 53, 52, 51, 51, 51, 51, 53, 56, 60, 61, 66, 69, 73, 77, 78, 84, 86, 89, 93, 94, 100, 102, 106, 108, 109, 112, 113, 115, 114, 116, 119, + 59, 56, 55, 54, 54, 54, 53, 55, 58, 62, 64, 69, 71, 75, 79, 80, 87, 89, 92, 97, 98, 103, 106, 110, 112, 113, 117, 118, 117, 121, 121, 119, + 64, 61, 60, 58, 58, 58, 57, 59, 62, 66, 67, 72, 75, 79, 83, 84, 91, 93, 97, 102, 103, 109, 112, 116, 118, 119, 122, 121, 125, 123, 125, 128, + 65, 62, 61, 59, 59, 59, 58, 60, 63, 67, 68, 73, 75, 79, 84, 85, 92, 94, 98, 103, 105, 111, 114, 118, 120, 121, 125, 129, 126, 129, 130, 129, + 71, 68, 67, 65, 64, 64, 63, 65, 68, 72, 73, 78, 80, 84, 89, 90, 97, 100, 103, 109, 111, 117, 120, 125, 127, 128, 133, 130, 134, 133, 133, 137, + 74, 71, 69, 68, 67, 67, 65, 67, 70, 74, 75, 80, 83, 86, 91, 93, 100, 102, 106, 112, 114, 120, 123, 128, 131, 131, 135, 137, 137, 138, 140, 137, + 80, 76, 74, 72, 71, 71, 69, 71, 74, 78, 79, 84, 86, 90, 95, 96, 104, 106, 110, 116, 118, 125, 128, 134, 136, 137, 142, 141, 142, 143, 143, 147, + 82, 78, 76, 74, 73, 73, 71, 73, 76, 79, 80, 86, 88, 92, 97, 98, 106, 108, 112, 118, 120, 127, 131, 136, 139, 139, 144, 147, 148, 147, 150, 148, + 83, 78, 77, 75, 74, 74, 72, 73, 76, 80, 81, 86, 89, 92, 97, 99, 106, 109, 113, 119, 121, 128, 131, 137, 139, 140, 145, 150, 152, 155, 152, 157, + 87, 83, 81, 79, 78, 78, 75, 77, 80, 83, 85, 90, 92, 96, 100, 102, 110, 112, 117, 122, 125, 133, 135, 142, 144, 145, 150, 151, 155, 158, 162, 158, + 90, 85, 84, 81, 80, 80, 78, 78, 82, 84, 87, 91, 93, 98, 99, 106, 108, 113, 118, 121, 129, 130, 137, 141, 147, 150, 151, 156, 156, 161, 164, 169, + 92, 88, 87, 84, 83, 82, 80, 80, 84, 85, 90, 91, 95, 98, 102, 106, 109, 115, 117, 125, 126, 134, 137, 142, 148, 152, 155, 156, 162, 162, 168, 170, + 95, 90, 89, 86, 85, 84, 83, 82, 85, 87, 91, 92, 97, 98, 105, 105, 112, 114, 121, 123, 129, 133, 138, 143, 147, 155, 158, 161, 162, 168, 168, 174, + 97, 92, 92, 88, 88, 86, 86, 84, 85, 90, 91, 95, 97, 101, 104, 108, 112, 116, 121, 125, 130, 133, 140, 143, 150, 152, 162, 164, 168, 168, 174, 175, + 100, 95, 95, 90, 90, 89, 89, 86, 86, 92, 92, 97, 98, 104, 104, 111, 111, 119, 119, 128, 129, 137, 137, 147, 148, 157, 158, 169, 170, 174, 175, 181, + }, { + 32, 31, 31, 30, 33, 33, 36, 38, 41, 47, 49, 48, 49, 49, 50, 50, 52, 53, 54, 56, 57, 60, 61, 63, 64, 65, 67, 67, 68, 69, 70, 71, + 31, 31, 31, 31, 34, 34, 38, 40, 42, 46, 47, 47, 47, 47, 48, 48, 50, 50, 52, 54, 54, 57, 58, 60, 61, 61, 63, 64, 65, 65, 66, 67, + 31, 31, 31, 31, 34, 35, 39, 40, 42, 46, 47, 46, 46, 46, 47, 47, 49, 50, 51, 53, 53, 56, 57, 59, 60, 60, 62, 63, 64, 65, 66, 67, + 30, 31, 31, 32, 34, 35, 40, 41, 42, 45, 46, 45, 45, 45, 46, 46, 47, 48, 49, 51, 52, 54, 55, 57, 58, 58, 60, 61, 62, 62, 63, 64, + 33, 34, 34, 34, 37, 38, 42, 43, 44, 46, 47, 46, 46, 45, 46, 46, 47, 48, 49, 51, 51, 53, 55, 56, 57, 57, 59, 60, 61, 62, 63, 64, + 33, 34, 35, 35, 38, 39, 43, 44, 45, 47, 47, 46, 46, 45, 46, 46, 47, 48, 49, 51, 51, 53, 54, 56, 57, 57, 59, 60, 60, 61, 62, 62, + 36, 38, 39, 40, 42, 43, 47, 47, 47, 47, 48, 46, 46, 45, 46, 46, 47, 47, 48, 49, 50, 52, 53, 54, 55, 55, 57, 58, 59, 60, 61, 62, + 38, 40, 40, 41, 43, 44, 47, 47, 48, 48, 49, 48, 47, 47, 47, 47, 48, 49, 49, 51, 51, 53, 54, 55, 56, 56, 58, 58, 58, 59, 60, 60, + 41, 42, 42, 42, 44, 45, 47, 48, 48, 50, 50, 49, 49, 49, 50, 50, 50, 51, 52, 53, 53, 55, 56, 57, 58, 58, 60, 61, 61, 61, 60, 60, + 47, 46, 46, 45, 46, 47, 47, 48, 50, 52, 52, 52, 52, 52, 53, 53, 53, 54, 55, 55, 56, 58, 58, 60, 60, 61, 62, 61, 61, 62, 63, 64, + 49, 47, 47, 46, 47, 47, 48, 49, 50, 52, 53, 53, 53, 53, 54, 54, 54, 55, 55, 56, 56, 58, 59, 60, 61, 61, 63, 63, 64, 64, 64, 64, + 48, 47, 46, 45, 46, 46, 46, 48, 49, 52, 53, 54, 55, 55, 56, 56, 57, 58, 58, 59, 60, 61, 62, 63, 64, 64, 66, 65, 65, 65, 66, 67, + 49, 47, 46, 45, 46, 46, 46, 47, 49, 52, 53, 55, 55, 57, 57, 58, 59, 59, 60, 61, 61, 63, 64, 65, 66, 66, 67, 67, 67, 68, 67, 67, + 49, 47, 46, 45, 45, 45, 45, 47, 49, 52, 53, 55, 57, 58, 59, 60, 61, 62, 62, 63, 63, 65, 66, 67, 68, 68, 69, 70, 69, 68, 69, 70, + 50, 48, 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 57, 59, 61, 61, 63, 64, 64, 66, 66, 68, 69, 70, 70, 71, 72, 70, 71, 72, 71, 70, + 50, 48, 47, 46, 46, 46, 46, 47, 50, 53, 54, 56, 58, 60, 61, 61, 63, 64, 65, 66, 67, 68, 69, 71, 71, 71, 73, 74, 73, 72, 73, 74, + 52, 50, 49, 47, 47, 47, 47, 48, 50, 53, 54, 57, 59, 61, 63, 63, 66, 67, 68, 70, 70, 72, 73, 75, 75, 75, 77, 75, 75, 76, 75, 74, + 53, 50, 50, 48, 48, 48, 47, 49, 51, 54, 55, 58, 59, 62, 64, 64, 67, 68, 69, 71, 71, 73, 74, 76, 77, 77, 78, 78, 78, 76, 77, 78, + 54, 52, 51, 49, 49, 49, 48, 49, 52, 55, 55, 58, 60, 62, 64, 65, 68, 69, 71, 73, 73, 75, 76, 78, 79, 79, 80, 80, 79, 80, 79, 78, + 56, 54, 53, 51, 51, 51, 49, 51, 53, 55, 56, 59, 61, 63, 66, 66, 70, 71, 73, 75, 76, 78, 79, 81, 82, 82, 83, 81, 83, 81, 81, 82, + 57, 54, 53, 52, 51, 51, 50, 51, 53, 56, 56, 60, 61, 63, 66, 67, 70, 71, 73, 76, 76, 79, 80, 82, 82, 83, 84, 85, 83, 84, 84, 82, + 60, 57, 56, 54, 53, 53, 52, 53, 55, 58, 58, 61, 63, 65, 68, 68, 72, 73, 75, 78, 79, 82, 83, 85, 86, 86, 88, 86, 87, 86, 85, 86, + 61, 58, 57, 55, 55, 54, 53, 54, 56, 58, 59, 62, 64, 66, 69, 69, 73, 74, 76, 79, 80, 83, 84, 86, 87, 88, 89, 89, 88, 88, 88, 86, + 63, 60, 59, 57, 56, 56, 54, 55, 57, 60, 60, 63, 65, 67, 70, 71, 75, 76, 78, 81, 82, 85, 86, 89, 90, 90, 92, 91, 91, 90, 89, 91, + 64, 61, 60, 58, 57, 57, 55, 56, 58, 60, 61, 64, 66, 68, 70, 71, 75, 77, 79, 82, 82, 86, 87, 90, 91, 91, 93, 93, 93, 92, 93, 91, + 65, 61, 60, 58, 57, 57, 55, 56, 58, 61, 61, 64, 66, 68, 71, 71, 75, 77, 79, 82, 83, 86, 88, 90, 91, 91, 93, 94, 95, 95, 93, 95, + 67, 63, 62, 60, 59, 59, 57, 58, 60, 62, 63, 66, 67, 69, 72, 73, 77, 78, 80, 83, 84, 88, 89, 92, 93, 93, 95, 95, 96, 96, 97, 95, + 67, 64, 63, 61, 60, 60, 58, 58, 61, 61, 63, 65, 67, 70, 70, 74, 75, 78, 80, 81, 85, 86, 89, 91, 93, 94, 95, 97, 97, 98, 98, 100, + 68, 65, 64, 62, 61, 60, 59, 58, 61, 61, 64, 65, 67, 69, 71, 73, 75, 78, 79, 83, 83, 87, 88, 91, 93, 95, 96, 97, 99, 98, 100, 100, + 69, 65, 65, 62, 62, 61, 60, 59, 61, 62, 64, 65, 68, 68, 72, 72, 76, 76, 80, 81, 84, 86, 88, 90, 92, 95, 96, 98, 98, 100, 100, 101, + 70, 66, 66, 63, 63, 62, 61, 60, 60, 63, 64, 66, 67, 69, 71, 73, 75, 77, 79, 81, 84, 85, 88, 89, 93, 93, 97, 98, 100, 100, 102, 101, + 71, 67, 67, 64, 64, 62, 62, 60, 60, 64, 64, 67, 67, 70, 70, 74, 74, 78, 78, 82, 82, 86, 86, 91, 91, 95, 95, 100, 100, 101, 101, 104, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 32, 32, 33, 34, 36, 36, 39, 41, 44, 46, 48, 52, 54, 58, 59, 65, 66, 71, 74, 80, 81, 83, 86, 89, 91, 93, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 38, 39, 42, 44, 46, 50, 51, 56, 56, 62, 63, 68, 71, 76, 77, 78, 82, 84, 86, 88, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 35, 38, 39, 42, 44, 46, 49, 51, 55, 56, 61, 63, 67, 70, 75, 77, 78, 81, 84, 86, 88, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 38, 41, 42, 44, 48, 49, 53, 54, 59, 60, 65, 68, 72, 74, 75, 78, 80, 82, 84, + 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 38, 39, 41, 43, 45, 48, 49, 53, 54, 59, 60, 65, 67, 72, 73, 74, 77, 80, 82, 84, + 31, 32, 32, 32, 33, 33, 33, 34, 35, 35, 36, 36, 39, 40, 42, 44, 45, 48, 50, 53, 54, 59, 60, 64, 67, 71, 73, 74, 77, 79, 81, 83, + 32, 32, 32, 32, 33, 33, 34, 35, 36, 36, 37, 38, 40, 40, 42, 44, 45, 48, 49, 53, 53, 58, 59, 63, 66, 70, 71, 72, 75, 78, 80, 83, + 32, 32, 32, 32, 33, 34, 35, 35, 36, 37, 38, 38, 40, 41, 42, 44, 46, 48, 49, 53, 53, 58, 59, 63, 65, 69, 71, 72, 74, 77, 79, 80, + 33, 33, 33, 33, 34, 35, 36, 36, 38, 39, 42, 42, 44, 45, 46, 48, 50, 52, 53, 57, 57, 62, 63, 67, 69, 73, 74, 75, 78, 79, 80, 81, + 34, 34, 34, 33, 34, 35, 36, 37, 39, 39, 42, 43, 45, 46, 47, 49, 51, 53, 54, 58, 58, 63, 64, 68, 70, 74, 75, 76, 79, 81, 84, 86, + 36, 35, 35, 34, 35, 36, 37, 38, 42, 42, 48, 48, 50, 51, 54, 55, 57, 59, 60, 63, 64, 68, 69, 73, 75, 79, 80, 81, 84, 85, 85, 86, + 36, 35, 35, 34, 35, 36, 38, 38, 42, 43, 48, 49, 51, 52, 54, 55, 57, 59, 60, 64, 64, 68, 69, 73, 75, 79, 80, 81, 84, 86, 88, 91, + 39, 38, 38, 37, 38, 39, 40, 40, 44, 45, 50, 51, 54, 55, 58, 59, 61, 64, 65, 68, 69, 73, 74, 78, 80, 84, 85, 86, 89, 91, 91, 91, + 41, 39, 39, 38, 39, 40, 40, 41, 45, 46, 51, 52, 55, 56, 59, 61, 63, 65, 67, 70, 70, 75, 76, 80, 82, 86, 87, 88, 91, 92, 94, 96, + 44, 42, 42, 41, 41, 42, 42, 42, 46, 47, 54, 54, 58, 59, 63, 65, 67, 70, 71, 75, 75, 79, 80, 84, 86, 90, 91, 92, 95, 97, 97, 97, + 46, 44, 44, 42, 43, 44, 44, 44, 48, 49, 55, 55, 59, 61, 65, 67, 69, 72, 74, 77, 78, 82, 83, 87, 89, 93, 94, 95, 98, 98, 100, 103, + 48, 46, 46, 44, 45, 45, 45, 46, 50, 51, 57, 57, 61, 63, 67, 69, 71, 74, 76, 80, 80, 85, 86, 90, 93, 96, 98, 99, 101, 104, 104, 103, + 52, 50, 49, 48, 48, 48, 48, 48, 52, 53, 59, 59, 64, 65, 70, 72, 74, 78, 80, 84, 85, 90, 91, 95, 97, 101, 103, 104, 106, 106, 107, 110, + 54, 51, 51, 49, 49, 50, 49, 49, 53, 54, 60, 60, 65, 67, 71, 74, 76, 80, 82, 86, 87, 92, 93, 97, 100, 104, 105, 106, 109, 112, 112, 110, + 58, 56, 55, 53, 53, 53, 53, 53, 57, 58, 63, 64, 68, 70, 75, 77, 80, 84, 86, 91, 91, 97, 98, 103, 105, 110, 111, 112, 115, 114, 115, 118, + 59, 56, 56, 54, 54, 54, 53, 53, 57, 58, 64, 64, 69, 70, 75, 78, 80, 85, 87, 91, 92, 98, 99, 103, 106, 110, 112, 113, 116, 119, 120, 119, + 65, 62, 61, 59, 59, 59, 58, 58, 62, 63, 68, 68, 73, 75, 79, 82, 85, 90, 92, 97, 98, 105, 106, 111, 114, 118, 120, 121, 124, 123, 123, 126, + 66, 63, 63, 60, 60, 60, 59, 59, 63, 64, 69, 69, 74, 76, 80, 83, 86, 91, 93, 98, 99, 106, 107, 112, 115, 119, 121, 122, 125, 128, 129, 126, + 71, 68, 67, 65, 65, 64, 63, 63, 67, 68, 73, 73, 78, 80, 84, 87, 90, 95, 97, 103, 103, 111, 112, 117, 120, 125, 127, 128, 131, 132, 132, 135, + 74, 71, 70, 68, 67, 67, 66, 65, 69, 70, 75, 75, 80, 82, 86, 89, 93, 97, 100, 105, 106, 114, 115, 120, 123, 128, 130, 131, 135, 135, 138, 136, + 80, 76, 75, 72, 72, 71, 70, 69, 73, 74, 79, 79, 84, 86, 90, 93, 96, 101, 104, 110, 110, 118, 119, 125, 128, 134, 136, 137, 140, 142, 140, 144, + 81, 77, 77, 74, 73, 73, 71, 71, 74, 75, 80, 80, 85, 87, 91, 94, 98, 103, 105, 111, 112, 120, 121, 127, 130, 136, 137, 139, 142, 145, 148, 144, + 83, 78, 78, 75, 74, 74, 72, 72, 75, 76, 81, 81, 86, 88, 92, 95, 99, 104, 106, 112, 113, 121, 122, 128, 131, 137, 139, 140, 144, 148, 150, 155, + 86, 82, 81, 78, 77, 77, 75, 74, 78, 79, 84, 84, 89, 91, 95, 98, 101, 106, 109, 115, 116, 124, 125, 131, 135, 140, 142, 144, 147, 149, 153, 155, + 89, 84, 84, 80, 80, 79, 78, 77, 79, 81, 85, 86, 91, 92, 97, 98, 104, 106, 112, 114, 119, 123, 128, 132, 135, 142, 145, 148, 149, 153, 154, 159, + 91, 86, 86, 82, 82, 81, 80, 79, 80, 84, 85, 88, 91, 94, 97, 100, 104, 107, 112, 115, 120, 123, 129, 132, 138, 140, 148, 150, 153, 154, 159, 159, + 93, 88, 88, 84, 84, 83, 83, 80, 81, 86, 86, 91, 91, 96, 97, 103, 103, 110, 110, 118, 119, 126, 126, 135, 136, 144, 144, 155, 155, 159, 159, 164, + }, { + 32, 31, 31, 30, 31, 33, 35, 36, 40, 41, 49, 49, 48, 48, 49, 50, 50, 52, 52, 54, 54, 57, 57, 60, 61, 63, 64, 65, 66, 67, 68, 69, + 31, 31, 31, 31, 32, 34, 37, 38, 41, 42, 47, 47, 47, 47, 47, 47, 48, 49, 50, 52, 52, 54, 55, 57, 58, 60, 61, 61, 63, 64, 64, 65, + 31, 31, 31, 31, 32, 35, 37, 39, 41, 42, 47, 47, 47, 46, 46, 47, 48, 49, 49, 51, 51, 54, 54, 56, 57, 59, 60, 61, 62, 63, 64, 65, + 30, 31, 31, 32, 33, 35, 38, 40, 42, 42, 46, 46, 45, 45, 45, 45, 46, 47, 47, 49, 49, 52, 52, 54, 55, 57, 58, 58, 60, 61, 61, 62, + 31, 32, 32, 33, 34, 37, 39, 41, 43, 43, 46, 46, 46, 45, 45, 46, 46, 47, 47, 49, 49, 51, 52, 54, 55, 57, 57, 58, 59, 60, 61, 62, + 33, 34, 35, 35, 37, 39, 41, 43, 44, 45, 47, 47, 46, 46, 45, 46, 46, 47, 47, 49, 49, 51, 51, 53, 54, 56, 57, 57, 58, 59, 60, 61, + 35, 37, 37, 38, 39, 41, 44, 46, 46, 46, 47, 47, 46, 46, 45, 46, 46, 47, 47, 48, 48, 50, 51, 52, 53, 55, 56, 56, 57, 58, 59, 61, + 36, 38, 39, 40, 41, 43, 46, 47, 47, 47, 48, 47, 46, 46, 45, 46, 46, 46, 47, 48, 48, 50, 50, 52, 53, 54, 55, 55, 56, 57, 58, 58, + 40, 41, 41, 42, 43, 44, 46, 47, 48, 48, 50, 49, 49, 49, 48, 49, 49, 49, 49, 51, 51, 52, 53, 54, 55, 57, 57, 58, 59, 59, 59, 59, + 41, 42, 42, 42, 43, 45, 46, 47, 48, 48, 50, 50, 49, 49, 49, 49, 50, 50, 50, 52, 52, 53, 53, 55, 56, 57, 58, 58, 59, 60, 61, 62, + 49, 47, 47, 46, 46, 47, 47, 48, 50, 50, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 57, 58, 59, 60, 61, 61, 62, 62, 62, 62, + 49, 47, 47, 46, 46, 47, 47, 47, 49, 50, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 56, 57, 57, 59, 59, 61, 61, 62, 63, 63, 64, 65, + 48, 47, 47, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 54, 55, 56, 56, 57, 57, 58, 58, 60, 60, 61, 62, 63, 64, 64, 65, 66, 65, 65, + 48, 47, 46, 45, 45, 46, 46, 46, 49, 49, 53, 53, 54, 55, 56, 57, 57, 58, 58, 59, 60, 61, 61, 63, 63, 65, 65, 65, 66, 66, 67, 68, + 49, 47, 46, 45, 45, 45, 45, 45, 48, 49, 53, 54, 55, 56, 58, 59, 60, 61, 61, 62, 62, 63, 64, 65, 66, 67, 68, 68, 69, 70, 69, 68, + 50, 47, 47, 45, 46, 46, 46, 46, 49, 49, 54, 54, 56, 57, 59, 60, 60, 62, 62, 63, 64, 65, 65, 67, 68, 69, 69, 70, 70, 70, 71, 71, + 50, 48, 48, 46, 46, 46, 46, 46, 49, 50, 54, 54, 56, 57, 60, 60, 61, 63, 63, 65, 65, 67, 67, 68, 69, 71, 71, 71, 72, 73, 72, 71, + 52, 49, 49, 47, 47, 47, 47, 46, 49, 50, 54, 54, 57, 58, 61, 62, 63, 65, 65, 67, 67, 69, 70, 71, 72, 73, 74, 74, 75, 74, 74, 75, + 52, 50, 49, 47, 47, 47, 47, 47, 49, 50, 54, 54, 57, 58, 61, 62, 63, 65, 66, 68, 68, 70, 71, 72, 73, 75, 75, 75, 76, 77, 77, 75, + 54, 52, 51, 49, 49, 49, 48, 48, 51, 52, 55, 55, 58, 59, 62, 63, 65, 67, 68, 70, 70, 73, 73, 75, 76, 78, 78, 78, 79, 78, 78, 79, + 54, 52, 51, 49, 49, 49, 48, 48, 51, 52, 55, 56, 58, 60, 62, 64, 65, 67, 68, 70, 71, 73, 74, 75, 76, 78, 78, 79, 80, 81, 81, 79, + 57, 54, 54, 52, 51, 51, 50, 50, 52, 53, 56, 57, 60, 61, 63, 65, 67, 69, 70, 73, 73, 76, 77, 79, 80, 82, 82, 83, 84, 83, 82, 83, + 57, 55, 54, 52, 52, 51, 51, 50, 53, 53, 57, 57, 60, 61, 64, 65, 67, 70, 71, 73, 74, 77, 77, 79, 80, 82, 83, 83, 84, 85, 85, 83, + 60, 57, 56, 54, 54, 53, 52, 52, 54, 55, 58, 59, 61, 63, 65, 67, 68, 71, 72, 75, 75, 79, 79, 82, 83, 85, 86, 86, 87, 87, 86, 87, + 61, 58, 57, 55, 55, 54, 53, 53, 55, 56, 59, 59, 62, 63, 66, 68, 69, 72, 73, 76, 76, 80, 80, 83, 84, 86, 87, 88, 89, 89, 89, 87, + 63, 60, 59, 57, 57, 56, 55, 54, 57, 57, 60, 61, 63, 65, 67, 69, 71, 73, 75, 78, 78, 82, 82, 85, 86, 89, 89, 90, 91, 92, 90, 91, + 64, 61, 60, 58, 57, 57, 56, 55, 57, 58, 61, 61, 64, 65, 68, 69, 71, 74, 75, 78, 78, 82, 83, 86, 87, 89, 90, 91, 92, 93, 94, 91, + 65, 61, 61, 58, 58, 57, 56, 55, 58, 58, 61, 62, 64, 65, 68, 70, 71, 74, 75, 78, 79, 83, 83, 86, 88, 90, 91, 91, 93, 94, 94, 96, + 66, 63, 62, 60, 59, 58, 57, 56, 59, 59, 62, 63, 65, 66, 69, 70, 72, 75, 76, 79, 80, 84, 84, 87, 89, 91, 92, 93, 94, 94, 96, 96, + 67, 64, 63, 61, 60, 59, 58, 57, 59, 60, 62, 63, 66, 66, 70, 70, 73, 74, 77, 78, 81, 83, 85, 87, 89, 92, 93, 94, 94, 96, 96, 97, + 68, 64, 64, 61, 61, 60, 59, 58, 59, 61, 62, 64, 65, 67, 69, 71, 72, 74, 77, 78, 81, 82, 85, 86, 89, 90, 94, 94, 96, 96, 98, 97, + 69, 65, 65, 62, 62, 61, 61, 58, 59, 62, 62, 65, 65, 68, 68, 71, 71, 75, 75, 79, 79, 83, 83, 87, 87, 91, 91, 96, 96, 97, 97, 99, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 32, 34, 34, 36, 36, 39, 39, 44, 44, 48, 48, 54, 54, 59, 59, 65, 65, 71, 71, 80, 80, 83, 83, 87, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 38, 38, 42, 42, 46, 46, 51, 51, 56, 56, 62, 62, 68, 68, 76, 76, 78, 78, 83, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 38, 38, 42, 42, 46, 46, 51, 51, 56, 56, 62, 62, 68, 68, 76, 76, 78, 78, 83, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 37, 41, 41, 44, 44, 49, 49, 54, 54, 59, 59, 65, 65, 72, 72, 75, 75, 79, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 37, 37, 41, 41, 44, 44, 49, 49, 54, 54, 59, 59, 65, 65, 72, 72, 75, 75, 79, + 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 39, 39, 42, 42, 45, 45, 50, 50, 54, 54, 59, 59, 64, 64, 71, 71, 74, 74, 77, + 31, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 36, 36, 39, 39, 42, 42, 45, 45, 50, 50, 54, 54, 59, 59, 64, 64, 71, 71, 74, 74, 77, + 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 42, 42, 46, 46, 49, 49, 53, 53, 58, 58, 63, 63, 69, 69, 72, 72, 75, + 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 37, 38, 38, 40, 40, 42, 42, 46, 46, 49, 49, 53, 53, 58, 58, 63, 63, 69, 69, 72, 72, 75, + 34, 34, 34, 33, 33, 35, 35, 37, 37, 39, 39, 42, 42, 45, 45, 47, 47, 51, 51, 54, 54, 58, 58, 63, 63, 68, 68, 74, 74, 76, 76, 80, + 34, 34, 34, 33, 33, 35, 35, 37, 37, 39, 39, 42, 42, 45, 45, 47, 47, 51, 51, 54, 54, 58, 58, 63, 63, 68, 68, 74, 74, 76, 76, 80, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 54, 54, 57, 57, 60, 60, 64, 64, 68, 68, 73, 73, 79, 79, 81, 81, 84, + 36, 35, 35, 34, 34, 36, 36, 38, 38, 42, 42, 48, 48, 50, 50, 54, 54, 57, 57, 60, 60, 64, 64, 68, 68, 73, 73, 79, 79, 81, 81, 84, + 39, 38, 38, 37, 37, 39, 39, 40, 40, 45, 45, 50, 50, 54, 54, 58, 58, 61, 61, 65, 65, 69, 69, 73, 73, 78, 78, 84, 84, 86, 86, 90, + 39, 38, 38, 37, 37, 39, 39, 40, 40, 45, 45, 50, 50, 54, 54, 58, 58, 61, 61, 65, 65, 69, 69, 73, 73, 78, 78, 84, 84, 86, 86, 90, + 44, 42, 42, 41, 41, 42, 42, 42, 42, 47, 47, 54, 54, 58, 58, 63, 63, 67, 67, 71, 71, 75, 75, 79, 79, 84, 84, 90, 90, 92, 92, 96, + 44, 42, 42, 41, 41, 42, 42, 42, 42, 47, 47, 54, 54, 58, 58, 63, 63, 67, 67, 71, 71, 75, 75, 79, 79, 84, 84, 90, 90, 92, 92, 96, + 48, 46, 46, 44, 44, 45, 45, 46, 46, 51, 51, 57, 57, 61, 61, 67, 67, 71, 71, 76, 76, 80, 80, 85, 85, 90, 90, 96, 96, 99, 99, 102, + 48, 46, 46, 44, 44, 45, 45, 46, 46, 51, 51, 57, 57, 61, 61, 67, 67, 71, 71, 76, 76, 80, 80, 85, 85, 90, 90, 96, 96, 99, 99, 102, + 54, 51, 51, 49, 49, 50, 50, 49, 49, 54, 54, 60, 60, 65, 65, 71, 71, 76, 76, 82, 82, 87, 87, 92, 92, 97, 97, 104, 104, 106, 106, 109, + 54, 51, 51, 49, 49, 50, 50, 49, 49, 54, 54, 60, 60, 65, 65, 71, 71, 76, 76, 82, 82, 87, 87, 92, 92, 97, 97, 104, 104, 106, 106, 109, + 59, 56, 56, 54, 54, 54, 54, 53, 53, 58, 58, 64, 64, 69, 69, 75, 75, 80, 80, 87, 87, 92, 92, 98, 98, 103, 103, 110, 110, 113, 113, 116, + 59, 56, 56, 54, 54, 54, 54, 53, 53, 58, 58, 64, 64, 69, 69, 75, 75, 80, 80, 87, 87, 92, 92, 98, 98, 103, 103, 110, 110, 113, 113, 116, + 65, 62, 62, 59, 59, 59, 59, 58, 58, 63, 63, 68, 68, 73, 73, 79, 79, 85, 85, 92, 92, 98, 98, 105, 105, 111, 111, 118, 118, 121, 121, 124, + 65, 62, 62, 59, 59, 59, 59, 58, 58, 63, 63, 68, 68, 73, 73, 79, 79, 85, 85, 92, 92, 98, 98, 105, 105, 111, 111, 118, 118, 121, 121, 124, + 71, 68, 68, 65, 65, 64, 64, 63, 63, 68, 68, 73, 73, 78, 78, 84, 84, 90, 90, 97, 97, 103, 103, 111, 111, 117, 117, 125, 125, 128, 128, 132, + 71, 68, 68, 65, 65, 64, 64, 63, 63, 68, 68, 73, 73, 78, 78, 84, 84, 90, 90, 97, 97, 103, 103, 111, 111, 117, 117, 125, 125, 128, 128, 132, + 80, 76, 76, 72, 72, 71, 71, 69, 69, 74, 74, 79, 79, 84, 84, 90, 90, 96, 96, 104, 104, 110, 110, 118, 118, 125, 125, 134, 134, 137, 137, 141, + 80, 76, 76, 72, 72, 71, 71, 69, 69, 74, 74, 79, 79, 84, 84, 90, 90, 96, 96, 104, 104, 110, 110, 118, 118, 125, 125, 134, 134, 137, 137, 141, + 83, 78, 78, 75, 75, 74, 74, 72, 72, 76, 76, 81, 81, 86, 86, 92, 92, 99, 99, 106, 106, 113, 113, 121, 121, 128, 128, 137, 137, 140, 140, 144, + 83, 78, 78, 75, 75, 74, 74, 72, 72, 76, 76, 81, 81, 86, 86, 92, 92, 99, 99, 106, 106, 113, 113, 121, 121, 128, 128, 137, 137, 140, 140, 144, + 87, 83, 83, 79, 79, 77, 77, 75, 75, 80, 80, 84, 84, 90, 90, 96, 96, 102, 102, 109, 109, 116, 116, 124, 124, 132, 132, 141, 141, 144, 144, 149, + }, { + 32, 31, 31, 30, 30, 33, 33, 36, 36, 41, 41, 49, 49, 48, 48, 49, 49, 50, 50, 52, 52, 54, 54, 57, 57, 60, 60, 63, 63, 65, 65, 67, + 31, 31, 31, 31, 31, 34, 34, 38, 38, 42, 42, 47, 47, 47, 47, 47, 47, 48, 48, 50, 50, 52, 52, 54, 54, 57, 57, 60, 60, 61, 61, 63, + 31, 31, 31, 31, 31, 34, 34, 38, 38, 42, 42, 47, 47, 47, 47, 47, 47, 48, 48, 50, 50, 52, 52, 54, 54, 57, 57, 60, 60, 61, 61, 63, + 30, 31, 31, 32, 32, 35, 35, 40, 40, 42, 42, 46, 46, 45, 45, 45, 45, 46, 46, 47, 47, 49, 49, 52, 52, 54, 54, 57, 57, 58, 58, 60, + 30, 31, 31, 32, 32, 35, 35, 40, 40, 42, 42, 46, 46, 45, 45, 45, 45, 46, 46, 47, 47, 49, 49, 52, 52, 54, 54, 57, 57, 58, 58, 60, + 33, 34, 34, 35, 35, 39, 39, 43, 43, 45, 45, 47, 47, 46, 46, 45, 45, 46, 46, 47, 47, 49, 49, 51, 51, 53, 53, 56, 56, 57, 57, 59, + 33, 34, 34, 35, 35, 39, 39, 43, 43, 45, 45, 47, 47, 46, 46, 45, 45, 46, 46, 47, 47, 49, 49, 51, 51, 53, 53, 56, 56, 57, 57, 59, + 36, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 46, 46, 45, 45, 46, 46, 47, 47, 48, 48, 50, 50, 52, 52, 54, 54, 55, 55, 57, + 36, 38, 38, 40, 40, 43, 43, 47, 47, 47, 47, 48, 48, 46, 46, 45, 45, 46, 46, 47, 47, 48, 48, 50, 50, 52, 52, 54, 54, 55, 55, 57, + 41, 42, 42, 42, 42, 45, 45, 47, 47, 48, 48, 50, 50, 49, 49, 49, 49, 50, 50, 50, 50, 52, 52, 53, 53, 55, 55, 57, 57, 58, 58, 60, + 41, 42, 42, 42, 42, 45, 45, 47, 47, 48, 48, 50, 50, 49, 49, 49, 49, 50, 50, 50, 50, 52, 52, 53, 53, 55, 55, 57, 57, 58, 58, 60, + 49, 47, 47, 46, 46, 47, 47, 48, 48, 50, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 58, 58, 60, 60, 61, 61, 62, + 49, 47, 47, 46, 46, 47, 47, 48, 48, 50, 50, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 58, 58, 60, 60, 61, 61, 62, + 48, 47, 47, 45, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 60, 60, 61, 61, 63, 63, 64, 64, 66, + 48, 47, 47, 45, 45, 46, 46, 46, 46, 49, 49, 53, 53, 54, 54, 55, 55, 56, 56, 57, 57, 58, 58, 60, 60, 61, 61, 63, 63, 64, 64, 66, + 49, 47, 47, 45, 45, 45, 45, 45, 45, 49, 49, 53, 53, 55, 55, 58, 58, 60, 60, 61, 61, 62, 62, 63, 63, 65, 65, 67, 67, 68, 68, 69, + 49, 47, 47, 45, 45, 45, 45, 45, 45, 49, 49, 53, 53, 55, 55, 58, 58, 60, 60, 61, 61, 62, 62, 63, 63, 65, 65, 67, 67, 68, 68, 69, + 50, 48, 48, 46, 46, 46, 46, 46, 46, 50, 50, 54, 54, 56, 56, 60, 60, 61, 61, 63, 63, 65, 65, 67, 67, 68, 68, 71, 71, 71, 71, 72, + 50, 48, 48, 46, 46, 46, 46, 46, 46, 50, 50, 54, 54, 56, 56, 60, 60, 61, 61, 63, 63, 65, 65, 67, 67, 68, 68, 71, 71, 71, 71, 72, + 52, 50, 50, 47, 47, 47, 47, 47, 47, 50, 50, 54, 54, 57, 57, 61, 61, 63, 63, 66, 66, 68, 68, 70, 70, 72, 72, 75, 75, 75, 75, 76, + 52, 50, 50, 47, 47, 47, 47, 47, 47, 50, 50, 54, 54, 57, 57, 61, 61, 63, 63, 66, 66, 68, 68, 70, 70, 72, 72, 75, 75, 75, 75, 76, + 54, 52, 52, 49, 49, 49, 49, 48, 48, 52, 52, 55, 55, 58, 58, 62, 62, 65, 65, 68, 68, 71, 71, 73, 73, 75, 75, 78, 78, 79, 79, 80, + 54, 52, 52, 49, 49, 49, 49, 48, 48, 52, 52, 55, 55, 58, 58, 62, 62, 65, 65, 68, 68, 71, 71, 73, 73, 75, 75, 78, 78, 79, 79, 80, + 57, 54, 54, 52, 52, 51, 51, 50, 50, 53, 53, 56, 56, 60, 60, 63, 63, 67, 67, 70, 70, 73, 73, 76, 76, 79, 79, 82, 82, 83, 83, 84, + 57, 54, 54, 52, 52, 51, 51, 50, 50, 53, 53, 56, 56, 60, 60, 63, 63, 67, 67, 70, 70, 73, 73, 76, 76, 79, 79, 82, 82, 83, 83, 84, + 60, 57, 57, 54, 54, 53, 53, 52, 52, 55, 55, 58, 58, 61, 61, 65, 65, 68, 68, 72, 72, 75, 75, 79, 79, 82, 82, 85, 85, 86, 86, 88, + 60, 57, 57, 54, 54, 53, 53, 52, 52, 55, 55, 58, 58, 61, 61, 65, 65, 68, 68, 72, 72, 75, 75, 79, 79, 82, 82, 85, 85, 86, 86, 88, + 63, 60, 60, 57, 57, 56, 56, 54, 54, 57, 57, 60, 60, 63, 63, 67, 67, 71, 71, 75, 75, 78, 78, 82, 82, 85, 85, 89, 89, 90, 90, 92, + 63, 60, 60, 57, 57, 56, 56, 54, 54, 57, 57, 60, 60, 63, 63, 67, 67, 71, 71, 75, 75, 78, 78, 82, 82, 85, 85, 89, 89, 90, 90, 92, + 65, 61, 61, 58, 58, 57, 57, 55, 55, 58, 58, 61, 61, 64, 64, 68, 68, 71, 71, 75, 75, 79, 79, 83, 83, 86, 86, 90, 90, 91, 91, 93, + 65, 61, 61, 58, 58, 57, 57, 55, 55, 58, 58, 61, 61, 64, 64, 68, 68, 71, 71, 75, 75, 79, 79, 83, 83, 86, 86, 90, 90, 91, 91, 93, + 67, 63, 63, 60, 60, 59, 59, 57, 57, 60, 60, 62, 62, 66, 66, 69, 69, 72, 72, 76, 76, 80, 80, 84, 84, 88, 88, 92, 92, 93, 93, 95, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 32, 32, 32, 34, 34, 35, 36, 38, 39, 41, 44, 45, 48, 48, 53, 54, 57, 59, 62, 65, 67, 71, 72, 80, 80, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 38, 40, 42, 43, 46, 46, 51, 52, 55, 56, 59, 62, 64, 68, 69, 76, 76, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 34, 34, 35, 35, 37, 38, 40, 42, 43, 46, 46, 51, 51, 55, 56, 59, 62, 64, 68, 69, 76, 76, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 36, 38, 39, 41, 42, 45, 45, 49, 50, 53, 54, 57, 60, 62, 66, 66, 73, 73, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 36, 37, 38, 41, 41, 44, 44, 49, 49, 52, 54, 56, 59, 61, 65, 65, 72, 72, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 35, 35, 37, 38, 39, 41, 42, 45, 45, 49, 49, 52, 54, 56, 59, 61, 64, 65, 72, 72, + 31, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 36, 36, 38, 39, 40, 42, 42, 45, 45, 49, 50, 52, 54, 56, 59, 60, 64, 65, 71, 71, + 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 35, 36, 37, 38, 39, 40, 42, 43, 45, 45, 49, 49, 52, 54, 56, 59, 60, 64, 64, 70, 70, + 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 37, 37, 38, 38, 39, 40, 41, 42, 43, 46, 46, 49, 49, 52, 53, 55, 58, 59, 63, 63, 69, 69, + 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 37, 37, 38, 38, 40, 41, 41, 43, 43, 46, 46, 49, 50, 52, 54, 56, 58, 60, 63, 64, 70, 70, + 34, 34, 34, 33, 33, 34, 35, 35, 37, 37, 39, 39, 42, 42, 44, 45, 46, 47, 48, 51, 51, 54, 54, 57, 58, 60, 63, 64, 68, 68, 74, 74, + 34, 34, 34, 33, 33, 34, 35, 35, 37, 37, 39, 39, 42, 42, 44, 45, 46, 47, 48, 51, 51, 54, 54, 57, 58, 60, 63, 64, 68, 68, 74, 74, + 35, 35, 35, 34, 34, 35, 36, 36, 38, 38, 42, 42, 46, 47, 48, 49, 50, 52, 53, 55, 55, 58, 59, 61, 62, 64, 67, 68, 72, 72, 78, 78, + 36, 35, 35, 34, 34, 35, 36, 37, 38, 38, 42, 42, 47, 48, 50, 50, 52, 54, 54, 57, 57, 59, 60, 62, 64, 66, 68, 69, 73, 73, 79, 79, + 38, 37, 37, 36, 36, 37, 38, 38, 39, 40, 44, 44, 48, 50, 51, 52, 54, 56, 57, 59, 59, 62, 63, 65, 67, 69, 71, 72, 76, 76, 82, 82, + 39, 38, 38, 38, 37, 38, 39, 39, 40, 41, 45, 45, 49, 50, 52, 54, 55, 58, 58, 61, 61, 64, 65, 67, 69, 71, 73, 74, 78, 78, 84, 84, + 41, 40, 40, 39, 38, 39, 40, 40, 41, 41, 46, 46, 50, 52, 54, 55, 57, 60, 60, 63, 63, 67, 67, 70, 71, 73, 75, 77, 80, 81, 86, 86, + 44, 42, 42, 41, 41, 41, 42, 42, 42, 43, 47, 47, 52, 54, 56, 58, 60, 63, 64, 67, 67, 71, 71, 74, 75, 77, 79, 81, 84, 85, 90, 90, + 45, 43, 43, 42, 41, 42, 42, 43, 43, 43, 48, 48, 53, 54, 57, 58, 60, 64, 65, 68, 68, 72, 72, 75, 76, 78, 81, 82, 85, 86, 91, 91, + 48, 46, 46, 45, 44, 45, 45, 45, 46, 46, 51, 51, 55, 57, 59, 61, 63, 67, 68, 71, 71, 75, 76, 79, 80, 83, 85, 87, 90, 91, 96, 96, + 48, 46, 46, 45, 44, 45, 45, 45, 46, 46, 51, 51, 55, 57, 59, 61, 63, 67, 68, 71, 71, 75, 76, 79, 80, 83, 85, 87, 90, 91, 96, 96, + 53, 51, 51, 49, 49, 49, 49, 49, 49, 49, 54, 54, 58, 59, 62, 64, 67, 71, 72, 75, 75, 81, 81, 85, 86, 89, 91, 93, 97, 97, 103, 103, + 54, 52, 51, 50, 49, 49, 50, 49, 49, 50, 54, 54, 59, 60, 63, 65, 67, 71, 72, 76, 76, 81, 82, 85, 87, 89, 92, 94, 97, 98, 104, 104, + 57, 55, 55, 53, 52, 52, 52, 52, 52, 52, 57, 57, 61, 62, 65, 67, 70, 74, 75, 79, 79, 85, 85, 89, 90, 93, 96, 98, 102, 102, 108, 108, + 59, 56, 56, 54, 54, 54, 54, 54, 53, 54, 58, 58, 62, 64, 67, 69, 71, 75, 76, 80, 80, 86, 87, 90, 92, 95, 98, 99, 103, 104, 110, 110, + 62, 59, 59, 57, 56, 56, 56, 56, 55, 56, 60, 60, 64, 66, 69, 71, 73, 77, 78, 83, 83, 89, 89, 93, 95, 98, 101, 103, 107, 108, 114, 114, + 65, 62, 62, 60, 59, 59, 59, 59, 58, 58, 63, 63, 67, 68, 71, 73, 75, 79, 81, 85, 85, 91, 92, 96, 98, 101, 105, 106, 111, 111, 118, 118, + 67, 64, 64, 62, 61, 61, 60, 60, 59, 60, 64, 64, 68, 69, 72, 74, 77, 81, 82, 87, 87, 93, 94, 98, 99, 103, 106, 108, 113, 113, 120, 120, + 71, 68, 68, 66, 65, 64, 64, 64, 63, 63, 68, 68, 72, 73, 76, 78, 80, 84, 85, 90, 90, 97, 97, 102, 103, 107, 111, 113, 117, 118, 125, 125, + 72, 69, 69, 66, 65, 65, 65, 64, 63, 64, 68, 68, 72, 73, 76, 78, 81, 85, 86, 91, 91, 97, 98, 102, 104, 108, 111, 113, 118, 119, 126, 126, + 80, 76, 76, 73, 72, 72, 71, 70, 69, 70, 74, 74, 78, 79, 82, 84, 86, 90, 91, 96, 96, 103, 104, 108, 110, 114, 118, 120, 125, 126, 134, 134, + 80, 76, 76, 73, 72, 72, 71, 70, 69, 70, 74, 74, 78, 79, 82, 84, 86, 90, 91, 96, 96, 103, 104, 108, 110, 114, 118, 120, 125, 126, 134, 134, + }, { + 32, 31, 31, 30, 30, 32, 33, 34, 36, 37, 41, 41, 47, 49, 49, 48, 49, 49, 49, 50, 50, 52, 52, 54, 54, 56, 57, 58, 60, 60, 63, 63, + 31, 31, 31, 31, 31, 32, 34, 35, 38, 38, 42, 42, 46, 48, 47, 47, 47, 47, 47, 48, 48, 50, 50, 51, 52, 53, 54, 55, 57, 57, 60, 60, + 31, 31, 31, 31, 31, 33, 34, 35, 38, 39, 42, 42, 46, 47, 47, 47, 47, 47, 47, 48, 48, 49, 50, 51, 52, 53, 54, 55, 57, 57, 60, 60, + 30, 31, 31, 31, 31, 33, 35, 36, 39, 40, 42, 42, 46, 47, 46, 46, 46, 45, 46, 47, 47, 48, 48, 50, 50, 51, 52, 53, 55, 55, 58, 58, + 30, 31, 31, 31, 32, 33, 35, 36, 40, 40, 42, 42, 45, 46, 46, 45, 45, 45, 45, 46, 46, 47, 47, 49, 49, 51, 52, 52, 54, 54, 57, 57, + 32, 32, 33, 33, 33, 35, 37, 38, 41, 42, 43, 43, 46, 47, 46, 46, 45, 45, 45, 46, 46, 47, 47, 49, 49, 50, 51, 52, 54, 54, 57, 57, + 33, 34, 34, 35, 35, 37, 39, 40, 43, 43, 45, 45, 47, 47, 46, 46, 46, 45, 45, 46, 46, 47, 47, 49, 49, 50, 51, 52, 53, 54, 56, 56, + 34, 35, 35, 36, 36, 38, 40, 41, 44, 44, 45, 45, 47, 47, 47, 46, 46, 45, 45, 46, 46, 47, 47, 48, 49, 50, 51, 51, 53, 53, 55, 55, + 36, 38, 38, 39, 40, 41, 43, 44, 47, 47, 47, 47, 47, 48, 47, 46, 46, 45, 45, 46, 46, 46, 47, 48, 48, 49, 50, 50, 52, 52, 54, 54, + 37, 38, 39, 40, 40, 42, 43, 44, 47, 47, 47, 47, 48, 48, 47, 47, 46, 45, 46, 46, 46, 47, 47, 48, 48, 49, 50, 51, 52, 52, 55, 55, + 41, 42, 42, 42, 42, 43, 45, 45, 47, 47, 48, 48, 50, 50, 50, 49, 49, 49, 49, 50, 50, 50, 50, 51, 52, 52, 53, 54, 55, 55, 57, 57, + 41, 42, 42, 42, 42, 43, 45, 45, 47, 47, 48, 48, 50, 50, 50, 49, 49, 49, 49, 50, 50, 50, 50, 51, 52, 52, 53, 54, 55, 55, 57, 57, + 47, 46, 46, 46, 45, 46, 47, 47, 47, 48, 50, 50, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, 54, 55, 55, 56, 56, 58, 58, 60, 60, + 49, 48, 47, 47, 46, 47, 47, 47, 48, 48, 50, 50, 52, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 55, 55, 56, 56, 57, 58, 58, 60, 60, + 49, 47, 47, 46, 46, 46, 46, 47, 47, 47, 50, 50, 52, 53, 53, 54, 54, 55, 55, 55, 55, 56, 56, 57, 57, 58, 58, 59, 60, 60, 62, 62, + 48, 47, 47, 46, 45, 46, 46, 46, 46, 47, 49, 49, 52, 53, 54, 54, 55, 55, 56, 56, 56, 57, 57, 58, 58, 59, 60, 60, 61, 62, 63, 63, + 49, 47, 47, 46, 45, 45, 46, 46, 46, 46, 49, 49, 52, 53, 54, 55, 55, 57, 57, 58, 58, 59, 59, 60, 60, 61, 61, 62, 63, 63, 65, 65, + 49, 47, 47, 45, 45, 45, 45, 45, 45, 45, 49, 49, 52, 53, 55, 55, 57, 58, 59, 60, 60, 61, 61, 62, 62, 63, 63, 64, 65, 65, 67, 67, + 49, 47, 47, 46, 45, 45, 45, 45, 45, 46, 49, 49, 52, 53, 55, 56, 57, 59, 59, 60, 60, 61, 61, 62, 63, 63, 64, 65, 66, 66, 68, 68, + 50, 48, 48, 47, 46, 46, 46, 46, 46, 46, 50, 50, 53, 54, 55, 56, 58, 60, 60, 61, 61, 63, 63, 65, 65, 66, 67, 67, 68, 69, 71, 71, + 50, 48, 48, 47, 46, 46, 46, 46, 46, 46, 50, 50, 53, 54, 55, 56, 58, 60, 60, 61, 61, 63, 63, 65, 65, 66, 67, 67, 68, 69, 71, 71, + 52, 50, 49, 48, 47, 47, 47, 47, 46, 47, 50, 50, 53, 54, 56, 57, 59, 61, 61, 63, 63, 66, 66, 67, 68, 69, 70, 71, 72, 72, 74, 74, + 52, 50, 50, 48, 47, 47, 47, 47, 47, 47, 50, 50, 53, 54, 56, 57, 59, 61, 61, 63, 63, 66, 66, 68, 68, 69, 70, 71, 72, 73, 75, 75, + 54, 51, 51, 50, 49, 49, 49, 48, 48, 48, 51, 51, 54, 55, 57, 58, 60, 62, 62, 65, 65, 67, 68, 69, 70, 71, 72, 73, 74, 75, 77, 77, + 54, 52, 52, 50, 49, 49, 49, 49, 48, 48, 52, 52, 55, 55, 57, 58, 60, 62, 63, 65, 65, 68, 68, 70, 71, 72, 73, 74, 75, 76, 78, 78, + 56, 53, 53, 51, 51, 50, 50, 50, 49, 49, 52, 52, 55, 56, 58, 59, 61, 63, 63, 66, 66, 69, 69, 71, 72, 73, 75, 75, 77, 77, 80, 80, + 57, 54, 54, 52, 52, 51, 51, 51, 50, 50, 53, 53, 56, 56, 58, 60, 61, 63, 64, 67, 67, 70, 70, 72, 73, 75, 76, 77, 79, 79, 82, 82, + 58, 55, 55, 53, 52, 52, 52, 51, 50, 51, 54, 54, 56, 57, 59, 60, 62, 64, 65, 67, 67, 71, 71, 73, 74, 75, 77, 78, 80, 80, 83, 83, + 60, 57, 57, 55, 54, 54, 53, 53, 52, 52, 55, 55, 58, 58, 60, 61, 63, 65, 66, 68, 68, 72, 72, 74, 75, 77, 79, 80, 82, 82, 85, 85, + 60, 57, 57, 55, 54, 54, 54, 53, 52, 52, 55, 55, 58, 58, 60, 62, 63, 65, 66, 69, 69, 72, 73, 75, 76, 77, 79, 80, 82, 82, 85, 85, + 63, 60, 60, 58, 57, 57, 56, 55, 54, 55, 57, 57, 60, 60, 62, 63, 65, 67, 68, 71, 71, 74, 75, 77, 78, 80, 82, 83, 85, 85, 89, 89, + 63, 60, 60, 58, 57, 57, 56, 55, 54, 55, 57, 57, 60, 60, 62, 63, 65, 67, 68, 71, 71, 74, 75, 77, 78, 80, 82, 83, 85, 85, 89, 89, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 34, 36, 36, 38, 39, 41, 44, 44, 47, 48, 50, 54, 54, 58, 59, 61, 65, 65, 70, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 38, 38, 40, 42, 42, 46, 47, 49, 52, 52, 56, 57, 59, 63, 63, 67, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 38, 38, 40, 42, 42, 45, 46, 48, 51, 51, 55, 56, 58, 62, 62, 67, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 37, 38, 39, 42, 42, 45, 45, 47, 50, 50, 54, 55, 57, 61, 61, 65, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 37, 37, 38, 41, 41, 44, 44, 46, 49, 49, 53, 54, 56, 59, 59, 64, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 37, 37, 38, 41, 41, 44, 44, 46, 49, 49, 53, 54, 56, 59, 59, 64, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 35, 35, 36, 36, 38, 39, 40, 42, 42, 44, 45, 47, 49, 49, 53, 54, 56, 59, 59, 63, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 35, 35, 36, 36, 36, 38, 39, 40, 42, 42, 45, 45, 47, 50, 50, 53, 54, 56, 59, 59, 63, + 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 35, 36, 36, 37, 37, 39, 39, 40, 42, 42, 45, 45, 47, 49, 49, 53, 54, 55, 59, 59, 63, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, 38, 38, 40, 40, 41, 42, 42, 45, 46, 47, 49, 49, 52, 53, 55, 58, 58, 62, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 36, 37, 37, 38, 38, 40, 40, 41, 42, 42, 45, 46, 47, 49, 49, 52, 53, 55, 58, 58, 62, + 33, 33, 33, 33, 33, 33, 34, 35, 35, 36, 36, 38, 39, 40, 42, 42, 43, 44, 45, 46, 46, 49, 50, 51, 53, 53, 56, 57, 59, 62, 62, 66, + 34, 34, 34, 34, 33, 33, 35, 35, 36, 37, 37, 39, 39, 41, 42, 42, 44, 45, 46, 47, 47, 50, 51, 52, 54, 54, 57, 58, 60, 63, 63, 67, + 34, 34, 34, 34, 34, 34, 35, 36, 36, 37, 37, 40, 41, 42, 45, 45, 46, 47, 48, 50, 50, 52, 53, 54, 56, 56, 59, 60, 62, 65, 65, 69, + 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 42, 42, 45, 48, 48, 50, 50, 52, 54, 54, 56, 57, 58, 60, 60, 63, 64, 65, 68, 68, 72, + 36, 35, 35, 35, 34, 34, 36, 36, 37, 38, 38, 42, 42, 45, 48, 48, 50, 50, 52, 54, 54, 56, 57, 58, 60, 60, 63, 64, 65, 68, 68, 72, + 38, 38, 38, 37, 37, 37, 38, 38, 39, 40, 40, 43, 44, 46, 50, 50, 52, 53, 54, 57, 57, 59, 60, 61, 64, 64, 67, 68, 69, 72, 72, 76, + 39, 38, 38, 38, 37, 37, 39, 39, 39, 40, 40, 44, 45, 47, 50, 50, 53, 54, 55, 58, 58, 60, 61, 62, 65, 65, 68, 69, 70, 73, 73, 77, + 41, 40, 40, 39, 38, 38, 40, 40, 40, 41, 41, 45, 46, 48, 52, 52, 54, 55, 57, 60, 60, 62, 63, 65, 67, 67, 70, 71, 73, 75, 75, 79, + 44, 42, 42, 42, 41, 41, 42, 42, 42, 42, 42, 46, 47, 50, 54, 54, 57, 58, 60, 63, 63, 66, 67, 68, 71, 71, 74, 75, 77, 79, 79, 83, + 44, 42, 42, 42, 41, 41, 42, 42, 42, 42, 42, 46, 47, 50, 54, 54, 57, 58, 60, 63, 63, 66, 67, 68, 71, 71, 74, 75, 77, 79, 79, 83, + 47, 46, 45, 45, 44, 44, 44, 45, 45, 45, 45, 49, 50, 52, 56, 56, 59, 60, 62, 66, 66, 69, 70, 72, 75, 75, 78, 79, 81, 84, 84, 88, + 48, 47, 46, 45, 44, 44, 45, 45, 45, 46, 46, 50, 51, 53, 57, 57, 60, 61, 63, 67, 67, 70, 71, 73, 76, 76, 79, 80, 82, 85, 85, 89, + 50, 49, 48, 47, 46, 46, 47, 47, 47, 47, 47, 51, 52, 54, 58, 58, 61, 62, 65, 68, 68, 72, 73, 75, 78, 78, 82, 83, 85, 88, 88, 92, + 54, 52, 51, 50, 49, 49, 49, 50, 49, 49, 49, 53, 54, 56, 60, 60, 64, 65, 67, 71, 71, 75, 76, 78, 82, 82, 86, 87, 89, 92, 92, 96, + 54, 52, 51, 50, 49, 49, 49, 50, 49, 49, 49, 53, 54, 56, 60, 60, 64, 65, 67, 71, 71, 75, 76, 78, 82, 82, 86, 87, 89, 92, 92, 96, + 58, 56, 55, 54, 53, 53, 53, 53, 53, 52, 52, 56, 57, 59, 63, 63, 67, 68, 70, 74, 74, 78, 79, 82, 86, 86, 90, 91, 93, 97, 97, 101, + 59, 57, 56, 55, 54, 54, 54, 54, 54, 53, 53, 57, 58, 60, 64, 64, 68, 69, 71, 75, 75, 79, 80, 83, 87, 87, 91, 92, 94, 98, 98, 102, + 61, 59, 58, 57, 56, 56, 56, 56, 55, 55, 55, 59, 60, 62, 65, 65, 69, 70, 73, 77, 77, 81, 82, 85, 89, 89, 93, 94, 97, 101, 101, 105, + 65, 63, 62, 61, 59, 59, 59, 59, 59, 58, 58, 62, 63, 65, 68, 68, 72, 73, 75, 79, 79, 84, 85, 88, 92, 92, 97, 98, 101, 105, 105, 109, + 65, 63, 62, 61, 59, 59, 59, 59, 59, 58, 58, 62, 63, 65, 68, 68, 72, 73, 75, 79, 79, 84, 85, 88, 92, 92, 97, 98, 101, 105, 105, 109, + 70, 67, 67, 65, 64, 64, 63, 63, 63, 62, 62, 66, 67, 69, 72, 72, 76, 77, 79, 83, 83, 88, 89, 92, 96, 96, 101, 102, 105, 109, 109, 114, + }, { + 32, 31, 31, 31, 30, 30, 33, 33, 34, 36, 36, 40, 41, 44, 49, 49, 48, 48, 49, 49, 49, 50, 50, 51, 52, 52, 54, 54, 55, 57, 57, 59, + 31, 31, 31, 31, 31, 31, 33, 34, 36, 38, 38, 41, 42, 44, 48, 48, 47, 47, 47, 47, 47, 48, 49, 49, 50, 50, 52, 52, 53, 55, 55, 57, + 31, 31, 31, 31, 31, 31, 34, 34, 36, 38, 38, 41, 42, 44, 47, 47, 47, 47, 47, 47, 47, 48, 48, 49, 50, 50, 51, 52, 53, 54, 54, 56, + 31, 31, 31, 31, 31, 31, 34, 35, 36, 39, 39, 41, 42, 44, 47, 47, 46, 46, 46, 46, 46, 47, 47, 48, 49, 49, 50, 51, 52, 53, 53, 55, + 30, 31, 31, 31, 32, 32, 34, 35, 37, 40, 40, 42, 42, 44, 46, 46, 45, 45, 45, 45, 45, 46, 46, 47, 47, 47, 49, 49, 50, 52, 52, 54, + 30, 31, 31, 31, 32, 32, 34, 35, 37, 40, 40, 42, 42, 44, 46, 46, 45, 45, 45, 45, 45, 46, 46, 47, 47, 47, 49, 49, 50, 52, 52, 54, + 33, 33, 34, 34, 34, 34, 37, 38, 40, 42, 42, 44, 44, 45, 47, 47, 46, 46, 46, 45, 45, 46, 46, 47, 47, 47, 49, 49, 50, 51, 51, 53, + 33, 34, 34, 35, 35, 35, 38, 39, 40, 43, 43, 44, 45, 46, 47, 47, 46, 46, 46, 45, 45, 46, 46, 47, 47, 47, 49, 49, 50, 51, 51, 53, + 34, 36, 36, 36, 37, 37, 40, 40, 42, 45, 45, 45, 46, 46, 47, 47, 46, 46, 46, 45, 45, 46, 46, 47, 47, 47, 48, 49, 49, 50, 50, 52, + 36, 38, 38, 39, 40, 40, 42, 43, 45, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 45, 45, 46, 46, 46, 47, 47, 48, 48, 49, 50, 50, 51, + 36, 38, 38, 39, 40, 40, 42, 43, 45, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 45, 45, 46, 46, 46, 47, 47, 48, 48, 49, 50, 50, 51, + 40, 41, 41, 41, 42, 42, 44, 44, 45, 47, 47, 48, 48, 49, 50, 50, 49, 49, 49, 48, 48, 49, 49, 49, 49, 49, 51, 51, 51, 52, 52, 54, + 41, 42, 42, 42, 42, 42, 44, 45, 46, 47, 47, 48, 48, 49, 50, 50, 50, 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, 53, 53, 55, + 44, 44, 44, 44, 44, 44, 45, 46, 46, 47, 47, 49, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 53, 53, 54, 54, 54, 56, + 49, 48, 47, 47, 46, 46, 47, 47, 47, 48, 48, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 56, 56, 56, 58, + 49, 48, 47, 47, 46, 46, 47, 47, 47, 48, 48, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 55, 55, 56, 56, 56, 58, + 48, 47, 47, 46, 45, 45, 46, 46, 46, 47, 47, 49, 50, 51, 53, 53, 54, 54, 54, 55, 55, 56, 56, 56, 57, 57, 58, 58, 58, 59, 59, 60, + 48, 47, 47, 46, 45, 45, 46, 46, 46, 46, 46, 49, 49, 51, 53, 53, 54, 54, 55, 55, 55, 56, 56, 57, 57, 57, 58, 58, 59, 60, 60, 61, + 49, 47, 47, 46, 45, 45, 46, 46, 46, 46, 46, 49, 49, 51, 53, 53, 54, 55, 55, 57, 57, 57, 58, 58, 59, 59, 60, 60, 60, 61, 61, 63, + 49, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 48, 49, 51, 53, 53, 55, 55, 57, 58, 58, 59, 60, 60, 61, 61, 62, 62, 63, 63, 63, 65, + 49, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 48, 49, 51, 53, 53, 55, 55, 57, 58, 58, 59, 60, 60, 61, 61, 62, 62, 63, 63, 63, 65, + 50, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 49, 50, 51, 54, 54, 56, 56, 57, 59, 59, 61, 61, 62, 63, 63, 64, 64, 65, 66, 66, 67, + 50, 49, 48, 47, 46, 46, 46, 46, 46, 46, 46, 49, 50, 51, 54, 54, 56, 56, 58, 60, 60, 61, 61, 62, 63, 63, 65, 65, 66, 67, 67, 68, + 51, 49, 49, 48, 47, 47, 47, 47, 47, 46, 46, 49, 50, 51, 54, 54, 56, 57, 58, 60, 60, 62, 62, 63, 65, 65, 66, 66, 67, 68, 68, 70, + 52, 50, 50, 49, 47, 47, 47, 47, 47, 47, 47, 49, 50, 52, 54, 54, 57, 57, 59, 61, 61, 63, 63, 65, 66, 66, 68, 68, 69, 70, 70, 72, + 52, 50, 50, 49, 47, 47, 47, 47, 47, 47, 47, 49, 50, 52, 54, 54, 57, 57, 59, 61, 61, 63, 63, 65, 66, 66, 68, 68, 69, 70, 70, 72, + 54, 52, 51, 50, 49, 49, 49, 49, 48, 48, 48, 51, 51, 53, 55, 55, 58, 58, 60, 62, 62, 64, 65, 66, 68, 68, 70, 70, 71, 73, 73, 74, + 54, 52, 52, 51, 49, 49, 49, 49, 49, 48, 48, 51, 52, 53, 55, 55, 58, 58, 60, 62, 62, 64, 65, 66, 68, 68, 70, 71, 72, 73, 73, 75, + 55, 53, 53, 52, 50, 50, 50, 50, 49, 49, 49, 51, 52, 54, 56, 56, 58, 59, 60, 63, 63, 65, 66, 67, 69, 69, 71, 72, 73, 74, 74, 76, + 57, 55, 54, 53, 52, 52, 51, 51, 50, 50, 50, 52, 53, 54, 56, 56, 59, 60, 61, 63, 63, 66, 67, 68, 70, 70, 73, 73, 74, 76, 76, 78, + 57, 55, 54, 53, 52, 52, 51, 51, 50, 50, 50, 52, 53, 54, 56, 56, 59, 60, 61, 63, 63, 66, 67, 68, 70, 70, 73, 73, 74, 76, 76, 78, + 59, 57, 56, 55, 54, 54, 53, 53, 52, 51, 51, 54, 55, 56, 58, 58, 60, 61, 63, 65, 65, 67, 68, 70, 72, 72, 74, 75, 76, 78, 78, 80, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 35, 36, 36, 38, 39, 39, 42, 44, 44, 47, 48, 49, 53, 54, 55, 59, 59, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 37, 39, 39, 41, 43, 43, 46, 47, 48, 51, 52, 53, 57, 57, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 37, 38, 38, 41, 42, 43, 45, 46, 47, 51, 51, 53, 56, 56, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 37, 38, 38, 41, 42, 42, 45, 46, 47, 51, 51, 52, 56, 56, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 36, 37, 37, 40, 41, 41, 44, 45, 46, 49, 50, 51, 54, 54, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 36, 37, 37, 40, 41, 41, 44, 44, 45, 49, 49, 50, 54, 54, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 35, 36, 38, 38, 40, 41, 41, 44, 45, 45, 49, 49, 50, 54, 54, + 31, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 38, 39, 39, 41, 42, 42, 44, 45, 46, 49, 50, 51, 54, 54, + 31, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 36, 36, 36, 38, 39, 39, 41, 42, 42, 45, 45, 46, 49, 50, 51, 54, 54, + 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 37, 37, 38, 39, 39, 41, 42, 42, 45, 45, 46, 49, 49, 51, 54, 54, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 36, 37, 37, 37, 38, 38, 39, 40, 40, 42, 42, 43, 45, 46, 46, 49, 49, 50, 53, 53, + 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 36, 37, 37, 37, 38, 38, 39, 40, 40, 42, 42, 43, 45, 46, 46, 49, 49, 50, 53, 53, + 32, 33, 33, 33, 33, 33, 33, 34, 34, 35, 36, 36, 36, 38, 38, 39, 40, 40, 41, 42, 42, 44, 44, 45, 47, 47, 48, 51, 51, 52, 55, 55, + 34, 34, 34, 34, 33, 33, 34, 35, 35, 35, 37, 37, 38, 39, 39, 41, 42, 42, 44, 45, 45, 47, 47, 48, 50, 51, 51, 54, 54, 55, 58, 58, + 34, 34, 34, 34, 33, 33, 34, 35, 35, 35, 37, 37, 38, 39, 39, 41, 42, 42, 44, 45, 45, 47, 47, 48, 50, 51, 51, 54, 54, 55, 58, 58, + 35, 34, 34, 34, 34, 34, 34, 35, 36, 36, 37, 37, 39, 41, 41, 43, 45, 45, 47, 47, 47, 49, 50, 51, 53, 53, 54, 57, 57, 58, 61, 61, + 36, 35, 35, 35, 34, 34, 35, 36, 36, 37, 38, 38, 40, 42, 42, 45, 48, 48, 50, 50, 50, 53, 54, 54, 56, 57, 57, 59, 60, 61, 64, 64, + 36, 35, 35, 35, 34, 34, 35, 36, 36, 37, 38, 38, 40, 42, 42, 45, 48, 48, 50, 50, 50, 53, 54, 54, 56, 57, 57, 59, 60, 61, 64, 64, + 38, 37, 37, 37, 36, 36, 36, 38, 38, 38, 39, 39, 41, 44, 44, 47, 50, 50, 51, 52, 52, 55, 56, 56, 58, 59, 60, 62, 63, 64, 67, 67, + 39, 39, 38, 38, 37, 37, 38, 39, 39, 39, 40, 40, 42, 45, 45, 47, 50, 50, 52, 54, 54, 56, 58, 58, 60, 61, 62, 64, 65, 66, 69, 69, + 39, 39, 38, 38, 37, 37, 38, 39, 39, 39, 40, 40, 42, 45, 45, 47, 50, 50, 52, 54, 54, 56, 58, 58, 60, 61, 62, 64, 65, 66, 69, 69, + 42, 41, 41, 41, 40, 40, 40, 41, 41, 41, 42, 42, 44, 47, 47, 49, 53, 53, 55, 56, 56, 60, 61, 62, 64, 65, 66, 69, 69, 70, 73, 73, + 44, 43, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 44, 47, 47, 50, 54, 54, 56, 58, 58, 61, 63, 64, 66, 67, 68, 71, 71, 72, 75, 75, + 44, 43, 43, 42, 41, 41, 41, 42, 42, 42, 43, 43, 45, 48, 48, 51, 54, 54, 56, 58, 58, 62, 64, 64, 66, 67, 68, 71, 72, 73, 76, 76, + 47, 46, 45, 45, 44, 44, 44, 44, 45, 45, 45, 45, 47, 50, 50, 53, 56, 56, 58, 60, 60, 64, 66, 66, 69, 70, 71, 74, 75, 76, 79, 79, + 48, 47, 46, 46, 45, 44, 45, 45, 45, 45, 46, 46, 47, 51, 51, 53, 57, 57, 59, 61, 61, 65, 67, 67, 70, 71, 72, 75, 76, 77, 80, 80, + 49, 48, 47, 47, 46, 45, 45, 46, 46, 46, 46, 46, 48, 51, 51, 54, 57, 57, 60, 62, 62, 66, 68, 68, 71, 72, 73, 77, 77, 78, 81, 81, + 53, 51, 51, 51, 49, 49, 49, 49, 49, 49, 49, 49, 51, 54, 54, 57, 59, 59, 62, 64, 64, 69, 71, 71, 74, 75, 77, 81, 81, 83, 86, 86, + 54, 52, 51, 51, 50, 49, 49, 50, 50, 49, 49, 49, 51, 54, 54, 57, 60, 60, 63, 65, 65, 69, 71, 72, 75, 76, 77, 81, 82, 83, 87, 87, + 55, 53, 53, 52, 51, 50, 50, 51, 51, 51, 50, 50, 52, 55, 55, 58, 61, 61, 64, 66, 66, 70, 72, 73, 76, 77, 78, 83, 83, 85, 88, 88, + 59, 57, 56, 56, 54, 54, 54, 54, 54, 54, 53, 53, 55, 58, 58, 61, 64, 64, 67, 69, 69, 73, 75, 76, 79, 80, 81, 86, 87, 88, 92, 92, + 59, 57, 56, 56, 54, 54, 54, 54, 54, 54, 53, 53, 55, 58, 58, 61, 64, 64, 67, 69, 69, 73, 75, 76, 79, 80, 81, 86, 87, 88, 92, 92, + }, { + 32, 31, 31, 31, 30, 30, 31, 33, 33, 34, 36, 36, 38, 41, 41, 45, 49, 49, 49, 48, 48, 49, 49, 49, 50, 50, 51, 52, 52, 53, 54, 54, + 31, 31, 31, 31, 31, 31, 31, 34, 34, 35, 38, 38, 39, 42, 42, 45, 48, 48, 47, 47, 47, 47, 47, 47, 49, 49, 49, 50, 50, 51, 53, 53, + 31, 31, 31, 31, 31, 31, 32, 34, 34, 35, 38, 38, 40, 42, 42, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 52, 52, + 31, 31, 31, 31, 31, 31, 32, 34, 34, 36, 38, 38, 40, 42, 42, 45, 47, 47, 47, 47, 47, 47, 46, 47, 48, 48, 48, 49, 49, 50, 52, 52, + 30, 31, 31, 31, 31, 31, 32, 35, 35, 36, 39, 39, 41, 42, 42, 44, 46, 46, 46, 46, 46, 45, 45, 45, 46, 47, 47, 48, 48, 48, 50, 50, + 30, 31, 31, 31, 31, 32, 32, 35, 35, 36, 40, 40, 41, 42, 42, 44, 46, 46, 46, 45, 45, 45, 45, 45, 46, 46, 46, 47, 47, 48, 49, 49, + 31, 31, 32, 32, 32, 32, 33, 35, 36, 37, 40, 40, 41, 43, 43, 44, 46, 46, 46, 45, 45, 45, 45, 45, 46, 46, 46, 47, 47, 48, 49, 49, + 33, 34, 34, 34, 35, 35, 35, 38, 38, 40, 43, 43, 43, 44, 44, 46, 47, 47, 46, 46, 46, 45, 45, 45, 46, 46, 47, 47, 47, 48, 49, 49, + 33, 34, 34, 34, 35, 35, 36, 38, 39, 40, 43, 43, 44, 45, 45, 46, 47, 47, 46, 46, 46, 45, 45, 45, 46, 46, 47, 47, 47, 48, 49, 49, + 34, 35, 35, 36, 36, 36, 37, 40, 40, 41, 44, 44, 45, 45, 45, 46, 47, 47, 47, 46, 46, 45, 45, 45, 46, 46, 46, 47, 47, 48, 49, 49, + 36, 38, 38, 38, 39, 40, 40, 43, 43, 44, 47, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 45, 45, 45, 46, 46, 46, 46, 47, 47, 48, 48, + 36, 38, 38, 38, 39, 40, 40, 43, 43, 44, 47, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 45, 45, 45, 46, 46, 46, 46, 47, 47, 48, 48, + 38, 39, 40, 40, 41, 41, 41, 43, 44, 45, 47, 47, 47, 48, 48, 48, 49, 49, 48, 48, 48, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 49, + 41, 42, 42, 42, 42, 42, 43, 44, 45, 45, 47, 47, 48, 48, 48, 49, 50, 50, 50, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, + 41, 42, 42, 42, 42, 42, 43, 44, 45, 45, 47, 47, 48, 48, 48, 49, 50, 50, 50, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50, 51, 52, 52, + 45, 45, 45, 45, 44, 44, 44, 46, 46, 46, 47, 47, 48, 49, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52, 52, 52, 53, 53, + 49, 48, 47, 47, 46, 46, 46, 47, 47, 47, 48, 48, 49, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, + 49, 48, 47, 47, 46, 46, 46, 47, 47, 47, 48, 48, 49, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, 54, 54, 55, 55, + 49, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 48, 50, 50, 51, 53, 53, 53, 54, 54, 54, 55, 55, 55, 55, 55, 56, 56, 56, 57, 57, + 48, 47, 47, 47, 46, 45, 45, 46, 46, 46, 46, 46, 48, 49, 49, 51, 53, 53, 54, 54, 54, 55, 55, 56, 56, 56, 56, 57, 57, 58, 58, 58, + 48, 47, 47, 47, 46, 45, 45, 46, 46, 46, 46, 46, 48, 49, 49, 51, 53, 53, 54, 54, 54, 55, 55, 56, 56, 56, 56, 57, 57, 58, 58, 58, + 49, 47, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 51, 53, 53, 54, 55, 55, 57, 57, 58, 58, 59, 59, 60, 60, 60, 61, 61, + 49, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 51, 53, 53, 55, 55, 55, 57, 58, 58, 59, 60, 60, 61, 61, 61, 62, 62, + 49, 47, 47, 47, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 51, 53, 53, 55, 56, 56, 58, 58, 59, 59, 60, 60, 61, 61, 62, 63, 63, + 50, 49, 48, 48, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 52, 54, 54, 55, 56, 56, 58, 59, 59, 61, 61, 61, 63, 63, 63, 64, 64, + 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 52, 54, 54, 55, 56, 56, 59, 60, 60, 61, 61, 62, 63, 63, 64, 65, 65, + 51, 49, 48, 48, 47, 46, 46, 47, 47, 46, 46, 46, 47, 50, 50, 52, 54, 54, 55, 56, 56, 59, 60, 60, 61, 62, 62, 64, 64, 64, 66, 66, + 52, 50, 49, 49, 48, 47, 47, 47, 47, 47, 46, 46, 48, 50, 50, 52, 54, 54, 56, 57, 57, 60, 61, 61, 63, 63, 64, 66, 66, 67, 68, 68, + 52, 50, 50, 49, 48, 47, 47, 47, 47, 47, 47, 47, 48, 50, 50, 52, 54, 54, 56, 57, 57, 60, 61, 61, 63, 63, 64, 66, 66, 67, 68, 68, + 53, 51, 50, 50, 48, 48, 48, 48, 48, 48, 47, 47, 48, 51, 51, 52, 54, 54, 56, 58, 58, 60, 61, 62, 63, 64, 64, 67, 67, 68, 69, 69, + 54, 53, 52, 52, 50, 49, 49, 49, 49, 49, 48, 48, 49, 52, 52, 53, 55, 55, 57, 58, 58, 61, 62, 63, 64, 65, 66, 68, 68, 69, 71, 71, + 54, 53, 52, 52, 50, 49, 49, 49, 49, 49, 48, 48, 49, 52, 52, 53, 55, 55, 57, 58, 58, 61, 62, 63, 64, 65, 66, 68, 68, 69, 71, 71, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 34, 34, 34, 35, 36, 36, 38, 39, 39, 41, 44, 44, 45, 47, 48, 48, 51, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 35, 37, 39, 39, 40, 43, 43, 44, 46, 47, 47, 50, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 35, 37, 38, 38, 40, 42, 42, 43, 45, 46, 46, 49, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 35, 35, 35, 37, 38, 38, 40, 42, 42, 43, 45, 46, 46, 49, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 36, 38, 38, 39, 42, 42, 42, 45, 45, 45, 48, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 36, 37, 37, 38, 41, 41, 41, 44, 44, 44, 47, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 36, 37, 37, 38, 41, 41, 41, 44, 44, 44, 47, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 36, 38, 38, 39, 41, 41, 42, 44, 45, 45, 47, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 35, 35, 35, 36, 36, 36, 37, 39, 39, 40, 42, 42, 42, 44, 45, 45, 48, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 38, 39, 39, 40, 42, 42, 42, 45, 45, 45, 48, + 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 36, 36, 36, 38, 39, 39, 40, 42, 42, 42, 45, 45, 45, 48, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 35, 35, 35, 36, 36, 36, 37, 37, 37, 39, 40, 40, 41, 42, 42, 43, 45, 45, 45, 48, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 37, 37, 37, 38, 38, 38, 39, 40, 40, 41, 42, 42, 43, 45, 46, 46, 48, + 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 35, 35, 35, 36, 37, 37, 37, 38, 38, 38, 39, 40, 40, 41, 42, 42, 43, 45, 46, 46, 48, + 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 36, 36, 36, 38, 38, 38, 39, 40, 40, 41, 42, 42, 43, 44, 44, 45, 47, 47, 47, 50, + 34, 34, 34, 34, 34, 33, 33, 34, 35, 35, 35, 36, 37, 37, 38, 39, 39, 40, 42, 42, 42, 44, 45, 45, 46, 47, 47, 48, 50, 51, 51, 53, + 34, 34, 34, 34, 34, 33, 33, 34, 35, 35, 35, 36, 37, 37, 38, 39, 39, 40, 42, 42, 42, 44, 45, 45, 46, 47, 47, 48, 50, 51, 51, 53, + 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 38, 40, 40, 41, 43, 44, 44, 45, 46, 46, 47, 49, 49, 49, 51, 52, 52, 54, + 35, 35, 35, 35, 34, 34, 34, 34, 36, 36, 36, 37, 38, 38, 39, 42, 42, 43, 46, 47, 47, 48, 49, 49, 50, 52, 52, 53, 55, 55, 55, 57, + 36, 35, 35, 35, 35, 34, 34, 35, 36, 36, 36, 37, 38, 38, 40, 42, 42, 44, 47, 48, 48, 50, 50, 50, 52, 54, 54, 54, 56, 57, 57, 58, + 36, 35, 35, 35, 35, 34, 34, 35, 36, 36, 36, 37, 38, 38, 40, 42, 42, 44, 47, 48, 48, 50, 50, 50, 52, 54, 54, 54, 56, 57, 57, 58, + 38, 37, 37, 37, 36, 36, 36, 36, 37, 38, 38, 39, 39, 39, 41, 44, 44, 45, 48, 50, 50, 51, 52, 52, 54, 56, 56, 57, 58, 59, 59, 61, + 39, 39, 38, 38, 38, 37, 37, 38, 39, 39, 39, 40, 40, 40, 42, 45, 45, 46, 49, 50, 50, 52, 54, 54, 55, 58, 58, 58, 60, 61, 61, 63, + 39, 39, 38, 38, 38, 37, 37, 38, 39, 39, 39, 40, 40, 40, 42, 45, 45, 46, 49, 50, 50, 52, 54, 54, 55, 58, 58, 58, 60, 61, 61, 63, + 41, 40, 40, 40, 39, 38, 38, 39, 40, 40, 40, 41, 41, 41, 43, 46, 46, 47, 50, 52, 52, 54, 55, 55, 57, 60, 60, 60, 62, 63, 63, 66, + 44, 43, 42, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 42, 44, 47, 47, 49, 52, 54, 54, 56, 58, 58, 60, 63, 63, 64, 66, 67, 67, 69, + 44, 43, 42, 42, 42, 41, 41, 41, 42, 42, 42, 42, 42, 42, 44, 47, 47, 49, 52, 54, 54, 56, 58, 58, 60, 63, 63, 64, 66, 67, 67, 69, + 45, 44, 43, 43, 42, 41, 41, 42, 42, 42, 42, 43, 43, 43, 45, 48, 48, 49, 53, 54, 54, 57, 58, 58, 60, 64, 64, 65, 67, 68, 68, 70, + 47, 46, 45, 45, 45, 44, 44, 44, 44, 45, 45, 45, 45, 45, 47, 50, 50, 51, 55, 56, 56, 58, 60, 60, 62, 66, 66, 67, 69, 70, 70, 73, + 48, 47, 46, 46, 45, 44, 44, 45, 45, 45, 45, 45, 46, 46, 47, 51, 51, 52, 55, 57, 57, 59, 61, 61, 63, 67, 67, 68, 70, 71, 71, 74, + 48, 47, 46, 46, 45, 44, 44, 45, 45, 45, 45, 45, 46, 46, 47, 51, 51, 52, 55, 57, 57, 59, 61, 61, 63, 67, 67, 68, 70, 71, 71, 74, + 51, 50, 49, 49, 48, 47, 47, 47, 48, 48, 48, 48, 48, 48, 50, 53, 53, 54, 57, 58, 58, 61, 63, 63, 66, 69, 69, 70, 73, 74, 74, 77, + }, { + 32, 31, 31, 31, 31, 30, 30, 31, 33, 33, 33, 35, 36, 36, 38, 41, 41, 43, 47, 49, 49, 49, 48, 48, 49, 49, 49, 49, 50, 50, 50, 51, + 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 34, 36, 37, 37, 39, 42, 42, 43, 47, 48, 48, 48, 47, 47, 47, 47, 47, 48, 49, 49, 49, 50, + 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 37, 38, 38, 40, 42, 42, 43, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, + 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 37, 38, 38, 40, 42, 42, 43, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, + 31, 31, 31, 31, 31, 31, 31, 32, 34, 35, 35, 37, 39, 39, 40, 42, 42, 43, 46, 47, 47, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 48, + 30, 31, 31, 31, 31, 32, 32, 32, 34, 35, 35, 38, 40, 40, 41, 42, 42, 43, 45, 46, 46, 46, 45, 45, 45, 45, 45, 45, 46, 46, 46, 47, + 30, 31, 31, 31, 31, 32, 32, 32, 34, 35, 35, 38, 40, 40, 41, 42, 42, 43, 45, 46, 46, 46, 45, 45, 45, 45, 45, 45, 46, 46, 46, 47, + 31, 31, 32, 32, 32, 32, 32, 33, 35, 36, 36, 38, 40, 40, 41, 43, 43, 43, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, 46, 46, 46, 47, + 33, 33, 34, 34, 34, 34, 34, 35, 37, 38, 38, 41, 42, 42, 43, 44, 44, 45, 46, 47, 47, 46, 46, 46, 46, 45, 45, 45, 46, 46, 46, 47, + 33, 34, 34, 34, 35, 35, 35, 36, 38, 39, 39, 41, 43, 43, 44, 45, 45, 45, 47, 47, 47, 46, 46, 46, 46, 45, 45, 45, 46, 46, 46, 47, + 33, 34, 34, 34, 35, 35, 35, 36, 38, 39, 39, 41, 43, 43, 44, 45, 45, 45, 47, 47, 47, 46, 46, 46, 46, 45, 45, 45, 46, 46, 46, 47, + 35, 36, 37, 37, 37, 38, 38, 38, 41, 41, 41, 44, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 46, 46, 46, 45, 45, 45, 46, 46, 46, 47, + 36, 37, 38, 38, 39, 40, 40, 40, 42, 43, 43, 46, 47, 47, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 46, 45, 45, 45, 46, 46, 46, 46, + 36, 37, 38, 38, 39, 40, 40, 40, 42, 43, 43, 46, 47, 47, 47, 47, 47, 47, 47, 48, 48, 47, 46, 46, 46, 45, 45, 45, 46, 46, 46, 46, + 38, 39, 40, 40, 40, 41, 41, 41, 43, 44, 44, 46, 47, 47, 47, 48, 48, 48, 48, 49, 49, 48, 48, 48, 47, 47, 47, 47, 47, 47, 47, 48, + 41, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 46, 47, 47, 48, 48, 48, 49, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, + 41, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 46, 47, 47, 48, 48, 48, 49, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, + 43, 43, 43, 43, 43, 43, 43, 43, 45, 45, 45, 46, 47, 47, 48, 49, 49, 49, 50, 51, 51, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, + 47, 47, 46, 46, 46, 45, 45, 46, 46, 47, 47, 47, 47, 47, 48, 50, 50, 50, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53, 53, + 49, 48, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 48, 48, 49, 50, 50, 51, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, + 49, 48, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 48, 48, 49, 50, 50, 51, 52, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, 54, + 49, 48, 47, 47, 46, 46, 46, 46, 46, 46, 46, 47, 47, 47, 48, 50, 50, 50, 52, 53, 53, 53, 54, 54, 54, 55, 55, 55, 55, 55, 55, 56, + 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 48, 49, 49, 50, 52, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 56, 57, + 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 48, 49, 49, 50, 52, 53, 53, 54, 54, 54, 55, 55, 55, 56, 56, 56, 56, 57, + 49, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 47, 49, 49, 50, 52, 53, 53, 54, 55, 55, 55, 57, 57, 57, 57, 58, 58, 58, + 49, 47, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 50, 52, 53, 53, 55, 55, 55, 57, 58, 58, 59, 59, 60, 60, 60, + 49, 47, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 50, 52, 53, 53, 55, 55, 55, 57, 58, 58, 59, 59, 60, 60, 60, + 49, 48, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 50, 52, 53, 53, 55, 56, 56, 57, 59, 59, 59, 60, 60, 60, 61, + 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 50, 53, 54, 54, 55, 56, 56, 57, 59, 59, 60, 61, 61, 61, 62, + 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 50, 53, 54, 54, 55, 56, 56, 58, 60, 60, 60, 61, 61, 61, 63, + 50, 49, 48, 48, 47, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47, 50, 50, 50, 53, 54, 54, 55, 56, 56, 58, 60, 60, 60, 61, 61, 61, 63, + 51, 50, 49, 49, 48, 47, 47, 47, 47, 47, 47, 47, 46, 46, 48, 50, 50, 51, 53, 54, 54, 56, 57, 57, 58, 60, 60, 61, 62, 63, 63, 64, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 33, 34, 34, 34, 35, 36, 36, 36, 37, 39, 39, 39, 41, 44, 44, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 37, 39, 39, 39, 41, 43, 43, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 37, 38, 38, 38, 40, 42, 42, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 36, 38, 38, 38, 39, 41, 41, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 36, 37, 37, 37, 39, 41, 41, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, 38, 38, 38, 40, 41, 41, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 38, 39, 39, 39, 40, 42, 42, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 38, 39, 39, 39, 40, 42, 42, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 38, 39, 39, 39, 40, 42, 42, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 37, 37, 38, 40, 40, 40, 41, 42, 42, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 42, 42, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 42, 42, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 36, 37, 37, 37, 37, 38, 38, 38, 39, 40, 40, 40, 41, 42, 42, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 39, 40, 40, 40, 41, 42, 42, 42, 44, 45, 45, + 34, 34, 34, 34, 34, 34, 33, 33, 33, 34, 35, 35, 35, 36, 37, 37, 37, 38, 39, 39, 39, 41, 42, 42, 42, 44, 45, 45, 45, 46, 47, 47, + 34, 34, 34, 34, 34, 34, 33, 33, 33, 34, 35, 35, 35, 36, 37, 37, 37, 38, 39, 39, 39, 41, 42, 42, 42, 44, 45, 45, 45, 46, 47, 47, + 34, 34, 34, 34, 34, 34, 33, 33, 33, 34, 35, 35, 35, 36, 37, 37, 37, 38, 39, 39, 39, 41, 42, 42, 42, 44, 45, 45, 45, 46, 47, 47, + 35, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 37, 37, 39, 41, 41, 41, 43, 45, 45, 45, 46, 47, 47, 47, 49, 50, 50, + 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 54, 54, + 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 54, 54, + 36, 35, 35, 35, 35, 35, 34, 34, 34, 35, 36, 36, 36, 37, 38, 38, 38, 40, 42, 42, 42, 45, 48, 48, 48, 49, 50, 50, 50, 52, 54, 54, + 37, 37, 37, 37, 37, 36, 36, 36, 36, 37, 38, 38, 38, 38, 39, 39, 39, 41, 44, 44, 44, 46, 49, 49, 49, 51, 52, 52, 52, 54, 56, 56, + 39, 39, 38, 38, 38, 38, 37, 37, 37, 38, 39, 39, 39, 40, 40, 40, 40, 42, 45, 45, 45, 47, 50, 50, 50, 52, 54, 54, 54, 56, 58, 58, + 39, 39, 38, 38, 38, 38, 37, 37, 37, 38, 39, 39, 39, 40, 40, 40, 40, 42, 45, 45, 45, 47, 50, 50, 50, 52, 54, 54, 54, 56, 58, 58, + 39, 39, 38, 38, 38, 38, 37, 37, 37, 38, 39, 39, 39, 40, 40, 40, 40, 42, 45, 45, 45, 47, 50, 50, 50, 52, 54, 54, 54, 56, 58, 58, + 41, 41, 40, 40, 40, 39, 39, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 44, 46, 46, 46, 49, 52, 52, 52, 54, 56, 56, 56, 58, 60, 60, + 44, 43, 42, 42, 42, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 45, 47, 47, 47, 50, 54, 54, 54, 56, 58, 58, 58, 60, 63, 63, + 44, 43, 42, 42, 42, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 42, 42, 45, 47, 47, 47, 50, 54, 54, 54, 56, 58, 58, 58, 60, 63, 63, + }, { + 32, 31, 31, 31, 31, 31, 30, 30, 30, 32, 33, 33, 33, 35, 36, 36, 36, 39, 41, 41, 41, 45, 49, 49, 49, 49, 48, 48, 48, 49, 49, 49, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 35, 37, 37, 37, 39, 42, 42, 42, 45, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 34, 34, 36, 38, 38, 38, 40, 42, 42, 42, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 34, 34, 36, 38, 38, 38, 40, 42, 42, 42, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 34, 34, 36, 38, 38, 38, 40, 42, 42, 42, 45, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 35, 35, 35, 37, 39, 39, 39, 41, 42, 42, 42, 44, 47, 47, 47, 46, 46, 46, 46, 46, 46, 46, + 30, 31, 31, 31, 31, 31, 32, 32, 32, 33, 35, 35, 35, 37, 40, 40, 40, 41, 42, 42, 42, 44, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, + 30, 31, 31, 31, 31, 31, 32, 32, 32, 33, 35, 35, 35, 37, 40, 40, 40, 41, 42, 42, 42, 44, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, + 30, 31, 31, 31, 31, 31, 32, 32, 32, 33, 35, 35, 35, 37, 40, 40, 40, 41, 42, 42, 42, 44, 46, 46, 46, 46, 45, 45, 45, 45, 45, 45, + 32, 32, 33, 33, 33, 33, 33, 33, 33, 35, 37, 37, 37, 39, 41, 41, 41, 42, 43, 43, 43, 45, 47, 47, 47, 46, 46, 46, 46, 45, 45, 45, + 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, 39, 39, 39, 41, 43, 43, 43, 44, 45, 45, 45, 46, 47, 47, 47, 47, 46, 46, 46, 46, 45, 45, + 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, 39, 39, 39, 41, 43, 43, 43, 44, 45, 45, 45, 46, 47, 47, 47, 47, 46, 46, 46, 46, 45, 45, + 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, 39, 39, 39, 41, 43, 43, 43, 44, 45, 45, 45, 46, 47, 47, 47, 47, 46, 46, 46, 46, 45, 45, + 35, 35, 36, 36, 36, 37, 37, 37, 37, 39, 41, 41, 41, 43, 45, 45, 45, 45, 46, 46, 46, 47, 47, 47, 47, 47, 46, 46, 46, 46, 45, 45, + 36, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 46, 46, 46, 46, 45, 45, + 36, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 46, 46, 46, 46, 45, 45, + 36, 37, 38, 38, 38, 39, 40, 40, 40, 41, 43, 43, 43, 45, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, 46, 46, 46, 46, 45, 45, + 39, 39, 40, 40, 40, 41, 41, 41, 41, 42, 44, 44, 44, 45, 47, 47, 47, 47, 48, 48, 48, 48, 49, 49, 49, 48, 48, 48, 48, 47, 47, 47, + 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 45, 45, 45, 46, 47, 47, 47, 48, 48, 48, 48, 49, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, + 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 45, 45, 45, 46, 47, 47, 47, 48, 48, 48, 48, 49, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, + 41, 42, 42, 42, 42, 42, 42, 42, 42, 43, 45, 45, 45, 46, 47, 47, 47, 48, 48, 48, 48, 49, 50, 50, 50, 50, 49, 49, 49, 49, 49, 49, + 45, 45, 45, 45, 45, 44, 44, 44, 44, 45, 46, 46, 46, 47, 47, 47, 47, 48, 49, 49, 49, 50, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, + 49, 48, 47, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 49, 48, 47, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 49, 48, 47, 47, 47, 47, 46, 46, 46, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, + 49, 48, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 48, 50, 50, 50, 51, 53, 53, 53, 53, 53, 53, 53, 54, 54, 54, + 48, 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 48, 49, 49, 49, 51, 53, 53, 53, 53, 54, 54, 54, 55, 55, 55, + 48, 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 48, 49, 49, 49, 51, 53, 53, 53, 53, 54, 54, 54, 55, 55, 55, + 48, 48, 47, 47, 47, 46, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 46, 48, 49, 49, 49, 51, 53, 53, 53, 53, 54, 54, 54, 55, 55, 55, + 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 46, 46, 46, 46, 46, 46, 46, 47, 49, 49, 49, 51, 53, 53, 53, 54, 55, 55, 55, 56, 57, 57, + 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 49, 51, 53, 53, 53, 54, 55, 55, 55, 57, 58, 58, + 49, 48, 47, 47, 47, 46, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 47, 49, 49, 49, 51, 53, 53, 53, 54, 55, 55, 55, 57, 58, 58, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 36, 36, 36, 37, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 34, 34, 35, 35, 35, 35, 37, + 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 36, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 36, 37, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 36, 37, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 36, 37, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 35, 36, 36, 36, 36, 37, 37, 38, 38, 38, 39, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, 34, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 38, 38, 39, + 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 35, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 39, 40, 40, 40, 41, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 36, 36, 36, 36, 37, 38, 39, 39, 39, 40, 41, 42, 42, 42, 42, + 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 38, 39, 39, 39, 39, 41, 42, 42, 42, 42, 43, + 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 38, 39, 39, 39, 39, 41, 42, 42, 42, 42, 43, + 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 34, 35, 35, 35, 35, 36, 36, 37, 37, 37, 38, 39, 39, 39, 39, 41, 42, 42, 42, 42, 43, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 37, 37, 37, 38, 40, 41, 41, 41, 42, 44, 45, 45, 45, 45, + 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 37, 38, 38, 38, 39, 41, 42, 42, 42, 44, 46, 47, 47, 47, 48, + 36, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 42, 42, 42, 42, 45, 47, 48, 48, 48, 49, + 36, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 42, 42, 42, 42, 45, 47, 48, 48, 48, 49, + 36, 35, 35, 35, 35, 35, 35, 34, 34, 34, 34, 35, 36, 36, 36, 36, 37, 38, 38, 38, 38, 40, 42, 42, 42, 42, 45, 47, 48, 48, 48, 49, + 37, 37, 36, 36, 36, 36, 36, 35, 35, 35, 35, 36, 37, 37, 37, 37, 38, 39, 39, 39, 39, 41, 42, 43, 43, 43, 45, 48, 49, 49, 49, 50, + }, { + 32, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 33, 33, 33, 33, 34, 36, 36, 36, 36, 38, 40, 41, 41, 41, 44, 47, 49, 49, 49, 49, + 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 32, 33, 34, 34, 34, 35, 36, 37, 37, 37, 39, 41, 42, 42, 42, 44, 47, 48, 48, 48, 48, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 36, 37, 38, 38, 38, 39, 41, 42, 42, 42, 44, 46, 48, 48, 48, 47, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, 36, 37, 38, 38, 38, 40, 41, 42, 42, 42, 44, 46, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, 36, 37, 38, 38, 38, 40, 41, 42, 42, 42, 44, 46, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 34, 34, 34, 34, 36, 37, 38, 38, 38, 40, 41, 42, 42, 42, 44, 46, 47, 47, 47, 47, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 35, 35, 36, 38, 39, 39, 39, 40, 41, 42, 42, 42, 44, 46, 47, 47, 47, 47, + 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 33, 34, 35, 35, 35, 37, 38, 39, 39, 39, 41, 42, 42, 42, 42, 44, 46, 46, 46, 46, 46, + 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 37, 39, 40, 40, 40, 41, 42, 42, 42, 42, 44, 45, 46, 46, 46, 46, + 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 37, 39, 40, 40, 40, 41, 42, 42, 42, 42, 44, 45, 46, 46, 46, 46, + 30, 30, 31, 31, 31, 31, 31, 31, 32, 32, 32, 33, 34, 35, 35, 35, 37, 39, 40, 40, 40, 41, 42, 42, 42, 42, 44, 45, 46, 46, 46, 46, + 31, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 34, 36, 37, 37, 37, 38, 40, 41, 41, 41, 42, 43, 43, 43, 43, 44, 46, 46, 46, 46, 46, + 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 36, 37, 38, 38, 38, 40, 41, 42, 42, 42, 43, 44, 44, 44, 44, 45, 46, 47, 47, 47, 46, + 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, 40, 42, 43, 43, 43, 44, 44, 45, 45, 45, 46, 47, 47, 47, 47, 47, + 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, 40, 42, 43, 43, 43, 44, 44, 45, 45, 45, 46, 47, 47, 47, 47, 47, + 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 37, 38, 39, 39, 39, 40, 42, 43, 43, 43, 44, 44, 45, 45, 45, 46, 47, 47, 47, 47, 47, + 34, 35, 36, 36, 36, 36, 36, 37, 37, 37, 37, 38, 40, 40, 40, 40, 42, 44, 45, 45, 45, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 47, + 36, 36, 37, 37, 37, 37, 38, 38, 39, 39, 39, 40, 41, 42, 42, 42, 44, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 36, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 45, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, + 36, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 45, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, + 36, 37, 38, 38, 38, 38, 39, 39, 40, 40, 40, 41, 42, 43, 43, 43, 45, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48, 47, + 38, 39, 39, 40, 40, 40, 40, 41, 41, 41, 41, 42, 43, 44, 44, 44, 45, 47, 47, 47, 47, 47, 48, 48, 48, 48, 48, 48, 49, 49, 49, 48, + 40, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 43, 44, 44, 44, 44, 45, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 49, 50, 50, 50, 49, + 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 45, 46, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 50, 50, 50, 50, 50, + 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 45, 46, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 50, 50, 50, 50, 50, + 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 44, 45, 45, 45, 46, 47, 47, 47, 47, 48, 48, 48, 48, 48, 49, 50, 50, 50, 50, 50, + 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 49, 49, 49, 49, 50, 51, 51, 51, 51, 51, + 47, 47, 46, 46, 46, 46, 46, 46, 45, 45, 45, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, 50, 50, 51, 52, 52, 52, 52, 52, + 49, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 50, 50, 51, 52, 53, 53, 53, 53, + 49, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 50, 50, 51, 52, 53, 53, 53, 53, + 49, 48, 48, 47, 47, 47, 47, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, 49, 50, 50, 50, 50, 51, 52, 53, 53, 53, 53, + 49, 48, 47, 47, 47, 47, 47, 46, 46, 46, 46, 46, 46, 47, 47, 47, 47, 47, 47, 47, 47, 48, 49, 50, 50, 50, 51, 52, 53, 53, 53, 53, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 34, 34, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 36, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, + 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 35, 35, 35, 36, 36, 36, 36, 36, 37, 38, 38, + 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 37, 38, 38, 38, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 39, 39, + 34, 34, 34, 34, 34, 34, 34, 34, 34, 33, 33, 33, 33, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, 36, 37, 37, 37, 37, 38, 38, 39, 39, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 31, 32, 33, 33, 33, 33, 33, 34, 35, 36, 36, 36, 36, 37, 38, 40, 41, 41, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 31, 32, 33, 34, 34, 34, 34, 35, 36, 37, 37, 37, 37, 37, 39, 40, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 35, 36, 37, 37, 37, 37, 38, 39, 40, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 34, 34, 34, 34, 34, 35, 36, 38, 38, 38, 38, 38, 40, 41, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 34, 35, 37, 38, 38, 38, 38, 39, 40, 41, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 34, 35, 37, 38, 38, 38, 38, 39, 40, 41, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 34, 35, 37, 38, 38, 38, 38, 39, 40, 41, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 34, 34, 34, 36, 37, 38, 38, 38, 38, 39, 40, 41, 42, 42, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 42, 42, + 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 35, 35, 35, 35, 36, 37, 39, 39, 39, 39, 40, 40, 41, 42, 42, + 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, 35, 36, 38, 39, 40, 40, 40, 40, 41, 42, 42, 42, + 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, 35, 36, 38, 39, 40, 40, 40, 40, 41, 42, 42, 42, + 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, 35, 36, 38, 39, 40, 40, 40, 40, 41, 42, 42, 42, + 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 33, 34, 35, 35, 35, 35, 36, 38, 39, 40, 40, 40, 40, 41, 42, 42, 42, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 35, 36, 36, 36, 36, 37, 38, 40, 40, 40, 40, 41, 41, 42, 43, 43, + 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 35, 36, 37, 37, 37, 37, 38, 39, 41, 41, 41, 41, 42, 42, 43, 43, 43, + 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 37, 38, 38, 38, 38, 39, 41, 42, 42, 42, 42, 43, 43, 44, 44, 44, + 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 43, 43, 43, 43, 43, 44, 44, 45, 45, + 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 43, 43, 43, 43, 43, 44, 44, 45, 45, + 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 43, 43, 43, 43, 43, 44, 44, 45, 45, + 33, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 36, 37, 38, 39, 39, 39, 39, 40, 41, 43, 43, 43, 43, 43, 44, 44, 45, 45, + 34, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 37, 38, 39, 40, 40, 40, 40, 41, 42, 44, 44, 44, 44, 44, 45, 45, 45, 45, + 35, 36, 36, 36, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38, 38, 39, 41, 41, 41, 41, 41, 42, 44, 45, 46, 46, 46, 46, 46, 46, 46, 46, + 36, 37, 37, 38, 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 40, 41, 42, 43, 43, 43, 43, 44, 45, 46, 47, 47, 47, 47, 47, 47, 47, 47, + 36, 37, 37, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 36, 37, 37, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 36, 37, 37, 38, 38, 38, 38, 38, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 37, 37, 38, 38, 39, 39, 39, 39, 39, 40, 40, 40, 40, 40, 41, 42, 43, 43, 43, 43, 43, 44, 46, 47, 47, 47, 47, 47, 47, 47, 47, 47, + 38, 39, 39, 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 42, 43, 44, 44, 44, 44, 45, 46, 47, 47, 47, 47, 47, 47, 48, 48, 48, + 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 42, 42, 42, 42, 42, 43, 44, 44, 44, 44, 44, 45, 46, 47, 47, 47, 47, 47, 48, 48, 48, 48, + 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 44, 45, 45, 45, 45, 45, 46, 47, 47, 47, 47, 47, 48, 48, 48, 48, + 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 44, 45, 45, 45, 45, 45, 46, 47, 47, 47, 47, 47, 48, 48, 48, 48, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, + 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 30, 31, 31, 32, 33, 33, 33, 33, 33, 33, 33, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 30, 31, 31, 32, 33, 33, 33, 33, 33, 33, 33, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33, 34, 34, 34, 34, 34, 34, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33, 34, 34, 34, 34, 34, 34, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, 34, 34, 34, 34, 34, 34, 34, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 33, 34, 35, 35, 35, 35, 35, 35, 35, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33, 34, 35, 35, 35, 35, 35, 35, 35, + 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 33, 34, 35, 35, 35, 35, 35, 35, 36, + 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, + 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, + 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, + 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, + 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, + 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, + 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 35, 35, 35, 35, 35, 36, + 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 34, 34, 35, 36, 36, 36, 36, 36, 36, 37, + 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 35, 36, 37, 37, 37, 37, 37, 37, 37, + 32, 32, 32, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 37, 37, 37, 37, 37, 38, + 33, 33, 33, 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 36, 37, 37, 38, 38, 38, 38, 38, 38, 39, + 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 39, 39, 39, 39, 39, 39, 40, + 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 39, 39, 39, 39, 39, 39, 40, + 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 39, 39, 39, 39, 39, 39, 40, + 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 39, 39, 39, 39, 39, 39, 40, + 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 39, 39, 39, 39, 39, 39, 40, + 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 37, 37, 38, 39, 39, 39, 39, 39, 39, 40, + 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 38, 39, 40, 40, 40, 40, 40, 40, 40, + }, + }, { + { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, + }, { + 32, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 30, 30, 30, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, + 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, + 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, + }, + }, +}; + + +const uint8_t *dav1d_qm_tbl[16][2][N_RECT_TX_SIZES]; + +COLD void dav1d_init_qm_tables(void) { + // This function is guaranteed to be called only once + + for (int i = 0; i < 15; i++) + for (int j = 0; j < 2; j++) { + // note that the w/h in the assignment is inverted, this is on purpose + // because we store coefficients transposed + dav1d_qm_tbl[i][j][RTX_4X8 ] = qm_tbl_8x4[i][j]; + dav1d_qm_tbl[i][j][RTX_8X4 ] = qm_tbl_4x8[i][j]; + dav1d_qm_tbl[i][j][RTX_4X16 ] = qm_tbl_16x4[i][j]; + dav1d_qm_tbl[i][j][RTX_16X4 ] = qm_tbl_4x16[i][j]; + dav1d_qm_tbl[i][j][RTX_8X16 ] = qm_tbl_16x8[i][j]; + dav1d_qm_tbl[i][j][RTX_16X8 ] = qm_tbl_8x16[i][j]; + dav1d_qm_tbl[i][j][RTX_8X32 ] = qm_tbl_32x8[i][j]; + dav1d_qm_tbl[i][j][RTX_32X8 ] = qm_tbl_8x32[i][j]; + dav1d_qm_tbl[i][j][RTX_16X32] = qm_tbl_32x16[i][j]; + dav1d_qm_tbl[i][j][RTX_32X16] = qm_tbl_16x32[i][j]; + + dav1d_qm_tbl[i][j][ TX_4X4 ] = qm_tbl_4x4[i][j]; + dav1d_qm_tbl[i][j][ TX_8X8 ] = qm_tbl_8x8[i][j]; + dav1d_qm_tbl[i][j][ TX_16X16] = qm_tbl_16x16[i][j]; + dav1d_qm_tbl[i][j][ TX_32X32] = qm_tbl_32x32[i][j]; + + dav1d_qm_tbl[i][j][ TX_64X64] = dav1d_qm_tbl[i][j][ TX_32X32]; + dav1d_qm_tbl[i][j][RTX_64X32] = dav1d_qm_tbl[i][j][ TX_32X32]; + dav1d_qm_tbl[i][j][RTX_64X16] = dav1d_qm_tbl[i][j][RTX_32X16]; + dav1d_qm_tbl[i][j][RTX_32X64] = dav1d_qm_tbl[i][j][ TX_32X32]; + dav1d_qm_tbl[i][j][RTX_16X64] = dav1d_qm_tbl[i][j][RTX_16X32]; + } + + // dav1d_qm_tbl[15][*][*] == NULL +} diff --git a/3rdparty/dav1d/src/qm.h b/3rdparty/dav1d/src/qm.h new file mode 100644 index 0000000..8191c8a --- /dev/null +++ b/3rdparty/dav1d/src/qm.h @@ -0,0 +1,37 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_QM_H +#define DAV1D_SRC_QM_H + +#include "src/levels.h" + +EXTERN const uint8_t *dav1d_qm_tbl[16][2][N_RECT_TX_SIZES]; + +void dav1d_init_qm_tables(void); + +#endif /* DAV1D_SRC_QM_H */ diff --git a/3rdparty/dav1d/src/recon.h b/3rdparty/dav1d/src/recon.h new file mode 100644 index 0000000..7219249 --- /dev/null +++ b/3rdparty/dav1d/src/recon.h @@ -0,0 +1,106 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_RECON_H +#define DAV1D_SRC_RECON_H + +#include "src/internal.h" +#include "src/levels.h" + +#define DEBUG_BLOCK_INFO 0 && \ + f->frame_hdr->frame_offset == 2 && t->by >= 0 && t->by < 4 && \ + t->bx >= 8 && t->bx < 12 +#define DEBUG_B_PIXELS 0 + +#define decl_recon_b_intra_fn(name) \ +void (name)(Dav1dTaskContext *t, enum BlockSize bs, \ + enum EdgeFlags intra_edge_flags, const Av1Block *b) +typedef decl_recon_b_intra_fn(*recon_b_intra_fn); + +#define decl_recon_b_inter_fn(name) \ +int (name)(Dav1dTaskContext *t, enum BlockSize bs, const Av1Block *b) +typedef decl_recon_b_inter_fn(*recon_b_inter_fn); + +#define decl_filter_sbrow_fn(name) \ +void (name)(Dav1dFrameContext *f, int sby) +typedef decl_filter_sbrow_fn(*filter_sbrow_fn); + +#define decl_backup_ipred_edge_fn(name) \ +void (name)(Dav1dTaskContext *t) +typedef decl_backup_ipred_edge_fn(*backup_ipred_edge_fn); + +#define decl_read_coef_blocks_fn(name) \ +void (name)(Dav1dTaskContext *t, enum BlockSize bs, const Av1Block *b) +typedef decl_read_coef_blocks_fn(*read_coef_blocks_fn); + +#define decl_copy_pal_block_fn(name) \ +void (name)(Dav1dTaskContext *t, int bx4, int by4, int bw4, int bh4) +typedef decl_copy_pal_block_fn(*copy_pal_block_fn); + +#define decl_read_pal_plane_fn(name) \ +void (name)(Dav1dTaskContext *t, Av1Block *b, int pl, int sz_ctx, int bx4, int by4) +typedef decl_read_pal_plane_fn(*read_pal_plane_fn); + +#define decl_read_pal_uv_fn(name) \ +void (name)(Dav1dTaskContext *t, Av1Block *b, int sz_ctx, int bx4, int by4) +typedef decl_read_pal_uv_fn(*read_pal_uv_fn); + +decl_recon_b_intra_fn(dav1d_recon_b_intra_8bpc); +decl_recon_b_intra_fn(dav1d_recon_b_intra_16bpc); + +decl_recon_b_inter_fn(dav1d_recon_b_inter_8bpc); +decl_recon_b_inter_fn(dav1d_recon_b_inter_16bpc); + +decl_filter_sbrow_fn(dav1d_filter_sbrow_8bpc); +decl_filter_sbrow_fn(dav1d_filter_sbrow_16bpc); +decl_filter_sbrow_fn(dav1d_filter_sbrow_deblock_cols_8bpc); +decl_filter_sbrow_fn(dav1d_filter_sbrow_deblock_cols_16bpc); +decl_filter_sbrow_fn(dav1d_filter_sbrow_deblock_rows_8bpc); +decl_filter_sbrow_fn(dav1d_filter_sbrow_deblock_rows_16bpc); +void dav1d_filter_sbrow_cdef_8bpc(Dav1dTaskContext *tc, int sby); +void dav1d_filter_sbrow_cdef_16bpc(Dav1dTaskContext *tc, int sby); +decl_filter_sbrow_fn(dav1d_filter_sbrow_resize_8bpc); +decl_filter_sbrow_fn(dav1d_filter_sbrow_resize_16bpc); +decl_filter_sbrow_fn(dav1d_filter_sbrow_lr_8bpc); +decl_filter_sbrow_fn(dav1d_filter_sbrow_lr_16bpc); + +decl_backup_ipred_edge_fn(dav1d_backup_ipred_edge_8bpc); +decl_backup_ipred_edge_fn(dav1d_backup_ipred_edge_16bpc); + +decl_read_coef_blocks_fn(dav1d_read_coef_blocks_8bpc); +decl_read_coef_blocks_fn(dav1d_read_coef_blocks_16bpc); + +decl_copy_pal_block_fn(dav1d_copy_pal_block_y_8bpc); +decl_copy_pal_block_fn(dav1d_copy_pal_block_y_16bpc); +decl_copy_pal_block_fn(dav1d_copy_pal_block_uv_8bpc); +decl_copy_pal_block_fn(dav1d_copy_pal_block_uv_16bpc); +decl_read_pal_plane_fn(dav1d_read_pal_plane_8bpc); +decl_read_pal_plane_fn(dav1d_read_pal_plane_16bpc); +decl_read_pal_uv_fn(dav1d_read_pal_uv_8bpc); +decl_read_pal_uv_fn(dav1d_read_pal_uv_16bpc); + +#endif /* DAV1D_SRC_RECON_H */ diff --git a/3rdparty/dav1d/src/recon_tmpl.c b/3rdparty/dav1d/src/recon_tmpl.c new file mode 100644 index 0000000..dbe2435 --- /dev/null +++ b/3rdparty/dav1d/src/recon_tmpl.c @@ -0,0 +1,2310 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include + +#include "common/attributes.h" +#include "common/bitdepth.h" +#include "common/dump.h" +#include "common/frame.h" +#include "common/intops.h" + +#include "src/cdef_apply.h" +#include "src/ctx.h" +#include "src/ipred_prepare.h" +#include "src/lf_apply.h" +#include "src/lr_apply.h" +#include "src/recon.h" +#include "src/scan.h" +#include "src/tables.h" +#include "src/wedge.h" + +static inline unsigned read_golomb(MsacContext *const msac) { + int len = 0; + unsigned val = 1; + + while (!dav1d_msac_decode_bool_equi(msac) && len < 32) len++; + while (len--) val = (val << 1) + dav1d_msac_decode_bool_equi(msac); + + return val - 1; +} + +static inline unsigned get_skip_ctx(const TxfmInfo *const t_dim, + const enum BlockSize bs, + const uint8_t *const a, + const uint8_t *const l, + const int chroma, + const enum Dav1dPixelLayout layout) +{ + const uint8_t *const b_dim = dav1d_block_dimensions[bs]; + + if (chroma) { + const int ss_ver = layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = layout != DAV1D_PIXEL_LAYOUT_I444; + const int not_one_blk = b_dim[2] - (!!b_dim[2] && ss_hor) > t_dim->lw || + b_dim[3] - (!!b_dim[3] && ss_ver) > t_dim->lh; + unsigned ca, cl; + +#define MERGE_CTX(dir, type, no_val) \ + c##dir = *(const type *) dir != no_val; \ + break + + switch (t_dim->lw) { + /* For some reason the MSVC CRT _wassert() function is not flagged as + * __declspec(noreturn), so when using those headers the compiler will + * expect execution to continue after an assertion has been triggered + * and will therefore complain about the use of uninitialized variables + * when compiled in debug mode if we put the default case at the end. */ + default: assert(0); /* fall-through */ + case TX_4X4: MERGE_CTX(a, uint8_t, 0x40); + case TX_8X8: MERGE_CTX(a, uint16_t, 0x4040); + case TX_16X16: MERGE_CTX(a, uint32_t, 0x40404040U); + case TX_32X32: MERGE_CTX(a, uint64_t, 0x4040404040404040ULL); + } + switch (t_dim->lh) { + default: assert(0); /* fall-through */ + case TX_4X4: MERGE_CTX(l, uint8_t, 0x40); + case TX_8X8: MERGE_CTX(l, uint16_t, 0x4040); + case TX_16X16: MERGE_CTX(l, uint32_t, 0x40404040U); + case TX_32X32: MERGE_CTX(l, uint64_t, 0x4040404040404040ULL); + } +#undef MERGE_CTX + + return 7 + not_one_blk * 3 + ca + cl; + } else if (b_dim[2] == t_dim->lw && b_dim[3] == t_dim->lh) { + return 0; + } else { + unsigned la, ll; + +#define MERGE_CTX(dir, type, tx) \ + if (tx == TX_64X64) { \ + uint64_t tmp = *(const uint64_t *) dir; \ + tmp |= *(const uint64_t *) &dir[8]; \ + l##dir = (unsigned) (tmp >> 32) | (unsigned) tmp; \ + } else \ + l##dir = *(const type *) dir; \ + if (tx == TX_32X32) l##dir |= *(const type *) &dir[sizeof(type)]; \ + if (tx >= TX_16X16) l##dir |= l##dir >> 16; \ + if (tx >= TX_8X8) l##dir |= l##dir >> 8; \ + break + + switch (t_dim->lw) { + default: assert(0); /* fall-through */ + case TX_4X4: MERGE_CTX(a, uint8_t, TX_4X4); + case TX_8X8: MERGE_CTX(a, uint16_t, TX_8X8); + case TX_16X16: MERGE_CTX(a, uint32_t, TX_16X16); + case TX_32X32: MERGE_CTX(a, uint32_t, TX_32X32); + case TX_64X64: MERGE_CTX(a, uint32_t, TX_64X64); + } + switch (t_dim->lh) { + default: assert(0); /* fall-through */ + case TX_4X4: MERGE_CTX(l, uint8_t, TX_4X4); + case TX_8X8: MERGE_CTX(l, uint16_t, TX_8X8); + case TX_16X16: MERGE_CTX(l, uint32_t, TX_16X16); + case TX_32X32: MERGE_CTX(l, uint32_t, TX_32X32); + case TX_64X64: MERGE_CTX(l, uint32_t, TX_64X64); + } +#undef MERGE_CTX + + return dav1d_skip_ctx[umin(la & 0x3F, 4)][umin(ll & 0x3F, 4)]; + } +} + +static inline unsigned get_dc_sign_ctx(const int /*enum RectTxfmSize*/ tx, + const uint8_t *const a, + const uint8_t *const l) +{ + uint64_t mask = 0xC0C0C0C0C0C0C0C0ULL, mul = 0x0101010101010101ULL; + int s; + +#if ARCH_X86_64 && defined(__GNUC__) + /* Coerce compilers into producing better code. For some reason + * every x86-64 compiler is awful at handling 64-bit constants. */ + __asm__("" : "+r"(mask), "+r"(mul)); +#endif + + switch(tx) { + default: assert(0); /* fall-through */ + case TX_4X4: { + int t = *(const uint8_t *) a >> 6; + t += *(const uint8_t *) l >> 6; + s = t - 1 - 1; + break; + } + case TX_8X8: { + uint32_t t = *(const uint16_t *) a & (uint32_t) mask; + t += *(const uint16_t *) l & (uint32_t) mask; + t *= 0x04040404U; + s = (int) (t >> 24) - 2 - 2; + break; + } + case TX_16X16: { + uint32_t t = (*(const uint32_t *) a & (uint32_t) mask) >> 6; + t += (*(const uint32_t *) l & (uint32_t) mask) >> 6; + t *= (uint32_t) mul; + s = (int) (t >> 24) - 4 - 4; + break; + } + case TX_32X32: { + uint64_t t = (*(const uint64_t *) a & mask) >> 6; + t += (*(const uint64_t *) l & mask) >> 6; + t *= mul; + s = (int) (t >> 56) - 8 - 8; + break; + } + case TX_64X64: { + uint64_t t = (*(const uint64_t *) &a[0] & mask) >> 6; + t += (*(const uint64_t *) &a[8] & mask) >> 6; + t += (*(const uint64_t *) &l[0] & mask) >> 6; + t += (*(const uint64_t *) &l[8] & mask) >> 6; + t *= mul; + s = (int) (t >> 56) - 16 - 16; + break; + } + case RTX_4X8: { + uint32_t t = *(const uint8_t *) a & (uint32_t) mask; + t += *(const uint16_t *) l & (uint32_t) mask; + t *= 0x04040404U; + s = (int) (t >> 24) - 1 - 2; + break; + } + case RTX_8X4: { + uint32_t t = *(const uint16_t *) a & (uint32_t) mask; + t += *(const uint8_t *) l & (uint32_t) mask; + t *= 0x04040404U; + s = (int) (t >> 24) - 2 - 1; + break; + } + case RTX_8X16: { + uint32_t t = *(const uint16_t *) a & (uint32_t) mask; + t += *(const uint32_t *) l & (uint32_t) mask; + t = (t >> 6) * (uint32_t) mul; + s = (int) (t >> 24) - 2 - 4; + break; + } + case RTX_16X8: { + uint32_t t = *(const uint32_t *) a & (uint32_t) mask; + t += *(const uint16_t *) l & (uint32_t) mask; + t = (t >> 6) * (uint32_t) mul; + s = (int) (t >> 24) - 4 - 2; + break; + } + case RTX_16X32: { + uint64_t t = *(const uint32_t *) a & (uint32_t) mask; + t += *(const uint64_t *) l & mask; + t = (t >> 6) * mul; + s = (int) (t >> 56) - 4 - 8; + break; + } + case RTX_32X16: { + uint64_t t = *(const uint64_t *) a & mask; + t += *(const uint32_t *) l & (uint32_t) mask; + t = (t >> 6) * mul; + s = (int) (t >> 56) - 8 - 4; + break; + } + case RTX_32X64: { + uint64_t t = (*(const uint64_t *) &a[0] & mask) >> 6; + t += (*(const uint64_t *) &l[0] & mask) >> 6; + t += (*(const uint64_t *) &l[8] & mask) >> 6; + t *= mul; + s = (int) (t >> 56) - 8 - 16; + break; + } + case RTX_64X32: { + uint64_t t = (*(const uint64_t *) &a[0] & mask) >> 6; + t += (*(const uint64_t *) &a[8] & mask) >> 6; + t += (*(const uint64_t *) &l[0] & mask) >> 6; + t *= mul; + s = (int) (t >> 56) - 16 - 8; + break; + } + case RTX_4X16: { + uint32_t t = *(const uint8_t *) a & (uint32_t) mask; + t += *(const uint32_t *) l & (uint32_t) mask; + t = (t >> 6) * (uint32_t) mul; + s = (int) (t >> 24) - 1 - 4; + break; + } + case RTX_16X4: { + uint32_t t = *(const uint32_t *) a & (uint32_t) mask; + t += *(const uint8_t *) l & (uint32_t) mask; + t = (t >> 6) * (uint32_t) mul; + s = (int) (t >> 24) - 4 - 1; + break; + } + case RTX_8X32: { + uint64_t t = *(const uint16_t *) a & (uint32_t) mask; + t += *(const uint64_t *) l & mask; + t = (t >> 6) * mul; + s = (int) (t >> 56) - 2 - 8; + break; + } + case RTX_32X8: { + uint64_t t = *(const uint64_t *) a & mask; + t += *(const uint16_t *) l & (uint32_t) mask; + t = (t >> 6) * mul; + s = (int) (t >> 56) - 8 - 2; + break; + } + case RTX_16X64: { + uint64_t t = *(const uint32_t *) a & (uint32_t) mask; + t += *(const uint64_t *) &l[0] & mask; + t = (t >> 6) + ((*(const uint64_t *) &l[8] & mask) >> 6); + t *= mul; + s = (int) (t >> 56) - 4 - 16; + break; + } + case RTX_64X16: { + uint64_t t = *(const uint64_t *) &a[0] & mask; + t += *(const uint32_t *) l & (uint32_t) mask; + t = (t >> 6) + ((*(const uint64_t *) &a[8] & mask) >> 6); + t *= mul; + s = (int) (t >> 56) - 16 - 4; + break; + } + } + + return (s != 0) + (s > 0); +} + +static inline unsigned get_lo_ctx(const uint8_t *const levels, + const enum TxClass tx_class, + unsigned *const hi_mag, + const uint8_t (*const ctx_offsets)[5], + const unsigned x, const unsigned y, + const ptrdiff_t stride) +{ + unsigned mag = levels[0 * stride + 1] + levels[1 * stride + 0]; + unsigned offset; + if (tx_class == TX_CLASS_2D) { + mag += levels[1 * stride + 1]; + *hi_mag = mag; + mag += levels[0 * stride + 2] + levels[2 * stride + 0]; + offset = ctx_offsets[umin(y, 4)][umin(x, 4)]; + } else { + mag += levels[0 * stride + 2]; + *hi_mag = mag; + mag += levels[0 * stride + 3] + levels[0 * stride + 4]; + offset = 26 + (y > 1 ? 10 : y * 5); + } + return offset + (mag > 512 ? 4 : (mag + 64) >> 7); +} + +static int decode_coefs(Dav1dTaskContext *const t, + uint8_t *const a, uint8_t *const l, + const enum RectTxfmSize tx, const enum BlockSize bs, + const Av1Block *const b, const int intra, + const int plane, coef *cf, + enum TxfmType *const txtp, uint8_t *res_ctx) +{ + Dav1dTileState *const ts = t->ts; + const int chroma = !!plane; + const Dav1dFrameContext *const f = t->f; + const int lossless = f->frame_hdr->segmentation.lossless[b->seg_id]; + const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[tx]; + const int dbg = DEBUG_BLOCK_INFO && plane && 0; + + if (dbg) + printf("Start: r=%d\n", ts->msac.rng); + + // does this block have any non-zero coefficients + const int sctx = get_skip_ctx(t_dim, bs, a, l, chroma, f->cur.p.layout); + const int all_skip = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.coef.skip[t_dim->ctx][sctx]); + if (dbg) + printf("Post-non-zero[%d][%d][%d]: r=%d\n", + t_dim->ctx, sctx, all_skip, ts->msac.rng); + if (all_skip) { + *res_ctx = 0x40; + *txtp = lossless * WHT_WHT; /* lossless ? WHT_WHT : DCT_DCT */ + return -1; + } + + // transform type (chroma: derived, luma: explicitly coded) + if (lossless) { + assert(t_dim->max == TX_4X4); + *txtp = WHT_WHT; + } else if (t_dim->max + intra >= TX_64X64) { + *txtp = DCT_DCT; + } else if (chroma) { + // inferred from either the luma txtp (inter) or a LUT (intra) + *txtp = intra ? dav1d_txtp_from_uvmode[b->uv_mode] : + get_uv_inter_txtp(t_dim, *txtp); + } else if (!f->frame_hdr->segmentation.qidx[b->seg_id]) { + // In libaom, lossless is checked by a literal qidx == 0, but not all + // such blocks are actually lossless. The remainder gets an implicit + // transform type (for luma) + *txtp = DCT_DCT; + } else { + unsigned idx; + if (intra) { + const enum IntraPredMode y_mode_nofilt = b->y_mode == FILTER_PRED ? + dav1d_filter_mode_to_y_mode[b->y_angle] : b->y_mode; + if (f->frame_hdr->reduced_txtp_set || t_dim->min == TX_16X16) { + idx = dav1d_msac_decode_symbol_adapt8(&ts->msac, + ts->cdf.m.txtp_intra2[t_dim->min][y_mode_nofilt], 4); + *txtp = dav1d_tx_types_per_set[idx + 0]; + } else { + idx = dav1d_msac_decode_symbol_adapt8(&ts->msac, + ts->cdf.m.txtp_intra1[t_dim->min][y_mode_nofilt], 6); + *txtp = dav1d_tx_types_per_set[idx + 5]; + } + if (dbg) + printf("Post-txtp-intra[%d->%d][%d][%d->%d]: r=%d\n", + tx, t_dim->min, y_mode_nofilt, idx, *txtp, ts->msac.rng); + } else { + if (f->frame_hdr->reduced_txtp_set || t_dim->max == TX_32X32) { + idx = dav1d_msac_decode_bool_adapt(&ts->msac, + ts->cdf.m.txtp_inter3[t_dim->min]); + *txtp = (idx - 1) & IDTX; /* idx ? DCT_DCT : IDTX */ + } else if (t_dim->min == TX_16X16) { + idx = dav1d_msac_decode_symbol_adapt16(&ts->msac, + ts->cdf.m.txtp_inter2, 11); + *txtp = dav1d_tx_types_per_set[idx + 12]; + } else { + idx = dav1d_msac_decode_symbol_adapt16(&ts->msac, + ts->cdf.m.txtp_inter1[t_dim->min], 15); + *txtp = dav1d_tx_types_per_set[idx + 24]; + } + if (dbg) + printf("Post-txtp-inter[%d->%d][%d->%d]: r=%d\n", + tx, t_dim->min, idx, *txtp, ts->msac.rng); + } + } + + // find end-of-block (eob) + int eob; + const int slw = imin(t_dim->lw, TX_32X32), slh = imin(t_dim->lh, TX_32X32); + const int tx2dszctx = slw + slh; + const enum TxClass tx_class = dav1d_tx_type_class[*txtp]; + const int is_1d = tx_class != TX_CLASS_2D; + switch (tx2dszctx) { +#define case_sz(sz, bin, ns, is_1d) \ + case sz: { \ + uint16_t *const eob_bin_cdf = ts->cdf.coef.eob_bin_##bin[chroma]is_1d; \ + eob = dav1d_msac_decode_symbol_adapt##ns(&ts->msac, eob_bin_cdf, 4 + sz); \ + break; \ + } + case_sz(0, 16, 8, [is_1d]); + case_sz(1, 32, 8, [is_1d]); + case_sz(2, 64, 8, [is_1d]); + case_sz(3, 128, 8, [is_1d]); + case_sz(4, 256, 16, [is_1d]); + case_sz(5, 512, 16, ); + case_sz(6, 1024, 16, ); +#undef case_sz + } + if (dbg) + printf("Post-eob_bin_%d[%d][%d][%d]: r=%d\n", + 16 << tx2dszctx, chroma, is_1d, eob, ts->msac.rng); + if (eob > 1) { + const int eob_bin = eob - 2; + uint16_t *const eob_hi_bit_cdf = + ts->cdf.coef.eob_hi_bit[t_dim->ctx][chroma][eob_bin]; + const int eob_hi_bit = dav1d_msac_decode_bool_adapt(&ts->msac, eob_hi_bit_cdf); + if (dbg) + printf("Post-eob_hi_bit[%d][%d][%d][%d]: r=%d\n", + t_dim->ctx, chroma, eob_bin, eob_hi_bit, ts->msac.rng); + eob = ((eob_hi_bit | 2) << eob_bin) | dav1d_msac_decode_bools(&ts->msac, eob_bin); + if (dbg) + printf("Post-eob[%d]: r=%d\n", eob, ts->msac.rng); + } + assert(eob >= 0); + + // base tokens + uint16_t (*const eob_cdf)[4] = ts->cdf.coef.eob_base_tok[t_dim->ctx][chroma]; + uint16_t (*const hi_cdf)[4] = ts->cdf.coef.br_tok[imin(t_dim->ctx, 3)][chroma]; + unsigned rc, dc_tok; + + if (eob) { + uint16_t (*const lo_cdf)[4] = ts->cdf.coef.base_tok[t_dim->ctx][chroma]; + uint8_t *const levels = t->scratch.levels; // bits 0-5: tok, 6-7: lo_tok + + /* eob */ + unsigned ctx = 1 + (eob > 2 << tx2dszctx) + (eob > 4 << tx2dszctx); + int eob_tok = dav1d_msac_decode_symbol_adapt4(&ts->msac, eob_cdf[ctx], 2); + int tok = eob_tok + 1; + int level_tok = tok * 0x41; + unsigned mag; + +#define DECODE_COEFS_CLASS(tx_class) \ + unsigned x, y; \ + uint8_t *level; \ + if (tx_class == TX_CLASS_2D) \ + rc = scan[eob], x = rc >> shift, y = rc & mask; \ + else if (tx_class == TX_CLASS_H) \ + /* Transposing reduces the stride and padding requirements */ \ + x = eob & mask, y = eob >> shift, rc = eob; \ + else /* tx_class == TX_CLASS_V */ \ + x = eob & mask, y = eob >> shift, rc = (x << shift2) | y; \ + if (dbg) \ + printf("Post-lo_tok[%d][%d][%d][%d=%d=%d]: r=%d\n", \ + t_dim->ctx, chroma, ctx, eob, rc, tok, ts->msac.rng); \ + if (eob_tok == 2) { \ + ctx = (tx_class == TX_CLASS_2D ? (x | y) > 1 : y != 0) ? 14 : 7; \ + tok = dav1d_msac_decode_hi_tok(&ts->msac, hi_cdf[ctx]); \ + level_tok = tok + (3 << 6); \ + if (dbg) \ + printf("Post-hi_tok[%d][%d][%d][%d=%d=%d]: r=%d\n", \ + imin(t_dim->ctx, 3), chroma, ctx, eob, rc, tok, \ + ts->msac.rng); \ + } \ + cf[rc] = tok << 11; \ + if (tx_class == TX_CLASS_2D) \ + level = levels + rc; \ + else \ + level = levels + x * stride + y; \ + *level = (uint8_t) level_tok; \ + for (int i = eob - 1; i > 0; i--) { /* ac */ \ + unsigned rc_i; \ + if (tx_class == TX_CLASS_2D) \ + rc_i = scan[i], x = rc_i >> shift, y = rc_i & mask; \ + else if (tx_class == TX_CLASS_H) \ + x = i & mask, y = i >> shift, rc_i = i; \ + else /* tx_class == TX_CLASS_V */ \ + x = i & mask, y = i >> shift, rc_i = (x << shift2) | y; \ + assert(x < 32 && y < 32); \ + if (tx_class == TX_CLASS_2D) \ + level = levels + rc_i; \ + else \ + level = levels + x * stride + y; \ + ctx = get_lo_ctx(level, tx_class, &mag, lo_ctx_offsets, x, y, stride); \ + if (tx_class == TX_CLASS_2D) \ + y |= x; \ + tok = dav1d_msac_decode_symbol_adapt4(&ts->msac, lo_cdf[ctx], 3); \ + if (dbg) \ + printf("Post-lo_tok[%d][%d][%d][%d=%d=%d]: r=%d\n", \ + t_dim->ctx, chroma, ctx, i, rc_i, tok, ts->msac.rng); \ + if (tok == 3) { \ + mag &= 63; \ + ctx = (y > (tx_class == TX_CLASS_2D) ? 14 : 7) + \ + (mag > 12 ? 6 : (mag + 1) >> 1); \ + tok = dav1d_msac_decode_hi_tok(&ts->msac, hi_cdf[ctx]); \ + if (dbg) \ + printf("Post-hi_tok[%d][%d][%d][%d=%d=%d]: r=%d\n", \ + imin(t_dim->ctx, 3), chroma, ctx, i, rc_i, tok, \ + ts->msac.rng); \ + *level = (uint8_t) (tok + (3 << 6)); \ + cf[rc_i] = (tok << 11) | rc; \ + rc = rc_i; \ + } else { \ + /* 0x1 for tok, 0x7ff as bitmask for rc, 0x41 for level_tok */ \ + tok *= 0x17ff41; \ + *level = (uint8_t) tok; \ + /* tok ? (tok << 11) | rc : 0 */ \ + tok = (tok >> 9) & (rc + ~0x7ffu); \ + if (tok) rc = rc_i; \ + cf[rc_i] = tok; \ + } \ + } \ + /* dc */ \ + ctx = (tx_class == TX_CLASS_2D) ? 0 : \ + get_lo_ctx(levels, tx_class, &mag, lo_ctx_offsets, 0, 0, stride); \ + dc_tok = dav1d_msac_decode_symbol_adapt4(&ts->msac, lo_cdf[ctx], 3); \ + if (dbg) \ + printf("Post-dc_lo_tok[%d][%d][%d][%d]: r=%d\n", \ + t_dim->ctx, chroma, ctx, dc_tok, ts->msac.rng); \ + if (dc_tok == 3) { \ + if (tx_class == TX_CLASS_2D) \ + mag = levels[0 * stride + 1] + levels[1 * stride + 0] + \ + levels[1 * stride + 1]; \ + mag &= 63; \ + ctx = mag > 12 ? 6 : (mag + 1) >> 1; \ + dc_tok = dav1d_msac_decode_hi_tok(&ts->msac, hi_cdf[ctx]); \ + if (dbg) \ + printf("Post-dc_hi_tok[%d][%d][0][%d]: r=%d\n", \ + imin(t_dim->ctx, 3), chroma, dc_tok, ts->msac.rng); \ + } \ + break + + const uint16_t *scan; + switch (tx_class) { + case TX_CLASS_2D: { + const unsigned nonsquare_tx = tx >= RTX_4X8; + const uint8_t (*const lo_ctx_offsets)[5] = + dav1d_lo_ctx_offsets[nonsquare_tx + (tx & nonsquare_tx)]; + scan = dav1d_scans[tx]; + const ptrdiff_t stride = 4 << slh; + const unsigned shift = slh + 2, shift2 = 0; + const unsigned mask = (4 << slh) - 1; + memset(levels, 0, stride * ((4 << slw) + 2)); + DECODE_COEFS_CLASS(TX_CLASS_2D); + } + case TX_CLASS_H: { + const uint8_t (*const lo_ctx_offsets)[5] = NULL; + const ptrdiff_t stride = 16; + const unsigned shift = slh + 2, shift2 = 0; + const unsigned mask = (4 << slh) - 1; + memset(levels, 0, stride * ((4 << slh) + 2)); + DECODE_COEFS_CLASS(TX_CLASS_H); + } + case TX_CLASS_V: { + const uint8_t (*const lo_ctx_offsets)[5] = NULL; + const ptrdiff_t stride = 16; + const unsigned shift = slw + 2, shift2 = slh + 2; + const unsigned mask = (4 << slw) - 1; + memset(levels, 0, stride * ((4 << slw) + 2)); + DECODE_COEFS_CLASS(TX_CLASS_V); + } +#undef DECODE_COEFS_CLASS + default: assert(0); + } + } else { // dc-only + int tok_br = dav1d_msac_decode_symbol_adapt4(&ts->msac, eob_cdf[0], 2); + dc_tok = 1 + tok_br; + if (dbg) + printf("Post-dc_lo_tok[%d][%d][%d][%d]: r=%d\n", + t_dim->ctx, chroma, 0, dc_tok, ts->msac.rng); + if (tok_br == 2) { + dc_tok = dav1d_msac_decode_hi_tok(&ts->msac, hi_cdf[0]); + if (dbg) + printf("Post-dc_hi_tok[%d][%d][0][%d]: r=%d\n", + imin(t_dim->ctx, 3), chroma, dc_tok, ts->msac.rng); + } + rc = 0; + } + + // residual and sign + const uint16_t *const dq_tbl = ts->dq[b->seg_id][plane]; + const uint8_t *const qm_tbl = *txtp < IDTX ? f->qm[tx][plane] : NULL; + const int dq_shift = imax(0, t_dim->ctx - 2); + const int cf_max = ~(~127U << (BITDEPTH == 8 ? 8 : f->cur.p.bpc)); + unsigned cul_level, dc_sign_level; + + if (!dc_tok) { + cul_level = 0; + dc_sign_level = 1 << 6; + if (qm_tbl) goto ac_qm; + goto ac_noqm; + } + + const int dc_sign_ctx = get_dc_sign_ctx(tx, a, l); + uint16_t *const dc_sign_cdf = ts->cdf.coef.dc_sign[chroma][dc_sign_ctx]; + const int dc_sign = dav1d_msac_decode_bool_adapt(&ts->msac, dc_sign_cdf); + if (dbg) + printf("Post-dc_sign[%d][%d][%d]: r=%d\n", + chroma, dc_sign_ctx, dc_sign, ts->msac.rng); + + int dc_dq = dq_tbl[0]; + dc_sign_level = (dc_sign - 1) & (2 << 6); + + if (qm_tbl) { + dc_dq = (dc_dq * qm_tbl[0] + 16) >> 5; + + if (dc_tok == 15) { + dc_tok = read_golomb(&ts->msac) + 15; + if (dbg) + printf("Post-dc_residual[%d->%d]: r=%d\n", + dc_tok - 15, dc_tok, ts->msac.rng); + + dc_tok &= 0xfffff; + dc_dq = (dc_dq * dc_tok) & 0xffffff; + } else { + dc_dq *= dc_tok; + assert(dc_dq <= 0xffffff); + } + cul_level = dc_tok; + dc_dq >>= dq_shift; + dc_dq = umin(dc_dq, cf_max + dc_sign); + cf[0] = (coef) (dc_sign ? -dc_dq : dc_dq); + + if (rc) ac_qm: { + const unsigned ac_dq = dq_tbl[1]; + do { + const int sign = dav1d_msac_decode_bool_equi(&ts->msac); + if (dbg) + printf("Post-sign[%d=%d]: r=%d\n", rc, sign, ts->msac.rng); + const unsigned rc_tok = cf[rc]; + unsigned tok, dq = (ac_dq * qm_tbl[rc] + 16) >> 5; + int dq_sat; + + if (rc_tok >= (15 << 11)) { + tok = read_golomb(&ts->msac) + 15; + if (dbg) + printf("Post-residual[%d=%d->%d]: r=%d\n", + rc, tok - 15, tok, ts->msac.rng); + + tok &= 0xfffff; + dq = (dq * tok) & 0xffffff; + } else { + tok = rc_tok >> 11; + dq *= tok; + assert(dq <= 0xffffff); + } + cul_level += tok; + dq >>= dq_shift; + dq_sat = umin(dq, cf_max + sign); + cf[rc] = (coef) (sign ? -dq_sat : dq_sat); + + rc = rc_tok & 0x3ff; + } while (rc); + } + } else { + // non-qmatrix is the common case and allows for additional optimizations + if (dc_tok == 15) { + dc_tok = read_golomb(&ts->msac) + 15; + if (dbg) + printf("Post-dc_residual[%d->%d]: r=%d\n", + dc_tok - 15, dc_tok, ts->msac.rng); + + dc_tok &= 0xfffff; + dc_dq = ((dc_dq * dc_tok) & 0xffffff) >> dq_shift; + dc_dq = umin(dc_dq, cf_max + dc_sign); + } else { + dc_dq = ((dc_dq * dc_tok) >> dq_shift); + assert(dc_dq <= cf_max); + } + cul_level = dc_tok; + cf[0] = (coef) (dc_sign ? -dc_dq : dc_dq); + + if (rc) ac_noqm: { + const unsigned ac_dq = dq_tbl[1]; + do { + const int sign = dav1d_msac_decode_bool_equi(&ts->msac); + if (dbg) + printf("Post-sign[%d=%d]: r=%d\n", rc, sign, ts->msac.rng); + const unsigned rc_tok = cf[rc]; + unsigned tok; + int dq; + + // residual + if (rc_tok >= (15 << 11)) { + tok = read_golomb(&ts->msac) + 15; + if (dbg) + printf("Post-residual[%d=%d->%d]: r=%d\n", + rc, tok - 15, tok, ts->msac.rng); + + // coefficient parsing, see 5.11.39 + tok &= 0xfffff; + + // dequant, see 7.12.3 + dq = ((ac_dq * tok) & 0xffffff) >> dq_shift; + dq = umin(dq, cf_max + sign); + } else { + // cannot exceed cf_max, so we can avoid the clipping + tok = rc_tok >> 11; + dq = ((ac_dq * tok) >> dq_shift); + assert(dq <= cf_max); + } + cul_level += tok; + cf[rc] = (coef) (sign ? -dq : dq); + + rc = rc_tok & 0x3ff; // next non-zero rc, zero if eob + } while (rc); + } + } + + // context + *res_ctx = umin(cul_level, 63) | dc_sign_level; + + return eob; +} + +static void read_coef_tree(Dav1dTaskContext *const t, + const enum BlockSize bs, const Av1Block *const b, + const enum RectTxfmSize ytx, const int depth, + const uint16_t *const tx_split, + const int x_off, const int y_off, pixel *dst) +{ + const Dav1dFrameContext *const f = t->f; + Dav1dTileState *const ts = t->ts; + const Dav1dDSPContext *const dsp = f->dsp; + const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[ytx]; + const int txw = t_dim->w, txh = t_dim->h; + + /* y_off can be larger than 3 since lossless blocks use TX_4X4 but can't + * be splitted. Aviods an undefined left shift. */ + if (depth < 2 && tx_split[depth] && + tx_split[depth] & (1 << (y_off * 4 + x_off))) + { + const enum RectTxfmSize sub = t_dim->sub; + const TxfmInfo *const sub_t_dim = &dav1d_txfm_dimensions[sub]; + const int txsw = sub_t_dim->w, txsh = sub_t_dim->h; + + read_coef_tree(t, bs, b, sub, depth + 1, tx_split, + x_off * 2 + 0, y_off * 2 + 0, dst); + t->bx += txsw; + if (txw >= txh && t->bx < f->bw) + read_coef_tree(t, bs, b, sub, depth + 1, tx_split, x_off * 2 + 1, + y_off * 2 + 0, dst ? &dst[4 * txsw] : NULL); + t->bx -= txsw; + t->by += txsh; + if (txh >= txw && t->by < f->bh) { + if (dst) + dst += 4 * txsh * PXSTRIDE(f->cur.stride[0]); + read_coef_tree(t, bs, b, sub, depth + 1, tx_split, + x_off * 2 + 0, y_off * 2 + 1, dst); + t->bx += txsw; + if (txw >= txh && t->bx < f->bw) + read_coef_tree(t, bs, b, sub, depth + 1, tx_split, x_off * 2 + 1, + y_off * 2 + 1, dst ? &dst[4 * txsw] : NULL); + t->bx -= txsw; + } + t->by -= txsh; + } else { + const int bx4 = t->bx & 31, by4 = t->by & 31; + enum TxfmType txtp; + uint8_t cf_ctx; + int eob; + coef *cf; + + if (t->frame_thread.pass) { + const int p = t->frame_thread.pass & 1; + assert(ts->frame_thread[p].cf); + cf = ts->frame_thread[p].cf; + ts->frame_thread[p].cf += imin(t_dim->w, 8) * imin(t_dim->h, 8) * 16; + } else { + cf = bitfn(t->cf); + } + if (t->frame_thread.pass != 2) { + eob = decode_coefs(t, &t->a->lcoef[bx4], &t->l.lcoef[by4], + ytx, bs, b, 0, 0, cf, &txtp, &cf_ctx); + if (DEBUG_BLOCK_INFO) + printf("Post-y-cf-blk[tx=%d,txtp=%d,eob=%d]: r=%d\n", + ytx, txtp, eob, ts->msac.rng); + dav1d_memset_likely_pow2(&t->a->lcoef[bx4], cf_ctx, imin(txw, f->bw - t->bx)); + dav1d_memset_likely_pow2(&t->l.lcoef[by4], cf_ctx, imin(txh, f->bh - t->by)); +#define set_ctx(rep_macro) \ + for (int y = 0; y < txh; y++) { \ + rep_macro(txtp_map, 0, txtp); \ + txtp_map += 32; \ + } + uint8_t *txtp_map = &t->scratch.txtp_map[by4 * 32 + bx4]; + case_set_upto16(t_dim->lw); +#undef set_ctx + if (t->frame_thread.pass == 1) + *ts->frame_thread[1].cbi++ = eob * (1 << 5) + txtp; + } else { + const int cbi = *ts->frame_thread[0].cbi++; + eob = cbi >> 5; + txtp = cbi & 0x1f; + } + if (!(t->frame_thread.pass & 1)) { + assert(dst); + if (eob >= 0) { + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) + coef_dump(cf, imin(t_dim->h, 8) * 4, imin(t_dim->w, 8) * 4, 3, "dq"); + dsp->itx.itxfm_add[ytx][txtp](dst, f->cur.stride[0], cf, eob + HIGHBD_CALL_SUFFIX); + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) + hex_dump(dst, f->cur.stride[0], t_dim->w * 4, t_dim->h * 4, "recon"); + } + } + } +} + +void bytefn(dav1d_read_coef_blocks)(Dav1dTaskContext *const t, + const enum BlockSize bs, const Av1Block *const b) +{ + const Dav1dFrameContext *const f = t->f; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int bx4 = t->bx & 31, by4 = t->by & 31; + const int cbx4 = bx4 >> ss_hor, cby4 = by4 >> ss_ver; + const uint8_t *const b_dim = dav1d_block_dimensions[bs]; + const int bw4 = b_dim[0], bh4 = b_dim[1]; + const int cbw4 = (bw4 + ss_hor) >> ss_hor, cbh4 = (bh4 + ss_ver) >> ss_ver; + const int has_chroma = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400 && + (bw4 > ss_hor || t->bx & 1) && + (bh4 > ss_ver || t->by & 1); + + if (b->skip) { + BlockContext *const a = t->a; + dav1d_memset_pow2[b_dim[2]](&a->lcoef[bx4], 0x40); + dav1d_memset_pow2[b_dim[3]](&t->l.lcoef[by4], 0x40); + if (has_chroma) { + dav1d_memset_pow2_fn memset_cw = dav1d_memset_pow2[ulog2(cbw4)]; + dav1d_memset_pow2_fn memset_ch = dav1d_memset_pow2[ulog2(cbh4)]; + memset_cw(&a->ccoef[0][cbx4], 0x40); + memset_cw(&a->ccoef[1][cbx4], 0x40); + memset_ch(&t->l.ccoef[0][cby4], 0x40); + memset_ch(&t->l.ccoef[1][cby4], 0x40); + } + return; + } + + Dav1dTileState *const ts = t->ts; + const int w4 = imin(bw4, f->bw - t->bx), h4 = imin(bh4, f->bh - t->by); + const int cw4 = (w4 + ss_hor) >> ss_hor, ch4 = (h4 + ss_ver) >> ss_ver; + assert(t->frame_thread.pass == 1); + assert(!b->skip); + const TxfmInfo *const uv_t_dim = &dav1d_txfm_dimensions[b->uvtx]; + const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[b->intra ? b->tx : b->max_ytx]; + const uint16_t tx_split[2] = { b->tx_split0, b->tx_split1 }; + + for (int init_y = 0; init_y < h4; init_y += 16) { + const int sub_h4 = imin(h4, 16 + init_y); + for (int init_x = 0; init_x < w4; init_x += 16) { + const int sub_w4 = imin(w4, init_x + 16); + int y_off = !!init_y, y, x; + for (y = init_y, t->by += init_y; y < sub_h4; + y += t_dim->h, t->by += t_dim->h, y_off++) + { + int x_off = !!init_x; + for (x = init_x, t->bx += init_x; x < sub_w4; + x += t_dim->w, t->bx += t_dim->w, x_off++) + { + if (!b->intra) { + read_coef_tree(t, bs, b, b->max_ytx, 0, tx_split, + x_off, y_off, NULL); + } else { + uint8_t cf_ctx = 0x40; + enum TxfmType txtp; + const int eob = + decode_coefs(t, &t->a->lcoef[bx4 + x], + &t->l.lcoef[by4 + y], b->tx, bs, b, 1, + 0, ts->frame_thread[1].cf, &txtp, &cf_ctx); + if (DEBUG_BLOCK_INFO) + printf("Post-y-cf-blk[tx=%d,txtp=%d,eob=%d]: r=%d\n", + b->tx, txtp, eob, ts->msac.rng); + *ts->frame_thread[1].cbi++ = eob * (1 << 5) + txtp; + ts->frame_thread[1].cf += imin(t_dim->w, 8) * imin(t_dim->h, 8) * 16; + dav1d_memset_likely_pow2(&t->a->lcoef[bx4 + x], cf_ctx, imin(t_dim->w, f->bw - t->bx)); + dav1d_memset_likely_pow2(&t->l.lcoef[by4 + y], cf_ctx, imin(t_dim->h, f->bh - t->by)); + } + } + t->bx -= x; + } + t->by -= y; + + if (!has_chroma) continue; + + const int sub_ch4 = imin(ch4, (init_y + 16) >> ss_ver); + const int sub_cw4 = imin(cw4, (init_x + 16) >> ss_hor); + for (int pl = 0; pl < 2; pl++) { + for (y = init_y >> ss_ver, t->by += init_y; y < sub_ch4; + y += uv_t_dim->h, t->by += uv_t_dim->h << ss_ver) + { + for (x = init_x >> ss_hor, t->bx += init_x; x < sub_cw4; + x += uv_t_dim->w, t->bx += uv_t_dim->w << ss_hor) + { + uint8_t cf_ctx = 0x40; + enum TxfmType txtp; + if (!b->intra) + txtp = t->scratch.txtp_map[(by4 + (y << ss_ver)) * 32 + + bx4 + (x << ss_hor)]; + const int eob = + decode_coefs(t, &t->a->ccoef[pl][cbx4 + x], + &t->l.ccoef[pl][cby4 + y], b->uvtx, bs, + b, b->intra, 1 + pl, ts->frame_thread[1].cf, + &txtp, &cf_ctx); + if (DEBUG_BLOCK_INFO) + printf("Post-uv-cf-blk[pl=%d,tx=%d," + "txtp=%d,eob=%d]: r=%d\n", + pl, b->uvtx, txtp, eob, ts->msac.rng); + *ts->frame_thread[1].cbi++ = eob * (1 << 5) + txtp; + ts->frame_thread[1].cf += uv_t_dim->w * uv_t_dim->h * 16; + int ctw = imin(uv_t_dim->w, (f->bw - t->bx + ss_hor) >> ss_hor); + int cth = imin(uv_t_dim->h, (f->bh - t->by + ss_ver) >> ss_ver); + dav1d_memset_likely_pow2(&t->a->ccoef[pl][cbx4 + x], cf_ctx, ctw); + dav1d_memset_likely_pow2(&t->l.ccoef[pl][cby4 + y], cf_ctx, cth); + } + t->bx -= x << ss_hor; + } + t->by -= y << ss_ver; + } + } + } +} + +static int mc(Dav1dTaskContext *const t, + pixel *const dst8, int16_t *const dst16, const ptrdiff_t dst_stride, + const int bw4, const int bh4, + const int bx, const int by, const int pl, + const mv mv, const Dav1dThreadPicture *const refp, const int refidx, + const enum Filter2d filter_2d) +{ + assert((dst8 != NULL) ^ (dst16 != NULL)); + const Dav1dFrameContext *const f = t->f; + const int ss_ver = !!pl && f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = !!pl && f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int h_mul = 4 >> ss_hor, v_mul = 4 >> ss_ver; + const int mvx = mv.x, mvy = mv.y; + const int mx = mvx & (15 >> !ss_hor), my = mvy & (15 >> !ss_ver); + ptrdiff_t ref_stride = refp->p.stride[!!pl]; + const pixel *ref; + + if (refp->p.p.w == f->cur.p.w && refp->p.p.h == f->cur.p.h) { + const int dx = bx * h_mul + (mvx >> (3 + ss_hor)); + const int dy = by * v_mul + (mvy >> (3 + ss_ver)); + int w, h; + + if (refp->p.data[0] != f->cur.data[0]) { // i.e. not for intrabc + w = (f->cur.p.w + ss_hor) >> ss_hor; + h = (f->cur.p.h + ss_ver) >> ss_ver; + } else { + w = f->bw * 4 >> ss_hor; + h = f->bh * 4 >> ss_ver; + } + if (dx < !!mx * 3 || dy < !!my * 3 || + dx + bw4 * h_mul + !!mx * 4 > w || + dy + bh4 * v_mul + !!my * 4 > h) + { + pixel *const emu_edge_buf = bitfn(t->scratch.emu_edge); + f->dsp->mc.emu_edge(bw4 * h_mul + !!mx * 7, bh4 * v_mul + !!my * 7, + w, h, dx - !!mx * 3, dy - !!my * 3, + emu_edge_buf, 192 * sizeof(pixel), + refp->p.data[pl], ref_stride); + ref = &emu_edge_buf[192 * !!my * 3 + !!mx * 3]; + ref_stride = 192 * sizeof(pixel); + } else { + ref = ((pixel *) refp->p.data[pl]) + PXSTRIDE(ref_stride) * dy + dx; + } + + if (dst8 != NULL) { + f->dsp->mc.mc[filter_2d](dst8, dst_stride, ref, ref_stride, bw4 * h_mul, + bh4 * v_mul, mx << !ss_hor, my << !ss_ver + HIGHBD_CALL_SUFFIX); + } else { + f->dsp->mc.mct[filter_2d](dst16, ref, ref_stride, bw4 * h_mul, + bh4 * v_mul, mx << !ss_hor, my << !ss_ver + HIGHBD_CALL_SUFFIX); + } + } else { + assert(refp != &f->sr_cur); + + const int orig_pos_y = (by * v_mul << 4) + mvy * (1 << !ss_ver); + const int orig_pos_x = (bx * h_mul << 4) + mvx * (1 << !ss_hor); +#define scale_mv(res, val, scale) do { \ + const int64_t tmp = (int64_t)(val) * scale + (scale - 0x4000) * 8; \ + res = apply_sign64((int) ((llabs(tmp) + 128) >> 8), tmp) + 32; \ + } while (0) + int pos_y, pos_x; + scale_mv(pos_x, orig_pos_x, f->svc[refidx][0].scale); + scale_mv(pos_y, orig_pos_y, f->svc[refidx][1].scale); +#undef scale_mv + const int left = pos_x >> 10; + const int top = pos_y >> 10; + const int right = + ((pos_x + (bw4 * h_mul - 1) * f->svc[refidx][0].step) >> 10) + 1; + const int bottom = + ((pos_y + (bh4 * v_mul - 1) * f->svc[refidx][1].step) >> 10) + 1; + + if (DEBUG_BLOCK_INFO) + printf("Off %dx%d [%d,%d,%d], size %dx%d [%d,%d]\n", + left, top, orig_pos_x, f->svc[refidx][0].scale, refidx, + right-left, bottom-top, + f->svc[refidx][0].step, f->svc[refidx][1].step); + + const int w = (refp->p.p.w + ss_hor) >> ss_hor; + const int h = (refp->p.p.h + ss_ver) >> ss_ver; + if (left < 3 || top < 3 || right + 4 > w || bottom + 4 > h) { + pixel *const emu_edge_buf = bitfn(t->scratch.emu_edge); + f->dsp->mc.emu_edge(right - left + 7, bottom - top + 7, + w, h, left - 3, top - 3, + emu_edge_buf, 320 * sizeof(pixel), + refp->p.data[pl], ref_stride); + ref = &emu_edge_buf[320 * 3 + 3]; + ref_stride = 320 * sizeof(pixel); + if (DEBUG_BLOCK_INFO) printf("Emu\n"); + } else { + ref = ((pixel *) refp->p.data[pl]) + PXSTRIDE(ref_stride) * top + left; + } + + if (dst8 != NULL) { + f->dsp->mc.mc_scaled[filter_2d](dst8, dst_stride, ref, ref_stride, + bw4 * h_mul, bh4 * v_mul, + pos_x & 0x3ff, pos_y & 0x3ff, + f->svc[refidx][0].step, + f->svc[refidx][1].step + HIGHBD_CALL_SUFFIX); + } else { + f->dsp->mc.mct_scaled[filter_2d](dst16, ref, ref_stride, + bw4 * h_mul, bh4 * v_mul, + pos_x & 0x3ff, pos_y & 0x3ff, + f->svc[refidx][0].step, + f->svc[refidx][1].step + HIGHBD_CALL_SUFFIX); + } + } + + return 0; +} + +static int obmc(Dav1dTaskContext *const t, + pixel *const dst, const ptrdiff_t dst_stride, + const uint8_t *const b_dim, const int pl, + const int bx4, const int by4, const int w4, const int h4) +{ + assert(!(t->bx & 1) && !(t->by & 1)); + const Dav1dFrameContext *const f = t->f; + /*const*/ refmvs_block **r = &t->rt.r[(t->by & 31) + 5]; + pixel *const lap = bitfn(t->scratch.lap); + const int ss_ver = !!pl && f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = !!pl && f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int h_mul = 4 >> ss_hor, v_mul = 4 >> ss_ver; + int res; + + if (t->by > t->ts->tiling.row_start && + (!pl || b_dim[0] * h_mul + b_dim[1] * v_mul >= 16)) + { + for (int i = 0, x = 0; x < w4 && i < imin(b_dim[2], 4); ) { + // only odd blocks are considered for overlap handling, hence +1 + const refmvs_block *const a_r = &r[-1][t->bx + x + 1]; + const uint8_t *const a_b_dim = dav1d_block_dimensions[a_r->bs]; + const int step4 = iclip(a_b_dim[0], 2, 16); + + if (a_r->ref.ref[0] > 0) { + const int ow4 = imin(step4, b_dim[0]); + const int oh4 = imin(b_dim[1], 16) >> 1; + res = mc(t, lap, NULL, ow4 * h_mul * sizeof(pixel), ow4, (oh4 * 3 + 3) >> 2, + t->bx + x, t->by, pl, a_r->mv.mv[0], + &f->refp[a_r->ref.ref[0] - 1], a_r->ref.ref[0] - 1, + dav1d_filter_2d[t->a->filter[1][bx4 + x + 1]][t->a->filter[0][bx4 + x + 1]]); + if (res) return res; + f->dsp->mc.blend_h(&dst[x * h_mul], dst_stride, lap, + h_mul * ow4, v_mul * oh4); + i++; + } + x += step4; + } + } + + if (t->bx > t->ts->tiling.col_start) + for (int i = 0, y = 0; y < h4 && i < imin(b_dim[3], 4); ) { + // only odd blocks are considered for overlap handling, hence +1 + const refmvs_block *const l_r = &r[y + 1][t->bx - 1]; + const uint8_t *const l_b_dim = dav1d_block_dimensions[l_r->bs]; + const int step4 = iclip(l_b_dim[1], 2, 16); + + if (l_r->ref.ref[0] > 0) { + const int ow4 = imin(b_dim[0], 16) >> 1; + const int oh4 = imin(step4, b_dim[1]); + res = mc(t, lap, NULL, h_mul * ow4 * sizeof(pixel), ow4, oh4, + t->bx, t->by + y, pl, l_r->mv.mv[0], + &f->refp[l_r->ref.ref[0] - 1], l_r->ref.ref[0] - 1, + dav1d_filter_2d[t->l.filter[1][by4 + y + 1]][t->l.filter[0][by4 + y + 1]]); + if (res) return res; + f->dsp->mc.blend_v(&dst[y * v_mul * PXSTRIDE(dst_stride)], + dst_stride, lap, h_mul * ow4, v_mul * oh4); + i++; + } + y += step4; + } + return 0; +} + +static int warp_affine(Dav1dTaskContext *const t, + pixel *dst8, int16_t *dst16, const ptrdiff_t dstride, + const uint8_t *const b_dim, const int pl, + const Dav1dThreadPicture *const refp, + const Dav1dWarpedMotionParams *const wmp) +{ + assert((dst8 != NULL) ^ (dst16 != NULL)); + const Dav1dFrameContext *const f = t->f; + const Dav1dDSPContext *const dsp = f->dsp; + const int ss_ver = !!pl && f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = !!pl && f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int h_mul = 4 >> ss_hor, v_mul = 4 >> ss_ver; + assert(!((b_dim[0] * h_mul) & 7) && !((b_dim[1] * v_mul) & 7)); + const int32_t *const mat = wmp->matrix; + const int width = (refp->p.p.w + ss_hor) >> ss_hor; + const int height = (refp->p.p.h + ss_ver) >> ss_ver; + + for (int y = 0; y < b_dim[1] * v_mul; y += 8) { + const int src_y = t->by * 4 + ((y + 4) << ss_ver); + const int64_t mat3_y = (int64_t) mat[3] * src_y + mat[0]; + const int64_t mat5_y = (int64_t) mat[5] * src_y + mat[1]; + for (int x = 0; x < b_dim[0] * h_mul; x += 8) { + // calculate transformation relative to center of 8x8 block in + // luma pixel units + const int src_x = t->bx * 4 + ((x + 4) << ss_hor); + const int64_t mvx = ((int64_t) mat[2] * src_x + mat3_y) >> ss_hor; + const int64_t mvy = ((int64_t) mat[4] * src_x + mat5_y) >> ss_ver; + + const int dx = (int) (mvx >> 16) - 4; + const int mx = (((int) mvx & 0xffff) - wmp->u.p.alpha * 4 - + wmp->u.p.beta * 7) & ~0x3f; + const int dy = (int) (mvy >> 16) - 4; + const int my = (((int) mvy & 0xffff) - wmp->u.p.gamma * 4 - + wmp->u.p.delta * 4) & ~0x3f; + + const pixel *ref_ptr; + ptrdiff_t ref_stride = refp->p.stride[!!pl]; + + if (dx < 3 || dx + 8 + 4 > width || dy < 3 || dy + 8 + 4 > height) { + pixel *const emu_edge_buf = bitfn(t->scratch.emu_edge); + f->dsp->mc.emu_edge(15, 15, width, height, dx - 3, dy - 3, + emu_edge_buf, 32 * sizeof(pixel), + refp->p.data[pl], ref_stride); + ref_ptr = &emu_edge_buf[32 * 3 + 3]; + ref_stride = 32 * sizeof(pixel); + } else { + ref_ptr = ((pixel *) refp->p.data[pl]) + PXSTRIDE(ref_stride) * dy + dx; + } + if (dst16 != NULL) + dsp->mc.warp8x8t(&dst16[x], dstride, ref_ptr, ref_stride, + wmp->u.abcd, mx, my HIGHBD_CALL_SUFFIX); + else + dsp->mc.warp8x8(&dst8[x], dstride, ref_ptr, ref_stride, + wmp->u.abcd, mx, my HIGHBD_CALL_SUFFIX); + } + if (dst8) dst8 += 8 * PXSTRIDE(dstride); + else dst16 += 8 * dstride; + } + return 0; +} + +void bytefn(dav1d_recon_b_intra)(Dav1dTaskContext *const t, const enum BlockSize bs, + const enum EdgeFlags intra_edge_flags, + const Av1Block *const b) +{ + Dav1dTileState *const ts = t->ts; + const Dav1dFrameContext *const f = t->f; + const Dav1dDSPContext *const dsp = f->dsp; + const int bx4 = t->bx & 31, by4 = t->by & 31; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int cbx4 = bx4 >> ss_hor, cby4 = by4 >> ss_ver; + const uint8_t *const b_dim = dav1d_block_dimensions[bs]; + const int bw4 = b_dim[0], bh4 = b_dim[1]; + const int w4 = imin(bw4, f->bw - t->bx), h4 = imin(bh4, f->bh - t->by); + const int cw4 = (w4 + ss_hor) >> ss_hor, ch4 = (h4 + ss_ver) >> ss_ver; + const int has_chroma = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400 && + (bw4 > ss_hor || t->bx & 1) && + (bh4 > ss_ver || t->by & 1); + const TxfmInfo *const t_dim = &dav1d_txfm_dimensions[b->tx]; + const TxfmInfo *const uv_t_dim = &dav1d_txfm_dimensions[b->uvtx]; + + // coefficient coding + pixel *const edge = bitfn(t->scratch.edge) + 128; + const int cbw4 = (bw4 + ss_hor) >> ss_hor, cbh4 = (bh4 + ss_ver) >> ss_ver; + + const int intra_edge_filter_flag = f->seq_hdr->intra_edge_filter << 10; + + for (int init_y = 0; init_y < h4; init_y += 16) { + const int sub_h4 = imin(h4, 16 + init_y); + const int sub_ch4 = imin(ch4, (init_y + 16) >> ss_ver); + for (int init_x = 0; init_x < w4; init_x += 16) { + if (b->pal_sz[0]) { + pixel *dst = ((pixel *) f->cur.data[0]) + + 4 * (t->by * PXSTRIDE(f->cur.stride[0]) + t->bx); + const uint8_t *pal_idx; + if (t->frame_thread.pass) { + const int p = t->frame_thread.pass & 1; + assert(ts->frame_thread[p].pal_idx); + pal_idx = ts->frame_thread[p].pal_idx; + ts->frame_thread[p].pal_idx += bw4 * bh4 * 8; + } else { + pal_idx = t->scratch.pal_idx_y; + } + const pixel *const pal = t->frame_thread.pass ? + f->frame_thread.pal[((t->by >> 1) + (t->bx & 1)) * (f->b4_stride >> 1) + + ((t->bx >> 1) + (t->by & 1))][0] : + bytefn(t->scratch.pal)[0]; + f->dsp->ipred.pal_pred(dst, f->cur.stride[0], pal, + pal_idx, bw4 * 4, bh4 * 4); + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) + hex_dump(dst, PXSTRIDE(f->cur.stride[0]), + bw4 * 4, bh4 * 4, "y-pal-pred"); + } + + const int intra_flags = (sm_flag(t->a, bx4) | + sm_flag(&t->l, by4) | + intra_edge_filter_flag); + const int sb_has_tr = init_x + 16 < w4 ? 1 : init_y ? 0 : + intra_edge_flags & EDGE_I444_TOP_HAS_RIGHT; + const int sb_has_bl = init_x ? 0 : init_y + 16 < h4 ? 1 : + intra_edge_flags & EDGE_I444_LEFT_HAS_BOTTOM; + int y, x; + const int sub_w4 = imin(w4, init_x + 16); + for (y = init_y, t->by += init_y; y < sub_h4; + y += t_dim->h, t->by += t_dim->h) + { + pixel *dst = ((pixel *) f->cur.data[0]) + + 4 * (t->by * PXSTRIDE(f->cur.stride[0]) + + t->bx + init_x); + for (x = init_x, t->bx += init_x; x < sub_w4; + x += t_dim->w, t->bx += t_dim->w) + { + if (b->pal_sz[0]) goto skip_y_pred; + + int angle = b->y_angle; + const enum EdgeFlags edge_flags = + (((y > init_y || !sb_has_tr) && (x + t_dim->w >= sub_w4)) ? + 0 : EDGE_I444_TOP_HAS_RIGHT) | + ((x > init_x || (!sb_has_bl && y + t_dim->h >= sub_h4)) ? + 0 : EDGE_I444_LEFT_HAS_BOTTOM); + const pixel *top_sb_edge = NULL; + if (!(t->by & (f->sb_step - 1))) { + top_sb_edge = f->ipred_edge[0]; + const int sby = t->by >> f->sb_shift; + top_sb_edge += f->sb128w * 128 * (sby - 1); + } + const enum IntraPredMode m = + bytefn(dav1d_prepare_intra_edges)(t->bx, + t->bx > ts->tiling.col_start, + t->by, + t->by > ts->tiling.row_start, + ts->tiling.col_end, + ts->tiling.row_end, + edge_flags, dst, + f->cur.stride[0], top_sb_edge, + b->y_mode, &angle, + t_dim->w, t_dim->h, + f->seq_hdr->intra_edge_filter, + edge HIGHBD_CALL_SUFFIX); + dsp->ipred.intra_pred[m](dst, f->cur.stride[0], edge, + t_dim->w * 4, t_dim->h * 4, + angle | intra_flags, + 4 * f->bw - 4 * t->bx, + 4 * f->bh - 4 * t->by + HIGHBD_CALL_SUFFIX); + + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) { + hex_dump(edge - t_dim->h * 4, t_dim->h * 4, + t_dim->h * 4, 2, "l"); + hex_dump(edge, 0, 1, 1, "tl"); + hex_dump(edge + 1, t_dim->w * 4, + t_dim->w * 4, 2, "t"); + hex_dump(dst, f->cur.stride[0], + t_dim->w * 4, t_dim->h * 4, "y-intra-pred"); + } + + skip_y_pred: {} + if (!b->skip) { + coef *cf; + int eob; + enum TxfmType txtp; + if (t->frame_thread.pass) { + const int p = t->frame_thread.pass & 1; + const int cbi = *ts->frame_thread[p].cbi++; + cf = ts->frame_thread[p].cf; + ts->frame_thread[p].cf += imin(t_dim->w, 8) * imin(t_dim->h, 8) * 16; + eob = cbi >> 5; + txtp = cbi & 0x1f; + } else { + uint8_t cf_ctx; + cf = bitfn(t->cf); + eob = decode_coefs(t, &t->a->lcoef[bx4 + x], + &t->l.lcoef[by4 + y], b->tx, bs, + b, 1, 0, cf, &txtp, &cf_ctx); + if (DEBUG_BLOCK_INFO) + printf("Post-y-cf-blk[tx=%d,txtp=%d,eob=%d]: r=%d\n", + b->tx, txtp, eob, ts->msac.rng); + dav1d_memset_likely_pow2(&t->a->lcoef[bx4 + x], cf_ctx, imin(t_dim->w, f->bw - t->bx)); + dav1d_memset_likely_pow2(&t->l.lcoef[by4 + y], cf_ctx, imin(t_dim->h, f->bh - t->by)); + } + if (eob >= 0) { + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) + coef_dump(cf, imin(t_dim->h, 8) * 4, + imin(t_dim->w, 8) * 4, 3, "dq"); + dsp->itx.itxfm_add[b->tx] + [txtp](dst, + f->cur.stride[0], + cf, eob HIGHBD_CALL_SUFFIX); + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) + hex_dump(dst, f->cur.stride[0], + t_dim->w * 4, t_dim->h * 4, "recon"); + } + } else if (!t->frame_thread.pass) { + dav1d_memset_pow2[t_dim->lw](&t->a->lcoef[bx4 + x], 0x40); + dav1d_memset_pow2[t_dim->lh](&t->l.lcoef[by4 + y], 0x40); + } + dst += 4 * t_dim->w; + } + t->bx -= x; + } + t->by -= y; + + if (!has_chroma) continue; + + const ptrdiff_t stride = f->cur.stride[1]; + + if (b->uv_mode == CFL_PRED) { + assert(!init_x && !init_y); + + int16_t *const ac = t->scratch.ac; + pixel *y_src = ((pixel *) f->cur.data[0]) + 4 * (t->bx & ~ss_hor) + + 4 * (t->by & ~ss_ver) * PXSTRIDE(f->cur.stride[0]); + const ptrdiff_t uv_off = 4 * ((t->bx >> ss_hor) + + (t->by >> ss_ver) * PXSTRIDE(stride)); + pixel *const uv_dst[2] = { ((pixel *) f->cur.data[1]) + uv_off, + ((pixel *) f->cur.data[2]) + uv_off }; + + const int furthest_r = + ((cw4 << ss_hor) + t_dim->w - 1) & ~(t_dim->w - 1); + const int furthest_b = + ((ch4 << ss_ver) + t_dim->h - 1) & ~(t_dim->h - 1); + dsp->ipred.cfl_ac[f->cur.p.layout - 1](ac, y_src, f->cur.stride[0], + cbw4 - (furthest_r >> ss_hor), + cbh4 - (furthest_b >> ss_ver), + cbw4 * 4, cbh4 * 4); + for (int pl = 0; pl < 2; pl++) { + if (!b->cfl_alpha[pl]) continue; + int angle = 0; + const pixel *top_sb_edge = NULL; + if (!((t->by & ~ss_ver) & (f->sb_step - 1))) { + top_sb_edge = f->ipred_edge[pl + 1]; + const int sby = t->by >> f->sb_shift; + top_sb_edge += f->sb128w * 128 * (sby - 1); + } + const int xpos = t->bx >> ss_hor, ypos = t->by >> ss_ver; + const int xstart = ts->tiling.col_start >> ss_hor; + const int ystart = ts->tiling.row_start >> ss_ver; + const enum IntraPredMode m = + bytefn(dav1d_prepare_intra_edges)(xpos, xpos > xstart, + ypos, ypos > ystart, + ts->tiling.col_end >> ss_hor, + ts->tiling.row_end >> ss_ver, + 0, uv_dst[pl], stride, + top_sb_edge, DC_PRED, &angle, + uv_t_dim->w, uv_t_dim->h, 0, + edge HIGHBD_CALL_SUFFIX); + dsp->ipred.cfl_pred[m](uv_dst[pl], stride, edge, + uv_t_dim->w * 4, + uv_t_dim->h * 4, + ac, b->cfl_alpha[pl] + HIGHBD_CALL_SUFFIX); + } + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) { + ac_dump(ac, 4*cbw4, 4*cbh4, "ac"); + hex_dump(uv_dst[0], stride, cbw4 * 4, cbh4 * 4, "u-cfl-pred"); + hex_dump(uv_dst[1], stride, cbw4 * 4, cbh4 * 4, "v-cfl-pred"); + } + } else if (b->pal_sz[1]) { + const ptrdiff_t uv_dstoff = 4 * ((t->bx >> ss_hor) + + (t->by >> ss_ver) * PXSTRIDE(f->cur.stride[1])); + const pixel (*pal)[8]; + const uint8_t *pal_idx; + if (t->frame_thread.pass) { + const int p = t->frame_thread.pass & 1; + assert(ts->frame_thread[p].pal_idx); + pal = f->frame_thread.pal[((t->by >> 1) + (t->bx & 1)) * (f->b4_stride >> 1) + + ((t->bx >> 1) + (t->by & 1))]; + pal_idx = ts->frame_thread[p].pal_idx; + ts->frame_thread[p].pal_idx += cbw4 * cbh4 * 8; + } else { + pal = bytefn(t->scratch.pal); + pal_idx = t->scratch.pal_idx_uv; + } + + f->dsp->ipred.pal_pred(((pixel *) f->cur.data[1]) + uv_dstoff, + f->cur.stride[1], pal[1], + pal_idx, cbw4 * 4, cbh4 * 4); + f->dsp->ipred.pal_pred(((pixel *) f->cur.data[2]) + uv_dstoff, + f->cur.stride[1], pal[2], + pal_idx, cbw4 * 4, cbh4 * 4); + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) { + hex_dump(((pixel *) f->cur.data[1]) + uv_dstoff, + PXSTRIDE(f->cur.stride[1]), + cbw4 * 4, cbh4 * 4, "u-pal-pred"); + hex_dump(((pixel *) f->cur.data[2]) + uv_dstoff, + PXSTRIDE(f->cur.stride[1]), + cbw4 * 4, cbh4 * 4, "v-pal-pred"); + } + } + + const int sm_uv_fl = sm_uv_flag(t->a, cbx4) | + sm_uv_flag(&t->l, cby4); + const int uv_sb_has_tr = + ((init_x + 16) >> ss_hor) < cw4 ? 1 : init_y ? 0 : + intra_edge_flags & (EDGE_I420_TOP_HAS_RIGHT >> (f->cur.p.layout - 1)); + const int uv_sb_has_bl = + init_x ? 0 : ((init_y + 16) >> ss_ver) < ch4 ? 1 : + intra_edge_flags & (EDGE_I420_LEFT_HAS_BOTTOM >> (f->cur.p.layout - 1)); + const int sub_cw4 = imin(cw4, (init_x + 16) >> ss_hor); + for (int pl = 0; pl < 2; pl++) { + for (y = init_y >> ss_ver, t->by += init_y; y < sub_ch4; + y += uv_t_dim->h, t->by += uv_t_dim->h << ss_ver) + { + pixel *dst = ((pixel *) f->cur.data[1 + pl]) + + 4 * ((t->by >> ss_ver) * PXSTRIDE(stride) + + ((t->bx + init_x) >> ss_hor)); + for (x = init_x >> ss_hor, t->bx += init_x; x < sub_cw4; + x += uv_t_dim->w, t->bx += uv_t_dim->w << ss_hor) + { + if ((b->uv_mode == CFL_PRED && b->cfl_alpha[pl]) || + b->pal_sz[1]) + { + goto skip_uv_pred; + } + + int angle = b->uv_angle; + // this probably looks weird because we're using + // luma flags in a chroma loop, but that's because + // prepare_intra_edges() expects luma flags as input + const enum EdgeFlags edge_flags = + (((y > (init_y >> ss_ver) || !uv_sb_has_tr) && + (x + uv_t_dim->w >= sub_cw4)) ? + 0 : EDGE_I444_TOP_HAS_RIGHT) | + ((x > (init_x >> ss_hor) || + (!uv_sb_has_bl && y + uv_t_dim->h >= sub_ch4)) ? + 0 : EDGE_I444_LEFT_HAS_BOTTOM); + const pixel *top_sb_edge = NULL; + if (!((t->by & ~ss_ver) & (f->sb_step - 1))) { + top_sb_edge = f->ipred_edge[1 + pl]; + const int sby = t->by >> f->sb_shift; + top_sb_edge += f->sb128w * 128 * (sby - 1); + } + const enum IntraPredMode uv_mode = + b->uv_mode == CFL_PRED ? DC_PRED : b->uv_mode; + const int xpos = t->bx >> ss_hor, ypos = t->by >> ss_ver; + const int xstart = ts->tiling.col_start >> ss_hor; + const int ystart = ts->tiling.row_start >> ss_ver; + const enum IntraPredMode m = + bytefn(dav1d_prepare_intra_edges)(xpos, xpos > xstart, + ypos, ypos > ystart, + ts->tiling.col_end >> ss_hor, + ts->tiling.row_end >> ss_ver, + edge_flags, dst, stride, + top_sb_edge, uv_mode, + &angle, uv_t_dim->w, + uv_t_dim->h, + f->seq_hdr->intra_edge_filter, + edge HIGHBD_CALL_SUFFIX); + angle |= intra_edge_filter_flag; + dsp->ipred.intra_pred[m](dst, stride, edge, + uv_t_dim->w * 4, + uv_t_dim->h * 4, + angle | sm_uv_fl, + (4 * f->bw + ss_hor - + 4 * (t->bx & ~ss_hor)) >> ss_hor, + (4 * f->bh + ss_ver - + 4 * (t->by & ~ss_ver)) >> ss_ver + HIGHBD_CALL_SUFFIX); + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) { + hex_dump(edge - uv_t_dim->h * 4, uv_t_dim->h * 4, + uv_t_dim->h * 4, 2, "l"); + hex_dump(edge, 0, 1, 1, "tl"); + hex_dump(edge + 1, uv_t_dim->w * 4, + uv_t_dim->w * 4, 2, "t"); + hex_dump(dst, stride, uv_t_dim->w * 4, + uv_t_dim->h * 4, pl ? "v-intra-pred" : "u-intra-pred"); + } + + skip_uv_pred: {} + if (!b->skip) { + enum TxfmType txtp; + int eob; + coef *cf; + if (t->frame_thread.pass) { + const int p = t->frame_thread.pass & 1; + const int cbi = *ts->frame_thread[p].cbi++; + cf = ts->frame_thread[p].cf; + ts->frame_thread[p].cf += uv_t_dim->w * uv_t_dim->h * 16; + eob = cbi >> 5; + txtp = cbi & 0x1f; + } else { + uint8_t cf_ctx; + cf = bitfn(t->cf); + eob = decode_coefs(t, &t->a->ccoef[pl][cbx4 + x], + &t->l.ccoef[pl][cby4 + y], + b->uvtx, bs, b, 1, 1 + pl, cf, + &txtp, &cf_ctx); + if (DEBUG_BLOCK_INFO) + printf("Post-uv-cf-blk[pl=%d,tx=%d," + "txtp=%d,eob=%d]: r=%d [x=%d,cbx4=%d]\n", + pl, b->uvtx, txtp, eob, ts->msac.rng, x, cbx4); + int ctw = imin(uv_t_dim->w, (f->bw - t->bx + ss_hor) >> ss_hor); + int cth = imin(uv_t_dim->h, (f->bh - t->by + ss_ver) >> ss_ver); + dav1d_memset_likely_pow2(&t->a->ccoef[pl][cbx4 + x], cf_ctx, ctw); + dav1d_memset_likely_pow2(&t->l.ccoef[pl][cby4 + y], cf_ctx, cth); + } + if (eob >= 0) { + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) + coef_dump(cf, uv_t_dim->h * 4, + uv_t_dim->w * 4, 3, "dq"); + dsp->itx.itxfm_add[b->uvtx] + [txtp](dst, stride, + cf, eob HIGHBD_CALL_SUFFIX); + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) + hex_dump(dst, stride, uv_t_dim->w * 4, + uv_t_dim->h * 4, "recon"); + } + } else if (!t->frame_thread.pass) { + dav1d_memset_pow2[uv_t_dim->lw](&t->a->ccoef[pl][cbx4 + x], 0x40); + dav1d_memset_pow2[uv_t_dim->lh](&t->l.ccoef[pl][cby4 + y], 0x40); + } + dst += uv_t_dim->w * 4; + } + t->bx -= x << ss_hor; + } + t->by -= y << ss_ver; + } + } + } +} + +int bytefn(dav1d_recon_b_inter)(Dav1dTaskContext *const t, const enum BlockSize bs, + const Av1Block *const b) +{ + Dav1dTileState *const ts = t->ts; + const Dav1dFrameContext *const f = t->f; + const Dav1dDSPContext *const dsp = f->dsp; + const int bx4 = t->bx & 31, by4 = t->by & 31; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int cbx4 = bx4 >> ss_hor, cby4 = by4 >> ss_ver; + const uint8_t *const b_dim = dav1d_block_dimensions[bs]; + const int bw4 = b_dim[0], bh4 = b_dim[1]; + const int w4 = imin(bw4, f->bw - t->bx), h4 = imin(bh4, f->bh - t->by); + const int has_chroma = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400 && + (bw4 > ss_hor || t->bx & 1) && + (bh4 > ss_ver || t->by & 1); + const int chr_layout_idx = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I400 ? 0 : + DAV1D_PIXEL_LAYOUT_I444 - f->cur.p.layout; + int res; + + // prediction + const int cbh4 = (bh4 + ss_ver) >> ss_ver, cbw4 = (bw4 + ss_hor) >> ss_hor; + pixel *dst = ((pixel *) f->cur.data[0]) + + 4 * (t->by * PXSTRIDE(f->cur.stride[0]) + t->bx); + const ptrdiff_t uvdstoff = + 4 * ((t->bx >> ss_hor) + (t->by >> ss_ver) * PXSTRIDE(f->cur.stride[1])); + if (IS_KEY_OR_INTRA(f->frame_hdr)) { + // intrabc + assert(!f->frame_hdr->super_res.enabled); + res = mc(t, dst, NULL, f->cur.stride[0], bw4, bh4, t->bx, t->by, 0, + b->mv[0], &f->sr_cur, 0 /* unused */, FILTER_2D_BILINEAR); + if (res) return res; + if (has_chroma) for (int pl = 1; pl < 3; pl++) { + res = mc(t, ((pixel *)f->cur.data[pl]) + uvdstoff, NULL, f->cur.stride[1], + bw4 << (bw4 == ss_hor), bh4 << (bh4 == ss_ver), + t->bx & ~ss_hor, t->by & ~ss_ver, pl, b->mv[0], + &f->sr_cur, 0 /* unused */, FILTER_2D_BILINEAR); + if (res) return res; + } + } else if (b->comp_type == COMP_INTER_NONE) { + const Dav1dThreadPicture *const refp = &f->refp[b->ref[0]]; + const enum Filter2d filter_2d = b->filter2d; + + if (imin(bw4, bh4) > 1 && + ((b->inter_mode == GLOBALMV && f->gmv_warp_allowed[b->ref[0]]) || + (b->motion_mode == MM_WARP && t->warpmv.type > DAV1D_WM_TYPE_TRANSLATION))) + { + res = warp_affine(t, dst, NULL, f->cur.stride[0], b_dim, 0, refp, + b->motion_mode == MM_WARP ? &t->warpmv : + &f->frame_hdr->gmv[b->ref[0]]); + if (res) return res; + } else { + res = mc(t, dst, NULL, f->cur.stride[0], + bw4, bh4, t->bx, t->by, 0, b->mv[0], refp, b->ref[0], filter_2d); + if (res) return res; + if (b->motion_mode == MM_OBMC) { + res = obmc(t, dst, f->cur.stride[0], b_dim, 0, bx4, by4, w4, h4); + if (res) return res; + } + } + if (b->interintra_type) { + pixel *const tl_edge = bitfn(t->scratch.edge) + 32; + enum IntraPredMode m = b->interintra_mode == II_SMOOTH_PRED ? + SMOOTH_PRED : b->interintra_mode; + pixel *const tmp = bitfn(t->scratch.interintra); + int angle = 0; + const pixel *top_sb_edge = NULL; + if (!(t->by & (f->sb_step - 1))) { + top_sb_edge = f->ipred_edge[0]; + const int sby = t->by >> f->sb_shift; + top_sb_edge += f->sb128w * 128 * (sby - 1); + } + m = bytefn(dav1d_prepare_intra_edges)(t->bx, t->bx > ts->tiling.col_start, + t->by, t->by > ts->tiling.row_start, + ts->tiling.col_end, ts->tiling.row_end, + 0, dst, f->cur.stride[0], top_sb_edge, + m, &angle, bw4, bh4, 0, tl_edge + HIGHBD_CALL_SUFFIX); + dsp->ipred.intra_pred[m](tmp, 4 * bw4 * sizeof(pixel), + tl_edge, bw4 * 4, bh4 * 4, 0, 0, 0 + HIGHBD_CALL_SUFFIX); + dsp->mc.blend(dst, f->cur.stride[0], tmp, + bw4 * 4, bh4 * 4, II_MASK(0, bs, b)); + } + + if (!has_chroma) goto skip_inter_chroma_pred; + + // sub8x8 derivation + int is_sub8x8 = bw4 == ss_hor || bh4 == ss_ver; + refmvs_block *const *r; + if (is_sub8x8) { + assert(ss_hor == 1); + r = &t->rt.r[(t->by & 31) + 5]; + if (bw4 == 1) is_sub8x8 &= r[0][t->bx - 1].ref.ref[0] > 0; + if (bh4 == ss_ver) is_sub8x8 &= r[-1][t->bx].ref.ref[0] > 0; + if (bw4 == 1 && bh4 == ss_ver) + is_sub8x8 &= r[-1][t->bx - 1].ref.ref[0] > 0; + } + + // chroma prediction + if (is_sub8x8) { + assert(ss_hor == 1); + ptrdiff_t h_off = 0, v_off = 0; + if (bw4 == 1 && bh4 == ss_ver) { + for (int pl = 0; pl < 2; pl++) { + res = mc(t, ((pixel *) f->cur.data[1 + pl]) + uvdstoff, + NULL, f->cur.stride[1], + bw4, bh4, t->bx - 1, t->by - 1, 1 + pl, + r[-1][t->bx - 1].mv.mv[0], + &f->refp[r[-1][t->bx - 1].ref.ref[0] - 1], + r[-1][t->bx - 1].ref.ref[0] - 1, + t->frame_thread.pass != 2 ? t->tl_4x4_filter : + f->frame_thread.b[((t->by - 1) * f->b4_stride) + t->bx - 1].filter2d); + if (res) return res; + } + v_off = 2 * PXSTRIDE(f->cur.stride[1]); + h_off = 2; + } + if (bw4 == 1) { + const enum Filter2d left_filter_2d = + dav1d_filter_2d[t->l.filter[1][by4]][t->l.filter[0][by4]]; + for (int pl = 0; pl < 2; pl++) { + res = mc(t, ((pixel *) f->cur.data[1 + pl]) + uvdstoff + v_off, NULL, + f->cur.stride[1], bw4, bh4, t->bx - 1, + t->by, 1 + pl, r[0][t->bx - 1].mv.mv[0], + &f->refp[r[0][t->bx - 1].ref.ref[0] - 1], + r[0][t->bx - 1].ref.ref[0] - 1, + t->frame_thread.pass != 2 ? left_filter_2d : + f->frame_thread.b[(t->by * f->b4_stride) + t->bx - 1].filter2d); + if (res) return res; + } + h_off = 2; + } + if (bh4 == ss_ver) { + const enum Filter2d top_filter_2d = + dav1d_filter_2d[t->a->filter[1][bx4]][t->a->filter[0][bx4]]; + for (int pl = 0; pl < 2; pl++) { + res = mc(t, ((pixel *) f->cur.data[1 + pl]) + uvdstoff + h_off, NULL, + f->cur.stride[1], bw4, bh4, t->bx, t->by - 1, + 1 + pl, r[-1][t->bx].mv.mv[0], + &f->refp[r[-1][t->bx].ref.ref[0] - 1], + r[-1][t->bx].ref.ref[0] - 1, + t->frame_thread.pass != 2 ? top_filter_2d : + f->frame_thread.b[((t->by - 1) * f->b4_stride) + t->bx].filter2d); + if (res) return res; + } + v_off = 2 * PXSTRIDE(f->cur.stride[1]); + } + for (int pl = 0; pl < 2; pl++) { + res = mc(t, ((pixel *) f->cur.data[1 + pl]) + uvdstoff + h_off + v_off, NULL, f->cur.stride[1], + bw4, bh4, t->bx, t->by, 1 + pl, b->mv[0], + refp, b->ref[0], filter_2d); + if (res) return res; + } + } else { + if (imin(cbw4, cbh4) > 1 && + ((b->inter_mode == GLOBALMV && f->gmv_warp_allowed[b->ref[0]]) || + (b->motion_mode == MM_WARP && t->warpmv.type > DAV1D_WM_TYPE_TRANSLATION))) + { + for (int pl = 0; pl < 2; pl++) { + res = warp_affine(t, ((pixel *) f->cur.data[1 + pl]) + uvdstoff, NULL, + f->cur.stride[1], b_dim, 1 + pl, refp, + b->motion_mode == MM_WARP ? &t->warpmv : + &f->frame_hdr->gmv[b->ref[0]]); + if (res) return res; + } + } else { + for (int pl = 0; pl < 2; pl++) { + res = mc(t, ((pixel *) f->cur.data[1 + pl]) + uvdstoff, + NULL, f->cur.stride[1], + bw4 << (bw4 == ss_hor), bh4 << (bh4 == ss_ver), + t->bx & ~ss_hor, t->by & ~ss_ver, + 1 + pl, b->mv[0], refp, b->ref[0], filter_2d); + if (res) return res; + if (b->motion_mode == MM_OBMC) { + res = obmc(t, ((pixel *) f->cur.data[1 + pl]) + uvdstoff, + f->cur.stride[1], b_dim, 1 + pl, bx4, by4, w4, h4); + if (res) return res; + } + } + } + if (b->interintra_type) { + // FIXME for 8x32 with 4:2:2 subsampling, this probably does + // the wrong thing since it will select 4x16, not 4x32, as a + // transform size... + const uint8_t *const ii_mask = II_MASK(chr_layout_idx, bs, b); + + for (int pl = 0; pl < 2; pl++) { + pixel *const tmp = bitfn(t->scratch.interintra); + pixel *const tl_edge = bitfn(t->scratch.edge) + 32; + enum IntraPredMode m = + b->interintra_mode == II_SMOOTH_PRED ? + SMOOTH_PRED : b->interintra_mode; + int angle = 0; + pixel *const uvdst = ((pixel *) f->cur.data[1 + pl]) + uvdstoff; + const pixel *top_sb_edge = NULL; + if (!(t->by & (f->sb_step - 1))) { + top_sb_edge = f->ipred_edge[pl + 1]; + const int sby = t->by >> f->sb_shift; + top_sb_edge += f->sb128w * 128 * (sby - 1); + } + m = bytefn(dav1d_prepare_intra_edges)(t->bx >> ss_hor, + (t->bx >> ss_hor) > + (ts->tiling.col_start >> ss_hor), + t->by >> ss_ver, + (t->by >> ss_ver) > + (ts->tiling.row_start >> ss_ver), + ts->tiling.col_end >> ss_hor, + ts->tiling.row_end >> ss_ver, + 0, uvdst, f->cur.stride[1], + top_sb_edge, m, + &angle, cbw4, cbh4, 0, tl_edge + HIGHBD_CALL_SUFFIX); + dsp->ipred.intra_pred[m](tmp, cbw4 * 4 * sizeof(pixel), + tl_edge, cbw4 * 4, cbh4 * 4, 0, 0, 0 + HIGHBD_CALL_SUFFIX); + dsp->mc.blend(uvdst, f->cur.stride[1], tmp, + cbw4 * 4, cbh4 * 4, ii_mask); + } + } + } + + skip_inter_chroma_pred: {} + t->tl_4x4_filter = filter_2d; + } else { + const enum Filter2d filter_2d = b->filter2d; + // Maximum super block size is 128x128 + int16_t (*tmp)[128 * 128] = t->scratch.compinter; + int jnt_weight; + uint8_t *const seg_mask = t->scratch.seg_mask; + const uint8_t *mask; + + for (int i = 0; i < 2; i++) { + const Dav1dThreadPicture *const refp = &f->refp[b->ref[i]]; + + if (b->inter_mode == GLOBALMV_GLOBALMV && f->gmv_warp_allowed[b->ref[i]]) { + res = warp_affine(t, NULL, tmp[i], bw4 * 4, b_dim, 0, refp, + &f->frame_hdr->gmv[b->ref[i]]); + if (res) return res; + } else { + res = mc(t, NULL, tmp[i], 0, bw4, bh4, t->bx, t->by, 0, + b->mv[i], refp, b->ref[i], filter_2d); + if (res) return res; + } + } + switch (b->comp_type) { + case COMP_INTER_AVG: + dsp->mc.avg(dst, f->cur.stride[0], tmp[0], tmp[1], + bw4 * 4, bh4 * 4 HIGHBD_CALL_SUFFIX); + break; + case COMP_INTER_WEIGHTED_AVG: + jnt_weight = f->jnt_weights[b->ref[0]][b->ref[1]]; + dsp->mc.w_avg(dst, f->cur.stride[0], tmp[0], tmp[1], + bw4 * 4, bh4 * 4, jnt_weight HIGHBD_CALL_SUFFIX); + break; + case COMP_INTER_SEG: + dsp->mc.w_mask[chr_layout_idx](dst, f->cur.stride[0], + tmp[b->mask_sign], tmp[!b->mask_sign], + bw4 * 4, bh4 * 4, seg_mask, + b->mask_sign HIGHBD_CALL_SUFFIX); + mask = seg_mask; + break; + case COMP_INTER_WEDGE: + mask = WEDGE_MASK(0, bs, 0, b->wedge_idx); + dsp->mc.mask(dst, f->cur.stride[0], + tmp[b->mask_sign], tmp[!b->mask_sign], + bw4 * 4, bh4 * 4, mask HIGHBD_CALL_SUFFIX); + if (has_chroma) + mask = WEDGE_MASK(chr_layout_idx, bs, b->mask_sign, b->wedge_idx); + break; + } + + // chroma + if (has_chroma) for (int pl = 0; pl < 2; pl++) { + for (int i = 0; i < 2; i++) { + const Dav1dThreadPicture *const refp = &f->refp[b->ref[i]]; + if (b->inter_mode == GLOBALMV_GLOBALMV && + imin(cbw4, cbh4) > 1 && f->gmv_warp_allowed[b->ref[i]]) + { + res = warp_affine(t, NULL, tmp[i], bw4 * 4 >> ss_hor, + b_dim, 1 + pl, + refp, &f->frame_hdr->gmv[b->ref[i]]); + if (res) return res; + } else { + res = mc(t, NULL, tmp[i], 0, bw4, bh4, t->bx, t->by, + 1 + pl, b->mv[i], refp, b->ref[i], filter_2d); + if (res) return res; + } + } + pixel *const uvdst = ((pixel *) f->cur.data[1 + pl]) + uvdstoff; + switch (b->comp_type) { + case COMP_INTER_AVG: + dsp->mc.avg(uvdst, f->cur.stride[1], tmp[0], tmp[1], + bw4 * 4 >> ss_hor, bh4 * 4 >> ss_ver + HIGHBD_CALL_SUFFIX); + break; + case COMP_INTER_WEIGHTED_AVG: + dsp->mc.w_avg(uvdst, f->cur.stride[1], tmp[0], tmp[1], + bw4 * 4 >> ss_hor, bh4 * 4 >> ss_ver, jnt_weight + HIGHBD_CALL_SUFFIX); + break; + case COMP_INTER_WEDGE: + case COMP_INTER_SEG: + dsp->mc.mask(uvdst, f->cur.stride[1], + tmp[b->mask_sign], tmp[!b->mask_sign], + bw4 * 4 >> ss_hor, bh4 * 4 >> ss_ver, mask + HIGHBD_CALL_SUFFIX); + break; + } + } + } + + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) { + hex_dump(dst, f->cur.stride[0], b_dim[0] * 4, b_dim[1] * 4, "y-pred"); + if (has_chroma) { + hex_dump(&((pixel *) f->cur.data[1])[uvdstoff], f->cur.stride[1], + cbw4 * 4, cbh4 * 4, "u-pred"); + hex_dump(&((pixel *) f->cur.data[2])[uvdstoff], f->cur.stride[1], + cbw4 * 4, cbh4 * 4, "v-pred"); + } + } + + const int cw4 = (w4 + ss_hor) >> ss_hor, ch4 = (h4 + ss_ver) >> ss_ver; + + if (b->skip) { + // reset coef contexts + BlockContext *const a = t->a; + dav1d_memset_pow2[b_dim[2]](&a->lcoef[bx4], 0x40); + dav1d_memset_pow2[b_dim[3]](&t->l.lcoef[by4], 0x40); + if (has_chroma) { + dav1d_memset_pow2_fn memset_cw = dav1d_memset_pow2[ulog2(cbw4)]; + dav1d_memset_pow2_fn memset_ch = dav1d_memset_pow2[ulog2(cbh4)]; + memset_cw(&a->ccoef[0][cbx4], 0x40); + memset_cw(&a->ccoef[1][cbx4], 0x40); + memset_ch(&t->l.ccoef[0][cby4], 0x40); + memset_ch(&t->l.ccoef[1][cby4], 0x40); + } + return 0; + } + + const TxfmInfo *const uvtx = &dav1d_txfm_dimensions[b->uvtx]; + const TxfmInfo *const ytx = &dav1d_txfm_dimensions[b->max_ytx]; + const uint16_t tx_split[2] = { b->tx_split0, b->tx_split1 }; + + for (int init_y = 0; init_y < bh4; init_y += 16) { + for (int init_x = 0; init_x < bw4; init_x += 16) { + // coefficient coding & inverse transforms + int y_off = !!init_y, y; + dst += PXSTRIDE(f->cur.stride[0]) * 4 * init_y; + for (y = init_y, t->by += init_y; y < imin(h4, init_y + 16); + y += ytx->h, y_off++) + { + int x, x_off = !!init_x; + for (x = init_x, t->bx += init_x; x < imin(w4, init_x + 16); + x += ytx->w, x_off++) + { + read_coef_tree(t, bs, b, b->max_ytx, 0, tx_split, + x_off, y_off, &dst[x * 4]); + t->bx += ytx->w; + } + dst += PXSTRIDE(f->cur.stride[0]) * 4 * ytx->h; + t->bx -= x; + t->by += ytx->h; + } + dst -= PXSTRIDE(f->cur.stride[0]) * 4 * y; + t->by -= y; + + // chroma coefs and inverse transform + if (has_chroma) for (int pl = 0; pl < 2; pl++) { + pixel *uvdst = ((pixel *) f->cur.data[1 + pl]) + uvdstoff + + (PXSTRIDE(f->cur.stride[1]) * init_y * 4 >> ss_ver); + for (y = init_y >> ss_ver, t->by += init_y; + y < imin(ch4, (init_y + 16) >> ss_ver); y += uvtx->h) + { + int x; + for (x = init_x >> ss_hor, t->bx += init_x; + x < imin(cw4, (init_x + 16) >> ss_hor); x += uvtx->w) + { + coef *cf; + int eob; + enum TxfmType txtp; + if (t->frame_thread.pass) { + const int p = t->frame_thread.pass & 1; + const int cbi = *ts->frame_thread[p].cbi++; + cf = ts->frame_thread[p].cf; + ts->frame_thread[p].cf += uvtx->w * uvtx->h * 16; + eob = cbi >> 5; + txtp = cbi & 0x1f; + } else { + uint8_t cf_ctx; + cf = bitfn(t->cf); + txtp = t->scratch.txtp_map[(by4 + (y << ss_ver)) * 32 + + bx4 + (x << ss_hor)]; + eob = decode_coefs(t, &t->a->ccoef[pl][cbx4 + x], + &t->l.ccoef[pl][cby4 + y], + b->uvtx, bs, b, 0, 1 + pl, + cf, &txtp, &cf_ctx); + if (DEBUG_BLOCK_INFO) + printf("Post-uv-cf-blk[pl=%d,tx=%d," + "txtp=%d,eob=%d]: r=%d\n", + pl, b->uvtx, txtp, eob, ts->msac.rng); + int ctw = imin(uvtx->w, (f->bw - t->bx + ss_hor) >> ss_hor); + int cth = imin(uvtx->h, (f->bh - t->by + ss_ver) >> ss_ver); + dav1d_memset_likely_pow2(&t->a->ccoef[pl][cbx4 + x], cf_ctx, ctw); + dav1d_memset_likely_pow2(&t->l.ccoef[pl][cby4 + y], cf_ctx, cth); + } + if (eob >= 0) { + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) + coef_dump(cf, uvtx->h * 4, uvtx->w * 4, 3, "dq"); + dsp->itx.itxfm_add[b->uvtx] + [txtp](&uvdst[4 * x], + f->cur.stride[1], + cf, eob HIGHBD_CALL_SUFFIX); + if (DEBUG_BLOCK_INFO && DEBUG_B_PIXELS) + hex_dump(&uvdst[4 * x], f->cur.stride[1], + uvtx->w * 4, uvtx->h * 4, "recon"); + } + t->bx += uvtx->w << ss_hor; + } + uvdst += PXSTRIDE(f->cur.stride[1]) * 4 * uvtx->h; + t->bx -= x << ss_hor; + t->by += uvtx->h << ss_ver; + } + t->by -= y << ss_ver; + } + } + } + return 0; +} + +void bytefn(dav1d_filter_sbrow_deblock_cols)(Dav1dFrameContext *const f, const int sby) { + if (!(f->c->inloop_filters & DAV1D_INLOOPFILTER_DEBLOCK) || + (!f->frame_hdr->loopfilter.level_y[0] && !f->frame_hdr->loopfilter.level_y[1])) + { + return; + } + const int y = sby * f->sb_step * 4; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + pixel *const p[3] = { + f->lf.p[0] + y * PXSTRIDE(f->cur.stride[0]), + f->lf.p[1] + (y * PXSTRIDE(f->cur.stride[1]) >> ss_ver), + f->lf.p[2] + (y * PXSTRIDE(f->cur.stride[1]) >> ss_ver) + }; + Av1Filter *mask = f->lf.mask + (sby >> !f->seq_hdr->sb128) * f->sb128w; + bytefn(dav1d_loopfilter_sbrow_cols)(f, p, mask, sby, + f->lf.start_of_tile_row[sby]); +} + +void bytefn(dav1d_filter_sbrow_deblock_rows)(Dav1dFrameContext *const f, const int sby) { + const int y = sby * f->sb_step * 4; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + pixel *const p[3] = { + f->lf.p[0] + y * PXSTRIDE(f->cur.stride[0]), + f->lf.p[1] + (y * PXSTRIDE(f->cur.stride[1]) >> ss_ver), + f->lf.p[2] + (y * PXSTRIDE(f->cur.stride[1]) >> ss_ver) + }; + Av1Filter *mask = f->lf.mask + (sby >> !f->seq_hdr->sb128) * f->sb128w; + if (f->c->inloop_filters & DAV1D_INLOOPFILTER_DEBLOCK && + (f->frame_hdr->loopfilter.level_y[0] || f->frame_hdr->loopfilter.level_y[1])) + { + bytefn(dav1d_loopfilter_sbrow_rows)(f, p, mask, sby); + } + if (f->seq_hdr->cdef || f->lf.restore_planes) { + // Store loop filtered pixels required by CDEF / LR + bytefn(dav1d_copy_lpf)(f, p, sby); + } +} + +void bytefn(dav1d_filter_sbrow_cdef)(Dav1dTaskContext *const tc, const int sby) { + const Dav1dFrameContext *const f = tc->f; + if (!(f->c->inloop_filters & DAV1D_INLOOPFILTER_CDEF)) return; + const int sbsz = f->sb_step; + const int y = sby * sbsz * 4; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + pixel *const p[3] = { + f->lf.p[0] + y * PXSTRIDE(f->cur.stride[0]), + f->lf.p[1] + (y * PXSTRIDE(f->cur.stride[1]) >> ss_ver), + f->lf.p[2] + (y * PXSTRIDE(f->cur.stride[1]) >> ss_ver) + }; + Av1Filter *prev_mask = f->lf.mask + ((sby - 1) >> !f->seq_hdr->sb128) * f->sb128w; + Av1Filter *mask = f->lf.mask + (sby >> !f->seq_hdr->sb128) * f->sb128w; + const int start = sby * sbsz; + if (sby) { + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + pixel *p_up[3] = { + p[0] - 8 * PXSTRIDE(f->cur.stride[0]), + p[1] - (8 * PXSTRIDE(f->cur.stride[1]) >> ss_ver), + p[2] - (8 * PXSTRIDE(f->cur.stride[1]) >> ss_ver), + }; + bytefn(dav1d_cdef_brow)(tc, p_up, prev_mask, start - 2, start, 1, sby); + } + const int n_blks = sbsz - 2 * (sby + 1 < f->sbh); + const int end = imin(start + n_blks, f->bh); + bytefn(dav1d_cdef_brow)(tc, p, mask, start, end, 0, sby); +} + +void bytefn(dav1d_filter_sbrow_resize)(Dav1dFrameContext *const f, const int sby) { + const int sbsz = f->sb_step; + const int y = sby * sbsz * 4; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const pixel *const p[3] = { + f->lf.p[0] + y * PXSTRIDE(f->cur.stride[0]), + f->lf.p[1] + (y * PXSTRIDE(f->cur.stride[1]) >> ss_ver), + f->lf.p[2] + (y * PXSTRIDE(f->cur.stride[1]) >> ss_ver) + }; + pixel *const sr_p[3] = { + f->lf.sr_p[0] + y * PXSTRIDE(f->sr_cur.p.stride[0]), + f->lf.sr_p[1] + (y * PXSTRIDE(f->sr_cur.p.stride[1]) >> ss_ver), + f->lf.sr_p[2] + (y * PXSTRIDE(f->sr_cur.p.stride[1]) >> ss_ver) + }; + const int has_chroma = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400; + for (int pl = 0; pl < 1 + 2 * has_chroma; pl++) { + const int ss_ver = pl && f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int h_start = 8 * !!sby >> ss_ver; + const ptrdiff_t dst_stride = f->sr_cur.p.stride[!!pl]; + pixel *dst = sr_p[pl] - h_start * PXSTRIDE(dst_stride); + const ptrdiff_t src_stride = f->cur.stride[!!pl]; + const pixel *src = p[pl] - h_start * PXSTRIDE(src_stride); + const int h_end = 4 * (sbsz - 2 * (sby + 1 < f->sbh)) >> ss_ver; + const int ss_hor = pl && f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + const int dst_w = (f->sr_cur.p.p.w + ss_hor) >> ss_hor; + const int src_w = (4 * f->bw + ss_hor) >> ss_hor; + const int img_h = (f->cur.p.h - sbsz * 4 * sby + ss_ver) >> ss_ver; + + f->dsp->mc.resize(dst, dst_stride, src, src_stride, dst_w, + imin(img_h, h_end) + h_start, src_w, + f->resize_step[!!pl], f->resize_start[!!pl] + HIGHBD_CALL_SUFFIX); + } +} + +void bytefn(dav1d_filter_sbrow_lr)(Dav1dFrameContext *const f, const int sby) { + if (!(f->c->inloop_filters & DAV1D_INLOOPFILTER_RESTORATION)) return; + const int y = sby * f->sb_step * 4; + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + pixel *const sr_p[3] = { + f->lf.sr_p[0] + y * PXSTRIDE(f->sr_cur.p.stride[0]), + f->lf.sr_p[1] + (y * PXSTRIDE(f->sr_cur.p.stride[1]) >> ss_ver), + f->lf.sr_p[2] + (y * PXSTRIDE(f->sr_cur.p.stride[1]) >> ss_ver) + }; + bytefn(dav1d_lr_sbrow)(f, sr_p, sby); +} + +void bytefn(dav1d_filter_sbrow)(Dav1dFrameContext *const f, const int sby) { + bytefn(dav1d_filter_sbrow_deblock_cols)(f, sby); + bytefn(dav1d_filter_sbrow_deblock_rows)(f, sby); + if (f->seq_hdr->cdef) + bytefn(dav1d_filter_sbrow_cdef)(f->c->tc, sby); + if (f->frame_hdr->width[0] != f->frame_hdr->width[1]) + bytefn(dav1d_filter_sbrow_resize)(f, sby); + if (f->lf.restore_planes) + bytefn(dav1d_filter_sbrow_lr)(f, sby); +} + +void bytefn(dav1d_backup_ipred_edge)(Dav1dTaskContext *const t) { + const Dav1dFrameContext *const f = t->f; + Dav1dTileState *const ts = t->ts; + const int sby = t->by >> f->sb_shift; + const int sby_off = f->sb128w * 128 * sby; + const int x_off = ts->tiling.col_start; + + const pixel *const y = + ((const pixel *) f->cur.data[0]) + x_off * 4 + + ((t->by + f->sb_step) * 4 - 1) * PXSTRIDE(f->cur.stride[0]); + pixel_copy(&f->ipred_edge[0][sby_off + x_off * 4], y, + 4 * (ts->tiling.col_end - x_off)); + + if (f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I400) { + const int ss_ver = f->cur.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const int ss_hor = f->cur.p.layout != DAV1D_PIXEL_LAYOUT_I444; + + const ptrdiff_t uv_off = (x_off * 4 >> ss_hor) + + (((t->by + f->sb_step) * 4 >> ss_ver) - 1) * PXSTRIDE(f->cur.stride[1]); + for (int pl = 1; pl <= 2; pl++) + pixel_copy(&f->ipred_edge[pl][sby_off + (x_off * 4 >> ss_hor)], + &((const pixel *) f->cur.data[pl])[uv_off], + 4 * (ts->tiling.col_end - x_off) >> ss_hor); + } +} + +void bytefn(dav1d_copy_pal_block_y)(Dav1dTaskContext *const t, + const int bx4, const int by4, + const int bw4, const int bh4) + +{ + const Dav1dFrameContext *const f = t->f; + pixel *const pal = t->frame_thread.pass ? + f->frame_thread.pal[((t->by >> 1) + (t->bx & 1)) * (f->b4_stride >> 1) + + ((t->bx >> 1) + (t->by & 1))][0] : + bytefn(t->scratch.pal)[0]; + for (int x = 0; x < bw4; x++) + memcpy(bytefn(t->al_pal)[0][bx4 + x][0], pal, 8 * sizeof(pixel)); + for (int y = 0; y < bh4; y++) + memcpy(bytefn(t->al_pal)[1][by4 + y][0], pal, 8 * sizeof(pixel)); +} + +void bytefn(dav1d_copy_pal_block_uv)(Dav1dTaskContext *const t, + const int bx4, const int by4, + const int bw4, const int bh4) + +{ + const Dav1dFrameContext *const f = t->f; + const pixel (*const pal)[8] = t->frame_thread.pass ? + f->frame_thread.pal[((t->by >> 1) + (t->bx & 1)) * (f->b4_stride >> 1) + + ((t->bx >> 1) + (t->by & 1))] : + bytefn(t->scratch.pal); + // see aomedia bug 2183 for why we use luma coordinates here + for (int pl = 1; pl <= 2; pl++) { + for (int x = 0; x < bw4; x++) + memcpy(bytefn(t->al_pal)[0][bx4 + x][pl], pal[pl], 8 * sizeof(pixel)); + for (int y = 0; y < bh4; y++) + memcpy(bytefn(t->al_pal)[1][by4 + y][pl], pal[pl], 8 * sizeof(pixel)); + } +} + +void bytefn(dav1d_read_pal_plane)(Dav1dTaskContext *const t, Av1Block *const b, + const int pl, const int sz_ctx, + const int bx4, const int by4) +{ + Dav1dTileState *const ts = t->ts; + const Dav1dFrameContext *const f = t->f; + const int pal_sz = b->pal_sz[pl] = dav1d_msac_decode_symbol_adapt8(&ts->msac, + ts->cdf.m.pal_sz[pl][sz_ctx], 6) + 2; + pixel cache[16], used_cache[8]; + int l_cache = pl ? t->pal_sz_uv[1][by4] : t->l.pal_sz[by4]; + int n_cache = 0; + // don't reuse above palette outside SB64 boundaries + int a_cache = by4 & 15 ? pl ? t->pal_sz_uv[0][bx4] : t->a->pal_sz[bx4] : 0; + const pixel *l = bytefn(t->al_pal)[1][by4][pl]; + const pixel *a = bytefn(t->al_pal)[0][bx4][pl]; + + // fill/sort cache + while (l_cache && a_cache) { + if (*l < *a) { + if (!n_cache || cache[n_cache - 1] != *l) + cache[n_cache++] = *l; + l++; + l_cache--; + } else { + if (*a == *l) { + l++; + l_cache--; + } + if (!n_cache || cache[n_cache - 1] != *a) + cache[n_cache++] = *a; + a++; + a_cache--; + } + } + if (l_cache) { + do { + if (!n_cache || cache[n_cache - 1] != *l) + cache[n_cache++] = *l; + l++; + } while (--l_cache > 0); + } else if (a_cache) { + do { + if (!n_cache || cache[n_cache - 1] != *a) + cache[n_cache++] = *a; + a++; + } while (--a_cache > 0); + } + + // find reused cache entries + int i = 0; + for (int n = 0; n < n_cache && i < pal_sz; n++) + if (dav1d_msac_decode_bool_equi(&ts->msac)) + used_cache[i++] = cache[n]; + const int n_used_cache = i; + + // parse new entries + pixel *const pal = t->frame_thread.pass ? + f->frame_thread.pal[((t->by >> 1) + (t->bx & 1)) * (f->b4_stride >> 1) + + ((t->bx >> 1) + (t->by & 1))][pl] : + bytefn(t->scratch.pal)[pl]; + if (i < pal_sz) { + const int bpc = BITDEPTH == 8 ? 8 : f->cur.p.bpc; + int prev = pal[i++] = dav1d_msac_decode_bools(&ts->msac, bpc); + + if (i < pal_sz) { + int bits = bpc - 3 + dav1d_msac_decode_bools(&ts->msac, 2); + const int max = (1 << bpc) - 1; + + do { + const int delta = dav1d_msac_decode_bools(&ts->msac, bits); + prev = pal[i++] = imin(prev + delta + !pl, max); + if (prev + !pl >= max) { + for (; i < pal_sz; i++) + pal[i] = max; + break; + } + bits = imin(bits, 1 + ulog2(max - prev - !pl)); + } while (i < pal_sz); + } + + // merge cache+new entries + int n = 0, m = n_used_cache; + for (i = 0; i < pal_sz; i++) { + if (n < n_used_cache && (m >= pal_sz || used_cache[n] <= pal[m])) { + pal[i] = used_cache[n++]; + } else { + assert(m < pal_sz); + pal[i] = pal[m++]; + } + } + } else { + memcpy(pal, used_cache, n_used_cache * sizeof(*used_cache)); + } + + if (DEBUG_BLOCK_INFO) { + printf("Post-pal[pl=%d,sz=%d,cache_size=%d,used_cache=%d]: r=%d, cache=", + pl, pal_sz, n_cache, n_used_cache, ts->msac.rng); + for (int n = 0; n < n_cache; n++) + printf("%c%02x", n ? ' ' : '[', cache[n]); + printf("%s, pal=", n_cache ? "]" : "[]"); + for (int n = 0; n < pal_sz; n++) + printf("%c%02x", n ? ' ' : '[', pal[n]); + printf("]\n"); + } +} + +void bytefn(dav1d_read_pal_uv)(Dav1dTaskContext *const t, Av1Block *const b, + const int sz_ctx, const int bx4, const int by4) +{ + bytefn(dav1d_read_pal_plane)(t, b, 1, sz_ctx, bx4, by4); + + // V pal coding + Dav1dTileState *const ts = t->ts; + const Dav1dFrameContext *const f = t->f; + pixel *const pal = t->frame_thread.pass ? + f->frame_thread.pal[((t->by >> 1) + (t->bx & 1)) * (f->b4_stride >> 1) + + ((t->bx >> 1) + (t->by & 1))][2] : + bytefn(t->scratch.pal)[2]; + const int bpc = BITDEPTH == 8 ? 8 : f->cur.p.bpc; + if (dav1d_msac_decode_bool_equi(&ts->msac)) { + const int bits = bpc - 4 + dav1d_msac_decode_bools(&ts->msac, 2); + int prev = pal[0] = dav1d_msac_decode_bools(&ts->msac, bpc); + const int max = (1 << bpc) - 1; + for (int i = 1; i < b->pal_sz[1]; i++) { + int delta = dav1d_msac_decode_bools(&ts->msac, bits); + if (delta && dav1d_msac_decode_bool_equi(&ts->msac)) delta = -delta; + prev = pal[i] = (prev + delta) & max; + } + } else { + for (int i = 0; i < b->pal_sz[1]; i++) + pal[i] = dav1d_msac_decode_bools(&ts->msac, bpc); + } + if (DEBUG_BLOCK_INFO) { + printf("Post-pal[pl=2]: r=%d ", ts->msac.rng); + for (int n = 0; n < b->pal_sz[1]; n++) + printf("%c%02x", n ? ' ' : '[', pal[n]); + printf("]\n"); + } +} diff --git a/3rdparty/dav1d/src/ref.c b/3rdparty/dav1d/src/ref.c new file mode 100644 index 0000000..aa8f78b --- /dev/null +++ b/3rdparty/dav1d/src/ref.c @@ -0,0 +1,85 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include "src/ref.h" + +static void default_free_callback(const uint8_t *const data, void *const user_data) { + assert(data == user_data); + dav1d_free_aligned(user_data); +} + +Dav1dRef *dav1d_ref_create(const enum AllocationType type, size_t size) { + size = (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1); + + uint8_t *const data = dav1d_alloc_aligned(type, size + sizeof(Dav1dRef), 64); + if (!data) return NULL; + + Dav1dRef *const res = (Dav1dRef*)(data + size); + res->const_data = res->user_data = res->data = data; + atomic_init(&res->ref_cnt, 1); + res->free_ref = 0; + res->free_callback = default_free_callback; + + return res; +} + +static void pool_free_callback(const uint8_t *const data, void *const user_data) { + dav1d_mem_pool_push((Dav1dMemPool*)data, user_data); +} + +Dav1dRef *dav1d_ref_create_using_pool(Dav1dMemPool *const pool, size_t size) { + void *const buf = dav1d_mem_pool_pop(pool, size); + if (!buf) return NULL; + + /* Store Dav1dRef inside the Dav1dMemPoolBuffer alignment padding */ + assert(sizeof(Dav1dMemPoolBuffer) + sizeof(Dav1dRef) <= 64); + Dav1dRef *const res = &((Dav1dRef*)buf)[-1]; + res->data = buf; + res->const_data = pool; + atomic_init(&res->ref_cnt, 1); + res->free_ref = 0; + res->free_callback = pool_free_callback; + res->user_data = buf; + + return res; +} + +void dav1d_ref_dec(Dav1dRef **const pref) { + assert(pref != NULL); + + Dav1dRef *const ref = *pref; + if (!ref) return; + + *pref = NULL; + if (atomic_fetch_sub(&ref->ref_cnt, 1) == 1) { + const int free_ref = ref->free_ref; + ref->free_callback(ref->const_data, ref->user_data); + if (free_ref) dav1d_free(ref); + } +} diff --git a/3rdparty/dav1d/src/ref.h b/3rdparty/dav1d/src/ref.h new file mode 100644 index 0000000..f1c96eb --- /dev/null +++ b/3rdparty/dav1d/src/ref.h @@ -0,0 +1,77 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_REF_H +#define DAV1D_SRC_REF_H + +#include "dav1d/dav1d.h" + +#include "src/mem.h" +#include "src/thread.h" + +#include +#include + +struct Dav1dRef { + void *data; + const void *const_data; + atomic_int ref_cnt; + int free_ref; + void (*free_callback)(const uint8_t *data, void *user_data); + void *user_data; +}; + +#if !TRACK_HEAP_ALLOCATIONS +#define dav1d_ref_create(type, size) dav1d_ref_create(size) +#endif + +Dav1dRef *dav1d_ref_create(enum AllocationType type, size_t size); +Dav1dRef *dav1d_ref_create_using_pool(Dav1dMemPool *pool, size_t size); +void dav1d_ref_dec(Dav1dRef **ref); + +static inline Dav1dRef *dav1d_ref_init(Dav1dRef *const ref, const void *const ptr, + void (*const free_callback)(const uint8_t *data, void *user_data), + void *const user_data, const int free_ref) +{ + ref->data = NULL; + ref->const_data = ptr; + atomic_init(&ref->ref_cnt, 1); + ref->free_ref = free_ref; + ref->free_callback = free_callback; + ref->user_data = user_data; + return ref; +} + +static inline void dav1d_ref_inc(Dav1dRef *const ref) { + atomic_fetch_add_explicit(&ref->ref_cnt, 1, memory_order_relaxed); +} + +static inline int dav1d_ref_is_writable(Dav1dRef *const ref) { + return atomic_load(&ref->ref_cnt) == 1 && ref->data; +} + +#endif /* DAV1D_SRC_REF_H */ diff --git a/3rdparty/dav1d/src/refmvs.c b/3rdparty/dav1d/src/refmvs.c new file mode 100644 index 0000000..ac86c92 --- /dev/null +++ b/3rdparty/dav1d/src/refmvs.c @@ -0,0 +1,935 @@ +/* + * Copyright © 2020, VideoLAN and dav1d authors + * Copyright © 2020, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include + +#include "dav1d/common.h" + +#include "common/intops.h" + +#include "src/env.h" +#include "src/mem.h" +#include "src/refmvs.h" + +static void add_spatial_candidate(refmvs_candidate *const mvstack, int *const cnt, + const int weight, const refmvs_block *const b, + const union refmvs_refpair ref, const mv gmv[2], + int *const have_newmv_match, + int *const have_refmv_match) +{ + if (b->mv.mv[0].n == INVALID_MV) return; // intra block, no intrabc + + if (ref.ref[1] == -1) { + for (int n = 0; n < 2; n++) { + if (b->ref.ref[n] == ref.ref[0]) { + const mv cand_mv = ((b->mf & 1) && gmv[0].n != INVALID_MV) ? + gmv[0] : b->mv.mv[n]; + + *have_refmv_match = 1; + *have_newmv_match |= b->mf >> 1; + + const int last = *cnt; + for (int m = 0; m < last; m++) + if (mvstack[m].mv.mv[0].n == cand_mv.n) { + mvstack[m].weight += weight; + return; + } + + if (last < 8) { + mvstack[last].mv.mv[0] = cand_mv; + mvstack[last].weight = weight; + *cnt = last + 1; + } + return; + } + } + } else if (b->ref.pair == ref.pair) { + const refmvs_mvpair cand_mv = { .mv = { + [0] = ((b->mf & 1) && gmv[0].n != INVALID_MV) ? gmv[0] : b->mv.mv[0], + [1] = ((b->mf & 1) && gmv[1].n != INVALID_MV) ? gmv[1] : b->mv.mv[1], + }}; + + *have_refmv_match = 1; + *have_newmv_match |= b->mf >> 1; + + const int last = *cnt; + for (int n = 0; n < last; n++) + if (mvstack[n].mv.n == cand_mv.n) { + mvstack[n].weight += weight; + return; + } + + if (last < 8) { + mvstack[last].mv = cand_mv; + mvstack[last].weight = weight; + *cnt = last + 1; + } + } +} + +static int scan_row(refmvs_candidate *const mvstack, int *const cnt, + const union refmvs_refpair ref, const mv gmv[2], + const refmvs_block *b, const int bw4, const int w4, + const int max_rows, const int step, + int *const have_newmv_match, int *const have_refmv_match) +{ + const refmvs_block *cand_b = b; + const enum BlockSize first_cand_bs = cand_b->bs; + const uint8_t *const first_cand_b_dim = dav1d_block_dimensions[first_cand_bs]; + int cand_bw4 = first_cand_b_dim[0]; + int len = imax(step, imin(bw4, cand_bw4)); + + if (bw4 <= cand_bw4) { + // FIXME weight can be higher for odd blocks (bx4 & 1), but then the + // position of the first block has to be odd already, i.e. not just + // for row_offset=-3/-5 + // FIXME why can this not be cand_bw4? + const int weight = bw4 == 1 ? 2 : + imax(2, imin(2 * max_rows, first_cand_b_dim[1])); + add_spatial_candidate(mvstack, cnt, len * weight, cand_b, ref, gmv, + have_newmv_match, have_refmv_match); + return weight >> 1; + } + + for (int x = 0;;) { + // FIXME if we overhang above, we could fill a bitmask so we don't have + // to repeat the add_spatial_candidate() for the next row, but just increase + // the weight here + add_spatial_candidate(mvstack, cnt, len * 2, cand_b, ref, gmv, + have_newmv_match, have_refmv_match); + x += len; + if (x >= w4) return 1; + cand_b = &b[x]; + cand_bw4 = dav1d_block_dimensions[cand_b->bs][0]; + assert(cand_bw4 < bw4); + len = imax(step, cand_bw4); + } +} + +static int scan_col(refmvs_candidate *const mvstack, int *const cnt, + const union refmvs_refpair ref, const mv gmv[2], + /*const*/ refmvs_block *const *b, const int bh4, const int h4, + const int bx4, const int max_cols, const int step, + int *const have_newmv_match, int *const have_refmv_match) +{ + const refmvs_block *cand_b = &b[0][bx4]; + const enum BlockSize first_cand_bs = cand_b->bs; + const uint8_t *const first_cand_b_dim = dav1d_block_dimensions[first_cand_bs]; + int cand_bh4 = first_cand_b_dim[1]; + int len = imax(step, imin(bh4, cand_bh4)); + + if (bh4 <= cand_bh4) { + // FIXME weight can be higher for odd blocks (by4 & 1), but then the + // position of the first block has to be odd already, i.e. not just + // for col_offset=-3/-5 + // FIXME why can this not be cand_bh4? + const int weight = bh4 == 1 ? 2 : + imax(2, imin(2 * max_cols, first_cand_b_dim[0])); + add_spatial_candidate(mvstack, cnt, len * weight, cand_b, ref, gmv, + have_newmv_match, have_refmv_match); + return weight >> 1; + } + + for (int y = 0;;) { + // FIXME if we overhang above, we could fill a bitmask so we don't have + // to repeat the add_spatial_candidate() for the next row, but just increase + // the weight here + add_spatial_candidate(mvstack, cnt, len * 2, cand_b, ref, gmv, + have_newmv_match, have_refmv_match); + y += len; + if (y >= h4) return 1; + cand_b = &b[y][bx4]; + cand_bh4 = dav1d_block_dimensions[cand_b->bs][1]; + assert(cand_bh4 < bh4); + len = imax(step, cand_bh4); + } +} + +static inline union mv mv_projection(const union mv mv, const int num, const int den) { + static const uint16_t div_mult[32] = { + 0, 16384, 8192, 5461, 4096, 3276, 2730, 2340, + 2048, 1820, 1638, 1489, 1365, 1260, 1170, 1092, + 1024, 963, 910, 862, 819, 780, 744, 712, + 682, 655, 630, 606, 585, 564, 546, 528 + }; + assert(den > 0 && den < 32); + assert(num > -32 && num < 32); + const int frac = num * div_mult[den]; + const int y = mv.y * frac, x = mv.x * frac; + // Round and clip according to AV1 spec section 7.9.3 + return (union mv) { // 0x3fff == (1 << 14) - 1 + .y = iclip((y + 8192 + (y >> 31)) >> 14, -0x3fff, 0x3fff), + .x = iclip((x + 8192 + (x >> 31)) >> 14, -0x3fff, 0x3fff) + }; +} + +static void add_temporal_candidate(const refmvs_frame *const rf, + refmvs_candidate *const mvstack, int *const cnt, + const refmvs_temporal_block *const rb, + const union refmvs_refpair ref, int *const globalmv_ctx, + const union mv gmv[]) +{ + if (rb->mv.n == INVALID_MV) return; + + union mv mv = mv_projection(rb->mv, rf->pocdiff[ref.ref[0] - 1], rb->ref); + fix_mv_precision(rf->frm_hdr, &mv); + + const int last = *cnt; + if (ref.ref[1] == -1) { + if (globalmv_ctx) + *globalmv_ctx = (abs(mv.x - gmv[0].x) | abs(mv.y - gmv[0].y)) >= 16; + + for (int n = 0; n < last; n++) + if (mvstack[n].mv.mv[0].n == mv.n) { + mvstack[n].weight += 2; + return; + } + if (last < 8) { + mvstack[last].mv.mv[0] = mv; + mvstack[last].weight = 2; + *cnt = last + 1; + } + } else { + refmvs_mvpair mvp = { .mv = { + [0] = mv, + [1] = mv_projection(rb->mv, rf->pocdiff[ref.ref[1] - 1], rb->ref), + }}; + fix_mv_precision(rf->frm_hdr, &mvp.mv[1]); + + for (int n = 0; n < last; n++) + if (mvstack[n].mv.n == mvp.n) { + mvstack[n].weight += 2; + return; + } + if (last < 8) { + mvstack[last].mv = mvp; + mvstack[last].weight = 2; + *cnt = last + 1; + } + } +} + +static void add_compound_extended_candidate(refmvs_candidate *const same, + int *const same_count, + const refmvs_block *const cand_b, + const int sign0, const int sign1, + const union refmvs_refpair ref, + const uint8_t *const sign_bias) +{ + refmvs_candidate *const diff = &same[2]; + int *const diff_count = &same_count[2]; + + for (int n = 0; n < 2; n++) { + const int cand_ref = cand_b->ref.ref[n]; + + if (cand_ref <= 0) break; + + mv cand_mv = cand_b->mv.mv[n]; + if (cand_ref == ref.ref[0]) { + if (same_count[0] < 2) + same[same_count[0]++].mv.mv[0] = cand_mv; + if (diff_count[1] < 2) { + if (sign1 ^ sign_bias[cand_ref - 1]) { + cand_mv.y = -cand_mv.y; + cand_mv.x = -cand_mv.x; + } + diff[diff_count[1]++].mv.mv[1] = cand_mv; + } + } else if (cand_ref == ref.ref[1]) { + if (same_count[1] < 2) + same[same_count[1]++].mv.mv[1] = cand_mv; + if (diff_count[0] < 2) { + if (sign0 ^ sign_bias[cand_ref - 1]) { + cand_mv.y = -cand_mv.y; + cand_mv.x = -cand_mv.x; + } + diff[diff_count[0]++].mv.mv[0] = cand_mv; + } + } else { + mv i_cand_mv = (union mv) { + .x = -cand_mv.x, + .y = -cand_mv.y + }; + + if (diff_count[0] < 2) { + diff[diff_count[0]++].mv.mv[0] = + sign0 ^ sign_bias[cand_ref - 1] ? + i_cand_mv : cand_mv; + } + + if (diff_count[1] < 2) { + diff[diff_count[1]++].mv.mv[1] = + sign1 ^ sign_bias[cand_ref - 1] ? + i_cand_mv : cand_mv; + } + } + } +} + +static void add_single_extended_candidate(refmvs_candidate mvstack[8], int *const cnt, + const refmvs_block *const cand_b, + const int sign, const uint8_t *const sign_bias) +{ + for (int n = 0; n < 2; n++) { + const int cand_ref = cand_b->ref.ref[n]; + + if (cand_ref <= 0) break; + // we need to continue even if cand_ref == ref.ref[0], since + // the candidate could have been added as a globalmv variant, + // which changes the value + // FIXME if scan_{row,col}() returned a mask for the nearest + // edge, we could skip the appropriate ones here + + mv cand_mv = cand_b->mv.mv[n]; + if (sign ^ sign_bias[cand_ref - 1]) { + cand_mv.y = -cand_mv.y; + cand_mv.x = -cand_mv.x; + } + + int m; + const int last = *cnt; + for (m = 0; m < last; m++) + if (cand_mv.n == mvstack[m].mv.mv[0].n) + break; + if (m == last) { + mvstack[m].mv.mv[0] = cand_mv; + mvstack[m].weight = 2; // "minimal" + *cnt = last + 1; + } + } +} + +/* + * refmvs_frame allocates memory for one sbrow (32 blocks high, whole frame + * wide) of 4x4-resolution refmvs_block entries for spatial MV referencing. + * mvrefs_tile[] keeps a list of 35 (32 + 3 above) pointers into this memory, + * and each sbrow, the bottom entries (y=27/29/31) are exchanged with the top + * (-5/-3/-1) pointers by calling dav1d_refmvs_tile_sbrow_init() at the start + * of each tile/sbrow. + * + * For temporal MV referencing, we call dav1d_refmvs_save_tmvs() at the end of + * each tile/sbrow (when tile column threading is enabled), or at the start of + * each interleaved sbrow (i.e. once for all tile columns together, when tile + * column threading is disabled). This will copy the 4x4-resolution spatial MVs + * into 8x8-resolution refmvs_temporal_block structures. Then, for subsequent + * frames, at the start of each tile/sbrow (when tile column threading is + * enabled) or at the start of each interleaved sbrow (when tile column + * threading is disabled), we call load_tmvs(), which will project the MVs to + * their respective position in the current frame. + */ + +void dav1d_refmvs_find(const refmvs_tile *const rt, + refmvs_candidate mvstack[8], int *const cnt, + int *const ctx, + const union refmvs_refpair ref, const enum BlockSize bs, + const enum EdgeFlags edge_flags, + const int by4, const int bx4) +{ + const refmvs_frame *const rf = rt->rf; + const uint8_t *const b_dim = dav1d_block_dimensions[bs]; + const int bw4 = b_dim[0], w4 = imin(imin(bw4, 16), rt->tile_col.end - bx4); + const int bh4 = b_dim[1], h4 = imin(imin(bh4, 16), rt->tile_row.end - by4); + mv gmv[2], tgmv[2]; + + *cnt = 0; + assert(ref.ref[0] >= 0 && ref.ref[0] <= 8 && + ref.ref[1] >= -1 && ref.ref[1] <= 8); + if (ref.ref[0] > 0) { + tgmv[0] = get_gmv_2d(&rf->frm_hdr->gmv[ref.ref[0] - 1], + bx4, by4, bw4, bh4, rf->frm_hdr); + gmv[0] = rf->frm_hdr->gmv[ref.ref[0] - 1].type > DAV1D_WM_TYPE_TRANSLATION ? + tgmv[0] : (mv) { .n = INVALID_MV }; + } else { + tgmv[0] = (mv) { .n = 0 }; + gmv[0] = (mv) { .n = INVALID_MV }; + } + if (ref.ref[1] > 0) { + tgmv[1] = get_gmv_2d(&rf->frm_hdr->gmv[ref.ref[1] - 1], + bx4, by4, bw4, bh4, rf->frm_hdr); + gmv[1] = rf->frm_hdr->gmv[ref.ref[1] - 1].type > DAV1D_WM_TYPE_TRANSLATION ? + tgmv[1] : (mv) { .n = INVALID_MV }; + } + + // top + int have_newmv = 0, have_col_mvs = 0, have_row_mvs = 0; + unsigned max_rows = 0, n_rows = ~0; + const refmvs_block *b_top; + if (by4 > rt->tile_row.start) { + max_rows = imin((by4 - rt->tile_row.start + 1) >> 1, 2 + (bh4 > 1)); + b_top = &rt->r[(by4 & 31) - 1 + 5][bx4]; + n_rows = scan_row(mvstack, cnt, ref, gmv, b_top, + bw4, w4, max_rows, bw4 >= 16 ? 4 : 1, + &have_newmv, &have_row_mvs); + } + + // left + unsigned max_cols = 0, n_cols = ~0U; + refmvs_block *const *b_left; + if (bx4 > rt->tile_col.start) { + max_cols = imin((bx4 - rt->tile_col.start + 1) >> 1, 2 + (bw4 > 1)); + b_left = &rt->r[(by4 & 31) + 5]; + n_cols = scan_col(mvstack, cnt, ref, gmv, b_left, + bh4, h4, bx4 - 1, max_cols, bh4 >= 16 ? 4 : 1, + &have_newmv, &have_col_mvs); + } + + // top/right + if (n_rows != ~0U && edge_flags & EDGE_I444_TOP_HAS_RIGHT && + imax(bw4, bh4) <= 16 && bw4 + bx4 < rt->tile_col.end) + { + add_spatial_candidate(mvstack, cnt, 4, &b_top[bw4], ref, gmv, + &have_newmv, &have_row_mvs); + } + + const int nearest_match = have_col_mvs + have_row_mvs; + const int nearest_cnt = *cnt; + for (int n = 0; n < nearest_cnt; n++) + mvstack[n].weight += 640; + + // temporal + int globalmv_ctx = rf->frm_hdr->use_ref_frame_mvs; + if (rf->use_ref_frame_mvs) { + const ptrdiff_t stride = rf->rp_stride; + const int by8 = by4 >> 1, bx8 = bx4 >> 1; + const refmvs_temporal_block *const rbi = &rt->rp_proj[(by8 & 15) * stride + bx8]; + const refmvs_temporal_block *rb = rbi; + const int step_h = bw4 >= 16 ? 2 : 1, step_v = bh4 >= 16 ? 2 : 1; + const int w8 = imin((w4 + 1) >> 1, 8), h8 = imin((h4 + 1) >> 1, 8); + for (int y = 0; y < h8; y += step_v) { + for (int x = 0; x < w8; x+= step_h) { + add_temporal_candidate(rf, mvstack, cnt, &rb[x], ref, + !(x | y) ? &globalmv_ctx : NULL, tgmv); + } + rb += stride * step_v; + } + if (imin(bw4, bh4) >= 2 && imax(bw4, bh4) < 16) { + const int bh8 = bh4 >> 1, bw8 = bw4 >> 1; + rb = &rbi[bh8 * stride]; + const int has_bottom = by8 + bh8 < imin(rt->tile_row.end >> 1, + (by8 & ~7) + 8); + if (has_bottom && bx8 - 1 >= imax(rt->tile_col.start >> 1, bx8 & ~7)) { + add_temporal_candidate(rf, mvstack, cnt, &rb[-1], ref, + NULL, NULL); + } + if (bx8 + bw8 < imin(rt->tile_col.end >> 1, (bx8 & ~7) + 8)) { + if (has_bottom) { + add_temporal_candidate(rf, mvstack, cnt, &rb[bw8], ref, + NULL, NULL); + } + if (by8 + bh8 - 1 < imin(rt->tile_row.end >> 1, (by8 & ~7) + 8)) { + add_temporal_candidate(rf, mvstack, cnt, &rb[bw8 - stride], + ref, NULL, NULL); + } + } + } + } + assert(*cnt <= 8); + + // top/left (which, confusingly, is part of "secondary" references) + int have_dummy_newmv_match; + if ((n_rows | n_cols) != ~0U) { + add_spatial_candidate(mvstack, cnt, 4, &b_top[-1], ref, gmv, + &have_dummy_newmv_match, &have_row_mvs); + } + + // "secondary" (non-direct neighbour) top & left edges + // what is different about secondary is that everything is now in 8x8 resolution + for (int n = 2; n <= 3; n++) { + if ((unsigned) n > n_rows && (unsigned) n <= max_rows) { + n_rows += scan_row(mvstack, cnt, ref, gmv, + &rt->r[(((by4 & 31) - 2 * n + 1) | 1) + 5][bx4 | 1], + bw4, w4, 1 + max_rows - n, bw4 >= 16 ? 4 : 2, + &have_dummy_newmv_match, &have_row_mvs); + } + + if ((unsigned) n > n_cols && (unsigned) n <= max_cols) { + n_cols += scan_col(mvstack, cnt, ref, gmv, &rt->r[((by4 & 31) | 1) + 5], + bh4, h4, (bx4 - n * 2 + 1) | 1, + 1 + max_cols - n, bh4 >= 16 ? 4 : 2, + &have_dummy_newmv_match, &have_col_mvs); + } + } + assert(*cnt <= 8); + + const int ref_match_count = have_col_mvs + have_row_mvs; + + // context build-up + int refmv_ctx, newmv_ctx; + switch (nearest_match) { + case 0: + refmv_ctx = imin(2, ref_match_count); + newmv_ctx = ref_match_count > 0; + break; + case 1: + refmv_ctx = imin(ref_match_count * 3, 4); + newmv_ctx = 3 - have_newmv; + break; + case 2: + refmv_ctx = 5; + newmv_ctx = 5 - have_newmv; + break; + } + + // sorting (nearest, then "secondary") + int len = nearest_cnt; + while (len) { + int last = 0; + for (int n = 1; n < len; n++) { + if (mvstack[n - 1].weight < mvstack[n].weight) { +#define EXCHANGE(a, b) do { refmvs_candidate tmp = a; a = b; b = tmp; } while (0) + EXCHANGE(mvstack[n - 1], mvstack[n]); + last = n; + } + } + len = last; + } + len = *cnt; + while (len > nearest_cnt) { + int last = nearest_cnt; + for (int n = nearest_cnt + 1; n < len; n++) { + if (mvstack[n - 1].weight < mvstack[n].weight) { + EXCHANGE(mvstack[n - 1], mvstack[n]); +#undef EXCHANGE + last = n; + } + } + len = last; + } + + if (ref.ref[1] > 0) { + if (*cnt < 2) { + const int sign0 = rf->sign_bias[ref.ref[0] - 1]; + const int sign1 = rf->sign_bias[ref.ref[1] - 1]; + const int sz4 = imin(w4, h4); + refmvs_candidate *const same = &mvstack[*cnt]; + int same_count[4] = { 0 }; + + // non-self references in top + if (n_rows != ~0U) for (int x = 0; x < sz4;) { + const refmvs_block *const cand_b = &b_top[x]; + add_compound_extended_candidate(same, same_count, cand_b, + sign0, sign1, ref, rf->sign_bias); + x += dav1d_block_dimensions[cand_b->bs][0]; + } + + // non-self references in left + if (n_cols != ~0U) for (int y = 0; y < sz4;) { + const refmvs_block *const cand_b = &b_left[y][bx4 - 1]; + add_compound_extended_candidate(same, same_count, cand_b, + sign0, sign1, ref, rf->sign_bias); + y += dav1d_block_dimensions[cand_b->bs][1]; + } + + refmvs_candidate *const diff = &same[2]; + const int *const diff_count = &same_count[2]; + + // merge together + for (int n = 0; n < 2; n++) { + int m = same_count[n]; + + if (m >= 2) continue; + + const int l = diff_count[n]; + if (l) { + same[m].mv.mv[n] = diff[0].mv.mv[n]; + if (++m == 2) continue; + if (l == 2) { + same[1].mv.mv[n] = diff[1].mv.mv[n]; + continue; + } + } + do { + same[m].mv.mv[n] = tgmv[n]; + } while (++m < 2); + } + + // if the first extended was the same as the non-extended one, + // then replace it with the second extended one + int n = *cnt; + if (n == 1 && mvstack[0].mv.n == same[0].mv.n) + mvstack[1].mv = mvstack[2].mv; + do { + mvstack[n].weight = 2; + } while (++n < 2); + *cnt = 2; + } + + // clamping + const int left = -(bx4 + bw4 + 4) * 4 * 8; + const int right = (rf->iw4 - bx4 + 4) * 4 * 8; + const int top = -(by4 + bh4 + 4) * 4 * 8; + const int bottom = (rf->ih4 - by4 + 4) * 4 * 8; + + const int n_refmvs = *cnt; + int n = 0; + do { + mvstack[n].mv.mv[0].x = iclip(mvstack[n].mv.mv[0].x, left, right); + mvstack[n].mv.mv[0].y = iclip(mvstack[n].mv.mv[0].y, top, bottom); + mvstack[n].mv.mv[1].x = iclip(mvstack[n].mv.mv[1].x, left, right); + mvstack[n].mv.mv[1].y = iclip(mvstack[n].mv.mv[1].y, top, bottom); + } while (++n < n_refmvs); + + switch (refmv_ctx >> 1) { + case 0: + *ctx = imin(newmv_ctx, 1); + break; + case 1: + *ctx = 1 + imin(newmv_ctx, 3); + break; + case 2: + *ctx = iclip(3 + newmv_ctx, 4, 7); + break; + } + + return; + } else if (*cnt < 2 && ref.ref[0] > 0) { + const int sign = rf->sign_bias[ref.ref[0] - 1]; + const int sz4 = imin(w4, h4); + + // non-self references in top + if (n_rows != ~0U) for (int x = 0; x < sz4 && *cnt < 2;) { + const refmvs_block *const cand_b = &b_top[x]; + add_single_extended_candidate(mvstack, cnt, cand_b, sign, rf->sign_bias); + x += dav1d_block_dimensions[cand_b->bs][0]; + } + + // non-self references in left + if (n_cols != ~0U) for (int y = 0; y < sz4 && *cnt < 2;) { + const refmvs_block *const cand_b = &b_left[y][bx4 - 1]; + add_single_extended_candidate(mvstack, cnt, cand_b, sign, rf->sign_bias); + y += dav1d_block_dimensions[cand_b->bs][1]; + } + } + assert(*cnt <= 8); + + // clamping + int n_refmvs = *cnt; + if (n_refmvs) { + const int left = -(bx4 + bw4 + 4) * 4 * 8; + const int right = (rf->iw4 - bx4 + 4) * 4 * 8; + const int top = -(by4 + bh4 + 4) * 4 * 8; + const int bottom = (rf->ih4 - by4 + 4) * 4 * 8; + + int n = 0; + do { + mvstack[n].mv.mv[0].x = iclip(mvstack[n].mv.mv[0].x, left, right); + mvstack[n].mv.mv[0].y = iclip(mvstack[n].mv.mv[0].y, top, bottom); + } while (++n < n_refmvs); + } + + for (int n = *cnt; n < 2; n++) + mvstack[n].mv.mv[0] = tgmv[0]; + + *ctx = (refmv_ctx << 4) | (globalmv_ctx << 3) | newmv_ctx; +} + +void dav1d_refmvs_tile_sbrow_init(refmvs_tile *const rt, const refmvs_frame *const rf, + const int tile_col_start4, const int tile_col_end4, + const int tile_row_start4, const int tile_row_end4, + const int sby, int tile_row_idx, const int pass) +{ + if (rf->n_tile_threads == 1) tile_row_idx = 0; + rt->rp_proj = &rf->rp_proj[16 * rf->rp_stride * tile_row_idx]; + const ptrdiff_t r_stride = rf->rp_stride * 2; + const ptrdiff_t pass_off = (rf->n_frame_threads > 1 && pass == 2) ? + 35 * 2 * rf->n_blocks : 0; + refmvs_block *r = &rf->r[35 * r_stride * tile_row_idx + pass_off]; + const int sbsz = rf->sbsz; + const int off = (sbsz * sby) & 16; + for (int i = 0; i < sbsz; i++, r += r_stride) + rt->r[off + 5 + i] = r; + rt->r[off + 0] = r; + r += r_stride; + rt->r[off + 1] = NULL; + rt->r[off + 2] = r; + r += r_stride; + rt->r[off + 3] = NULL; + rt->r[off + 4] = r; + if (sby & 1) { +#define EXCHANGE(a, b) do { void *const tmp = a; a = b; b = tmp; } while (0) + EXCHANGE(rt->r[off + 0], rt->r[off + sbsz + 0]); + EXCHANGE(rt->r[off + 2], rt->r[off + sbsz + 2]); + EXCHANGE(rt->r[off + 4], rt->r[off + sbsz + 4]); +#undef EXCHANGE + } + + rt->rf = rf; + rt->tile_row.start = tile_row_start4; + rt->tile_row.end = imin(tile_row_end4, rf->ih4); + rt->tile_col.start = tile_col_start4; + rt->tile_col.end = imin(tile_col_end4, rf->iw4); +} + +static void load_tmvs_c(const refmvs_frame *const rf, int tile_row_idx, + const int col_start8, const int col_end8, + const int row_start8, int row_end8) +{ + if (rf->n_tile_threads == 1) tile_row_idx = 0; + assert(row_start8 >= 0); + assert((unsigned) (row_end8 - row_start8) <= 16U); + row_end8 = imin(row_end8, rf->ih8); + const int col_start8i = imax(col_start8 - 8, 0); + const int col_end8i = imin(col_end8 + 8, rf->iw8); + + const ptrdiff_t stride = rf->rp_stride; + refmvs_temporal_block *rp_proj = + &rf->rp_proj[16 * stride * tile_row_idx + (row_start8 & 15) * stride]; + for (int y = row_start8; y < row_end8; y++) { + for (int x = col_start8; x < col_end8; x++) + rp_proj[x].mv.n = INVALID_MV; + rp_proj += stride; + } + + rp_proj = &rf->rp_proj[16 * stride * tile_row_idx]; + for (int n = 0; n < rf->n_mfmvs; n++) { + const int ref2cur = rf->mfmv_ref2cur[n]; + if (ref2cur == INVALID_REF2CUR) continue; + + const int ref = rf->mfmv_ref[n]; + const int ref_sign = ref - 4; + const refmvs_temporal_block *r = &rf->rp_ref[ref][row_start8 * stride]; + for (int y = row_start8; y < row_end8; y++) { + const int y_sb_align = y & ~7; + const int y_proj_start = imax(y_sb_align, row_start8); + const int y_proj_end = imin(y_sb_align + 8, row_end8); + for (int x = col_start8i; x < col_end8i; x++) { + const refmvs_temporal_block *rb = &r[x]; + const int b_ref = rb->ref; + if (!b_ref) continue; + const int ref2ref = rf->mfmv_ref2ref[n][b_ref - 1]; + if (!ref2ref) continue; + const mv b_mv = rb->mv; + const mv offset = mv_projection(b_mv, ref2cur, ref2ref); + int pos_x = x + apply_sign(abs(offset.x) >> 6, + offset.x ^ ref_sign); + const int pos_y = y + apply_sign(abs(offset.y) >> 6, + offset.y ^ ref_sign); + if (pos_y >= y_proj_start && pos_y < y_proj_end) { + const ptrdiff_t pos = (pos_y & 15) * stride; + for (;;) { + const int x_sb_align = x & ~7; + if (pos_x >= imax(x_sb_align - 8, col_start8) && + pos_x < imin(x_sb_align + 16, col_end8)) + { + rp_proj[pos + pos_x].mv = rb->mv; + rp_proj[pos + pos_x].ref = ref2ref; + } + if (++x >= col_end8i) break; + rb++; + if (rb->ref != b_ref || rb->mv.n != b_mv.n) break; + pos_x++; + } + } else { + for (;;) { + if (++x >= col_end8i) break; + rb++; + if (rb->ref != b_ref || rb->mv.n != b_mv.n) break; + } + } + x--; + } + r += stride; + } + } +} + +static void save_tmvs_c(refmvs_temporal_block *rp, const ptrdiff_t stride, + refmvs_block *const *const rr, + const uint8_t *const ref_sign, + const int col_end8, const int row_end8, + const int col_start8, const int row_start8) +{ + for (int y = row_start8; y < row_end8; y++) { + const refmvs_block *const b = rr[(y & 15) * 2]; + + for (int x = col_start8; x < col_end8;) { + const refmvs_block *const cand_b = &b[x * 2 + 1]; + const int bw8 = (dav1d_block_dimensions[cand_b->bs][0] + 1) >> 1; + + if (cand_b->ref.ref[1] > 0 && ref_sign[cand_b->ref.ref[1] - 1] && + (abs(cand_b->mv.mv[1].y) | abs(cand_b->mv.mv[1].x)) < 4096) + { + for (int n = 0; n < bw8; n++, x++) + rp[x] = (refmvs_temporal_block) { .mv = cand_b->mv.mv[1], + .ref = cand_b->ref.ref[1] }; + } else if (cand_b->ref.ref[0] > 0 && ref_sign[cand_b->ref.ref[0] - 1] && + (abs(cand_b->mv.mv[0].y) | abs(cand_b->mv.mv[0].x)) < 4096) + { + for (int n = 0; n < bw8; n++, x++) + rp[x] = (refmvs_temporal_block) { .mv = cand_b->mv.mv[0], + .ref = cand_b->ref.ref[0] }; + } else { + for (int n = 0; n < bw8; n++, x++) { + rp[x].mv.n = 0; + rp[x].ref = 0; // "invalid" + } + } + } + rp += stride; + } +} + +int dav1d_refmvs_init_frame(refmvs_frame *const rf, + const Dav1dSequenceHeader *const seq_hdr, + const Dav1dFrameHeader *const frm_hdr, + const uint8_t ref_poc[7], + refmvs_temporal_block *const rp, + const uint8_t ref_ref_poc[7][7], + /*const*/ refmvs_temporal_block *const rp_ref[7], + const int n_tile_threads, const int n_frame_threads) +{ + const int rp_stride = ((frm_hdr->width[0] + 127) & ~127) >> 3; + const int n_tile_rows = n_tile_threads > 1 ? frm_hdr->tiling.rows : 1; + const int n_blocks = rp_stride * n_tile_rows; + + rf->sbsz = 16 << seq_hdr->sb128; + rf->frm_hdr = frm_hdr; + rf->iw8 = (frm_hdr->width[0] + 7) >> 3; + rf->ih8 = (frm_hdr->height + 7) >> 3; + rf->iw4 = rf->iw8 << 1; + rf->ih4 = rf->ih8 << 1; + rf->rp = rp; + rf->rp_stride = rp_stride; + rf->n_tile_threads = n_tile_threads; + rf->n_frame_threads = n_frame_threads; + + if (n_blocks != rf->n_blocks) { + const size_t r_sz = sizeof(*rf->r) * 35 * 2 * n_blocks * (1 + (n_frame_threads > 1)); + const size_t rp_proj_sz = sizeof(*rf->rp_proj) * 16 * n_blocks; + /* Note that sizeof(*rf->r) == 12, but it's accessed using 16-byte unaligned + * loads in save_tmvs() asm which can overread 4 bytes into rp_proj. */ + dav1d_free_aligned(rf->r); + rf->r = dav1d_alloc_aligned(ALLOC_REFMVS, r_sz + rp_proj_sz, 64); + if (!rf->r) { + rf->n_blocks = 0; + return DAV1D_ERR(ENOMEM); + } + + rf->rp_proj = (refmvs_temporal_block*)((uintptr_t)rf->r + r_sz); + rf->n_blocks = n_blocks; + } + + const int poc = frm_hdr->frame_offset; + for (int i = 0; i < 7; i++) { + const int poc_diff = get_poc_diff(seq_hdr->order_hint_n_bits, + ref_poc[i], poc); + rf->sign_bias[i] = poc_diff > 0; + rf->mfmv_sign[i] = poc_diff < 0; + rf->pocdiff[i] = iclip(get_poc_diff(seq_hdr->order_hint_n_bits, + poc, ref_poc[i]), -31, 31); + } + + // temporal MV setup + rf->n_mfmvs = 0; + rf->rp_ref = rp_ref; + if (frm_hdr->use_ref_frame_mvs && seq_hdr->order_hint_n_bits) { + int total = 2; + if (rp_ref[0] && ref_ref_poc[0][6] != ref_poc[3] /* alt-of-last != gold */) { + rf->mfmv_ref[rf->n_mfmvs++] = 0; // last + total = 3; + } + if (rp_ref[4] && get_poc_diff(seq_hdr->order_hint_n_bits, ref_poc[4], + frm_hdr->frame_offset) > 0) + { + rf->mfmv_ref[rf->n_mfmvs++] = 4; // bwd + } + if (rp_ref[5] && get_poc_diff(seq_hdr->order_hint_n_bits, ref_poc[5], + frm_hdr->frame_offset) > 0) + { + rf->mfmv_ref[rf->n_mfmvs++] = 5; // altref2 + } + if (rf->n_mfmvs < total && rp_ref[6] && + get_poc_diff(seq_hdr->order_hint_n_bits, ref_poc[6], + frm_hdr->frame_offset) > 0) + { + rf->mfmv_ref[rf->n_mfmvs++] = 6; // altref + } + if (rf->n_mfmvs < total && rp_ref[1]) + rf->mfmv_ref[rf->n_mfmvs++] = 1; // last2 + + for (int n = 0; n < rf->n_mfmvs; n++) { + const int rpoc = ref_poc[rf->mfmv_ref[n]]; + const int diff1 = get_poc_diff(seq_hdr->order_hint_n_bits, + rpoc, frm_hdr->frame_offset); + if (abs(diff1) > 31) { + rf->mfmv_ref2cur[n] = INVALID_REF2CUR; + } else { + rf->mfmv_ref2cur[n] = rf->mfmv_ref[n] < 4 ? -diff1 : diff1; + for (int m = 0; m < 7; m++) { + const int rrpoc = ref_ref_poc[rf->mfmv_ref[n]][m]; + const int diff2 = get_poc_diff(seq_hdr->order_hint_n_bits, + rpoc, rrpoc); + // unsigned comparison also catches the < 0 case + rf->mfmv_ref2ref[n][m] = (unsigned) diff2 > 31U ? 0 : diff2; + } + } + } + } + rf->use_ref_frame_mvs = rf->n_mfmvs > 0; + + return 0; +} + +static void splat_mv_c(refmvs_block **rr, const refmvs_block *const rmv, + const int bx4, const int bw4, int bh4) +{ + do { + refmvs_block *const r = *rr++ + bx4; + for (int x = 0; x < bw4; x++) + r[x] = *rmv; + } while (--bh4); +} + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM +#include "src/arm/refmvs.h" +#elif ARCH_LOONGARCH64 +#include "src/loongarch/refmvs.h" +#elif ARCH_X86 +#include "src/x86/refmvs.h" +#endif +#endif + +COLD void dav1d_refmvs_dsp_init(Dav1dRefmvsDSPContext *const c) +{ + c->load_tmvs = load_tmvs_c; + c->save_tmvs = save_tmvs_c; + c->splat_mv = splat_mv_c; + +#if HAVE_ASM +#if ARCH_AARCH64 || ARCH_ARM + refmvs_dsp_init_arm(c); +#elif ARCH_LOONGARCH64 + refmvs_dsp_init_loongarch(c); +#elif ARCH_X86 + refmvs_dsp_init_x86(c); +#endif +#endif +} diff --git a/3rdparty/dav1d/src/refmvs.h b/3rdparty/dav1d/src/refmvs.h new file mode 100644 index 0000000..9e0ed74 --- /dev/null +++ b/3rdparty/dav1d/src/refmvs.h @@ -0,0 +1,178 @@ +/* + * Copyright © 2020, VideoLAN and dav1d authors + * Copyright © 2020, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_REF_MVS_H +#define DAV1D_SRC_REF_MVS_H + +#include + +#include "dav1d/headers.h" + +#include "common/intops.h" + +#include "src/intra_edge.h" +#include "src/tables.h" + +#define INVALID_MV 0x80008000 +#define INVALID_REF2CUR (-32) + +PACKED(typedef struct refmvs_temporal_block { + mv mv; + uint8_t ref; +}) refmvs_temporal_block; +CHECK_SIZE(refmvs_temporal_block, 5); + +PACKED(typedef union refmvs_refpair { + int8_t ref[2]; // [0] = 0: intra=1, [1] = -1: comp=0 + uint16_t pair; +}) ALIGN(refmvs_refpair, 2); +CHECK_SIZE(refmvs_refpair, 2); + +typedef union refmvs_mvpair { + mv mv[2]; + uint64_t n; +} refmvs_mvpair; +CHECK_SIZE(refmvs_mvpair, 8); + +PACKED(typedef struct refmvs_block { + refmvs_mvpair mv; + refmvs_refpair ref; + uint8_t bs, mf; // 1 = globalmv+affine, 2 = newmv +}) ALIGN(refmvs_block, 4); +CHECK_SIZE(refmvs_block, 12); + +typedef struct refmvs_frame { + const Dav1dFrameHeader *frm_hdr; + int iw4, ih4, iw8, ih8; + int sbsz; + int use_ref_frame_mvs; + uint8_t sign_bias[7], mfmv_sign[7]; + int8_t pocdiff[7]; + uint8_t mfmv_ref[3]; + int8_t mfmv_ref2cur[3]; + uint8_t mfmv_ref2ref[3][7]; + int n_mfmvs; + + int n_blocks; + refmvs_temporal_block *rp; + /*const*/ refmvs_temporal_block *const *rp_ref; + refmvs_temporal_block *rp_proj; + ptrdiff_t rp_stride; + + refmvs_block *r; // 35 x r_stride memory + int n_tile_threads, n_frame_threads; +} refmvs_frame; + +typedef struct refmvs_tile { + const refmvs_frame *rf; + refmvs_block *r[32 + 5]; + refmvs_temporal_block *rp_proj; + struct { + int start, end; + } tile_col, tile_row; +} refmvs_tile; + +typedef struct refmvs_candidate { + refmvs_mvpair mv; + int weight; +} refmvs_candidate; + +// initialize temporal MVs; this can be done in any configuration, e.g. one +// tile/sbrow at a time, where col_{start,end}8 are the tile boundaries; or +// it can just be for the whole frame's sbrow, where col_{start,end}8 are the +// frame boundaries. row_{start,end}8 are the superblock row boundaries. +#define decl_load_tmvs_fn(name) \ +void (name)(const refmvs_frame *rf, int tile_row_idx, \ + int col_start8, int col_end8, int row_start8, int row_end8) +typedef decl_load_tmvs_fn(*load_tmvs_fn); + +#define decl_save_tmvs_fn(name) \ +void (name)(refmvs_temporal_block *rp, const ptrdiff_t stride, \ + refmvs_block *const *const rr, const uint8_t *const ref_sign, \ + int col_end8, int row_end8, int col_start8, int row_start8) +typedef decl_save_tmvs_fn(*save_tmvs_fn); + +#define decl_splat_mv_fn(name) \ +void (name)(refmvs_block **rr, const refmvs_block *rmv, int bx4, int bw4, int bh4) +typedef decl_splat_mv_fn(*splat_mv_fn); + +typedef struct Dav1dRefmvsDSPContext { + load_tmvs_fn load_tmvs; + save_tmvs_fn save_tmvs; + splat_mv_fn splat_mv; +} Dav1dRefmvsDSPContext; + +// call once per frame +int dav1d_refmvs_init_frame(refmvs_frame *rf, + const Dav1dSequenceHeader *seq_hdr, + const Dav1dFrameHeader *frm_hdr, + const uint8_t ref_poc[7], + refmvs_temporal_block *rp, + const uint8_t ref_ref_poc[7][7], + /*const*/ refmvs_temporal_block *const rp_ref[7], + int n_tile_threads, int n_frame_threads); + +// cache the current tile/sbrow (or frame/sbrow)'s projectable motion vectors +// into buffers for use in future frame's temporal MV prediction +static inline void dav1d_refmvs_save_tmvs(const Dav1dRefmvsDSPContext *const dsp, + const refmvs_tile *const rt, + const int col_start8, int col_end8, + const int row_start8, int row_end8) +{ + const refmvs_frame *const rf = rt->rf; + + assert(row_start8 >= 0); + assert((unsigned) (row_end8 - row_start8) <= 16U); + row_end8 = imin(row_end8, rf->ih8); + col_end8 = imin(col_end8, rf->iw8); + + const ptrdiff_t stride = rf->rp_stride; + const uint8_t *const ref_sign = rf->mfmv_sign; + refmvs_temporal_block *rp = &rf->rp[row_start8 * stride]; + + dsp->save_tmvs(rp, stride, rt->r + 6, ref_sign, + col_end8, row_end8, col_start8, row_start8); +} + +// initialize tile boundaries and refmvs_block pointers for one tile/sbrow +void dav1d_refmvs_tile_sbrow_init(refmvs_tile *rt, const refmvs_frame *rf, + int tile_col_start4, int tile_col_end4, + int tile_row_start4, int tile_row_end4, + int sby, int tile_row_idx, int pass); + +// call for each block +void dav1d_refmvs_find(const refmvs_tile *rt, + refmvs_candidate mvstack[8], int *cnt, + int *ctx, const refmvs_refpair ref, enum BlockSize bs, + enum EdgeFlags edge_flags, int by4, int bx4); + +void dav1d_refmvs_dsp_init(Dav1dRefmvsDSPContext *dsp); +void dav1d_refmvs_dsp_init_arm(Dav1dRefmvsDSPContext *dsp); +void dav1d_refmvs_dsp_init_loongarch(Dav1dRefmvsDSPContext *dsp); +void dav1d_refmvs_dsp_init_x86(Dav1dRefmvsDSPContext *dsp); + +#endif /* DAV1D_SRC_REF_MVS_H */ diff --git a/3rdparty/dav1d/src/scan.c b/3rdparty/dav1d/src/scan.c new file mode 100644 index 0000000..f07eddd --- /dev/null +++ b/3rdparty/dav1d/src/scan.c @@ -0,0 +1,375 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include "common/attributes.h" +#include "common/intops.h" + +#include "src/scan.h" +#include "src/thread.h" + +static const uint16_t ALIGN(scan_4x4[], 32) = { + 0, 4, 1, 2, + 5, 8, 12, 9, + 6, 3, 7, 10, + 13, 14, 11, 15, +}; + +static const uint16_t ALIGN(scan_4x8[], 32) = { + 0, 8, 1, 16, + 9, 2, 24, 17, + 10, 3, 25, 18, + 11, 4, 26, 19, + 12, 5, 27, 20, + 13, 6, 28, 21, + 14, 7, 29, 22, + 15, 30, 23, 31, +}; + +static const uint16_t ALIGN(scan_4x16[], 32) = { + 0, 16, 1, 32, + 17, 2, 48, 33, + 18, 3, 49, 34, + 19, 4, 50, 35, + 20, 5, 51, 36, + 21, 6, 52, 37, + 22, 7, 53, 38, + 23, 8, 54, 39, + 24, 9, 55, 40, + 25, 10, 56, 41, + 26, 11, 57, 42, + 27, 12, 58, 43, + 28, 13, 59, 44, + 29, 14, 60, 45, + 30, 15, 61, 46, + 31, 62, 47, 63, +}; + +static const uint16_t ALIGN(scan_8x4[], 32) = { + 0, 1, 4, 2, 5, 8, 3, 6, + 9, 12, 7, 10, 13, 16, 11, 14, + 17, 20, 15, 18, 21, 24, 19, 22, + 25, 28, 23, 26, 29, 27, 30, 31, +}; + +static const uint16_t ALIGN(scan_8x8[], 32) = { + 0, 8, 1, 2, 9, 16, 24, 17, + 10, 3, 4, 11, 18, 25, 32, 40, + 33, 26, 19, 12, 5, 6, 13, 20, + 27, 34, 41, 48, 56, 49, 42, 35, + 28, 21, 14, 7, 15, 22, 29, 36, + 43, 50, 57, 58, 51, 44, 37, 30, + 23, 31, 38, 45, 52, 59, 60, 53, + 46, 39, 47, 54, 61, 62, 55, 63, +}; + +static const uint16_t ALIGN(scan_8x16[], 32) = { + 0, 16, 1, 32, 17, 2, 48, 33, + 18, 3, 64, 49, 34, 19, 4, 80, + 65, 50, 35, 20, 5, 96, 81, 66, + 51, 36, 21, 6, 112, 97, 82, 67, + 52, 37, 22, 7, 113, 98, 83, 68, + 53, 38, 23, 8, 114, 99, 84, 69, + 54, 39, 24, 9, 115, 100, 85, 70, + 55, 40, 25, 10, 116, 101, 86, 71, + 56, 41, 26, 11, 117, 102, 87, 72, + 57, 42, 27, 12, 118, 103, 88, 73, + 58, 43, 28, 13, 119, 104, 89, 74, + 59, 44, 29, 14, 120, 105, 90, 75, + 60, 45, 30, 15, 121, 106, 91, 76, + 61, 46, 31, 122, 107, 92, 77, 62, + 47, 123, 108, 93, 78, 63, 124, 109, + 94, 79, 125, 110, 95, 126, 111, 127, +}; + +static const uint16_t ALIGN(scan_8x32[], 32) = { + 0, 32, 1, 64, 33, 2, 96, 65, + 34, 3, 128, 97, 66, 35, 4, 160, + 129, 98, 67, 36, 5, 192, 161, 130, + 99, 68, 37, 6, 224, 193, 162, 131, + 100, 69, 38, 7, 225, 194, 163, 132, + 101, 70, 39, 8, 226, 195, 164, 133, + 102, 71, 40, 9, 227, 196, 165, 134, + 103, 72, 41, 10, 228, 197, 166, 135, + 104, 73, 42, 11, 229, 198, 167, 136, + 105, 74, 43, 12, 230, 199, 168, 137, + 106, 75, 44, 13, 231, 200, 169, 138, + 107, 76, 45, 14, 232, 201, 170, 139, + 108, 77, 46, 15, 233, 202, 171, 140, + 109, 78, 47, 16, 234, 203, 172, 141, + 110, 79, 48, 17, 235, 204, 173, 142, + 111, 80, 49, 18, 236, 205, 174, 143, + 112, 81, 50, 19, 237, 206, 175, 144, + 113, 82, 51, 20, 238, 207, 176, 145, + 114, 83, 52, 21, 239, 208, 177, 146, + 115, 84, 53, 22, 240, 209, 178, 147, + 116, 85, 54, 23, 241, 210, 179, 148, + 117, 86, 55, 24, 242, 211, 180, 149, + 118, 87, 56, 25, 243, 212, 181, 150, + 119, 88, 57, 26, 244, 213, 182, 151, + 120, 89, 58, 27, 245, 214, 183, 152, + 121, 90, 59, 28, 246, 215, 184, 153, + 122, 91, 60, 29, 247, 216, 185, 154, + 123, 92, 61, 30, 248, 217, 186, 155, + 124, 93, 62, 31, 249, 218, 187, 156, + 125, 94, 63, 250, 219, 188, 157, 126, + 95, 251, 220, 189, 158, 127, 252, 221, + 190, 159, 253, 222, 191, 254, 223, 255, +}; + +static const uint16_t ALIGN(scan_16x4[], 32) = { + 0, 1, 4, 2, 5, 8, 3, 6, 9, 12, 7, 10, 13, 16, 11, 14, + 17, 20, 15, 18, 21, 24, 19, 22, 25, 28, 23, 26, 29, 32, 27, 30, + 33, 36, 31, 34, 37, 40, 35, 38, 41, 44, 39, 42, 45, 48, 43, 46, + 49, 52, 47, 50, 53, 56, 51, 54, 57, 60, 55, 58, 61, 59, 62, 63, +}; + +static const uint16_t ALIGN(scan_16x8[], 32) = { + 0, 1, 8, 2, 9, 16, 3, 10, 17, 24, 4, 11, 18, 25, 32, 5, + 12, 19, 26, 33, 40, 6, 13, 20, 27, 34, 41, 48, 7, 14, 21, 28, + 35, 42, 49, 56, 15, 22, 29, 36, 43, 50, 57, 64, 23, 30, 37, 44, + 51, 58, 65, 72, 31, 38, 45, 52, 59, 66, 73, 80, 39, 46, 53, 60, + 67, 74, 81, 88, 47, 54, 61, 68, 75, 82, 89, 96, 55, 62, 69, 76, + 83, 90, 97, 104, 63, 70, 77, 84, 91, 98, 105, 112, 71, 78, 85, 92, + 99, 106, 113, 120, 79, 86, 93, 100, 107, 114, 121, 87, 94, 101, 108, 115, + 122, 95, 102, 109, 116, 123, 103, 110, 117, 124, 111, 118, 125, 119, 126, 127, +}; + +static const uint16_t ALIGN(scan_16x16[], 32) = { + 0, 16, 1, 2, 17, 32, 48, 33, 18, 3, 4, 19, 34, 49, 64, 80, + 65, 50, 35, 20, 5, 6, 21, 36, 51, 66, 81, 96, 112, 97, 82, 67, + 52, 37, 22, 7, 8, 23, 38, 53, 68, 83, 98, 113, 128, 144, 129, 114, + 99, 84, 69, 54, 39, 24, 9, 10, 25, 40, 55, 70, 85, 100, 115, 130, + 145, 160, 176, 161, 146, 131, 116, 101, 86, 71, 56, 41, 26, 11, 12, 27, + 42, 57, 72, 87, 102, 117, 132, 147, 162, 177, 192, 208, 193, 178, 163, 148, + 133, 118, 103, 88, 73, 58, 43, 28, 13, 14, 29, 44, 59, 74, 89, 104, + 119, 134, 149, 164, 179, 194, 209, 224, 240, 225, 210, 195, 180, 165, 150, 135, + 120, 105, 90, 75, 60, 45, 30, 15, 31, 46, 61, 76, 91, 106, 121, 136, + 151, 166, 181, 196, 211, 226, 241, 242, 227, 212, 197, 182, 167, 152, 137, 122, + 107, 92, 77, 62, 47, 63, 78, 93, 108, 123, 138, 153, 168, 183, 198, 213, + 228, 243, 244, 229, 214, 199, 184, 169, 154, 139, 124, 109, 94, 79, 95, 110, + 125, 140, 155, 170, 185, 200, 215, 230, 245, 246, 231, 216, 201, 186, 171, 156, + 141, 126, 111, 127, 142, 157, 172, 187, 202, 217, 232, 247, 248, 233, 218, 203, + 188, 173, 158, 143, 159, 174, 189, 204, 219, 234, 249, 250, 235, 220, 205, 190, + 175, 191, 206, 221, 236, 251, 252, 237, 222, 207, 223, 238, 253, 254, 239, 255, +}; + +static const uint16_t ALIGN(scan_16x32[], 32) = { + 0, 32, 1, 64, 33, 2, 96, 65, 34, 3, 128, 97, 66, 35, 4, 160, + 129, 98, 67, 36, 5, 192, 161, 130, 99, 68, 37, 6, 224, 193, 162, 131, + 100, 69, 38, 7, 256, 225, 194, 163, 132, 101, 70, 39, 8, 288, 257, 226, + 195, 164, 133, 102, 71, 40, 9, 320, 289, 258, 227, 196, 165, 134, 103, 72, + 41, 10, 352, 321, 290, 259, 228, 197, 166, 135, 104, 73, 42, 11, 384, 353, + 322, 291, 260, 229, 198, 167, 136, 105, 74, 43, 12, 416, 385, 354, 323, 292, + 261, 230, 199, 168, 137, 106, 75, 44, 13, 448, 417, 386, 355, 324, 293, 262, + 231, 200, 169, 138, 107, 76, 45, 14, 480, 449, 418, 387, 356, 325, 294, 263, + 232, 201, 170, 139, 108, 77, 46, 15, 481, 450, 419, 388, 357, 326, 295, 264, + 233, 202, 171, 140, 109, 78, 47, 16, 482, 451, 420, 389, 358, 327, 296, 265, + 234, 203, 172, 141, 110, 79, 48, 17, 483, 452, 421, 390, 359, 328, 297, 266, + 235, 204, 173, 142, 111, 80, 49, 18, 484, 453, 422, 391, 360, 329, 298, 267, + 236, 205, 174, 143, 112, 81, 50, 19, 485, 454, 423, 392, 361, 330, 299, 268, + 237, 206, 175, 144, 113, 82, 51, 20, 486, 455, 424, 393, 362, 331, 300, 269, + 238, 207, 176, 145, 114, 83, 52, 21, 487, 456, 425, 394, 363, 332, 301, 270, + 239, 208, 177, 146, 115, 84, 53, 22, 488, 457, 426, 395, 364, 333, 302, 271, + 240, 209, 178, 147, 116, 85, 54, 23, 489, 458, 427, 396, 365, 334, 303, 272, + 241, 210, 179, 148, 117, 86, 55, 24, 490, 459, 428, 397, 366, 335, 304, 273, + 242, 211, 180, 149, 118, 87, 56, 25, 491, 460, 429, 398, 367, 336, 305, 274, + 243, 212, 181, 150, 119, 88, 57, 26, 492, 461, 430, 399, 368, 337, 306, 275, + 244, 213, 182, 151, 120, 89, 58, 27, 493, 462, 431, 400, 369, 338, 307, 276, + 245, 214, 183, 152, 121, 90, 59, 28, 494, 463, 432, 401, 370, 339, 308, 277, + 246, 215, 184, 153, 122, 91, 60, 29, 495, 464, 433, 402, 371, 340, 309, 278, + 247, 216, 185, 154, 123, 92, 61, 30, 496, 465, 434, 403, 372, 341, 310, 279, + 248, 217, 186, 155, 124, 93, 62, 31, 497, 466, 435, 404, 373, 342, 311, 280, + 249, 218, 187, 156, 125, 94, 63, 498, 467, 436, 405, 374, 343, 312, 281, 250, + 219, 188, 157, 126, 95, 499, 468, 437, 406, 375, 344, 313, 282, 251, 220, 189, + 158, 127, 500, 469, 438, 407, 376, 345, 314, 283, 252, 221, 190, 159, 501, 470, + 439, 408, 377, 346, 315, 284, 253, 222, 191, 502, 471, 440, 409, 378, 347, 316, + 285, 254, 223, 503, 472, 441, 410, 379, 348, 317, 286, 255, 504, 473, 442, 411, + 380, 349, 318, 287, 505, 474, 443, 412, 381, 350, 319, 506, 475, 444, 413, 382, + 351, 507, 476, 445, 414, 383, 508, 477, 446, 415, 509, 478, 447, 510, 479, 511, +}; + +static const uint16_t ALIGN(scan_32x8[], 32) = { + 0, 1, 8, 2, 9, 16, 3, 10, 17, 24, 4, 11, 18, 25, 32, 5, 12, 19, 26, 33, 40, 6, 13, 20, 27, 34, 41, 48, 7, 14, 21, 28, + 35, 42, 49, 56, 15, 22, 29, 36, 43, 50, 57, 64, 23, 30, 37, 44, 51, 58, 65, 72, 31, 38, 45, 52, 59, 66, 73, 80, 39, 46, 53, 60, + 67, 74, 81, 88, 47, 54, 61, 68, 75, 82, 89, 96, 55, 62, 69, 76, 83, 90, 97, 104, 63, 70, 77, 84, 91, 98, 105, 112, 71, 78, 85, 92, + 99, 106, 113, 120, 79, 86, 93, 100, 107, 114, 121, 128, 87, 94, 101, 108, 115, 122, 129, 136, 95, 102, 109, 116, 123, 130, 137, 144, 103, 110, 117, 124, + 131, 138, 145, 152, 111, 118, 125, 132, 139, 146, 153, 160, 119, 126, 133, 140, 147, 154, 161, 168, 127, 134, 141, 148, 155, 162, 169, 176, 135, 142, 149, 156, + 163, 170, 177, 184, 143, 150, 157, 164, 171, 178, 185, 192, 151, 158, 165, 172, 179, 186, 193, 200, 159, 166, 173, 180, 187, 194, 201, 208, 167, 174, 181, 188, + 195, 202, 209, 216, 175, 182, 189, 196, 203, 210, 217, 224, 183, 190, 197, 204, 211, 218, 225, 232, 191, 198, 205, 212, 219, 226, 233, 240, 199, 206, 213, 220, + 227, 234, 241, 248, 207, 214, 221, 228, 235, 242, 249, 215, 222, 229, 236, 243, 250, 223, 230, 237, 244, 251, 231, 238, 245, 252, 239, 246, 253, 247, 254, 255, +}; + +static const uint16_t ALIGN(scan_32x16[], 32) = { + 0, 1, 16, 2, 17, 32, 3, 18, 33, 48, 4, 19, 34, 49, 64, 5, 20, 35, 50, 65, 80, 6, 21, 36, 51, 66, 81, 96, 7, 22, 37, 52, + 67, 82, 97, 112, 8, 23, 38, 53, 68, 83, 98, 113, 128, 9, 24, 39, 54, 69, 84, 99, 114, 129, 144, 10, 25, 40, 55, 70, 85, 100, 115, 130, + 145, 160, 11, 26, 41, 56, 71, 86, 101, 116, 131, 146, 161, 176, 12, 27, 42, 57, 72, 87, 102, 117, 132, 147, 162, 177, 192, 13, 28, 43, 58, 73, + 88, 103, 118, 133, 148, 163, 178, 193, 208, 14, 29, 44, 59, 74, 89, 104, 119, 134, 149, 164, 179, 194, 209, 224, 15, 30, 45, 60, 75, 90, 105, 120, + 135, 150, 165, 180, 195, 210, 225, 240, 31, 46, 61, 76, 91, 106, 121, 136, 151, 166, 181, 196, 211, 226, 241, 256, 47, 62, 77, 92, 107, 122, 137, 152, + 167, 182, 197, 212, 227, 242, 257, 272, 63, 78, 93, 108, 123, 138, 153, 168, 183, 198, 213, 228, 243, 258, 273, 288, 79, 94, 109, 124, 139, 154, 169, 184, + 199, 214, 229, 244, 259, 274, 289, 304, 95, 110, 125, 140, 155, 170, 185, 200, 215, 230, 245, 260, 275, 290, 305, 320, 111, 126, 141, 156, 171, 186, 201, 216, + 231, 246, 261, 276, 291, 306, 321, 336, 127, 142, 157, 172, 187, 202, 217, 232, 247, 262, 277, 292, 307, 322, 337, 352, 143, 158, 173, 188, 203, 218, 233, 248, + 263, 278, 293, 308, 323, 338, 353, 368, 159, 174, 189, 204, 219, 234, 249, 264, 279, 294, 309, 324, 339, 354, 369, 384, 175, 190, 205, 220, 235, 250, 265, 280, + 295, 310, 325, 340, 355, 370, 385, 400, 191, 206, 221, 236, 251, 266, 281, 296, 311, 326, 341, 356, 371, 386, 401, 416, 207, 222, 237, 252, 267, 282, 297, 312, + 327, 342, 357, 372, 387, 402, 417, 432, 223, 238, 253, 268, 283, 298, 313, 328, 343, 358, 373, 388, 403, 418, 433, 448, 239, 254, 269, 284, 299, 314, 329, 344, + 359, 374, 389, 404, 419, 434, 449, 464, 255, 270, 285, 300, 315, 330, 345, 360, 375, 390, 405, 420, 435, 450, 465, 480, 271, 286, 301, 316, 331, 346, 361, 376, + 391, 406, 421, 436, 451, 466, 481, 496, 287, 302, 317, 332, 347, 362, 377, 392, 407, 422, 437, 452, 467, 482, 497, 303, 318, 333, 348, 363, 378, 393, 408, 423, + 438, 453, 468, 483, 498, 319, 334, 349, 364, 379, 394, 409, 424, 439, 454, 469, 484, 499, 335, 350, 365, 380, 395, 410, 425, 440, 455, 470, 485, 500, 351, 366, + 381, 396, 411, 426, 441, 456, 471, 486, 501, 367, 382, 397, 412, 427, 442, 457, 472, 487, 502, 383, 398, 413, 428, 443, 458, 473, 488, 503, 399, 414, 429, 444, + 459, 474, 489, 504, 415, 430, 445, 460, 475, 490, 505, 431, 446, 461, 476, 491, 506, 447, 462, 477, 492, 507, 463, 478, 493, 508, 479, 494, 509, 495, 510, 511, +}; + +static const uint16_t ALIGN(scan_32x32[], 32) = { + 0, 32, 1, 2, 33, 64, 96, 65, 34, 3, 4, 35, 66, 97, 128, 160, 129, 98, 67, 36, 5, 6, 37, 68, 99, 130, 161, 192, 224, 193, 162, 131, + 100, 69, 38, 7, 8, 39, 70, 101, 132, 163, 194, 225, 256, 288, 257, 226, 195, 164, 133, 102, 71, 40, 9, 10, 41, 72, 103, 134, 165, 196, 227, 258, + 289, 320, 352, 321, 290, 259, 228, 197, 166, 135, 104, 73, 42, 11, 12, 43, 74, 105, 136, 167, 198, 229, 260, 291, 322, 353, 384, 416, 385, 354, 323, 292, + 261, 230, 199, 168, 137, 106, 75, 44, 13, 14, 45, 76, 107, 138, 169, 200, 231, 262, 293, 324, 355, 386, 417, 448, 480, 449, 418, 387, 356, 325, 294, 263, + 232, 201, 170, 139, 108, 77, 46, 15, 16, 47, 78, 109, 140, 171, 202, 233, 264, 295, 326, 357, 388, 419, 450, 481, 512, 544, 513, 482, 451, 420, 389, 358, + 327, 296, 265, 234, 203, 172, 141, 110, 79, 48, 17, 18, 49, 80, 111, 142, 173, 204, 235, 266, 297, 328, 359, 390, 421, 452, 483, 514, 545, 576, 608, 577, + 546, 515, 484, 453, 422, 391, 360, 329, 298, 267, 236, 205, 174, 143, 112, 81, 50, 19, 20, 51, 82, 113, 144, 175, 206, 237, 268, 299, 330, 361, 392, 423, + 454, 485, 516, 547, 578, 609, 640, 672, 641, 610, 579, 548, 517, 486, 455, 424, 393, 362, 331, 300, 269, 238, 207, 176, 145, 114, 83, 52, 21, 22, 53, 84, + 115, 146, 177, 208, 239, 270, 301, 332, 363, 394, 425, 456, 487, 518, 549, 580, 611, 642, 673, 704, 736, 705, 674, 643, 612, 581, 550, 519, 488, 457, 426, 395, + 364, 333, 302, 271, 240, 209, 178, 147, 116, 85, 54, 23, 24, 55, 86, 117, 148, 179, 210, 241, 272, 303, 334, 365, 396, 427, 458, 489, 520, 551, 582, 613, + 644, 675, 706, 737, 768, 800, 769, 738, 707, 676, 645, 614, 583, 552, 521, 490, 459, 428, 397, 366, 335, 304, 273, 242, 211, 180, 149, 118, 87, 56, 25, 26, + 57, 88, 119, 150, 181, 212, 243, 274, 305, 336, 367, 398, 429, 460, 491, 522, 553, 584, 615, 646, 677, 708, 739, 770, 801, 832, 864, 833, 802, 771, 740, 709, + 678, 647, 616, 585, 554, 523, 492, 461, 430, 399, 368, 337, 306, 275, 244, 213, 182, 151, 120, 89, 58, 27, 28, 59, 90, 121, 152, 183, 214, 245, 276, 307, + 338, 369, 400, 431, 462, 493, 524, 555, 586, 617, 648, 679, 710, 741, 772, 803, 834, 865, 896, 928, 897, 866, 835, 804, 773, 742, 711, 680, 649, 618, 587, 556, + 525, 494, 463, 432, 401, 370, 339, 308, 277, 246, 215, 184, 153, 122, 91, 60, 29, 30, 61, 92, 123, 154, 185, 216, 247, 278, 309, 340, 371, 402, 433, 464, + 495, 526, 557, 588, 619, 650, 681, 712, 743, 774, 805, 836, 867, 898, 929, 960, 992, 961, 930, 899, 868, 837, 806, 775, 744, 713, 682, 651, 620, 589, 558, 527, + 496, 465, 434, 403, 372, 341, 310, 279, 248, 217, 186, 155, 124, 93, 62, 31, 63, 94, 125, 156, 187, 218, 249, 280, 311, 342, 373, 404, 435, 466, 497, 528, + 559, 590, 621, 652, 683, 714, 745, 776, 807, 838, 869, 900, 931, 962, 993, 994, 963, 932, 901, 870, 839, 808, 777, 746, 715, 684, 653, 622, 591, 560, 529, 498, + 467, 436, 405, 374, 343, 312, 281, 250, 219, 188, 157, 126, 95, 127, 158, 189, 220, 251, 282, 313, 344, 375, 406, 437, 468, 499, 530, 561, 592, 623, 654, 685, + 716, 747, 778, 809, 840, 871, 902, 933, 964, 995, 996, 965, 934, 903, 872, 841, 810, 779, 748, 717, 686, 655, 624, 593, 562, 531, 500, 469, 438, 407, 376, 345, + 314, 283, 252, 221, 190, 159, 191, 222, 253, 284, 315, 346, 377, 408, 439, 470, 501, 532, 563, 594, 625, 656, 687, 718, 749, 780, 811, 842, 873, 904, 935, 966, + 997, 998, 967, 936, 905, 874, 843, 812, 781, 750, 719, 688, 657, 626, 595, 564, 533, 502, 471, 440, 409, 378, 347, 316, 285, 254, 223, 255, 286, 317, 348, 379, + 410, 441, 472, 503, 534, 565, 596, 627, 658, 689, 720, 751, 782, 813, 844, 875, 906, 937, 968, 999, 1000, 969, 938, 907, 876, 845, 814, 783, 752, 721, 690, 659, + 628, 597, 566, 535, 504, 473, 442, 411, 380, 349, 318, 287, 319, 350, 381, 412, 443, 474, 505, 536, 567, 598, 629, 660, 691, 722, 753, 784, 815, 846, 877, 908, + 939, 970, 1001, 1002, 971, 940, 909, 878, 847, 816, 785, 754, 723, 692, 661, 630, 599, 568, 537, 506, 475, 444, 413, 382, 351, 383, 414, 445, 476, 507, 538, 569, + 600, 631, 662, 693, 724, 755, 786, 817, 848, 879, 910, 941, 972, 1003, 1004, 973, 942, 911, 880, 849, 818, 787, 756, 725, 694, 663, 632, 601, 570, 539, 508, 477, + 446, 415, 447, 478, 509, 540, 571, 602, 633, 664, 695, 726, 757, 788, 819, 850, 881, 912, 943, 974, 1005, 1006, 975, 944, 913, 882, 851, 820, 789, 758, 727, 696, + 665, 634, 603, 572, 541, 510, 479, 511, 542, 573, 604, 635, 666, 697, 728, 759, 790, 821, 852, 883, 914, 945, 976, 1007, 1008, 977, 946, 915, 884, 853, 822, 791, + 760, 729, 698, 667, 636, 605, 574, 543, 575, 606, 637, 668, 699, 730, 761, 792, 823, 854, 885, 916, 947, 978, 1009, 1010, 979, 948, 917, 886, 855, 824, 793, 762, + 731, 700, 669, 638, 607, 639, 670, 701, 732, 763, 794, 825, 856, 887, 918, 949, 980, 1011, 1012, 981, 950, 919, 888, 857, 826, 795, 764, 733, 702, 671, 703, 734, + 765, 796, 827, 858, 889, 920, 951, 982, 1013, 1014, 983, 952, 921, 890, 859, 828, 797, 766, 735, 767, 798, 829, 860, 891, 922, 953, 984, 1015, 1016, 985, 954, 923, + 892, 861, 830, 799, 831, 862, 893, 924, 955, 986, 1017, 1018, 987, 956, 925, 894, 863, 895, 926, 957, 988, 1019, 1020, 989, 958, 927, 959, 990, 1021, 1022, 991, 1023, +}; + +const uint16_t *const dav1d_scans[N_RECT_TX_SIZES] = { + [ TX_4X4 ] = scan_4x4, + [ TX_8X8 ] = scan_8x8, + [ TX_16X16] = scan_16x16, + [ TX_32X32] = scan_32x32, + [ TX_64X64] = scan_32x32, + [RTX_4X8 ] = scan_4x8, + [RTX_8X4 ] = scan_8x4, + [RTX_8X16 ] = scan_8x16, + [RTX_16X8 ] = scan_16x8, + [RTX_16X32] = scan_16x32, + [RTX_32X16] = scan_32x16, + [RTX_32X64] = scan_32x32, + [RTX_64X32] = scan_32x32, + [RTX_4X16 ] = scan_4x16, + [RTX_16X4 ] = scan_16x4, + [RTX_8X32 ] = scan_8x32, + [RTX_32X8 ] = scan_32x8, + [RTX_16X64] = scan_16x32, + [RTX_64X16] = scan_32x16, +}; + +static uint8_t last_nonzero_col_from_eob_4x4[16]; +static uint8_t last_nonzero_col_from_eob_8x8[64]; +static uint8_t last_nonzero_col_from_eob_16x16[256]; +static uint8_t last_nonzero_col_from_eob_32x32[1024]; +static uint8_t last_nonzero_col_from_eob_4x8[32]; +static uint8_t last_nonzero_col_from_eob_8x4[32]; +static uint8_t last_nonzero_col_from_eob_8x16[128]; +static uint8_t last_nonzero_col_from_eob_16x8[128]; +static uint8_t last_nonzero_col_from_eob_16x32[512]; +static uint8_t last_nonzero_col_from_eob_32x16[512]; +static uint8_t last_nonzero_col_from_eob_4x16[64]; +static uint8_t last_nonzero_col_from_eob_16x4[64]; +static uint8_t last_nonzero_col_from_eob_8x32[256]; +static uint8_t last_nonzero_col_from_eob_32x8[256]; + +static COLD void init_tbl(uint8_t *const last_nonzero_col_from_eob, + const uint16_t *const scan, const int w, const int h) +{ + int max_col = 0; + for (int y = 0, n = 0; y < h; y++) { + for (int x = 0; x < w; x++, n++) { + const int rc = scan[n]; + const int rcx = rc & (h - 1); + max_col = imax(max_col, rcx); + last_nonzero_col_from_eob[n] = max_col; + } + } +} + +static COLD void scan_init_internal(void) { + init_tbl(last_nonzero_col_from_eob_4x4, scan_4x4, 4, 4); + init_tbl(last_nonzero_col_from_eob_8x8, scan_8x8, 8, 8); + init_tbl(last_nonzero_col_from_eob_16x16, scan_16x16, 16, 16); + init_tbl(last_nonzero_col_from_eob_32x32, scan_32x32, 32, 32); + init_tbl(last_nonzero_col_from_eob_4x8, scan_4x8, 4, 8); + init_tbl(last_nonzero_col_from_eob_8x4, scan_8x4, 8, 4); + init_tbl(last_nonzero_col_from_eob_8x16, scan_8x16, 8, 16); + init_tbl(last_nonzero_col_from_eob_16x8, scan_16x8, 16, 8); + init_tbl(last_nonzero_col_from_eob_16x32, scan_16x32, 16, 32); + init_tbl(last_nonzero_col_from_eob_32x16, scan_32x16, 32, 16); + init_tbl(last_nonzero_col_from_eob_4x16, scan_4x16, 4, 16); + init_tbl(last_nonzero_col_from_eob_16x4, scan_16x4, 16, 4); + init_tbl(last_nonzero_col_from_eob_8x32, scan_8x32, 8, 32); + init_tbl(last_nonzero_col_from_eob_32x8, scan_32x8, 32, 8); +} + +COLD void dav1d_init_last_nonzero_col_from_eob_tables(void) { + static pthread_once_t initted = PTHREAD_ONCE_INIT; + pthread_once(&initted, scan_init_internal); +} + +const uint8_t *const dav1d_last_nonzero_col_from_eob[N_RECT_TX_SIZES] = { + [ TX_4X4 ] = last_nonzero_col_from_eob_4x4, + [ TX_8X8 ] = last_nonzero_col_from_eob_8x8, + [ TX_16X16] = last_nonzero_col_from_eob_16x16, + [ TX_32X32] = last_nonzero_col_from_eob_32x32, + [ TX_64X64] = last_nonzero_col_from_eob_32x32, + [RTX_4X8 ] = last_nonzero_col_from_eob_4x8, + [RTX_8X4 ] = last_nonzero_col_from_eob_8x4, + [RTX_8X16 ] = last_nonzero_col_from_eob_8x16, + [RTX_16X8 ] = last_nonzero_col_from_eob_16x8, + [RTX_16X32] = last_nonzero_col_from_eob_16x32, + [RTX_32X16] = last_nonzero_col_from_eob_32x16, + [RTX_32X64] = last_nonzero_col_from_eob_32x32, + [RTX_64X32] = last_nonzero_col_from_eob_32x32, + [RTX_4X16 ] = last_nonzero_col_from_eob_4x16, + [RTX_16X4 ] = last_nonzero_col_from_eob_16x4, + [RTX_8X32 ] = last_nonzero_col_from_eob_8x32, + [RTX_32X8 ] = last_nonzero_col_from_eob_32x8, + [RTX_16X64] = last_nonzero_col_from_eob_16x32, + [RTX_64X16] = last_nonzero_col_from_eob_32x16, +}; diff --git a/3rdparty/dav1d/src/scan.h b/3rdparty/dav1d/src/scan.h new file mode 100644 index 0000000..2bd0b5b --- /dev/null +++ b/3rdparty/dav1d/src/scan.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_SCAN_H +#define DAV1D_SRC_SCAN_H + +#include + +#include "src/levels.h" + +EXTERN const uint16_t *const dav1d_scans[N_RECT_TX_SIZES]; +EXTERN const uint8_t *const dav1d_last_nonzero_col_from_eob[N_RECT_TX_SIZES]; + +void dav1d_init_last_nonzero_col_from_eob_tables(void); + +#endif /* DAV1D_SRC_SCAN_H */ diff --git a/3rdparty/dav1d/src/tables.c b/3rdparty/dav1d/src/tables.c new file mode 100644 index 0000000..524864c --- /dev/null +++ b/3rdparty/dav1d/src/tables.c @@ -0,0 +1,1020 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/attributes.h" + +#include "src/levels.h" +#include "src/tables.h" + +const uint8_t dav1d_al_part_ctx[2][N_BL_LEVELS][N_PARTITIONS] = { + { + // partitions: + // none, h, v, split, tts, tbs, tls, trs, h4, v4 + { 0x00, 0x00, 0x10, -1, 0x00, 0x10, 0x10, 0x10, -1, -1 }, // bl128 + { 0x10, 0x10, 0x18, -1, 0x10, 0x18, 0x18, 0x18, 0x10, 0x1c }, // bl64 + { 0x18, 0x18, 0x1c, -1, 0x18, 0x1c, 0x1c, 0x1c, 0x18, 0x1e }, // bl32 + { 0x1c, 0x1c, 0x1e, -1, 0x1c, 0x1e, 0x1e, 0x1e, 0x1c, 0x1f }, // bl16 + { 0x1e, 0x1e, 0x1f, 0x1f, -1, -1, -1, -1, -1, -1 }, // bl8 + }, { + { 0x00, 0x10, 0x00, -1, 0x10, 0x10, 0x00, 0x10, -1, -1 }, // bl128 + { 0x10, 0x18, 0x10, -1, 0x18, 0x18, 0x10, 0x18, 0x1c, 0x10 }, // bl64 + { 0x18, 0x1c, 0x18, -1, 0x1c, 0x1c, 0x18, 0x1c, 0x1e, 0x18 }, // bl32 + { 0x1c, 0x1e, 0x1c, -1, 0x1e, 0x1e, 0x1c, 0x1e, 0x1f, 0x1c }, // bl16 + { 0x1e, 0x1f, 0x1e, 0x1f, -1, -1, -1, -1, -1, -1 }, // bl8 + } +}; + +const uint8_t /* enum BlockSize */ + dav1d_block_sizes[N_BL_LEVELS][N_PARTITIONS][2] = +{ + [BL_128X128] = { + [PARTITION_NONE] = { BS_128x128 }, + [PARTITION_H] = { BS_128x64 }, + [PARTITION_V] = { BS_64x128 }, + [PARTITION_T_TOP_SPLIT] = { BS_64x64, BS_128x64 }, + [PARTITION_T_BOTTOM_SPLIT] = { BS_128x64, BS_64x64 }, + [PARTITION_T_LEFT_SPLIT] = { BS_64x64, BS_64x128 }, + [PARTITION_T_RIGHT_SPLIT] = { BS_64x128, BS_64x64 }, + }, [BL_64X64] = { + [PARTITION_NONE] = { BS_64x64 }, + [PARTITION_H] = { BS_64x32 }, + [PARTITION_V] = { BS_32x64 }, + [PARTITION_T_TOP_SPLIT] = { BS_32x32, BS_64x32 }, + [PARTITION_T_BOTTOM_SPLIT] = { BS_64x32, BS_32x32 }, + [PARTITION_T_LEFT_SPLIT] = { BS_32x32, BS_32x64 }, + [PARTITION_T_RIGHT_SPLIT] = { BS_32x64, BS_32x32 }, + [PARTITION_H4] = { BS_64x16 }, + [PARTITION_V4] = { BS_16x64 }, + }, [BL_32X32] = { + [PARTITION_NONE] = { BS_32x32 }, + [PARTITION_H] = { BS_32x16 }, + [PARTITION_V] = { BS_16x32 }, + [PARTITION_T_TOP_SPLIT] = { BS_16x16, BS_32x16 }, + [PARTITION_T_BOTTOM_SPLIT] = { BS_32x16, BS_16x16 }, + [PARTITION_T_LEFT_SPLIT] = { BS_16x16, BS_16x32 }, + [PARTITION_T_RIGHT_SPLIT] = { BS_16x32, BS_16x16 }, + [PARTITION_H4] = { BS_32x8 }, + [PARTITION_V4] = { BS_8x32 }, + }, [BL_16X16] = { + [PARTITION_NONE] = { BS_16x16 }, + [PARTITION_H] = { BS_16x8 }, + [PARTITION_V] = { BS_8x16 }, + [PARTITION_T_TOP_SPLIT] = { BS_8x8, BS_16x8 }, + [PARTITION_T_BOTTOM_SPLIT] = { BS_16x8, BS_8x8 }, + [PARTITION_T_LEFT_SPLIT] = { BS_8x8, BS_8x16 }, + [PARTITION_T_RIGHT_SPLIT] = { BS_8x16, BS_8x8 }, + [PARTITION_H4] = { BS_16x4 }, + [PARTITION_V4] = { BS_4x16 }, + }, [BL_8X8] = { + [PARTITION_NONE] = { BS_8x8 }, + [PARTITION_H] = { BS_8x4 }, + [PARTITION_V] = { BS_4x8 }, + [PARTITION_SPLIT] = { BS_4x4 }, + } +}; + +const uint8_t dav1d_block_dimensions[N_BS_SIZES][4] = { + [BS_128x128] = { 32, 32, 5, 5 }, + [BS_128x64] = { 32, 16, 5, 4 }, + [BS_64x128] = { 16, 32, 4, 5 }, + [BS_64x64] = { 16, 16, 4, 4 }, + [BS_64x32] = { 16, 8, 4, 3 }, + [BS_64x16] = { 16, 4, 4, 2 }, + [BS_32x64] = { 8, 16, 3, 4 }, + [BS_32x32] = { 8, 8, 3, 3 }, + [BS_32x16] = { 8, 4, 3, 2 }, + [BS_32x8] = { 8, 2, 3, 1 }, + [BS_16x64] = { 4, 16, 2, 4 }, + [BS_16x32] = { 4, 8, 2, 3 }, + [BS_16x16] = { 4, 4, 2, 2 }, + [BS_16x8] = { 4, 2, 2, 1 }, + [BS_16x4] = { 4, 1, 2, 0 }, + [BS_8x32] = { 2, 8, 1, 3 }, + [BS_8x16] = { 2, 4, 1, 2 }, + [BS_8x8] = { 2, 2, 1, 1 }, + [BS_8x4] = { 2, 1, 1, 0 }, + [BS_4x16] = { 1, 4, 0, 2 }, + [BS_4x8] = { 1, 2, 0, 1 }, + [BS_4x4] = { 1, 1, 0, 0 }, +}; + +const TxfmInfo dav1d_txfm_dimensions[N_RECT_TX_SIZES] = { + [ TX_4X4] = { .w = 1, .h = 1, .lw = 0, .lh = 0, + .min = 0, .max = 0, .ctx = 0 }, + [ TX_8X8] = { .w = 2, .h = 2, .lw = 1, .lh = 1, + .min = 1, .max = 1, .sub = TX_4X4, .ctx = 1 }, + [ TX_16X16] = { .w = 4, .h = 4, .lw = 2, .lh = 2, + .min = 2, .max = 2, .sub = TX_8X8, .ctx = 2 }, + [ TX_32X32] = { .w = 8, .h = 8, .lw = 3, .lh = 3, + .min = 3, .max = 3, .sub = TX_16X16, .ctx = 3 }, + [ TX_64X64] = { .w = 16, .h = 16, .lw = 4, .lh = 4, + .min = 4, .max = 4, .sub = TX_32X32, .ctx = 4 }, + [RTX_4X8] = { .w = 1, .h = 2, .lw = 0, .lh = 1, + .min = 0, .max = 1, .sub = TX_4X4, .ctx = 1 }, + [RTX_8X4] = { .w = 2, .h = 1, .lw = 1, .lh = 0, + .min = 0, .max = 1, .sub = TX_4X4, .ctx = 1 }, + [RTX_8X16] = { .w = 2, .h = 4, .lw = 1, .lh = 2, + .min = 1, .max = 2, .sub = TX_8X8, .ctx = 2 }, + [RTX_16X8] = { .w = 4, .h = 2, .lw = 2, .lh = 1, + .min = 1, .max = 2, .sub = TX_8X8, .ctx = 2 }, + [RTX_16X32] = { .w = 4, .h = 8, .lw = 2, .lh = 3, + .min = 2, .max = 3, .sub = TX_16X16, .ctx = 3 }, + [RTX_32X16] = { .w = 8, .h = 4, .lw = 3, .lh = 2, + .min = 2, .max = 3, .sub = TX_16X16, .ctx = 3 }, + [RTX_32X64] = { .w = 8, .h = 16, .lw = 3, .lh = 4, + .min = 3, .max = 4, .sub = TX_32X32, .ctx = 4 }, + [RTX_64X32] = { .w = 16, .h = 8, .lw = 4, .lh = 3, + .min = 3, .max = 4, .sub = TX_32X32, .ctx = 4 }, + [RTX_4X16] = { .w = 1, .h = 4, .lw = 0, .lh = 2, + .min = 0, .max = 2, .sub = RTX_4X8, .ctx = 1 }, + [RTX_16X4] = { .w = 4, .h = 1, .lw = 2, .lh = 0, + .min = 0, .max = 2, .sub = RTX_8X4, .ctx = 1 }, + [RTX_8X32] = { .w = 2, .h = 8, .lw = 1, .lh = 3, + .min = 1, .max = 3, .sub = RTX_8X16, .ctx = 2 }, + [RTX_32X8] = { .w = 8, .h = 2, .lw = 3, .lh = 1, + .min = 1, .max = 3, .sub = RTX_16X8, .ctx = 2 }, + [RTX_16X64] = { .w = 4, .h = 16, .lw = 2, .lh = 4, + .min = 2, .max = 4, .sub = RTX_16X32, .ctx = 3 }, + [RTX_64X16] = { .w = 16, .h = 4, .lw = 4, .lh = 2, + .min = 2, .max = 4, .sub = RTX_32X16, .ctx = 3 }, +}; + +const uint8_t /* enum (Rect)TxfmSize */ + dav1d_max_txfm_size_for_bs[N_BS_SIZES][4 /* y, 420, 422, 444 */] = +{ + [BS_128x128] = { TX_64X64, TX_32X32, TX_32X32, TX_32X32 }, + [BS_128x64] = { TX_64X64, TX_32X32, TX_32X32, TX_32X32 }, + [BS_64x128] = { TX_64X64, TX_32X32, 0, TX_32X32 }, + [BS_64x64] = { TX_64X64, TX_32X32, TX_32X32, TX_32X32 }, + [BS_64x32] = { RTX_64X32, RTX_32X16, TX_32X32, TX_32X32 }, + [BS_64x16] = { RTX_64X16, RTX_32X8, RTX_32X16, RTX_32X16 }, + [BS_32x64] = { RTX_32X64, RTX_16X32, 0, TX_32X32 }, + [BS_32x32] = { TX_32X32, TX_16X16, RTX_16X32, TX_32X32 }, + [BS_32x16] = { RTX_32X16, RTX_16X8, TX_16X16, RTX_32X16 }, + [BS_32x8] = { RTX_32X8, RTX_16X4, RTX_16X8, RTX_32X8 }, + [BS_16x64] = { RTX_16X64, RTX_8X32, 0, RTX_16X32 }, + [BS_16x32] = { RTX_16X32, RTX_8X16, 0, RTX_16X32 }, + [BS_16x16] = { TX_16X16, TX_8X8, RTX_8X16, TX_16X16 }, + [BS_16x8] = { RTX_16X8, RTX_8X4, TX_8X8, RTX_16X8 }, + [BS_16x4] = { RTX_16X4, RTX_8X4, RTX_8X4, RTX_16X4 }, + [BS_8x32] = { RTX_8X32, RTX_4X16, 0, RTX_8X32 }, + [BS_8x16] = { RTX_8X16, RTX_4X8, 0, RTX_8X16 }, + [BS_8x8] = { TX_8X8, TX_4X4, RTX_4X8, TX_8X8 }, + [BS_8x4] = { RTX_8X4, TX_4X4, TX_4X4, RTX_8X4 }, + [BS_4x16] = { RTX_4X16, RTX_4X8, 0, RTX_4X16 }, + [BS_4x8] = { RTX_4X8, TX_4X4, 0, RTX_4X8 }, + [BS_4x4] = { TX_4X4, TX_4X4, TX_4X4, TX_4X4 }, +}; + +const uint8_t /* enum TxfmType */ + dav1d_txtp_from_uvmode[N_UV_INTRA_PRED_MODES] = +{ + [DC_PRED] = DCT_DCT, + [VERT_PRED] = ADST_DCT, + [HOR_PRED] = DCT_ADST, + [DIAG_DOWN_LEFT_PRED] = DCT_DCT, + [DIAG_DOWN_RIGHT_PRED] = ADST_ADST, + [VERT_RIGHT_PRED] = ADST_DCT, + [HOR_DOWN_PRED] = DCT_ADST, + [HOR_UP_PRED] = DCT_ADST, + [VERT_LEFT_PRED] = ADST_DCT, + [SMOOTH_PRED] = ADST_ADST, + [SMOOTH_V_PRED] = ADST_DCT, + [SMOOTH_H_PRED] = DCT_ADST, + [PAETH_PRED] = ADST_ADST, +}; + +const uint8_t /* enum InterPredMode */ + dav1d_comp_inter_pred_modes[N_COMP_INTER_PRED_MODES][2] = +{ + [NEARESTMV_NEARESTMV] = { NEARESTMV, NEARESTMV }, + [NEARMV_NEARMV] = { NEARMV, NEARMV }, + [NEWMV_NEWMV] = { NEWMV, NEWMV }, + [GLOBALMV_GLOBALMV] = { GLOBALMV, GLOBALMV }, + [NEWMV_NEARESTMV] = { NEWMV, NEARESTMV }, + [NEWMV_NEARMV] = { NEWMV, NEARMV }, + [NEARESTMV_NEWMV] = { NEARESTMV, NEWMV }, + [NEARMV_NEWMV] = { NEARMV, NEWMV }, +}; + +const uint8_t dav1d_partition_type_count[N_BL_LEVELS] = { + [BL_128X128] = N_PARTITIONS - 3, + [BL_64X64] = N_PARTITIONS - 1, + [BL_32X32] = N_PARTITIONS - 1, + [BL_16X16] = N_PARTITIONS - 1, + [BL_8X8] = N_SUB8X8_PARTITIONS - 1, +}; + +const uint8_t /* enum TxfmType */ dav1d_tx_types_per_set[40] = { + /* Intra2 */ + IDTX, DCT_DCT, ADST_ADST, ADST_DCT, DCT_ADST, + /* Intra1 */ + IDTX, DCT_DCT, V_DCT, H_DCT, ADST_ADST, ADST_DCT, DCT_ADST, + /* Inter2 */ + IDTX, V_DCT, H_DCT, DCT_DCT, ADST_DCT, DCT_ADST, FLIPADST_DCT, + DCT_FLIPADST, ADST_ADST, FLIPADST_FLIPADST, ADST_FLIPADST, FLIPADST_ADST, + /* Inter1 */ + IDTX, V_DCT, H_DCT, V_ADST, H_ADST, V_FLIPADST, H_FLIPADST, + DCT_DCT, ADST_DCT, DCT_ADST, FLIPADST_DCT, DCT_FLIPADST, + ADST_ADST, FLIPADST_FLIPADST, ADST_FLIPADST, FLIPADST_ADST, +}; + +const uint8_t dav1d_ymode_size_context[N_BS_SIZES] = { + [BS_128x128] = 3, + [BS_128x64] = 3, + [BS_64x128] = 3, + [BS_64x64] = 3, + [BS_64x32] = 3, + [BS_64x16] = 2, + [BS_32x64] = 3, + [BS_32x32] = 3, + [BS_32x16] = 2, + [BS_32x8 ] = 1, + [BS_16x64] = 2, + [BS_16x32] = 2, + [BS_16x16] = 2, + [BS_16x8 ] = 1, + [BS_16x4 ] = 0, + [BS_8x32 ] = 1, + [BS_8x16 ] = 1, + [BS_8x8 ] = 1, + [BS_8x4 ] = 0, + [BS_4x16 ] = 0, + [BS_4x8 ] = 0, + [BS_4x4 ] = 0, +}; + +const uint8_t dav1d_lo_ctx_offsets[3][5][5] = { + { /* w == h */ + { 0, 1, 6, 6, 21 }, + { 1, 6, 6, 21, 21 }, + { 6, 6, 21, 21, 21 }, + { 6, 21, 21, 21, 21 }, + { 21, 21, 21, 21, 21 }, + }, { /* w > h */ + { 0, 16, 6, 6, 21 }, + { 16, 16, 6, 21, 21 }, + { 16, 16, 21, 21, 21 }, + { 16, 16, 21, 21, 21 }, + { 16, 16, 21, 21, 21 }, + }, { /* w < h */ + { 0, 11, 11, 11, 11 }, + { 11, 11, 11, 11, 11 }, + { 6, 6, 21, 21, 21 }, + { 6, 21, 21, 21, 21 }, + { 21, 21, 21, 21, 21 }, + }, +}; + +const uint8_t dav1d_skip_ctx[5][5] = { + { 1, 2, 2, 2, 3 }, + { 2, 4, 4, 4, 5 }, + { 2, 4, 4, 4, 5 }, + { 2, 4, 4, 4, 5 }, + { 3, 5, 5, 5, 6 }, +}; + +const uint8_t /* enum TxClass */ dav1d_tx_type_class[N_TX_TYPES_PLUS_LL] = { + [DCT_DCT] = TX_CLASS_2D, + [ADST_DCT] = TX_CLASS_2D, + [DCT_ADST] = TX_CLASS_2D, + [ADST_ADST] = TX_CLASS_2D, + [FLIPADST_DCT] = TX_CLASS_2D, + [DCT_FLIPADST] = TX_CLASS_2D, + [FLIPADST_FLIPADST] = TX_CLASS_2D, + [ADST_FLIPADST] = TX_CLASS_2D, + [FLIPADST_ADST] = TX_CLASS_2D, + [IDTX] = TX_CLASS_2D, + [V_DCT] = TX_CLASS_V, + [H_DCT] = TX_CLASS_H, + [V_ADST] = TX_CLASS_V, + [H_ADST] = TX_CLASS_H, + [V_FLIPADST] = TX_CLASS_V, + [H_FLIPADST] = TX_CLASS_H, + [WHT_WHT] = TX_CLASS_2D, +}; + +const uint8_t /* enum Filter2d */ dav1d_filter_2d[DAV1D_N_FILTERS][DAV1D_N_FILTERS] = { + [DAV1D_FILTER_8TAP_REGULAR] = { + [DAV1D_FILTER_8TAP_REGULAR] = FILTER_2D_8TAP_REGULAR, + [DAV1D_FILTER_8TAP_SHARP] = FILTER_2D_8TAP_REGULAR_SHARP, + [DAV1D_FILTER_8TAP_SMOOTH] = FILTER_2D_8TAP_REGULAR_SMOOTH, + }, [DAV1D_FILTER_8TAP_SHARP] = { + [DAV1D_FILTER_8TAP_REGULAR] = FILTER_2D_8TAP_SHARP_REGULAR, + [DAV1D_FILTER_8TAP_SHARP] = FILTER_2D_8TAP_SHARP, + [DAV1D_FILTER_8TAP_SMOOTH] = FILTER_2D_8TAP_SHARP_SMOOTH, + }, [DAV1D_FILTER_8TAP_SMOOTH] = { + [DAV1D_FILTER_8TAP_REGULAR] = FILTER_2D_8TAP_SMOOTH_REGULAR, + [DAV1D_FILTER_8TAP_SHARP] = FILTER_2D_8TAP_SMOOTH_SHARP, + [DAV1D_FILTER_8TAP_SMOOTH] = FILTER_2D_8TAP_SMOOTH, + }, [DAV1D_FILTER_BILINEAR] = { + [DAV1D_FILTER_BILINEAR] = FILTER_2D_BILINEAR, + } +}; + +const uint8_t /* enum Dav1dFilterMode */ dav1d_filter_dir[N_2D_FILTERS][2] = { + [FILTER_2D_8TAP_REGULAR] = { DAV1D_FILTER_8TAP_REGULAR, DAV1D_FILTER_8TAP_REGULAR }, + [FILTER_2D_8TAP_REGULAR_SMOOTH] = { DAV1D_FILTER_8TAP_SMOOTH, DAV1D_FILTER_8TAP_REGULAR }, + [FILTER_2D_8TAP_REGULAR_SHARP] = { DAV1D_FILTER_8TAP_SHARP, DAV1D_FILTER_8TAP_REGULAR }, + [FILTER_2D_8TAP_SHARP_REGULAR] = { DAV1D_FILTER_8TAP_REGULAR, DAV1D_FILTER_8TAP_SHARP }, + [FILTER_2D_8TAP_SHARP_SMOOTH] = { DAV1D_FILTER_8TAP_SMOOTH, DAV1D_FILTER_8TAP_SHARP }, + [FILTER_2D_8TAP_SHARP] = { DAV1D_FILTER_8TAP_SHARP, DAV1D_FILTER_8TAP_SHARP }, + [FILTER_2D_8TAP_SMOOTH_REGULAR] = { DAV1D_FILTER_8TAP_REGULAR, DAV1D_FILTER_8TAP_SMOOTH }, + [FILTER_2D_8TAP_SMOOTH] = { DAV1D_FILTER_8TAP_SMOOTH, DAV1D_FILTER_8TAP_SMOOTH }, + [FILTER_2D_8TAP_SMOOTH_SHARP] = { DAV1D_FILTER_8TAP_SHARP, DAV1D_FILTER_8TAP_SMOOTH }, + [FILTER_2D_BILINEAR] = { DAV1D_FILTER_BILINEAR, DAV1D_FILTER_BILINEAR }, +}; + +const uint8_t dav1d_filter_mode_to_y_mode[5] = { + DC_PRED, VERT_PRED, HOR_PRED, HOR_DOWN_PRED, DC_PRED +}; + +const uint8_t dav1d_intra_mode_context[N_INTRA_PRED_MODES] = { + [DC_PRED] = 0, + [VERT_PRED] = 1, + [HOR_PRED] = 2, + [DIAG_DOWN_LEFT_PRED] = 3, + [DIAG_DOWN_RIGHT_PRED] = 4, + [VERT_RIGHT_PRED] = 4, + [HOR_DOWN_PRED] = 4, + [HOR_UP_PRED] = 4, + [VERT_LEFT_PRED] = 3, + [SMOOTH_PRED] = 0, + [SMOOTH_V_PRED] = 1, + [SMOOTH_H_PRED] = 2, + [PAETH_PRED] = 0, +}; + +const uint8_t dav1d_wedge_ctx_lut[N_BS_SIZES] = { + [BS_32x32] = 6, + [BS_32x16] = 5, + [BS_32x8] = 8, + [BS_16x32] = 4, + [BS_16x16] = 3, + [BS_16x8] = 2, + [BS_8x32] = 7, + [BS_8x16] = 1, + [BS_8x8] = 0, +}; + +const Dav1dWarpedMotionParams dav1d_default_wm_params = { + .type = DAV1D_WM_TYPE_IDENTITY, + .matrix = { + 0, 0, 1 << 16, + 0, 0, 1 << 16, + }, + .u.p.alpha = 0, + .u.p.beta = 0, + .u.p.gamma = 0, + .u.p.delta = 0, +}; + +const int8_t dav1d_cdef_directions[2 + 8 + 2 /* dir */][2 /* pass */] = { + { 1 * 12 + 0, 2 * 12 + 0 }, // 6 + { 1 * 12 + 0, 2 * 12 - 1 }, // 7 + { -1 * 12 + 1, -2 * 12 + 2 }, // 0 + { 0 * 12 + 1, -1 * 12 + 2 }, // 1 + { 0 * 12 + 1, 0 * 12 + 2 }, // 2 + { 0 * 12 + 1, 1 * 12 + 2 }, // 3 + { 1 * 12 + 1, 2 * 12 + 2 }, // 4 + { 1 * 12 + 0, 2 * 12 + 1 }, // 5 + { 1 * 12 + 0, 2 * 12 + 0 }, // 6 + { 1 * 12 + 0, 2 * 12 - 1 }, // 7 + { -1 * 12 + 1, -2 * 12 + 2 }, // 0 + { 0 * 12 + 1, -1 * 12 + 2 }, // 1 +}; + +const uint16_t ALIGN(dav1d_sgr_params[16][2], 4) = { + { 140, 3236 }, { 112, 2158 }, { 93, 1618 }, { 80, 1438 }, + { 70, 1295 }, { 58, 1177 }, { 47, 1079 }, { 37, 996 }, + { 30, 925 }, { 25, 863 }, { 0, 2589 }, { 0, 1618 }, + { 0, 1177 }, { 0, 925 }, { 56, 0 }, { 22, 0 }, +}; + +ATTR_MCMODEL_SMALL +const uint8_t ALIGN(dav1d_sgr_x_by_x[256], 64) = { + 255, 128, 85, 64, 51, 43, 37, 32, 28, 26, 23, 21, 20, 18, 17, + 16, 15, 14, 13, 13, 12, 12, 11, 11, 10, 10, 9, 9, 9, 9, + 8, 8, 8, 8, 7, 7, 7, 7, 7, 6, 6, 6, 6, 6, 6, + 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 0 +}; + +ATTR_MCMODEL_SMALL +const int8_t ALIGN(dav1d_mc_subpel_filters[6][15][8], 8) = { + [DAV1D_FILTER_8TAP_REGULAR] = { + { 0, 1, -3, 63, 4, -1, 0, 0 }, + { 0, 1, -5, 61, 9, -2, 0, 0 }, + { 0, 1, -6, 58, 14, -4, 1, 0 }, + { 0, 1, -7, 55, 19, -5, 1, 0 }, + { 0, 1, -7, 51, 24, -6, 1, 0 }, + { 0, 1, -8, 47, 29, -6, 1, 0 }, + { 0, 1, -7, 42, 33, -6, 1, 0 }, + { 0, 1, -7, 38, 38, -7, 1, 0 }, + { 0, 1, -6, 33, 42, -7, 1, 0 }, + { 0, 1, -6, 29, 47, -8, 1, 0 }, + { 0, 1, -6, 24, 51, -7, 1, 0 }, + { 0, 1, -5, 19, 55, -7, 1, 0 }, + { 0, 1, -4, 14, 58, -6, 1, 0 }, + { 0, 0, -2, 9, 61, -5, 1, 0 }, + { 0, 0, -1, 4, 63, -3, 1, 0 } + }, [DAV1D_FILTER_8TAP_SMOOTH] = { + { 0, 1, 14, 31, 17, 1, 0, 0 }, + { 0, 0, 13, 31, 18, 2, 0, 0 }, + { 0, 0, 11, 31, 20, 2, 0, 0 }, + { 0, 0, 10, 30, 21, 3, 0, 0 }, + { 0, 0, 9, 29, 22, 4, 0, 0 }, + { 0, 0, 8, 28, 23, 5, 0, 0 }, + { 0, -1, 8, 27, 24, 6, 0, 0 }, + { 0, -1, 7, 26, 26, 7, -1, 0 }, + { 0, 0, 6, 24, 27, 8, -1, 0 }, + { 0, 0, 5, 23, 28, 8, 0, 0 }, + { 0, 0, 4, 22, 29, 9, 0, 0 }, + { 0, 0, 3, 21, 30, 10, 0, 0 }, + { 0, 0, 2, 20, 31, 11, 0, 0 }, + { 0, 0, 2, 18, 31, 13, 0, 0 }, + { 0, 0, 1, 17, 31, 14, 1, 0 } + }, [DAV1D_FILTER_8TAP_SHARP] = { + { -1, 1, -3, 63, 4, -1, 1, 0 }, + { -1, 3, -6, 62, 8, -3, 2, -1 }, + { -1, 4, -9, 60, 13, -5, 3, -1 }, + { -2, 5, -11, 58, 19, -7, 3, -1 }, + { -2, 5, -11, 54, 24, -9, 4, -1 }, + { -2, 5, -12, 50, 30, -10, 4, -1 }, + { -2, 5, -12, 45, 35, -11, 5, -1 }, + { -2, 6, -12, 40, 40, -12, 6, -2 }, + { -1, 5, -11, 35, 45, -12, 5, -2 }, + { -1, 4, -10, 30, 50, -12, 5, -2 }, + { -1, 4, -9, 24, 54, -11, 5, -2 }, + { -1, 3, -7, 19, 58, -11, 5, -2 }, + { -1, 3, -5, 13, 60, -9, 4, -1 }, + { -1, 2, -3, 8, 62, -6, 3, -1 }, + { 0, 1, -1, 4, 63, -3, 1, -1 } + /* width <= 4 */ + }, [3 + DAV1D_FILTER_8TAP_REGULAR] = { + { 0, 0, -2, 63, 4, -1, 0, 0 }, + { 0, 0, -4, 61, 9, -2, 0, 0 }, + { 0, 0, -5, 58, 14, -3, 0, 0 }, + { 0, 0, -6, 55, 19, -4, 0, 0 }, + { 0, 0, -6, 51, 24, -5, 0, 0 }, + { 0, 0, -7, 47, 29, -5, 0, 0 }, + { 0, 0, -6, 42, 33, -5, 0, 0 }, + { 0, 0, -6, 38, 38, -6, 0, 0 }, + { 0, 0, -5, 33, 42, -6, 0, 0 }, + { 0, 0, -5, 29, 47, -7, 0, 0 }, + { 0, 0, -5, 24, 51, -6, 0, 0 }, + { 0, 0, -4, 19, 55, -6, 0, 0 }, + { 0, 0, -3, 14, 58, -5, 0, 0 }, + { 0, 0, -2, 9, 61, -4, 0, 0 }, + { 0, 0, -1, 4, 63, -2, 0, 0 } + }, [3 + DAV1D_FILTER_8TAP_SMOOTH] = { + { 0, 0, 15, 31, 17, 1, 0, 0 }, + { 0, 0, 13, 31, 18, 2, 0, 0 }, + { 0, 0, 11, 31, 20, 2, 0, 0 }, + { 0, 0, 10, 30, 21, 3, 0, 0 }, + { 0, 0, 9, 29, 22, 4, 0, 0 }, + { 0, 0, 8, 28, 23, 5, 0, 0 }, + { 0, 0, 7, 27, 24, 6, 0, 0 }, + { 0, 0, 6, 26, 26, 6, 0, 0 }, + { 0, 0, 6, 24, 27, 7, 0, 0 }, + { 0, 0, 5, 23, 28, 8, 0, 0 }, + { 0, 0, 4, 22, 29, 9, 0, 0 }, + { 0, 0, 3, 21, 30, 10, 0, 0 }, + { 0, 0, 2, 20, 31, 11, 0, 0 }, + { 0, 0, 2, 18, 31, 13, 0, 0 }, + { 0, 0, 1, 17, 31, 15, 0, 0 } + /* Bilin scaled being very rarely used, add a new table entry + * and use the put/prep_8tap_scaled code, thus acting as a + * scaled bilinear filter. */ + }, [5] = { + { 0, 0, 0, 60, 4, 0, 0, 0 }, + { 0, 0, 0, 56, 8, 0, 0, 0 }, + { 0, 0, 0, 52, 12, 0, 0, 0 }, + { 0, 0, 0, 48, 16, 0, 0, 0 }, + { 0, 0, 0, 44, 20, 0, 0, 0 }, + { 0, 0, 0, 40, 24, 0, 0, 0 }, + { 0, 0, 0, 36, 28, 0, 0, 0 }, + { 0, 0, 0, 32, 32, 0, 0, 0 }, + { 0, 0, 0, 28, 36, 0, 0, 0 }, + { 0, 0, 0, 24, 40, 0, 0, 0 }, + { 0, 0, 0, 20, 44, 0, 0, 0 }, + { 0, 0, 0, 16, 48, 0, 0, 0 }, + { 0, 0, 0, 12, 52, 0, 0, 0 }, + { 0, 0, 0, 8, 56, 0, 0, 0 }, + { 0, 0, 0, 4, 60, 0, 0, 0 } + } +}; + +ATTR_MCMODEL_SMALL +const int8_t ALIGN(dav1d_mc_warp_filter[193][8], 8) = { + // [-1, 0) + { 0, 0, 127, 1, 0, 0, 0, 0 }, { 0, -1, 127, 2, 0, 0, 0, 0 }, + { 1, -3, 127, 4, - 1, 0, 0, 0 }, { 1, -4, 126, 6, -2, 1, 0, 0 }, + { 1, -5, 126, 8, - 3, 1, 0, 0 }, { 1, -6, 125, 11, -4, 1, 0, 0 }, + { 1, -7, 124, 13, - 4, 1, 0, 0 }, { 2, -8, 123, 15, -5, 1, 0, 0 }, + { 2, -9, 122, 18, - 6, 1, 0, 0 }, { 2, -10, 121, 20, -6, 1, 0, 0 }, + { 2, -11, 120, 22, - 7, 2, 0, 0 }, { 2, -12, 119, 25, -8, 2, 0, 0 }, + { 3, -13, 117, 27, - 8, 2, 0, 0 }, { 3, -13, 116, 29, -9, 2, 0, 0 }, + { 3, -14, 114, 32, -10, 3, 0, 0 }, { 3, -15, 113, 35, -10, 2, 0, 0 }, + { 3, -15, 111, 37, -11, 3, 0, 0 }, { 3, -16, 109, 40, -11, 3, 0, 0 }, + { 3, -16, 108, 42, -12, 3, 0, 0 }, { 4, -17, 106, 45, -13, 3, 0, 0 }, + { 4, -17, 104, 47, -13, 3, 0, 0 }, { 4, -17, 102, 50, -14, 3, 0, 0 }, + { 4, -17, 100, 52, -14, 3, 0, 0 }, { 4, -18, 98, 55, -15, 4, 0, 0 }, + { 4, -18, 96, 58, -15, 3, 0, 0 }, { 4, -18, 94, 60, -16, 4, 0, 0 }, + { 4, -18, 91, 63, -16, 4, 0, 0 }, { 4, -18, 89, 65, -16, 4, 0, 0 }, + { 4, -18, 87, 68, -17, 4, 0, 0 }, { 4, -18, 85, 70, -17, 4, 0, 0 }, + { 4, -18, 82, 73, -17, 4, 0, 0 }, { 4, -18, 80, 75, -17, 4, 0, 0 }, + { 4, -18, 78, 78, -18, 4, 0, 0 }, { 4, -17, 75, 80, -18, 4, 0, 0 }, + { 4, -17, 73, 82, -18, 4, 0, 0 }, { 4, -17, 70, 85, -18, 4, 0, 0 }, + { 4, -17, 68, 87, -18, 4, 0, 0 }, { 4, -16, 65, 89, -18, 4, 0, 0 }, + { 4, -16, 63, 91, -18, 4, 0, 0 }, { 4, -16, 60, 94, -18, 4, 0, 0 }, + { 3, -15, 58, 96, -18, 4, 0, 0 }, { 4, -15, 55, 98, -18, 4, 0, 0 }, + { 3, -14, 52, 100, -17, 4, 0, 0 }, { 3, -14, 50, 102, -17, 4, 0, 0 }, + { 3, -13, 47, 104, -17, 4, 0, 0 }, { 3, -13, 45, 106, -17, 4, 0, 0 }, + { 3, -12, 42, 108, -16, 3, 0, 0 }, { 3, -11, 40, 109, -16, 3, 0, 0 }, + { 3, -11, 37, 111, -15, 3, 0, 0 }, { 2, -10, 35, 113, -15, 3, 0, 0 }, + { 3, -10, 32, 114, -14, 3, 0, 0 }, { 2, - 9, 29, 116, -13, 3, 0, 0 }, + { 2, -8, 27, 117, -13, 3, 0, 0 }, { 2, - 8, 25, 119, -12, 2, 0, 0 }, + { 2, -7, 22, 120, -11, 2, 0, 0 }, { 1, - 6, 20, 121, -10, 2, 0, 0 }, + { 1, -6, 18, 122, - 9, 2, 0, 0 }, { 1, - 5, 15, 123, - 8, 2, 0, 0 }, + { 1, -4, 13, 124, - 7, 1, 0, 0 }, { 1, - 4, 11, 125, - 6, 1, 0, 0 }, + { 1, -3, 8, 126, - 5, 1, 0, 0 }, { 1, - 2, 6, 126, - 4, 1, 0, 0 }, + { 0, -1, 4, 127, - 3, 1, 0, 0 }, { 0, 0, 2, 127, - 1, 0, 0, 0 }, + // [0, 1) + { 0, 0, 0, 127, 1, 0, 0, 0 }, { 0, 0, -1, 127, 2, 0, 0, 0 }, + { 0, 1, -3, 127, 4, -2, 1, 0 }, { 0, 1, -5, 127, 6, -2, 1, 0 }, + { 0, 2, -6, 126, 8, -3, 1, 0 }, { -1, 2, -7, 126, 11, -4, 2, -1 }, + { -1, 3, -8, 125, 13, -5, 2, -1 }, { -1, 3, -10, 124, 16, -6, 3, -1 }, + { -1, 4, -11, 123, 18, -7, 3, -1 }, { -1, 4, -12, 122, 20, -7, 3, -1 }, + { -1, 4, -13, 121, 23, -8, 3, -1 }, { -2, 5, -14, 120, 25, -9, 4, -1 }, + { -1, 5, -15, 119, 27, -10, 4, -1 }, { -1, 5, -16, 118, 30, -11, 4, -1 }, + { -2, 6, -17, 116, 33, -12, 5, -1 }, { -2, 6, -17, 114, 35, -12, 5, -1 }, + { -2, 6, -18, 113, 38, -13, 5, -1 }, { -2, 7, -19, 111, 41, -14, 6, -2 }, + { -2, 7, -19, 110, 43, -15, 6, -2 }, { -2, 7, -20, 108, 46, -15, 6, -2 }, + { -2, 7, -20, 106, 49, -16, 6, -2 }, { -2, 7, -21, 104, 51, -16, 7, -2 }, + { -2, 7, -21, 102, 54, -17, 7, -2 }, { -2, 8, -21, 100, 56, -18, 7, -2 }, + { -2, 8, -22, 98, 59, -18, 7, -2 }, { -2, 8, -22, 96, 62, -19, 7, -2 }, + { -2, 8, -22, 94, 64, -19, 7, -2 }, { -2, 8, -22, 91, 67, -20, 8, -2 }, + { -2, 8, -22, 89, 69, -20, 8, -2 }, { -2, 8, -22, 87, 72, -21, 8, -2 }, + { -2, 8, -21, 84, 74, -21, 8, -2 }, { -2, 8, -22, 82, 77, -21, 8, -2 }, + { -2, 8, -21, 79, 79, -21, 8, -2 }, { -2, 8, -21, 77, 82, -22, 8, -2 }, + { -2, 8, -21, 74, 84, -21, 8, -2 }, { -2, 8, -21, 72, 87, -22, 8, -2 }, + { -2, 8, -20, 69, 89, -22, 8, -2 }, { -2, 8, -20, 67, 91, -22, 8, -2 }, + { -2, 7, -19, 64, 94, -22, 8, -2 }, { -2, 7, -19, 62, 96, -22, 8, -2 }, + { -2, 7, -18, 59, 98, -22, 8, -2 }, { -2, 7, -18, 56, 100, -21, 8, -2 }, + { -2, 7, -17, 54, 102, -21, 7, -2 }, { -2, 7, -16, 51, 104, -21, 7, -2 }, + { -2, 6, -16, 49, 106, -20, 7, -2 }, { -2, 6, -15, 46, 108, -20, 7, -2 }, + { -2, 6, -15, 43, 110, -19, 7, -2 }, { -2, 6, -14, 41, 111, -19, 7, -2 }, + { -1, 5, -13, 38, 113, -18, 6, -2 }, { -1, 5, -12, 35, 114, -17, 6, -2 }, + { -1, 5, -12, 33, 116, -17, 6, -2 }, { -1, 4, -11, 30, 118, -16, 5, -1 }, + { -1, 4, -10, 27, 119, -15, 5, -1 }, { -1, 4, -9, 25, 120, -14, 5, -2 }, + { -1, 3, -8, 23, 121, -13, 4, -1 }, { -1, 3, -7, 20, 122, -12, 4, -1 }, + { -1, 3, -7, 18, 123, -11, 4, -1 }, { -1, 3, -6, 16, 124, -10, 3, -1 }, + { -1, 2, -5, 13, 125, -8, 3, -1 }, { -1, 2, -4, 11, 126, -7, 2, -1 }, + { 0, 1, -3, 8, 126, -6, 2, 0 }, { 0, 1, -2, 6, 127, -5, 1, 0 }, + { 0, 1, -2, 4, 127, -3, 1, 0 }, { 0, 0, 0, 2, 127, -1, 0, 0 }, + // [1, 2) + { 0, 0, 0, 1, 127, 0, 0, 0 }, { 0, 0, 0, -1, 127, 2, 0, 0 }, + { 0, 0, 1, -3, 127, 4, -1, 0 }, { 0, 0, 1, -4, 126, 6, -2, 1 }, + { 0, 0, 1, -5, 126, 8, -3, 1 }, { 0, 0, 1, -6, 125, 11, -4, 1 }, + { 0, 0, 1, -7, 124, 13, -4, 1 }, { 0, 0, 2, -8, 123, 15, -5, 1 }, + { 0, 0, 2, -9, 122, 18, -6, 1 }, { 0, 0, 2, -10, 121, 20, -6, 1 }, + { 0, 0, 2, -11, 120, 22, -7, 2 }, { 0, 0, 2, -12, 119, 25, -8, 2 }, + { 0, 0, 3, -13, 117, 27, -8, 2 }, { 0, 0, 3, -13, 116, 29, -9, 2 }, + { 0, 0, 3, -14, 114, 32, -10, 3 }, { 0, 0, 3, -15, 113, 35, -10, 2 }, + { 0, 0, 3, -15, 111, 37, -11, 3 }, { 0, 0, 3, -16, 109, 40, -11, 3 }, + { 0, 0, 3, -16, 108, 42, -12, 3 }, { 0, 0, 4, -17, 106, 45, -13, 3 }, + { 0, 0, 4, -17, 104, 47, -13, 3 }, { 0, 0, 4, -17, 102, 50, -14, 3 }, + { 0, 0, 4, -17, 100, 52, -14, 3 }, { 0, 0, 4, -18, 98, 55, -15, 4 }, + { 0, 0, 4, -18, 96, 58, -15, 3 }, { 0, 0, 4, -18, 94, 60, -16, 4 }, + { 0, 0, 4, -18, 91, 63, -16, 4 }, { 0, 0, 4, -18, 89, 65, -16, 4 }, + { 0, 0, 4, -18, 87, 68, -17, 4 }, { 0, 0, 4, -18, 85, 70, -17, 4 }, + { 0, 0, 4, -18, 82, 73, -17, 4 }, { 0, 0, 4, -18, 80, 75, -17, 4 }, + { 0, 0, 4, -18, 78, 78, -18, 4 }, { 0, 0, 4, -17, 75, 80, -18, 4 }, + { 0, 0, 4, -17, 73, 82, -18, 4 }, { 0, 0, 4, -17, 70, 85, -18, 4 }, + { 0, 0, 4, -17, 68, 87, -18, 4 }, { 0, 0, 4, -16, 65, 89, -18, 4 }, + { 0, 0, 4, -16, 63, 91, -18, 4 }, { 0, 0, 4, -16, 60, 94, -18, 4 }, + { 0, 0, 3, -15, 58, 96, -18, 4 }, { 0, 0, 4, -15, 55, 98, -18, 4 }, + { 0, 0, 3, -14, 52, 100, -17, 4 }, { 0, 0, 3, -14, 50, 102, -17, 4 }, + { 0, 0, 3, -13, 47, 104, -17, 4 }, { 0, 0, 3, -13, 45, 106, -17, 4 }, + { 0, 0, 3, -12, 42, 108, -16, 3 }, { 0, 0, 3, -11, 40, 109, -16, 3 }, + { 0, 0, 3, -11, 37, 111, -15, 3 }, { 0, 0, 2, -10, 35, 113, -15, 3 }, + { 0, 0, 3, -10, 32, 114, -14, 3 }, { 0, 0, 2, -9, 29, 116, -13, 3 }, + { 0, 0, 2, -8, 27, 117, -13, 3 }, { 0, 0, 2, -8, 25, 119, -12, 2 }, + { 0, 0, 2, -7, 22, 120, -11, 2 }, { 0, 0, 1, -6, 20, 121, -10, 2 }, + { 0, 0, 1, -6, 18, 122, -9, 2 }, { 0, 0, 1, -5, 15, 123, -8, 2 }, + { 0, 0, 1, -4, 13, 124, -7, 1 }, { 0, 0, 1, -4, 11, 125, -6, 1 }, + { 0, 0, 1, -3, 8, 126, -5, 1 }, { 0, 0, 1, -2, 6, 126, -4, 1 }, + { 0, 0, 0, -1, 4, 127, -3, 1 }, { 0, 0, 0, 0, 2, 127, -1, 0 }, + // dummy (replicate row index 191) + { 0, 0, 0, 0, 2, 127, -1, 0 }, +}; + +ATTR_MCMODEL_SMALL +const int8_t ALIGN(dav1d_resize_filter[64][8], 8) = { + { 0, 0, 0, -128, 0, 0, 0, 0 }, { 0, 0, 1, -128, -2, 1, 0, 0 }, + { 0, -1, 3, -127, -4, 2, -1, 0 }, { 0, -1, 4, -127, -6, 3, -1, 0 }, + { 0, -2, 6, -126, -8, 3, -1, 0 }, { 0, -2, 7, -125, -11, 4, -1, 0 }, + { 1, -2, 8, -125, -13, 5, -2, 0 }, { 1, -3, 9, -124, -15, 6, -2, 0 }, + { 1, -3, 10, -123, -18, 6, -2, 1 }, { 1, -3, 11, -122, -20, 7, -3, 1 }, + { 1, -4, 12, -121, -22, 8, -3, 1 }, { 1, -4, 13, -120, -25, 9, -3, 1 }, + { 1, -4, 14, -118, -28, 9, -3, 1 }, { 1, -4, 15, -117, -30, 10, -4, 1 }, + { 1, -5, 16, -116, -32, 11, -4, 1 }, { 1, -5, 16, -114, -35, 12, -4, 1 }, + { 1, -5, 17, -112, -38, 12, -4, 1 }, { 1, -5, 18, -111, -40, 13, -5, 1 }, + { 1, -5, 18, -109, -43, 14, -5, 1 }, { 1, -6, 19, -107, -45, 14, -5, 1 }, + { 1, -6, 19, -105, -48, 15, -5, 1 }, { 1, -6, 19, -103, -51, 16, -5, 1 }, + { 1, -6, 20, -101, -53, 16, -6, 1 }, { 1, -6, 20, -99, -56, 17, -6, 1 }, + { 1, -6, 20, -97, -58, 17, -6, 1 }, { 1, -6, 20, -95, -61, 18, -6, 1 }, + { 2, -7, 20, -93, -64, 18, -6, 2 }, { 2, -7, 20, -91, -66, 19, -6, 1 }, + { 2, -7, 20, -88, -69, 19, -6, 1 }, { 2, -7, 20, -86, -71, 19, -6, 1 }, + { 2, -7, 20, -84, -74, 20, -7, 2 }, { 2, -7, 20, -81, -76, 20, -7, 1 }, + { 2, -7, 20, -79, -79, 20, -7, 2 }, { 1, -7, 20, -76, -81, 20, -7, 2 }, + { 2, -7, 20, -74, -84, 20, -7, 2 }, { 1, -6, 19, -71, -86, 20, -7, 2 }, + { 1, -6, 19, -69, -88, 20, -7, 2 }, { 1, -6, 19, -66, -91, 20, -7, 2 }, + { 2, -6, 18, -64, -93, 20, -7, 2 }, { 1, -6, 18, -61, -95, 20, -6, 1 }, + { 1, -6, 17, -58, -97, 20, -6, 1 }, { 1, -6, 17, -56, -99, 20, -6, 1 }, + { 1, -6, 16, -53, -101, 20, -6, 1 }, { 1, -5, 16, -51, -103, 19, -6, 1 }, + { 1, -5, 15, -48, -105, 19, -6, 1 }, { 1, -5, 14, -45, -107, 19, -6, 1 }, + { 1, -5, 14, -43, -109, 18, -5, 1 }, { 1, -5, 13, -40, -111, 18, -5, 1 }, + { 1, -4, 12, -38, -112, 17, -5, 1 }, { 1, -4, 12, -35, -114, 16, -5, 1 }, + { 1, -4, 11, -32, -116, 16, -5, 1 }, { 1, -4, 10, -30, -117, 15, -4, 1 }, + { 1, -3, 9, -28, -118, 14, -4, 1 }, { 1, -3, 9, -25, -120, 13, -4, 1 }, + { 1, -3, 8, -22, -121, 12, -4, 1 }, { 1, -3, 7, -20, -122, 11, -3, 1 }, + { 1, -2, 6, -18, -123, 10, -3, 1 }, { 0, -2, 6, -15, -124, 9, -3, 1 }, + { 0, -2, 5, -13, -125, 8, -2, 1 }, { 0, -1, 4, -11, -125, 7, -2, 0 }, + { 0, -1, 3, -8, -126, 6, -2, 0 }, { 0, -1, 3, -6, -127, 4, -1, 0 }, + { 0, -1, 2, -4, -127, 3, -1, 0 }, { 0, 0, 1, -2, -128, 1, 0, 0 }, +}; + +const uint8_t ALIGN(dav1d_sm_weights[128], 16) = { + // Unused, because we always offset by bs, which is at least 2. + 0, 0, + // bs = 2 + 255, 128, + // bs = 4 + 255, 149, 85, 64, + // bs = 8 + 255, 197, 146, 105, 73, 50, 37, 32, + // bs = 16 + 255, 225, 196, 170, 145, 123, 102, 84, + 68, 54, 43, 33, 26, 20, 17, 16, + // bs = 32 + 255, 240, 225, 210, 196, 182, 169, 157, + 145, 133, 122, 111, 101, 92, 83, 74, + 66, 59, 52, 45, 39, 34, 29, 25, + 21, 17, 14, 12, 10, 9, 8, 8, + // bs = 64 + 255, 248, 240, 233, 225, 218, 210, 203, + 196, 189, 182, 176, 169, 163, 156, 150, + 144, 138, 133, 127, 121, 116, 111, 106, + 101, 96, 91, 86, 82, 77, 73, 69, + 65, 61, 57, 54, 50, 47, 44, 41, + 38, 35, 32, 29, 27, 25, 22, 20, + 18, 16, 15, 13, 12, 10, 9, 8, + 7, 6, 6, 5, 5, 4, 4, 4 +}; + +ATTR_MCMODEL_SMALL +const uint16_t dav1d_dr_intra_derivative[44] = { + // Values that are 0 will never be used + 0, // Angles: + 1023, 0, // 3, 93, 183 + 547, // 6, 96, 186 + 372, 0, 0, // 9, 99, 189 + 273, // 14, 104, 194 + 215, 0, // 17, 107, 197 + 178, // 20, 110, 200 + 151, 0, // 23, 113, 203 (113 & 203 are base angles) + 132, // 26, 116, 206 + 116, 0, // 29, 119, 209 + 102, 0, // 32, 122, 212 + 90, // 36, 126, 216 + 80, 0, // 39, 129, 219 + 71, // 42, 132, 222 + 64, 0, // 45, 135, 225 (45 & 135 are base angles) + 57, // 48, 138, 228 + 51, 0, // 51, 141, 231 + 45, 0, // 54, 144, 234 + 40, // 58, 148, 238 + 35, 0, // 61, 151, 241 + 31, // 64, 154, 244 + 27, 0, // 67, 157, 247 (67 & 157 are base angles) + 23, // 70, 160, 250 + 19, 0, // 73, 163, 253 + 15, 0, // 76, 166, 256 + 11, 0, // 81, 171, 261 + 7, // 84, 174, 264 + 3 // 87, 177, 267 +}; + +#if ARCH_X86 +#define F(idx, f0, f1, f2, f3, f4, f5, f6) \ + [2*idx+0] = f0, [2*idx+1] = f1, \ + [2*idx+16] = f2, [2*idx+17] = f3, \ + [2*idx+32] = f4, [2*idx+33] = f5, \ + [2*idx+48] = f6 +#else +#define F(idx, f0, f1, f2, f3, f4, f5, f6) \ + [1*idx+0] = f0, [1*idx+8] = f1, \ + [1*idx+16] = f2, [1*idx+24] = f3, \ + [1*idx+32] = f4, [1*idx+40] = f5, \ + [1*idx+48] = f6 +#endif +ATTR_MCMODEL_SMALL +const int8_t ALIGN(dav1d_filter_intra_taps[5][64], 64) = { + { + F( 0, -6, 10, 0, 0, 0, 12, 0 ), + F( 1, -5, 2, 10, 0, 0, 9, 0 ), + F( 2, -3, 1, 1, 10, 0, 7, 0 ), + F( 3, -3, 1, 1, 2, 10, 5, 0 ), + F( 4, -4, 6, 0, 0, 0, 2, 12 ), + F( 5, -3, 2, 6, 0, 0, 2, 9 ), + F( 6, -3, 2, 2, 6, 0, 2, 7 ), + F( 7, -3, 1, 2, 2, 6, 3, 5 ), + }, { + F( 0, -10, 16, 0, 0, 0, 10, 0 ), + F( 1, -6, 0, 16, 0, 0, 6, 0 ), + F( 2, -4, 0, 0, 16, 0, 4, 0 ), + F( 3, -2, 0, 0, 0, 16, 2, 0 ), + F( 4, -10, 16, 0, 0, 0, 0, 10 ), + F( 5, -6, 0, 16, 0, 0, 0, 6 ), + F( 6, -4, 0, 0, 16, 0, 0, 4 ), + F( 7, -2, 0, 0, 0, 16, 0, 2 ), + }, { + F( 0, -8, 8, 0, 0, 0, 16, 0 ), + F( 1, -8, 0, 8, 0, 0, 16, 0 ), + F( 2, -8, 0, 0, 8, 0, 16, 0 ), + F( 3, -8, 0, 0, 0, 8, 16, 0 ), + F( 4, -4, 4, 0, 0, 0, 0, 16 ), + F( 5, -4, 0, 4, 0, 0, 0, 16 ), + F( 6, -4, 0, 0, 4, 0, 0, 16 ), + F( 7, -4, 0, 0, 0, 4, 0, 16 ), + }, { + F( 0, -2, 8, 0, 0, 0, 10, 0 ), + F( 1, -1, 3, 8, 0, 0, 6, 0 ), + F( 2, -1, 2, 3, 8, 0, 4, 0 ), + F( 3, 0, 1, 2, 3, 8, 2, 0 ), + F( 4, -1, 4, 0, 0, 0, 3, 10 ), + F( 5, -1, 3, 4, 0, 0, 4, 6 ), + F( 6, -1, 2, 3, 4, 0, 4, 4 ), + F( 7, -1, 2, 2, 3, 4, 3, 3 ), + }, { + F( 0, -12, 14, 0, 0, 0, 14, 0 ), + F( 1, -10, 0, 14, 0, 0, 12, 0 ), + F( 2, -9, 0, 0, 14, 0, 11, 0 ), + F( 3, -8, 0, 0, 0, 14, 10, 0 ), + F( 4, -10, 12, 0, 0, 0, 0, 14 ), + F( 5, -9, 1, 12, 0, 0, 0, 12 ), + F( 6, -8, 0, 0, 12, 0, 1, 11 ), + F( 7, -7, 0, 0, 1, 12, 1, 9 ), + } +}; + +const uint8_t ALIGN(dav1d_obmc_masks[64], 16) = { + /* Unused */ + 0, 0, + /* 2 */ + 19, 0, + /* 4 */ + 25, 14, 5, 0, + /* 8 */ + 28, 22, 16, 11, 7, 3, 0, 0, + /* 16 */ + 30, 27, 24, 21, 18, 15, 12, 10, 8, 6, 4, 3, 0, 0, 0, 0, + /* 32 */ + 31, 29, 28, 26, 24, 23, 21, 20, 19, 17, 16, 14, 13, 12, 11, 9, + 8, 7, 6, 5, 4, 4, 3, 2, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// Taken from the spec. Range is [-2048, 2047], mean is 0 and stddev is 512 +ATTR_MCMODEL_SMALL +const int16_t dav1d_gaussian_sequence[2048] = { + 56, 568, -180, 172, 124, -84, 172, -64, -900, 24, 820, + 224, 1248, 996, 272, -8, -916, -388, -732, -104, -188, 800, + 112, -652, -320, -376, 140, -252, 492, -168, 44, -788, 588, + -584, 500, -228, 12, 680, 272, -476, 972, -100, 652, 368, + 432, -196, -720, -192, 1000, -332, 652, -136, -552, -604, -4, + 192, -220, -136, 1000, -52, 372, -96, -624, 124, -24, 396, + 540, -12, -104, 640, 464, 244, -208, -84, 368, -528, -740, + 248, -968, -848, 608, 376, -60, -292, -40, -156, 252, -292, + 248, 224, -280, 400, -244, 244, -60, 76, -80, 212, 532, + 340, 128, -36, 824, -352, -60, -264, -96, -612, 416, -704, + 220, -204, 640, -160, 1220, -408, 900, 336, 20, -336, -96, + -792, 304, 48, -28, -1232, -1172, -448, 104, -292, -520, 244, + 60, -948, 0, -708, 268, 108, 356, -548, 488, -344, -136, + 488, -196, -224, 656, -236, -1128, 60, 4, 140, 276, -676, + -376, 168, -108, 464, 8, 564, 64, 240, 308, -300, -400, + -456, -136, 56, 120, -408, -116, 436, 504, -232, 328, 844, + -164, -84, 784, -168, 232, -224, 348, -376, 128, 568, 96, + -1244, -288, 276, 848, 832, -360, 656, 464, -384, -332, -356, + 728, -388, 160, -192, 468, 296, 224, 140, -776, -100, 280, + 4, 196, 44, -36, -648, 932, 16, 1428, 28, 528, 808, + 772, 20, 268, 88, -332, -284, 124, -384, -448, 208, -228, + -1044, -328, 660, 380, -148, -300, 588, 240, 540, 28, 136, + -88, -436, 256, 296, -1000, 1400, 0, -48, 1056, -136, 264, + -528, -1108, 632, -484, -592, -344, 796, 124, -668, -768, 388, + 1296, -232, -188, -200, -288, -4, 308, 100, -168, 256, -500, + 204, -508, 648, -136, 372, -272, -120, -1004, -552, -548, -384, + 548, -296, 428, -108, -8, -912, -324, -224, -88, -112, -220, + -100, 996, -796, 548, 360, -216, 180, 428, -200, -212, 148, + 96, 148, 284, 216, -412, -320, 120, -300, -384, -604, -572, + -332, -8, -180, -176, 696, 116, -88, 628, 76, 44, -516, + 240, -208, -40, 100, -592, 344, -308, -452, -228, 20, 916, + -1752, -136, -340, -804, 140, 40, 512, 340, 248, 184, -492, + 896, -156, 932, -628, 328, -688, -448, -616, -752, -100, 560, + -1020, 180, -800, -64, 76, 576, 1068, 396, 660, 552, -108, + -28, 320, -628, 312, -92, -92, -472, 268, 16, 560, 516, + -672, -52, 492, -100, 260, 384, 284, 292, 304, -148, 88, + -152, 1012, 1064, -228, 164, -376, -684, 592, -392, 156, 196, + -524, -64, -884, 160, -176, 636, 648, 404, -396, -436, 864, + 424, -728, 988, -604, 904, -592, 296, -224, 536, -176, -920, + 436, -48, 1176, -884, 416, -776, -824, -884, 524, -548, -564, + -68, -164, -96, 692, 364, -692, -1012, -68, 260, -480, 876, + -1116, 452, -332, -352, 892, -1088, 1220, -676, 12, -292, 244, + 496, 372, -32, 280, 200, 112, -440, -96, 24, -644, -184, + 56, -432, 224, -980, 272, -260, 144, -436, 420, 356, 364, + -528, 76, 172, -744, -368, 404, -752, -416, 684, -688, 72, + 540, 416, 92, 444, 480, -72, -1416, 164, -1172, -68, 24, + 424, 264, 1040, 128, -912, -524, -356, 64, 876, -12, 4, + -88, 532, 272, -524, 320, 276, -508, 940, 24, -400, -120, + 756, 60, 236, -412, 100, 376, -484, 400, -100, -740, -108, + -260, 328, -268, 224, -200, -416, 184, -604, -564, -20, 296, + 60, 892, -888, 60, 164, 68, -760, 216, -296, 904, -336, + -28, 404, -356, -568, -208, -1480, -512, 296, 328, -360, -164, + -1560, -776, 1156, -428, 164, -504, -112, 120, -216, -148, -264, + 308, 32, 64, -72, 72, 116, 176, -64, -272, 460, -536, + -784, -280, 348, 108, -752, -132, 524, -540, -776, 116, -296, + -1196, -288, -560, 1040, -472, 116, -848, -1116, 116, 636, 696, + 284, -176, 1016, 204, -864, -648, -248, 356, 972, -584, -204, + 264, 880, 528, -24, -184, 116, 448, -144, 828, 524, 212, + -212, 52, 12, 200, 268, -488, -404, -880, 824, -672, -40, + 908, -248, 500, 716, -576, 492, -576, 16, 720, -108, 384, + 124, 344, 280, 576, -500, 252, 104, -308, 196, -188, -8, + 1268, 296, 1032, -1196, 436, 316, 372, -432, -200, -660, 704, + -224, 596, -132, 268, 32, -452, 884, 104, -1008, 424, -1348, + -280, 4, -1168, 368, 476, 696, 300, -8, 24, 180, -592, + -196, 388, 304, 500, 724, -160, 244, -84, 272, -256, -420, + 320, 208, -144, -156, 156, 364, 452, 28, 540, 316, 220, + -644, -248, 464, 72, 360, 32, -388, 496, -680, -48, 208, + -116, -408, 60, -604, -392, 548, -840, 784, -460, 656, -544, + -388, -264, 908, -800, -628, -612, -568, 572, -220, 164, 288, + -16, -308, 308, -112, -636, -760, 280, -668, 432, 364, 240, + -196, 604, 340, 384, 196, 592, -44, -500, 432, -580, -132, + 636, -76, 392, 4, -412, 540, 508, 328, -356, -36, 16, + -220, -64, -248, -60, 24, -192, 368, 1040, 92, -24, -1044, + -32, 40, 104, 148, 192, -136, -520, 56, -816, -224, 732, + 392, 356, 212, -80, -424, -1008, -324, 588, -1496, 576, 460, + -816, -848, 56, -580, -92, -1372, -112, -496, 200, 364, 52, + -140, 48, -48, -60, 84, 72, 40, 132, -356, -268, -104, + -284, -404, 732, -520, 164, -304, -540, 120, 328, -76, -460, + 756, 388, 588, 236, -436, -72, -176, -404, -316, -148, 716, + -604, 404, -72, -88, -888, -68, 944, 88, -220, -344, 960, + 472, 460, -232, 704, 120, 832, -228, 692, -508, 132, -476, + 844, -748, -364, -44, 1116, -1104, -1056, 76, 428, 552, -692, + 60, 356, 96, -384, -188, -612, -576, 736, 508, 892, 352, + -1132, 504, -24, -352, 324, 332, -600, -312, 292, 508, -144, + -8, 484, 48, 284, -260, -240, 256, -100, -292, -204, -44, + 472, -204, 908, -188, -1000, -256, 92, 1164, -392, 564, 356, + 652, -28, -884, 256, 484, -192, 760, -176, 376, -524, -452, + -436, 860, -736, 212, 124, 504, -476, 468, 76, -472, 552, + -692, -944, -620, 740, -240, 400, 132, 20, 192, -196, 264, + -668, -1012, -60, 296, -316, -828, 76, -156, 284, -768, -448, + -832, 148, 248, 652, 616, 1236, 288, -328, -400, -124, 588, + 220, 520, -696, 1032, 768, -740, -92, -272, 296, 448, -464, + 412, -200, 392, 440, -200, 264, -152, -260, 320, 1032, 216, + 320, -8, -64, 156, -1016, 1084, 1172, 536, 484, -432, 132, + 372, -52, -256, 84, 116, -352, 48, 116, 304, -384, 412, + 924, -300, 528, 628, 180, 648, 44, -980, -220, 1320, 48, + 332, 748, 524, -268, -720, 540, -276, 564, -344, -208, -196, + 436, 896, 88, -392, 132, 80, -964, -288, 568, 56, -48, + -456, 888, 8, 552, -156, -292, 948, 288, 128, -716, -292, + 1192, -152, 876, 352, -600, -260, -812, -468, -28, -120, -32, + -44, 1284, 496, 192, 464, 312, -76, -516, -380, -456, -1012, + -48, 308, -156, 36, 492, -156, -808, 188, 1652, 68, -120, + -116, 316, 160, -140, 352, 808, -416, 592, 316, -480, 56, + 528, -204, -568, 372, -232, 752, -344, 744, -4, 324, -416, + -600, 768, 268, -248, -88, -132, -420, -432, 80, -288, 404, + -316, -1216, -588, 520, -108, 92, -320, 368, -480, -216, -92, + 1688, -300, 180, 1020, -176, 820, -68, -228, -260, 436, -904, + 20, 40, -508, 440, -736, 312, 332, 204, 760, -372, 728, + 96, -20, -632, -520, -560, 336, 1076, -64, -532, 776, 584, + 192, 396, -728, -520, 276, -188, 80, -52, -612, -252, -48, + 648, 212, -688, 228, -52, -260, 428, -412, -272, -404, 180, + 816, -796, 48, 152, 484, -88, -216, 988, 696, 188, -528, + 648, -116, -180, 316, 476, 12, -564, 96, 476, -252, -364, + -376, -392, 556, -256, -576, 260, -352, 120, -16, -136, -260, + -492, 72, 556, 660, 580, 616, 772, 436, 424, -32, -324, + -1268, 416, -324, -80, 920, 160, 228, 724, 32, -516, 64, + 384, 68, -128, 136, 240, 248, -204, -68, 252, -932, -120, + -480, -628, -84, 192, 852, -404, -288, -132, 204, 100, 168, + -68, -196, -868, 460, 1080, 380, -80, 244, 0, 484, -888, + 64, 184, 352, 600, 460, 164, 604, -196, 320, -64, 588, + -184, 228, 12, 372, 48, -848, -344, 224, 208, -200, 484, + 128, -20, 272, -468, -840, 384, 256, -720, -520, -464, -580, + 112, -120, 644, -356, -208, -608, -528, 704, 560, -424, 392, + 828, 40, 84, 200, -152, 0, -144, 584, 280, -120, 80, + -556, -972, -196, -472, 724, 80, 168, -32, 88, 160, -688, + 0, 160, 356, 372, -776, 740, -128, 676, -248, -480, 4, + -364, 96, 544, 232, -1032, 956, 236, 356, 20, -40, 300, + 24, -676, -596, 132, 1120, -104, 532, -1096, 568, 648, 444, + 508, 380, 188, -376, -604, 1488, 424, 24, 756, -220, -192, + 716, 120, 920, 688, 168, 44, -460, 568, 284, 1144, 1160, + 600, 424, 888, 656, -356, -320, 220, 316, -176, -724, -188, + -816, -628, -348, -228, -380, 1012, -452, -660, 736, 928, 404, + -696, -72, -268, -892, 128, 184, -344, -780, 360, 336, 400, + 344, 428, 548, -112, 136, -228, -216, -820, -516, 340, 92, + -136, 116, -300, 376, -244, 100, -316, -520, -284, -12, 824, + 164, -548, -180, -128, 116, -924, -828, 268, -368, -580, 620, + 192, 160, 0, -1676, 1068, 424, -56, -360, 468, -156, 720, + 288, -528, 556, -364, 548, -148, 504, 316, 152, -648, -620, + -684, -24, -376, -384, -108, -920, -1032, 768, 180, -264, -508, + -1268, -260, -60, 300, -240, 988, 724, -376, -576, -212, -736, + 556, 192, 1092, -620, -880, 376, -56, -4, -216, -32, 836, + 268, 396, 1332, 864, -600, 100, 56, -412, -92, 356, 180, + 884, -468, -436, 292, -388, -804, -704, -840, 368, -348, 140, + -724, 1536, 940, 372, 112, -372, 436, -480, 1136, 296, -32, + -228, 132, -48, -220, 868, -1016, -60, -1044, -464, 328, 916, + 244, 12, -736, -296, 360, 468, -376, -108, -92, 788, 368, + -56, 544, 400, -672, -420, 728, 16, 320, 44, -284, -380, + -796, 488, 132, 204, -596, -372, 88, -152, -908, -636, -572, + -624, -116, -692, -200, -56, 276, -88, 484, -324, 948, 864, + 1000, -456, -184, -276, 292, -296, 156, 676, 320, 160, 908, + -84, -1236, -288, -116, 260, -372, -644, 732, -756, -96, 84, + 344, -520, 348, -688, 240, -84, 216, -1044, -136, -676, -396, + -1500, 960, -40, 176, 168, 1516, 420, -504, -344, -364, -360, + 1216, -940, -380, -212, 252, -660, -708, 484, -444, -152, 928, + -120, 1112, 476, -260, 560, -148, -344, 108, -196, 228, -288, + 504, 560, -328, -88, 288, -1008, 460, -228, 468, -836, -196, + 76, 388, 232, 412, -1168, -716, -644, 756, -172, -356, -504, + 116, 432, 528, 48, 476, -168, -608, 448, 160, -532, -272, + 28, -676, -12, 828, 980, 456, 520, 104, -104, 256, -344, + -4, -28, -368, -52, -524, -572, -556, -200, 768, 1124, -208, + -512, 176, 232, 248, -148, -888, 604, -600, -304, 804, -156, + -212, 488, -192, -804, -256, 368, -360, -916, -328, 228, -240, + -448, -472, 856, -556, -364, 572, -12, -156, -368, -340, 432, + 252, -752, -152, 288, 268, -580, -848, -592, 108, -76, 244, + 312, -716, 592, -80, 436, 360, 4, -248, 160, 516, 584, + 732, 44, -468, -280, -292, -156, -588, 28, 308, 912, 24, + 124, 156, 180, -252, 944, -924, -772, -520, -428, -624, 300, + -212, -1144, 32, -724, 800, -1128, -212, -1288, -848, 180, -416, + 440, 192, -576, -792, -76, -1080, 80, -532, -352, -132, 380, + -820, 148, 1112, 128, 164, 456, 700, -924, 144, -668, -384, + 648, -832, 508, 552, -52, -100, -656, 208, -568, 748, -88, + 680, 232, 300, 192, -408, -1012, -152, -252, -268, 272, -876, + -664, -648, -332, -136, 16, 12, 1152, -28, 332, -536, 320, + -672, -460, -316, 532, -260, 228, -40, 1052, -816, 180, 88, + -496, -556, -672, -368, 428, 92, 356, 404, -408, 252, 196, + -176, -556, 792, 268, 32, 372, 40, 96, -332, 328, 120, + 372, -900, -40, 472, -264, -592, 952, 128, 656, 112, 664, + -232, 420, 4, -344, -464, 556, 244, -416, -32, 252, 0, + -412, 188, -696, 508, -476, 324, -1096, 656, -312, 560, 264, + -136, 304, 160, -64, -580, 248, 336, -720, 560, -348, -288, + -276, -196, -500, 852, -544, -236, -1128, -992, -776, 116, 56, + 52, 860, 884, 212, -12, 168, 1020, 512, -552, 924, -148, + 716, 188, 164, -340, -520, -184, 880, -152, -680, -208, -1156, + -300, -528, -472, 364, 100, -744, -1056, -32, 540, 280, 144, + -676, -32, -232, -280, -224, 96, 568, -76, 172, 148, 148, + 104, 32, -296, -32, 788, -80, 32, -16, 280, 288, 944, + 428, -484 +}; diff --git a/3rdparty/dav1d/src/tables.h b/3rdparty/dav1d/src/tables.h new file mode 100644 index 0000000..f3c00cf --- /dev/null +++ b/3rdparty/dav1d/src/tables.h @@ -0,0 +1,125 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_TABLES_H +#define DAV1D_SRC_TABLES_H + +#include + +#include "common/intops.h" + +#include "src/levels.h" + +EXTERN const uint8_t dav1d_al_part_ctx[2][N_BL_LEVELS][N_PARTITIONS]; +EXTERN const uint8_t /* enum BlockSize */ + dav1d_block_sizes[N_BL_LEVELS][N_PARTITIONS][2]; +// width, height (in 4px blocks), log2 versions of these two +EXTERN const uint8_t dav1d_block_dimensions[N_BS_SIZES][4]; +typedef struct TxfmInfo { + // width, height (in 4px blocks), log2 of them, min/max of log2, sub, pad + uint8_t w, h, lw, lh, min, max, sub, ctx; +} TxfmInfo; +EXTERN const TxfmInfo dav1d_txfm_dimensions[N_RECT_TX_SIZES]; +EXTERN const uint8_t /* enum (Rect)TxfmSize */ + dav1d_max_txfm_size_for_bs[N_BS_SIZES][4 /* y, 420, 422, 444 */]; +EXTERN const uint8_t /* enum TxfmType */ + dav1d_txtp_from_uvmode[N_UV_INTRA_PRED_MODES]; + +EXTERN const uint8_t /* enum InterPredMode */ + dav1d_comp_inter_pred_modes[N_COMP_INTER_PRED_MODES][2]; + +EXTERN const uint8_t dav1d_partition_type_count[N_BL_LEVELS]; +EXTERN const uint8_t /* enum TxfmType */ dav1d_tx_types_per_set[40]; + +EXTERN const uint8_t dav1d_filter_mode_to_y_mode[5]; +EXTERN const uint8_t dav1d_ymode_size_context[N_BS_SIZES]; +EXTERN const uint8_t dav1d_lo_ctx_offsets[3][5][5]; +EXTERN const uint8_t dav1d_skip_ctx[5][5]; +EXTERN const uint8_t /* enum TxClass */ + dav1d_tx_type_class[N_TX_TYPES_PLUS_LL]; +EXTERN const uint8_t /* enum Filter2d */ + dav1d_filter_2d[DAV1D_N_FILTERS /* h */][DAV1D_N_FILTERS /* v */]; +EXTERN const uint8_t /* enum Dav1dFilterMode */ dav1d_filter_dir[N_2D_FILTERS][2]; +EXTERN const uint8_t dav1d_intra_mode_context[N_INTRA_PRED_MODES]; +EXTERN const uint8_t dav1d_wedge_ctx_lut[N_BS_SIZES]; + +static const unsigned cfl_allowed_mask = + (1 << BS_32x32) | + (1 << BS_32x16) | + (1 << BS_32x8) | + (1 << BS_16x32) | + (1 << BS_16x16) | + (1 << BS_16x8) | + (1 << BS_16x4) | + (1 << BS_8x32) | + (1 << BS_8x16) | + (1 << BS_8x8) | + (1 << BS_8x4) | + (1 << BS_4x16) | + (1 << BS_4x8) | + (1 << BS_4x4); + +static const unsigned wedge_allowed_mask = + (1 << BS_32x32) | + (1 << BS_32x16) | + (1 << BS_32x8) | + (1 << BS_16x32) | + (1 << BS_16x16) | + (1 << BS_16x8) | + (1 << BS_8x32) | + (1 << BS_8x16) | + (1 << BS_8x8); + +static const unsigned interintra_allowed_mask = + (1 << BS_32x32) | + (1 << BS_32x16) | + (1 << BS_16x32) | + (1 << BS_16x16) | + (1 << BS_16x8) | + (1 << BS_8x16) | + (1 << BS_8x8); + +EXTERN const Dav1dWarpedMotionParams dav1d_default_wm_params; + +EXTERN const int8_t dav1d_cdef_directions[12][2]; + +EXTERN const uint16_t dav1d_sgr_params[16][2]; +EXTERN const uint8_t dav1d_sgr_x_by_x[256]; + +EXTERN const int8_t dav1d_mc_subpel_filters[6][15][8]; +EXTERN const int8_t dav1d_mc_warp_filter[193][8]; +EXTERN const int8_t dav1d_resize_filter[64][8]; + +EXTERN const uint8_t dav1d_sm_weights[128]; +EXTERN const uint16_t dav1d_dr_intra_derivative[44]; +EXTERN const int8_t dav1d_filter_intra_taps[5][64]; + +EXTERN const uint8_t dav1d_obmc_masks[64]; + +EXTERN const int16_t dav1d_gaussian_sequence[2048]; // for fgs + +#endif /* DAV1D_SRC_TABLES_H */ diff --git a/3rdparty/dav1d/src/thread.h b/3rdparty/dav1d/src/thread.h new file mode 100644 index 0000000..459aace --- /dev/null +++ b/3rdparty/dav1d/src/thread.h @@ -0,0 +1,196 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_THREAD_H +#define DAV1D_SRC_THREAD_H + +#if defined(_WIN32) + +#include +#include + +#define PTHREAD_MUTEX_INITIALIZER SRWLOCK_INIT +#define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT + +typedef struct { + HANDLE h; + void *(*func)(void*); + void *arg; +} pthread_t; + +typedef struct { + unsigned stack_size; +} pthread_attr_t; + +typedef SRWLOCK pthread_mutex_t; +typedef CONDITION_VARIABLE pthread_cond_t; +typedef INIT_ONCE pthread_once_t; + +void dav1d_init_thread(void); +void dav1d_set_thread_name(const wchar_t *name); +#define dav1d_set_thread_name(name) dav1d_set_thread_name(L##name) + +int dav1d_pthread_create(pthread_t *thread, const pthread_attr_t *attr, + void *(*func)(void*), void *arg); +int dav1d_pthread_join(pthread_t *thread, void **res); +int dav1d_pthread_once(pthread_once_t *once_control, + void (*init_routine)(void)); + +#define pthread_create dav1d_pthread_create +#define pthread_join(thread, res) dav1d_pthread_join(&(thread), res) +#define pthread_once dav1d_pthread_once + +static inline int pthread_attr_init(pthread_attr_t *const attr) { + attr->stack_size = 0; + return 0; +} + +static inline int pthread_attr_destroy(pthread_attr_t *const attr) { + return 0; +} + +static inline int pthread_attr_setstacksize(pthread_attr_t *const attr, + const size_t stack_size) +{ + if (stack_size > UINT_MAX) return 1; + attr->stack_size = (unsigned) stack_size; + return 0; +} + +static inline int pthread_mutex_init(pthread_mutex_t *const mutex, + const void *const attr) +{ + InitializeSRWLock(mutex); + return 0; +} + +static inline int pthread_mutex_destroy(pthread_mutex_t *const mutex) { + return 0; +} + +static inline int pthread_mutex_lock(pthread_mutex_t *const mutex) { + AcquireSRWLockExclusive(mutex); + return 0; +} + +static inline int pthread_mutex_unlock(pthread_mutex_t *const mutex) { + ReleaseSRWLockExclusive(mutex); + return 0; +} + +static inline int pthread_cond_init(pthread_cond_t *const cond, + const void *const attr) +{ + InitializeConditionVariable(cond); + return 0; +} + +static inline int pthread_cond_destroy(pthread_cond_t *const cond) { + return 0; +} + +static inline int pthread_cond_wait(pthread_cond_t *const cond, + pthread_mutex_t *const mutex) +{ + return !SleepConditionVariableSRW(cond, mutex, INFINITE, 0); +} + +static inline int pthread_cond_signal(pthread_cond_t *const cond) { + WakeConditionVariable(cond); + return 0; +} + +static inline int pthread_cond_broadcast(pthread_cond_t *const cond) { + WakeAllConditionVariable(cond); + return 0; +} + +#else + +#include +#if defined(__FreeBSD__) + /* ALIGN from conflicts with ALIGN from "common/attributes.h" */ +#define _SYS_PARAM_H_ +#include +#endif +#if HAVE_PTHREAD_NP_H +#include +#endif + +#define dav1d_init_thread() do {} while (0) + +/* Thread naming support */ + +#ifdef __linux__ + +#include + +static inline void dav1d_set_thread_name(const char *const name) { + prctl(PR_SET_NAME, name); +} + +#elif HAVE_PTHREAD_SETNAME_NP && defined(__APPLE__) + +static inline void dav1d_set_thread_name(const char *const name) { + pthread_setname_np(name); +} + +#elif HAVE_PTHREAD_SETNAME_NP && defined(__NetBSD__) + +static inline void dav1d_set_thread_name(const char *const name) { + pthread_setname_np(pthread_self(), "%s", (void*)name); +} + +#elif HAVE_PTHREAD_SETNAME_NP + +static inline void dav1d_set_thread_name(const char *const name) { + pthread_setname_np(pthread_self(), name); +} + +#elif HAVE_PTHREAD_SET_NAME_NP + +static inline void dav1d_set_thread_name(const char *const name) { + pthread_set_name_np(pthread_self(), name); +} + +#elif defined(__HAIKU__) + +#include + +static inline void dav1d_set_thread_name(const char *const name) { + rename_thread(find_thread(NULL), name); +} + +#else + +#define dav1d_set_thread_name(name) do {} while (0) + +#endif + +#endif + +#endif /* DAV1D_SRC_THREAD_H */ diff --git a/3rdparty/dav1d/src/thread_data.h b/3rdparty/dav1d/src/thread_data.h new file mode 100644 index 0000000..62814e6 --- /dev/null +++ b/3rdparty/dav1d/src/thread_data.h @@ -0,0 +1,40 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_THREAD_DATA_H +#define DAV1D_SRC_THREAD_DATA_H + +#include "src/thread.h" + +struct thread_data { + pthread_t thread; + pthread_cond_t cond; + pthread_mutex_t lock; + int inited; +}; + +#endif /* DAV1D_SRC_THREAD_DATA_H */ diff --git a/3rdparty/dav1d/src/thread_task.c b/3rdparty/dav1d/src/thread_task.c new file mode 100644 index 0000000..d705d95 --- /dev/null +++ b/3rdparty/dav1d/src/thread_task.c @@ -0,0 +1,919 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include "common/frame.h" + +#include "src/thread_task.h" +#include "src/fg_apply.h" + +// This function resets the cur pointer to the first frame theoretically +// executable after a task completed (ie. each time we update some progress or +// insert some tasks in the queue). +// When frame_idx is set, it can be either from a completed task, or from tasks +// inserted in the queue, in which case we have to make sure the cur pointer +// isn't past this insert. +// The special case where frame_idx is UINT_MAX is to handle the reset after +// completing a task and locklessly signaling progress. In this case we don't +// enter a critical section, which is needed for this function, so we set an +// atomic for a delayed handling, happening here. Meaning we can call this +// function without any actual update other than what's in the atomic, hence +// this special case. +static inline int reset_task_cur(const Dav1dContext *const c, + struct TaskThreadData *const ttd, + unsigned frame_idx) +{ + const unsigned first = atomic_load(&ttd->first); + unsigned reset_frame_idx = atomic_exchange(&ttd->reset_task_cur, UINT_MAX); + if (reset_frame_idx < first) { + if (frame_idx == UINT_MAX) return 0; + reset_frame_idx = UINT_MAX; + } + if (!ttd->cur && c->fc[first].task_thread.task_cur_prev == NULL) + return 0; + if (reset_frame_idx != UINT_MAX) { + if (frame_idx == UINT_MAX) { + if (reset_frame_idx > first + ttd->cur) + return 0; + ttd->cur = reset_frame_idx - first; + goto cur_found; + } + } else if (frame_idx == UINT_MAX) + return 0; + if (frame_idx < first) frame_idx += c->n_fc; + const unsigned min_frame_idx = umin(reset_frame_idx, frame_idx); + const unsigned cur_frame_idx = first + ttd->cur; + if (ttd->cur < c->n_fc && cur_frame_idx < min_frame_idx) + return 0; + for (ttd->cur = min_frame_idx - first; ttd->cur < c->n_fc; ttd->cur++) + if (c->fc[(first + ttd->cur) % c->n_fc].task_thread.task_head) + break; +cur_found: + for (unsigned i = ttd->cur; i < c->n_fc; i++) + c->fc[(first + i) % c->n_fc].task_thread.task_cur_prev = NULL; + return 1; +} + +static inline void reset_task_cur_async(struct TaskThreadData *const ttd, + unsigned frame_idx, unsigned n_frames) +{ + const unsigned first = atomic_load(&ttd->first); + if (frame_idx < first) frame_idx += n_frames; + unsigned last_idx = frame_idx; + do { + frame_idx = last_idx; + last_idx = atomic_exchange(&ttd->reset_task_cur, frame_idx); + } while (last_idx < frame_idx); + if (frame_idx == first && atomic_load(&ttd->first) != first) { + unsigned expected = frame_idx; + atomic_compare_exchange_strong(&ttd->reset_task_cur, &expected, UINT_MAX); + } +} + +static void insert_tasks_between(Dav1dFrameContext *const f, + Dav1dTask *const first, Dav1dTask *const last, + Dav1dTask *const a, Dav1dTask *const b, + const int cond_signal) +{ + struct TaskThreadData *const ttd = f->task_thread.ttd; + if (atomic_load(f->c->flush)) return; + assert(!a || a->next == b); + if (!a) f->task_thread.task_head = first; + else a->next = first; + if (!b) f->task_thread.task_tail = last; + last->next = b; + reset_task_cur(f->c, ttd, first->frame_idx); + if (cond_signal && !atomic_fetch_or(&ttd->cond_signaled, 1)) + pthread_cond_signal(&ttd->cond); +} + +static void insert_tasks(Dav1dFrameContext *const f, + Dav1dTask *const first, Dav1dTask *const last, + const int cond_signal) +{ + // insert task back into task queue + Dav1dTask *t_ptr, *prev_t = NULL; + for (t_ptr = f->task_thread.task_head; + t_ptr; prev_t = t_ptr, t_ptr = t_ptr->next) + { + // entropy coding precedes other steps + if (t_ptr->type == DAV1D_TASK_TYPE_TILE_ENTROPY) { + if (first->type > DAV1D_TASK_TYPE_TILE_ENTROPY) continue; + // both are entropy + if (first->sby > t_ptr->sby) continue; + if (first->sby < t_ptr->sby) { + insert_tasks_between(f, first, last, prev_t, t_ptr, cond_signal); + return; + } + // same sby + } else { + if (first->type == DAV1D_TASK_TYPE_TILE_ENTROPY) { + insert_tasks_between(f, first, last, prev_t, t_ptr, cond_signal); + return; + } + if (first->sby > t_ptr->sby) continue; + if (first->sby < t_ptr->sby) { + insert_tasks_between(f, first, last, prev_t, t_ptr, cond_signal); + return; + } + // same sby + if (first->type > t_ptr->type) continue; + if (first->type < t_ptr->type) { + insert_tasks_between(f, first, last, prev_t, t_ptr, cond_signal); + return; + } + // same task type + } + + // sort by tile-id + assert(first->type == DAV1D_TASK_TYPE_TILE_RECONSTRUCTION || + first->type == DAV1D_TASK_TYPE_TILE_ENTROPY); + assert(first->type == t_ptr->type); + assert(t_ptr->sby == first->sby); + const int p = first->type == DAV1D_TASK_TYPE_TILE_ENTROPY; + const int t_tile_idx = (int) (first - f->task_thread.tile_tasks[p]); + const int p_tile_idx = (int) (t_ptr - f->task_thread.tile_tasks[p]); + assert(t_tile_idx != p_tile_idx); + if (t_tile_idx > p_tile_idx) continue; + insert_tasks_between(f, first, last, prev_t, t_ptr, cond_signal); + return; + } + // append at the end + insert_tasks_between(f, first, last, prev_t, NULL, cond_signal); +} + +static inline void insert_task(Dav1dFrameContext *const f, + Dav1dTask *const t, const int cond_signal) +{ + insert_tasks(f, t, t, cond_signal); +} + +static inline void add_pending(Dav1dFrameContext *const f, Dav1dTask *const t) { + pthread_mutex_lock(&f->task_thread.pending_tasks.lock); + t->next = NULL; + if (!f->task_thread.pending_tasks.head) + f->task_thread.pending_tasks.head = t; + else + f->task_thread.pending_tasks.tail->next = t; + f->task_thread.pending_tasks.tail = t; + atomic_store(&f->task_thread.pending_tasks.merge, 1); + pthread_mutex_unlock(&f->task_thread.pending_tasks.lock); +} + +static inline int merge_pending_frame(Dav1dFrameContext *const f) { + int const merge = atomic_load(&f->task_thread.pending_tasks.merge); + if (merge) { + pthread_mutex_lock(&f->task_thread.pending_tasks.lock); + Dav1dTask *t = f->task_thread.pending_tasks.head; + f->task_thread.pending_tasks.head = NULL; + f->task_thread.pending_tasks.tail = NULL; + atomic_store(&f->task_thread.pending_tasks.merge, 0); + pthread_mutex_unlock(&f->task_thread.pending_tasks.lock); + while (t) { + Dav1dTask *const tmp = t->next; + insert_task(f, t, 0); + t = tmp; + } + } + return merge; +} + +static inline int merge_pending(const Dav1dContext *const c) { + int res = 0; + for (unsigned i = 0; i < c->n_fc; i++) + res |= merge_pending_frame(&c->fc[i]); + return res; +} + +static int create_filter_sbrow(Dav1dFrameContext *const f, + const int pass, Dav1dTask **res_t) +{ + const int has_deblock = f->frame_hdr->loopfilter.level_y[0] || + f->frame_hdr->loopfilter.level_y[1]; + const int has_cdef = f->seq_hdr->cdef; + const int has_resize = f->frame_hdr->width[0] != f->frame_hdr->width[1]; + const int has_lr = f->lf.restore_planes; + + Dav1dTask *tasks = f->task_thread.tasks; + const int uses_2pass = f->c->n_fc > 1; + int num_tasks = f->sbh * (1 + uses_2pass); + if (num_tasks > f->task_thread.num_tasks) { + const size_t size = sizeof(Dav1dTask) * num_tasks; + tasks = dav1d_realloc(ALLOC_COMMON_CTX, f->task_thread.tasks, size); + if (!tasks) return -1; + memset(tasks, 0, size); + f->task_thread.tasks = tasks; + f->task_thread.num_tasks = num_tasks; + } + tasks += f->sbh * (pass & 1); + + if (pass & 1) { + f->frame_thread.entropy_progress = 0; + } else { + const int prog_sz = ((f->sbh + 31) & ~31) >> 5; + if (prog_sz > f->frame_thread.prog_sz) { + atomic_uint *const prog = dav1d_realloc(ALLOC_COMMON_CTX, f->frame_thread.frame_progress, + 2 * prog_sz * sizeof(*prog)); + if (!prog) return -1; + f->frame_thread.frame_progress = prog; + f->frame_thread.copy_lpf_progress = prog + prog_sz; + } + f->frame_thread.prog_sz = prog_sz; + memset(f->frame_thread.frame_progress, 0, prog_sz * sizeof(atomic_uint)); + memset(f->frame_thread.copy_lpf_progress, 0, prog_sz * sizeof(atomic_uint)); + atomic_store(&f->frame_thread.deblock_progress, 0); + } + f->frame_thread.next_tile_row[pass & 1] = 0; + + Dav1dTask *t = &tasks[0]; + t->sby = 0; + t->recon_progress = 1; + t->deblock_progress = 0; + t->type = pass == 1 ? DAV1D_TASK_TYPE_ENTROPY_PROGRESS : + has_deblock ? DAV1D_TASK_TYPE_DEBLOCK_COLS : + has_cdef || has_lr /* i.e. LR backup */ ? DAV1D_TASK_TYPE_DEBLOCK_ROWS : + has_resize ? DAV1D_TASK_TYPE_SUPER_RESOLUTION : + DAV1D_TASK_TYPE_RECONSTRUCTION_PROGRESS; + t->frame_idx = (int)(f - f->c->fc); + + *res_t = t; + return 0; +} + +int dav1d_task_create_tile_sbrow(Dav1dFrameContext *const f, const int pass, + const int cond_signal) +{ + Dav1dTask *tasks = f->task_thread.tile_tasks[0]; + const int uses_2pass = f->c->n_fc > 1; + const int n_tasks_per_pass = f->frame_hdr->tiling.cols * f->frame_hdr->tiling.rows; + const int n_tasks = n_tasks_per_pass * (1 + uses_2pass); + if (pass < 2) { + if (n_tasks > f->task_thread.num_tile_tasks) { + const size_t size = sizeof(Dav1dTask) * n_tasks; + tasks = dav1d_realloc(ALLOC_COMMON_CTX, f->task_thread.tile_tasks[0], size); + if (!tasks) return -1; + memset(tasks, 0, size); + f->task_thread.tile_tasks[0] = tasks; + f->task_thread.num_tile_tasks = n_tasks; + } + f->task_thread.tile_tasks[1] = tasks + n_tasks_per_pass; + } + assert(n_tasks <= f->task_thread.num_tile_tasks); + + Dav1dTask *pf_t; + if (create_filter_sbrow(f, pass, &pf_t)) + return -1; + + Dav1dTask *const p1_tasks = f->task_thread.tile_tasks[1]; + Dav1dTask *prev_t = NULL; + if (pass == 2) { + prev_t = &p1_tasks[n_tasks_per_pass - 1]; + // PF task is scheduled after the last sby=0 TILE task + if (f->frame_hdr->tiling.rows == 1) + prev_t = prev_t->next; + } + tasks += (pass & 1) * n_tasks_per_pass; + for (int tile_idx = 0; tile_idx < n_tasks_per_pass; tile_idx++) { + Dav1dTileState *const ts = &f->ts[tile_idx]; + Dav1dTask *t = &tasks[tile_idx]; + t->sby = ts->tiling.row_start >> f->sb_shift; + if (pf_t && t->sby) { + prev_t->next = pf_t; + prev_t = pf_t; + pf_t = NULL; + } + t->recon_progress = 0; + t->deblock_progress = 0; + t->deps_skip = 0; + t->type = pass != 1 ? DAV1D_TASK_TYPE_TILE_RECONSTRUCTION : + DAV1D_TASK_TYPE_TILE_ENTROPY; + t->frame_idx = (int)(f - f->c->fc); + if (prev_t) prev_t->next = t; + prev_t = t; + } + if (pf_t) { + prev_t->next = pf_t; + prev_t = pf_t; + } + prev_t->next = NULL; + + atomic_store(&f->task_thread.done[pass & 1], 0); + + // XXX in theory this could be done locklessly, at this point they are no + // tasks in the frameQ, so no other runner should be using this lock, but + // we must add both passes at once + if (!(pass & 1)) { + pthread_mutex_lock(&f->task_thread.pending_tasks.lock); + assert(f->task_thread.pending_tasks.head == NULL); + f->task_thread.pending_tasks.head = f->task_thread.tile_tasks[pass == 2]; + f->task_thread.pending_tasks.tail = prev_t; + atomic_store(&f->task_thread.pending_tasks.merge, 1); + atomic_store(&f->task_thread.init_done, 1); + pthread_mutex_unlock(&f->task_thread.pending_tasks.lock); + } + return 0; +} + +void dav1d_task_frame_init(Dav1dFrameContext *const f) { + const Dav1dContext *const c = f->c; + + atomic_store(&f->task_thread.init_done, 0); + // schedule init task, which will schedule the remaining tasks + Dav1dTask *const t = &f->task_thread.init_task; + t->type = DAV1D_TASK_TYPE_INIT; + t->frame_idx = (int)(f - c->fc); + t->sby = 0; + t->recon_progress = t->deblock_progress = 0; + insert_task(f, t, 1); +} + +void dav1d_task_delayed_fg(Dav1dContext *const c, Dav1dPicture *const out, + const Dav1dPicture *const in) +{ + struct TaskThreadData *const ttd = &c->task_thread; + ttd->delayed_fg.in = in; + ttd->delayed_fg.out = out; + ttd->delayed_fg.type = DAV1D_TASK_TYPE_FG_PREP; + atomic_init(&ttd->delayed_fg.progress[0], 0); + atomic_init(&ttd->delayed_fg.progress[1], 0); + pthread_mutex_lock(&ttd->lock); + ttd->delayed_fg.exec = 1; + ttd->delayed_fg.finished = 0; + pthread_cond_signal(&ttd->cond); + do { + pthread_cond_wait(&ttd->delayed_fg.cond, &ttd->lock); + } while (!ttd->delayed_fg.finished); + pthread_mutex_unlock(&ttd->lock); +} + +static inline int ensure_progress(struct TaskThreadData *const ttd, + Dav1dFrameContext *const f, + Dav1dTask *const t, const enum TaskType type, + atomic_int *const state, int *const target) +{ + // deblock_rows (non-LR portion) depends on deblock of previous sbrow, + // so ensure that completed. if not, re-add to task-queue; else, fall-through + int p1 = atomic_load(state); + if (p1 < t->sby) { + t->type = type; + t->recon_progress = t->deblock_progress = 0; + *target = t->sby; + add_pending(f, t); + pthread_mutex_lock(&ttd->lock); + return 1; + } + return 0; +} + +static inline int check_tile(Dav1dTask *const t, Dav1dFrameContext *const f, + const int frame_mt) +{ + const int tp = t->type == DAV1D_TASK_TYPE_TILE_ENTROPY; + const int tile_idx = (int)(t - f->task_thread.tile_tasks[tp]); + Dav1dTileState *const ts = &f->ts[tile_idx]; + const int p1 = atomic_load(&ts->progress[tp]); + if (p1 < t->sby) return 1; + int error = p1 == TILE_ERROR; + error |= atomic_fetch_or(&f->task_thread.error, error); + if (!error && frame_mt && !tp) { + const int p2 = atomic_load(&ts->progress[1]); + if (p2 <= t->sby) return 1; + error = p2 == TILE_ERROR; + error |= atomic_fetch_or(&f->task_thread.error, error); + } + if (!error && frame_mt && !IS_KEY_OR_INTRA(f->frame_hdr)) { + // check reference state + const Dav1dThreadPicture *p = &f->sr_cur; + const int ss_ver = p->p.p.layout == DAV1D_PIXEL_LAYOUT_I420; + const unsigned p_b = (t->sby + 1) << (f->sb_shift + 2); + const int tile_sby = t->sby - (ts->tiling.row_start >> f->sb_shift); + const int (*const lowest_px)[2] = ts->lowest_pixel[tile_sby]; + for (int n = t->deps_skip; n < 7; n++, t->deps_skip++) { + unsigned lowest; + if (tp) { + // if temporal mv refs are disabled, we only need this + // for the primary ref; if segmentation is disabled, we + // don't even need that + lowest = p_b; + } else { + // +8 is postfilter-induced delay + const int y = lowest_px[n][0] == INT_MIN ? INT_MIN : + lowest_px[n][0] + 8; + const int uv = lowest_px[n][1] == INT_MIN ? INT_MIN : + lowest_px[n][1] * (1 << ss_ver) + 8; + const int max = imax(y, uv); + if (max == INT_MIN) continue; + lowest = iclip(max, 1, f->refp[n].p.p.h); + } + const unsigned p3 = atomic_load(&f->refp[n].progress[!tp]); + if (p3 < lowest) return 1; + atomic_fetch_or(&f->task_thread.error, p3 == FRAME_ERROR); + } + } + return 0; +} + +static inline int get_frame_progress(const Dav1dContext *const c, + const Dav1dFrameContext *const f) +{ + unsigned frame_prog = c->n_fc > 1 ? atomic_load(&f->sr_cur.progress[1]) : 0; + if (frame_prog >= FRAME_ERROR) + return f->sbh - 1; + int idx = frame_prog >> (f->sb_shift + 7); + int prog; + do { + atomic_uint *state = &f->frame_thread.frame_progress[idx]; + const unsigned val = ~atomic_load(state); + prog = val ? ctz(val) : 32; + if (prog != 32) break; + prog = 0; + } while (++idx < f->frame_thread.prog_sz); + return ((idx << 5) | prog) - 1; +} + +static inline void abort_frame(Dav1dFrameContext *const f, const int error) { + atomic_store(&f->task_thread.error, error == DAV1D_ERR(EINVAL) ? 1 : -1); + atomic_store(&f->task_thread.task_counter, 0); + atomic_store(&f->task_thread.done[0], 1); + atomic_store(&f->task_thread.done[1], 1); + atomic_store(&f->sr_cur.progress[0], FRAME_ERROR); + atomic_store(&f->sr_cur.progress[1], FRAME_ERROR); + dav1d_decode_frame_exit(f, error); + f->n_tile_data = 0; + pthread_cond_signal(&f->task_thread.cond); +} + +static inline void delayed_fg_task(const Dav1dContext *const c, + struct TaskThreadData *const ttd) +{ + const Dav1dPicture *const in = ttd->delayed_fg.in; + Dav1dPicture *const out = ttd->delayed_fg.out; +#if CONFIG_16BPC + int off; + if (out->p.bpc != 8) + off = (out->p.bpc >> 1) - 4; +#endif + switch (ttd->delayed_fg.type) { + case DAV1D_TASK_TYPE_FG_PREP: + ttd->delayed_fg.exec = 0; + if (atomic_load(&ttd->cond_signaled)) + pthread_cond_signal(&ttd->cond); + pthread_mutex_unlock(&ttd->lock); + switch (out->p.bpc) { +#if CONFIG_8BPC + case 8: + dav1d_prep_grain_8bpc(&c->dsp[0].fg, out, in, + ttd->delayed_fg.scaling_8bpc, + ttd->delayed_fg.grain_lut_8bpc); + break; +#endif +#if CONFIG_16BPC + case 10: + case 12: + dav1d_prep_grain_16bpc(&c->dsp[off].fg, out, in, + ttd->delayed_fg.scaling_16bpc, + ttd->delayed_fg.grain_lut_16bpc); + break; +#endif + default: abort(); + } + ttd->delayed_fg.type = DAV1D_TASK_TYPE_FG_APPLY; + pthread_mutex_lock(&ttd->lock); + ttd->delayed_fg.exec = 1; + // fall-through + case DAV1D_TASK_TYPE_FG_APPLY:; + int row = atomic_fetch_add(&ttd->delayed_fg.progress[0], 1); + pthread_mutex_unlock(&ttd->lock); + int progmax = (out->p.h + FG_BLOCK_SIZE - 1) / FG_BLOCK_SIZE; + while (row < progmax) { + if (row + 1 < progmax) + pthread_cond_signal(&ttd->cond); + else { + pthread_mutex_lock(&ttd->lock); + ttd->delayed_fg.exec = 0; + pthread_mutex_unlock(&ttd->lock); + } + switch (out->p.bpc) { +#if CONFIG_8BPC + case 8: + dav1d_apply_grain_row_8bpc(&c->dsp[0].fg, out, in, + ttd->delayed_fg.scaling_8bpc, + ttd->delayed_fg.grain_lut_8bpc, row); + break; +#endif +#if CONFIG_16BPC + case 10: + case 12: + dav1d_apply_grain_row_16bpc(&c->dsp[off].fg, out, in, + ttd->delayed_fg.scaling_16bpc, + ttd->delayed_fg.grain_lut_16bpc, row); + break; +#endif + default: abort(); + } + row = atomic_fetch_add(&ttd->delayed_fg.progress[0], 1); + atomic_fetch_add(&ttd->delayed_fg.progress[1], 1); + } + pthread_mutex_lock(&ttd->lock); + ttd->delayed_fg.exec = 0; + int done = atomic_fetch_add(&ttd->delayed_fg.progress[1], 1) + 1; + progmax = atomic_load(&ttd->delayed_fg.progress[0]); + // signal for completion only once the last runner reaches this + if (done >= progmax) { + ttd->delayed_fg.finished = 1; + pthread_cond_signal(&ttd->delayed_fg.cond); + } + break; + default: abort(); + } +} + +void *dav1d_worker_task(void *data) { + Dav1dTaskContext *const tc = data; + const Dav1dContext *const c = tc->c; + struct TaskThreadData *const ttd = tc->task_thread.ttd; + + dav1d_set_thread_name("dav1d-worker"); + + pthread_mutex_lock(&ttd->lock); + for (;;) { + if (tc->task_thread.die) break; + if (atomic_load(c->flush)) goto park; + + merge_pending(c); + if (ttd->delayed_fg.exec) { // run delayed film grain first + delayed_fg_task(c, ttd); + continue; + } + Dav1dFrameContext *f; + Dav1dTask *t, *prev_t = NULL; + if (c->n_fc > 1) { // run init tasks second + for (unsigned i = 0; i < c->n_fc; i++) { + const unsigned first = atomic_load(&ttd->first); + f = &c->fc[(first + i) % c->n_fc]; + if (atomic_load(&f->task_thread.init_done)) continue; + t = f->task_thread.task_head; + if (!t) continue; + if (t->type == DAV1D_TASK_TYPE_INIT) goto found; + if (t->type == DAV1D_TASK_TYPE_INIT_CDF) { + // XXX This can be a simple else, if adding tasks of both + // passes at once (in dav1d_task_create_tile_sbrow). + // Adding the tasks to the pending Q can result in a + // thread merging them before setting init_done. + // We will need to set init_done before adding to the + // pending Q, so maybe return the tasks, set init_done, + // and add to pending Q only then. + const int p1 = f->in_cdf.progress ? + atomic_load(f->in_cdf.progress) : 1; + if (p1) { + atomic_fetch_or(&f->task_thread.error, p1 == TILE_ERROR); + goto found; + } + } + } + } + while (ttd->cur < c->n_fc) { // run decoding tasks last + const unsigned first = atomic_load(&ttd->first); + f = &c->fc[(first + ttd->cur) % c->n_fc]; + merge_pending_frame(f); + prev_t = f->task_thread.task_cur_prev; + t = prev_t ? prev_t->next : f->task_thread.task_head; + while (t) { + if (t->type == DAV1D_TASK_TYPE_INIT_CDF) goto next; + else if (t->type == DAV1D_TASK_TYPE_TILE_ENTROPY || + t->type == DAV1D_TASK_TYPE_TILE_RECONSTRUCTION) + { + // if not bottom sbrow of tile, this task will be re-added + // after it's finished + if (!check_tile(t, f, c->n_fc > 1)) + goto found; + } else if (t->recon_progress) { + const int p = t->type == DAV1D_TASK_TYPE_ENTROPY_PROGRESS; + int error = atomic_load(&f->task_thread.error); + assert(!atomic_load(&f->task_thread.done[p]) || error); + const int tile_row_base = f->frame_hdr->tiling.cols * + f->frame_thread.next_tile_row[p]; + if (p) { + atomic_int *const prog = &f->frame_thread.entropy_progress; + const int p1 = atomic_load(prog); + if (p1 < t->sby) goto next; + atomic_fetch_or(&f->task_thread.error, p1 == TILE_ERROR); + } + for (int tc = 0; tc < f->frame_hdr->tiling.cols; tc++) { + Dav1dTileState *const ts = &f->ts[tile_row_base + tc]; + const int p2 = atomic_load(&ts->progress[p]); + if (p2 < t->recon_progress) goto next; + atomic_fetch_or(&f->task_thread.error, p2 == TILE_ERROR); + } + if (t->sby + 1 < f->sbh) { + // add sby+1 to list to replace this one + Dav1dTask *next_t = &t[1]; + *next_t = *t; + next_t->sby++; + const int ntr = f->frame_thread.next_tile_row[p] + 1; + const int start = f->frame_hdr->tiling.row_start_sb[ntr]; + if (next_t->sby == start) + f->frame_thread.next_tile_row[p] = ntr; + next_t->recon_progress = next_t->sby + 1; + insert_task(f, next_t, 0); + } + goto found; + } else if (t->type == DAV1D_TASK_TYPE_CDEF) { + atomic_uint *prog = f->frame_thread.copy_lpf_progress; + const int p1 = atomic_load(&prog[(t->sby - 1) >> 5]); + if (p1 & (1U << ((t->sby - 1) & 31))) + goto found; + } else { + assert(t->deblock_progress); + const int p1 = atomic_load(&f->frame_thread.deblock_progress); + if (p1 >= t->deblock_progress) { + atomic_fetch_or(&f->task_thread.error, p1 == TILE_ERROR); + goto found; + } + } + next: + prev_t = t; + t = t->next; + f->task_thread.task_cur_prev = prev_t; + } + ttd->cur++; + } + if (reset_task_cur(c, ttd, UINT_MAX)) continue; + if (merge_pending(c)) continue; + park: + tc->task_thread.flushed = 1; + pthread_cond_signal(&tc->task_thread.td.cond); + // we want to be woken up next time progress is signaled + atomic_store(&ttd->cond_signaled, 0); + pthread_cond_wait(&ttd->cond, &ttd->lock); + tc->task_thread.flushed = 0; + reset_task_cur(c, ttd, UINT_MAX); + continue; + + found: + // remove t from list + if (prev_t) prev_t->next = t->next; + else f->task_thread.task_head = t->next; + if (!t->next) f->task_thread.task_tail = prev_t; + if (t->type > DAV1D_TASK_TYPE_INIT_CDF && !f->task_thread.task_head) + ttd->cur++; + t->next = NULL; + // we don't need to check cond_signaled here, since we found a task + // after the last signal so we want to re-signal the next waiting thread + // and again won't need to signal after that + atomic_store(&ttd->cond_signaled, 1); + pthread_cond_signal(&ttd->cond); + pthread_mutex_unlock(&ttd->lock); + found_unlocked:; + const int flush = atomic_load(c->flush); + int error = atomic_fetch_or(&f->task_thread.error, flush) | flush; + + // run it + tc->f = f; + int sby = t->sby; + switch (t->type) { + case DAV1D_TASK_TYPE_INIT: { + assert(c->n_fc > 1); + int res = dav1d_decode_frame_init(f); + int p1 = f->in_cdf.progress ? atomic_load(f->in_cdf.progress) : 1; + if (res || p1 == TILE_ERROR) { + pthread_mutex_lock(&ttd->lock); + abort_frame(f, res ? res : DAV1D_ERR(EINVAL)); + reset_task_cur(c, ttd, t->frame_idx); + } else { + t->type = DAV1D_TASK_TYPE_INIT_CDF; + if (p1) goto found_unlocked; + add_pending(f, t); + pthread_mutex_lock(&ttd->lock); + } + continue; + } + case DAV1D_TASK_TYPE_INIT_CDF: { + assert(c->n_fc > 1); + int res = DAV1D_ERR(EINVAL); + if (!atomic_load(&f->task_thread.error)) + res = dav1d_decode_frame_init_cdf(f); + if (f->frame_hdr->refresh_context && !f->task_thread.update_set) + atomic_store(f->out_cdf.progress, res < 0 ? TILE_ERROR : 1); + for (int p = 1; p <= 2 && !res; p++) + res = dav1d_task_create_tile_sbrow(f, p, 0); + pthread_mutex_lock(&ttd->lock); + if (res) { + abort_frame(f, DAV1D_ERR(ENOMEM)); + reset_task_cur(c, ttd, t->frame_idx); + atomic_store(&f->task_thread.init_done, 1); + } + continue; + } + case DAV1D_TASK_TYPE_TILE_ENTROPY: + case DAV1D_TASK_TYPE_TILE_RECONSTRUCTION: { + const int p = t->type == DAV1D_TASK_TYPE_TILE_ENTROPY; + const int tile_idx = (int)(t - f->task_thread.tile_tasks[p]); + Dav1dTileState *const ts = &f->ts[tile_idx]; + + tc->ts = ts; + tc->by = sby << f->sb_shift; + const int uses_2pass = c->n_fc > 1; + tc->frame_thread.pass = !uses_2pass ? 0 : + 1 + (t->type == DAV1D_TASK_TYPE_TILE_RECONSTRUCTION); + if (!error) error = dav1d_decode_tile_sbrow(tc); + const int progress = error ? TILE_ERROR : 1 + sby; + + // signal progress + atomic_fetch_or(&f->task_thread.error, error); + if (((sby + 1) << f->sb_shift) < ts->tiling.row_end) { + t->sby++; + t->deps_skip = 0; + if (!check_tile(t, f, uses_2pass)) { + atomic_store(&ts->progress[p], progress); + reset_task_cur_async(ttd, t->frame_idx, c->n_fc); + if (!atomic_fetch_or(&ttd->cond_signaled, 1)) + pthread_cond_signal(&ttd->cond); + goto found_unlocked; + } + atomic_store(&ts->progress[p], progress); + add_pending(f, t); + pthread_mutex_lock(&ttd->lock); + } else { + pthread_mutex_lock(&ttd->lock); + atomic_store(&ts->progress[p], progress); + reset_task_cur(c, ttd, t->frame_idx); + error = atomic_load(&f->task_thread.error); + if (f->frame_hdr->refresh_context && + tc->frame_thread.pass <= 1 && f->task_thread.update_set && + f->frame_hdr->tiling.update == tile_idx) + { + if (!error) + dav1d_cdf_thread_update(f->frame_hdr, f->out_cdf.data.cdf, + &f->ts[f->frame_hdr->tiling.update].cdf); + if (c->n_fc > 1) + atomic_store(f->out_cdf.progress, error ? TILE_ERROR : 1); + } + if (atomic_fetch_sub(&f->task_thread.task_counter, 1) - 1 == 0 && + atomic_load(&f->task_thread.done[0]) && + (!uses_2pass || atomic_load(&f->task_thread.done[1]))) + { + error = atomic_load(&f->task_thread.error); + dav1d_decode_frame_exit(f, error == 1 ? DAV1D_ERR(EINVAL) : + error ? DAV1D_ERR(ENOMEM) : 0); + f->n_tile_data = 0; + pthread_cond_signal(&f->task_thread.cond); + } + assert(atomic_load(&f->task_thread.task_counter) >= 0); + if (!atomic_fetch_or(&ttd->cond_signaled, 1)) + pthread_cond_signal(&ttd->cond); + } + continue; + } + case DAV1D_TASK_TYPE_DEBLOCK_COLS: + if (!atomic_load(&f->task_thread.error)) + f->bd_fn.filter_sbrow_deblock_cols(f, sby); + if (ensure_progress(ttd, f, t, DAV1D_TASK_TYPE_DEBLOCK_ROWS, + &f->frame_thread.deblock_progress, + &t->deblock_progress)) continue; + // fall-through + case DAV1D_TASK_TYPE_DEBLOCK_ROWS: + if (!atomic_load(&f->task_thread.error)) + f->bd_fn.filter_sbrow_deblock_rows(f, sby); + // signal deblock progress + if (f->frame_hdr->loopfilter.level_y[0] || + f->frame_hdr->loopfilter.level_y[1]) + { + error = atomic_load(&f->task_thread.error); + atomic_store(&f->frame_thread.deblock_progress, + error ? TILE_ERROR : sby + 1); + reset_task_cur_async(ttd, t->frame_idx, c->n_fc); + if (!atomic_fetch_or(&ttd->cond_signaled, 1)) + pthread_cond_signal(&ttd->cond); + } else if (f->seq_hdr->cdef || f->lf.restore_planes) { + atomic_fetch_or(&f->frame_thread.copy_lpf_progress[sby >> 5], + 1U << (sby & 31)); + // CDEF needs the top buffer to be saved by lr_copy_lpf of the + // previous sbrow + if (sby) { + int prog = atomic_load(&f->frame_thread.copy_lpf_progress[(sby - 1) >> 5]); + if (~prog & (1U << ((sby - 1) & 31))) { + t->type = DAV1D_TASK_TYPE_CDEF; + t->recon_progress = t->deblock_progress = 0; + add_pending(f, t); + pthread_mutex_lock(&ttd->lock); + continue; + } + } + } + // fall-through + case DAV1D_TASK_TYPE_CDEF: + if (f->seq_hdr->cdef) { + if (!atomic_load(&f->task_thread.error)) + f->bd_fn.filter_sbrow_cdef(tc, sby); + reset_task_cur_async(ttd, t->frame_idx, c->n_fc); + if (!atomic_fetch_or(&ttd->cond_signaled, 1)) + pthread_cond_signal(&ttd->cond); + } + // fall-through + case DAV1D_TASK_TYPE_SUPER_RESOLUTION: + if (f->frame_hdr->width[0] != f->frame_hdr->width[1]) + if (!atomic_load(&f->task_thread.error)) + f->bd_fn.filter_sbrow_resize(f, sby); + // fall-through + case DAV1D_TASK_TYPE_LOOP_RESTORATION: + if (!atomic_load(&f->task_thread.error) && f->lf.restore_planes) + f->bd_fn.filter_sbrow_lr(f, sby); + // fall-through + case DAV1D_TASK_TYPE_RECONSTRUCTION_PROGRESS: + // dummy to cover for no post-filters + case DAV1D_TASK_TYPE_ENTROPY_PROGRESS: + // dummy to convert tile progress to frame + break; + default: abort(); + } + // if task completed [typically LR], signal picture progress as per below + const int uses_2pass = c->n_fc > 1; + const int sbh = f->sbh; + const int sbsz = f->sb_step * 4; + if (t->type == DAV1D_TASK_TYPE_ENTROPY_PROGRESS) { + error = atomic_load(&f->task_thread.error); + const unsigned y = sby + 1 == sbh ? UINT_MAX : (unsigned)(sby + 1) * sbsz; + assert(c->n_fc > 1); + if (f->sr_cur.p.data[0] /* upon flush, this can be free'ed already */) + atomic_store(&f->sr_cur.progress[0], error ? FRAME_ERROR : y); + atomic_store(&f->frame_thread.entropy_progress, + error ? TILE_ERROR : sby + 1); + if (sby + 1 == sbh) + atomic_store(&f->task_thread.done[1], 1); + pthread_mutex_lock(&ttd->lock); + const int num_tasks = atomic_fetch_sub(&f->task_thread.task_counter, 1) - 1; + if (sby + 1 < sbh && num_tasks) { + reset_task_cur(c, ttd, t->frame_idx); + continue; + } + if (!num_tasks && atomic_load(&f->task_thread.done[0]) && + atomic_load(&f->task_thread.done[1])) + { + error = atomic_load(&f->task_thread.error); + dav1d_decode_frame_exit(f, error == 1 ? DAV1D_ERR(EINVAL) : + error ? DAV1D_ERR(ENOMEM) : 0); + f->n_tile_data = 0; + pthread_cond_signal(&f->task_thread.cond); + } + reset_task_cur(c, ttd, t->frame_idx); + continue; + } + // t->type != DAV1D_TASK_TYPE_ENTROPY_PROGRESS + atomic_fetch_or(&f->frame_thread.frame_progress[sby >> 5], + 1U << (sby & 31)); + pthread_mutex_lock(&f->task_thread.lock); + sby = get_frame_progress(c, f); + error = atomic_load(&f->task_thread.error); + const unsigned y = sby + 1 == sbh ? UINT_MAX : (unsigned)(sby + 1) * sbsz; + if (c->n_fc > 1 && f->sr_cur.p.data[0] /* upon flush, this can be free'ed already */) + atomic_store(&f->sr_cur.progress[1], error ? FRAME_ERROR : y); + pthread_mutex_unlock(&f->task_thread.lock); + if (sby + 1 == sbh) + atomic_store(&f->task_thread.done[0], 1); + pthread_mutex_lock(&ttd->lock); + const int num_tasks = atomic_fetch_sub(&f->task_thread.task_counter, 1) - 1; + if (sby + 1 < sbh && num_tasks) { + reset_task_cur(c, ttd, t->frame_idx); + continue; + } + if (!num_tasks && atomic_load(&f->task_thread.done[0]) && + (!uses_2pass || atomic_load(&f->task_thread.done[1]))) + { + error = atomic_load(&f->task_thread.error); + dav1d_decode_frame_exit(f, error == 1 ? DAV1D_ERR(EINVAL) : + error ? DAV1D_ERR(ENOMEM) : 0); + f->n_tile_data = 0; + pthread_cond_signal(&f->task_thread.cond); + } + reset_task_cur(c, ttd, t->frame_idx); + } + pthread_mutex_unlock(&ttd->lock); + + return NULL; +} diff --git a/3rdparty/dav1d/src/thread_task.h b/3rdparty/dav1d/src/thread_task.h new file mode 100644 index 0000000..257da1a --- /dev/null +++ b/3rdparty/dav1d/src/thread_task.h @@ -0,0 +1,53 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_THREAD_TASK_H +#define DAV1D_SRC_THREAD_TASK_H + +#include + +#include "src/internal.h" + +#define FRAME_ERROR (UINT_MAX - 1) +#define TILE_ERROR (INT_MAX - 1) + +// these functions assume the task scheduling lock is already taken +int dav1d_task_create_tile_sbrow(Dav1dFrameContext *f, int pass, int cond_signal); +void dav1d_task_frame_init(Dav1dFrameContext *f); + +void dav1d_task_delayed_fg(Dav1dContext *c, Dav1dPicture *out, const Dav1dPicture *in); + +void *dav1d_worker_task(void *data); + +int dav1d_decode_frame_init(Dav1dFrameContext *f); +int dav1d_decode_frame_init_cdf(Dav1dFrameContext *f); +int dav1d_decode_frame_main(Dav1dFrameContext *f); +void dav1d_decode_frame_exit(Dav1dFrameContext *f, int retval); +int dav1d_decode_frame(Dav1dFrameContext *f); +int dav1d_decode_tile_sbrow(Dav1dTaskContext *t); + +#endif /* DAV1D_SRC_THREAD_TASK_H */ diff --git a/3rdparty/dav1d/src/warpmv.c b/3rdparty/dav1d/src/warpmv.c new file mode 100644 index 0000000..439c430 --- /dev/null +++ b/3rdparty/dav1d/src/warpmv.c @@ -0,0 +1,209 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include + +#include "common/intops.h" + +#include "src/warpmv.h" + +static const uint16_t div_lut[257] = { + 16384, 16320, 16257, 16194, 16132, 16070, 16009, 15948, 15888, 15828, 15768, + 15709, 15650, 15592, 15534, 15477, 15420, 15364, 15308, 15252, 15197, 15142, + 15087, 15033, 14980, 14926, 14873, 14821, 14769, 14717, 14665, 14614, 14564, + 14513, 14463, 14413, 14364, 14315, 14266, 14218, 14170, 14122, 14075, 14028, + 13981, 13935, 13888, 13843, 13797, 13752, 13707, 13662, 13618, 13574, 13530, + 13487, 13443, 13400, 13358, 13315, 13273, 13231, 13190, 13148, 13107, 13066, + 13026, 12985, 12945, 12906, 12866, 12827, 12788, 12749, 12710, 12672, 12633, + 12596, 12558, 12520, 12483, 12446, 12409, 12373, 12336, 12300, 12264, 12228, + 12193, 12157, 12122, 12087, 12053, 12018, 11984, 11950, 11916, 11882, 11848, + 11815, 11782, 11749, 11716, 11683, 11651, 11619, 11586, 11555, 11523, 11491, + 11460, 11429, 11398, 11367, 11336, 11305, 11275, 11245, 11215, 11185, 11155, + 11125, 11096, 11067, 11038, 11009, 10980, 10951, 10923, 10894, 10866, 10838, + 10810, 10782, 10755, 10727, 10700, 10673, 10645, 10618, 10592, 10565, 10538, + 10512, 10486, 10460, 10434, 10408, 10382, 10356, 10331, 10305, 10280, 10255, + 10230, 10205, 10180, 10156, 10131, 10107, 10082, 10058, 10034, 10010, 9986, + 9963, 9939, 9916, 9892, 9869, 9846, 9823, 9800, 9777, 9754, 9732, + 9709, 9687, 9664, 9642, 9620, 9598, 9576, 9554, 9533, 9511, 9489, + 9468, 9447, 9425, 9404, 9383, 9362, 9341, 9321, 9300, 9279, 9259, + 9239, 9218, 9198, 9178, 9158, 9138, 9118, 9098, 9079, 9059, 9039, + 9020, 9001, 8981, 8962, 8943, 8924, 8905, 8886, 8867, 8849, 8830, + 8812, 8793, 8775, 8756, 8738, 8720, 8702, 8684, 8666, 8648, 8630, + 8613, 8595, 8577, 8560, 8542, 8525, 8508, 8490, 8473, 8456, 8439, + 8422, 8405, 8389, 8372, 8355, 8339, 8322, 8306, 8289, 8273, 8257, + 8240, 8224, 8208, 8192, +}; + +static inline int iclip_wmp(const int v) { + const int cv = iclip(v, INT16_MIN, INT16_MAX); + + return apply_sign((abs(cv) + 32) >> 6, cv) * (1 << 6); +} + +static inline int resolve_divisor_32(const unsigned d, int *const shift) { + *shift = ulog2(d); + const int e = d - (1 << *shift); + const int f = *shift > 8 ? (e + (1 << (*shift - 9))) >> (*shift - 8) : + e << (8 - *shift); + assert(f <= 256); + *shift += 14; + // Use f as lookup into the precomputed table of multipliers + return div_lut[f]; +} + +int dav1d_get_shear_params(Dav1dWarpedMotionParams *const wm) { + const int32_t *const mat = wm->matrix; + + if (mat[2] <= 0) return 1; + + wm->u.p.alpha = iclip_wmp(mat[2] - 0x10000); + wm->u.p.beta = iclip_wmp(mat[3]); + + int shift; + const int y = apply_sign(resolve_divisor_32(abs(mat[2]), &shift), mat[2]); + const int64_t v1 = ((int64_t) mat[4] * 0x10000) * y; + const int rnd = (1 << shift) >> 1; + wm->u.p.gamma = iclip_wmp(apply_sign64((int) ((llabs(v1) + rnd) >> shift), v1)); + const int64_t v2 = ((int64_t) mat[3] * mat[4]) * y; + wm->u.p.delta = iclip_wmp(mat[5] - + apply_sign64((int) ((llabs(v2) + rnd) >> shift), v2) - + 0x10000); + + return (4 * abs(wm->u.p.alpha) + 7 * abs(wm->u.p.beta) >= 0x10000) || + (4 * abs(wm->u.p.gamma) + 4 * abs(wm->u.p.delta) >= 0x10000); +} + +static int resolve_divisor_64(const uint64_t d, int *const shift) { + *shift = u64log2(d); + const int64_t e = d - (1LL << *shift); + const int64_t f = *shift > 8 ? (e + (1LL << (*shift - 9))) >> (*shift - 8) : + e << (8 - *shift); + assert(f <= 256); + *shift += 14; + // Use f as lookup into the precomputed table of multipliers + return div_lut[f]; +} + +static int get_mult_shift_ndiag(const int64_t px, + const int idet, const int shift) +{ + const int64_t v1 = px * idet; + const int v2 = apply_sign64((int) ((llabs(v1) + + ((1LL << shift) >> 1)) >> shift), + v1); + return iclip(v2, -0x1fff, 0x1fff); +} + +static int get_mult_shift_diag(const int64_t px, + const int idet, const int shift) +{ + const int64_t v1 = px * idet; + const int v2 = apply_sign64((int) ((llabs(v1) + + ((1LL << shift) >> 1)) >> shift), + v1); + return iclip(v2, 0xe001, 0x11fff); +} + +void dav1d_set_affine_mv2d(const int bw4, const int bh4, + const mv mv, Dav1dWarpedMotionParams *const wm, + const int bx4, const int by4) +{ + int32_t *const mat = wm->matrix; + const int rsuy = 2 * bh4 - 1; + const int rsux = 2 * bw4 - 1; + const int isuy = by4 * 4 + rsuy; + const int isux = bx4 * 4 + rsux; + + mat[0] = iclip(mv.x * 0x2000 - (isux * (mat[2] - 0x10000) + isuy * mat[3]), + -0x800000, 0x7fffff); + mat[1] = iclip(mv.y * 0x2000 - (isux * mat[4] + isuy * (mat[5] - 0x10000)), + -0x800000, 0x7fffff); +} + +int dav1d_find_affine_int(const int (*pts)[2][2], const int np, + const int bw4, const int bh4, + const mv mv, Dav1dWarpedMotionParams *const wm, + const int bx4, const int by4) +{ + int32_t *const mat = wm->matrix; + int a[2][2] = { { 0, 0 }, { 0, 0 } }; + int bx[2] = { 0, 0 }; + int by[2] = { 0, 0 }; + const int rsuy = 2 * bh4 - 1; + const int rsux = 2 * bw4 - 1; + const int suy = rsuy * 8; + const int sux = rsux * 8; + const int duy = suy + mv.y; + const int dux = sux + mv.x; + const int isuy = by4 * 4 + rsuy; + const int isux = bx4 * 4 + rsux; + + for (int i = 0; i < np; i++) { + const int dx = pts[i][1][0] - dux; + const int dy = pts[i][1][1] - duy; + const int sx = pts[i][0][0] - sux; + const int sy = pts[i][0][1] - suy; + if (abs(sx - dx) < 256 && abs(sy - dy) < 256) { + a[0][0] += ((sx * sx) >> 2) + sx * 2 + 8; + a[0][1] += ((sx * sy) >> 2) + sx + sy + 4; + a[1][1] += ((sy * sy) >> 2) + sy * 2 + 8; + bx[0] += ((sx * dx) >> 2) + sx + dx + 8; + bx[1] += ((sy * dx) >> 2) + sy + dx + 4; + by[0] += ((sx * dy) >> 2) + sx + dy + 4; + by[1] += ((sy * dy) >> 2) + sy + dy + 8; + } + } + + // compute determinant of a + const int64_t det = (int64_t) a[0][0] * a[1][1] - (int64_t) a[0][1] * a[0][1]; + if (det == 0) return 1; + int shift, idet = apply_sign64(resolve_divisor_64(llabs(det), &shift), det); + shift -= 16; + if (shift < 0) { + idet <<= -shift; + shift = 0; + } + + // solve the least-squares + mat[2] = get_mult_shift_diag((int64_t) a[1][1] * bx[0] - + (int64_t) a[0][1] * bx[1], idet, shift); + mat[3] = get_mult_shift_ndiag((int64_t) a[0][0] * bx[1] - + (int64_t) a[0][1] * bx[0], idet, shift); + mat[4] = get_mult_shift_ndiag((int64_t) a[1][1] * by[0] - + (int64_t) a[0][1] * by[1], idet, shift); + mat[5] = get_mult_shift_diag((int64_t) a[0][0] * by[1] - + (int64_t) a[0][1] * by[0], idet, shift); + + mat[0] = iclip(mv.x * 0x2000 - (isux * (mat[2] - 0x10000) + isuy * mat[3]), + -0x800000, 0x7fffff); + mat[1] = iclip(mv.y * 0x2000 - (isux * mat[4] + isuy * (mat[5] - 0x10000)), + -0x800000, 0x7fffff); + + return 0; +} diff --git a/3rdparty/dav1d/src/warpmv.h b/3rdparty/dav1d/src/warpmv.h new file mode 100644 index 0000000..08e841d --- /dev/null +++ b/3rdparty/dav1d/src/warpmv.h @@ -0,0 +1,39 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_WARPMV_H +#define DAV1D_SRC_WARPMV_H + +#include "src/levels.h" + +int dav1d_get_shear_params(Dav1dWarpedMotionParams *wm); +int dav1d_find_affine_int(const int (*pts)[2][2], int np, int bw4, int bh4, + mv mv, Dav1dWarpedMotionParams *wm, int bx, int by); +void dav1d_set_affine_mv2d(int bw4, int bh4, + mv mv, Dav1dWarpedMotionParams *wm, int bx, int by); + +#endif /* DAV1D_SRC_WARPMV_H */ diff --git a/3rdparty/dav1d/src/wedge.c b/3rdparty/dav1d/src/wedge.c new file mode 100644 index 0000000..2bea139 --- /dev/null +++ b/3rdparty/dav1d/src/wedge.c @@ -0,0 +1,299 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#include +#include + +#include "common/intops.h" + +#include "src/wedge.h" + +enum WedgeDirectionType { + WEDGE_HORIZONTAL = 0, + WEDGE_VERTICAL = 1, + WEDGE_OBLIQUE27 = 2, + WEDGE_OBLIQUE63 = 3, + WEDGE_OBLIQUE117 = 4, + WEDGE_OBLIQUE153 = 5, + N_WEDGE_DIRECTIONS +}; + +typedef struct { + uint8_t /* enum WedgeDirectionType */ direction; + uint8_t x_offset; + uint8_t y_offset; +} wedge_code_type; + +static const wedge_code_type wedge_codebook_16_hgtw[16] = { + { WEDGE_OBLIQUE27, 4, 4 }, { WEDGE_OBLIQUE63, 4, 4 }, + { WEDGE_OBLIQUE117, 4, 4 }, { WEDGE_OBLIQUE153, 4, 4 }, + { WEDGE_HORIZONTAL, 4, 2 }, { WEDGE_HORIZONTAL, 4, 4 }, + { WEDGE_HORIZONTAL, 4, 6 }, { WEDGE_VERTICAL, 4, 4 }, + { WEDGE_OBLIQUE27, 4, 2 }, { WEDGE_OBLIQUE27, 4, 6 }, + { WEDGE_OBLIQUE153, 4, 2 }, { WEDGE_OBLIQUE153, 4, 6 }, + { WEDGE_OBLIQUE63, 2, 4 }, { WEDGE_OBLIQUE63, 6, 4 }, + { WEDGE_OBLIQUE117, 2, 4 }, { WEDGE_OBLIQUE117, 6, 4 }, +}; + +static const wedge_code_type wedge_codebook_16_hltw[16] = { + { WEDGE_OBLIQUE27, 4, 4 }, { WEDGE_OBLIQUE63, 4, 4 }, + { WEDGE_OBLIQUE117, 4, 4 }, { WEDGE_OBLIQUE153, 4, 4 }, + { WEDGE_VERTICAL, 2, 4 }, { WEDGE_VERTICAL, 4, 4 }, + { WEDGE_VERTICAL, 6, 4 }, { WEDGE_HORIZONTAL, 4, 4 }, + { WEDGE_OBLIQUE27, 4, 2 }, { WEDGE_OBLIQUE27, 4, 6 }, + { WEDGE_OBLIQUE153, 4, 2 }, { WEDGE_OBLIQUE153, 4, 6 }, + { WEDGE_OBLIQUE63, 2, 4 }, { WEDGE_OBLIQUE63, 6, 4 }, + { WEDGE_OBLIQUE117, 2, 4 }, { WEDGE_OBLIQUE117, 6, 4 }, +}; + +static const wedge_code_type wedge_codebook_16_heqw[16] = { + { WEDGE_OBLIQUE27, 4, 4 }, { WEDGE_OBLIQUE63, 4, 4 }, + { WEDGE_OBLIQUE117, 4, 4 }, { WEDGE_OBLIQUE153, 4, 4 }, + { WEDGE_HORIZONTAL, 4, 2 }, { WEDGE_HORIZONTAL, 4, 6 }, + { WEDGE_VERTICAL, 2, 4 }, { WEDGE_VERTICAL, 6, 4 }, + { WEDGE_OBLIQUE27, 4, 2 }, { WEDGE_OBLIQUE27, 4, 6 }, + { WEDGE_OBLIQUE153, 4, 2 }, { WEDGE_OBLIQUE153, 4, 6 }, + { WEDGE_OBLIQUE63, 2, 4 }, { WEDGE_OBLIQUE63, 6, 4 }, + { WEDGE_OBLIQUE117, 2, 4 }, { WEDGE_OBLIQUE117, 6, 4 }, +}; + +Dav1dMasks dav1d_masks; + +static void insert_border(uint8_t *const dst, const uint8_t *const src, + const int ctr) +{ + if (ctr > 4) memset(dst, 0, ctr - 4); + memcpy(dst + imax(ctr, 4) - 4, src + imax(4 - ctr, 0), imin(64 - ctr, 8)); + if (ctr < 64 - 4) + memset(dst + ctr + 4, 64, 64 - 4 - ctr); +} + +static void transpose(uint8_t *const dst, const uint8_t *const src) { + for (int y = 0, y_off = 0; y < 64; y++, y_off += 64) + for (int x = 0, x_off = 0; x < 64; x++, x_off += 64) + dst[x_off + y] = src[y_off + x]; +} + +static void hflip(uint8_t *const dst, const uint8_t *const src) { + for (int y = 0, y_off = 0; y < 64; y++, y_off += 64) + for (int x = 0; x < 64; x++) + dst[y_off + 64 - 1 - x] = src[y_off + x]; +} + +static void copy2d(uint8_t *dst, const uint8_t *src, int sign, + const int w, const int h, const int x_off, const int y_off) +{ + src += y_off * 64 + x_off; + if (sign) { + for (int y = 0; y < h; y++) { + for (int x = 0; x < w; x++) + dst[x] = 64 - src[x]; + src += 64; + dst += w; + } + } else { + for (int y = 0; y < h; y++) { + memcpy(dst, src, w); + src += 64; + dst += w; + } + } +} + +#define MASK_OFFSET(x) ((uint16_t)(((uintptr_t)(x) - (uintptr_t)&dav1d_masks) >> 3)) + +static COLD uint16_t init_chroma(uint8_t *chroma, const uint8_t *luma, + const int sign, const int w, const int h, + const int ss_ver) +{ + const uint16_t offset = MASK_OFFSET(chroma); + for (int y = 0; y < h; y += 1 + ss_ver) { + for (int x = 0; x < w; x += 2) { + int sum = luma[x] + luma[x + 1] + 1; + if (ss_ver) sum += luma[w + x] + luma[w + x + 1] + 1; + chroma[x >> 1] = (sum - sign) >> (1 + ss_ver); + } + luma += w << ss_ver; + chroma += w >> 1; + } + return offset; +} + +static COLD void fill2d_16x2(const int w, const int h, const enum BlockSize bs, + const uint8_t (*const master)[64 * 64], + const wedge_code_type *const cb, + uint8_t *masks_444, uint8_t *masks_422, + uint8_t *masks_420, unsigned signs) +{ + const int n_stride_444 = (w * h); + const int n_stride_422 = n_stride_444 >> 1; + const int n_stride_420 = n_stride_444 >> 2; + const int sign_stride_422 = 16 * n_stride_422; + const int sign_stride_420 = 16 * n_stride_420; + + // assign pointer offsets in lookup table + for (int n = 0; n < 16; n++) { + const int sign = signs & 1; + + copy2d(masks_444, master[cb[n].direction], sign, w, h, + 32 - (w * cb[n].x_offset >> 3), 32 - (h * cb[n].y_offset >> 3)); + + // not using !sign is intentional here, since 444 does not require + // any rounding since no chroma subsampling is applied. + dav1d_masks.offsets[0][bs].wedge[0][n] = + dav1d_masks.offsets[0][bs].wedge[1][n] = MASK_OFFSET(masks_444); + + dav1d_masks.offsets[1][bs].wedge[0][n] = + init_chroma(&masks_422[ sign * sign_stride_422], masks_444, 0, w, h, 0); + dav1d_masks.offsets[1][bs].wedge[1][n] = + init_chroma(&masks_422[!sign * sign_stride_422], masks_444, 1, w, h, 0); + dav1d_masks.offsets[2][bs].wedge[0][n] = + init_chroma(&masks_420[ sign * sign_stride_420], masks_444, 0, w, h, 1); + dav1d_masks.offsets[2][bs].wedge[1][n] = + init_chroma(&masks_420[!sign * sign_stride_420], masks_444, 1, w, h, 1); + + signs >>= 1; + masks_444 += n_stride_444; + masks_422 += n_stride_422; + masks_420 += n_stride_420; + } +} + +static COLD void build_nondc_ii_masks(uint8_t *const mask_v, const int w, + const int h, const int step) +{ + static const uint8_t ii_weights_1d[32] = { + 60, 52, 45, 39, 34, 30, 26, 22, 19, 17, 15, 13, 11, 10, 8, 7, + 6, 6, 5, 4, 4, 3, 3, 2, 2, 2, 2, 1, 1, 1, 1, 1, + }; + + uint8_t *const mask_h = &mask_v[w * h]; + uint8_t *const mask_sm = &mask_h[w * h]; + for (int y = 0, off = 0; y < h; y++, off += w) { + memset(&mask_v[off], ii_weights_1d[y * step], w); + for (int x = 0; x < w; x++) { + mask_sm[off + x] = ii_weights_1d[imin(x, y) * step]; + mask_h[off + x] = ii_weights_1d[x * step]; + } + } +} + +COLD void dav1d_init_ii_wedge_masks(void) { + // This function is guaranteed to be called only once + + enum WedgeMasterLineType { + WEDGE_MASTER_LINE_ODD, + WEDGE_MASTER_LINE_EVEN, + WEDGE_MASTER_LINE_VERT, + N_WEDGE_MASTER_LINES, + }; + static const uint8_t wedge_master_border[N_WEDGE_MASTER_LINES][8] = { + [WEDGE_MASTER_LINE_ODD] = { 1, 2, 6, 18, 37, 53, 60, 63 }, + [WEDGE_MASTER_LINE_EVEN] = { 1, 4, 11, 27, 46, 58, 62, 63 }, + [WEDGE_MASTER_LINE_VERT] = { 0, 2, 7, 21, 43, 57, 62, 64 }, + }; + uint8_t master[6][64 * 64]; + + // create master templates + for (int y = 0, off = 0; y < 64; y++, off += 64) + insert_border(&master[WEDGE_VERTICAL][off], + wedge_master_border[WEDGE_MASTER_LINE_VERT], 32); + for (int y = 0, off = 0, ctr = 48; y < 64; y += 2, off += 128, ctr--) + { + insert_border(&master[WEDGE_OBLIQUE63][off], + wedge_master_border[WEDGE_MASTER_LINE_EVEN], ctr); + insert_border(&master[WEDGE_OBLIQUE63][off + 64], + wedge_master_border[WEDGE_MASTER_LINE_ODD], ctr - 1); + } + + transpose(master[WEDGE_OBLIQUE27], master[WEDGE_OBLIQUE63]); + transpose(master[WEDGE_HORIZONTAL], master[WEDGE_VERTICAL]); + hflip(master[WEDGE_OBLIQUE117], master[WEDGE_OBLIQUE63]); + hflip(master[WEDGE_OBLIQUE153], master[WEDGE_OBLIQUE27]); + +#define fill(w, h, sz_422, sz_420, hvsw, signs) \ + fill2d_16x2(w, h, BS_##w##x##h - BS_32x32, \ + master, wedge_codebook_16_##hvsw, \ + dav1d_masks.wedge_444_##w##x##h, \ + dav1d_masks.wedge_422_##sz_422, \ + dav1d_masks.wedge_420_##sz_420, signs) + + fill(32, 32, 16x32, 16x16, heqw, 0x7bfb); + fill(32, 16, 16x16, 16x8, hltw, 0x7beb); + fill(32, 8, 16x8, 16x4, hltw, 0x6beb); + fill(16, 32, 8x32, 8x16, hgtw, 0x7beb); + fill(16, 16, 8x16, 8x8, heqw, 0x7bfb); + fill(16, 8, 8x8, 8x4, hltw, 0x7beb); + fill( 8, 32, 4x32, 4x16, hgtw, 0x7aeb); + fill( 8, 16, 4x16, 4x8, hgtw, 0x7beb); + fill( 8, 8, 4x8, 4x4, heqw, 0x7bfb); +#undef fill + + memset(dav1d_masks.ii_dc, 32, 32 * 32); + for (int c = 0; c < 3; c++) { + dav1d_masks.offsets[c][BS_32x32-BS_32x32].ii[II_DC_PRED] = + dav1d_masks.offsets[c][BS_32x16-BS_32x32].ii[II_DC_PRED] = + dav1d_masks.offsets[c][BS_16x32-BS_32x32].ii[II_DC_PRED] = + dav1d_masks.offsets[c][BS_16x16-BS_32x32].ii[II_DC_PRED] = + dav1d_masks.offsets[c][BS_16x8 -BS_32x32].ii[II_DC_PRED] = + dav1d_masks.offsets[c][BS_8x16 -BS_32x32].ii[II_DC_PRED] = + dav1d_masks.offsets[c][BS_8x8 -BS_32x32].ii[II_DC_PRED] = + MASK_OFFSET(dav1d_masks.ii_dc); + } + +#define BUILD_NONDC_II_MASKS(w, h, step) \ + build_nondc_ii_masks(dav1d_masks.ii_nondc_##w##x##h, w, h, step) + +#define ASSIGN_NONDC_II_OFFSET(bs, w444, h444, w422, h422, w420, h420) \ + dav1d_masks.offsets[0][bs-BS_32x32].ii[p + 1] = \ + MASK_OFFSET(&dav1d_masks.ii_nondc_##w444##x##h444[p*w444*h444]); \ + dav1d_masks.offsets[1][bs-BS_32x32].ii[p + 1] = \ + MASK_OFFSET(&dav1d_masks.ii_nondc_##w422##x##h422[p*w422*h422]); \ + dav1d_masks.offsets[2][bs-BS_32x32].ii[p + 1] = \ + MASK_OFFSET(&dav1d_masks.ii_nondc_##w420##x##h420[p*w420*h420]) + + BUILD_NONDC_II_MASKS(32, 32, 1); + BUILD_NONDC_II_MASKS(16, 32, 1); + BUILD_NONDC_II_MASKS(16, 16, 2); + BUILD_NONDC_II_MASKS( 8, 32, 1); + BUILD_NONDC_II_MASKS( 8, 16, 2); + BUILD_NONDC_II_MASKS( 8, 8, 4); + BUILD_NONDC_II_MASKS( 4, 16, 2); + BUILD_NONDC_II_MASKS( 4, 8, 4); + BUILD_NONDC_II_MASKS( 4, 4, 8); + for (int p = 0; p < 3; p++) { + ASSIGN_NONDC_II_OFFSET(BS_32x32, 32, 32, 16, 32, 16, 16); + ASSIGN_NONDC_II_OFFSET(BS_32x16, 32, 32, 16, 16, 16, 16); + ASSIGN_NONDC_II_OFFSET(BS_16x32, 16, 32, 8, 32, 8, 16); + ASSIGN_NONDC_II_OFFSET(BS_16x16, 16, 16, 8, 16, 8, 8); + ASSIGN_NONDC_II_OFFSET(BS_16x8, 16, 16, 8, 8, 8, 8); + ASSIGN_NONDC_II_OFFSET(BS_8x16, 8, 16, 4, 16, 4, 8); + ASSIGN_NONDC_II_OFFSET(BS_8x8, 8, 8, 4, 8, 4, 4); + } +} diff --git a/3rdparty/dav1d/src/wedge.h b/3rdparty/dav1d/src/wedge.h new file mode 100644 index 0000000..244e04a --- /dev/null +++ b/3rdparty/dav1d/src/wedge.h @@ -0,0 +1,96 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#ifndef DAV1D_SRC_WEDGE_H +#define DAV1D_SRC_WEDGE_H + +#include "src/levels.h" + +typedef struct { + /* Offsets, in units of 8 bytes, relative to the start of the struct. */ + struct { + uint16_t wedge[2 /* sign */][16 /* wedge_idx */]; + uint16_t ii[N_INTER_INTRA_PRED_MODES]; + } offsets[3 /* 444, 422, 420 */][BS_8x8 - BS_32x32 + 1]; + + uint8_t ALIGN(wedge_444_32x32[ 16 * 32 * 32], 64); + uint8_t ALIGN(wedge_444_32x16[ 16 * 32 * 16], 64); + uint8_t ALIGN(wedge_444_32x8 [ 16 * 32 * 8], 64); + uint8_t ALIGN(wedge_444_16x32[ 16 * 16 * 32], 64); + uint8_t ALIGN(wedge_444_16x16[ 16 * 16 * 16], 64); + uint8_t ALIGN(wedge_444_16x8 [ 16 * 16 * 8], 64); + uint8_t ALIGN(wedge_444_8x32 [ 16 * 8 * 32], 64); + uint8_t ALIGN(wedge_444_8x16 [ 16 * 8 * 16], 64); + uint8_t ALIGN(wedge_444_8x8 [ 16 * 8 * 8], 64); + + uint8_t ALIGN(wedge_422_16x32[2 * 16 * 16 * 32], 64); + uint8_t ALIGN(wedge_422_16x16[2 * 16 * 16 * 16], 64); + uint8_t ALIGN(wedge_422_16x8 [2 * 16 * 16 * 8], 64); + uint8_t ALIGN(wedge_422_8x32 [2 * 16 * 8 * 32], 64); + uint8_t ALIGN(wedge_422_8x16 [2 * 16 * 8 * 16], 64); + uint8_t ALIGN(wedge_422_8x8 [2 * 16 * 8 * 8], 64); + uint8_t ALIGN(wedge_422_4x32 [2 * 16 * 4 * 32], 64); + uint8_t ALIGN(wedge_422_4x16 [2 * 16 * 4 * 16], 64); + uint8_t ALIGN(wedge_422_4x8 [2 * 16 * 4 * 8], 64); + + uint8_t ALIGN(wedge_420_16x16[2 * 16 * 16 * 16], 64); + uint8_t ALIGN(wedge_420_16x8 [2 * 16 * 16 * 8], 64); + uint8_t ALIGN(wedge_420_16x4 [2 * 16 * 16 * 4], 64); + uint8_t ALIGN(wedge_420_8x16 [2 * 16 * 8 * 16], 64); + uint8_t ALIGN(wedge_420_8x8 [2 * 16 * 8 * 8], 64); + uint8_t ALIGN(wedge_420_8x4 [2 * 16 * 8 * 4], 64); + uint8_t ALIGN(wedge_420_4x16 [2 * 16 * 4 * 16], 64); + uint8_t ALIGN(wedge_420_4x8 [2 * 16 * 4 * 8], 64); + uint8_t ALIGN(wedge_420_4x4 [2 * 16 * 4 * 4], 64); + + uint8_t ALIGN(ii_dc [ 32 * 32], 64); + uint8_t ALIGN(ii_nondc_32x32[3 * 32 * 32], 64); + uint8_t ALIGN(ii_nondc_16x32[3 * 16 * 32], 64); + uint8_t ALIGN(ii_nondc_16x16[3 * 16 * 16], 64); + uint8_t ALIGN(ii_nondc_8x32 [3 * 8 * 32], 64); + uint8_t ALIGN(ii_nondc_8x16 [3 * 8 * 16], 64); + uint8_t ALIGN(ii_nondc_8x8 [3 * 8 * 8], 64); + uint8_t ALIGN(ii_nondc_4x16 [3 * 4 * 16], 64); + uint8_t ALIGN(ii_nondc_4x8 [3 * 4 * 8], 32); + uint8_t ALIGN(ii_nondc_4x4 [3 * 4 * 4], 16); +} Dav1dMasks; + +#define II_MASK(c, bs, b) \ + ((const uint8_t*)((uintptr_t)&dav1d_masks + \ + (size_t)((b)->interintra_type == INTER_INTRA_BLEND ? \ + dav1d_masks.offsets[c][(bs)-BS_32x32].ii[(b)->interintra_mode] : \ + dav1d_masks.offsets[c][(bs)-BS_32x32].wedge[0][(b)->wedge_idx]) * 8)) + +#define WEDGE_MASK(c, bs, sign, idx) \ + ((const uint8_t*)((uintptr_t)&dav1d_masks + \ + (size_t)dav1d_masks.offsets[c][(bs)-BS_32x32].wedge[sign][idx] * 8)) + +EXTERN Dav1dMasks dav1d_masks; + +void dav1d_init_ii_wedge_masks(void); + +#endif /* DAV1D_SRC_WEDGE_H */ diff --git a/3rdparty/dav1d/src/win32/thread.c b/3rdparty/dav1d/src/win32/thread.c new file mode 100644 index 0000000..b89bd6b --- /dev/null +++ b/3rdparty/dav1d/src/win32/thread.c @@ -0,0 +1,99 @@ +/* + * Copyright © 2018-2021, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +#include "config.h" + +#if defined(_WIN32) + +#include +#include +#include + +#include "common/attributes.h" + +#include "src/thread.h" + +static HRESULT (WINAPI *set_thread_description)(HANDLE, PCWSTR); + +COLD void dav1d_init_thread(void) { +#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) + HANDLE kernel32 = GetModuleHandleW(L"kernel32.dll"); + if (kernel32) + set_thread_description = + (void*)GetProcAddress(kernel32, "SetThreadDescription"); +#endif +} + +#undef dav1d_set_thread_name +COLD void dav1d_set_thread_name(const wchar_t *const name) { + if (set_thread_description) /* Only available since Windows 10 1607 */ + set_thread_description(GetCurrentThread(), name); +} + +static COLD unsigned __stdcall thread_entrypoint(void *const data) { + pthread_t *const t = data; + t->arg = t->func(t->arg); + return 0; +} + +COLD int dav1d_pthread_create(pthread_t *const thread, + const pthread_attr_t *const attr, + void *(*const func)(void*), void *const arg) +{ + const unsigned stack_size = attr ? attr->stack_size : 0; + thread->func = func; + thread->arg = arg; + thread->h = (HANDLE)_beginthreadex(NULL, stack_size, thread_entrypoint, thread, + STACK_SIZE_PARAM_IS_A_RESERVATION, NULL); + return !thread->h; +} + +COLD int dav1d_pthread_join(pthread_t *const thread, void **const res) { + if (WaitForSingleObject(thread->h, INFINITE)) + return 1; + + if (res) + *res = thread->arg; + + return !CloseHandle(thread->h); +} + +COLD int dav1d_pthread_once(pthread_once_t *const once_control, + void (*const init_routine)(void)) +{ + BOOL pending = FALSE; + + if (InitOnceBeginInitialize(once_control, 0, &pending, NULL) != TRUE) + return 1; + + if (pending == TRUE) + init_routine(); + + return !InitOnceComplete(once_control, 0, NULL); +} + +#endif diff --git a/3rdparty/dav1d/vcs_version.h b/3rdparty/dav1d/vcs_version.h new file mode 100644 index 0000000..0e20d6b --- /dev/null +++ b/3rdparty/dav1d/vcs_version.h @@ -0,0 +1,2 @@ +/* auto-generated, do not edit */ +#define DAV1D_VERSION "1.5.3" diff --git a/3rdparty/libavif/LICENSE b/3rdparty/libavif/LICENSE new file mode 100644 index 0000000..350eb9d --- /dev/null +++ b/3rdparty/libavif/LICENSE @@ -0,0 +1,387 @@ +Copyright 2019 Joe Drago. 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. + +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. + +------------------------------------------------------------------------------ + +Files: src/obu.c + +Copyright © 2018-2019, VideoLAN and dav1d authors +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. + +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 OWNER 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. + +------------------------------------------------------------------------------ + +Files: third_party/iccjpeg/* + +In plain English: + +1. We don't promise that this software works. (But if you find any bugs, + please let us know!) +2. You can use this software for whatever you want. You don't have to pay us. +3. You may not pretend that you wrote this software. If you use it in a + program, you must acknowledge somewhere in your documentation that + you've used the IJG code. + +In legalese: + +The authors make NO WARRANTY or representation, either express or implied, +with respect to this software, its quality, accuracy, merchantability, or +fitness for a particular purpose. This software is provided "AS IS", and you, +its user, assume the entire risk as to its quality and accuracy. + +This software is copyright (C) 1991-2013, Thomas G. Lane, Guido Vollbeding. +All Rights Reserved except as specified below. + +Permission is hereby granted to use, copy, modify, and distribute this +software (or portions thereof) for any purpose, without fee, subject to these +conditions: +(1) If any part of the source code for this software is distributed, then this +README file must be included, with this copyright and no-warranty notice +unaltered; and any additions, deletions, or changes to the original files +must be clearly indicated in accompanying documentation. +(2) If only executable code is distributed, then the accompanying +documentation must state that "this software is based in part on the work of +the Independent JPEG Group". +(3) Permission for use of this software is granted only if the user accepts +full responsibility for any undesirable consequences; the authors accept +NO LIABILITY for damages of any kind. + +These conditions apply to any software derived from or based on the IJG code, +not just to the unmodified library. If you use our work, you ought to +acknowledge us. + +Permission is NOT granted for the use of any IJG author's name or company name +in advertising or publicity relating to this software or products derived from +it. This software may be referred to only as "the Independent JPEG Group's +software". + +We specifically permit and encourage the use of this software as the basis of +commercial products, provided that all warranty or liability claims are +assumed by the product vendor. + + +The Unix configuration script "configure" was produced with GNU Autoconf. +It is copyright by the Free Software Foundation but is freely distributable. +The same holds for its supporting scripts (config.guess, config.sub, +ltmain.sh). Another support script, install-sh, is copyright by X Consortium +but is also freely distributable. + +The IJG distribution formerly included code to read and write GIF files. +To avoid entanglement with the Unisys LZW patent, GIF reading support has +been removed altogether, and the GIF writer has been simplified to produce +"uncompressed GIFs". This technique does not use the LZW algorithm; the +resulting GIF files are larger than usual, but are readable by all standard +GIF decoders. + +We are required to state that + "The Graphics Interchange Format(c) is the Copyright property of + CompuServe Incorporated. GIF(sm) is a Service Mark property of + CompuServe Incorporated." + +------------------------------------------------------------------------------ + +Files: contrib/gdk-pixbuf/* + +Copyright 2020 Emmanuel Gil Peyrot. 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. + +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. + +------------------------------------------------------------------------------ + +Files: android_jni/gradlew* + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor 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, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + +------------------------------------------------------------------------------ + +Files: third_party/libyuv/* + +Copyright 2011 The LibYuv Project Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * 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. + + * Neither the name of Google nor the names of its contributors may + be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/libavif/include/avif/avif.h b/3rdparty/libavif/include/avif/avif.h new file mode 100644 index 0000000..28b6e2d --- /dev/null +++ b/3rdparty/libavif/include/avif/avif.h @@ -0,0 +1,1764 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#ifndef AVIF_AVIF_H +#define AVIF_AVIF_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// --------------------------------------------------------------------------- +// Export macros + +// AVIF_BUILDING_SHARED_LIBS should only be defined when libavif is being built +// as a shared library. +// AVIF_DLL should be defined if libavif is a shared library. If you are using +// libavif as a CMake dependency, through a CMake package config file or through +// pkg-config, this is defined automatically. +// +// Here's what AVIF_API will be defined as in shared build: +// | | Windows | Unix | +// | Build | __declspec(dllexport) | __attribute__((visibility("default"))) | +// | Use | __declspec(dllimport) | | +// +// For static build, AVIF_API is always defined as nothing. + +#if defined(_WIN32) +#define AVIF_HELPER_EXPORT __declspec(dllexport) +#define AVIF_HELPER_IMPORT __declspec(dllimport) +#elif defined(__GNUC__) && __GNUC__ >= 4 +#define AVIF_HELPER_EXPORT __attribute__((visibility("default"))) +#define AVIF_HELPER_IMPORT +#else +#define AVIF_HELPER_EXPORT +#define AVIF_HELPER_IMPORT +#endif + +#if defined(AVIF_DLL) +#if defined(AVIF_BUILDING_SHARED_LIBS) +#define AVIF_API AVIF_HELPER_EXPORT +#else +#define AVIF_API AVIF_HELPER_IMPORT +#endif // defined(AVIF_BUILDING_SHARED_LIBS) +#else +#define AVIF_API +#endif // defined(AVIF_DLL) + +// [[nodiscard]] requires C++17 and C23. +// +// If the -std=c2x or -std=gnu2x option is specified, __STDC_VERSION__ is +// * 202000L in GCC 13.2.0, Clang 16.0.6, and Apple Clang 15.0.0; or +// * 202311L in Clang 19.0.0git. +// If the /std:clatest option is specified, __STDC_VERSION__ is +// * 202312L in Microsoft Visual Studio 17.10.5. +#if (defined(__cplusplus) && __cplusplus >= 201703L) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 202000L) +#define AVIF_NODISCARD [[nodiscard]] +#else +// Starting with 3.9, clang allows defining the warn_unused_result attribute for enums. +#if defined(__clang__) && defined(__has_attribute) && ((__clang_major__ << 8) | __clang_minor__) >= ((3 << 8) | 9) +#if __has_attribute(warn_unused_result) +#define AVIF_NODISCARD __attribute__((warn_unused_result)) +#else +#define AVIF_NODISCARD +#endif +#else +#define AVIF_NODISCARD +#endif +#endif + +// --------------------------------------------------------------------------- +// Constants + +// AVIF_VERSION_DEVEL should always be 0 for official releases / version tags, +// and non-zero during development of the next release. This should allow for +// downstream projects to do greater-than preprocessor checks on AVIF_VERSION +// to leverage in-development code without breaking their stable builds. +#define AVIF_VERSION_MAJOR 1 +#define AVIF_VERSION_MINOR 4 +#define AVIF_VERSION_PATCH 2 +#define AVIF_VERSION_DEVEL 1 +#define AVIF_VERSION \ + ((AVIF_VERSION_MAJOR * 1000000) + (AVIF_VERSION_MINOR * 10000) + (AVIF_VERSION_PATCH * 100) + AVIF_VERSION_DEVEL) + +typedef int avifBool; +#define AVIF_TRUE 1 +#define AVIF_FALSE 0 + +#define AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE 256 + +// A reasonable default for maximum image size (in pixel count) to avoid out-of-memory errors or +// integer overflow in (32-bit) int or unsigned int arithmetic operations. +#define AVIF_DEFAULT_IMAGE_SIZE_LIMIT (16384 * 16384) + +// A reasonable default for maximum image dimension (width or height). +#define AVIF_DEFAULT_IMAGE_DIMENSION_LIMIT 32768 + +// a 12 hour AVIF image sequence, running at 60 fps (a basic sanity check as this is quite ridiculous) +#define AVIF_DEFAULT_IMAGE_COUNT_LIMIT (12 * 3600 * 60) + +#define AVIF_QUALITY_DEFAULT -1 +#define AVIF_QUALITY_WORST 0 +#define AVIF_QUALITY_BEST 100 +#define AVIF_QUALITY_LOSSLESS 100 + +#define AVIF_QUANTIZER_LOSSLESS 0 +#define AVIF_QUANTIZER_BEST_QUALITY 0 +#define AVIF_QUANTIZER_WORST_QUALITY 63 + +#define AVIF_PLANE_COUNT_YUV 3 + +#define AVIF_SPEED_DEFAULT -1 +#define AVIF_SPEED_SLOWEST 0 +#define AVIF_SPEED_FASTEST 10 + +// This value is used to indicate that an animated AVIF file has to be repeated infinitely. +#define AVIF_REPETITION_COUNT_INFINITE -1 +// This value is used if an animated AVIF file does not have repetitions specified using an EditList box. Applications can choose +// to handle this case however they want. +#define AVIF_REPETITION_COUNT_UNKNOWN -2 + +// The number of spatial layers in AV1, with spatial_id = 0..3. +#define AVIF_MAX_AV1_LAYER_COUNT 4 + +typedef enum avifPlanesFlag +{ + AVIF_PLANES_YUV = (1 << 0), + AVIF_PLANES_A = (1 << 1), + + AVIF_PLANES_ALL = 0xff +} avifPlanesFlag; +typedef uint32_t avifPlanesFlags; + +typedef enum avifChannelIndex +{ + // These can be used as the index for the yuvPlanes and yuvRowBytes arrays in avifImage. + AVIF_CHAN_Y = 0, + AVIF_CHAN_U = 1, + AVIF_CHAN_V = 2, + + // This may not be used in yuvPlanes and yuvRowBytes, but is available for use with avifImagePlane(). + AVIF_CHAN_A = 3 +} avifChannelIndex; + +// --------------------------------------------------------------------------- +// Version + +AVIF_API const char * avifVersion(void); +AVIF_API void avifCodecVersions(char outBuffer[256]); +AVIF_API unsigned int avifLibYUVVersion(void); // returns 0 if libavif wasn't compiled with libyuv support + +// --------------------------------------------------------------------------- +// Memory management + +// Returns NULL on memory allocation failure or if size is 0. +AVIF_API void * avifAlloc(size_t size); +AVIF_API void avifFree(void * p); + +// --------------------------------------------------------------------------- +// avifResult + +typedef enum AVIF_NODISCARD avifResult +{ + AVIF_RESULT_OK = 0, + AVIF_RESULT_UNKNOWN_ERROR = 1, + AVIF_RESULT_INVALID_FTYP = 2, + AVIF_RESULT_NO_CONTENT = 3, + AVIF_RESULT_NO_YUV_FORMAT_SELECTED = 4, + AVIF_RESULT_REFORMAT_FAILED = 5, + AVIF_RESULT_UNSUPPORTED_DEPTH = 6, + AVIF_RESULT_ENCODE_COLOR_FAILED = 7, + AVIF_RESULT_ENCODE_ALPHA_FAILED = 8, + AVIF_RESULT_BMFF_PARSE_FAILED = 9, + AVIF_RESULT_MISSING_IMAGE_ITEM = 10, + AVIF_RESULT_DECODE_COLOR_FAILED = 11, + AVIF_RESULT_DECODE_ALPHA_FAILED = 12, + AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH = 13, + AVIF_RESULT_ISPE_SIZE_MISMATCH = 14, + AVIF_RESULT_NO_CODEC_AVAILABLE = 15, + AVIF_RESULT_NO_IMAGES_REMAINING = 16, + AVIF_RESULT_INVALID_EXIF_PAYLOAD = 17, + AVIF_RESULT_INVALID_IMAGE_GRID = 18, + AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION = 19, + AVIF_RESULT_TRUNCATED_DATA = 20, + AVIF_RESULT_IO_NOT_SET = 21, // the avifIO field of avifDecoder is not set + AVIF_RESULT_IO_ERROR = 22, + AVIF_RESULT_WAITING_ON_IO = 23, // similar to EAGAIN/EWOULDBLOCK, this means the avifIO doesn't have necessary data available yet + AVIF_RESULT_INVALID_ARGUMENT = 24, // an argument passed into this function is invalid + AVIF_RESULT_NOT_IMPLEMENTED = 25, // a requested code path is not (yet) implemented + AVIF_RESULT_OUT_OF_MEMORY = 26, + AVIF_RESULT_CANNOT_CHANGE_SETTING = 27, // a setting that can't change is changed during encoding + AVIF_RESULT_INCOMPATIBLE_IMAGE = 28, // the image is incompatible with already encoded images + AVIF_RESULT_INTERNAL_ERROR = 29, // some invariants have not been satisfied (likely a bug in libavif) + AVIF_RESULT_ENCODE_GAIN_MAP_FAILED = 30, + AVIF_RESULT_DECODE_GAIN_MAP_FAILED = 31, + AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE = 32, + AVIF_RESULT_ENCODE_SAMPLE_TRANSFORM_FAILED = 33, + AVIF_RESULT_DECODE_SAMPLE_TRANSFORM_FAILED = 34, + + // Kept for backward compatibility; please use the symbols above instead. + AVIF_RESULT_NO_AV1_ITEMS_FOUND = AVIF_RESULT_MISSING_IMAGE_ITEM +} avifResult; + +AVIF_API const char * avifResultToString(avifResult result); + +// --------------------------------------------------------------------------- +// avifHeaderFormat + +// Bit flag for selecting container strategies when encoding an image. +typedef enum avifHeaderFormat +{ + AVIF_HEADER_DEFAULT = 0x0, +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + // AVIF file with a "mif3" brand and a MinimizedImageBox to reduce the encoded file size. + // This is based on the w24144 "Low-overhead image file format" MPEG proposal for HEIF. + // WARNING: Experimental feature. Produces files that are incompatible with older decoders. + // If this flag is omitted or if MinimizedImageBox cannot be used at encoding, falls back to an + // AVIF file with an "avif" brand, a MetaBox and all its required boxes for maximum compatibility. + AVIF_HEADER_MINI = 0x1, +#endif +#if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI) + // Use the full syntax of the PixelInformationProperty from HEIF 3rd edition Amendment 2. + // WARNING: Experimental feature. Produces files that may be incompatible with older decoders. + // Only relevant if a MetaBox is used. No effect if a MinimizedImageBox is used. + AVIF_HEADER_EXTENDED_PIXI = 0x2, +#endif + + // Deprecated. + AVIF_HEADER_FULL = AVIF_HEADER_DEFAULT, +} avifHeaderFormat; +typedef int avifHeaderFormatFlags; + +// --------------------------------------------------------------------------- +// avifROData/avifRWData: Generic raw memory storage + +typedef struct avifROData +{ + const uint8_t * data; + size_t size; +} avifROData; + +// Note: Use avifRWDataFree() if any avif*() function populates one of these. + +typedef struct avifRWData +{ + uint8_t * data; + size_t size; +} avifRWData; + +// clang-format off +// Initialize avifROData/avifRWData on the stack with this +#define AVIF_DATA_EMPTY { NULL, 0 } +// clang-format on + +// The avifRWData input must be zero-initialized before being manipulated with these functions. +// If AVIF_RESULT_OUT_OF_MEMORY is returned, raw is left unchanged. +AVIF_API avifResult avifRWDataRealloc(avifRWData * raw, size_t newSize); +AVIF_API avifResult avifRWDataSet(avifRWData * raw, const uint8_t * data, size_t len); +AVIF_API void avifRWDataFree(avifRWData * raw); + +// --------------------------------------------------------------------------- +// Metadata + +// Validates the first bytes of the Exif payload and finds the TIFF header offset (up to UINT32_MAX). +AVIF_API avifResult avifGetExifTiffHeaderOffset(const uint8_t * exif, size_t exifSize, size_t * offset); +// Returns the offset to the Exif 8-bit orientation value and AVIF_RESULT_OK, or an error. +// If the offset is set to exifSize, there was no parsing error but no orientation tag was found. +AVIF_API avifResult avifGetExifOrientationOffset(const uint8_t * exif, size_t exifSize, size_t * offset); + +// --------------------------------------------------------------------------- +// avifPixelFormat +// +// Note to libavif maintainers: The lookup tables in avifImageYUVToRGBLibYUV +// rely on the ordering of this enum values for their correctness. So changing +// the values in this enum will require auditing avifImageYUVToRGBLibYUV for +// correctness. +typedef enum avifPixelFormat +{ + // No YUV pixels are present. Alpha plane can still be present. + AVIF_PIXEL_FORMAT_NONE = 0, + + AVIF_PIXEL_FORMAT_YUV444, + AVIF_PIXEL_FORMAT_YUV422, + AVIF_PIXEL_FORMAT_YUV420, + AVIF_PIXEL_FORMAT_YUV400, + AVIF_PIXEL_FORMAT_COUNT +} avifPixelFormat; +AVIF_API const char * avifPixelFormatToString(avifPixelFormat format); + +typedef struct avifPixelFormatInfo +{ + avifBool monochrome; + int chromaShiftX; + int chromaShiftY; +} avifPixelFormatInfo; + +// Returns the avifPixelFormatInfo depending on the avifPixelFormat. +// When monochrome is AVIF_TRUE, chromaShiftX and chromaShiftY are set to 1 according to the AV1 specification but they should be ignored. +// +// Note: This function implements the second table on page 119 of the AV1 specification version 1.0.0 with Errata 1. +// For monochrome 4:0:0, subsampling_x and subsampling are specified as 1 to allow +// an AV1 implementation that only supports profile 0 to hardcode subsampling_x and subsampling_y to 1. +AVIF_API void avifGetPixelFormatInfo(avifPixelFormat format, avifPixelFormatInfo * info); + +// --------------------------------------------------------------------------- +// avifChromaSamplePosition + +typedef enum avifChromaSamplePosition +{ + AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN = 0, + AVIF_CHROMA_SAMPLE_POSITION_VERTICAL = 1, + AVIF_CHROMA_SAMPLE_POSITION_COLOCATED = 2, + AVIF_CHROMA_SAMPLE_POSITION_RESERVED = 3 +} avifChromaSamplePosition; + +// --------------------------------------------------------------------------- +// avifRange + +typedef enum avifRange +{ + // avifRange is only applicable to YUV planes. RGB and alpha planes are always full range. + AVIF_RANGE_LIMITED = 0, /**<- Y [16..235], UV [16..240] (bit depth 8) */ + /**<- Y [64..940], UV [64..960] (bit depth 10) */ + /**<- Y [256..3760], UV [256..3840] (bit depth 12) */ + AVIF_RANGE_FULL = 1 /**<- [0..255] (bit depth 8) */ + /**<- [0..1023] (bit depth 10) */ + /**<- [0..4095] (bit depth 12) */ +} avifRange; + +// --------------------------------------------------------------------------- +// CICP enums - https://www.itu.int/rec/T-REC-H.273-201612-S/en + +enum +{ + // This is actually reserved, but libavif uses it as a sentinel value. + AVIF_COLOR_PRIMARIES_UNKNOWN = 0, + + AVIF_COLOR_PRIMARIES_BT709 = 1, + AVIF_COLOR_PRIMARIES_SRGB = 1, + AVIF_COLOR_PRIMARIES_IEC61966_2_4 = 1, + AVIF_COLOR_PRIMARIES_UNSPECIFIED = 2, + AVIF_COLOR_PRIMARIES_BT470M = 4, + AVIF_COLOR_PRIMARIES_BT470BG = 5, + AVIF_COLOR_PRIMARIES_BT601 = 6, + AVIF_COLOR_PRIMARIES_SMPTE240 = 7, + AVIF_COLOR_PRIMARIES_GENERIC_FILM = 8, + AVIF_COLOR_PRIMARIES_BT2020 = 9, + AVIF_COLOR_PRIMARIES_BT2100 = 9, + AVIF_COLOR_PRIMARIES_XYZ = 10, + AVIF_COLOR_PRIMARIES_SMPTE431 = 11, + AVIF_COLOR_PRIMARIES_SMPTE432 = 12, + AVIF_COLOR_PRIMARIES_DCI_P3 = 12, + AVIF_COLOR_PRIMARIES_EBU3213 = 22 +}; +typedef uint16_t avifColorPrimaries; // AVIF_COLOR_PRIMARIES_* + +// outPrimaries: rX, rY, gX, gY, bX, bY, wX, wY +AVIF_API void avifColorPrimariesGetValues(avifColorPrimaries acp, float outPrimaries[8]); +AVIF_API avifColorPrimaries avifColorPrimariesFind(const float inPrimaries[8], const char ** outName); + +enum +{ + // This is actually reserved, but libavif uses it as a sentinel value. + AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN = 0, + + AVIF_TRANSFER_CHARACTERISTICS_BT709 = 1, + AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED = 2, + AVIF_TRANSFER_CHARACTERISTICS_BT470M = 4, // 2.2 gamma + AVIF_TRANSFER_CHARACTERISTICS_BT470BG = 5, // 2.8 gamma + AVIF_TRANSFER_CHARACTERISTICS_BT601 = 6, + AVIF_TRANSFER_CHARACTERISTICS_SMPTE240 = 7, + AVIF_TRANSFER_CHARACTERISTICS_LINEAR = 8, + AVIF_TRANSFER_CHARACTERISTICS_LOG100 = 9, + AVIF_TRANSFER_CHARACTERISTICS_LOG100_SQRT10 = 10, + AVIF_TRANSFER_CHARACTERISTICS_IEC61966 = 11, + AVIF_TRANSFER_CHARACTERISTICS_BT1361 = 12, + AVIF_TRANSFER_CHARACTERISTICS_SRGB = 13, + AVIF_TRANSFER_CHARACTERISTICS_BT2020_10BIT = 14, + AVIF_TRANSFER_CHARACTERISTICS_BT2020_12BIT = 15, + AVIF_TRANSFER_CHARACTERISTICS_PQ = 16, // Perceptual Quantizer (HDR); BT.2100 PQ + AVIF_TRANSFER_CHARACTERISTICS_SMPTE2084 = 16, + AVIF_TRANSFER_CHARACTERISTICS_SMPTE428 = 17, + AVIF_TRANSFER_CHARACTERISTICS_HLG = 18 // Hybrid Log-Gamma (HDR); ARIB STD-B67; BT.2100 HLG +}; +typedef uint16_t avifTransferCharacteristics; // AVIF_TRANSFER_CHARACTERISTICS_* + +// If the given transfer characteristics can be expressed with a simple gamma value, sets 'gamma' +// to that value and returns AVIF_RESULT_OK. Returns an error otherwise. +AVIF_API avifResult avifTransferCharacteristicsGetGamma(avifTransferCharacteristics atc, float * gamma); +AVIF_API avifTransferCharacteristics avifTransferCharacteristicsFindByGamma(float gamma); + +enum +{ + AVIF_MATRIX_COEFFICIENTS_IDENTITY = 0, + AVIF_MATRIX_COEFFICIENTS_BT709 = 1, + AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED = 2, + AVIF_MATRIX_COEFFICIENTS_FCC = 4, + AVIF_MATRIX_COEFFICIENTS_BT470BG = 5, + AVIF_MATRIX_COEFFICIENTS_BT601 = 6, + AVIF_MATRIX_COEFFICIENTS_SMPTE240 = 7, + AVIF_MATRIX_COEFFICIENTS_YCGCO = 8, + AVIF_MATRIX_COEFFICIENTS_BT2020_NCL = 9, + AVIF_MATRIX_COEFFICIENTS_BT2020_CL = 10, + AVIF_MATRIX_COEFFICIENTS_SMPTE2085 = 11, + AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL = 12, + AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL = 13, + AVIF_MATRIX_COEFFICIENTS_ICTCP = 14, + AVIF_MATRIX_COEFFICIENTS_YCGCO_RE = 16, // Added to libavif in Feb 2025 + AVIF_MATRIX_COEFFICIENTS_YCGCO_RO = 17, // Added to libavif in Feb 2025 + AVIF_MATRIX_COEFFICIENTS_LAST +}; +typedef uint16_t avifMatrixCoefficients; // AVIF_MATRIX_COEFFICIENTS_* + +// --------------------------------------------------------------------------- +// avifDiagnostics + +typedef struct avifDiagnostics +{ + // Upon receiving an error from any non-const libavif API call, if the toplevel structure used + // in the API call (avifDecoder, avifEncoder) contains a diag member, this buffer may be + // populated with a NULL-terminated, freeform error string explaining the first encountered error in + // more detail. It will be cleared at the beginning of every non-const API call. + // + // Note: If an error string contains the "[Strict]" prefix, it means that you encountered an + // error that only occurs during strict decoding. If you disable strict mode, you will no + // longer encounter this error. + char error[AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE]; +} avifDiagnostics; + +AVIF_API void avifDiagnosticsClearError(avifDiagnostics * diag); + +// --------------------------------------------------------------------------- +// Fraction utilities + +typedef struct avifFraction +{ + int32_t n; + int32_t d; +} avifFraction; + +typedef struct avifSignedFraction +{ + int32_t n; + uint32_t d; +} avifSignedFraction; + +typedef struct avifUnsignedFraction +{ + uint32_t n; + uint32_t d; +} avifUnsignedFraction; + +// Creates an int32/uint32 fraction that is approximately equal to 'v'. +// Returns AVIF_FALSE if 'v' is NaN or abs(v) is > INT32_MAX. +AVIF_NODISCARD AVIF_API avifBool avifDoubleToSignedFraction(double v, avifSignedFraction * fraction); +// Creates a uint32/uint32 fraction that is approximately equal to 'v'. +// Returns AVIF_FALSE if 'v' is < 0 or > UINT32_MAX or NaN. +AVIF_NODISCARD AVIF_API avifBool avifDoubleToUnsignedFraction(double v, avifUnsignedFraction * fraction); + +// --------------------------------------------------------------------------- +// Optional transformation structs + +typedef enum avifTransformFlag +{ + AVIF_TRANSFORM_NONE = 0, + + AVIF_TRANSFORM_PASP = (1 << 0), + AVIF_TRANSFORM_CLAP = (1 << 1), + AVIF_TRANSFORM_IROT = (1 << 2), + AVIF_TRANSFORM_IMIR = (1 << 3) +} avifTransformFlag; +typedef uint32_t avifTransformFlags; + +typedef struct avifPixelAspectRatioBox +{ + // 'pasp' from ISO/IEC 14496-12:2022 12.1.4.3 + + // define the relative width and height of a pixel + uint32_t hSpacing; + uint32_t vSpacing; +} avifPixelAspectRatioBox; + +// NOTE: The members of the avifCleanApertureBox struct are declared as uint32_t to match the +// unsigned int(32) type used in ISO/IEC 14496-12:2022 faithfully. However, ISO/IEC 14496-12:2022 +// 12.1.4.1 clearly interprets these values as signed int(32) and talks about them being strictly +// positive, positive, or negative. Cast these struct members to int32_t before use. See also the +// clean aperture extension in the QuickTime File Format: +// https://developer.apple.com/documentation/quicktime-file-format/clean_aperture + +typedef struct avifCleanApertureBox +{ + // 'clap' from ISO/IEC 14496-12:2022 12.1.4.3 + // Note that ISO/IEC 23000-22:2024 7.3.6.7 requires the decoded image to be upsampled to 4:4:4 before + // clean aperture is applied if a clean aperture size or offset is odd in a subsampled dimension. + // However, AV1 supports odd dimensions with chroma subsampling in those directions, so only apply the + // requirements to offsets. + + // a fractional number which defines the width of the clean aperture image + uint32_t widthN; + uint32_t widthD; + + // a fractional number which defines the height of the clean aperture image + uint32_t heightN; + uint32_t heightD; + + // a fractional number which defines the horizontal offset between the clean aperture image + // centre and the full aperture image centre. Typically 0. + uint32_t horizOffN; + uint32_t horizOffD; + + // a fractional number which defines the vertical offset between clean aperture image centre + // and the full aperture image centre. Typically 0. + uint32_t vertOffN; + uint32_t vertOffD; +} avifCleanApertureBox; + +typedef struct avifImageRotation +{ + // 'irot' from ISO/IEC 23008-12:2017 6.5.10 + + // angle * 90 specifies the angle (in anti-clockwise direction) in units of degrees. + uint8_t angle; // legal values: [0-3] +} avifImageRotation; + +typedef struct avifImageMirror +{ + // 'imir' from ISO/IEC 23008-12:2022 6.5.12: + // + // 'axis' specifies how the mirroring is performed: + // + // 0 indicates that the top and bottom parts of the image are exchanged; + // 1 specifies that the left and right parts are exchanged. + // + // NOTE In Exif, orientation tag can be used to signal mirroring operations. Exif + // orientation tag 4 corresponds to axis = 0 of ImageMirror, and Exif orientation tag 2 + // corresponds to axis = 1 accordingly. + // + // Legal values: [0, 1] + uint8_t axis; +} avifImageMirror; + +// --------------------------------------------------------------------------- +// avifCropRect - Helper struct/functions to work with avifCleanApertureBox + +typedef struct avifCropRect +{ + uint32_t x; + uint32_t y; + uint32_t width; + uint32_t height; +} avifCropRect; + +// These will return AVIF_FALSE if the resultant values violate any standards, and if so, the output +// values are not guaranteed to be complete or correct and should not be used. +AVIF_NODISCARD AVIF_API avifBool avifCropRectFromCleanApertureBox(avifCropRect * cropRect, + const avifCleanApertureBox * clap, + uint32_t imageW, + uint32_t imageH, + avifDiagnostics * diag); +AVIF_NODISCARD AVIF_API avifBool avifCleanApertureBoxFromCropRect(avifCleanApertureBox * clap, + const avifCropRect * cropRect, + uint32_t imageW, + uint32_t imageH, + avifDiagnostics * diag); +// If this function returns true, the image must be upsampled from 4:2:0 or 4:2:2 to 4:4:4 before +// Clean Aperture values are applied. This can be done by converting the avifImage to RGB using +// avifImageYUVToRGB() and only using the cropRect region of the avifRGBImage. +AVIF_NODISCARD AVIF_API avifBool avifCropRectRequiresUpsampling(const avifCropRect * cropRect, avifPixelFormat yuvFormat); + +// Deprecated. Use avifCropRectFromCleanApertureBox() instead. +AVIF_NODISCARD AVIF_API avifBool +avifCropRectConvertCleanApertureBox(avifCropRect *, const avifCleanApertureBox *, uint32_t, uint32_t, avifPixelFormat, avifDiagnostics *); +// Deprecated. Use avifCleanApertureBoxFromCropRect() instead. +AVIF_NODISCARD AVIF_API avifBool +avifCleanApertureBoxConvertCropRect(avifCleanApertureBox *, const avifCropRect *, uint32_t, uint32_t, avifPixelFormat, avifDiagnostics *); + +// --------------------------------------------------------------------------- +// avifContentLightLevelInformationBox + +typedef struct avifContentLightLevelInformationBox +{ + // 'clli' from ISO/IEC 23000-22:2019 (MIAF) 7.4.4.2.2. The SEI message semantics written above + // each entry were originally described in ISO/IEC 23008-2:2020 (HEVC) section D.3.35, + // available at https://standards.iso.org/ittf/PubliclyAvailableStandards/ + + // Given the red, green, and blue colour primary intensities in the linear light domain for the + // location of a luma sample in a corresponding 4:4:4 representation, denoted as E_R, E_G, and E_B, + // the maximum component intensity is defined as E_Max = Max(E_R, Max(E_G, E_B)). + // The light level corresponding to the stimulus is then defined as the CIE 1931 luminance + // corresponding to equal amplitudes of E_Max for all three colour primary intensities for red, + // green, and blue (with appropriate scaling to reflect the nominal luminance level associated + // with peak white, e.g. ordinarily scaling to associate peak white with 10 000 candelas per + // square metre when transfer_characteristics is equal to 16). + + // max_content_light_level, when not equal to 0, indicates an upper bound on the maximum light + // level among all individual samples in a 4:4:4 representation of red, green, and blue colour + // primary intensities (in the linear light domain) for the pictures of the CLVS, in units of + // candelas per square metre. When equal to 0, no such upper bound is indicated by + // max_content_light_level. + uint16_t maxCLL; + + // max_pic_average_light_level, when not equal to 0, indicates an upper bound on the maximum + // average light level among the samples in a 4:4:4 representation of red, green, and blue + // colour primary intensities (in the linear light domain) for any individual picture of the + // CLVS, in units of candelas per square metre. When equal to 0, no such upper bound is + // indicated by max_pic_average_light_level. + uint16_t maxPALL; +} avifContentLightLevelInformationBox; + +// --------------------------------------------------------------------------- +// avifGainMap +// Gain Maps are a solution for a consistent and adaptive display of HDR images +// standardized in ISO 21496-1. +// +// Terms: +// base image: main image stored in the file, shown by viewers that do not support +// gain maps +// alternate image: image obtained by combining the base image and the gain map +// gain map: data structure that contains pixels and metadata used for conversion +// between the base image and the alternate image + +struct avifImage; + +// Gain map image and associated metadata. +// +// NOTE: The avifGainMap struct may be extended in a future release. Code outside the libavif +// library must allocate avifGainMap by calling the avifGainMapCreate() function. +typedef struct avifGainMap +{ + // Gain map pixels. + // Owned by the avifGainMap and gets freed when calling avifGainMapDestroy(). + // Used fields: width, height, depth, yuvFormat, yuvRange, + // yuvChromaSamplePosition, yuvPlanes, yuvRowBytes, imageOwnsYUVPlanes, + // matrixCoefficients. The colorPrimaries and transferCharacteristics fields + // shall be 2. Other fields are ignored. + struct avifImage * image; + + // Gain map metadata used to interpret and apply the gain map pixel data. + // When encoding an image grid, all metadata below shall be identical for all + // cells. + + // Parameters for converting the gain map from its image encoding to log2 space. + // gainMapLog2 = lerp(gainMapMin, gainMapMax, pow(gainMapEncoded, 1/gainMapGamma)); + // where 'lerp' is a linear interpolation function. + // Minimum value in the gain map, log2-encoded, per RGB channel. + avifSignedFraction gainMapMin[3]; + // Maximum value in the gain map, log2-encoded, per RGB channel. + avifSignedFraction gainMapMax[3]; + // Gain map gamma value with which the gain map was encoded, per RGB channel. + // For decoding, the inverse value (1/gamma) should be used. + avifUnsignedFraction gainMapGamma[3]; + + // Parameters used in gain map computation/tone mapping to avoid numerical + // instability. + // toneMappedLinear = ((baseImageLinear + baseOffset) * exp2(gainMapLog * w)) - alternateOffset; + // Where 'w' is a weight parameter based on the display's HDR capacity + // (see below). + + // Offset constants for the base image, per RGB channel. + avifSignedFraction baseOffset[3]; + // Offset constants for the alternate image, per RGB channel. + avifSignedFraction alternateOffset[3]; + + // Log2-encoded HDR headroom of the base and alternate images respectively. + // If baseHdrHeadroom is < alternateHdrHeadroom, the result of tone mapping + // for a display with an HDR headroom that is <= baseHdrHeadroom is the base + // image, and the result of tone mapping for a display with an HDR headroom >= + // alternateHdrHeadroom is the alternate image. + // Conversely, if baseHdrHeadroom is > alternateHdrHeadroom, the result of + // tone mapping for a display with an HDR headroom that is >= baseHdrHeadroom + // is the base image, and the result of tone mapping for a display with an HDR + // headroom <= alternateHdrHeadroom is the alternate image. + // For a display with a capacity between baseHdrHeadroom and alternateHdrHeadroom, + // tone mapping results in an interpolation between the base and alternate + // versions. baseHdrHeadroom and alternateHdrHeadroom can be tuned to change how + // the gain map should be applied. + // + // If 'H' is the display's current log2-encoded HDR capacity (HDR to SDR ratio), + // then the weight 'w' to apply the gain map is computed as follows: + // f = clamp((H - baseHdrHeadroom) / + // (alternateHdrHeadroom - baseHdrHeadroom), 0, 1); + // w = sign(alternateHdrHeadroom - baseHdrHeadroom) * f + avifUnsignedFraction baseHdrHeadroom; + avifUnsignedFraction alternateHdrHeadroom; + + // True if tone mapping should be performed in the color space of the + // base image. If false, the color space of the alternate image should + // be used. + avifBool useBaseColorSpace; + + // Colorimetry of the alternate image (ICC profile and/or CICP information + // of the alternate image that the gain map was created from). + avifRWData altICC; + avifColorPrimaries altColorPrimaries; + avifTransferCharacteristics altTransferCharacteristics; + avifMatrixCoefficients altMatrixCoefficients; + avifRange altYUVRange; + + // Hint on the approximate amount of colour resolution available after fully + // applying the gain map ('pixi' box content of the alternate image that the + // gain map was created from). + uint32_t altDepth; + uint32_t altPlaneCount; + + // Optimal viewing conditions of the alternate image ('clli' box content + // of the alternate image that the gain map was created from). + avifContentLightLevelInformationBox altCLLI; + + // Version 1.2.0 ends here. Add any new members after this line. +} avifGainMap; + +// Allocates a gain map. Returns NULL if a memory allocation failed. +// The 'image' field is NULL by default and must be allocated separately. +AVIF_API avifGainMap * avifGainMapCreate(void); +// Frees a gain map, including the 'image' field if non NULL. +AVIF_API void avifGainMapDestroy(avifGainMap * gainMap); + +// --------------------------------------------------------------------------- + +// Sample Transforms are a mechanism introduced in the version 1.2.0 of the +// AVIF specification. They enable the creation of derived images that are +// constructed from multiple input images according to a mathematical formula. +// This can for example be used to enable higher bit depths even when the +// underlying codec does not natively support 16-bit or higher precision. +// See https://aomediacodec.github.io/av1-avif/v1.2.0.html#sample-transform. +typedef enum avifSampleTransformRecipe +{ + AVIF_SAMPLE_TRANSFORM_NONE, + // Encode the 8 most significant bits of each input image sample losslessly + // into a base image. The remaining 8 least significant bits are encoded in + // a separate hidden image item. The two are combined at decoding into one + // image with the same bit depth as the original image. It is backward + // compatible in the sense that it is possible to decode only the base image + // (ignoring the hidden image item), leading to a valid image but with + // precision loss (16-bit samples truncated to the 8 most significant bits). + AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B, + // Encode the 12 most significant bits of each input image sample losslessly + // into a base image. The remaining 4 least significant bits are encoded in + // a separate hidden image item. The two are combined at decoding into one + // image with the same bit depth as the original image. It is backward + // compatible in the sense that it is possible to decode only the base image + // (ignoring the hidden image item), leading to a valid image but with + // precision loss (16-bit samples truncated to the 12 most significant + // bits). + AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B, + // Encode the 12 most significant bits of each input image sample lossily or + // losslessly into a base image. The difference between the original and + // decoded values of these samples is encoded as a separate 8-bit hidden + // image item. The two are combined at decoding into one image with the same + // bit depth as the original image. It is backward compatible in the sense + // that it is possible to decode only the base image (ignoring the hidden + // image item), leading to a valid image but with loss due to precision + // truncation and/or compression. + AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_8B_OVERLAP_4B +} avifSampleTransformRecipe; + +// --------------------------------------------------------------------------- +// Opaque image item properties + +// This struct represents an opaque ItemProperty (Box) or ItemFullProperty (FullBox) in ISO/IEC 14496-12. +typedef struct avifImageItemProperty +{ + uint8_t boxtype[4]; // boxtype as defined in ISO/IEC 14496-12. + uint8_t usertype[16]; // Universally Unique IDentifier as defined in IETF RFC 4122 and ISO/IEC 9834-8. + // Used only when boxtype is "uuid". + avifRWData boxPayload; // BoxPayload as defined in ISO/IEC 14496-12. + // Starts with the version (1 byte) and flags (3 bytes) fields in case of a FullBox. +} avifImageItemProperty; + +// --------------------------------------------------------------------------- +// avifImage + +// NOTE: The avifImage struct may be extended in a future release. Code outside the libavif library +// must allocate avifImage by calling the avifImageCreate() or avifImageCreateEmpty() function. +typedef struct avifImage +{ + // Image information + uint32_t width; + uint32_t height; + uint32_t depth; // all planes must share this depth; if depth>8, all planes are uint16_t internally + + avifPixelFormat yuvFormat; + avifRange yuvRange; + avifChromaSamplePosition yuvChromaSamplePosition; + uint8_t * yuvPlanes[AVIF_PLANE_COUNT_YUV]; + uint32_t yuvRowBytes[AVIF_PLANE_COUNT_YUV]; + avifBool imageOwnsYUVPlanes; + + uint8_t * alphaPlane; + uint32_t alphaRowBytes; + avifBool imageOwnsAlphaPlane; + avifBool alphaPremultiplied; + + // ICC Profile + avifRWData icc; + + // CICP information: + // These are stored in the AV1 payload and used to signal YUV conversion. Additionally, if an + // ICC profile is not specified, these will be stored in the AVIF container's `colr` box with + // a type of `nclx`. If your system supports ICC profiles, be sure to check for the existence + // of one (avifImage.icc) before relying on the values listed here! + avifColorPrimaries colorPrimaries; + avifTransferCharacteristics transferCharacteristics; + avifMatrixCoefficients matrixCoefficients; + + // CLLI information: + // Content Light Level Information. Used to represent maximum and average light level of an + // image. Useful for tone mapping HDR images, especially when using transfer characteristics + // SMPTE2084 (PQ). The default value of (0, 0) means the content light level information is + // unknown or unavailable, and will cause libavif to avoid writing a clli box for it. + avifContentLightLevelInformationBox clli; + + // Transformations - These metadata values are encoded/decoded when transformFlags are set + // appropriately, but do not impact/adjust the actual pixel buffers used (images won't be + // pre-cropped or mirrored upon decode). Basic explanations from the standards are offered in + // comments above, but for detailed explanations, please refer to the HEIF standard (ISO/IEC + // 23008-12:2017) and the BMFF standard (ISO/IEC 14496-12:2022). + // + // To encode any of these boxes, set the values in the associated box, then enable the flag in + // transformFlags. On decode, only honor the values in boxes with the associated transform flag set. + // These also apply to gainMap->image, if any. + avifTransformFlags transformFlags; + avifPixelAspectRatioBox pasp; + avifCleanApertureBox clap; + avifImageRotation irot; + avifImageMirror imir; + + // Metadata - set with avifImageSetMetadata*() before write, check .size>0 for existence after read + avifRWData exif; // exif_payload chunk from the ExifDataBlock specified in ISO/IEC 23008-12:2022 Section A.2.1. + // The value of the 4-byte exif_tiff_header_offset field, which is not part of this avifRWData + // byte sequence, can be retrieved by calling avifGetExifTiffHeaderOffset(avifImage.exif). + avifRWData xmp; + + // Version 1.0.0 ends here. + + // Other properties attached to this image item (primary or gainmap). + // At decoding: Forwarded here as opaque byte sequences by the avifDecoder. + // At encoding: Set using avifImageAddOpaqueProperty() or avifImageAddUUIDProperty() and written by the + // avifEncoder as non-essential properties in the order that they are added to the image. + avifImageItemProperty * properties; // NULL only if numProperties is 0. + size_t numProperties; + + // Gain map image and metadata. NULL if no gain map is present. + // Owned by the avifImage and gets freed when calling avifImageDestroy(). + // gainMap->image->transformFlags is always AVIF_TRANSFORM_NONE. + avifGainMap * gainMap; + + // Version 1.2.0 ends here. Add any new members after this line. +} avifImage; + +// avifImageCreate() and avifImageCreateEmpty() return NULL if arguments are invalid or if a memory allocation failed. +AVIF_NODISCARD AVIF_API avifImage * avifImageCreate(uint32_t width, uint32_t height, uint32_t depth, avifPixelFormat yuvFormat); +AVIF_NODISCARD AVIF_API avifImage * avifImageCreateEmpty(void); // helper for making an image to decode into +// Performs a deep copy of an image, including all metadata and planes, and the gain map metadata/planes if present. +AVIF_API avifResult avifImageCopy(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes); +// Performs a shallow copy of a rectangular area of an image. 'dstImage' does not own the planes. +// Ignores the gainMap field. +AVIF_API avifResult avifImageSetViewRect(avifImage * dstImage, const avifImage * srcImage, const avifCropRect * rect); +AVIF_API void avifImageDestroy(avifImage * image); + +AVIF_API avifResult avifImageSetProfileICC(avifImage * image, const uint8_t * icc, size_t iccSize); +// Sets Exif metadata. Attempts to parse the Exif metadata for Exif orientation. Sets +// image->transformFlags, image->irot and image->imir if the Exif metadata is parsed successfully, +// otherwise leaves image->transformFlags, image->irot and image->imir unchanged. +// Warning: If the Exif payload is set and invalid, avifEncoderWrite() may return AVIF_RESULT_INVALID_EXIF_PAYLOAD. +AVIF_API avifResult avifImageSetMetadataExif(avifImage * image, const uint8_t * exif, size_t exifSize); +// Sets XMP metadata. +AVIF_API avifResult avifImageSetMetadataXMP(avifImage * image, const uint8_t * xmp, size_t xmpSize); + +// Allocate/free/steal planes. These functions ignore the gainMap field. +AVIF_API avifResult avifImageAllocatePlanes(avifImage * image, avifPlanesFlags planes); // Ignores any pre-existing planes +AVIF_API void avifImageFreePlanes(avifImage * image, avifPlanesFlags planes); // Ignores already-freed planes +AVIF_API void avifImageStealPlanes(avifImage * dstImage, avifImage * srcImage, avifPlanesFlags planes); + +// Add arbitrary (opaque) properties to the image. +// Note: This is an advanced usage, intended for users with specific requirements who are familiar with the +// HEIF and ISO BMFF standards. Use of these functions for properties and boxes that are handled by +// libavif (e.g. ispe or meta) will likely result in invalid files, and should be avoided. +// If creating an ItemFullProperty, the version and flags values should be provided as the first four bytes of +// the data argument, and those four bytes included in the dataSize. +// Any properties will be added after the usual libavif descriptive properties, and before the libavif +// transformative properties (e.g. irot, imir, clap). Be aware that readers will apply transformative +// properties in the order they occur. An avifEncoder instance supports up to 127 unique properties. +// Users of this API should consider calling avifParse() on the resulting file (i.e. the encoder output) to +// check that the arbitrary properties have not resulted in an invalid file. +AVIF_API avifResult avifImageAddOpaqueProperty(avifImage * image, const uint8_t boxtype[4], const uint8_t * data, size_t dataSize); +// This version adds an ItemProperty (or ItemFullProperty if version and flags are provided in data argument), using +// the user extension (uuid) mechanism, see ISO/IEC 14496-12:2022 Section 4.2. The box type is set to 'uuid'. +AVIF_API avifResult avifImageAddUUIDProperty(avifImage * image, const uint8_t uuid[16], const uint8_t * data, size_t dataSize); + +// --------------------------------------------------------------------------- +// Understanding maxThreads +// +// libavif's structures and API use the setting 'maxThreads' in a few places. The intent of this +// setting is to limit concurrent thread activity/usage, not necessarily to put a hard ceiling on +// how many sleeping threads happen to exist behind the scenes. The goal of this setting is to +// ensure that at any given point during libavif's encoding or decoding, no more than *maxThreads* +// threads are simultaneously **active and taking CPU time**. +// +// As an important example, when encoding an image sequence that has an alpha channel, two +// long-lived underlying AV1 encoders must simultaneously exist (one for color, one for alpha). For +// each additional frame fed into libavif, its YUV planes are fed into one instance of the AV1 +// encoder, and its alpha plane is fed into another. These operations happen serially, so only one +// of these AV1 encoders is ever active at a time. However, the AV1 encoders might pre-create a +// pool of worker threads upon initialization, so during this process, twice the amount of worker +// threads actually simultaneously exist on the machine, but half of them are guaranteed to be +// sleeping. +// +// This design ensures that AV1 implementations are given as many threads as possible to ensure a +// speedy encode or decode, despite the complexities of occasionally needing two AV1 codec instances +// (due to alpha payloads being separate from color payloads). If your system has a hard ceiling on +// the number of threads that can ever be in flight at a given time, please account for this +// accordingly. + +// --------------------------------------------------------------------------- +// Scaling + +// Scales the YUV/A planes in-place. dstWidth and dstHeight must both be <= AVIF_DEFAULT_IMAGE_DIMENSION_LIMIT and +// dstWidth*dstHeight should be <= AVIF_DEFAULT_IMAGE_SIZE_LIMIT. +AVIF_API avifResult avifImageScale(avifImage * image, uint32_t dstWidth, uint32_t dstHeight, avifDiagnostics * diag); + +// --------------------------------------------------------------------------- +// Optional YUV<->RGB support + +// To convert to/from RGB, create an avifRGBImage on the stack, call avifRGBImageSetDefaults() on +// it, and then tweak the values inside of it accordingly. At a minimum, you should populate +// ->pixels and ->rowBytes with an appropriately sized pixel buffer, which should be at least +// (->rowBytes * ->height) bytes, where ->rowBytes is at least (->width * avifRGBImagePixelSize()). +// If you don't want to supply your own pixel buffer, you can use the +// avifRGBImageAllocatePixels()/avifRGBImageFreePixels() convenience functions. + +// avifImageRGBToYUV() and avifImageYUVToRGB() will perform depth rescaling and limited<->full range +// conversion, if necessary. Pixels in an avifRGBImage buffer are always full range, and conversion +// routines will fail if the width and height don't match the associated avifImage. + +// If libavif is built with a version of libyuv offering a fast conversion between RGB and YUV for +// the given inputs, libavif will use it. See reformat_libyuv.c for the details. +// libyuv is faster but may have slightly less precision than built-in conversion, so avoidLibYUV +// can be set to AVIF_TRUE when AVIF_CHROMA_UPSAMPLING_BEST_QUALITY or +// AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY is used, to get the most precise but slowest results. + +// Note to libavif maintainers: The lookup tables in avifImageYUVToRGBLibYUV +// rely on the ordering of this enum values for their correctness. So changing +// the values in this enum will require auditing avifImageYUVToRGBLibYUV for +// correctness. +typedef enum avifRGBFormat +{ + AVIF_RGB_FORMAT_RGB = 0, + AVIF_RGB_FORMAT_RGBA, // This is the default format set in avifRGBImageSetDefaults(). + AVIF_RGB_FORMAT_ARGB, + AVIF_RGB_FORMAT_BGR, + AVIF_RGB_FORMAT_BGRA, + AVIF_RGB_FORMAT_ABGR, + // RGB_565 format uses five bits for the red and blue components and six + // bits for the green component. Each RGB pixel is 16 bits (2 bytes), which + // is packed as follows: + // uint16_t: [r4 r3 r2 r1 r0 g5 g4 g3 g2 g1 g0 b4 b3 b2 b1 b0] + // r4 and r0 are the MSB and LSB of the red component respectively. + // g5 and g0 are the MSB and LSB of the green component respectively. + // b4 and b0 are the MSB and LSB of the blue component respectively. + // This format is only supported for YUV -> RGB conversion and when + // avifRGBImage.depth is set to 8. + AVIF_RGB_FORMAT_RGB_565, + AVIF_RGB_FORMAT_GRAY, + AVIF_RGB_FORMAT_GRAYA, + AVIF_RGB_FORMAT_AGRAY, + AVIF_RGB_FORMAT_COUNT +} avifRGBFormat; +AVIF_API uint32_t avifRGBFormatChannelCount(avifRGBFormat format); +AVIF_API avifBool avifRGBFormatHasAlpha(avifRGBFormat format); +AVIF_API avifBool avifRGBFormatIsGray(avifRGBFormat format); + +typedef enum avifChromaUpsampling +{ + AVIF_CHROMA_UPSAMPLING_AUTOMATIC = 0, // Chooses best trade off of speed/quality (uses BILINEAR libyuv if available, + // or falls back to NEAREST libyuv if available, or falls back to BILINEAR built-in) + AVIF_CHROMA_UPSAMPLING_FASTEST = 1, // Chooses speed over quality (same as NEAREST) + AVIF_CHROMA_UPSAMPLING_BEST_QUALITY = 2, // Chooses the best quality upsampling, given settings (same as BILINEAR) + AVIF_CHROMA_UPSAMPLING_NEAREST = 3, // Uses nearest-neighbor filter + AVIF_CHROMA_UPSAMPLING_BILINEAR = 4 // Uses bilinear filter +} avifChromaUpsampling; + +typedef enum avifChromaDownsampling +{ + AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC = 0, // Chooses best trade off of speed/quality (same as AVERAGE) + AVIF_CHROMA_DOWNSAMPLING_FASTEST = 1, // Chooses speed over quality (same as AVERAGE) + AVIF_CHROMA_DOWNSAMPLING_BEST_QUALITY = 2, // Chooses the best quality upsampling (same as AVERAGE) + AVIF_CHROMA_DOWNSAMPLING_AVERAGE = 3, // Uses averaging filter + AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV = 4 // Uses sharp yuv filter (libsharpyuv), available for 4:2:0 only, ignored for 4:2:2 +} avifChromaDownsampling; + +// NOTE: avifRGBImage must be initialized with avifRGBImageSetDefaults() (preferred) or memset() +// before use. +typedef struct avifRGBImage +{ + uint32_t width; // must match associated avifImage + uint32_t height; // must match associated avifImage + uint32_t depth; // legal depths [8, 10, 12, 16]. if depth>8, pixels must be uint16_t internally + avifRGBFormat format; // all channels are always full range + avifChromaUpsampling chromaUpsampling; // How to upsample from 4:2:0 or 4:2:2 UV when converting to RGB (ignored for 4:4:4 and 4:0:0). + // Ignored when converting to YUV. Defaults to AVIF_CHROMA_UPSAMPLING_AUTOMATIC. + avifChromaDownsampling chromaDownsampling; // How to downsample to 4:2:0 or 4:2:2 UV when converting from RGB (ignored for 4:4:4 and 4:0:0). + // Ignored when converting to RGB. Defaults to AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC. + avifBool avoidLibYUV; // If AVIF_FALSE and libyuv conversion between RGB and YUV (including upsampling or downsampling if any) + // is available for the avifImage/avifRGBImage combination, then libyuv is used. Default is AVIF_FALSE. + avifBool ignoreAlpha; // Used for XRGB formats, treats formats containing alpha (such as ARGB) as if they were RGB, treating + // the alpha bits as if they were all 1. + avifBool alphaPremultiplied; // indicates if RGB value is pre-multiplied by alpha. Default: false + avifBool isFloat; // indicates if RGBA values are in half float (f16) format. Valid only when depth == 16. Default: false + int maxThreads; // Number of threads to be used for the YUV to RGB conversion. Note that this value is ignored for RGB to YUV + // conversion. Setting this to zero has the same effect as setting it to one. Negative values are invalid. + // Default: 1. + + uint8_t * pixels; + uint32_t rowBytes; +} avifRGBImage; + +// Sets rgb->width, rgb->height, and rgb->depth to image->width, image->height, and image->depth. +// Sets rgb->pixels to NULL and rgb->rowBytes to 0. Sets the other fields of 'rgb' to default +// values. +AVIF_API void avifRGBImageSetDefaults(avifRGBImage * rgb, const avifImage * image); +AVIF_API uint32_t avifRGBImagePixelSize(const avifRGBImage * rgb); + +// Convenience functions. If you supply your own pixels/rowBytes, you do not need to use these. +AVIF_API avifResult avifRGBImageAllocatePixels(avifRGBImage * rgb); +AVIF_API void avifRGBImageFreePixels(avifRGBImage * rgb); + +// The main conversion functions +AVIF_API avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb); +AVIF_API avifResult avifImageYUVToRGB(const avifImage * image, avifRGBImage * rgb); + +// Premultiply handling functions. +// (Un)premultiply is automatically done by the main conversion functions above, +// so usually you don't need to call these. They are there for convenience. +AVIF_API avifResult avifRGBImagePremultiplyAlpha(avifRGBImage * rgb); +AVIF_API avifResult avifRGBImageUnpremultiplyAlpha(avifRGBImage * rgb); + +// --------------------------------------------------------------------------- +// YUV Utils + +AVIF_API int avifFullToLimitedY(uint32_t depth, int v); +AVIF_API int avifFullToLimitedUV(uint32_t depth, int v); +AVIF_API int avifLimitedToFullY(uint32_t depth, int v); +AVIF_API int avifLimitedToFullUV(uint32_t depth, int v); + +// --------------------------------------------------------------------------- +// Codec selection + +typedef enum avifCodecChoice +{ + AVIF_CODEC_CHOICE_AUTO = 0, + AVIF_CODEC_CHOICE_AOM, + AVIF_CODEC_CHOICE_DAV1D, // Decode only + AVIF_CODEC_CHOICE_LIBGAV1, // Decode only + AVIF_CODEC_CHOICE_RAV1E, // Encode only + AVIF_CODEC_CHOICE_SVT, // Encode only + AVIF_CODEC_CHOICE_AVM // Experimental (AV2) +} avifCodecChoice; + +typedef enum avifCodecFlag +{ + AVIF_CODEC_FLAG_CAN_DECODE = (1 << 0), + AVIF_CODEC_FLAG_CAN_ENCODE = (1 << 1) +} avifCodecFlag; +typedef uint32_t avifCodecFlags; + +// If this returns NULL, the codec choice/flag combination is unavailable +AVIF_API const char * avifCodecName(avifCodecChoice choice, avifCodecFlags requiredFlags); +AVIF_API avifCodecChoice avifCodecChoiceFromName(const char * name); + +// --------------------------------------------------------------------------- +// avifIO + +struct avifIO; + +// Destroy must completely destroy all child structures *and* free the avifIO object itself. +// This function pointer is optional, however, if the avifIO object isn't intended to be owned by +// a libavif encoder/decoder. +typedef void (*avifIODestroyFunc)(struct avifIO * io); + +// This function should return a block of memory that *must* remain valid until another read call to +// this avifIO struct is made (reusing a read buffer is acceptable/expected). +// +// * If offset exceeds the size of the content (past EOF), return AVIF_RESULT_IO_ERROR. +// * If offset is *exactly* at EOF, provide a 0-byte buffer and return AVIF_RESULT_OK. +// * If (offset+size) exceeds the contents' size, it must truncate the range to provide all +// bytes from the offset to EOF. +// * If the range is unavailable yet (due to network conditions or any other reason), +// return AVIF_RESULT_WAITING_ON_IO. +// * Otherwise, provide the range and return AVIF_RESULT_OK. +typedef avifResult (*avifIOReadFunc)(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out); + +typedef avifResult (*avifIOWriteFunc)(struct avifIO * io, uint32_t writeFlags, uint64_t offset, const uint8_t * data, size_t size); + +typedef struct avifIO +{ + avifIODestroyFunc destroy; + avifIOReadFunc read; + + // This is reserved for future use - but currently ignored. Set it to a null pointer. + avifIOWriteFunc write; + + // If non-zero, this is a hint to internal structures of the max size offered by the content + // this avifIO structure is reading. If it is a static memory source, it should be the size of + // the memory buffer; if it is a file, it should be the file's size. If this information cannot + // be known (as it is streamed-in), set a reasonable upper boundary here (larger than the file + // can possibly be for your environment, but within your environment's memory constraints). This + // is used for sanity checks when allocating internal buffers to protect against + // malformed/malicious files. + uint64_t sizeHint; + + // If true, *all* memory regions returned from *all* calls to read are guaranteed to be + // persistent and exist for the lifetime of the avifIO object. If false, libavif will make + // in-memory copies of samples and metadata content, and a memory region returned from read must + // only persist until the next call to read. + avifBool persistent; + + // The contents of this are defined by the avifIO implementation, and should be fully destroyed + // by the implementation of the associated destroy function, unless it isn't owned by the avifIO + // struct. It is not necessary to use this pointer in your implementation. + void * data; +} avifIO; + +// Returns NULL if the reader cannot be allocated. +AVIF_API avifIO * avifIOCreateMemoryReader(const uint8_t * data, size_t size); +// Returns NULL if the file cannot be opened or if the reader cannot be allocated. +AVIF_API avifIO * avifIOCreateFileReader(const char * filename); +AVIF_API void avifIODestroy(avifIO * io); + +// --------------------------------------------------------------------------- +// avifDecoder + +// Some encoders (including very old versions of avifenc) do not implement the AVIF standard +// perfectly, and thus create invalid files. However, these files are likely still recoverable / +// decodable, if it wasn't for the strict requirements imposed by libavif's decoder. These flags +// allow a user of avifDecoder to decide what level of strictness they want in their project. +typedef enum avifStrictFlag +{ + // Disables all strict checks. + AVIF_STRICT_DISABLED = 0, + + // Requires the PixelInformationProperty ('pixi') be present in AV1 image items. libheif v1.11.0 + // or older does not add the 'pixi' item property to AV1 image items. If you need to decode AVIF + // images encoded by libheif v1.11.0 or older, be sure to disable this bit. (This issue has been + // corrected in libheif v1.12.0.) + AVIF_STRICT_PIXI_REQUIRED = (1 << 0), + + // This demands that the values surfaced in the clap box are valid, determined by attempting to + // convert the clap box to a crop rect using avifCropRectFromCleanApertureBox(). If this + // function returns AVIF_FALSE and this strict flag is set, the decode will fail. + AVIF_STRICT_CLAP_VALID = (1 << 1), + + // Requires the ImageSpatialExtentsProperty ('ispe') be present in alpha auxiliary image items. + // avif-serialize 0.7.3 or older does not add the 'ispe' item property to alpha auxiliary image + // items. If you need to decode AVIF images encoded by the cavif encoder with avif-serialize + // 0.7.3 or older, be sure to disable this bit. (This issue has been corrected in avif-serialize + // 0.7.4.) See https://github.com/kornelski/avif-serialize/issues/3 and + // https://crbug.com/1246678. + AVIF_STRICT_ALPHA_ISPE_REQUIRED = (1 << 2), + + // Maximum strictness; enables all bits above. This is avifDecoder's default. + AVIF_STRICT_ENABLED = AVIF_STRICT_PIXI_REQUIRED | AVIF_STRICT_CLAP_VALID | AVIF_STRICT_ALPHA_ISPE_REQUIRED +} avifStrictFlag; +typedef uint32_t avifStrictFlags; + +// Useful stats related to a read/write +typedef struct avifIOStats +{ + // Size in bytes of the AV1 image item or track data containing color samples. + size_t colorOBUSize; + // Size in bytes of the AV1 image item or track data containing alpha samples. + size_t alphaOBUSize; +} avifIOStats; + +struct avifDecoderData; + +typedef enum avifDecoderSource +{ + // Honor the major brand signaled in the beginning of the file to pick between an AVIF sequence + // ('avis', tracks-based) or a single image ('avif', item-based). If the major brand is neither + // of these, prefer the AVIF sequence ('avis', tracks-based), if present. + AVIF_DECODER_SOURCE_AUTO = 0, + + // Use the primary item and the aux (alpha) item in the avif(s). + // This is where single-image avifs store their image. + AVIF_DECODER_SOURCE_PRIMARY_ITEM, + + // Use the chunks inside primary/aux tracks in the moov block. + // This is where avifs image sequences store their images. + AVIF_DECODER_SOURCE_TRACKS + + // Decode the thumbnail item. Currently unimplemented. + // AVIF_DECODER_SOURCE_THUMBNAIL_ITEM +} avifDecoderSource; + +// Information about the timing of a single image in an image sequence +typedef struct avifImageTiming +{ + uint64_t timescale; // timescale of the media (Hz) + double pts; // presentation timestamp in seconds (ptsInTimescales / timescale) + uint64_t ptsInTimescales; // presentation timestamp in "timescales" + double duration; // in seconds (durationInTimescales / timescale) + uint64_t durationInTimescales; // duration in "timescales" +} avifImageTiming; + +typedef enum avifProgressiveState +{ + // The current AVIF/Source does not offer a progressive image. This will always be the state + // for an image sequence. + AVIF_PROGRESSIVE_STATE_UNAVAILABLE = 0, + + // The current AVIF/Source offers a progressive image, but avifDecoder.allowProgressive is not + // enabled, so it will behave as if the image was not progressive and will simply decode the + // best version of this item. + AVIF_PROGRESSIVE_STATE_AVAILABLE, + + // The current AVIF/Source offers a progressive image, and avifDecoder.allowProgressive is true. + // In this state, avifDecoder.imageCount will be the count of all of the available progressive + // layers, and any specific layer can be decoded using avifDecoderNthImage() as if it was an + // image sequence, or simply using repeated calls to avifDecoderNextImage() to decode better and + // better versions of this image. + AVIF_PROGRESSIVE_STATE_ACTIVE +} avifProgressiveState; +AVIF_API const char * avifProgressiveStateToString(avifProgressiveState progressiveState); + +// Types of image content that can be decoded. +typedef enum avifImageContentTypeFlag +{ + AVIF_IMAGE_CONTENT_NONE = 0, + // Color only or alpha only is not currently supported. + AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA = (1 << 0) | (1 << 1), + AVIF_IMAGE_CONTENT_GAIN_MAP = (1 << 2), + AVIF_IMAGE_CONTENT_ALL = AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA | AVIF_IMAGE_CONTENT_GAIN_MAP, + + // Mostly used for bit depth extensions to go beyond the underlying codec capability + // (e.g. 16-bit AVIF). Not part of AVIF_IMAGE_CONTENT_ALL as this is a rare use case. + // Has no effect without AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA. + AVIF_IMAGE_CONTENT_SAMPLE_TRANSFORMS = (1 << 3), + + AVIF_IMAGE_CONTENT_DECODE_DEFAULT = AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA, +} avifImageContentTypeFlag; +typedef uint32_t avifImageContentTypeFlags; + +// AVIF decoder struct. It may be extended in a future release. Code outside the libavif +// library must allocate avifDecoder by calling the avifDecoderCreate() function, and destroy it with +// avifDecoderDestroy(). +// This struct contains three types of fields: +// * Changeable settings, which users of the API may set. +// * Output data fields, that are set by libavif and which users of the API may read. +// * Internal fields, which users of the API should ignore. +typedef struct avifDecoder +{ + // -------------------------------------------------------------------------------------------- + // Inputs (changeable decoder settings) + // Additional settings are available at the end of the struct after the version 1.1.0 end marker. + + // Defaults to AVIF_CODEC_CHOICE_AUTO: Preference determined by order in availableCodecs table (avif.c) + avifCodecChoice codecChoice; + + // Defaults to 1. If < 2, multithreading is disabled. See also 'Understanding maxThreads' above. + int maxThreads; + + // AVIF files can have multiple sets of images in them. This specifies which to decode. + // Set this via avifDecoderSetSource(). + avifDecoderSource requestedSource; + + // If this is true and a progressive AVIF is decoded, avifDecoder will behave as if the AVIF is + // an image sequence, in that it will set imageCount to the number of progressive frames + // available, and avifDecoderNextImage()/avifDecoderNthImage() will allow for specific layers + // of a progressive image to be decoded. To distinguish between a progressive AVIF and an AVIF + // image sequence, inspect avifDecoder.progressiveState. + avifBool allowProgressive; + + // If this is false, avifDecoderNextImage() will start decoding a frame only after there are + // enough input bytes to decode all of that frame. If this is true, avifDecoder will decode each + // subimage or grid cell as soon as possible. The benefits are: grid images may be partially + // displayed before being entirely available, and the overall decoding may finish earlier. + // Must be set before calling avifDecoderNextImage() or avifDecoderNthImage(). + // WARNING: Experimental feature. + avifBool allowIncremental; + + // Enable any of these to avoid reading and surfacing specific data to the decoded avifImage. + // These can be useful if your avifIO implementation heavily uses AVIF_RESULT_WAITING_ON_IO for + // streaming data, as some of these payloads are (unfortunately) packed at the end of the file, + // which will cause avifDecoderParse() to return AVIF_RESULT_WAITING_ON_IO until it finds them. + // If you don't actually leverage this data, it is best to ignore it here. + avifBool ignoreExif; + avifBool ignoreXMP; + + // This represents the maximum size of an image (in pixel count) that libavif and the underlying + // AV1 decoder should attempt to decode. It defaults to AVIF_DEFAULT_IMAGE_SIZE_LIMIT, and can + // be set to a smaller value. The value 0 is reserved. + // Note: Only some underlying AV1 codecs support a configurable size limit (such as dav1d). + uint32_t imageSizeLimit; + + // This represents the maximum dimension of an image (width or height) that libavif should + // attempt to decode. It defaults to AVIF_DEFAULT_IMAGE_DIMENSION_LIMIT. Set it to 0 to ignore + // the limit. + uint32_t imageDimensionLimit; + + // This provides an upper bound on how many images the decoder is willing to attempt to decode, + // to provide a bit of protection from malicious or malformed AVIFs citing millions upon + // millions of frames, only to be invalid later. The default is AVIF_DEFAULT_IMAGE_COUNT_LIMIT + // (see comment above), and setting this to 0 disables the limit. + uint32_t imageCountLimit; + + // Strict flags. Defaults to AVIF_STRICT_ENABLED. See avifStrictFlag definitions above. + avifStrictFlags strictFlags; + + // -------------------------------------------------------------------------------------------- + // Outputs + // Additional outputs are available at the end of the struct after the version 1.0.0 end marker. + + // All decoded image data; owned by the decoder. All information in this image is incrementally + // added and updated as avifDecoder*() functions are called. After a successful call to + // avifDecoderParse(), all values in decoder->image (other than the planes/rowBytes themselves) + // will be pre-populated with all information found in the outer AVIF container, prior to any + // AV1 decoding. If the contents of the inner AV1 payload disagree with the outer container, + // these values may change after calls to avifDecoderRead*(),avifDecoderNextImage(), or + // avifDecoderNthImage(). + // + // The YUV and A contents of this image are likely owned by the decoder, so be sure to copy any + // data inside of this image before advancing to the next image or reusing the decoder. It is + // legal to call avifImageYUVToRGB() on this in between calls to avifDecoderNextImage(), but use + // avifImageCopy() if you want to make a complete, permanent copy of this image's YUV content or + // metadata. + // + // For each field among clap, irot and imir, if the corresponding avifTransformFlag is set, the + // transform must be applied before rendering or converting the image, or forwarded along as + // attached metadata. + avifImage * image; + + // Counts and timing for the current image in an image sequence. Uninteresting for single image files. + int imageIndex; // 0-based + int imageCount; // Always 1 for non-progressive, non-sequence AVIFs. + avifProgressiveState progressiveState; // See avifProgressiveState declaration + avifImageTiming imageTiming; // + uint64_t timescale; // timescale of the media (Hz) + double duration; // duration of a single playback of the image sequence in seconds + // (durationInTimescales / timescale) + uint64_t durationInTimescales; // duration of a single playback of the image sequence in "timescales" + int repetitionCount; // number of times the sequence has to be repeated. This can also be one of + // AVIF_REPETITION_COUNT_INFINITE or AVIF_REPETITION_COUNT_UNKNOWN. Essentially, if + // repetitionCount is a non-negative integer `n`, then the image sequence should be + // played back `n + 1` times. + + // This is true when avifDecoderParse() detects an alpha plane. Use this to find out if alpha is + // present after a successful call to avifDecoderParse(), but prior to any call to + // avifDecoderNextImage() or avifDecoderNthImage(), as decoder->image->alphaPlane won't exist yet. + avifBool alphaPresent; + + // stats from the most recent read, possibly 0s if reading an image sequence + avifIOStats ioStats; + + // Additional diagnostics (such as detailed error state) + avifDiagnostics diag; + + // -------------------------------------------------------------------------------------------- + // Internals + + // IO source. This field is managed by the decoder. Use one of the avifDecoderSetIO*() functions to set it. + avifIO * io; + + // Internals used by the decoder + struct avifDecoderData * data; + + // Version 1.0.0 ends here. + // -------------------------------------------------------------------------------------------- + + // This is true when avifDecoderParse() detects an image sequence track in the image. If this is true, the image can be + // decoded either as an animated image sequence or as a still image (the primary image item) by setting avifDecoderSetSource + // to the appropriate source. + avifBool imageSequenceTrackPresent; // Output data field. + + // Version 1.1.0 ends here. + // -------------------------------------------------------------------------------------------- + + // Image content to decode (if present). Defaults to AVIF_IMAGE_CONTENT_DECODE_DEFAULT. + // The value is only taken into account when calling avifDecoderRead*(), avifDecoderSetSource(), + // avifDecoderParse(), or avifDecoderReset(). + avifImageContentTypeFlags imageContentToDecode; // Changeable decoder setting. + + // Version 1.2.0 ends here. Add any new members after this line. + // -------------------------------------------------------------------------------------------- +} avifDecoder; + +// Creates a decoder initialized with default settings values. +// Returns NULL in case of memory allocation failure. +AVIF_API avifDecoder * avifDecoderCreate(void); +AVIF_API void avifDecoderDestroy(avifDecoder * decoder); + +// Simple interfaces to decode a single image, independent of the decoder afterwards (decoder may be destroyed). +AVIF_API avifResult avifDecoderRead(avifDecoder * decoder, avifImage * image); // call avifDecoderSetIO*() first +AVIF_API avifResult avifDecoderReadMemory(avifDecoder * decoder, avifImage * image, const uint8_t * data, size_t size); +AVIF_API avifResult avifDecoderReadFile(avifDecoder * decoder, avifImage * image, const char * filename); + +// Multi-function alternative to avifDecoderRead() for image sequences and gaining direct access +// to the decoder's YUV buffers (for performance's sake). Data passed into avifDecoderParse() is NOT +// copied, so it must continue to exist until the decoder is destroyed. +// +// Usage / function call order is: +// * avifDecoderCreate() +// * avifDecoderSetSource() - optional, the default (AVIF_DECODER_SOURCE_AUTO) is usually sufficient +// * avifDecoderSetIO*() +// * avifDecoderParse() +// * avifDecoderNextImage() - in a loop, using decoder->image after each successful call +// * avifDecoderDestroy() +// +// NOTE: Until avifDecoderParse() returns AVIF_RESULT_OK, no data in avifDecoder should +// be considered valid, and no queries (such as Keyframe/Timing/MaxExtent) should be made. +// +// You can use avifDecoderReset() any time after a successful call to avifDecoderParse() +// to reset the internal decoder back to before the first frame. Calling either +// avifDecoderSetSource() or avifDecoderParse() will automatically Reset the decoder. +// +// The decoder must be destroyed once there is no need for further parsing or decoding. +// Reusing the decoder instance for another file is not recommended. Call avifDecoderCreate() instead. +// +// avifDecoderSetSource() allows you not only to choose whether to parse tracks or +// items in a file containing both, but switch between sources without having to +// Parse again. Normally AVIF_DECODER_SOURCE_AUTO is enough for the common path. +AVIF_API avifResult avifDecoderSetSource(avifDecoder * decoder, avifDecoderSource source); +// Note: When avifDecoderSetIO() is called, whether 'decoder' takes ownership of 'io' depends on +// whether io->destroy is set. avifDecoderDestroy(decoder) calls avifIODestroy(io), which calls +// io->destroy(io) if io->destroy is set. Therefore, if io->destroy is not set, then +// avifDecoderDestroy(decoder) has no effects on 'io'. +AVIF_API void avifDecoderSetIO(avifDecoder * decoder, avifIO * io); +AVIF_API avifResult avifDecoderSetIOMemory(avifDecoder * decoder, const uint8_t * data, size_t size); +AVIF_API avifResult avifDecoderSetIOFile(avifDecoder * decoder, const char * filename); +AVIF_API avifResult avifDecoderParse(avifDecoder * decoder); +AVIF_API avifResult avifDecoderNextImage(avifDecoder * decoder); +AVIF_API avifResult avifDecoderNthImage(avifDecoder * decoder, uint32_t frameIndex); +AVIF_API avifResult avifDecoderReset(avifDecoder * decoder); + +// Keyframe information +// frameIndex - 0-based, matching avifDecoder->imageIndex, bound by avifDecoder->imageCount +// "nearest" keyframe means the keyframe prior to this frame index (returns frameIndex if it is a keyframe) +// These functions may be used after a successful call (AVIF_RESULT_OK) to avifDecoderParse(). +AVIF_NODISCARD AVIF_API avifBool avifDecoderIsKeyframe(const avifDecoder * decoder, uint32_t frameIndex); +AVIF_API uint32_t avifDecoderNearestKeyframe(const avifDecoder * decoder, uint32_t frameIndex); + +// Timing helper - This does not change the current image or invoke the codec (safe to call repeatedly) +// This function may be used after a successful call (AVIF_RESULT_OK) to avifDecoderParse(). +AVIF_API avifResult avifDecoderNthImageTiming(const avifDecoder * decoder, uint32_t frameIndex, avifImageTiming * outTiming); + +// When avifDecoderNextImage() or avifDecoderNthImage() returns AVIF_RESULT_WAITING_ON_IO, this +// function can be called next to retrieve the number of top rows that can be immediately accessed +// from the luma plane of decoder->image, and alpha if any. The corresponding rows from the chroma planes, +// if any, can also be accessed (half rounded up if subsampled, same number of rows otherwise). +// If a gain map is present and (imageContentToDecode & AVIF_IMAGE_CONTENT_GAIN_MAP) was nonzero when +// avifDecoderNextImage() or avifDecoderNthImage() was called, the gain map's planes can also be accessed +// in the same way. If the gain map's height is different from the main image, then the number of +// available gain map rows is at least: +// roundf((float)decoded_row_count / decoder->image->height * decoder->image->gainMap.image->height) +// When gain map scaling is needed, callers might choose to use a few less rows depending on how many rows +// are needed by the scaling algorithm, to avoid the last row(s) changing when more data becomes available. +// decoder->allowIncremental must be set to true before calling avifDecoderNextImage() or +// avifDecoderNthImage(). Returns decoder->image->height when the last call to avifDecoderNextImage() or +// avifDecoderNthImage() returned AVIF_RESULT_OK. Returns 0 in all other cases. +AVIF_API uint32_t avifDecoderDecodedRowCount(const avifDecoder * decoder); + +// --------------------------------------------------------------------------- +// avifExtent + +typedef struct avifExtent +{ + uint64_t offset; + size_t size; +} avifExtent; + +// Streaming data helper - Use this to calculate the maximal AVIF data extent encompassing all AV1 +// sample data needed to decode the Nth image. The offset will be the earliest offset of all +// required AV1 extents for this frame, and the size will create a range including the last byte of +// the last AV1 sample needed. Note that this extent may include non-sample data, as a frame's +// sample data may be broken into multiple extents and interleaved with other data, or in +// non-sequential order. This extent will also encompass all AV1 samples that this frame's sample +// depends on to decode (such as samples for reference frames), from the nearest keyframe up to this +// Nth frame. +// +// If avifDecoderNthImageMaxExtent() returns AVIF_RESULT_OK and the extent's size is 0 bytes, this +// signals that libavif doesn't expect to call avifIO's Read for this frame's decode. This happens if +// data for this frame was read as a part of avifDecoderParse() (typically in an idat box inside of +// a meta box). +// +// This function may be used after a successful call (AVIF_RESULT_OK) to avifDecoderParse(). +AVIF_API avifResult avifDecoderNthImageMaxExtent(const avifDecoder * decoder, uint32_t frameIndex, avifExtent * outExtent); + +// --------------------------------------------------------------------------- +// avifEncoder + +struct avifEncoderData; +struct avifCodecSpecificOptions; + +typedef struct avifScalingMode +{ + avifFraction horizontal; + avifFraction vertical; +} avifScalingMode; + +// AVIF encoder struct. It may be extended in a future release. Code outside the libavif library +// must allocate avifEncoder by calling the avifEncoderCreate() function, and destroy it with +// avifEncoderDestroy(). +// This struct contains three types of fields: +// * Changeable settings, which users of the API may set. +// * Output data fields, that are set by libavif and which users of the API may read. +// * Internal fields, which users of the API should ignore. +// Some encoder settings can be changed after encoding starts. Changes will take effect in the next +// call to avifEncoderAddImage(). +typedef struct avifEncoder +{ + // -------------------------------------------------------------------------------------------- + // Changeable encoder settings + // Additional settings are available at the end of the struct after the version 1.0.0 end marker. + + // Defaults to AVIF_CODEC_CHOICE_AUTO: Preference determined by order in availableCodecs table (avif.c) + avifCodecChoice codecChoice; + + // Defaults to 1. If < 2, multithreading is disabled. See also 'Understanding maxThreads' above. + int maxThreads; + // Speed range: [AVIF_SPEED_SLOWEST - AVIF_SPEED_FASTEST]. Slower should make for a better quality + // image in fewer bytes. AVIF_SPEED_DEFAULT means "Leave the AV1 codec to its default speed settings". + // If avifEncoder uses rav1e, the speed value is directly passed through (0-10). If libaom is used, + // a combination of settings are tweaked to simulate this speed range. + int speed; + + // For image sequences (animations), maximum interval between keyframes. Any set of |keyframeInterval| + // consecutive frames will have at least one keyframe. When it is 0, no restriction is applied. + int keyframeInterval; + // For image sequences (animations), timescale of the media in Hz, i.e. the number of time units per second. + uint64_t timescale; + // For image sequences, number of times the image sequence should be repeated. This can also be set to + // AVIF_REPETITION_COUNT_INFINITE for infinite repetitions. + // Essentially, if repetitionCount is a non-negative integer `n`, then the image sequence should be + // played back `n + 1` times. Defaults to AVIF_REPETITION_COUNT_INFINITE. + int repetitionCount; + + // A non-zero value indicates a layered (progressive) image. + // Range: [0 - (AVIF_MAX_AV1_LAYER_COUNT-1)]. + // To encode a progressive image, set `extraLayerCount` to the number of extra images, then call + // `avifEncoderAddImage()` or `avifEncoderAddImageGrid()` exactly `encoder->extraLayerCount+1` times. + uint32_t extraLayerCount; + + // Encode quality for the YUV image, in [AVIF_QUALITY_WORST - AVIF_QUALITY_BEST]. + int quality; + // Encode quality for the alpha layer if present, in [AVIF_QUALITY_WORST - AVIF_QUALITY_BEST]. + int qualityAlpha; + // Note: libavif internally converts between quality and quantizer using the + // following formulas. Both variables are of the int type and the division + // is integer division. + // quantizer = ((100 - quality) * 63 + 50) / 100 + // quality = ((63 - quantizer) * 100 + 31) / 63 + // + // These formulas are the integer equivalents of the following formulas: + // quantizer = (int)round(((100.0 - quality) * 63.0) / 100.0) + // quality = (int)round(((63.0 - quantizer) * 100.0) / 63.0) + // + // The conversion formulas have two nice properties: + // 1. Each quantizer in 0..63 can be converted to quality and back to itself. + // 2. The lossless quality 100 is the only quality that is converted to the + // lossless quantizer 0, and vice versa. + int minQuantizer; // Deprecated, use `quality` instead. + int maxQuantizer; // Deprecated, use `quality` instead. + int minQuantizerAlpha; // Deprecated, use `qualityAlpha` instead. + int maxQuantizerAlpha; // Deprecated, use `qualityAlpha` instead. + + // Tiling splits the image into a grid of smaller images (tiles), allowing parallelization of + // encoding/decoding and/or incremental decoding. Tiling also allows encoding larger images. + // To enable tiling, set tileRowsLog2 > 0 and/or tileColsLog2 > 0, or set autoTiling to AVIF_TRUE. + // Range: [0-6], where the value indicates a request for 2^n tiles in that dimension. + int tileRowsLog2; + int tileColsLog2; + // If autoTiling is set to AVIF_TRUE, libavif ignores tileRowsLog2 and tileColsLog2 and + // automatically chooses suitable tiling values. + avifBool autoTiling; + + // Up/down scaling of the image to perform before encoding. + avifScalingMode scalingMode; + + // -------------------------------------------------------------------------------------------- + // Outputs + + // Stats from the most recent write. + avifIOStats ioStats; + + // Additional diagnostics (such as detailed error state). + avifDiagnostics diag; + + // -------------------------------------------------------------------------------------------- + // Internals + + struct avifEncoderData * data; + struct avifCodecSpecificOptions * csOptions; + + // Version 1.0.0 ends here. + // -------------------------------------------------------------------------------------------- + + // Defaults to AVIF_HEADER_DEFAULT + avifHeaderFormatFlags headerFormat; // Changeable encoder setting. + + // Version 1.1.0 ends here. + // -------------------------------------------------------------------------------------------- + + // Encode quality for the gain map image if present, in [AVIF_QUALITY_WORST - AVIF_QUALITY_BEST]. + int qualityGainMap; // Changeable encoder setting. + + // Version 1.2.0 ends here. + // -------------------------------------------------------------------------------------------- + + // Only used when encoding an image sequence (animated image). In seconds since midnight, + // Jan. 1, 1970 UTC (the Unix epoch). If set to 0 (the default), libavif sets the creation time + // to the modification time. + uint64_t creationTime; + // Only used when encoding an image sequence (animated image). In seconds since midnight, + // Jan. 1, 1970 UTC (the Unix epoch). If set to 0 (the default), libavif sets the modification + // time to the current time. + uint64_t modificationTime; + + // Perform extra steps at encoding and decoding to extend AV1 features using bundled additional image items. + avifSampleTransformRecipe sampleTransformRecipe; // Changeable encoder setting. + + // Version 1.4.0 ends here. Add any new members after this line. + // -------------------------------------------------------------------------------------------- +} avifEncoder; + +// Creates an encoder initialized with default settings values. +// Returns NULL if a memory allocation failed. +AVIF_NODISCARD AVIF_API avifEncoder * avifEncoderCreate(void); +// Encodes and writes a single image to `output`. +// On success (AVIF_RESULT_OK), `output` must be freed with avifRWDataFree(). +// For more complex use cases, see `avifEncoderAddImage()` and `avifEncoderAddImageGrid()` below. +AVIF_API avifResult avifEncoderWrite(avifEncoder * encoder, const avifImage * image, avifRWData * output); +AVIF_API void avifEncoderDestroy(avifEncoder * encoder); + +typedef enum avifAddImageFlag +{ + AVIF_ADD_IMAGE_FLAG_NONE = 0, + + // Force this frame to be a keyframe (sync frame). + AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME = (1 << 0), + + // Use this flag when encoding a single frame, single layer image. + // Signals "still_picture" to AV1 encoders, which tweaks various compression rules. + // This is enabled automatically when using the avifEncoderWrite() single-image encode path. + AVIF_ADD_IMAGE_FLAG_SINGLE = (1 << 1) +} avifAddImageFlag; +typedef uint32_t avifAddImageFlags; + +// Multi-function alternative to avifEncoderWrite() for advanced features. +// +// Usage / function call order is: +// * avifEncoderCreate() +// +// - Still image: +// * avifEncoderAddImage() [exactly once] +// - Still image grid: +// * avifEncoderAddImageGrid() [exactly once, AVIF_ADD_IMAGE_FLAG_SINGLE is assumed] +// - Image sequence (animation): +// * Set encoder->timescale (Hz) correctly +// * avifEncoderAddImage() ... [repeatedly; at least once] +// - Still layered image: +// * Set encoder->extraLayerCount correctly +// * avifEncoderAddImage() ... [exactly encoder->extraLayerCount+1 times] +// - Still layered grid: +// * Set encoder->extraLayerCount correctly +// * avifEncoderAddImageGrid() ... [exactly encoder->extraLayerCount+1 times] +// +// * avifEncoderFinish() +// * avifEncoderDestroy() +// +// The image passed to avifEncoderAddImage() or avifEncoderAddImageGrid() is encoded during the +// call (which may be slow) and can be freed after the function returns. +// +// The encoder must be destroyed after avifEncoderFinish() is called. +// The encoder instance cannot be reused. Call avifEncoderCreate() instead. +// +// durationInTimescales is ignored if AVIF_ADD_IMAGE_FLAG_SINGLE is set in addImageFlags, +// or if we are encoding a layered image. +AVIF_API avifResult avifEncoderAddImage(avifEncoder * encoder, const avifImage * image, uint64_t durationInTimescales, avifAddImageFlags addImageFlags); +AVIF_API avifResult avifEncoderAddImageGrid(avifEncoder * encoder, + uint32_t gridCols, + uint32_t gridRows, + const avifImage * const * cellImages, + avifAddImageFlags addImageFlags); +AVIF_API avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output); + +// Codec-specific, optional "advanced" tuning settings, in the form of string key/value pairs, +// to be consumed by the codec in the next avifEncoderAddImage() call. +// See the codec documentation to know if a setting is persistent or applied only to the next frame. +// key must be non-NULL, but passing a NULL value will delete the pending key, if it exists. +// Setting an incorrect or unknown option for the current codec will cause errors of type +// AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION from avifEncoderWrite() or avifEncoderAddImage(). +AVIF_API avifResult avifEncoderSetCodecSpecificOption(avifEncoder * encoder, const char * key, const char * value); + +// Returns the size in bytes of the AV1 image item containing gain map samples, or 0 if no gain map was encoded. +AVIF_API size_t avifEncoderGetGainMapSizeBytes(avifEncoder * encoder); + +// Helpers +AVIF_NODISCARD AVIF_API avifBool avifImageUsesU16(const avifImage * image); +AVIF_NODISCARD AVIF_API avifBool avifImageIsOpaque(const avifImage * image); +// channel can be an avifChannelIndex. +AVIF_API uint8_t * avifImagePlane(const avifImage * image, int channel); +AVIF_API uint32_t avifImagePlaneRowBytes(const avifImage * image, int channel); +AVIF_API uint32_t avifImagePlaneWidth(const avifImage * image, int channel); +AVIF_API uint32_t avifImagePlaneHeight(const avifImage * image, int channel); + +// Returns AVIF_TRUE if input begins with a valid FileTypeBox (ftyp) that supports +// either the brand 'avif' or 'avis' (or both), without performing any allocations. +AVIF_NODISCARD AVIF_API avifBool avifPeekCompatibleFileType(const avifROData * input); + +// --------------------------------------------------------------------------- +// Gain Map utilities. + +// Performs tone mapping on a base image using the provided gain map. +// The HDR headroom is log2 of the ratio of HDR to SDR white brightness of the display to tone map for. +// 'toneMappedImage' should have the 'format', 'depth', and 'isFloat' fields set to the desired values. +// If non NULL, 'clli' will be filled with the light level information of the tone mapped image. +AVIF_API avifResult avifImageApplyGainMap(const avifImage * baseImage, + const avifGainMap * gainMap, + float hdrHeadroom, + avifColorPrimaries outputColorPrimaries, + avifTransferCharacteristics outputTransferCharacteristics, + avifRGBImage * toneMappedImage, + avifContentLightLevelInformationBox * clli, + avifDiagnostics * diag); +// Same as above but takes an avifRGBImage as input instead of avifImage. +AVIF_API avifResult avifRGBImageApplyGainMap(const avifRGBImage * baseImage, + avifColorPrimaries baseColorPrimaries, + avifTransferCharacteristics baseTransferCharacteristics, + const avifGainMap * gainMap, + float hdrHeadroom, + avifColorPrimaries outputColorPrimaries, + avifTransferCharacteristics outputTransferCharacteristics, + avifRGBImage * toneMappedImage, + avifContentLightLevelInformationBox * clli, + avifDiagnostics * diag); + +// Computes a gain map between two images: a base image and an alternate image. +// Both images should have the same width and height. +// gainMap->image should be initialized with avifImageCreate(), with the width, +// height, depth and yuvFormat fields set to the desired output values for the +// gain map. All of these fields may differ from the source images. +AVIF_API avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage, + avifColorPrimaries baseColorPrimaries, + avifTransferCharacteristics baseTransferCharacteristics, + const avifRGBImage * altRgbImage, + avifColorPrimaries altColorPrimaries, + avifTransferCharacteristics altTransferCharacteristics, + avifGainMap * gainMap, + avifDiagnostics * diag); +// Convenience function. Same as above but takes avifImage images as input +// instead of avifRGBImage. Gain map computation is performed in RGB space so +// the images are converted to RGB first. +AVIF_API avifResult avifImageComputeGainMap(const avifImage * baseImage, + const avifImage * altImage, + avifGainMap * gainMap, + avifDiagnostics * diag); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ifndef AVIF_AVIF_H diff --git a/3rdparty/libavif/include/avif/internal.h b/3rdparty/libavif/include/avif/internal.h new file mode 100644 index 0000000..825dd29 --- /dev/null +++ b/3rdparty/libavif/include/avif/internal.h @@ -0,0 +1,855 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#ifndef AVIF_INTERNAL_H +#define AVIF_INTERNAL_H + +#include "avif/avif.h" // IWYU pragma: export + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(AVIF_DLL) && defined(AVIF_USING_STATIC_LIBS) +#error "Your target is linking against avif and avif_internal: only one should be chosen" +#endif + +// Yes, clamp macros are nasty. Do not use them. +#define AVIF_CLAMP(x, low, high) (((x) < (low)) ? (low) : (((high) < (x)) ? (high) : (x))) +#define AVIF_MIN(a, b) (((a) < (b)) ? (a) : (b)) +#define AVIF_MAX(a, b) (((a) > (b)) ? (a) : (b)) + +// Used for debugging. Define AVIF_BREAK_ON_ERROR to catch the earliest failure during encoding or decoding. +#if defined(AVIF_BREAK_ON_ERROR) +static inline void avifBreakOnError() +{ + // Same mechanism as OpenCV's error() function, or replace by a breakpoint. + int * p = NULL; + *p = 0; +} +#else +#define avifBreakOnError() +#endif + +// Used by stream related things. +#define AVIF_CHECK(A) \ + do { \ + if (!(A)) { \ + avifBreakOnError(); \ + return AVIF_FALSE; \ + } \ + } while (0) + +// Used instead of CHECK if needing to return a specific error on failure, instead of AVIF_FALSE +#define AVIF_CHECKERR(A, ERR) \ + do { \ + if (!(A)) { \ + avifBreakOnError(); \ + return ERR; \ + } \ + } while (0) + +// Forward any error to the caller now or continue execution. +#define AVIF_CHECKRES(A) \ + do { \ + const avifResult result__ = (A); \ + if (result__ != AVIF_RESULT_OK) { \ + avifBreakOnError(); \ + return result__; \ + } \ + } while (0) + +// AVIF_ASSERT_OR_RETURN() can be used instead of assert() for extra security in release builds. +#ifdef NDEBUG +#define AVIF_ASSERT_OR_RETURN(A) AVIF_CHECKERR((A), AVIF_RESULT_INTERNAL_ERROR) +#define AVIF_ASSERT_NOT_REACHED_OR_RETURN \ + do { \ + avifBreakOnError(); \ + return AVIF_RESULT_INTERNAL_ERROR; \ + } while (0) +#else +#define AVIF_ASSERT_OR_RETURN(A) assert(A) +#define AVIF_ASSERT_NOT_REACHED_OR_RETURN assert(0); +#endif + +// --------------------------------------------------------------------------- +// URNs and Content-Types + +#define AVIF_URN_ALPHA0 "urn:mpeg:mpegB:cicp:systems:auxiliary:alpha" +#define AVIF_URN_ALPHA1 "urn:mpeg:hevc:2015:auxid:1" + +#define AVIF_CONTENT_TYPE_XMP "application/rdf+xml" + +// --------------------------------------------------------------------------- +// Memory management + +// Allocates count * size bytes and zero-initializes them. Returns NULL on memory +// allocation failure, including the case when count * size overflows size_t. +void * avifCalloc(size_t count, size_t size); + +// --------------------------------------------------------------------------- +// Utils + +float avifRoundf(float v); + +// H (host) is platform-dependent. Could be little- or big-endian. +// N (network) is big-endian: most- to least-significant bytes. +// C (custom) is little-endian: least- to most-significant bytes. +// Never read N or C values; only access after casting to uint8_t*. +uint16_t avifHTONS(uint16_t s); +uint16_t avifNTOHS(uint16_t s); +uint16_t avifCTOHS(uint16_t s); +uint32_t avifHTONL(uint32_t l); +uint32_t avifNTOHL(uint32_t l); +uint32_t avifCTOHL(uint32_t l); +uint64_t avifHTON64(uint64_t l); +uint64_t avifNTOH64(uint64_t l); + +void avifCalcYUVCoefficients(const avifImage * image, float * outR, float * outG, float * outB); + +typedef float (*avifTransferFunction)(float); +// Returns a function to map from gamma-encoded values in the [0.0, 1.0] range to linear extended SDR values. +// Extended SDR values are in [0.0, 1.0] for SDR transfer chracteristics (all transfer characteristics except PQ and HLG) +// and can go beyond 1.0 for HDR transfer characteristics: +// - For AVIF_TRANSFER_CHARACTERISTICS_PQ, the linear range is [0.0, 10000/203] +// - For AVIF_TRANSFER_CHARACTERISTICS_HLG, the linear range is [0.0, 1000/203] +avifTransferFunction avifTransferCharacteristicsGetGammaToLinearFunction(avifTransferCharacteristics atc); +// Same as above in the opposite direction. toGamma(toLinear(v)) ~= v. +avifTransferFunction avifTransferCharacteristicsGetLinearToGammaFunction(avifTransferCharacteristics atc); + +// Computes the RGB->YUV conversion coefficients kr, kg, kb, such that Y=kr*R+kg*G+kb*B. +void avifColorPrimariesComputeYCoeffs(avifColorPrimaries colorPrimaries, float coeffs[3]); + +// Computes a conversion matrix from RGB to XYZ with a D50 white point. +AVIF_NODISCARD avifBool avifColorPrimariesComputeRGBToXYZD50Matrix(avifColorPrimaries colorPrimaries, double coeffs[3][3]); +// Computes a conversion matrix from XYZ with a D50 white point to RGB. +AVIF_NODISCARD avifBool avifColorPrimariesComputeXYZD50ToRGBMatrix(avifColorPrimaries colorPrimaries, double coeffs[3][3]); +// Computes the RGB->RGB conversion matrix to convert from one set of RGB primaries to another. +AVIF_NODISCARD avifBool avifColorPrimariesComputeRGBToRGBMatrix(avifColorPrimaries srcColorPrimaries, + avifColorPrimaries dstColorPrimaries, + double coeffs[3][3]); +// Converts the given linear RGB pixel from one color space to another using the provided coefficients. +// The coefficients can be obtained with avifColorPrimariesComputeRGBToRGBMatrix(). +// The output values are not clamped and may be < 0 or > 1. +void avifLinearRGBConvertColorSpace(float rgb[4], double coeffs[3][3]); + +#define AVIF_ARRAY_DECLARE(TYPENAME, ITEMSTYPE, ITEMSNAME) \ + typedef struct TYPENAME \ + { \ + ITEMSTYPE * ITEMSNAME; \ + uint32_t elementSize; \ + uint32_t count; \ + uint32_t capacity; \ + } TYPENAME +AVIF_NODISCARD avifBool avifArrayCreate(void * arrayStruct, uint32_t elementSize, uint32_t initialCapacity); +AVIF_NODISCARD void * avifArrayPush(void * arrayStruct); +void avifArrayPop(void * arrayStruct); +void avifArrayDestroy(void * arrayStruct); + +void avifFractionSimplify(avifFraction * f); +// Returns AVIF_TRUE if x does not fit in a signed 32-bit integer. +// Defined here (rather than in multiple .c files) so the sources can be +// amalgamated into a single translation unit. +static inline avifBool overflowsInt32(int64_t x) +{ + return (x < INT32_MIN) || (x > INT32_MAX); +} +// Makes the fractions have a common denominator. +AVIF_NODISCARD avifBool avifFractionCD(avifFraction * a, avifFraction * b); +AVIF_NODISCARD avifBool avifFractionAdd(avifFraction a, avifFraction b, avifFraction * result); +AVIF_NODISCARD avifBool avifFractionSub(avifFraction a, avifFraction b, avifFraction * result); + +void avifImageSetDefaults(avifImage * image); +// Copies all fields that do not need to be freed/allocated from srcImage to dstImage. +void avifImageCopyNoAlloc(avifImage * dstImage, const avifImage * srcImage); + +// Copies the samples from srcImage to dstImage. dstImage must be allocated. +// srcImage and dstImage must have the same width, height, and depth. +// If the AVIF_PLANES_YUV bit is set in planes, then srcImage and dstImage must have the same yuvFormat. +// Ignores the gainMap field. +void avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes); + +// Appends an opaque image item property. +avifResult avifImagePushProperty(avifImage * image, + const uint8_t boxtype[4], + const uint8_t usertype[16], + const uint8_t * boxPayload, + size_t boxPayloadSize); + +// Check if the FourCC property value is a known value +AVIF_NODISCARD avifBool avifIsKnownPropertyType(const uint8_t boxtype[4]); +// Check if the extended property (UUID) is valid +AVIF_NODISCARD avifBool avifIsValidUUID(const uint8_t uuid[16]); + +// --------------------------------------------------------------------------- + +// Mapping used in the coding of Sample Transform metadata. +typedef enum avifSampleTransformBitDepth +{ + AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_8 = 0, // Signed 8-bit. + AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_16 = 1, // Signed 16-bit. + AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32 = 2, // Signed 32-bit. + AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_64 = 3 // Signed 64-bit. +} avifSampleTransformBitDepth; + +// Meaning of an operand or operator in Sample Transform metadata. +typedef enum avifSampleTransformTokenType +{ + // Operands. + AVIF_SAMPLE_TRANSFORM_CONSTANT = 0, + AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX = 1, + AVIF_SAMPLE_TRANSFORM_FIRST_INPUT_IMAGE_ITEM_INDEX = 1, + AVIF_SAMPLE_TRANSFORM_LAST_INPUT_IMAGE_ITEM_INDEX = 32, + + // Unary operators. L is the operand. + AVIF_SAMPLE_TRANSFORM_FIRST_UNARY_OPERATOR = 64, + AVIF_SAMPLE_TRANSFORM_NEGATION = 64, // S = -L + AVIF_SAMPLE_TRANSFORM_ABSOLUTE = 65, // S = |L| + AVIF_SAMPLE_TRANSFORM_NOT = 66, // S = ~L + AVIF_SAMPLE_TRANSFORM_BSR = 67, // S = L<=0 ? 0 : truncate(log2(L)) + AVIF_SAMPLE_TRANSFORM_LAST_UNARY_OPERATOR = 67, + + // Binary operators. L is the left operand. R is the right operand. + AVIF_SAMPLE_TRANSFORM_FIRST_BINARY_OPERATOR = 128, + AVIF_SAMPLE_TRANSFORM_SUM = 128, // S = L + R + AVIF_SAMPLE_TRANSFORM_DIFFERENCE = 129, // S = L - R + AVIF_SAMPLE_TRANSFORM_PRODUCT = 130, // S = L * R + AVIF_SAMPLE_TRANSFORM_QUOTIENT = 131, // S = R==0 ? L : truncate(L / R) + AVIF_SAMPLE_TRANSFORM_AND = 132, // S = L & R + AVIF_SAMPLE_TRANSFORM_OR = 133, // S = L | R + AVIF_SAMPLE_TRANSFORM_XOR = 134, // S = L ^ R + AVIF_SAMPLE_TRANSFORM_POW = 135, // S = L==0 ? 0 : truncate(pow(L, R)) + AVIF_SAMPLE_TRANSFORM_MIN = 136, // S = L<=R ? L : R + AVIF_SAMPLE_TRANSFORM_MAX = 137, // S = L<=R ? R : L + AVIF_SAMPLE_TRANSFORM_LAST_BINARY_OPERATOR = 137, + + AVIF_SAMPLE_TRANSFORM_RESERVED = 138 +} avifSampleTransformTokenType; + +typedef struct avifSampleTransformToken +{ + avifSampleTransformTokenType type; + int32_t constant; // If type is AVIF_SAMPLE_TRANSFORM_CONSTANT. + // Only 32-bit (bit_depth=2) constants are supported. + uint8_t inputImageItemIndex; // If type is AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX. 1-based. +} avifSampleTransformToken; + +AVIF_ARRAY_DECLARE(avifSampleTransformExpression, avifSampleTransformToken, tokens); +avifBool avifSampleTransformExpressionIsValid(const avifSampleTransformExpression * tokens, uint32_t numInputImageItems); +avifBool avifSampleTransformExpressionIsEquivalentTo(const avifSampleTransformExpression * a, const avifSampleTransformExpression * b); + +avifResult avifSampleTransformRecipeToExpression(avifSampleTransformRecipe recipe, avifSampleTransformExpression * expression); +avifResult avifSampleTransformExpressionToRecipe(const avifSampleTransformExpression * expression, avifSampleTransformRecipe * recipe); + +// Applies the expression to the samples of the inputImageItems in the selected planes and stores +// the results in dstImage. dstImage can be part of the inputImageItems. +// dstImage and inputImageItems must be allocated and have the same planes and dimensions. +avifResult avifImageApplyExpression(avifImage * dstImage, + avifSampleTransformBitDepth bitDepth, + const avifSampleTransformExpression * expression, + uint8_t numInputImageItems, + const avifImage * inputImageItems[], + avifPlanesFlags planes); + +// Same as avifImageApplyExpression(). Convenience function. +avifResult avifImageApplyOperations(avifImage * dstImage, + avifSampleTransformBitDepth bitDepth, + uint32_t numTokens, + const avifSampleTransformToken tokens[], + uint8_t numInputImageItems, + const avifImage * inputImageItems[], + avifPlanesFlags planes); + +// --------------------------------------------------------------------------- +// Alpha + +typedef struct avifAlphaParams +{ + uint32_t width; + uint32_t height; + + uint32_t srcDepth; + const uint8_t * srcPlane; + uint32_t srcRowBytes; + uint32_t srcOffsetBytes; + uint32_t srcPixelBytes; + + uint32_t dstDepth; + uint8_t * dstPlane; + uint32_t dstRowBytes; + uint32_t dstOffsetBytes; + uint32_t dstPixelBytes; + +} avifAlphaParams; + +void avifFillAlpha(const avifAlphaParams * params); +void avifReformatAlpha(const avifAlphaParams * params); + +typedef enum avifReformatMode +{ + AVIF_REFORMAT_MODE_YUV_COEFFICIENTS = 0, // Normal YUV conversion using coefficients + AVIF_REFORMAT_MODE_IDENTITY, // Pack GBR directly into YUV planes (AVIF_MATRIX_COEFFICIENTS_IDENTITY) + AVIF_REFORMAT_MODE_YCGCO, // YUV conversion using AVIF_MATRIX_COEFFICIENTS_YCGCO + AVIF_REFORMAT_MODE_YCGCO_RE, // YUV conversion using AVIF_MATRIX_COEFFICIENTS_YCGCO_RE + AVIF_REFORMAT_MODE_YCGCO_RO, // YUV conversion using AVIF_MATRIX_COEFFICIENTS_YCGCO_RO +} avifReformatMode; + +typedef enum avifAlphaMultiplyMode +{ + AVIF_ALPHA_MULTIPLY_MODE_NO_OP = 0, + AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY, + AVIF_ALPHA_MULTIPLY_MODE_UNMULTIPLY +} avifAlphaMultiplyMode; + +// Information about an RGB color space. +typedef struct avifRGBColorSpaceInfo +{ + uint32_t channelBytes; // Number of bytes per channel. + uint32_t pixelBytes; // Number of bytes per pixel (= channelBytes * num channels). + uint32_t offsetBytesR; // Offset in bytes of the red channel in a pixel. + uint32_t offsetBytesG; // Offset in bytes of the green channel in a pixel. + uint32_t offsetBytesB; // Offset in bytes of the blue channel in a pixel. + uint32_t offsetBytesA; // Offset in bytes of the alpha channel in a pixel. + uint32_t offsetBytesGray; // Offset in bytes of the gray channel in a pixel. + + int maxChannel; // Maximum value for a channel (e.g. 255 for 8 bit). + float maxChannelF; // Same as maxChannel but as a float. +} avifRGBColorSpaceInfo; + +avifBool avifGetRGBColorSpaceInfo(const avifRGBImage * rgb, avifRGBColorSpaceInfo * info); + +// Information about a YUV color space. +typedef struct avifYUVColorSpaceInfo +{ + // YUV coefficients. Y = kr*R + kg*G + kb*B. + float kr; + float kg; + float kb; + + uint32_t channelBytes; // Number of bytes per channel. + uint32_t depth; // Bit depth. + avifRange range; // Full or limited range. + int maxChannel; // Maximum value for a channel (e.g. 255 for 8 bit). + float biasY; // Minimum Y value. + float biasUV; // The value of 0.5 for the appropriate bit depth (128 for 8 bit, 512 for 10 bit, 2048 for 12 bit). + float rangeY; // Difference between max and min Y. + float rangeUV; // Difference between max and min UV. + + avifPixelFormatInfo formatInfo; // Chroma subsampling information. + avifReformatMode mode; // Appropriate RGB<->YUV conversion mode. +} avifYUVColorSpaceInfo; + +avifBool avifGetYUVColorSpaceInfo(const avifImage * image, avifYUVColorSpaceInfo * info); + +typedef struct avifReformatState +{ + avifRGBColorSpaceInfo rgb; + avifYUVColorSpaceInfo yuv; +} avifReformatState; + +// Retrieves the pixel value at position (x, y) expressed as floats in [0, 1]. If the image's format doesn't have alpha, +// rgbaPixel[3] is set to 1.0f. +void avifGetRGBAPixel(const avifRGBImage * src, uint32_t x, uint32_t y, const avifRGBColorSpaceInfo * info, float rgbaPixel[4]); +// Sets the pixel value at position (i, j) from RGBA values expressed as floats in [0, 1]. If the image's format doesn't +// support alpha, rgbaPixel[3] is ignored. +void avifSetRGBAPixel(const avifRGBImage * dst, uint32_t x, uint32_t y, const avifRGBColorSpaceInfo * info, const float rgbaPixel[4]); + +// Returns: +// * AVIF_RESULT_OK - Converted successfully with libyuv +// * AVIF_RESULT_NOT_IMPLEMENTED - The fast path for this combination is not implemented with libyuv, use built-in RGB conversion +// * [any other error] - Return error to caller +avifResult avifImageRGBToYUVLibYUV(avifImage * image, const avifRGBImage * rgb); + +// Parameters: +// * image - input YUV image +// * rgb - output RGB image +// * reformatAlpha - if set to AVIF_TRUE, the function will attempt to copy the alpha channel to the output RGB image using +// libyuv. +// * alphaReformattedWithLibYUV - Output parameter. If reformatAlpha is set to true and libyuv was able to copy over the alpha +// channel, then this will be set to AVIF_TRUE. Otherwise, this will be set to AVIF_FALSE. The value in this parameter is valid +// only if the return value of the function is AVIF_RESULT_OK or AVIF_RESULT_NOT_IMPLEMENTED. +// Returns: +// * AVIF_RESULT_OK - Converted successfully with libyuv +// * AVIF_RESULT_NOT_IMPLEMENTED - The fast path for this combination is not implemented with libyuv, use built-in YUV conversion +// * [any other error] - Return error to caller +avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifBool reformatAlpha, avifBool * alphaReformattedWithLibYUV); + +// Returns: +// * AVIF_RESULT_OK - Converted successfully with libsharpyuv +// * AVIF_RESULT_NOT_IMPLEMENTED - libsharpyuv is not compiled in, or doesn't support this type of input +// * [any other error] - Return error to caller +avifResult avifImageRGBToYUVLibSharpYUV(avifImage * image, const avifRGBImage * rgb, const avifReformatState * state); + +// Returns: +// * AVIF_RESULT_OK - Converted successfully with libyuv. +// * AVIF_RESULT_NOT_IMPLEMENTED - The fast path for this conversion is not implemented with libyuv, use built-in conversion. +// * AVIF_RESULT_INVALID_ARGUMENT - Return error to caller. +avifResult avifRGBImageToF16LibYUV(avifRGBImage * rgb); + +// Returns: +// * AVIF_RESULT_OK - (Un)Premultiply successfully with libyuv +// * AVIF_RESULT_NOT_IMPLEMENTED - The fast path for this combination is not implemented with libyuv, use built-in (Un)Premultiply +// * [any other error] - Return error to caller +avifResult avifRGBImagePremultiplyAlphaLibYUV(avifRGBImage * rgb); +avifResult avifRGBImageUnpremultiplyAlphaLibYUV(avifRGBImage * rgb); + +AVIF_NODISCARD avifBool avifDimensionsTooLarge(uint32_t width, uint32_t height, uint32_t imageSizeLimit, uint32_t imageDimensionLimit); + +// Given the number of encoding threads or decoding threads available and the image dimensions, +// chooses suitable values of *tileRowsLog2 and *tileColsLog2. +// +// Note: Although avifSetTileConfiguration() is only used in src/write.c and could be a static +// function in that file, it is defined as an internal global function so that it can be tested by +// unit tests. +void avifSetTileConfiguration(int threads, uint32_t width, uint32_t height, int * tileRowsLog2, int * tileColsLog2); + +// --------------------------------------------------------------------------- +// Scaling + +// Scales the YUV/A planes in-place. +avifResult avifImageScaleWithLimit(avifImage * image, + uint32_t dstWidth, + uint32_t dstHeight, + uint32_t imageSizeLimit, + uint32_t imageDimensionLimit, + avifDiagnostics * diag); + +// --------------------------------------------------------------------------- +// AVIF item category + +typedef enum avifItemCategory +{ + AVIF_ITEM_COLOR, + AVIF_ITEM_ALPHA, + AVIF_ITEM_GAIN_MAP, + AVIF_ITEM_SAMPLE_TRANSFORM, // Sample Transform derived image item 'sato'. + // Extra input image items for AVIF_ITEM_SAMPLE_TRANSFORM. "Extra" because AVIF_ITEM_COLOR could be one too. + AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_COLOR, + AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_1_COLOR, + AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_ALPHA, + AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_1_ALPHA, + AVIF_ITEM_CATEGORY_COUNT +} avifItemCategory; + +avifBool avifIsAlpha(avifItemCategory itemCategory); + +// AVIF allows up to 32 inputs for sample transforms but we only support a smaller number. +#define AVIF_SAMPLE_TRANSFORM_MAX_NUM_EXTRA_INPUT_IMAGE_ITEMS \ + (AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_ALPHA - AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_COLOR) +#define AVIF_SAMPLE_TRANSFORM_MAX_NUM_INPUT_IMAGE_ITEMS \ + (1 /* for AVIF_ITEM_COLOR */ + AVIF_SAMPLE_TRANSFORM_MAX_NUM_EXTRA_INPUT_IMAGE_ITEMS) + +#define AVIF_SAMPLE_TRANSFORM_MIN_CATEGORY AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_COLOR +#define AVIF_SAMPLE_TRANSFORM_MAX_CATEGORY \ + (AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_ALPHA + AVIF_SAMPLE_TRANSFORM_MAX_NUM_EXTRA_INPUT_IMAGE_ITEMS - 1) + +// --------------------------------------------------------------------------- +// Grid AVIF images + +// Returns false if the tiles in a grid image violate any standards. +// The image contains imageW*imageH pixels. The tiles are of tileW*tileH pixels each. +AVIF_NODISCARD avifBool avifAreGridDimensionsValid(avifPixelFormat yuvFormat, + uint32_t imageW, + uint32_t imageH, + uint32_t tileW, + uint32_t tileH, + avifDiagnostics * diag); + +// --------------------------------------------------------------------------- +// Metadata + +// Attempts to parse the image->exif payload for Exif orientation and sets image->transformFlags, image->irot and +// image->imir on success. Returns AVIF_RESULT_INVALID_EXIF_PAYLOAD on failure. +avifResult avifImageExtractExifOrientationToIrotImir(avifImage * image); + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) +// Returns the Exif orientation in [1-8] as defined in JEITA CP-3451C section 4.6.4.A Orientation +// corresponding to image->irot and image->imir. +uint8_t avifImageIrotImirToExifOrientation(const avifImage * image); +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + +// --------------------------------------------------------------------------- +// avifCodecDecodeInput + +// Legal spatial_id values are [0,1,2,3], so this serves as a sentinel value for "do not filter by spatial_id" +#define AVIF_SPATIAL_ID_UNSET 0xff + +typedef struct avifDecodeSample +{ + avifROData data; + avifBool ownsData; + avifBool partialData; // if true, data exists but doesn't have all of the sample in it + + uint32_t itemID; // if non-zero, data comes from a mergedExtents buffer in an avifDecoderItem, not a file offset + uint64_t offset; // additional offset into data. Can be used to offset into an itemID's payload as well. + size_t size; // + uint8_t spatialID; // If set to a value other than AVIF_SPATIAL_ID_UNSET, output frames from this sample should be + // skipped until the output frame's spatial_id matches this ID. + avifBool sync; // is sync sample (keyframe) +} avifDecodeSample; +AVIF_ARRAY_DECLARE(avifDecodeSampleArray, avifDecodeSample, sample); + +typedef struct avifCodecDecodeInput +{ + avifDecodeSampleArray samples; + avifBool allLayers; // if true, the underlying codec must decode all layers, not just the best layer + avifItemCategory itemCategory; // category of item being decoded +} avifCodecDecodeInput; + +AVIF_NODISCARD avifCodecDecodeInput * avifCodecDecodeInputCreate(void); +void avifCodecDecodeInputDestroy(avifCodecDecodeInput * decodeInput); + +// --------------------------------------------------------------------------- +// avifCodecEncodeOutput + +typedef struct avifEncodeSample +{ + avifRWData data; + avifBool sync; // is sync sample (keyframe) +} avifEncodeSample; +AVIF_ARRAY_DECLARE(avifEncodeSampleArray, avifEncodeSample, sample); + +typedef struct avifCodecEncodeOutput +{ + avifEncodeSampleArray samples; +} avifCodecEncodeOutput; + +AVIF_NODISCARD avifCodecEncodeOutput * avifCodecEncodeOutputCreate(void); +avifResult avifCodecEncodeOutputAddSample(avifCodecEncodeOutput * encodeOutput, const uint8_t * data, size_t len, avifBool sync); +void avifCodecEncodeOutputDestroy(avifCodecEncodeOutput * encodeOutput); + +// --------------------------------------------------------------------------- +// avifCodecSpecificOptions (key/value string pairs for advanced tuning) + +typedef struct avifCodecSpecificOption +{ + char * key; // Must be a simple lowercase alphanumeric string + char * value; // Free-form string to be interpreted by the codec +} avifCodecSpecificOption; +AVIF_ARRAY_DECLARE(avifCodecSpecificOptions, avifCodecSpecificOption, entries); + +// Returns NULL if a memory allocation failed. +AVIF_NODISCARD avifCodecSpecificOptions * avifCodecSpecificOptionsCreate(void); +void avifCodecSpecificOptionsClear(avifCodecSpecificOptions * csOptions); +void avifCodecSpecificOptionsDestroy(avifCodecSpecificOptions * csOptions); +avifResult avifCodecSpecificOptionsSet(avifCodecSpecificOptions * csOptions, const char * key, const char * value); // if(value==NULL), key is deleted + +// --------------------------------------------------------------------------- +// avifCodecType (underlying video format) + +// Alliance for Open Media video formats that can be used in the AVIF image format. +typedef enum avifCodecType +{ + AVIF_CODEC_TYPE_UNKNOWN, + AVIF_CODEC_TYPE_AV1, +#if defined(AVIF_CODEC_AVM) + AVIF_CODEC_TYPE_AV2, // Experimental. +#endif +} avifCodecType; + +// Returns AVIF_CODEC_TYPE_UNKNOWN unless the chosen codec is available with the requiredFlags. +avifCodecType avifCodecTypeFromChoice(avifCodecChoice choice, avifCodecFlags requiredFlags); + +// --------------------------------------------------------------------------- +// avifCodec (abstraction layer to use different codec implementations) + +struct avifCodec; +struct avifCodecInternal; + +typedef enum avifEncoderChange +{ + AVIF_ENCODER_CHANGE_MIN_QUANTIZER = (1 << 0), + AVIF_ENCODER_CHANGE_MAX_QUANTIZER = (1 << 1), + AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA = (1 << 2), + AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA = (1 << 3), + AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2 = (1 << 4), + AVIF_ENCODER_CHANGE_TILE_COLS_LOG2 = (1 << 5), + AVIF_ENCODER_CHANGE_QUALITY = (1 << 6), + AVIF_ENCODER_CHANGE_QUALITY_ALPHA = (1 << 7), + AVIF_ENCODER_CHANGE_SCALING_MODE = (1 << 8), + + AVIF_ENCODER_CHANGE_CODEC_SPECIFIC = (1 << 30) +} avifEncoderChange; +typedef int avifEncoderChanges; + +typedef avifBool (*avifCodecGetNextImageFunc)(struct avifCodec * codec, + const avifDecodeSample * sample, + avifBool alpha, + avifBool * isLimitedRangeAlpha, + avifImage * image); +// EncodeImage and EncodeFinish are not required to always emit a sample, but when all images are +// encoded and EncodeFinish is called, the number of samples emitted must match the number of submitted frames. +// avifCodecEncodeImageFunc may return AVIF_RESULT_UNKNOWN_ERROR to automatically emit the appropriate +// AVIF_RESULT_ENCODE_COLOR_FAILED or AVIF_RESULT_ENCODE_ALPHA_FAILED depending on the alpha argument. +// avifCodecEncodeImageFunc should use tileRowsLog2 and tileColsLog2 instead of +// encoder->tileRowsLog2, encoder->tileColsLog2, and encoder->autoTiling. The caller of +// avifCodecEncodeImageFunc is responsible for automatic tiling if encoder->autoTiling is set to +// AVIF_TRUE. The actual tiling values are passed to avifCodecEncodeImageFunc as parameters. +// Similarly, avifCodecEncodeImageFunc should use the quality parameter instead of +// encoder->quality, encoder->qualityAlpha, and encoder->qualityGainMap. If disableLaggedOutput is AVIF_TRUE, then +// the encoder will emit the output frame without any lag (if supported). Note that disableLaggedOutput is only +// used by the first call to this function (which initializes the encoder) and is ignored by the subsequent calls. +// +// Note: The caller of avifCodecEncodeImageFunc always passes encoder->data->tileRowsLog2 and +// encoder->data->tileColsLog2 as the tileRowsLog2 and tileColsLog2 arguments. Because +// encoder->data is of a struct type defined in src/write.c, avifCodecEncodeImageFunc cannot +// dereference encoder->data and has to receive encoder->data->tileRowsLog2 and +// encoder->data->tileColsLog2 via function parameters. +typedef avifResult (*avifCodecEncodeImageFunc)(struct avifCodec * codec, + avifEncoder * encoder, + const avifImage * image, + avifBool alpha, + int tileRowsLog2, + int tileColsLog2, + int quality, + avifEncoderChanges encoderChanges, + avifBool disableLaggedOutput, + avifAddImageFlags addImageFlags, + avifCodecEncodeOutput * output); +typedef avifBool (*avifCodecEncodeFinishFunc)(struct avifCodec * codec, avifCodecEncodeOutput * output); +typedef void (*avifCodecDestroyInternalFunc)(struct avifCodec * codec); + +typedef struct avifCodec +{ + const avifCodecSpecificOptions * csOptions; // Contains codec-specific key/value pairs for advanced tuning. + // This array is NOT owned by avifCodec. + struct avifCodecInternal * internal; // up to each codec to use how it wants + avifDiagnostics * diag; // Shallow copy; owned by avifEncoder or avifDecoder + + // Decoder options (for getNextImage): + int maxThreads; // See avifDecoder::maxThreads. + uint32_t imageSizeLimit; // See avifDecoder::imageSizeLimit. + uint32_t imageDimensionLimit; // See avifDecoder::imageDimensionLimit. + uint8_t operatingPoint; // Operating point, defaults to 0. + avifBool allLayers; // if true, the underlying codec must decode all layers, not just the best layer + + avifCodecGetNextImageFunc getNextImage; + avifCodecEncodeImageFunc encodeImage; + avifCodecEncodeFinishFunc encodeFinish; + avifCodecDestroyInternalFunc destroyInternal; +} avifCodec; + +avifResult avifCodecCreate(avifCodecChoice choice, avifCodecFlags requiredFlags, avifCodec ** codec); +void avifCodecDestroy(avifCodec * codec); + +AVIF_NODISCARD avifCodec * avifCodecCreateAOM(void); // requires AVIF_CODEC_AOM (codec_aom.c) +const char * avifCodecVersionAOM(void); // requires AVIF_CODEC_AOM (codec_aom.c) +AVIF_NODISCARD avifCodec * avifCodecCreateDav1d(void); // requires AVIF_CODEC_DAV1D (codec_dav1d.c) +const char * avifCodecVersionDav1d(void); // requires AVIF_CODEC_DAV1D (codec_dav1d.c) +AVIF_NODISCARD avifCodec * avifCodecCreateGav1(void); // requires AVIF_CODEC_LIBGAV1 (codec_libgav1.c) +const char * avifCodecVersionGav1(void); // requires AVIF_CODEC_LIBGAV1 (codec_libgav1.c) +AVIF_NODISCARD avifCodec * avifCodecCreateRav1e(void); // requires AVIF_CODEC_RAV1E (codec_rav1e.c) +const char * avifCodecVersionRav1e(void); // requires AVIF_CODEC_RAV1E (codec_rav1e.c) +AVIF_NODISCARD avifCodec * avifCodecCreateSvt(void); // requires AVIF_CODEC_SVT (codec_svt.c) +const char * avifCodecVersionSvt(void); // requires AVIF_CODEC_SVT (codec_svt.c) +AVIF_NODISCARD avifCodec * avifCodecCreateAVM(void); // requires AVIF_CODEC_AVM (codec_avm.c) +const char * avifCodecVersionAVM(void); // requires AVIF_CODEC_AVM (codec_avm.c) + +// --------------------------------------------------------------------------- +// avifDiagnostics + +#ifdef __clang__ +__attribute__((__format__(__printf__, 2, 3))) +#endif +void avifDiagnosticsPrintf(avifDiagnostics * diag, const char * format, ...); + +#if defined(AVIF_ENABLE_COMPLIANCE_WARDEN) +avifResult avifIsCompliant(const uint8_t * data, size_t size); +#endif + +// --------------------------------------------------------------------------- +// avifStream +// +// In network byte order (big-endian) unless otherwise specified. + +typedef size_t avifBoxMarker; + +typedef struct avifBoxHeader +{ + // If set to AVIF_TRUE, it means that the box goes on until the end of the + // stream. So, |size| must be set to the number of bytes left in the input + // stream. If set to AVIF_FALSE, |size| indicates the size of the box in + // bytes, excluding the box header. + avifBool isSizeZeroBox; + // Size of the box in bytes, excluding the box header. + size_t size; + + uint8_t type[4]; + uint8_t usertype[16]; // Unused unless |type| is "uuid". +} avifBoxHeader; + +// IMPORTANT: Functions operating on avifROStream * stream shall maintain the +// invariant that stream->offset <= stream->raw->size. +// Code outside src/stream.c shall only access stream->raw and stream->offset +// through the avifROStream*() functions. +typedef struct avifROStream +{ + avifROData * raw; + + // Index of the next byte in the raw stream. + size_t offset; + + // If 0, byte-aligned functions can be used (avifROStreamRead() etc.). + // Otherwise, it represents the number of bits already used in the last byte + // (located at offset-1). + size_t numUsedBitsInPartialByte; + + // Error information, if any. + avifDiagnostics * diag; + const char * diagContext; +} avifROStream; + +const uint8_t * avifROStreamCurrent(avifROStream * stream); +void avifROStreamStart(avifROStream * stream, avifROData * raw, avifDiagnostics * diag, const char * diagContext); +size_t avifROStreamOffset(const avifROStream * stream); +void avifROStreamSetOffset(avifROStream * stream, size_t offset); + +AVIF_NODISCARD avifBool avifROStreamHasBytesLeft(const avifROStream * stream, size_t byteCount); +size_t avifROStreamRemainingBytes(const avifROStream * stream); +// The following functions require byte alignment. +AVIF_NODISCARD avifBool avifROStreamSkip(avifROStream * stream, size_t byteCount); +AVIF_NODISCARD avifBool avifROStreamRead(avifROStream * stream, uint8_t * data, size_t size); +AVIF_NODISCARD avifBool avifROStreamReadU16(avifROStream * stream, uint16_t * v); +AVIF_NODISCARD avifBool avifROStreamReadU16Endianness(avifROStream * stream, uint16_t * v, avifBool littleEndian); +AVIF_NODISCARD avifBool avifROStreamReadU32(avifROStream * stream, uint32_t * v); +AVIF_NODISCARD avifBool avifROStreamReadU32Endianness(avifROStream * stream, uint32_t * v, avifBool littleEndian); +// Reads a factor*8 sized uint, saves in v. If factor is 0, reads nothing and saves 0 in v. +AVIF_NODISCARD avifBool avifROStreamReadUX8(avifROStream * stream, uint64_t * v, uint64_t factor); +AVIF_NODISCARD avifBool avifROStreamReadU64(avifROStream * stream, uint64_t * v); +AVIF_NODISCARD avifBool avifROStreamReadString(avifROStream * stream, char * output, size_t outputSize); +AVIF_NODISCARD avifBool avifROStreamReadBoxHeader(avifROStream * stream, avifBoxHeader * header); // This fails if the size reported by the header cannot fit in the stream +AVIF_NODISCARD avifBool avifROStreamReadBoxHeaderPartial(avifROStream * stream, avifBoxHeader * header, avifBool topLevel); // This doesn't require that the full box can fit in the stream +AVIF_NODISCARD avifBool avifROStreamReadVersionAndFlags(avifROStream * stream, uint8_t * version, uint32_t * flags); // version and flags ptrs are both optional +AVIF_NODISCARD avifBool avifROStreamReadAndEnforceVersion(avifROStream * stream, uint8_t enforcedVersion, uint32_t * flags); // flags ptr is optional +// The following functions can read non-aligned bits. +AVIF_NODISCARD avifBool avifROStreamSkipBits(avifROStream * stream, size_t bitCount); +AVIF_NODISCARD avifBool avifROStreamReadBitsU8(avifROStream * stream, uint8_t * v, size_t bitCount); +AVIF_NODISCARD avifBool avifROStreamReadBitsU16(avifROStream * stream, uint16_t * v, size_t bitCount); +AVIF_NODISCARD avifBool avifROStreamReadBitsU32(avifROStream * stream, uint32_t * v, size_t bitCount); + +typedef struct avifRWStream +{ + avifRWData * raw; + + // Index of the next byte in the raw stream. + size_t offset; + + // If 0, byte-aligned functions can be used (avifRWStreamWrite() etc.). + // Otherwise, it represents the number of bits already used in the last byte + // (located at offset-1). + size_t numUsedBitsInPartialByte; +} avifRWStream; + +void avifRWStreamStart(avifRWStream * stream, avifRWData * raw); +size_t avifRWStreamOffset(const avifRWStream * stream); +void avifRWStreamSetOffset(avifRWStream * stream, size_t offset); + +void avifRWStreamFinishWrite(avifRWStream * stream); +// The following functions require byte alignment. +avifResult avifRWStreamWrite(avifRWStream * stream, const void * data, size_t size); +avifResult avifRWStreamWriteChars(avifRWStream * stream, const char * chars, size_t size); +// On success, if marker is not null, *marker contains the offset of the size +// field in stream and should be passed to avifRWStreamFinishBox(). +avifResult avifRWStreamWriteBox(avifRWStream * stream, const char * type, size_t contentSize, avifBoxMarker * marker); +// On success, if marker is not null, *marker contains the offset of the size +// field in stream and should be passed to avifRWStreamFinishBox(). +avifResult avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, size_t contentSize, int version, uint32_t flags, avifBoxMarker * marker); +// marker is the offset of the size field in stream, returned by a previous +// avifRWStreamWriteBox() or avifRWStreamWriteFullBox() call. +AVIF_NODISCARD avifResult avifRWStreamFinishBox(avifRWStream * stream, avifBoxMarker marker); +avifResult avifRWStreamWriteU8(avifRWStream * stream, uint8_t v); +avifResult avifRWStreamWriteU16(avifRWStream * stream, uint16_t v); +avifResult avifRWStreamWriteU32(avifRWStream * stream, uint32_t v); +avifResult avifRWStreamWriteU64(avifRWStream * stream, uint64_t v); +avifResult avifRWStreamWriteZeros(avifRWStream * stream, size_t byteCount); +// The following functions can write non-aligned bits. +avifResult avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount); + +// This is to make it clear that the box size is currently unknown, and will be determined later (with a call to avifRWStreamFinishBox) +#define AVIF_BOX_SIZE_TBD 0 + +// Used for both av1C and av2C. +typedef struct avifCodecConfigurationBox +{ + // [skipped; is constant] unsigned int (1)marker = 1; + // [skipped; is constant] unsigned int (7)version = 1; + + uint8_t seqProfile; // unsigned int (3) seq_profile; + uint8_t seqLevelIdx0; // unsigned int (5) seq_level_idx_0; + uint8_t seqTier0; // unsigned int (1) seq_tier_0; + uint8_t highBitdepth; // unsigned int (1) high_bitdepth; + uint8_t twelveBit; // unsigned int (1) twelve_bit; + uint8_t monochrome; // unsigned int (1) monochrome; + uint8_t chromaSubsamplingX; // unsigned int (1) chroma_subsampling_x; + uint8_t chromaSubsamplingY; // unsigned int (1) chroma_subsampling_y; + uint8_t chromaSamplePosition; // unsigned int (2) chroma_sample_position; + + // unsigned int (3)reserved = 0; + // unsigned int (1)initial_presentation_delay_present; + // if (initial_presentation_delay_present) { + // unsigned int (4)initial_presentation_delay_minus_one; + // } else { + // unsigned int (4)reserved = 0; + // } +} avifCodecConfigurationBox; + +typedef struct avifSequenceHeader +{ + uint8_t reduced_still_picture_header; + uint32_t maxWidth; + uint32_t maxHeight; + uint32_t bitDepth; + avifPixelFormat yuvFormat; + avifChromaSamplePosition chromaSamplePosition; + avifColorPrimaries colorPrimaries; + avifTransferCharacteristics transferCharacteristics; + avifMatrixCoefficients matrixCoefficients; + avifRange range; + avifCodecConfigurationBox av1C; // TODO(yguyon): Rename or add av2C +} avifSequenceHeader; + +AVIF_NODISCARD avifBool avifSequenceHeaderParse(avifSequenceHeader * header, const avifROData * sample, avifCodecType codecType); + +#if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI) +// Subsampling type as defined in ISO/IEC 23008-12:2024/CDAM 2:2025 section 6.5.6.3. +typedef enum avifPixiSubsamplingType +{ + AVIF_PIXI_444 = 0, + AVIF_PIXI_422 = 1, + AVIF_PIXI_420 = 2, + AVIF_PIXI_411 = 3, + AVIF_PIXI_440 = 4, + AVIF_PIXI_SUBSAMPLING_RESERVED = 5, +} avifPixiSubsamplingType; + +// Mapping from subsampling_x, subsampling_y as defined in AV1 specification Section 6.4.2 +// to PixelInformationBox subsampling_type as defined in ISO/IEC 23008-12:2024/CDAM 2:2025 section 6.5.6.3. +uint8_t avifCodecConfigurationBoxGetSubsamplingType(const avifCodecConfigurationBox * av1C, uint8_t channelIndex); +#endif + +// --------------------------------------------------------------------------- +// gain maps + +// Initializes avifGainMap to default values. +void avifGainMapSetDefaults(avifGainMap * gainMap); + +// Finds the approximate min/max values from the given gain map values, excluding outliers. +// Uses a histogram, with outliers defined as having at least one empty bucket between them +// and the rest of the distribution. Discards at most 0.1% of values. +// Removing outliers helps with accuracy/compression. +avifResult avifFindMinMaxWithoutOutliers(const float * gainMapF, size_t numPixels, float * rangeMin, float * rangeMax); + +avifResult avifGainMapValidateMetadata(const avifGainMap * gainMap, avifDiagnostics * diag); + +// Returns true if both gain maps have the same metadata. Pixels are not checked. +avifBool avifSameGainMapMetadata(const avifGainMap * a, const avifGainMap * b); +avifBool avifSameGainMapAltMetadata(const avifGainMap * a, const avifGainMap * b); + +#define AVIF_INDEFINITE_DURATION64 UINT64_MAX +#define AVIF_INDEFINITE_DURATION32 UINT32_MAX + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // ifndef AVIF_INTERNAL_H diff --git a/3rdparty/libavif/libavif-amalgamated.c b/3rdparty/libavif/libavif-amalgamated.c new file mode 100644 index 0000000..5762524 --- /dev/null +++ b/3rdparty/libavif/libavif-amalgamated.c @@ -0,0 +1,88 @@ +/* bimg: amalgamated libavif decoder (dav1d codec only) + bundled libyuv subset. */ + +/* Platform feature macros must be set before any system header is included. */ +#if !defined(_WIN32) +# ifndef _GNU_SOURCE +# define _GNU_SOURCE // posix_memalign, PTHREAD_STACK_MIN (glibc), etc. +# endif +# ifndef _FILE_OFFSET_BITS +# define _FILE_OFFSET_BITS 64 // 64-bit off_t on 32-bit POSIX (e.g. Android) +# endif +#endif + +#if defined(_MSC_VER) +# pragma warning(push) +# pragma warning(disable: 4057) // warning C4057: indirection to slightly different base types +# pragma warning(disable: 4090) // warning C4090: different 'const' qualifiers +# pragma warning(disable: 4100) // warning C4100: unreferenced formal parameter +# pragma warning(disable: 4127) // warning C4127: conditional expression is constant +# pragma warning(disable: 4152) // warning C4152: function/data pointer conversion in expression +# pragma warning(disable: 4189) // warning C4189: local variable is initialized but not referenced +# pragma warning(disable: 4200) // warning C4200: zero-sized array in struct/union +# pragma warning(disable: 4201) // warning C4201: nonstandard extension: nameless struct/union +# pragma warning(disable: 4244) // warning C4244: conversion, possible loss of data +# pragma warning(disable: 4245) // warning C4245: conversion, signed/unsigned mismatch +# pragma warning(disable: 4324) // warning C4324: structure was padded due to alignment specifier +# pragma warning(disable: 4389) // warning C4389: signed/unsigned mismatch +# pragma warning(disable: 4456) // warning C4456: declaration hides previous local declaration +# pragma warning(disable: 4457) // warning C4457: declaration hides function parameter +# pragma warning(disable: 4701) // warning C4701: potentially uninitialized local variable used +# pragma warning(disable: 4702) // warning C4702: unreachable code +# pragma warning(disable: 4703) // warning C4703: potentially uninitialized local pointer variable used +# pragma warning(disable: 5287) // warning C5287: operands are different enum types +#elif defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wunused-parameter" +# pragma clang diagnostic ignored "-Wunused-function" +# pragma clang diagnostic ignored "-Wunused-variable" +# pragma clang diagnostic ignored "-Wsign-compare" +# pragma clang diagnostic ignored "-Wmissing-field-initializers" +# pragma clang diagnostic ignored "-Wimplicit-fallthrough" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-parameter" +# pragma GCC diagnostic ignored "-Wunused-function" +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wunused-but-set-variable" +# pragma GCC diagnostic ignored "-Wsign-compare" +# pragma GCC diagnostic ignored "-Wmaybe-uninitialized" +# pragma GCC diagnostic ignored "-Wmissing-field-initializers" +# pragma GCC diagnostic ignored "-Wimplicit-fallthrough" +# pragma GCC diagnostic ignored "-Wtype-limits" +#endif + +#include "src/alpha.c" +#include "src/avif.c" +#include "src/codec_dav1d.c" +#include "src/colr.c" +#include "src/colrconvert.c" +#include "src/diag.c" +#include "src/exif.c" +#include "src/gainmap.c" +#include "src/io.c" +#include "src/mem.c" +#include "src/obu.c" +#include "src/properties.c" +#include "src/rawdata.c" +#include "src/read.c" +#include "src/reformat_libsharpyuv.c" +#include "src/reformat_libyuv.c" +#include "src/reformat.c" +#include "src/sampletransform.c" +#include "src/scale.c" +#include "src/stream.c" +#include "src/utils.c" + +#include "third_party/libyuv/source/planar_functions.c" +#include "third_party/libyuv/source/row_common.c" +#include "third_party/libyuv/source/scale.c" +#include "third_party/libyuv/source/scale_any.c" +#include "third_party/libyuv/source/scale_common.c" + +#if defined(_MSC_VER) +# pragma warning(pop) +#elif defined(__clang__) +# pragma clang diagnostic pop +#elif defined(__GNUC__) +# pragma GCC diagnostic pop +#endif diff --git a/3rdparty/libavif/src/alpha.c b/3rdparty/libavif/src/alpha.c new file mode 100644 index 0000000..5c9b7a9 --- /dev/null +++ b/3rdparty/libavif/src/alpha.c @@ -0,0 +1,535 @@ +// Copyright 2020 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include + +void avifFillAlpha(const avifAlphaParams * params) +{ + if (params->dstDepth > 8) { + const uint16_t maxChannel = (uint16_t)((1 << params->dstDepth) - 1); + uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes]; + for (uint32_t j = 0; j < params->height; ++j) { + uint8_t * dstPixel = dstRow; + for (uint32_t i = 0; i < params->width; ++i) { + *((uint16_t *)dstPixel) = maxChannel; + dstPixel += params->dstPixelBytes; + } + dstRow += params->dstRowBytes; + } + } else { + // In this case, (1 << params->dstDepth) - 1 is always equal to 255. + const uint8_t maxChannel = 255; + uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes]; + for (uint32_t j = 0; j < params->height; ++j) { + uint8_t * dstPixel = dstRow; + for (uint32_t i = 0; i < params->width; ++i) { + *dstPixel = maxChannel; + dstPixel += params->dstPixelBytes; + } + dstRow += params->dstRowBytes; + } + } +} + +void avifReformatAlpha(const avifAlphaParams * params) +{ + const int srcMaxChannel = (1 << params->srcDepth) - 1; + const int dstMaxChannel = (1 << params->dstDepth) - 1; + const float srcMaxChannelF = (float)srcMaxChannel; + const float dstMaxChannelF = (float)dstMaxChannel; + + if (params->srcDepth == params->dstDepth) { + // no depth rescale + + if (params->srcDepth > 8) { + // no depth rescale, uint16_t -> uint16_t + + const uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes]; + uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes]; + for (uint32_t j = 0; j < params->height; ++j) { + const uint8_t * srcPixel = srcRow; + uint8_t * dstPixel = dstRow; + for (uint32_t i = 0; i < params->width; ++i) { + *((uint16_t *)dstPixel) = *((const uint16_t *)srcPixel); + srcPixel += params->srcPixelBytes; + dstPixel += params->dstPixelBytes; + } + srcRow += params->srcRowBytes; + dstRow += params->dstRowBytes; + } + } else { + // no depth rescale, uint8_t -> uint8_t + + const uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes]; + uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes]; + for (uint32_t j = 0; j < params->height; ++j) { + const uint8_t * srcPixel = srcRow; + uint8_t * dstPixel = dstRow; + for (uint32_t i = 0; i < params->width; ++i) { + *dstPixel = *srcPixel; + srcPixel += params->srcPixelBytes; + dstPixel += params->dstPixelBytes; + } + srcRow += params->srcRowBytes; + dstRow += params->dstRowBytes; + } + } + } else { + // depth rescale + + if (params->srcDepth > 8) { + if (params->dstDepth > 8) { + // depth rescale, uint16_t -> uint16_t + + const uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes]; + uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes]; + for (uint32_t j = 0; j < params->height; ++j) { + const uint8_t * srcPixel = srcRow; + uint8_t * dstPixel = dstRow; + for (uint32_t i = 0; i < params->width; ++i) { + int srcAlpha = *((const uint16_t *)srcPixel); + float alphaF = (float)srcAlpha / srcMaxChannelF; + int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); + dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); + *((uint16_t *)dstPixel) = (uint16_t)dstAlpha; + srcPixel += params->srcPixelBytes; + dstPixel += params->dstPixelBytes; + } + srcRow += params->srcRowBytes; + dstRow += params->dstRowBytes; + } + } else { + // depth rescale, uint16_t -> uint8_t + + const uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes]; + uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes]; + for (uint32_t j = 0; j < params->height; ++j) { + const uint8_t * srcPixel = srcRow; + uint8_t * dstPixel = dstRow; + for (uint32_t i = 0; i < params->width; ++i) { + int srcAlpha = *((const uint16_t *)srcPixel); + float alphaF = (float)srcAlpha / srcMaxChannelF; + int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); + dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); + *dstPixel = (uint8_t)dstAlpha; + srcPixel += params->srcPixelBytes; + dstPixel += params->dstPixelBytes; + } + srcRow += params->srcRowBytes; + dstRow += params->dstRowBytes; + } + } + } else { + // If (srcDepth == 8), dstDepth must be >8 otherwise we'd be in the (params->srcDepth == params->dstDepth) block above. + assert(params->dstDepth > 8); + + // depth rescale, uint8_t -> uint16_t + const uint8_t * srcRow = ¶ms->srcPlane[params->srcOffsetBytes]; + uint8_t * dstRow = ¶ms->dstPlane[params->dstOffsetBytes]; + for (uint32_t j = 0; j < params->height; ++j) { + const uint8_t * srcPixel = srcRow; + uint8_t * dstPixel = dstRow; + for (uint32_t i = 0; i < params->width; ++i) { + int srcAlpha = *srcPixel; + float alphaF = (float)srcAlpha / srcMaxChannelF; + int dstAlpha = (int)(0.5f + (alphaF * dstMaxChannelF)); + dstAlpha = AVIF_CLAMP(dstAlpha, 0, dstMaxChannel); + *((uint16_t *)dstPixel) = (uint16_t)dstAlpha; + srcPixel += params->srcPixelBytes; + dstPixel += params->dstPixelBytes; + } + srcRow += params->srcRowBytes; + dstRow += params->dstRowBytes; + } + } + } +} + +avifResult avifRGBImagePremultiplyAlpha(avifRGBImage * rgb) +{ + // no data + if (!rgb->pixels || !rgb->rowBytes) { + return AVIF_RESULT_REFORMAT_FAILED; + } + + // no alpha. + if (!avifRGBFormatHasAlpha(rgb->format)) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + + avifResult libyuvResult = avifRGBImagePremultiplyAlphaLibYUV(rgb); + if (libyuvResult != AVIF_RESULT_NOT_IMPLEMENTED) { + return libyuvResult; + } + + assert(rgb->depth >= 8 && rgb->depth <= 16); + + uint32_t max = (1 << rgb->depth) - 1; + float maxF = (float)max; + + if (rgb->depth > 8) { + if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint16_t * pixel = (uint16_t *)row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint16_t a = pixel[3]; + if (a >= max) { + // opaque is no-op + } else if (a == 0) { + // result must be zero + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + } else { + // a < maxF is always true now, so we don't need clamp here + pixel[0] = (uint16_t)avifRoundf((float)pixel[0] * (float)a / maxF); + pixel[1] = (uint16_t)avifRoundf((float)pixel[1] * (float)a / maxF); + pixel[2] = (uint16_t)avifRoundf((float)pixel[2] * (float)a / maxF); + } + pixel += 4; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_ARGB || rgb->format == AVIF_RGB_FORMAT_ABGR) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint16_t * pixel = (uint16_t *)row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint16_t a = pixel[0]; + if (a >= max) { + } else if (a == 0) { + pixel[1] = 0; + pixel[2] = 0; + pixel[3] = 0; + } else { + pixel[1] = (uint16_t)avifRoundf((float)pixel[1] * (float)a / maxF); + pixel[2] = (uint16_t)avifRoundf((float)pixel[2] * (float)a / maxF); + pixel[3] = (uint16_t)avifRoundf((float)pixel[3] * (float)a / maxF); + } + pixel += 4; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_GRAYA) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint16_t * pixel = (uint16_t *)row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint16_t a = pixel[1]; + if (a >= max) { + // opaque is no-op + } else if (a == 0) { + // result must be zero + pixel[0] = 0; + } else { + // a < maxF is always true now, so we don't need clamp here + pixel[0] = (uint16_t)avifRoundf((float)pixel[0] * (float)a / maxF); + } + pixel += 2; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_AGRAY) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint16_t * pixel = (uint16_t *)row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint16_t a = pixel[0]; + if (a >= max) { + } else if (a == 0) { + pixel[1] = 0; + } else { + pixel[1] = (uint16_t)avifRoundf((float)pixel[1] * (float)a / maxF); + } + pixel += 2; + } + row += rgb->rowBytes; + } + } else { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + } else { + if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint8_t * pixel = row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint8_t a = pixel[3]; + // uint8_t can't exceed 255 + if (a == max) { + } else if (a == 0) { + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + } else { + pixel[0] = (uint8_t)avifRoundf((float)pixel[0] * (float)a / maxF); + pixel[1] = (uint8_t)avifRoundf((float)pixel[1] * (float)a / maxF); + pixel[2] = (uint8_t)avifRoundf((float)pixel[2] * (float)a / maxF); + } + pixel += 4; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_ARGB || rgb->format == AVIF_RGB_FORMAT_ABGR) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint8_t * pixel = row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint8_t a = pixel[0]; + if (a == max) { + } else if (a == 0) { + pixel[1] = 0; + pixel[2] = 0; + pixel[3] = 0; + } else { + pixel[1] = (uint8_t)avifRoundf((float)pixel[1] * (float)a / maxF); + pixel[2] = (uint8_t)avifRoundf((float)pixel[2] * (float)a / maxF); + pixel[3] = (uint8_t)avifRoundf((float)pixel[3] * (float)a / maxF); + } + pixel += 4; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_GRAYA) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint8_t * pixel = row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint8_t a = pixel[1]; + // uint8_t can't exceed 255 + if (a == max) { + } else if (a == 0) { + pixel[0] = 0; + } else { + pixel[0] = (uint8_t)avifRoundf((float)pixel[0] * (float)a / maxF); + } + pixel += 2; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_AGRAY) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint8_t * pixel = row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint8_t a = pixel[0]; + if (a == max) { + } else if (a == 0) { + pixel[1] = 0; + } else { + pixel[1] = (uint8_t)avifRoundf((float)pixel[1] * (float)a / maxF); + } + pixel += 2; + } + row += rgb->rowBytes; + } + } else { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + } + + return AVIF_RESULT_OK; +} + +avifResult avifRGBImageUnpremultiplyAlpha(avifRGBImage * rgb) +{ + // no data + if (!rgb->pixels || !rgb->rowBytes) { + return AVIF_RESULT_REFORMAT_FAILED; + } + + // no alpha. + if (!avifRGBFormatHasAlpha(rgb->format)) { + return AVIF_RESULT_REFORMAT_FAILED; + } + + avifResult libyuvResult = avifRGBImageUnpremultiplyAlphaLibYUV(rgb); + if (libyuvResult != AVIF_RESULT_NOT_IMPLEMENTED) { + return libyuvResult; + } + + assert(rgb->depth >= 8 && rgb->depth <= 16); + + uint32_t max = (1 << rgb->depth) - 1; + float maxF = (float)max; + + if (rgb->depth > 8) { + if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint16_t * pixel = (uint16_t *)row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint16_t a = pixel[3]; + if (a >= max) { + // opaque is no-op + } else if (a == 0) { + // prevent division by zero + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + } else { + float c1 = avifRoundf((float)pixel[0] * maxF / (float)a); + float c2 = avifRoundf((float)pixel[1] * maxF / (float)a); + float c3 = avifRoundf((float)pixel[2] * maxF / (float)a); + pixel[0] = (uint16_t)AVIF_MIN(c1, maxF); + pixel[1] = (uint16_t)AVIF_MIN(c2, maxF); + pixel[2] = (uint16_t)AVIF_MIN(c3, maxF); + } + pixel += 4; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_ARGB || rgb->format == AVIF_RGB_FORMAT_ABGR) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint16_t * pixel = (uint16_t *)row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint16_t a = pixel[0]; + if (a >= max) { + } else if (a == 0) { + pixel[1] = 0; + pixel[2] = 0; + pixel[3] = 0; + } else { + float c1 = avifRoundf((float)pixel[1] * maxF / (float)a); + float c2 = avifRoundf((float)pixel[2] * maxF / (float)a); + float c3 = avifRoundf((float)pixel[3] * maxF / (float)a); + pixel[1] = (uint16_t)AVIF_MIN(c1, maxF); + pixel[2] = (uint16_t)AVIF_MIN(c2, maxF); + pixel[3] = (uint16_t)AVIF_MIN(c3, maxF); + } + pixel += 4; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_GRAYA) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint16_t * pixel = (uint16_t *)row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint16_t a = pixel[1]; + if (a >= max) { + // opaque is no-op + } else if (a == 0) { + // prevent division by zero + pixel[0] = 0; + } else { + float c1 = avifRoundf((float)pixel[0] * maxF / (float)a); + pixel[0] = (uint16_t)AVIF_MIN(c1, maxF); + } + pixel += 2; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_AGRAY) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint16_t * pixel = (uint16_t *)row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint16_t a = pixel[0]; + if (a >= max) { + } else if (a == 0) { + pixel[1] = 0; + } else { + float c1 = avifRoundf((float)pixel[1] * maxF / (float)a); + pixel[1] = (uint16_t)AVIF_MIN(c1, maxF); + } + pixel += 2; + } + row += rgb->rowBytes; + } + } else { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + } else { + if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint8_t * pixel = row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint8_t a = pixel[3]; + if (a == max) { + } else if (a == 0) { + pixel[0] = 0; + pixel[1] = 0; + pixel[2] = 0; + } else { + float c1 = avifRoundf((float)pixel[0] * maxF / (float)a); + float c2 = avifRoundf((float)pixel[1] * maxF / (float)a); + float c3 = avifRoundf((float)pixel[2] * maxF / (float)a); + pixel[0] = (uint8_t)AVIF_MIN(c1, maxF); + pixel[1] = (uint8_t)AVIF_MIN(c2, maxF); + pixel[2] = (uint8_t)AVIF_MIN(c3, maxF); + } + pixel += 4; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_ARGB || rgb->format == AVIF_RGB_FORMAT_ABGR) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint8_t * pixel = row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint8_t a = pixel[0]; + if (a == max) { + } else if (a == 0) { + pixel[1] = 0; + pixel[2] = 0; + pixel[3] = 0; + } else { + float c1 = avifRoundf((float)pixel[1] * maxF / (float)a); + float c2 = avifRoundf((float)pixel[2] * maxF / (float)a); + float c3 = avifRoundf((float)pixel[3] * maxF / (float)a); + pixel[1] = (uint8_t)AVIF_MIN(c1, maxF); + pixel[2] = (uint8_t)AVIF_MIN(c2, maxF); + pixel[3] = (uint8_t)AVIF_MIN(c3, maxF); + } + pixel += 4; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_GRAYA) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint8_t * pixel = row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint8_t a = pixel[1]; + if (a == max) { + } else if (a == 0) { + pixel[0] = 0; + } else { + float c1 = avifRoundf((float)pixel[0] * maxF / (float)a); + pixel[0] = (uint8_t)AVIF_MIN(c1, maxF); + } + pixel += 2; + } + row += rgb->rowBytes; + } + } else if (rgb->format == AVIF_RGB_FORMAT_AGRAY) { + uint8_t * row = rgb->pixels; + for (uint32_t j = 0; j < rgb->height; ++j) { + uint8_t * pixel = row; + for (uint32_t i = 0; i < rgb->width; ++i) { + uint8_t a = pixel[0]; + if (a == max) { + } else if (a == 0) { + pixel[1] = 0; + } else { + float c1 = avifRoundf((float)pixel[1] * maxF / (float)a); + pixel[1] = (uint8_t)AVIF_MIN(c1, maxF); + } + pixel += 2; + } + row += rgb->rowBytes; + } + } else { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + } + + return AVIF_RESULT_OK; +} diff --git a/3rdparty/libavif/src/avif.c b/3rdparty/libavif/src/avif.c new file mode 100644 index 0000000..a6f12f0 --- /dev/null +++ b/3rdparty/libavif/src/avif.c @@ -0,0 +1,1348 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include +#include +#include + +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#define AVIF_VERSION_STRING (STR(AVIF_VERSION_MAJOR) "." STR(AVIF_VERSION_MINOR) "." STR(AVIF_VERSION_PATCH)) + +const char * avifVersion(void) +{ + return AVIF_VERSION_STRING; +} + +const char * avifPixelFormatToString(avifPixelFormat format) +{ + switch (format) { + case AVIF_PIXEL_FORMAT_YUV444: + return "YUV444"; + case AVIF_PIXEL_FORMAT_YUV420: + return "YUV420"; + case AVIF_PIXEL_FORMAT_YUV422: + return "YUV422"; + case AVIF_PIXEL_FORMAT_YUV400: + return "YUV400"; + case AVIF_PIXEL_FORMAT_NONE: + case AVIF_PIXEL_FORMAT_COUNT: + default: + break; + } + return "Unknown"; +} + +void avifGetPixelFormatInfo(avifPixelFormat format, avifPixelFormatInfo * info) +{ + memset(info, 0, sizeof(avifPixelFormatInfo)); + + switch (format) { + case AVIF_PIXEL_FORMAT_YUV444: + info->chromaShiftX = 0; + info->chromaShiftY = 0; + break; + + case AVIF_PIXEL_FORMAT_YUV422: + info->chromaShiftX = 1; + info->chromaShiftY = 0; + break; + + case AVIF_PIXEL_FORMAT_YUV420: + info->chromaShiftX = 1; + info->chromaShiftY = 1; + break; + + case AVIF_PIXEL_FORMAT_YUV400: + info->monochrome = AVIF_TRUE; + // The nonexistent chroma is considered as subsampled in each dimension + // according to the AV1 specification. See sections 5.5.2 and 6.4.2. + info->chromaShiftX = 1; + info->chromaShiftY = 1; + break; + + case AVIF_PIXEL_FORMAT_NONE: + case AVIF_PIXEL_FORMAT_COUNT: + default: + break; + } +} + +const char * avifResultToString(avifResult result) +{ + // clang-format off + switch (result) { + case AVIF_RESULT_OK: return "OK"; + case AVIF_RESULT_INVALID_FTYP: return "Invalid ftyp"; + case AVIF_RESULT_NO_CONTENT: return "No content"; + case AVIF_RESULT_NO_YUV_FORMAT_SELECTED: return "No YUV format selected"; + case AVIF_RESULT_REFORMAT_FAILED: return "Reformat failed"; + case AVIF_RESULT_UNSUPPORTED_DEPTH: return "Unsupported depth"; + case AVIF_RESULT_ENCODE_COLOR_FAILED: return "Encoding of color planes failed"; + case AVIF_RESULT_ENCODE_ALPHA_FAILED: return "Encoding of alpha plane failed"; + case AVIF_RESULT_BMFF_PARSE_FAILED: return "BMFF parsing failed"; + case AVIF_RESULT_MISSING_IMAGE_ITEM: return "Missing or empty image item"; + case AVIF_RESULT_DECODE_COLOR_FAILED: return "Decoding of color planes failed"; + case AVIF_RESULT_DECODE_ALPHA_FAILED: return "Decoding of alpha plane failed"; + case AVIF_RESULT_COLOR_ALPHA_SIZE_MISMATCH: return "Color and alpha planes size mismatch"; + case AVIF_RESULT_ISPE_SIZE_MISMATCH: return "Plane sizes don't match ispe values"; + case AVIF_RESULT_NO_CODEC_AVAILABLE: return "No codec available"; + case AVIF_RESULT_NO_IMAGES_REMAINING: return "No images remaining"; + case AVIF_RESULT_INVALID_EXIF_PAYLOAD: return "Invalid Exif payload"; + case AVIF_RESULT_INVALID_IMAGE_GRID: return "Invalid image grid"; + case AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION: return "Invalid codec-specific option"; + case AVIF_RESULT_TRUNCATED_DATA: return "Truncated data"; + case AVIF_RESULT_IO_NOT_SET: return "IO not set"; + case AVIF_RESULT_IO_ERROR: return "IO Error"; + case AVIF_RESULT_WAITING_ON_IO: return "Waiting on IO"; + case AVIF_RESULT_INVALID_ARGUMENT: return "Invalid argument"; + case AVIF_RESULT_NOT_IMPLEMENTED: return "Not implemented"; + case AVIF_RESULT_OUT_OF_MEMORY: return "Out of memory"; + case AVIF_RESULT_CANNOT_CHANGE_SETTING: return "Cannot change some setting during encoding"; + case AVIF_RESULT_INCOMPATIBLE_IMAGE: return "The image is incompatible with already encoded images"; + case AVIF_RESULT_INTERNAL_ERROR: return "Internal error"; + case AVIF_RESULT_ENCODE_GAIN_MAP_FAILED: return "Encoding of gain map planes failed"; + case AVIF_RESULT_DECODE_GAIN_MAP_FAILED: return "Decoding of gain map planes failed"; + case AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE: return "Invalid tone mapped image item"; + case AVIF_RESULT_ENCODE_SAMPLE_TRANSFORM_FAILED: return "Encoding of sample transformed image failed"; + case AVIF_RESULT_DECODE_SAMPLE_TRANSFORM_FAILED: return "Decoding of sample transformed image failed"; + case AVIF_RESULT_UNKNOWN_ERROR: + default: + break; + } + // clang-format on + return "Unknown Error"; +} + +const char * avifProgressiveStateToString(avifProgressiveState progressiveState) +{ + // clang-format off + switch (progressiveState) { + case AVIF_PROGRESSIVE_STATE_UNAVAILABLE: return "Unavailable"; + case AVIF_PROGRESSIVE_STATE_AVAILABLE: return "Available"; + case AVIF_PROGRESSIVE_STATE_ACTIVE: return "Active"; + default: + break; + } + // clang-format on + return "Unknown"; +} + +void avifImageSetDefaults(avifImage * image) +{ + memset(image, 0, sizeof(avifImage)); + image->yuvRange = AVIF_RANGE_FULL; + image->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED; + image->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED; + image->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED; +} + +avifImage * avifImageCreate(uint32_t width, uint32_t height, uint32_t depth, avifPixelFormat yuvFormat) +{ + // width and height are checked when actually used, for example by avifImageAllocatePlanes(). + AVIF_CHECKERR(depth <= 16, NULL); // avifImage only supports up to 16 bits per sample. See avifImageUsesU16(). + // Cast to silence "comparison of unsigned expression is always true" warning. + AVIF_CHECKERR((int)yuvFormat >= AVIF_PIXEL_FORMAT_NONE && yuvFormat < AVIF_PIXEL_FORMAT_COUNT, NULL); + + avifImage * image = (avifImage *)avifAlloc(sizeof(avifImage)); + AVIF_CHECKERR(image, NULL); + avifImageSetDefaults(image); + image->width = width; + image->height = height; + image->depth = depth; + image->yuvFormat = yuvFormat; + return image; +} + +avifImage * avifImageCreateEmpty(void) +{ + return avifImageCreate(0, 0, 0, AVIF_PIXEL_FORMAT_NONE); +} + +void avifImageCopyNoAlloc(avifImage * dstImage, const avifImage * srcImage) +{ + dstImage->width = srcImage->width; + dstImage->height = srcImage->height; + dstImage->depth = srcImage->depth; + dstImage->yuvFormat = srcImage->yuvFormat; + dstImage->yuvRange = srcImage->yuvRange; + dstImage->yuvChromaSamplePosition = srcImage->yuvChromaSamplePosition; + dstImage->alphaPremultiplied = srcImage->alphaPremultiplied; + + dstImage->colorPrimaries = srcImage->colorPrimaries; + dstImage->transferCharacteristics = srcImage->transferCharacteristics; + dstImage->matrixCoefficients = srcImage->matrixCoefficients; + dstImage->clli = srcImage->clli; + + dstImage->transformFlags = srcImage->transformFlags; + dstImage->pasp = srcImage->pasp; + dstImage->clap = srcImage->clap; + dstImage->irot = srcImage->irot; + dstImage->imir = srcImage->imir; +} + +void avifImageCopySamples(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes) +{ + assert(srcImage->depth == dstImage->depth); + if (planes & AVIF_PLANES_YUV) { + assert(srcImage->yuvFormat == dstImage->yuvFormat); + // Note that there may be a mismatch between srcImage->yuvRange and dstImage->yuvRange + // because libavif allows for 'colr' and AV1 OBU video range values to differ. + } + const size_t bytesPerPixel = avifImageUsesU16(srcImage) ? 2 : 1; + + const avifBool skipColor = !(planes & AVIF_PLANES_YUV); + const avifBool skipAlpha = !(planes & AVIF_PLANES_A); + for (int c = AVIF_CHAN_Y; c <= AVIF_CHAN_A; ++c) { + const avifBool alpha = c == AVIF_CHAN_A; + if ((skipColor && !alpha) || (skipAlpha && alpha)) { + continue; + } + + const uint32_t planeWidth = avifImagePlaneWidth(srcImage, c); + const uint32_t planeHeight = avifImagePlaneHeight(srcImage, c); + const uint8_t * srcRow = avifImagePlane(srcImage, c); + uint8_t * dstRow = avifImagePlane(dstImage, c); + const uint32_t srcRowBytes = avifImagePlaneRowBytes(srcImage, c); + const uint32_t dstRowBytes = avifImagePlaneRowBytes(dstImage, c); + assert(!srcRow == !dstRow); + if (!srcRow) { + continue; + } + assert(planeWidth == avifImagePlaneWidth(dstImage, c)); + assert(planeHeight == avifImagePlaneHeight(dstImage, c)); + + const size_t planeWidthBytes = planeWidth * bytesPerPixel; + for (uint32_t y = 0; y < planeHeight; ++y) { + memcpy(dstRow, srcRow, planeWidthBytes); + srcRow += srcRowBytes; + dstRow += dstRowBytes; + } + } +} + +static avifResult avifImageCopyProperties(avifImage * dstImage, const avifImage * srcImage) +{ + for (size_t i = 0; i < dstImage->numProperties; ++i) { + avifRWDataFree(&dstImage->properties[i].boxPayload); + } + avifFree(dstImage->properties); + dstImage->properties = NULL; + dstImage->numProperties = 0; + + if (srcImage->numProperties != 0) { + dstImage->properties = (avifImageItemProperty *)avifCalloc(srcImage->numProperties, sizeof(srcImage->properties[0])); + AVIF_CHECKERR(dstImage->properties != NULL, AVIF_RESULT_OUT_OF_MEMORY); + dstImage->numProperties = srcImage->numProperties; + for (size_t i = 0; i < srcImage->numProperties; ++i) { + memcpy(dstImage->properties[i].boxtype, srcImage->properties[i].boxtype, sizeof(srcImage->properties[i].boxtype)); + memcpy(dstImage->properties[i].usertype, srcImage->properties[i].usertype, sizeof(srcImage->properties[i].usertype)); + AVIF_CHECKRES(avifRWDataSet(&dstImage->properties[i].boxPayload, + srcImage->properties[i].boxPayload.data, + srcImage->properties[i].boxPayload.size)); + } + } + return AVIF_RESULT_OK; +} + +avifResult avifImageCopy(avifImage * dstImage, const avifImage * srcImage, avifPlanesFlags planes) +{ + // Disallow self copy even though it could be supported easily. Self copy is + // unlikely to be needed, so it almost always indicates a programming error. + AVIF_CHECKERR(dstImage != srcImage, AVIF_RESULT_INVALID_ARGUMENT); + avifImageFreePlanes(dstImage, AVIF_PLANES_ALL); + avifImageCopyNoAlloc(dstImage, srcImage); + + AVIF_CHECKRES(avifImageSetProfileICC(dstImage, srcImage->icc.data, srcImage->icc.size)); + + AVIF_CHECKRES(avifRWDataSet(&dstImage->exif, srcImage->exif.data, srcImage->exif.size)); + AVIF_CHECKRES(avifImageSetMetadataXMP(dstImage, srcImage->xmp.data, srcImage->xmp.size)); + + AVIF_CHECKRES(avifImageCopyProperties(dstImage, srcImage)); + + if ((planes & AVIF_PLANES_YUV) && srcImage->yuvPlanes[AVIF_CHAN_Y]) { + if ((srcImage->yuvFormat != AVIF_PIXEL_FORMAT_YUV400) && + (!srcImage->yuvPlanes[AVIF_CHAN_U] || !srcImage->yuvPlanes[AVIF_CHAN_V])) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + const avifResult allocationResult = avifImageAllocatePlanes(dstImage, AVIF_PLANES_YUV); + if (allocationResult != AVIF_RESULT_OK) { + return allocationResult; + } + } + if ((planes & AVIF_PLANES_A) && srcImage->alphaPlane) { + const avifResult allocationResult = avifImageAllocatePlanes(dstImage, AVIF_PLANES_A); + if (allocationResult != AVIF_RESULT_OK) { + return allocationResult; + } + } + avifImageCopySamples(dstImage, srcImage, planes); + + if (srcImage->gainMap) { + if (!dstImage->gainMap) { + dstImage->gainMap = avifGainMapCreate(); + AVIF_CHECKERR(dstImage->gainMap, AVIF_RESULT_OUT_OF_MEMORY); + } + for (int c = 0; c < 3; ++c) { + dstImage->gainMap->gainMapMin[c] = srcImage->gainMap->gainMapMin[c]; + dstImage->gainMap->gainMapMax[c] = srcImage->gainMap->gainMapMax[c]; + dstImage->gainMap->gainMapGamma[c] = srcImage->gainMap->gainMapGamma[c]; + dstImage->gainMap->baseOffset[c] = srcImage->gainMap->baseOffset[c]; + dstImage->gainMap->alternateOffset[c] = srcImage->gainMap->alternateOffset[c]; + } + dstImage->gainMap->baseHdrHeadroom = srcImage->gainMap->baseHdrHeadroom; + dstImage->gainMap->alternateHdrHeadroom = srcImage->gainMap->alternateHdrHeadroom; + dstImage->gainMap->useBaseColorSpace = srcImage->gainMap->useBaseColorSpace; + AVIF_CHECKRES(avifRWDataSet(&dstImage->gainMap->altICC, srcImage->gainMap->altICC.data, srcImage->gainMap->altICC.size)); + dstImage->gainMap->altColorPrimaries = srcImage->gainMap->altColorPrimaries; + dstImage->gainMap->altTransferCharacteristics = srcImage->gainMap->altTransferCharacteristics; + dstImage->gainMap->altMatrixCoefficients = srcImage->gainMap->altMatrixCoefficients; + dstImage->gainMap->altDepth = srcImage->gainMap->altDepth; + dstImage->gainMap->altPlaneCount = srcImage->gainMap->altPlaneCount; + dstImage->gainMap->altCLLI = srcImage->gainMap->altCLLI; + + if (srcImage->gainMap->image) { + if (!dstImage->gainMap->image) { + dstImage->gainMap->image = avifImageCreateEmpty(); + AVIF_CHECKERR(dstImage->gainMap->image, AVIF_RESULT_OUT_OF_MEMORY); + } + AVIF_CHECKRES(avifImageCopy(dstImage->gainMap->image, srcImage->gainMap->image, planes)); + } else if (dstImage->gainMap->image) { + avifImageDestroy(dstImage->gainMap->image); + dstImage->gainMap->image = NULL; + } + } else if (dstImage->gainMap) { + avifGainMapDestroy(dstImage->gainMap); + dstImage->gainMap = NULL; + } + + return AVIF_RESULT_OK; +} + +avifResult avifImageSetViewRect(avifImage * dstImage, const avifImage * srcImage, const avifCropRect * rect) +{ + AVIF_CHECKERR(dstImage != srcImage, AVIF_RESULT_INVALID_ARGUMENT); + avifPixelFormatInfo formatInfo; + avifGetPixelFormatInfo(srcImage->yuvFormat, &formatInfo); + if ((rect->width > srcImage->width) || (rect->height > srcImage->height) || (rect->x > (srcImage->width - rect->width)) || + (rect->y > (srcImage->height - rect->height))) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + if (!formatInfo.monochrome && ((rect->x & formatInfo.chromaShiftX) || (rect->y & formatInfo.chromaShiftY))) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + avifImageFreePlanes(dstImage, AVIF_PLANES_ALL); // dstImage->imageOwnsYUVPlanes and dstImage->imageOwnsAlphaPlane set to AVIF_FALSE. + avifImageCopyNoAlloc(dstImage, srcImage); + dstImage->width = rect->width; + dstImage->height = rect->height; + const uint32_t pixelBytes = (srcImage->depth > 8) ? 2 : 1; + if (srcImage->yuvPlanes[AVIF_CHAN_Y]) { + for (int yuvPlane = AVIF_CHAN_Y; yuvPlane <= AVIF_CHAN_V; ++yuvPlane) { + if (srcImage->yuvRowBytes[yuvPlane]) { + const size_t planeX = (yuvPlane == AVIF_CHAN_Y) ? rect->x : (rect->x >> formatInfo.chromaShiftX); + const size_t planeY = (yuvPlane == AVIF_CHAN_Y) ? rect->y : (rect->y >> formatInfo.chromaShiftY); + dstImage->yuvPlanes[yuvPlane] = + srcImage->yuvPlanes[yuvPlane] + planeY * srcImage->yuvRowBytes[yuvPlane] + planeX * pixelBytes; + dstImage->yuvRowBytes[yuvPlane] = srcImage->yuvRowBytes[yuvPlane]; + } + } + } + if (srcImage->alphaPlane) { + dstImage->alphaPlane = srcImage->alphaPlane + (size_t)rect->y * srcImage->alphaRowBytes + (size_t)rect->x * pixelBytes; + dstImage->alphaRowBytes = srcImage->alphaRowBytes; + } + return AVIF_RESULT_OK; +} + +void avifImageDestroy(avifImage * image) +{ + if (image->gainMap) { + avifGainMapDestroy(image->gainMap); + } + avifImageFreePlanes(image, AVIF_PLANES_ALL); + avifRWDataFree(&image->icc); + avifRWDataFree(&image->exif); + avifRWDataFree(&image->xmp); + for (size_t i = 0; i < image->numProperties; ++i) { + avifRWDataFree(&image->properties[i].boxPayload); + } + avifFree(image->properties); + image->properties = NULL; + image->numProperties = 0; + avifFree(image); +} + +avifResult avifImageSetProfileICC(avifImage * image, const uint8_t * icc, size_t iccSize) +{ + return avifRWDataSet(&image->icc, icc, iccSize); +} + +avifResult avifImageSetMetadataXMP(avifImage * image, const uint8_t * xmp, size_t xmpSize) +{ + return avifRWDataSet(&image->xmp, xmp, xmpSize); +} + +avifResult avifImagePushProperty(avifImage * image, const uint8_t boxtype[4], const uint8_t usertype[16], const uint8_t * boxPayload, size_t boxPayloadSize) +{ + AVIF_CHECKERR(image->numProperties < SIZE_MAX / sizeof(avifImageItemProperty), AVIF_RESULT_INVALID_ARGUMENT); + // Shallow copy the current properties. + const size_t numProperties = image->numProperties + 1; + avifImageItemProperty * const properties = (avifImageItemProperty *)avifAlloc(numProperties * sizeof(properties[0])); + AVIF_CHECKERR(properties != NULL, AVIF_RESULT_OUT_OF_MEMORY); + if (image->numProperties != 0) { + memcpy(properties, image->properties, image->numProperties * sizeof(properties[0])); + } + // Free the old array and replace it by the new one. + avifFree(image->properties); + image->properties = properties; + image->numProperties = numProperties; + // Set the new property. + avifImageItemProperty * const property = &image->properties[image->numProperties - 1]; + memset(property, 0, sizeof(*property)); + memcpy(property->boxtype, boxtype, sizeof(property->boxtype)); + memcpy(property->usertype, usertype, sizeof(property->usertype)); + AVIF_CHECKRES(avifRWDataSet(&property->boxPayload, boxPayload, boxPayloadSize)); + return AVIF_RESULT_OK; +} + +avifResult avifImageAddOpaqueProperty(avifImage * image, const uint8_t boxtype[4], const uint8_t * data, size_t dataSize) +{ + const uint8_t uuid[16] = { 0 }; + // Do not allow adding properties that are also handled by libavif + if (avifIsKnownPropertyType(boxtype)) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + return avifImagePushProperty(image, boxtype, uuid, data, dataSize); +} + +avifResult avifImageAddUUIDProperty(avifImage * image, const uint8_t uuid[16], const uint8_t * data, size_t dataSize) +{ + const uint8_t boxtype[4] = { 'u', 'u', 'i', 'd' }; + // Do not allow adding invalid UUIDs, or using uuid representation of properties that are also handled by libavif + if (!avifIsValidUUID(uuid)) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + return avifImagePushProperty(image, boxtype, uuid, data, dataSize); +} + +avifResult avifImageAllocatePlanes(avifImage * image, avifPlanesFlags planes) +{ + if (image->width == 0 || image->height == 0 || image->depth == 0 || image->depth > 16) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + const uint32_t channelSize = avifImageUsesU16(image) ? 2 : 1; + if (image->width > UINT32_MAX / channelSize) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + const uint32_t fullRowBytes = channelSize * image->width; + if (image->height > PTRDIFF_MAX / fullRowBytes) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + const size_t fullSize = (size_t)fullRowBytes * image->height; + + if ((planes & AVIF_PLANES_YUV) && (image->yuvFormat != AVIF_PIXEL_FORMAT_NONE)) { + avifPixelFormatInfo info; + avifGetPixelFormatInfo(image->yuvFormat, &info); + + image->imageOwnsYUVPlanes = AVIF_TRUE; + if (!image->yuvPlanes[AVIF_CHAN_Y]) { + image->yuvPlanes[AVIF_CHAN_Y] = (uint8_t *)avifAlloc(fullSize); + if (!image->yuvPlanes[AVIF_CHAN_Y]) { + return AVIF_RESULT_OUT_OF_MEMORY; + } + image->yuvRowBytes[AVIF_CHAN_Y] = fullRowBytes; + } + + if (!info.monochrome) { + // Intermediary computation as 64 bits in case width or height is exactly UINT32_MAX. + const uint32_t shiftedW = (uint32_t)(((uint64_t)image->width + info.chromaShiftX) >> info.chromaShiftX); + const uint32_t shiftedH = (uint32_t)(((uint64_t)image->height + info.chromaShiftY) >> info.chromaShiftY); + + // These are less than or equal to fullRowBytes/fullSize. No need to check overflows. + const uint32_t uvRowBytes = channelSize * shiftedW; + const size_t uvSize = (size_t)uvRowBytes * shiftedH; + + for (int uvPlane = AVIF_CHAN_U; uvPlane <= AVIF_CHAN_V; ++uvPlane) { + if (!image->yuvPlanes[uvPlane]) { + image->yuvPlanes[uvPlane] = (uint8_t *)avifAlloc(uvSize); + if (!image->yuvPlanes[uvPlane]) { + return AVIF_RESULT_OUT_OF_MEMORY; + } + image->yuvRowBytes[uvPlane] = uvRowBytes; + } + } + } + } + if (planes & AVIF_PLANES_A) { + image->imageOwnsAlphaPlane = AVIF_TRUE; + if (!image->alphaPlane) { + image->alphaPlane = (uint8_t *)avifAlloc(fullSize); + if (!image->alphaPlane) { + return AVIF_RESULT_OUT_OF_MEMORY; + } + image->alphaRowBytes = fullRowBytes; + } + } + return AVIF_RESULT_OK; +} + +void avifImageFreePlanes(avifImage * image, avifPlanesFlags planes) +{ + if ((planes & AVIF_PLANES_YUV) && (image->yuvFormat != AVIF_PIXEL_FORMAT_NONE)) { + if (image->imageOwnsYUVPlanes) { + avifFree(image->yuvPlanes[AVIF_CHAN_Y]); + avifFree(image->yuvPlanes[AVIF_CHAN_U]); + avifFree(image->yuvPlanes[AVIF_CHAN_V]); + } + image->yuvPlanes[AVIF_CHAN_Y] = NULL; + image->yuvRowBytes[AVIF_CHAN_Y] = 0; + image->yuvPlanes[AVIF_CHAN_U] = NULL; + image->yuvRowBytes[AVIF_CHAN_U] = 0; + image->yuvPlanes[AVIF_CHAN_V] = NULL; + image->yuvRowBytes[AVIF_CHAN_V] = 0; + image->imageOwnsYUVPlanes = AVIF_FALSE; + } + if (planes & AVIF_PLANES_A) { + if (image->imageOwnsAlphaPlane) { + avifFree(image->alphaPlane); + } + image->alphaPlane = NULL; + image->alphaRowBytes = 0; + image->imageOwnsAlphaPlane = AVIF_FALSE; + } +} + +void avifImageStealPlanes(avifImage * dstImage, avifImage * srcImage, avifPlanesFlags planes) +{ + avifImageFreePlanes(dstImage, planes); + + if (planes & AVIF_PLANES_YUV) { + dstImage->yuvPlanes[AVIF_CHAN_Y] = srcImage->yuvPlanes[AVIF_CHAN_Y]; + dstImage->yuvRowBytes[AVIF_CHAN_Y] = srcImage->yuvRowBytes[AVIF_CHAN_Y]; + dstImage->yuvPlanes[AVIF_CHAN_U] = srcImage->yuvPlanes[AVIF_CHAN_U]; + dstImage->yuvRowBytes[AVIF_CHAN_U] = srcImage->yuvRowBytes[AVIF_CHAN_U]; + dstImage->yuvPlanes[AVIF_CHAN_V] = srcImage->yuvPlanes[AVIF_CHAN_V]; + dstImage->yuvRowBytes[AVIF_CHAN_V] = srcImage->yuvRowBytes[AVIF_CHAN_V]; + + srcImage->yuvPlanes[AVIF_CHAN_Y] = NULL; + srcImage->yuvRowBytes[AVIF_CHAN_Y] = 0; + srcImage->yuvPlanes[AVIF_CHAN_U] = NULL; + srcImage->yuvRowBytes[AVIF_CHAN_U] = 0; + srcImage->yuvPlanes[AVIF_CHAN_V] = NULL; + srcImage->yuvRowBytes[AVIF_CHAN_V] = 0; + + dstImage->yuvFormat = srcImage->yuvFormat; + dstImage->imageOwnsYUVPlanes = srcImage->imageOwnsYUVPlanes; + srcImage->imageOwnsYUVPlanes = AVIF_FALSE; + } + if (planes & AVIF_PLANES_A) { + dstImage->alphaPlane = srcImage->alphaPlane; + dstImage->alphaRowBytes = srcImage->alphaRowBytes; + + srcImage->alphaPlane = NULL; + srcImage->alphaRowBytes = 0; + + dstImage->imageOwnsAlphaPlane = srcImage->imageOwnsAlphaPlane; + srcImage->imageOwnsAlphaPlane = AVIF_FALSE; + } +} + +avifBool avifImageUsesU16(const avifImage * image) +{ + return (image->depth > 8); +} + +avifBool avifImageIsOpaque(const avifImage * image) +{ + if (!image->alphaPlane) { + return AVIF_TRUE; + } + + const uint32_t opaqueValue = (1u << image->depth) - 1u; + const uint8_t * row = image->alphaPlane; + for (uint32_t y = 0; y < image->height; ++y) { + if (avifImageUsesU16(image)) { + const uint16_t * row16 = (const uint16_t *)row; + for (uint32_t x = 0; x < image->width; ++x) { + if (row16[x] != opaqueValue) { + return AVIF_FALSE; + } + } + } else { + for (uint32_t x = 0; x < image->width; ++x) { + if (row[x] != opaqueValue) { + return AVIF_FALSE; + } + } + } + row += image->alphaRowBytes; + } + return AVIF_TRUE; +} + +uint8_t * avifImagePlane(const avifImage * image, int channel) +{ + if ((channel == AVIF_CHAN_Y) || (channel == AVIF_CHAN_U) || (channel == AVIF_CHAN_V)) { + return image->yuvPlanes[channel]; + } + if (channel == AVIF_CHAN_A) { + return image->alphaPlane; + } + return NULL; +} + +uint32_t avifImagePlaneRowBytes(const avifImage * image, int channel) +{ + if ((channel == AVIF_CHAN_Y) || (channel == AVIF_CHAN_U) || (channel == AVIF_CHAN_V)) { + return image->yuvRowBytes[channel]; + } + if (channel == AVIF_CHAN_A) { + return image->alphaRowBytes; + } + return 0; +} + +uint32_t avifImagePlaneWidth(const avifImage * image, int channel) +{ + if (channel == AVIF_CHAN_Y) { + return image->width; + } + if ((channel == AVIF_CHAN_U) || (channel == AVIF_CHAN_V)) { + avifPixelFormatInfo formatInfo; + avifGetPixelFormatInfo(image->yuvFormat, &formatInfo); + if (formatInfo.monochrome) { + return 0; + } + return (image->width + formatInfo.chromaShiftX) >> formatInfo.chromaShiftX; + } + if ((channel == AVIF_CHAN_A) && image->alphaPlane) { + return image->width; + } + return 0; +} + +uint32_t avifImagePlaneHeight(const avifImage * image, int channel) +{ + if (channel == AVIF_CHAN_Y) { + return image->height; + } + if ((channel == AVIF_CHAN_U) || (channel == AVIF_CHAN_V)) { + avifPixelFormatInfo formatInfo; + avifGetPixelFormatInfo(image->yuvFormat, &formatInfo); + if (formatInfo.monochrome) { + return 0; + } + return (image->height + formatInfo.chromaShiftY) >> formatInfo.chromaShiftY; + } + if ((channel == AVIF_CHAN_A) && image->alphaPlane) { + return image->height; + } + return 0; +} + +avifBool avifDimensionsTooLarge(uint32_t width, uint32_t height, uint32_t imageSizeLimit, uint32_t imageDimensionLimit) +{ + if (width > (imageSizeLimit / height)) { + return AVIF_TRUE; + } + if ((imageDimensionLimit != 0) && ((width > imageDimensionLimit) || (height > imageDimensionLimit))) { + return AVIF_TRUE; + } + return AVIF_FALSE; +} + +// avifCodecCreate*() functions are in their respective codec_*.c files + +void avifCodecDestroy(avifCodec * codec) +{ + if (codec && codec->destroyInternal) { + codec->destroyInternal(codec); + } + avifFree(codec); +} + +// --------------------------------------------------------------------------- +// avifRGBImage + +avifBool avifRGBFormatIsGray(avifRGBFormat format) +{ + return (format == AVIF_RGB_FORMAT_GRAY) || (format == AVIF_RGB_FORMAT_GRAYA) || (format == AVIF_RGB_FORMAT_AGRAY); +} + +avifBool avifRGBFormatHasAlpha(avifRGBFormat format) +{ + return (format != AVIF_RGB_FORMAT_RGB) && (format != AVIF_RGB_FORMAT_BGR) && (format != AVIF_RGB_FORMAT_RGB_565) && + (format != AVIF_RGB_FORMAT_GRAY); +} + +uint32_t avifRGBFormatChannelCount(avifRGBFormat format) +{ + if (format == AVIF_RGB_FORMAT_GRAY) { + return 1; + } + if ((format == AVIF_RGB_FORMAT_GRAYA) || (format == AVIF_RGB_FORMAT_AGRAY)) { + return 2; + } + return avifRGBFormatHasAlpha(format) ? 4 : 3; +} + +uint32_t avifRGBImagePixelSize(const avifRGBImage * rgb) +{ + if (rgb->format == AVIF_RGB_FORMAT_RGB_565) { + return 2; + } + return avifRGBFormatChannelCount(rgb->format) * ((rgb->depth > 8) ? 2 : 1); +} + +void avifRGBImageSetDefaults(avifRGBImage * rgb, const avifImage * image) +{ + rgb->width = image->width; + rgb->height = image->height; + rgb->depth = image->depth; + rgb->format = AVIF_RGB_FORMAT_RGBA; + rgb->chromaUpsampling = AVIF_CHROMA_UPSAMPLING_AUTOMATIC; + rgb->chromaDownsampling = AVIF_CHROMA_DOWNSAMPLING_AUTOMATIC; + rgb->avoidLibYUV = AVIF_FALSE; + rgb->ignoreAlpha = AVIF_FALSE; + rgb->pixels = NULL; + rgb->rowBytes = 0; + rgb->alphaPremultiplied = AVIF_FALSE; // Most expect RGBA output to *not* be premultiplied. Those that do can opt-in by + // setting this to match image->alphaPremultiplied or forcing this to true + // after calling avifRGBImageSetDefaults(), + rgb->isFloat = AVIF_FALSE; + rgb->maxThreads = 1; +} + +avifResult avifRGBImageAllocatePixels(avifRGBImage * rgb) +{ + avifRGBImageFreePixels(rgb); + if (rgb->width == 0 || rgb->height == 0) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + const uint32_t pixelSize = avifRGBImagePixelSize(rgb); + if (rgb->width > UINT32_MAX / pixelSize) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + const uint32_t rowBytes = rgb->width * pixelSize; + if (rgb->height > PTRDIFF_MAX / rowBytes) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + rgb->pixels = (uint8_t *)avifAlloc((size_t)rowBytes * rgb->height); + AVIF_CHECKERR(rgb->pixels, AVIF_RESULT_OUT_OF_MEMORY); + rgb->rowBytes = rowBytes; + return AVIF_RESULT_OK; +} + +void avifRGBImageFreePixels(avifRGBImage * rgb) +{ + if (rgb->pixels) { + avifFree(rgb->pixels); + } + + rgb->pixels = NULL; + rgb->rowBytes = 0; +} + +// --------------------------------------------------------------------------- +// avifCropRect + +static avifFraction calcCenter(int32_t dim) +{ + avifFraction f; + f.n = dim >> 1; + f.d = 1; + if ((dim % 2) != 0) { + f.n = dim; + f.d = 2; + } + return f; +} + +static avifBool avifCropRectIsValid(const avifCropRect * cropRect, uint32_t imageW, uint32_t imageH, avifDiagnostics * diag) +{ + if ((cropRect->width == 0) || (cropRect->height == 0)) { + avifDiagnosticsPrintf(diag, "[Strict] crop rect width and height must be nonzero"); + return AVIF_FALSE; + } + if ((cropRect->x > (UINT32_MAX - cropRect->width)) || ((cropRect->x + cropRect->width) > imageW) || + (cropRect->y > (UINT32_MAX - cropRect->height)) || ((cropRect->y + cropRect->height) > imageH)) { + avifDiagnosticsPrintf(diag, "[Strict] crop rect is out of the image's bounds"); + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +avifBool avifCropRectFromCleanApertureBox(avifCropRect * cropRect, + const avifCleanApertureBox * clap, + uint32_t imageW, + uint32_t imageH, + avifDiagnostics * diag) +{ + avifDiagnosticsClearError(diag); + + // ISO/IEC 14496-12:2022, Section 12.1.4.1: + // For horizOff and vertOff, D shall be strictly positive and N may be + // positive or negative. For cleanApertureWidth and cleanApertureHeight, + // N shall be positive and D shall be strictly positive. + + const int32_t widthN = (int32_t)clap->widthN; + const int32_t widthD = (int32_t)clap->widthD; + const int32_t heightN = (int32_t)clap->heightN; + const int32_t heightD = (int32_t)clap->heightD; + const int32_t horizOffN = (int32_t)clap->horizOffN; + const int32_t horizOffD = (int32_t)clap->horizOffD; + const int32_t vertOffN = (int32_t)clap->vertOffN; + const int32_t vertOffD = (int32_t)clap->vertOffD; + if ((widthD <= 0) || (heightD <= 0) || (horizOffD <= 0) || (vertOffD <= 0)) { + avifDiagnosticsPrintf(diag, "[Strict] clap contains a denominator that is not strictly positive"); + return AVIF_FALSE; + } + if ((widthN < 0) || (heightN < 0)) { + avifDiagnosticsPrintf(diag, "[Strict] clap width or height is negative"); + return AVIF_FALSE; + } + + // ISO/IEC 23000-22:2019/Amd. 2:2021, Section 7.3.6.7: + // - cleanApertureWidth and cleanApertureHeight shall be integers; + // - The leftmost pixel and the topmost line of the clean aperture as + // defined in ISO/IEC 14496-12:2020, Section 12.1.4.1 shall be integers; + // ... + + if ((widthN % widthD) != 0) { + avifDiagnosticsPrintf(diag, "[Strict] clap width %d/%d is not an integer", widthN, widthD); + return AVIF_FALSE; + } + if ((heightN % heightD) != 0) { + avifDiagnosticsPrintf(diag, "[Strict] clap height %d/%d is not an integer", heightN, heightD); + return AVIF_FALSE; + } + const int32_t clapW = widthN / widthD; + const int32_t clapH = heightN / heightD; + + if ((imageW > INT32_MAX) || (imageH > INT32_MAX)) { + avifDiagnosticsPrintf(diag, "[Strict] image width %u or height %u is greater than INT32_MAX", imageW, imageH); + return AVIF_FALSE; + } + avifFraction uncroppedCenterX = calcCenter((int32_t)imageW); + avifFraction uncroppedCenterY = calcCenter((int32_t)imageH); + + avifFraction horizOff; + horizOff.n = horizOffN; + horizOff.d = horizOffD; + avifFraction croppedCenterX; + if (!avifFractionAdd(uncroppedCenterX, horizOff, &croppedCenterX)) { + avifDiagnosticsPrintf(diag, "[Strict] croppedCenterX overflowed"); + return AVIF_FALSE; + } + + avifFraction vertOff; + vertOff.n = vertOffN; + vertOff.d = vertOffD; + avifFraction croppedCenterY; + if (!avifFractionAdd(uncroppedCenterY, vertOff, &croppedCenterY)) { + avifDiagnosticsPrintf(diag, "[Strict] croppedCenterY overflowed"); + return AVIF_FALSE; + } + + avifFraction halfW; + halfW.n = clapW; + halfW.d = 2; + avifFraction cropX; + if (!avifFractionSub(croppedCenterX, halfW, &cropX)) { + avifDiagnosticsPrintf(diag, "[Strict] cropX overflowed"); + return AVIF_FALSE; + } + if ((cropX.n % cropX.d) != 0) { + avifDiagnosticsPrintf(diag, "[Strict] calculated crop X offset %d/%d is not an integer", cropX.n, cropX.d); + return AVIF_FALSE; + } + + avifFraction halfH; + halfH.n = clapH; + halfH.d = 2; + avifFraction cropY; + if (!avifFractionSub(croppedCenterY, halfH, &cropY)) { + avifDiagnosticsPrintf(diag, "[Strict] cropY overflowed"); + return AVIF_FALSE; + } + if ((cropY.n % cropY.d) != 0) { + avifDiagnosticsPrintf(diag, "[Strict] calculated crop Y offset %d/%d is not an integer", cropY.n, cropY.d); + return AVIF_FALSE; + } + + if ((cropX.n < 0) || (cropY.n < 0)) { + avifDiagnosticsPrintf(diag, "[Strict] at least one crop offset is not positive"); + return AVIF_FALSE; + } + + cropRect->x = (uint32_t)(cropX.n / cropX.d); + cropRect->y = (uint32_t)(cropY.n / cropY.d); + cropRect->width = (uint32_t)clapW; + cropRect->height = (uint32_t)clapH; + return avifCropRectIsValid(cropRect, imageW, imageH, diag); +} + +avifBool avifCleanApertureBoxFromCropRect(avifCleanApertureBox * clap, + const avifCropRect * cropRect, + uint32_t imageW, + uint32_t imageH, + avifDiagnostics * diag) +{ + avifDiagnosticsClearError(diag); + + if (!avifCropRectIsValid(cropRect, imageW, imageH, diag)) { + return AVIF_FALSE; + } + + if ((imageW > INT32_MAX) || (imageH > INT32_MAX)) { + avifDiagnosticsPrintf(diag, "[Strict] image width %u or height %u is greater than INT32_MAX", imageW, imageH); + return AVIF_FALSE; + } + avifFraction uncroppedCenterX = calcCenter((int32_t)imageW); + avifFraction uncroppedCenterY = calcCenter((int32_t)imageH); + + if ((cropRect->width > INT32_MAX) || (cropRect->height > INT32_MAX)) { + avifDiagnosticsPrintf(diag, + "[Strict] crop rect width %u or height %u is greater than INT32_MAX", + cropRect->width, + cropRect->height); + return AVIF_FALSE; + } + avifFraction croppedCenterX = calcCenter((int32_t)cropRect->width); + const int64_t croppedCenterXN = croppedCenterX.n + (int64_t)cropRect->x * croppedCenterX.d; + if (overflowsInt32(croppedCenterXN)) { + avifDiagnosticsPrintf(diag, "[Strict] croppedCenterX overflowed"); + return AVIF_FALSE; + } + croppedCenterX.n = (int32_t)croppedCenterXN; + avifFraction croppedCenterY = calcCenter((int32_t)cropRect->height); + const int64_t croppedCenterYN = croppedCenterY.n + (int64_t)cropRect->y * croppedCenterY.d; + if (overflowsInt32(croppedCenterYN)) { + avifDiagnosticsPrintf(diag, "[Strict] croppedCenterY overflowed"); + return AVIF_FALSE; + } + croppedCenterY.n = (int32_t)croppedCenterYN; + + avifFraction horizOff; + if (!avifFractionSub(croppedCenterX, uncroppedCenterX, &horizOff)) { + avifDiagnosticsPrintf(diag, "[Strict] horizOff overflowed"); + return AVIF_FALSE; + } + avifFraction vertOff; + if (!avifFractionSub(croppedCenterY, uncroppedCenterY, &vertOff)) { + avifDiagnosticsPrintf(diag, "[Strict] vertOff overflowed"); + return AVIF_FALSE; + } + + clap->widthN = cropRect->width; + clap->widthD = 1; + clap->heightN = cropRect->height; + clap->heightD = 1; + clap->horizOffN = horizOff.n; + clap->horizOffD = horizOff.d; + clap->vertOffN = vertOff.n; + clap->vertOffD = vertOff.d; + return AVIF_TRUE; +} + +avifBool avifCropRectRequiresUpsampling(const avifCropRect * cropRect, avifPixelFormat yuvFormat) +{ + // ISO/IEC 23000-22:2024 FDIS, Section 7.3.6.7: + // - If any of the following conditions hold true, the image is first implicitly upsampled to 4:4:4: + // - chroma is subsampled horizontally (i.e., 4:2:2 and 4:2:0) and cleanApertureWidth is odd + // - chroma is subsampled horizontally (i.e., 4:2:2 and 4:2:0) and left-most pixel is on an odd position + // - chroma is subsampled vertically (i.e., 4:2:0) and cleanApertureHeight is odd + // - chroma is subsampled vertically (i.e., 4:2:0) and topmost line is on an odd position + + // AV1 supports odd dimensions with chroma subsampling in those directions, so only look for x and y. + return ((yuvFormat == AVIF_PIXEL_FORMAT_YUV420 || yuvFormat == AVIF_PIXEL_FORMAT_YUV422) && (cropRect->x % 2)) || + (yuvFormat == AVIF_PIXEL_FORMAT_YUV420 && (cropRect->y % 2)); +} + +avifBool avifCropRectConvertCleanApertureBox(avifCropRect * cropRect, + const avifCleanApertureBox * clap, + uint32_t imageW, + uint32_t imageH, + avifPixelFormat yuvFormat, + avifDiagnostics * diag) +{ + if (!avifCropRectFromCleanApertureBox(cropRect, clap, imageW, imageH, diag)) { + return AVIF_FALSE; + } + // Keep the same pre-deprecation behavior. + + // ISO/IEC 23000-22:2019/Amd. 2:2021, Section 7.3.6.7: + // - If chroma is subsampled horizontally (i.e., 4:2:2 and 4:2:0), + // the leftmost pixel of the clean aperture shall be even numbers; + // - If chroma is subsampled vertically (i.e., 4:2:0), + // the topmost line of the clean aperture shall be even numbers. + + if (avifCropRectRequiresUpsampling(cropRect, yuvFormat)) { + avifDiagnosticsPrintf(diag, "[Strict] crop rect X and Y offsets must be even due to this image's YUV subsampling"); + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +avifBool avifCleanApertureBoxConvertCropRect(avifCleanApertureBox * clap, + const avifCropRect * cropRect, + uint32_t imageW, + uint32_t imageH, + avifPixelFormat yuvFormat, + avifDiagnostics * diag) +{ + // Keep the same pre-deprecation behavior. + + // ISO/IEC 23000-22:2019/Amd. 2:2021, Section 7.3.6.7: + // - If chroma is subsampled horizontally (i.e., 4:2:2 and 4:2:0), + // the leftmost pixel of the clean aperture shall be even numbers; + // - If chroma is subsampled vertically (i.e., 4:2:0), + // the topmost line of the clean aperture shall be even numbers. + + if (avifCropRectRequiresUpsampling(cropRect, yuvFormat)) { + avifDiagnosticsPrintf(diag, "[Strict] crop rect X and Y offsets must be even due to this image's YUV subsampling"); + return AVIF_FALSE; + } + + return avifCleanApertureBoxFromCropRect(clap, cropRect, imageW, imageH, diag); +} + +// --------------------------------------------------------------------------- + +avifBool avifIsAlpha(avifItemCategory itemCategory) +{ + if (itemCategory == AVIF_ITEM_ALPHA) { + return AVIF_TRUE; + } + if (itemCategory >= AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_ALPHA && + itemCategory < AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_ALPHA + AVIF_SAMPLE_TRANSFORM_MAX_NUM_EXTRA_INPUT_IMAGE_ITEMS) { + return AVIF_TRUE; + } + return AVIF_FALSE; +} + +// --------------------------------------------------------------------------- + +avifBool avifAreGridDimensionsValid(avifPixelFormat yuvFormat, uint32_t imageW, uint32_t imageH, uint32_t tileW, uint32_t tileH, avifDiagnostics * diag) +{ + // ISO/IEC 23000-22:2019, Section 7.3.11.4.2: + // - the tile_width shall be greater than or equal to 64, and should be a multiple of 64 + // - the tile_height shall be greater than or equal to 64, and should be a multiple of 64 + // The "should" part is ignored here. + if ((tileW < 64) || (tileH < 64)) { + avifDiagnosticsPrintf(diag, + "Grid image tile width (%u) or height (%u) cannot be smaller than 64. " + "See MIAF (ISO/IEC 23000-22:2019), Section 7.3.11.4.2", + tileW, + tileH); + return AVIF_FALSE; + } + + // ISO/IEC 23000-22:2019, Section 7.3.11.4.2: + // - when the images are in the 4:2:2 chroma sampling format the horizontal tile offsets and widths, + // and the output width, shall be even numbers; + // - when the images are in the 4:2:0 chroma sampling format both the horizontal and vertical tile + // offsets and widths, and the output width and height, shall be even numbers. + // If the rules above were not respected, the following problematic situation may happen: + // Some 4:2:0 image is 650 pixels wide and has 10 cell columns, each being 65 pixels wide. + // The chroma plane of the whole image is 325 pixels wide. The chroma plane of each cell is 33 pixels wide. + // 33*10 - 325 gives 5 extra pixels with no specified destination in the reconstructed image. + + // Tile offsets are not enforced since they depend on tile size (ISO/IEC 23008-12:2017, Section 6.6.2.3.1): + // The reconstructed image is formed by tiling the input images into a grid [...] without gap or overlap + if ((((yuvFormat == AVIF_PIXEL_FORMAT_YUV420) || (yuvFormat == AVIF_PIXEL_FORMAT_YUV422)) && + (((imageW % 2) != 0) || ((tileW % 2) != 0))) || + ((yuvFormat == AVIF_PIXEL_FORMAT_YUV420) && (((imageH % 2) != 0) || ((tileH % 2) != 0)))) { + avifDiagnosticsPrintf(diag, + "Grid image width (%u) or height (%u) or tile width (%u) or height (%u) " + "shall be even if chroma is subsampled in that dimension. " + "See MIAF (ISO/IEC 23000-22:2019), Section 7.3.11.4.2", + imageW, + imageH, + tileW, + tileH); + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +// --------------------------------------------------------------------------- +// avifCodecSpecificOption + +// Returns NULL if a memory allocation failed. +static char * avifStrdup(const char * str) +{ + size_t len = strlen(str); + char * dup = (char *)avifAlloc(len + 1); + if (!dup) { + return NULL; + } + memcpy(dup, str, len + 1); + return dup; +} + +avifCodecSpecificOptions * avifCodecSpecificOptionsCreate(void) +{ + avifCodecSpecificOptions * ava = (avifCodecSpecificOptions *)avifAlloc(sizeof(avifCodecSpecificOptions)); + if (!ava || !avifArrayCreate(ava, sizeof(avifCodecSpecificOption), 4)) { + goto error; + } + return ava; + +error: + avifFree(ava); + return NULL; +} + +void avifCodecSpecificOptionsClear(avifCodecSpecificOptions * csOptions) +{ + for (uint32_t i = 0; i < csOptions->count; ++i) { + avifCodecSpecificOption * entry = &csOptions->entries[i]; + avifFree(entry->key); + avifFree(entry->value); + } + + csOptions->count = 0; +} + +void avifCodecSpecificOptionsDestroy(avifCodecSpecificOptions * csOptions) +{ + avifCodecSpecificOptionsClear(csOptions); + avifArrayDestroy(csOptions); + avifFree(csOptions); +} + +avifResult avifCodecSpecificOptionsSet(avifCodecSpecificOptions * csOptions, const char * key, const char * value) +{ + // Check to see if a key must be replaced + for (uint32_t i = 0; i < csOptions->count; ++i) { + avifCodecSpecificOption * entry = &csOptions->entries[i]; + if (!strcmp(entry->key, key)) { + if (value) { + // Update the value + avifFree(entry->value); + entry->value = avifStrdup(value); + AVIF_CHECKERR(entry->value, AVIF_RESULT_OUT_OF_MEMORY); + } else { + // Delete the value + avifFree(entry->key); + avifFree(entry->value); + --csOptions->count; + if (csOptions->count > 0) { + memmove(&csOptions->entries[i], &csOptions->entries[i + 1], (csOptions->count - i) * (size_t)csOptions->elementSize); + } + } + return AVIF_RESULT_OK; + } + } + + if (value) { + // Add a new key + avifCodecSpecificOption * entry = (avifCodecSpecificOption *)avifArrayPush(csOptions); + AVIF_CHECKERR(entry, AVIF_RESULT_OUT_OF_MEMORY); + entry->key = avifStrdup(key); + AVIF_CHECKERR(entry->key, AVIF_RESULT_OUT_OF_MEMORY); + entry->value = avifStrdup(value); + AVIF_CHECKERR(entry->value, AVIF_RESULT_OUT_OF_MEMORY); + } + return AVIF_RESULT_OK; +} + +// --------------------------------------------------------------------------- +// Codec availability and versions + +typedef const char * (*versionFunc)(void); +typedef avifCodec * (*avifCodecCreateFunc)(void); + +struct AvailableCodec +{ + avifCodecChoice choice; + avifCodecType type; + const char * name; + versionFunc version; + avifCodecCreateFunc create; + uint32_t flags; +}; + +// This is the main codec table; it determines all usage/availability in libavif. + +static struct AvailableCodec availableCodecs[] = { +// Ordered by preference (for AUTO) + +#if defined(AVIF_CODEC_DAV1D) + { AVIF_CODEC_CHOICE_DAV1D, AVIF_CODEC_TYPE_AV1, "dav1d", avifCodecVersionDav1d, avifCodecCreateDav1d, AVIF_CODEC_FLAG_CAN_DECODE }, +#endif +#if defined(AVIF_CODEC_LIBGAV1) + { AVIF_CODEC_CHOICE_LIBGAV1, AVIF_CODEC_TYPE_AV1, "libgav1", avifCodecVersionGav1, avifCodecCreateGav1, AVIF_CODEC_FLAG_CAN_DECODE }, +#endif +#if defined(AVIF_CODEC_AOM) + { AVIF_CODEC_CHOICE_AOM, + AVIF_CODEC_TYPE_AV1, + "aom", + avifCodecVersionAOM, + avifCodecCreateAOM, +#if defined(AVIF_CODEC_AOM_DECODE) && defined(AVIF_CODEC_AOM_ENCODE) + AVIF_CODEC_FLAG_CAN_DECODE | AVIF_CODEC_FLAG_CAN_ENCODE +#elif defined(AVIF_CODEC_AOM_DECODE) + AVIF_CODEC_FLAG_CAN_DECODE +#elif defined(AVIF_CODEC_AOM_ENCODE) + AVIF_CODEC_FLAG_CAN_ENCODE +#else +#error AVIF_CODEC_AOM_DECODE or AVIF_CODEC_AOM_ENCODE must be defined +#endif + }, +#endif +#if defined(AVIF_CODEC_RAV1E) + { AVIF_CODEC_CHOICE_RAV1E, AVIF_CODEC_TYPE_AV1, "rav1e", avifCodecVersionRav1e, avifCodecCreateRav1e, AVIF_CODEC_FLAG_CAN_ENCODE }, +#endif +#if defined(AVIF_CODEC_SVT) + { AVIF_CODEC_CHOICE_SVT, AVIF_CODEC_TYPE_AV1, "svt", avifCodecVersionSvt, avifCodecCreateSvt, AVIF_CODEC_FLAG_CAN_ENCODE }, +#endif +#if defined(AVIF_CODEC_AVM) + { AVIF_CODEC_CHOICE_AVM, AVIF_CODEC_TYPE_AV2, "avm", avifCodecVersionAVM, avifCodecCreateAVM, AVIF_CODEC_FLAG_CAN_DECODE | AVIF_CODEC_FLAG_CAN_ENCODE }, +#endif + { AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_TYPE_UNKNOWN, NULL, NULL, NULL, 0 } +}; + +static const int availableCodecsCount = (sizeof(availableCodecs) / sizeof(availableCodecs[0])) - 1; + +static struct AvailableCodec * findAvailableCodec(avifCodecChoice choice, avifCodecFlags requiredFlags) +{ + for (int i = 0; i < availableCodecsCount; ++i) { + if ((choice != AVIF_CODEC_CHOICE_AUTO) && (availableCodecs[i].choice != choice)) { + continue; + } + if (requiredFlags && ((availableCodecs[i].flags & requiredFlags) != requiredFlags)) { + continue; + } + if ((choice == AVIF_CODEC_CHOICE_AUTO) && (availableCodecs[i].choice == AVIF_CODEC_CHOICE_AVM)) { + // AV2 is experimental and cannot be the default, it must be explicitly selected. + continue; + } + return &availableCodecs[i]; + } + return NULL; +} + +const char * avifCodecName(avifCodecChoice choice, avifCodecFlags requiredFlags) +{ + struct AvailableCodec * availableCodec = findAvailableCodec(choice, requiredFlags); + if (availableCodec) { + return availableCodec->name; + } + return NULL; +} + +avifCodecType avifCodecTypeFromChoice(avifCodecChoice choice, avifCodecFlags requiredFlags) +{ + struct AvailableCodec * availableCodec = findAvailableCodec(choice, requiredFlags); + if (availableCodec) { + return availableCodec->type; + } + return AVIF_CODEC_TYPE_UNKNOWN; +} + +avifCodecChoice avifCodecChoiceFromName(const char * name) +{ + for (int i = 0; i < availableCodecsCount; ++i) { + if (!strcmp(availableCodecs[i].name, name)) { + return availableCodecs[i].choice; + } + } + return AVIF_CODEC_CHOICE_AUTO; +} + +avifResult avifCodecCreate(avifCodecChoice choice, avifCodecFlags requiredFlags, avifCodec ** codec) +{ + *codec = NULL; + struct AvailableCodec * availableCodec = findAvailableCodec(choice, requiredFlags); + AVIF_CHECKERR(availableCodec != NULL, AVIF_RESULT_NO_CODEC_AVAILABLE); + *codec = availableCodec->create(); + AVIF_CHECKERR(*codec != NULL, AVIF_RESULT_OUT_OF_MEMORY); + return AVIF_RESULT_OK; +} + +static void append(char ** writePos, size_t * remainingLen, const char * appendStr) +{ + size_t appendLen = strlen(appendStr); + if (appendLen > *remainingLen) { + appendLen = *remainingLen; + } + + memcpy(*writePos, appendStr, appendLen); + *remainingLen -= appendLen; + *writePos += appendLen; + *(*writePos) = 0; +} + +void avifCodecVersions(char outBuffer[256]) +{ + size_t remainingLen = 255; + char * writePos = outBuffer; + *writePos = 0; + + for (int i = 0; i < availableCodecsCount; ++i) { + if (i > 0) { + append(&writePos, &remainingLen, ", "); + } + append(&writePos, &remainingLen, availableCodecs[i].name); + if ((availableCodecs[i].flags & (AVIF_CODEC_FLAG_CAN_ENCODE | AVIF_CODEC_FLAG_CAN_DECODE)) == + (AVIF_CODEC_FLAG_CAN_ENCODE | AVIF_CODEC_FLAG_CAN_DECODE)) { + append(&writePos, &remainingLen, " [enc/dec]"); + } else if (availableCodecs[i].flags & AVIF_CODEC_FLAG_CAN_ENCODE) { + append(&writePos, &remainingLen, " [enc]"); + } else if (availableCodecs[i].flags & AVIF_CODEC_FLAG_CAN_DECODE) { + append(&writePos, &remainingLen, " [dec]"); + } + append(&writePos, &remainingLen, ":"); + append(&writePos, &remainingLen, availableCodecs[i].version()); + } +} + +avifGainMap * avifGainMapCreate(void) +{ + avifGainMap * gainMap = (avifGainMap *)avifAlloc(sizeof(avifGainMap)); + if (!gainMap) { + return NULL; + } + avifGainMapSetDefaults(gainMap); + // Note that some functions like avifDecoderFindGainMapItem() allocate avifGainMap directly on + // the stack instead of calling avifGainMapCreate() to simplify error handling. This works under + // the assumption that no complex initialization (such as dynamic allocation of fields) takes + // place here. If this function becomes more complex than one alloc + setDefaults, such code + // might need to be changed. + return gainMap; +} + +void avifGainMapSetDefaults(avifGainMap * gainMap) +{ + memset(gainMap, 0, sizeof(avifGainMap)); + gainMap->altColorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED; + gainMap->altTransferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED; + gainMap->altMatrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED; + gainMap->altYUVRange = AVIF_RANGE_FULL; + gainMap->useBaseColorSpace = AVIF_TRUE; + // Set all denominators to valid values (1). + for (int i = 0; i < 3; ++i) { + gainMap->gainMapMin[i].d = 1; + gainMap->gainMapMax[i].d = 1; + gainMap->gainMapGamma[i].n = 1; + gainMap->gainMapGamma[i].d = 1; + gainMap->baseOffset[i].d = 1; + gainMap->alternateOffset[i].d = 1; + } + gainMap->baseHdrHeadroom.d = 1; + gainMap->alternateHdrHeadroom.d = 1; +} + +void avifGainMapDestroy(avifGainMap * gainMap) +{ + if (gainMap->image) { + avifImageDestroy(gainMap->image); + } + avifRWDataFree(&gainMap->altICC); + avifFree(gainMap); +} diff --git a/3rdparty/libavif/src/codec_aom.c b/3rdparty/libavif/src/codec_aom.c new file mode 100644 index 0000000..beba54e --- /dev/null +++ b/3rdparty/libavif/src/codec_aom.c @@ -0,0 +1,1423 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +// These are for libaom to deal with +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wduplicate-enum" +#pragma clang diagnostic ignored "-Wextra-semi" +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#endif + +#if defined(AVIF_CODEC_AOM_ENCODE) +#include "aom/aom_encoder.h" +#include "aom/aomcx.h" +#endif + +#if defined(AVIF_CODEC_AOM_DECODE) +#include "aom/aom_decoder.h" +#include "aom/aomdx.h" +#endif + +#ifdef __clang__ +#pragma clang diagnostic pop + +// This fixes complaints with aom_codec_control() and aom_img_fmt that are from libaom +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wused-but-marked-unused" +#pragma clang diagnostic ignored "-Wassign-enum" +#endif + +#include +#include +#include +#include +#include + +#if defined(AVIF_CODEC_AOM_ENCODE) +// Detect whether the aom_codec_set_option() function is available. See aom/aom_codec.h +// in https://aomedia-review.googlesource.com/c/aom/+/126302. +#if AOM_CODEC_ABI_VERSION >= (6 + AOM_IMAGE_ABI_VERSION) +#define HAVE_AOM_CODEC_SET_OPTION 1 +#endif + +// Speeds 7-9 were added to all intra mode in https://aomedia-review.googlesource.com/c/aom/+/140624. +// They first appeared in the libaom v3.2.0 release. +#if AOM_ENCODER_ABI_VERSION >= (10 + AOM_CODEC_ABI_VERSION + /*AOM_EXT_PART_ABI_VERSION=*/1) +#define ALL_INTRA_HAS_SPEEDS_7_TO_9 1 +#endif +#endif + +#define TWO_LAYER_ALL_INTRA_QUALITY_THRESHOLD 10 + +struct avifCodecInternal +{ +#if defined(AVIF_CODEC_AOM_DECODE) + avifBool decoderInitialized; + aom_codec_ctx_t decoder; + aom_codec_iter_t iter; + aom_image_t * image; +#endif + +#if defined(AVIF_CODEC_AOM_ENCODE) + avifBool encoderInitialized; + aom_codec_ctx_t encoder; + struct aom_codec_enc_cfg cfg; + avifPixelFormatInfo formatInfo; + aom_img_fmt_t aomFormat; + uint32_t currentLayer; + int qualityFirstLayer; + avifBool previousFrameUsedTuneIq; +#endif +}; + +static void aomCodecDestroyInternal(avifCodec * codec) +{ +#if defined(AVIF_CODEC_AOM_DECODE) + if (codec->internal->decoderInitialized) { + aom_codec_destroy(&codec->internal->decoder); + } +#endif + +#if defined(AVIF_CODEC_AOM_ENCODE) + if (codec->internal->encoderInitialized) { + aom_codec_destroy(&codec->internal->encoder); + } +#endif + + avifFree(codec->internal); +} + +// Writes a libaom error code and error detail into diagnostics. +static void aomDiagPrintf(avifDiagnostics * diag, const char * func, const aom_codec_ctx_t * ctx) +{ + const char * error = aom_codec_error(ctx); + const char * error_detail = aom_codec_error_detail(ctx); + avifDiagnosticsPrintf(diag, "%s failed: %s: %s", func, error, error_detail ? error_detail : "no error detail"); +} + +#if defined(AVIF_CODEC_AOM_DECODE) + +static avifBool aomCodecGetNextImage(struct avifCodec * codec, + const avifDecodeSample * sample, + avifBool alpha, + avifBool * isLimitedRangeAlpha, + avifImage * image) +{ + assert(sample); + + aom_codec_iface_t * const decoderInterface = aom_codec_av1_dx(); +#if !defined(AOM_CTRL_AOMD_SET_FRAME_SIZE_LIMIT) + // The AOMD_SET_FRAME_SIZE_LIMIT codec control (added in libaom v3.14.0) + // can be used to impose a maximum on AV1 frame size. When + // AOMD_SET_FRAME_SIZE_LIMIT is not available, approximate it with + // aom_codec_peek_stream_info() and avifDimensionsTooLarge(). + struct aom_codec_stream_info streamInfo = { 0 }; + aom_codec_err_t err = aom_codec_peek_stream_info(decoderInterface, sample->data.data, sample->data.size, &streamInfo); + if (err != AOM_CODEC_OK) { + avifDiagnosticsPrintf(codec->diag, "aom_codec_peek_stream_info() failed: %s", aom_codec_err_to_string(err)); + return AVIF_FALSE; + } + if (streamInfo.w == 0 || streamInfo.h == 0) { + // The sequence header was not found. + if (!codec->internal->decoderInitialized) { + // Treat it as an error if the first frame isn't preceded by a sequence header. + return AVIF_FALSE; + } + } else { + if (avifDimensionsTooLarge(streamInfo.w, streamInfo.h, codec->imageSizeLimit, codec->imageDimensionLimit)) { + avifDiagnosticsPrintf(codec->diag, "Image dimensions too large: %dx%d", streamInfo.w, streamInfo.h); + return AVIF_FALSE; + } + } +#endif // !defined(AOM_CTRL_AOMD_SET_FRAME_SIZE_LIMIT) + + if (!codec->internal->decoderInitialized) { + aom_codec_dec_cfg_t cfg; + memset(&cfg, 0, sizeof(aom_codec_dec_cfg_t)); + cfg.threads = codec->maxThreads; + cfg.allow_lowbitdepth = 1; + + if (aom_codec_dec_init(&codec->internal->decoder, decoderInterface, &cfg, 0)) { + aomDiagPrintf(codec->diag, "aom_codec_dec_init()", &codec->internal->decoder); + return AVIF_FALSE; + } + codec->internal->decoderInitialized = AVIF_TRUE; + +#if defined(AOM_CTRL_AOMD_SET_FRAME_SIZE_LIMIT) + if (codec->imageSizeLimit != 0 && aom_codec_control(&codec->internal->decoder, AOMD_SET_FRAME_SIZE_LIMIT, codec->imageSizeLimit)) { + aomDiagPrintf(codec->diag, "aom_codec_control(AOMD_SET_FRAME_SIZE_LIMIT)", &codec->internal->decoder); + return AVIF_FALSE; + } +#endif // defined(AOM_CTRL_AOMD_SET_FRAME_SIZE_LIMIT) + if (aom_codec_control(&codec->internal->decoder, AV1D_SET_OUTPUT_ALL_LAYERS, codec->allLayers)) { + aomDiagPrintf(codec->diag, "aom_codec_control(AV1D_SET_OUTPUT_ALL_LAYERS)", &codec->internal->decoder); + return AVIF_FALSE; + } + if (aom_codec_control(&codec->internal->decoder, AV1D_SET_OPERATING_POINT, codec->operatingPoint)) { + aomDiagPrintf(codec->diag, "aom_codec_control(AV1D_SET_OPERATING_POINT)", &codec->internal->decoder); + return AVIF_FALSE; + } + + codec->internal->iter = NULL; + } + + aom_image_t * nextFrame = NULL; + uint8_t spatialID = AVIF_SPATIAL_ID_UNSET; + for (;;) { + nextFrame = aom_codec_get_frame(&codec->internal->decoder, &codec->internal->iter); + if (nextFrame) { + if (spatialID != AVIF_SPATIAL_ID_UNSET) { + // This requires libaom v3.1.2 or later, which has the fix for + // https://crbug.com/aomedia/2993. + if (spatialID == nextFrame->spatial_id) { + // Found the correct spatial_id. + break; + } + } else { + // Got an image! + break; + } + } else if (sample) { + codec->internal->iter = NULL; + if (aom_codec_decode(&codec->internal->decoder, sample->data.data, sample->data.size, NULL)) { + aomDiagPrintf(codec->diag, "aom_codec_decode()", &codec->internal->decoder); + return AVIF_FALSE; + } + spatialID = sample->spatialID; + sample = NULL; + } else { + break; + } + } + + if (nextFrame) { + codec->internal->image = nextFrame; + } else { + if (alpha && codec->internal->image) { + // Special case: reuse last alpha frame + } else { + return AVIF_FALSE; + } + } + + avifBool isColor = !alpha; + if (isColor) { + // Color (YUV) planes - set image to correct size / format, fill color + + avifPixelFormat yuvFormat = AVIF_PIXEL_FORMAT_NONE; + switch (codec->internal->image->fmt) { + case AOM_IMG_FMT_I420: + case AOM_IMG_FMT_I42016: + yuvFormat = AVIF_PIXEL_FORMAT_YUV420; + break; + case AOM_IMG_FMT_I422: + case AOM_IMG_FMT_I42216: + yuvFormat = AVIF_PIXEL_FORMAT_YUV422; + break; + case AOM_IMG_FMT_I444: + case AOM_IMG_FMT_I44416: + yuvFormat = AVIF_PIXEL_FORMAT_YUV444; + break; + case AOM_IMG_FMT_NONE: +#if defined(AOM_HAVE_IMG_FMT_NV12) + // Although the libaom encoder supports the NV12 image format as an input format, the + // libaom decoder does not support NV12 as an output format. + case AOM_IMG_FMT_NV12: +#endif + case AOM_IMG_FMT_YV12: + case AOM_IMG_FMT_YV1216: + default: + return AVIF_FALSE; + } + if (codec->internal->image->monochrome) { + yuvFormat = AVIF_PIXEL_FORMAT_YUV400; + } + + image->width = codec->internal->image->d_w; + image->height = codec->internal->image->d_h; + image->depth = codec->internal->image->bit_depth; + + image->yuvFormat = yuvFormat; + image->yuvRange = (codec->internal->image->range == AOM_CR_STUDIO_RANGE) ? AVIF_RANGE_LIMITED : AVIF_RANGE_FULL; + image->yuvChromaSamplePosition = (avifChromaSamplePosition)codec->internal->image->csp; + + image->colorPrimaries = (avifColorPrimaries)codec->internal->image->cp; + image->transferCharacteristics = (avifTransferCharacteristics)codec->internal->image->tc; + image->matrixCoefficients = (avifMatrixCoefficients)codec->internal->image->mc; + + // Steal the pointers from the decoder's image directly + avifImageFreePlanes(image, AVIF_PLANES_YUV); + int yuvPlaneCount = (yuvFormat == AVIF_PIXEL_FORMAT_YUV400) ? 1 : 3; + for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { + image->yuvPlanes[yuvPlane] = codec->internal->image->planes[yuvPlane]; + image->yuvRowBytes[yuvPlane] = codec->internal->image->stride[yuvPlane]; + } + image->imageOwnsYUVPlanes = AVIF_FALSE; + } else { + // Alpha plane - set image to correct size, fill alpha + + image->width = codec->internal->image->d_w; + image->height = codec->internal->image->d_h; + image->depth = codec->internal->image->bit_depth; + + avifImageFreePlanes(image, AVIF_PLANES_A); + image->alphaPlane = codec->internal->image->planes[0]; + image->alphaRowBytes = codec->internal->image->stride[0]; + *isLimitedRangeAlpha = (codec->internal->image->range == AOM_CR_STUDIO_RANGE); + image->imageOwnsAlphaPlane = AVIF_FALSE; + } + + return AVIF_TRUE; +} +#endif // defined(AVIF_CODEC_AOM_DECODE) + +#if defined(AVIF_CODEC_AOM_ENCODE) + +static aom_img_fmt_t avifImageCalcAOMFmt(const avifImage * image, avifBool alpha) +{ + aom_img_fmt_t fmt; + if (alpha) { + // We're going monochrome, who cares about chroma quality + fmt = AOM_IMG_FMT_I420; + } else { + switch (image->yuvFormat) { + case AVIF_PIXEL_FORMAT_YUV444: + fmt = AOM_IMG_FMT_I444; + break; + case AVIF_PIXEL_FORMAT_YUV422: + fmt = AOM_IMG_FMT_I422; + break; + case AVIF_PIXEL_FORMAT_YUV420: + case AVIF_PIXEL_FORMAT_YUV400: + fmt = AOM_IMG_FMT_I420; + break; + case AVIF_PIXEL_FORMAT_NONE: + case AVIF_PIXEL_FORMAT_COUNT: + default: + return AOM_IMG_FMT_NONE; + } + } + + if (image->depth > 8) { + fmt |= AOM_IMG_FMT_HIGHBITDEPTH; + } + + return fmt; +} + +#if !defined(HAVE_AOM_CODEC_SET_OPTION) +static avifBool aomOptionParseInt(const char * str, int * val) +{ + char * endptr; + const long rawval = strtol(str, &endptr, 10); + + if (str[0] != '\0' && endptr[0] == '\0' && rawval >= INT_MIN && rawval <= INT_MAX) { + *val = (int)rawval; + return AVIF_TRUE; + } + + return AVIF_FALSE; +} + +static avifBool aomOptionParseUInt(const char * str, unsigned int * val) +{ + char * endptr; + const unsigned long rawval = strtoul(str, &endptr, 10); + + if (str[0] != '\0' && endptr[0] == '\0' && rawval <= UINT_MAX) { + *val = (unsigned int)rawval; + return AVIF_TRUE; + } + + return AVIF_FALSE; +} +#endif // !defined(HAVE_AOM_CODEC_SET_OPTION) + +struct aomOptionEnumList +{ + const char * name; + int val; +}; + +static avifBool aomOptionParseEnum(const char * str, const struct aomOptionEnumList * enums, int * val) +{ + const struct aomOptionEnumList * listptr; + long int rawval; + char * endptr; + + // First see if the value can be parsed as a raw value. + rawval = strtol(str, &endptr, 10); + if (str[0] != '\0' && endptr[0] == '\0') { + // Got a raw value, make sure it's valid. + for (listptr = enums; listptr->name; listptr++) + if (listptr->val == rawval) { + *val = (int)rawval; + return AVIF_TRUE; + } + } + + // Next see if it can be parsed as a string. + for (listptr = enums; listptr->name; listptr++) { + if (!strcmp(str, listptr->name)) { + *val = listptr->val; + return AVIF_TRUE; + } + } + + return AVIF_FALSE; +} + +static const struct aomOptionEnumList endUsageEnum[] = { { "vbr", AOM_VBR }, // Variable Bit Rate (VBR) mode + { "cbr", AOM_CBR }, // Constant Bit Rate (CBR) mode + { "cq", AOM_CQ }, // Constrained Quality (CQ) mode + { "q", AOM_Q }, // Constant Quality (Q) mode + { NULL, 0 } }; + +// Returns true if equals or , where is "color:" or "alpha:" +// or the abbreviated form "c:" or "a:". +static avifBool avifKeyEqualsName(const char * key, const char * name, avifBool alpha) +{ + const char * prefix = alpha ? "alpha:" : "color:"; + size_t prefixLen = 6; + const char * shortPrefix = alpha ? "a:" : "c:"; + size_t shortPrefixLen = 2; + return !strcmp(key, name) || (!strncmp(key, prefix, prefixLen) && !strcmp(key + prefixLen, name)) || + (!strncmp(key, shortPrefix, shortPrefixLen) && !strcmp(key + shortPrefixLen, name)); +} + +#if !defined(AOM_HAVE_TUNE_IQ) +// Define the tune IQ value here if libaom doesn't define it. The enum value is guaranteed to never change +// in libaom, so this definition won't ever get out of sync. +#define AOM_TUNE_IQ 10 +#endif +// Tune IQ string -> enum mapping +static const struct aomOptionEnumList tuneIqEnum[] = { { "iq", AOM_TUNE_IQ }, { NULL, 0 } }; + +// Returns true if codec-specific options for AOM contain a tune metric setting. Returns false otherwise. +// Sets *useTuneIq to true if codec-specific options for AOM contain AOM_TUNE_IQ. Otherwise sets *useTuneIq to false. +static avifBool avifAOMOptionsContainExplicitTuning(const avifCodec * codec, avifBool alpha, avifBool * useTuneIq) +{ + *useTuneIq = AVIF_FALSE; + avifBool isAnyTuneDefined = AVIF_FALSE; + + // If there are multiple "tune" options specified, honor the last one. + // For consistent behavior, handle both cases where tune was either specified as a string (e.g. tune=iq), + // or as an enum value (e.g. tune=10). + for (uint32_t i = 0; i < codec->csOptions->count; ++i) { + const avifCodecSpecificOption * entry = &codec->csOptions->entries[i]; + if (avifKeyEqualsName(entry->key, "tune", alpha)) { + isAnyTuneDefined = AVIF_TRUE; + + int val; + if (aomOptionParseEnum(entry->value, tuneIqEnum, &val)) { + assert(val == AOM_TUNE_IQ); + *useTuneIq = AVIF_TRUE; + } else { + *useTuneIq = AVIF_FALSE; + } + } + } + return isAnyTuneDefined; +} + +static avifBool avifProcessAOMOptionsPreInit(avifCodec * codec, avifBool alpha, struct aom_codec_enc_cfg * cfg) +{ + for (uint32_t i = 0; i < codec->csOptions->count; ++i) { + const avifCodecSpecificOption * entry = &codec->csOptions->entries[i]; + int val; + if (avifKeyEqualsName(entry->key, "end-usage", alpha)) { // Rate control mode + if (!aomOptionParseEnum(entry->value, endUsageEnum, &val)) { + avifDiagnosticsPrintf(codec->diag, "Invalid value for end-usage: %s", entry->value); + return AVIF_FALSE; + } + cfg->rc_end_usage = val; + } + } + return AVIF_TRUE; +} + +#if !defined(HAVE_AOM_CODEC_SET_OPTION) +typedef enum +{ + AVIF_AOM_OPTION_NUL = 0, + AVIF_AOM_OPTION_STR, + AVIF_AOM_OPTION_INT, + AVIF_AOM_OPTION_UINT, + AVIF_AOM_OPTION_ENUM, +} aomOptionType; + +struct aomOptionDef +{ + const char * name; + int controlId; + aomOptionType type; + // If type is AVIF_AOM_OPTION_ENUM, this must be set. Otherwise should be NULL. + const struct aomOptionEnumList * enums; +}; + +static const struct aomOptionEnumList tuningEnum[] = { // + { "psnr", AOM_TUNE_PSNR }, // + { "ssim", AOM_TUNE_SSIM }, // + { NULL, 0 } +}; + +static const struct aomOptionDef aomOptionDefs[] = { + // Adaptive quantization mode + { "aq-mode", AV1E_SET_AQ_MODE, AVIF_AOM_OPTION_UINT, NULL }, + // Constant/Constrained Quality level + { "cq-level", AOME_SET_CQ_LEVEL, AVIF_AOM_OPTION_UINT, NULL }, + // Enable delta quantization in chroma planes + { "enable-chroma-deltaq", AV1E_SET_ENABLE_CHROMA_DELTAQ, AVIF_AOM_OPTION_INT, NULL }, + // Bias towards block sharpness in rate-distortion optimization of transform coefficients + { "sharpness", AOME_SET_SHARPNESS, AVIF_AOM_OPTION_UINT, NULL }, + // Tune distortion metric + { "tune", AOME_SET_TUNING, AVIF_AOM_OPTION_ENUM, tuningEnum }, + // Film grain test vector + { "film-grain-test", AV1E_SET_FILM_GRAIN_TEST_VECTOR, AVIF_AOM_OPTION_INT, NULL }, + // Film grain table file + { "film-grain-table", AV1E_SET_FILM_GRAIN_TABLE, AVIF_AOM_OPTION_STR, NULL }, + + // Sentinel + { NULL, 0, AVIF_AOM_OPTION_NUL, NULL } +}; +#endif // !defined(HAVE_AOM_CODEC_SET_OPTION) + +static avifBool avifProcessAOMOptionsPostInit(avifCodec * codec, avifBool alpha) +{ + for (uint32_t i = 0; i < codec->csOptions->count; ++i) { + const avifCodecSpecificOption * entry = &codec->csOptions->entries[i]; + // Skip options for the other kind of plane. + const char * otherPrefix = alpha ? "color:" : "alpha:"; + size_t otherPrefixLen = 6; + const char * otherShortPrefix = alpha ? "c:" : "a:"; + size_t otherShortPrefixLen = 2; + if (!strncmp(entry->key, otherPrefix, otherPrefixLen) || !strncmp(entry->key, otherShortPrefix, otherShortPrefixLen)) { + continue; + } + + // Skip options processed by avifProcessAOMOptionsPreInit. + if (avifKeyEqualsName(entry->key, "end-usage", alpha)) { + continue; + } + +#if defined(HAVE_AOM_CODEC_SET_OPTION) + const char * prefix = alpha ? "alpha:" : "color:"; + size_t prefixLen = 6; + const char * shortPrefix = alpha ? "a:" : "c:"; + size_t shortPrefixLen = 2; + const char * key = entry->key; + if (!strncmp(key, prefix, prefixLen)) { + key += prefixLen; + } else if (!strncmp(key, shortPrefix, shortPrefixLen)) { + key += shortPrefixLen; + } + if (aom_codec_set_option(&codec->internal->encoder, key, entry->value) != AOM_CODEC_OK) { + const char * error_detail = aom_codec_error_detail(&codec->internal->encoder); + avifDiagnosticsPrintf(codec->diag, + "aom_codec_set_option(\"%s\", \"%s\") failed: %s: %s", + key, + entry->value, + aom_codec_error(&codec->internal->encoder), + error_detail ? error_detail : "no error detail"); + return AVIF_FALSE; + } +#else // !defined(HAVE_AOM_CODEC_SET_OPTION) + avifBool match = AVIF_FALSE; + for (int j = 0; aomOptionDefs[j].name; ++j) { + if (avifKeyEqualsName(entry->key, aomOptionDefs[j].name, alpha)) { + match = AVIF_TRUE; + avifBool success = AVIF_FALSE; + int valInt; + unsigned int valUInt; + switch (aomOptionDefs[j].type) { + case AVIF_AOM_OPTION_NUL: + success = AVIF_FALSE; + break; + case AVIF_AOM_OPTION_STR: + success = aom_codec_control(&codec->internal->encoder, aomOptionDefs[j].controlId, entry->value) == AOM_CODEC_OK; + break; + case AVIF_AOM_OPTION_INT: + success = aomOptionParseInt(entry->value, &valInt) && + aom_codec_control(&codec->internal->encoder, aomOptionDefs[j].controlId, valInt) == AOM_CODEC_OK; + break; + case AVIF_AOM_OPTION_UINT: + success = aomOptionParseUInt(entry->value, &valUInt) && + aom_codec_control(&codec->internal->encoder, aomOptionDefs[j].controlId, valUInt) == AOM_CODEC_OK; + break; + case AVIF_AOM_OPTION_ENUM: + success = aomOptionParseEnum(entry->value, aomOptionDefs[j].enums, &valInt) && + aom_codec_control(&codec->internal->encoder, aomOptionDefs[j].controlId, valInt) == AOM_CODEC_OK; + break; + } + if (!success) { + return AVIF_FALSE; + } + break; + } + } + if (!match) { + return AVIF_FALSE; + } +#endif // defined(HAVE_AOM_CODEC_SET_OPTION) + } + return AVIF_TRUE; +} + +struct aomScalingModeMapList +{ + avifFraction avifMode; + AOM_SCALING_MODE aomMode; +}; + +static const struct aomScalingModeMapList scalingModeMap[] = { + { { 1, 1 }, AOME_NORMAL }, { { 1, 2 }, AOME_ONETWO }, { { 1, 4 }, AOME_ONEFOUR }, { { 1, 8 }, AOME_ONEEIGHT }, + { { 3, 4 }, AOME_THREEFOUR }, { { 3, 5 }, AOME_THREEFIVE }, { { 4, 5 }, AOME_FOURFIVE }, +}; + +static const int scalingModeMapSize = sizeof(scalingModeMap) / sizeof(scalingModeMap[0]); + +static avifBool avifFindAOMScalingMode(const avifFraction * avifMode, AOM_SCALING_MODE * aomMode) +{ + avifFraction simplifiedFraction = *avifMode; + avifFractionSimplify(&simplifiedFraction); + for (int i = 0; i < scalingModeMapSize; ++i) { + if (scalingModeMap[i].avifMode.n == simplifiedFraction.n && scalingModeMap[i].avifMode.d == simplifiedFraction.d) { + *aomMode = scalingModeMap[i].aomMode; + return AVIF_TRUE; + } + } + + return AVIF_FALSE; +} + +static avifBool doesLevelMatch(int width, int height, int levelWidth, int levelHeight, int levelDimMult) +{ + const int64_t levelLumaPels = (int64_t)levelWidth * levelHeight; + const int64_t lumaPels = (int64_t)width * height; + return lumaPels <= levelLumaPels && width <= levelWidth * levelDimMult && height <= levelHeight * levelDimMult; +} + +// Quality (q) to quantizer (qp) formula for tune=iq (Image Quality), expressed as a look-up table for more clarity. +// The formula below is a piecewise linear function. Each segment was empirically selected to correct for the +// non-linear bitrate increase from encoding content with tune=iq relative to tune=ssim with the same qp. +// +// | Quality | Quantizer | Step size | +// |---------|------------------------------------|-----------| +// | 0 - 6 | 63 - floor(quality / 3) | 3 | +// | 7 - 28 | 61 - round((quality - 7) / 2) | 2 | +// | 29 - 53 | 50 - round((quality - 29) * 3 / 5) | 1.66 | +// | 54 - 99 | 35 - round((quality - 54) * 3 / 4) | 1.33 | +// | 100 | 0 (lossless) | 1 | +// +// The formula has these properties, in addition to the general conversion formula properties described in avif.h: +// - Encoding and decoding time with tune=iq are closer to tune=ssim's at a given quality level, with an +// overall smaller (but still predictable) file size and a similar to better quality +// - The qp of tune=ssim <= qp of tune=iq for all quality values +// - Quality 60 (the default in avifenc) = qp 30 +// - The step size of the quantizers monotonically decreases as quality increases (from 3 to 1) +// +// The x axis of the table represents the ones digit, while the y axis represents the tens digit +// of the q value [0-100], which is then mapped to a qp value [0-63]. +// clang-format off +static const int tuneIqQualityToQuantizer[101] = { +// 1s digit: *0 *1 *2 *3 *4 *5 *6 *7 *8 *9 10s digit: + 63, 63, 63, 62, 62, 62, 61, 61, 60, 60, // 0* + 59, 59, 58, 58, 57, 57, 56, 56, 55, 55, // 1* + 54, 54, 53, 53, 52, 52, 51, 51, 50, 50, // 2* + 49, 49, 48, 48, 47, 46, 46, 45, 45, 44, // 3* + 43, 43, 42, 42, 41, 40, 40, 39, 39, 38, // 4* + 37, 37, 36, 36, 35, 34, 33, 33, 32, 31, // 5* + 30, 30, 29, 28, 27, 27, 26, 25, 24, 24, // 6* + 23, 22, 21, 21, 20, 19, 18, 18, 17, 16, // 7* + 15, 15, 14, 13, 12, 12, 11, 10, 9, 9, // 8* + 8, 7, 6, 6, 5, 4, 3, 3, 2, 1, // 9* + 0 // quality 100 +}; +// clang-format on + +static avifBool aomCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output); + +static int aomQualityToQuantizer(int quality, avifBool isTuneIq) +{ + int quantizer; + + if (isTuneIq) { + quantizer = tuneIqQualityToQuantizer[quality]; + } else { + quantizer = ((100 - quality) * 63 + 50) / 100; + } + + return quantizer; +} + +static avifResult aomCodecEncodeImage(avifCodec * codec, + avifEncoder * encoder, + const avifImage * image, + avifBool alpha, + int tileRowsLog2, + int tileColsLog2, + int quality, + avifEncoderChanges encoderChanges, + avifBool disableLaggedOutput, + avifAddImageFlags addImageFlags, + avifCodecEncodeOutput * output) +{ + // Save the quality of the first layer because it's useful for further decision making + if (encoder->extraLayerCount > 0 && codec->internal->currentLayer == 0) { + codec->internal->qualityFirstLayer = quality; + } + + // Determine whether the encoder should be configured to use intra frames only, either by setting aomUsage to + // AOM_USAGE_ALL_INTRA, or by manually configuring the encoder so all frames will be key frames (if AOM_USAGE_ALL_INTRA isn't + // available). + + // For libaom versions older than 3.14.0, all-intra encoding is beneficial when encoding a two-layer image item and the + // quality of the first layer is very low. Switching to all-intra encoding comes with the following benefits: + // - The first layer will be smaller than the second layer (which is often not the case with inter encoding) + // - Outputs have predictable file sizes: the sum of the first layer (quality <= 10) plus the second layer (quality set by + // the caller) + // - Because the first layer is very small, layered encoding overhead is also smaller and more stable (about 5-8% for quality + // 40 and 2-4% for quality 60) + // Note: libaom 3.14.0 introduces a mechanism to completely control each layer's QP, and extends tune IQ to inter-frame + // encoding modes (AOM_USAGE_GOOD_QUALITY and AOM_USAGE_REALTIME), so there's no need to use all-intra encoding for layered. + + // aom_codec.h says: aom_codec_version() == (major<<16 | minor<<8 | patch) + static const int aomVersion_3_14_0 = (3 << 16) | (14 << 8); + const int aomVersion = aom_codec_version(); + avifBool useAllIntraForLayered = aomVersion < aomVersion_3_14_0 && encoder->extraLayerCount == 1 && + codec->internal->qualityFirstLayer <= TWO_LAYER_ALL_INTRA_QUALITY_THRESHOLD; + // Also use all-intra encoding when encoding still images. + avifBool useAllIntra = (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) || useAllIntraForLayered; + + // Map encoder speed to AOM usage + CpuUsed: + // Speed 0: GoodQuality CpuUsed 0 + // Speed 1: GoodQuality CpuUsed 1 + // Speed 2: GoodQuality CpuUsed 2 + // Speed 3: GoodQuality CpuUsed 3 + // Speed 4: GoodQuality CpuUsed 4 + // Speed 5: GoodQuality CpuUsed 5 + // Speed 6: GoodQuality CpuUsed 6 + // Speed 7: RealTime CpuUsed 7 + // Speed 8: RealTime CpuUsed 8 + // Speed 9: RealTime CpuUsed 9 + // Speed 10: RealTime CpuUsed 9 + unsigned int aomUsage = AOM_USAGE_GOOD_QUALITY; + // Use AOM_USAGE_ALL_INTRA (added in https://crbug.com/aomedia/2959) if available. +#if defined(AOM_USAGE_ALL_INTRA) + if (useAllIntra) { + aomUsage = AOM_USAGE_ALL_INTRA; + } +#endif + int aomCpuUsed = -1; + if (encoder->speed != AVIF_SPEED_DEFAULT) { + aomCpuUsed = AVIF_CLAMP(encoder->speed, 0, 9); + if (aomCpuUsed >= 7) { +#if defined(AOM_USAGE_ALL_INTRA) && defined(ALL_INTRA_HAS_SPEEDS_7_TO_9) + if (!useAllIntra) { + aomUsage = AOM_USAGE_REALTIME; + } +#else + aomUsage = AOM_USAGE_REALTIME; +#endif + } + } + + static const int aomVersion_2_0_0 = (2 << 16); + if (aomVersion <= aomVersion_2_0_0) { + // Issue with v1.0.0-errata1-avif: https://github.com/AOMediaCodec/libavif/issues/56 + // Issue with v2.0.0: https://aomedia-review.googlesource.com/q/I26a39791f820b4d4e1d63ff7141f594c3c7181f5 + // v2.0.1 was released on 2020-11-25. + avifDiagnosticsPrintf(codec->diag, "a libaom version strictly greater than 2.0.0 is required"); + return AVIF_RESULT_UNKNOWN_ERROR; + } + + avifBool useLibavifDefaultTuneMetric = AVIF_FALSE; // If true, override libaom's default tune option. + aom_tune_metric libavifDefaultTuneMetric = AOM_TUNE_PSNR; // Meaningless unless useLibavifDefaultTuneMetric. + + // True if libavif knows that tune=iq is used, either by default by libavif, or explicitly set by the user. + // False otherwise (including if libaom uses tune=iq by default, which is not the case as of v3.13.1 and earlier versions). + avifBool useTuneIq; + + if (avifAOMOptionsContainExplicitTuning(codec, alpha, &useTuneIq)) { + // avifAOMOptionsContainExplicitTuning() has set useTuneIq. + } else if (!codec->internal->encoderInitialized) { + // libavif only needs to set the default tune metric for the first frame, + // because libaom will persist that setting until explicitly changed. + + if (quality == AVIF_QUALITY_LOSSLESS) { + // AOM_TUNE_IQ is not libaom's default tune option as of v3.13.1. + // Even if it was, it does not matter for lossless. + useTuneIq = AVIF_FALSE; + } else { + useLibavifDefaultTuneMetric = AVIF_TRUE; + if (alpha) { + // Minimize ringing for alpha. + libavifDefaultTuneMetric = AOM_TUNE_PSNR; + } else { + libavifDefaultTuneMetric = AOM_TUNE_SSIM; +#if defined(AOM_HAVE_TUNE_IQ) + // AOM_TUNE_IQ has been tuned for the YCbCr family of color spaces, and is favored for + // its low perceptual distortion. AOM_TUNE_IQ partially generalizes to, and benefits + // from other "YUV-like" spaces (e.g. YCgCo and ICtCp) including monochrome (luma only). + // + // AOM_TUNE_IQ was introduced in libaom v3.12.0 but it has significantly different bit + // allocation characteristics compared to v3.13.0. AOM_TUNE_IQ is used by default + // starting with v3.13.0 for fewer behavior changes in libavif. + // + // Starting with libaom v3.14.0, AOM_TUNE_IQ supports all-intra, good-quality and + // realtime modes (for single and layered images). Prior to v3.14.0, AOM_TUNE_IQ is only + // supported in all-intra mode. + static const int aomVersion_3_13_0 = (3 << 16) | (13 << 8); + if (image->matrixCoefficients != AVIF_MATRIX_COEFFICIENTS_IDENTITY && + ((aomUsage == AOM_USAGE_ALL_INTRA && aomVersion >= aomVersion_3_13_0) || + (encoder->extraLayerCount > 0 && aomVersion >= aomVersion_3_14_0))) { + libavifDefaultTuneMetric = AOM_TUNE_IQ; + } +#endif + } + useTuneIq = (libavifDefaultTuneMetric == AOM_TUNE_IQ); + } + } else { + // The tune option persists across frames in libaom until explicitly set to another value. + useTuneIq = codec->internal->previousFrameUsedTuneIq; + } + // Remember the current tune option for the next frame. + codec->internal->previousFrameUsedTuneIq = useTuneIq; + + struct aom_codec_enc_cfg * cfg = &codec->internal->cfg; + avifBool quantizerUpdated = AVIF_FALSE; + const int quantizer = aomQualityToQuantizer(quality, useTuneIq); + + // For encoder->scalingMode.horizontal and encoder->scalingMode.vertical to take effect in AOM + // encoder, config should be applied for each frame, so we don't care about changes on these + // two fields. + encoderChanges &= ~AVIF_ENCODER_CHANGE_SCALING_MODE; + + if (!codec->internal->encoderInitialized) { + codec->internal->aomFormat = avifImageCalcAOMFmt(image, alpha); + if (codec->internal->aomFormat == AOM_IMG_FMT_NONE) { + return AVIF_RESULT_UNKNOWN_ERROR; + } + + avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo); + + aom_codec_iface_t * encoderInterface = aom_codec_av1_cx(); + aom_codec_err_t err = aom_codec_enc_config_default(encoderInterface, cfg, aomUsage); + if (err != AOM_CODEC_OK) { + avifDiagnosticsPrintf(codec->diag, "aom_codec_enc_config_default() failed: %s", aom_codec_err_to_string(err)); + return AVIF_RESULT_UNKNOWN_ERROR; + } + + // Set our own default cfg->rc_end_usage value, which may differ from libaom's default. + switch (aomUsage) { + case AOM_USAGE_GOOD_QUALITY: + // libaom's default is AOM_VBR. Change the default to AOM_Q since we don't need to + // hit a certain target bit rate. It's easier to control the worst quality in Q + // mode. + cfg->rc_end_usage = AOM_Q; + break; + case AOM_USAGE_REALTIME: + // For real-time mode we need to use CBR rate control mode. AOM_Q doesn't fit the + // rate control requirements for real-time mode. CBR does. + cfg->rc_end_usage = AOM_CBR; + break; +#if defined(AOM_USAGE_ALL_INTRA) + case AOM_USAGE_ALL_INTRA: + cfg->rc_end_usage = AOM_Q; + break; +#endif + } + + // Profile 0. 8-bit and 10-bit 4:2:0 and 4:0:0 only. + // Profile 1. 8-bit and 10-bit 4:4:4 + // Profile 2. 8-bit and 10-bit 4:2:2 + // 12-bit 4:0:0, 4:2:0, 4:2:2 and 4:4:4 + uint8_t seqProfile = 0; + if (image->depth == 12) { + // Only seqProfile 2 can handle 12 bit + seqProfile = 2; + } else { + // 8-bit or 10-bit + + if (alpha) { + seqProfile = 0; + } else { + switch (image->yuvFormat) { + case AVIF_PIXEL_FORMAT_YUV444: + seqProfile = 1; + break; + case AVIF_PIXEL_FORMAT_YUV422: + seqProfile = 2; + break; + case AVIF_PIXEL_FORMAT_YUV420: + seqProfile = 0; + break; + case AVIF_PIXEL_FORMAT_YUV400: + seqProfile = 0; + break; + case AVIF_PIXEL_FORMAT_NONE: + case AVIF_PIXEL_FORMAT_COUNT: + default: + break; + } + } + } + + cfg->g_profile = seqProfile; + cfg->g_bit_depth = image->depth; + cfg->g_input_bit_depth = image->depth; + cfg->g_w = image->width; + cfg->g_h = image->height; + + // Detect the libaom v3.6.0 bug described in + // https://crbug.com/aomedia/2871#c12. See the changes to + // av1/encoder/encoder.c in + // https://aomedia-review.googlesource.com/c/aom/+/174421. + static const int aomVersion_3_6_0 = (3 << 16) | (6 << 8); + if (aomVersion == aomVersion_3_6_0) { + // Detect the use of levels 7.x and 8.x, which use a larger max + // tile area (4096 * 4608) than MAX_TILE_AREA (4096 * 2304). The + // larger max tile area may not result in a different bitstream + // (see the tile_info() function in the AV1 spec, Section 5.9.15), + // so this is just a necessary condition for the bug. + if (!doesLevelMatch(image->width, image->height, 8192, 4352, 2) && + (doesLevelMatch(image->width, image->height, 16384, 8704, 2) || + doesLevelMatch(image->width, image->height, 32768, 17408, 2))) { + avifDiagnosticsPrintf(codec->diag, "Detected libaom v3.6.0 bug with large images. Upgrade to libaom v3.6.1 or later."); + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + + if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { + // Set the maximum number of frames to encode to 1. This instructs + // libaom to set still_picture and reduced_still_picture_header to + // 1 in AV1 sequence headers. + cfg->g_limit = 1; + } + if (useAllIntra) { +#if !defined(AOM_USAGE_ALL_INTRA) + // Use the default settings of the new AOM_USAGE_ALL_INTRA (added in + // https://crbug.com/aomedia/2959). + // + // Set g_lag_in_frames to 0 to reduce the number of frame buffers + // (from 20 to 2) in libaom's lookahead structure. This reduces + // memory consumption when encoding a single image. + cfg->g_lag_in_frames = 0; + // Disable automatic placement of key frames by the encoder. + cfg->kf_mode = AOM_KF_DISABLED; + // Tell libaom that all frames will be key frames. + cfg->kf_max_dist = 0; +#endif + } else { + if (encoder->keyframeInterval > 0) { + cfg->kf_max_dist = encoder->keyframeInterval; + } + } + if (encoder->extraLayerCount > 0) { + cfg->g_limit = encoder->extraLayerCount + 1; + // For layered image, disable lagged encoding to always get output + // frame for each input frame. + cfg->g_lag_in_frames = 0; + + if (aomVersion >= aomVersion_3_14_0) { + // Disable QP offsets, so CQ level = frame QP for every frame. + // This feature requires libaom 3.14.0 or later. + if (cfg->rc_end_usage == AOM_Q) { + cfg->use_fixed_qp_offsets = 2; + } + } + } + if (disableLaggedOutput) { + cfg->g_lag_in_frames = 0; + } + if (encoder->maxThreads > 1) { + // libaom fails if cfg->g_threads is greater than 64 threads. See MAX_NUM_THREADS in + // aom/aom_util/aom_thread.h. + cfg->g_threads = AVIF_MIN(encoder->maxThreads, 64); + } + + if (alpha || (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) { + cfg->monochrome = 1; + } + + if (!avifProcessAOMOptionsPreInit(codec, alpha, cfg)) { + return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + } + + int minQuantizer; + int maxQuantizer; + if (alpha) { + minQuantizer = encoder->minQuantizerAlpha; + maxQuantizer = encoder->maxQuantizerAlpha; + } else { + minQuantizer = encoder->minQuantizer; + maxQuantizer = encoder->maxQuantizer; + } + minQuantizer = AVIF_CLAMP(minQuantizer, 0, 63); + maxQuantizer = AVIF_CLAMP(maxQuantizer, 0, 63); + if ((cfg->rc_end_usage == AOM_VBR) || (cfg->rc_end_usage == AOM_CBR)) { + // cq-level is ignored in these two end-usage modes, so adjust minQuantizer and + // maxQuantizer to the target quantizer. + if (quantizer == AVIF_QUANTIZER_LOSSLESS) { + minQuantizer = AVIF_QUANTIZER_LOSSLESS; + maxQuantizer = AVIF_QUANTIZER_LOSSLESS; + } else { + minQuantizer = AVIF_MAX(quantizer - 4, minQuantizer); + maxQuantizer = AVIF_MIN(quantizer + 4, maxQuantizer); + } + } + cfg->rc_min_quantizer = minQuantizer; + cfg->rc_max_quantizer = maxQuantizer; + quantizerUpdated = AVIF_TRUE; + + aom_codec_flags_t encoderFlags = 0; + if (image->depth > 8) { + encoderFlags |= AOM_CODEC_USE_HIGHBITDEPTH; + } + if (aom_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, encoderFlags) != AOM_CODEC_OK) { + aomDiagPrintf(codec->diag, "aom_codec_enc_init()", &codec->internal->encoder); + return AVIF_RESULT_UNKNOWN_ERROR; + } + codec->internal->encoderInitialized = AVIF_TRUE; + + if ((cfg->rc_end_usage == AOM_CQ) || (cfg->rc_end_usage == AOM_Q)) { + aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, quantizer); + } + avifBool lossless = (quantizer == AVIF_QUANTIZER_LOSSLESS); + if (lossless) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, 1); + } + if (tileRowsLog2 != 0) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2); + } + if (tileColsLog2 != 0) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2); + } + if (encoder->extraLayerCount > 0) { + int layerCount = encoder->extraLayerCount + 1; + if (aom_codec_control(&codec->internal->encoder, AOME_SET_NUMBER_SPATIAL_LAYERS, layerCount) != AOM_CODEC_OK) { + aomDiagPrintf(codec->diag, "aom_codec_control(AOME_SET_NUMBER_SPATIAL_LAYERS)", &codec->internal->encoder); + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + if (aomCpuUsed != -1) { + if (aom_codec_control(&codec->internal->encoder, AOME_SET_CPUUSED, aomCpuUsed) != AOM_CODEC_OK) { + aomDiagPrintf(codec->diag, "aom_codec_control(AOME_SET_CPUUSED)", &codec->internal->encoder); + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + + // Set color_config() in the sequence header OBU. + if (alpha) { + // AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. + aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, AOM_CR_FULL_RANGE); + + // Keep the default AOM_CSP_UNKNOWN value. + + // CICP (CP/TC/MC) does not apply to the alpha auxiliary image. + // Keep default Unspecified (2) colour primaries, transfer characteristics, + // and matrix coefficients. + } else { + // AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. + // CICP values could be set to 2/2/2 (Unspecified) in the Sequence Header OBU for + // simplicity and to save 3 bytes, but some decoders ignore the colr box and rely + // on the OBU contents instead. See #2850. + // libaom's defaults are AOM_CICP_CP_UNSPECIFIED, AOM_CICP_TC_UNSPECIFIED, + // AOM_CICP_MC_UNSPECIFIED, AOM_CSP_UNKNOWN, and 0 (studio/limited range). Call + // aom_codec_control() only if the values are not the defaults. + if (image->colorPrimaries != AVIF_COLOR_PRIMARIES_UNSPECIFIED) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_PRIMARIES, (int)image->colorPrimaries); + } + if (image->transferCharacteristics != AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_TRANSFER_CHARACTERISTICS, (int)image->transferCharacteristics); + } + if (image->matrixCoefficients != AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_MATRIX_COEFFICIENTS, (int)image->matrixCoefficients); + } + if (image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_CHROMA_SAMPLE_POSITION, (int)image->yuvChromaSamplePosition); + } + + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. + if (image->yuvRange != AVIF_RANGE_LIMITED) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_COLOR_RANGE, (int)image->yuvRange); + } + + // Section 2.3.4 of AV1-ISOBMFF says 'colr' with 'nclx' should be present and shall match CICP + // values in the Sequence Header OBU, unless the latter has 2/2/2 (Unspecified). + // So set CICP values to 2/2/2 (Unspecified) in the Sequence Header OBU for simplicity. + // It may also save 3 bytes since the AV1 encoder can set color_description_present_flag to 0 + // (see Section 5.5.2 "Color config syntax" of the AV1 specification). + // libaom's defaults are AOM_CICP_CP_UNSPECIFIED, AOM_CICP_TC_UNSPECIFIED, and + // AOM_CICP_MC_UNSPECIFIED. No need to call aom_codec_control(). + // aom_image_t::cp, aom_image_t::tc and aom_image_t::mc are ignored by aom_codec_encode(). + } + +#if defined(AOM_CTRL_AV1E_SET_SKIP_POSTPROC_FILTERING) + if (cfg->g_usage == AOM_USAGE_ALL_INTRA) { + // Enable AV1E_SET_SKIP_POSTPROC_FILTERING for still-picture encoding, which is + // disabled by default. + aom_codec_control(&codec->internal->encoder, AV1E_SET_SKIP_POSTPROC_FILTERING, 1); + } +#endif + + if (useLibavifDefaultTuneMetric && + aom_codec_control(&codec->internal->encoder, AOME_SET_TUNING, libavifDefaultTuneMetric) != AOM_CODEC_OK) { + aomDiagPrintf(codec->diag, "aom_codec_control(AOME_SET_TUNING)", &codec->internal->encoder); + return AVIF_RESULT_UNKNOWN_ERROR; + } + if (!avifProcessAOMOptionsPostInit(codec, alpha)) { + return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + } + + if (image->depth == 12) { + // The encoder may produce integer overflows with 12-bit input when loop restoration is enabled. See crbug.com/aomedia/42302587. + if (aom_codec_control(&codec->internal->encoder, AV1E_SET_ENABLE_RESTORATION, 0) != AOM_CODEC_OK) { + aomDiagPrintf(codec->diag, "aom_codec_control(AV1E_SET_ENABLE_RESTORATION)", &codec->internal->encoder); + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + } else { + avifBool dimensionsChanged = AVIF_FALSE; + if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) { + // We are not ready for dimension change for now. + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (alpha) { + if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA | AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA)) { + cfg->rc_min_quantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63); + cfg->rc_max_quantizer = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63); + quantizerUpdated = AVIF_TRUE; + } + } else { + if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER | AVIF_ENCODER_CHANGE_MAX_QUANTIZER)) { + cfg->rc_min_quantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63); + cfg->rc_max_quantizer = AVIF_CLAMP(encoder->maxQuantizer, 0, 63); + quantizerUpdated = AVIF_TRUE; + } + } + const int qualityChangedBit = alpha ? AVIF_ENCODER_CHANGE_QUALITY_ALPHA : AVIF_ENCODER_CHANGE_QUALITY; + if (encoderChanges & qualityChangedBit) { + if ((cfg->rc_end_usage == AOM_VBR) || (cfg->rc_end_usage == AOM_CBR)) { + // cq-level is ignored in these two end-usage modes, so adjust minQuantizer and + // maxQuantizer to the target quantizer. + if (quantizer == AVIF_QUANTIZER_LOSSLESS) { + cfg->rc_min_quantizer = AVIF_QUANTIZER_LOSSLESS; + cfg->rc_max_quantizer = AVIF_QUANTIZER_LOSSLESS; + } else { + int minQuantizer; + int maxQuantizer; + if (alpha) { + minQuantizer = encoder->minQuantizerAlpha; + maxQuantizer = encoder->maxQuantizerAlpha; + } else { + minQuantizer = encoder->minQuantizer; + maxQuantizer = encoder->maxQuantizer; + } + minQuantizer = AVIF_CLAMP(minQuantizer, 0, 63); + maxQuantizer = AVIF_CLAMP(maxQuantizer, 0, 63); + cfg->rc_min_quantizer = AVIF_MAX(quantizer - 4, minQuantizer); + cfg->rc_max_quantizer = AVIF_MIN(quantizer + 4, maxQuantizer); + } + quantizerUpdated = AVIF_TRUE; + } + } + if (quantizerUpdated || dimensionsChanged) { + aom_codec_err_t err = aom_codec_enc_config_set(&codec->internal->encoder, cfg); + if (err != AOM_CODEC_OK) { + aomDiagPrintf(codec->diag, "aom_codec_enc_config_set()", &codec->internal->encoder); + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_ROWS, tileRowsLog2); + } + if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_COLS_LOG2) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_TILE_COLUMNS, tileColsLog2); + } + if (encoderChanges & qualityChangedBit) { + if ((cfg->rc_end_usage == AOM_CQ) || (cfg->rc_end_usage == AOM_Q)) { + aom_codec_control(&codec->internal->encoder, AOME_SET_CQ_LEVEL, quantizer); + } + avifBool lossless = (quantizer == AVIF_QUANTIZER_LOSSLESS); + aom_codec_control(&codec->internal->encoder, AV1E_SET_LOSSLESS, lossless); + } + if (encoderChanges & AVIF_ENCODER_CHANGE_CODEC_SPECIFIC) { + // Do not apply libavifDefaultTuneMetric even if useLibavifDefaultTuneMetric is true: + // codec-specific settings persist on the libaom side, so keep the same behavior for tune. + if (!avifProcessAOMOptionsPostInit(codec, alpha)) { + return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + } + } + } + + if (codec->internal->currentLayer > encoder->extraLayerCount) { + avifDiagnosticsPrintf(codec->diag, + "Too many layers sent. Expected %u layers, but got %u layers.", + encoder->extraLayerCount + 1, + codec->internal->currentLayer + 1); + return AVIF_RESULT_INVALID_ARGUMENT; + } + if (encoder->extraLayerCount > 0) { + aom_codec_control(&codec->internal->encoder, AOME_SET_SPATIAL_LAYER_ID, codec->internal->currentLayer); + } + + aom_scaling_mode_t aomScalingMode; + if (!avifFindAOMScalingMode(&encoder->scalingMode.horizontal, &aomScalingMode.h_scaling_mode)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (!avifFindAOMScalingMode(&encoder->scalingMode.vertical, &aomScalingMode.v_scaling_mode)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if ((aomScalingMode.h_scaling_mode != AOME_NORMAL) || (aomScalingMode.v_scaling_mode != AOME_NORMAL)) { + // AOME_SET_SCALEMODE only applies to next frame (layer), so we have to set it every time. + aom_codec_control(&codec->internal->encoder, AOME_SET_SCALEMODE, &aomScalingMode); + + // Check if we need to avoid a multithreading crash in loop restoration + // code in libaom < 3.13.3 when the first layer of a layered image is + // scaled. See https://aomedia-review.googlesource.com/208901. + static const int aomVersion_3_13_3 = (3 << 16) | (13 << 8) | 3; + if (aomVersion < aomVersion_3_13_3 && encoder->maxThreads > 1 && encoder->extraLayerCount > 0 && + codec->internal->currentLayer == 0) { + aom_codec_control(&codec->internal->encoder, AV1E_SET_ENABLE_RESTORATION, 0); + } + } + + aom_image_t aomImage; + // We prefer to simply set the aomImage.planes[] pointers to the plane buffers in 'image'. When + // doing this, we set aomImage.w equal to aomImage.d_w and aomImage.h equal to aomImage.d_h and + // do not "align" aomImage.w and aomImage.h. Unfortunately this exposes a bug in libaom + // (https://crbug.com/aomedia/3113) if chroma is subsampled and image->width or image->height is + // equal to 1. To work around this libaom bug, we allocate the aomImage.planes[] buffers and + // copy the image YUV data if image->width or image->height is equal to 1. This bug has been + // fixed in libaom v3.1.3. + // + // Note: The exact condition for the bug is + // ((image->width == 1) && (chroma is subsampled horizontally)) || + // ((image->height == 1) && (chroma is subsampled vertically)) + // Since an image width or height of 1 is uncommon in practice, we test an inexact but simpler + // condition. + avifBool aomImageAllocated = (image->width == 1) || (image->height == 1); + if (aomImageAllocated) { + aom_img_alloc(&aomImage, codec->internal->aomFormat, image->width, image->height, 16); + } else { + memset(&aomImage, 0, sizeof(aomImage)); + aomImage.fmt = codec->internal->aomFormat; + aomImage.bit_depth = (image->depth > 8) ? 16 : 8; + aomImage.w = image->width; + aomImage.h = image->height; + aomImage.d_w = image->width; + aomImage.d_h = image->height; + // Get sample size for this format. + unsigned int bps; + if (codec->internal->aomFormat == AOM_IMG_FMT_I420) { + bps = 12; + } else if (codec->internal->aomFormat == AOM_IMG_FMT_I422) { + bps = 16; + } else if (codec->internal->aomFormat == AOM_IMG_FMT_I444) { + bps = 24; + } else if (codec->internal->aomFormat == AOM_IMG_FMT_I42016) { + bps = 24; + } else if (codec->internal->aomFormat == AOM_IMG_FMT_I42216) { + bps = 32; + } else if (codec->internal->aomFormat == AOM_IMG_FMT_I44416) { + bps = 48; + } else { + bps = 16; + } + aomImage.bps = bps; + // See avifImageCalcAOMFmt(). libaom doesn't have AOM_IMG_FMT_I400, so we use AOM_IMG_FMT_I420 as a substitute for monochrome. + aomImage.x_chroma_shift = (alpha || codec->internal->formatInfo.monochrome) ? 1 : codec->internal->formatInfo.chromaShiftX; + aomImage.y_chroma_shift = (alpha || codec->internal->formatInfo.monochrome) ? 1 : codec->internal->formatInfo.chromaShiftY; + } + + if (alpha) { + // AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. + aomImage.range = AOM_CR_FULL_RANGE; + + // AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The mono_chrome field in the Sequence Header OBU shall be set to 1. + // Some encoders do not support 4:0:0 and encode alpha as 4:2:0 so it is not always respected. + aomImage.monochrome = 1; + if (aomImageAllocated) { + const uint32_t bytesPerRow = ((image->depth > 8) ? 2 : 1) * image->width; + for (uint32_t j = 0; j < image->height; ++j) { + const uint8_t * srcAlphaRow = &image->alphaPlane[(size_t)j * image->alphaRowBytes]; + uint8_t * dstAlphaRow = &aomImage.planes[0][(size_t)j * aomImage.stride[0]]; + memcpy(dstAlphaRow, srcAlphaRow, bytesPerRow); + } + } else { + aomImage.planes[0] = image->alphaPlane; + aomImage.stride[0] = image->alphaRowBytes; + } + + // Ignore UV planes when monochrome. Keep the default AOM_CSP_UNKNOWN value. + } else { + int yuvPlaneCount = 3; + if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { + yuvPlaneCount = 1; // Ignore UV planes when monochrome + aomImage.monochrome = 1; + } + if (aomImageAllocated) { + uint32_t bytesPerPixel = (image->depth > 8) ? 2 : 1; + for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { + uint32_t planeWidth = avifImagePlaneWidth(image, yuvPlane); + uint32_t planeHeight = avifImagePlaneHeight(image, yuvPlane); + uint32_t bytesPerRow = bytesPerPixel * planeWidth; + + for (uint32_t j = 0; j < planeHeight; ++j) { + const uint8_t * srcRow = &image->yuvPlanes[yuvPlane][(size_t)j * image->yuvRowBytes[yuvPlane]]; + uint8_t * dstRow = &aomImage.planes[yuvPlane][(size_t)j * aomImage.stride[yuvPlane]]; + memcpy(dstRow, srcRow, bytesPerRow); + } + } + } else { + for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { + aomImage.planes[yuvPlane] = image->yuvPlanes[yuvPlane]; + aomImage.stride[yuvPlane] = image->yuvRowBytes[yuvPlane]; + } + } + + aomImage.cp = (aom_color_primaries_t)image->colorPrimaries; + aomImage.tc = (aom_transfer_characteristics_t)image->transferCharacteristics; + aomImage.mc = (aom_matrix_coefficients_t)image->matrixCoefficients; + // AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. + aomImage.csp = (aom_chroma_sample_position_t)image->yuvChromaSamplePosition; + + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. + aomImage.range = (aom_color_range_t)image->yuvRange; + } + + aom_enc_frame_flags_t encodeFlags = 0; + if (addImageFlags & AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME) { + encodeFlags |= AOM_EFLAG_FORCE_KF; + } + if (codec->internal->currentLayer > 0) { + encodeFlags |= AOM_EFLAG_NO_REF_GF | AOM_EFLAG_NO_REF_ARF | AOM_EFLAG_NO_REF_BWD | AOM_EFLAG_NO_REF_ARF2 | + AOM_EFLAG_NO_UPD_GF | AOM_EFLAG_NO_UPD_ARF; + } + aom_codec_err_t encodeErr = aom_codec_encode(&codec->internal->encoder, &aomImage, 0, 1, encodeFlags); + if (aomImageAllocated) { + aom_img_free(&aomImage); + } + if (encodeErr != AOM_CODEC_OK) { + aomDiagPrintf(codec->diag, "aom_codec_encode()", &codec->internal->encoder); + return AVIF_RESULT_UNKNOWN_ERROR; + } + + aom_codec_iter_t iter = NULL; + for (;;) { + const aom_codec_cx_pkt_t * pkt = aom_codec_get_cx_data(&codec->internal->encoder, &iter); + if (pkt == NULL) { + break; + } + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + AVIF_CHECKRES( + avifCodecEncodeOutputAddSample(output, pkt->data.frame.buf, pkt->data.frame.sz, (pkt->data.frame.flags & AOM_FRAME_IS_KEY))); + } + } + + if ((addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) || + ((encoder->extraLayerCount > 0) && (encoder->extraLayerCount == codec->internal->currentLayer))) { + // Flush and clean up encoder resources early to save on overhead when encoding alpha or grid images, + // as encoding is finished now. For layered image, encoding finishes when the last layer is encoded. + + if (!aomCodecEncodeFinish(codec, output)) { + return AVIF_RESULT_UNKNOWN_ERROR; + } + aom_codec_destroy(&codec->internal->encoder); + codec->internal->encoderInitialized = AVIF_FALSE; + } + if (encoder->extraLayerCount > 0) { + ++codec->internal->currentLayer; + } + return AVIF_RESULT_OK; +} + +static avifBool aomCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output) +{ + if (!codec->internal->encoderInitialized) { + return AVIF_TRUE; + } + for (;;) { + // flush encoder + if (aom_codec_encode(&codec->internal->encoder, NULL, 0, 1, 0) != AOM_CODEC_OK) { + aomDiagPrintf(codec->diag, "aom_codec_encode() with img=NULL", &codec->internal->encoder); + return AVIF_FALSE; + } + + avifBool gotPacket = AVIF_FALSE; + aom_codec_iter_t iter = NULL; + for (;;) { + const aom_codec_cx_pkt_t * pkt = aom_codec_get_cx_data(&codec->internal->encoder, &iter); + if (pkt == NULL) { + break; + } + if (pkt->kind == AOM_CODEC_CX_FRAME_PKT) { + gotPacket = AVIF_TRUE; + const avifResult result = avifCodecEncodeOutputAddSample(output, + pkt->data.frame.buf, + pkt->data.frame.sz, + (pkt->data.frame.flags & AOM_FRAME_IS_KEY)); + if (result != AVIF_RESULT_OK) { + avifDiagnosticsPrintf(codec->diag, "avifCodecEncodeOutputAddSample() failed: %s", avifResultToString(result)); + return AVIF_FALSE; + } + } + } + + if (!gotPacket) { + break; + } + } + return AVIF_TRUE; +} + +#endif // defined(AVIF_CODEC_AOM_ENCODE) + +const char * avifCodecVersionAOM(void) +{ + return aom_codec_version_str(); +} + +avifCodec * avifCodecCreateAOM(void) +{ + avifCodec * codec = (avifCodec *)avifCalloc(1, sizeof(avifCodec)); + if (codec == NULL) { + return NULL; + } + +#if defined(AVIF_CODEC_AOM_DECODE) + codec->getNextImage = aomCodecGetNextImage; +#endif + +#if defined(AVIF_CODEC_AOM_ENCODE) + codec->encodeImage = aomCodecEncodeImage; + codec->encodeFinish = aomCodecEncodeFinish; +#endif + + codec->destroyInternal = aomCodecDestroyInternal; + codec->internal = (struct avifCodecInternal *)avifCalloc(1, sizeof(struct avifCodecInternal)); + if (codec->internal == NULL) { + avifFree(codec); + return NULL; + } + return codec; +} + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif diff --git a/3rdparty/libavif/src/codec_avm.c b/3rdparty/libavif/src/codec_avm.c new file mode 100644 index 0000000..6222f7f --- /dev/null +++ b/3rdparty/libavif/src/codec_avm.c @@ -0,0 +1,1063 @@ +// Copyright 2023 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include "avm/avm_decoder.h" +#include "avm/avm_encoder.h" +#include "avm/avmcx.h" +#include "avm/avmdx.h" + +#include +#include +#include +#include +#include + +#define TWO_LAYER_ALL_INTRA_QUALITY_THRESHOLD 10 + +struct avifCodecInternal +{ + avifBool decoderInitialized; + avm_codec_ctx_t decoder; + avm_codec_iter_t iter; + avm_image_t * image; + + avifBool encoderInitialized; + avm_codec_ctx_t encoder; + struct avm_codec_enc_cfg cfg; + avifPixelFormatInfo formatInfo; + avm_img_fmt_t avmFormat; + avifBool monochromeEnabled; + // Whether 'tuning' (of the specified distortion metric) was set with an + // avifEncoderSetCodecSpecificOption(encoder, "tune", value) call. + avifBool tuningSet; + uint32_t currentLayer; +}; + +static void avmCodecDestroyInternal(avifCodec * codec) +{ + if (codec->internal->decoderInitialized) { + avm_codec_destroy(&codec->internal->decoder); + } + + if (codec->internal->encoderInitialized) { + avm_codec_destroy(&codec->internal->encoder); + } + + avifFree(codec->internal); +} + +static avifBool avmCodecGetNextImage(struct avifCodec * codec, + const avifDecodeSample * sample, + avifBool alpha, + avifBool * isLimitedRangeAlpha, + avifImage * image) +{ + assert(sample); + + if (!codec->internal->decoderInitialized) { + avm_codec_dec_cfg_t cfg; + memset(&cfg, 0, sizeof(avm_codec_dec_cfg_t)); + cfg.threads = codec->maxThreads; + + avm_codec_iface_t * decoder_interface = avm_codec_av2_dx(); + if (avm_codec_dec_init(&codec->internal->decoder, decoder_interface, &cfg, 0)) { + return AVIF_FALSE; + } + codec->internal->decoderInitialized = AVIF_TRUE; + + if (avm_codec_control(&codec->internal->decoder, AV2D_SET_OUTPUT_ALL_LAYERS, codec->allLayers)) { + return AVIF_FALSE; + } + if (codec->operatingPoint != 0) { + // Not implemented. + return AVIF_FALSE; + } + + codec->internal->iter = NULL; + } + + avm_image_t * nextFrame = NULL; + uint8_t spatialID = AVIF_SPATIAL_ID_UNSET; + for (;;) { + nextFrame = avm_codec_get_frame(&codec->internal->decoder, &codec->internal->iter); + if (nextFrame) { + if (spatialID != AVIF_SPATIAL_ID_UNSET) { + if (spatialID == nextFrame->mlayer_id) { + // Found the correct spatial_id. + break; + } + } else { + // Got an image! + break; + } + } else if (sample) { + codec->internal->iter = NULL; + if (avm_codec_decode(&codec->internal->decoder, sample->data.data, sample->data.size, NULL)) { + return AVIF_FALSE; + } + spatialID = sample->spatialID; + sample = NULL; + } else { + break; + } + } + + if (nextFrame) { + codec->internal->image = nextFrame; + } else { + if (alpha && codec->internal->image) { + // Special case: reuse last alpha frame + } else { + return AVIF_FALSE; + } + } + + avifBool isColor = !alpha; + if (isColor) { + // Color (YUV) planes - set image to correct size / format, fill color + + avifPixelFormat yuvFormat = AVIF_PIXEL_FORMAT_NONE; + switch (codec->internal->image->fmt) { + case AVM_IMG_FMT_I420: + case AVM_IMG_FMT_I42016: + yuvFormat = AVIF_PIXEL_FORMAT_YUV420; + break; + case AVM_IMG_FMT_I422: + case AVM_IMG_FMT_I42216: + yuvFormat = AVIF_PIXEL_FORMAT_YUV422; + break; + case AVM_IMG_FMT_I444: + case AVM_IMG_FMT_I44416: + yuvFormat = AVIF_PIXEL_FORMAT_YUV444; + break; + case AVM_IMG_FMT_NONE: + case AVM_IMG_FMT_YV12: + case AVM_IMG_FMT_YV1216: + default: + return AVIF_FALSE; + } + if (codec->internal->image->monochrome) { + yuvFormat = AVIF_PIXEL_FORMAT_YUV400; + } + + image->width = codec->internal->image->d_w; + image->height = codec->internal->image->d_h; + image->depth = codec->internal->image->bit_depth; + + image->yuvFormat = yuvFormat; + image->yuvRange = (codec->internal->image->range == AVM_CR_STUDIO_RANGE) ? AVIF_RANGE_LIMITED : AVIF_RANGE_FULL; + if (codec->internal->image->csp == AVM_CSP_LEFT) { + // CSP_LEFT: Horizontal offset 0, vertical offset 0.5 + image->yuvChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_VERTICAL; + } else if (codec->internal->image->csp == AVM_CSP_CENTER) { + // CSP_CENTER: Horizontal offset 0.5, vertical offset 0.5 + image->yuvChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + } else if (codec->internal->image->csp == AVM_CSP_TOPLEFT) { + // CSP_TOPLEFT: Horizontal offset 0, vertical offset 0 + image->yuvChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + } else { + image->yuvChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + } + + image->colorPrimaries = (avifColorPrimaries)codec->internal->image->cp; + image->transferCharacteristics = (avifTransferCharacteristics)codec->internal->image->tc; + image->matrixCoefficients = (avifMatrixCoefficients)codec->internal->image->mc; + + avifImageFreePlanes(image, AVIF_PLANES_YUV); + int yuvPlaneCount = (yuvFormat == AVIF_PIXEL_FORMAT_YUV400) ? 1 : 3; + + // avifImage assumes that a depth of 8 bits means an 8-bit buffer. + // avm_image does not. The buffer depth depends on fmt|AVM_IMG_FMT_HIGHBITDEPTH, even for 8-bit values. + if (!avifImageUsesU16(image) && (codec->internal->image->fmt & AVM_IMG_FMT_HIGHBITDEPTH)) { + AVIF_CHECK(avifImageAllocatePlanes(image, AVIF_PLANES_YUV) == AVIF_RESULT_OK); + for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { + const uint32_t planeWidth = avifImagePlaneWidth(image, yuvPlane); + const uint32_t planeHeight = avifImagePlaneHeight(image, yuvPlane); + const uint8_t * srcRow = codec->internal->image->planes[yuvPlane]; + uint8_t * dstRow = avifImagePlane(image, yuvPlane); + const uint32_t dstRowBytes = avifImagePlaneRowBytes(image, yuvPlane); + for (uint32_t y = 0; y < planeHeight; ++y) { + const uint16_t * srcRow16 = (const uint16_t *)srcRow; + for (uint32_t x = 0; x < planeWidth; ++x) { + dstRow[x] = (uint8_t)srcRow16[x]; + } + srcRow += codec->internal->image->stride[yuvPlane]; + dstRow += dstRowBytes; + } + } + } else { + // Steal the pointers from the decoder's image directly + for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { + image->yuvPlanes[yuvPlane] = codec->internal->image->planes[yuvPlane]; + image->yuvRowBytes[yuvPlane] = codec->internal->image->stride[yuvPlane]; + } + image->imageOwnsYUVPlanes = AVIF_FALSE; + } + } else { + // Alpha plane - set image to correct size, fill alpha + + image->width = codec->internal->image->d_w; + image->height = codec->internal->image->d_h; + image->depth = codec->internal->image->bit_depth; + + avifImageFreePlanes(image, AVIF_PLANES_A); + + if (!avifImageUsesU16(image) && (codec->internal->image->fmt & AVM_IMG_FMT_HIGHBITDEPTH)) { + AVIF_CHECK(avifImageAllocatePlanes(image, AVIF_PLANES_A) == AVIF_RESULT_OK); + const uint8_t * srcRow = codec->internal->image->planes[0]; + uint8_t * dstRow = image->alphaPlane; + for (uint32_t y = 0; y < image->height; ++y) { + const uint16_t * srcRow16 = (const uint16_t *)srcRow; + for (uint32_t x = 0; x < image->width; ++x) { + dstRow[x] = (uint8_t)srcRow16[x]; + } + srcRow += codec->internal->image->stride[0]; + dstRow += image->alphaRowBytes; + } + } else { + image->alphaPlane = codec->internal->image->planes[0]; + image->alphaRowBytes = codec->internal->image->stride[0]; + image->imageOwnsAlphaPlane = AVIF_FALSE; + } + *isLimitedRangeAlpha = (codec->internal->image->range == AVM_CR_STUDIO_RANGE); + } + + return AVIF_TRUE; +} + +static avm_img_fmt_t avifImageCalcAVMFmt(const avifImage * image, avifBool alpha) +{ + avm_img_fmt_t fmt; + if (alpha) { + // We're going monochrome, who cares about chroma quality + fmt = AVM_IMG_FMT_I420; + } else { + switch (image->yuvFormat) { + case AVIF_PIXEL_FORMAT_YUV444: + fmt = AVM_IMG_FMT_I444; + break; + case AVIF_PIXEL_FORMAT_YUV422: + fmt = AVM_IMG_FMT_I422; + break; + case AVIF_PIXEL_FORMAT_YUV420: + case AVIF_PIXEL_FORMAT_YUV400: + fmt = AVM_IMG_FMT_I420; + break; + case AVIF_PIXEL_FORMAT_NONE: + case AVIF_PIXEL_FORMAT_COUNT: + default: + return AVM_IMG_FMT_NONE; + } + } + + if (image->depth > 8) { + fmt |= AVM_IMG_FMT_HIGHBITDEPTH; + } + + return fmt; +} + +struct avmOptionEnumList +{ + const char * name; + int val; +}; + +static avifBool avmOptionParseEnum(const char * str, const struct avmOptionEnumList * enums, int * val) +{ + const struct avmOptionEnumList * listptr; + long int rawval; + char * endptr; + + // First see if the value can be parsed as a raw value. + rawval = strtol(str, &endptr, 10); + if (str[0] != '\0' && endptr[0] == '\0') { + // Got a raw value, make sure it's valid. + for (listptr = enums; listptr->name; listptr++) + if (listptr->val == rawval) { + *val = (int)rawval; + return AVIF_TRUE; + } + } + + // Next see if it can be parsed as a string. + for (listptr = enums; listptr->name; listptr++) { + if (!strcmp(str, listptr->name)) { + *val = listptr->val; + return AVIF_TRUE; + } + } + + return AVIF_FALSE; +} + +static const struct avmOptionEnumList endUsageEnum[] = { // + { "vbr", AVM_VBR }, // Variable Bit Rate (VBR) mode + { "cbr", AVM_CBR }, // Constant Bit Rate (CBR) mode + { "cq", AVM_CQ }, // Constrained Quality (CQ) mode + { "q", AVM_Q }, // Constant Quality (Q) mode + { NULL, 0 } +}; + +// Returns true if equals or , where is "color:" or "alpha:" +// or the abbreviated form "c:" or "a:". +static avifBool avifKeyEqualsName(const char * key, const char * name, avifBool alpha) +{ + const char * prefix = alpha ? "alpha:" : "color:"; + size_t prefixLen = 6; + const char * shortPrefix = alpha ? "a:" : "c:"; + size_t shortPrefixLen = 2; + return !strcmp(key, name) || (!strncmp(key, prefix, prefixLen) && !strcmp(key + prefixLen, name)) || + (!strncmp(key, shortPrefix, shortPrefixLen) && !strcmp(key + shortPrefixLen, name)); +} + +static avifBool avifProcessAVMOptionsPreInit(avifCodec * codec, avifBool alpha, struct avm_codec_enc_cfg * cfg) +{ + for (uint32_t i = 0; i < codec->csOptions->count; ++i) { + const avifCodecSpecificOption * entry = &codec->csOptions->entries[i]; + int val; + if (avifKeyEqualsName(entry->key, "end-usage", alpha)) { // Rate control mode + if (!avmOptionParseEnum(entry->value, endUsageEnum, &val)) { + avifDiagnosticsPrintf(codec->diag, "Invalid value for end-usage: %s", entry->value); + return AVIF_FALSE; + } + cfg->rc_end_usage = val; + } + } + return AVIF_TRUE; +} + +static avifBool avifProcessAVMOptionsPostInit(avifCodec * codec, avifBool alpha) +{ + for (uint32_t i = 0; i < codec->csOptions->count; ++i) { + const avifCodecSpecificOption * entry = &codec->csOptions->entries[i]; + // Skip options for the other kind of plane. + const char * otherPrefix = alpha ? "color:" : "alpha:"; + size_t otherPrefixLen = 6; + const char * otherShortPrefix = alpha ? "c:" : "a:"; + size_t otherShortPrefixLen = 2; + if (!strncmp(entry->key, otherPrefix, otherPrefixLen) || !strncmp(entry->key, otherShortPrefix, otherShortPrefixLen)) { + continue; + } + + // Skip options processed by avifProcessAVMOptionsPreInit. + if (avifKeyEqualsName(entry->key, "end-usage", alpha)) { + continue; + } + + const char * prefix = alpha ? "alpha:" : "color:"; + size_t prefixLen = 6; + const char * shortPrefix = alpha ? "a:" : "c:"; + size_t shortPrefixLen = 2; + const char * key = entry->key; + if (!strncmp(key, prefix, prefixLen)) { + key += prefixLen; + } else if (!strncmp(key, shortPrefix, shortPrefixLen)) { + key += shortPrefixLen; + } + if (avm_codec_set_option(&codec->internal->encoder, key, entry->value) != AVM_CODEC_OK) { + avifDiagnosticsPrintf(codec->diag, + "avm_codec_set_option(\"%s\", \"%s\") failed: %s: %s", + key, + entry->value, + avm_codec_error(&codec->internal->encoder), + avm_codec_error_detail(&codec->internal->encoder)); + return AVIF_FALSE; + } + if (!strcmp(key, "tune")) { + codec->internal->tuningSet = AVIF_TRUE; + } + } + return AVIF_TRUE; +} + +struct avmScalingModeMapList +{ + avifFraction avifMode; + AVM_SCALING_MODE avmMode; +}; + +static const struct avmScalingModeMapList scalingModeMap[] = { + { { 1, 1 }, AVME_NORMAL }, { { 1, 2 }, AVME_ONETWO }, { { 1, 4 }, AVME_ONEFOUR }, { { 1, 8 }, AVME_ONEEIGHT }, + { { 3, 4 }, AVME_THREEFOUR }, { { 3, 5 }, AVME_THREEFIVE }, { { 4, 5 }, AVME_FOURFIVE }, +}; + +static const int scalingModeMapSize = sizeof(scalingModeMap) / sizeof(scalingModeMap[0]); + +static avifBool avifFindAVMScalingMode(const avifFraction * avifMode, AVM_SCALING_MODE * avmMode) +{ + avifFraction simplifiedFraction = *avifMode; + avifFractionSimplify(&simplifiedFraction); + for (int i = 0; i < scalingModeMapSize; ++i) { + if (scalingModeMap[i].avifMode.n == simplifiedFraction.n && scalingModeMap[i].avifMode.d == simplifiedFraction.d) { + *avmMode = scalingModeMap[i].avmMode; + return AVIF_TRUE; + } + } + + return AVIF_FALSE; +} + +// Scales from aom's [0:63] to avm's [M:255], where M=0/-48/-96 for 8/10/12 bit. +// See --min-qp help in +// https://gitlab.com/AOMediaCodec/avm/-/blob/main/apps/avmenc.c +static int avmScaleQuantizer(int quantizer, uint32_t depth) +{ + if (depth == 10) { + return AVIF_CLAMP((quantizer * (255 + 48) + 31) / 63 - 48, -48, 255); + } + if (depth == 12) { + return AVIF_CLAMP((quantizer * (255 + 96) + 31) / 63 - 96, -96, 255); + } + assert(depth == 8); + return AVIF_CLAMP((quantizer * 255 + 31) / 63, 0, 255); +} + +// Converts quality to avm's quantizer in the range of [M:255], where M=0/-48/-96 for 8/10/12 bit. +// See --min-qp help in +// https://gitlab.com/AOMediaCodec/avm/-/blob/main/apps/avmenc.c +static int avmQualityToQuantizer(int quality, uint32_t depth) +{ + if (depth == 10) { + return 255 - (quality * (255 + 48) + 50) / 100; + } + if (depth == 12) { + return 255 - (quality * (255 + 96) + 50) / 100; + } + assert(depth == 8); + return 255 - (quality * 255 + 50) / 100; +} + +static avifBool avmCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output); + +static avifResult avmCodecEncodeImage(avifCodec * codec, + avifEncoder * encoder, + const avifImage * image, + avifBool alpha, + int tileRowsLog2, + int tileColsLog2, + int quality, + avifEncoderChanges encoderChanges, + avifBool disableLaggedOutput, + avifAddImageFlags addImageFlags, + avifCodecEncodeOutput * output) +{ + struct avm_codec_enc_cfg * cfg = &codec->internal->cfg; + avifBool quantizerUpdated = AVIF_FALSE; + const int quantizer = avmQualityToQuantizer(quality, image->depth); + + // For encoder->scalingMode.horizontal and encoder->scalingMode.vertical to take effect in AV2 + // encoder, config should be applied for each frame, so we don't care about changes on these + // two fields. + encoderChanges &= ~AVIF_ENCODER_CHANGE_SCALING_MODE; + + if (!codec->internal->encoderInitialized) { + int avmCpuUsed = -1; + if (encoder->speed != AVIF_SPEED_DEFAULT) { + avmCpuUsed = AVIF_CLAMP(encoder->speed, 0, 9); + } + + codec->internal->avmFormat = avifImageCalcAVMFmt(image, alpha); + if (codec->internal->avmFormat == AVM_IMG_FMT_NONE) { + return AVIF_RESULT_UNKNOWN_ERROR; + } + + avifGetPixelFormatInfo(image->yuvFormat, &codec->internal->formatInfo); + + avm_codec_iface_t * encoderInterface = avm_codec_av2_cx(); + avm_codec_err_t err = avm_codec_enc_config_default(encoderInterface, cfg, AVM_USAGE_GOOD_QUALITY); + if (err != AVM_CODEC_OK) { + avifDiagnosticsPrintf(codec->diag, "avm_codec_enc_config_default() failed: %s", avm_codec_err_to_string(err)); + return AVIF_RESULT_UNKNOWN_ERROR; + } + + // avm's default is AVM_VBR. Change the default to AVM_Q since we don't need to hit a certain target bit rate. + // It's easier to control the worst quality in Q mode. + cfg->rc_end_usage = AVM_Q; + + // Profile 0. 8-bit and 10-bit 4:2:0 and 4:0:0 only. + // Profile 1. 8-bit and 10-bit 4:4:4 + // Profile 2. 8-bit and 10-bit 4:2:2 + // 12-bit 4:0:0, 4:2:0, 4:2:2 and 4:4:4 + uint8_t seqProfile = 0; + if (image->depth != 8 && image->depth != 10) { + avifDiagnosticsPrintf(codec->diag, "%d-bit is not supported in AV2 encoder.", image->depth); + return AVIF_RESULT_INVALID_ARGUMENT; + } + // Based on AV2 spec Section A.3 Profiles. + if (alpha) { + seqProfile = 1; // Main_420_10_IP1 + } else { + switch (image->yuvFormat) { + case AVIF_PIXEL_FORMAT_YUV444: + seqProfile = 4; // Main_444_10_IP1 + break; + case AVIF_PIXEL_FORMAT_YUV422: + seqProfile = 3; // Main_422_10_IP1 + break; + case AVIF_PIXEL_FORMAT_YUV420: + seqProfile = 1; // Main_420_10_IP1 + break; + case AVIF_PIXEL_FORMAT_YUV400: + seqProfile = 1; // Main_420_10_IP1 + break; + case AVIF_PIXEL_FORMAT_NONE: + case AVIF_PIXEL_FORMAT_COUNT: + default: + break; + } + } + + cfg->g_profile = seqProfile; + cfg->g_bit_depth = image->depth; + cfg->g_input_bit_depth = image->depth; + cfg->g_w = image->width; + cfg->g_h = image->height; + if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { + // Set the maximum number of frames to encode to 1. This instructs + // libavm to set still_picture and reduced_still_picture_header to + // 1 in AV2 sequence headers. + cfg->g_limit = 1; + } + + // Determine whether the encoder should be configured to use intra frames only, by manually configuring the encoder so all + // frames will be key frames. + + // All-intra encoding is beneficial when encoding a two-layer image item and the quality of the first layer is very low. + // Switching to all-intra encoding comes with the following benefits: + // - The first layer will be smaller than the second layer (which is often not the case with inter encoding) + // - Outputs have predictable file sizes: the sum of the first layer (quality <= 10) plus the second layer (quality set by the caller) + // - Because the first layer is very small, layered encoding overhead is also smaller and more stable (about 5-8% for quality 40 and 2-4% for quality 60) + avifBool useAllIntraForLayered = encoder->extraLayerCount == 1 && quality <= TWO_LAYER_ALL_INTRA_QUALITY_THRESHOLD; + avifBool useAllIntra = (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) || useAllIntraForLayered; + + if (useAllIntra) { + // Use the default settings of libaom's AOM_USAGE_ALL_INTRA (added + // in https://crbug.com/aomedia/2959). + // + // Set g_lag_in_frames to 0 to reduce the number of frame buffers + // (from 20 to 2) in libavm's lookahead structure. This reduces + // memory consumption when encoding a single image. + cfg->g_lag_in_frames = 0; + // Disable automatic placement of key frames by the encoder. + cfg->kf_mode = AVM_KF_DISABLED; + // Tell libavm that all frames will be key frames. + cfg->kf_max_dist = 0; + } + if (encoder->extraLayerCount > 0) { + cfg->g_limit = encoder->extraLayerCount + 1; + // For layered image, disable lagged encoding to always get output + // frame for each input frame. + cfg->g_lag_in_frames = 0; + } + if (disableLaggedOutput) { + cfg->g_lag_in_frames = 0; + } + if (encoder->maxThreads > 1) { + // libavm fails if cfg->g_threads is greater than 64 threads. See MAX_NUM_THREADS in + // avm/avm_util/avm_thread.h. + cfg->g_threads = AVIF_MIN(encoder->maxThreads, 64); + } + + codec->internal->monochromeEnabled = AVIF_FALSE; + if (alpha || (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) { + codec->internal->monochromeEnabled = AVIF_TRUE; + cfg->monochrome = 1; + } + + if (!avifProcessAVMOptionsPreInit(codec, alpha, cfg)) { + return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + } + + int minQuantizer; + int maxQuantizer; + if (alpha) { + minQuantizer = encoder->minQuantizerAlpha; + maxQuantizer = encoder->maxQuantizerAlpha; + } else { + minQuantizer = encoder->minQuantizer; + maxQuantizer = encoder->maxQuantizer; + } + minQuantizer = avmScaleQuantizer(minQuantizer, image->depth); + maxQuantizer = avmScaleQuantizer(maxQuantizer, image->depth); + if ((cfg->rc_end_usage == AVM_VBR) || (cfg->rc_end_usage == AVM_CBR)) { + // cq-level is ignored in these two end-usage modes, so adjust minQuantizer and + // maxQuantizer to the target quantizer. + if (quantizer == AVIF_QUANTIZER_LOSSLESS) { + minQuantizer = AVIF_QUANTIZER_LOSSLESS; + maxQuantizer = AVIF_QUANTIZER_LOSSLESS; + } else { + minQuantizer = AVIF_MAX(quantizer - 4, minQuantizer); + maxQuantizer = AVIF_MIN(quantizer + 4, maxQuantizer); + } + } + cfg->rc_min_quantizer = minQuantizer; + cfg->rc_max_quantizer = maxQuantizer; + quantizerUpdated = AVIF_TRUE; + + if (avm_codec_enc_init(&codec->internal->encoder, encoderInterface, cfg, /*flags=*/0) != AVM_CODEC_OK) { + avifDiagnosticsPrintf(codec->diag, + "avm_codec_enc_init() failed: %s: %s", + avm_codec_error(&codec->internal->encoder), + avm_codec_error_detail(&codec->internal->encoder)); + return AVIF_RESULT_UNKNOWN_ERROR; + } + codec->internal->encoderInitialized = AVIF_TRUE; + + if ((cfg->rc_end_usage == AVM_CQ) || (cfg->rc_end_usage == AVM_Q)) { + avm_codec_control(&codec->internal->encoder, AVME_SET_QP, quantizer); + } + avifBool lossless = (quantizer == AVIF_QUANTIZER_LOSSLESS); + if (lossless) { + avm_codec_control(&codec->internal->encoder, AV2E_SET_LOSSLESS, 1); + } + if (encoder->maxThreads > 1) { + avm_codec_control(&codec->internal->encoder, AV2E_SET_ROW_MT, 1); + } + if (tileRowsLog2 != 0) { + avm_codec_control(&codec->internal->encoder, AV2E_SET_TILE_ROWS, tileRowsLog2); + } + if (tileColsLog2 != 0) { + avm_codec_control(&codec->internal->encoder, AV2E_SET_TILE_COLUMNS, tileColsLog2); + } + if (encoder->extraLayerCount > 0) { + int layerCount = encoder->extraLayerCount + 1; + if (avm_codec_control(&codec->internal->encoder, AVME_SET_NUMBER_MLAYERS, layerCount) != AVM_CODEC_OK) { + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + if (avmCpuUsed != -1) { + if (avm_codec_control(&codec->internal->encoder, AVME_SET_CPUUSED, avmCpuUsed) != AVM_CODEC_OK) { + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + + // Set color_config() in the sequence header OBU. + if (alpha) { + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. + avm_codec_control(&codec->internal->encoder, AV2E_SET_COLOR_RANGE, AVM_CR_FULL_RANGE); + + // Keep the default AVM_CSP_UNKNOWN value. + + // CICP (CP/TC/MC) does not apply to the alpha auxiliary image. + // Keep default Unspecified (2) colour primaries, transfer characteristics, + // and matrix coefficients. + } else { + // libavm's defaults are AVM_CSP_UNKNOWN and 0 (studio/limited range). + // Call avm_codec_control() only if the values are not the defaults. + + // AV1-AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. + if (image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN) { + avm_codec_control(&codec->internal->encoder, AV2E_SET_CHROMA_SAMPLE_POSITION, (int)image->yuvChromaSamplePosition); + } + + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. + if (image->yuvRange != AVIF_RANGE_LIMITED) { + avm_codec_control(&codec->internal->encoder, AV2E_SET_COLOR_RANGE, (int)image->yuvRange); + } + + // Section 2.3.4 of AV1-ISOBMFF says 'colr' with 'nclx' should be present and shall match CICP + // values in the Sequence Header OBU, unless the latter has 2/2/2 (Unspecified). + // So set CICP values to 2/2/2 (Unspecified) in the Sequence Header OBU for simplicity. + // libavm's defaults are AVM_CICP_CP_UNSPECIFIED, AVM_CICP_TC_UNSPECIFIED, and + // AVM_CICP_MC_UNSPECIFIED. No need to call avm_codec_control(). + // avm_image_t::cp, avm_image_t::tc and avm_image_t::mc are ignored by avm_codec_encode(). + } + + if (!avifProcessAVMOptionsPostInit(codec, alpha)) { + return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + } + // Disabling these two gives 1.19% PSNR YUV loss in All-Intra config, but encode will be ~4X faster. + if (avm_codec_set_option(&codec->internal->encoder, "enable-ext-partitions", "0") != AVM_CODEC_OK || + avm_codec_set_option(&codec->internal->encoder, "enable-uneven-4way-partitions", "0") != AVM_CODEC_OK) { + return AVIF_RESULT_UNKNOWN_ERROR; + } + if (!codec->internal->tuningSet) { + if (avm_codec_control(&codec->internal->encoder, AVME_SET_TUNING, AVM_TUNE_SSIM) != AVM_CODEC_OK) { + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + } else { + avifBool dimensionsChanged = AVIF_FALSE; + if ((cfg->g_w != image->width) || (cfg->g_h != image->height)) { + // We are not ready for dimension change for now. + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (alpha) { + if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA | AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA)) { + cfg->rc_min_quantizer = avmScaleQuantizer(encoder->minQuantizerAlpha, image->depth); + cfg->rc_max_quantizer = avmScaleQuantizer(encoder->maxQuantizerAlpha, image->depth); + quantizerUpdated = AVIF_TRUE; + } + } else { + if (encoderChanges & (AVIF_ENCODER_CHANGE_MIN_QUANTIZER | AVIF_ENCODER_CHANGE_MAX_QUANTIZER)) { + cfg->rc_min_quantizer = avmScaleQuantizer(encoder->minQuantizer, image->depth); + cfg->rc_max_quantizer = avmScaleQuantizer(encoder->maxQuantizer, image->depth); + quantizerUpdated = AVIF_TRUE; + } + } + const int qualityChangedBit = alpha ? AVIF_ENCODER_CHANGE_QUALITY_ALPHA : AVIF_ENCODER_CHANGE_QUALITY; + if (encoderChanges & qualityChangedBit) { + if ((cfg->rc_end_usage == AVM_VBR) || (cfg->rc_end_usage == AVM_CBR)) { + // cq-level is ignored in these two end-usage modes, so adjust minQuantizer and + // maxQuantizer to the target quantizer. + if (quantizer == AVIF_QUANTIZER_LOSSLESS) { + cfg->rc_min_quantizer = AVIF_QUANTIZER_LOSSLESS; + cfg->rc_max_quantizer = AVIF_QUANTIZER_LOSSLESS; + } else { + int minQuantizer; + int maxQuantizer; + if (alpha) { + minQuantizer = encoder->minQuantizerAlpha; + maxQuantizer = encoder->maxQuantizerAlpha; + } else { + minQuantizer = encoder->minQuantizer; + maxQuantizer = encoder->maxQuantizer; + } + minQuantizer = avmScaleQuantizer(minQuantizer, image->depth); + maxQuantizer = avmScaleQuantizer(maxQuantizer, image->depth); + cfg->rc_min_quantizer = AVIF_MAX(quantizer - 4, minQuantizer); + cfg->rc_max_quantizer = AVIF_MIN(quantizer + 4, maxQuantizer); + } + quantizerUpdated = AVIF_TRUE; + } + } + if (quantizerUpdated || dimensionsChanged) { + avm_codec_err_t err = avm_codec_enc_config_set(&codec->internal->encoder, cfg); + if (err != AVM_CODEC_OK) { + avifDiagnosticsPrintf(codec->diag, + "avm_codec_enc_config_set() failed: %s: %s", + avm_codec_error(&codec->internal->encoder), + avm_codec_error_detail(&codec->internal->encoder)); + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2) { + avm_codec_control(&codec->internal->encoder, AV2E_SET_TILE_ROWS, tileRowsLog2); + } + if (encoderChanges & AVIF_ENCODER_CHANGE_TILE_COLS_LOG2) { + avm_codec_control(&codec->internal->encoder, AV2E_SET_TILE_COLUMNS, tileColsLog2); + } + if (encoderChanges & qualityChangedBit) { + if ((cfg->rc_end_usage == AVM_CQ) || (cfg->rc_end_usage == AVM_Q)) { + avm_codec_control(&codec->internal->encoder, AVME_SET_QP, quantizer); + } + avifBool lossless = (quantizer == AVIF_QUANTIZER_LOSSLESS); + avm_codec_control(&codec->internal->encoder, AV2E_SET_LOSSLESS, lossless); + } + if (encoderChanges & AVIF_ENCODER_CHANGE_CODEC_SPECIFIC) { + if (!avifProcessAVMOptionsPostInit(codec, alpha)) { + return AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + } + } + } + + if (codec->internal->currentLayer > encoder->extraLayerCount) { + avifDiagnosticsPrintf(codec->diag, + "Too many layers sent. Expected %u layers, but got %u layers.", + encoder->extraLayerCount + 1, + codec->internal->currentLayer + 1); + return AVIF_RESULT_INVALID_ARGUMENT; + } + if (encoder->extraLayerCount > 0) { + avm_codec_control(&codec->internal->encoder, AVME_SET_MLAYER_ID, codec->internal->currentLayer); + } + + avm_scaling_mode_t avmScalingMode; + if (!avifFindAVMScalingMode(&encoder->scalingMode.horizontal, &avmScalingMode.h_scaling_mode)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (!avifFindAVMScalingMode(&encoder->scalingMode.vertical, &avmScalingMode.v_scaling_mode)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if ((avmScalingMode.h_scaling_mode != AVME_NORMAL) || (avmScalingMode.v_scaling_mode != AVME_NORMAL)) { + // AVME_SET_SCALEMODE only applies to next frame (layer), so we have to set it every time. + avm_codec_control(&codec->internal->encoder, AVME_SET_SCALEMODE, &avmScalingMode); + } + + avm_image_t avmImage; + // We prefer to simply set the avmImage.planes[] pointers to the plane buffers in 'image'. When + // doing this, we set avmImage.w equal to avmImage.d_w and avmImage.h equal to avmImage.d_h and + // do not "align" avmImage.w and avmImage.h. Unfortunately this exposes a libaom bug in libavm + // (https://crbug.com/aomedia/3113) if chroma is subsampled and image->width or image->height is + // equal to 1. To work around this libavm bug, we allocate the avmImage.planes[] buffers and + // copy the image YUV data if image->width or image->height is equal to 1. This bug has been + // fixed in libaom v3.1.3 but not in libavm. + // + // Note: The exact condition for the bug is + // ((image->width == 1) && (chroma is subsampled horizontally)) || + // ((image->height == 1) && (chroma is subsampled vertically)) + // Since an image width or height of 1 is uncommon in practice, we test an inexact but simpler + // condition. + avifBool avmImageAllocated = (image->width == 1) || (image->height == 1); + if (avmImageAllocated) { + avm_img_alloc(&avmImage, codec->internal->avmFormat, image->width, image->height, 16); + } else { + memset(&avmImage, 0, sizeof(avmImage)); + avmImage.fmt = codec->internal->avmFormat; + avmImage.bit_depth = (image->depth > 8) ? 16 : 8; + avmImage.w = image->width; + avmImage.h = image->height; + avmImage.d_w = image->width; + avmImage.d_h = image->height; + // Get sample size for this format. + unsigned int bps; + if (codec->internal->avmFormat == AVM_IMG_FMT_I420) { + bps = 12; + } else if (codec->internal->avmFormat == AVM_IMG_FMT_I422) { + bps = 16; + } else if (codec->internal->avmFormat == AVM_IMG_FMT_I444) { + bps = 24; + } else if (codec->internal->avmFormat == AVM_IMG_FMT_I42016) { + bps = 24; + } else if (codec->internal->avmFormat == AVM_IMG_FMT_I42216) { + bps = 32; + } else if (codec->internal->avmFormat == AVM_IMG_FMT_I44416) { + bps = 48; + } else { + bps = 16; + } + avmImage.bps = bps; + // See avifImageCalcAVMFmt(). libavm doesn't have AVM_IMG_FMT_I400, so we use AVM_IMG_FMT_I420 as a substitute for monochrome. + avmImage.x_chroma_shift = (alpha || codec->internal->formatInfo.monochrome) ? 1 : codec->internal->formatInfo.chromaShiftX; + avmImage.y_chroma_shift = (alpha || codec->internal->formatInfo.monochrome) ? 1 : codec->internal->formatInfo.chromaShiftY; + } + + avifBool monochromeRequested = AVIF_FALSE; + + if (alpha) { + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. + avmImage.range = AVM_CR_FULL_RANGE; + + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The mono_chrome field in the Sequence Header OBU shall be set to 1. + // Some encoders do not support 4:0:0 and encode alpha as 4:2:0 so it is not always respected. + monochromeRequested = AVIF_TRUE; + if (avmImageAllocated) { + const uint32_t bytesPerRow = ((image->depth > 8) ? 2 : 1) * image->width; + for (uint32_t j = 0; j < image->height; ++j) { + const uint8_t * srcAlphaRow = &image->alphaPlane[(size_t)j * image->alphaRowBytes]; + uint8_t * dstAlphaRow = &avmImage.planes[0][(size_t)j * avmImage.stride[0]]; + memcpy(dstAlphaRow, srcAlphaRow, bytesPerRow); + } + } else { + avmImage.planes[0] = image->alphaPlane; + avmImage.stride[0] = image->alphaRowBytes; + } + + // Ignore UV planes when monochrome. Keep the default AVM_CSP_UNKNOWN value. + } else { + int yuvPlaneCount = 3; + if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { + yuvPlaneCount = 1; // Ignore UV planes when monochrome + monochromeRequested = AVIF_TRUE; + } + if (avmImageAllocated) { + uint32_t bytesPerPixel = (image->depth > 8) ? 2 : 1; + for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { + uint32_t planeWidth = avifImagePlaneWidth(image, yuvPlane); + uint32_t planeHeight = avifImagePlaneHeight(image, yuvPlane); + uint32_t bytesPerRow = bytesPerPixel * planeWidth; + + for (uint32_t j = 0; j < planeHeight; ++j) { + const uint8_t * srcRow = &image->yuvPlanes[yuvPlane][(size_t)j * image->yuvRowBytes[yuvPlane]]; + uint8_t * dstRow = &avmImage.planes[yuvPlane][(size_t)j * avmImage.stride[yuvPlane]]; + memcpy(dstRow, srcRow, bytesPerRow); + } + } + } else { + for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { + avmImage.planes[yuvPlane] = image->yuvPlanes[yuvPlane]; + avmImage.stride[yuvPlane] = image->yuvRowBytes[yuvPlane]; + } + } + + // AV1-AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. + if (image->yuvChromaSamplePosition == AVIF_CHROMA_SAMPLE_POSITION_VERTICAL) { + // CSP_LEFT: Horizontal offset 0, vertical offset 0.5 + avmImage.csp = AVM_CSP_LEFT; + } else if (image->yuvChromaSamplePosition == AVIF_CHROMA_SAMPLE_POSITION_COLOCATED) { + // CSP_TOPLEFT: Horizontal offset 0, vertical offset 0 + avmImage.csp = AVM_CSP_TOPLEFT; + } else if (image->yuvChromaSamplePosition == AVIF_CHROMA_SAMPLE_POSITION_RESERVED) { + // CSP_CENTER: Horizontal offset 0.5, vertical offset 0.5 + avmImage.csp = AVM_CSP_CENTER; + } else { // AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN or invalid values + avmImage.csp = AVM_CSP_UNSPECIFIED; + } + + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. + avmImage.range = (avm_color_range_t)image->yuvRange; + } + + unsigned char * monoUVPlane = NULL; + if (monochromeRequested) { + if (codec->internal->monochromeEnabled) { + avmImage.monochrome = 1; + } else { + // The user requested monochrome (via alpha or YUV400) but libavm does not support + // monochrome. Manually set UV planes to 0.5. + + // avmImage is always 420 when we're monochrome + uint32_t monoUVWidth = (image->width + 1) >> 1; + uint32_t monoUVHeight = (image->height + 1) >> 1; + + // Allocate the U plane if necessary. + if (!avmImageAllocated) { + uint32_t channelSize = avifImageUsesU16(image) ? 2 : 1; + uint32_t monoUVRowBytes = channelSize * monoUVWidth; + size_t monoUVSize = (size_t)monoUVHeight * monoUVRowBytes; + + monoUVPlane = avifAlloc(monoUVSize); + AVIF_CHECKERR(monoUVPlane != NULL, AVIF_RESULT_OUT_OF_MEMORY); // No need for avm_img_free() because !avmImageAllocated + avmImage.planes[1] = monoUVPlane; + avmImage.stride[1] = monoUVRowBytes; + } + // Set the U plane to 0.5. + if (image->depth > 8) { + const uint16_t half = 1 << (image->depth - 1); + for (uint32_t j = 0; j < monoUVHeight; ++j) { + uint16_t * dstRow = (uint16_t *)&avmImage.planes[1][(size_t)j * avmImage.stride[1]]; + for (uint32_t i = 0; i < monoUVWidth; ++i) { + dstRow[i] = half; + } + } + } else { + const uint8_t half = 128; + size_t planeSize = (size_t)monoUVHeight * avmImage.stride[1]; + memset(avmImage.planes[1], half, planeSize); + } + // Make the V plane the same as the U plane. + avmImage.planes[2] = avmImage.planes[1]; + avmImage.stride[2] = avmImage.stride[1]; + } + } + + avm_enc_frame_flags_t encodeFlags = 0; + if (addImageFlags & AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME) { + encodeFlags |= AVM_EFLAG_FORCE_KF; + } + if (codec->internal->currentLayer > 0) { + encodeFlags |= AVM_EFLAG_NO_REF_GF | AVM_EFLAG_NO_REF_ARF | AVM_EFLAG_NO_REF_BWD | AVM_EFLAG_NO_REF_ARF2 | AVM_EFLAG_NO_UPD_ALL; + } + avm_codec_err_t encodeErr = avm_codec_encode(&codec->internal->encoder, &avmImage, 0, 1, encodeFlags); + avifFree(monoUVPlane); + if (avmImageAllocated) { + avm_img_free(&avmImage); + } + if (encodeErr != AVM_CODEC_OK) { + avifDiagnosticsPrintf(codec->diag, + "avm_codec_encode() failed: %s: %s", + avm_codec_error(&codec->internal->encoder), + avm_codec_error_detail(&codec->internal->encoder)); + return AVIF_RESULT_UNKNOWN_ERROR; + } + + avm_codec_iter_t iter = NULL; + for (;;) { + const avm_codec_cx_pkt_t * pkt = avm_codec_get_cx_data(&codec->internal->encoder, &iter); + if (pkt == NULL) { + break; + } + if (pkt->kind == AVM_CODEC_CX_FRAME_PKT) { + AVIF_CHECKRES( + avifCodecEncodeOutputAddSample(output, pkt->data.frame.buf, pkt->data.frame.sz, (pkt->data.frame.flags & AVM_FRAME_IS_KEY))); + } + } + + if ((addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) || + ((encoder->extraLayerCount > 0) && (encoder->extraLayerCount == codec->internal->currentLayer))) { + // Flush and clean up encoder resources early to save on overhead when encoding alpha or grid images, + // as encoding is finished now. For layered image, encoding finishes when the last layer is encoded. + + if (!avmCodecEncodeFinish(codec, output)) { + return AVIF_RESULT_UNKNOWN_ERROR; + } + avm_codec_destroy(&codec->internal->encoder); + codec->internal->encoderInitialized = AVIF_FALSE; + } + if (encoder->extraLayerCount > 0) { + ++codec->internal->currentLayer; + } + return AVIF_RESULT_OK; +} + +static avifBool avmCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output) +{ + if (!codec->internal->encoderInitialized) { + return AVIF_TRUE; + } + for (;;) { + // flush encoder + if (avm_codec_encode(&codec->internal->encoder, NULL, 0, 1, 0) != AVM_CODEC_OK) { + avifDiagnosticsPrintf(codec->diag, + "avm_codec_encode() with img=NULL failed: %s: %s", + avm_codec_error(&codec->internal->encoder), + avm_codec_error_detail(&codec->internal->encoder)); + return AVIF_FALSE; + } + + avifBool gotPacket = AVIF_FALSE; + avm_codec_iter_t iter = NULL; + for (;;) { + const avm_codec_cx_pkt_t * pkt = avm_codec_get_cx_data(&codec->internal->encoder, &iter); + if (pkt == NULL) { + break; + } + if (pkt->kind == AVM_CODEC_CX_FRAME_PKT) { + gotPacket = AVIF_TRUE; + const avifResult result = avifCodecEncodeOutputAddSample(output, + pkt->data.frame.buf, + pkt->data.frame.sz, + (pkt->data.frame.flags & AVM_FRAME_IS_KEY)); + if (result != AVIF_RESULT_OK) { + avifDiagnosticsPrintf(codec->diag, "avifCodecEncodeOutputAddSample() failed: %s", avifResultToString(result)); + return AVIF_FALSE; + } + } + } + + if (!gotPacket) { + break; + } + } + return AVIF_TRUE; +} + +const char * avifCodecVersionAVM(void) +{ + return avm_codec_version_str(); +} + +avifCodec * avifCodecCreateAVM(void) +{ + avifCodec * codec = (avifCodec *)avifCalloc(1, sizeof(avifCodec)); + if (codec == NULL) { + return NULL; + } + + codec->getNextImage = avmCodecGetNextImage; + + codec->encodeImage = avmCodecEncodeImage; + codec->encodeFinish = avmCodecEncodeFinish; + + codec->destroyInternal = avmCodecDestroyInternal; + codec->internal = (struct avifCodecInternal *)avifCalloc(1, sizeof(struct avifCodecInternal)); + if (codec->internal == NULL) { + avifFree(codec); + return NULL; + } + return codec; +} diff --git a/3rdparty/libavif/src/codec_dav1d.c b/3rdparty/libavif/src/codec_dav1d.c new file mode 100644 index 0000000..2f1210a --- /dev/null +++ b/3rdparty/libavif/src/codec_dav1d.c @@ -0,0 +1,248 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#if defined(_MSC_VER) +#pragma warning(disable : 4201) // nonstandard extension used: nameless struct/union +#endif +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc11-extensions" // C11 extension used: nameless struct/union +#endif +#include "dav1d/dav1d.h" +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +#include +#include + +// For those building with an older version of dav1d (not recommended). +#ifndef DAV1D_ERR +#define DAV1D_ERR(e) (-(e)) +#endif + +struct avifCodecInternal +{ + Dav1dContext * dav1dContext; + Dav1dPicture dav1dPicture; + avifBool hasPicture; + avifRange colorRange; +}; + +static void avifDav1dFreeCallback(const uint8_t * buf, void * cookie) +{ + // This data is owned by the decoder; nothing to free here + (void)buf; + (void)cookie; +} + +static void avifDav1dLogCallback(void * cookie, const char * format, va_list ap) +{ + avifCodec * codec = (avifCodec *)cookie; + vsnprintf(codec->diag->error, AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE, format, ap); +} + +static void dav1dCodecDestroyInternal(avifCodec * codec) +{ + if (codec->internal->hasPicture) { + dav1d_picture_unref(&codec->internal->dav1dPicture); + } + if (codec->internal->dav1dContext) { + dav1d_close(&codec->internal->dav1dContext); + } + avifFree(codec->internal); +} + +static avifBool dav1dCodecGetNextImage(struct avifCodec * codec, + const avifDecodeSample * sample, + avifBool alpha, + avifBool * isLimitedRangeAlpha, + avifImage * image) +{ + if (codec->internal->dav1dContext == NULL) { + Dav1dSettings dav1dSettings; + dav1d_default_settings(&dav1dSettings); + // Give all available threads to decode a single frame as fast as possible +#if DAV1D_API_VERSION_MAJOR >= 6 + dav1dSettings.max_frame_delay = 1; + dav1dSettings.n_threads = AVIF_CLAMP(codec->maxThreads, 1, DAV1D_MAX_THREADS); +#else + dav1dSettings.n_frame_threads = 1; + dav1dSettings.n_tile_threads = AVIF_CLAMP(codec->maxThreads, 1, DAV1D_MAX_TILE_THREADS); +#endif // DAV1D_API_VERSION_MAJOR >= 6 + // Set a maximum frame size limit to avoid OOM'ing fuzzers. In 32-bit builds, if + // frame_size_limit > 8192 * 8192, dav1d reduces frame_size_limit to 8192 * 8192 and logs + // a message, so we set frame_size_limit to at most 8192 * 8192 to avoid the dav1d_log + // message. + dav1dSettings.frame_size_limit = (sizeof(size_t) < 8) ? AVIF_MIN(codec->imageSizeLimit, 8192 * 8192) : codec->imageSizeLimit; + dav1dSettings.logger.cookie = codec; + dav1dSettings.logger.callback = avifDav1dLogCallback; + dav1dSettings.operating_point = codec->operatingPoint; + dav1dSettings.all_layers = codec->allLayers; + + if (dav1d_open(&codec->internal->dav1dContext, &dav1dSettings) != 0) { + return AVIF_FALSE; + } + } + + avifBool gotPicture = AVIF_FALSE; + Dav1dPicture nextFrame; + memset(&nextFrame, 0, sizeof(Dav1dPicture)); + + Dav1dData dav1dData; + if (dav1d_data_wrap(&dav1dData, sample->data.data, sample->data.size, avifDav1dFreeCallback, NULL) != 0) { + return AVIF_FALSE; + } + + int res; + for (;;) { + if (dav1dData.data) { + res = dav1d_send_data(codec->internal->dav1dContext, &dav1dData); + if ((res < 0) && (res != DAV1D_ERR(EAGAIN))) { + dav1d_data_unref(&dav1dData); + return AVIF_FALSE; + } + } + + res = dav1d_get_picture(codec->internal->dav1dContext, &nextFrame); + if (res == DAV1D_ERR(EAGAIN)) { + if (dav1dData.data) { + // send more data + continue; + } + return AVIF_FALSE; + } else if (res < 0) { + // No more frames + if (dav1dData.data) { + dav1d_data_unref(&dav1dData); + } + return AVIF_FALSE; + } else { + // Got a picture! + if ((sample->spatialID != AVIF_SPATIAL_ID_UNSET) && (sample->spatialID != nextFrame.frame_hdr->spatial_id)) { + // Layer selection: skip this unwanted layer + dav1d_picture_unref(&nextFrame); + } else { + gotPicture = AVIF_TRUE; + break; + } + } + } + if (dav1dData.data) { + dav1d_data_unref(&dav1dData); + } + + // Drain all buffered frames in the decoder. + // + // The sample should have only one frame of the desired layer. If there are more frames after + // that frame, we need to discard them so that they won't be mistakenly output when the decoder + // is used to decode another sample. + Dav1dPicture bufferedFrame; + memset(&bufferedFrame, 0, sizeof(Dav1dPicture)); + do { + res = dav1d_get_picture(codec->internal->dav1dContext, &bufferedFrame); + if (res < 0) { + if (res != DAV1D_ERR(EAGAIN)) { + if (gotPicture) { + dav1d_picture_unref(&nextFrame); + } + return AVIF_FALSE; + } + } else { + dav1d_picture_unref(&bufferedFrame); + } + } while (res == 0); + + if (gotPicture) { + dav1d_picture_unref(&codec->internal->dav1dPicture); + codec->internal->dav1dPicture = nextFrame; + codec->internal->colorRange = codec->internal->dav1dPicture.seq_hdr->color_range ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED; + codec->internal->hasPicture = AVIF_TRUE; + } else { + if (alpha && codec->internal->hasPicture) { + // Special case: reuse last alpha frame + } else { + return AVIF_FALSE; + } + } + + Dav1dPicture * dav1dImage = &codec->internal->dav1dPicture; + avifBool isColor = !alpha; + if (isColor) { + // Color (YUV) planes - set image to correct size / format, fill color + + avifPixelFormat yuvFormat = AVIF_PIXEL_FORMAT_NONE; + switch (dav1dImage->p.layout) { + case DAV1D_PIXEL_LAYOUT_I400: + yuvFormat = AVIF_PIXEL_FORMAT_YUV400; + break; + case DAV1D_PIXEL_LAYOUT_I420: + yuvFormat = AVIF_PIXEL_FORMAT_YUV420; + break; + case DAV1D_PIXEL_LAYOUT_I422: + yuvFormat = AVIF_PIXEL_FORMAT_YUV422; + break; + case DAV1D_PIXEL_LAYOUT_I444: + yuvFormat = AVIF_PIXEL_FORMAT_YUV444; + break; + } + + image->width = dav1dImage->p.w; + image->height = dav1dImage->p.h; + image->depth = dav1dImage->p.bpc; + + image->yuvFormat = yuvFormat; + image->yuvRange = codec->internal->colorRange; + image->yuvChromaSamplePosition = (avifChromaSamplePosition)dav1dImage->seq_hdr->chr; + + image->colorPrimaries = (avifColorPrimaries)dav1dImage->seq_hdr->pri; + image->transferCharacteristics = (avifTransferCharacteristics)dav1dImage->seq_hdr->trc; + image->matrixCoefficients = (avifMatrixCoefficients)dav1dImage->seq_hdr->mtrx; + + // Steal the pointers from the decoder's image directly + avifImageFreePlanes(image, AVIF_PLANES_YUV); + int yuvPlaneCount = (yuvFormat == AVIF_PIXEL_FORMAT_YUV400) ? 1 : 3; + for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { + image->yuvPlanes[yuvPlane] = dav1dImage->data[yuvPlane]; + image->yuvRowBytes[yuvPlane] = (uint32_t)dav1dImage->stride[(yuvPlane == AVIF_CHAN_Y) ? 0 : 1]; + } + image->imageOwnsYUVPlanes = AVIF_FALSE; + } else { + // Alpha plane - set image to correct size, fill alpha + + image->width = dav1dImage->p.w; + image->height = dav1dImage->p.h; + image->depth = dav1dImage->p.bpc; + + avifImageFreePlanes(image, AVIF_PLANES_A); + image->alphaPlane = dav1dImage->data[0]; + image->alphaRowBytes = (uint32_t)dav1dImage->stride[0]; + *isLimitedRangeAlpha = (codec->internal->colorRange == AVIF_RANGE_LIMITED); + image->imageOwnsAlphaPlane = AVIF_FALSE; + } + return AVIF_TRUE; +} + +const char * avifCodecVersionDav1d(void) +{ + return dav1d_version(); +} + +avifCodec * avifCodecCreateDav1d(void) +{ + avifCodec * codec = (avifCodec *)avifCalloc(1, sizeof(avifCodec)); + if (codec == NULL) { + return NULL; + } + codec->getNextImage = dav1dCodecGetNextImage; + codec->destroyInternal = dav1dCodecDestroyInternal; + + codec->internal = (struct avifCodecInternal *)avifCalloc(1, sizeof(struct avifCodecInternal)); + if (codec->internal == NULL) { + avifFree(codec); + return NULL; + } + return codec; +} diff --git a/3rdparty/libavif/src/codec_libgav1.c b/3rdparty/libavif/src/codec_libgav1.c new file mode 100644 index 0000000..a529b5a --- /dev/null +++ b/3rdparty/libavif/src/codec_libgav1.c @@ -0,0 +1,157 @@ +// Copyright 2020 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include "gav1/decoder.h" + +#include + +struct avifCodecInternal +{ + Libgav1DecoderSettings gav1Settings; + Libgav1Decoder * gav1Decoder; + const Libgav1DecoderBuffer * gav1Image; + avifRange colorRange; +}; + +static void gav1CodecDestroyInternal(avifCodec * codec) +{ + if (codec->internal->gav1Decoder != NULL) { + Libgav1DecoderDestroy(codec->internal->gav1Decoder); + } + avifFree(codec->internal); +} + +static avifBool gav1CodecGetNextImage(struct avifCodec * codec, + const avifDecodeSample * sample, + avifBool alpha, + avifBool * isLimitedRangeAlpha, + avifImage * image) +{ + if (codec->internal->gav1Decoder == NULL) { + codec->internal->gav1Settings.threads = codec->maxThreads; + codec->internal->gav1Settings.operating_point = codec->operatingPoint; + codec->internal->gav1Settings.output_all_layers = codec->allLayers; + + if (Libgav1DecoderCreate(&codec->internal->gav1Settings, &codec->internal->gav1Decoder) != kLibgav1StatusOk) { + return AVIF_FALSE; + } + } + + if (Libgav1DecoderEnqueueFrame(codec->internal->gav1Decoder, + sample->data.data, + sample->data.size, + /*user_private_data=*/0, + /*buffer_private_data=*/NULL) != kLibgav1StatusOk) { + return AVIF_FALSE; + } + // Each Libgav1DecoderDequeueFrame() call invalidates the output frame + // returned by the previous Libgav1DecoderDequeueFrame() call. Clear + // our pointer to the previous output frame. + codec->internal->gav1Image = NULL; + + const Libgav1DecoderBuffer * nextFrame = NULL; + for (;;) { + if (Libgav1DecoderDequeueFrame(codec->internal->gav1Decoder, &nextFrame) != kLibgav1StatusOk) { + return AVIF_FALSE; + } + if (nextFrame && (sample->spatialID != AVIF_SPATIAL_ID_UNSET) && (nextFrame->spatial_id != sample->spatialID)) { + nextFrame = NULL; + } else { + break; + } + } + // Got an image! + + if (nextFrame) { + codec->internal->gav1Image = nextFrame; + codec->internal->colorRange = (nextFrame->color_range == kLibgav1ColorRangeStudio) ? AVIF_RANGE_LIMITED : AVIF_RANGE_FULL; + } else { + if (alpha && codec->internal->gav1Image) { + // Special case: reuse last alpha frame + } else { + return AVIF_FALSE; + } + } + + const Libgav1DecoderBuffer * gav1Image = codec->internal->gav1Image; + avifBool isColor = !alpha; + if (isColor) { + // Color (YUV) planes - set image to correct size / format, fill color + + avifPixelFormat yuvFormat = AVIF_PIXEL_FORMAT_NONE; + switch (gav1Image->image_format) { + case kLibgav1ImageFormatMonochrome400: + yuvFormat = AVIF_PIXEL_FORMAT_YUV400; + break; + case kLibgav1ImageFormatYuv420: + yuvFormat = AVIF_PIXEL_FORMAT_YUV420; + break; + case kLibgav1ImageFormatYuv422: + yuvFormat = AVIF_PIXEL_FORMAT_YUV422; + break; + case kLibgav1ImageFormatYuv444: + yuvFormat = AVIF_PIXEL_FORMAT_YUV444; + break; + } + + image->width = gav1Image->displayed_width[0]; + image->height = gav1Image->displayed_height[0]; + image->depth = gav1Image->bitdepth; + + image->yuvFormat = yuvFormat; + image->yuvRange = codec->internal->colorRange; + image->yuvChromaSamplePosition = (avifChromaSamplePosition)gav1Image->chroma_sample_position; + + image->colorPrimaries = (avifColorPrimaries)gav1Image->color_primary; + image->transferCharacteristics = (avifTransferCharacteristics)gav1Image->transfer_characteristics; + image->matrixCoefficients = (avifMatrixCoefficients)gav1Image->matrix_coefficients; + + // Steal the pointers from the decoder's image directly + avifImageFreePlanes(image, AVIF_PLANES_YUV); + int yuvPlaneCount = (yuvFormat == AVIF_PIXEL_FORMAT_YUV400) ? 1 : 3; + for (int yuvPlane = 0; yuvPlane < yuvPlaneCount; ++yuvPlane) { + image->yuvPlanes[yuvPlane] = gav1Image->plane[yuvPlane]; + image->yuvRowBytes[yuvPlane] = gav1Image->stride[yuvPlane]; + } + image->imageOwnsYUVPlanes = AVIF_FALSE; + } else { + // Alpha plane - set image to correct size, fill alpha + + image->width = gav1Image->displayed_width[0]; + image->height = gav1Image->displayed_height[0]; + image->depth = gav1Image->bitdepth; + + avifImageFreePlanes(image, AVIF_PLANES_A); + image->alphaPlane = gav1Image->plane[0]; + image->alphaRowBytes = gav1Image->stride[0]; + *isLimitedRangeAlpha = (codec->internal->colorRange == AVIF_RANGE_LIMITED); + image->imageOwnsAlphaPlane = AVIF_FALSE; + } + + return AVIF_TRUE; +} + +const char * avifCodecVersionGav1(void) +{ + return Libgav1GetVersionString(); +} + +avifCodec * avifCodecCreateGav1(void) +{ + avifCodec * codec = (avifCodec *)avifCalloc(1, sizeof(avifCodec)); + if (codec == NULL) { + return NULL; + } + codec->getNextImage = gav1CodecGetNextImage; + codec->destroyInternal = gav1CodecDestroyInternal; + + codec->internal = (struct avifCodecInternal *)avifCalloc(1, sizeof(struct avifCodecInternal)); + if (codec->internal == NULL) { + avifFree(codec); + return NULL; + } + Libgav1DecoderSettingsInitDefault(&codec->internal->gav1Settings); + return codec; +} diff --git a/3rdparty/libavif/src/codec_rav1e.c b/3rdparty/libavif/src/codec_rav1e.c new file mode 100644 index 0000000..97c92be --- /dev/null +++ b/3rdparty/libavif/src/codec_rav1e.c @@ -0,0 +1,350 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include "rav1e.h" + +#include + +struct avifCodecInternal +{ + RaContext * rav1eContext; + RaChromaSampling chromaSampling; + int yShift; + uint32_t encodeWidth; + uint32_t encodeHeight; +}; + +static void rav1eCodecDestroyInternal(avifCodec * codec) +{ + if (codec->internal->rav1eContext) { + rav1e_context_unref(codec->internal->rav1eContext); + codec->internal->rav1eContext = NULL; + } + avifFree(codec->internal); +} + +// Official support wasn't added until v0.4.0 +static avifBool rav1eSupports400(void) +{ + const char * rav1eVersionString = rav1e_version_short(); + + // Check major version > 0 + int majorVersion = atoi(rav1eVersionString); + if (majorVersion > 0) { + return AVIF_TRUE; + } + + // Check minor version >= 4 + const char * minorVersionString = strchr(rav1eVersionString, '.'); + if (!minorVersionString) { + return AVIF_FALSE; + } + ++minorVersionString; + if (!(*minorVersionString)) { + return AVIF_FALSE; + } + int minorVersion = atoi(minorVersionString); + return minorVersion >= 4; +} + +// rav1e's QP range is [0,255] +static int rav1eQualityToQuantizer(int quality) +{ + const int quantizer = ((100 - quality) * 255 + 50) / 100; + + return quantizer; +} + +static avifResult rav1eCodecEncodeImage(avifCodec * codec, + avifEncoder * encoder, + const avifImage * image, + avifBool alpha, + int tileRowsLog2, + int tileColsLog2, + int quality, + avifEncoderChanges encoderChanges, + avifBool disableLaggedOutput, + uint32_t addImageFlags, + avifCodecEncodeOutput * output) +{ + // rav1e does not support changing encoder settings. + if (encoderChanges) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + // rav1e does not support changing image dimensions. + if (!codec->internal->rav1eContext) { + codec->internal->encodeWidth = image->width; + codec->internal->encodeHeight = image->height; + } else if ((codec->internal->encodeWidth != image->width) || (codec->internal->encodeHeight != image->height)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + // rav1e does not support encoding layered image. + if (encoder->extraLayerCount > 0) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + // rav1e does not support disabling lagged output. See https://github.com/xiph/rav1e/issues/2267. Ignore this setting. + (void)disableLaggedOutput; + + avifResult result = AVIF_RESULT_UNKNOWN_ERROR; + + RaConfig * rav1eConfig = NULL; + RaFrame * rav1eFrame = NULL; + + if (!codec->internal->rav1eContext) { + const avifBool supports400 = rav1eSupports400(); + RaPixelRange rav1eRange; + if (alpha) { + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. + rav1eRange = RA_PIXEL_RANGE_FULL; + + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The mono_chrome field in the Sequence Header OBU shall be set to 1. + // Some encoders do not support 4:0:0 and encode alpha as 4:2:0 so it is not always respected. + codec->internal->chromaSampling = supports400 ? RA_CHROMA_SAMPLING_CS400 : RA_CHROMA_SAMPLING_CS420; + codec->internal->yShift = 1; + + // CICP (CP/TC/MC) does not apply to the alpha auxiliary image. + // Use Unspecified (2) colour primaries, transfer characteristics, and matrix coefficients below. + } else { + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. + rav1eRange = (image->yuvRange == AVIF_RANGE_FULL) ? RA_PIXEL_RANGE_FULL : RA_PIXEL_RANGE_LIMITED; + + // AV1-AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. + codec->internal->yShift = 0; + switch (image->yuvFormat) { + case AVIF_PIXEL_FORMAT_YUV444: + codec->internal->chromaSampling = RA_CHROMA_SAMPLING_CS444; + break; + case AVIF_PIXEL_FORMAT_YUV422: + codec->internal->chromaSampling = RA_CHROMA_SAMPLING_CS422; + break; + case AVIF_PIXEL_FORMAT_YUV420: + codec->internal->chromaSampling = RA_CHROMA_SAMPLING_CS420; + codec->internal->yShift = 1; + break; + case AVIF_PIXEL_FORMAT_YUV400: + codec->internal->chromaSampling = supports400 ? RA_CHROMA_SAMPLING_CS400 : RA_CHROMA_SAMPLING_CS420; + codec->internal->yShift = 1; + break; + case AVIF_PIXEL_FORMAT_NONE: + case AVIF_PIXEL_FORMAT_COUNT: + default: + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + + rav1eConfig = rav1e_config_default(); + if (rav1e_config_set_pixel_format(rav1eConfig, + (uint8_t)image->depth, + codec->internal->chromaSampling, + (RaChromaSamplePosition)image->yuvChromaSamplePosition, + rav1eRange) < 0) { + goto cleanup; + } + + if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { + if (rav1e_config_parse(rav1eConfig, "still_picture", "true") == -1) { + goto cleanup; + } + } + if (rav1e_config_parse_int(rav1eConfig, "width", image->width) == -1) { + goto cleanup; + } + if (rav1e_config_parse_int(rav1eConfig, "height", image->height) == -1) { + goto cleanup; + } + if (rav1e_config_parse_int(rav1eConfig, "threads", encoder->maxThreads) == -1) { + goto cleanup; + } + + int minQuantizer = AVIF_CLAMP(encoder->minQuantizer, 0, 63); + if (alpha) { + minQuantizer = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 63); + } + minQuantizer = (minQuantizer * 255) / 63; // Rescale quantizer values as rav1e's QP range is [0,255] + const int quantizer = rav1eQualityToQuantizer(quality); + if (rav1e_config_parse_int(rav1eConfig, "min_quantizer", minQuantizer) == -1) { + goto cleanup; + } + if (rav1e_config_parse_int(rav1eConfig, "quantizer", quantizer) == -1) { + goto cleanup; + } + if (tileRowsLog2 != 0) { + if (rav1e_config_parse_int(rav1eConfig, "tile_rows", 1 << tileRowsLog2) == -1) { + goto cleanup; + } + } + if (tileColsLog2 != 0) { + if (rav1e_config_parse_int(rav1eConfig, "tile_cols", 1 << tileColsLog2) == -1) { + goto cleanup; + } + } + if (encoder->speed != AVIF_SPEED_DEFAULT) { + int speed = AVIF_CLAMP(encoder->speed, 0, 10); + if (rav1e_config_parse_int(rav1eConfig, "speed", speed) == -1) { + goto cleanup; + } + } + if (encoder->keyframeInterval > 0) { + // "key_frame_interval" is the maximum interval between two keyframes. + if (rav1e_config_parse_int(rav1eConfig, "key_frame_interval", encoder->keyframeInterval) == -1) { + goto cleanup; + } + } + for (uint32_t i = 0; i < codec->csOptions->count; ++i) { + const avifCodecSpecificOption * entry = &codec->csOptions->entries[i]; + if (rav1e_config_parse(rav1eConfig, entry->key, entry->value) < 0) { + avifDiagnosticsPrintf(codec->diag, "Invalid value for %s: %s.", entry->key, entry->value); + result = AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + goto cleanup; + } + } + + // AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. + // CICP values could be set to 2/2/2 (Unspecified) in the Sequence Header OBU for + // simplicity and to save 3 bytes, but some decoders ignore the colr box and rely + // on the OBU contents instead. See #2850. + rav1e_config_set_color_description(rav1eConfig, + (RaMatrixCoefficients)image->matrixCoefficients, + (RaColorPrimaries)image->colorPrimaries, + (RaTransferCharacteristics)image->transferCharacteristics); + + codec->internal->rav1eContext = rav1e_context_new(rav1eConfig); + if (!codec->internal->rav1eContext) { + goto cleanup; + } + } + + rav1eFrame = rav1e_frame_new(codec->internal->rav1eContext); + + int byteWidth = (image->depth > 8) ? 2 : 1; + if (alpha) { + rav1e_frame_fill_plane(rav1eFrame, 0, image->alphaPlane, (size_t)image->alphaRowBytes * image->height, image->alphaRowBytes, byteWidth); + } else { + rav1e_frame_fill_plane(rav1eFrame, 0, image->yuvPlanes[0], (size_t)image->yuvRowBytes[0] * image->height, image->yuvRowBytes[0], byteWidth); + if (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400) { + uint32_t uvHeight = (image->height + codec->internal->yShift) >> codec->internal->yShift; + rav1e_frame_fill_plane(rav1eFrame, 1, image->yuvPlanes[1], (size_t)image->yuvRowBytes[1] * uvHeight, image->yuvRowBytes[1], byteWidth); + rav1e_frame_fill_plane(rav1eFrame, 2, image->yuvPlanes[2], (size_t)image->yuvRowBytes[2] * uvHeight, image->yuvRowBytes[2], byteWidth); + } + } + + RaFrameTypeOverride frameType = RA_FRAME_TYPE_OVERRIDE_NO; + if (addImageFlags & AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME) { + frameType = RA_FRAME_TYPE_OVERRIDE_KEY; + } + rav1e_frame_set_type(rav1eFrame, frameType); + + RaEncoderStatus encoderStatus = rav1e_send_frame(codec->internal->rav1eContext, rav1eFrame); + if (encoderStatus != RA_ENCODER_STATUS_SUCCESS) { + goto cleanup; + } + + RaPacket * pkt = NULL; + for (;;) { + encoderStatus = rav1e_receive_packet(codec->internal->rav1eContext, &pkt); + if (encoderStatus == RA_ENCODER_STATUS_ENCODED) { + continue; + } + if ((encoderStatus != RA_ENCODER_STATUS_SUCCESS) && (encoderStatus != RA_ENCODER_STATUS_NEED_MORE_DATA)) { + goto cleanup; + } else if (pkt) { + if (pkt->data && (pkt->len > 0)) { + result = avifCodecEncodeOutputAddSample(output, pkt->data, pkt->len, (pkt->frame_type == RA_FRAME_TYPE_KEY)); + if (result != AVIF_RESULT_OK) { + goto cleanup; + } + } + rav1e_packet_unref(pkt); + pkt = NULL; + } else { + break; + } + } + result = AVIF_RESULT_OK; +cleanup: + if (rav1eFrame) { + rav1e_frame_unref(rav1eFrame); + rav1eFrame = NULL; + } + if (rav1eConfig) { + rav1e_config_unref(rav1eConfig); + rav1eConfig = NULL; + } + return result; +} + +static avifBool rav1eCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output) +{ + for (;;) { + RaEncoderStatus encoderStatus = rav1e_send_frame(codec->internal->rav1eContext, NULL); // flush + if (encoderStatus != RA_ENCODER_STATUS_SUCCESS) { + return AVIF_FALSE; + } + + avifBool gotPacket = AVIF_FALSE; + RaPacket * pkt = NULL; + for (;;) { + encoderStatus = rav1e_receive_packet(codec->internal->rav1eContext, &pkt); + if (encoderStatus == RA_ENCODER_STATUS_ENCODED) { + continue; + } + if ((encoderStatus != RA_ENCODER_STATUS_SUCCESS) && (encoderStatus != RA_ENCODER_STATUS_LIMIT_REACHED)) { + return AVIF_FALSE; + } + if (pkt) { + gotPacket = AVIF_TRUE; + if (pkt->data && (pkt->len > 0)) { + if (avifCodecEncodeOutputAddSample(output, pkt->data, pkt->len, (pkt->frame_type == RA_FRAME_TYPE_KEY)) != + AVIF_RESULT_OK) { + return AVIF_FALSE; + } + } + rav1e_packet_unref(pkt); + pkt = NULL; + } else { + break; + } + } + + if (!gotPacket) { + break; + } + } + return AVIF_TRUE; +} + +const char * avifCodecVersionRav1e(void) +{ + return rav1e_version_full(); +} + +avifCodec * avifCodecCreateRav1e(void) +{ + avifCodec * codec = (avifCodec *)avifCalloc(1, sizeof(avifCodec)); + if (codec == NULL) { + return NULL; + } + codec->encodeImage = rav1eCodecEncodeImage; + codec->encodeFinish = rav1eCodecEncodeFinish; + codec->destroyInternal = rav1eCodecDestroyInternal; + + codec->internal = (struct avifCodecInternal *)avifCalloc(1, sizeof(struct avifCodecInternal)); + if (codec->internal == NULL) { + avifFree(codec); + return NULL; + } + return codec; +} diff --git a/3rdparty/libavif/src/codec_svt.c b/3rdparty/libavif/src/codec_svt.c new file mode 100644 index 0000000..a76fb5b --- /dev/null +++ b/3rdparty/libavif/src/codec_svt.c @@ -0,0 +1,466 @@ +// Copyright 2020 Cloudinary. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include "svt-av1/EbSvtAv1.h" + +#include "svt-av1/EbSvtAv1Enc.h" + +#include +#include +#include + +// The SVT_AV1_VERSION_MAJOR, SVT_AV1_VERSION_MINOR, SVT_AV1_VERSION_PATCHLEVEL, and +// SVT_AV1_CHECK_VERSION macros were added in SVT-AV1 v0.9.0. Define these macros for older +// versions of SVT-AV1. +#ifndef SVT_AV1_VERSION_MAJOR +#define SVT_AV1_VERSION_MAJOR SVT_VERSION_MAJOR +#define SVT_AV1_VERSION_MINOR SVT_VERSION_MINOR +#define SVT_AV1_VERSION_PATCHLEVEL SVT_VERSION_PATCHLEVEL +// clang-format off +#define SVT_AV1_CHECK_VERSION(major, minor, patch) \ + (SVT_AV1_VERSION_MAJOR > (major) || \ + (SVT_AV1_VERSION_MAJOR == (major) && SVT_AV1_VERSION_MINOR > (minor)) || \ + (SVT_AV1_VERSION_MAJOR == (major) && SVT_AV1_VERSION_MINOR == (minor) && \ + SVT_AV1_VERSION_PATCHLEVEL >= (patch))) +// clang-format on +#endif + +#if !SVT_AV1_CHECK_VERSION(0, 9, 0) +#define STR_HELPER(x) #x +#define STR(x) STR_HELPER(x) +#define SVT_FULL_VERSION "v" STR(SVT_AV1_VERSION_MAJOR) "." STR(SVT_AV1_VERSION_MINOR) "." STR(SVT_AV1_VERSION_PATCHLEVEL) +#endif + +typedef struct avifCodecInternal +{ + /* SVT-AV1 Encoder Handle */ + EbComponentType * svt_encoder; + + EbSvtAv1EncConfiguration svt_config; +} avifCodecInternal; + +static avifBool allocate_svt_buffers(EbBufferHeaderType ** input_buf); +static avifResult dequeue_frame(avifCodec * codec, avifCodecEncodeOutput * output, avifBool done_sending_pics); + +static int svtQualityToQuantizer(int quality) +{ + const int quantizer = ((100 - quality) * 63 + 50) / 100; + + return quantizer; +} + +static avifResult svtCodecEncodeImage(avifCodec * codec, + avifEncoder * encoder, + const avifImage * image, + avifBool alpha, + int tileRowsLog2, + int tileColsLog2, + int quality, + avifEncoderChanges encoderChanges, + avifBool disableLaggedOutput, + uint32_t addImageFlags, + avifCodecEncodeOutput * output) +{ + // SVT-AV1 does not support changing encoder settings. + if (encoderChanges) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + // SVT-AV1 does not support changing image dimensions. + if (codec->internal->svt_encoder != NULL) { + if ((codec->internal->svt_config.source_width != image->width) || (codec->internal->svt_config.source_height != image->height)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + } + + // SVT-AV1 does not support encoding layered image. + if (encoder->extraLayerCount > 0) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + // SVT-AV1 does not support disabling lagged output. Ignore this setting. + (void)disableLaggedOutput; + + avifResult result = AVIF_RESULT_UNKNOWN_ERROR; + EbColorFormat color_format = EB_YUV420; + uint8_t * uvPlanes = NULL; // 4:2:0 U and V placeholder for alpha because SVT-AV1 does not support 4:0:0. + EbBufferHeaderType * input_buffer = NULL; + EbErrorType res = EB_ErrorNone; + + int y_shift = 0; + EbColorRange svt_range; + if (alpha) { + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The color_range field in the Sequence Header OBU shall be set to 1. + svt_range = EB_CR_FULL_RANGE; + + // AV1-AVIF specification, Section 4 "Auxiliary Image Items and Sequences": + // The mono_chrome field in the Sequence Header OBU shall be set to 1. + // Some encoders do not support 4:0:0 and encode alpha as 4:2:0 so it is not always respected. + y_shift = 1; + + // CICP (CP/TC/MC) does not apply to the alpha auxiliary image. + // Use Unspecified (2) colour primaries, transfer characteristics, and matrix coefficients below. + } else { + // AV1-ISOBMFF specification, Section 2.3.4: + // The value of full_range_flag in the 'colr' box SHALL match the color_range + // flag in the Sequence Header OBU. + svt_range = (image->yuvRange == AVIF_RANGE_FULL) ? EB_CR_FULL_RANGE : EB_CR_STUDIO_RANGE; + + // AV1-AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. + switch (image->yuvFormat) { + case AVIF_PIXEL_FORMAT_YUV444: + color_format = EB_YUV444; + break; + case AVIF_PIXEL_FORMAT_YUV422: + color_format = EB_YUV422; + break; + case AVIF_PIXEL_FORMAT_YUV420: + color_format = EB_YUV420; + y_shift = 1; + break; + case AVIF_PIXEL_FORMAT_YUV400: + // Setting color_format = EB_YUV400; results in "Svt[error]: Instance 1: Only support 420 now". + case AVIF_PIXEL_FORMAT_NONE: + case AVIF_PIXEL_FORMAT_COUNT: + default: + return AVIF_RESULT_UNKNOWN_ERROR; + } + } + + if (codec->internal->svt_encoder == NULL) { + EbSvtAv1EncConfiguration * svt_config = &codec->internal->svt_config; + // Zero-initialize svt_config because svt_av1_enc_init_handle() does not set many fields of svt_config. + // See https://gitlab.com/AOMediaCodec/SVT-AV1/-/issues/1697. + memset(svt_config, 0, sizeof(EbSvtAv1EncConfiguration)); + +#if SVT_AV1_CHECK_VERSION(3, 0, 0) + res = svt_av1_enc_init_handle(&codec->internal->svt_encoder, svt_config); +#else + res = svt_av1_enc_init_handle(&codec->internal->svt_encoder, NULL, svt_config); +#endif + if (res != EB_ErrorNone) { + goto cleanup; + } + svt_config->encoder_color_format = color_format; + svt_config->encoder_bit_depth = (uint8_t)image->depth; + + // AVIF specification, Section 2.2.1. "AV1 Item Configuration Property": + // The values of the fields in the AV1CodecConfigurationBox shall match those + // of the Sequence Header OBU in the AV1 Image Item Data. + // CICP values could be set to 2/2/2 (Unspecified) in the Sequence Header OBU for + // simplicity and to save 3 bytes, but some decoders ignore the colr box and rely + // on the OBU contents instead. See #2850. + svt_config->color_primaries = (EbColorPrimaries)image->colorPrimaries; + svt_config->transfer_characteristics = (EbTransferCharacteristics)image->transferCharacteristics; + svt_config->matrix_coefficients = (EbMatrixCoefficients)image->matrixCoefficients; + + svt_config->color_range = svt_range; +#if !SVT_AV1_CHECK_VERSION(0, 9, 0) + svt_config->is_16bit_pipeline = image->depth > 8; +#endif + svt_config->source_width = image->width; + svt_config->source_height = image->height; +#if SVT_AV1_CHECK_VERSION(3, 0, 0) + svt_config->level_of_parallelism = encoder->maxThreads; +#else + svt_config->logical_processors = encoder->maxThreads; +#endif +#if SVT_AV1_CHECK_VERSION(4, 0, 0) + svt_config->aq_mode = 2; +#else + svt_config->enable_adaptive_quantization = 2; +#endif + // disable 2-pass +#if SVT_AV1_CHECK_VERSION(0, 9, 0) + svt_config->rc_stats_buffer = (SvtAv1FixedBuf) { NULL, 0 }; +#else + svt_config->rc_firstpass_stats_out = AVIF_FALSE; + svt_config->rc_twopass_stats_in = (SvtAv1FixedBuf) { NULL, 0 }; +#endif + + svt_config->rate_control_mode = 0; // CRF because enable_adaptive_quantization is 2 + if (alpha) { + svt_config->min_qp_allowed = AVIF_CLAMP(encoder->minQuantizerAlpha, 0, 62); + svt_config->max_qp_allowed = AVIF_CLAMP(encoder->maxQuantizerAlpha, 0, 63); + } else { + svt_config->min_qp_allowed = AVIF_CLAMP(encoder->minQuantizer, 0, 62); + svt_config->max_qp_allowed = AVIF_CLAMP(encoder->maxQuantizer, 0, 63); + } + svt_config->qp = svtQualityToQuantizer(quality); + + if (tileRowsLog2 != 0) { + svt_config->tile_rows = tileRowsLog2; + } + if (tileColsLog2 != 0) { + svt_config->tile_columns = tileColsLog2; + } + if (encoder->speed != AVIF_SPEED_DEFAULT) { +#if SVT_AV1_CHECK_VERSION(0, 9, 0) + svt_config->enc_mode = (int8_t)encoder->speed; +#else + int speed = AVIF_CLAMP(encoder->speed, 0, 8); + svt_config->enc_mode = (int8_t)speed; +#endif + } + + if (color_format == EB_YUV422 || image->depth > 10) { + svt_config->profile = PROFESSIONAL_PROFILE; + } else if (color_format == EB_YUV444) { + svt_config->profile = HIGH_PROFILE; + } + + // In order for SVT-AV1 to force keyframes by setting pic_type to + // EB_AV1_KEY_PICTURE on any frame, force_key_frames has to be set. + svt_config->force_key_frames = true; + + // keyframeInterval == 1 case is handled when encoding each frame by + // setting pic_type to EB_AV1_KEY_PICTURE. For keyframeInterval > 1, + // set the intra_period_length. Even though setting intra_period_length + // to 0 should work in this case, it does not. + if (encoder->keyframeInterval > 1) { + svt_config->intra_period_length = encoder->keyframeInterval - 1; + } + +#if SVT_AV1_CHECK_VERSION(0, 9, 1) + for (uint32_t i = 0; i < codec->csOptions->count; ++i) { + const avifCodecSpecificOption * entry = &codec->csOptions->entries[i]; + if (svt_av1_enc_parse_parameter(svt_config, entry->key, entry->value) < 0) { + avifDiagnosticsPrintf(codec->diag, "Invalid value for %s: %s.", entry->key, entry->value); + result = AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + goto cleanup; + } + } +#else + if (codec->csOptions->count > 0) { + avifDiagnosticsPrintf(codec->diag, "SVT-AV1 does not support setting options"); + result = AVIF_RESULT_INVALID_CODEC_SPECIFIC_OPTION; + goto cleanup; + } +#endif + +#if SVT_AV1_CHECK_VERSION(3, 0, 0) + svt_config->lossless = quality == AVIF_QUALITY_LOSSLESS; +#endif + +#if SVT_AV1_CHECK_VERSION(4, 0, 0) + // Although the `avif` option was added in v3.0.0, it had a serious bug that was not fixed + // until v4.0.0. See https://gitlab.com/AOMediaCodec/SVT-AV1/-/issues/2245. + svt_config->avif = (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) != 0; +#endif + + res = svt_av1_enc_set_parameter(codec->internal->svt_encoder, svt_config); + if (res == EB_ErrorBadParameter) { + goto cleanup; + } + + res = svt_av1_enc_init(codec->internal->svt_encoder); + if (res != EB_ErrorNone) { + goto cleanup; + } + } + + if (!allocate_svt_buffers(&input_buffer)) { + goto cleanup; + } + EbSvtIOFormat * input_picture_buffer = (EbSvtIOFormat *)input_buffer->p_buffer; + + const uint32_t bytesPerPixel = image->depth > 8 ? 2 : 1; + const uint32_t uvHeight = (image->height + y_shift) >> y_shift; + if (alpha) { + input_picture_buffer->y_stride = image->alphaRowBytes / bytesPerPixel; + input_picture_buffer->luma = image->alphaPlane; + const size_t alphaSize = (size_t)image->alphaRowBytes * image->height; + if (alphaSize > UINT32_MAX) { + goto cleanup; + } + input_buffer->n_filled_len = (uint32_t)alphaSize; + +#if SVT_AV1_CHECK_VERSION(1, 8, 0) + // Simulate 4:2:0 UV planes. SVT-AV1 does not support 4:0:0 samples. + const uint32_t uvWidth = (image->width + y_shift) >> y_shift; + const uint32_t uvRowBytes = uvWidth * bytesPerPixel; + const size_t uvSize = (size_t)uvRowBytes * uvHeight; + if (uvSize > UINT32_MAX / 2) { + goto cleanup; + } + if (uvSize * 2 > UINT32_MAX - input_buffer->n_filled_len) { + goto cleanup; + } + uvPlanes = avifCalloc(uvSize, sizeof(uint8_t)); + if (uvPlanes == NULL) { + goto cleanup; + } + input_picture_buffer->cb = uvPlanes; + input_buffer->n_filled_len += (uint32_t)uvSize; + input_picture_buffer->cr = uvPlanes; + input_buffer->n_filled_len += (uint32_t)uvSize; + input_picture_buffer->cb_stride = uvWidth; + input_picture_buffer->cr_stride = uvWidth; +#else + // This workaround was not needed before SVT-AV1 1.8.0. + // See https://github.com/AOMediaCodec/libavif/issues/1992. + (void)uvPlanes; +#endif + } else { + input_picture_buffer->y_stride = image->yuvRowBytes[0] / bytesPerPixel; + input_picture_buffer->luma = image->yuvPlanes[0]; + const size_t ySize = (size_t)image->yuvRowBytes[0] * image->height; + if (ySize > UINT32_MAX) { + goto cleanup; + } + input_buffer->n_filled_len = (uint32_t)ySize; + input_picture_buffer->cb = image->yuvPlanes[1]; + const size_t uSize = (size_t)image->yuvRowBytes[1] * uvHeight; + if (uSize > UINT32_MAX - input_buffer->n_filled_len) { + goto cleanup; + } + input_buffer->n_filled_len += (uint32_t)uSize; + input_picture_buffer->cr = image->yuvPlanes[2]; + const size_t vSize = (size_t)image->yuvRowBytes[2] * uvHeight; + if (vSize > UINT32_MAX - input_buffer->n_filled_len) { + goto cleanup; + } + input_buffer->n_filled_len += (uint32_t)vSize; + input_picture_buffer->cb_stride = image->yuvRowBytes[1] / bytesPerPixel; + input_picture_buffer->cr_stride = image->yuvRowBytes[2] / bytesPerPixel; + } + + input_buffer->flags = 0; + input_buffer->pts = 0; + + EbAv1PictureType frame_type = EB_AV1_INVALID_PICTURE; + if ((addImageFlags & AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME) || (encoder->keyframeInterval == 1)) { + frame_type = EB_AV1_KEY_PICTURE; + } + input_buffer->pic_type = frame_type; + + res = svt_av1_enc_send_picture(codec->internal->svt_encoder, input_buffer); + if (res != EB_ErrorNone) { + goto cleanup; + } + + result = dequeue_frame(codec, output, AVIF_FALSE); +cleanup: + if (uvPlanes) { + avifFree(uvPlanes); + } + if (input_buffer) { + if (input_buffer->p_buffer) { + avifFree(input_buffer->p_buffer); + } + avifFree(input_buffer); + } + return result; +} + +static avifBool svtCodecEncodeFinish(avifCodec * codec, avifCodecEncodeOutput * output) +{ + EbErrorType ret = EB_ErrorNone; + + EbBufferHeaderType input_buffer; + input_buffer.n_alloc_len = 0; + input_buffer.n_filled_len = 0; + input_buffer.n_tick_count = 0; + input_buffer.p_app_private = NULL; + input_buffer.flags = EB_BUFFERFLAG_EOS; + input_buffer.p_buffer = NULL; + input_buffer.metadata = NULL; + + // flush + ret = svt_av1_enc_send_picture(codec->internal->svt_encoder, &input_buffer); + + if (ret != EB_ErrorNone) + return AVIF_FALSE; + + return (dequeue_frame(codec, output, AVIF_TRUE) == AVIF_RESULT_OK); +} + +const char * avifCodecVersionSvt(void) +{ +#if SVT_AV1_CHECK_VERSION(0, 9, 0) + return svt_av1_get_version(); +#else + return SVT_FULL_VERSION; +#endif +} + +static void svtCodecDestroyInternal(avifCodec * codec) +{ + if (codec->internal->svt_encoder) { + svt_av1_enc_deinit(codec->internal->svt_encoder); + svt_av1_enc_deinit_handle(codec->internal->svt_encoder); + codec->internal->svt_encoder = NULL; + } + avifFree(codec->internal); +} + +avifCodec * avifCodecCreateSvt(void) +{ + avifCodec * codec = (avifCodec *)avifCalloc(1, sizeof(avifCodec)); + if (codec == NULL) { + return NULL; + } + codec->encodeImage = svtCodecEncodeImage; + codec->encodeFinish = svtCodecEncodeFinish; + codec->destroyInternal = svtCodecDestroyInternal; + + codec->internal = (struct avifCodecInternal *)avifCalloc(1, sizeof(avifCodecInternal)); + if (codec->internal == NULL) { + avifFree(codec); + return NULL; + } + return codec; +} + +static avifBool allocate_svt_buffers(EbBufferHeaderType ** input_buf) +{ + *input_buf = avifAlloc(sizeof(EbBufferHeaderType)); + if (!(*input_buf)) { + return AVIF_FALSE; + } + (*input_buf)->p_buffer = avifCalloc(1, sizeof(EbSvtIOFormat)); + if (!(*input_buf)->p_buffer) { + return AVIF_FALSE; + } + (*input_buf)->size = sizeof(EbBufferHeaderType); + (*input_buf)->p_app_private = NULL; + (*input_buf)->pic_type = EB_AV1_INVALID_PICTURE; + (*input_buf)->metadata = NULL; + + return AVIF_TRUE; +} + +static avifResult dequeue_frame(avifCodec * codec, avifCodecEncodeOutput * output, avifBool done_sending_pics) +{ + EbErrorType res; + int encode_at_eos = 0; + + do { + EbBufferHeaderType * output_buf = NULL; + + res = svt_av1_enc_get_packet(codec->internal->svt_encoder, &output_buf, (uint8_t)done_sending_pics); + if (output_buf != NULL) { + encode_at_eos = ((output_buf->flags & EB_BUFFERFLAG_EOS) == EB_BUFFERFLAG_EOS); + if (output_buf->p_buffer && (output_buf->n_filled_len > 0)) { + const avifResult result = avifCodecEncodeOutputAddSample(output, + output_buf->p_buffer, + output_buf->n_filled_len, + (output_buf->pic_type == EB_AV1_KEY_PICTURE)); + if (result != AVIF_RESULT_OK) { + svt_av1_enc_release_out_buffer(&output_buf); + return result; + } + } + svt_av1_enc_release_out_buffer(&output_buf); + } + output_buf = NULL; + } while (res == EB_ErrorNone && !encode_at_eos); + if (!done_sending_pics && ((res == EB_ErrorNone) || (res == EB_NoErrorEmptyQueue))) + return AVIF_RESULT_OK; + return (res == EB_ErrorNone ? AVIF_RESULT_OK : AVIF_RESULT_UNKNOWN_ERROR); +} diff --git a/3rdparty/libavif/src/colr.c b/3rdparty/libavif/src/colr.c new file mode 100644 index 0000000..1b92dcd --- /dev/null +++ b/3rdparty/libavif/src/colr.c @@ -0,0 +1,542 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include +#include + +struct avifColorPrimariesTable +{ + avifColorPrimaries colorPrimariesEnum; + const char * name; + float primaries[8]; // rX, rY, gX, gY, bX, bY, wX, wY +}; +static const struct avifColorPrimariesTable avifColorPrimariesTables[] = { + { AVIF_COLOR_PRIMARIES_BT709, "BT.709", { 0.64f, 0.33f, 0.3f, 0.6f, 0.15f, 0.06f, 0.3127f, 0.329f } }, + { AVIF_COLOR_PRIMARIES_BT470M, "BT.470-6 System M", { 0.67f, 0.33f, 0.21f, 0.71f, 0.14f, 0.08f, 0.310f, 0.316f } }, + { AVIF_COLOR_PRIMARIES_BT470BG, "BT.470-6 System BG", { 0.64f, 0.33f, 0.29f, 0.60f, 0.15f, 0.06f, 0.3127f, 0.3290f } }, + { AVIF_COLOR_PRIMARIES_BT601, "BT.601", { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f } }, + { AVIF_COLOR_PRIMARIES_SMPTE240, "SMPTE 240M", { 0.630f, 0.340f, 0.310f, 0.595f, 0.155f, 0.070f, 0.3127f, 0.3290f } }, + { AVIF_COLOR_PRIMARIES_GENERIC_FILM, "Generic film", { 0.681f, 0.319f, 0.243f, 0.692f, 0.145f, 0.049f, 0.310f, 0.316f } }, + { AVIF_COLOR_PRIMARIES_BT2020, "BT.2020", { 0.708f, 0.292f, 0.170f, 0.797f, 0.131f, 0.046f, 0.3127f, 0.3290f } }, + { AVIF_COLOR_PRIMARIES_XYZ, "XYZ", { 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.3333f, 0.3333f } }, + { AVIF_COLOR_PRIMARIES_SMPTE431, "SMPTE RP 431-2", { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.314f, 0.351f } }, + { AVIF_COLOR_PRIMARIES_SMPTE432, "SMPTE EG 432-1 (DCI P3)", { 0.680f, 0.320f, 0.265f, 0.690f, 0.150f, 0.060f, 0.3127f, 0.3290f } }, + { AVIF_COLOR_PRIMARIES_EBU3213, "EBU Tech. 3213-E", { 0.630f, 0.340f, 0.295f, 0.605f, 0.155f, 0.077f, 0.3127f, 0.3290f } } +}; +static const int avifColorPrimariesTableSize = sizeof(avifColorPrimariesTables) / sizeof(avifColorPrimariesTables[0]); + +void avifColorPrimariesGetValues(avifColorPrimaries acp, float outPrimaries[8]) +{ + for (int i = 0; i < avifColorPrimariesTableSize; ++i) { + if (avifColorPrimariesTables[i].colorPrimariesEnum == acp) { + memcpy(outPrimaries, avifColorPrimariesTables[i].primaries, sizeof(avifColorPrimariesTables[i].primaries)); + return; + } + } + + // if we get here, the color primaries are unknown. Just return a reasonable default. + memcpy(outPrimaries, avifColorPrimariesTables[0].primaries, sizeof(avifColorPrimariesTables[0].primaries)); +} + +static avifBool matchesTo3RoundedPlaces(float a, float b) +{ + return (fabsf(a - b) < 0.001f); +} + +static avifBool primariesMatch(const float p1[8], const float p2[8]) +{ + return matchesTo3RoundedPlaces(p1[0], p2[0]) && matchesTo3RoundedPlaces(p1[1], p2[1]) && + matchesTo3RoundedPlaces(p1[2], p2[2]) && matchesTo3RoundedPlaces(p1[3], p2[3]) && matchesTo3RoundedPlaces(p1[4], p2[4]) && + matchesTo3RoundedPlaces(p1[5], p2[5]) && matchesTo3RoundedPlaces(p1[6], p2[6]) && matchesTo3RoundedPlaces(p1[7], p2[7]); +} + +avifColorPrimaries avifColorPrimariesFind(const float inPrimaries[8], const char ** outName) +{ + if (outName) { + *outName = NULL; + } + + for (int i = 0; i < avifColorPrimariesTableSize; ++i) { + if (primariesMatch(inPrimaries, avifColorPrimariesTables[i].primaries)) { + if (outName) { + *outName = avifColorPrimariesTables[i].name; + } + return avifColorPrimariesTables[i].colorPrimariesEnum; + } + } + return AVIF_COLOR_PRIMARIES_UNKNOWN; +} + +avifResult avifTransferCharacteristicsGetGamma(avifTransferCharacteristics atc, float * gamma) +{ + if (gamma == NULL) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + + switch (atc) { + case AVIF_TRANSFER_CHARACTERISTICS_BT470M: + *gamma = 2.2f; + return AVIF_RESULT_OK; + + case AVIF_TRANSFER_CHARACTERISTICS_BT470BG: + *gamma = 2.8f; + return AVIF_RESULT_OK; + + case AVIF_TRANSFER_CHARACTERISTICS_LINEAR: + *gamma = 1.0f; + return AVIF_RESULT_OK; + + default: + // Not representable as a single gamma value (e.g. sRGB, BT.709, PQ, HLG, etc.). + return AVIF_RESULT_NOT_IMPLEMENTED; + } +} + +avifTransferCharacteristics avifTransferCharacteristicsFindByGamma(float gamma) +{ + if (matchesTo3RoundedPlaces(gamma, 2.2f)) { + return AVIF_TRANSFER_CHARACTERISTICS_BT470M; + } else if (matchesTo3RoundedPlaces(gamma, 1.0f)) { + return AVIF_TRANSFER_CHARACTERISTICS_LINEAR; + } else if (matchesTo3RoundedPlaces(gamma, 2.8f)) { + return AVIF_TRANSFER_CHARACTERISTICS_BT470BG; + } + + return AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN; +} + +struct avifMatrixCoefficientsTable +{ + avifMatrixCoefficients matrixCoefficientsEnum; + const char * name; + const float kr; + const float kb; +}; + +// https://www.itu.int/rec/T-REC-H.273-201612-S +static const struct avifMatrixCoefficientsTable matrixCoefficientsTables[] = { + //{ AVIF_MATRIX_COEFFICIENTS_IDENTITY, "Identity", 0.0f, 0.0f, }, // Handled elsewhere + { AVIF_MATRIX_COEFFICIENTS_BT709, "BT.709", 0.2126f, 0.0722f }, + { AVIF_MATRIX_COEFFICIENTS_FCC, "FCC USFC 73.682", 0.30f, 0.11f }, + { AVIF_MATRIX_COEFFICIENTS_BT470BG, "BT.470-6 System BG", 0.299f, 0.114f }, + { AVIF_MATRIX_COEFFICIENTS_BT601, "BT.601", 0.299f, 0.114f }, + { AVIF_MATRIX_COEFFICIENTS_SMPTE240, "SMPTE ST 240", 0.212f, 0.087f }, + //{ AVIF_MATRIX_COEFFICIENTS_YCGCO, "YCgCo", 0.0f, 0.0f, }, // Handled elsewhere + { AVIF_MATRIX_COEFFICIENTS_BT2020_NCL, "BT.2020 (non-constant luminance)", 0.2627f, 0.0593f }, + //{ AVIF_MATRIX_COEFFICIENTS_BT2020_CL, "BT.2020 (constant luminance)", 0.2627f, 0.0593f }, // FIXME: It is not an linear transformation. + //{ AVIF_MATRIX_COEFFICIENTS_SMPTE2085, "ST 2085", 0.0f, 0.0f }, // FIXME: ST2085 can't represent using Kr and Kb. + //{ AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL, "Chromaticity-derived constant luminance system", 0.0f, 0.0f } // FIXME: It is not an linear transformation. + //{ AVIF_MATRIX_COEFFICIENTS_ICTCP, "BT.2100-0 ICtCp", 0.0f, 0.0f }, // FIXME: This can't represent using Kr and Kb. +}; + +static const int avifMatrixCoefficientsTableSize = sizeof(matrixCoefficientsTables) / sizeof(matrixCoefficientsTables[0]); + +static avifBool calcYUVInfoFromCICP(const avifImage * image, float coeffs[3]) +{ + if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL) { + avifColorPrimariesComputeYCoeffs(image->colorPrimaries, coeffs); + return AVIF_TRUE; + } else { + for (int i = 0; i < avifMatrixCoefficientsTableSize; ++i) { + const struct avifMatrixCoefficientsTable * const table = &matrixCoefficientsTables[i]; + if (table->matrixCoefficientsEnum == image->matrixCoefficients) { + coeffs[0] = table->kr; + coeffs[2] = table->kb; + coeffs[1] = 1.0f - coeffs[0] - coeffs[2]; + return AVIF_TRUE; + } + } + } + return AVIF_FALSE; +} + +void avifCalcYUVCoefficients(const avifImage * image, float * outR, float * outG, float * outB) +{ + // (As of ISO/IEC 23000-22:2019 Amendment 2) + // MIAF Section 7.3.6.4 "Colour information property": + // + // If a coded image has no associated colour property, the default property is defined as having + // colour_type equal to 'nclx' with properties as follows: + // - colour_primaries equal to 1, + // - transfer_characteristics equal to 13, + // - matrix_coefficients equal to 5 or 6 (which are functionally identical), and + // - full_range_flag equal to 1. + // Only if the colour information property of the image matches these default values, the colour + // property may be omitted; all other images shall have an explicitly declared colour space via + // association with a property of this type. + // + // See here for the discussion: https://github.com/AOMediaCodec/av1-avif/issues/77#issuecomment-676526097 + + // matrix_coefficients of [5,6] == BT.601: + float kr = 0.299f; + float kb = 0.114f; + float kg = 1.0f - kr - kb; + + float coeffs[3]; + if (calcYUVInfoFromCICP(image, coeffs)) { + kr = coeffs[0]; + kg = coeffs[1]; + kb = coeffs[2]; + } + + *outR = kr; + *outG = kg; + *outB = kb; +} + +// --------------------------------------------------------------------------- +// Transfer characteristics +// +// Transfer characteristics are defined in ITU-T H.273 https://www.itu.int/rec/T-REC-H.273-201612-S/en +// with formulas for linear to gamma conversion in Table 3. +// This is based on tongyuantongyu's implementation in https://github.com/AOMediaCodec/libavif/pull/444 +// with some fixes/changes in the first commit: +// - Fixed 5 transfer curves where toLinear and toGamma functions were swapped (470M, 470BG, Log100, +// Log100Sqrt10 and SMPTE428) +// - 'avifToLinearLog100' and 'avifToLinearLog100Sqrt10' were modified to return the middle of the +// range of linear values that are gamma-encoded to 0.0 in order to reduce the max round trip error, +// based on vrabaud's change in +// https://chromium.googlesource.com/webm/libwebp/+/25d94f473b10882b8bee9288d00539001b692042 +// - In this file, PQ and HLG return "extended SDR" linear values in [0.0, 10000/203] and +// [0.0, 1000/203] respectively, where a value of 1.0 means SDR white brightness (203 nits), and any +// value above 1.0 is brighter. +// See git history for further changes. + +struct avifTransferCharacteristicsTable +{ + avifTransferCharacteristics transferCharacteristicsEnum; + const char * name; + avifTransferFunction toLinear; + avifTransferFunction toGamma; +}; + +static float avifToLinear709(float gamma) +{ + if (gamma < 0.0f) { + return 0.0f; + } else if (gamma < 4.5f * 0.018053968510807f) { + return gamma / 4.5f; + } else if (gamma < 1.0f) { + return powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.0f / 0.45f); + } else { + return 1.0f; + } +} + +static float avifToGamma709(float linear) +{ + if (linear < 0.0f) { + return 0.0f; + } else if (linear < 0.018053968510807f) { + return linear * 4.5f; + } else if (linear < 1.0f) { + return 1.09929682680944f * powf(linear, 0.45f) - 0.09929682680944f; + } else { + return 1.0f; + } +} + +static float avifToLinear470M(float gamma) +{ + return powf(AVIF_CLAMP(gamma, 0.0f, 1.0f), 2.2f); +} + +static float avifToGamma470M(float linear) +{ + return powf(AVIF_CLAMP(linear, 0.0f, 1.0f), 1.0f / 2.2f); +} + +static float avifToLinear470BG(float gamma) +{ + return powf(AVIF_CLAMP(gamma, 0.0f, 1.0f), 2.8f); +} + +static float avifToGamma470BG(float linear) +{ + return powf(AVIF_CLAMP(linear, 0.0f, 1.0f), 1.0f / 2.8f); +} + +static float avifToLinearSMPTE240(float gamma) +{ + if (gamma < 0.0f) { + return 0.0f; + } else if (gamma < 4.0f * 0.022821585529445f) { + return gamma / 4.0f; + } else if (gamma < 1.0f) { + return powf((gamma + 0.111572195921731f) / 1.111572195921731f, 1.0f / 0.45f); + } else { + return 1.0f; + } +} + +static float avifToGammaSMPTE240(float linear) +{ + if (linear < 0.0f) { + return 0.0f; + } else if (linear < 0.022821585529445f) { + return linear * 4.0f; + } else if (linear < 1.0f) { + return 1.111572195921731f * powf(linear, 0.45f) - 0.111572195921731f; + } else { + return 1.0f; + } +} + +static float avifToGammaLinear(float gamma) +{ + return AVIF_CLAMP(gamma, 0.0f, 1.0f); +} + +static float avifToLinearLog100(float gamma) +{ + // The function is non-bijective so choose the middle of [0, 0.01]. + const float mid_interval = 0.01f / 2.f; + return (gamma <= 0.0f) ? mid_interval : powf(10.0f, 2.f * (AVIF_MIN(gamma, 1.f) - 1.0f)); +} + +static float avifToGammaLog100(float linear) +{ + return linear <= 0.01f ? 0.0f : 1.0f + log10f(AVIF_MIN(linear, 1.0f)) / 2.0f; +} + +static float avifToLinearLog100Sqrt10(float gamma) +{ + // The function is non-bijective so choose the middle of [0, 0.00316227766f]. + const float mid_interval = 0.00316227766f / 2.f; + return (gamma <= 0.0f) ? mid_interval : powf(10.0f, 2.5f * (AVIF_MIN(gamma, 1.f) - 1.0f)); +} + +static float avifToGammaLog100Sqrt10(float linear) +{ + return linear <= 0.00316227766f ? 0.0f : 1.0f + log10f(AVIF_MIN(linear, 1.0f)) / 2.5f; +} + +static float avifToLinearIEC61966(float gamma) +{ + if (gamma < -4.5f * 0.018053968510807f) { + return -powf((gamma - 0.09929682680944f) / -1.09929682680944f, 1.0f / 0.45f); + } else if (gamma < 4.5f * 0.018053968510807f) { + return gamma / 4.5f; + } else { + return powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.0f / 0.45f); + } +} + +static float avifToGammaIEC61966(float linear) +{ + if (linear < -0.018053968510807f) { + return -1.09929682680944f * powf(-linear, 0.45f) + 0.09929682680944f; + } else if (linear < 0.018053968510807f) { + return linear * 4.5f; + } else { + return 1.09929682680944f * powf(linear, 0.45f) - 0.09929682680944f; + } +} + +static float avifToLinearBT1361(float gamma) +{ + if (gamma < -0.25f) { + return -0.25f; + } else if (gamma < 0.0f) { + return powf((gamma - 0.02482420670236f) / -0.27482420670236f, 1.0f / 0.45f) / -4.0f; + } else if (gamma < 4.5f * 0.018053968510807f) { + return gamma / 4.5f; + } else if (gamma < 1.0f) { + return powf((gamma + 0.09929682680944f) / 1.09929682680944f, 1.0f / 0.45f); + } else { + return 1.0f; + } +} + +static float avifToGammaBT1361(float linear) +{ + if (linear < -0.25f) { + return -0.25f; + } else if (linear < 0.0f) { + return -0.27482420670236f * powf(-4.0f * linear, 0.45f) + 0.02482420670236f; + } else if (linear < 0.018053968510807f) { + return linear * 4.5f; + } else if (linear < 1.0f) { + return 1.09929682680944f * powf(linear, 0.45f) - 0.09929682680944f; + } else { + return 1.0f; + } +} + +static float avifToLinearSRGB(float gamma) +{ + if (gamma < 0.0f) { + return 0.0f; + } else if (gamma < 12.92f * 0.0030412825601275209f) { + return gamma / 12.92f; + } else if (gamma < 1.0f) { + return powf((gamma + 0.0550107189475866f) / 1.0550107189475866f, 2.4f); + } else { + return 1.0f; + } +} + +static float avifToGammaSRGB(float linear) +{ + if (linear < 0.0f) { + return 0.0f; + } else if (linear < 0.0030412825601275209f) { + return linear * 12.92f; + } else if (linear < 1.0f) { + return 1.0550107189475866f * powf(linear, 1.0f / 2.4f) - 0.0550107189475866f; + } else { + return 1.0f; + } +} + +#define PQ_MAX_NITS 10000.0f +#define HLG_PEAK_LUMINANCE_NITS 1000.0f +#define SDR_WHITE_NITS 203.0f + +static float avifToLinearPQ(float gamma) +{ + if (gamma > 0.0f) { + const float powGamma = powf(gamma, 1.0f / 78.84375f); + const float num = AVIF_MAX(powGamma - 0.8359375f, 0.0f); + const float den = AVIF_MAX(18.8515625f - 18.6875f * powGamma, FLT_MIN); + const float linear = powf(num / den, 1.0f / 0.1593017578125f); + // Scale so that SDR white is 1.0 (extended SDR). + return linear * PQ_MAX_NITS / SDR_WHITE_NITS; + } else { + return 0.0f; + } +} + +static float avifToGammaPQ(float linear) +{ + if (linear > 0.0f) { + // Scale from extended SDR range to [0.0, 1.0]. + linear = AVIF_CLAMP(linear * SDR_WHITE_NITS / PQ_MAX_NITS, 0.0f, 1.0f); + const float powLinear = powf(linear, 0.1593017578125f); + const float num = 0.1640625f * powLinear - 0.1640625f; + const float den = 1.0f + 18.6875f * powLinear; + return powf(1.0f + num / den, 78.84375f); + } else { + return 0.0f; + } +} + +static float avifToLinearSMPTE428(float gamma) +{ + return powf(AVIF_MAX(gamma, 0.0f), 2.6f) / 0.91655527974030934f; +} + +static float avifToGammaSMPTE428(float linear) +{ + return powf(0.91655527974030934f * AVIF_MAX(linear, 0.0f), 1.0f / 2.6f); +} + +// Formula from ITU-R BT.2100-2 +// Assumes Lw=1000 (max display luminance in nits). +// For simplicity, approximates Ys (which should be 0.2627*r+0.6780*g+0.0593*b) +// to the input value (r, g, or b depending on the current channel). +static float avifToLinearHLG(float gamma) +{ + // Inverse OETF followed by the OOTF, see Table 5 in ITU-R BT.2100-2 page 7. + // Note that this differs slightly from ITU-T H.273 which doesn't use the OOTF. + if (gamma < 0.0f) { + return 0.0f; + } + float linear = 0.0f; + if (gamma <= 0.5f) { + linear = powf((gamma * gamma) * (1.0f / 3.0f), 1.2f); + } else { + linear = powf((expf((gamma - 0.55991073f) / 0.17883277f) + 0.28466892f) / 12.0f, 1.2f); + } + // Scale so that SDR white is 1.0 (extended SDR). + return linear * HLG_PEAK_LUMINANCE_NITS / SDR_WHITE_NITS; +} + +static float avifToGammaHLG(float linear) +{ + // Scale from extended SDR range to [0.0, 1.0]. + linear = AVIF_CLAMP(linear * SDR_WHITE_NITS / HLG_PEAK_LUMINANCE_NITS, 0.0f, 1.0f); + // Inverse OOTF followed by OETF see Table 5 and Note 5i in ITU-R BT.2100-2 page 7-8. + linear = powf(linear, 1.0f / 1.2f); + if (linear < 0.0f) { + return 0.0f; + } else if (linear <= (1.0f / 12.0f)) { + return sqrtf(3.0f * linear); + } else { + return 0.17883277f * logf(12.0f * linear - 0.28466892f) + 0.55991073f; + } +} + +static const struct avifTransferCharacteristicsTable transferCharacteristicsTables[] = { + { AVIF_TRANSFER_CHARACTERISTICS_BT709, "BT.709", avifToLinear709, avifToGamma709 }, + { AVIF_TRANSFER_CHARACTERISTICS_BT470M, "BT.470-6 System M", avifToLinear470M, avifToGamma470M }, + { AVIF_TRANSFER_CHARACTERISTICS_BT470BG, "BT.470-6 System BG", avifToLinear470BG, avifToGamma470BG }, + { AVIF_TRANSFER_CHARACTERISTICS_BT601, "BT.601", avifToLinear709, avifToGamma709 }, + { AVIF_TRANSFER_CHARACTERISTICS_SMPTE240, "SMPTE 240M", avifToLinearSMPTE240, avifToGammaSMPTE240 }, + { AVIF_TRANSFER_CHARACTERISTICS_LINEAR, "Linear", avifToGammaLinear, avifToGammaLinear }, + { AVIF_TRANSFER_CHARACTERISTICS_LOG100, "100:1 Log", avifToLinearLog100, avifToGammaLog100 }, + { AVIF_TRANSFER_CHARACTERISTICS_LOG100_SQRT10, "100sqrt(10):1 Log", avifToLinearLog100Sqrt10, avifToGammaLog100Sqrt10 }, + { AVIF_TRANSFER_CHARACTERISTICS_IEC61966, "IEC 61966-2-4", avifToLinearIEC61966, avifToGammaIEC61966 }, + { AVIF_TRANSFER_CHARACTERISTICS_BT1361, "BT.1361", avifToLinearBT1361, avifToGammaBT1361 }, + { AVIF_TRANSFER_CHARACTERISTICS_SRGB, "sRGB", avifToLinearSRGB, avifToGammaSRGB }, + { AVIF_TRANSFER_CHARACTERISTICS_BT2020_10BIT, "10bit BT.2020", avifToLinear709, avifToGamma709 }, + { AVIF_TRANSFER_CHARACTERISTICS_BT2020_12BIT, "12bit BT.2020", avifToLinear709, avifToGamma709 }, + { AVIF_TRANSFER_CHARACTERISTICS_SMPTE2084, "SMPTE ST 2084 (PQ)", avifToLinearPQ, avifToGammaPQ }, + { AVIF_TRANSFER_CHARACTERISTICS_SMPTE428, "SMPTE ST 428-1", avifToLinearSMPTE428, avifToGammaSMPTE428 }, + { AVIF_TRANSFER_CHARACTERISTICS_HLG, "ARIB STD-B67 (HLG)", avifToLinearHLG, avifToGammaHLG } +}; + +static const int avifTransferCharacteristicsTableSize = + sizeof(transferCharacteristicsTables) / sizeof(transferCharacteristicsTables[0]); + +avifTransferFunction avifTransferCharacteristicsGetGammaToLinearFunction(avifTransferCharacteristics atc) +{ + for (int i = 0; i < avifTransferCharacteristicsTableSize; ++i) { + const struct avifTransferCharacteristicsTable * const table = &transferCharacteristicsTables[i]; + if (table->transferCharacteristicsEnum == atc) { + return table->toLinear; + } + } + return avifToLinear709; // Provide a reasonable default. +} + +avifTransferFunction avifTransferCharacteristicsGetLinearToGammaFunction(avifTransferCharacteristics atc) +{ + for (int i = 0; i < avifTransferCharacteristicsTableSize; ++i) { + const struct avifTransferCharacteristicsTable * const table = &transferCharacteristicsTables[i]; + if (table->transferCharacteristicsEnum == atc) { + return table->toGamma; + } + } + return avifToGamma709; // Provide a reasonable default. +} + +void avifColorPrimariesComputeYCoeffs(avifColorPrimaries colorPrimaries, float coeffs[3]) +{ + float primaries[8]; + avifColorPrimariesGetValues(colorPrimaries, primaries); + float const rX = primaries[0]; + float const rY = primaries[1]; + float const gX = primaries[2]; + float const gY = primaries[3]; + float const bX = primaries[4]; + float const bY = primaries[5]; + float const wX = primaries[6]; + float const wY = primaries[7]; + float const rZ = 1.0f - (rX + rY); // (Eq. 34) + float const gZ = 1.0f - (gX + gY); // (Eq. 35) + float const bZ = 1.0f - (bX + bY); // (Eq. 36) + float const wZ = 1.0f - (wX + wY); // (Eq. 37) + float const kr = (rY * (wX * (gY * bZ - bY * gZ) + wY * (bX * gZ - gX * bZ) + wZ * (gX * bY - bX * gY))) / + (wY * (rX * (gY * bZ - bY * gZ) + gX * (bY * rZ - rY * bZ) + bX * (rY * gZ - gY * rZ))); + // (Eq. 32) + float const kb = (bY * (wX * (rY * gZ - gY * rZ) + wY * (gX * rZ - rX * gZ) + wZ * (rX * gY - gX * rY))) / + (wY * (rX * (gY * bZ - bY * gZ) + gX * (bY * rZ - rY * bZ) + bX * (rY * gZ - gY * rZ))); + // (Eq. 33) + coeffs[0] = kr; + coeffs[2] = kb; + coeffs[1] = 1.0f - coeffs[0] - coeffs[2]; +} diff --git a/3rdparty/libavif/src/colrconvert.c b/3rdparty/libavif/src/colrconvert.c new file mode 100644 index 0000000..a58f5e9 --- /dev/null +++ b/3rdparty/libavif/src/colrconvert.c @@ -0,0 +1,186 @@ +// Copyright 2023 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include + +static const double epsilon = 1e-12; + +static avifBool avifXyToXYZ(const float xy[2], double XYZ[3]) +{ + if (fabsf(xy[1]) < epsilon) { + return AVIF_FALSE; + } + + const double factor = 1.0 / xy[1]; + XYZ[0] = xy[0] * factor; + XYZ[1] = 1; + XYZ[2] = (1 - xy[0] - xy[1]) * factor; + + return AVIF_TRUE; +} + +// Computes I = M^-1. Returns false if M seems to be singular. +static avifBool avifMatInv(double M[3][3], double I[3][3]) +{ + double det = M[0][0] * (M[1][1] * M[2][2] - M[2][1] * M[1][2]) - M[0][1] * (M[1][0] * M[2][2] - M[1][2] * M[2][0]) + + M[0][2] * (M[1][0] * M[2][1] - M[1][1] * M[2][0]); + if (fabs(det) < epsilon) { + return AVIF_FALSE; + } + det = 1.0 / det; + + I[0][0] = (M[1][1] * M[2][2] - M[2][1] * M[1][2]) * det; + I[0][1] = (M[0][2] * M[2][1] - M[0][1] * M[2][2]) * det; + I[0][2] = (M[0][1] * M[1][2] - M[0][2] * M[1][1]) * det; + I[1][0] = (M[1][2] * M[2][0] - M[1][0] * M[2][2]) * det; + I[1][1] = (M[0][0] * M[2][2] - M[0][2] * M[2][0]) * det; + I[1][2] = (M[1][0] * M[0][2] - M[0][0] * M[1][2]) * det; + I[2][0] = (M[1][0] * M[2][1] - M[2][0] * M[1][1]) * det; + I[2][1] = (M[2][0] * M[0][1] - M[0][0] * M[2][1]) * det; + I[2][2] = (M[0][0] * M[1][1] - M[1][0] * M[0][1]) * det; + + return AVIF_TRUE; +} + +// Computes C = A*B +static void avifMatMul(double A[3][3], double B[3][3], double C[3][3]) +{ + C[0][0] = A[0][0] * B[0][0] + A[0][1] * B[1][0] + A[0][2] * B[2][0]; + C[0][1] = A[0][0] * B[0][1] + A[0][1] * B[1][1] + A[0][2] * B[2][1]; + C[0][2] = A[0][0] * B[0][2] + A[0][1] * B[1][2] + A[0][2] * B[2][2]; + C[1][0] = A[1][0] * B[0][0] + A[1][1] * B[1][0] + A[1][2] * B[2][0]; + C[1][1] = A[1][0] * B[0][1] + A[1][1] * B[1][1] + A[1][2] * B[2][1]; + C[1][2] = A[1][0] * B[0][2] + A[1][1] * B[1][2] + A[1][2] * B[2][2]; + C[2][0] = A[2][0] * B[0][0] + A[2][1] * B[1][0] + A[2][2] * B[2][0]; + C[2][1] = A[2][0] * B[0][1] + A[2][1] * B[1][1] + A[2][2] * B[2][1]; + C[2][2] = A[2][0] * B[0][2] + A[2][1] * B[1][2] + A[2][2] * B[2][2]; +} + +// Set M to have values of d on the leading diagonal, and zero elsewhere. +static void avifMatDiag(const double d[3], double M[3][3]) +{ + M[0][0] = d[0]; + M[0][1] = 0; + M[0][2] = 0; + M[1][0] = 0; + M[1][1] = d[1]; + M[1][2] = 0; + M[2][0] = 0; + M[2][1] = 0; + M[2][2] = d[2]; +} + +// Computes y = M.x +static void avifVecMul(double M[3][3], const double x[3], double y[3]) +{ + y[0] = M[0][0] * x[0] + M[0][1] * x[1] + M[0][2] * x[2]; + y[1] = M[1][0] * x[0] + M[1][1] * x[1] + M[1][2] * x[2]; + y[2] = M[2][0] * x[0] + M[2][1] * x[1] + M[2][2] * x[2]; +} + +// Bradford chromatic adaptation matrix +// from https://www.researchgate.net/publication/253799640_A_uniform_colour_space_based_upon_CIECAM97s +static double avifBradford[3][3] = { + { 0.8951, 0.2664, -0.1614 }, + { -0.7502, 1.7135, 0.0367 }, + { 0.0389, -0.0685, 1.0296 }, +}; + +// LMS values for D50 whitepoint +static const double avifLmsD50[3] = { 0.996284, 1.02043, 0.818644 }; + +avifBool avifColorPrimariesComputeRGBToXYZD50Matrix(avifColorPrimaries colorPrimaries, double coeffs[3][3]) +{ + float primaries[8]; + avifColorPrimariesGetValues(colorPrimaries, primaries); + + double whitePointXYZ[3]; + AVIF_CHECK(avifXyToXYZ(&primaries[6], whitePointXYZ)); + + double rgbPrimaries[3][3] = { + { primaries[0], primaries[2], primaries[4] }, + { primaries[1], primaries[3], primaries[5] }, + { 1.0 - primaries[0] - primaries[1], 1.0 - primaries[2] - primaries[3], 1.0 - primaries[4] - primaries[5] } + }; + + double rgbPrimariesInv[3][3]; + AVIF_CHECK(avifMatInv(rgbPrimaries, rgbPrimariesInv)); + + double rgbCoefficients[3]; + avifVecMul(rgbPrimariesInv, whitePointXYZ, rgbCoefficients); + + double rgbCoefficientsMat[3][3]; + avifMatDiag(rgbCoefficients, rgbCoefficientsMat); + + double rgbXYZ[3][3]; + avifMatMul(rgbPrimaries, rgbCoefficientsMat, rgbXYZ); + + // ICC stores primaries XYZ under PCS. + // Adapt using linear bradford transform + // from https://onlinelibrary.wiley.com/doi/pdf/10.1002/9781119021780.app3 + double lms[3]; + avifVecMul(avifBradford, whitePointXYZ, lms); + for (int i = 0; i < 3; ++i) { + if (fabs(lms[i]) < epsilon) { + return AVIF_FALSE; + } + lms[i] = avifLmsD50[i] / lms[i]; + } + + double adaptation[3][3]; + avifMatDiag(lms, adaptation); + + double tmp[3][3]; + avifMatMul(adaptation, avifBradford, tmp); + + double bradfordInv[3][3]; + if (!avifMatInv(avifBradford, bradfordInv)) { + return AVIF_FALSE; + } + avifMatMul(bradfordInv, tmp, adaptation); + + avifMatMul(adaptation, rgbXYZ, coeffs); + + return AVIF_TRUE; +} + +avifBool avifColorPrimariesComputeXYZD50ToRGBMatrix(avifColorPrimaries colorPrimaries, double coeffs[3][3]) +{ + double rgbToXyz[3][3]; + AVIF_CHECK(avifColorPrimariesComputeRGBToXYZD50Matrix(colorPrimaries, rgbToXyz)); + AVIF_CHECK(avifMatInv(rgbToXyz, coeffs)); + return AVIF_TRUE; +} + +avifBool avifColorPrimariesComputeRGBToRGBMatrix(avifColorPrimaries srcColorPrimaries, + avifColorPrimaries dstColorPrimaries, + double coeffs[3][3]) +{ + // Note: no special casing for srcColorPrimaries == dstColorPrimaries to allow + // testing that the computation actually produces the identity matrix. + double srcRGBToXYZ[3][3]; + AVIF_CHECK(avifColorPrimariesComputeRGBToXYZD50Matrix(srcColorPrimaries, srcRGBToXYZ)); + double xyzToDstRGB[3][3]; + AVIF_CHECK(avifColorPrimariesComputeXYZD50ToRGBMatrix(dstColorPrimaries, xyzToDstRGB)); + // coeffs = xyzToDstRGB * srcRGBToXYZ + // i.e. srcRGB -> XYZ -> dstRGB + avifMatMul(xyzToDstRGB, srcRGBToXYZ, coeffs); + return AVIF_TRUE; +} + +// Converts a linear RGBA pixel to a different color space. This function actually works for gamma encoded +// RGB as well but linear gives better results. Also, for gamma encoded values, it would be +// better to clamp the output to [0, 1]. Linear values don't need clamping because values +// > 1.0 are valid for HDR transfer curves, and the gamma compression function will do the +// clamping as necessary. +void avifLinearRGBConvertColorSpace(float rgb[4], double coeffs[3][3]) +{ + const double rgbDouble[3] = { rgb[0], rgb[1], rgb[2] }; + double converted[3]; + avifVecMul(coeffs, rgbDouble, converted); + rgb[0] = (float)converted[0]; + rgb[1] = (float)converted[1]; + rgb[2] = (float)converted[2]; +} diff --git a/3rdparty/libavif/src/compliance.cc b/3rdparty/libavif/src/compliance.cc new file mode 100644 index 0000000..28ab270 --- /dev/null +++ b/3rdparty/libavif/src/compliance.cc @@ -0,0 +1,54 @@ +// Copyright 2023 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include +#include +#include + +#include "avif/internal.h" + +// From ../ext/ComplianceWarden/src/utils/ +#include "box_reader_impl.h" +#include "spec.h" + +bool checkComplianceStd(Box const & file, SpecDesc const * spec); + +SpecDesc const * specFind(const char * name); +std::vector & g_allSpecs(); +extern const SpecDesc * const globalSpecAvif; +extern const SpecDesc * const globalSpecAv1Hdr10plus; +extern const SpecDesc * const globalSpecHeif; +extern const SpecDesc * const globalSpecIsobmff; +extern const SpecDesc * const globalSpecMiaf; + +extern "C" avifResult avifIsCompliant(const uint8_t * data, size_t size) +{ + // See compliance_warden.sh. + if (g_allSpecs().empty()) { + registerSpec(globalSpecAvif); + registerSpec(globalSpecAv1Hdr10plus); + registerSpec(globalSpecHeif); + registerSpec(globalSpecIsobmff); + registerSpec(globalSpecMiaf); + } + + // Inspired from ext/ComplianceWarden/src/app/cw.cpp + BoxReader topReader; + for (char sym : { 'f', 'i', 'l', 'e', '.', 'a', 'v', 'i', 'f' }) { + // Setting made-up file name (letter by letter). + topReader.myBox.syms.push_back({ "filename", static_cast(sym), 8 }); + } + AVIF_CHECKERR(size <= std::numeric_limits::max(), AVIF_RESULT_INVALID_ARGUMENT); + topReader.br = { const_cast(data), static_cast(size) }; + topReader.myBox.original = const_cast(data); + topReader.myBox.position = 0; + topReader.myBox.size = size; + topReader.myBox.fourcc = FOURCC("root"); + topReader.specs = { specFind("avif") }; + AVIF_CHECKERR(topReader.specs[0] != nullptr, AVIF_RESULT_UNKNOWN_ERROR); + auto parseFunc = getParseFunction(topReader.myBox.fourcc); + parseFunc(&topReader); + // gpac/ComplianceWarden will print the formatted result page to stdout, warnings and errors inclusive. + AVIF_CHECKERR(!checkComplianceStd(topReader.myBox, topReader.specs[0]), AVIF_RESULT_BMFF_PARSE_FAILED); + return AVIF_RESULT_OK; +} diff --git a/3rdparty/libavif/src/diag.c b/3rdparty/libavif/src/diag.c new file mode 100644 index 0000000..8537018 --- /dev/null +++ b/3rdparty/libavif/src/diag.c @@ -0,0 +1,34 @@ +// Copyright 2021 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include +#include + +void avifDiagnosticsClearError(avifDiagnostics * diag) +{ + *diag->error = '\0'; +} + +#ifdef __clang__ +__attribute__((__format__(__printf__, 2, 3))) +#endif +void avifDiagnosticsPrintf(avifDiagnostics * diag, const char * format, ...) +{ + if (!diag) { + // It is possible this is NULL (e.g. calls to avifPeekCompatibleFileType()) + return; + } + if (*diag->error) { + // There is already a detailed error set. + return; + } + + va_list args; + va_start(args, format); + vsnprintf(diag->error, AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE, format, args); + diag->error[AVIF_DIAGNOSTICS_ERROR_BUFFER_SIZE - 1] = '\0'; + va_end(args); +} diff --git a/3rdparty/libavif/src/exif.c b/3rdparty/libavif/src/exif.c new file mode 100644 index 0000000..dc89060 --- /dev/null +++ b/3rdparty/libavif/src/exif.c @@ -0,0 +1,200 @@ +// Copyright 2022 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include + +avifResult avifGetExifTiffHeaderOffset(const uint8_t * exif, size_t exifSize, size_t * offset) +{ + const uint8_t tiffHeaderBE[4] = { 'M', 'M', 0, 42 }; + const uint8_t tiffHeaderLE[4] = { 'I', 'I', 42, 0 }; + exifSize = AVIF_MIN(exifSize, UINT32_MAX); + for (*offset = 0; *offset + 4 < exifSize; ++*offset) { + if (!memcmp(&exif[*offset], tiffHeaderBE, 4) || !memcmp(&exif[*offset], tiffHeaderLE, 4)) { + return AVIF_RESULT_OK; + } + } + // Couldn't find the TIFF header + return AVIF_RESULT_INVALID_EXIF_PAYLOAD; +} + +// Returns the offset to the Exif 8-bit orientation value and AVIF_RESULT_OK, or an error. +// If the offset is set to exifSize, there was no parsing error but no orientation tag was found. +avifResult avifGetExifOrientationOffset(const uint8_t * exif, size_t exifSize, size_t * offset) +{ + const avifResult result = avifGetExifTiffHeaderOffset(exif, exifSize, offset); + if (result != AVIF_RESULT_OK) { + // Couldn't find the TIFF header + return result; + } + + avifROData raw = { exif + *offset, exifSize - *offset }; + const avifBool littleEndian = (raw.data[0] == 'I'); + avifROStream stream; + avifROStreamStart(&stream, &raw, NULL, NULL); + + // TIFF Header + uint32_t offsetTo0thIfd; + if (!avifROStreamSkip(&stream, 4) || // Skip tiffHeaderBE or tiffHeaderLE. + !avifROStreamReadU32Endianness(&stream, &offsetTo0thIfd, littleEndian)) { + return AVIF_RESULT_INVALID_EXIF_PAYLOAD; + } + + avifROStreamSetOffset(&stream, offsetTo0thIfd); + uint16_t fieldCount; + if (!avifROStreamReadU16Endianness(&stream, &fieldCount, littleEndian)) { + return AVIF_RESULT_INVALID_EXIF_PAYLOAD; + } + for (uint16_t field = 0; field < fieldCount; ++field) { // for each field interoperability array + uint16_t tag; + uint16_t type; + uint32_t count; + uint16_t firstHalfOfValueOffset; + if (!avifROStreamReadU16Endianness(&stream, &tag, littleEndian) || !avifROStreamReadU16Endianness(&stream, &type, littleEndian) || + !avifROStreamReadU32Endianness(&stream, &count, littleEndian) || + !avifROStreamReadU16Endianness(&stream, &firstHalfOfValueOffset, littleEndian) || !avifROStreamSkip(&stream, 2)) { + return AVIF_RESULT_INVALID_EXIF_PAYLOAD; + } + // Orientation attribute according to JEITA CP-3451C section 4.6.4 (TIFF Rev. 6.0 Attribute Information): + const uint16_t shortType = 0x03; + if (tag == 0x0112 && type == shortType && count == 0x01) { + // Only consider non-reserved orientation values, so that it is known that + // the most meaningful byte of firstHalfOfValueOffset is 0. + if (firstHalfOfValueOffset >= 1 && firstHalfOfValueOffset <= 8) { + // Offset to the least meaningful byte of firstHalfOfValueOffset. + *offset += avifROStreamOffset(&stream) - (littleEndian ? 4 : 3); + return AVIF_RESULT_OK; + } + } + } + // Orientation is in the 0th IFD, so no need to parse the following ones. + + *offset = exifSize; // Signal missing orientation tag in valid Exif payload. + return AVIF_RESULT_OK; +} + +avifResult avifImageExtractExifOrientationToIrotImir(avifImage * image) +{ + const avifTransformFlags otherFlags = image->transformFlags & ~(AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR); + size_t offset; + const avifResult result = avifGetExifOrientationOffset(image->exif.data, image->exif.size, &offset); + if (result != AVIF_RESULT_OK) { + return result; + } + if (offset < image->exif.size) { + const uint8_t orientation = image->exif.data[offset]; + // Mapping from Exif orientation as defined in JEITA CP-3451C section 4.6.4.A Orientation + // to irot and imir boxes as defined in HEIF ISO/IEC 28002-12:2021 sections 6.5.10 and 6.5.12. + switch (orientation) { + case 1: // The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side. + image->transformFlags = otherFlags; + image->irot.angle = 0; // ignored + image->imir.axis = 0; // ignored + return AVIF_RESULT_OK; + case 2: // The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side. + image->transformFlags = otherFlags | AVIF_TRANSFORM_IMIR; + image->irot.angle = 0; // ignored + image->imir.axis = 1; + return AVIF_RESULT_OK; + case 3: // The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side. + image->transformFlags = otherFlags | AVIF_TRANSFORM_IROT; + image->irot.angle = 2; + image->imir.axis = 0; // ignored + return AVIF_RESULT_OK; + case 4: // The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side. + image->transformFlags = otherFlags | AVIF_TRANSFORM_IMIR; + image->irot.angle = 0; // ignored + image->imir.axis = 0; + return AVIF_RESULT_OK; + case 5: // The 0th row is the visual left-hand side of the image, and the 0th column is the visual top. + image->transformFlags = otherFlags | AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR; + image->irot.angle = 1; // applied before imir according to MIAF spec ISO/IEC 28002-12:2021 - section 7.3.6.7 + image->imir.axis = 0; + return AVIF_RESULT_OK; + case 6: // The 0th row is the visual right-hand side of the image, and the 0th column is the visual top. + image->transformFlags = otherFlags | AVIF_TRANSFORM_IROT; + image->irot.angle = 3; + image->imir.axis = 0; // ignored + return AVIF_RESULT_OK; + case 7: // The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom. + image->transformFlags = otherFlags | AVIF_TRANSFORM_IROT | AVIF_TRANSFORM_IMIR; + image->irot.angle = 3; // applied before imir according to MIAF spec ISO/IEC 28002-12:2021 - section 7.3.6.7 + image->imir.axis = 0; + return AVIF_RESULT_OK; + case 8: // The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom. + image->transformFlags = otherFlags | AVIF_TRANSFORM_IROT; + image->irot.angle = 1; + image->imir.axis = 0; // ignored + return AVIF_RESULT_OK; + default: // reserved + break; + } + } + + // The orientation tag is not mandatory (only recommended) according to JEITA CP-3451C section 4.6.8.A. + // The default value is 1 if the orientation tag is missing, meaning: + // The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side. + image->transformFlags = otherFlags; + image->irot.angle = 0; // ignored + image->imir.axis = 0; // ignored + return AVIF_RESULT_OK; +} + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) +uint8_t avifImageIrotImirToExifOrientation(const avifImage * image) +{ + if (!(image->transformFlags & AVIF_TRANSFORM_IROT) || image->irot.angle == 0) { + if (!(image->transformFlags & AVIF_TRANSFORM_IMIR)) { + return 1; // The 0th row is at the visual top of the image, and the 0th column is the visual left-hand side. + } + if (image->imir.axis == 0) { + return 4; // The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side. + } + // image->imir.axis == 1 + return 2; // The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side. + } + + if (image->irot.angle == 1) { + if (!(image->transformFlags & AVIF_TRANSFORM_IMIR)) { + return 8; // The 0th row is the visual left-hand side of the image, and the 0th column is the visual bottom. + } + if (image->imir.axis == 0) { + return 5; // The 0th row is the visual left-hand side of the image, and the 0th column is the visual top. + } + // image->imir.axis == 1 + return 7; // The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom. + } + + if (image->irot.angle == 2) { + if (!(image->transformFlags & AVIF_TRANSFORM_IMIR)) { + return 3; // The 0th row is at the visual bottom of the image, and the 0th column is the visual right-hand side. + } + if (image->imir.axis == 0) { + return 2; // The 0th row is at the visual top of the image, and the 0th column is the visual right-hand side. + } + // image->imir.axis == 1 + return 4; // The 0th row is at the visual bottom of the image, and the 0th column is the visual left-hand side. + } + + // image->irot.angle == 3 + if (!(image->transformFlags & AVIF_TRANSFORM_IMIR)) { + return 6; // The 0th row is the visual right-hand side of the image, and the 0th column is the visual top. + } + if (image->imir.axis == 0) { + return 7; // The 0th row is the visual right-hand side of the image, and the 0th column is the visual bottom. + } + // image->imir.axis == 1 + return 5; // The 0th row is the visual left-hand side of the image, and the 0th column is the visual top. +} +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + +avifResult avifImageSetMetadataExif(avifImage * image, const uint8_t * exif, size_t exifSize) +{ + AVIF_CHECKRES(avifRWDataSet(&image->exif, exif, exifSize)); + // Ignore any Exif parsing failure. + // TODO(wtc): Decide whether to ignore or return Exif parsing failures. + (void)avifImageExtractExifOrientationToIrotImir(image); + return AVIF_RESULT_OK; +} diff --git a/3rdparty/libavif/src/gainmap.c b/3rdparty/libavif/src/gainmap.c new file mode 100644 index 0000000..d87f96a --- /dev/null +++ b/3rdparty/libavif/src/gainmap.c @@ -0,0 +1,913 @@ +// Copyright 2023 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" +#include +#include +#include +#include + +// NaN-safe clamp to [0, 1]. AVIF_CLAMP passes NaN through because IEEE 754 +// comparisons with NaN always return false. fmaxf/fminf return the non-NaN +// argument per C99 §7.12.12, so this clamps NaN to 0. +static float avifNanSafeClamp(float val) +{ + return fminf(1.0f, fmaxf(0.0f, val)); +} + +static void avifGainMapSetEncodingDefaults(avifGainMap * gainMap) +{ + for (int i = 0; i < 3; ++i) { + gainMap->gainMapMin[i] = (avifSignedFraction) { 1, 1 }; + gainMap->gainMapMax[i] = (avifSignedFraction) { 1, 1 }; + gainMap->baseOffset[i] = (avifSignedFraction) { 1, 64 }; + gainMap->alternateOffset[i] = (avifSignedFraction) { 1, 64 }; + gainMap->gainMapGamma[i] = (avifUnsignedFraction) { 1, 1 }; + } + gainMap->baseHdrHeadroom = (avifUnsignedFraction) { 0, 1 }; + gainMap->alternateHdrHeadroom = (avifUnsignedFraction) { 1, 1 }; + gainMap->useBaseColorSpace = AVIF_TRUE; +} + +static float avifSignedFractionToFloat(avifSignedFraction f) +{ + if (f.d == 0) { + return 0.0f; + } + return (float)f.n / f.d; +} + +static float avifUnsignedFractionToFloat(avifUnsignedFraction f) +{ + if (f.d == 0) { + return 0.0f; + } + return (float)f.n / f.d; +} + +// --------------------------------------------------------------------------- +// Apply a gain map. + +// Returns a weight in [-1.0, 1.0] that represents how much the gain map should be applied. +static float avifGetGainMapWeight(float hdrHeadroom, const avifGainMap * gainMap) +{ + const float baseHdrHeadroom = avifUnsignedFractionToFloat(gainMap->baseHdrHeadroom); + const float alternateHdrHeadroom = avifUnsignedFractionToFloat(gainMap->alternateHdrHeadroom); + if (baseHdrHeadroom == alternateHdrHeadroom) { + // Do not apply the gain map if the HDR headroom is the same. + // This case is not handled in the specification and does not make practical sense. + return 0.0f; + } + const float w = AVIF_CLAMP((hdrHeadroom - baseHdrHeadroom) / (alternateHdrHeadroom - baseHdrHeadroom), 0.0f, 1.0f); + return (alternateHdrHeadroom < baseHdrHeadroom) ? -w : w; +} + +// Linear interpolation between 'a' and 'b' (returns 'a' if w == 0.0f, returns 'b' if w == 1.0f). +static inline float lerp(float a, float b, float w) +{ + return (1.0f - w) * a + w * b; +} + +#define SDR_WHITE_NITS 203.0f + +avifResult avifRGBImageApplyGainMap(const avifRGBImage * baseImage, + avifColorPrimaries baseColorPrimaries, + avifTransferCharacteristics baseTransferCharacteristics, + const avifGainMap * gainMap, + float hdrHeadroom, + avifColorPrimaries outputColorPrimaries, + avifTransferCharacteristics outputTransferCharacteristics, + avifRGBImage * toneMappedImage, + avifContentLightLevelInformationBox * clli, + avifDiagnostics * diag) +{ + avifDiagnosticsClearError(diag); + + if (hdrHeadroom < 0.0f) { + avifDiagnosticsPrintf(diag, "hdrHeadroom should be >= 0, got %f", hdrHeadroom); + return AVIF_RESULT_INVALID_ARGUMENT; + } + if (baseImage == NULL || gainMap == NULL || toneMappedImage == NULL) { + avifDiagnosticsPrintf(diag, "NULL input image"); + return AVIF_RESULT_INVALID_ARGUMENT; + } + AVIF_CHECKRES(avifGainMapValidateMetadata(gainMap, diag)); + + const uint32_t width = baseImage->width; + const uint32_t height = baseImage->height; + + const avifBool useBaseColorSpace = gainMap->useBaseColorSpace; + const avifColorPrimaries gainMapMathPrimaries = + (useBaseColorSpace || (gainMap->altColorPrimaries == AVIF_COLOR_PRIMARIES_UNSPECIFIED)) ? baseColorPrimaries + : gainMap->altColorPrimaries; + const avifBool needsInputColorConversion = (baseColorPrimaries != gainMapMathPrimaries); + const avifBool needsOutputColorConversion = (gainMapMathPrimaries != outputColorPrimaries); + + avifImage * rescaledGainMap = NULL; + avifRGBImage rgbGainMap; + // Basic zero-initialization for now, avifRGBImageSetDefaults() is called later on. + memset(&rgbGainMap, 0, sizeof(rgbGainMap)); + + avifResult res = AVIF_RESULT_OK; + toneMappedImage->width = width; + toneMappedImage->height = height; + AVIF_CHECKRES(avifRGBImageAllocatePixels(toneMappedImage)); + + // --- After this point, the function should exit with 'goto cleanup' to free allocated pixels. + + const float weight = avifGetGainMapWeight(hdrHeadroom, gainMap); + + // Early exit if the gain map does not need to be applied and the pixel format is the same. + if (weight == 0.0f && outputTransferCharacteristics == baseTransferCharacteristics && + outputColorPrimaries == baseColorPrimaries && baseImage->format == toneMappedImage->format && + baseImage->depth == toneMappedImage->depth && baseImage->isFloat == toneMappedImage->isFloat) { + assert(baseImage->rowBytes == toneMappedImage->rowBytes); + assert(baseImage->height == toneMappedImage->height); + // Copy the base image. + memcpy(toneMappedImage->pixels, baseImage->pixels, (size_t)baseImage->rowBytes * baseImage->height); + goto cleanup; + } + + avifRGBColorSpaceInfo baseRGBInfo; + avifRGBColorSpaceInfo toneMappedPixelRGBInfo; + if (!avifGetRGBColorSpaceInfo(baseImage, &baseRGBInfo) || !avifGetRGBColorSpaceInfo(toneMappedImage, &toneMappedPixelRGBInfo)) { + avifDiagnosticsPrintf(diag, "Unsupported RGB color space"); + res = AVIF_RESULT_NOT_IMPLEMENTED; + goto cleanup; + } + + const avifTransferFunction gammaToLinear = avifTransferCharacteristicsGetGammaToLinearFunction(baseTransferCharacteristics); + const avifTransferFunction linearToGamma = avifTransferCharacteristicsGetLinearToGammaFunction(outputTransferCharacteristics); + + // Early exit if the gain map does not need to be applied. + if (weight == 0.0f) { + const avifBool primariesDiffer = (baseColorPrimaries != outputColorPrimaries); + double conversionCoeffs[3][3]; + if (primariesDiffer && !avifColorPrimariesComputeRGBToRGBMatrix(baseColorPrimaries, outputColorPrimaries, conversionCoeffs)) { + avifDiagnosticsPrintf(diag, "Unsupported RGB color space conversion"); + res = AVIF_RESULT_NOT_IMPLEMENTED; + goto cleanup; + } + // Just convert from one rgb format to another. + for (uint32_t j = 0; j < height; ++j) { + for (uint32_t i = 0; i < width; ++i) { + float basePixelRGBA[4]; + avifGetRGBAPixel(baseImage, i, j, &baseRGBInfo, basePixelRGBA); + if (outputTransferCharacteristics != baseTransferCharacteristics || primariesDiffer) { + for (int c = 0; c < 3; ++c) { + basePixelRGBA[c] = gammaToLinear(basePixelRGBA[c]); + } + if (primariesDiffer) { + avifLinearRGBConvertColorSpace(basePixelRGBA, conversionCoeffs); + } + for (int c = 0; c < 3; ++c) { + basePixelRGBA[c] = avifNanSafeClamp(linearToGamma(basePixelRGBA[c])); + } + } + avifSetRGBAPixel(toneMappedImage, i, j, &toneMappedPixelRGBInfo, basePixelRGBA); + } + } + goto cleanup; + } + + double inputConversionCoeffs[3][3]; + double outputConversionCoeffs[3][3]; + if (needsInputColorConversion && + !avifColorPrimariesComputeRGBToRGBMatrix(baseColorPrimaries, gainMapMathPrimaries, inputConversionCoeffs)) { + avifDiagnosticsPrintf(diag, "Unsupported RGB color space conversion"); + res = AVIF_RESULT_NOT_IMPLEMENTED; + goto cleanup; + } + if (needsOutputColorConversion && + !avifColorPrimariesComputeRGBToRGBMatrix(gainMapMathPrimaries, outputColorPrimaries, outputConversionCoeffs)) { + avifDiagnosticsPrintf(diag, "Unsupported RGB color space conversion"); + res = AVIF_RESULT_NOT_IMPLEMENTED; + goto cleanup; + } + + if (gainMap->image->width != width || gainMap->image->height != height) { + rescaledGainMap = avifImageCreateEmpty(); + if (rescaledGainMap == NULL) { + res = AVIF_RESULT_OUT_OF_MEMORY; + goto cleanup; + } + const avifCropRect rect = { 0, 0, gainMap->image->width, gainMap->image->height }; + res = avifImageSetViewRect(rescaledGainMap, gainMap->image, &rect); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + res = avifImageScale(rescaledGainMap, width, height, diag); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + } + const avifImage * const gainMapImage = (rescaledGainMap != NULL) ? rescaledGainMap : gainMap->image; + + avifRGBImageSetDefaults(&rgbGainMap, gainMapImage); + res = avifRGBImageAllocatePixels(&rgbGainMap); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + res = avifImageYUVToRGB(gainMapImage, &rgbGainMap); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + + avifRGBColorSpaceInfo gainMapRGBInfo; + if (!avifGetRGBColorSpaceInfo(&rgbGainMap, &gainMapRGBInfo)) { + avifDiagnosticsPrintf(diag, "Unsupported RGB color space"); + res = AVIF_RESULT_NOT_IMPLEMENTED; + goto cleanup; + } + + float rgbMaxLinear = 0; // Max tone mapped pixel value across R, G and B channels. + float rgbSumLinear = 0; // Sum of max(r, g, b) for mapped pixels. + // The gain map metadata contains the encoding gamma, and 1/gamma should be used for decoding. + const float gammaInv[3] = { 1.0f / avifUnsignedFractionToFloat(gainMap->gainMapGamma[0]), + 1.0f / avifUnsignedFractionToFloat(gainMap->gainMapGamma[1]), + 1.0f / avifUnsignedFractionToFloat(gainMap->gainMapGamma[2]) }; + const float gainMapMin[3] = { avifSignedFractionToFloat(gainMap->gainMapMin[0]), + avifSignedFractionToFloat(gainMap->gainMapMin[1]), + avifSignedFractionToFloat(gainMap->gainMapMin[2]) }; + const float gainMapMax[3] = { avifSignedFractionToFloat(gainMap->gainMapMax[0]), + avifSignedFractionToFloat(gainMap->gainMapMax[1]), + avifSignedFractionToFloat(gainMap->gainMapMax[2]) }; + const float baseOffset[3] = { avifSignedFractionToFloat(gainMap->baseOffset[0]), + avifSignedFractionToFloat(gainMap->baseOffset[1]), + avifSignedFractionToFloat(gainMap->baseOffset[2]) }; + const float alternateOffset[3] = { avifSignedFractionToFloat(gainMap->alternateOffset[0]), + avifSignedFractionToFloat(gainMap->alternateOffset[1]), + avifSignedFractionToFloat(gainMap->alternateOffset[2]) }; + for (uint32_t j = 0; j < height; ++j) { + for (uint32_t i = 0; i < width; ++i) { + float basePixelRGBA[4]; + avifGetRGBAPixel(baseImage, i, j, &baseRGBInfo, basePixelRGBA); + float gainMapRGBA[4]; + avifGetRGBAPixel(&rgbGainMap, i, j, &gainMapRGBInfo, gainMapRGBA); + + // Apply gain map. + float toneMappedPixelRGBA[4]; + float pixelRgbMaxLinear = 0.0f; // = max(r, g, b) for this pixel + + for (int c = 0; c < 3; ++c) { + basePixelRGBA[c] = gammaToLinear(basePixelRGBA[c]); + } + + if (needsInputColorConversion) { + // Convert basePixelRGBA to gainMapMathPrimaries. + avifLinearRGBConvertColorSpace(basePixelRGBA, inputConversionCoeffs); + } + + for (int c = 0; c < 3; ++c) { + const float baseLinear = basePixelRGBA[c]; + const float gainMapValue = gainMapRGBA[c]; + + // Undo gamma & affine transform; the result is in log2 space. + const float gainMapLog2 = lerp(gainMapMin[c], gainMapMax[c], powf(gainMapValue, gammaInv[c])); + const float toneMappedLinear = (baseLinear + baseOffset[c]) * exp2f(gainMapLog2 * weight) - alternateOffset[c]; + + if (toneMappedLinear > rgbMaxLinear) { + rgbMaxLinear = toneMappedLinear; + } + if (toneMappedLinear > pixelRgbMaxLinear) { + pixelRgbMaxLinear = toneMappedLinear; + } + + toneMappedPixelRGBA[c] = toneMappedLinear; + } + + if (needsOutputColorConversion) { + // Convert toneMappedPixelRGBA to outputColorPrimaries. + avifLinearRGBConvertColorSpace(toneMappedPixelRGBA, outputConversionCoeffs); + } + + for (int c = 0; c < 3; ++c) { + if (isnan(toneMappedPixelRGBA[c])) { + avifDiagnosticsPrintf(diag, "Degenerate gain map parameters produce NaN at pixel (%u, %u)", i, j); + res = AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE; + goto cleanup; + } + toneMappedPixelRGBA[c] = avifNanSafeClamp(linearToGamma(toneMappedPixelRGBA[c])); + } + + toneMappedPixelRGBA[3] = basePixelRGBA[3]; // Alpha is unaffected by tone mapping. + rgbSumLinear += pixelRgbMaxLinear; + avifSetRGBAPixel(toneMappedImage, i, j, &toneMappedPixelRGBInfo, toneMappedPixelRGBA); + } + } + if (clli != NULL) { + // For exact CLLI value definitions, see ISO/IEC 23008-2 section D.3.35 + // at https://standards.iso.org/ittf/PubliclyAvailableStandards/index.html + // See also discussion in https://github.com/AOMediaCodec/libavif/issues/1727 + + // Convert extended SDR (where 1.0 is SDR white) to nits. + clli->maxCLL = (uint16_t)AVIF_CLAMP(avifRoundf(rgbMaxLinear * SDR_WHITE_NITS), 0.0f, (float)UINT16_MAX); + const float rgbAverageLinear = rgbSumLinear / ((size_t)width * height); + clli->maxPALL = (uint16_t)AVIF_CLAMP(avifRoundf(rgbAverageLinear * SDR_WHITE_NITS), 0.0f, (float)UINT16_MAX); + } + +cleanup: + avifRGBImageFreePixels(&rgbGainMap); + if (rescaledGainMap != NULL) { + avifImageDestroy(rescaledGainMap); + } + + return res; +} + +avifResult avifImageApplyGainMap(const avifImage * baseImage, + const avifGainMap * gainMap, + float hdrHeadroom, + avifColorPrimaries outputColorPrimaries, + avifTransferCharacteristics outputTransferCharacteristics, + avifRGBImage * toneMappedImage, + avifContentLightLevelInformationBox * clli, + avifDiagnostics * diag) +{ + avifDiagnosticsClearError(diag); + + if (baseImage->icc.size > 0 || gainMap->altICC.size > 0) { + avifDiagnosticsPrintf(diag, "Tone mapping for images with ICC profiles is not supported"); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + avifRGBImage baseImageRgb; + avifRGBImageSetDefaults(&baseImageRgb, baseImage); + AVIF_CHECKRES(avifRGBImageAllocatePixels(&baseImageRgb)); + avifResult res = avifImageYUVToRGB(baseImage, &baseImageRgb); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + + res = avifRGBImageApplyGainMap(&baseImageRgb, + baseImage->colorPrimaries, + baseImage->transferCharacteristics, + gainMap, + hdrHeadroom, + outputColorPrimaries, + outputTransferCharacteristics, + toneMappedImage, + clli, + diag); + +cleanup: + avifRGBImageFreePixels(&baseImageRgb); + + return res; +} + +// --------------------------------------------------------------------------- +// Create a gain map. + +// Returns the index of the histogram bucket for a given value, for a histogram with 'numBuckets' buckets, +// and values ranging in [bucketMin, bucketMax] (values outside of the range are added to the first/last buckets). +static int avifValueToBucketIdx(float v, float bucketMin, float bucketMax, int numBuckets) +{ + v = AVIF_CLAMP(v, bucketMin, bucketMax); + return AVIF_MIN((int)avifRoundf((v - bucketMin) / (bucketMax - bucketMin) * numBuckets), numBuckets - 1); +} +// Returns the lower end of the value range belonging to the given histogram bucket. +static float avifBucketIdxToValue(int idx, float bucketMin, float bucketMax, int numBuckets) +{ + return idx * (bucketMax - bucketMin) / numBuckets + bucketMin; +} + +avifResult avifFindMinMaxWithoutOutliers(const float * gainMapF, size_t numPixels, float * rangeMin, float * rangeMax) +{ + const float bucketSize = 0.01f; // Size of one bucket. Empirical value. + const float maxOutliersRatio = 0.001f; // 0.1% + const int maxOutliersOnEachSide = (int)avifRoundf(numPixels * maxOutliersRatio / 2.0f); + + float min = gainMapF[0]; + float max = gainMapF[0]; + for (size_t i = 1; i < numPixels; ++i) { + min = AVIF_MIN(min, gainMapF[i]); + max = AVIF_MAX(max, gainMapF[i]); + } + + *rangeMin = min; + *rangeMax = max; + if ((max - min) <= (bucketSize * 2) || maxOutliersOnEachSide == 0) { + return AVIF_RESULT_OK; + } + + const int maxNumBuckets = 10000; + const int numBuckets = AVIF_MIN((int)ceilf((max - min) / bucketSize), maxNumBuckets); + int * histogram = avifCalloc(numBuckets, sizeof(int)); + if (histogram == NULL) { + return AVIF_RESULT_OUT_OF_MEMORY; + } + for (size_t i = 0; i < numPixels; ++i) { + ++(histogram[avifValueToBucketIdx(gainMapF[i], min, max, numBuckets)]); + } + + int leftOutliers = 0; + for (int i = 0; i < numBuckets; ++i) { + leftOutliers += histogram[i]; + if (leftOutliers > maxOutliersOnEachSide) { + break; + } + if (histogram[i] == 0) { + // +1 to get the higher end of the bucket. + *rangeMin = avifBucketIdxToValue(i + 1, min, max, numBuckets); + } + } + + int rightOutliers = 0; + for (int i = numBuckets - 1; i >= 0; --i) { + rightOutliers += histogram[i]; + if (rightOutliers > maxOutliersOnEachSide) { + break; + } + if (histogram[i] == 0) { + *rangeMax = avifBucketIdxToValue(i, min, max, numBuckets); + } + } + + avifFree(histogram); + return AVIF_RESULT_OK; +} + +avifResult avifGainMapValidateMetadata(const avifGainMap * gainMap, avifDiagnostics * diag) +{ + for (int i = 0; i < 3; ++i) { + if (gainMap->gainMapMin[i].d == 0 || gainMap->gainMapMax[i].d == 0 || gainMap->gainMapGamma[i].d == 0 || + gainMap->baseOffset[i].d == 0 || gainMap->alternateOffset[i].d == 0) { + avifDiagnosticsPrintf(diag, "Per-channel denominator is 0 in gain map metadata"); + return AVIF_RESULT_INVALID_ARGUMENT; + } + if ((int64_t)gainMap->gainMapMax[i].n * gainMap->gainMapMin[i].d < + (int64_t)gainMap->gainMapMin[i].n * gainMap->gainMapMax[i].d) { + avifDiagnosticsPrintf(diag, "Per-channel max is less than per-channel min in gain map metadata"); + return AVIF_RESULT_INVALID_ARGUMENT; + } + if (gainMap->gainMapGamma[i].n == 0) { + avifDiagnosticsPrintf(diag, "Per-channel gamma is 0 in gain map metadata"); + return AVIF_RESULT_INVALID_ARGUMENT; + } + } + if (gainMap->baseHdrHeadroom.d == 0 || gainMap->alternateHdrHeadroom.d == 0) { + avifDiagnosticsPrintf(diag, "Headroom denominator is 0 in gain map metadata"); + return AVIF_RESULT_INVALID_ARGUMENT; + } + if (gainMap->useBaseColorSpace != 0 && gainMap->useBaseColorSpace != 1) { + avifDiagnosticsPrintf(diag, "useBaseColorSpace is %d in gain map metadata", gainMap->useBaseColorSpace); + return AVIF_RESULT_INVALID_ARGUMENT; + } + return AVIF_RESULT_OK; +} + +avifBool avifSameGainMapMetadata(const avifGainMap * a, const avifGainMap * b) +{ + if (a->baseHdrHeadroom.n != b->baseHdrHeadroom.n || a->baseHdrHeadroom.d != b->baseHdrHeadroom.d || + a->alternateHdrHeadroom.n != b->alternateHdrHeadroom.n || a->alternateHdrHeadroom.d != b->alternateHdrHeadroom.d) { + return AVIF_FALSE; + } + for (int c = 0; c < 3; ++c) { + if (a->gainMapMin[c].n != b->gainMapMin[c].n || a->gainMapMin[c].d != b->gainMapMin[c].d || + a->gainMapMax[c].n != b->gainMapMax[c].n || a->gainMapMax[c].d != b->gainMapMax[c].d || + a->gainMapGamma[c].n != b->gainMapGamma[c].n || a->gainMapGamma[c].d != b->gainMapGamma[c].d || + a->baseOffset[c].n != b->baseOffset[c].n || a->baseOffset[c].d != b->baseOffset[c].d || + a->alternateOffset[c].n != b->alternateOffset[c].n || a->alternateOffset[c].d != b->alternateOffset[c].d) { + return AVIF_FALSE; + } + } + return AVIF_TRUE; +} + +avifBool avifSameGainMapAltMetadata(const avifGainMap * a, const avifGainMap * b) +{ + if (a->altICC.size != b->altICC.size || memcmp(a->altICC.data, b->altICC.data, a->altICC.size) != 0 || + a->altColorPrimaries != b->altColorPrimaries || a->altTransferCharacteristics != b->altTransferCharacteristics || + a->altMatrixCoefficients != b->altMatrixCoefficients || a->altYUVRange != b->altYUVRange || a->altDepth != b->altDepth || + a->altPlaneCount != b->altPlaneCount || a->altCLLI.maxCLL != b->altCLLI.maxCLL || a->altCLLI.maxPALL != b->altCLLI.maxPALL) { + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +static const float kEpsilon = 1e-10f; + +// Decides which of 'basePrimaries' or 'altPrimaries' should be used for doing gain map math when creating a gain map. +// The other image (base or alternate) will be converted to this color space before computing +// the ratio between the two images. +// If a pixel color is outside of the target color space, some of the converted channel values will be negative. +// This should be avoided, as the negative values must either be clamped or offset before computing the log2() +// (since log2 only works on > 0 values). But a large offset causes artefacts when partially applying the gain map. +// Therefore we want to do gain map math in the larger of the two color spaces. +static avifResult avifChooseColorSpaceForGainMapMath(avifColorPrimaries basePrimaries, + avifColorPrimaries altPrimaries, + avifColorPrimaries * gainMapMathColorSpace) +{ + if (basePrimaries == altPrimaries) { + *gainMapMathColorSpace = basePrimaries; + return AVIF_RESULT_OK; + } + // Color convert pure red, pure green and pure blue in turn and see if they result in negative values. + float rgba[4] = { 0 }; + double baseToAltCoeffs[3][3]; + double altToBaseCoeffs[3][3]; + if (!avifColorPrimariesComputeRGBToRGBMatrix(basePrimaries, altPrimaries, baseToAltCoeffs) || + !avifColorPrimariesComputeRGBToRGBMatrix(altPrimaries, basePrimaries, altToBaseCoeffs)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + float baseColorspaceChannelMin = 0; + float altColorspaceChannelMin = 0; + for (int c = 0; c < 3; ++c) { + rgba[0] = rgba[1] = rgba[2] = 0; + rgba[c] = 1.0f; + avifLinearRGBConvertColorSpace(rgba, altToBaseCoeffs); + for (int i = 0; i < 3; ++i) { + baseColorspaceChannelMin = AVIF_MIN(baseColorspaceChannelMin, rgba[i]); + } + rgba[0] = rgba[1] = rgba[2] = 0; + rgba[c] = 1.0f; + avifLinearRGBConvertColorSpace(rgba, baseToAltCoeffs); + for (int i = 0; i < 3; ++i) { + altColorspaceChannelMin = AVIF_MIN(altColorspaceChannelMin, rgba[i]); + } + } + // Pick the colorspace that has the largest min value (which is more or less the largest color space). + *gainMapMathColorSpace = (altColorspaceChannelMin <= baseColorspaceChannelMin) ? basePrimaries : altPrimaries; + return AVIF_RESULT_OK; +} + +avifResult avifRGBImageComputeGainMap(const avifRGBImage * baseRgbImage, + avifColorPrimaries baseColorPrimaries, + avifTransferCharacteristics baseTransferCharacteristics, + const avifRGBImage * altRgbImage, + avifColorPrimaries altColorPrimaries, + avifTransferCharacteristics altTransferCharacteristics, + avifGainMap * gainMap, + avifDiagnostics * diag) +{ + avifDiagnosticsClearError(diag); + + AVIF_CHECKERR(baseRgbImage != NULL && altRgbImage != NULL && gainMap != NULL && gainMap->image != NULL, AVIF_RESULT_INVALID_ARGUMENT); + if (baseRgbImage->width != altRgbImage->width || baseRgbImage->height != altRgbImage->height) { + avifDiagnosticsPrintf(diag, "Both images should have the same dimensions"); + return AVIF_RESULT_INVALID_ARGUMENT; + } + if (gainMap->image->width == 0 || gainMap->image->height == 0 || gainMap->image->depth == 0 || + gainMap->image->yuvFormat <= AVIF_PIXEL_FORMAT_NONE || gainMap->image->yuvFormat >= AVIF_PIXEL_FORMAT_COUNT) { + avifDiagnosticsPrintf(diag, "gainMap->image should be non null with desired width, height, depth and yuvFormat set"); + return AVIF_RESULT_INVALID_ARGUMENT; + } + const avifBool colorSpacesDiffer = (baseColorPrimaries != altColorPrimaries); + avifColorPrimaries gainMapMathPrimaries; + AVIF_CHECKRES(avifChooseColorSpaceForGainMapMath(baseColorPrimaries, altColorPrimaries, &gainMapMathPrimaries)); + const uint32_t width = baseRgbImage->width; + const uint32_t height = baseRgbImage->height; + + avifRGBColorSpaceInfo baseRGBInfo; + avifRGBColorSpaceInfo altRGBInfo; + if (!avifGetRGBColorSpaceInfo(baseRgbImage, &baseRGBInfo) || !avifGetRGBColorSpaceInfo(altRgbImage, &altRGBInfo)) { + avifDiagnosticsPrintf(diag, "Unsupported RGB color space"); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + float * gainMapF[3] = { 0 }; // Temporary buffers for the gain map as floating point values, one per RGB channel. + avifRGBImage gainMapRGB; + memset(&gainMapRGB, 0, sizeof(gainMapRGB)); + avifImage * gainMapImage = gainMap->image; + + avifResult res = AVIF_RESULT_OK; + // --- After this point, the function should exit with 'goto cleanup' to free allocated resources. + + const size_t numPixels = (size_t)width * height; + if (numPixels > SIZE_MAX / sizeof(float)) { + res = AVIF_RESULT_INVALID_ARGUMENT; + goto cleanup; + } + const size_t gainMapChannelSize = numPixels * sizeof(float); + const avifBool singleChannel = (gainMap->image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400); + const int numGainMapChannels = singleChannel ? 1 : 3; + for (int c = 0; c < numGainMapChannels; ++c) { + gainMapF[c] = avifAlloc(gainMapChannelSize); + if (gainMapF[c] == NULL) { + res = AVIF_RESULT_OUT_OF_MEMORY; + goto cleanup; + } + } + + avifGainMapSetEncodingDefaults(gainMap); + gainMap->useBaseColorSpace = (gainMapMathPrimaries == baseColorPrimaries); + + float (*baseGammaToLinear)(float) = avifTransferCharacteristicsGetGammaToLinearFunction(baseTransferCharacteristics); + float (*altGammaToLinear)(float) = avifTransferCharacteristicsGetGammaToLinearFunction(altTransferCharacteristics); + float yCoeffs[3]; + avifColorPrimariesComputeYCoeffs(gainMapMathPrimaries, yCoeffs); + + double rgbConversionCoeffs[3][3]; + if (colorSpacesDiffer) { + if (gainMap->useBaseColorSpace) { + if (!avifColorPrimariesComputeRGBToRGBMatrix(altColorPrimaries, baseColorPrimaries, rgbConversionCoeffs)) { + avifDiagnosticsPrintf(diag, "Unsupported RGB color space conversion"); + res = AVIF_RESULT_NOT_IMPLEMENTED; + goto cleanup; + } + } else { + if (!avifColorPrimariesComputeRGBToRGBMatrix(baseColorPrimaries, altColorPrimaries, rgbConversionCoeffs)) { + avifDiagnosticsPrintf(diag, "Unsupported RGB color space conversion"); + res = AVIF_RESULT_NOT_IMPLEMENTED; + goto cleanup; + } + } + } + + float baseOffset[3] = { avifSignedFractionToFloat(gainMap->baseOffset[0]), + avifSignedFractionToFloat(gainMap->baseOffset[1]), + avifSignedFractionToFloat(gainMap->baseOffset[2]) }; + float alternateOffset[3] = { avifSignedFractionToFloat(gainMap->alternateOffset[0]), + avifSignedFractionToFloat(gainMap->alternateOffset[1]), + avifSignedFractionToFloat(gainMap->alternateOffset[2]) }; + + // If we are converting from one colorspace to another, some RGB values may be negative and an offset must be added to + // avoid clamping (although the choice of color space to do the gain map computation with + // avifChooseColorSpaceForGainMapMath() should mostly avoid this). + if (colorSpacesDiffer) { + // Color convert pure red, pure green and pure blue in turn and see if they result in negative values. + float rgba[4] = { 0.0f }; + float channelMin[3] = { 0.0f }; + for (uint32_t j = 0; j < height; ++j) { + for (uint32_t i = 0; i < width; ++i) { + avifGetRGBAPixel(gainMap->useBaseColorSpace ? altRgbImage : baseRgbImage, + i, + j, + gainMap->useBaseColorSpace ? &altRGBInfo : &baseRGBInfo, + rgba); + + // Convert to linear. + for (int c = 0; c < 3; ++c) { + if (gainMap->useBaseColorSpace) { + rgba[c] = altGammaToLinear(rgba[c]); + } else { + rgba[c] = baseGammaToLinear(rgba[c]); + } + } + avifLinearRGBConvertColorSpace(rgba, rgbConversionCoeffs); + for (int c = 0; c < 3; ++c) { + channelMin[c] = AVIF_MIN(channelMin[c], rgba[c]); + } + } + } + + for (int c = 0; c < 3; ++c) { + // Large offsets cause artefacts when partially applying the gain map, so set a max (empirical) offset value. + // If the offset is clamped, some gain map values will get clamped as well. + const float maxOffset = 0.1f; + if (channelMin[c] < -kEpsilon) { + // Increase the offset to avoid negative values. + if (gainMap->useBaseColorSpace) { + alternateOffset[c] = AVIF_MIN(alternateOffset[c] - channelMin[c], maxOffset); + } else { + baseOffset[c] = AVIF_MIN(baseOffset[c] - channelMin[c], maxOffset); + } + } + } + } + + // Compute raw gain map values. + float baseMax = 1.0f; + float altMax = 1.0f; + for (uint32_t j = 0; j < height; ++j) { + for (uint32_t i = 0; i < width; ++i) { + float baseRGBA[4]; + avifGetRGBAPixel(baseRgbImage, i, j, &baseRGBInfo, baseRGBA); + float altRGBA[4]; + avifGetRGBAPixel(altRgbImage, i, j, &altRGBInfo, altRGBA); + + // Convert to linear. + for (int c = 0; c < 3; ++c) { + baseRGBA[c] = baseGammaToLinear(baseRGBA[c]); + altRGBA[c] = altGammaToLinear(altRGBA[c]); + } + + if (colorSpacesDiffer) { + if (gainMap->useBaseColorSpace) { + // convert altRGBA to baseRGBA's color space + avifLinearRGBConvertColorSpace(altRGBA, rgbConversionCoeffs); + } else { + // convert baseRGBA to altRGBA's color space + avifLinearRGBConvertColorSpace(baseRGBA, rgbConversionCoeffs); + } + } + + for (int c = 0; c < numGainMapChannels; ++c) { + float base = baseRGBA[c]; + float alt = altRGBA[c]; + if (singleChannel) { + // Convert to grayscale. + base = yCoeffs[0] * baseRGBA[0] + yCoeffs[1] * baseRGBA[1] + yCoeffs[2] * baseRGBA[2]; + alt = yCoeffs[0] * altRGBA[0] + yCoeffs[1] * altRGBA[1] + yCoeffs[2] * altRGBA[2]; + } + if (base > baseMax) { + baseMax = base; + } + if (alt > altMax) { + altMax = alt; + } + const float ratio = (alt + alternateOffset[c]) / (base + baseOffset[c]); + const float ratioLog2 = log2f(AVIF_MAX(ratio, kEpsilon)); + gainMapF[c][(size_t)j * width + i] = ratioLog2; + } + } + } + + // Populate the gain map metadata's headrooms. + const double baseHeadroom = log2f(AVIF_MAX(baseMax, kEpsilon)); + const double alternateHeadroom = log2f(AVIF_MAX(altMax, kEpsilon)); + if (!avifDoubleToUnsignedFraction(baseHeadroom, &gainMap->baseHdrHeadroom) || + !avifDoubleToUnsignedFraction(alternateHeadroom, &gainMap->alternateHdrHeadroom)) { + res = AVIF_RESULT_INVALID_ARGUMENT; + goto cleanup; + } + + // Multiply the gainmap by sign(alternateHdrHeadroom - baseHdrHeadroom), to + // ensure that it stores the log-ratio of the HDR representation to the SDR + // representation. + if (alternateHeadroom < baseHeadroom) { + for (int c = 0; c < numGainMapChannels; ++c) { + for (uint32_t j = 0; j < height; ++j) { + for (uint32_t i = 0; i < width; ++i) { + gainMapF[c][(size_t)j * width + i] *= -1.f; + } + } + } + } + + // Find approximate min/max for each channel, discarding outliers. + float gainMapMinLog2[3] = { 0.0f, 0.0f, 0.0f }; + float gainMapMaxLog2[3] = { 0.0f, 0.0f, 0.0f }; + for (int c = 0; c < numGainMapChannels; ++c) { + res = avifFindMinMaxWithoutOutliers(gainMapF[c], numPixels, &gainMapMinLog2[c], &gainMapMaxLog2[c]); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + } + + // Populate the gain map metadata's min and max values. + for (int c = 0; c < 3; ++c) { + if (!avifDoubleToSignedFraction(gainMapMinLog2[singleChannel ? 0 : c], &gainMap->gainMapMin[c]) || + !avifDoubleToSignedFraction(gainMapMaxLog2[singleChannel ? 0 : c], &gainMap->gainMapMax[c]) || + !avifDoubleToSignedFraction(alternateOffset[c], &gainMap->alternateOffset[c]) || + !avifDoubleToSignedFraction(baseOffset[c], &gainMap->baseOffset[c])) { + res = AVIF_RESULT_INVALID_ARGUMENT; + goto cleanup; + } + } + + // Scale the gain map values to map [min, max] range to [0, 1]. + for (int c = 0; c < numGainMapChannels; ++c) { + const float range = AVIF_MAX(gainMapMaxLog2[c] - gainMapMinLog2[c], 0.0f); + + if (range == 0.0f) { + for (uint32_t j = 0; j < height; ++j) { + for (uint32_t i = 0; i < width; ++i) { + // If the range is 0, the gain map values will be multiplied by zero when tonemapping so the values + // don't matter, but we still need to make sure that gainMapF is in [0,1]. + gainMapF[c][(size_t)j * width + i] = 0.0f; + } + } + } else { + // Remap [min; max] range to [0; 1] + const float gainMapGamma = avifUnsignedFractionToFloat(gainMap->gainMapGamma[c]); + for (uint32_t j = 0; j < height; ++j) { + for (uint32_t i = 0; i < width; ++i) { + float v = gainMapF[c][(size_t)j * width + i]; + v = AVIF_CLAMP(v, gainMapMinLog2[c], gainMapMaxLog2[c]); + v = powf((v - gainMapMinLog2[c]) / range, gainMapGamma); + gainMapF[c][(size_t)j * width + i] = avifNanSafeClamp(v); + } + } + } + } + + // Convert the gain map to YUV. + const uint32_t requestedWidth = gainMapImage->width; + const uint32_t requestedHeight = gainMapImage->height; + gainMapImage->width = width; + gainMapImage->height = height; + + avifImageFreePlanes(gainMapImage, AVIF_PLANES_ALL); // Free planes in case they were already allocated. + res = avifImageAllocatePlanes(gainMapImage, AVIF_PLANES_YUV); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + + avifRGBImageSetDefaults(&gainMapRGB, gainMapImage); + res = avifRGBImageAllocatePixels(&gainMapRGB); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + + avifRGBColorSpaceInfo gainMapRGBInfo; + if (!avifGetRGBColorSpaceInfo(&gainMapRGB, &gainMapRGBInfo)) { + avifDiagnosticsPrintf(diag, "Unsupported RGB color space"); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + for (uint32_t j = 0; j < height; ++j) { + for (uint32_t i = 0; i < width; ++i) { + const size_t offset = (size_t)j * width + i; + const float r = gainMapF[0][offset]; + const float g = singleChannel ? r : gainMapF[1][offset]; + const float b = singleChannel ? r : gainMapF[2][offset]; + const float rgbaPixel[4] = { r, g, b, 1.0f }; + avifSetRGBAPixel(&gainMapRGB, i, j, &gainMapRGBInfo, rgbaPixel); + } + } + + res = avifImageRGBToYUV(gainMapImage, &gainMapRGB); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + + // Scale down the gain map if requested. + // Another way would be to scale the source images, but it seems to perform worse. + if (requestedWidth != gainMapImage->width || requestedHeight != gainMapImage->height) { + AVIF_CHECKRES(avifImageScale(gainMap->image, requestedWidth, requestedHeight, diag)); + } + +cleanup: + for (int c = 0; c < 3; ++c) { + avifFree(gainMapF[c]); + } + avifRGBImageFreePixels(&gainMapRGB); + if (res != AVIF_RESULT_OK) { + avifImageFreePlanes(gainMapImage, AVIF_PLANES_ALL); + } + + return res; +} + +avifResult avifImageComputeGainMap(const avifImage * baseImage, const avifImage * altImage, avifGainMap * gainMap, avifDiagnostics * diag) +{ + avifDiagnosticsClearError(diag); + + if (baseImage == NULL || altImage == NULL || gainMap == NULL) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + if (baseImage->icc.size > 0 || altImage->icc.size > 0) { + avifDiagnosticsPrintf(diag, "Computing gain maps for images with ICC profiles is not supported"); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (baseImage->width != altImage->width || baseImage->height != altImage->height) { + avifDiagnosticsPrintf(diag, + "Image dimensions don't match, got %dx%d and %dx%d", + baseImage->width, + baseImage->height, + altImage->width, + altImage->height); + return AVIF_RESULT_INVALID_ARGUMENT; + } + + avifResult res = AVIF_RESULT_OK; + + avifRGBImage baseImageRgb; + avifRGBImageSetDefaults(&baseImageRgb, baseImage); + avifRGBImage altImageRgb; + avifRGBImageSetDefaults(&altImageRgb, altImage); + + AVIF_CHECKRES(avifRGBImageAllocatePixels(&baseImageRgb)); + // --- After this point, the function should exit with 'goto cleanup' to free allocated resources. + + res = avifImageYUVToRGB(baseImage, &baseImageRgb); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + res = avifRGBImageAllocatePixels(&altImageRgb); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + res = avifImageYUVToRGB(altImage, &altImageRgb); + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + + res = avifRGBImageComputeGainMap(&baseImageRgb, + baseImage->colorPrimaries, + baseImage->transferCharacteristics, + &altImageRgb, + altImage->colorPrimaries, + altImage->transferCharacteristics, + gainMap, + diag); + + if (res != AVIF_RESULT_OK) { + goto cleanup; + } + + AVIF_CHECKRES(avifRWDataSet(&gainMap->altICC, altImage->icc.data, altImage->icc.size)); + gainMap->altColorPrimaries = altImage->colorPrimaries; + gainMap->altTransferCharacteristics = altImage->transferCharacteristics; + gainMap->altMatrixCoefficients = altImage->matrixCoefficients; + gainMap->altDepth = altImage->depth; + gainMap->altPlaneCount = (altImage->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) ? 1 : 3; + gainMap->altCLLI = altImage->clli; + +cleanup: + avifRGBImageFreePixels(&baseImageRgb); + avifRGBImageFreePixels(&altImageRgb); + return res; +} diff --git a/3rdparty/libavif/src/io.c b/3rdparty/libavif/src/io.c new file mode 100644 index 0000000..bd0ebda --- /dev/null +++ b/3rdparty/libavif/src/io.c @@ -0,0 +1,248 @@ +// Copyright 2020 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#if !defined(_WIN32) +// Ensure off_t is 64 bits. +#undef _FILE_OFFSET_BITS +#define _FILE_OFFSET_BITS 64 +// Ensure we have some POSIX compatibility with fseeko/ftello. +#undef _POSIX_C_SOURCE +#define _POSIX_C_SOURCE 200112L +#endif + +#include "avif/internal.h" + +#include +#include +#include +#include +#include + +#if defined(_WIN32) +// Windows uses _fseeki64 / _ftelli64 for large file support +typedef __int64 avif_off_t; +#define AVIF_OFF_MAX INT64_MAX + +static int avif_fseeko(FILE * stream, avif_off_t offset, int whence) +{ + return _fseeki64(stream, offset, whence); +} + +static avif_off_t avif_ftello(FILE * stream) +{ + return _ftelli64(stream); +} +#else + +#include + +#if defined(__ANDROID__) +#include +#if __ANDROID_API__ >= 24 +#define AVIF_USE_FSEEKO +#endif +#elif defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L +// Standard Modern POSIX. The _POSIX_VERSION >= 200112L test for fseeko/ftello +// is used in the first example in the APPLICATION USAGE section in +// https://pubs.opengroup.org/onlinepubs/9799919799/basedefs/unistd.h.html. +#define AVIF_USE_FSEEKO +#endif + +#if defined(AVIF_USE_FSEEKO) +// POSIX large file support +static_assert(sizeof(off_t) == sizeof(int64_t), ""); +typedef off_t avif_off_t; +#define AVIF_OFF_MAX INT64_MAX + +static int avif_fseeko(FILE * stream, avif_off_t offset, int whence) +{ + return fseeko(stream, offset, whence); +} + +static avif_off_t avif_ftello(FILE * stream) +{ + return ftello(stream); +} +#else +// Unknown or very old platform. Fall back on fseek/ftell. +typedef long avif_off_t; +#define AVIF_OFF_MAX LONG_MAX + +static int avif_fseeko(FILE * stream, avif_off_t offset, int whence) +{ + return fseek(stream, offset, whence); +} + +static avif_off_t avif_ftello(FILE * stream) +{ + return ftell(stream); +} +#endif // defined(AVIF_USE_FSEEKO) + +#endif // defined(_WIN32) + +void avifIODestroy(avifIO * io) +{ + if (io && io->destroy) { + io->destroy(io); + } +} + +// -------------------------------------------------------------------------------------- +// avifIOMemoryReader + +typedef struct avifIOMemoryReader +{ + avifIO io; // this must be the first member for easy casting to avifIO* + avifROData rodata; +} avifIOMemoryReader; + +static avifResult avifIOMemoryReaderRead(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out) +{ + // printf("avifIOMemoryReaderRead offset %" PRIu64 " size %zu\n", offset, size); + + if (readFlags != 0) { + // Unsupported readFlags + return AVIF_RESULT_IO_ERROR; + } + + avifIOMemoryReader * reader = (avifIOMemoryReader *)io; + + // Sanitize/clamp incoming request + if (offset > reader->rodata.size) { + // The offset is past the end of the buffer. + return AVIF_RESULT_IO_ERROR; + } + uint64_t availableSize = reader->rodata.size - offset; + if (size > availableSize) { + size = (size_t)availableSize; + } + + // Prevent the offset addition from triggering an undefined behavior + // sanitizer error if data is NULL (happens even with offset zero). + out->data = offset ? reader->rodata.data + offset : reader->rodata.data; + out->size = size; + return AVIF_RESULT_OK; +} + +static void avifIOMemoryReaderDestroy(struct avifIO * io) +{ + avifFree(io); +} + +avifIO * avifIOCreateMemoryReader(const uint8_t * data, size_t size) +{ + avifIOMemoryReader * reader = (avifIOMemoryReader *)avifCalloc(1, sizeof(avifIOMemoryReader)); + if (reader == NULL) { + return NULL; + } + reader->io.destroy = avifIOMemoryReaderDestroy; + reader->io.read = avifIOMemoryReaderRead; + reader->io.sizeHint = size; + reader->io.persistent = AVIF_TRUE; + reader->rodata.data = data; + reader->rodata.size = size; + return (avifIO *)reader; +} + +// -------------------------------------------------------------------------------------- +// avifIOFileReader + +typedef struct avifIOFileReader +{ + avifIO io; // this must be the first member for easy casting to avifIO* + avifRWData buffer; + FILE * f; +} avifIOFileReader; + +static avifResult avifIOFileReaderRead(struct avifIO * io, uint32_t readFlags, uint64_t offset, size_t size, avifROData * out) +{ + // printf("avifIOFileReaderRead offset %" PRIu64 " size %zu\n", offset, size); + + if (readFlags != 0) { + // Unsupported readFlags + return AVIF_RESULT_IO_ERROR; + } + + avifIOFileReader * reader = (avifIOFileReader *)io; + + // Sanitize/clamp incoming request + if (offset > reader->io.sizeHint) { + // The offset is past the EOF. + return AVIF_RESULT_IO_ERROR; + } + uint64_t availableSize = reader->io.sizeHint - offset; + if (size > availableSize) { + size = (size_t)availableSize; + } + + if (size > 0) { + if (offset > AVIF_OFF_MAX) { + return AVIF_RESULT_IO_ERROR; + } + if (reader->buffer.size < size) { + AVIF_CHECKRES(avifRWDataRealloc(&reader->buffer, size)); + } + if (avif_fseeko(reader->f, (avif_off_t)offset, SEEK_SET) != 0) { + return AVIF_RESULT_IO_ERROR; + } + size_t bytesRead = fread(reader->buffer.data, 1, size, reader->f); + if (size != bytesRead) { + if (ferror(reader->f)) { + return AVIF_RESULT_IO_ERROR; + } + size = bytesRead; + } + } + + out->data = reader->buffer.data; + out->size = size; + return AVIF_RESULT_OK; +} + +static void avifIOFileReaderDestroy(struct avifIO * io) +{ + avifIOFileReader * reader = (avifIOFileReader *)io; + fclose(reader->f); + avifRWDataFree(&reader->buffer); + avifFree(io); +} + +avifIO * avifIOCreateFileReader(const char * filename) +{ + FILE * f = fopen(filename, "rb"); + if (!f) { + return NULL; + } + + if (avif_fseeko(f, 0, SEEK_END) != 0) { + fclose(f); + return NULL; + } + avif_off_t fileSize = avif_ftello(f); + if (fileSize < 0) { + fclose(f); + return NULL; + } + if (avif_fseeko(f, 0, SEEK_SET) != 0) { + fclose(f); + return NULL; + } + + avifIOFileReader * reader = (avifIOFileReader *)avifCalloc(1, sizeof(avifIOFileReader)); + if (!reader) { + fclose(f); + return NULL; + } + reader->f = f; + reader->io.destroy = avifIOFileReaderDestroy; + reader->io.read = avifIOFileReaderRead; + reader->io.sizeHint = (uint64_t)fileSize; + reader->io.persistent = AVIF_FALSE; + if (avifRWDataRealloc(&reader->buffer, 1024) != AVIF_RESULT_OK) { + avifFree(reader); + fclose(f); + return NULL; + } + return (avifIO *)reader; +} diff --git a/3rdparty/libavif/src/mem.c b/3rdparty/libavif/src/mem.c new file mode 100644 index 0000000..46f9fb5 --- /dev/null +++ b/3rdparty/libavif/src/mem.c @@ -0,0 +1,28 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include + +void * avifAlloc(size_t size) +{ + // malloc(0) is implementation-defined (see + // https://en.cppreference.com/w/cpp/memory/c/malloc), so collapse the + // zero-size case to a deterministic NULL return. Callers must either treat + // 0 as an allocation failure or guard against it before calling. + if (size == 0) { + return NULL; + } + return malloc(size); +} + +void * avifCalloc(size_t count, size_t size) +{ + return calloc(count, size); +} + +void avifFree(void * p) +{ + free(p); +} diff --git a/3rdparty/libavif/src/obu.c b/3rdparty/libavif/src/obu.c new file mode 100644 index 0000000..620d543 --- /dev/null +++ b/3rdparty/libavif/src/obu.c @@ -0,0 +1,724 @@ +/* + * Copyright © 2018, VideoLAN and dav1d authors + * Copyright © 2018, Two Orioles, LLC + * 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. + * + * 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 OWNER 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. + */ + +// OBU parsing and bit magic all originally from dav1d's obu.c and getbits.c, +// but heavily modified/reduced down to simply find the Sequence Header OBU +// and pull a few interesting pieces from it. +// +// Any other code in here is under this license: +// +// Copyright 2020 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include + +#if defined(AVIF_CODEC_AVM) +#include "avm/avm_codec.h" +#endif + +// --------------------------------------------------------------------------- +// avifBits - Originally dav1d's GetBits struct (see dav1d's getbits.c) + +typedef struct avifBits +{ + int error, eof; + uint64_t state; + uint32_t bitsLeft; + const uint8_t *ptr, *start, *end; +} avifBits; + +static inline uint32_t avifBitsReadPos(const avifBits * bits) +{ + return (uint32_t)(bits->ptr - bits->start) * 8 - bits->bitsLeft; +} + +static void avifBitsInit(avifBits * const bits, const uint8_t * const data, const size_t size) +{ + bits->ptr = bits->start = data; + bits->end = &bits->start[size]; + bits->bitsLeft = 0; + bits->state = 0; + bits->error = 0; + bits->eof = (size == 0); +} + +static void avifBitsRefill(avifBits * const bits, const uint32_t n) +{ + uint64_t state = 0; + do { + state <<= 8; + bits->bitsLeft += 8; + if (!bits->eof) + state |= *bits->ptr++; + if (bits->ptr >= bits->end) { + bits->error = bits->eof; + bits->eof = 1; + } + } while (n > bits->bitsLeft); + bits->state |= state << (64 - bits->bitsLeft); +} + +static uint32_t avifBitsRead(avifBits * const bits, const uint32_t n) +{ + if (n > bits->bitsLeft) + avifBitsRefill(bits, n); + + const uint64_t state = bits->state; + bits->bitsLeft -= n; + bits->state <<= n; + + return (uint32_t)(state >> (64 - n)); +} + +static uint32_t avifBitsReadUleb128(avifBits * bits) +{ + uint64_t val = 0; + uint32_t more; + uint32_t i = 0; + + do { + const uint32_t v = avifBitsRead(bits, 8); + more = v & 0x80; + val |= ((uint64_t)(v & 0x7F)) << i; + i += 7; + } while (more && i < 56); + + if (val > UINT32_MAX || more) { + bits->error = 1; + return 0; + } + + return (uint32_t)val; +} + +static uint32_t avifBitsReadVLC(avifBits * const bits) +{ + int numBits = 0; + while (!avifBitsRead(bits, 1)) + if (++numBits == 32) + return 0xFFFFFFFFU; + return numBits ? ((1U << numBits) - 1) + avifBitsRead(bits, numBits) : 0; +} + +#if defined(AVIF_CODEC_AVM) +// Rice-Golomb coding with parameter n. +static uint32_t avifBitsReadRG(avifBits * const bits, const uint32_t n) +{ + for (uint32_t q = 0; q < 32; q++) { + const uint32_t rgBit = avifBitsRead(bits, 1); + if (rgBit == 0) { + const uint32_t remainder = avifBitsRead(bits, n); + return (q << n) + remainder; + } + } + return 0xFFFFFFFFU; +} +#endif // defined(AVIF_CODEC_AVM) + +// --------------------------------------------------------------------------- +// Variables in here use snake_case to self-document from the AV1 spec and the draft AV2 spec: +// +// https://aomediacodec.github.io/av1-spec/av1-spec.pdf +// +// Originally dav1d's parse_seq_hdr() function (heavily modified and split) + +static avifBool parseAV1SequenceHeaderProfile(avifBits * bits, avifSequenceHeader * header) +{ + uint32_t seq_profile = avifBitsRead(bits, 3); + if (seq_profile > 2) { + return AVIF_FALSE; + } + header->av1C.seqProfile = (uint8_t)seq_profile; + return !bits->error; +} + +#if defined(AVIF_CODEC_AVM) +static avifBool parseAV2SequenceHeaderProfile(avifBits * bits, avifSequenceHeader * header) +{ + uint32_t seq_profile = avifBitsRead(bits, 5); + if (seq_profile >= 32) { + return AVIF_FALSE; + } + header->av1C.seqProfile = (uint8_t)seq_profile; + return !bits->error; +} +#endif // defined(AVIF_CODEC_AVM) + +static avifBool parseSequenceHeaderLevelIdxAndTier(avifBits * bits, avifSequenceHeader * header) +{ + uint32_t still_picture = avifBitsRead(bits, 1); + header->reduced_still_picture_header = (uint8_t)avifBitsRead(bits, 1); + if (header->reduced_still_picture_header && !still_picture) { + return AVIF_FALSE; + } + + if (header->reduced_still_picture_header) { + header->av1C.seqLevelIdx0 = (uint8_t)avifBitsRead(bits, 5); + header->av1C.seqTier0 = 0; + } else { + uint32_t timing_info_present_flag = avifBitsRead(bits, 1); + uint32_t decoder_model_info_present_flag = 0; + uint32_t buffer_delay_length = 0; + if (timing_info_present_flag) { // timing_info() + avifBitsRead(bits, 32); // num_units_in_display_tick + avifBitsRead(bits, 32); // time_scale + uint32_t equal_picture_interval = avifBitsRead(bits, 1); + if (equal_picture_interval) { + uint32_t num_ticks_per_picture_minus_1 = avifBitsReadVLC(bits); + if (num_ticks_per_picture_minus_1 == 0xFFFFFFFFU) + return AVIF_FALSE; + } + + decoder_model_info_present_flag = avifBitsRead(bits, 1); + if (decoder_model_info_present_flag) { // decoder_model_info() + buffer_delay_length = avifBitsRead(bits, 5) + 1; + avifBitsRead(bits, 32); // num_units_in_decoding_tick + avifBitsRead(bits, 10); // buffer_removal_time_length_minus_1, frame_presentation_time_length_minus_1 + } + } + + uint32_t initial_display_delay_present_flag = avifBitsRead(bits, 1); + uint32_t operating_points_cnt = avifBitsRead(bits, 5) + 1; + for (uint32_t i = 0; i < operating_points_cnt; i++) { + avifBitsRead(bits, 12); // operating_point_idc + uint32_t seq_level_idx = avifBitsRead(bits, 5); + if (i == 0) { + header->av1C.seqLevelIdx0 = (uint8_t)seq_level_idx; + header->av1C.seqTier0 = 0; + } + if (seq_level_idx > 7) { + uint32_t seq_tier = avifBitsRead(bits, 1); + if (i == 0) { + header->av1C.seqTier0 = (uint8_t)seq_tier; + } + } + if (decoder_model_info_present_flag) { + uint32_t decoder_model_present_for_this_op = avifBitsRead(bits, 1); + if (decoder_model_present_for_this_op) { // operating_parameters_info() + avifBitsRead(bits, buffer_delay_length); // decoder_buffer_delay + avifBitsRead(bits, buffer_delay_length); // encoder_buffer_delay + avifBitsRead(bits, 1); // low_delay_mode_flag + } + } + if (initial_display_delay_present_flag) { + uint32_t initial_display_delay_present_for_this_op = avifBitsRead(bits, 1); + if (initial_display_delay_present_for_this_op) { + avifBitsRead(bits, 4); // initial_display_delay_minus_1 + } + } + } + } + return !bits->error; +} + +static avifBool parseSequenceHeaderFrameMaxDimensions(avifBits * bits, avifSequenceHeader * header) +{ + uint32_t frame_width_bits = avifBitsRead(bits, 4) + 1; + uint32_t frame_height_bits = avifBitsRead(bits, 4) + 1; + header->maxWidth = avifBitsRead(bits, frame_width_bits) + 1; // max_frame_width + header->maxHeight = avifBitsRead(bits, frame_height_bits) + 1; // max_frame_height + uint32_t frame_id_numbers_present_flag = 0; + if (!header->reduced_still_picture_header) { + frame_id_numbers_present_flag = avifBitsRead(bits, 1); + } + if (frame_id_numbers_present_flag) { + avifBitsRead(bits, 7); // delta_frame_id_length_minus_2, additional_frame_id_length_minus_1 + } + return !bits->error; +} + +static avifBool parseSequenceHeaderEnabledFeatures(avifBits * bits, avifSequenceHeader * header) +{ + avifBitsRead(bits, 2); // enable_filter_intra, enable_intra_edge_filter + + if (!header->reduced_still_picture_header) { + avifBitsRead(bits, 4); // enable_interintra_compound, enable_masked_compound, enable_warped_motion, enable_dual_filter + uint32_t enable_order_hint = avifBitsRead(bits, 1); + if (enable_order_hint) { + avifBitsRead(bits, 2); // enable_jnt_comp, enable_ref_frame_mvs + } + + uint32_t seq_force_screen_content_tools = 0; + uint32_t seq_choose_screen_content_tools = avifBitsRead(bits, 1); + if (seq_choose_screen_content_tools) { + seq_force_screen_content_tools = 2; + } else { + seq_force_screen_content_tools = avifBitsRead(bits, 1); + } + if (seq_force_screen_content_tools > 0) { + uint32_t seq_choose_integer_mv = avifBitsRead(bits, 1); + if (!seq_choose_integer_mv) { + avifBitsRead(bits, 1); // seq_force_integer_mv + } + } + if (enable_order_hint) { + avifBitsRead(bits, 3); // order_hint_bits_minus_1 + } + } + + return !bits->error; +} + +// Note: Does not parse separate_uv_delta_q. +static avifBool parseAV1SequenceHeaderColorConfig(avifBits * bits, avifSequenceHeader * header) +{ + header->bitDepth = 8; + header->chromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + header->av1C.chromaSamplePosition = (uint8_t)header->chromaSamplePosition; + uint32_t high_bitdepth = avifBitsRead(bits, 1); + header->av1C.highBitdepth = (uint8_t)high_bitdepth; + if ((header->av1C.seqProfile == 2) && high_bitdepth) { + uint32_t twelve_bit = avifBitsRead(bits, 1); + header->bitDepth = twelve_bit ? 12 : 10; + header->av1C.twelveBit = (uint8_t)twelve_bit; + } else /* if (seq_profile <= 2) */ { + header->bitDepth = high_bitdepth ? 10 : 8; + header->av1C.twelveBit = 0; + } + uint32_t mono_chrome = 0; + if (header->av1C.seqProfile != 1) { + mono_chrome = avifBitsRead(bits, 1); + } + header->av1C.monochrome = (uint8_t)mono_chrome; + uint32_t color_description_present_flag = avifBitsRead(bits, 1); + if (color_description_present_flag) { + header->colorPrimaries = (avifColorPrimaries)avifBitsRead(bits, 8); // color_primaries + header->transferCharacteristics = (avifTransferCharacteristics)avifBitsRead(bits, 8); // transfer_characteristics + header->matrixCoefficients = (avifMatrixCoefficients)avifBitsRead(bits, 8); // matrix_coefficients + } else { + header->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED; + header->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED; + header->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED; + } + if (mono_chrome) { + header->range = avifBitsRead(bits, 1) ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED; // color_range + header->av1C.chromaSubsamplingX = 1; + header->av1C.chromaSubsamplingY = 1; + header->yuvFormat = AVIF_PIXEL_FORMAT_YUV400; + } else if (header->colorPrimaries == AVIF_COLOR_PRIMARIES_BT709 && + header->transferCharacteristics == AVIF_TRANSFER_CHARACTERISTICS_SRGB && + header->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) { + header->range = AVIF_RANGE_FULL; + header->av1C.chromaSubsamplingX = 0; + header->av1C.chromaSubsamplingY = 0; + header->yuvFormat = AVIF_PIXEL_FORMAT_YUV444; + } else { + uint32_t subsampling_x = 0; + uint32_t subsampling_y = 0; + header->range = avifBitsRead(bits, 1) ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED; // color_range + switch (header->av1C.seqProfile) { + case 0: + subsampling_x = 1; + subsampling_y = 1; + header->yuvFormat = AVIF_PIXEL_FORMAT_YUV420; + break; + case 1: + subsampling_x = 0; + subsampling_y = 0; + header->yuvFormat = AVIF_PIXEL_FORMAT_YUV444; + break; + case 2: + if (header->bitDepth == 12) { + subsampling_x = avifBitsRead(bits, 1); + if (subsampling_x) { + subsampling_y = avifBitsRead(bits, 1); + } + } else { + subsampling_x = 1; + subsampling_y = 0; + } + if (subsampling_x) { + header->yuvFormat = subsampling_y ? AVIF_PIXEL_FORMAT_YUV420 : AVIF_PIXEL_FORMAT_YUV422; + } else { + header->yuvFormat = AVIF_PIXEL_FORMAT_YUV444; + } + break; + default: + return AVIF_FALSE; + } + + if (subsampling_x && subsampling_y) { + header->chromaSamplePosition = (avifChromaSamplePosition)avifBitsRead(bits, 2); // chroma_sample_position + header->av1C.chromaSamplePosition = (uint8_t)header->chromaSamplePosition; + } + header->av1C.chromaSubsamplingX = (uint8_t)subsampling_x; + header->av1C.chromaSubsamplingY = (uint8_t)subsampling_y; + } + + return !bits->error; +} + +#if defined(AVIF_CODEC_AVM) +enum +{ + AV2_CHROMA_FORMAT_420 = 0, + AV2_CHROMA_FORMAT_400 = 1, + AV2_CHROMA_FORMAT_444 = 2, + AV2_CHROMA_FORMAT_422 = 3, +}; + +static avifChromaSamplePosition av2ChromaSamplePositionToAv1ChromaSamplePosition(uint32_t av2ChromaSamplePosition) +{ + if (av2ChromaSamplePosition == 0) { + // AVM_CSP_LEFT: Horizontal offset 0, vertical offset 0.5 + return AVIF_CHROMA_SAMPLE_POSITION_VERTICAL; + } else if (av2ChromaSamplePosition == 1) { + // AVM_CSP_CENTER: Horizontal offset 0.5, vertical offset 0.5 + return AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + } else if (av2ChromaSamplePosition == 2) { + // AVM_CSP_TOPLEFT: Horizontal offset 0, vertical offset 0 + return AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + } else { + return AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + } +} + +static avifBool parseAV2ChromaFormatBitdepth(avifBits * bits, avifSequenceHeader * header) +{ + const uint32_t chromaFormatIdc = avifBitsReadVLC(bits); + + const uint32_t bitdepthIdx = avifBitsReadVLC(bits); + if (bitdepthIdx == 0) { + header->bitDepth = 10; + } else if (bitdepthIdx == 1) { + header->bitDepth = 8; + } else if (bitdepthIdx == 2) { + header->bitDepth = 12; + } else { + return AVIF_FALSE; + } + header->av1C.highBitdepth = header->bitDepth > 8; + header->av1C.twelveBit = header->bitDepth == 12; + header->av1C.monochrome = chromaFormatIdc == AV2_CHROMA_FORMAT_400; + + if (header->av1C.monochrome) { + header->av1C.chromaSubsamplingX = 1; + header->av1C.chromaSubsamplingY = 1; + header->yuvFormat = AVIF_PIXEL_FORMAT_YUV400; + } else if (chromaFormatIdc == AV2_CHROMA_FORMAT_420) { + header->av1C.chromaSubsamplingX = 1; + header->av1C.chromaSubsamplingY = 1; + header->yuvFormat = AVIF_PIXEL_FORMAT_YUV420; + } else if (chromaFormatIdc == AV2_CHROMA_FORMAT_444) { + header->av1C.chromaSubsamplingX = 0; + header->av1C.chromaSubsamplingY = 0; + header->yuvFormat = AVIF_PIXEL_FORMAT_YUV444; + } else if (chromaFormatIdc == AV2_CHROMA_FORMAT_422) { + header->av1C.chromaSubsamplingX = 1; + header->av1C.chromaSubsamplingY = 0; + header->yuvFormat = AVIF_PIXEL_FORMAT_YUV422; + } else { + return AVIF_FALSE; + } + + return !bits->error; +} +#endif // defined(AVIF_CODEC_AVM) + +static avifBool parseAV1SequenceHeader(avifBits * bits, avifSequenceHeader * header) +{ + AVIF_CHECK(parseAV1SequenceHeaderProfile(bits, header)); + AVIF_CHECK(parseSequenceHeaderLevelIdxAndTier(bits, header)); + + AVIF_CHECK(parseSequenceHeaderFrameMaxDimensions(bits, header)); + avifBitsRead(bits, 1); // use_128x128_superblock + AVIF_CHECK(parseSequenceHeaderEnabledFeatures(bits, header)); + + avifBitsRead(bits, 3); // enable_superres, enable_cdef, enable_restoration + + AVIF_CHECK(parseAV1SequenceHeaderColorConfig(bits, header)); + if (!header->av1C.monochrome) { + avifBitsRead(bits, 1); // separate_uv_delta_q + } + + avifBitsRead(bits, 1); // film_grain_params_present + return !bits->error; +} + +#if defined(AVIF_CODEC_AVM) +static int avifCeilLog2(int x) +{ + if (x < 2) + return 0; + int i = 1; + int p = 2; + while (p < x) { + i++; + p <<= 1; + } + return i; +} + +// See read_sequence_header_obu() in av2/decoder/obu.c. +static avifBool parseAV2SequenceHeader(avifBits * bits, avifSequenceHeader * header) +{ + uint32_t seqHeaderId = avifBitsReadVLC(bits); + if (seqHeaderId >= 16) { + return AVIF_FALSE; + } + + AVIF_CHECK(parseAV2SequenceHeaderProfile(bits, header)); + header->reduced_still_picture_header = (uint8_t)avifBitsRead(bits, 1); // single_picture_header_flag + header->av1C.seqLevelIdx0 = (uint8_t)avifBitsRead(bits, 5); + if (header->av1C.seqLevelIdx0 >= 4 && !header->reduced_still_picture_header) { + header->av1C.seqTier0 = avifBitsRead(bits, 1); + } else { + header->av1C.seqTier0 = 0; + } + + AVIF_CHECK(parseAV2ChromaFormatBitdepth(bits, header)); + + if (!header->reduced_still_picture_header) { + avifBitsRead(bits, 3); // seq_lcr_id + avifBitsRead(bits, 1); // still_picture + avifBitsRead(bits, 2); // max_tlayer_id + const int maxMlayerId = avifBitsRead(bits, 3); // max_mlayer_id + if (maxMlayerId > 0) { + const int n = avifCeilLog2(maxMlayerId + 1); + avifBitsRead(bits, n); // seq_max_mlayer_cnt_minus_1 + } + avifBitsRead(bits, 1); // monotonic_output_order_flag + } + + uint32_t frame_width_bits = avifBitsRead(bits, 4) + 1; + uint32_t frame_height_bits = avifBitsRead(bits, 4) + 1; + header->maxWidth = avifBitsRead(bits, frame_width_bits) + 1; // max_frame_width + header->maxHeight = avifBitsRead(bits, frame_height_bits) + 1; // max_frame_height + + header->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED; + header->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED; + header->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED; + header->range = AVIF_RANGE_LIMITED; + + header->chromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + header->av1C.chromaSamplePosition = (uint8_t)header->chromaSamplePosition; + + // Other ignored fields. + return !bits->error; +} + +// See av2_read_content_interpretation_obu() in av2/decoder/obu_ci.c. +static avifBool parseAV2ContentInterpretation(avifBits * bits, avifSequenceHeader * header) +{ + avifBitsRead(bits, 2); // ci_scan_type_idc + const uint32_t colorDescriptionPresent = avifBitsRead(bits, 1); // ci_color_description_present_flag + const uint32_t chromaSamplePositionPresent = avifBitsRead(bits, 1); // ci_chroma_sample_position_present_flag + avifBitsRead(bits, 1); // ci_aspect_ratio_info_present_flag + avifBitsRead(bits, 1); // ci_timing_info_present_flag + avifBitsRead(bits, 1); // ci_extension_present_flag + avifBitsRead(bits, 1); // reserved_bit + + if (colorDescriptionPresent) { + // Override the default CICP values. + const uint32_t colorDescriptionIdc = avifBitsReadRG(bits, 2); // color_description_idc + if (colorDescriptionIdc == 0xFFFFFFFFU) { + return AVIF_FALSE; + } + if (colorDescriptionIdc == 0) { + // Explicitly signaled + header->colorPrimaries = (avifColorPrimaries)avifBitsRead(bits, 8); // color_primaries + header->transferCharacteristics = (avifTransferCharacteristics)avifBitsRead(bits, 8); // transfer_characteristics + header->matrixCoefficients = (avifMatrixCoefficients)avifBitsRead(bits, 8); // matrix_coefficients + } else if (colorDescriptionIdc == 1) { + // BT.709 SDR + header->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709; // 1 + header->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_BT709; // 1 + header->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT709; // 1 + } else if (colorDescriptionIdc == 2) { + // BT.2100 PQ + header->colorPrimaries = AVIF_COLOR_PRIMARIES_BT2100; // 9 + header->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_PQ; // 16 + header->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT2020_NCL; // 9 + } else if (colorDescriptionIdc == 3) { + // BT.2100 HLG + header->colorPrimaries = AVIF_COLOR_PRIMARIES_BT2100; // 9 + header->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_HLG; // 18 + header->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT2020_NCL; // 9 + } else if (colorDescriptionIdc == 4) { + // sRGB + header->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709; // 1 + header->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB; // 13 + header->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY; // 0 + } else if (colorDescriptionIdc == 5) { + // sYCC + header->colorPrimaries = AVIF_COLOR_PRIMARIES_BT709; // 1 + header->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB; // 13 + header->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT470BG; // 5 + } else { + // Reserved + header->colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED; // 2 + header->transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED; // 2 + header->matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED; // 2 + } + header->range = avifBitsRead(bits, 1) ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED; // color_range + } else { + // Keep the default CICP values. + } + + if (chromaSamplePositionPresent) { + const uint32_t chromaSamplePosition = avifBitsReadVLC(bits); // ci_chroma_sample_position_0 + header->chromaSamplePosition = av2ChromaSamplePositionToAv1ChromaSamplePosition(chromaSamplePosition); + header->av1C.chromaSamplePosition = (uint8_t)header->chromaSamplePosition; + } + + // Other ignored fields. + return !bits->error; +} +#endif // defined(AVIF_CODEC_AVM) + +static avifBool av1SequenceHeaderParse(avifSequenceHeader * header, const avifROData * sample) +{ + avifROData obus = *sample; + + // Find the sequence header OBU + while (obus.size > 0) { + avifBits bits; + avifBitsInit(&bits, obus.data, obus.size); + + // obu_header() + const uint32_t obu_forbidden_bit = avifBitsRead(&bits, 1); + if (obu_forbidden_bit != 0) { + return AVIF_FALSE; + } + const uint32_t obu_type = avifBitsRead(&bits, 4); + const uint32_t obu_extension_flag = avifBitsRead(&bits, 1); + const uint32_t obu_has_size_field = avifBitsRead(&bits, 1); + avifBitsRead(&bits, 1); // obu_reserved_1bit + + if (obu_extension_flag) { // obu_extension_header() + avifBitsRead(&bits, 8); // temporal_id, spatial_id, extension_header_reserved_3bits + } + + uint32_t obu_size = 0; + if (obu_has_size_field) + obu_size = avifBitsReadUleb128(&bits); + else + obu_size = (int)obus.size - 1 - obu_extension_flag; + + if (bits.error) { + return AVIF_FALSE; + } + + const uint32_t init_bit_pos = avifBitsReadPos(&bits); + const uint32_t init_byte_pos = init_bit_pos >> 3; + if (obu_size > obus.size - init_byte_pos) + return AVIF_FALSE; + + if (obu_type == 1) { // Sequence Header + avifBits seqHdrBits; + avifBitsInit(&seqHdrBits, obus.data + init_byte_pos, obu_size); + return parseAV1SequenceHeader(&seqHdrBits, header); + } + + // Skip this OBU + obus.data += (size_t)obu_size + init_byte_pos; + obus.size -= (size_t)obu_size + init_byte_pos; + } + return AVIF_FALSE; +} + +#if defined(AVIF_CODEC_AVM) +static avifBool av2SequenceHeaderParse(avifSequenceHeader * header, const avifROData * sample) +{ + avifBool sequenceHeaderFound = AVIF_FALSE; + avifROData obus = *sample; + + // Find the Sequence Header OBU, and the Content Interpretation OBU if any. + while (obus.size > 0) { + avifBits bits; + avifBitsInit(&bits, obus.data, obus.size); + + const uint32_t obuSize = avifBitsReadUleb128(&bits); + + // obu_header() + const uint32_t obuHeaderExtensionFlag = avifBitsRead(&bits, 1); + const uint32_t obuType = avifBitsRead(&bits, 5); + avifBitsRead(&bits, 2); // obu_tlayer_id + + if (obuHeaderExtensionFlag) { + avifBitsRead(&bits, 8); // obu_mlayer_id, obu_xlayer_id + } + + if (bits.error) { + return AVIF_FALSE; + } + + const uint32_t obuHeaderSize = 1 + obuHeaderExtensionFlag; + if (obuSize < obuHeaderSize) { + return AVIF_FALSE; + } + const uint32_t obuPayloadSize = obuSize - obuHeaderSize; + const uint32_t initBitPos = avifBitsReadPos(&bits); + const uint32_t initBytePos = initBitPos >> 3; + if (obuPayloadSize > obus.size - initBytePos) { + return AVIF_FALSE; + } + + if (obuType == OBU_SEQUENCE_HEADER) { + if (sequenceHeaderFound) { + return AVIF_FALSE; + } + avifBits seqHdrBits; + avifBitsInit(&seqHdrBits, obus.data + initBytePos, obuPayloadSize); + if (!parseAV2SequenceHeader(&seqHdrBits, header)) { + return AVIF_FALSE; + } + sequenceHeaderFound = AVIF_TRUE; + } else if (obuType == OBU_CONTENT_INTERPRETATION) { // optional + if (!sequenceHeaderFound) { + return AVIF_FALSE; + } + avifBits ciBits; + avifBitsInit(&ciBits, obus.data + initBytePos, obuPayloadSize); + if (!parseAV2ContentInterpretation(&ciBits, header)) { + return AVIF_FALSE; + } + break; + } + obus.data += (size_t)obuPayloadSize + initBytePos; + obus.size -= (size_t)obuPayloadSize + initBytePos; + } + return sequenceHeaderFound; +} +#endif // defined(AVIF_CODEC_AVM) + +avifBool avifSequenceHeaderParse(avifSequenceHeader * header, const avifROData * sample, avifCodecType codecType) +{ + switch (codecType) { + case AVIF_CODEC_TYPE_AV1: + return av1SequenceHeaderParse(header, sample); +#if defined(AVIF_CODEC_AVM) + case AVIF_CODEC_TYPE_AV2: + return av2SequenceHeaderParse(header, sample); +#endif + default: + return AVIF_FALSE; + } +} diff --git a/3rdparty/libavif/src/properties.c b/3rdparty/libavif/src/properties.c new file mode 100644 index 0000000..05ee7b9 --- /dev/null +++ b/3rdparty/libavif/src/properties.c @@ -0,0 +1,69 @@ +// Copyright 2024 Brad Hards. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include + +struct avifKnownProperty +{ + uint8_t fourcc[4]; +}; + +static const struct avifKnownProperty knownProperties[] = { + { { 'f', 't', 'y', 'p' } }, { { 'u', 'u', 'i', 'd' } }, { { 'm', 'e', 't', 'a' } }, { { 'h', 'd', 'l', 'r' } }, + { { 'p', 'i', 't', 'm' } }, { { 'd', 'i', 'n', 'f' } }, { { 'd', 'r', 'e', 'f' } }, { { 'i', 'd', 'a', 't' } }, + { { 'i', 'l', 'o', 'c' } }, { { 'i', 'i', 'n', 'f' } }, { { 'i', 'n', 'f', 'e' } }, { { 'i', 'p', 'r', 'p' } }, + { { 'i', 'p', 'c', 'o' } }, { { 'a', 'v', '1', 'C' } }, { { 'a', 'v', '2', 'C' } }, { { 'i', 's', 'p', 'e' } }, + { { 'p', 'i', 'x', 'i' } }, { { 'p', 'a', 's', 'p' } }, { { 'c', 'o', 'l', 'r' } }, { { 'a', 'u', 'x', 'C' } }, + { { 'c', 'l', 'a', 'p' } }, { { 'i', 'r', 'o', 't' } }, { { 'i', 'm', 'i', 'r' } }, { { 'c', 'l', 'l', 'i' } }, + { { 'c', 'c', 'l', 'v' } }, { { 'm', 'd', 'c', 'v' } }, { { 'a', 'm', 'v', 'e' } }, { { 'r', 'e', 'v', 'e' } }, + { { 'n', 'd', 'w', 't' } }, { { 'a', '1', 'o', 'p' } }, { { 'l', 's', 'e', 'l' } }, { { 'a', '1', 'l', 'x' } }, + { { 'c', 'm', 'i', 'n' } }, { { 'c', 'm', 'e', 'x' } }, { { 'i', 'p', 'm', 'a' } }, { { 'i', 'r', 'e', 'f' } }, + { { 'a', 'u', 'x', 'l' } }, { { 't', 'h', 'm', 'b' } }, { { 'd', 'i', 'm', 'g' } }, { { 'p', 'r', 'e', 'm' } }, + { { 'c', 'd', 's', 'c' } }, { { 'g', 'r', 'p', 'l' } }, { { 'a', 'l', 't', 'r' } }, { { 's', 't', 'e', 'r' } }, + { { 'm', 'd', 'a', 't' } }, +}; + +static const size_t numKnownProperties = sizeof(knownProperties) / sizeof(knownProperties[0]); + +static const size_t FOURCC_BYTES = 4; +static const size_t UUID_BYTES = 16; + +static const uint8_t ISO_UUID_SUFFIX[12] = { 0x00, 0x01, 0x00, 0x10, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9b, 0x71 }; + +avifBool avifIsKnownPropertyType(const uint8_t boxtype[4]) +{ + for (size_t i = 0; i < numKnownProperties; i++) { + if (memcmp(knownProperties[i].fourcc, boxtype, FOURCC_BYTES) == 0) { + return AVIF_TRUE; + } + } + return AVIF_FALSE; +} + +avifBool avifIsValidUUID(const uint8_t uuid[16]) +{ + // This check is to reject encoding a known property via the UUID mechanism + // See ISO/IEC 14496-12 Section 4.2.3 + for (size_t i = 0; i < numKnownProperties; i++) { + if ((memcmp(knownProperties[i].fourcc, uuid, FOURCC_BYTES) == 0) && + (memcmp(ISO_UUID_SUFFIX, uuid + FOURCC_BYTES, UUID_BYTES - FOURCC_BYTES) == 0)) { + return AVIF_FALSE; + } + } + // This check rejects UUIDs with unexpected variant field values, including Nil UUID and Max UUID. + // See RFC 9562 Section 4.1 + uint8_t variant = uuid[8] >> 4; + if ((variant < 0x08) || (variant > 0x0b)) { + return AVIF_FALSE; + } + // This check rejects UUIDs with unexpected version field values. + // See RFC 9562 Section 4.2 + uint8_t version = uuid[6] >> 4; + if ((version < 1) || (version > 8)) { + return AVIF_FALSE; + } + // The rest of a UUID is pretty much a bucket of bits, so assume its OK. + return AVIF_TRUE; +} diff --git a/3rdparty/libavif/src/rawdata.c b/3rdparty/libavif/src/rawdata.c new file mode 100644 index 0000000..7ffdaf6 --- /dev/null +++ b/3rdparty/libavif/src/rawdata.c @@ -0,0 +1,44 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include + +avifResult avifRWDataRealloc(avifRWData * raw, size_t newSize) +{ + if (raw->size != newSize) { + if (newSize == 0) { + // avifAlloc(0) returns NULL, so handle the shrink-to-zero case by freeing the buffer. + avifRWDataFree(raw); + return AVIF_RESULT_OK; + } + uint8_t * newData = (uint8_t *)avifAlloc(newSize); + AVIF_CHECKERR(newData, AVIF_RESULT_OUT_OF_MEMORY); + if (raw->size) { + memcpy(newData, raw->data, AVIF_MIN(raw->size, newSize)); + } + avifFree(raw->data); + raw->data = newData; + raw->size = newSize; + } + return AVIF_RESULT_OK; +} + +avifResult avifRWDataSet(avifRWData * raw, const uint8_t * data, size_t len) +{ + if (len) { + AVIF_CHECKRES(avifRWDataRealloc(raw, len)); + memcpy(raw->data, data, len); + } else { + avifRWDataFree(raw); + } + return AVIF_RESULT_OK; +} + +void avifRWDataFree(avifRWData * raw) +{ + avifFree(raw->data); + raw->data = NULL; + raw->size = 0; +} diff --git a/3rdparty/libavif/src/read.c b/3rdparty/libavif/src/read.c new file mode 100644 index 0000000..2bc7696 --- /dev/null +++ b/3rdparty/libavif/src/read.c @@ -0,0 +1,7290 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/avif.h" +#include "avif/internal.h" + +#include +#include +#include +#include +#include +#include +#include + +#define AUXTYPE_SIZE 64 +#define CONTENTTYPE_SIZE 64 + +// class VisualSampleEntry(codingname) extends SampleEntry(codingname) { +// unsigned int(16) pre_defined = 0; +// const unsigned int(16) reserved = 0; +// unsigned int(32)[3] pre_defined = 0; +// unsigned int(16) width; +// unsigned int(16) height; +// template unsigned int(32) horizresolution = 0x00480000; // 72 dpi +// template unsigned int(32) vertresolution = 0x00480000; // 72 dpi +// const unsigned int(32) reserved = 0; +// template unsigned int(16) frame_count = 1; +// string[32] compressorname; +// template unsigned int(16) depth = 0x0018; +// int(16) pre_defined = -1; +// // other boxes from derived specifications +// CleanApertureBox clap; // optional +// PixelAspectRatioBox pasp; // optional +// } +static const size_t VISUALSAMPLEENTRY_SIZE = 78; + +// The only supported ipma box values for both version and flags are [0,1], so there technically +// can't be more than 4 unique tuples right now. +#define MAX_IPMA_VERSION_AND_FLAGS_SEEN 4 + +// --------------------------------------------------------------------------- +// AVIF codec type (AV1 or AV2) + +static avifCodecType avifGetCodecType(const uint8_t * fourcc) +{ + if (!memcmp(fourcc, "av01", 4)) { + return AVIF_CODEC_TYPE_AV1; + } +#if defined(AVIF_CODEC_AVM) + if (!memcmp(fourcc, "av02", 4)) { + return AVIF_CODEC_TYPE_AV2; + } +#endif + return AVIF_CODEC_TYPE_UNKNOWN; +} + +static const char * avifGetConfigurationPropertyName(avifCodecType codecType) +{ + static const char kUnknown[] = "****"; + switch (codecType) { + case AVIF_CODEC_TYPE_AV1: + return "av1C"; +#if defined(AVIF_CODEC_AVM) + case AVIF_CODEC_TYPE_AV2: + return "av2C"; +#endif + default: + assert(AVIF_FALSE); + return kUnknown; // Easier to deal with than NULL. + } +} + +// --------------------------------------------------------------------------- +// Box data structures + +typedef uint8_t avifBrand[4]; +AVIF_ARRAY_DECLARE(avifBrandArray, avifBrand, brand); + +// ftyp +typedef struct avifFileType +{ + uint8_t majorBrand[4]; + uint8_t minorVersion[4]; + // If not null, points to a memory block of 4 * compatibleBrandsCount bytes. + const uint8_t * compatibleBrands; + int compatibleBrandsCount; +} avifFileType; + +// ispe +typedef struct avifImageSpatialExtents +{ + uint32_t width; + uint32_t height; +} avifImageSpatialExtents; + +// auxC +typedef struct avifAuxiliaryType +{ + char auxType[AUXTYPE_SIZE]; +} avifAuxiliaryType; + +// infe mime content_type +typedef struct avifContentType +{ + char contentType[CONTENTTYPE_SIZE]; +} avifContentType; + +// colr +typedef struct avifColourInformationBox +{ + avifBool hasICC; + uint64_t iccOffset; + size_t iccSize; + + avifBool hasNCLX; + avifColorPrimaries colorPrimaries; + avifTransferCharacteristics transferCharacteristics; + avifMatrixCoefficients matrixCoefficients; + avifRange range; +} avifColourInformationBox; + +#define MAX_PIXI_PLANE_DEPTHS 4 +typedef struct avifPixelInformationProperty +{ + uint8_t planeDepths[MAX_PIXI_PLANE_DEPTHS]; + uint8_t planeCount; +#if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI) + avifBool hasExtendedFields; // The fields below were signaled if this is true. + uint8_t subsamplingFlag[MAX_PIXI_PLANE_DEPTHS]; // The fields below were signaled if this is true for a given channel. + uint8_t subsamplingType[MAX_PIXI_PLANE_DEPTHS]; + uint8_t subsamplingLocation[MAX_PIXI_PLANE_DEPTHS]; +#endif // AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI +} avifPixelInformationProperty; + +typedef struct avifOperatingPointSelectorProperty +{ + uint8_t opIndex; +} avifOperatingPointSelectorProperty; + +typedef struct avifLayerSelectorProperty +{ + uint16_t layerID; +} avifLayerSelectorProperty; + +typedef struct avifAV1LayeredImageIndexingProperty +{ + uint32_t layerSize[3]; +} avifAV1LayeredImageIndexingProperty; + +typedef struct avifOpaqueProperty +{ + uint8_t usertype[16]; // Same as in avifImageItemProperty. + avifRWData boxPayload; // Same as in avifImageItemProperty. +} avifOpaqueProperty; + +// Array of item or track ids. +AVIF_ARRAY_DECLARE(avifCodecEntityIDs, uint32_t, ids); + +// Content of a box inside a 'grpl' box, representing a group of entities. +typedef struct avifEntityToGroup +{ + uint8_t groupingType[4]; + uint32_t groupID; + avifCodecEntityIDs entityIDs; +} avifEntityToGroup; +AVIF_ARRAY_DECLARE(avifEntityToGroups, avifEntityToGroup, groups); + +// --------------------------------------------------------------------------- +// Top-level structures + +struct avifMeta; + +// Temporary storage for ipco/stsd contents until they can be associated and memcpy'd to an avifDecoderItem +typedef struct avifProperty +{ + uint8_t type[4]; + avifBool isOpaque; + union + { + avifImageSpatialExtents ispe; + avifAuxiliaryType auxC; // Contents of 'auxC' for items, or 'auxi' for tracks + avifColourInformationBox colr; + avifCodecConfigurationBox av1C; // TODO(yguyon): Rename or add av2C + avifPixelAspectRatioBox pasp; + avifCleanApertureBox clap; + avifImageRotation irot; + avifImageMirror imir; + avifPixelInformationProperty pixi; + avifOperatingPointSelectorProperty a1op; + avifLayerSelectorProperty lsel; + avifAV1LayeredImageIndexingProperty a1lx; + avifContentLightLevelInformationBox clli; + avifOpaqueProperty opaque; + } u; +} avifProperty; +AVIF_ARRAY_DECLARE(avifPropertyArray, avifProperty, prop); + +// Finds the first property of a given type. +static const avifProperty * avifPropertyArrayFind(const avifPropertyArray * properties, const char * type) +{ + for (uint32_t propertyIndex = 0; propertyIndex < properties->count; ++propertyIndex) { + const avifProperty * prop = &properties->prop[propertyIndex]; + if (!memcmp(prop->type, type, 4)) { + return prop; + } + } + return NULL; +} + +AVIF_ARRAY_DECLARE(avifExtentArray, avifExtent, extent); + +// one "item" worth for decoding (all iref, iloc, iprp, etc refer to one of these) +typedef struct avifDecoderItem +{ + uint32_t id; + struct avifMeta * meta; // Unowned; A back-pointer for convenience + uint8_t type[4]; + size_t size; + avifBool idatStored; // If true, offset is relative to the associated meta box's idat box (iloc construction_method==1) + uint32_t width; // Set from this item's ispe property, if present + uint32_t height; // Set from this item's ispe property, if present + avifContentType contentType; + avifPropertyArray properties; + avifExtentArray extents; // All extent offsets/sizes + avifRWData mergedExtents; // A single contiguous block of this item's extents + avifBool ownsMergedExtents; // If true, mergedExtents must be freed when this item is destroyed. + // If false, mergedExtents is used as an avifROData and points to a + // buffer it doesn't own. + avifBool partialMergedExtents; // If true, mergedExtents doesn't have all of the item data yet + uint32_t thumbnailForID; // if non-zero, this item is a thumbnail for Item #{thumbnailForID} + uint32_t auxForID; // if non-zero, this item is an auxC plane for Item #{auxForID} + uint32_t descForID; // if non-zero, this item is a content description for Item #{descForID} + uint32_t dimgForID; // if non-zero, this item is an input of derived Item #{dimgForID} + uint32_t dimgIdx; // If dimgForId is non-zero, this is the zero-based index of this item in the list of Item #{dimgForID}'s dimg. + avifBool hasDimgFrom; // whether there is a 'dimg' box with this item's id as 'fromID' + uint32_t premByID; // if non-zero, this item is premultiplied by Item #{premByID} + avifBool hasUnsupportedEssentialProperty; // If true, this item cites a property flagged as 'essential' that libavif doesn't support (yet). Ignore the item, if so. + avifBool ipmaSeen; // if true, this item already received a property association + avifBool progressive; // if true, this item has progressive layers (a1lx), but does not select a specific layer (the layer_id value in lsel is set to 0xFFFF) +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + avifPixelFormat miniBoxPixelFormat; // Set from the MinimizedImageBox, if present (AVIF_PIXEL_FORMAT_NONE otherwise) + avifChromaSamplePosition miniBoxChromaSamplePosition; // Set from the MinimizedImageBox, if present (AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN otherwise) +#endif +} avifDecoderItem; +AVIF_ARRAY_DECLARE(avifDecoderItemArray, avifDecoderItem *, item); + +// grid storage +typedef struct avifImageGrid +{ + uint32_t rows; // Legal range: [1-256] + uint32_t columns; // Legal range: [1-256] + uint32_t outputWidth; + uint32_t outputHeight; +} avifImageGrid; + +// --------------------------------------------------------------------------- +// avifTrack + +typedef struct avifSampleTableChunk +{ + uint64_t offset; +} avifSampleTableChunk; +AVIF_ARRAY_DECLARE(avifSampleTableChunkArray, avifSampleTableChunk, chunk); + +typedef struct avifSampleTableSampleToChunk +{ + uint32_t firstChunk; + uint32_t samplesPerChunk; + uint32_t sampleDescriptionIndex; +} avifSampleTableSampleToChunk; +AVIF_ARRAY_DECLARE(avifSampleTableSampleToChunkArray, avifSampleTableSampleToChunk, sampleToChunk); + +typedef struct avifSampleTableSampleSize +{ + uint32_t size; +} avifSampleTableSampleSize; +AVIF_ARRAY_DECLARE(avifSampleTableSampleSizeArray, avifSampleTableSampleSize, sampleSize); + +typedef struct avifSampleTableTimeToSample +{ + uint32_t sampleCount; + uint32_t sampleDelta; +} avifSampleTableTimeToSample; +AVIF_ARRAY_DECLARE(avifSampleTableTimeToSampleArray, avifSampleTableTimeToSample, timeToSample); + +typedef struct avifSyncSample +{ + uint32_t sampleNumber; +} avifSyncSample; +AVIF_ARRAY_DECLARE(avifSyncSampleArray, avifSyncSample, syncSample); + +typedef struct avifSampleDescription +{ + uint8_t format[4]; + avifPropertyArray properties; +} avifSampleDescription; +AVIF_ARRAY_DECLARE(avifSampleDescriptionArray, avifSampleDescription, description); + +typedef struct avifSampleTable +{ + avifSampleTableChunkArray chunks; + avifSampleDescriptionArray sampleDescriptions; + avifSampleTableSampleToChunkArray sampleToChunks; + avifSampleTableSampleSizeArray sampleSizes; + avifSampleTableTimeToSampleArray timeToSamples; + avifSyncSampleArray syncSamples; + uint32_t allSamplesSize; // If this is non-zero, sampleSizes will be empty and all samples will be this size +} avifSampleTable; + +static void avifSampleTableDestroy(avifSampleTable * sampleTable); + +static avifSampleTable * avifSampleTableCreate(void) +{ + avifSampleTable * sampleTable = (avifSampleTable *)avifAlloc(sizeof(avifSampleTable)); + if (sampleTable == NULL) { + return NULL; + } + memset(sampleTable, 0, sizeof(avifSampleTable)); + if (!avifArrayCreate(&sampleTable->chunks, sizeof(avifSampleTableChunk), 16) || + !avifArrayCreate(&sampleTable->sampleDescriptions, sizeof(avifSampleDescription), 2) || + !avifArrayCreate(&sampleTable->sampleToChunks, sizeof(avifSampleTableSampleToChunk), 16) || + !avifArrayCreate(&sampleTable->sampleSizes, sizeof(avifSampleTableSampleSize), 16) || + !avifArrayCreate(&sampleTable->timeToSamples, sizeof(avifSampleTableTimeToSample), 16) || + !avifArrayCreate(&sampleTable->syncSamples, sizeof(avifSyncSample), 16)) { + avifSampleTableDestroy(sampleTable); + return NULL; + } + return sampleTable; +} + +static void avifPropertyArrayDestroy(avifPropertyArray * array) +{ + for (size_t i = 0; i < array->count; ++i) { + if (array->prop[i].isOpaque) { + avifRWDataFree(&array->prop[i].u.opaque.boxPayload); + } + } + avifArrayDestroy(array); +} + +static void avifSampleTableDestroy(avifSampleTable * sampleTable) +{ + avifArrayDestroy(&sampleTable->chunks); + for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) { + avifSampleDescription * description = &sampleTable->sampleDescriptions.description[i]; + avifPropertyArrayDestroy(&description->properties); + } + avifArrayDestroy(&sampleTable->sampleDescriptions); + avifArrayDestroy(&sampleTable->sampleToChunks); + avifArrayDestroy(&sampleTable->sampleSizes); + avifArrayDestroy(&sampleTable->timeToSamples); + avifArrayDestroy(&sampleTable->syncSamples); + avifFree(sampleTable); +} + +static uint32_t avifSampleTableGetImageDelta(const avifSampleTable * sampleTable, uint32_t imageIndex) +{ + uint32_t maxSampleIndex = 0; + for (uint32_t i = 0; i < sampleTable->timeToSamples.count; ++i) { + const avifSampleTableTimeToSample * timeToSample = &sampleTable->timeToSamples.timeToSample[i]; + maxSampleIndex += timeToSample->sampleCount; + if ((imageIndex < maxSampleIndex) || (i == (sampleTable->timeToSamples.count - 1))) { + return timeToSample->sampleDelta; + } + } + + // TODO: fail here? + return 1; +} + +static avifCodecType avifSampleTableGetCodecType(const avifSampleTable * sampleTable) +{ + for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) { + const avifCodecType codecType = avifGetCodecType(sampleTable->sampleDescriptions.description[i].format); + if (codecType != AVIF_CODEC_TYPE_UNKNOWN) { + return codecType; + } + } + return AVIF_CODEC_TYPE_UNKNOWN; +} + +static uint32_t avifCodecConfigurationBoxGetDepth(const avifCodecConfigurationBox * av1C) +{ + if (av1C->twelveBit) { + return 12; + } else if (av1C->highBitdepth) { + return 10; + } + return 8; +} + +#if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI) +uint8_t avifCodecConfigurationBoxGetSubsamplingType(const avifCodecConfigurationBox * av1C, uint8_t channelIndex) +{ + if (channelIndex == 0) { + return AVIF_PIXI_444; + } + if (av1C->chromaSubsamplingX == 0) { + if (av1C->chromaSubsamplingY == 0) { + return AVIF_PIXI_444; + } + return AVIF_PIXI_440; + } + if (av1C->chromaSubsamplingY == 0) { + return AVIF_PIXI_422; + } + return AVIF_PIXI_420; +} + +// Mapping from PixelInformationBox subsampling_type and subsampling_location as defined in ISO/IEC 23008-12:2024/CDAM 2:2025 section 6.5.6.3 +// to chroma_sample_position as defined in AV1 specification Section 6.4.2. +static uint8_t avifSubsamplingLocationToChromaSamplePosition(uint8_t subsamplingType, uint8_t subsamplingLocation) +{ + if (subsamplingType == AVIF_PIXI_444) { + return AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + } + if (subsamplingType == AVIF_PIXI_422) { + if (subsamplingLocation == 0 || subsamplingLocation == 2 || subsamplingLocation == 4) { + return AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + } + } + if (subsamplingType == AVIF_PIXI_420) { + if (subsamplingLocation == 0) { + return AVIF_CHROMA_SAMPLE_POSITION_VERTICAL; + } + if (subsamplingLocation == 2) { + return AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + } + } + if (subsamplingType == AVIF_PIXI_411) { + if (subsamplingLocation == 0 || subsamplingLocation == 2 || subsamplingLocation == 4) { + return AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + } + } + if (subsamplingType == AVIF_PIXI_440) { + if (subsamplingLocation == 0 || subsamplingLocation == 1) { + return AVIF_CHROMA_SAMPLE_POSITION_VERTICAL; + } + if (subsamplingLocation == 2 || subsamplingLocation == 3) { + return AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + } + } + return AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; +} +#endif // AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI + +static const avifPropertyArray * avifSampleTableGetProperties(const avifSampleTable * sampleTable, avifCodecType codecType) +{ + for (uint32_t i = 0; i < sampleTable->sampleDescriptions.count; ++i) { + const avifSampleDescription * description = &sampleTable->sampleDescriptions.description[i]; + if (avifGetCodecType(description->format) == codecType) { + return &description->properties; + } + } + return NULL; +} + +// one video track ("trak" contents) +typedef struct avifTrack +{ + uint32_t id; + uint8_t handlerType[4]; + uint32_t auxForID; // if non-zero, this track is an auxC plane for Track #{auxForID} + uint32_t premByID; // if non-zero, this track is premultiplied by Track #{premByID} + uint32_t mediaTimescale; + uint64_t mediaDuration; + uint64_t trackDuration; + uint64_t segmentDuration; + avifBool isRepeating; + int repetitionCount; + uint32_t width; + uint32_t height; + avifSampleTable * sampleTable; + struct avifMeta * meta; +} avifTrack; +AVIF_ARRAY_DECLARE(avifTrackArray, avifTrack, track); + +// --------------------------------------------------------------------------- +// avifCodecDecodeInput + +avifCodecDecodeInput * avifCodecDecodeInputCreate(void) +{ + avifCodecDecodeInput * decodeInput = (avifCodecDecodeInput *)avifAlloc(sizeof(avifCodecDecodeInput)); + if (decodeInput == NULL) { + return NULL; + } + memset(decodeInput, 0, sizeof(avifCodecDecodeInput)); + if (!avifArrayCreate(&decodeInput->samples, sizeof(avifDecodeSample), 1)) { + avifFree(decodeInput); + return NULL; + } + return decodeInput; +} + +void avifCodecDecodeInputDestroy(avifCodecDecodeInput * decodeInput) +{ + for (uint32_t sampleIndex = 0; sampleIndex < decodeInput->samples.count; ++sampleIndex) { + avifDecodeSample * sample = &decodeInput->samples.sample[sampleIndex]; + if (sample->ownsData) { + avifRWDataFree((avifRWData *)&sample->data); + } + } + avifArrayDestroy(&decodeInput->samples); + avifFree(decodeInput); +} + +// Returns how many samples are in the chunk. +static uint32_t avifGetSampleCountOfChunk(const avifSampleTableSampleToChunkArray * sampleToChunks, uint32_t chunkIndex) +{ + uint32_t sampleCount = 0; + for (int sampleToChunkIndex = sampleToChunks->count - 1; sampleToChunkIndex >= 0; --sampleToChunkIndex) { + const avifSampleTableSampleToChunk * sampleToChunk = &sampleToChunks->sampleToChunk[sampleToChunkIndex]; + if (sampleToChunk->firstChunk <= (chunkIndex + 1)) { + sampleCount = sampleToChunk->samplesPerChunk; + break; + } + } + return sampleCount; +} + +static avifResult avifCodecDecodeInputFillFromSampleTable(avifCodecDecodeInput * decodeInput, + avifSampleTable * sampleTable, + const uint32_t imageCountLimit, + const uint64_t sizeHint, + avifDiagnostics * diag) +{ + if (imageCountLimit) { + // Verify that the we're not about to exceed the frame count limit. + + uint32_t imageCountLeft = imageCountLimit; + for (uint32_t chunkIndex = 0; chunkIndex < sampleTable->chunks.count; ++chunkIndex) { + // First, figure out how many samples are in this chunk + uint32_t sampleCount = avifGetSampleCountOfChunk(&sampleTable->sampleToChunks, chunkIndex); + if (sampleCount == 0) { + // chunks with 0 samples are invalid + avifDiagnosticsPrintf(diag, "Sample table contains a chunk with 0 samples"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + if (sampleCount > imageCountLeft) { + // This file exceeds the imageCountLimit, bail out + avifDiagnosticsPrintf(diag, "Exceeded avifDecoder's imageCountLimit"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + imageCountLeft -= sampleCount; + } + } + + uint32_t sampleSizeIndex = 0; + for (uint32_t chunkIndex = 0; chunkIndex < sampleTable->chunks.count; ++chunkIndex) { + avifSampleTableChunk * chunk = &sampleTable->chunks.chunk[chunkIndex]; + + // First, figure out how many samples are in this chunk + uint32_t sampleCount = avifGetSampleCountOfChunk(&sampleTable->sampleToChunks, chunkIndex); + if (sampleCount == 0) { + // chunks with 0 samples are invalid + avifDiagnosticsPrintf(diag, "Sample table contains a chunk with 0 samples"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + uint64_t sampleOffset = chunk->offset; + for (uint32_t sampleIndex = 0; sampleIndex < sampleCount; ++sampleIndex) { + uint32_t sampleSize = sampleTable->allSamplesSize; + if (sampleSize == 0) { + if (sampleSizeIndex >= sampleTable->sampleSizes.count) { + // We've run out of samples to sum + avifDiagnosticsPrintf(diag, "Truncated sample table"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + avifSampleTableSampleSize * sampleSizePtr = &sampleTable->sampleSizes.sampleSize[sampleSizeIndex]; + sampleSize = sampleSizePtr->size; + } + + avifDecodeSample * sample = (avifDecodeSample *)avifArrayPush(&decodeInput->samples); + AVIF_CHECKERR(sample != NULL, AVIF_RESULT_OUT_OF_MEMORY); + sample->offset = sampleOffset; + sample->size = sampleSize; + sample->spatialID = AVIF_SPATIAL_ID_UNSET; // Not filtering by spatial_id + sample->sync = AVIF_FALSE; // to potentially be set to true following the outer loop + + if (sampleSize > UINT64_MAX - sampleOffset) { + avifDiagnosticsPrintf(diag, + "Sample table contains an offset/size pair which overflows: [%" PRIu64 " / %u]", + sampleOffset, + sampleSize); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + if (sizeHint && ((sampleOffset + sampleSize) > sizeHint)) { + avifDiagnosticsPrintf(diag, "Exceeded avifIO's sizeHint, possibly truncated data"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + sampleOffset += sampleSize; + ++sampleSizeIndex; + } + } + + // Mark appropriate samples as sync + for (uint32_t syncSampleIndex = 0; syncSampleIndex < sampleTable->syncSamples.count; ++syncSampleIndex) { + uint32_t frameIndex = sampleTable->syncSamples.syncSample[syncSampleIndex].sampleNumber - 1; // sampleNumber is 1-based + if (frameIndex < decodeInput->samples.count) { + decodeInput->samples.sample[frameIndex].sync = AVIF_TRUE; + } + } + + // Assume frame 0 is sync, just in case the stss box is absent in the BMFF. (Unnecessary?) + if (decodeInput->samples.count > 0) { + decodeInput->samples.sample[0].sync = AVIF_TRUE; + } + return AVIF_RESULT_OK; +} + +static avifResult avifCodecDecodeInputFillFromDecoderItem(avifCodecDecodeInput * decodeInput, + avifDecoderItem * item, + avifBool allowProgressive, + const uint32_t imageCountLimit, + const uint64_t sizeHint, + avifDiagnostics * diag) +{ + if (sizeHint && (item->size > sizeHint)) { + avifDiagnosticsPrintf(diag, "Exceeded avifIO's sizeHint, possibly truncated data"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + uint8_t layerCount = 0; + size_t layerSizes[4] = { 0 }; + const avifProperty * a1lxProp = avifPropertyArrayFind(&item->properties, "a1lx"); + if (a1lxProp) { + // Calculate layer count and all layer sizes from the a1lx box, and then validate + + size_t remainingSize = item->size; + for (int i = 0; i < 3; ++i) { + ++layerCount; + + const size_t layerSize = (size_t)a1lxProp->u.a1lx.layerSize[i]; + if (layerSize) { + if (layerSize >= remainingSize) { // >= instead of > because there must be room for the last layer + avifDiagnosticsPrintf(diag, "a1lx layer index [%d] does not fit in item size", i); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + layerSizes[i] = layerSize; + remainingSize -= layerSize; + } else { + layerSizes[i] = remainingSize; + remainingSize = 0; + break; + } + } + if (remainingSize > 0) { + AVIF_ASSERT_OR_RETURN(layerCount == 3); + ++layerCount; + layerSizes[3] = remainingSize; + } + } + + const avifProperty * lselProp = avifPropertyArrayFind(&item->properties, "lsel"); + // Progressive images offer layers via the a1lxProp, but don't specify a layer selection with lsel. + // + // For backward compatibility with earlier drafts of AVIF spec v1.1.0, treat an absent lsel as + // equivalent to layer_id == 0xFFFF during the transitional period. Remove !lselProp when the test + // images have been updated to the v1.1.0 spec. + item->progressive = (a1lxProp && (!lselProp || (lselProp->u.lsel.layerID == 0xFFFF))); + if (lselProp && (lselProp->u.lsel.layerID != 0xFFFF)) { + // Layer selection. This requires that the underlying AV1 codec decodes all layers, + // and then only returns the requested layer as a single frame. To the user of libavif, + // this appears to be a single frame. + + decodeInput->allLayers = AVIF_TRUE; + + size_t sampleSize = 0; + if (layerCount > 0) { + // Optimization: If we're selecting a layer that doesn't require the entire image's payload (hinted via the a1lx box) + + if (lselProp->u.lsel.layerID >= layerCount) { + avifDiagnosticsPrintf(diag, + "lsel property requests layer index [%u] which isn't present in a1lx property ([%u] layers)", + lselProp->u.lsel.layerID, + layerCount); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + for (uint8_t i = 0; i <= lselProp->u.lsel.layerID; ++i) { + sampleSize += layerSizes[i]; + } + } else { + // This layer's payload subsection is unknown, just use the whole payload + sampleSize = item->size; + } + + avifDecodeSample * sample = (avifDecodeSample *)avifArrayPush(&decodeInput->samples); + AVIF_CHECKERR(sample != NULL, AVIF_RESULT_OUT_OF_MEMORY); + sample->itemID = item->id; + sample->offset = 0; + sample->size = sampleSize; + AVIF_ASSERT_OR_RETURN(lselProp->u.lsel.layerID < AVIF_MAX_AV1_LAYER_COUNT); + sample->spatialID = (uint8_t)lselProp->u.lsel.layerID; + sample->sync = AVIF_TRUE; + } else if (allowProgressive && item->progressive) { + // Progressive image. Decode all layers and expose them all to the user. + + if (imageCountLimit && (layerCount > imageCountLimit)) { + avifDiagnosticsPrintf(diag, "Exceeded avifDecoder's imageCountLimit (progressive)"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + decodeInput->allLayers = AVIF_TRUE; + + size_t offset = 0; + for (int i = 0; i < layerCount; ++i) { + avifDecodeSample * sample = (avifDecodeSample *)avifArrayPush(&decodeInput->samples); + AVIF_CHECKERR(sample != NULL, AVIF_RESULT_OUT_OF_MEMORY); + sample->itemID = item->id; + sample->offset = offset; + sample->size = layerSizes[i]; + sample->spatialID = AVIF_SPATIAL_ID_UNSET; + sample->sync = (i == 0); // Assume all layers depend on the first layer + + offset += layerSizes[i]; + } + } else { + // Typical case: Use the entire item's payload for a single frame output + + avifDecodeSample * sample = (avifDecodeSample *)avifArrayPush(&decodeInput->samples); + AVIF_CHECKERR(sample != NULL, AVIF_RESULT_OUT_OF_MEMORY); + sample->itemID = item->id; + sample->offset = 0; + sample->size = item->size; + sample->spatialID = AVIF_SPATIAL_ID_UNSET; + sample->sync = AVIF_TRUE; + } + return AVIF_RESULT_OK; +} + +// --------------------------------------------------------------------------- +// Helper macros / functions + +#define BEGIN_STREAM(VARNAME, PTR, SIZE, DIAG, CONTEXT) \ + avifROStream VARNAME; \ + avifROData VARNAME##_roData; \ + VARNAME##_roData.data = PTR; \ + VARNAME##_roData.size = SIZE; \ + avifROStreamStart(&VARNAME, &VARNAME##_roData, DIAG, CONTEXT) + +typedef enum avifUniqueBoxFlag +{ + AVIF_UNIQUE_ILOC = 0, + AVIF_UNIQUE_PITM, + AVIF_UNIQUE_IDAT, + AVIF_UNIQUE_IPRP, + AVIF_UNIQUE_IINF, + AVIF_UNIQUE_IREF, + AVIF_UNIQUE_GRPL, +} avifUniqueBoxFlag; +// Use this to keep track of whether or not a child box that must be unique (0 or 1 present) has +// been seen yet, when parsing a parent box. If the "seen" bit is already set for a given box when +// it is encountered during parse, an error is thrown. Which bit corresponds to which box is +// dictated entirely by the calling function. +static avifBool uniqueBoxSeen(uint32_t * uniqueBoxFlags, + avifUniqueBoxFlag whichFlag, + const char * parentBoxType, + const char * boxType, + avifDiagnostics * diagnostics) +{ + const uint32_t flag = 1 << whichFlag; + if (*uniqueBoxFlags & flag) { + // This box has already been seen. Error! + avifDiagnosticsPrintf(diagnostics, "Box[%s] contains a duplicate unique box of type '%s'", parentBoxType, boxType); + return AVIF_FALSE; + } + + // Mark this box as seen. + *uniqueBoxFlags |= flag; + return AVIF_TRUE; +} + +// --------------------------------------------------------------------------- +// avifDecoderData + +typedef struct avifTile +{ + avifCodecDecodeInput * input; + avifCodecType codecType; + // This may point to a codec that it owns or point to a shared codec that it does not own. In the shared case, this will + // point to one of the avifCodec instances in avifDecoderData. + struct avifCodec * codec; + avifImage * image; + uint32_t width; // Either avifTrack.width or avifDecoderItem.width + uint32_t height; // Either avifTrack.height or avifDecoderItem.height + uint8_t operatingPoint; +} avifTile; +AVIF_ARRAY_DECLARE(avifTileArray, avifTile, tile); + +// This holds one "meta" box (from the BMFF and HEIF standards) worth of relevant-to-AVIF information. +// * If a meta box is parsed from the root level of the BMFF, it can contain the information about +// "items" which might be color planes, alpha planes, or EXIF or XMP metadata. +// * If a meta box is parsed from inside of a track ("trak") box, any metadata (EXIF/XMP) items inside +// of that box are implicitly associated with that track. +typedef struct avifMeta +{ + // Items (from HEIF) are the generic storage for any data that does not require timed processing + // (single image color planes, alpha planes, EXIF, XMP, etc). Each item has a unique integer ID >1, + // and is defined by a series of child boxes in a meta box: + // * iloc - location: byte offset to item data, item size in bytes + // * iinf - information: type of item (color planes, alpha plane, EXIF, XMP) + // * ipco - properties: dimensions, aspect ratio, image transformations, references to other items + // * ipma - associations: Attaches an item in the properties list to a given item + // + // Items are lazily created in this array when any of the above boxes refer to one by a new (unseen) ID, + // and are then further modified/updated as new information for an item's ID is parsed. + avifDecoderItemArray items; + + // Any ipco boxes explained above are populated into this array as a staging area, which are + // then duplicated into the appropriate items upon encountering an item property association + // (ipma) box. + avifPropertyArray properties; + + // Filled with the contents of this meta box's "idat" box, which is raw data that an item can + // directly refer to in its item location box (iloc) instead of just giving an offset into the + // overall file. If all items' iloc boxes simply point at an offset/length in the file itself, + // this buffer will likely be empty. + avifRWData idat; + + // Ever-incrementing ID for uniquely identifying which 'meta' box contains an idat (when + // multiple meta boxes exist as BMFF siblings). Each time avifParseMetaBox() is called on an + // avifMeta struct, this value is incremented. Any time an additional meta box is detected at + // the same "level" (root level, trak level, etc), this ID helps distinguish which meta box's + // "idat" is which, as items implicitly reference idat boxes that exist in the same meta + // box. + uint32_t idatID; + + // Contents of a pitm box, which signal which of the items in this file is the main image. For + // AVIF, this should point at an image item containing color planes, and all other items + // are ignored unless they refer to this item in some way (alpha plane, EXIF/XMP metadata). + uint32_t primaryItemID; + + // Contents of grpl box, which signal groups of entities (items or tracks). + avifEntityToGroups entityToGroups; + + // Parsed from Sample Transform metadata if present, otherwise empty. + avifSampleTransformExpression sampleTransformExpression; + // Bit depth extracted from the pixi property of the Sample Transform derived image item, if any. + uint32_t sampleTransformDepth; + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + // If true, the fields above were extracted from a MinimizedImageBox. + avifBool fromMiniBox; +#endif +} avifMeta; + +static void avifMetaDestroy(avifMeta * meta); + +static avifMeta * avifMetaCreate(void) +{ + avifMeta * meta = (avifMeta *)avifAlloc(sizeof(avifMeta)); + if (meta == NULL) { + return NULL; + } + memset(meta, 0, sizeof(avifMeta)); + if (!avifArrayCreate(&meta->items, sizeof(avifDecoderItem *), 8) || !avifArrayCreate(&meta->properties, sizeof(avifProperty), 16) || + !avifArrayCreate(&meta->entityToGroups, sizeof(avifEntityToGroup), 1)) { + avifMetaDestroy(meta); + return NULL; + } + return meta; +} + +static void avifMetaDestroy(avifMeta * meta) +{ + for (uint32_t i = 0; i < meta->items.count; ++i) { + avifDecoderItem * item = meta->items.item[i]; + avifPropertyArrayDestroy(&item->properties); + avifArrayDestroy(&item->extents); + if (item->ownsMergedExtents) { + avifRWDataFree(&item->mergedExtents); + } + avifFree(item); + } + avifArrayDestroy(&meta->items); + avifPropertyArrayDestroy(&meta->properties); + avifRWDataFree(&meta->idat); + avifArrayDestroy(&meta->sampleTransformExpression); + for (uint32_t i = 0; i < meta->entityToGroups.count; ++i) { + avifArrayDestroy(&meta->entityToGroups.groups[i].entityIDs); + } + avifArrayDestroy(&meta->entityToGroups); + avifFree(meta); +} + +static avifResult avifCheckItemID(const char * boxFourcc, uint32_t itemID, avifDiagnostics * diag) +{ + // Section 8.11.1.1 of ISO/IEC 14496-12 about MetaBox definition: + // The item_ID value of 0 should not be used + // Section 8.11.6 of ISO/IEC 14496-12 about ItemInfoEntry syntax and semantics: + // item_ID contains either 0 for the primary resource (e.g. the XML contained in an XMLBox) + // or the ID of the item for which the following information is defined. + // Assuming 'infe' is the only way to properly define an item in AVIF, a compliant item cannot have an ID of zero. + // One way to bypass that rule would be to have 'infe' with item_ID being 0, referring to "the primary resource", + // and 'pitm' defining "the primary resource" as the item with an item_ID of 0. libavif considers that as invalid. + if (itemID == 0) { + avifDiagnosticsPrintf(diag, "Box[%.4s] has an invalid item ID [%u]", boxFourcc, itemID); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + return AVIF_RESULT_OK; +} + +static avifResult avifMetaFindOrCreateItem(avifMeta * meta, uint32_t itemID, avifDecoderItem ** item) +{ + *item = NULL; + AVIF_ASSERT_OR_RETURN(itemID != 0); + + for (uint32_t i = 0; i < meta->items.count; ++i) { + if (meta->items.item[i]->id == itemID) { + *item = meta->items.item[i]; + return AVIF_RESULT_OK; + } + } + + avifDecoderItem ** itemPtr = (avifDecoderItem **)avifArrayPush(&meta->items); + AVIF_CHECKERR(itemPtr != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *item = (avifDecoderItem *)avifAlloc(sizeof(avifDecoderItem)); + if (*item == NULL) { + avifArrayPop(&meta->items); + return AVIF_RESULT_OUT_OF_MEMORY; + } + memset(*item, 0, sizeof(avifDecoderItem)); + + *itemPtr = *item; + if (!avifArrayCreate(&(*item)->properties, sizeof(avifProperty), 16)) { + avifFree(*item); + *item = NULL; + avifArrayPop(&meta->items); + return AVIF_RESULT_OUT_OF_MEMORY; + } + if (!avifArrayCreate(&(*item)->extents, sizeof(avifExtent), 1)) { + avifPropertyArrayDestroy(&(*item)->properties); + avifFree(*item); + *item = NULL; + avifArrayPop(&meta->items); + return AVIF_RESULT_OUT_OF_MEMORY; + } + (*item)->id = itemID; + (*item)->meta = meta; + return AVIF_RESULT_OK; +} + +// A group of AVIF tiles in an image item, such as a single tile or a grid of multiple tiles. +typedef struct avifTileInfo +{ + unsigned int tileCount; + unsigned int decodedTileCount; + unsigned int firstTileIndex; // Within avifDecoderData.tiles. + avifImageGrid grid; +} avifTileInfo; + +typedef struct avifDecoderData +{ + avifMeta * meta; // The root-level meta box + avifTrackArray tracks; + avifTileArray tiles; + avifTileInfo tileInfos[AVIF_ITEM_CATEGORY_COUNT]; + avifDecoderSource source; + // When decoding AVIF images with grid, use a single decoder instance for all the tiles instead of creating a decoder instance + // for each tile. If that is the case, |codec| will be used by all the tiles. + // + // There are some edge cases where we will still need multiple decoder instances: + // * For animated AVIF with alpha, we will need two instances (one for the color planes and one for the alpha plane since they are both + // encoded as separate video sequences). In this case, |codec| will be used for the color planes and |codecAlpha| will be + // used for the alpha plane. + // * For grid images with multiple layers. In this case, each tile will need its own decoder instance since there would be + // multiple layers in each tile. In this case, |codec| and |codecAlpha| are not used and each tile will have its own + // decoder instance. + // * For grid images where the operating points of all the tiles are not the same. In this case, each tile needs its own + // decoder instance (same as above). + avifCodec * codec; + avifCodec * codecAlpha; + uint8_t majorBrand[4]; // From the file's ftyp, used by AVIF_DECODER_SOURCE_AUTO + avifBrandArray compatibleBrands; // From the file's ftyp + avifDiagnostics * diag; // Shallow copy; owned by avifDecoder + const avifSampleTable * sourceSampleTable; // NULL unless (source == AVIF_DECODER_SOURCE_TRACKS), owned by an avifTrack + avifBool cicpSet; // True if avifDecoder's image has had its CICP set correctly yet. + // This allows nclx colr boxes to override AV1 CICP, as specified in the MIAF + // standard (ISO/IEC 23000-22:2019), section 7.3.6.4: + // The colour information property takes precedence over any colour information + // in the image bitstream, i.e. if the property is present, colour information in + // the bitstream shall be ignored. + + // Remember the dimg association order to the Sample Transform derived image item. + // Colour items only. The alpha items are implicit. + uint8_t sampleTransformNumInputImageItems; // At most AVIF_SAMPLE_TRANSFORM_MAX_NUM_INPUT_IMAGE_ITEMS. + avifItemCategory sampleTransformInputImageItems[AVIF_SAMPLE_TRANSFORM_MAX_NUM_INPUT_IMAGE_ITEMS]; +} avifDecoderData; + +static void avifDecoderDataDestroy(avifDecoderData * data); + +static avifDecoderData * avifDecoderDataCreate(void) +{ + avifDecoderData * data = (avifDecoderData *)avifAlloc(sizeof(avifDecoderData)); + if (data == NULL) { + return NULL; + } + memset(data, 0, sizeof(avifDecoderData)); + data->meta = avifMetaCreate(); + if (data->meta == NULL || !avifArrayCreate(&data->tracks, sizeof(avifTrack), 2) || + !avifArrayCreate(&data->tiles, sizeof(avifTile), 8)) { + avifDecoderDataDestroy(data); + return NULL; + } + return data; +} + +static void avifDecoderDataResetCodec(avifDecoderData * data) +{ + for (unsigned int i = 0; i < data->tiles.count; ++i) { + avifTile * tile = &data->tiles.tile[i]; + if (tile->image) { + avifImageFreePlanes(tile->image, AVIF_PLANES_ALL); // forget any pointers into codec image buffers + } + if (tile->codec) { + // Check if tile->codec was created separately and destroy it in that case. + if (tile->codec != data->codec && tile->codec != data->codecAlpha) { + avifCodecDestroy(tile->codec); + } + tile->codec = NULL; + } + } + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + data->tileInfos[c].decodedTileCount = 0; + } + if (data->codec) { + avifCodecDestroy(data->codec); + data->codec = NULL; + } + if (data->codecAlpha) { + avifCodecDestroy(data->codecAlpha); + data->codecAlpha = NULL; + } +} + +static avifTile * avifDecoderDataCreateTile(avifDecoderData * data, avifCodecType codecType, uint32_t width, uint32_t height, uint8_t operatingPoint) +{ + avifTile * tile = (avifTile *)avifArrayPush(&data->tiles); + if (tile == NULL) { + return NULL; + } + tile->codecType = codecType; + tile->image = avifImageCreateEmpty(); + if (!tile->image) { + goto error; + } + tile->input = avifCodecDecodeInputCreate(); + if (!tile->input) { + goto error; + } + tile->width = width; + tile->height = height; + tile->operatingPoint = operatingPoint; + return tile; + +error: + if (tile->input) { + avifCodecDecodeInputDestroy(tile->input); + } + if (tile->image) { + avifImageDestroy(tile->image); + } + avifArrayPop(&data->tiles); + return NULL; +} + +static avifTrack * avifDecoderDataCreateTrack(avifDecoderData * data) +{ + avifTrack * track = (avifTrack *)avifArrayPush(&data->tracks); + if (track == NULL) { + return NULL; + } + track->meta = avifMetaCreate(); + if (track->meta == NULL) { + avifArrayPop(&data->tracks); + return NULL; + } + return track; +} + +static void avifDecoderDataClearTiles(avifDecoderData * data) +{ + for (unsigned int i = 0; i < data->tiles.count; ++i) { + avifTile * tile = &data->tiles.tile[i]; + if (tile->input) { + avifCodecDecodeInputDestroy(tile->input); + tile->input = NULL; + } + if (tile->codec) { + // Check if tile->codec was created separately and destroy it in that case. + if (tile->codec != data->codec && tile->codec != data->codecAlpha) { + avifCodecDestroy(tile->codec); + } + tile->codec = NULL; + } + if (tile->image) { + avifImageDestroy(tile->image); + tile->image = NULL; + } + } + data->tiles.count = 0; + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + data->tileInfos[c].tileCount = 0; + data->tileInfos[c].decodedTileCount = 0; + } + if (data->codec) { + avifCodecDestroy(data->codec); + data->codec = NULL; + } + if (data->codecAlpha) { + avifCodecDestroy(data->codecAlpha); + data->codecAlpha = NULL; + } +} + +static void avifDecoderDataDestroy(avifDecoderData * data) +{ + if (data->meta) { + avifMetaDestroy(data->meta); + } + for (uint32_t i = 0; i < data->tracks.count; ++i) { + avifTrack * track = &data->tracks.track[i]; + if (track->sampleTable) { + avifSampleTableDestroy(track->sampleTable); + } + if (track->meta) { + avifMetaDestroy(track->meta); + } + } + avifArrayDestroy(&data->tracks); + avifDecoderDataClearTiles(data); + avifArrayDestroy(&data->tiles); + avifArrayDestroy(&data->compatibleBrands); + avifFree(data); +} + +// This returns the max extent that has to be read in order to decode this item. If +// the item is stored in an idat, the data has already been read during Parse() and +// this function will return AVIF_RESULT_OK with a 0-byte extent. +static avifResult avifDecoderItemMaxExtent(const avifDecoderItem * item, const avifDecodeSample * sample, avifExtent * outExtent) +{ + if (item->extents.count == 0) { + return AVIF_RESULT_TRUNCATED_DATA; + } + + if (item->idatStored) { + // construction_method: idat(1) + + if (item->meta->idat.size > 0) { + // Already read from a meta box during Parse() + memset(outExtent, 0, sizeof(avifExtent)); + return AVIF_RESULT_OK; + } + + // no associated idat box was found in the meta box, bail out + return AVIF_RESULT_NO_CONTENT; + } + + // construction_method: file(0) + + if (sample->size == 0) { + return AVIF_RESULT_TRUNCATED_DATA; + } + uint64_t remainingOffset = sample->offset; + size_t remainingBytes = sample->size; // This may be smaller than item->size if the item is progressive + + // Assert that the for loop below will execute at least one iteration. + AVIF_ASSERT_OR_RETURN(item->extents.count != 0); + uint64_t minOffset = UINT64_MAX; + uint64_t maxOffset = 0; + for (uint32_t extentIter = 0; extentIter < item->extents.count; ++extentIter) { + avifExtent * extent = &item->extents.extent[extentIter]; + + // Make local copies of extent->offset and extent->size as they might need to be adjusted + // due to the sample's offset. + uint64_t startOffset = extent->offset; + size_t extentSize = extent->size; + if (remainingOffset) { + if (remainingOffset >= extentSize) { + remainingOffset -= extentSize; + continue; + } else { + if (remainingOffset > UINT64_MAX - startOffset) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + startOffset += remainingOffset; + extentSize -= (size_t)remainingOffset; + remainingOffset = 0; + } + } + + const size_t usedExtentSize = (extentSize < remainingBytes) ? extentSize : remainingBytes; + + if (usedExtentSize > UINT64_MAX - startOffset) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + const uint64_t endOffset = startOffset + usedExtentSize; + + if (minOffset > startOffset) { + minOffset = startOffset; + } + if (maxOffset < endOffset) { + maxOffset = endOffset; + } + + remainingBytes -= usedExtentSize; + if (remainingBytes == 0) { + // We've got enough bytes for this sample. + break; + } + } + + if (remainingBytes != 0) { + return AVIF_RESULT_TRUNCATED_DATA; + } + + outExtent->offset = minOffset; + const uint64_t extentLength = maxOffset - minOffset; +#if UINT64_MAX > SIZE_MAX + if (extentLength > SIZE_MAX) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } +#endif + outExtent->size = (size_t)extentLength; + return AVIF_RESULT_OK; +} + +static uint8_t avifDecoderItemOperatingPoint(const avifDecoderItem * item) +{ + const avifProperty * a1opProp = avifPropertyArrayFind(&item->properties, "a1op"); + if (a1opProp) { + return a1opProp->u.a1op.opIndex; + } + return 0; // default +} + +static avifResult avifDecoderItemValidateProperties(const avifDecoderItem * item, + const char * configPropName, + avifDiagnostics * diag, + const avifStrictFlags strictFlags) +{ + const avifProperty * const configProp = avifPropertyArrayFind(&item->properties, configPropName); + if (!configProp) { + // An item configuration property box is mandatory in all valid AVIF configurations. Bail out. + avifDiagnosticsPrintf(diag, "Item ID %u of type '%.4s' is missing mandatory %s property", item->id, (const char *)item->type, configPropName); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + if (!memcmp(item->type, "grid", 4)) { + for (uint32_t i = 0; i < item->meta->items.count; ++i) { + avifDecoderItem * tile = item->meta->items.item[i]; + if (tile->dimgForID != item->id) { + continue; + } + // Tile item types were checked in avifDecoderGenerateImageTiles(), no need to do it here. + + // MIAF (ISO 23000-22:2019), Section 7.3.11.4.1: + // All input images of a grid image item shall use the same [...] chroma sampling format, + // and the same decoder configuration (see 7.3.6.2). + + // The chroma sampling format is part of the decoder configuration. + const avifProperty * tileConfigProp = avifPropertyArrayFind(&tile->properties, configPropName); + if (!tileConfigProp) { + avifDiagnosticsPrintf(diag, + "Tile item ID %u of type '%.4s' is missing mandatory %s property", + tile->id, + (const char *)tile->type, + configPropName); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + // configProp was copied from a tile item to the grid item. Comparing tileConfigProp with it + // is equivalent to comparing tileConfigProp with the configPropName from the first tile. + if ((tileConfigProp->u.av1C.seqProfile != configProp->u.av1C.seqProfile) || + (tileConfigProp->u.av1C.seqLevelIdx0 != configProp->u.av1C.seqLevelIdx0) || + (tileConfigProp->u.av1C.seqTier0 != configProp->u.av1C.seqTier0) || + (tileConfigProp->u.av1C.highBitdepth != configProp->u.av1C.highBitdepth) || + (tileConfigProp->u.av1C.twelveBit != configProp->u.av1C.twelveBit) || + (tileConfigProp->u.av1C.monochrome != configProp->u.av1C.monochrome) || + (tileConfigProp->u.av1C.chromaSubsamplingX != configProp->u.av1C.chromaSubsamplingX) || + (tileConfigProp->u.av1C.chromaSubsamplingY != configProp->u.av1C.chromaSubsamplingY) || + (tileConfigProp->u.av1C.chromaSamplePosition != configProp->u.av1C.chromaSamplePosition)) { + avifDiagnosticsPrintf(diag, + "The fields of the %s property of tile item ID %u of type '%.4s' differs from other tiles", + configPropName, + tile->id, + (const char *)tile->type); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } + } + + const avifProperty * pixiProp = avifPropertyArrayFind(&item->properties, "pixi"); + if (!pixiProp && (strictFlags & AVIF_STRICT_PIXI_REQUIRED)) { + // A pixi box is mandatory in all valid AVIF configurations. Bail out. + avifDiagnosticsPrintf(diag, + "[Strict] Item ID %u of type '%.4s' is missing mandatory pixi property", + item->id, + (const char *)item->type); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + if (pixiProp) { + const uint32_t configDepth = avifCodecConfigurationBoxGetDepth(&configProp->u.av1C); + for (uint8_t i = 0; i < pixiProp->u.pixi.planeCount; ++i) { + if (pixiProp->u.pixi.planeDepths[i] != configDepth) { + // pixi depth must match configuration property depth + avifDiagnosticsPrintf(diag, + "Item ID %u depth specified by pixi property [%u] does not match %s property depth [%u]", + item->id, + pixiProp->u.pixi.planeDepths[i], + configPropName, + configDepth); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } +#if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI) + if (pixiProp->u.pixi.subsamplingFlag[i]) { + if (pixiProp->u.pixi.subsamplingType[i] != avifCodecConfigurationBoxGetSubsamplingType(&configProp->u.av1C, i)) { + avifDiagnosticsPrintf(diag, + "Item ID %u subsampling type specified by pixi property [%u] for channel %u does not match %s property [%u,%u]", + item->id, + pixiProp->u.pixi.subsamplingType[i], + i, + configPropName, + configProp->u.av1C.chromaSubsamplingX, + configProp->u.av1C.chromaSubsamplingY); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + if (configProp->u.av1C.chromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN) { + const avifChromaSamplePosition expectedChromaSamplePosition = + i == AVIF_CHAN_Y ? AVIF_CHROMA_SAMPLE_POSITION_COLOCATED : configProp->u.av1C.chromaSamplePosition; + if (avifSubsamplingLocationToChromaSamplePosition(pixiProp->u.pixi.subsamplingType[i], + pixiProp->u.pixi.subsamplingLocation[i]) != + expectedChromaSamplePosition) { + avifDiagnosticsPrintf(diag, + "Item ID %u subsampling type and location specified by pixi property [%u,%u] for channel %u does not match %s property chroma sample position [%u]", + item->id, + pixiProp->u.pixi.subsamplingType[i], + pixiProp->u.pixi.subsamplingLocation[i], + i, + configPropName, + configProp->u.av1C.chromaSamplePosition); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } + } +#endif // AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI + } + } + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + if (item->miniBoxPixelFormat != AVIF_PIXEL_FORMAT_NONE) { + // This is a MinimizedImageBox ('mini'). + + avifPixelFormat av1CPixelFormat; + if (configProp->u.av1C.monochrome) { + av1CPixelFormat = AVIF_PIXEL_FORMAT_YUV400; + } else if (configProp->u.av1C.chromaSubsamplingY == 1) { + av1CPixelFormat = AVIF_PIXEL_FORMAT_YUV420; + } else if (configProp->u.av1C.chromaSubsamplingX == 1) { + av1CPixelFormat = AVIF_PIXEL_FORMAT_YUV422; + } else { + av1CPixelFormat = AVIF_PIXEL_FORMAT_YUV444; + } + if (item->miniBoxPixelFormat != av1CPixelFormat) { + avifDiagnosticsPrintf(diag, + "Item ID %u format [%s] specified by MinimizedImageBox does not match %s property format [%s]", + item->id, + avifPixelFormatToString(item->miniBoxPixelFormat), + configPropName, + avifPixelFormatToString(av1CPixelFormat)); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + if (configProp->u.av1C.chromaSamplePosition == /*CSP_UNKNOWN=*/0) { + // Section 6.4.2. Color config semantics of AV1 specification says: + // CSP_UNKNOWN - the source video transfer function must be signaled outside the AV1 bitstream + // See https://aomediacodec.github.io/av1-spec/#color-config-semantics + + // So item->miniBoxChromaSamplePosition can differ and will override the AV1 value. + } else if ((uint8_t)item->miniBoxChromaSamplePosition != configProp->u.av1C.chromaSamplePosition) { + avifDiagnosticsPrintf(diag, + "Item ID %u chroma sample position [%u] specified by MinimizedImageBox does not match %s property chroma sample position [%u]", + item->id, + (uint32_t)item->miniBoxChromaSamplePosition, + configPropName, + configProp->u.av1C.chromaSamplePosition); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + + if (strictFlags & AVIF_STRICT_CLAP_VALID) { + const avifProperty * clapProp = avifPropertyArrayFind(&item->properties, "clap"); + if (clapProp) { + const avifProperty * ispeProp = avifPropertyArrayFind(&item->properties, "ispe"); + if (!ispeProp) { + avifDiagnosticsPrintf(diag, + "[Strict] Item ID %u is missing an ispe property, so its clap property cannot be validated", + item->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + avifCropRect cropRect; + const uint32_t imageW = ispeProp->u.ispe.width; + const uint32_t imageH = ispeProp->u.ispe.height; + const avifBool validClap = avifCropRectFromCleanApertureBox(&cropRect, &clapProp->u.clap, imageW, imageH, diag); + if (!validClap) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifDecoderItemRead(avifDecoderItem * item, + avifIO * io, + avifROData * outData, + size_t offset, + size_t partialByteCount, + avifDiagnostics * diag) +{ + if (item->mergedExtents.data && !item->partialMergedExtents) { + // Multiple extents have already been concatenated for this item, just return it + if (offset >= item->mergedExtents.size) { + avifDiagnosticsPrintf(diag, "Item ID %u read has overflowing offset", item->id); + return AVIF_RESULT_TRUNCATED_DATA; + } + outData->data = item->mergedExtents.data + offset; + outData->size = item->mergedExtents.size - offset; + return AVIF_RESULT_OK; + } + + if (item->extents.count == 0) { + avifDiagnosticsPrintf(diag, "Item ID %u has zero extents", item->id); + return AVIF_RESULT_TRUNCATED_DATA; + } + + // Find this item's source of all extents' data, based on the construction method + const avifRWData * idatBuffer = NULL; + if (item->idatStored) { + // construction_method: idat(1) + + if (item->meta->idat.size > 0) { + idatBuffer = &item->meta->idat; + } else { + // no associated idat box was found in the meta box, bail out + avifDiagnosticsPrintf(diag, "Item ID %u is stored in an idat, but no associated idat box was found", item->id); + return AVIF_RESULT_NO_CONTENT; + } + } + + // Merge extents into a single contiguous buffer + if ((io->sizeHint > 0) && (item->size > io->sizeHint)) { + // Sanity check: somehow the sum of extents exceeds the entire file or idat size! + avifDiagnosticsPrintf(diag, "Item ID %u reported size failed size hint sanity check. Truncated data?", item->id); + return AVIF_RESULT_TRUNCATED_DATA; + } + + if (offset >= item->size) { + avifDiagnosticsPrintf(diag, "Item ID %u read has overflowing offset", item->id); + return AVIF_RESULT_TRUNCATED_DATA; + } + const size_t maxOutputSize = item->size - offset; + const size_t readOutputSize = (partialByteCount && (partialByteCount < maxOutputSize)) ? partialByteCount : maxOutputSize; + const size_t totalBytesToRead = offset + readOutputSize; + + // If there is a single extent for this item and the source of the read buffer is going to be + // persistent for the lifetime of the avifDecoder (whether it comes from its own internal + // idatBuffer or from a known-persistent IO), we can avoid buffer duplication and just use the + // preexisting buffer. + avifBool singlePersistentBuffer = ((item->extents.count == 1) && (idatBuffer || io->persistent)); + if (!singlePersistentBuffer) { + // Always allocate the item's full size here, as progressive image decodes will do partial + // reads into this buffer and begin feeding the buffer to the underlying AV1 decoder, but + // will then write more into this buffer without flushing the AV1 decoder (which is still + // holding the address of the previous allocation of this buffer). This strategy avoids + // use-after-free issues in the AV1 decoder and unnecessary reallocs as a typical + // progressive decode use case will eventually decode the final layer anyway. + AVIF_CHECKRES(avifRWDataRealloc(&item->mergedExtents, item->size)); + item->ownsMergedExtents = AVIF_TRUE; + } + + // Set this until we manage to fill the entire mergedExtents buffer + item->partialMergedExtents = AVIF_TRUE; + + size_t writeOffset = 0; // Write offset for item->mergedExtents.data + size_t remainingBytes = totalBytesToRead; + for (uint32_t extentIter = 0; extentIter < item->extents.count; ++extentIter) { + avifExtent * extent = &item->extents.extent[extentIter]; + + size_t bytesToRead = extent->size; + if (bytesToRead > remainingBytes) { + bytesToRead = remainingBytes; + } + + avifROData offsetBuffer; + if (idatBuffer) { + if (extent->offset > idatBuffer->size) { + avifDiagnosticsPrintf(diag, "Item ID %u has impossible extent offset in idat buffer", item->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + // Since extent->offset (a uint64_t) is not bigger than idatBuffer->size (a size_t), + // it is safe to cast extent->offset to size_t. + const size_t extentOffset = (size_t)extent->offset; + if (extent->size > idatBuffer->size - extentOffset) { + avifDiagnosticsPrintf(diag, "Item ID %u has impossible extent size in idat buffer", item->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + offsetBuffer.data = idatBuffer->data + extentOffset; + offsetBuffer.size = idatBuffer->size - extentOffset; + } else { + // construction_method: file(0) + + if ((io->sizeHint > 0) && (extent->offset > io->sizeHint)) { + avifDiagnosticsPrintf(diag, "Item ID %u extent offset failed size hint sanity check. Truncated data?", item->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + avifResult readResult = io->read(io, 0, extent->offset, bytesToRead, &offsetBuffer); + if (readResult != AVIF_RESULT_OK) { + return readResult; + } + if (bytesToRead != offsetBuffer.size) { + avifDiagnosticsPrintf(diag, + "Item ID %u tried to read %zu bytes, but only received %zu bytes", + item->id, + bytesToRead, + offsetBuffer.size); + return AVIF_RESULT_TRUNCATED_DATA; + } + } + + if (singlePersistentBuffer) { + item->mergedExtents.data = (uint8_t *)offsetBuffer.data; // const_cast + AVIF_ASSERT_OR_RETURN(bytesToRead <= offsetBuffer.size); + item->mergedExtents.size = bytesToRead; + } else { + AVIF_ASSERT_OR_RETURN(item->ownsMergedExtents); + AVIF_ASSERT_OR_RETURN(writeOffset < item->mergedExtents.size); + AVIF_ASSERT_OR_RETURN(bytesToRead <= item->mergedExtents.size - writeOffset); + memcpy(item->mergedExtents.data + writeOffset, offsetBuffer.data, bytesToRead); + writeOffset += bytesToRead; + } + + remainingBytes -= bytesToRead; + if (remainingBytes == 0) { + // This happens when partialByteCount is set + break; + } + } + if (remainingBytes != 0) { + // This should be impossible? + avifDiagnosticsPrintf(diag, "Item ID %u has %zu unexpected trailing bytes", item->id, remainingBytes); + return AVIF_RESULT_TRUNCATED_DATA; + } + + outData->data = item->mergedExtents.data + offset; + outData->size = readOutputSize; + item->partialMergedExtents = (item->size != totalBytesToRead); + return AVIF_RESULT_OK; +} + +// Returns the avifCodecType of the first tile of the gridItem. +static avifCodecType avifDecoderItemGetGridCodecType(const avifDecoderItem * gridItem) +{ + for (uint32_t i = 0; i < gridItem->meta->items.count; ++i) { + avifDecoderItem * item = gridItem->meta->items.item[i]; + const avifCodecType tileCodecType = avifGetCodecType(item->type); + if ((item->dimgForID == gridItem->id) && (tileCodecType != AVIF_CODEC_TYPE_UNKNOWN)) { + return tileCodecType; + } + } + return AVIF_CODEC_TYPE_UNKNOWN; +} + +// Fills the dimgIdxToItemIdx array with a mapping from each 0-based tile index in the 'dimg' reference +// to its corresponding 0-based index in the avifMeta::items array. +static avifResult avifFillDimgIdxToItemIdxArray(uint32_t * dimgIdxToItemIdx, uint32_t numExpectedTiles, const avifDecoderItem * gridItem) +{ + const uint32_t itemIndexNotSet = UINT32_MAX; + for (uint32_t dimgIdx = 0; dimgIdx < numExpectedTiles; ++dimgIdx) { + dimgIdxToItemIdx[dimgIdx] = itemIndexNotSet; + } + uint32_t numTiles = 0; + for (uint32_t i = 0; i < gridItem->meta->items.count; ++i) { + if (gridItem->meta->items.item[i]->dimgForID == gridItem->id) { + const uint32_t tileItemDimgIdx = gridItem->meta->items.item[i]->dimgIdx; + AVIF_CHECKERR(tileItemDimgIdx < numExpectedTiles, AVIF_RESULT_INVALID_IMAGE_GRID); + AVIF_CHECKERR(dimgIdxToItemIdx[tileItemDimgIdx] == itemIndexNotSet, AVIF_RESULT_INVALID_IMAGE_GRID); + dimgIdxToItemIdx[tileItemDimgIdx] = i; + ++numTiles; + } + } + // The number of tiles has been verified in avifDecoderItemReadAndParse(). + AVIF_ASSERT_OR_RETURN(numTiles == numExpectedTiles); + return AVIF_RESULT_OK; +} + +// Copies the codec type property (av1C or av2C) from the first grid tile to the grid item. +// Also checks that all tiles have the same codec type and that it's valid. +static avifResult avifDecoderAdoptGridTileCodecType(avifDecoder * decoder, + avifDecoderItem * gridItem, + const uint32_t * dimgIdxToItemIdx, + uint32_t numTiles) +{ + avifDecoderItem * firstTileItem = NULL; + for (uint32_t dimgIdx = 0; dimgIdx < numTiles; ++dimgIdx) { + const uint32_t itemIdx = dimgIdxToItemIdx[dimgIdx]; + AVIF_ASSERT_OR_RETURN(itemIdx < gridItem->meta->items.count); + avifDecoderItem * item = gridItem->meta->items.item[itemIdx]; + + // According to HEIF (ISO 14496-12), Section 6.6.2.3.1, the SingleItemTypeReferenceBox of type 'dimg' + // identifies the input images of the derived image item of type 'grid'. Since the reference_count + // shall be equal to rows*columns, unknown tile item types cannot be skipped but must be considered + // as errors. + const avifCodecType tileCodecType = avifGetCodecType(item->type); + if (tileCodecType == AVIF_CODEC_TYPE_UNKNOWN) { + char type[4]; + for (int j = 0; j < 4; j++) { + if (isprint((unsigned char)item->type[j])) { + type[j] = item->type[j]; + } else { + type[j] = '.'; + } + } + avifDiagnosticsPrintf(&decoder->diag, + "Tile item ID %u has an unknown item type '%.4s' (%02x%02x%02x%02x)", + item->id, + type, + item->type[0], + item->type[1], + item->type[2], + item->type[3]); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + + if (item->hasUnsupportedEssentialProperty) { + // An essential property isn't supported by libavif; can't + // decode a grid image if any tile in the grid isn't supported. + avifDiagnosticsPrintf(&decoder->diag, "Grid image contains tile with an unsupported property marked as essential"); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + + if (firstTileItem == NULL) { + firstTileItem = item; + // Adopt the configuration property of the first image item tile, so that it can be queried from + // the top-level color/alpha item during avifDecoderReset(). + const avifCodecType codecType = avifGetCodecType(item->type); + const char * configPropName = avifGetConfigurationPropertyName(codecType); + const avifProperty * srcProp = avifPropertyArrayFind(&item->properties, configPropName); + if (!srcProp) { + avifDiagnosticsPrintf(&decoder->diag, "Grid image's first tile is missing an %s property", configPropName); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + avifProperty * dstProp = (avifProperty *)avifArrayPush(&gridItem->properties); + AVIF_CHECKERR(dstProp != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *dstProp = *srcProp; + + } else if (memcmp(item->type, firstTileItem->type, 4)) { + // MIAF (ISO 23000-22:2019), Section 7.3.11.4.1: + // All input images of a grid image item shall use the same coding format [...] + // The coding format is defined by the item type. + avifDiagnosticsPrintf(&decoder->diag, + "Tile item ID %u of type '%.4s' differs from other tile type '%.4s'", + item->id, + (const char *)item->type, + (const char *)firstTileItem->type); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + } + return AVIF_RESULT_OK; +} + +// If the item is a grid, copies the codec type property (av1C or av2C) from the first grid tile to the grid item. +// Also checks that all tiles have the same codec type and that it's valid. +static avifResult avifDecoderAdoptGridTileCodecTypeIfNeeded(avifDecoder * decoder, avifDecoderItem * item, const avifTileInfo * info) +{ + if ((info->grid.rows > 0) && (info->grid.columns > 0)) { + // The number of tiles was verified in avifDecoderItemReadAndParse(). + const uint32_t numTiles = info->grid.rows * info->grid.columns; + uint32_t * dimgIdxToItemIdx = (uint32_t *)avifAlloc(numTiles * sizeof(uint32_t)); + AVIF_CHECKERR(dimgIdxToItemIdx != NULL, AVIF_RESULT_OUT_OF_MEMORY); + avifResult result = avifFillDimgIdxToItemIdxArray(dimgIdxToItemIdx, numTiles, item); + if (result == AVIF_RESULT_OK) { + result = avifDecoderAdoptGridTileCodecType(decoder, item, dimgIdxToItemIdx, numTiles); + } + avifFree(dimgIdxToItemIdx); + AVIF_CHECKRES(result); + } + return AVIF_RESULT_OK; +} + +// Creates the tiles and associate them to the items in the order of the 'dimg' association. +static avifResult avifDecoderGenerateImageGridTiles(avifDecoder * decoder, + avifDecoderItem * gridItem, + avifItemCategory itemCategory, + const uint32_t * dimgIdxToItemIdx, + uint32_t numTiles) +{ + avifBool progressive = AVIF_TRUE; + for (uint32_t dimgIdx = 0; dimgIdx < numTiles; ++dimgIdx) { + const uint32_t itemIdx = dimgIdxToItemIdx[dimgIdx]; + AVIF_ASSERT_OR_RETURN(itemIdx < gridItem->meta->items.count); + avifDecoderItem * item = gridItem->meta->items.item[itemIdx]; + + const avifCodecType tileCodecType = avifGetCodecType(item->type); + AVIF_CHECKERR(tileCodecType != AVIF_CODEC_TYPE_UNKNOWN, AVIF_RESULT_INVALID_IMAGE_GRID); + const avifTile * tile = + avifDecoderDataCreateTile(decoder->data, tileCodecType, item->width, item->height, avifDecoderItemOperatingPoint(item)); + AVIF_CHECKERR(tile != NULL, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKRES(avifCodecDecodeInputFillFromDecoderItem(tile->input, + item, + decoder->allowProgressive, + decoder->imageCountLimit, + decoder->io->sizeHint, + &decoder->diag)); + tile->input->itemCategory = itemCategory; + + if (!item->progressive) { + progressive = AVIF_FALSE; + } + } + if (itemCategory == AVIF_ITEM_COLOR && progressive) { + // If all the items that make up the grid are progressive, then propagate that status to the top-level grid item. + gridItem->progressive = AVIF_TRUE; + } + return AVIF_RESULT_OK; +} + +// Allocates the dstImage. Also verifies some spec compliance rules for grids, if relevant. +static avifResult avifDecoderDataAllocateImagePlanes(const avifDecoderData * data, const avifTileInfo * info, avifImage * dstImage, avifBool * cicpSet) +{ + const avifTile * tile = &data->tiles.tile[info->firstTileIndex]; + uint32_t dstWidth; + uint32_t dstHeight; + + if (info->grid.rows > 0 && info->grid.columns > 0) { + const avifImageGrid * grid = &info->grid; + // Validate grid image size and tile size. + // + // HEIF (ISO/IEC 23008-12:2017), Section 6.6.2.3.1: + // The tiled input images shall completely "cover" the reconstructed image grid canvas, ... + if ((((uint64_t)tile->image->width * grid->columns) < grid->outputWidth) || + (((uint64_t)tile->image->height * grid->rows) < grid->outputHeight)) { + avifDiagnosticsPrintf(data->diag, + "Grid image tiles do not completely cover the image (HEIF (ISO/IEC 23008-12:2017), Section 6.6.2.3.1)"); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + // Tiles in the rightmost column and bottommost row must overlap the reconstructed image grid canvas. See MIAF (ISO/IEC 23000-22:2019), Section 7.3.11.4.2, Figure 2. + if ((((uint64_t)tile->image->width * (grid->columns - 1)) >= grid->outputWidth) || + (((uint64_t)tile->image->height * (grid->rows - 1)) >= grid->outputHeight)) { + avifDiagnosticsPrintf(data->diag, + "Grid image tiles in the rightmost column and bottommost row do not overlap the reconstructed image grid canvas. See MIAF (ISO/IEC 23000-22:2019), Section 7.3.11.4.2, Figure 2"); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + if (!avifAreGridDimensionsValid(tile->image->yuvFormat, + grid->outputWidth, + grid->outputHeight, + tile->image->width, + tile->image->height, + data->diag)) { + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + dstWidth = grid->outputWidth; + dstHeight = grid->outputHeight; + } else { + // Only one tile. Width and height are inherited from the 'ispe' property of the corresponding avifDecoderItem. + dstWidth = tile->width; + dstHeight = tile->height; + } + + const avifBool alpha = avifIsAlpha(tile->input->itemCategory); + if (alpha) { + // An alpha tile does not contain any YUV pixels. + AVIF_ASSERT_OR_RETURN(tile->image->yuvFormat == AVIF_PIXEL_FORMAT_NONE); + } + + const uint32_t dstDepth = tile->image->depth; + + // Lazily populate dstImage with the new frame's properties. + const avifBool dimsOrDepthIsDifferent = (dstImage->width != dstWidth) || (dstImage->height != dstHeight) || + (dstImage->depth != dstDepth); + const avifBool yuvFormatIsDifferent = !alpha && (dstImage->yuvFormat != tile->image->yuvFormat); + if (dimsOrDepthIsDifferent || yuvFormatIsDifferent) { + if (alpha) { + // Alpha doesn't match size, just bail out + avifDiagnosticsPrintf(data->diag, "Alpha plane dimensions do not match color plane dimensions"); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + + if (dimsOrDepthIsDifferent) { + avifImageFreePlanes(dstImage, AVIF_PLANES_ALL); + dstImage->width = dstWidth; + dstImage->height = dstHeight; + dstImage->depth = dstDepth; + } + if (yuvFormatIsDifferent) { + avifImageFreePlanes(dstImage, AVIF_PLANES_YUV); + dstImage->yuvFormat = tile->image->yuvFormat; + } + // Keep dstImage->yuvRange which is already set to its correct value + // (extracted from the 'colr' box if parsed or from a Sequence Header OBU otherwise). + + if (!*cicpSet) { + *cicpSet = AVIF_TRUE; + dstImage->colorPrimaries = tile->image->colorPrimaries; + dstImage->transferCharacteristics = tile->image->transferCharacteristics; + dstImage->matrixCoefficients = tile->image->matrixCoefficients; + } + } + + if (avifImageAllocatePlanes(dstImage, alpha ? AVIF_PLANES_A : AVIF_PLANES_YUV) != AVIF_RESULT_OK) { + avifDiagnosticsPrintf(data->diag, "Image allocation failure"); + return AVIF_RESULT_OUT_OF_MEMORY; + } + return AVIF_RESULT_OK; +} + +// Copies over the pixels from the tile into dstImage. +// Verifies that the relevant properties of the tile match those of the first tile in case of a grid. +static avifResult avifDecoderDataCopyTileToImage(avifDecoderData * data, + const avifTileInfo * info, + avifImage * dstImage, + const avifTile * tile, + unsigned int tileIndex) +{ + const avifTile * firstTile = &data->tiles.tile[info->firstTileIndex]; + if (tile != firstTile) { + // Check for tile consistency. All tiles in a grid image should match the first tile in the properties checked below. + if ((tile->image->width != firstTile->image->width) || (tile->image->height != firstTile->image->height) || + (tile->image->depth != firstTile->image->depth) || (tile->image->yuvFormat != firstTile->image->yuvFormat) || + (tile->image->yuvRange != firstTile->image->yuvRange) || (tile->image->colorPrimaries != firstTile->image->colorPrimaries) || + (tile->image->transferCharacteristics != firstTile->image->transferCharacteristics) || + (tile->image->matrixCoefficients != firstTile->image->matrixCoefficients)) { + avifDiagnosticsPrintf(data->diag, "Grid image contains mismatched tiles"); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + } + + // Only keep the relevant planes in the destination image. Otherwise, + // unjustified failures may come from trying to copy alpha tiles with odd + // coordinates into the dstImage when the chroma planes are subsampled. + avifImage dstView; + avifImageSetDefaults(&dstView); + const avifCropRect srcViewRect = { 0, 0, dstImage->width, dstImage->height }; + AVIF_ASSERT_OR_RETURN(avifImageSetViewRect(&dstView, dstImage, &srcViewRect) == AVIF_RESULT_OK); + if (avifIsAlpha(tile->input->itemCategory)) { + avifImageFreePlanes(&dstView, AVIF_PLANES_YUV); + dstView.yuvFormat = AVIF_PIXEL_FORMAT_NONE; + } else { + avifImageFreePlanes(&dstView, AVIF_PLANES_A); + } + + avifImage srcTileView; + avifImageSetDefaults(&srcTileView); + avifImage dstTileView; + avifImageSetDefaults(&dstTileView); + avifCropRect dstTileViewRect = { 0, 0, firstTile->image->width, firstTile->image->height }; + if (info->grid.rows > 0 && info->grid.columns > 0) { + unsigned int rowIndex = tileIndex / info->grid.columns; + unsigned int colIndex = tileIndex % info->grid.columns; + dstTileViewRect.x = firstTile->image->width * colIndex; + dstTileViewRect.y = firstTile->image->height * rowIndex; + if (dstTileViewRect.x + dstTileViewRect.width > info->grid.outputWidth) { + dstTileViewRect.width = info->grid.outputWidth - dstTileViewRect.x; + } + if (dstTileViewRect.y + dstTileViewRect.height > info->grid.outputHeight) { + dstTileViewRect.height = info->grid.outputHeight - dstTileViewRect.y; + } + } + const avifCropRect srcTileViewRect = { 0, 0, dstTileViewRect.width, dstTileViewRect.height }; + AVIF_ASSERT_OR_RETURN(avifImageSetViewRect(&dstTileView, &dstView, &dstTileViewRect) == AVIF_RESULT_OK); + AVIF_ASSERT_OR_RETURN(avifImageSetViewRect(&srcTileView, tile->image, &srcTileViewRect) == AVIF_RESULT_OK); + avifImageCopySamples(&dstTileView, &srcTileView, avifIsAlpha(tile->input->itemCategory) ? AVIF_PLANES_A : AVIF_PLANES_YUV); + return AVIF_RESULT_OK; +} + +// If colorId == 0 (a sentinel value as item IDs must be nonzero), accept any found EXIF/XMP metadata. Passing in 0 +// is used when finding metadata in a meta box embedded in a trak box, as any items inside of a meta box that is +// inside of a trak box are implicitly associated to the track. +static avifResult avifDecoderFindMetadata(avifDecoder * decoder, avifMeta * meta, avifImage * image, uint32_t colorId) +{ + if (decoder->ignoreExif && decoder->ignoreXMP) { + // Nothing to do! + return AVIF_RESULT_OK; + } + + for (uint32_t itemIndex = 0; itemIndex < meta->items.count; ++itemIndex) { + avifDecoderItem * item = meta->items.item[itemIndex]; + if (!item->size) { + continue; + } + if (item->hasUnsupportedEssentialProperty) { + // An essential property isn't supported by libavif; ignore the item. + continue; + } + + if ((colorId > 0) && (item->descForID != colorId)) { + // Not a content description (metadata) for the colorOBU, skip it + continue; + } + + if (!decoder->ignoreExif && !memcmp(item->type, "Exif", 4)) { + avifROData exifContents; + avifResult readResult = avifDecoderItemRead(item, decoder->io, &exifContents, 0, 0, &decoder->diag); + if (readResult != AVIF_RESULT_OK) { + return readResult; + } + + // Advance past Annex A.2.1's header + BEGIN_STREAM(exifBoxStream, exifContents.data, exifContents.size, &decoder->diag, "Exif header"); +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + // The MinimizedImageBox does not signal the exifTiffHeaderOffset. + if (!meta->fromMiniBox) +#endif + { + uint32_t exifTiffHeaderOffset; + AVIF_CHECKERR(avifROStreamReadU32(&exifBoxStream, &exifTiffHeaderOffset), + AVIF_RESULT_INVALID_EXIF_PAYLOAD); // unsigned int(32) exif_tiff_header_offset; + size_t expectedExifTiffHeaderOffset; + AVIF_CHECKRES(avifGetExifTiffHeaderOffset(avifROStreamCurrent(&exifBoxStream), + avifROStreamRemainingBytes(&exifBoxStream), + &expectedExifTiffHeaderOffset)); + AVIF_CHECKERR(exifTiffHeaderOffset == expectedExifTiffHeaderOffset, AVIF_RESULT_INVALID_EXIF_PAYLOAD); + } + + AVIF_CHECKRES(avifRWDataSet(&image->exif, avifROStreamCurrent(&exifBoxStream), avifROStreamRemainingBytes(&exifBoxStream))); + } else if (!decoder->ignoreXMP && !memcmp(item->type, "mime", 4) && + !strcmp(item->contentType.contentType, AVIF_CONTENT_TYPE_XMP)) { + avifROData xmpContents; + avifResult readResult = avifDecoderItemRead(item, decoder->io, &xmpContents, 0, 0, &decoder->diag); + if (readResult != AVIF_RESULT_OK) { + return readResult; + } + + AVIF_CHECKRES(avifImageSetMetadataXMP(image, xmpContents.data, xmpContents.size)); + } + } + return AVIF_RESULT_OK; +} + +// --------------------------------------------------------------------------- +// URN + +static avifBool isAlphaURN(const char * urn) +{ + return !strcmp(urn, AVIF_URN_ALPHA0) || !strcmp(urn, AVIF_URN_ALPHA1); +} + +// --------------------------------------------------------------------------- +// BMFF Parsing + +static avifBool avifParseHandlerBox(const uint8_t * raw, size_t rawLen, uint8_t handlerType[4], avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[hdlr]"); + + AVIF_CHECK(avifROStreamReadAndEnforceVersion(&s, /*enforcedVersion=*/0, /*flags=*/NULL)); + + uint32_t predefined; + AVIF_CHECK(avifROStreamReadU32(&s, &predefined)); // unsigned int(32) pre_defined = 0; + if (predefined != 0) { + avifDiagnosticsPrintf(diag, "Box[hdlr] contains a pre_defined value that is nonzero"); + return AVIF_FALSE; + } + + AVIF_CHECK(avifROStreamRead(&s, handlerType, 4)); // unsigned int(32) handler_type; + + for (int i = 0; i < 3; ++i) { + uint32_t reserved; + AVIF_CHECK(avifROStreamReadU32(&s, &reserved)); // const unsigned int(32)[3] reserved = 0; + } + + // Verify that a valid string is here, but don't bother to store it + AVIF_CHECK(avifROStreamReadString(&s, NULL, 0)); // string name; + return AVIF_TRUE; +} + +static avifResult avifParseItemLocationBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[iloc]"); + + // Section 8.11.3.2 of ISO/IEC 14496-12. + uint8_t version; + AVIF_CHECKERR(avifROStreamReadVersionAndFlags(&s, &version, NULL), AVIF_RESULT_BMFF_PARSE_FAILED); + if (version > 2) { + avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported version [%u]", version); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + uint8_t offsetSize, lengthSize, baseOffsetSize, indexSize = 0; + uint32_t reserved; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &offsetSize, /*bitCount=*/4), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(4) offset_size; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &lengthSize, /*bitCount=*/4), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(4) length_size; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &baseOffsetSize, /*bitCount=*/4), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(4) base_offset_size; + if (version == 1 || version == 2) { + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &indexSize, /*bitCount=*/4), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(4) index_size; + } else { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &reserved, /*bitCount=*/4), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(4) reserved; + } + + // Section 8.11.3.3 of ISO/IEC 14496-12. + if ((offsetSize != 0 && offsetSize != 4 && offsetSize != 8) || (lengthSize != 0 && lengthSize != 4 && lengthSize != 8) || + (baseOffsetSize != 0 && baseOffsetSize != 4 && baseOffsetSize != 8) || (indexSize != 0 && indexSize != 4 && indexSize != 8)) { + avifDiagnosticsPrintf(diag, "Box[iloc] has an invalid size"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + uint16_t tmp16; + uint32_t itemCount; + if (version < 2) { + AVIF_CHECKERR(avifROStreamReadU16(&s, &tmp16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) item_count; + itemCount = tmp16; + } else { + AVIF_CHECKERR(avifROStreamReadU32(&s, &itemCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) item_count; + } + for (uint32_t i = 0; i < itemCount; ++i) { + uint32_t itemID; + if (version < 2) { + AVIF_CHECKERR(avifROStreamReadU16(&s, &tmp16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) item_ID; + itemID = tmp16; + } else { + AVIF_CHECKERR(avifROStreamReadU32(&s, &itemID), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) item_ID; + } + AVIF_CHECKRES(avifCheckItemID("iloc", itemID, diag)); + + avifDecoderItem * item; + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, itemID, &item)); + if (item->extents.count > 0) { + // This item has already been given extents via this iloc box. This is invalid. + avifDiagnosticsPrintf(diag, "Item ID [%u] contains duplicate sets of extents", itemID); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + if (version == 1 || version == 2) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &reserved, /*bitCount=*/12), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(12) reserved = 0; + if (reserved) { + avifDiagnosticsPrintf(diag, "Box[iloc] has a non null reserved field [%u]", reserved); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + uint8_t constructionMethod; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &constructionMethod, /*bitCount=*/4), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(4) construction_method; + if (constructionMethod != 0 /* file offset */ && constructionMethod != 1 /* idat offset */) { + // construction method 2 (item offset) unsupported + avifDiagnosticsPrintf(diag, "Box[iloc] has an unsupported construction method [%u]", constructionMethod); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + if (constructionMethod == 1) { + item->idatStored = AVIF_TRUE; + } + } + + uint16_t dataReferenceIndex; + AVIF_CHECKERR(avifROStreamReadU16(&s, &dataReferenceIndex), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) data_reference_index; + uint64_t baseOffset; + AVIF_CHECKERR(avifROStreamReadUX8(&s, &baseOffset, baseOffsetSize), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(base_offset_size*8) base_offset; + uint16_t extentCount; + AVIF_CHECKERR(avifROStreamReadU16(&s, &extentCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) extent_count; + for (int extentIter = 0; extentIter < extentCount; ++extentIter) { + if ((version == 1 || version == 2) && indexSize > 0) { + // Section 8.11.3.1 of ISO/IEC 14496-12: + // The item_reference_index is only used for the method item_offset; it indicates the 1-based index + // of the item reference with referenceType 'iloc' linked from this item. If index_size is 0, then + // the value 1 is implied; the value 0 is reserved. + uint64_t itemReferenceIndex; // Ignored unless construction_method=2 which is unsupported, but still read it. + AVIF_CHECKERR(avifROStreamReadUX8(&s, &itemReferenceIndex, indexSize), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(index_size*8) item_reference_index; + } + + uint64_t extentOffset; + AVIF_CHECKERR(avifROStreamReadUX8(&s, &extentOffset, offsetSize), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(offset_size*8) extent_offset; + uint64_t extentLength; + AVIF_CHECKERR(avifROStreamReadUX8(&s, &extentLength, lengthSize), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(length_size*8) extent_length; + + avifExtent * extent = (avifExtent *)avifArrayPush(&item->extents); + AVIF_CHECKERR(extent != NULL, AVIF_RESULT_OUT_OF_MEMORY); + if (extentOffset > UINT64_MAX - baseOffset) { + avifDiagnosticsPrintf(diag, + "Item ID [%u] contains an extent offset which overflows: [base: %" PRIu64 " offset:%" PRIu64 "]", + itemID, + baseOffset, + extentOffset); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + uint64_t offset = baseOffset + extentOffset; + extent->offset = offset; +#if UINT64_MAX > SIZE_MAX + if (extentLength > SIZE_MAX) { + avifDiagnosticsPrintf(diag, "Item ID [%u] contains an extent length which overflows: [%" PRIu64 "]", itemID, extentLength); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } +#endif + extent->size = (size_t)extentLength; + if (extent->size > SIZE_MAX - item->size) { + avifDiagnosticsPrintf(diag, + "Item ID [%u] contains an extent length which overflows the item size: [%zu, %zu]", + itemID, + extent->size, + item->size); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + item->size += extent->size; + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseImageGridBox(avifImageGrid * grid, + const uint8_t * raw, + size_t rawLen, + uint32_t imageSizeLimit, + uint32_t imageDimensionLimit, + avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[grid]"); + + uint8_t version, flags; + AVIF_CHECKERR(avifROStreamRead(&s, &version, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) version = 0; + if (version != 0) { + avifDiagnosticsPrintf(diag, "Box[grid] has unsupported version [%u]", version); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + uint8_t rowsMinusOne, columnsMinusOne; + AVIF_CHECKERR(avifROStreamRead(&s, &flags, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) flags; + AVIF_CHECKERR(avifROStreamRead(&s, &rowsMinusOne, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) rows_minus_one; + AVIF_CHECKERR(avifROStreamRead(&s, &columnsMinusOne, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) columns_minus_one; + grid->rows = (uint32_t)rowsMinusOne + 1; + grid->columns = (uint32_t)columnsMinusOne + 1; + + uint32_t fieldLength = ((flags & 1) + 1) * 16; + if (fieldLength == 16) { + uint16_t outputWidth16, outputHeight16; + AVIF_CHECKERR(avifROStreamReadU16(&s, &outputWidth16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(FieldLength) output_width; + AVIF_CHECKERR(avifROStreamReadU16(&s, &outputHeight16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(FieldLength) output_height; + grid->outputWidth = outputWidth16; + grid->outputHeight = outputHeight16; + } else { + if (fieldLength != 32) { + // This should be impossible + avifDiagnosticsPrintf(diag, "Grid box contains illegal field length: [%u]", fieldLength); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + AVIF_CHECKERR(avifROStreamReadU32(&s, &grid->outputWidth), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(FieldLength) output_width; + AVIF_CHECKERR(avifROStreamReadU32(&s, &grid->outputHeight), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(FieldLength) output_height; + } + if ((grid->outputWidth == 0) || (grid->outputHeight == 0)) { + avifDiagnosticsPrintf(diag, "Grid box contains illegal dimensions: [%u x %u]", grid->outputWidth, grid->outputHeight); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + if (avifDimensionsTooLarge(grid->outputWidth, grid->outputHeight, imageSizeLimit, imageDimensionLimit)) { + avifDiagnosticsPrintf(diag, "Grid box dimensions are too large: [%u x %u]", grid->outputWidth, grid->outputHeight); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (avifROStreamRemainingBytes(&s) != 0) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + return AVIF_RESULT_OK; +} + +static avifBool avifParseGainMapMetadata(avifGainMap * gainMap, avifROStream * s) +{ + uint32_t isMultichannel; + AVIF_CHECK(avifROStreamReadBitsU32(s, &isMultichannel, 1)); // unsigned int(1) is_multichannel; + const uint8_t channelCount = isMultichannel ? 3 : 1; + + uint32_t useBaseColorSpace; + AVIF_CHECK(avifROStreamReadBitsU32(s, &useBaseColorSpace, 1)); // unsigned int(1) use_base_colour_space; + gainMap->useBaseColorSpace = useBaseColorSpace ? AVIF_TRUE : AVIF_FALSE; + + uint32_t reserved; + AVIF_CHECK(avifROStreamReadBitsU32(s, &reserved, 6)); // unsigned int(6) reserved; + + AVIF_CHECK(avifROStreamReadU32(s, &gainMap->baseHdrHeadroom.n)); // unsigned int(32) base_hdr_headroom_numerator; + AVIF_CHECK(avifROStreamReadU32(s, &gainMap->baseHdrHeadroom.d)); // unsigned int(32) base_hdr_headroom_denominator; + AVIF_CHECK(avifROStreamReadU32(s, &gainMap->alternateHdrHeadroom.n)); // unsigned int(32) alternate_hdr_headroom_numerator; + AVIF_CHECK(avifROStreamReadU32(s, &gainMap->alternateHdrHeadroom.d)); // unsigned int(32) alternate_hdr_headroom_denominator; + + for (int c = 0; c < channelCount; ++c) { + AVIF_CHECK(avifROStreamReadU32(s, (uint32_t *)&gainMap->gainMapMin[c].n)); // int(32) gain_map_min_numerator; + AVIF_CHECK(avifROStreamReadU32(s, &gainMap->gainMapMin[c].d)); // unsigned int(32) gain_map_min_denominator; + AVIF_CHECK(avifROStreamReadU32(s, (uint32_t *)&gainMap->gainMapMax[c].n)); // int(32) gain_map_max_numerator; + AVIF_CHECK(avifROStreamReadU32(s, &gainMap->gainMapMax[c].d)); // unsigned int(32) gain_map_max_denominator; + AVIF_CHECK(avifROStreamReadU32(s, &gainMap->gainMapGamma[c].n)); // unsigned int(32) gamma_numerator; + AVIF_CHECK(avifROStreamReadU32(s, &gainMap->gainMapGamma[c].d)); // unsigned int(32) gamma_denominator; + AVIF_CHECK(avifROStreamReadU32(s, (uint32_t *)&gainMap->baseOffset[c].n)); // int(32) base_offset_numerator; + AVIF_CHECK(avifROStreamReadU32(s, &gainMap->baseOffset[c].d)); // unsigned int(32) base_offset_denominator; + AVIF_CHECK(avifROStreamReadU32(s, (uint32_t *)&gainMap->alternateOffset[c].n)); // int(32) alternate_offset_numerator; + AVIF_CHECK(avifROStreamReadU32(s, &gainMap->alternateOffset[c].d)); // unsigned int(32) alternate_offset_denominator; + } + + // Fill the remaining values by copying those from the first channel. + for (int c = channelCount; c < 3; ++c) { + gainMap->gainMapMin[c] = gainMap->gainMapMin[0]; + gainMap->gainMapMax[c] = gainMap->gainMapMax[0]; + gainMap->gainMapGamma[c] = gainMap->gainMapGamma[0]; + gainMap->baseOffset[c] = gainMap->baseOffset[0]; + gainMap->alternateOffset[c] = gainMap->alternateOffset[0]; + } + return AVIF_TRUE; +} + +// If the gain map's version or minimum_version tag is not supported, returns AVIF_RESULT_NOT_IMPLEMENTED. +static avifResult avifParseToneMappedImageBox(avifGainMap * gainMap, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[tmap]"); + + uint8_t version; + AVIF_CHECKERR(avifROStreamRead(&s, &version, 1), AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE); // unsigned int(8) version = 0; + if (version != 0) { + avifDiagnosticsPrintf(diag, "Box[tmap] has unsupported version [%u]", version); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + uint16_t minimumVersion; + AVIF_CHECKERR(avifROStreamReadU16(&s, &minimumVersion), AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE); // unsigned int(16) minimum_version; + const uint16_t supportedMetadataVersion = 0; + if (minimumVersion > supportedMetadataVersion) { + avifDiagnosticsPrintf(diag, "Box[tmap] has unsupported minimum version [%u]", minimumVersion); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + uint16_t writerVersion; + AVIF_CHECKERR(avifROStreamReadU16(&s, &writerVersion), AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE); // unsigned int(16) writer_version; + AVIF_CHECKERR(writerVersion >= minimumVersion, AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE); + + AVIF_CHECKERR(avifParseGainMapMetadata(gainMap, &s), AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE); + + if (writerVersion <= supportedMetadataVersion) { + AVIF_CHECKERR(avifROStreamRemainingBytes(&s) == 0, AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE); + } + + if (avifGainMapValidateMetadata(gainMap, diag) != AVIF_RESULT_OK) { + return AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE; + } + + return AVIF_RESULT_OK; +} + +// bit_depth is assumed to be 2 (32-bit). +static avifResult avifParseSampleTransformTokens(avifROStream * s, avifSampleTransformExpression * expression) +{ + uint8_t tokenCount; + AVIF_CHECKERR(avifROStreamRead(s, &tokenCount, /*size=*/1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) token_count; + AVIF_CHECKERR(tokenCount != 0, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifArrayCreate(expression, sizeof(expression->tokens[0]), tokenCount), AVIF_RESULT_OUT_OF_MEMORY); + + for (uint32_t t = 0; t < tokenCount; ++t) { + avifSampleTransformToken * token = (avifSampleTransformToken *)avifArrayPush(expression); + AVIF_CHECKERR(token != NULL, AVIF_RESULT_OUT_OF_MEMORY); + + uint8_t tokenValue; + AVIF_CHECKERR(avifROStreamRead(s, &tokenValue, /*size=*/1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) token; + if (tokenValue == AVIF_SAMPLE_TRANSFORM_CONSTANT) { + token->type = AVIF_SAMPLE_TRANSFORM_CONSTANT; + // Two's complement representation is assumed here. + uint32_t constant; + AVIF_CHECKERR(avifROStreamReadU32(s, &constant), AVIF_RESULT_BMFF_PARSE_FAILED); // signed int(1<<(bit_depth+3)) constant; + token->constant = (int32_t)constant; + } else if (tokenValue <= AVIF_SAMPLE_TRANSFORM_LAST_INPUT_IMAGE_ITEM_INDEX) { + AVIF_ASSERT_OR_RETURN(tokenValue >= AVIF_SAMPLE_TRANSFORM_FIRST_INPUT_IMAGE_ITEM_INDEX); + token->type = AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX; + token->inputImageItemIndex = tokenValue; + } else if (tokenValue >= AVIF_SAMPLE_TRANSFORM_FIRST_UNARY_OPERATOR && tokenValue <= AVIF_SAMPLE_TRANSFORM_LAST_UNARY_OPERATOR) { + token->type = (avifSampleTransformTokenType)tokenValue; // unary operator + } else if (tokenValue >= AVIF_SAMPLE_TRANSFORM_FIRST_BINARY_OPERATOR && tokenValue <= AVIF_SAMPLE_TRANSFORM_LAST_BINARY_OPERATOR) { + token->type = (avifSampleTransformTokenType)tokenValue; // binary operator + } else { + token->type = AVIF_SAMPLE_TRANSFORM_RESERVED; + } + } + AVIF_CHECKERR(avifROStreamRemainingBytes(s) == 0, AVIF_RESULT_BMFF_PARSE_FAILED); + return AVIF_RESULT_OK; +} + +// Parses the raw bitstream of the 'sato' Sample Transform derived image item and extracts the expression. +static avifResult avifParseSampleTransformImageBox(const uint8_t * raw, + size_t rawLen, + uint32_t numInputImageItems, + avifSampleTransformExpression * expression, + avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[sato]"); + + uint8_t version, reserved, bitDepth; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &version, /*bitCount=*/2), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(2) version = 0; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &reserved, /*bitCount=*/4), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(4) reserved; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &bitDepth, /*bitCount=*/2), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(2) bit_depth; + AVIF_CHECKERR(version == 0, AVIF_RESULT_NOT_IMPLEMENTED); + AVIF_CHECKERR(bitDepth == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32, AVIF_RESULT_NOT_IMPLEMENTED); + + const avifResult result = avifParseSampleTransformTokens(&s, expression); + if (result != AVIF_RESULT_OK) { + avifArrayDestroy(expression); + return result; + } + if (!avifSampleTransformExpressionIsValid(expression, numInputImageItems)) { + avifArrayDestroy(expression); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + return AVIF_RESULT_OK; +} + +static const avifProperty * avifDecoderItemCodecConfigOrFirstCellCodecConfig(const avifDecoderItem * item) +{ + if (!memcmp(item->type, "grid", 4)) { + // In case of a grid, return the codec configuration property of the first cell. + // avifDecoderAdoptGridTileCodecType() copies that property from the first cell to the grid item anyway. + for (uint32_t i = 0; i < item->meta->items.count; ++i) { + avifDecoderItem * inputImageItem = item->meta->items.item[i]; + if (inputImageItem->dimgForID == item->id) { + return avifPropertyArrayFind(&inputImageItem->properties, + avifGetConfigurationPropertyName(avifGetCodecType(inputImageItem->type))); + } + } + // The number of tiles was verified in avifDecoderItemReadAndParse(). + assert(AVIF_FALSE); + } + return avifPropertyArrayFind(&item->properties, avifGetConfigurationPropertyName(avifGetCodecType(item->type))); +} + +static avifResult avifDecoderSampleTransformItemValidateProperties(const avifDecoderItem * satoItem, avifDiagnostics * diag) +{ + AVIF_ASSERT_OR_RETURN(memcmp(satoItem->type, "sato", 4) == 0); + const avifProperty * pixiProp = avifPropertyArrayFind(&satoItem->properties, "pixi"); + if (!pixiProp) { + avifDiagnosticsPrintf(diag, "Item ID %u of type 'sato' is missing mandatory pixi property", satoItem->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + for (uint8_t i = 1; i < pixiProp->u.pixi.planeCount; ++i) { + // This is enforced in avifParsePixelInformationProperty(). + AVIF_ASSERT_OR_RETURN(pixiProp->u.pixi.planeDepths[i] == pixiProp->u.pixi.planeDepths[0]); + } + AVIF_ASSERT_OR_RETURN(pixiProp->u.pixi.planeCount >= 1); + const uint8_t depth = pixiProp->u.pixi.planeDepths[0]; + if (depth != 8 && depth != 10 && depth != 12 && depth != 16) { + avifDiagnosticsPrintf(diag, + "Item ID %u of type 'sato' with depth %u (specified by pixi property) is not supported", + satoItem->id, + depth); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + const avifProperty * ispeProp = avifPropertyArrayFind(&satoItem->properties, "ispe"); + if (!ispeProp) { + avifDiagnosticsPrintf(diag, "Item ID %u of type 'sato' is missing mandatory ispe property", satoItem->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + // Check that all input image items of the 'sato' derived image item share the same properties. + for (uint32_t i = 0; i < satoItem->meta->items.count; ++i) { + avifDecoderItem * inputImageItem = satoItem->meta->items.item[i]; + if (inputImageItem->dimgForID != satoItem->id) { + continue; + } + + // Require all input image items of the 'sato' derived image item to be associated with a ImageSpatialExtentsProperty. + const avifProperty * inputImageItemIspeProp = avifPropertyArrayFind(&inputImageItem->properties, "ispe"); + if (inputImageItemIspeProp == NULL) { + avifDiagnosticsPrintf(diag, "Item ID %u is missing mandatory ispe property", inputImageItem->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + // The codec configuration property must be present, at least on the first cell for a 'grid' item. + const avifProperty * inputImageItemCodecConfig = avifDecoderItemCodecConfigOrFirstCellCodecConfig(inputImageItem); + if (inputImageItemCodecConfig == NULL) { + avifDiagnosticsPrintf(diag, + "Item ID %u of type '%.4s' is missing mandatory codec configuration property", + inputImageItem->id, + (const char *)inputImageItem->type); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + for (uint32_t j = i + 1; j < satoItem->meta->items.count; ++j) { + avifDecoderItem * otherInputImageItem = satoItem->meta->items.item[j]; + if (otherInputImageItem->dimgForID != satoItem->id) { + continue; + } + + // Require all input image items of the 'sato' derived image item to be associated with a ImageSpatialExtentsProperty. + const avifProperty * otherInputImageItemIspeProp = avifPropertyArrayFind(&otherInputImageItem->properties, "ispe"); + if (otherInputImageItemIspeProp == NULL) { + avifDiagnosticsPrintf(diag, + "Item ID %u of type '%.4s' is missing mandatory ispe property", + otherInputImageItem->id, + (const char *)otherInputImageItem->type); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + if (inputImageItemIspeProp->u.ispe.width != otherInputImageItemIspeProp->u.ispe.width || + inputImageItemIspeProp->u.ispe.height != otherInputImageItemIspeProp->u.ispe.height) { + avifDiagnosticsPrintf(diag, + "The fields of the ispe property of item ID %u of type '%.4s' differs from item ID %u", + inputImageItem->id, + (const char *)inputImageItem->type, + otherInputImageItem->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + // The codec configuration property must be present, at least on the first cell for a 'grid' item. + const avifProperty * otherInputImageItemCodecConfig = avifDecoderItemCodecConfigOrFirstCellCodecConfig(otherInputImageItem); + if (otherInputImageItemCodecConfig == NULL) { + avifDiagnosticsPrintf(diag, + "Item ID %u of type '%.4s' is missing mandatory codec configuration property", + otherInputImageItem->id, + (const char *)otherInputImageItem->type); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + if (inputImageItemCodecConfig->u.av1C.monochrome != otherInputImageItemCodecConfig->u.av1C.monochrome || + inputImageItemCodecConfig->u.av1C.chromaSubsamplingX != otherInputImageItemCodecConfig->u.av1C.chromaSubsamplingX || + inputImageItemCodecConfig->u.av1C.chromaSubsamplingY != otherInputImageItemCodecConfig->u.av1C.chromaSubsamplingY || + inputImageItemCodecConfig->u.av1C.chromaSamplePosition != otherInputImageItemCodecConfig->u.av1C.chromaSamplePosition) { + avifDiagnosticsPrintf(diag, + "The plane count or subsampling in the codec configuration property of item ID %u of type '%.4s' differs from item ID %u", + inputImageItem->id, + (const char *)inputImageItem->type, + otherInputImageItem->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + // If the input image item of the 'sato' derived image item is itself a grid, + // its own input image items will be checked in avifDecoderItemValidateProperties(). + } + break; + } + + AVIF_CHECKERR(avifPropertyArrayFind(&satoItem->properties, "clap") == NULL, AVIF_RESULT_NOT_IMPLEMENTED); + return AVIF_RESULT_OK; +} + +// Extracts the codecType from the item type or from its children. +// Also parses and outputs grid information if the item is a grid. +// isItemInInput must be false if the item is a made-up structure +// (and thus not part of the parseable input bitstream). +static avifResult avifDecoderItemReadAndParse(const avifDecoder * decoder, + avifDecoderItem * item, + avifBool isItemInInput, + avifImageGrid * grid, + avifCodecType * codecType) +{ + if (!memcmp(item->type, "grid", 4)) { + if (isItemInInput) { + avifROData readData; + AVIF_CHECKRES(avifDecoderItemRead(item, decoder->io, &readData, 0, 0, decoder->data->diag)); + AVIF_CHECKRES(avifParseImageGridBox(grid, + readData.data, + readData.size, + decoder->imageSizeLimit, + decoder->imageDimensionLimit, + decoder->data->diag)); + // Validate that there are exactly the same number of dimg items to form the grid. + uint32_t dimgItemCount = 0; + for (uint32_t i = 0; i < item->meta->items.count; ++i) { + if (item->meta->items.item[i]->dimgForID == item->id) { + ++dimgItemCount; + } + } + AVIF_CHECKERR(dimgItemCount == grid->rows * grid->columns, AVIF_RESULT_INVALID_IMAGE_GRID); + } else { + // item was generated for convenience and is not part of the bitstream. + // grid information should already be set. + AVIF_ASSERT_OR_RETURN(grid->rows > 0 && grid->columns > 0); + } + *codecType = avifDecoderItemGetGridCodecType(item); + AVIF_CHECKERR(*codecType != AVIF_CODEC_TYPE_UNKNOWN, AVIF_RESULT_INVALID_IMAGE_GRID); + } else { + *codecType = avifGetCodecType(item->type); + AVIF_ASSERT_OR_RETURN(*codecType != AVIF_CODEC_TYPE_UNKNOWN); + } + return AVIF_RESULT_OK; +} + +static avifBool avifParseImageSpatialExtentsProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[ispe]"); + AVIF_CHECK(avifROStreamReadAndEnforceVersion(&s, /*enforcedVersion=*/0, /*flags=*/NULL)); + + avifImageSpatialExtents * ispe = &prop->u.ispe; + AVIF_CHECK(avifROStreamReadU32(&s, &ispe->width)); + AVIF_CHECK(avifROStreamReadU32(&s, &ispe->height)); + return AVIF_TRUE; +} + +static avifBool avifParseAuxiliaryTypeProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[auxC]"); + AVIF_CHECK(avifROStreamReadAndEnforceVersion(&s, /*enforcedVersion=*/0, /*flags=*/NULL)); + + AVIF_CHECK(avifROStreamReadString(&s, prop->u.auxC.auxType, AUXTYPE_SIZE)); + return AVIF_TRUE; +} + +static avifBool avifParseColourInformationBox(avifProperty * prop, uint64_t rawOffset, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[colr]"); + + avifColourInformationBox * colr = &prop->u.colr; + colr->hasICC = AVIF_FALSE; + colr->hasNCLX = AVIF_FALSE; + + uint8_t colorType[4]; // unsigned int(32) colour_type; + AVIF_CHECK(avifROStreamRead(&s, colorType, 4)); + if (!memcmp(colorType, "rICC", 4) || !memcmp(colorType, "prof", 4)) { + // Remember the offset of the ICC payload relative to the beginning of the stream. A direct pointer cannot be stored + // because decoder->io->persistent could have been AVIF_FALSE when obtaining raw through decoder->io->read(). + // The bytes could be copied now instead of remembering the offset, but it is as invasive as passing rawOffset everywhere. + colr->iccOffset = rawOffset + avifROStreamOffset(&s); + colr->iccSize = avifROStreamRemainingBytes(&s); + if (colr->iccSize == 0) { + avifDiagnosticsPrintf(diag, "Box[colr] contains empty ICC_profile"); + return AVIF_FALSE; + } + colr->hasICC = AVIF_TRUE; + } else if (!memcmp(colorType, "nclx", 4)) { + AVIF_CHECK(avifROStreamReadU16(&s, &colr->colorPrimaries)); // unsigned int(16) colour_primaries; + AVIF_CHECK(avifROStreamReadU16(&s, &colr->transferCharacteristics)); // unsigned int(16) transfer_characteristics; + AVIF_CHECK(avifROStreamReadU16(&s, &colr->matrixCoefficients)); // unsigned int(16) matrix_coefficients; + uint8_t full_range_flag; + AVIF_CHECK(avifROStreamReadBitsU8(&s, &full_range_flag, /*bitCount=*/1)); // unsigned int(1) full_range_flag; + colr->range = full_range_flag ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED; + uint8_t reserved; + AVIF_CHECK(avifROStreamReadBitsU8(&s, &reserved, /*bitCount=*/7)); // unsigned int(7) reserved = 0; + if (reserved) { + avifDiagnosticsPrintf(diag, "Box[colr] contains nonzero reserved bits [%u]", reserved); + return AVIF_FALSE; + } + colr->hasNCLX = AVIF_TRUE; + } + return AVIF_TRUE; +} + +static avifResult avifParseContentLightLevelInformation(avifROStream * s, avifContentLightLevelInformationBox * clli) +{ + AVIF_CHECKERR(avifROStreamReadBitsU16(s, &clli->maxCLL, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) max_content_light_level + AVIF_CHECKERR(avifROStreamReadBitsU16(s, &clli->maxPALL, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) max_pic_average_light_level + return AVIF_RESULT_OK; +} +static avifResult avifParseContentLightLevelInformationBox(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[clli]"); + AVIF_CHECKRES(avifParseContentLightLevelInformation(&s, &prop->u.clli)); + return AVIF_RESULT_OK; +} + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) +static avifResult avifSkipMasteringDisplayColourVolume(avifROStream * s) +{ + for (int c = 0; c < 3; c++) { + AVIF_CHECKERR(avifROStreamSkipBits(s, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) display_primaries_x; + AVIF_CHECKERR(avifROStreamSkipBits(s, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) display_primaries_y; + } + AVIF_CHECKERR(avifROStreamSkipBits(s, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) white_point_x; + AVIF_CHECKERR(avifROStreamSkipBits(s, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) white_point_y; + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) max_display_mastering_luminance; + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) min_display_mastering_luminance; + return AVIF_RESULT_OK; +} + +static avifResult avifSkipContentColourVolume(avifROStream * s) +{ + AVIF_CHECKERR(avifROStreamSkipBits(s, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(1) reserved = 0; // ccv_cancel_flag + AVIF_CHECKERR(avifROStreamSkipBits(s, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(1) reserved = 0; // ccv_persistence_flag + uint8_t ccvPrimariesPresent; + AVIF_CHECKERR(avifROStreamReadBitsU8(s, &ccvPrimariesPresent, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(1) ccv_primaries_present_flag; + uint8_t ccvMinLuminanceValuePresent, ccvMaxLuminanceValuePresent, ccvAvgLuminanceValuePresent; + AVIF_CHECKERR(avifROStreamReadBitsU8(s, &ccvMinLuminanceValuePresent, 1), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(1) ccv_min_luminance_value_present_flag; + AVIF_CHECKERR(avifROStreamReadBitsU8(s, &ccvMaxLuminanceValuePresent, 1), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(1) ccv_max_luminance_value_present_flag; + AVIF_CHECKERR(avifROStreamReadBitsU8(s, &ccvAvgLuminanceValuePresent, 1), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(1) ccv_avg_luminance_value_present_flag; + AVIF_CHECKERR(avifROStreamSkipBits(s, 2), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(2) reserved = 0; + + if (ccvPrimariesPresent) { + for (int c = 0; c < 3; c++) { + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // signed int(32) ccv_primaries_x[[c]]; + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // signed int(32) ccv_primaries_y[[c]]; + } + } + if (ccvMinLuminanceValuePresent) { + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) ccv_min_luminance_value; + } + if (ccvMaxLuminanceValuePresent) { + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) ccv_max_luminance_value; + } + if (ccvAvgLuminanceValuePresent) { + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) ccv_avg_luminance_value; + } + return AVIF_RESULT_OK; +} + +static avifResult avifSkipAmbientViewingEnvironment(avifROStream * s) +{ + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) ambient_illuminance; + AVIF_CHECKERR(avifROStreamSkipBits(s, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) ambient_light_x; + AVIF_CHECKERR(avifROStreamSkipBits(s, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) ambient_light_y; + return AVIF_RESULT_OK; +} + +static avifResult avifSkipReferenceViewingEnvironment(avifROStream * s) +{ + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) surround_luminance; + AVIF_CHECKERR(avifROStreamSkipBits(s, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) surround_light_x; + AVIF_CHECKERR(avifROStreamSkipBits(s, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) surround_light_y; + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) periphery_luminance; + AVIF_CHECKERR(avifROStreamSkipBits(s, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) periphery_light_x; + AVIF_CHECKERR(avifROStreamSkipBits(s, 16), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) periphery_light_y; + return AVIF_RESULT_OK; +} + +static avifResult avifSkipNominalDiffuseWhite(avifROStream * s) +{ + AVIF_CHECKERR(avifROStreamSkipBits(s, 32), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) diffuse_white_luminance; + return AVIF_RESULT_OK; +} + +static avifResult avifParseMiniHDRProperties(avifROStream * s, uint32_t * hasClli, avifContentLightLevelInformationBox * clli) +{ + AVIF_CHECKERR(avifROStreamReadBitsU32(s, hasClli, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) clli_flag; + uint32_t hasMdcv, hasCclv, hasAmve, hasReve, hasNdwt; + AVIF_CHECKERR(avifROStreamReadBitsU32(s, &hasMdcv, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) mdcv_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(s, &hasCclv, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) cclv_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(s, &hasAmve, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) amve_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(s, &hasReve, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) reve_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(s, &hasNdwt, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) ndwt_flag; + if (*hasClli) { + AVIF_CHECKRES(avifParseContentLightLevelInformation(s, clli)); // ContentLightLevel clli; + } + if (hasMdcv) { + AVIF_CHECKRES(avifSkipMasteringDisplayColourVolume(s)); // MasteringDisplayColourVolume mdcv; + } + if (hasCclv) { + AVIF_CHECKRES(avifSkipContentColourVolume(s)); // ContentColourVolume cclv; + } + if (hasAmve) { + AVIF_CHECKRES(avifSkipAmbientViewingEnvironment(s)); // AmbientViewingEnvironment amve; + } + if (hasReve) { + AVIF_CHECKRES(avifSkipReferenceViewingEnvironment(s)); // ReferenceViewingEnvironment reve; + } + if (hasNdwt) { + AVIF_CHECKRES(avifSkipNominalDiffuseWhite(s)); // NominalDiffuseWhite ndwt; + } + return AVIF_RESULT_OK; +} +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + +// Implementation of section 2.3.3 of AV1 Codec ISO Media File Format Binding specification v1.2.0. +// See https://aomediacodec.github.io/av1-isobmff/v1.2.0.html#av1codecconfigurationbox-syntax. +static avifBool avifParseCodecConfiguration(avifROStream * s, avifCodecConfigurationBox * config, const char * configPropName, avifDiagnostics * diag) +{ + const size_t av1COffset = avifROStreamOffset(s); + + uint32_t marker, version; + AVIF_CHECK(avifROStreamReadBitsU32(s, &marker, /*bitCount=*/1)); // unsigned int (1) marker = 1; + if (!marker) { + avifDiagnosticsPrintf(diag, "%.4s contains illegal marker: [%u]", configPropName, marker); + return AVIF_FALSE; + } + AVIF_CHECK(avifROStreamReadBitsU32(s, &version, /*bitCount=*/7)); // unsigned int (7) version = 1; + if (version != 1) { + avifDiagnosticsPrintf(diag, "%.4s contains illegal version: [%u]", configPropName, version); + return AVIF_FALSE; + } + + AVIF_CHECK(avifROStreamReadBitsU8(s, &config->seqProfile, /*bitCount=*/3)); // unsigned int (3) seq_profile; + AVIF_CHECK(avifROStreamReadBitsU8(s, &config->seqLevelIdx0, /*bitCount=*/5)); // unsigned int (5) seq_level_idx_0; + AVIF_CHECK(avifROStreamReadBitsU8(s, &config->seqTier0, /*bitCount=*/1)); // unsigned int (1) seq_tier_0; + AVIF_CHECK(avifROStreamReadBitsU8(s, &config->highBitdepth, /*bitCount=*/1)); // unsigned int (1) high_bitdepth; + AVIF_CHECK(avifROStreamReadBitsU8(s, &config->twelveBit, /*bitCount=*/1)); // unsigned int (1) twelve_bit; + AVIF_CHECK(avifROStreamReadBitsU8(s, &config->monochrome, /*bitCount=*/1)); // unsigned int (1) monochrome; + AVIF_CHECK(avifROStreamReadBitsU8(s, &config->chromaSubsamplingX, /*bitCount=*/1)); // unsigned int (1) chroma_subsampling_x; + AVIF_CHECK(avifROStreamReadBitsU8(s, &config->chromaSubsamplingY, /*bitCount=*/1)); // unsigned int (1) chroma_subsampling_y; + AVIF_CHECK(avifROStreamReadBitsU8(s, &config->chromaSamplePosition, /*bitCount=*/2)); // unsigned int (2) chroma_sample_position; + + // unsigned int (3) reserved = 0; + // unsigned int (1) initial_presentation_delay_present; + // if (initial_presentation_delay_present) { + // unsigned int (4) initial_presentation_delay_minus_one; + // } else { + // unsigned int (4) reserved = 0; + // } + AVIF_CHECK(avifROStreamSkip(s, /*byteCount=*/1)); + + // According to section 2.2.1 of AV1 Image File Format specification v1.1.0: + // - Sequence Header OBUs should not be present in the AV1CodecConfigurationBox. + // - If a Sequence Header OBU is present in the AV1CodecConfigurationBox, + // it shall match the Sequence Header OBU in the AV1 Image Item Data. + // - Metadata OBUs, if present, shall match the values given in other item properties, + // such as the PixelInformationProperty or ColourInformationBox. + // See https://aomediacodec.github.io/av1-avif/v1.1.0.html#av1-configuration-item-property. + // For simplicity, the constraints above are not enforced. + // The following is skipped by avifParseItemPropertyContainerBox(). + // unsigned int (8) configOBUs[]; + + AVIF_CHECK(avifROStreamOffset(s) - av1COffset == 4); // Make sure avifParseCodecConfiguration() reads exactly 4 bytes. + return AVIF_TRUE; +} + +static avifBool avifParseCodecConfigurationBoxProperty(avifProperty * prop, + const uint8_t * raw, + size_t rawLen, + const char * configPropName, + avifDiagnostics * diag) +{ + char diagContext[10]; + snprintf(diagContext, sizeof(diagContext), "Box[%.4s]", configPropName); // "Box[av1C]" or "Box[av2C]" + BEGIN_STREAM(s, raw, rawLen, diag, diagContext); + return avifParseCodecConfiguration(&s, &prop->u.av1C, configPropName, diag); +} + +static avifBool avifParsePixelAspectRatioBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[pasp]"); + + avifPixelAspectRatioBox * pasp = &prop->u.pasp; + AVIF_CHECK(avifROStreamReadU32(&s, &pasp->hSpacing)); // unsigned int(32) hSpacing; + AVIF_CHECK(avifROStreamReadU32(&s, &pasp->vSpacing)); // unsigned int(32) vSpacing; + return AVIF_TRUE; +} + +static avifBool avifParseCleanApertureBoxProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[clap]"); + + avifCleanApertureBox * clap = &prop->u.clap; + AVIF_CHECK(avifROStreamReadU32(&s, &clap->widthN)); // unsigned int(32) cleanApertureWidthN; + AVIF_CHECK(avifROStreamReadU32(&s, &clap->widthD)); // unsigned int(32) cleanApertureWidthD; + AVIF_CHECK(avifROStreamReadU32(&s, &clap->heightN)); // unsigned int(32) cleanApertureHeightN; + AVIF_CHECK(avifROStreamReadU32(&s, &clap->heightD)); // unsigned int(32) cleanApertureHeightD; + AVIF_CHECK(avifROStreamReadU32(&s, &clap->horizOffN)); // unsigned int(32) horizOffN; + AVIF_CHECK(avifROStreamReadU32(&s, &clap->horizOffD)); // unsigned int(32) horizOffD; + AVIF_CHECK(avifROStreamReadU32(&s, &clap->vertOffN)); // unsigned int(32) vertOffN; + AVIF_CHECK(avifROStreamReadU32(&s, &clap->vertOffD)); // unsigned int(32) vertOffD; + return AVIF_TRUE; +} + +static avifBool avifParseImageRotationProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[irot]"); + + avifImageRotation * irot = &prop->u.irot; + uint8_t reserved; + AVIF_CHECK(avifROStreamReadBitsU8(&s, &reserved, /*bitCount=*/6)); // unsigned int (6) reserved = 0; + if (reserved) { + avifDiagnosticsPrintf(diag, "Box[irot] contains nonzero reserved bits [%u]", reserved); + return AVIF_FALSE; + } + AVIF_CHECK(avifROStreamReadBitsU8(&s, &irot->angle, /*bitCount=*/2)); // unsigned int (2) angle; + return AVIF_TRUE; +} + +static avifBool avifParseImageMirrorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[imir]"); + + avifImageMirror * imir = &prop->u.imir; + uint8_t reserved; + AVIF_CHECK(avifROStreamReadBitsU8(&s, &reserved, /*bitCount=*/7)); // unsigned int(7) reserved = 0; + if (reserved) { + avifDiagnosticsPrintf(diag, "Box[imir] contains nonzero reserved bits [%u]", reserved); + return AVIF_FALSE; + } + AVIF_CHECK(avifROStreamReadBitsU8(&s, &imir->axis, /*bitCount=*/1)); // unsigned int(1) axis; + return AVIF_TRUE; +} + +static avifResult avifParsePixelInformationProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[pixi]"); + uint32_t flags = 0; // px_flags + AVIF_CHECKERR(avifROStreamReadAndEnforceVersion(&s, /*enforcedVersion=*/0, &flags), AVIF_RESULT_BMFF_PARSE_FAILED); + + avifPixelInformationProperty * pixi = &prop->u.pixi; + AVIF_CHECKERR(avifROStreamRead(&s, &pixi->planeCount, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int (8) num_channels; + if (pixi->planeCount < 1 || pixi->planeCount > MAX_PIXI_PLANE_DEPTHS) { + avifDiagnosticsPrintf(diag, "Box[pixi] contains unsupported plane count [%u]", pixi->planeCount); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + for (uint8_t i = 0; i < pixi->planeCount; ++i) { + AVIF_CHECKERR(avifROStreamRead(&s, &pixi->planeDepths[i], 1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int (8) bits_per_channel; + if (pixi->planeDepths[i] == 0) { + avifDiagnosticsPrintf(diag, "Box[pixi] plane depth shall not be 0 for channel %u", i); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + if (pixi->planeDepths[i] > 16) { + avifDiagnosticsPrintf(diag, "Box[pixi] plane depth %d is not supported", (int)pixi->planeDepths[i]); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (pixi->planeDepths[i] != pixi->planeDepths[0]) { + avifDiagnosticsPrintf(diag, + "Box[pixi] contains unsupported mismatched plane depths [%u != %u]", + pixi->planeDepths[i], + pixi->planeDepths[0]); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + } +#if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI) + if (flags & 1) { + for (uint8_t i = 0; i < pixi->planeCount; ++i) { + uint8_t channelIdc, reserved, componentFormat, channelLabelFlag; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &channelIdc, /*bitCount=*/3), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(3) channel_idc; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &reserved, /*bitCount=*/1), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(1) reserved = 0; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &componentFormat, /*bitCount=*/2), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(2) component_format; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &pixi->subsamplingFlag[i], /*bitCount=*/1), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(1) subsampling_flag; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &channelLabelFlag, /*bitCount=*/1), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(1) channel_label_flag; + if (pixi->subsamplingFlag[i]) { + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &pixi->subsamplingType[i], /*bitCount=*/4), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(4) subsampling_type; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &pixi->subsamplingLocation[i], /*bitCount=*/4), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(4) subsampling_location; + } + + // ISO/IEC 23008-12:2024/CDAM 2:2025 section 6.5.6.3: + // This field indicates the contents of the channel. A value of 0 indicates colour/grayscale. A value of + // 1 indicates alpha. A value of 2 indicates depth. Values 3-7 are reserved for future use. At most one + // channel shall have a channel_idc of 1. + if (channelIdc != 0) { + avifDiagnosticsPrintf(diag, "Box[pixi] contains unsupported channel_idc %u for channel %u", channelIdc, i); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (reserved != 0) { + avifDiagnosticsPrintf(diag, "Box[pixi] contains non-zero reserved field %u for channel %u", reserved, i); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + // ISO/IEC 23008-12:2024/CDAM 2:2025 section 6.5.6.3: + // component_format: This field indicates the data type of the channel as defined by the component_format + // values in ISO/IEC 23001-17 where component_bit_depth is considered to be equal to bits_per_channel. + // ISO/IEC 23001-17 section 5.2.1.2: + // component_format: When equal to 0, component value is an unsigned integer coded on component_bit_depth bits. + if (componentFormat != 0) { + avifDiagnosticsPrintf(diag, "Box[pixi] contains unsupported component_format %u for channel %u", componentFormat, i); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (pixi->subsamplingFlag[i]) { + if (pixi->subsamplingType[i] >= AVIF_PIXI_SUBSAMPLING_RESERVED) { + avifDiagnosticsPrintf(diag, + "Box[pixi] contains reserved subsampling_type %u for channel %u", + pixi->subsamplingType[i], + i); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + if (pixi->subsamplingLocation[i] > 4) { + avifDiagnosticsPrintf(diag, + "Box[pixi] contains reserved subsampling_location %u for channel %u", + pixi->subsamplingLocation[i], + i); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } + if (channelLabelFlag) { + AVIF_CHECKERR(avifROStreamReadString(&s, NULL, 0), AVIF_RESULT_BMFF_PARSE_FAILED); // utf8string channel_label; (skipped) + } + } + } +#endif // AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI + return AVIF_RESULT_OK; +} + +static avifBool avifParseOperatingPointSelectorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[a1op]"); + + avifOperatingPointSelectorProperty * a1op = &prop->u.a1op; + AVIF_CHECK(avifROStreamRead(&s, &a1op->opIndex, 1)); + if (a1op->opIndex > 31) { // 31 is AV1's max operating point value + avifDiagnosticsPrintf(diag, "Box[a1op] contains an unsupported operating point [%u]", a1op->opIndex); + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +static avifBool avifParseLayerSelectorProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[lsel]"); + + avifLayerSelectorProperty * lsel = &prop->u.lsel; + AVIF_CHECK(avifROStreamReadU16(&s, &lsel->layerID)); + if ((lsel->layerID != 0xFFFF) && (lsel->layerID >= AVIF_MAX_AV1_LAYER_COUNT)) { + avifDiagnosticsPrintf(diag, "Box[lsel] contains an unsupported layer [%u]", lsel->layerID); + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +static avifBool avifParseAV1LayeredImageIndexingProperty(avifProperty * prop, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[a1lx]"); + + avifAV1LayeredImageIndexingProperty * a1lx = &prop->u.a1lx; + + uint8_t largeSize = 0; + AVIF_CHECK(avifROStreamRead(&s, &largeSize, 1)); + if (largeSize & 0xFE) { + avifDiagnosticsPrintf(diag, "Box[a1lx] has bits set in the reserved section [%u]", largeSize); + return AVIF_FALSE; + } + + for (int i = 0; i < 3; ++i) { + if (largeSize) { + AVIF_CHECK(avifROStreamReadU32(&s, &a1lx->layerSize[i])); + } else { + uint16_t layerSize16; + AVIF_CHECK(avifROStreamReadU16(&s, &layerSize16)); + a1lx->layerSize[i] = (uint32_t)layerSize16; + } + } + + // Layer sizes will be validated later (when the item's size is known) + return AVIF_TRUE; +} + +static avifResult avifParseItemPropertyContainerBox(avifPropertyArray * properties, + uint64_t rawOffset, + const uint8_t * raw, + size_t rawLen, + avifBool isTrack, + avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[ipco]"); + + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader header; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &header), AVIF_RESULT_BMFF_PARSE_FAILED); + + avifProperty * prop = (avifProperty *)avifArrayPush(properties); + AVIF_CHECKERR(prop != NULL, AVIF_RESULT_OUT_OF_MEMORY); + memcpy(prop->type, header.type, 4); + prop->isOpaque = AVIF_FALSE; + if (!memcmp(header.type, "ispe", 4)) { + AVIF_CHECKERR(avifParseImageSpatialExtentsProperty(prop, avifROStreamCurrent(&s), header.size, diag), + AVIF_RESULT_BMFF_PARSE_FAILED); + } else if ((!memcmp(header.type, "auxC", 4) && !isTrack) || (!memcmp(header.type, "auxi", 4) && isTrack)) { + AVIF_CHECKERR(avifParseAuxiliaryTypeProperty(prop, avifROStreamCurrent(&s), header.size, diag), AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "colr", 4)) { + AVIF_CHECKERR(avifParseColourInformationBox(prop, rawOffset + avifROStreamOffset(&s), avifROStreamCurrent(&s), header.size, diag), + AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "av1C", 4)) { + AVIF_CHECKERR(avifParseCodecConfigurationBoxProperty(prop, avifROStreamCurrent(&s), header.size, "av1C", diag), + AVIF_RESULT_BMFF_PARSE_FAILED); +#if defined(AVIF_CODEC_AVM) + } else if (!memcmp(header.type, "av2C", 4)) { + AVIF_CHECKERR(avifParseCodecConfigurationBoxProperty(prop, avifROStreamCurrent(&s), header.size, "av2C", diag), + AVIF_RESULT_BMFF_PARSE_FAILED); +#endif + } else if (!memcmp(header.type, "pasp", 4)) { + AVIF_CHECKERR(avifParsePixelAspectRatioBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag), + AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "clap", 4)) { + AVIF_CHECKERR(avifParseCleanApertureBoxProperty(prop, avifROStreamCurrent(&s), header.size, diag), + AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "irot", 4)) { + AVIF_CHECKERR(avifParseImageRotationProperty(prop, avifROStreamCurrent(&s), header.size, diag), AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "imir", 4)) { + AVIF_CHECKERR(avifParseImageMirrorProperty(prop, avifROStreamCurrent(&s), header.size, diag), AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "pixi", 4)) { + AVIF_CHECKRES(avifParsePixelInformationProperty(prop, avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "a1op", 4)) { + AVIF_CHECKERR(avifParseOperatingPointSelectorProperty(prop, avifROStreamCurrent(&s), header.size, diag), + AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "lsel", 4)) { + AVIF_CHECKERR(avifParseLayerSelectorProperty(prop, avifROStreamCurrent(&s), header.size, diag), AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "a1lx", 4)) { + AVIF_CHECKERR(avifParseAV1LayeredImageIndexingProperty(prop, avifROStreamCurrent(&s), header.size, diag), + AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "clli", 4)) { + AVIF_CHECKRES(avifParseContentLightLevelInformationBox(prop, avifROStreamCurrent(&s), header.size, diag)); + } else { + prop->isOpaque = AVIF_TRUE; + memset(&prop->u.opaque, 0, sizeof(prop->u.opaque)); + memcpy(prop->u.opaque.usertype, header.usertype, sizeof(prop->u.opaque.usertype)); + AVIF_CHECKRES(avifRWDataSet(&prop->u.opaque.boxPayload, avifROStreamCurrent(&s), header.size)); + } + + AVIF_CHECKERR(avifROStreamSkip(&s, header.size), AVIF_RESULT_BMFF_PARSE_FAILED); + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseItemPropertyAssociation(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag, uint32_t * outVersionAndFlags) +{ + // NOTE: If this function ever adds support for versions other than [0,1] or flags other than + // [0,1], please increase the value of MAX_IPMA_VERSION_AND_FLAGS_SEEN accordingly. + + BEGIN_STREAM(s, raw, rawLen, diag, "Box[ipma]"); + + uint8_t version; + uint32_t flags; + AVIF_CHECKERR(avifROStreamReadVersionAndFlags(&s, &version, &flags), AVIF_RESULT_BMFF_PARSE_FAILED); + avifBool propertyIndexIsU15 = ((flags & 0x1) != 0); + *outVersionAndFlags = ((uint32_t)version << 24) | flags; + + uint32_t entryCount; + AVIF_CHECKERR(avifROStreamReadU32(&s, &entryCount), AVIF_RESULT_BMFF_PARSE_FAILED); + unsigned int prevItemID = 0; + for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) { + // ISO/IEC 14496-12, Seventh edition, 2022-01, Section 8.11.14.1: + // Each ItemPropertyAssociationBox shall be ordered by increasing item_ID, and there shall + // be at most one occurrence of a given item_ID, in the set of ItemPropertyAssociationBox + // boxes. + unsigned int itemID; + if (version < 1) { + uint16_t tmp; + AVIF_CHECKERR(avifROStreamReadU16(&s, &tmp), AVIF_RESULT_BMFF_PARSE_FAILED); + itemID = tmp; + } else { + AVIF_CHECKERR(avifROStreamReadU32(&s, &itemID), AVIF_RESULT_BMFF_PARSE_FAILED); + } + AVIF_CHECKRES(avifCheckItemID("ipma", itemID, diag)); + if (itemID <= prevItemID) { + avifDiagnosticsPrintf(diag, "Box[ipma] item IDs are not ordered by increasing ID"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + prevItemID = itemID; + + avifDecoderItem * item; + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, itemID, &item)); + if (item->ipmaSeen) { + avifDiagnosticsPrintf(diag, "Duplicate Box[ipma] for item ID [%u]", itemID); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + item->ipmaSeen = AVIF_TRUE; + + uint8_t associationCount; + AVIF_CHECKERR(avifROStreamRead(&s, &associationCount, 1), AVIF_RESULT_BMFF_PARSE_FAILED); + for (uint8_t associationIndex = 0; associationIndex < associationCount; ++associationIndex) { + uint8_t essential; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &essential, /*bitCount=*/1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) essential; + uint32_t propertyIndex; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &propertyIndex, /*bitCount=*/propertyIndexIsU15 ? 15 : 7), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(7/15) property_index; + + // ISO/IEC 14496-12 Section 8.11.14.3: + // 0 indicating that no property is associated (the essential indicator shall also be 0) + if (propertyIndex == 0) { + if (essential) { + avifDiagnosticsPrintf(diag, "Box[ipma] for item ID [%u] contains an illegal essential property index 0", itemID); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + continue; + } + --propertyIndex; // 1-indexed + + if (propertyIndex >= meta->properties.count) { + avifDiagnosticsPrintf(diag, + "Box[ipma] for item ID [%u] contains an illegal property index [%u] (out of [%u] properties)", + itemID, + propertyIndex, + meta->properties.count); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + // Copy property to item + const avifProperty * srcProp = &meta->properties.prop[propertyIndex]; + + // Some properties are supported and parsed by libavif. + // Other properties are forwarded to the user as opaque blobs. + const avifBool supportedType = !srcProp->isOpaque; + if (supportedType) { + if (essential) { + // Verify that it is legal for this property to be flagged as essential. Any + // types in this list are *required* in the spec to not be flagged as essential + // when associated with an item. + static const char * const nonessentialTypes[] = { + + // AVIF: Section 2.3.2.3.2: "If associated, it shall not be marked as essential." + "a1lx" + + }; + size_t nonessentialTypesCount = sizeof(nonessentialTypes) / sizeof(nonessentialTypes[0]); + for (size_t i = 0; i < nonessentialTypesCount; ++i) { + if (!memcmp(srcProp->type, nonessentialTypes[i], 4)) { + avifDiagnosticsPrintf(diag, + "Item ID [%u] has a %s property association which must not be marked essential, but is", + itemID, + nonessentialTypes[i]); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } + } else { + // Verify that it is legal for this property to not be flagged as essential. Any + // types in this list are *required* in the spec to be flagged as essential when + // associated with an item. + static const char * const essentialTypes[] = { + + // AVIF: Section 2.3.2.1.1: "If associated, it shall be marked as essential." + "a1op", + + // HEIF: Section 6.5.11.1: "essential shall be equal to 1 for an 'lsel' item property." + "lsel", + + // MIAF 2019/Amd. 2:2021: Section 7.3.9: + // All transformative properties associated with coded and derived images shall be + // marked as essential + // It makes no sense to allow for non-essential crop/orientation associated with an item + // that is not a coded or derived image, so for simplicity 'item' is not checked here. + "clap", + "irot", + "imir" + + }; + size_t essentialTypesCount = sizeof(essentialTypes) / sizeof(essentialTypes[0]); + for (size_t i = 0; i < essentialTypesCount; ++i) { + if (!memcmp(srcProp->type, essentialTypes[i], 4)) { + avifDiagnosticsPrintf(diag, + "Item ID [%u] has a %s property association which must be marked essential, but is not", + itemID, + essentialTypes[i]); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } + } + + // Supported and valid; associate it with this item. + avifProperty * dstProp = (avifProperty *)avifArrayPush(&item->properties); + AVIF_CHECKERR(dstProp != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *dstProp = *srcProp; + } else { + if (essential) { + // ISO/IEC 23008-12 Section 10.2.1: + // Under any brand, the primary item (or an alternative if alternative support is required) + // shall be processable by a reader implementing only the required features of that brand. + // Specifically, given that each brand has a set of properties that a reader is required to + // support: the item shall not have properties that are marked as essential and are outside + // this set. + // It is assumed that this rule also applies to items the primary item depends on (such as + // the cells of a grid). + + // Discovered an essential item property that libavif doesn't support! + // Make a note to ignore this item later. + item->hasUnsupportedEssentialProperty = AVIF_TRUE; + } + + // Will be forwarded to the user through avifImage::properties. + avifProperty * dstProp = (avifProperty *)avifArrayPush(&item->properties); + AVIF_CHECKERR(dstProp != NULL, AVIF_RESULT_OUT_OF_MEMORY); + dstProp->isOpaque = AVIF_TRUE; + memcpy(dstProp->type, srcProp->type, sizeof(dstProp->type)); + memcpy(dstProp->u.opaque.usertype, srcProp->u.opaque.usertype, sizeof(dstProp->u.opaque.usertype)); + AVIF_CHECKRES( + avifRWDataSet(&dstProp->u.opaque.boxPayload, srcProp->u.opaque.boxPayload.data, srcProp->u.opaque.boxPayload.size)); + } + } + } + return AVIF_RESULT_OK; +} + +static avifBool avifParsePrimaryItemBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + if (meta->primaryItemID > 0) { + // Illegal to have multiple pitm boxes, bail out + avifDiagnosticsPrintf(diag, "Multiple boxes of unique Box[pitm] found"); + return AVIF_FALSE; + } + + BEGIN_STREAM(s, raw, rawLen, diag, "Box[pitm]"); + + uint8_t version; + AVIF_CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL)); + + if (version == 0) { + uint16_t tmp16; + AVIF_CHECK(avifROStreamReadU16(&s, &tmp16)); // unsigned int(16) item_ID; + meta->primaryItemID = tmp16; + } else { + AVIF_CHECK(avifROStreamReadU32(&s, &meta->primaryItemID)); // unsigned int(32) item_ID; + } + return AVIF_TRUE; +} + +static avifBool avifParseItemDataBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + // Check to see if we've already seen an idat box for this meta box. If so, bail out + if (meta->idat.size > 0) { + avifDiagnosticsPrintf(diag, "Meta box contains multiple idat boxes"); + return AVIF_FALSE; + } + if (rawLen == 0) { + avifDiagnosticsPrintf(diag, "idat box has a length of 0"); + return AVIF_FALSE; + } + + if (avifRWDataSet(&meta->idat, raw, rawLen) != AVIF_RESULT_OK) { + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +static avifResult avifParseItemPropertiesBox(avifMeta * meta, uint64_t rawOffset, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[iprp]"); + + avifBoxHeader ipcoHeader; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &ipcoHeader), AVIF_RESULT_BMFF_PARSE_FAILED); + if (memcmp(ipcoHeader.type, "ipco", 4)) { + avifDiagnosticsPrintf(diag, "Failed to find Box[ipco] as the first box in Box[iprp]"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + // Read all item properties inside of ItemPropertyContainerBox + AVIF_CHECKRES(avifParseItemPropertyContainerBox(&meta->properties, + rawOffset + avifROStreamOffset(&s), + avifROStreamCurrent(&s), + ipcoHeader.size, + /*isTrack=*/AVIF_FALSE, + diag)); + AVIF_CHECKERR(avifROStreamSkip(&s, ipcoHeader.size), AVIF_RESULT_BMFF_PARSE_FAILED); + + uint32_t versionAndFlagsSeen[MAX_IPMA_VERSION_AND_FLAGS_SEEN]; + uint32_t versionAndFlagsSeenCount = 0; + + // Now read all ItemPropertyAssociation until the end of the box, and make associations + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader ipmaHeader; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &ipmaHeader), AVIF_RESULT_BMFF_PARSE_FAILED); + + if (!memcmp(ipmaHeader.type, "ipma", 4)) { + uint32_t versionAndFlags; + AVIF_CHECKRES(avifParseItemPropertyAssociation(meta, avifROStreamCurrent(&s), ipmaHeader.size, diag, &versionAndFlags)); + for (uint32_t i = 0; i < versionAndFlagsSeenCount; ++i) { + if (versionAndFlagsSeen[i] == versionAndFlags) { + // BMFF (ISO/IEC 14496-12:2022) 8.11.14.1 - There shall be at most one + // ItemPropertyAssociationBox with a given pair of values of version and + // flags. + avifDiagnosticsPrintf(diag, "Multiple Box[ipma] with a given pair of values of version and flags. See BMFF (ISO/IEC 14496-12:2022) 8.11.14.1"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } + if (versionAndFlagsSeenCount == MAX_IPMA_VERSION_AND_FLAGS_SEEN) { + avifDiagnosticsPrintf(diag, "Exceeded possible count of unique ipma version and flags tuples"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + versionAndFlagsSeen[versionAndFlagsSeenCount] = versionAndFlags; + ++versionAndFlagsSeenCount; + } else { + // These must all be type ipma + avifDiagnosticsPrintf(diag, "Box[iprp] contains a box that isn't type 'ipma'"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + AVIF_CHECKERR(avifROStreamSkip(&s, ipmaHeader.size), AVIF_RESULT_BMFF_PARSE_FAILED); + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseItemInfoEntry(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + // Section 8.11.6.2 of ISO/IEC 14496-12. + BEGIN_STREAM(s, raw, rawLen, diag, "Box[infe]"); + + uint8_t version; + uint32_t flags; + AVIF_CHECKERR(avifROStreamReadVersionAndFlags(&s, &version, &flags), AVIF_RESULT_BMFF_PARSE_FAILED); + // Version 2+ is required for item_type + if (version != 2 && version != 3) { + avifDiagnosticsPrintf(s.diag, "%s: Expecting box version 2 or 3, got version %u", s.diagContext, version); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + // Ignore flags&1. A value of 1 corresponds to a hidden image item (not intended to be displayed). + // There could be files wrongly setting that flag to 1 for items output as "to be displayed" + // by libavif so far, so keep that lenient behavior for simplicity and backward compatibility. + + uint32_t itemID; + if (version == 2) { + uint16_t tmp; + AVIF_CHECKERR(avifROStreamReadU16(&s, &tmp), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) item_ID; + itemID = tmp; + } else { + AVIF_ASSERT_OR_RETURN(version == 3); + AVIF_CHECKERR(avifROStreamReadU32(&s, &itemID), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) item_ID; + } + AVIF_CHECKRES(avifCheckItemID("infe", itemID, diag)); + uint16_t itemProtectionIndex; + AVIF_CHECKERR(avifROStreamReadU16(&s, &itemProtectionIndex), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) item_protection_index; + uint8_t itemType[4]; + AVIF_CHECKERR(avifROStreamRead(&s, itemType, 4), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) item_type; + AVIF_CHECKERR(avifROStreamReadString(&s, NULL, 0), AVIF_RESULT_BMFF_PARSE_FAILED); // utf8string item_name; (skipped) + avifContentType contentType; + if (!memcmp(itemType, "mime", 4)) { + AVIF_CHECKERR(avifROStreamReadString(&s, contentType.contentType, CONTENTTYPE_SIZE), AVIF_RESULT_BMFF_PARSE_FAILED); // utf8string content_type; + // utf8string content_encoding; //optional + } else { + // if (item_type == 'uri ') { + // utf8string item_uri_type; + // } + memset(&contentType, 0, sizeof(contentType)); + } + + avifDecoderItem * item; + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, itemID, &item)); + + memcpy(item->type, itemType, sizeof(itemType)); + item->contentType = contentType; + return AVIF_RESULT_OK; +} + +static avifResult avifParseItemInfoBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[iinf]"); + + uint8_t version; + AVIF_CHECKERR(avifROStreamReadVersionAndFlags(&s, &version, NULL), AVIF_RESULT_BMFF_PARSE_FAILED); + uint32_t entryCount; + if (version == 0) { + uint16_t tmp; + AVIF_CHECKERR(avifROStreamReadU16(&s, &tmp), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) entry_count; + entryCount = tmp; + } else if (version == 1) { + AVIF_CHECKERR(avifROStreamReadU32(&s, &entryCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) entry_count; + } else { + avifDiagnosticsPrintf(diag, "Box[iinf] has an unsupported version %u", version); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + for (uint32_t entryIndex = 0; entryIndex < entryCount; ++entryIndex) { + avifBoxHeader infeHeader; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &infeHeader), AVIF_RESULT_BMFF_PARSE_FAILED); + + if (!memcmp(infeHeader.type, "infe", 4)) { + AVIF_CHECKRES(avifParseItemInfoEntry(meta, avifROStreamCurrent(&s), infeHeader.size, diag)); + } else { + // These must all be type infe + avifDiagnosticsPrintf(diag, "Box[iinf] contains a box that isn't type 'infe'"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + AVIF_CHECKERR(avifROStreamSkip(&s, infeHeader.size), AVIF_RESULT_BMFF_PARSE_FAILED); + } + + return AVIF_RESULT_OK; +} + +static avifResult avifParseItemReferenceBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[iref]"); + + uint8_t version; + AVIF_CHECKERR(avifROStreamReadVersionAndFlags(&s, &version, NULL), AVIF_RESULT_BMFF_PARSE_FAILED); + if (version > 1) { + // iref versions > 1 are not supported. Skip it. + return AVIF_RESULT_OK; + } + + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader irefHeader; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &irefHeader), AVIF_RESULT_BMFF_PARSE_FAILED); + + uint32_t fromID = 0; + if (version == 0) { + uint16_t tmp; + AVIF_CHECKERR(avifROStreamReadU16(&s, &tmp), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) from_item_ID; + fromID = tmp; + } else { + // version == 1 + AVIF_CHECKERR(avifROStreamReadU32(&s, &fromID), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) from_item_ID; + } + // ISO 14496-12 section 8.11.12.1: "index values start at 1" + AVIF_CHECKRES(avifCheckItemID("iref", fromID, diag)); + + avifDecoderItem * item; + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, fromID, &item)); + if (!memcmp(irefHeader.type, "dimg", 4)) { + if (item->hasDimgFrom) { + // ISO/IEC 23008-12 (HEIF) 6.6.1: The number of SingleItemTypeReferenceBoxes with the box type 'dimg' + // and with the same value of from_item_ID shall not be greater than 1. + avifDiagnosticsPrintf(diag, "Box[iinf] contains duplicate boxes of type 'dimg' with the same from_item_ID value %u", fromID); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + item->hasDimgFrom = AVIF_TRUE; + } + + uint16_t referenceCount = 0; + AVIF_CHECKERR(avifROStreamReadU16(&s, &referenceCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) reference_count; + + for (uint16_t refIndex = 0; refIndex < referenceCount; ++refIndex) { + uint32_t toID = 0; + if (version == 0) { + uint16_t tmp; + AVIF_CHECKERR(avifROStreamReadU16(&s, &tmp), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(16) to_item_ID; + toID = tmp; + } else { + // version == 1 + AVIF_CHECKERR(avifROStreamReadU32(&s, &toID), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) to_item_ID; + } + AVIF_CHECKRES(avifCheckItemID("iref", toID, diag)); + + // Read this reference as "{fromID} is a {irefType} for {toID}" + if (!memcmp(irefHeader.type, "thmb", 4)) { + item->thumbnailForID = toID; + } else if (!memcmp(irefHeader.type, "auxl", 4)) { + item->auxForID = toID; + } else if (!memcmp(irefHeader.type, "cdsc", 4)) { + item->descForID = toID; + } else if (!memcmp(irefHeader.type, "dimg", 4)) { + // derived images refer in the opposite direction + avifDecoderItem * dimg; + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, toID, &dimg)); + + // Section 8.11.12.1 of ISO/IEC 14496-12: + // The items linked to are then represented by an array of to_item_IDs; + // within a given array, a given value shall occur at most once. + AVIF_CHECKERR(dimg->dimgForID != fromID, AVIF_RESULT_INVALID_IMAGE_GRID); + // A given value may occur within multiple arrays but this is not supported by libavif. + AVIF_CHECKERR(dimg->dimgForID == 0, AVIF_RESULT_NOT_IMPLEMENTED); + dimg->dimgForID = fromID; + dimg->dimgIdx = refIndex; + } else if (!memcmp(irefHeader.type, "prem", 4)) { + item->premByID = toID; + } + } + } + + return AVIF_RESULT_OK; +} + +static avifResult avifParseGroupsListBox(avifMeta * meta, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[grpl]"); + + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader groupHeader; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &groupHeader), AVIF_RESULT_BMFF_PARSE_FAILED); + // We don't check the flag or version as they depend on the grouping type (and for simplicity). + // ISO/IEC 14496-12:2024 Section 8.15.3.2 + // version shall be 0 unless defined otherwise for the grouping_type. Any values of flags such that + // (flags & 0x000FFF) is not equal to 0 are reserved. The values of flags shall be such that (flags + // & 0xFFF000) is equal to 0 unless defined otherwise for the grouping_type. + AVIF_CHECKERR(avifROStreamReadVersionAndFlags(&s, NULL, NULL), AVIF_RESULT_BMFF_PARSE_FAILED); + + avifEntityToGroup * group = avifArrayPush(&meta->entityToGroups); + AVIF_CHECKERR(group != NULL, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKERR(avifArrayCreate(&group->entityIDs, sizeof(uint32_t), 2), AVIF_RESULT_OUT_OF_MEMORY); + + memcpy(group->groupingType, groupHeader.type, 4); + AVIF_CHECKERR(avifROStreamReadU32(&s, &group->groupID), AVIF_RESULT_BMFF_PARSE_FAILED); + uint32_t numEntitiesInGroup; + AVIF_CHECKERR(avifROStreamReadU32(&s, &numEntitiesInGroup), AVIF_RESULT_BMFF_PARSE_FAILED); + for (uint32_t i = 0; i < numEntitiesInGroup; ++i) { + uint32_t * entityId = avifArrayPush(&group->entityIDs); + AVIF_CHECKERR(entityId != NULL, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKERR(avifROStreamReadU32(&s, entityId), AVIF_RESULT_BMFF_PARSE_FAILED); + } + } + + return AVIF_RESULT_OK; +} + +static avifResult avifParseMetaBox(avifMeta * meta, uint64_t rawOffset, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[meta]"); + + uint32_t flags; + AVIF_CHECKERR(avifROStreamReadAndEnforceVersion(&s, 0, &flags), AVIF_RESULT_BMFF_PARSE_FAILED); + + ++meta->idatID; // for tracking idat + + avifBool firstBox = AVIF_TRUE; + uint32_t uniqueBoxFlags = 0; + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader header; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &header), AVIF_RESULT_BMFF_PARSE_FAILED); + + if (firstBox) { + if (!memcmp(header.type, "hdlr", 4)) { + uint8_t handlerType[4]; + AVIF_CHECKERR(avifParseHandlerBox(avifROStreamCurrent(&s), header.size, handlerType, diag), AVIF_RESULT_BMFF_PARSE_FAILED); + // HEIF (ISO/IEC 23008-12:2022), Section 6.2: + // The handler type for the MetaBox shall be 'pict'. + if (memcmp(handlerType, "pict", 4) != 0) { + avifDiagnosticsPrintf(diag, "Box[hdlr] handler_type is not 'pict'"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + firstBox = AVIF_FALSE; + } else { + // hdlr must be the first box! + avifDiagnosticsPrintf(diag, "Box[meta] does not have a Box[hdlr] as its first child box"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } else if (!memcmp(header.type, "hdlr", 4)) { + avifDiagnosticsPrintf(diag, "Box[meta] contains a duplicate unique box of type 'hdlr'"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } else if (!memcmp(header.type, "iloc", 4)) { + AVIF_CHECKERR(uniqueBoxSeen(&uniqueBoxFlags, AVIF_UNIQUE_ILOC, "meta", "iloc", diag), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKRES(avifParseItemLocationBox(meta, avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "pitm", 4)) { + AVIF_CHECKERR(uniqueBoxSeen(&uniqueBoxFlags, AVIF_UNIQUE_PITM, "meta", "pitm", diag), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifParsePrimaryItemBox(meta, avifROStreamCurrent(&s), header.size, diag), AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "idat", 4)) { + AVIF_CHECKERR(uniqueBoxSeen(&uniqueBoxFlags, AVIF_UNIQUE_IDAT, "meta", "idat", diag), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifParseItemDataBox(meta, avifROStreamCurrent(&s), header.size, diag), AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "iprp", 4)) { + AVIF_CHECKERR(uniqueBoxSeen(&uniqueBoxFlags, AVIF_UNIQUE_IPRP, "meta", "iprp", diag), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKRES(avifParseItemPropertiesBox(meta, rawOffset + avifROStreamOffset(&s), avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "iinf", 4)) { + AVIF_CHECKERR(uniqueBoxSeen(&uniqueBoxFlags, AVIF_UNIQUE_IINF, "meta", "iinf", diag), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKRES(avifParseItemInfoBox(meta, avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "iref", 4)) { + AVIF_CHECKERR(uniqueBoxSeen(&uniqueBoxFlags, AVIF_UNIQUE_IREF, "meta", "iref", diag), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKRES(avifParseItemReferenceBox(meta, avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "grpl", 4)) { + AVIF_CHECKERR(uniqueBoxSeen(&uniqueBoxFlags, AVIF_UNIQUE_GRPL, "meta", "grpl", diag), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKRES(avifParseGroupsListBox(meta, avifROStreamCurrent(&s), header.size, diag)); + } + + AVIF_CHECKERR(avifROStreamSkip(&s, header.size), AVIF_RESULT_BMFF_PARSE_FAILED); + } + if (firstBox) { + // The meta box must not be empty (it must contain at least a hdlr box) + avifDiagnosticsPrintf(diag, "Box[meta] has no child boxes"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + return AVIF_RESULT_OK; +} + +static avifBool avifParseTrackHeaderBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[tkhd]"); + + uint8_t version; + AVIF_CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL)); + + uint32_t ignored32, trackID; + uint64_t ignored64; + if (version == 1) { + AVIF_CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time; + AVIF_CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time; + AVIF_CHECK(avifROStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID; + AVIF_CHECK(avifROStreamReadU32(&s, &ignored32)); // const unsigned int(32) reserved = 0; + AVIF_CHECK(avifROStreamReadU64(&s, &track->trackDuration)); // unsigned int(64) duration; + } else if (version == 0) { + uint32_t trackDuration; + AVIF_CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time; + AVIF_CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time; + AVIF_CHECK(avifROStreamReadU32(&s, &trackID)); // unsigned int(32) track_ID; + AVIF_CHECK(avifROStreamReadU32(&s, &ignored32)); // const unsigned int(32) reserved = 0; + AVIF_CHECK(avifROStreamReadU32(&s, &trackDuration)); // unsigned int(32) duration; + track->trackDuration = (trackDuration == AVIF_INDEFINITE_DURATION32) ? AVIF_INDEFINITE_DURATION64 : trackDuration; + } else { + // Unsupported version + avifDiagnosticsPrintf(diag, "Box[tkhd] has an unsupported version [%u]", version); + return AVIF_FALSE; + } + track->id = trackID; + + // Skipping the following 52 bytes here: + // ------------------------------------ + // const unsigned int(32)[2] reserved = 0; + // template int(16) layer = 0; + // template int(16) alternate_group = 0; + // template int(16) volume = {if track_is_audio 0x0100 else 0}; + // const unsigned int(16) reserved = 0; + // template int(32)[9] matrix= { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; // unity matrix + AVIF_CHECK(avifROStreamSkip(&s, 52)); + + uint32_t width, height; + AVIF_CHECK(avifROStreamReadU32(&s, &width)); // unsigned int(32) width; + AVIF_CHECK(avifROStreamReadU32(&s, &height)); // unsigned int(32) height; + track->width = width >> 16; + track->height = height >> 16; + + // TODO: support scaling based on width/height track header info? + + return AVIF_TRUE; +} + +static avifBool avifParseMediaHeaderBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[mdhd]"); + + uint8_t version; + AVIF_CHECK(avifROStreamReadVersionAndFlags(&s, &version, NULL)); + + uint32_t ignored32, mediaTimescale, mediaDuration32; + uint64_t ignored64, mediaDuration64; + if (version == 1) { + AVIF_CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) creation_time; + AVIF_CHECK(avifROStreamReadU64(&s, &ignored64)); // unsigned int(64) modification_time; + AVIF_CHECK(avifROStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale; + AVIF_CHECK(avifROStreamReadU64(&s, &mediaDuration64)); // unsigned int(64) duration; + track->mediaDuration = mediaDuration64; + } else if (version == 0) { + AVIF_CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) creation_time; + AVIF_CHECK(avifROStreamReadU32(&s, &ignored32)); // unsigned int(32) modification_time; + AVIF_CHECK(avifROStreamReadU32(&s, &mediaTimescale)); // unsigned int(32) timescale; + AVIF_CHECK(avifROStreamReadU32(&s, &mediaDuration32)); // unsigned int(32) duration; + track->mediaDuration = (uint64_t)mediaDuration32; + } else { + // Unsupported version + avifDiagnosticsPrintf(diag, "Box[mdhd] has an unsupported version [%u]", version); + return AVIF_FALSE; + } + + track->mediaTimescale = mediaTimescale; + return AVIF_TRUE; +} + +static avifResult avifParseChunkOffsetBox(avifSampleTable * sampleTable, avifBool largeOffsets, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, largeOffsets ? "Box[co64]" : "Box[stco]"); + + AVIF_CHECKERR(avifROStreamReadAndEnforceVersion(&s, /*enforcedVersion=*/0, /*flags=*/NULL), AVIF_RESULT_BMFF_PARSE_FAILED); + + uint32_t entryCount; + AVIF_CHECKERR(avifROStreamReadU32(&s, &entryCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) entry_count; + for (uint32_t i = 0; i < entryCount; ++i) { + uint64_t offset; + if (largeOffsets) { + AVIF_CHECKERR(avifROStreamReadU64(&s, &offset), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(64) chunk_offset; + } else { + uint32_t offset32; + AVIF_CHECKERR(avifROStreamReadU32(&s, &offset32), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) chunk_offset; + offset = (uint64_t)offset32; + } + + avifSampleTableChunk * chunk = (avifSampleTableChunk *)avifArrayPush(&sampleTable->chunks); + AVIF_CHECKERR(chunk != NULL, AVIF_RESULT_OUT_OF_MEMORY); + chunk->offset = offset; + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseSampleToChunkBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsc]"); + + AVIF_CHECKERR(avifROStreamReadAndEnforceVersion(&s, /*enforcedVersion=*/0, /*flags=*/NULL), AVIF_RESULT_BMFF_PARSE_FAILED); + + uint32_t entryCount; + AVIF_CHECKERR(avifROStreamReadU32(&s, &entryCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) entry_count; + uint32_t prevFirstChunk = 0; + for (uint32_t i = 0; i < entryCount; ++i) { + avifSampleTableSampleToChunk * sampleToChunk = (avifSampleTableSampleToChunk *)avifArrayPush(&sampleTable->sampleToChunks); + AVIF_CHECKERR(sampleToChunk != NULL, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKERR(avifROStreamReadU32(&s, &sampleToChunk->firstChunk), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) first_chunk; + AVIF_CHECKERR(avifROStreamReadU32(&s, &sampleToChunk->samplesPerChunk), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) samples_per_chunk; + AVIF_CHECKERR(avifROStreamReadU32(&s, &sampleToChunk->sampleDescriptionIndex), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) sample_description_index; + // The first_chunk fields should start with 1 and be strictly increasing. + if (i == 0) { + if (sampleToChunk->firstChunk != 1) { + avifDiagnosticsPrintf(diag, "Box[stsc] does not begin with chunk 1 [%u]", sampleToChunk->firstChunk); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } else { + if (sampleToChunk->firstChunk <= prevFirstChunk) { + avifDiagnosticsPrintf(diag, "Box[stsc] chunks are not strictly increasing"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } + prevFirstChunk = sampleToChunk->firstChunk; + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseSampleSizeBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsz]"); + + AVIF_CHECKERR(avifROStreamReadAndEnforceVersion(&s, /*enforcedVersion=*/0, /*flags=*/NULL), AVIF_RESULT_BMFF_PARSE_FAILED); + + uint32_t allSamplesSize, sampleCount; + AVIF_CHECKERR(avifROStreamReadU32(&s, &allSamplesSize), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) sample_size; + AVIF_CHECKERR(avifROStreamReadU32(&s, &sampleCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) sample_count; + + if (allSamplesSize > 0) { + sampleTable->allSamplesSize = allSamplesSize; + } else { + for (uint32_t i = 0; i < sampleCount; ++i) { + avifSampleTableSampleSize * sampleSize = (avifSampleTableSampleSize *)avifArrayPush(&sampleTable->sampleSizes); + AVIF_CHECKERR(sampleSize != NULL, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKERR(avifROStreamReadU32(&s, &sampleSize->size), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) entry_size; + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseSyncSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[stss]"); + + AVIF_CHECKERR(avifROStreamReadAndEnforceVersion(&s, /*enforcedVersion=*/0, /*flags=*/NULL), AVIF_RESULT_BMFF_PARSE_FAILED); + + uint32_t entryCount; + AVIF_CHECKERR(avifROStreamReadU32(&s, &entryCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) entry_count; + + for (uint32_t i = 0; i < entryCount; ++i) { + uint32_t sampleNumber = 0; + AVIF_CHECKERR(avifROStreamReadU32(&s, &sampleNumber), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) sample_number; + avifSyncSample * syncSample = (avifSyncSample *)avifArrayPush(&sampleTable->syncSamples); + AVIF_CHECKERR(syncSample != NULL, AVIF_RESULT_OUT_OF_MEMORY); + syncSample->sampleNumber = sampleNumber; + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseTimeToSampleBox(avifSampleTable * sampleTable, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[stts]"); + + AVIF_CHECKERR(avifROStreamReadAndEnforceVersion(&s, /*enforcedVersion=*/0, /*flags=*/NULL), AVIF_RESULT_BMFF_PARSE_FAILED); + + uint32_t entryCount; + AVIF_CHECKERR(avifROStreamReadU32(&s, &entryCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) entry_count; + + for (uint32_t i = 0; i < entryCount; ++i) { + avifSampleTableTimeToSample * timeToSample = (avifSampleTableTimeToSample *)avifArrayPush(&sampleTable->timeToSamples); + AVIF_CHECKERR(timeToSample != NULL, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKERR(avifROStreamReadU32(&s, &timeToSample->sampleCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) sample_count; + AVIF_CHECKERR(avifROStreamReadU32(&s, &timeToSample->sampleDelta), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) sample_delta; + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseSampleDescriptionBox(avifSampleTable * sampleTable, + uint64_t rawOffset, + const uint8_t * raw, + size_t rawLen, + avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[stsd]"); + + uint8_t version; + AVIF_CHECKERR(avifROStreamReadVersionAndFlags(&s, &version, NULL), AVIF_RESULT_BMFF_PARSE_FAILED); + + // Section 8.5.2.3 of ISO/IEC 14496-12: + // version is set to zero. A version number of 1 shall be treated as a version of 0. + if (version != 0 && version != 1) { + avifDiagnosticsPrintf(diag, "Box[stsd]: Expecting box version 0 or 1, got version %u", version); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + uint32_t entryCount; + AVIF_CHECKERR(avifROStreamReadU32(&s, &entryCount), AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(32) entry_count; + + for (uint32_t i = 0; i < entryCount; ++i) { + avifBoxHeader sampleEntryHeader; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &sampleEntryHeader), AVIF_RESULT_BMFF_PARSE_FAILED); + + avifSampleDescription * description = (avifSampleDescription *)avifArrayPush(&sampleTable->sampleDescriptions); + AVIF_CHECKERR(description != NULL, AVIF_RESULT_OUT_OF_MEMORY); + if (!avifArrayCreate(&description->properties, sizeof(avifProperty), 16)) { + avifArrayPop(&sampleTable->sampleDescriptions); + return AVIF_RESULT_OUT_OF_MEMORY; + } + memcpy(description->format, sampleEntryHeader.type, sizeof(description->format)); + const size_t sampleEntryBytes = sampleEntryHeader.size; + if (avifGetCodecType(description->format) != AVIF_CODEC_TYPE_UNKNOWN) { + if (sampleEntryBytes < VISUALSAMPLEENTRY_SIZE) { + avifDiagnosticsPrintf(diag, "Not enough bytes to parse VisualSampleEntry"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + AVIF_CHECKRES(avifParseItemPropertyContainerBox(&description->properties, + rawOffset + avifROStreamOffset(&s) + VISUALSAMPLEENTRY_SIZE, + avifROStreamCurrent(&s) + VISUALSAMPLEENTRY_SIZE, + sampleEntryBytes - VISUALSAMPLEENTRY_SIZE, + /*isTrack=*/AVIF_TRUE, + diag)); + } + + AVIF_CHECKERR(avifROStreamSkip(&s, sampleEntryBytes), AVIF_RESULT_BMFF_PARSE_FAILED); + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseSampleTableBox(avifTrack * track, uint64_t rawOffset, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + if (track->sampleTable) { + // A TrackBox may only have one SampleTable + avifDiagnosticsPrintf(diag, "Duplicate Box[stbl] for a single track detected"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + track->sampleTable = avifSampleTableCreate(); + AVIF_CHECKERR(track->sampleTable != NULL, AVIF_RESULT_OUT_OF_MEMORY); + + BEGIN_STREAM(s, raw, rawLen, diag, "Box[stbl]"); + + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader header; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &header), AVIF_RESULT_BMFF_PARSE_FAILED); + + if (!memcmp(header.type, "stco", 4)) { + AVIF_CHECKRES(avifParseChunkOffsetBox(track->sampleTable, AVIF_FALSE, avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "co64", 4)) { + AVIF_CHECKRES(avifParseChunkOffsetBox(track->sampleTable, AVIF_TRUE, avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "stsc", 4)) { + AVIF_CHECKRES(avifParseSampleToChunkBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "stsz", 4)) { + AVIF_CHECKRES(avifParseSampleSizeBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "stss", 4)) { + AVIF_CHECKRES(avifParseSyncSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "stts", 4)) { + AVIF_CHECKRES(avifParseTimeToSampleBox(track->sampleTable, avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "stsd", 4)) { + AVIF_CHECKRES(avifParseSampleDescriptionBox(track->sampleTable, + rawOffset + avifROStreamOffset(&s), + avifROStreamCurrent(&s), + header.size, + diag)); + } + + AVIF_CHECKERR(avifROStreamSkip(&s, header.size), AVIF_RESULT_BMFF_PARSE_FAILED); + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseMediaInformationBox(avifTrack * track, uint64_t rawOffset, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[minf]"); + + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader header; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &header), AVIF_RESULT_BMFF_PARSE_FAILED); + + if (!memcmp(header.type, "stbl", 4)) { + AVIF_CHECKRES(avifParseSampleTableBox(track, rawOffset + avifROStreamOffset(&s), avifROStreamCurrent(&s), header.size, diag)); + } + + AVIF_CHECKERR(avifROStreamSkip(&s, header.size), AVIF_RESULT_BMFF_PARSE_FAILED); + } + return AVIF_RESULT_OK; +} + +static avifResult avifParseMediaBox(avifTrack * track, uint64_t rawOffset, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[mdia]"); + + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader header; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &header), AVIF_RESULT_BMFF_PARSE_FAILED); + + if (!memcmp(header.type, "mdhd", 4)) { + AVIF_CHECKERR(avifParseMediaHeaderBox(track, avifROStreamCurrent(&s), header.size, diag), AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "minf", 4)) { + AVIF_CHECKRES( + avifParseMediaInformationBox(track, rawOffset + avifROStreamOffset(&s), avifROStreamCurrent(&s), header.size, diag)); + } else if (!memcmp(header.type, "hdlr", 4)) { + AVIF_CHECKERR(avifParseHandlerBox(avifROStreamCurrent(&s), header.size, track->handlerType, diag), + AVIF_RESULT_BMFF_PARSE_FAILED); + } + + AVIF_CHECKERR(avifROStreamSkip(&s, header.size), AVIF_RESULT_BMFF_PARSE_FAILED); + } + return AVIF_RESULT_OK; +} + +static avifBool avifTrackReferenceBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[tref]"); + + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader header; + AVIF_CHECK(avifROStreamReadBoxHeader(&s, &header)); + + if (!memcmp(header.type, "auxl", 4)) { + uint32_t toID; + AVIF_CHECK(avifROStreamReadU32(&s, &toID)); // unsigned int(32) track_IDs[]; + AVIF_CHECK(avifROStreamSkip(&s, header.size - sizeof(uint32_t))); // just take the first one + track->auxForID = toID; + } else if (!memcmp(header.type, "prem", 4)) { + uint32_t byID; + AVIF_CHECK(avifROStreamReadU32(&s, &byID)); // unsigned int(32) track_IDs[]; + AVIF_CHECK(avifROStreamSkip(&s, header.size - sizeof(uint32_t))); // just take the first one + track->premByID = byID; + } else { + AVIF_CHECK(avifROStreamSkip(&s, header.size)); + } + } + return AVIF_TRUE; +} + +static avifBool avifParseEditListBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[elst]"); + + uint8_t version; + uint32_t flags; + AVIF_CHECK(avifROStreamReadVersionAndFlags(&s, &version, &flags)); + + if ((flags & 1) == 0) { + track->isRepeating = AVIF_FALSE; + return AVIF_TRUE; + } + + track->isRepeating = AVIF_TRUE; + uint32_t entryCount; + AVIF_CHECK(avifROStreamReadU32(&s, &entryCount)); // unsigned int(32) entry_count; + if (entryCount != 1) { + avifDiagnosticsPrintf(diag, "Box[elst] contains an entry_count != 1 [%u]", entryCount); + return AVIF_FALSE; + } + + if (version == 1) { + AVIF_CHECK(avifROStreamReadU64(&s, &track->segmentDuration)); // unsigned int(64) segment_duration; + } else if (version == 0) { + uint32_t segmentDuration; + AVIF_CHECK(avifROStreamReadU32(&s, &segmentDuration)); // unsigned int(32) segment_duration; + track->segmentDuration = segmentDuration; + } else { + // Unsupported version + avifDiagnosticsPrintf(diag, "Box[elst] has an unsupported version [%u]", version); + return AVIF_FALSE; + } + if (track->segmentDuration == 0) { + avifDiagnosticsPrintf(diag, "Box[elst] Invalid value for segment_duration (0)."); + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +static avifBool avifParseEditBox(avifTrack * track, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[edts]"); + + avifBool elstBoxSeen = AVIF_FALSE; + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader header; + AVIF_CHECK(avifROStreamReadBoxHeader(&s, &header)); + + if (!memcmp(header.type, "elst", 4)) { + if (elstBoxSeen) { + avifDiagnosticsPrintf(diag, "More than one [elst] Box was found."); + return AVIF_FALSE; + } + AVIF_CHECK(avifParseEditListBox(track, avifROStreamCurrent(&s), header.size, diag)); + elstBoxSeen = AVIF_TRUE; + } + AVIF_CHECK(avifROStreamSkip(&s, header.size)); + } + if (!elstBoxSeen) { + avifDiagnosticsPrintf(diag, "Box[edts] contains no [elst] Box."); + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +static avifResult avifParseTrackBox(avifDecoderData * data, uint64_t rawOffset, const uint8_t * raw, size_t rawLen) +{ + BEGIN_STREAM(s, raw, rawLen, data->diag, "Box[trak]"); + + avifTrack * track = avifDecoderDataCreateTrack(data); + AVIF_CHECKERR(track != NULL, AVIF_RESULT_OUT_OF_MEMORY); + + avifBool edtsBoxSeen = AVIF_FALSE; + avifBool tkhdSeen = AVIF_FALSE; + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader header; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &header), AVIF_RESULT_BMFF_PARSE_FAILED); + + if (!memcmp(header.type, "tkhd", 4)) { + if (tkhdSeen) { + avifDiagnosticsPrintf(data->diag, "Box[trak] contains a duplicate unique box of type 'tkhd'"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + AVIF_CHECKERR(avifParseTrackHeaderBox(track, avifROStreamCurrent(&s), header.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED); + tkhdSeen = AVIF_TRUE; + } else if (!memcmp(header.type, "meta", 4)) { + AVIF_CHECKRES( + avifParseMetaBox(track->meta, rawOffset + avifROStreamOffset(&s), avifROStreamCurrent(&s), header.size, data->diag)); + } else if (!memcmp(header.type, "mdia", 4)) { + AVIF_CHECKRES(avifParseMediaBox(track, rawOffset + avifROStreamOffset(&s), avifROStreamCurrent(&s), header.size, data->diag)); + } else if (!memcmp(header.type, "tref", 4)) { + AVIF_CHECKERR(avifTrackReferenceBox(track, avifROStreamCurrent(&s), header.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED); + } else if (!memcmp(header.type, "edts", 4)) { + if (edtsBoxSeen) { + avifDiagnosticsPrintf(data->diag, "Box[trak] contains a duplicate unique box of type 'edts'"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + AVIF_CHECKERR(avifParseEditBox(track, avifROStreamCurrent(&s), header.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED); + edtsBoxSeen = AVIF_TRUE; + } + + AVIF_CHECKERR(avifROStreamSkip(&s, header.size), AVIF_RESULT_BMFF_PARSE_FAILED); + } + if (!tkhdSeen) { + avifDiagnosticsPrintf(data->diag, "Box[trak] does not contain a mandatory [tkhd] box"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + if (!edtsBoxSeen) { + track->repetitionCount = AVIF_REPETITION_COUNT_UNKNOWN; + } else if (track->isRepeating) { + if (track->trackDuration == AVIF_INDEFINITE_DURATION64) { + // If isRepeating is true and the track duration is unknown/indefinite, then set the repetition count to infinite + // (Section 9.6.1 of ISO/IEC 23008-12 Part 12). + track->repetitionCount = AVIF_REPETITION_COUNT_INFINITE; + } else { + // Section 9.6.1. of ISO/IEC 23008-12 Part 12: 1, the entire edit list is repeated a sufficient number of times to + // equal the track duration. + // + // Since libavif uses repetitionCount (which is 0-based), we subtract the value by 1 to derive the number of + // repetitions. + AVIF_ASSERT_OR_RETURN(track->segmentDuration != 0); + // We specifically check for trackDuration == 0 here and not when it is actually read in order to accept files which + // inadvertently has a trackDuration of 0 without any edit lists. + if (track->trackDuration == 0) { + avifDiagnosticsPrintf(data->diag, "Invalid track duration 0."); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + const uint64_t repetitionCount = + (track->trackDuration / track->segmentDuration) + (track->trackDuration % track->segmentDuration != 0) - 1; + if (repetitionCount > INT_MAX) { + // repetitionCount does not fit in an integer and hence it is + // likely to be a very large value. So, we just set it to + // infinite. + track->repetitionCount = AVIF_REPETITION_COUNT_INFINITE; + } else { + track->repetitionCount = (int)repetitionCount; + } + } + } else { + track->repetitionCount = 0; + } + + return AVIF_RESULT_OK; +} + +static avifResult avifParseMovieBox(avifDecoderData * data, + uint64_t rawOffset, + const uint8_t * raw, + size_t rawLen, + uint32_t imageSizeLimit, + uint32_t imageDimensionLimit) +{ + BEGIN_STREAM(s, raw, rawLen, data->diag, "Box[moov]"); + + avifBool hasTrak = AVIF_FALSE; + while (avifROStreamHasBytesLeft(&s, 1)) { + avifBoxHeader header; + AVIF_CHECKERR(avifROStreamReadBoxHeader(&s, &header), AVIF_RESULT_BMFF_PARSE_FAILED); + + if (!memcmp(header.type, "trak", 4)) { + AVIF_CHECKRES(avifParseTrackBox(data, rawOffset + avifROStreamOffset(&s), avifROStreamCurrent(&s), header.size)); + hasTrak = AVIF_TRUE; + + const avifTrack * track = &data->tracks.track[data->tracks.count - 1]; + if (!memcmp(track->handlerType, "pict", 4) || !memcmp(track->handlerType, "vide", 4) || + !memcmp(track->handlerType, "auxv", 4)) { + if ((track->width == 0) || (track->height == 0)) { + avifDiagnosticsPrintf(data->diag, "Track ID [%u] has an invalid size [%ux%u]", track->id, track->width, track->height); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + if (avifDimensionsTooLarge(track->width, track->height, imageSizeLimit, imageDimensionLimit)) { + avifDiagnosticsPrintf(data->diag, + "Track ID [%u] dimensions are too large [%ux%u]", + track->id, + track->width, + track->height); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } + } + + AVIF_CHECKERR(avifROStreamSkip(&s, header.size), AVIF_RESULT_BMFF_PARSE_FAILED); + } + if (!hasTrak) { + avifDiagnosticsPrintf(data->diag, "moov box does not contain any tracks"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + return AVIF_RESULT_OK; +} + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) +static avifProperty * avifMetaCreateProperty(avifMeta * meta, const char * propertyType) +{ + avifProperty * metaProperty = avifArrayPush(&meta->properties); + AVIF_CHECKERR(metaProperty, NULL); + memcpy(metaProperty->type, propertyType, 4); + return metaProperty; +} + +static avifProperty * avifDecoderItemAddProperty(avifDecoderItem * item, const avifProperty * metaProperty) +{ + avifProperty * itemProperty = avifArrayPush(&item->properties); + AVIF_CHECKERR(itemProperty, NULL); + *itemProperty = *metaProperty; + return itemProperty; +} + +static avifResult avifParseMinimizedImageBox(avifDecoderData * data, + uint64_t rawOffset, + const uint8_t * raw, + size_t rawLen, + avifBool isAvifAccordingToMinorVersion, + avifDiagnostics * diag) +{ + avifMeta * meta = data->meta; + BEGIN_STREAM(s, raw, rawLen, diag, "Box[mini]"); + + meta->fromMiniBox = AVIF_TRUE; + + uint32_t version; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &version, 2), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(2) version = 0; + AVIF_CHECKERR(version == 0, AVIF_RESULT_BMFF_PARSE_FAILED); + + // flags + uint32_t hasExplicitCodecTypes, floatFlag, fullRange, hasAlpha, hasExplicitCicp, hasHdr, hasIcc, hasExif, hasXmp; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &hasExplicitCodecTypes, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) explicit_codec_types_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &floatFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) float_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &fullRange, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) full_range_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &hasAlpha, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) alpha_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &hasExplicitCicp, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) explicit_cicp_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &hasHdr, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) hdr_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &hasIcc, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) icc_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &hasExif, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) exif_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &hasXmp, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) xmp_flag; + + uint32_t chromaSubsampling, orientation; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &chromaSubsampling, 2), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(2) chroma_subsampling; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &orientation, 3), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(3) orientation_minus1; + ++orientation; + + // Spatial extents + uint32_t largeDimensionsFlag, width, height; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &largeDimensionsFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) large_dimensions_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &width, largeDimensionsFlag ? 15 : 7), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_dimensions_flag ? 15 : 7) width_minus1; + ++width; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &height, largeDimensionsFlag ? 15 : 7), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_dimensions_flag ? 15 : 7) height_minus1; + ++height; + + // Pixel information + uint32_t chromaIsHorizontallyCentered = 0, chromaIsVerticallyCentered = 0; + if (chromaSubsampling == 1 || chromaSubsampling == 2) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &chromaIsHorizontallyCentered, 1), + AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) chroma_is_horizontally_centered; + } + if (chromaSubsampling == 1) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &chromaIsVerticallyCentered, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) chroma_is_vertically_centered; + } + + uint32_t bitDepth; + if (floatFlag) { + // bit(2) bit_depth_log2_minus4; + return AVIF_RESULT_BMFF_PARSE_FAILED; // Either invalid AVIF or unsupported non-AVIF. + } else { + uint32_t highBitDepthFlag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &highBitDepthFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) high_bit_depth_flag; + if (highBitDepthFlag) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &bitDepth, 3), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(3) bit_depth_minus9; + bitDepth += 9; + } else { + bitDepth = 8; + } + } + + uint32_t alphaIsPremultiplied = 0; + if (hasAlpha) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &alphaIsPremultiplied, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) alpha_is_premultiplied; + } + + // Colour properties + uint8_t colorPrimaries; + uint8_t transferCharacteristics; + uint8_t matrixCoefficients; + if (hasExplicitCicp) { + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &colorPrimaries, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) colour_primaries; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &transferCharacteristics, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) transfer_characteristics; + if (chromaSubsampling != 0) { + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &matrixCoefficients, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) matrix_coefficients; + } else { + matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED; // 2 + } + } else { + colorPrimaries = hasIcc ? AVIF_COLOR_PRIMARIES_UNSPECIFIED // 2 + : AVIF_COLOR_PRIMARIES_BT709; // 1 + transferCharacteristics = hasIcc ? AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED // 2 + : AVIF_TRANSFER_CHARACTERISTICS_SRGB; // 13 + matrixCoefficients = chromaSubsampling == 0 ? AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED // 2 + : AVIF_MATRIX_COEFFICIENTS_BT601; // 6 + } + + uint8_t infeType[4]; + uint8_t codecConfigType[4]; + if (hasExplicitCodecTypes) { + // bit(32) infe_type; + for (int i = 0; i < 4; ++i) { + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &infeType[i], 8), AVIF_RESULT_BMFF_PARSE_FAILED); + } + // bit(32) codec_config_type; + for (int i = 0; i < 4; ++i) { + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &codecConfigType[i], 8), AVIF_RESULT_BMFF_PARSE_FAILED); + } +#if defined(AVIF_CODEC_AVM) + AVIF_CHECKERR((!memcmp(infeType, "av01", 4) && !memcmp(codecConfigType, "av1C", 4)) || + (!memcmp(infeType, "av02", 4) && !memcmp(codecConfigType, "av2C", 4)), + AVIF_RESULT_BMFF_PARSE_FAILED); +#else + AVIF_CHECKERR(!memcmp(infeType, "av01", 4) && !memcmp(codecConfigType, "av1C", 4), AVIF_RESULT_BMFF_PARSE_FAILED); +#endif + } else { + AVIF_CHECKERR(isAvifAccordingToMinorVersion, AVIF_RESULT_BMFF_PARSE_FAILED); + memcpy(infeType, "av01", 4); + memcpy(codecConfigType, "av1C", 4); + } + + // High Dynamic Range properties + uint32_t hasGainmap = AVIF_FALSE; + uint32_t tmapHasIcc = AVIF_FALSE; + uint32_t gainmapWidth = 0, gainmapHeight = 0; + uint8_t gainmapMatrixCoefficients = 0; + uint32_t gainmapFullRange = 0; + uint32_t gainmapChromaSubsampling = 0; + uint32_t gainmapBitDepth = 0; + uint32_t tmapHasExplicitCicp = AVIF_FALSE; + uint8_t tmapColorPrimaries = AVIF_COLOR_PRIMARIES_UNKNOWN; + uint8_t tmapTransferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNKNOWN; + uint8_t tmapMatrixCoefficients = AVIF_MATRIX_COEFFICIENTS_IDENTITY; + uint32_t tmapFullRange = AVIF_FALSE; + uint32_t hasClli = AVIF_FALSE, tmapHasClli = AVIF_FALSE; + avifContentLightLevelInformationBox clli = { 0 }, tmapClli = { 0 }; + if (hasHdr) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &hasGainmap, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) gainmap_flag; + if (hasGainmap) { + // avifDecoderReset() requires the 'tmap' brand to be registered for the tone mapping derived image item to be parsed. + if (data->compatibleBrands.capacity == 0) { + AVIF_CHECKERR(avifArrayCreate(&data->compatibleBrands, sizeof(avifBrand), 1), AVIF_RESULT_OUT_OF_MEMORY); + } + avifBrand * brand = avifArrayPush(&data->compatibleBrands); + AVIF_CHECKERR(brand != NULL, AVIF_RESULT_OUT_OF_MEMORY); + memcpy(brand, "tmap", sizeof(avifBrand)); + + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapWidth, largeDimensionsFlag ? 15 : 7), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_dimensions_flag ? 15 : 7) gainmap_width_minus1; + ++gainmapWidth; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapHeight, largeDimensionsFlag ? 15 : 7), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_dimensions_flag ? 15 : 7) gainmap_height_minus1; + ++gainmapHeight; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &gainmapMatrixCoefficients, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) gainmap_matrix_coefficients; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapFullRange, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) gainmap_full_range_flag; + + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapChromaSubsampling, 2), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(2) gainmap_chroma_subsampling; + uint32_t gainmapChromaIsHorizontallyCentered = 0, gainmapChromaIsVerticallyCentered = 0; + if (gainmapChromaSubsampling == 1 || gainmapChromaSubsampling == 2) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapChromaIsHorizontallyCentered, 1), + AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) gainmap_chroma_is_horizontally_centered; + } + if (gainmapChromaSubsampling == 1) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapChromaIsVerticallyCentered, 1), + AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) gainmap_chroma_is_vertically_centered; + } + + uint32_t gainmapFloatFlag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapFloatFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) gainmap_float_flag; + if (gainmapFloatFlag) { + // bit(2) gainmap_bit_depth_log2_minus4; + return AVIF_RESULT_BMFF_PARSE_FAILED; // Either invalid AVIF or unsupported non-AVIF. + } else { + uint32_t gainmapHighBitDepthFlag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapHighBitDepthFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) gainmap_high_bit_depth_flag; + if (gainmapHighBitDepthFlag) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapBitDepth, 3), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(3) gainmap_bit_depth_minus9; + gainmapBitDepth += 9; + } else { + gainmapBitDepth = 8; + } + } + + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &tmapHasIcc, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) tmap_icc_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &tmapHasExplicitCicp, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) tmap_explicit_cicp_flag; + if (tmapHasExplicitCicp) { + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &tmapColorPrimaries, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) tmap_colour_primaries; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &tmapTransferCharacteristics, 8), + AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) tmap_transfer_characteristics; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &tmapMatrixCoefficients, 8), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(8) tmap_matrix_coefficients; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &tmapFullRange, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) tmap_full_range_flag; + } else { + tmapColorPrimaries = AVIF_COLOR_PRIMARIES_BT709; // 1 + tmapTransferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_SRGB; // 13 + tmapMatrixCoefficients = AVIF_MATRIX_COEFFICIENTS_BT601; // 6 + tmapFullRange = 1; + } + } + AVIF_CHECKRES(avifParseMiniHDRProperties(&s, &hasClli, &clli)); + if (hasGainmap) { + AVIF_CHECKRES(avifParseMiniHDRProperties(&s, &tmapHasClli, &tmapClli)); + } + } + + // Chunk sizes + uint32_t largeMetadataFlag = 0, largeCodecConfigFlag = 0, largeItemDataFlag = 0; + if (hasIcc || hasExif || hasXmp || (hasHdr && hasGainmap)) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &largeMetadataFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) large_metadata_flag; + } + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &largeCodecConfigFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) large_codec_config_flag; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &largeItemDataFlag, 1), AVIF_RESULT_BMFF_PARSE_FAILED); // bit(1) large_item_data_flag; + + uint32_t iccDataSize = 0; + if (hasIcc) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &iccDataSize, largeMetadataFlag ? 20 : 10), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_metadata_flag ? 20 : 10) icc_data_size_minus1; + ++iccDataSize; + } + uint32_t tmapIccDataSize = 0; + if (hasHdr && hasGainmap && tmapHasIcc) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &tmapIccDataSize, largeMetadataFlag ? 20 : 10), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_metadata_flag ? 20 : 10) tmap_icc_data_size_minus1; + ++tmapIccDataSize; + } + + uint32_t gainmapMetadataSize = 0, gainmapItemDataSize = 0, gainmapItemCodecConfigSize = 0; + if (hasHdr && hasGainmap) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapMetadataSize, largeMetadataFlag ? 20 : 10), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_metadata_flag ? 20 : 10) gainmap_metadata_size; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapItemDataSize, largeItemDataFlag ? 28 : 15), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_item_data_flag ? 28 : 15) gainmap_item_data_size; + if (gainmapItemDataSize != 0) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &gainmapItemCodecConfigSize, largeCodecConfigFlag ? 12 : 3), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_codec_config_flag ? 12 : 3) gainmap_item_codec_config_size; + } + } + + uint32_t mainItemCodecConfigSize, mainItemDataSize; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &mainItemCodecConfigSize, largeCodecConfigFlag ? 12 : 3), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_codec_config_flag ? 12 : 3) main_item_codec_config_size; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &mainItemDataSize, largeItemDataFlag ? 28 : 15), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_item_data_flag ? 28 : 15) main_item_data_size_minus1; + ++mainItemDataSize; + + uint32_t alphaItemCodecConfigSize = 0, alphaItemDataSize = 0; + if (hasAlpha) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &alphaItemDataSize, largeItemDataFlag ? 28 : 15), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_item_data_flag ? 28 : 15) alpha_item_data_size; + } + if (hasAlpha && alphaItemDataSize != 0) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &alphaItemCodecConfigSize, largeCodecConfigFlag ? 12 : 3), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_codec_config_flag ? 12 : 3) alpha_item_codec_config_size; + } + + if (hasExif || hasXmp) { + uint8_t exifXmpCompressedFlag; + AVIF_CHECKERR(avifROStreamReadBitsU8(&s, &exifXmpCompressedFlag, 1), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(1) exif_xmp_compressed_flag; + AVIF_CHECKERR(!exifXmpCompressedFlag, AVIF_RESULT_NOT_IMPLEMENTED); + } + uint32_t exifDataSize = 0; + if (hasExif) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &exifDataSize, largeMetadataFlag ? 20 : 10), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_metadata_flag ? 20 : 10) exif_data_size_minus_one; + ++exifDataSize; + } + uint32_t xmpDataSize = 0; + if (hasXmp) { + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &xmpDataSize, largeMetadataFlag ? 20 : 10), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(large_metadata_flag ? 20 : 10) xmp_data_size_minus_one; + ++xmpDataSize; + } + + // trailing_bits(); // bit padding till byte alignment + if (s.numUsedBitsInPartialByte) { + uint32_t padding; + AVIF_CHECKERR(avifROStreamReadBitsU32(&s, &padding, 8 - s.numUsedBitsInPartialByte), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(padding == 0, AVIF_RESULT_BMFF_PARSE_FAILED); // Only accept zeros as padding. + } + + // Codec configuration ('av1C' always uses 4 bytes) + avifCodecConfigurationBox mainItemCodecConfig; + AVIF_CHECKERR(mainItemCodecConfigSize == 4, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifParseCodecConfiguration(&s, &mainItemCodecConfig, (const char *)codecConfigType, diag), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) main_item_codec_config[main_item_codec_config_size]; + avifCodecConfigurationBox alphaItemCodecConfig = { 0 }; + if (hasAlpha && alphaItemDataSize != 0) { + if (alphaItemCodecConfigSize == 0) { + alphaItemCodecConfigSize = mainItemCodecConfigSize; + alphaItemCodecConfig = mainItemCodecConfig; + } else { + AVIF_CHECKERR(alphaItemCodecConfigSize == 4, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifParseCodecConfiguration(&s, &alphaItemCodecConfig, (const char *)codecConfigType, diag), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) alpha_item_codec_config[alpha_item_codec_config_size]; + } + } + avifCodecConfigurationBox gainmapItemCodecConfig = { 0 }; + if (hasHdr && hasGainmap && gainmapItemDataSize != 0) { + if (gainmapItemCodecConfigSize == 0) { + gainmapItemCodecConfigSize = mainItemCodecConfigSize; + gainmapItemCodecConfig = mainItemCodecConfig; + } else { + AVIF_CHECKERR(gainmapItemCodecConfigSize == 4, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifParseCodecConfiguration(&s, &gainmapItemCodecConfig, (const char *)codecConfigType, diag), + AVIF_RESULT_BMFF_PARSE_FAILED); // unsigned int(8) gainmap_item_codec_config[gainmap_item_codec_config_size]; + } + } + + // Make sure all metadata and coded chunks fit into the 'meta' box whose size is rawLen. + // There should be no missing nor unused byte. + + AVIF_CHECKERR(avifROStreamRemainingBytes(&s) == (uint64_t)iccDataSize + tmapIccDataSize + gainmapMetadataSize + alphaItemDataSize + + gainmapItemDataSize + mainItemDataSize + exifDataSize + xmpDataSize, + AVIF_RESULT_BMFF_PARSE_FAILED); + + // Create the items and properties generated by the MinimizedImageBox. + // The MinimizedImageBox always creates 8 properties for specification easiness. + // Use FreeSpaceBoxes as no-op placeholder properties when necessary. + // There is no need to use placeholder items because item IDs do not have to + // be contiguous, whereas property indices shall be 1, 2, 3, 4, 5 etc. + + meta->primaryItemID = 1; + avifDecoderItem * colorItem; + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, meta->primaryItemID, &colorItem)); + memcpy(colorItem->type, infeType, 4); + colorItem->width = width; + colorItem->height = height; + colorItem->miniBoxPixelFormat = chromaSubsampling == 0 ? AVIF_PIXEL_FORMAT_YUV400 + : chromaSubsampling == 1 ? AVIF_PIXEL_FORMAT_YUV420 + : chromaSubsampling == 2 ? AVIF_PIXEL_FORMAT_YUV422 + : AVIF_PIXEL_FORMAT_YUV444; + if (colorItem->miniBoxPixelFormat == AVIF_PIXEL_FORMAT_YUV422) { + // In AV1, the chroma_sample_position syntax element is not present for the YUV 4:2:2 format. + // Assume that AV1 uses the same 4:2:2 chroma sample location as HEVC and VVC (colocated). + AVIF_CHECKERR(!chromaIsHorizontallyCentered, AVIF_RESULT_BMFF_PARSE_FAILED); + // chromaIsVerticallyCentered: Ignored unless chroma_subsampling is 1. + colorItem->miniBoxChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + } else if (colorItem->miniBoxPixelFormat == AVIF_PIXEL_FORMAT_YUV420) { + if (chromaIsHorizontallyCentered) { + // There is no way to describe this with AV1's chroma_sample_position enum besides CSP_UNKNOWN. + // There is a proposal to assign the reserved value 3 (CSP_RESERVED) to the center chroma sample position. + colorItem->miniBoxChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + } else { + colorItem->miniBoxChromaSamplePosition = chromaIsVerticallyCentered ? AVIF_CHROMA_SAMPLE_POSITION_VERTICAL + : AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + } + } else { + // chromaIsHorizontallyCentered: Ignored unless chroma_subsampling is 1 or 2. + // chromaIsVerticallyCentered: Ignored unless chroma_subsampling is 1. + colorItem->miniBoxChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + } + + avifDecoderItem * alphaItem = NULL; + if (hasAlpha) { + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, /*itemID=*/2, &alphaItem)); + memcpy(alphaItem->type, infeType, 4); + alphaItem->width = width; + alphaItem->height = height; + alphaItem->miniBoxPixelFormat = AVIF_PIXEL_FORMAT_YUV400; + alphaItem->miniBoxChromaSamplePosition = AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN; + } + + avifDecoderItem * tmapItem = NULL; + if (hasGainmap) { + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, /*itemID=*/3, &tmapItem)); + memcpy(tmapItem->type, "tmap", 4); + colorItem->dimgForID = tmapItem->id; + colorItem->dimgIdx = 0; + + // avifDecoderReset() requires the 'tmap' item to be an alternative to the primary item. + avifEntityToGroup * group = avifArrayPush(&data->meta->entityToGroups); + AVIF_CHECKERR(group != NULL, AVIF_RESULT_OUT_OF_MEMORY); + memcpy(group->groupingType, "altr", 4); + AVIF_CHECKERR(avifArrayCreate(&group->entityIDs, sizeof(uint32_t), 2), AVIF_RESULT_OUT_OF_MEMORY); + uint32_t * groupEntityId = avifArrayPush(&group->entityIDs); + AVIF_CHECKERR(groupEntityId != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *groupEntityId = tmapItem->id; + groupEntityId = avifArrayPush(&group->entityIDs); + AVIF_CHECKERR(groupEntityId != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *groupEntityId = colorItem->id; + } + avifDecoderItem * gainmapItem = NULL; + if (gainmapItemDataSize != 0) { + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, /*itemID=*/4, &gainmapItem)); + memcpy(gainmapItem->type, infeType, 4); + gainmapItem->width = gainmapWidth; + gainmapItem->height = gainmapHeight; + gainmapItem->dimgForID = tmapItem->id; + gainmapItem->dimgIdx = 1; + } + + // Property with fixed index 1. + avifProperty * colorCodecConfigProp = avifMetaCreateProperty(meta, (const char *)codecConfigType); + AVIF_CHECKERR(colorCodecConfigProp, AVIF_RESULT_OUT_OF_MEMORY); + colorCodecConfigProp->u.av1C = mainItemCodecConfig; + AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, colorCodecConfigProp), AVIF_RESULT_OUT_OF_MEMORY); + + // Property with fixed index 2. + avifProperty * ispeProp = avifMetaCreateProperty(meta, "ispe"); + AVIF_CHECKERR(ispeProp, AVIF_RESULT_OUT_OF_MEMORY); + ispeProp->u.ispe.width = width; + ispeProp->u.ispe.height = height; + AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, ispeProp), AVIF_RESULT_OUT_OF_MEMORY); + + // Property with fixed index 3. + avifProperty * pixiProp = avifMetaCreateProperty(meta, "pixi"); + AVIF_CHECKERR(pixiProp, AVIF_RESULT_OUT_OF_MEMORY); + pixiProp->u.pixi.planeCount = chromaSubsampling == 0 ? 1 : 3; + for (uint8_t plane = 0; plane < pixiProp->u.pixi.planeCount; ++plane) { + pixiProp->u.pixi.planeDepths[plane] = (uint8_t)bitDepth; + } + AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, pixiProp), AVIF_RESULT_OUT_OF_MEMORY); + + // Property with fixed index 4. + avifProperty * colrPropNCLX = avifMetaCreateProperty(meta, "colr"); + AVIF_CHECKERR(colrPropNCLX, AVIF_RESULT_OUT_OF_MEMORY); + colrPropNCLX->u.colr.hasNCLX = AVIF_TRUE; // colour_type "nclx" + colrPropNCLX->u.colr.colorPrimaries = (avifColorPrimaries)colorPrimaries; + colrPropNCLX->u.colr.transferCharacteristics = (avifTransferCharacteristics)transferCharacteristics; + colrPropNCLX->u.colr.matrixCoefficients = (avifMatrixCoefficients)matrixCoefficients; + colrPropNCLX->u.colr.range = fullRange ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED; + AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, colrPropNCLX), AVIF_RESULT_OUT_OF_MEMORY); + + // Property with fixed index 5. + if (iccDataSize != 0) { + avifProperty * colrPropICC = avifMetaCreateProperty(meta, "colr"); + AVIF_CHECKERR(colrPropICC, AVIF_RESULT_OUT_OF_MEMORY); + colrPropICC->u.colr.hasICC = AVIF_TRUE; // colour_type "rICC" or "prof" + colrPropICC->u.colr.iccOffset = rawOffset + avifROStreamOffset(&s); + colrPropICC->u.colr.iccSize = (size_t)iccDataSize; + AVIF_CHECKERR(avifROStreamSkip(&s, colrPropICC->u.colr.iccSize), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, colrPropICC), AVIF_RESULT_OUT_OF_MEMORY); + } else { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + + if (alphaItemCodecConfigSize != 0) { + // Property with fixed index 6. + avifProperty * alphaCodecConfigProp = avifMetaCreateProperty(meta, (const char *)codecConfigType); + AVIF_CHECKERR(alphaCodecConfigProp, AVIF_RESULT_OUT_OF_MEMORY); + alphaCodecConfigProp->u.av1C = alphaItemCodecConfig; + AVIF_CHECKERR(avifDecoderItemAddProperty(alphaItem, alphaCodecConfigProp), AVIF_RESULT_OUT_OF_MEMORY); + } else { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + + if (hasAlpha) { + // Property with fixed index 7. + alphaItem->auxForID = colorItem->id; + colorItem->premByID = alphaIsPremultiplied; + avifProperty * alphaAuxProp = avifMetaCreateProperty(meta, "auxC"); + AVIF_CHECKERR(alphaAuxProp, AVIF_RESULT_OUT_OF_MEMORY); + static_assert(sizeof(alphaAuxProp->u.auxC.auxType) >= sizeof(AVIF_URN_ALPHA0), ""); + memcpy(alphaAuxProp->u.auxC.auxType, AVIF_URN_ALPHA0, sizeof(AVIF_URN_ALPHA0)); + AVIF_CHECKERR(avifDecoderItemAddProperty(alphaItem, alphaAuxProp), AVIF_RESULT_OUT_OF_MEMORY); + + // Property with fixed index 2 (reused). + AVIF_CHECKERR(avifDecoderItemAddProperty(alphaItem, ispeProp), AVIF_RESULT_OUT_OF_MEMORY); + + // Property with fixed index 8. + avifProperty * alphaPixiProp = avifMetaCreateProperty(meta, "pixi"); + AVIF_CHECKERR(alphaPixiProp, AVIF_RESULT_OUT_OF_MEMORY); + memcpy(alphaPixiProp->type, "pixi", 4); + alphaPixiProp->u.pixi.planeCount = 1; + alphaPixiProp->u.pixi.planeDepths[0] = (uint8_t)bitDepth; + AVIF_CHECKERR(avifDecoderItemAddProperty(alphaItem, alphaPixiProp), AVIF_RESULT_OUT_OF_MEMORY); + } else { + // Placeholders 7 and 8. + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); + } + + uint32_t irotPropIndex = 0; // 0-based. + uint32_t imirPropIndex = 0; + // Same behavior as avifImageExtractExifOrientationToIrotImir(). + if (orientation == 3 || orientation == 5 || orientation == 6 || orientation == 7 || orientation == 8) { + irotPropIndex = meta->properties.count; // Store index instead of pointer which may be invalidated by avifMetaCreateProperty(). + // Property with fixed 1-based index 9. + assert(irotPropIndex + 1 == 9); + avifProperty * irotProp = avifMetaCreateProperty(meta, "irot"); + AVIF_CHECKERR(irotProp, AVIF_RESULT_OUT_OF_MEMORY); + irotProp->u.irot.angle = orientation == 3 ? 2 : (orientation == 5 || orientation == 8) ? 1 : 3; + } else { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + if (orientation == 2 || orientation == 4 || orientation == 5 || orientation == 7) { + imirPropIndex = meta->properties.count; + // Property with fixed 1-based index 10. + assert(imirPropIndex + 1 == 10); + avifProperty * imirProp = avifMetaCreateProperty(meta, "imir"); + AVIF_CHECKERR(imirProp, AVIF_RESULT_OUT_OF_MEMORY); + imirProp->u.imir.axis = orientation == 2 ? 1 : 0; + } else { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + + if (hasClli) { + // Property with fixed index 11. + avifProperty * clliProp = avifMetaCreateProperty(meta, "clli"); + AVIF_CHECKERR(clliProp, AVIF_RESULT_OUT_OF_MEMORY); + clliProp->u.clli = clli; + AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, clliProp), AVIF_RESULT_OUT_OF_MEMORY); + } else { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + // Properties with fixed indices 12 to 16 are ignored by libavif (mdcv, cclv, amve, reve and ndwt). + for (int i = 12; i <= 16; ++i) { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + + if (gainmapItemCodecConfigSize != 0) { + // Property with fixed index 17. + avifProperty * gainmapCodecConfigProp = avifMetaCreateProperty(meta, (const char *)codecConfigType); + AVIF_CHECKERR(gainmapCodecConfigProp, AVIF_RESULT_OUT_OF_MEMORY); + gainmapCodecConfigProp->u.av1C = gainmapItemCodecConfig; + AVIF_CHECKERR(avifDecoderItemAddProperty(gainmapItem, gainmapCodecConfigProp), AVIF_RESULT_OUT_OF_MEMORY); + } else { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + + if (gainmapItemDataSize != 0) { + // Property with fixed index 18. + avifProperty * gainmapIspeProp = avifMetaCreateProperty(meta, "ispe"); + AVIF_CHECKERR(gainmapIspeProp, AVIF_RESULT_OUT_OF_MEMORY); + gainmapIspeProp->u.ispe.width = gainmapWidth; + gainmapIspeProp->u.ispe.height = gainmapHeight; + AVIF_CHECKERR(avifDecoderItemAddProperty(gainmapItem, gainmapIspeProp), AVIF_RESULT_OUT_OF_MEMORY); + + // Property with fixed index 19. + avifProperty * gainmapPixiProp = avifMetaCreateProperty(meta, "pixi"); + AVIF_CHECKERR(gainmapPixiProp, AVIF_RESULT_OUT_OF_MEMORY); + memcpy(gainmapPixiProp->type, "pixi", 4); + gainmapPixiProp->u.pixi.planeCount = gainmapChromaSubsampling == 0 ? 1 : 3; + for (uint8_t plane = 0; plane < gainmapPixiProp->u.pixi.planeCount; ++plane) { + gainmapPixiProp->u.pixi.planeDepths[plane] = (uint8_t)gainmapBitDepth; + } + AVIF_CHECKERR(avifDecoderItemAddProperty(gainmapItem, gainmapPixiProp), AVIF_RESULT_OUT_OF_MEMORY); + + // Property with fixed index 20. + avifProperty * gainmapColrPropNCLX = avifMetaCreateProperty(meta, "colr"); + AVIF_CHECKERR(gainmapColrPropNCLX, AVIF_RESULT_OUT_OF_MEMORY); + gainmapColrPropNCLX->u.colr.hasNCLX = AVIF_TRUE; // colour_type "nclx" + gainmapColrPropNCLX->u.colr.colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED; // 2 + gainmapColrPropNCLX->u.colr.transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED; // 2 + gainmapColrPropNCLX->u.colr.matrixCoefficients = (avifMatrixCoefficients)gainmapMatrixCoefficients; + gainmapColrPropNCLX->u.colr.range = gainmapFullRange ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED; + AVIF_CHECKERR(avifDecoderItemAddProperty(gainmapItem, gainmapColrPropNCLX), AVIF_RESULT_OUT_OF_MEMORY); + } else { + // Placeholders 18, 19 and 20. + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); + } + + if (hasGainmap) { + // Property with fixed index 21. + avifProperty * tmapIspeProp = avifMetaCreateProperty(meta, "ispe"); + AVIF_CHECKERR(tmapIspeProp, AVIF_RESULT_OUT_OF_MEMORY); + tmapIspeProp->u.ispe.width = orientation <= 4 ? width : height; + tmapIspeProp->u.ispe.height = orientation <= 4 ? height : width; + AVIF_CHECKERR(avifDecoderItemAddProperty(tmapItem, tmapIspeProp), AVIF_RESULT_OUT_OF_MEMORY); + } else { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + + if (hasGainmap && (tmapHasExplicitCicp || !tmapHasIcc)) { + // Property with fixed index 22. + avifProperty * tmapColrPropNCLX = avifMetaCreateProperty(meta, "colr"); + AVIF_CHECKERR(tmapColrPropNCLX, AVIF_RESULT_OUT_OF_MEMORY); + tmapColrPropNCLX->u.colr.hasNCLX = AVIF_TRUE; // colour_type "nclx" + tmapColrPropNCLX->u.colr.colorPrimaries = (avifColorPrimaries)tmapColorPrimaries; + tmapColrPropNCLX->u.colr.transferCharacteristics = (avifTransferCharacteristics)tmapTransferCharacteristics; + tmapColrPropNCLX->u.colr.matrixCoefficients = (avifMatrixCoefficients)tmapMatrixCoefficients; + tmapColrPropNCLX->u.colr.range = tmapFullRange ? AVIF_RANGE_FULL : AVIF_RANGE_LIMITED; + AVIF_CHECKERR(avifDecoderItemAddProperty(tmapItem, tmapColrPropNCLX), AVIF_RESULT_OUT_OF_MEMORY); + } else { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + + if (tmapIccDataSize != 0) { + // Property with fixed index 23. + avifProperty * tmapColrPropICC = avifMetaCreateProperty(meta, "colr"); + AVIF_CHECKERR(tmapColrPropICC, AVIF_RESULT_OUT_OF_MEMORY); + tmapColrPropICC->u.colr.hasICC = AVIF_TRUE; // colour_type "rICC" or "prof" + tmapColrPropICC->u.colr.iccOffset = rawOffset + avifROStreamOffset(&s); + tmapColrPropICC->u.colr.iccSize = tmapIccDataSize; + AVIF_CHECKERR(avifROStreamSkip(&s, tmapColrPropICC->u.colr.iccSize), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, tmapColrPropICC), AVIF_RESULT_OUT_OF_MEMORY); + } else { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + + if (tmapHasClli) { + // Property with fixed index 24. + avifProperty * tmapClliProp = avifMetaCreateProperty(meta, "clli"); + AVIF_CHECKERR(tmapClliProp, AVIF_RESULT_OUT_OF_MEMORY); + tmapClliProp->u.clli = tmapClli; + AVIF_CHECKERR(avifDecoderItemAddProperty(tmapItem, tmapClliProp), AVIF_RESULT_OUT_OF_MEMORY); + } else { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + // Properties with fixed indices 25 to 29 are ignored by libavif (mdcv, cclv, amve, reve and ndwt). + for (int i = 25; i <= 29; ++i) { + AVIF_CHECKERR(avifMetaCreateProperty(meta, "skip"), AVIF_RESULT_OUT_OF_MEMORY); // Placeholder. + } + AVIF_ASSERT_OR_RETURN(meta->properties.count == 29); + + // ISO/IEC 23008-12 Section 6.5.1: + // Writers should arrange the descriptive properties specified in 6.5 prior to any other properties in the + // sequence associating properties with an item. + // + // irot and imir are transformative properties, so associate them last. + if (irotPropIndex != 0) { + const avifProperty * irotProp = &meta->properties.prop[irotPropIndex]; + AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, irotProp), AVIF_RESULT_OUT_OF_MEMORY); + if (hasAlpha) { + AVIF_CHECKERR(avifDecoderItemAddProperty(alphaItem, irotProp), AVIF_RESULT_OUT_OF_MEMORY); + } + if (gainmapItemDataSize != 0) { + AVIF_CHECKERR(avifDecoderItemAddProperty(gainmapItem, irotProp), AVIF_RESULT_OUT_OF_MEMORY); + } + } + if (imirPropIndex != 0) { + const avifProperty * imirProp = &meta->properties.prop[imirPropIndex]; + AVIF_CHECKERR(avifDecoderItemAddProperty(colorItem, imirProp), AVIF_RESULT_OUT_OF_MEMORY); + if (hasAlpha) { + AVIF_CHECKERR(avifDecoderItemAddProperty(alphaItem, imirProp), AVIF_RESULT_OUT_OF_MEMORY); + } + if (gainmapItemDataSize != 0) { + AVIF_CHECKERR(avifDecoderItemAddProperty(gainmapItem, imirProp), AVIF_RESULT_OUT_OF_MEMORY); + } + } + + // Extents. + + if (gainmapMetadataSize != 0) { + // Prepend the version field to the GainMapMetadata to form the ToneMapImage syntax. + tmapItem->size = gainmapMetadataSize + 1; + AVIF_CHECKRES(avifRWDataRealloc(&tmapItem->mergedExtents, tmapItem->size)); + tmapItem->ownsMergedExtents = AVIF_TRUE; + tmapItem->mergedExtents.data[0] = 0; // unsigned int(8) version = 0; + AVIF_CHECKERR(avifROStreamRead(&s, tmapItem->mergedExtents.data + 1, gainmapMetadataSize), AVIF_RESULT_BMFF_PARSE_FAILED); + } + + if (hasAlpha) { + avifExtent * alphaExtent = (avifExtent *)avifArrayPush(&alphaItem->extents); + AVIF_CHECKERR(alphaExtent, AVIF_RESULT_OUT_OF_MEMORY); + alphaExtent->offset = rawOffset + avifROStreamOffset(&s); + alphaExtent->size = alphaItemDataSize; + AVIF_CHECKERR(avifROStreamSkip(&s, alphaExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); + alphaItem->size = alphaExtent->size; + } + + if (gainmapItemDataSize != 0) { + avifExtent * gainmapExtent = (avifExtent *)avifArrayPush(&gainmapItem->extents); + AVIF_CHECKERR(gainmapExtent, AVIF_RESULT_OUT_OF_MEMORY); + gainmapExtent->offset = rawOffset + avifROStreamOffset(&s); + gainmapExtent->size = gainmapItemDataSize; + AVIF_CHECKERR(avifROStreamSkip(&s, gainmapExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); + gainmapItem->size = gainmapExtent->size; + } + + avifExtent * colorExtent = (avifExtent *)avifArrayPush(&colorItem->extents); + AVIF_CHECKERR(colorExtent, AVIF_RESULT_OUT_OF_MEMORY); + colorExtent->offset = rawOffset + avifROStreamOffset(&s); + colorExtent->size = mainItemDataSize; + AVIF_CHECKERR(avifROStreamSkip(&s, colorExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); + colorItem->size = colorExtent->size; + + if (hasExif) { + avifDecoderItem * exifItem; + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, /*itemID=*/6, &exifItem)); + memcpy(exifItem->type, "Exif", 4); + exifItem->descForID = colorItem->id; // 'cdsc' + + avifExtent * exifExtent = (avifExtent *)avifArrayPush(&exifItem->extents); + AVIF_CHECKERR(exifExtent, AVIF_RESULT_OUT_OF_MEMORY); + exifExtent->offset = rawOffset + avifROStreamOffset(&s); + exifExtent->size = exifDataSize; // Does not include unsigned int(32) exif_tiff_header_offset; + AVIF_CHECKERR(avifROStreamSkip(&s, exifExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); + exifItem->size = exifExtent->size; + } + + if (hasXmp) { + avifDecoderItem * xmpItem; + AVIF_CHECKRES(avifMetaFindOrCreateItem(meta, /*itemID=*/7, &xmpItem)); + memcpy(xmpItem->type, "mime", 4); + memcpy(xmpItem->contentType.contentType, AVIF_CONTENT_TYPE_XMP, sizeof(AVIF_CONTENT_TYPE_XMP)); + xmpItem->descForID = colorItem->id; // 'cdsc' + + avifExtent * xmpExtent = (avifExtent *)avifArrayPush(&xmpItem->extents); + AVIF_CHECKERR(xmpExtent, AVIF_RESULT_OUT_OF_MEMORY); + xmpExtent->offset = rawOffset + avifROStreamOffset(&s); + xmpExtent->size = xmpDataSize; + AVIF_CHECKERR(avifROStreamSkip(&s, xmpExtent->size), AVIF_RESULT_BMFF_PARSE_FAILED); + xmpItem->size = xmpExtent->size; + } + return AVIF_RESULT_OK; +} +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + +static avifBool avifParseFileTypeBox(avifFileType * ftyp, const uint8_t * raw, size_t rawLen, avifDiagnostics * diag) +{ + BEGIN_STREAM(s, raw, rawLen, diag, "Box[ftyp]"); + + AVIF_CHECK(avifROStreamRead(&s, ftyp->majorBrand, 4)); + AVIF_CHECK(avifROStreamRead(&s, ftyp->minorVersion, 4)); + + size_t compatibleBrandsBytes = avifROStreamRemainingBytes(&s); + if ((compatibleBrandsBytes % 4) != 0) { + avifDiagnosticsPrintf(diag, "Box[ftyp] contains a compatible brands section that isn't divisible by 4 [%zu]", compatibleBrandsBytes); + return AVIF_FALSE; + } + ftyp->compatibleBrands = avifROStreamCurrent(&s); + AVIF_CHECK(avifROStreamSkip(&s, compatibleBrandsBytes)); + ftyp->compatibleBrandsCount = (int)compatibleBrandsBytes / 4; + + return AVIF_TRUE; +} + +static avifBool avifFileTypeHasBrand(avifFileType * ftyp, const char * brand); +static avifBool avifFileTypeIsCompatible(avifFileType * ftyp); + +static avifResult avifParse(avifDecoder * decoder) +{ + // Note: this top-level function is the only avifParse*() function that returns avifResult instead of avifBool. + // Be sure to use AVIF_CHECKERR() in this function with an explicit error result instead of simply using AVIF_CHECK(). + + avifResult readResult; + uint64_t parseOffset = 0; + avifDecoderData * data = decoder->data; + avifBool ftypSeen = AVIF_FALSE; + avifBool metaSeen = AVIF_FALSE; + avifBool metaIsSizeZero = AVIF_FALSE; + avifBool moovSeen = AVIF_FALSE; + avifBool needsMeta = AVIF_FALSE; + avifBool needsMoov = AVIF_FALSE; +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + avifBool miniSeen = AVIF_FALSE; + avifBool needsMini = AVIF_FALSE; +#endif + avifBool needsTmap = AVIF_FALSE; + avifBool tmapSeen = AVIF_FALSE; + avifFileType ftyp = { 0 }; + + for (;;) { + // Read just enough to get the next box header (a max of 32 bytes) + avifROData headerContents; + if ((decoder->io->sizeHint > 0) && (parseOffset > decoder->io->sizeHint)) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + readResult = decoder->io->read(decoder->io, 0, parseOffset, 32, &headerContents); + if (readResult != AVIF_RESULT_OK) { + return readResult; + } + if (!headerContents.size) { + // If we got AVIF_RESULT_OK from the reader but received 0 bytes, + // we've reached the end of the file with no errors. Hooray! + break; + } + + // Parse the header, and find out how many bytes it actually was + BEGIN_STREAM(headerStream, headerContents.data, headerContents.size, &decoder->diag, "File-level box header"); + avifBoxHeader header; + AVIF_CHECKERR(avifROStreamReadBoxHeaderPartial(&headerStream, &header, /*topLevel=*/AVIF_TRUE), AVIF_RESULT_BMFF_PARSE_FAILED); + parseOffset += avifROStreamOffset(&headerStream); + AVIF_ASSERT_OR_RETURN(decoder->io->sizeHint == 0 || parseOffset <= decoder->io->sizeHint); + + // Try to get the remainder of the box, if necessary + uint64_t boxOffset = 0; + avifROData boxContents = AVIF_DATA_EMPTY; + + avifBool isFtyp = AVIF_FALSE, isMeta = AVIF_FALSE, isMoov = AVIF_FALSE; + avifBool isNonSkippableVariableLengthBox = AVIF_FALSE; + if (!memcmp(header.type, "ftyp", 4)) { + isFtyp = AVIF_TRUE; + isNonSkippableVariableLengthBox = AVIF_TRUE; + } else if (!memcmp(header.type, "meta", 4)) { + isMeta = AVIF_TRUE; + isNonSkippableVariableLengthBox = AVIF_TRUE; + metaIsSizeZero = header.isSizeZeroBox; + } else if (!memcmp(header.type, "moov", 4)) { + isMoov = AVIF_TRUE; + isNonSkippableVariableLengthBox = AVIF_TRUE; + } +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + avifBool isMini = AVIF_FALSE; + if (!isNonSkippableVariableLengthBox && !memcmp(header.type, "mini", 4)) { + isMini = AVIF_TRUE; + isNonSkippableVariableLengthBox = AVIF_TRUE; + } +#endif + + if (!isFtyp && (isNonSkippableVariableLengthBox || !memcmp(header.type, "free", 4) || !memcmp(header.type, "skip", 4) || + !memcmp(header.type, "mdat", 4))) { + // Section 6.3.4 of ISO/IEC 14496-12: + // The FileTypeBox shall occur before any variable-length box (e.g. movie, free space, media data). + AVIF_CHECKERR(ftypSeen, AVIF_RESULT_BMFF_PARSE_FAILED); + } + + if (isNonSkippableVariableLengthBox) { + boxOffset = parseOffset; + size_t sizeToRead; + if (header.isSizeZeroBox) { + // The box body goes till the end of the file. + if (decoder->io->sizeHint != 0 && decoder->io->sizeHint - parseOffset < SIZE_MAX) { + sizeToRead = (size_t)(decoder->io->sizeHint - parseOffset); + } else { + sizeToRead = SIZE_MAX; // This will get truncated. See the documentation of avifIOReadFunc. + } + } else { + sizeToRead = header.size; + } + readResult = decoder->io->read(decoder->io, 0, parseOffset, sizeToRead, &boxContents); + if (readResult != AVIF_RESULT_OK) { + return readResult; + } + if (header.isSizeZeroBox) { + header.size = boxContents.size; + } else if (boxContents.size != header.size) { + // A truncated box, bail out + return AVIF_RESULT_TRUNCATED_DATA; + } + } else if (header.isSizeZeroBox) { + // An unknown top level box with size 0 was found. If we reach here it means we haven't completed parsing successfully + // since there are no further boxes left. + return AVIF_RESULT_BMFF_PARSE_FAILED; + } else if (header.size > (UINT64_MAX - parseOffset)) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + parseOffset += header.size; + + if (isFtyp) { + AVIF_CHECKERR(!ftypSeen, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifParseFileTypeBox(&ftyp, boxContents.data, boxContents.size, data->diag), AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifFileTypeIsCompatible(&ftyp), AVIF_RESULT_INVALID_FTYP); + ftypSeen = AVIF_TRUE; + memcpy(data->majorBrand, ftyp.majorBrand, 4); // Remember the major brand for future AVIF_DECODER_SOURCE_AUTO decisions + if (ftyp.compatibleBrandsCount > 0) { + AVIF_CHECKERR(avifArrayCreate(&data->compatibleBrands, sizeof(avifBrand), ftyp.compatibleBrandsCount), + AVIF_RESULT_OUT_OF_MEMORY); + memcpy(data->compatibleBrands.brand, ftyp.compatibleBrands, sizeof(avifBrand) * ftyp.compatibleBrandsCount); + data->compatibleBrands.count = ftyp.compatibleBrandsCount; + } + needsMeta = avifFileTypeHasBrand(&ftyp, "avif"); + needsMoov = avifFileTypeHasBrand(&ftyp, "avis"); +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + needsMini = avifFileTypeHasBrand(&ftyp, "mif3"); + if (needsMini) { + AVIF_CHECKERR(!needsMeta, AVIF_RESULT_INVALID_FTYP); + // Section O.2.1.2 of ISO/IEC 23008-12:2014, CDAM 2: + // When the 'mif3' brand is present as the major_brand of the FileTypeBox, + // the minor_version of the FileTypeBox shall be 0 or a brand that is either + // structurally compatible with the 'mif3' brand, such as a codec brand + // complying with the 'mif3' structural brand, or a brand to which the file + // conforms after the equivalent MetaBox has been transformed from + // MinimizedImageBox as specified in Clause O.4. + AVIF_CHECKERR(!memcmp(ftyp.minorVersion, "\0\0\0\0", 4) || !memcmp(ftyp.minorVersion, "avif", 4), + AVIF_RESULT_BMFF_PARSE_FAILED); + } +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + needsTmap = avifFileTypeHasBrand(&ftyp, "tmap"); + if (needsTmap) { + needsMeta = AVIF_TRUE; + } + } else if (isMeta) { + AVIF_CHECKERR(!metaSeen, AVIF_RESULT_BMFF_PARSE_FAILED); +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + AVIF_CHECKERR(!miniSeen, AVIF_RESULT_BMFF_PARSE_FAILED); +#endif + AVIF_CHECKRES(avifParseMetaBox(data->meta, boxOffset, boxContents.data, boxContents.size, data->diag)); + metaSeen = AVIF_TRUE; + + for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) { + if (!memcmp(data->meta->items.item[itemIndex]->type, "tmap", 4)) { + tmapSeen = AVIF_TRUE; + break; + } + } + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + } else if (isMini) { + AVIF_CHECKERR(!metaSeen, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(!miniSeen, AVIF_RESULT_BMFF_PARSE_FAILED); + const avifBool isAvifAccordingToMinorVersion = !memcmp(ftyp.minorVersion, "avif", 4); + AVIF_CHECKRES( + avifParseMinimizedImageBox(data, boxOffset, boxContents.data, boxContents.size, isAvifAccordingToMinorVersion, data->diag)); + miniSeen = AVIF_TRUE; +#endif + } else if (isMoov) { + AVIF_CHECKERR(!moovSeen, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKRES( + avifParseMovieBox(data, boxOffset, boxContents.data, boxContents.size, decoder->imageSizeLimit, decoder->imageDimensionLimit)); + moovSeen = AVIF_TRUE; + decoder->imageSequenceTrackPresent = AVIF_TRUE; + } + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + if (ftypSeen && !needsMini) { + // When MinimizedImageBox is present in a file, the 'mif3' brand or a derived brand that implies the 'mif3' + // brand shall be the major brand or present among the compatible brands in the FileTypeBox. + AVIF_CHECKERR(!miniSeen, AVIF_RESULT_BMFF_PARSE_FAILED); + } +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + + // See if there is enough information to consider Parse() a success and early-out: + // * If the brand 'avif' is present, require a meta box + // * If the brand 'avis' is present, require a moov box + // * If AVIF_ENABLE_EXPERIMENTAL_MINI is defined and the brand 'mif3' is present, require a mini box + avifBool sawEverythingNeeded = ftypSeen && (!needsMeta || metaSeen) && (!needsMoov || moovSeen) && (!needsTmap || tmapSeen); +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + sawEverythingNeeded = sawEverythingNeeded && (!needsMini || miniSeen); +#endif + if (sawEverythingNeeded) { + return AVIF_RESULT_OK; + } + } + if (!ftypSeen) { + return AVIF_RESULT_INVALID_FTYP; + } + if ((needsMeta && !metaSeen) || (needsMoov && !moovSeen)) { + return AVIF_RESULT_TRUNCATED_DATA; + } + if (needsTmap && !tmapSeen) { + return metaIsSizeZero ? AVIF_RESULT_TRUNCATED_DATA : AVIF_RESULT_BMFF_PARSE_FAILED; + } +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + if (needsMini && !miniSeen) { + return AVIF_RESULT_TRUNCATED_DATA; + } +#endif + return AVIF_RESULT_OK; +} + +// --------------------------------------------------------------------------- + +static avifBool avifFileTypeHasBrand(avifFileType * ftyp, const char * brand) +{ + if (!memcmp(ftyp->majorBrand, brand, 4)) { + return AVIF_TRUE; + } + + for (int compatibleBrandIndex = 0; compatibleBrandIndex < ftyp->compatibleBrandsCount; ++compatibleBrandIndex) { + const uint8_t * compatibleBrand = &ftyp->compatibleBrands[4 * compatibleBrandIndex]; + if (!memcmp(compatibleBrand, brand, 4)) { + return AVIF_TRUE; + } + } + return AVIF_FALSE; +} + +static avifBool avifFileTypeIsCompatible(avifFileType * ftyp) +{ + return avifFileTypeHasBrand(ftyp, "avif") || avifFileTypeHasBrand(ftyp, "avis") +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + || avifFileTypeHasBrand(ftyp, "mif3") +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + ; +} + +avifBool avifPeekCompatibleFileType(const avifROData * input) +{ + BEGIN_STREAM(s, input->data, input->size, NULL, NULL); + + avifBoxHeader header; + if (!avifROStreamReadBoxHeaderPartial(&s, &header, /*topLevel=*/AVIF_TRUE) || memcmp(header.type, "ftyp", 4)) { + return AVIF_FALSE; + } + if (header.isSizeZeroBox) { + // The ftyp box goes on till the end of the file. Either there is no brand requiring anything in the file but a + // FileTypebox (so not AVIF), or it is invalid. + return AVIF_FALSE; + } + AVIF_CHECK(avifROStreamHasBytesLeft(&s, header.size)); + + avifFileType ftyp; + memset(&ftyp, 0, sizeof(avifFileType)); + avifBool parsed = avifParseFileTypeBox(&ftyp, avifROStreamCurrent(&s), header.size, NULL); + if (!parsed) { + return AVIF_FALSE; + } + return avifFileTypeIsCompatible(&ftyp); +} + +static avifBool avifBrandArrayHasBrand(avifBrandArray * brands, const char * brand) +{ + for (uint32_t brandIndex = 0; brandIndex < brands->count; ++brandIndex) { + if (!memcmp(brands->brand[brandIndex], brand, 4)) { + return AVIF_TRUE; + } + } + return AVIF_FALSE; +} + +// --------------------------------------------------------------------------- + +avifDecoder * avifDecoderCreate(void) +{ + avifDecoder * decoder = (avifDecoder *)avifAlloc(sizeof(avifDecoder)); + if (decoder == NULL) { + return NULL; + } + memset(decoder, 0, sizeof(avifDecoder)); + decoder->maxThreads = 1; + decoder->imageSizeLimit = AVIF_DEFAULT_IMAGE_SIZE_LIMIT; + decoder->imageDimensionLimit = AVIF_DEFAULT_IMAGE_DIMENSION_LIMIT; + decoder->imageCountLimit = AVIF_DEFAULT_IMAGE_COUNT_LIMIT; + decoder->strictFlags = AVIF_STRICT_ENABLED; + decoder->imageContentToDecode = AVIF_IMAGE_CONTENT_DECODE_DEFAULT; + return decoder; +} + +static void avifDecoderCleanup(avifDecoder * decoder) +{ + if (decoder->data) { + avifDecoderDataDestroy(decoder->data); + decoder->data = NULL; + } + + if (decoder->image) { + avifImageDestroy(decoder->image); + decoder->image = NULL; + } + avifDiagnosticsClearError(&decoder->diag); +} + +void avifDecoderDestroy(avifDecoder * decoder) +{ + avifDecoderCleanup(decoder); + avifIODestroy(decoder->io); + avifFree(decoder); +} + +avifResult avifDecoderSetSource(avifDecoder * decoder, avifDecoderSource source) +{ + decoder->requestedSource = source; + return avifDecoderReset(decoder); +} + +void avifDecoderSetIO(avifDecoder * decoder, avifIO * io) +{ + avifIODestroy(decoder->io); + decoder->io = io; +} + +avifResult avifDecoderSetIOMemory(avifDecoder * decoder, const uint8_t * data, size_t size) +{ + avifIO * io = avifIOCreateMemoryReader(data, size); + AVIF_CHECKERR(io != NULL, AVIF_RESULT_OUT_OF_MEMORY); + avifDecoderSetIO(decoder, io); + return AVIF_RESULT_OK; +} + +avifResult avifDecoderSetIOFile(avifDecoder * decoder, const char * filename) +{ + avifIO * io = avifIOCreateFileReader(filename); + if (!io) { + return AVIF_RESULT_IO_ERROR; + } + avifDecoderSetIO(decoder, io); + return AVIF_RESULT_OK; +} + +// 0-byte extents are ignored/overwritten during the merge, as they are the signal from helper +// functions that no extent was necessary for this given sample. If both provided extents are +// >0 bytes, this will set dst to be an extent that bounds both supplied extents. +static avifResult avifExtentMerge(avifExtent * dst, const avifExtent * src) +{ + if (!dst->size) { + *dst = *src; + return AVIF_RESULT_OK; + } + if (!src->size) { + return AVIF_RESULT_OK; + } + + const uint64_t minExtent1 = dst->offset; + const uint64_t maxExtent1 = dst->offset + dst->size; + const uint64_t minExtent2 = src->offset; + const uint64_t maxExtent2 = src->offset + src->size; + dst->offset = AVIF_MIN(minExtent1, minExtent2); + const uint64_t extentLength = AVIF_MAX(maxExtent1, maxExtent2) - dst->offset; +#if UINT64_MAX > SIZE_MAX + if (extentLength > SIZE_MAX) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } +#endif + dst->size = (size_t)extentLength; + return AVIF_RESULT_OK; +} + +avifResult avifDecoderNthImageMaxExtent(const avifDecoder * decoder, uint32_t frameIndex, avifExtent * outExtent) +{ + if (!decoder->data) { + // Nothing has been parsed yet + return AVIF_RESULT_NO_CONTENT; + } + + memset(outExtent, 0, sizeof(avifExtent)); + + uint32_t startFrameIndex = avifDecoderNearestKeyframe(decoder, frameIndex); + uint32_t endFrameIndex = frameIndex; + for (uint32_t currentFrameIndex = startFrameIndex; currentFrameIndex <= endFrameIndex; ++currentFrameIndex) { + for (unsigned int tileIndex = 0; tileIndex < decoder->data->tiles.count; ++tileIndex) { + avifTile * tile = &decoder->data->tiles.tile[tileIndex]; + if (currentFrameIndex >= tile->input->samples.count) { + return AVIF_RESULT_NO_IMAGES_REMAINING; + } + + avifDecodeSample * sample = &tile->input->samples.sample[currentFrameIndex]; + avifExtent sampleExtent; + if (sample->itemID) { + // The data comes from an item. Let avifDecoderItemMaxExtent() do the heavy lifting. + + avifDecoderItem * item; + AVIF_CHECKRES(avifMetaFindOrCreateItem(decoder->data->meta, sample->itemID, &item)); + avifResult maxExtentResult = avifDecoderItemMaxExtent(item, sample, &sampleExtent); + if (maxExtentResult != AVIF_RESULT_OK) { + return maxExtentResult; + } + } else { + // The data likely comes from a sample table. Use the sample position directly. + + sampleExtent.offset = sample->offset; + sampleExtent.size = sample->size; + } + + if (sampleExtent.size > UINT64_MAX - sampleExtent.offset) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + avifResult extentMergeResult = avifExtentMerge(outExtent, &sampleExtent); + if (extentMergeResult != AVIF_RESULT_OK) { + return extentMergeResult; + } + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifDecoderPrepareSample(avifDecoder * decoder, avifDecodeSample * sample, size_t partialByteCount) +{ + if (!sample->data.size || sample->partialData) { + // This sample hasn't been read from IO or had its extents fully merged yet. + + size_t bytesToRead = sample->size; + if (partialByteCount && (bytesToRead > partialByteCount)) { + bytesToRead = partialByteCount; + } + + if (sample->itemID) { + // The data comes from an item. Let avifDecoderItemRead() do the heavy lifting. + + avifDecoderItem * item; + AVIF_CHECKRES(avifMetaFindOrCreateItem(decoder->data->meta, sample->itemID, &item)); + avifROData itemContents; +#if UINT64_MAX > SIZE_MAX + if (sample->offset > SIZE_MAX) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } +#endif + size_t offset = (size_t)sample->offset; + avifResult readResult = avifDecoderItemRead(item, decoder->io, &itemContents, offset, bytesToRead, &decoder->diag); + if (readResult != AVIF_RESULT_OK) { + return readResult; + } + + // avifDecoderItemRead is guaranteed to already be persisted by either the underlying IO + // or by mergedExtents; just reuse the buffer here. + sample->data = itemContents; + sample->ownsData = AVIF_FALSE; + sample->partialData = item->partialMergedExtents; + } else { + // The data likely comes from a sample table. Pull the sample and make a copy if necessary. + + avifROData sampleContents; + if ((decoder->io->sizeHint > 0) && (sample->offset > decoder->io->sizeHint)) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + avifResult readResult = decoder->io->read(decoder->io, 0, sample->offset, bytesToRead, &sampleContents); + if (readResult != AVIF_RESULT_OK) { + return readResult; + } + if (sampleContents.size != bytesToRead) { + return AVIF_RESULT_TRUNCATED_DATA; + } + + sample->ownsData = !decoder->io->persistent; + sample->partialData = (bytesToRead != sample->size); + if (decoder->io->persistent) { + sample->data = sampleContents; + } else { + AVIF_CHECKRES(avifRWDataSet((avifRWData *)&sample->data, sampleContents.data, sampleContents.size)); + } + } + } + return AVIF_RESULT_OK; +} + +// Returns AVIF_TRUE if the item should be skipped. Items should be skipped for one of the following reasons: +// * Size is 0. +// * Has an essential property that isn't supported by libavif. +// * Item is not a single image or a grid. +// * Item is a thumbnail. +static avifBool avifDecoderItemShouldBeSkipped(const avifDecoderItem * item) +{ + return !item->size || item->hasUnsupportedEssentialProperty || + (avifGetCodecType(item->type) == AVIF_CODEC_TYPE_UNKNOWN && memcmp(item->type, "grid", 4)) || item->thumbnailForID != 0; +} + +avifResult avifDecoderParse(avifDecoder * decoder) +{ + avifDiagnosticsClearError(&decoder->diag); + + // An imageSizeLimit greater than AVIF_DEFAULT_IMAGE_SIZE_LIMIT and the special value of 0 to + // disable the limit are not yet implemented. + if ((decoder->imageSizeLimit > AVIF_DEFAULT_IMAGE_SIZE_LIMIT) || (decoder->imageSizeLimit == 0)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + // Color only or alpha only is not currently supported. + if ((decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA) != 0 && + (decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA) != AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA) { + avifDiagnosticsPrintf(&decoder->diag, "imageContentToDecode set to only color or only alpha is not supported"); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (!decoder->io || !decoder->io->read) { + return AVIF_RESULT_IO_NOT_SET; + } + + // Cleanup anything lingering in the decoder + avifDecoderCleanup(decoder); + + // ----------------------------------------------------------------------- + // Parse BMFF boxes + + decoder->data = avifDecoderDataCreate(); + AVIF_CHECKERR(decoder->data != NULL, AVIF_RESULT_OUT_OF_MEMORY); + decoder->data->diag = &decoder->diag; + + AVIF_CHECKRES(avifParse(decoder)); + + // Walk the decoded items (if any) and harvest ispe + avifDecoderData * data = decoder->data; + for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) { + avifDecoderItem * item = data->meta->items.item[itemIndex]; + if (avifDecoderItemShouldBeSkipped(item)) { + continue; + } + + const avifProperty * ispeProp = avifPropertyArrayFind(&item->properties, "ispe"); + if (ispeProp) { + item->width = ispeProp->u.ispe.width; + item->height = ispeProp->u.ispe.height; + + if ((item->width == 0) || (item->height == 0)) { + avifDiagnosticsPrintf(data->diag, "Item ID [%u] has an invalid size [%ux%u]", item->id, item->width, item->height); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + if (avifDimensionsTooLarge(item->width, item->height, decoder->imageSizeLimit, decoder->imageDimensionLimit)) { + avifDiagnosticsPrintf(data->diag, "Item ID [%u] dimensions are too large [%ux%u]", item->id, item->width, item->height); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } else { + const avifProperty * auxCProp = avifPropertyArrayFind(&item->properties, "auxC"); + if (auxCProp && isAlphaURN(auxCProp->u.auxC.auxType)) { + if (decoder->strictFlags & AVIF_STRICT_ALPHA_ISPE_REQUIRED) { + avifDiagnosticsPrintf(data->diag, + "[Strict] Alpha auxiliary image item ID [%u] is missing a mandatory ispe property", + item->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } else { + avifDiagnosticsPrintf(data->diag, "Item ID [%u] is missing a mandatory ispe property", item->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + } + } + return avifDecoderReset(decoder); +} + +static avifResult avifCodecCreateInternal(avifCodecChoice choice, const avifTile * tile, avifDiagnostics * diag, avifCodec ** codec) +{ +#if defined(AVIF_CODEC_AVM) + // AVIF_CODEC_CHOICE_AUTO leads to AVIF_CODEC_TYPE_AV1 by default. Reroute correctly. + if (choice == AVIF_CODEC_CHOICE_AUTO && tile->codecType == AVIF_CODEC_TYPE_AV2) { + choice = AVIF_CODEC_CHOICE_AVM; + } +#endif + + const avifCodecType codecTypeFromChoice = avifCodecTypeFromChoice(choice, AVIF_CODEC_FLAG_CAN_DECODE); + if (codecTypeFromChoice == AVIF_CODEC_TYPE_UNKNOWN) { + avifDiagnosticsPrintf(diag, + "Tile type is %s but there is no compatible codec available to decode it", + avifGetConfigurationPropertyName(tile->codecType)); + return AVIF_RESULT_NO_CODEC_AVAILABLE; + } else if (choice != AVIF_CODEC_CHOICE_AUTO && codecTypeFromChoice != tile->codecType) { + avifDiagnosticsPrintf(diag, + "Tile type is %s but incompatible %s codec was explicitly set as decoding implementation", + avifGetConfigurationPropertyName(tile->codecType), + avifCodecName(choice, AVIF_CODEC_FLAG_CAN_DECODE)); + return AVIF_RESULT_DECODE_COLOR_FAILED; + } + + AVIF_CHECKRES(avifCodecCreate(choice, AVIF_CODEC_FLAG_CAN_DECODE, codec)); + AVIF_CHECKERR(*codec, AVIF_RESULT_OUT_OF_MEMORY); + (*codec)->diag = diag; + (*codec)->operatingPoint = tile->operatingPoint; + (*codec)->allLayers = tile->input->allLayers; + return AVIF_RESULT_OK; +} + +static avifBool avifTilesCanBeDecodedWithSameCodecInstance(const avifDecoderData * data) +{ + int32_t numImageBuffers = 0, numStolenImageBuffers = 0; + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + if (data->tileInfos[c].tileCount > 0) { + ++numImageBuffers; + } + // The sample operations require multiple buffers for compositing so no plane is stolen + // when there is a 'sato' Sample Transform derived image item. + if (c >= AVIF_SAMPLE_TRANSFORM_MIN_CATEGORY && c <= AVIF_SAMPLE_TRANSFORM_MAX_CATEGORY && data->tileInfos[c].tileCount > 0) { + continue; + } + if (data->tileInfos[c].tileCount == 1) { + ++numStolenImageBuffers; + } + } + if (numStolenImageBuffers > 0 && numImageBuffers > 1) { + // Single tile image with single tile alpha plane or gain map. In this case each tile needs its own decoder since the planes will be + // "stolen". Stealing either the color or the alpha plane (or gain map) will invalidate the other ones when decode is called the second + // (or third) time. + return AVIF_FALSE; + } + const uint8_t firstTileOperatingPoint = data->tiles.tile[0].operatingPoint; + const avifBool firstTileAllLayers = data->tiles.tile[0].input->allLayers; + for (unsigned int i = 1; i < data->tiles.count; ++i) { + const avifTile * tile = &data->tiles.tile[i]; + if (tile->operatingPoint != firstTileOperatingPoint || tile->input->allLayers != firstTileAllLayers) { + return AVIF_FALSE; + } + // avifDecoderItemValidateProperties() verified during avifDecoderParse() that all tiles + // share the same coding format so no need to check for codecType equality here. + } + return AVIF_TRUE; +} + +static avifResult avifDecoderCreateCodecs(avifDecoder * decoder) +{ + avifDecoderData * data = decoder->data; + avifDecoderDataResetCodec(data); + + if (data->source == AVIF_DECODER_SOURCE_TRACKS) { + // In this case, we will use at most two codec instances (one for the color planes and one for the alpha plane). + // Gain maps are not supported. + AVIF_CHECKRES(avifCodecCreateInternal(decoder->codecChoice, &decoder->data->tiles.tile[0], &decoder->diag, &data->codec)); + data->tiles.tile[0].codec = data->codec; + if (data->tiles.count > 1) { + AVIF_CHECKRES(avifCodecCreateInternal(decoder->codecChoice, &decoder->data->tiles.tile[1], &decoder->diag, &data->codecAlpha)); + data->tiles.tile[1].codec = data->codecAlpha; + } + } else { + // In this case, we will use one codec instance when there is only one tile or when all of the following conditions are + // met: + // - The image must have exactly one layer (i.e. decoder->imageCount == 1). + // - All the tiles must have the same operating point (because the codecs take operating point once at initialization + // and do not allow it to be changed later). + // - All the tiles must have the same value for allLayers (because the codecs take allLayers once at initialization + // and do not allow it to be changed later). + // - If the image has a single tile, it must not have a single tile alpha plane (in this case we will steal the planes + // from the decoder, so we cannot use the same decoder for both the color and the alpha planes). + // - All tiles have the same type (AV1 or AV2). + // - No tile buffer access after another tile was decoded (i.e. no Sample Transform compositing because it happens + // after decoding all tiles). + // Otherwise, we will use |tiles.count| decoder instances (one instance for each tile). + const avifBool canUseSingleCodecInstance = + ((data->tiles.count == 1) || (decoder->imageCount == 1 && avifTilesCanBeDecodedWithSameCodecInstance(data))) && + data->sampleTransformNumInputImageItems == 0; + if (canUseSingleCodecInstance) { + AVIF_CHECKRES(avifCodecCreateInternal(decoder->codecChoice, &decoder->data->tiles.tile[0], &decoder->diag, &data->codec)); + for (unsigned int i = 0; i < decoder->data->tiles.count; ++i) { + decoder->data->tiles.tile[i].codec = data->codec; + } + } else { + for (unsigned int i = 0; i < decoder->data->tiles.count; ++i) { + avifTile * tile = &decoder->data->tiles.tile[i]; + AVIF_CHECKRES(avifCodecCreateInternal(decoder->codecChoice, tile, &decoder->diag, &tile->codec)); + } + } + } + return AVIF_RESULT_OK; +} + +// Returns the primary color item if found, or NULL. +static avifDecoderItem * avifMetaFindColorItem(avifMeta * meta) +{ + for (uint32_t itemIndex = 0; itemIndex < meta->items.count; ++itemIndex) { + avifDecoderItem * item = meta->items.item[itemIndex]; + if (avifDecoderItemShouldBeSkipped(item)) { + continue; + } + if (item->id == meta->primaryItemID) { + return item; + } + } + return NULL; +} + +// Returns AVIF_TRUE if item is an alpha auxiliary item of the parent color +// item. +static avifBool avifDecoderItemIsAlphaAux(const avifDecoderItem * item, uint32_t colorItemId) +{ + if (item->auxForID != colorItemId) + return AVIF_FALSE; + const avifProperty * auxCProp = avifPropertyArrayFind(&item->properties, "auxC"); + return auxCProp && isAlphaURN(auxCProp->u.auxC.auxType); +} + +// Finds the alpha item whose parent item is colorItem and sets it in the alphaItem output parameter. Returns AVIF_RESULT_OK on +// success. Note that *alphaItem can be NULL even if the return value is AVIF_RESULT_OK. If the colorItem is a grid and the alpha +// item is represented as a set of auxl items to each color tile, then a fake item will be created and *isAlphaItemInInput will be +// set to AVIF_FALSE. In this case, the alpha item merely exists to hold the locations of the alpha tile items. The data of this +// item need not be read and the pixi property cannot be validated. Otherwise, *isAlphaItemInInput will be set to AVIF_TRUE when +// *alphaItem is not NULL. +static avifResult avifMetaFindAlphaItem(avifMeta * meta, + const avifDecoderItem * colorItem, + const avifTileInfo * colorInfo, + avifDecoderItem ** alphaItem, + avifTileInfo * alphaInfo, + avifBool * isAlphaItemInInput) +{ + for (uint32_t itemIndex = 0; itemIndex < meta->items.count; ++itemIndex) { + avifDecoderItem * item = meta->items.item[itemIndex]; + if (avifDecoderItemShouldBeSkipped(item)) { + continue; + } + if (avifDecoderItemIsAlphaAux(item, colorItem->id)) { + *alphaItem = item; + *isAlphaItemInInput = AVIF_TRUE; + return AVIF_RESULT_OK; + } + } + if (memcmp(colorItem->type, "grid", 4)) { + *alphaItem = NULL; + *isAlphaItemInInput = AVIF_FALSE; + return AVIF_RESULT_OK; + } + // If color item is a grid, check if there is an alpha channel which is represented as an auxl item to each color tile item. + const uint32_t tileCount = colorInfo->grid.rows * colorInfo->grid.columns; + if (tileCount == 0) { + *alphaItem = NULL; + *isAlphaItemInInput = AVIF_FALSE; + return AVIF_RESULT_OK; + } + // Keep the same 'dimg' order as it defines where each tile is located in the reconstructed image. + uint32_t * dimgIdxToAlphaItemIdx = (uint32_t *)avifAlloc(tileCount * sizeof(uint32_t)); + AVIF_CHECKERR(dimgIdxToAlphaItemIdx != NULL, AVIF_RESULT_OUT_OF_MEMORY); + const uint32_t itemIndexNotSet = UINT32_MAX; + for (uint32_t dimgIdx = 0; dimgIdx < tileCount; ++dimgIdx) { + dimgIdxToAlphaItemIdx[dimgIdx] = itemIndexNotSet; + } + uint32_t alphaItemCount = 0; + for (uint32_t i = 0; i < meta->items.count; ++i) { + const avifDecoderItem * const item = meta->items.item[i]; + if (item->dimgForID == colorItem->id) { + avifBool seenAlphaForCurrentItem = AVIF_FALSE; + for (uint32_t j = 0; j < meta->items.count; ++j) { + avifDecoderItem * auxlItem = meta->items.item[j]; + if (avifDecoderItemIsAlphaAux(auxlItem, item->id)) { + if (seenAlphaForCurrentItem || auxlItem->dimgForID != 0 || item->dimgIdx >= tileCount || + dimgIdxToAlphaItemIdx[item->dimgIdx] != itemIndexNotSet) { + // One of the following invalid cases: + // * Multiple items are claiming to be the alpha auxiliary of the current item. + // * Alpha auxiliary is dimg for another item. + // * There are too many items in the dimg array (also checked later in avifFillDimgIdxToItemIdxArray()). + // * There is a repetition in the dimg array (also checked later in avifFillDimgIdxToItemIdxArray()). + avifFree(dimgIdxToAlphaItemIdx); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + dimgIdxToAlphaItemIdx[item->dimgIdx] = j; + ++alphaItemCount; + seenAlphaForCurrentItem = AVIF_TRUE; + } + } + if (!seenAlphaForCurrentItem) { + // No alpha auxiliary item was found for the current item. Treat this as an image without alpha. + avifFree(dimgIdxToAlphaItemIdx); + *alphaItem = NULL; + *isAlphaItemInInput = AVIF_FALSE; + return AVIF_RESULT_OK; + } + } + } + if (alphaItemCount != tileCount) { + avifFree(dimgIdxToAlphaItemIdx); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + // Find an unused ID. + avifResult result; + if (meta->items.count >= UINT32_MAX - 1) { + // In the improbable case where all IDs are used. + result = AVIF_RESULT_DECODE_ALPHA_FAILED; + } else { + uint32_t newItemID = 0; + avifBool isUsed; + do { + ++newItemID; + isUsed = AVIF_FALSE; + for (uint32_t i = 0; i < meta->items.count; ++i) { + if (meta->items.item[i]->id == newItemID) { + isUsed = AVIF_TRUE; + break; + } + } + } while (isUsed && newItemID != 0); + result = avifMetaFindOrCreateItem(meta, newItemID, alphaItem); // Create new empty item. + } + if (result != AVIF_RESULT_OK) { + avifFree(dimgIdxToAlphaItemIdx); + return result; + } + memcpy((*alphaItem)->type, "grid", 4); // Make it a grid and register alpha items as its tiles. + (*alphaItem)->width = colorItem->width; + (*alphaItem)->height = colorItem->height; + for (uint32_t dimgIdx = 0; dimgIdx < tileCount; ++dimgIdx) { + if (dimgIdxToAlphaItemIdx[dimgIdx] >= meta->items.count) { + avifFree(dimgIdxToAlphaItemIdx); + AVIF_ASSERT_NOT_REACHED_OR_RETURN; + } + avifDecoderItem * alphaTileItem = meta->items.item[dimgIdxToAlphaItemIdx[dimgIdx]]; + alphaTileItem->dimgForID = (*alphaItem)->id; + alphaTileItem->dimgIdx = dimgIdx; + } + avifFree(dimgIdxToAlphaItemIdx); + *isAlphaItemInInput = AVIF_FALSE; + alphaInfo->grid = colorInfo->grid; + return AVIF_RESULT_OK; +} + +// If cicpSet is not NULL, the caller must set |*cicpSet| to AVIF_FALSE before +// calling this function. +// On success, this function returns AVIF_RESULT_OK and does the following: +// * If a nclx property was found in |properties|: +// - Set |*colorPrimaries|, |*transferCharacteristics|, |*matrixCoefficients| +// and |*yuvRange|. +// - If cicpSet is not NULL, set |*cicpSet| to AVIF_TRUE. +// This function fails if more than one nclx property is found in |properties|. +// The output parameters may be populated even in case of failure and must be +// ignored. +static avifResult avifReadColorNclxProperty(const avifPropertyArray * properties, + avifColorPrimaries * colorPrimaries, + avifTransferCharacteristics * transferCharacteristics, + avifMatrixCoefficients * matrixCoefficients, + avifRange * yuvRange, + avifBool * cicpSet) +{ + assert(cicpSet == NULL || *cicpSet == AVIF_FALSE); + avifBool colrNCLXSeen = AVIF_FALSE; + for (uint32_t propertyIndex = 0; propertyIndex < properties->count; ++propertyIndex) { + avifProperty * prop = &properties->prop[propertyIndex]; + if (!memcmp(prop->type, "colr", 4) && prop->u.colr.hasNCLX) { + if (colrNCLXSeen) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + colrNCLXSeen = AVIF_TRUE; + if (cicpSet != NULL) { + *cicpSet = AVIF_TRUE; + } + *colorPrimaries = prop->u.colr.colorPrimaries; + *transferCharacteristics = prop->u.colr.transferCharacteristics; + *matrixCoefficients = prop->u.colr.matrixCoefficients; + *yuvRange = prop->u.colr.range; + } + } + return AVIF_RESULT_OK; +} + +// On success, this function returns AVIF_RESULT_OK and does the following: +// * If a colr property was found in |properties|: +// - Read the icc data into |icc| from |io|. +// - Sets the CICP values as documented in avifReadColorNclxProperty(). +// This function fails if more than one icc or nclx property is found in +// |properties|. The output parameters may be populated even in case of failure +// and must be ignored (and the |icc| object may need to be freed). +static avifResult avifReadColorProperties(avifIO * io, + const avifPropertyArray * properties, + avifRWData * icc, + avifColorPrimaries * colorPrimaries, + avifTransferCharacteristics * transferCharacteristics, + avifMatrixCoefficients * matrixCoefficients, + avifRange * yuvRange, + avifBool * cicpSet) +{ + // Find and adopt all colr boxes "at most one for a given value of colour type" (HEIF 6.5.5.1, from Amendment 3) + // Accept one of each type, and bail out if more than one of a given type is provided. + avifBool colrICCSeen = AVIF_FALSE; + for (uint32_t propertyIndex = 0; propertyIndex < properties->count; ++propertyIndex) { + avifProperty * prop = &properties->prop[propertyIndex]; + if (!memcmp(prop->type, "colr", 4) && prop->u.colr.hasICC) { + if (colrICCSeen) { + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + avifROData iccRead; + AVIF_CHECKRES(io->read(io, 0, prop->u.colr.iccOffset, prop->u.colr.iccSize, &iccRead)); + colrICCSeen = AVIF_TRUE; + AVIF_CHECKRES(avifRWDataSet(icc, iccRead.data, iccRead.size)); + } + } + return avifReadColorNclxProperty(properties, colorPrimaries, transferCharacteristics, matrixCoefficients, yuvRange, cicpSet); +} + +// Finds a 'tmap' (tone mapped image item) box associated with the given 'colorItem'. +// If found, fills 'toneMappedImageItem' and sets 'gainMapItemID' to the id of the gain map +// item associated with the box. Otherwise, sets 'toneMappedImageItem' to NULL. +// Returns AVIF_RESULT_OK if no errors were encountered (whether or not a tmap box was found). +// Assumes that there is a single tmap item, and not, e.g., a grid of tmap items. +// TODO(maryla): add support for files with multiple tmap items if it gets allowed by the spec. +static avifResult avifDecoderDataFindToneMappedImageItem(const avifDecoderData * data, + const avifDecoderItem * colorItem, + avifDecoderItem ** toneMappedImageItem, + uint32_t * gainMapItemID) +{ + for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) { + avifDecoderItem * item = data->meta->items.item[itemIndex]; + if (!item->size || item->hasUnsupportedEssentialProperty || item->thumbnailForID != 0) { + continue; + } + if (!memcmp(item->type, "tmap", 4)) { + // The tmap box should be associated (via 'iref'->'dimg') to two items: + // the first one is the base image, the second one is the gain map. + uint32_t dimgItemIDs[2] = { 0, 0 }; + uint32_t numDimgItemIDs = 0; + for (uint32_t otherItemIndex = 0; otherItemIndex < data->meta->items.count; ++otherItemIndex) { + avifDecoderItem * otherItem = data->meta->items.item[otherItemIndex]; + if (otherItem->dimgForID != item->id) { + continue; + } + if (otherItem->dimgIdx < 2) { + AVIF_ASSERT_OR_RETURN(dimgItemIDs[otherItem->dimgIdx] == 0); + dimgItemIDs[otherItem->dimgIdx] = otherItem->id; + } + numDimgItemIDs++; + } + // Even with numDimgItemIDs == 2, one of the ids could be 0 if there are duplicate entries in the 'dimg' box. + if (numDimgItemIDs != 2 || dimgItemIDs[0] == 0 || dimgItemIDs[1] == 0) { + avifDiagnosticsPrintf(data->diag, "box[dimg] for 'tmap' item %d must have exactly 2 entries with distinct ids", item->id); + return AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE; + } + if (dimgItemIDs[0] != colorItem->id) { + continue; + } + + *toneMappedImageItem = item; + *gainMapItemID = dimgItemIDs[1]; + return AVIF_RESULT_OK; + } + } + *toneMappedImageItem = NULL; + *gainMapItemID = 0; + return AVIF_RESULT_OK; +} + +// Returns AVIF_TRUE if the two entity ids (usually item ids) are part of an +// 'altr' group (representing entities that are alternatives of each other) +// with 'id1' appearing before 'id2' (meaning that 'id1' should be preferred). +static avifBool avifIsPreferredAlternativeTo(const avifDecoderData * data, uint32_t id1, uint32_t id2) +{ + for (uint32_t i = 0; i < data->meta->entityToGroups.count; ++i) { + avifEntityToGroup * group = &data->meta->entityToGroups.groups[i]; + if (memcmp(group->groupingType, "altr", 4) != 0) { + continue; + } + avifBool id1Found = AVIF_FALSE; + for (uint32_t j = 0; j < group->entityIDs.count; ++j) { + if (group->entityIDs.ids[j] == id1) { + id1Found = AVIF_TRUE; + } else if (group->entityIDs.ids[j] == id2) { + // Assume id2 is only present in one altr group, as per ISO/IEC 14496-12:2022 + // Section 8.15.3.1: + // Any entity_id value shall be mapped to only one grouping of type 'altr'. + return id1Found; + } + } + } + return AVIF_FALSE; +} + +// Finds a 'tmap' (tone mapped image item) box associated with the given 'colorItem', +// then finds the associated gain map image. +// If found, fills 'gainMapItem' and 'gainMapCodecType', and allocates and fills in +// decoder->image->gainMap. +// Otherwise, sets 'gainMapItem' to NULL and gainMapCodecType to AVIF_CODEC_TYPE_UNKNOWN. +// Returns AVIF_RESULT_OK if no errors were encountered (whether or not a gain map was found). +// Assumes that there is a single tmap item, and not, e.g., a grid of tmap items. +static avifResult avifDecoderFindGainMapItem(const avifDecoder * decoder, + const avifDecoderItem * colorItem, + avifDecoderItem ** gainMapItem, + avifCodecType * gainMapCodecType) +{ + *gainMapItem = NULL; + *gainMapCodecType = AVIF_CODEC_TYPE_UNKNOWN; + + avifDecoderData * data = decoder->data; + + // Find tmap and gain map item ids. + uint32_t gainMapItemID; + avifDecoderItem * toneMappedImageItemTmp; + AVIF_CHECKRES(avifDecoderDataFindToneMappedImageItem(data, colorItem, &toneMappedImageItemTmp, &gainMapItemID)); + if (!toneMappedImageItemTmp || !gainMapItemID) { + return AVIF_RESULT_OK; + } + + if (!avifIsPreferredAlternativeTo(data, toneMappedImageItemTmp->id, colorItem->id)) { + return AVIF_RESULT_OK; + } + + // Parse tmap item data (containing the gain map metadata). + avifROData tmapData; + AVIF_CHECKRES(avifDecoderItemRead(toneMappedImageItemTmp, decoder->io, &tmapData, 0, 0, data->diag)); + // Allocate avifGainMap on the stack instead of using avifGainMapCreate() to simplify error handling. + avifGainMap gainMapTmp; + avifGainMapSetDefaults(&gainMapTmp); + avifResult result = avifParseToneMappedImageBox(&gainMapTmp, tmapData.data, tmapData.size, data->diag); + if (result == AVIF_RESULT_NOT_IMPLEMENTED) { + // Unsupported gain map version. Simply ignore the gain map. + return AVIF_RESULT_OK; + } + AVIF_CHECKRES(result); + + avifDecoderItem * gainMapItemTmp; + AVIF_CHECKRES(avifMetaFindOrCreateItem(data->meta, gainMapItemID, &gainMapItemTmp)); + if (avifDecoderItemShouldBeSkipped(gainMapItemTmp)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + avifCodecType gainMapCodecTypeTmp; + result = avifDecoderItemReadAndParse(decoder, + gainMapItemTmp, + /*isItemInInput=*/AVIF_TRUE, + &data->tileInfos[AVIF_ITEM_GAIN_MAP].grid, + &gainMapCodecTypeTmp); + if (result == AVIF_RESULT_NOT_IMPLEMENTED) { + return AVIF_RESULT_OK; + } + AVIF_CHECKRES(result); + + // This may allocate gainMapTmp.altICC which must be freed in case of error. + result = avifReadColorProperties(decoder->io, + &toneMappedImageItemTmp->properties, + &gainMapTmp.altICC, + &gainMapTmp.altColorPrimaries, + &gainMapTmp.altTransferCharacteristics, + &gainMapTmp.altMatrixCoefficients, + &gainMapTmp.altYUVRange, + /*cicpSet=*/NULL); + if (result != AVIF_RESULT_OK) { + avifRWDataFree(&gainMapTmp.altICC); + return result; + } + + const avifProperty * clliProp = avifPropertyArrayFind(&toneMappedImageItemTmp->properties, "clli"); + if (clliProp) { + gainMapTmp.altCLLI = clliProp->u.clli; + } + + const avifProperty * pixiProp = avifPropertyArrayFind(&toneMappedImageItemTmp->properties, "pixi"); + if (pixiProp) { + gainMapTmp.altPlaneCount = pixiProp->u.pixi.planeCount; + gainMapTmp.altDepth = pixiProp->u.pixi.planeDepths[0]; + } + + const avifProperty * ispeProp = avifPropertyArrayFind(&toneMappedImageItemTmp->properties, "ispe"); + if (!ispeProp) { + // HEIF (ISO/IEC 23008-12:2022), Section 6.5.3.1: + // Every image item shall be associated with one property of this type, prior to the association + // of all transformative properties. + avifDiagnosticsPrintf(data->diag, "Box[tmap] missing mandatory ispe property"); + avifRWDataFree(&gainMapTmp.altICC); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + if (ispeProp->u.ispe.width != colorItem->width || ispeProp->u.ispe.height != colorItem->height) { + avifDiagnosticsPrintf(data->diag, "Box[tmap] ispe property width/height does not match base image"); + avifRWDataFree(&gainMapTmp.altICC); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + if (avifPropertyArrayFind(&toneMappedImageItemTmp->properties, "pasp") || + avifPropertyArrayFind(&toneMappedImageItemTmp->properties, "clap") || + avifPropertyArrayFind(&toneMappedImageItemTmp->properties, "irot") || + avifPropertyArrayFind(&toneMappedImageItemTmp->properties, "imir")) { + // libavif requires the bitstream contain the same pasp, clap, irot, imir + // properties for both the base and gain map image items used as input to + // the tone-mapped derived image item. libavif also requires the tone-mapped + // derived image item itself not be associated with these properties. This is + // enforced at encoding. Other patterns are rejected at decoding. + avifDiagnosticsPrintf(data->diag, + "Box[tmap] 'pasp', 'clap', 'irot' and 'imir' properties must be associated with base and gain map items instead of 'tmap'"); + avifRWDataFree(&gainMapTmp.altICC); + return AVIF_RESULT_INVALID_TONE_MAPPED_IMAGE; + } + + avifColorPrimaries colorPrimaries = AVIF_COLOR_PRIMARIES_UNSPECIFIED; + avifTransferCharacteristics transferCharacteristics = AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED; + avifMatrixCoefficients matrixCoefficients = AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED; + avifRange yuvRange = AVIF_RANGE_FULL; + avifBool cicpSet = AVIF_FALSE; + // Look for a colr nclx box. Other colr box types (e.g. ICC) are not supported. + result = + avifReadColorNclxProperty(&gainMapItemTmp->properties, &colorPrimaries, &transferCharacteristics, &matrixCoefficients, &yuvRange, &cicpSet); + if (result != AVIF_RESULT_OK) { + avifRWDataFree(&gainMapTmp.altICC); + return result; + } + + // -- Everything is valid, do memory allocations and fill in output data. -- + + decoder->image->gainMap = avifGainMapCreate(); + if (!decoder->image->gainMap) { + avifRWDataFree(&gainMapTmp.altICC); + return AVIF_RESULT_OUT_OF_MEMORY; + } + + if (decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_GAIN_MAP) { + avifImage * image = avifImageCreateEmpty(); + if (!image) { + avifRWDataFree(&gainMapTmp.altICC); + return AVIF_RESULT_OUT_OF_MEMORY; + } + if (cicpSet) { + image->colorPrimaries = colorPrimaries; + image->transferCharacteristics = transferCharacteristics; + image->matrixCoefficients = matrixCoefficients; + image->yuvRange = yuvRange; + } + gainMapTmp.image = image; + } + + // Only set the output pointers after everything has been validated. + *decoder->image->gainMap = gainMapTmp; + *gainMapItem = gainMapItemTmp; + *gainMapCodecType = gainMapCodecTypeTmp; + return AVIF_RESULT_OK; +} + +static avifResult avifDecoderCheckAlphaProperties(avifDecoder * decoder, const avifPropertyArray * alphaProperties) +{ + const avifImage * image = decoder->image; + // The 'clap', 'irot' and 'imir' transformative properties should be applied to the alpha + // auxiliary image item before considering it a plane of the color image item. + // Alternatively, inequality with the transformative properties attached to the color image item + // should be treated as AVIF_RESULT_NOT_IMPLEMENTED. + // The latter is easier and is the behavior of libavif. + + const avifProperty * clapProp = avifPropertyArrayFind(alphaProperties, "clap"); + const avifProperty * irotProp = avifPropertyArrayFind(alphaProperties, "irot"); + const avifProperty * imirProp = avifPropertyArrayFind(alphaProperties, "imir"); + if (clapProp == NULL && irotProp == NULL && imirProp == NULL) { + // However, libavif up to version 1.3.0 generated images lacking transformative property + // associations with alpha auxiliary image items, so be lenient on their absence for + // backward compatibility with previously generated images. + return AVIF_RESULT_OK; + } + + // HEIF (ISO/IEC 23008-12), Section 6.9.1: + // When the width or the height of the alpha plane differs from the width or the height of the + // master image, respectively, the alpha plane is resized to have the same width and height as + // those of the master image. + // There is no need to enforce specific 'ispe' values describing the alpha item because + // the alpha item must be resized to the dimensions of the associated color item. + + if (!clapProp != !(image->transformFlags & AVIF_TRANSFORM_CLAP) || + (clapProp && (clapProp->u.clap.widthN != image->clap.widthN || clapProp->u.clap.widthD != image->clap.widthD || + clapProp->u.clap.heightN != image->clap.heightN || clapProp->u.clap.heightD != image->clap.heightD || + clapProp->u.clap.horizOffN != image->clap.horizOffN || clapProp->u.clap.horizOffD != image->clap.horizOffD || + clapProp->u.clap.vertOffN != image->clap.vertOffN || clapProp->u.clap.vertOffD != image->clap.vertOffD))) { + avifDiagnosticsPrintf(&decoder->diag, "Clean aperture property mismatch between alpha auxiliary image item and color item"); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (!irotProp != !(image->transformFlags & AVIF_TRANSFORM_IROT) || (irotProp && irotProp->u.irot.angle != image->irot.angle)) { + avifDiagnosticsPrintf(&decoder->diag, "Rotation property mismatch between alpha auxiliary image item and color item"); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (!imirProp != !(image->transformFlags & AVIF_TRANSFORM_IMIR) || (imirProp && imirProp->u.imir.axis != image->imir.axis)) { + avifDiagnosticsPrintf(&decoder->diag, "Mirroring property mismatch between alpha auxiliary image item and color item"); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + return AVIF_RESULT_OK; +} + +static avifResult avifDecoderCheckGainMapProperties(avifDecoder * decoder, const avifPropertyArray * gainMapProperties) +{ + const avifImage * image = decoder->image; + // libavif requires the bitstream contain the same 'pasp', 'clap', 'irot', 'imir' + // properties for both the base and gain map image items used as input to + // the tone-mapped derived image item. libavif also requires the tone-mapped + // derived image item itself not be associated with these properties. This is + // enforced at encoding. Other patterns are rejected at decoding. + const avifProperty * paspProp = avifPropertyArrayFind(gainMapProperties, "pasp"); + if (!paspProp != !(image->transformFlags & AVIF_TRANSFORM_PASP) || + (paspProp && (paspProp->u.pasp.hSpacing != image->pasp.hSpacing || paspProp->u.pasp.vSpacing != image->pasp.vSpacing))) { + avifDiagnosticsPrintf(&decoder->diag, + "Pixel aspect ratio property mismatch between input items of tone-mapping derived image item"); + return AVIF_RESULT_DECODE_GAIN_MAP_FAILED; + } + const avifProperty * clapProp = avifPropertyArrayFind(gainMapProperties, "clap"); + if (!clapProp != !(image->transformFlags & AVIF_TRANSFORM_CLAP) || + (clapProp && (clapProp->u.clap.widthN != image->clap.widthN || clapProp->u.clap.widthD != image->clap.widthD || + clapProp->u.clap.heightN != image->clap.heightN || clapProp->u.clap.heightD != image->clap.heightD || + clapProp->u.clap.horizOffN != image->clap.horizOffN || clapProp->u.clap.horizOffD != image->clap.horizOffD || + clapProp->u.clap.vertOffN != image->clap.vertOffN || clapProp->u.clap.vertOffD != image->clap.vertOffD))) { + avifDiagnosticsPrintf(&decoder->diag, "Clean aperture property mismatch between input items of tone-mapping derived image item"); + return AVIF_RESULT_DECODE_GAIN_MAP_FAILED; + } + const avifProperty * irotProp = avifPropertyArrayFind(gainMapProperties, "irot"); + if (!irotProp != !(image->transformFlags & AVIF_TRANSFORM_IROT) || (irotProp && irotProp->u.irot.angle != image->irot.angle)) { + avifDiagnosticsPrintf(&decoder->diag, "Rotation property mismatch between input items of tone-mapping derived image item"); + return AVIF_RESULT_DECODE_GAIN_MAP_FAILED; + } + const avifProperty * imirProp = avifPropertyArrayFind(gainMapProperties, "imir"); + if (!imirProp != !(image->transformFlags & AVIF_TRANSFORM_IMIR) || (imirProp && imirProp->u.imir.axis != image->imir.axis)) { + avifDiagnosticsPrintf(&decoder->diag, "Mirroring property mismatch between input items of tone-mapping derived image item"); + return AVIF_RESULT_DECODE_GAIN_MAP_FAILED; + } + return AVIF_RESULT_OK; +} + +// Finds any 'sato' Sample Transform derived image item, distinct from the primary image item, +// and in the same 'altr' group as the primary image item. Returns NULL otherwise. +static avifDecoderItem * avifDecoderDataFindSampleTransformImageItem(avifDecoderData * data) +{ + for (uint32_t itemIndex = 0; itemIndex < data->meta->items.count; ++itemIndex) { + avifDecoderItem * item = data->meta->items.item[itemIndex]; + if (!memcmp(item->type, "sato", 4) && item->id != data->meta->primaryItemID && item->size != 0 && + !item->hasUnsupportedEssentialProperty && item->thumbnailForID == 0 && + avifIsPreferredAlternativeTo(data, item->id, data->meta->primaryItemID)) { + return item; + } + } + return NULL; +} + +static avifResult avifDecoderGenerateImageTiles(avifDecoder * decoder, avifTileInfo * info, avifDecoderItem * item, avifItemCategory itemCategory) +{ + const uint32_t previousTileCount = decoder->data->tiles.count; + if ((info->grid.rows > 0) && (info->grid.columns > 0)) { + // The number of tiles was verified in avifDecoderItemReadAndParse(). + const uint32_t numTiles = info->grid.rows * info->grid.columns; + uint32_t * dimgIdxToItemIdx = (uint32_t *)avifAlloc(numTiles * sizeof(uint32_t)); + AVIF_CHECKERR(dimgIdxToItemIdx != NULL, AVIF_RESULT_OUT_OF_MEMORY); + avifResult result = avifFillDimgIdxToItemIdxArray(dimgIdxToItemIdx, numTiles, item); + if (result == AVIF_RESULT_OK) { + result = avifDecoderGenerateImageGridTiles(decoder, item, itemCategory, dimgIdxToItemIdx, numTiles); + } + avifFree(dimgIdxToItemIdx); + AVIF_CHECKRES(result); + } else { + AVIF_CHECKERR(item->size != 0, AVIF_RESULT_MISSING_IMAGE_ITEM); + + const avifCodecType codecType = avifGetCodecType(item->type); + AVIF_ASSERT_OR_RETURN(codecType != AVIF_CODEC_TYPE_UNKNOWN); + avifTile * tile = + avifDecoderDataCreateTile(decoder->data, codecType, item->width, item->height, avifDecoderItemOperatingPoint(item)); + AVIF_CHECKERR(tile, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKRES(avifCodecDecodeInputFillFromDecoderItem(tile->input, + item, + decoder->allowProgressive, + decoder->imageCountLimit, + decoder->io->sizeHint, + &decoder->diag)); + tile->input->itemCategory = itemCategory; + } + info->tileCount = decoder->data->tiles.count - previousTileCount; + return AVIF_RESULT_OK; +} + +// Populates depth, yuvFormat and yuvChromaSamplePosition fields on 'image' based on data from the codec config property (e.g. "av1C"). +static avifResult avifReadCodecConfigProperty(avifImage * image, const avifPropertyArray * properties, avifCodecType codecType) +{ + const avifProperty * configProp = avifPropertyArrayFind(properties, avifGetConfigurationPropertyName(codecType)); + if (configProp) { + image->depth = avifCodecConfigurationBoxGetDepth(&configProp->u.av1C); + if (configProp->u.av1C.monochrome) { + image->yuvFormat = AVIF_PIXEL_FORMAT_YUV400; + } else { + if (configProp->u.av1C.chromaSubsamplingX && configProp->u.av1C.chromaSubsamplingY) { + image->yuvFormat = AVIF_PIXEL_FORMAT_YUV420; + } else if (configProp->u.av1C.chromaSubsamplingX) { + image->yuvFormat = AVIF_PIXEL_FORMAT_YUV422; + } else { + image->yuvFormat = AVIF_PIXEL_FORMAT_YUV444; + } + } + image->yuvChromaSamplePosition = (avifChromaSamplePosition)configProp->u.av1C.chromaSamplePosition; + } else { + // A configuration property box is mandatory in all valid AVIF configurations. Bail out. + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + return AVIF_RESULT_OK; +} + +avifResult avifDecoderReset(avifDecoder * decoder) +{ + avifDiagnosticsClearError(&decoder->diag); + + avifDecoderData * data = decoder->data; + if (!data) { + // Nothing to reset. + return AVIF_RESULT_OK; + } + + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + memset(&data->tileInfos[c].grid, 0, sizeof(data->tileInfos[c].grid)); + } + avifDecoderDataClearTiles(data); + + // Prepare / cleanup decoded image state + if (decoder->image) { + avifImageDestroy(decoder->image); + } + decoder->image = avifImageCreateEmpty(); + AVIF_CHECKERR(decoder->image, AVIF_RESULT_OUT_OF_MEMORY); + decoder->progressiveState = AVIF_PROGRESSIVE_STATE_UNAVAILABLE; + data->cicpSet = AVIF_FALSE; + + memset(&decoder->ioStats, 0, sizeof(decoder->ioStats)); + + // Color only or alpha only is not currently supported. + if ((decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA) != 0 && + (decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA) != AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA) { + avifDiagnosticsPrintf(&decoder->diag, "imageContentToDecode set to only color or only alpha is not supported"); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + // ----------------------------------------------------------------------- + // Build decode input + + data->sourceSampleTable = NULL; // Reset + if (decoder->requestedSource == AVIF_DECODER_SOURCE_AUTO) { + // Honor the major brand (avif or avis) if present, otherwise prefer avis (tracks) if possible. + if (!memcmp(data->majorBrand, "avis", 4)) { + data->source = AVIF_DECODER_SOURCE_TRACKS; + } else if (!memcmp(data->majorBrand, "avif", 4)) { + data->source = AVIF_DECODER_SOURCE_PRIMARY_ITEM; + } else if (data->tracks.count > 0) { + data->source = AVIF_DECODER_SOURCE_TRACKS; + } else { + data->source = AVIF_DECODER_SOURCE_PRIMARY_ITEM; + } + } else { + data->source = decoder->requestedSource; + } + + avifCodecType colorCodecType = AVIF_CODEC_TYPE_UNKNOWN; + const avifPropertyArray * colorProperties = NULL; + const avifPropertyArray * alphaProperties = NULL; + const avifPropertyArray * gainMapProperties = NULL; + if (data->source == AVIF_DECODER_SOURCE_TRACKS) { + avifTrack * colorTrack = NULL; + avifTrack * alphaTrack = NULL; + + // Find primary track - this probably needs some better detection + uint32_t colorTrackIndex = 0; + for (; colorTrackIndex < data->tracks.count; ++colorTrackIndex) { + avifTrack * track = &data->tracks.track[colorTrackIndex]; + if (!track->sampleTable) { + continue; + } + if (!track->id) { // trak box might be missing a tkhd box inside, skip it + continue; + } + if (!track->sampleTable->chunks.count) { + continue; + } + colorCodecType = avifSampleTableGetCodecType(track->sampleTable); + if (colorCodecType == AVIF_CODEC_TYPE_UNKNOWN) { + continue; + } + if (track->auxForID != 0) { + continue; + } + // HEIF (ISO/IEC 23008-12:2022), Section 7.1: + // In order to distinguish image sequences from video, the handler type in the + // HandlerBox of the track is 'pict' to indicate an image sequence track. + // But we do not check the handler type because it may break some existing files. + + // Found one! + break; + } + if (colorTrackIndex == data->tracks.count) { + avifDiagnosticsPrintf(&decoder->diag, "Failed to find AV1 color track"); + return AVIF_RESULT_NO_CONTENT; + } + colorTrack = &data->tracks.track[colorTrackIndex]; + + colorProperties = avifSampleTableGetProperties(colorTrack->sampleTable, colorCodecType); + if (!colorProperties) { + avifDiagnosticsPrintf(&decoder->diag, "Failed to find AV1 color track's color properties"); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + // Find Exif and/or XMP metadata, if any + if (colorTrack->meta) { + // See the comment above avifDecoderFindMetadata() for the explanation of using 0 here + avifResult findResult = avifDecoderFindMetadata(decoder, colorTrack->meta, decoder->image, 0); + if (findResult != AVIF_RESULT_OK) { + return findResult; + } + } + + uint32_t alphaTrackIndex = 0; + avifCodecType alphaCodecType = AVIF_CODEC_TYPE_UNKNOWN; + for (; alphaTrackIndex < data->tracks.count; ++alphaTrackIndex) { + avifTrack * track = &data->tracks.track[alphaTrackIndex]; + if (!track->sampleTable) { + continue; + } + if (!track->id) { + continue; + } + if (!track->sampleTable->chunks.count) { + continue; + } + alphaCodecType = avifSampleTableGetCodecType(track->sampleTable); + if (alphaCodecType == AVIF_CODEC_TYPE_UNKNOWN) { + continue; + } + const avifPropertyArray * properties = avifSampleTableGetProperties(track->sampleTable, alphaCodecType); + const avifProperty * auxiProp = properties ? avifPropertyArrayFind(properties, "auxi") : NULL; + // If auxi is present, check that it contains the alpha URN. + // If auxi is not present, assume that the track is alpha. This is for backward compatibility with + // old versions of libavif that did not write this property, see + // https://github.com/AOMediaCodec/libavif/commit/98faa17 + if (auxiProp && !isAlphaURN(auxiProp->u.auxC.auxType)) { + continue; + } + // Do not check the track's handlerType. It should be "auxv" according to + // HEIF (ISO/IEC 23008-12:2022), Section 7.5.3.1, but old versions of libavif used to write + // "pict" instead. See https://github.com/AOMediaCodec/libavif/commit/65d0af9 + + if (track->auxForID == colorTrack->id) { + // Found it! + alphaProperties = properties; + break; + } + } + if (alphaTrackIndex != data->tracks.count) { + alphaTrack = &data->tracks.track[alphaTrackIndex]; + } + + const uint8_t operatingPoint = 0; // No way to set operating point via tracks + avifTile * colorTile = avifDecoderDataCreateTile(data, colorCodecType, colorTrack->width, colorTrack->height, operatingPoint); + AVIF_CHECKERR(colorTile != NULL, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKRES(avifCodecDecodeInputFillFromSampleTable(colorTile->input, + colorTrack->sampleTable, + decoder->imageCountLimit, + decoder->io->sizeHint, + data->diag)); + data->tileInfos[AVIF_ITEM_COLOR].tileCount = 1; + + if (alphaTrack) { + avifTile * alphaTile = avifDecoderDataCreateTile(data, alphaCodecType, alphaTrack->width, alphaTrack->height, operatingPoint); + AVIF_CHECKERR(alphaTile != NULL, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKRES(avifCodecDecodeInputFillFromSampleTable(alphaTile->input, + alphaTrack->sampleTable, + decoder->imageCountLimit, + decoder->io->sizeHint, + data->diag)); + alphaTile->input->itemCategory = AVIF_ITEM_ALPHA; + data->tileInfos[AVIF_ITEM_ALPHA].tileCount = 1; + } + + // Stash off sample table for future timing information + data->sourceSampleTable = colorTrack->sampleTable; + + // Image sequence timing + decoder->imageIndex = -1; + decoder->imageCount = (int)colorTile->input->samples.count; + decoder->timescale = colorTrack->mediaTimescale; + decoder->durationInTimescales = colorTrack->mediaDuration; + if (colorTrack->mediaTimescale) { + decoder->duration = (double)decoder->durationInTimescales / (double)colorTrack->mediaTimescale; + } else { + decoder->duration = 0; + } + // If the alphaTrack->repetitionCount and colorTrack->repetitionCount are different, we will simply use the + // colorTrack's repetitionCount. + decoder->repetitionCount = colorTrack->repetitionCount; + + memset(&decoder->imageTiming, 0, sizeof(decoder->imageTiming)); // to be set in avifDecoderNextImage() + + decoder->image->width = colorTrack->width; + decoder->image->height = colorTrack->height; + decoder->alphaPresent = (alphaTrack != NULL); + decoder->image->alphaPremultiplied = decoder->alphaPresent && (colorTrack->premByID == alphaTrack->id); + } else { + // Create from items + + if (data->meta->primaryItemID == 0) { + // A primary item is required + avifDiagnosticsPrintf(&decoder->diag, "Primary item not specified"); + return AVIF_RESULT_MISSING_IMAGE_ITEM; + } + + // Main item of each group category (top-level item such as grid or single tile), if any. + avifDecoderItem * mainItems[AVIF_ITEM_CATEGORY_COUNT]; + avifCodecType codecType[AVIF_ITEM_CATEGORY_COUNT]; + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + mainItems[c] = NULL; + codecType[c] = AVIF_CODEC_TYPE_UNKNOWN; + } + + // Mandatory primary color item + mainItems[AVIF_ITEM_COLOR] = avifMetaFindColorItem(data->meta); + if (!mainItems[AVIF_ITEM_COLOR]) { + avifDiagnosticsPrintf(&decoder->diag, "Primary item not found"); + return AVIF_RESULT_MISSING_IMAGE_ITEM; + } + AVIF_CHECKRES(avifDecoderItemReadAndParse(decoder, + mainItems[AVIF_ITEM_COLOR], + /*isItemInInput=*/AVIF_TRUE, + &data->tileInfos[AVIF_ITEM_COLOR].grid, + &codecType[AVIF_ITEM_COLOR])); + colorProperties = &mainItems[AVIF_ITEM_COLOR]->properties; + colorCodecType = codecType[AVIF_ITEM_COLOR]; + + // Optional alpha auxiliary item + avifBool isAlphaItemInInput; + AVIF_CHECKRES(avifMetaFindAlphaItem(data->meta, + mainItems[AVIF_ITEM_COLOR], + &data->tileInfos[AVIF_ITEM_COLOR], + &mainItems[AVIF_ITEM_ALPHA], + &data->tileInfos[AVIF_ITEM_ALPHA], + &isAlphaItemInInput)); + if (mainItems[AVIF_ITEM_ALPHA]) { + AVIF_CHECKRES(avifDecoderItemReadAndParse(decoder, + mainItems[AVIF_ITEM_ALPHA], + isAlphaItemInInput, + &data->tileInfos[AVIF_ITEM_ALPHA].grid, + &codecType[AVIF_ITEM_ALPHA])); + } + + // Section 10.2.6 of 23008-12:2024/AMD 1:2024(E): + // 'tmap' brand + // This brand enables file players to identify and decode HEIF files containing tone-map derived image + // items. When present, this brand shall be among the brands included in the compatible_brands + // array of the FileTypeBox. + // + // If the file contains a 'tmap' item but doesn't have the 'tmap' brand, it is technically invalid. + // However, we don't report any error because in order to do detect this case consistently, we would + // need to remove the early exit in avifParse() to check if a 'tmap' item might be present + // further down the file. Instead, we simply ignore tmap items in files that lack the 'tmap' brand. + if (avifBrandArrayHasBrand(&data->compatibleBrands, "tmap")) { + avifDecoderItem * gainMapItem; + avifCodecType gainMapCodecType; + AVIF_CHECKRES(avifDecoderFindGainMapItem(decoder, mainItems[AVIF_ITEM_COLOR], &gainMapItem, &gainMapCodecType)); + if (gainMapItem != NULL && decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_GAIN_MAP) { + mainItems[AVIF_ITEM_GAIN_MAP] = gainMapItem; + codecType[AVIF_ITEM_GAIN_MAP] = gainMapCodecType; + } + } + + // AVIF_ITEM_SAMPLE_TRANSFORM (not used through mainItems because not a coded item (well grids are not coded items either but it's different)). + avifDecoderItem * const sampleTransformItem = avifDecoderDataFindSampleTransformImageItem(data); + if ((decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA) && + (decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_SAMPLE_TRANSFORMS) && sampleTransformItem != NULL) { + AVIF_ASSERT_OR_RETURN(data->sampleTransformNumInputImageItems == 0); + + for (uint32_t i = 0; i < data->meta->items.count; ++i) { + avifDecoderItem * inputImageItem = data->meta->items.item[i]; + if (inputImageItem->dimgForID == sampleTransformItem->id) { + ++data->sampleTransformNumInputImageItems; + } + } + // A 'sato' derived image item must reference at least one input image item via 'iref dimg'. + if (data->sampleTransformNumInputImageItems == 0) { + avifDiagnosticsPrintf(data->diag, "Box[sato] item %u has no input image items", sampleTransformItem->id); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + // Check max number of input items allowed by the format. + if (data->sampleTransformNumInputImageItems > 32) { + avifDiagnosticsPrintf(data->diag, + "Box[sato] too many input items, format allows up to 32, got %d", + data->sampleTransformNumInputImageItems); + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + // Check max number of input items supported by this implementation. + AVIF_CHECKERR(data->sampleTransformNumInputImageItems <= AVIF_SAMPLE_TRANSFORM_MAX_NUM_INPUT_IMAGE_ITEMS, + AVIF_RESULT_NOT_IMPLEMENTED); + + uint32_t numExtraInputImageItems = 0; + for (uint32_t i = 0; i < data->meta->items.count; ++i) { + avifDecoderItem * inputImageItem = data->meta->items.item[i]; + if (inputImageItem->dimgForID != sampleTransformItem->id) { + continue; + } + if (avifDecoderItemShouldBeSkipped(inputImageItem)) { + avifDiagnosticsPrintf(data->diag, "Box[sato] input item %u is not a supported image type", inputImageItem->id); + return AVIF_RESULT_DECODE_SAMPLE_TRANSFORM_FAILED; + } + + AVIF_ASSERT_OR_RETURN(inputImageItem->dimgIdx < AVIF_SAMPLE_TRANSFORM_MAX_NUM_INPUT_IMAGE_ITEMS); + avifItemCategory * category = &data->sampleTransformInputImageItems[inputImageItem->dimgIdx]; + avifBool foundItem = AVIF_FALSE; + for (int c = AVIF_ITEM_COLOR; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + if (mainItems[c] && inputImageItem->id == mainItems[c]->id) { + *category = c; + AVIF_CHECKERR(*category == AVIF_ITEM_COLOR, AVIF_RESULT_NOT_IMPLEMENTED); + foundItem = AVIF_TRUE; + break; + } + } + if (!foundItem) { + AVIF_CHECKERR(numExtraInputImageItems < AVIF_SAMPLE_TRANSFORM_MAX_NUM_EXTRA_INPUT_IMAGE_ITEMS, + AVIF_RESULT_NOT_IMPLEMENTED); + *category = (avifItemCategory)(AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_COLOR + numExtraInputImageItems); + const avifItemCategory alphaCategory = + (avifItemCategory)(AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_ALPHA + numExtraInputImageItems); + mainItems[*category] = inputImageItem; + ++numExtraInputImageItems; + + AVIF_CHECKRES(avifDecoderItemReadAndParse(decoder, + inputImageItem, + /*isItemInInput=*/AVIF_TRUE, + &data->tileInfos[*category].grid, + &codecType[*category])); + + // Optional alpha auxiliary item + avifBool isAlphaInputImageItemInInput = AVIF_FALSE; + AVIF_CHECKRES(avifMetaFindAlphaItem(data->meta, + mainItems[*category], + &data->tileInfos[*category], + &mainItems[alphaCategory], + &data->tileInfos[alphaCategory], + &isAlphaInputImageItemInInput)); + + AVIF_CHECKERR(!mainItems[alphaCategory] == !mainItems[AVIF_ITEM_ALPHA], AVIF_RESULT_NOT_IMPLEMENTED); + if (mainItems[alphaCategory] != NULL) { + AVIF_CHECKERR(isAlphaInputImageItemInInput == isAlphaItemInInput, AVIF_RESULT_NOT_IMPLEMENTED); + AVIF_CHECKERR((mainItems[*category]->premByID == mainItems[alphaCategory]->id) == + (mainItems[AVIF_ITEM_COLOR]->premByID == mainItems[AVIF_ITEM_ALPHA]->id), + AVIF_RESULT_NOT_IMPLEMENTED); + AVIF_CHECKRES(avifDecoderItemReadAndParse(decoder, + mainItems[alphaCategory], + isAlphaInputImageItemInInput, + &data->tileInfos[alphaCategory].grid, + &codecType[alphaCategory])); + } + } + } + + AVIF_ASSERT_OR_RETURN(data->meta->sampleTransformExpression.tokens == NULL); + avifROData satoData; + AVIF_CHECKRES(avifDecoderItemRead(sampleTransformItem, decoder->io, &satoData, 0, 0, data->diag)); + AVIF_CHECKRES(avifParseSampleTransformImageBox(satoData.data, + satoData.size, + data->sampleTransformNumInputImageItems, + &data->meta->sampleTransformExpression, + data->diag)); + AVIF_CHECKRES(avifDecoderSampleTransformItemValidateProperties(sampleTransformItem, data->diag)); + const avifProperty * pixiProp = avifPropertyArrayFind(&sampleTransformItem->properties, "pixi"); + AVIF_ASSERT_OR_RETURN(pixiProp != NULL); + data->meta->sampleTransformDepth = pixiProp->u.pixi.planeDepths[0]; + } + + // Find Exif and/or XMP metadata, if any + AVIF_CHECKRES(avifDecoderFindMetadata(decoder, data->meta, decoder->image, mainItems[AVIF_ITEM_COLOR]->id)); + + // Set all counts and timing to safe-but-uninteresting values + decoder->imageIndex = -1; + decoder->imageCount = 1; + decoder->imageTiming.timescale = 1; + decoder->imageTiming.pts = 0; + decoder->imageTiming.ptsInTimescales = 0; + decoder->imageTiming.duration = 1; + decoder->imageTiming.durationInTimescales = 1; + decoder->timescale = 1; + decoder->duration = 1; + decoder->durationInTimescales = 1; + + for (int c = AVIF_ITEM_COLOR; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + if (!mainItems[c]) { + continue; + } + AVIF_ASSERT_OR_RETURN(c != AVIF_ITEM_SAMPLE_TRANSFORM); // See sampleTransformItem. + + if (avifIsAlpha((avifItemCategory)c) && !mainItems[c]->width && !mainItems[c]->height) { + // NON-STANDARD: Alpha subimage does not have an ispe property; adopt width/height from color item + AVIF_ASSERT_OR_RETURN(!(decoder->strictFlags & AVIF_STRICT_ALPHA_ISPE_REQUIRED)); + mainItems[c]->width = mainItems[AVIF_ITEM_COLOR]->width; + mainItems[c]->height = mainItems[AVIF_ITEM_COLOR]->height; + } + + AVIF_CHECKRES(avifDecoderAdoptGridTileCodecTypeIfNeeded(decoder, mainItems[c], &data->tileInfos[c])); + + if (c == AVIF_ITEM_COLOR || c == AVIF_ITEM_ALPHA) { + if (!(decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA)) { + continue; + } + } else if (c == AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_COLOR || c == AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_1_COLOR || + c == AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_ALPHA || c == AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_1_ALPHA) { + AVIF_ASSERT_OR_RETURN((decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA) && + (decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_SAMPLE_TRANSFORMS)); + } else { + AVIF_ASSERT_OR_RETURN(c == AVIF_ITEM_GAIN_MAP); + if (!(decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_GAIN_MAP)) { + continue; + } + } + + AVIF_CHECKRES(avifDecoderGenerateImageTiles(decoder, &data->tileInfos[c], mainItems[c], (avifItemCategory)c)); + + avifStrictFlags strictFlags = decoder->strictFlags; + if (avifIsAlpha((avifItemCategory)c) && !isAlphaItemInInput) { + // In this case, the made up grid item will not have an associated pixi property. So validate everything else + // but the pixi property. + strictFlags &= ~(avifStrictFlags)AVIF_STRICT_PIXI_REQUIRED; + } + AVIF_CHECKRES( + avifDecoderItemValidateProperties(mainItems[c], avifGetConfigurationPropertyName(codecType[c]), &decoder->diag, strictFlags)); + } + + if (mainItems[AVIF_ITEM_COLOR]->progressive) { + decoder->progressiveState = AVIF_PROGRESSIVE_STATE_AVAILABLE; + // data->tileInfos[AVIF_ITEM_COLOR].firstTileIndex is not yet defined but will be set to 0 a few lines below. + const avifTile * colorTile = &data->tiles.tile[0]; + if (colorTile->input->samples.count > 1) { + decoder->progressiveState = AVIF_PROGRESSIVE_STATE_ACTIVE; + decoder->imageCount = (int)colorTile->input->samples.count; + } + } + + decoder->image->width = mainItems[AVIF_ITEM_COLOR]->width; + decoder->image->height = mainItems[AVIF_ITEM_COLOR]->height; + decoder->alphaPresent = (mainItems[AVIF_ITEM_ALPHA] != NULL); + decoder->image->alphaPremultiplied = decoder->alphaPresent && + (mainItems[AVIF_ITEM_COLOR]->premByID == mainItems[AVIF_ITEM_ALPHA]->id); + + if (mainItems[AVIF_ITEM_ALPHA]) { + alphaProperties = &mainItems[AVIF_ITEM_ALPHA]->properties; + } + if (mainItems[AVIF_ITEM_GAIN_MAP]) { + AVIF_ASSERT_OR_RETURN(decoder->image->gainMap && decoder->image->gainMap->image); + decoder->image->gainMap->image->width = mainItems[AVIF_ITEM_GAIN_MAP]->width; + decoder->image->gainMap->image->height = mainItems[AVIF_ITEM_GAIN_MAP]->height; + // Must be called after avifDecoderAdoptGridTileCodecType() which among other things copies the + // codec config property from the first tile of a grid to the grid item (when grids are used). + AVIF_CHECKRES(avifReadCodecConfigProperty(decoder->image->gainMap->image, + &mainItems[AVIF_ITEM_GAIN_MAP]->properties, + codecType[AVIF_ITEM_GAIN_MAP])); + gainMapProperties = &mainItems[AVIF_ITEM_GAIN_MAP]->properties; + } + } + + uint32_t firstTileIndex = 0; + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + data->tileInfos[c].firstTileIndex = firstTileIndex; + firstTileIndex += data->tileInfos[c].tileCount; + } + + // Sanity check tiles + for (uint32_t tileIndex = 0; tileIndex < data->tiles.count; ++tileIndex) { + avifTile * tile = &data->tiles.tile[tileIndex]; + for (uint32_t sampleIndex = 0; sampleIndex < tile->input->samples.count; ++sampleIndex) { + avifDecodeSample * sample = &tile->input->samples.sample[sampleIndex]; + if (!sample->size) { + // Every sample must have some data + return AVIF_RESULT_BMFF_PARSE_FAILED; + } + + if (tile->input->itemCategory == AVIF_ITEM_COLOR) { + decoder->ioStats.colorOBUSize += sample->size; + } else if (tile->input->itemCategory == AVIF_ITEM_ALPHA) { + decoder->ioStats.alphaOBUSize += sample->size; + } + } + } + + AVIF_CHECKRES(avifReadColorProperties(decoder->io, + colorProperties, + &decoder->image->icc, + &decoder->image->colorPrimaries, + &decoder->image->transferCharacteristics, + &decoder->image->matrixCoefficients, + &decoder->image->yuvRange, + &data->cicpSet)); + + const avifProperty * clliProp = avifPropertyArrayFind(colorProperties, "clli"); + if (clliProp) { + decoder->image->clli = clliProp->u.clli; + } + + // Transformations + const avifProperty * paspProp = avifPropertyArrayFind(colorProperties, "pasp"); + if (paspProp) { + decoder->image->transformFlags |= AVIF_TRANSFORM_PASP; + decoder->image->pasp = paspProp->u.pasp; + } + const avifProperty * clapProp = avifPropertyArrayFind(colorProperties, "clap"); + if (clapProp) { + decoder->image->transformFlags |= AVIF_TRANSFORM_CLAP; + decoder->image->clap = clapProp->u.clap; + } + const avifProperty * irotProp = avifPropertyArrayFind(colorProperties, "irot"); + if (irotProp) { + decoder->image->transformFlags |= AVIF_TRANSFORM_IROT; + decoder->image->irot = irotProp->u.irot; + } + const avifProperty * imirProp = avifPropertyArrayFind(colorProperties, "imir"); + if (imirProp) { + decoder->image->transformFlags |= AVIF_TRANSFORM_IMIR; + decoder->image->imir = imirProp->u.imir; + } + if (alphaProperties) { + AVIF_CHECKRES(avifDecoderCheckAlphaProperties(decoder, alphaProperties)); + } + if (gainMapProperties) { + AVIF_CHECKRES(avifDecoderCheckGainMapProperties(decoder, gainMapProperties)); + } + + if (!data->cicpSet && (data->tiles.count > 0)) { + avifTile * firstTile = &data->tiles.tile[0]; + if (firstTile->input->samples.count > 0) { + avifDecodeSample * sample = &firstTile->input->samples.sample[0]; + + // Harvest CICP from the AV1's sequence header, which should be very close to the front + // of the first sample. Read in successively larger chunks until we successfully parse the sequence. + static const size_t searchSampleChunkIncrement = 64; + static const size_t searchSampleSizeMax = 4096; + size_t searchSampleSize = 0; + do { + searchSampleSize += searchSampleChunkIncrement; + if (searchSampleSize > sample->size) { + searchSampleSize = sample->size; + } + + avifResult prepareResult = avifDecoderPrepareSample(decoder, sample, searchSampleSize); + if (prepareResult != AVIF_RESULT_OK) { + return prepareResult; + } + + avifSequenceHeader sequenceHeader; + if (avifSequenceHeaderParse(&sequenceHeader, &sample->data, firstTile->codecType)) { + data->cicpSet = AVIF_TRUE; + decoder->image->colorPrimaries = sequenceHeader.colorPrimaries; + decoder->image->transferCharacteristics = sequenceHeader.transferCharacteristics; + decoder->image->matrixCoefficients = sequenceHeader.matrixCoefficients; + decoder->image->yuvRange = sequenceHeader.range; + break; + } + } while (searchSampleSize != sample->size && searchSampleSize < searchSampleSizeMax); + } + } + + AVIF_CHECKRES(avifReadCodecConfigProperty(decoder->image, colorProperties, colorCodecType)); + if (decoder->data->meta->sampleTransformExpression.count > 0) { + AVIF_ASSERT_OR_RETURN(decoder->data->meta->sampleTransformDepth != 0); + decoder->image->depth = decoder->data->meta->sampleTransformDepth; + } + + // Expose as raw bytes all other properties that libavif does not care about. + for (size_t i = 0; i < colorProperties->count; ++i) { + const avifProperty * property = &colorProperties->prop[i]; + if (property->isOpaque) { + AVIF_CHECKRES(avifImagePushProperty(decoder->image, + property->type, + property->u.opaque.usertype, + property->u.opaque.boxPayload.data, + property->u.opaque.boxPayload.size)); + } + } + + if (gainMapProperties) { + for (size_t i = 0; i < gainMapProperties->count; ++i) { + const avifProperty * property = &gainMapProperties->prop[i]; + if (property->isOpaque) { + AVIF_CHECKRES(avifImagePushProperty(decoder->image->gainMap->image, + property->type, + property->u.opaque.usertype, + property->u.opaque.boxPayload.data, + property->u.opaque.boxPayload.size)); + } + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifDecoderPrepareTiles(avifDecoder * decoder, uint32_t nextImageIndex, const avifTileInfo * info) +{ + for (unsigned int tileIndex = info->decodedTileCount; tileIndex < info->tileCount; ++tileIndex) { + avifTile * tile = &decoder->data->tiles.tile[info->firstTileIndex + tileIndex]; + + if (nextImageIndex >= tile->input->samples.count) { + return AVIF_RESULT_NO_IMAGES_REMAINING; + } + + avifDecodeSample * sample = &tile->input->samples.sample[nextImageIndex]; + avifResult prepareResult = avifDecoderPrepareSample(decoder, sample, 0); + if (prepareResult != AVIF_RESULT_OK) { + return prepareResult; + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifImageLimitedToFullAlpha(avifImage * image) +{ + if (image->imageOwnsAlphaPlane) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + const uint8_t * alphaPlane = image->alphaPlane; + const uint32_t alphaRowBytes = image->alphaRowBytes; + + // We cannot do the range conversion in place since it will modify the + // codec's internal frame buffers. Allocate memory for the conversion. + image->alphaPlane = NULL; + image->alphaRowBytes = 0; + const avifResult allocationResult = avifImageAllocatePlanes(image, AVIF_PLANES_A); + if (allocationResult != AVIF_RESULT_OK) { + return allocationResult; + } + + if (image->depth > 8) { + for (uint32_t j = 0; j < image->height; ++j) { + const uint8_t * srcRow = &alphaPlane[(size_t)j * alphaRowBytes]; + uint8_t * dstRow = &image->alphaPlane[(size_t)j * image->alphaRowBytes]; + for (uint32_t i = 0; i < image->width; ++i) { + int srcAlpha = *((const uint16_t *)&srcRow[i * 2]); + int dstAlpha = avifLimitedToFullY(image->depth, srcAlpha); + *((uint16_t *)&dstRow[i * 2]) = (uint16_t)dstAlpha; + } + } + } else { + for (uint32_t j = 0; j < image->height; ++j) { + const uint8_t * srcRow = &alphaPlane[(size_t)j * alphaRowBytes]; + uint8_t * dstRow = &image->alphaPlane[(size_t)j * image->alphaRowBytes]; + for (uint32_t i = 0; i < image->width; ++i) { + int srcAlpha = srcRow[i]; + int dstAlpha = avifLimitedToFullY(image->depth, srcAlpha); + dstRow[i] = (uint8_t)dstAlpha; + } + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifGetErrorForItemCategory(avifItemCategory itemCategory) +{ + if (itemCategory == AVIF_ITEM_GAIN_MAP) { + return AVIF_RESULT_DECODE_GAIN_MAP_FAILED; + } + if (itemCategory >= AVIF_SAMPLE_TRANSFORM_MIN_CATEGORY && itemCategory <= AVIF_SAMPLE_TRANSFORM_MAX_CATEGORY) { + return AVIF_RESULT_DECODE_SAMPLE_TRANSFORM_FAILED; + } + return avifIsAlpha(itemCategory) ? AVIF_RESULT_DECODE_ALPHA_FAILED : AVIF_RESULT_DECODE_COLOR_FAILED; +} + +static avifResult avifDecoderDecodeTiles(avifDecoder * decoder, uint32_t nextImageIndex, avifTileInfo * info) +{ + const unsigned int oldDecodedTileCount = info->decodedTileCount; + for (unsigned int tileIndex = oldDecodedTileCount; tileIndex < info->tileCount; ++tileIndex) { + avifTile * tile = &decoder->data->tiles.tile[info->firstTileIndex + tileIndex]; + + const avifDecodeSample * sample = &tile->input->samples.sample[nextImageIndex]; + if (sample->data.size < sample->size) { + AVIF_ASSERT_OR_RETURN(decoder->allowIncremental); + // Data is missing but there is no error yet. Output available pixel rows. + return AVIF_RESULT_OK; + } + + avifBool isLimitedRangeAlpha = AVIF_FALSE; + tile->codec->maxThreads = decoder->maxThreads; + tile->codec->imageSizeLimit = decoder->imageSizeLimit; + tile->codec->imageDimensionLimit = decoder->imageDimensionLimit; + if (!tile->codec->getNextImage(tile->codec, sample, avifIsAlpha(tile->input->itemCategory), &isLimitedRangeAlpha, tile->image)) { + avifDiagnosticsPrintf(&decoder->diag, "tile->codec->getNextImage() failed"); + return avifGetErrorForItemCategory(tile->input->itemCategory); + } + + // Section 2.3.4 of AV1 Codec ISO Media File Format Binding v1.2.0 says: + // the full_range_flag in the colr box shall match the color_range + // flag in the Sequence Header OBU. + // See https://aomediacodec.github.io/av1-isobmff/v1.2.0.html#av1codecconfigurationbox-semantics. + // If a 'colr' box of colour_type 'nclx' was parsed, a mismatch between + // the 'colr' decoder->image->yuvRange and the AV1 OBU + // tile->image->yuvRange should be treated as an error. + // However codec_svt.c was not encoding the color_range field for + // multiple years, so there probably are files in the wild that will + // fail decoding if this is enforced. Thus this pattern is allowed. + // Section 12.1.5.1 of ISO 14496-12 (ISOBMFF) says: + // If colour information is supplied in both this [colr] box, and also + // in the video bitstream, this box takes precedence, and over-rides + // the information in the bitstream. + // So decoder->image->yuvRange is kept because it was either the 'colr' + // value set when the 'colr' box was parsed, or it was the AV1 OBU value + // extracted from the sequence header OBU of the first tile of the first + // frame (if no 'colr' box of colour_type 'nclx' was found). + + // Alpha plane with limited range is not allowed by the latest revision + // of the specification. However, it was allowed in version 1.0.0 of the + // specification. To allow such files, simply convert the alpha plane to + // full range. + if (avifIsAlpha(tile->input->itemCategory) && isLimitedRangeAlpha) { + avifResult result = avifImageLimitedToFullAlpha(tile->image); + if (result != AVIF_RESULT_OK) { + avifDiagnosticsPrintf(&decoder->diag, "avifImageLimitedToFullAlpha failed"); + return result; + } + } + + // Scale the decoded image so that it corresponds to this tile's output dimensions + if ((tile->width != tile->image->width) || (tile->height != tile->image->height)) { + if (avifImageScaleWithLimit(tile->image, + tile->width, + tile->height, + decoder->imageSizeLimit, + decoder->imageDimensionLimit, + &decoder->diag) != AVIF_RESULT_OK) { + return avifGetErrorForItemCategory(tile->input->itemCategory); + } + } + + ++info->decodedTileCount; + + const avifBool isGrid = (info->grid.rows > 0) && (info->grid.columns > 0); + avifBool stealPlanes = !isGrid; + if (decoder->data->meta->sampleTransformExpression.count > 0) { + // Keep everything as a copy for now. + stealPlanes = AVIF_FALSE; + } + if (tile->input->itemCategory >= AVIF_SAMPLE_TRANSFORM_MIN_CATEGORY && + tile->input->itemCategory <= AVIF_SAMPLE_TRANSFORM_MAX_CATEGORY) { + // Keep Sample Transform input image item samples in tiles. + // The expression will be applied in avifDecoderNextImage() below instead, once all the tiles are available. + continue; + } + + if (!stealPlanes) { + avifImage * dstImage = decoder->image; + if (tile->input->itemCategory == AVIF_ITEM_GAIN_MAP) { + AVIF_ASSERT_OR_RETURN(dstImage->gainMap && dstImage->gainMap->image); + dstImage = dstImage->gainMap->image; + } + if (tileIndex == 0) { + AVIF_CHECKRES(avifDecoderDataAllocateImagePlanes(decoder->data, info, dstImage, &decoder->data->cicpSet)); + } + AVIF_CHECKRES(avifDecoderDataCopyTileToImage(decoder->data, info, dstImage, tile, tileIndex)); + } else { + AVIF_ASSERT_OR_RETURN(info->tileCount == 1); + AVIF_ASSERT_OR_RETURN(tileIndex == 0); + avifImage * src = tile->image; + + if (tile->input->itemCategory == AVIF_ITEM_GAIN_MAP) { + AVIF_ASSERT_OR_RETURN(decoder->image->gainMap && decoder->image->gainMap->image); + decoder->image->gainMap->image->width = src->width; + decoder->image->gainMap->image->height = src->height; + decoder->image->gainMap->image->depth = src->depth; + } else { + if ((decoder->image->width != src->width) || (decoder->image->height != src->height) || + (decoder->image->depth != src->depth)) { + if (avifIsAlpha(tile->input->itemCategory)) { + avifDiagnosticsPrintf(&decoder->diag, + "The color image item does not match the alpha image item in width, height, or bit depth"); + return AVIF_RESULT_DECODE_ALPHA_FAILED; + } + avifImageFreePlanes(decoder->image, AVIF_PLANES_ALL); + + decoder->image->width = src->width; + decoder->image->height = src->height; + decoder->image->depth = src->depth; + } + } + + if (avifIsAlpha(tile->input->itemCategory)) { + avifImageStealPlanes(decoder->image, src, AVIF_PLANES_A); + } else if (tile->input->itemCategory == AVIF_ITEM_GAIN_MAP) { + AVIF_ASSERT_OR_RETURN(decoder->image->gainMap && decoder->image->gainMap->image); + avifImageStealPlanes(decoder->image->gainMap->image, src, AVIF_PLANES_YUV); + } else { // AVIF_ITEM_COLOR + avifImageStealPlanes(decoder->image, src, AVIF_PLANES_YUV); + } + } + } + return AVIF_RESULT_OK; +} + +// Returns AVIF_FALSE if there is currently a partially decoded frame. +static avifBool avifDecoderDataFrameFullyDecoded(const avifDecoderData * data) +{ + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + if (data->tileInfos[c].decodedTileCount != data->tileInfos[c].tileCount) { + return AVIF_FALSE; + } + } + return AVIF_TRUE; +} + +// Composites hidden image items and/or the primary image item into the dstImage. +// Tiles are aggregated into temporary buffers (reconstructedInputImages) +// covering the whole dstImage dimensions in case of grids. +// Non-null elements of reconstructedInputImages must be destroyed after calling this function. +static avifResult avifDecoderApplySampleTransformForPlanesImpl(const avifDecoder * decoder, + avifPlanesFlag planes, + avifImage * dstImage, + avifImage * reconstructedInputImages[AVIF_SAMPLE_TRANSFORM_MAX_NUM_INPUT_IMAGE_ITEMS]) +{ + AVIF_ASSERT_OR_RETURN(decoder->data->sampleTransformNumInputImageItems != 0); + AVIF_ASSERT_OR_RETURN(decoder->data->sampleTransformNumInputImageItems <= AVIF_SAMPLE_TRANSFORM_MAX_NUM_INPUT_IMAGE_ITEMS); + const avifImage * inputImages[AVIF_SAMPLE_TRANSFORM_MAX_NUM_INPUT_IMAGE_ITEMS]; + for (uint32_t i = 0; i < decoder->data->sampleTransformNumInputImageItems; ++i) { + avifItemCategory category = decoder->data->sampleTransformInputImageItems[i]; + if (category == AVIF_ITEM_COLOR) { + // If the primary image item was a grid, it was already aggregated + // into this single output buffer in avifDecoderDecodeTiles(). + inputImages[i] = decoder->image; + } else { + AVIF_ASSERT_OR_RETURN(category >= AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_COLOR && + category < AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_COLOR + + AVIF_SAMPLE_TRANSFORM_MAX_NUM_EXTRA_INPUT_IMAGE_ITEMS); + if (planes == AVIF_PLANES_A) { + category += AVIF_SAMPLE_TRANSFORM_MAX_NUM_EXTRA_INPUT_IMAGE_ITEMS; + } + const avifTileInfo * info = &decoder->data->tileInfos[category]; + AVIF_ASSERT_OR_RETURN(info != NULL); + const avifTile * firstTile = &decoder->data->tiles.tile[info->firstTileIndex]; + AVIF_ASSERT_OR_RETURN(firstTile != NULL && firstTile->image != NULL); + if (info->tileCount == 1) { + inputImages[i] = firstTile->image; + } else { + // Combine the tiles into a single buffer used as one of the input images in avifImageApplyExpression(). + reconstructedInputImages[i] = avifImageCreateEmpty(); + AVIF_CHECKERR(reconstructedInputImages[i] != NULL, AVIF_RESULT_OUT_OF_MEMORY); + avifImageCopyNoAlloc(reconstructedInputImages[i], firstTile->image); + reconstructedInputImages[i]->width = decoder->image->width; + reconstructedInputImages[i]->height = decoder->image->height; + avifBool cicpSet = AVIF_TRUE; + AVIF_CHECKRES(avifDecoderDataAllocateImagePlanes(decoder->data, info, reconstructedInputImages[i], &cicpSet)); + for (unsigned int tileIndex = 0; tileIndex < info->tileCount; ++tileIndex) { + const avifTile * tile = firstTile + tileIndex; + AVIF_CHECKRES(avifDecoderDataCopyTileToImage(decoder->data, info, reconstructedInputImages[i], tile, tileIndex)); + } + inputImages[i] = reconstructedInputImages[i]; + } + } + } + AVIF_CHECKRES(avifImageApplyExpression(dstImage, + AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32, + &decoder->data->meta->sampleTransformExpression, + decoder->data->sampleTransformNumInputImageItems, + inputImages, + planes)); + return AVIF_RESULT_OK; +} + +// Intermediate function used to safely destroy temporary buffers even in case of error. +static avifResult avifDecoderApplySampleTransformForPlanes(const avifDecoder * decoder, avifPlanesFlag planes, avifImage * dstImage) +{ + avifImage * toDestroy[AVIF_SAMPLE_TRANSFORM_MAX_NUM_INPUT_IMAGE_ITEMS] = { NULL }; + const avifResult result = avifDecoderApplySampleTransformForPlanesImpl(decoder, planes, dstImage, toDestroy); + for (uint32_t i = 0; i < AVIF_SAMPLE_TRANSFORM_MAX_NUM_INPUT_IMAGE_ITEMS; ++i) { + if (toDestroy[i] != NULL) { + avifImageDestroy(toDestroy[i]); + } + } + return result; +} + +static avifResult avifDecoderApplySampleTransform(const avifDecoder * decoder, avifImage * dstImage) +{ + if (dstImage->depth != decoder->data->meta->sampleTransformDepth) { + AVIF_ASSERT_OR_RETURN(dstImage->yuvPlanes[0] != NULL); + AVIF_ASSERT_OR_RETURN(dstImage->imageOwnsYUVPlanes); + + // Use a temporary buffer because dstImage may point to decoder->image, which could be an input image. + avifImage * dstImageWithCorrectDepth = + avifImageCreate(dstImage->width, dstImage->height, decoder->data->meta->sampleTransformDepth, dstImage->yuvFormat); + AVIF_CHECKERR(dstImageWithCorrectDepth != NULL, AVIF_RESULT_OUT_OF_MEMORY); + dstImageWithCorrectDepth->yuvRange = dstImage->yuvRange; + avifResult result = + avifImageAllocatePlanes(dstImageWithCorrectDepth, dstImage->alphaPlane != NULL ? AVIF_PLANES_ALL : AVIF_PLANES_YUV); + if (result == AVIF_RESULT_OK) { + result = avifDecoderApplySampleTransform(decoder, dstImageWithCorrectDepth); + if (result == AVIF_RESULT_OK) { + // Keep the same dstImage object rather than swapping decoder->image, in case the user already accessed it. + avifImageFreePlanes(dstImage, AVIF_PLANES_ALL); + dstImage->depth = dstImageWithCorrectDepth->depth; + avifImageStealPlanes(dstImage, dstImageWithCorrectDepth, AVIF_PLANES_ALL); + } + } + avifImageDestroy(dstImageWithCorrectDepth); + return result; + } + + AVIF_CHECKRES(avifDecoderApplySampleTransformForPlanes(decoder, AVIF_PLANES_YUV, dstImage)); + if (decoder->alphaPresent) { + AVIF_CHECKRES(avifDecoderApplySampleTransformForPlanes(decoder, AVIF_PLANES_A, dstImage)); + } + return AVIF_RESULT_OK; +} + +avifResult avifDecoderNextImage(avifDecoder * decoder) +{ + avifDiagnosticsClearError(&decoder->diag); + + if (!decoder->data || decoder->data->tiles.count == 0) { + // Nothing has been parsed yet + return AVIF_RESULT_NO_CONTENT; + } + + if (!decoder->io || !decoder->io->read) { + return AVIF_RESULT_IO_NOT_SET; + } + + if (avifDecoderDataFrameFullyDecoded(decoder->data)) { + // A frame was decoded during the last avifDecoderNextImage() call. + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + decoder->data->tileInfos[c].decodedTileCount = 0; + } + } + + AVIF_ASSERT_OR_RETURN(decoder->data->tiles.count == (decoder->data->tileInfos[AVIF_ITEM_CATEGORY_COUNT - 1].firstTileIndex + + decoder->data->tileInfos[AVIF_ITEM_CATEGORY_COUNT - 1].tileCount)); + + const uint32_t nextImageIndex = (uint32_t)(decoder->imageIndex + 1); + + // Ensure that we have created the codecs before proceeding with the decoding. + if (!decoder->data->tiles.tile[0].codec) { + AVIF_CHECKRES(avifDecoderCreateCodecs(decoder)); + } + + // Acquire all sample data for the current image first, allowing for any read call to bail out + // with AVIF_RESULT_WAITING_ON_IO harmlessly / idempotently, unless decoder->allowIncremental. + avifResult prepareTileResult[AVIF_ITEM_CATEGORY_COUNT]; + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + prepareTileResult[c] = avifDecoderPrepareTiles(decoder, nextImageIndex, &decoder->data->tileInfos[c]); + if (!decoder->allowIncremental || (prepareTileResult[c] != AVIF_RESULT_WAITING_ON_IO)) { + AVIF_CHECKRES(prepareTileResult[c]); + } + } + + // Decode all available color tiles now, then all available alpha tiles, then all available bit + // depth extension tiles. The order of appearance of the tiles in the bitstream is left to the + // encoder's choice, and decoding as many as possible of each category in parallel is beneficial + // for incremental decoding, as pixel rows need all channels to be decoded before being + // accessible to the user. + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + AVIF_CHECKRES(avifDecoderDecodeTiles(decoder, nextImageIndex, &decoder->data->tileInfos[c])); + } + + if (!avifDecoderDataFrameFullyDecoded(decoder->data)) { + AVIF_ASSERT_OR_RETURN(decoder->allowIncremental); + // The image is not completely decoded. There should be no error unrelated to missing bytes, + // and at least some missing bytes. + avifResult firstNonOkResult = AVIF_RESULT_OK; + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + AVIF_ASSERT_OR_RETURN(prepareTileResult[c] == AVIF_RESULT_OK || prepareTileResult[c] == AVIF_RESULT_WAITING_ON_IO); + if (firstNonOkResult == AVIF_RESULT_OK) { + firstNonOkResult = prepareTileResult[c]; + } + } + AVIF_ASSERT_OR_RETURN(firstNonOkResult != AVIF_RESULT_OK); + // Return the "not enough bytes" status now instead of moving on to the next frame. + return AVIF_RESULT_WAITING_ON_IO; + } + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + AVIF_ASSERT_OR_RETURN(prepareTileResult[c] == AVIF_RESULT_OK); + } + + // If decoder->data->tileInfos[AVIF_ITEM_COLOR].tileCount == 0, it means + // decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA was equal to 0. + // Only apply Sample Transforms if there is a color item to apply it onto. + if (decoder->data->tileInfos[AVIF_ITEM_COLOR].tileCount != 0 && decoder->data->meta->sampleTransformExpression.count > 0) { + AVIF_CHECKRES(avifDecoderApplySampleTransform(decoder, decoder->image)); + } + + // Only advance decoder->imageIndex once the image is completely decoded, so that + // avifDecoderNthImage(decoder, decoder->imageIndex + 1) is equivalent to avifDecoderNextImage(decoder) + // if the previous call to avifDecoderNextImage() returned AVIF_RESULT_WAITING_ON_IO. + decoder->imageIndex = (int)nextImageIndex; + // The decoded tile counts will be reset to 0 the next time avifDecoderNextImage() is called, + // for avifDecoderDecodedRowCount() to work until then. + if (decoder->data->sourceSampleTable) { + // Decoding from a track! Provide timing information. + + avifResult timingResult = avifDecoderNthImageTiming(decoder, decoder->imageIndex, &decoder->imageTiming); + if (timingResult != AVIF_RESULT_OK) { + return timingResult; + } + } + return AVIF_RESULT_OK; +} + +avifResult avifDecoderNthImageTiming(const avifDecoder * decoder, uint32_t frameIndex, avifImageTiming * outTiming) +{ + if (!decoder->data) { + // Nothing has been parsed yet + return AVIF_RESULT_NO_CONTENT; + } + + if ((frameIndex > INT_MAX) || ((int)frameIndex >= decoder->imageCount)) { + // Impossible index + return AVIF_RESULT_NO_IMAGES_REMAINING; + } + + if (!decoder->data->sourceSampleTable) { + // There isn't any real timing associated with this decode, so + // just hand back the defaults chosen in avifDecoderReset(). + *outTiming = decoder->imageTiming; + return AVIF_RESULT_OK; + } + + outTiming->timescale = decoder->timescale; + outTiming->ptsInTimescales = 0; + for (uint32_t imageIndex = 0; imageIndex < frameIndex; ++imageIndex) { + outTiming->ptsInTimescales += avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, imageIndex); + } + outTiming->durationInTimescales = avifSampleTableGetImageDelta(decoder->data->sourceSampleTable, frameIndex); + + if (outTiming->timescale > 0) { + outTiming->pts = (double)outTiming->ptsInTimescales / (double)outTiming->timescale; + outTiming->duration = (double)outTiming->durationInTimescales / (double)outTiming->timescale; + } else { + outTiming->pts = 0.0; + outTiming->duration = 0.0; + } + return AVIF_RESULT_OK; +} + +avifResult avifDecoderNthImage(avifDecoder * decoder, uint32_t frameIndex) +{ + avifDiagnosticsClearError(&decoder->diag); + + if (!decoder->data) { + // Nothing has been parsed yet + return AVIF_RESULT_NO_CONTENT; + } + + if ((frameIndex > INT_MAX) || ((int)frameIndex >= decoder->imageCount)) { + // Impossible index + return AVIF_RESULT_NO_IMAGES_REMAINING; + } + + int requestedIndex = (int)frameIndex; + if (requestedIndex == (decoder->imageIndex + 1)) { + // It's just the next image (already partially decoded or not at all), nothing special here + return avifDecoderNextImage(decoder); + } + + if (requestedIndex == decoder->imageIndex) { + if (avifDecoderDataFrameFullyDecoded(decoder->data)) { + // The current fully decoded image (decoder->imageIndex) is requested, nothing to do + return AVIF_RESULT_OK; + } + // The next image (decoder->imageIndex + 1) is partially decoded but + // the previous image (decoder->imageIndex) is requested. + // Fall through to resetting the decoder data and start decoding from + // the nearest key frame. + } + + int nearestKeyFrame = (int)avifDecoderNearestKeyframe(decoder, frameIndex); + if ((nearestKeyFrame > (decoder->imageIndex + 1)) || (requestedIndex <= decoder->imageIndex)) { + // If we get here, we need to start decoding from the nearest key frame. + // So discard the unused decoder state and its previous frames. This + // will force the setup of new AV1 decoder (avifCodec) instances in + // avifDecoderNextImage(). + decoder->imageIndex = nearestKeyFrame - 1; // prepare to read nearest keyframe + avifDecoderDataResetCodec(decoder->data); + } + for (;;) { + avifResult result = avifDecoderNextImage(decoder); + if (result != AVIF_RESULT_OK) { + return result; + } + + if (requestedIndex == decoder->imageIndex) { + break; + } + } + return AVIF_RESULT_OK; +} + +avifBool avifDecoderIsKeyframe(const avifDecoder * decoder, uint32_t frameIndex) +{ + if (!decoder->data || (decoder->data->tiles.count == 0)) { + // Nothing has been parsed yet + return AVIF_FALSE; + } + + // *All* tiles for the requested frameIndex must be keyframes in order for + // avifDecoderIsKeyframe() to return true, otherwise we may seek to a frame in which the color + // planes are a keyframe but the alpha plane isn't a keyframe, which will cause an alpha plane + // decode failure. + for (unsigned int i = 0; i < decoder->data->tiles.count; ++i) { + const avifTile * tile = &decoder->data->tiles.tile[i]; + if ((frameIndex >= tile->input->samples.count) || !tile->input->samples.sample[frameIndex].sync) { + return AVIF_FALSE; + } + } + return AVIF_TRUE; +} + +uint32_t avifDecoderNearestKeyframe(const avifDecoder * decoder, uint32_t frameIndex) +{ + if (!decoder->data) { + // Nothing has been parsed yet + return 0; + } + + for (; frameIndex != 0; --frameIndex) { + if (avifDecoderIsKeyframe(decoder, frameIndex)) { + break; + } + } + return frameIndex; +} + +// Returns the number of available rows in decoder->image given a color or alpha subimage. +static uint32_t avifGetDecodedRowCount(const avifDecoder * decoder, const avifTileInfo * info, const avifImage * image) +{ + if (info->decodedTileCount == info->tileCount) { + return image->height; + } + if (info->decodedTileCount == 0) { + return 0; + } + + if (decoder->data->meta->sampleTransformExpression.count > 0) { + // TODO(yguyon): Support incremental Sample Transforms + return 0; + } + + if ((info->grid.rows > 0) && (info->grid.columns > 0)) { + // Grid of AVIF tiles (not to be confused with AV1 tiles). + const uint32_t tileHeight = decoder->data->tiles.tile[info->firstTileIndex].height; + return AVIF_MIN((info->decodedTileCount / info->grid.columns) * tileHeight, image->height); + } else { + // Non-grid image. + return image->height; + } +} + +uint32_t avifDecoderDecodedRowCount(const avifDecoder * decoder) +{ + if (decoder->data->tileInfos[AVIF_ITEM_COLOR].tileCount == 0) { + // decoder->imageContentToDecode & AVIF_IMAGE_CONTENT_COLOR_AND_ALPHA + // was likely 0 when avifDecoderNextImage() was called. + // avifDecoderDecodedRowCount() only describes decoder->image->yuvPlanes[0]. + // There is no available luma plane, so return 0 decoded rows. + return 0; + } + + uint32_t minRowCount = decoder->image->height; + for (int c = 0; c < AVIF_ITEM_CATEGORY_COUNT; ++c) { + if (c == AVIF_ITEM_GAIN_MAP) { + const avifImage * const gainMap = decoder->image->gainMap ? decoder->image->gainMap->image : NULL; + if (gainMap != NULL && gainMap->height != 0 && decoder->data->tileInfos[AVIF_ITEM_GAIN_MAP].tileCount != 0) { + uint32_t gainMapRowCount = avifGetDecodedRowCount(decoder, &decoder->data->tileInfos[AVIF_ITEM_GAIN_MAP], gainMap); + if (gainMap->height != decoder->image->height) { + const uint32_t scaledGainMapRowCount = + (uint32_t)floorf((float)gainMapRowCount / gainMap->height * decoder->image->height); + // Make sure it matches the formula described in the comment of avifDecoderDecodedRowCount() in avif.h. + AVIF_CHECKERR((uint32_t)lround((double)scaledGainMapRowCount / decoder->image->height * + decoder->image->gainMap->image->height) <= gainMapRowCount, + 0); + gainMapRowCount = scaledGainMapRowCount; + } + minRowCount = AVIF_MIN(minRowCount, gainMapRowCount); + } + continue; + } + const uint32_t rowCount = avifGetDecodedRowCount(decoder, &decoder->data->tileInfos[c], decoder->image); + minRowCount = AVIF_MIN(minRowCount, rowCount); + } + return minRowCount; +} + +avifResult avifDecoderRead(avifDecoder * decoder, avifImage * image) +{ + avifResult result = avifDecoderParse(decoder); + if (result != AVIF_RESULT_OK) { + return result; + } + result = avifDecoderNextImage(decoder); + if (result != AVIF_RESULT_OK) { + return result; + } + // If decoder->image->imageOwnsYUVPlanes is true and decoder->image is not used after this call, + // the ownership of the planes in decoder->image could be transferred here instead of copied. + // However most codec_*.c implementations allocate the output buffer themselves and return a + // view, unless some postprocessing is applied (container-level grid reconstruction for + // example), so the first condition rarely holds. + // The second condition does not hold either: it is not required by the documentation in avif.h. + return avifImageCopy(image, decoder->image, AVIF_PLANES_ALL); +} + +avifResult avifDecoderReadMemory(avifDecoder * decoder, avifImage * image, const uint8_t * data, size_t size) +{ + avifDiagnosticsClearError(&decoder->diag); + avifResult result = avifDecoderSetIOMemory(decoder, data, size); + if (result != AVIF_RESULT_OK) { + return result; + } + return avifDecoderRead(decoder, image); +} + +avifResult avifDecoderReadFile(avifDecoder * decoder, avifImage * image, const char * filename) +{ + avifDiagnosticsClearError(&decoder->diag); + avifResult result = avifDecoderSetIOFile(decoder, filename); + if (result != AVIF_RESULT_OK) { + return result; + } + return avifDecoderRead(decoder, image); +} diff --git a/3rdparty/libavif/src/reformat.c b/3rdparty/libavif/src/reformat.c new file mode 100644 index 0000000..aff25da --- /dev/null +++ b/3rdparty/libavif/src/reformat.c @@ -0,0 +1,1937 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include +#include + +#if defined(_WIN32) +#include +#include +#else +#include +#endif + +static void * avifMemset16(void * dest, int val, size_t count) +{ + uint16_t * dest16 = (uint16_t *)dest; + for (size_t i = 0; i < count; i++) + *dest16++ = (uint16_t)val; + return dest; +} + +struct YUVBlock +{ + float y; + float u; + float v; +}; + +avifBool avifGetRGBColorSpaceInfo(const avifRGBImage * rgb, avifRGBColorSpaceInfo * info) +{ + AVIF_CHECK(rgb->depth == 8 || rgb->depth == 10 || rgb->depth == 12 || rgb->depth == 16); + if (rgb->isFloat) { + AVIF_CHECK(rgb->depth == 16); + } + if (rgb->format == AVIF_RGB_FORMAT_RGB_565) { + AVIF_CHECK(rgb->depth == 8); + } + // Cast to silence "comparison of unsigned expression is always true" warning. + AVIF_CHECK((int)rgb->format >= AVIF_RGB_FORMAT_RGB && rgb->format < AVIF_RGB_FORMAT_COUNT); + + info->channelBytes = (rgb->depth > 8) ? 2 : 1; + info->pixelBytes = avifRGBImagePixelSize(rgb); + + info->offsetBytesR = 0; + info->offsetBytesG = 0; + info->offsetBytesB = 0; + info->offsetBytesA = 0; + info->offsetBytesGray = 0; + + switch (rgb->format) { + case AVIF_RGB_FORMAT_RGB: + info->offsetBytesR = info->channelBytes * 0; + info->offsetBytesG = info->channelBytes * 1; + info->offsetBytesB = info->channelBytes * 2; + break; + case AVIF_RGB_FORMAT_RGBA: + info->offsetBytesR = info->channelBytes * 0; + info->offsetBytesG = info->channelBytes * 1; + info->offsetBytesB = info->channelBytes * 2; + info->offsetBytesA = info->channelBytes * 3; + break; + case AVIF_RGB_FORMAT_ARGB: + info->offsetBytesA = info->channelBytes * 0; + info->offsetBytesR = info->channelBytes * 1; + info->offsetBytesG = info->channelBytes * 2; + info->offsetBytesB = info->channelBytes * 3; + break; + case AVIF_RGB_FORMAT_BGR: + info->offsetBytesB = info->channelBytes * 0; + info->offsetBytesG = info->channelBytes * 1; + info->offsetBytesR = info->channelBytes * 2; + break; + case AVIF_RGB_FORMAT_BGRA: + info->offsetBytesB = info->channelBytes * 0; + info->offsetBytesG = info->channelBytes * 1; + info->offsetBytesR = info->channelBytes * 2; + info->offsetBytesA = info->channelBytes * 3; + break; + case AVIF_RGB_FORMAT_ABGR: + info->offsetBytesA = info->channelBytes * 0; + info->offsetBytesB = info->channelBytes * 1; + info->offsetBytesG = info->channelBytes * 2; + info->offsetBytesR = info->channelBytes * 3; + break; + case AVIF_RGB_FORMAT_RGB_565: + // Since RGB_565 consists of two bytes per RGB pixel, we simply use + // the pointer to the red channel to populate the entire pixel value + // as a uint16_t. As a result only offsetBytesR is used and the + // other offsets are unused. + info->offsetBytesR = 0; + info->offsetBytesG = 0; + info->offsetBytesB = 0; + break; + case AVIF_RGB_FORMAT_GRAY: + info->offsetBytesGray = info->channelBytes * 0; + break; + case AVIF_RGB_FORMAT_GRAYA: + info->offsetBytesGray = info->channelBytes * 0; + info->offsetBytesA = info->channelBytes * 1; + break; + case AVIF_RGB_FORMAT_AGRAY: + info->offsetBytesA = info->channelBytes * 0; + info->offsetBytesGray = info->channelBytes * 1; + break; + + case AVIF_RGB_FORMAT_COUNT: + return AVIF_FALSE; + } + + info->maxChannel = (1 << rgb->depth) - 1; + info->maxChannelF = (float)info->maxChannel; + + return AVIF_TRUE; +} + +avifBool avifGetYUVColorSpaceInfo(const avifImage * image, avifYUVColorSpaceInfo * info) +{ + AVIF_CHECK(image->depth == 8 || image->depth == 10 || image->depth == 12 || image->depth == 16); + AVIF_CHECK(image->yuvFormat >= AVIF_PIXEL_FORMAT_YUV444 && image->yuvFormat < AVIF_PIXEL_FORMAT_COUNT); + AVIF_CHECK(image->yuvRange == AVIF_RANGE_LIMITED || image->yuvRange == AVIF_RANGE_FULL); + + // These matrix coefficients values are currently unsupported. Revise this list as more support is added. + // + // YCgCo performs limited-full range adjustment on R,G,B but the current implementation performs range adjustment + // on Y,U,V. So YCgCo with limited range is unsupported. + if ((image->matrixCoefficients == 3 /* CICP reserved */) || + ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO || image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO_RE || + image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO_RO) && + (image->yuvRange == AVIF_RANGE_LIMITED)) || + (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_BT2020_CL) || + (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_SMPTE2085) || + (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL) || + (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_ICTCP) || (image->matrixCoefficients >= AVIF_MATRIX_COEFFICIENTS_LAST)) { + return AVIF_FALSE; + } + + // Removing 400 here would break backward behavior but would respect the spec. + if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) && (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV444) && + (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400)) { + return AVIF_FALSE; + } + avifGetPixelFormatInfo(image->yuvFormat, &info->formatInfo); + avifCalcYUVCoefficients(image, &info->kr, &info->kg, &info->kb); + + info->channelBytes = (image->depth > 8) ? 2 : 1; + + info->depth = image->depth; + info->range = image->yuvRange; + info->maxChannel = (1 << image->depth) - 1; + info->biasY = (info->range == AVIF_RANGE_LIMITED) ? (float)(16 << (info->depth - 8)) : 0.0f; + info->biasUV = (float)(1 << (info->depth - 1)); + info->rangeY = (float)((info->range == AVIF_RANGE_LIMITED) ? (219 << (info->depth - 8)) : info->maxChannel); + info->rangeUV = (float)((info->range == AVIF_RANGE_LIMITED) ? (224 << (info->depth - 8)) : info->maxChannel); + + return AVIF_TRUE; +} + +static avifBool avifPrepareReformatState(const avifImage * image, const avifRGBImage * rgb, avifReformatState * state) +{ + const avifBool useYCgCoRe = (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO_RE); + const avifBool useYCgCoRo = (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO_RO); + if (useYCgCoRe || useYCgCoRo) { + const int bitOffset = (useYCgCoRe) ? 2 : 1; + if (image->depth - bitOffset != rgb->depth) { + return AVIF_FALSE; + } + } + + AVIF_CHECK(avifGetRGBColorSpaceInfo(rgb, &state->rgb)); + AVIF_CHECK(avifGetYUVColorSpaceInfo(image, &state->yuv)); + + state->yuv.mode = AVIF_REFORMAT_MODE_YUV_COEFFICIENTS; + + if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY) { + state->yuv.mode = AVIF_REFORMAT_MODE_IDENTITY; + } else if (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_YCGCO) { + state->yuv.mode = AVIF_REFORMAT_MODE_YCGCO; + } else if (useYCgCoRe) { + state->yuv.mode = AVIF_REFORMAT_MODE_YCGCO_RE; + } else if (useYCgCoRo) { + state->yuv.mode = AVIF_REFORMAT_MODE_YCGCO_RO; + } + + if (state->yuv.mode != AVIF_REFORMAT_MODE_YUV_COEFFICIENTS) { + state->yuv.kr = 0.0f; + state->yuv.kg = 0.0f; + state->yuv.kb = 0.0f; + } + + return AVIF_TRUE; +} + +// Formulas 20-31 from https://www.itu.int/rec/T-REC-H.273-201612-S +static int avifYUVColorSpaceInfoYToUNorm(avifYUVColorSpaceInfo * info, float v) +{ + int unorm = (int)avifRoundf(v * info->rangeY + info->biasY); + return AVIF_CLAMP(unorm, 0, info->maxChannel); +} + +static int avifYUVColorSpaceInfoUVToUNorm(avifYUVColorSpaceInfo * info, float v) +{ + int unorm; + + // YCgCo performs limited-full range adjustment on R,G,B but the current implementation performs range adjustment + // on Y,U,V. So YCgCo with limited range is unsupported. + assert((info->mode != AVIF_REFORMAT_MODE_YCGCO && info->mode != AVIF_REFORMAT_MODE_YCGCO_RE && info->mode != AVIF_REFORMAT_MODE_YCGCO_RO) || + (info->range == AVIF_RANGE_FULL)); + + if (info->mode == AVIF_REFORMAT_MODE_IDENTITY) { + unorm = (int)avifRoundf(v * info->rangeY + info->biasY); + } else { + unorm = (int)avifRoundf(v * info->rangeUV + info->biasUV); + } + + return AVIF_CLAMP(unorm, 0, info->maxChannel); +} + +avifResult avifImageRGBToYUV(avifImage * image, const avifRGBImage * rgb) +{ + if (!rgb->pixels || rgb->format == AVIF_RGB_FORMAT_RGB_565) { + return AVIF_RESULT_REFORMAT_FAILED; + } + + avifReformatState state; + if (!avifPrepareReformatState(image, rgb, &state)) { + return AVIF_RESULT_REFORMAT_FAILED; + } + + if (rgb->isFloat) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + const avifBool hasAlpha = avifRGBFormatHasAlpha(rgb->format) && !rgb->ignoreAlpha; + avifResult allocationResult = avifImageAllocatePlanes(image, hasAlpha ? AVIF_PLANES_ALL : AVIF_PLANES_YUV); + if (allocationResult != AVIF_RESULT_OK) { + return allocationResult; + } + + avifAlphaMultiplyMode alphaMode = AVIF_ALPHA_MULTIPLY_MODE_NO_OP; + if (hasAlpha) { + if (!rgb->alphaPremultiplied && image->alphaPremultiplied) { + alphaMode = AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY; + } else if (rgb->alphaPremultiplied && !image->alphaPremultiplied) { + alphaMode = AVIF_ALPHA_MULTIPLY_MODE_UNMULTIPLY; + } + } + + const avifBool isGray = avifRGBFormatIsGray(rgb->format); + avifBool converted = AVIF_FALSE; + + // Try converting with libsharpyuv. + if (!isGray) { + if ((rgb->chromaDownsampling == AVIF_CHROMA_DOWNSAMPLING_SHARP_YUV) && (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420)) { + const avifResult libSharpYUVResult = avifImageRGBToYUVLibSharpYUV(image, rgb, &state); + if (libSharpYUVResult != AVIF_RESULT_OK) { + // Return the error if sharpyuv was requested but failed for any reason, including libsharpyuv not being available. + return libSharpYUVResult; + } + converted = AVIF_TRUE; + } + + if (!converted && !rgb->avoidLibYUV && (alphaMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP)) { + avifResult libyuvResult = avifImageRGBToYUVLibYUV(image, rgb); + if (libyuvResult == AVIF_RESULT_OK) { + converted = AVIF_TRUE; + } else if (libyuvResult != AVIF_RESULT_NOT_IMPLEMENTED) { + return libyuvResult; + } + } + } + + if (!converted && !isGray) { + const float kr = state.yuv.kr; + const float kg = state.yuv.kg; + const float kb = state.yuv.kb; + + struct YUVBlock yuvBlock[2][2]; + float rgbPixel[3]; + const uint32_t rgbPixelBytes = state.rgb.pixelBytes; + const uint32_t offsetBytesR = state.rgb.offsetBytesR; + const uint32_t offsetBytesG = state.rgb.offsetBytesG; + const uint32_t offsetBytesB = state.rgb.offsetBytesB; + const uint32_t offsetBytesA = state.rgb.offsetBytesA; + const size_t rgbRowBytes = rgb->rowBytes; + const float rgbMaxChannelF = state.rgb.maxChannelF; + uint8_t * yPlane = image->yuvPlanes[AVIF_CHAN_Y]; + uint8_t * uPlane = image->yuvPlanes[AVIF_CHAN_U]; + uint8_t * vPlane = image->yuvPlanes[AVIF_CHAN_V]; + const size_t yRowBytes = image->yuvRowBytes[AVIF_CHAN_Y]; + const size_t uRowBytes = image->yuvRowBytes[AVIF_CHAN_U]; + const size_t vRowBytes = image->yuvRowBytes[AVIF_CHAN_V]; + for (size_t outerJ = 0; outerJ < image->height; outerJ += 2) { + for (size_t outerI = 0; outerI < image->width; outerI += 2) { + uint32_t blockW = 2, blockH = 2; + if ((outerI + 1) >= image->width) { + blockW = 1; + } + if ((outerJ + 1) >= image->height) { + blockH = 1; + } + + // Convert an entire 2x2 block to YUV, and populate any fully sampled channels as we go + for (uint32_t bJ = 0; bJ < blockH; ++bJ) { + for (uint32_t bI = 0; bI < blockW; ++bI) { + const size_t i = outerI + bI; + const size_t j = outerJ + bJ; + + // Unpack RGB into normalized float + if (state.rgb.channelBytes > 1) { + rgbPixel[0] = *((uint16_t *)(&rgb->pixels[offsetBytesR + (i * rgbPixelBytes) + (j * rgbRowBytes)])) / + rgbMaxChannelF; + rgbPixel[1] = *((uint16_t *)(&rgb->pixels[offsetBytesG + (i * rgbPixelBytes) + (j * rgbRowBytes)])) / + rgbMaxChannelF; + rgbPixel[2] = *((uint16_t *)(&rgb->pixels[offsetBytesB + (i * rgbPixelBytes) + (j * rgbRowBytes)])) / + rgbMaxChannelF; + } else { + rgbPixel[0] = rgb->pixels[offsetBytesR + (i * rgbPixelBytes) + (j * rgbRowBytes)] / rgbMaxChannelF; + rgbPixel[1] = rgb->pixels[offsetBytesG + (i * rgbPixelBytes) + (j * rgbRowBytes)] / rgbMaxChannelF; + rgbPixel[2] = rgb->pixels[offsetBytesB + (i * rgbPixelBytes) + (j * rgbRowBytes)] / rgbMaxChannelF; + } + + if (alphaMode != AVIF_ALPHA_MULTIPLY_MODE_NO_OP) { + float a; + if (state.rgb.channelBytes > 1) { + a = *((uint16_t *)(&rgb->pixels[offsetBytesA + (i * rgbPixelBytes) + (j * rgbRowBytes)])) / rgbMaxChannelF; + } else { + a = rgb->pixels[offsetBytesA + (i * rgbPixelBytes) + (j * rgbRowBytes)] / rgbMaxChannelF; + } + + if (alphaMode == AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY) { + if (a == 0) { + rgbPixel[0] = 0; + rgbPixel[1] = 0; + rgbPixel[2] = 0; + } else if (a < 1.0f) { + rgbPixel[0] *= a; + rgbPixel[1] *= a; + rgbPixel[2] *= a; + } + } else { + // alphaMode == AVIF_ALPHA_MULTIPLY_MODE_UNMULTIPLY + if (a == 0) { + rgbPixel[0] = 0; + rgbPixel[1] = 0; + rgbPixel[2] = 0; + } else if (a < 1.0f) { + rgbPixel[0] /= a; + rgbPixel[1] /= a; + rgbPixel[2] /= a; + rgbPixel[0] = AVIF_MIN(rgbPixel[0], 1.0f); + rgbPixel[1] = AVIF_MIN(rgbPixel[1], 1.0f); + rgbPixel[2] = AVIF_MIN(rgbPixel[2], 1.0f); + } + } + } + + // RGB -> YUV conversion + if (state.yuv.mode == AVIF_REFORMAT_MODE_IDENTITY) { + // Formulas 41,42,43 from https://www.itu.int/rec/T-REC-H.273-201612-S + yuvBlock[bI][bJ].y = rgbPixel[1]; // G + yuvBlock[bI][bJ].u = rgbPixel[2]; // B + yuvBlock[bI][bJ].v = rgbPixel[0]; // R + } else if (state.yuv.mode == AVIF_REFORMAT_MODE_YCGCO) { + // Formulas 44,45,46 from https://www.itu.int/rec/T-REC-H.273-201612-S + yuvBlock[bI][bJ].y = 0.5f * rgbPixel[1] + 0.25f * (rgbPixel[0] + rgbPixel[2]); + yuvBlock[bI][bJ].u = 0.5f * rgbPixel[1] - 0.25f * (rgbPixel[0] + rgbPixel[2]); + yuvBlock[bI][bJ].v = 0.5f * (rgbPixel[0] - rgbPixel[2]); + } else if (state.yuv.mode == AVIF_REFORMAT_MODE_YCGCO_RE || state.yuv.mode == AVIF_REFORMAT_MODE_YCGCO_RO) { + // Formulas 58,59,60,61 from https://www.itu.int/rec/T-REC-H.273-202407-P + const int R = (int)avifRoundf(AVIF_CLAMP(rgbPixel[0] * rgbMaxChannelF, 0.0f, rgbMaxChannelF)); + const int G = (int)avifRoundf(AVIF_CLAMP(rgbPixel[1] * rgbMaxChannelF, 0.0f, rgbMaxChannelF)); + const int B = (int)avifRoundf(AVIF_CLAMP(rgbPixel[2] * rgbMaxChannelF, 0.0f, rgbMaxChannelF)); + const int Co = R - B; + const int t = B + (Co >> 1); + const int Cg = G - t; + yuvBlock[bI][bJ].y = (t + (Cg >> 1)) / state.yuv.rangeY; + yuvBlock[bI][bJ].u = Cg / state.yuv.rangeUV; + yuvBlock[bI][bJ].v = Co / state.yuv.rangeUV; + } else { + float Y = (kr * rgbPixel[0]) + (kg * rgbPixel[1]) + (kb * rgbPixel[2]); + yuvBlock[bI][bJ].y = Y; + yuvBlock[bI][bJ].u = (rgbPixel[2] - Y) / (2 * (1 - kb)); + yuvBlock[bI][bJ].v = (rgbPixel[0] - Y) / (2 * (1 - kr)); + } + + if (state.yuv.channelBytes > 1) { + uint16_t * pY = (uint16_t *)&yPlane[(i * 2) + (j * yRowBytes)]; + *pY = (uint16_t)avifYUVColorSpaceInfoYToUNorm(&state.yuv, yuvBlock[bI][bJ].y); + if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) { + // YUV444, full chroma + uint16_t * pU = (uint16_t *)&uPlane[(i * 2) + (j * uRowBytes)]; + *pU = (uint16_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, yuvBlock[bI][bJ].u); + uint16_t * pV = (uint16_t *)&vPlane[(i * 2) + (j * vRowBytes)]; + *pV = (uint16_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, yuvBlock[bI][bJ].v); + } + } else { + yPlane[i + (j * yRowBytes)] = (uint8_t)avifYUVColorSpaceInfoYToUNorm(&state.yuv, yuvBlock[bI][bJ].y); + if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) { + // YUV444, full chroma + uPlane[i + (j * uRowBytes)] = (uint8_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, yuvBlock[bI][bJ].u); + vPlane[i + (j * vRowBytes)] = (uint8_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, yuvBlock[bI][bJ].v); + } + } + } + } + + // Populate any subsampled channels with averages from the 2x2 block + if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { + // Do nothing on chroma planes. + } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) { + // YUV420, average 4 samples (2x2) + + float sumU = 0.0f; + float sumV = 0.0f; + for (uint32_t bJ = 0; bJ < blockH; ++bJ) { + for (uint32_t bI = 0; bI < blockW; ++bI) { + sumU += yuvBlock[bI][bJ].u; + sumV += yuvBlock[bI][bJ].v; + } + } + float totalSamples = (float)(blockW * blockH); + float avgU = sumU / totalSamples; + float avgV = sumV / totalSamples; + + const int chromaShiftX = 1; + const int chromaShiftY = 1; + size_t uvI = outerI >> chromaShiftX; + size_t uvJ = outerJ >> chromaShiftY; + if (state.yuv.channelBytes > 1) { + uint16_t * pU = (uint16_t *)&uPlane[(uvI * 2) + (uvJ * uRowBytes)]; + *pU = (uint16_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, avgU); + uint16_t * pV = (uint16_t *)&vPlane[(uvI * 2) + (uvJ * vRowBytes)]; + *pV = (uint16_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, avgV); + } else { + uPlane[uvI + (uvJ * uRowBytes)] = (uint8_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, avgU); + vPlane[uvI + (uvJ * vRowBytes)] = (uint8_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, avgV); + } + } else if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422) { + // YUV422, average 2 samples (1x2), twice + + for (uint32_t bJ = 0; bJ < blockH; ++bJ) { + float sumU = 0.0f; + float sumV = 0.0f; + for (uint32_t bI = 0; bI < blockW; ++bI) { + sumU += yuvBlock[bI][bJ].u; + sumV += yuvBlock[bI][bJ].v; + } + float totalSamples = (float)blockW; + float avgU = sumU / totalSamples; + float avgV = sumV / totalSamples; + + const int chromaShiftX = 1; + size_t uvI = outerI >> chromaShiftX; + size_t uvJ = outerJ + bJ; + if (state.yuv.channelBytes > 1) { + uint16_t * pU = (uint16_t *)&uPlane[(uvI * 2) + (uvJ * uRowBytes)]; + *pU = (uint16_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, avgU); + uint16_t * pV = (uint16_t *)&vPlane[(uvI * 2) + (uvJ * vRowBytes)]; + *pV = (uint16_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, avgV); + } else { + uPlane[uvI + (uvJ * uRowBytes)] = (uint8_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, avgU); + vPlane[uvI + (uvJ * vRowBytes)] = (uint8_t)avifYUVColorSpaceInfoUVToUNorm(&state.yuv, avgV); + } + } + } + } + } + } else if (!converted && isGray) { + const uint32_t grayPixelBytes = state.rgb.pixelBytes; + const uint32_t offsetBytesGray = state.rgb.offsetBytesGray; + const uint32_t offsetBytesA = state.rgb.offsetBytesA; + const size_t grayRowBytes = rgb->rowBytes; + const float grayMaxChannelF = state.rgb.maxChannelF; + uint8_t * yPlane = image->yuvPlanes[AVIF_CHAN_Y]; + const size_t yRowBytes = image->yuvRowBytes[AVIF_CHAN_Y]; + for (size_t j = 0; j < image->height; ++j) { + for (size_t i = 0; i < image->width; ++i) { + float g; + if (state.rgb.channelBytes > 1) { + g = *(uint16_t *)&rgb->pixels[offsetBytesGray + i * grayPixelBytes + (j * grayRowBytes)] / grayMaxChannelF; + } else { + g = rgb->pixels[offsetBytesGray + i * grayPixelBytes + (j * grayRowBytes)] / grayMaxChannelF; + } + if (alphaMode != AVIF_ALPHA_MULTIPLY_MODE_NO_OP) { + float a; + if (state.rgb.channelBytes > 1) { + a = *((uint16_t *)(&rgb->pixels[offsetBytesA + (i * grayPixelBytes) + (j * grayRowBytes)])) / grayMaxChannelF; + } else { + a = rgb->pixels[offsetBytesA + (i * grayPixelBytes) + (j * grayRowBytes)] / grayMaxChannelF; + } + + if (alphaMode == AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY) { + if (a == 0) { + g = 0; + } else if (a < 1.0f) { + g *= a; + } + } else { + // alphaMode == AVIF_ALPHA_MULTIPLY_MODE_UNMULTIPLY + if (a == 0) { + g = 0; + } else if (a < 1.0f) { + g /= a; + g = AVIF_MIN(g, 1.0f); + } + } + } + int gInt = avifYUVColorSpaceInfoYToUNorm(&state.yuv, g); + if (state.yuv.channelBytes > 1) { + uint16_t * pY = (uint16_t *)&yPlane[(i * 2) + j * yRowBytes]; + *pY = (uint16_t)gInt; + } else { + yPlane[i + (j * yRowBytes)] = (uint8_t)gInt; + } + } + } + // Set the chroma planes, if any, to the half value. + avifPixelFormatInfo info; + avifGetPixelFormatInfo(image->yuvFormat, &info); + const uint32_t shiftedH = (uint32_t)(((uint64_t)image->height + info.chromaShiftY) >> info.chromaShiftY); + const int half = 1 << (image->depth - 1); + if (image->yuvPlanes[AVIF_CHAN_U]) { + uint8_t * uPlane = image->yuvPlanes[AVIF_CHAN_U]; + const size_t uRowBytes = image->yuvRowBytes[AVIF_CHAN_U]; + if (state.yuv.channelBytes > 1) { + avifMemset16(uPlane, half, shiftedH * uRowBytes / 2); + } else { + memset(uPlane, half, shiftedH * uRowBytes); + } + } + if (image->yuvPlanes[AVIF_CHAN_V]) { + uint8_t * vPlane = image->yuvPlanes[AVIF_CHAN_V]; + const size_t vRowBytes = image->yuvRowBytes[AVIF_CHAN_V]; + if (state.yuv.channelBytes > 1) { + avifMemset16(vPlane, half, shiftedH * vRowBytes / 2); + } else { + memset(vPlane, half, shiftedH * vRowBytes); + } + } + } + + if (image->alphaPlane && image->alphaRowBytes) { + avifAlphaParams params; + + params.width = image->width; + params.height = image->height; + params.dstDepth = image->depth; + params.dstPlane = image->alphaPlane; + params.dstRowBytes = image->alphaRowBytes; + params.dstOffsetBytes = 0; + params.dstPixelBytes = state.yuv.channelBytes; + + if (avifRGBFormatHasAlpha(rgb->format) && !rgb->ignoreAlpha) { + params.srcDepth = rgb->depth; + params.srcPlane = rgb->pixels; + params.srcRowBytes = rgb->rowBytes; + params.srcOffsetBytes = state.rgb.offsetBytesA; + params.srcPixelBytes = state.rgb.pixelBytes; + + avifReformatAlpha(¶ms); + } else { + // libyuv does not fill alpha when converting from RGB to YUV so + // fill it regardless of the value of convertedWithLibYUV. + avifFillAlpha(¶ms); + } + } + return AVIF_RESULT_OK; +} + +// Allocates and fills look-up tables for going from YUV limited/full unorm -> full range RGB FP32. +// Review this when implementing YCgCo limited range support. +static avifBool avifCreateYUVToRGBLookUpTables(float ** unormFloatTableY, float ** unormFloatTableUV, uint32_t depth, const avifReformatState * state) +{ + const size_t cpCount = (size_t)1 << depth; + + assert(unormFloatTableY); + *unormFloatTableY = (float *)avifAlloc(cpCount * sizeof(float)); + AVIF_CHECK(*unormFloatTableY); + for (uint32_t cp = 0; cp < cpCount; ++cp) { + (*unormFloatTableY)[cp] = ((float)cp - state->yuv.biasY) / state->yuv.rangeY; + } + + if (unormFloatTableUV) { + if (state->yuv.mode == AVIF_REFORMAT_MODE_IDENTITY) { + // Just reuse the luma table since the chroma values are the same. + *unormFloatTableUV = *unormFloatTableY; + } else { + *unormFloatTableUV = (float *)avifAlloc(cpCount * sizeof(float)); + if (!*unormFloatTableUV) { + avifFree(*unormFloatTableY); + *unormFloatTableY = NULL; + return AVIF_FALSE; + } + for (uint32_t cp = 0; cp < cpCount; ++cp) { + (*unormFloatTableUV)[cp] = ((float)cp - state->yuv.biasUV) / state->yuv.rangeUV; + } + } + } + return AVIF_TRUE; +} + +// Frees look-up tables allocated with avifCreateYUVToRGBLookUpTables(). +static void avifFreeYUVToRGBLookUpTables(float ** unormFloatTableY, float ** unormFloatTableUV) +{ + if (unormFloatTableUV) { + if (*unormFloatTableUV != *unormFloatTableY) { + avifFree(*unormFloatTableUV); + } + *unormFloatTableUV = NULL; + } + + avifFree(*unormFloatTableY); + *unormFloatTableY = NULL; +} + +#define RGB565(R, G, B) ((uint16_t)(((B) >> 3) | (((G) >> 2) << 5) | (((R) >> 3) << 11))) + +static void avifStoreRGB8Pixel(avifRGBFormat format, uint8_t R, uint8_t G, uint8_t B, uint8_t * ptrR, uint8_t * ptrG, uint8_t * ptrB) +{ + if (format == AVIF_RGB_FORMAT_RGB_565) { + // References for RGB565 color conversion: + // * https://docs.microsoft.com/en-us/windows/win32/directshow/working-with-16-bit-rgb + // * https://chromium.googlesource.com/libyuv/libyuv/+/9892d70c965678381d2a70a1c9002d1cf136ee78/source/row_common.cc#2362 + *(uint16_t *)ptrR = RGB565(R, G, B); + return; + } + *ptrR = R; + *ptrG = G; + *ptrB = B; +} + +static void avifGetRGB565(const uint8_t * ptrR, uint8_t * R, uint8_t * G, uint8_t * B) +{ + // References for RGB565 color conversion: + // * https://docs.microsoft.com/en-us/windows/win32/directshow/working-with-16-bit-rgb + // * https://chromium.googlesource.com/libyuv/libyuv/+/331c361581896292fb46c8c6905e41262b7ca95f/source/row_common.cc#185 + const uint16_t rgb656 = ((const uint16_t *)ptrR)[0]; + const uint16_t r5 = (rgb656 & 0xF800) >> 11; + const uint16_t g6 = (rgb656 & 0x07E0) >> 5; + const uint16_t b5 = (rgb656 & 0x001F); + *R = (uint8_t)((r5 << 3) | (r5 >> 2)); + *G = (uint8_t)((g6 << 2) | (g6 >> 4)); + *B = (uint8_t)((b5 << 3) | (b5 >> 2)); +} + +// Note: This function handles alpha (un)multiply. +static avifResult avifImageYUVAnyToRGBAnySlow(const avifImage * image, + avifRGBImage * rgb, + const avifReformatState * state, + avifAlphaMultiplyMode alphaMultiplyMode) +{ + // Aliases for some state + const float kr = state->yuv.kr; + const float kg = state->yuv.kg; + const float kb = state->yuv.kb; + float * unormFloatTableY = NULL; + float * unormFloatTableUV = NULL; + AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY); + const uint32_t yuvChannelBytes = state->yuv.channelBytes; + const uint32_t rgbPixelBytes = state->rgb.pixelBytes; + + // Aliases for plane data + const uint8_t * yPlane = image->yuvPlanes[AVIF_CHAN_Y]; + const uint8_t * uPlane = image->yuvPlanes[AVIF_CHAN_U]; + const uint8_t * vPlane = image->yuvPlanes[AVIF_CHAN_V]; + const uint8_t * aPlane = image->alphaPlane; + const uint32_t yRowBytes = image->yuvRowBytes[AVIF_CHAN_Y]; + const uint32_t uRowBytes = image->yuvRowBytes[AVIF_CHAN_U]; + const uint32_t vRowBytes = image->yuvRowBytes[AVIF_CHAN_V]; + const uint32_t aRowBytes = image->alphaRowBytes; + + // Various observations and limits + const avifBool yuvHasColor = (uPlane && vPlane && (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400)); + const avifBool rgbHasColor = !avifRGBFormatIsGray(rgb->format); + const uint16_t yuvMaxChannel = (uint16_t)state->yuv.maxChannel; + const float rgbMaxChannelF = state->rgb.maxChannelF; + + // If toRGBAlphaMode is active (not no-op), assert that the alpha plane is present. The end of + // the avifPrepareReformatState() function should ensure this, but this assert makes it clear + // to clang's analyzer. + assert((alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP) || aPlane); + + for (uint32_t j = 0; j < image->height; ++j) { + // uvJ is used only when yuvHasColor is true. + const uint32_t uvJ = yuvHasColor ? (j >> state->yuv.formatInfo.chromaShiftY) : 0; + const uint8_t * ptrY8 = &yPlane[j * yRowBytes]; + const uint8_t * ptrU8 = uPlane ? &uPlane[(uvJ * uRowBytes)] : NULL; + const uint8_t * ptrV8 = vPlane ? &vPlane[(uvJ * vRowBytes)] : NULL; + const uint8_t * ptrA8 = aPlane ? &aPlane[j * aRowBytes] : NULL; + const uint16_t * ptrY16 = (const uint16_t *)ptrY8; + const uint16_t * ptrU16 = (const uint16_t *)ptrU8; + const uint16_t * ptrV16 = (const uint16_t *)ptrV8; + const uint16_t * ptrA16 = (const uint16_t *)ptrA8; + + uint8_t * ptrR = &rgb->pixels[state->rgb.offsetBytesR + ((size_t)j * rgb->rowBytes)]; + uint8_t * ptrG = &rgb->pixels[state->rgb.offsetBytesG + ((size_t)j * rgb->rowBytes)]; + uint8_t * ptrB = &rgb->pixels[state->rgb.offsetBytesB + ((size_t)j * rgb->rowBytes)]; + uint8_t * ptrGray = &rgb->pixels[state->rgb.offsetBytesGray + ((size_t)j * rgb->rowBytes)]; + + for (uint32_t i = 0; i < image->width; ++i) { + float Y, Cb = 0.5f, Cr = 0.5f; + + // Calculate Y + uint16_t unormY; + if (image->depth == 8) { + unormY = ptrY8[i]; + } else { + // clamp incoming data to protect against bad LUT lookups + unormY = AVIF_MIN(ptrY16[i], yuvMaxChannel); + } + Y = unormFloatTableY[unormY]; + + // Calculate Cb and Cr + if (yuvHasColor) { + const uint32_t uvI = i >> state->yuv.formatInfo.chromaShiftX; + if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) { + uint16_t unormU, unormV; + + if (image->depth == 8) { + unormU = ptrU8[uvI]; + unormV = ptrV8[uvI]; + } else { + // clamp incoming data to protect against bad LUT lookups + unormU = AVIF_MIN(ptrU16[uvI], yuvMaxChannel); + unormV = AVIF_MIN(ptrV16[uvI], yuvMaxChannel); + } + + Cb = unormFloatTableUV[unormU]; + Cr = unormFloatTableUV[unormV]; + } else { + // Upsample to 444: + // + // * * * * + // A B + // * 1 2 * + // + // * 3 4 * + // C D + // * * * * + // + // When converting from YUV420 to RGB, for any given "high-resolution" RGB + // coordinate (1,2,3,4,*), there are up to four "low-resolution" UV samples + // (A,B,C,D) that are "nearest" to the pixel. For RGB pixel #1, A is the closest + // UV sample, B and C are "adjacent" to it on the same row and column, and D is + // the diagonal. For RGB pixel 3, C is the closest UV sample, A and D are + // adjacent, and B is the diagonal. Sometimes the adjacent pixel on the same row + // is to the left or right, and sometimes the adjacent pixel on the same column + // is up or down. For any edge or corner, there might only be only one or two + // samples nearby, so they'll be duplicated. + // + // The following code attempts to find all four nearest UV samples and put them + // in the following unormU and unormV grid as follows: + // + // unorm[0][0] = closest ( weights: bilinear: 9/16, nearest: 1 ) + // unorm[1][0] = adjacent col ( weights: bilinear: 3/16, nearest: 0 ) + // unorm[0][1] = adjacent row ( weights: bilinear: 3/16, nearest: 0 ) + // unorm[1][1] = diagonal ( weights: bilinear: 1/16, nearest: 0 ) + // + // It then weights them according to the requested upsampling set in avifRGBImage. + + uint16_t unormU[2][2], unormV[2][2]; + + // How many bytes to add to a uint8_t pointer index to get to the adjacent (lesser) sample in a given direction + int uAdjCol, vAdjCol, uAdjRow, vAdjRow; + if ((i == 0) || ((i == (image->width - 1)) && ((i % 2) != 0))) { + uAdjCol = 0; + vAdjCol = 0; + } else { + if ((i % 2) != 0) { + uAdjCol = yuvChannelBytes; + vAdjCol = yuvChannelBytes; + } else { + uAdjCol = -1 * yuvChannelBytes; + vAdjCol = -1 * yuvChannelBytes; + } + } + + // For YUV422, uvJ will always be a fresh value (always corresponds to j), so + // we'll simply duplicate the sample as if we were on the top or bottom row and + // it'll behave as plain old linear (1D) upsampling, which is all we want. + if ((j == 0) || ((j == (image->height - 1)) && ((j % 2) != 0)) || (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422)) { + uAdjRow = 0; + vAdjRow = 0; + } else { + if ((j % 2) != 0) { + uAdjRow = (int)uRowBytes; + vAdjRow = (int)vRowBytes; + } else { + uAdjRow = -1 * (int)uRowBytes; + vAdjRow = -1 * (int)vRowBytes; + } + } + + if (image->depth == 8) { + unormU[0][0] = uPlane[(uvJ * uRowBytes) + (uvI * yuvChannelBytes)]; + unormV[0][0] = vPlane[(uvJ * vRowBytes) + (uvI * yuvChannelBytes)]; + unormU[1][0] = uPlane[(uvJ * uRowBytes) + (uvI * yuvChannelBytes) + uAdjCol]; + unormV[1][0] = vPlane[(uvJ * vRowBytes) + (uvI * yuvChannelBytes) + vAdjCol]; + unormU[0][1] = uPlane[(uvJ * uRowBytes) + (uvI * yuvChannelBytes) + uAdjRow]; + unormV[0][1] = vPlane[(uvJ * vRowBytes) + (uvI * yuvChannelBytes) + vAdjRow]; + unormU[1][1] = uPlane[(uvJ * uRowBytes) + (uvI * yuvChannelBytes) + uAdjCol + uAdjRow]; + unormV[1][1] = vPlane[(uvJ * vRowBytes) + (uvI * yuvChannelBytes) + vAdjCol + vAdjRow]; + } else { + unormU[0][0] = *((const uint16_t *)&uPlane[(uvJ * uRowBytes) + (uvI * yuvChannelBytes)]); + unormV[0][0] = *((const uint16_t *)&vPlane[(uvJ * vRowBytes) + (uvI * yuvChannelBytes)]); + unormU[1][0] = *((const uint16_t *)&uPlane[(uvJ * uRowBytes) + (uvI * yuvChannelBytes) + uAdjCol]); + unormV[1][0] = *((const uint16_t *)&vPlane[(uvJ * vRowBytes) + (uvI * yuvChannelBytes) + vAdjCol]); + unormU[0][1] = *((const uint16_t *)&uPlane[(uvJ * uRowBytes) + (uvI * yuvChannelBytes) + uAdjRow]); + unormV[0][1] = *((const uint16_t *)&vPlane[(uvJ * vRowBytes) + (uvI * yuvChannelBytes) + vAdjRow]); + unormU[1][1] = *((const uint16_t *)&uPlane[(uvJ * uRowBytes) + (uvI * yuvChannelBytes) + uAdjCol + uAdjRow]); + unormV[1][1] = *((const uint16_t *)&vPlane[(uvJ * vRowBytes) + (uvI * yuvChannelBytes) + vAdjCol + vAdjRow]); + + // clamp incoming data to protect against bad LUT lookups + for (int bJ = 0; bJ < 2; ++bJ) { + for (int bI = 0; bI < 2; ++bI) { + unormU[bI][bJ] = AVIF_MIN(unormU[bI][bJ], yuvMaxChannel); + unormV[bI][bJ] = AVIF_MIN(unormV[bI][bJ], yuvMaxChannel); + } + } + } + + if ((rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) || + (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST)) { + // Nearest neighbor; ignore all UVs but the closest one + Cb = unormFloatTableUV[unormU[0][0]]; + Cr = unormFloatTableUV[unormV[0][0]]; + } else { + // Bilinear filtering with weights + Cb = (unormFloatTableUV[unormU[0][0]] * (9.0f / 16.0f)) + (unormFloatTableUV[unormU[1][0]] * (3.0f / 16.0f)) + + (unormFloatTableUV[unormU[0][1]] * (3.0f / 16.0f)) + (unormFloatTableUV[unormU[1][1]] * (1.0f / 16.0f)); + Cr = (unormFloatTableUV[unormV[0][0]] * (9.0f / 16.0f)) + (unormFloatTableUV[unormV[1][0]] * (3.0f / 16.0f)) + + (unormFloatTableUV[unormV[0][1]] * (3.0f / 16.0f)) + (unormFloatTableUV[unormV[1][1]] * (1.0f / 16.0f)); + } + } + } + + float Rc = 0.0f, Gc = 0.0f, Bc = 0.0f, grayc = 0.0f; + if (rgbHasColor) { + float R, G, B; + if (yuvHasColor) { + if (state->yuv.mode == AVIF_REFORMAT_MODE_IDENTITY) { + // Identity (GBR): Formulas 41,42,43 from + // https://www.itu.int/rec/T-REC-H.273-201612-S + G = Y; + B = Cb; + R = Cr; + } else if (state->yuv.mode == AVIF_REFORMAT_MODE_YCGCO) { + // YCgCo: Formulas 47,48,49,50 from + // https://www.itu.int/rec/T-REC-H.273-201612-S + const float t = Y - Cb; + G = Y + Cb; + B = t - Cr; + R = t + Cr; + } else if ((state->yuv.mode == AVIF_REFORMAT_MODE_YCGCO_RE) || (state->yuv.mode == AVIF_REFORMAT_MODE_YCGCO_RO)) { + // YCgCoRe/YCgCoRo: Formulas 62,63,64,65 from + // https://www.itu.int/rec/T-REC-H.273-202407-P + const int YY = unormY; + const int Cg = (int)avifRoundf(Cb * yuvMaxChannel); + const int Co = (int)avifRoundf(Cr * yuvMaxChannel); + const int t = YY - (Cg >> 1); + G = (float)AVIF_CLAMP(t + Cg, 0, state->rgb.maxChannel); + B = (float)AVIF_CLAMP(t - (Co >> 1), 0, state->rgb.maxChannel); + R = (float)AVIF_CLAMP(B + Co, 0, state->rgb.maxChannel); + G /= rgbMaxChannelF; + B /= rgbMaxChannelF; + R /= rgbMaxChannelF; + } else { + // Normal YUV + R = Y + (2 * (1 - kr)) * Cr; + B = Y + (2 * (1 - kb)) * Cb; + G = Y - ((2 * ((kr * (1 - kr) * Cr) + (kb * (1 - kb) * Cb))) / kg); + } + } else { + // Monochrome: just populate all channels with luma (state->yuv.mode + // is irrelevant) + R = Y; + G = Y; + B = Y; + } + Rc = AVIF_CLAMP(R, 0.0f, 1.0f); + Gc = AVIF_CLAMP(G, 0.0f, 1.0f); + Bc = AVIF_CLAMP(B, 0.0f, 1.0f); + } else { + // Monochrome: gray is luma + float gray = Y; + grayc = AVIF_CLAMP(gray, 0.0f, 1.0f); + } + + if (alphaMultiplyMode != AVIF_ALPHA_MULTIPLY_MODE_NO_OP) { + // Calculate A + uint16_t unormA; + if (image->depth == 8) { + unormA = ptrA8[i]; + } else { + unormA = AVIF_MIN(ptrA16[i], yuvMaxChannel); + } + const float A = unormA / ((float)state->yuv.maxChannel); + const float Ac = AVIF_CLAMP(A, 0.0f, 1.0f); + + if (alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY) { + if (rgbHasColor) { + if (Ac == 0.0f) { + Rc = 0.0f; + Gc = 0.0f; + Bc = 0.0f; + } else if (Ac < 1.0f) { + Rc *= Ac; + Gc *= Ac; + Bc *= Ac; + } + } else { + if (Ac == 0.0f) { + grayc = 0.0f; + } else if (Ac < 1.0f) { + grayc *= Ac; + } + } + } else { + // alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_UNMULTIPLY + if (rgbHasColor) { + if (Ac == 0.0f) { + Rc = 0.0f; + Gc = 0.0f; + Bc = 0.0f; + } else if (Ac < 1.0f) { + Rc /= Ac; + Gc /= Ac; + Bc /= Ac; + Rc = AVIF_MIN(Rc, 1.0f); + Gc = AVIF_MIN(Gc, 1.0f); + Bc = AVIF_MIN(Bc, 1.0f); + } + } else { + if (Ac == 0.0f) { + grayc = 0.0f; + } else if (Ac < 1.0f) { + grayc /= Ac; + grayc = AVIF_MIN(grayc, 1.0f); + } + } + } + } + + if (rgbHasColor) { + if (rgb->depth == 8) { + avifStoreRGB8Pixel(rgb->format, + (uint8_t)(0.5f + (Rc * rgbMaxChannelF)), + (uint8_t)(0.5f + (Gc * rgbMaxChannelF)), + (uint8_t)(0.5f + (Bc * rgbMaxChannelF)), + ptrR, + ptrG, + ptrB); + } else { + *((uint16_t *)ptrR) = (uint16_t)(0.5f + (Rc * rgbMaxChannelF)); + *((uint16_t *)ptrG) = (uint16_t)(0.5f + (Gc * rgbMaxChannelF)); + *((uint16_t *)ptrB) = (uint16_t)(0.5f + (Bc * rgbMaxChannelF)); + } + ptrR += rgbPixelBytes; + ptrG += rgbPixelBytes; + ptrB += rgbPixelBytes; + } else { + if (rgb->depth == 8) { + *ptrGray = (uint8_t)(0.5f + (grayc * rgbMaxChannelF)); + } else { + *((uint16_t *)ptrGray) = (uint16_t)(0.5f + (grayc * rgbMaxChannelF)); + } + ptrGray += rgbPixelBytes; + } + } + } + avifFreeYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV); + return AVIF_RESULT_OK; +} + +static avifResult avifImageYUV16ToRGB16Color(const avifImage * image, avifRGBImage * rgb, avifReformatState * state) +{ + const float kr = state->yuv.kr; + const float kg = state->yuv.kg; + const float kb = state->yuv.kb; + const uint32_t rgbPixelBytes = state->rgb.pixelBytes; + float * unormFloatTableY = NULL; + float * unormFloatTableUV = NULL; + AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY); + + const uint16_t yuvMaxChannel = (uint16_t)state->yuv.maxChannel; + const float rgbMaxChannelF = state->rgb.maxChannelF; + for (size_t j = 0; j < image->height; ++j) { + const size_t uvJ = j >> state->yuv.formatInfo.chromaShiftY; + const uint16_t * const ptrY = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; + const uint16_t * const ptrU = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_U][(uvJ * image->yuvRowBytes[AVIF_CHAN_U])]; + const uint16_t * const ptrV = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_V][(uvJ * image->yuvRowBytes[AVIF_CHAN_V])]; + uint8_t * ptrR = &rgb->pixels[state->rgb.offsetBytesR + (j * rgb->rowBytes)]; + uint8_t * ptrG = &rgb->pixels[state->rgb.offsetBytesG + (j * rgb->rowBytes)]; + uint8_t * ptrB = &rgb->pixels[state->rgb.offsetBytesB + (j * rgb->rowBytes)]; + + for (size_t i = 0; i < image->width; ++i) { + size_t uvI = i >> state->yuv.formatInfo.chromaShiftX; + + // clamp incoming data to protect against bad LUT lookups + const uint16_t unormY = AVIF_MIN(ptrY[i], yuvMaxChannel); + const uint16_t unormU = AVIF_MIN(ptrU[uvI], yuvMaxChannel); + const uint16_t unormV = AVIF_MIN(ptrV[uvI], yuvMaxChannel); + + // Convert unorm to float + const float Y = unormFloatTableY[unormY]; + const float Cb = unormFloatTableUV[unormU]; + const float Cr = unormFloatTableUV[unormV]; + + const float R = Y + (2 * (1 - kr)) * Cr; + const float B = Y + (2 * (1 - kb)) * Cb; + const float G = Y - ((2 * ((kr * (1 - kr) * Cr) + (kb * (1 - kb) * Cb))) / kg); + const float Rc = AVIF_CLAMP(R, 0.0f, 1.0f); + const float Gc = AVIF_CLAMP(G, 0.0f, 1.0f); + const float Bc = AVIF_CLAMP(B, 0.0f, 1.0f); + + *((uint16_t *)ptrR) = (uint16_t)(0.5f + (Rc * rgbMaxChannelF)); + *((uint16_t *)ptrG) = (uint16_t)(0.5f + (Gc * rgbMaxChannelF)); + *((uint16_t *)ptrB) = (uint16_t)(0.5f + (Bc * rgbMaxChannelF)); + + ptrR += rgbPixelBytes; + ptrG += rgbPixelBytes; + ptrB += rgbPixelBytes; + } + } + avifFreeYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV); + return AVIF_RESULT_OK; +} + +static avifResult avifImageYUV16ToRGB16Mono(const avifImage * image, avifRGBImage * rgb, avifReformatState * state) +{ + const float kr = state->yuv.kr; + const float kg = state->yuv.kg; + const float kb = state->yuv.kb; + const uint32_t rgbPixelBytes = state->rgb.pixelBytes; + float * unormFloatTableY = NULL; + AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, NULL, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY); + + const uint16_t maxChannel = (uint16_t)state->yuv.maxChannel; + const float maxChannelF = state->rgb.maxChannelF; + for (size_t j = 0; j < image->height; ++j) { + const uint16_t * const ptrY = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; + uint8_t * ptrR = &rgb->pixels[state->rgb.offsetBytesR + (j * rgb->rowBytes)]; + uint8_t * ptrG = &rgb->pixels[state->rgb.offsetBytesG + (j * rgb->rowBytes)]; + uint8_t * ptrB = &rgb->pixels[state->rgb.offsetBytesB + (j * rgb->rowBytes)]; + + for (size_t i = 0; i < image->width; ++i) { + // clamp incoming data to protect against bad LUT lookups + const uint16_t unormY = AVIF_MIN(ptrY[i], maxChannel); + + // Convert unorm to float + const float Y = unormFloatTableY[unormY]; + const float Cb = 0.0f; + const float Cr = 0.0f; + + const float R = Y + (2 * (1 - kr)) * Cr; + const float B = Y + (2 * (1 - kb)) * Cb; + const float G = Y - ((2 * ((kr * (1 - kr) * Cr) + (kb * (1 - kb) * Cb))) / kg); + const float Rc = AVIF_CLAMP(R, 0.0f, 1.0f); + const float Gc = AVIF_CLAMP(G, 0.0f, 1.0f); + const float Bc = AVIF_CLAMP(B, 0.0f, 1.0f); + + *((uint16_t *)ptrR) = (uint16_t)(0.5f + (Rc * maxChannelF)); + *((uint16_t *)ptrG) = (uint16_t)(0.5f + (Gc * maxChannelF)); + *((uint16_t *)ptrB) = (uint16_t)(0.5f + (Bc * maxChannelF)); + + ptrR += rgbPixelBytes; + ptrG += rgbPixelBytes; + ptrB += rgbPixelBytes; + } + } + avifFreeYUVToRGBLookUpTables(&unormFloatTableY, NULL); + return AVIF_RESULT_OK; +} + +static avifResult avifImageYUV16ToRGB8Color(const avifImage * image, avifRGBImage * rgb, avifReformatState * state) +{ + const float kr = state->yuv.kr; + const float kg = state->yuv.kg; + const float kb = state->yuv.kb; + const uint32_t rgbPixelBytes = state->rgb.pixelBytes; + float * unormFloatTableY = NULL; + float * unormFloatTableUV = NULL; + AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY); + + const uint16_t yuvMaxChannel = (uint16_t)state->yuv.maxChannel; + const float rgbMaxChannelF = state->rgb.maxChannelF; + for (size_t j = 0; j < image->height; ++j) { + const size_t uvJ = j >> state->yuv.formatInfo.chromaShiftY; + const uint16_t * const ptrY = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; + const uint16_t * const ptrU = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_U][(uvJ * image->yuvRowBytes[AVIF_CHAN_U])]; + const uint16_t * const ptrV = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_V][(uvJ * image->yuvRowBytes[AVIF_CHAN_V])]; + uint8_t * ptrR = &rgb->pixels[state->rgb.offsetBytesR + (j * rgb->rowBytes)]; + uint8_t * ptrG = &rgb->pixels[state->rgb.offsetBytesG + (j * rgb->rowBytes)]; + uint8_t * ptrB = &rgb->pixels[state->rgb.offsetBytesB + (j * rgb->rowBytes)]; + + for (size_t i = 0; i < image->width; ++i) { + size_t uvI = i >> state->yuv.formatInfo.chromaShiftX; + + // clamp incoming data to protect against bad LUT lookups + const uint16_t unormY = AVIF_MIN(ptrY[i], yuvMaxChannel); + const uint16_t unormU = AVIF_MIN(ptrU[uvI], yuvMaxChannel); + const uint16_t unormV = AVIF_MIN(ptrV[uvI], yuvMaxChannel); + + // Convert unorm to float + const float Y = unormFloatTableY[unormY]; + const float Cb = unormFloatTableUV[unormU]; + const float Cr = unormFloatTableUV[unormV]; + + const float R = Y + (2 * (1 - kr)) * Cr; + const float B = Y + (2 * (1 - kb)) * Cb; + const float G = Y - ((2 * ((kr * (1 - kr) * Cr) + (kb * (1 - kb) * Cb))) / kg); + const float Rc = AVIF_CLAMP(R, 0.0f, 1.0f); + const float Gc = AVIF_CLAMP(G, 0.0f, 1.0f); + const float Bc = AVIF_CLAMP(B, 0.0f, 1.0f); + + avifStoreRGB8Pixel(rgb->format, + (uint8_t)(0.5f + (Rc * rgbMaxChannelF)), + (uint8_t)(0.5f + (Gc * rgbMaxChannelF)), + (uint8_t)(0.5f + (Bc * rgbMaxChannelF)), + ptrR, + ptrG, + ptrB); + + ptrR += rgbPixelBytes; + ptrG += rgbPixelBytes; + ptrB += rgbPixelBytes; + } + } + avifFreeYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV); + return AVIF_RESULT_OK; +} + +static avifResult avifImageYUV16ToRGB8Mono(const avifImage * image, avifRGBImage * rgb, avifReformatState * state) +{ + const float kr = state->yuv.kr; + const float kg = state->yuv.kg; + const float kb = state->yuv.kb; + const uint32_t rgbPixelBytes = state->rgb.pixelBytes; + float * unormFloatTableY = NULL; + AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, NULL, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY); + + const uint16_t yuvMaxChannel = (uint16_t)state->yuv.maxChannel; + const float rgbMaxChannelF = state->rgb.maxChannelF; + for (size_t j = 0; j < image->height; ++j) { + const uint16_t * const ptrY = (uint16_t *)&image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; + uint8_t * ptrR = &rgb->pixels[state->rgb.offsetBytesR + (j * rgb->rowBytes)]; + uint8_t * ptrG = &rgb->pixels[state->rgb.offsetBytesG + (j * rgb->rowBytes)]; + uint8_t * ptrB = &rgb->pixels[state->rgb.offsetBytesB + (j * rgb->rowBytes)]; + + for (size_t i = 0; i < image->width; ++i) { + // clamp incoming data to protect against bad LUT lookups + const uint16_t unormY = AVIF_MIN(ptrY[i], yuvMaxChannel); + + // Convert unorm to float + const float Y = unormFloatTableY[unormY]; + const float Cb = 0.0f; + const float Cr = 0.0f; + + const float R = Y + (2 * (1 - kr)) * Cr; + const float B = Y + (2 * (1 - kb)) * Cb; + const float G = Y - ((2 * ((kr * (1 - kr) * Cr) + (kb * (1 - kb) * Cb))) / kg); + const float Rc = AVIF_CLAMP(R, 0.0f, 1.0f); + const float Gc = AVIF_CLAMP(G, 0.0f, 1.0f); + const float Bc = AVIF_CLAMP(B, 0.0f, 1.0f); + + avifStoreRGB8Pixel(rgb->format, + (uint8_t)(0.5f + (Rc * rgbMaxChannelF)), + (uint8_t)(0.5f + (Gc * rgbMaxChannelF)), + (uint8_t)(0.5f + (Bc * rgbMaxChannelF)), + ptrR, + ptrG, + ptrB); + + ptrR += rgbPixelBytes; + ptrG += rgbPixelBytes; + ptrB += rgbPixelBytes; + } + } + avifFreeYUVToRGBLookUpTables(&unormFloatTableY, NULL); + return AVIF_RESULT_OK; +} + +static avifResult avifImageYUV8ToRGB16Color(const avifImage * image, avifRGBImage * rgb, avifReformatState * state) +{ + const float kr = state->yuv.kr; + const float kg = state->yuv.kg; + const float kb = state->yuv.kb; + const uint32_t rgbPixelBytes = state->rgb.pixelBytes; + float * unormFloatTableY = NULL; + float * unormFloatTableUV = NULL; + AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY); + + const float rgbMaxChannelF = state->rgb.maxChannelF; + for (size_t j = 0; j < image->height; ++j) { + const size_t uvJ = j >> state->yuv.formatInfo.chromaShiftY; + const uint8_t * const ptrY = &image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; + const uint8_t * const ptrU = &image->yuvPlanes[AVIF_CHAN_U][(uvJ * image->yuvRowBytes[AVIF_CHAN_U])]; + const uint8_t * const ptrV = &image->yuvPlanes[AVIF_CHAN_V][(uvJ * image->yuvRowBytes[AVIF_CHAN_V])]; + uint8_t * ptrR = &rgb->pixels[state->rgb.offsetBytesR + (j * rgb->rowBytes)]; + uint8_t * ptrG = &rgb->pixels[state->rgb.offsetBytesG + (j * rgb->rowBytes)]; + uint8_t * ptrB = &rgb->pixels[state->rgb.offsetBytesB + (j * rgb->rowBytes)]; + + for (size_t i = 0; i < image->width; ++i) { + size_t uvI = i >> state->yuv.formatInfo.chromaShiftX; + + // Convert unorm to float (no clamp necessary, the full uint8_t range is a legal lookup) + const float Y = unormFloatTableY[ptrY[i]]; + const float Cb = unormFloatTableUV[ptrU[uvI]]; + const float Cr = unormFloatTableUV[ptrV[uvI]]; + + const float R = Y + (2 * (1 - kr)) * Cr; + const float B = Y + (2 * (1 - kb)) * Cb; + const float G = Y - ((2 * ((kr * (1 - kr) * Cr) + (kb * (1 - kb) * Cb))) / kg); + const float Rc = AVIF_CLAMP(R, 0.0f, 1.0f); + const float Gc = AVIF_CLAMP(G, 0.0f, 1.0f); + const float Bc = AVIF_CLAMP(B, 0.0f, 1.0f); + + *((uint16_t *)ptrR) = (uint16_t)(0.5f + (Rc * rgbMaxChannelF)); + *((uint16_t *)ptrG) = (uint16_t)(0.5f + (Gc * rgbMaxChannelF)); + *((uint16_t *)ptrB) = (uint16_t)(0.5f + (Bc * rgbMaxChannelF)); + + ptrR += rgbPixelBytes; + ptrG += rgbPixelBytes; + ptrB += rgbPixelBytes; + } + } + avifFreeYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV); + return AVIF_RESULT_OK; +} + +static avifResult avifImageYUV8ToRGB16Mono(const avifImage * image, avifRGBImage * rgb, avifReformatState * state) +{ + const float kr = state->yuv.kr; + const float kg = state->yuv.kg; + const float kb = state->yuv.kb; + const uint32_t rgbPixelBytes = state->rgb.pixelBytes; + float * unormFloatTableY = NULL; + AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, NULL, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY); + + const float rgbMaxChannelF = state->rgb.maxChannelF; + for (size_t j = 0; j < image->height; ++j) { + const uint8_t * const ptrY = &image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; + uint8_t * ptrR = &rgb->pixels[state->rgb.offsetBytesR + (j * rgb->rowBytes)]; + uint8_t * ptrG = &rgb->pixels[state->rgb.offsetBytesG + (j * rgb->rowBytes)]; + uint8_t * ptrB = &rgb->pixels[state->rgb.offsetBytesB + (j * rgb->rowBytes)]; + + for (size_t i = 0; i < image->width; ++i) { + // Convert unorm to float (no clamp necessary, the full uint8_t range is a legal lookup) + const float Y = unormFloatTableY[ptrY[i]]; + const float Cb = 0.0f; + const float Cr = 0.0f; + + const float R = Y + (2 * (1 - kr)) * Cr; + const float B = Y + (2 * (1 - kb)) * Cb; + const float G = Y - ((2 * ((kr * (1 - kr) * Cr) + (kb * (1 - kb) * Cb))) / kg); + const float Rc = AVIF_CLAMP(R, 0.0f, 1.0f); + const float Gc = AVIF_CLAMP(G, 0.0f, 1.0f); + const float Bc = AVIF_CLAMP(B, 0.0f, 1.0f); + + *((uint16_t *)ptrR) = (uint16_t)(0.5f + (Rc * rgbMaxChannelF)); + *((uint16_t *)ptrG) = (uint16_t)(0.5f + (Gc * rgbMaxChannelF)); + *((uint16_t *)ptrB) = (uint16_t)(0.5f + (Bc * rgbMaxChannelF)); + + ptrR += rgbPixelBytes; + ptrG += rgbPixelBytes; + ptrB += rgbPixelBytes; + } + } + avifFreeYUVToRGBLookUpTables(&unormFloatTableY, NULL); + return AVIF_RESULT_OK; +} + +static avifResult avifImageIdentity8ToRGB8ColorFullRange(const avifImage * image, avifRGBImage * rgb, avifReformatState * state) +{ + const uint32_t rgbPixelBytes = state->rgb.pixelBytes; + for (size_t j = 0; j < image->height; ++j) { + const uint8_t * const ptrY = &image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; + const uint8_t * const ptrU = &image->yuvPlanes[AVIF_CHAN_U][(j * image->yuvRowBytes[AVIF_CHAN_U])]; + const uint8_t * const ptrV = &image->yuvPlanes[AVIF_CHAN_V][(j * image->yuvRowBytes[AVIF_CHAN_V])]; + uint8_t * ptrR = &rgb->pixels[state->rgb.offsetBytesR + (j * rgb->rowBytes)]; + uint8_t * ptrG = &rgb->pixels[state->rgb.offsetBytesG + (j * rgb->rowBytes)]; + uint8_t * ptrB = &rgb->pixels[state->rgb.offsetBytesB + (j * rgb->rowBytes)]; + + // This is intentionally a per-row conditional instead of a per-pixel + // conditional. This makes the "else" path (much more common than the + // "if" path) much faster than having a per-pixel branch. + if (rgb->format == AVIF_RGB_FORMAT_RGB_565) { + for (size_t i = 0; i < image->width; ++i) { + *(uint16_t *)ptrR = RGB565(ptrV[i], ptrY[i], ptrU[i]); + ptrR += rgbPixelBytes; + } + } else { + for (size_t i = 0; i < image->width; ++i) { + *ptrR = ptrV[i]; + *ptrG = ptrY[i]; + *ptrB = ptrU[i]; + ptrR += rgbPixelBytes; + ptrG += rgbPixelBytes; + ptrB += rgbPixelBytes; + } + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifImageYUV8ToRGB8Color(const avifImage * image, avifRGBImage * rgb, avifReformatState * state) +{ + const float kr = state->yuv.kr; + const float kg = state->yuv.kg; + const float kb = state->yuv.kb; + const uint32_t rgbPixelBytes = state->rgb.pixelBytes; + float * unormFloatTableY = NULL; + float * unormFloatTableUV = NULL; + AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY); + + const float rgbMaxChannelF = state->rgb.maxChannelF; + for (size_t j = 0; j < image->height; ++j) { + const size_t uvJ = j >> state->yuv.formatInfo.chromaShiftY; + const uint8_t * const ptrY = &image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; + const uint8_t * const ptrU = &image->yuvPlanes[AVIF_CHAN_U][(uvJ * image->yuvRowBytes[AVIF_CHAN_U])]; + const uint8_t * const ptrV = &image->yuvPlanes[AVIF_CHAN_V][(uvJ * image->yuvRowBytes[AVIF_CHAN_V])]; + uint8_t * ptrR = &rgb->pixels[state->rgb.offsetBytesR + (j * rgb->rowBytes)]; + uint8_t * ptrG = &rgb->pixels[state->rgb.offsetBytesG + (j * rgb->rowBytes)]; + uint8_t * ptrB = &rgb->pixels[state->rgb.offsetBytesB + (j * rgb->rowBytes)]; + + for (size_t i = 0; i < image->width; ++i) { + size_t uvI = i >> state->yuv.formatInfo.chromaShiftX; + + // Convert unorm to float (no clamp necessary, the full uint8_t range is a legal lookup) + const float Y = unormFloatTableY[ptrY[i]]; + const float Cb = unormFloatTableUV[ptrU[uvI]]; + const float Cr = unormFloatTableUV[ptrV[uvI]]; + + const float R = Y + (2 * (1 - kr)) * Cr; + const float B = Y + (2 * (1 - kb)) * Cb; + const float G = Y - ((2 * ((kr * (1 - kr) * Cr) + (kb * (1 - kb) * Cb))) / kg); + const float Rc = AVIF_CLAMP(R, 0.0f, 1.0f); + const float Gc = AVIF_CLAMP(G, 0.0f, 1.0f); + const float Bc = AVIF_CLAMP(B, 0.0f, 1.0f); + + avifStoreRGB8Pixel(rgb->format, + (uint8_t)(0.5f + (Rc * rgbMaxChannelF)), + (uint8_t)(0.5f + (Gc * rgbMaxChannelF)), + (uint8_t)(0.5f + (Bc * rgbMaxChannelF)), + ptrR, + ptrG, + ptrB); + + ptrR += rgbPixelBytes; + ptrG += rgbPixelBytes; + ptrB += rgbPixelBytes; + } + } + avifFreeYUVToRGBLookUpTables(&unormFloatTableY, &unormFloatTableUV); + return AVIF_RESULT_OK; +} + +static avifResult avifImageYUV8ToRGB8Mono(const avifImage * image, avifRGBImage * rgb, avifReformatState * state) +{ + const float kr = state->yuv.kr; + const float kg = state->yuv.kg; + const float kb = state->yuv.kb; + const uint32_t rgbPixelBytes = state->rgb.pixelBytes; + float * unormFloatTableY = NULL; + AVIF_CHECKERR(avifCreateYUVToRGBLookUpTables(&unormFloatTableY, NULL, image->depth, state), AVIF_RESULT_OUT_OF_MEMORY); + + const float rgbMaxChannelF = state->rgb.maxChannelF; + for (size_t j = 0; j < image->height; ++j) { + const uint8_t * const ptrY = &image->yuvPlanes[AVIF_CHAN_Y][(j * image->yuvRowBytes[AVIF_CHAN_Y])]; + uint8_t * ptrR = &rgb->pixels[state->rgb.offsetBytesR + (j * rgb->rowBytes)]; + uint8_t * ptrG = &rgb->pixels[state->rgb.offsetBytesG + (j * rgb->rowBytes)]; + uint8_t * ptrB = &rgb->pixels[state->rgb.offsetBytesB + (j * rgb->rowBytes)]; + + for (size_t i = 0; i < image->width; ++i) { + // Convert unorm to float (no clamp necessary, the full uint8_t range is a legal lookup) + const float Y = unormFloatTableY[ptrY[i]]; + const float Cb = 0.0f; + const float Cr = 0.0f; + + const float R = Y + (2 * (1 - kr)) * Cr; + const float B = Y + (2 * (1 - kb)) * Cb; + const float G = Y - ((2 * ((kr * (1 - kr) * Cr) + (kb * (1 - kb) * Cb))) / kg); + const float Rc = AVIF_CLAMP(R, 0.0f, 1.0f); + const float Gc = AVIF_CLAMP(G, 0.0f, 1.0f); + const float Bc = AVIF_CLAMP(B, 0.0f, 1.0f); + + avifStoreRGB8Pixel(rgb->format, + (uint8_t)(0.5f + (Rc * rgbMaxChannelF)), + (uint8_t)(0.5f + (Gc * rgbMaxChannelF)), + (uint8_t)(0.5f + (Bc * rgbMaxChannelF)), + ptrR, + ptrG, + ptrB); + + ptrR += rgbPixelBytes; + ptrG += rgbPixelBytes; + ptrB += rgbPixelBytes; + } + } + avifFreeYUVToRGBLookUpTables(&unormFloatTableY, NULL); + return AVIF_RESULT_OK; +} + +// This constant comes from libyuv. For details, see here: +// https://chromium.googlesource.com/libyuv/libyuv/+/2f87e9a7/source/row_common.cc#3537 +#define F16_MULTIPLIER 1.9259299444e-34f + +typedef union avifF16 +{ + float f; + uint32_t u32; +} avifF16; + +static avifResult avifRGBImageToF16(avifRGBImage * rgb) +{ + avifResult libyuvResult = AVIF_RESULT_NOT_IMPLEMENTED; + if (!rgb->avoidLibYUV) { + libyuvResult = avifRGBImageToF16LibYUV(rgb); + } + if (libyuvResult != AVIF_RESULT_NOT_IMPLEMENTED) { + return libyuvResult; + } + const size_t channelCount = avifRGBFormatChannelCount(rgb->format); + const float scale = 1.0f / ((1 << rgb->depth) - 1); + const float multiplier = F16_MULTIPLIER * scale; + uint16_t * pixelRowBase = (uint16_t *)rgb->pixels; + const uint32_t stride = rgb->rowBytes >> 1; + for (size_t j = 0; j < rgb->height; ++j) { + uint16_t * pixel = pixelRowBase; + for (size_t i = 0; i < rgb->width * channelCount; ++i, ++pixel) { + avifF16 f16; + f16.f = *pixel * multiplier; + *pixel = (uint16_t)(f16.u32 >> 13); + } + pixelRowBase += stride; + } + return AVIF_RESULT_OK; +} + +static avifResult avifImageYUVToRGBImpl(const avifImage * image, avifRGBImage * rgb, avifReformatState * state, avifAlphaMultiplyMode alphaMultiplyMode) +{ + avifBool convertedWithLibYUV = AVIF_FALSE; + // Reformat alpha, if user asks for it, or (un)multiply processing needs it. + avifBool reformatAlpha = avifRGBFormatHasAlpha(rgb->format) && + (!rgb->ignoreAlpha || (alphaMultiplyMode != AVIF_ALPHA_MULTIPLY_MODE_NO_OP)); + // This value is used only when reformatAlpha is true. + avifBool alphaReformattedWithLibYUV = AVIF_FALSE; + if (!rgb->avoidLibYUV && ((alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP) || avifRGBFormatHasAlpha(rgb->format))) { + avifResult libyuvResult = avifImageYUVToRGBLibYUV(image, rgb, reformatAlpha, &alphaReformattedWithLibYUV); + if (libyuvResult == AVIF_RESULT_OK) { + convertedWithLibYUV = AVIF_TRUE; + } else { + if (libyuvResult != AVIF_RESULT_NOT_IMPLEMENTED) { + return libyuvResult; + } + } + } + + if (reformatAlpha && !alphaReformattedWithLibYUV) { + avifAlphaParams params; + + params.width = rgb->width; + params.height = rgb->height; + params.dstDepth = rgb->depth; + params.dstPlane = rgb->pixels; + params.dstRowBytes = rgb->rowBytes; + params.dstOffsetBytes = state->rgb.offsetBytesA; + params.dstPixelBytes = state->rgb.pixelBytes; + + if (image->alphaPlane && image->alphaRowBytes) { + params.srcDepth = image->depth; + params.srcPlane = image->alphaPlane; + params.srcRowBytes = image->alphaRowBytes; + params.srcOffsetBytes = 0; + params.srcPixelBytes = state->yuv.channelBytes; + + avifReformatAlpha(¶ms); + } else { + avifFillAlpha(¶ms); + } + } + + if (!convertedWithLibYUV) { + // libyuv is either unavailable or unable to perform the specific conversion required here. + // Look over the available built-in "fast" routines for YUV->RGB conversion and see if one + // fits the current combination, or as a last resort, call avifImageYUVAnyToRGBAnySlow(), + // which handles every possibly YUV->RGB combination, but very slowly (in comparison). + + avifResult convertResult = AVIF_RESULT_NOT_IMPLEMENTED; + + const avifBool hasColor = + (image->yuvRowBytes[AVIF_CHAN_U] && image->yuvRowBytes[AVIF_CHAN_V] && (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400)); + + if (!avifRGBFormatIsGray(rgb->format) && + (!hasColor || (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) || + ((rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) || (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST))) && + (alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_NO_OP || avifRGBFormatHasAlpha(rgb->format))) { + // Explanations on the above conditional: + // * None of these fast paths currently support bilinear upsampling, so avoid all of them + // unless the YUV data isn't subsampled or they explicitly requested AVIF_CHROMA_UPSAMPLING_NEAREST. + // * None of these fast paths currently handle alpha (un)multiply, so avoid all of them + // if we can't do alpha (un)multiply as a separated post step (destination format doesn't have alpha). + + if (state->yuv.mode == AVIF_REFORMAT_MODE_IDENTITY) { + if ((image->depth == 8) && (rgb->depth == 8) && (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) && + (image->yuvRange == AVIF_RANGE_FULL)) { + convertResult = avifImageIdentity8ToRGB8ColorFullRange(image, rgb, state); + } + + // TODO: Add more fast paths for identity + } else if (state->yuv.mode == AVIF_REFORMAT_MODE_YUV_COEFFICIENTS) { + if (image->depth > 8) { + // yuv:u16 + + if (rgb->depth > 8) { + // yuv:u16, rgb:u16 + + if (hasColor) { + convertResult = avifImageYUV16ToRGB16Color(image, rgb, state); + } else { + convertResult = avifImageYUV16ToRGB16Mono(image, rgb, state); + } + } else { + // yuv:u16, rgb:u8 + + if (hasColor) { + convertResult = avifImageYUV16ToRGB8Color(image, rgb, state); + } else { + convertResult = avifImageYUV16ToRGB8Mono(image, rgb, state); + } + } + } else { + // yuv:u8 + + if (rgb->depth > 8) { + // yuv:u8, rgb:u16 + + if (hasColor) { + convertResult = avifImageYUV8ToRGB16Color(image, rgb, state); + } else { + convertResult = avifImageYUV8ToRGB16Mono(image, rgb, state); + } + } else { + // yuv:u8, rgb:u8 + + if (hasColor) { + convertResult = avifImageYUV8ToRGB8Color(image, rgb, state); + } else { + convertResult = avifImageYUV8ToRGB8Mono(image, rgb, state); + } + } + } + } + } + + if (convertResult == AVIF_RESULT_NOT_IMPLEMENTED) { + // If we get here, there is no fast path for this combination. Time to be slow! + convertResult = avifImageYUVAnyToRGBAnySlow(image, rgb, state, alphaMultiplyMode); + + // The slow path also handles alpha (un)multiply, so forget the operation here. + alphaMultiplyMode = AVIF_ALPHA_MULTIPLY_MODE_NO_OP; + } + + if (convertResult != AVIF_RESULT_OK) { + return convertResult; + } + } + + // Process alpha premultiplication, if necessary + if (alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY) { + avifResult result = avifRGBImagePremultiplyAlpha(rgb); + if (result != AVIF_RESULT_OK) { + return result; + } + } else if (alphaMultiplyMode == AVIF_ALPHA_MULTIPLY_MODE_UNMULTIPLY) { + avifResult result = avifRGBImageUnpremultiplyAlpha(rgb); + if (result != AVIF_RESULT_OK) { + return result; + } + } + + // Convert pixels to half floats (F16), if necessary. + if (rgb->isFloat) { + return avifRGBImageToF16(rgb); + } + + return AVIF_RESULT_OK; +} + +typedef struct +{ +#if defined(_WIN32) + HANDLE thread; +#else + pthread_t thread; +#endif + avifImage image; + avifRGBImage rgb; + avifReformatState * state; + avifAlphaMultiplyMode alphaMultiplyMode; + avifResult result; + avifBool threadCreated; +} YUVToRGBThreadData; + +#if defined(_WIN32) +static unsigned int __stdcall avifImageYUVToRGBThreadWorker(void * arg) +#else +static void * avifImageYUVToRGBThreadWorker(void * arg) +#endif +{ + YUVToRGBThreadData * data = (YUVToRGBThreadData *)arg; + data->result = avifImageYUVToRGBImpl(&data->image, &data->rgb, data->state, data->alphaMultiplyMode); +#if defined(_WIN32) + return 0; +#else + return NULL; +#endif +} + +static avifBool avifCreateYUVToRGBThread(YUVToRGBThreadData * tdata) +{ +#if defined(_WIN32) + tdata->thread = (HANDLE)_beginthreadex(/*security=*/NULL, + /*stack_size=*/0, + &avifImageYUVToRGBThreadWorker, + tdata, + /*initflag=*/0, + /*thrdaddr=*/NULL); + return tdata->thread != NULL; +#else + return pthread_create(&tdata->thread, NULL, &avifImageYUVToRGBThreadWorker, tdata) == 0; +#endif +} + +static avifBool avifJoinYUVToRGBThread(YUVToRGBThreadData * tdata) +{ +#if defined(_WIN32) + return WaitForSingleObject(tdata->thread, INFINITE) == WAIT_OBJECT_0 && CloseHandle(tdata->thread) != 0; +#else + return pthread_join(tdata->thread, NULL) == 0; +#endif +} + +avifResult avifImageYUVToRGB(const avifImage * image, avifRGBImage * rgb) +{ + // It is okay for rgb->maxThreads to be equal to zero in order to allow clients to zero initialize the avifRGBImage struct + // with memset. + if (!image->yuvPlanes[AVIF_CHAN_Y] || rgb->maxThreads < 0) { + return AVIF_RESULT_REFORMAT_FAILED; + } + + avifReformatState state; + if (!avifPrepareReformatState(image, rgb, &state)) { + return AVIF_RESULT_REFORMAT_FAILED; + } + + avifAlphaMultiplyMode alphaMultiplyMode = AVIF_ALPHA_MULTIPLY_MODE_NO_OP; + if (image->alphaPlane) { + if (!avifRGBFormatHasAlpha(rgb->format) || rgb->ignoreAlpha) { + // if we are converting some image with alpha into a format without alpha, we should do 'premultiply alpha' before + // discarding alpha plane. This has the same effect of rendering this image on a black background, which makes sense. + if (!image->alphaPremultiplied) { + alphaMultiplyMode = AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY; + } + } else { + if (!image->alphaPremultiplied && rgb->alphaPremultiplied) { + alphaMultiplyMode = AVIF_ALPHA_MULTIPLY_MODE_MULTIPLY; + } else if (image->alphaPremultiplied && !rgb->alphaPremultiplied) { + alphaMultiplyMode = AVIF_ALPHA_MULTIPLY_MODE_UNMULTIPLY; + } + } + } + + // In practice, we rarely need more than 8 threads for YUV to RGB conversion. + uint32_t jobs = AVIF_CLAMP(rgb->maxThreads, 1, 8); + + // When yuv format is 420 and chromaUpsampling could be BILINEAR, there is a dependency across the horizontal borders of each + // job. So we disallow multithreading in that case. + if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 && (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_AUTOMATIC || + rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_BEST_QUALITY || + rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_BILINEAR)) { + jobs = 1; + } + + // Each thread worker needs at least 2 Y rows (to account for potential U/V subsampling). + if (jobs == 1 || (image->height / 2) < jobs) { + return avifImageYUVToRGBImpl(image, rgb, &state, alphaMultiplyMode); + } + + const size_t byteCount = sizeof(YUVToRGBThreadData) * jobs; + YUVToRGBThreadData * threadData = (YUVToRGBThreadData *)avifAlloc(byteCount); + if (!threadData) { + return AVIF_RESULT_OUT_OF_MEMORY; + } + memset(threadData, 0, byteCount); + uint32_t rowsPerJob = image->height / jobs; + if (rowsPerJob % 2) { + ++rowsPerJob; + jobs = (image->height + rowsPerJob - 1) / rowsPerJob; // ceil + } + const uint32_t rowsForLastJob = image->height - rowsPerJob * (jobs - 1); + uint32_t startRow = 0; + uint32_t i; + for (i = 0; i < jobs; ++i, startRow += rowsPerJob) { + YUVToRGBThreadData * tdata = &threadData[i]; + const avifCropRect rect = { .x = 0, .y = startRow, .width = image->width, .height = (i == jobs - 1) ? rowsForLastJob : rowsPerJob }; + if (avifImageSetViewRect(&tdata->image, image, &rect) != AVIF_RESULT_OK) { + tdata->result = AVIF_RESULT_REFORMAT_FAILED; + break; + } + + tdata->rgb = *rgb; + tdata->rgb.pixels += startRow * (size_t)rgb->rowBytes; + tdata->rgb.height = tdata->image.height; + + tdata->state = &state; + tdata->alphaMultiplyMode = alphaMultiplyMode; + + if (i > 0) { + tdata->threadCreated = avifCreateYUVToRGBThread(tdata); + if (!tdata->threadCreated) { + tdata->result = AVIF_RESULT_REFORMAT_FAILED; + break; + } + } + } + // If above loop ran successfully, run the first job in the current thread. + if (i == jobs) { + avifImageYUVToRGBThreadWorker(&threadData[0]); + } + avifResult result = AVIF_RESULT_OK; + for (i = 0; i < jobs; ++i) { + YUVToRGBThreadData * tdata = &threadData[i]; + if (tdata->threadCreated && !avifJoinYUVToRGBThread(tdata)) { + result = AVIF_RESULT_REFORMAT_FAILED; + } + if (tdata->result != AVIF_RESULT_OK) { + result = tdata->result; + } + } + avifFree(threadData); + return result; +} + +// Limited -> Full +// Plan: subtract limited offset, then multiply by ratio of FULLSIZE/LIMITEDSIZE (rounding), then clamp. +// RATIO = (FULLY - 0) / (MAXLIMITEDY - MINLIMITEDY) +// ----------------------------------------- +// ( ( (v - MINLIMITEDY) | subtract limited offset +// * FULLY | multiply numerator of ratio +// ) + ((MAXLIMITEDY - MINLIMITEDY) / 2) | add 0.5 (half of denominator) to round +// ) / (MAXLIMITEDY - MINLIMITEDY) | divide by denominator of ratio +// AVIF_CLAMP(v, 0, FULLY) | clamp to full range +// ----------------------------------------- +#define LIMITED_TO_FULL(MINLIMITEDY, MAXLIMITEDY, FULLY) \ + v = (((v - MINLIMITEDY) * FULLY) + ((MAXLIMITEDY - MINLIMITEDY) / 2)) / (MAXLIMITEDY - MINLIMITEDY); \ + v = AVIF_CLAMP(v, 0, FULLY) + +// Full -> Limited +// Plan: multiply by ratio of LIMITEDSIZE/FULLSIZE (rounding), then add limited offset, then clamp. +// RATIO = (MAXLIMITEDY - MINLIMITEDY) / (FULLY - 0) +// ----------------------------------------- +// ( ( (v * (MAXLIMITEDY - MINLIMITEDY)) | multiply numerator of ratio +// + (FULLY / 2) | add 0.5 (half of denominator) to round +// ) / FULLY | divide by denominator of ratio +// ) + MINLIMITEDY | add limited offset +// AVIF_CLAMP(v, MINLIMITEDY, MAXLIMITEDY) | clamp to limited range +// ----------------------------------------- +#define FULL_TO_LIMITED(MINLIMITEDY, MAXLIMITEDY, FULLY) \ + v = (((v * (MAXLIMITEDY - MINLIMITEDY)) + (FULLY / 2)) / FULLY) + MINLIMITEDY; \ + v = AVIF_CLAMP(v, MINLIMITEDY, MAXLIMITEDY) + +int avifLimitedToFullY(uint32_t depth, int v) +{ + switch (depth) { + case 8: + LIMITED_TO_FULL(16, 235, 255); + break; + case 10: + LIMITED_TO_FULL(64, 940, 1023); + break; + case 12: + LIMITED_TO_FULL(256, 3760, 4095); + break; + } + return v; +} + +int avifLimitedToFullUV(uint32_t depth, int v) +{ + switch (depth) { + case 8: + LIMITED_TO_FULL(16, 240, 255); + break; + case 10: + LIMITED_TO_FULL(64, 960, 1023); + break; + case 12: + LIMITED_TO_FULL(256, 3840, 4095); + break; + } + return v; +} + +int avifFullToLimitedY(uint32_t depth, int v) +{ + switch (depth) { + case 8: + FULL_TO_LIMITED(16, 235, 255); + break; + case 10: + FULL_TO_LIMITED(64, 940, 1023); + break; + case 12: + FULL_TO_LIMITED(256, 3760, 4095); + break; + } + return v; +} + +int avifFullToLimitedUV(uint32_t depth, int v) +{ + switch (depth) { + case 8: + FULL_TO_LIMITED(16, 240, 255); + break; + case 10: + FULL_TO_LIMITED(64, 960, 1023); + break; + case 12: + FULL_TO_LIMITED(256, 3840, 4095); + break; + } + return v; +} + +static inline uint16_t avifFloatToF16(float v) +{ + avifF16 f16; + f16.f = v * F16_MULTIPLIER; + return (uint16_t)(f16.u32 >> 13); +} + +static inline float avifF16ToFloat(uint16_t v) +{ + avifF16 f16; + f16.u32 = v << 13; + return f16.f / F16_MULTIPLIER; +} + +void avifGetRGBAPixel(const avifRGBImage * src, uint32_t x, uint32_t y, const avifRGBColorSpaceInfo * info, float rgbaPixel[4]) +{ + assert(src != NULL); + assert(!src->isFloat || src->depth == 16); + assert(src->format != AVIF_RGB_FORMAT_RGB_565 || src->depth == 8); + + const uint8_t * const srcPixel = &src->pixels[(size_t)y * src->rowBytes + (size_t)x * info->pixelBytes]; + if (info->channelBytes > 1) { + uint16_t r = *((const uint16_t *)(&srcPixel[info->offsetBytesR])); + uint16_t g = *((const uint16_t *)(&srcPixel[info->offsetBytesG])); + uint16_t b = *((const uint16_t *)(&srcPixel[info->offsetBytesB])); + uint16_t a = avifRGBFormatHasAlpha(src->format) ? *((const uint16_t *)(&srcPixel[info->offsetBytesA])) : (uint16_t)info->maxChannel; + if (src->isFloat) { + rgbaPixel[0] = avifF16ToFloat(r); + rgbaPixel[1] = avifF16ToFloat(g); + rgbaPixel[2] = avifF16ToFloat(b); + rgbaPixel[3] = avifRGBFormatHasAlpha(src->format) ? avifF16ToFloat(a) : 1.0f; + } else { + rgbaPixel[0] = r / info->maxChannelF; + rgbaPixel[1] = g / info->maxChannelF; + rgbaPixel[2] = b / info->maxChannelF; + rgbaPixel[3] = a / info->maxChannelF; + } + } else { + if (src->format == AVIF_RGB_FORMAT_RGB_565) { + uint8_t r, g, b; + avifGetRGB565(&srcPixel[info->offsetBytesR], &r, &g, &b); + rgbaPixel[0] = r / info->maxChannelF; + rgbaPixel[1] = g / info->maxChannelF; + rgbaPixel[2] = b / info->maxChannelF; + rgbaPixel[3] = 1.0f; + } else { + rgbaPixel[0] = srcPixel[info->offsetBytesR] / info->maxChannelF; + rgbaPixel[1] = srcPixel[info->offsetBytesG] / info->maxChannelF; + rgbaPixel[2] = srcPixel[info->offsetBytesB] / info->maxChannelF; + rgbaPixel[3] = avifRGBFormatHasAlpha(src->format) ? (srcPixel[info->offsetBytesA] / info->maxChannelF) : 1.0f; + } + } +} + +void avifSetRGBAPixel(const avifRGBImage * dst, uint32_t x, uint32_t y, const avifRGBColorSpaceInfo * info, const float rgbaPixel[4]) +{ + assert(dst != NULL); + assert(!dst->isFloat || dst->depth == 16); + assert(dst->format != AVIF_RGB_FORMAT_RGB_565 || dst->depth == 8); + assert(rgbaPixel[0] >= 0.0f && rgbaPixel[0] <= 1.0f); + assert(rgbaPixel[1] >= 0.0f && rgbaPixel[1] <= 1.0f); + assert(rgbaPixel[2] >= 0.0f && rgbaPixel[2] <= 1.0f); + + uint8_t * const dstPixel = &dst->pixels[(size_t)y * dst->rowBytes + (size_t)x * info->pixelBytes]; + + uint8_t * const ptrR = &dstPixel[info->offsetBytesR]; + uint8_t * const ptrG = &dstPixel[info->offsetBytesG]; + uint8_t * const ptrB = &dstPixel[info->offsetBytesB]; + uint8_t * const ptrA = avifRGBFormatHasAlpha(dst->format) ? &dstPixel[info->offsetBytesA] : NULL; + if (dst->depth > 8) { + if (dst->isFloat) { + *((uint16_t *)ptrR) = avifFloatToF16(rgbaPixel[0]); + *((uint16_t *)ptrG) = avifFloatToF16(rgbaPixel[1]); + *((uint16_t *)ptrB) = avifFloatToF16(rgbaPixel[2]); + if (ptrA) { + *((uint16_t *)ptrA) = avifFloatToF16(rgbaPixel[3]); + } + } else { + *((uint16_t *)ptrR) = (uint16_t)(0.5f + (rgbaPixel[0] * info->maxChannelF)); + *((uint16_t *)ptrG) = (uint16_t)(0.5f + (rgbaPixel[1] * info->maxChannelF)); + *((uint16_t *)ptrB) = (uint16_t)(0.5f + (rgbaPixel[2] * info->maxChannelF)); + if (ptrA) { + *((uint16_t *)ptrA) = (uint16_t)(0.5f + (rgbaPixel[3] * info->maxChannelF)); + } + } + } else { + avifStoreRGB8Pixel(dst->format, + (uint8_t)(0.5f + (rgbaPixel[0] * info->maxChannelF)), + (uint8_t)(0.5f + (rgbaPixel[1] * info->maxChannelF)), + (uint8_t)(0.5f + (rgbaPixel[2] * info->maxChannelF)), + ptrR, + ptrG, + ptrB); + if (ptrA) { + *ptrA = (uint8_t)(0.5f + (rgbaPixel[3] * info->maxChannelF)); + } + } +} diff --git a/3rdparty/libavif/src/reformat_libsharpyuv.c b/3rdparty/libavif/src/reformat_libsharpyuv.c new file mode 100644 index 0000000..a6bd523 --- /dev/null +++ b/3rdparty/libavif/src/reformat_libsharpyuv.c @@ -0,0 +1,84 @@ +// Copyright 2022 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#if defined(AVIF_LIBSHARPYUV_ENABLED) +#include +#include +#include + +avifResult avifImageRGBToYUVLibSharpYUV(avifImage * image, const avifRGBImage * rgb, const avifReformatState * state) +{ + // The width, height, and stride parameters of SharpYuvConvertWithOptions() + // and SharpYuvConvert() are all of the int type. + if (rgb->width > INT_MAX || rgb->height > INT_MAX || rgb->rowBytes > INT_MAX || image->yuvRowBytes[AVIF_CHAN_Y] > INT_MAX) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + const SharpYuvColorSpace colorSpace = { + state->yuv.kr, state->yuv.kb, image->depth, (state->yuv.range == AVIF_RANGE_LIMITED) ? kSharpYuvRangeLimited : kSharpYuvRangeFull + }; + + SharpYuvConversionMatrix matrix; + // Fills in 'matrix' for the given YUVColorSpace. + SharpYuvComputeConversionMatrix(&colorSpace, &matrix); +#if SHARPYUV_VERSION >= SHARPYUV_MAKE_VERSION(0, 4, 0) + SharpYuvOptions options; + SharpYuvOptionsInit(&matrix, &options); + if (image->transferCharacteristics == AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) { + // Set to sRGB for backward compatibility. + options.transfer_type = kSharpYuvTransferFunctionSrgb; + } else { + options.transfer_type = (SharpYuvTransferFunctionType)image->transferCharacteristics; + } + const int sharpyuvRes = SharpYuvConvertWithOptions(&rgb->pixels[state->rgb.offsetBytesR], + &rgb->pixels[state->rgb.offsetBytesG], + &rgb->pixels[state->rgb.offsetBytesB], + state->rgb.pixelBytes, + rgb->rowBytes, + rgb->depth, + image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y], + image->yuvPlanes[AVIF_CHAN_U], + image->yuvRowBytes[AVIF_CHAN_U], + image->yuvPlanes[AVIF_CHAN_V], + image->yuvRowBytes[AVIF_CHAN_V], + image->depth, + rgb->width, + rgb->height, + &options); +#else + const int sharpyuvRes = SharpYuvConvert(&rgb->pixels[state->rgb.offsetBytesR], + &rgb->pixels[state->rgb.offsetBytesG], + &rgb->pixels[state->rgb.offsetBytesB], + state->rgb.pixelBytes, + rgb->rowBytes, + rgb->depth, + image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y], + image->yuvPlanes[AVIF_CHAN_U], + image->yuvRowBytes[AVIF_CHAN_U], + image->yuvPlanes[AVIF_CHAN_V], + image->yuvRowBytes[AVIF_CHAN_V], + image->depth, + rgb->width, + rgb->height, + &matrix); +#endif // SHARPYUV_VERSION >= SHARPYUV_MAKE_VERSION(0, 4, 0) + if (!sharpyuvRes) { + return AVIF_RESULT_REFORMAT_FAILED; + } + + return AVIF_RESULT_OK; +} + +#else + +avifResult avifImageRGBToYUVLibSharpYUV(avifImage * image, const avifRGBImage * rgb, const avifReformatState * state) +{ + (void)image; + (void)rgb; + (void)state; + return AVIF_RESULT_NOT_IMPLEMENTED; +} +#endif // defined(AVIF_LIBSHARPYUV_ENABLED) diff --git a/3rdparty/libavif/src/reformat_libyuv.c b/3rdparty/libavif/src/reformat_libyuv.c new file mode 100644 index 0000000..18329c8 --- /dev/null +++ b/3rdparty/libavif/src/reformat_libyuv.c @@ -0,0 +1,1186 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#if !defined(AVIF_LIBYUV_ENABLED) + +// No libyuv! +avifResult avifImageRGBToYUVLibYUV(avifImage * image, const avifRGBImage * rgb) +{ + (void)image; + (void)rgb; + return AVIF_RESULT_NOT_IMPLEMENTED; +} +avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifBool reformatAlpha, avifBool * alphaReformattedWithLibYUV) +{ + (void)image; + (void)rgb; + (void)reformatAlpha; + *alphaReformattedWithLibYUV = AVIF_FALSE; + return AVIF_RESULT_NOT_IMPLEMENTED; +} +avifResult avifRGBImagePremultiplyAlphaLibYUV(avifRGBImage * rgb) +{ + (void)rgb; + return AVIF_RESULT_NOT_IMPLEMENTED; +} +avifResult avifRGBImageUnpremultiplyAlphaLibYUV(avifRGBImage * rgb) +{ + (void)rgb; + return AVIF_RESULT_NOT_IMPLEMENTED; +} +avifResult avifRGBImageToF16LibYUV(avifRGBImage * rgb) +{ + (void)rgb; + return AVIF_RESULT_NOT_IMPLEMENTED; +} +unsigned int avifLibYUVVersion(void) +{ + return 0; +} + +#else + +#include +#include +#include + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstrict-prototypes" // "this function declaration is not a prototype" +// The newline at the end of libyuv/version.h was accidentally deleted in version 1792 and restored +// in version 1813: +// https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3183182 +// https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3527834 +#pragma clang diagnostic ignored "-Wnewline-eof" // "no newline at end of file" +#endif +#include +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +// libyuv is a C++ library and defines custom types (struct, enum, etc) in the libyuv namespace when the libyuv header files are +// included by C++ code. When accessed from a C library like libavif, via a function pointer, this leads to signature mismatches +// in the CFI sanitizers since libyuv itself, compiled as C++ code, has the types within the namespace and the C code has the +// types without the namespace. The same thing happens with clang's undefined behavior sanitizer as well when invoked with +// -fsanitize=function. So we suppress both of these sanitizers in functions that call libyuv functions via a pointer. +// For a simpler example of this bug, please see: https://github.com/vigneshvg/cpp_c_potential_cfi_bug. +// For more details on clang's CFI see: https://clang.llvm.org/docs/ControlFlowIntegrity.html. +// For more details on clang's UBSan see: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html +#if defined(__clang__) +#define IGNORE_CFI_ICALL __attribute__((no_sanitize("cfi-icall", "function"))) +#else +#define IGNORE_CFI_ICALL +#endif + +//-------------------------------------------------------------------------------------------------- +// libyuv API availability management + +// These defines are used to create a NULL reference to libyuv functions that +// did not exist prior to a particular version of libyuv. +// Versions prior to 1755 are considered too old and not used (see CMakeLists.txt). +#if LIBYUV_VERSION < 1902 +// RAWToJ444 was added in libyuv version 1902. +// See https://chromium-review.googlesource.com/c/libyuv/libyuv/+/6207845. +// NEON was added afterward, but the version was not increased. +// See https://chromium-review.googlesource.com/c/libyuv/libyuv/+/6220890 +#define RAWToJ444 NULL +#endif +#if LIBYUV_VERSION < 1844 +// I444ToRGB24Matrix() and I422ToRGB24MatrixFilter() were added in libyuv version 1844. +// +// Note: Between the following two commits, libyuv version jumped from 1841 to 1844, down to 1843, +// and back to 1844. See https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3906082 and +// https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3906091. +#define I444ToRGB24Matrix NULL +#define I422ToRGB24MatrixFilter NULL +#endif +#if LIBYUV_VERSION < 1841 +// I420ToRGB24MatrixFilter() was added in libyuv version 1841. +// See https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3900298. +#define I420ToRGB24MatrixFilter NULL +#endif +#if LIBYUV_VERSION < 1840 +#define ABGRToJ400 NULL +#endif +#if LIBYUV_VERSION < 1838 +#define I422ToRGB565Matrix NULL +#endif +#if LIBYUV_VERSION < 1813 +#define I422ToARGBMatrixFilter NULL +#define I420ToARGBMatrixFilter NULL +#define I210ToARGBMatrixFilter NULL +#define I010ToARGBMatrixFilter NULL +#define I420AlphaToARGBMatrixFilter NULL +#define I422AlphaToARGBMatrixFilter NULL +#define I010AlphaToARGBMatrixFilter NULL +#define I210AlphaToARGBMatrixFilter NULL +#endif +#if LIBYUV_VERSION < 1782 +#define RAWToJ420 NULL +#endif +#if LIBYUV_VERSION < 1781 +#define I012ToARGBMatrix NULL +#endif +#if LIBYUV_VERSION < 1780 +#define I410ToARGBMatrix NULL +#define I410AlphaToARGBMatrix NULL +#define I210AlphaToARGBMatrix NULL +#define I010AlphaToARGBMatrix NULL +#endif +#if LIBYUV_VERSION < 1771 +#define I422AlphaToARGBMatrix NULL +#define I444AlphaToARGBMatrix NULL +#endif +#if LIBYUV_VERSION < 1756 +#define I400ToARGBMatrix NULL +#endif + +// Two-step replacement for the conversions to 8-bit BT.601 YUV which are missing from libyuv. +static int avifReorderARGBThenConvertToYUV(int (*ReorderARGB)(const uint8_t *, int, uint8_t *, int, int, int), + int (*ConvertToYUV)(const uint8_t *, int, uint8_t *, int, uint8_t *, int, uint8_t *, int, int, int), + const uint8_t * src_abgr, + int src_stride_abgr, + uint8_t * dst_y, + int dst_stride_y, + uint8_t * dst_u, + int dst_stride_u, + uint8_t * dst_v, + int dst_stride_v, + avifPixelFormat dst_format, + int width, + int height) +{ + // Only the vertically subsampled formats need to be processed by luma row pairs. + avifPixelFormatInfo format_info; + avifGetPixelFormatInfo(dst_format, &format_info); + const int min_num_rows = (format_info.chromaShiftY == 1) ? 2 : 1; + + // A temporary buffer is needed to call ReorderARGB(). + uint8_t * src_argb; + if ((int64_t)width * 4 > INT_MAX) { + return -1; + } + const int src_stride_argb = width * 4; + const int soft_allocation_limit = 16384; // Arbitrarily chosen trade-off between CPU and memory footprints. + int num_allocated_rows; + if ((height == 1) || ((int64_t)src_stride_argb * height <= soft_allocation_limit)) { + // Process the whole buffer in one go. + num_allocated_rows = height; + } else { + if ((int64_t)src_stride_argb * min_num_rows > INT_MAX) { + return -1; + } + // The last row of an odd number of RGB rows to be converted to vertically subsampled YUV is treated + // differently by libyuv, so make sure all steps but the last one process a multiple of min_num_rows rows. + // Try to process the highest multiple of min_num_rows rows possible in a single step without + // allocating more than soft_allocation_limit, unless min_num_rows rows need more than that. + num_allocated_rows = AVIF_MAX(1, soft_allocation_limit / (src_stride_argb * min_num_rows)) * min_num_rows; + } + src_argb = (uint8_t *)avifAlloc(num_allocated_rows * src_stride_argb); + if (!src_argb) { + return -1; + } + + for (int y = 0; y < height; y += num_allocated_rows) { + const int num_rows = AVIF_MIN(num_allocated_rows, height - y); + if (ReorderARGB(src_abgr, src_stride_abgr, src_argb, src_stride_argb, width, num_rows) || + ConvertToYUV(src_argb, src_stride_argb, dst_y, dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, width, num_rows)) { + avifFree(src_argb); + return -1; + } + src_abgr += (size_t)num_rows * src_stride_abgr; + dst_y += (size_t)num_rows * dst_stride_y; + // Either chroma is not vertically subsampled, num_rows is even, or this is the last iteration. + dst_u += (size_t)(num_rows >> format_info.chromaShiftY) * dst_stride_u; + dst_v += (size_t)(num_rows >> format_info.chromaShiftY) * dst_stride_v; + } + avifFree(src_argb); + return 0; +} + +#define AVIF_DEFINE_CONVERSION(NAME, REORDER_ARGB, CONVERT_TO_YUV, YUV_FORMAT) \ + static int NAME(const uint8_t * src_abgr, \ + int src_stride_abgr, \ + uint8_t * dst_y, \ + int dst_stride_y, \ + uint8_t * dst_u, \ + int dst_stride_u, \ + uint8_t * dst_v, \ + int dst_stride_v, \ + int width, \ + int height) \ + { \ + return avifReorderARGBThenConvertToYUV(REORDER_ARGB, \ + CONVERT_TO_YUV, \ + src_abgr, \ + src_stride_abgr, \ + dst_y, \ + dst_stride_y, \ + dst_u, \ + dst_stride_u, \ + dst_v, \ + dst_stride_v, \ + YUV_FORMAT, \ + width, \ + height); \ + } + +#if LIBYUV_VERSION < 1903 +// AVIF_RGB_FORMAT_RGB +// RAWToI444 was added in libyuv version 1903. +// https://chromium-review.googlesource.com/c/libyuv/libyuv/+/6223658 +AVIF_DEFINE_CONVERSION(RAWToI444, RAWToARGB, ARGBToI444, AVIF_PIXEL_FORMAT_YUV444) +#endif +#if LIBYUV_VERSION < 1840 +// AVIF_RGB_FORMAT_RGBA +AVIF_DEFINE_CONVERSION(ABGRToJ422, ABGRToARGB, ARGBToJ422, AVIF_PIXEL_FORMAT_YUV422) +AVIF_DEFINE_CONVERSION(ABGRToJ420, ABGRToARGB, ARGBToJ420, AVIF_PIXEL_FORMAT_YUV420) +#endif + +// These are not yet implemented in libyuv so they cannot be guarded by a version check. +// The "avif" prefix avoids any redefinition if they are available in libyuv one day. +// AVIF_RGB_FORMAT_RGB +AVIF_DEFINE_CONVERSION(avifRAWToI422, RAWToARGB, ARGBToI422, AVIF_PIXEL_FORMAT_YUV422) +AVIF_DEFINE_CONVERSION(avifRAWToJ422, RAWToARGB, ARGBToJ422, AVIF_PIXEL_FORMAT_YUV422) +// AVIF_RGB_FORMAT_RGBA +AVIF_DEFINE_CONVERSION(avifABGRToI444, ABGRToARGB, ARGBToI444, AVIF_PIXEL_FORMAT_YUV444) +AVIF_DEFINE_CONVERSION(avifABGRToI422, ABGRToARGB, ARGBToI422, AVIF_PIXEL_FORMAT_YUV422) +// AVIF_RGB_FORMAT_ARGB +AVIF_DEFINE_CONVERSION(avifBGRAToI444, BGRAToARGB, ARGBToI444, AVIF_PIXEL_FORMAT_YUV444) +AVIF_DEFINE_CONVERSION(avifBGRAToI422, BGRAToARGB, ARGBToI422, AVIF_PIXEL_FORMAT_YUV422) +AVIF_DEFINE_CONVERSION(avifBGRAToJ422, BGRAToARGB, ARGBToJ422, AVIF_PIXEL_FORMAT_YUV422) +AVIF_DEFINE_CONVERSION(avifBGRAToJ420, BGRAToARGB, ARGBToJ420, AVIF_PIXEL_FORMAT_YUV420) +// AVIF_RGB_FORMAT_BGR +AVIF_DEFINE_CONVERSION(avifRGB24ToI444, RGB24ToARGB, ARGBToI444, AVIF_PIXEL_FORMAT_YUV444) +AVIF_DEFINE_CONVERSION(avifRGB24ToI422, RGB24ToARGB, ARGBToI422, AVIF_PIXEL_FORMAT_YUV422) +AVIF_DEFINE_CONVERSION(avifRGB24ToJ422, RGB24ToARGB, ARGBToJ422, AVIF_PIXEL_FORMAT_YUV422) +// AVIF_RGB_FORMAT_ABGR +AVIF_DEFINE_CONVERSION(avifRGBAToI444, RGBAToARGB, ARGBToI444, AVIF_PIXEL_FORMAT_YUV444) +AVIF_DEFINE_CONVERSION(avifRGBAToI422, RGBAToARGB, ARGBToI422, AVIF_PIXEL_FORMAT_YUV422) +AVIF_DEFINE_CONVERSION(avifRGBAToJ422, RGBAToARGB, ARGBToJ422, AVIF_PIXEL_FORMAT_YUV422) +AVIF_DEFINE_CONVERSION(avifRGBAToJ420, RGBAToARGB, ARGBToJ420, AVIF_PIXEL_FORMAT_YUV420) + +//-------------------------------------------------------------------------------------------------- +// RGB to YUV + +static avifResult avifImageRGBToYUVLibYUV8bpc(avifImage * image, const avifRGBImage * rgb); + +avifResult avifImageRGBToYUVLibYUV(avifImage * image, const avifRGBImage * rgb) +{ + // The width, height, and stride parameters of libyuv functions are all of the int type. + if (image->width > INT_MAX || image->height > INT_MAX || image->yuvRowBytes[AVIF_CHAN_Y] > INT_MAX || rgb->rowBytes > INT_MAX) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + if ((image->depth == 8) && (rgb->depth == 8)) { + return avifImageRGBToYUVLibYUV8bpc(image, rgb); + } + + // This function didn't do anything; use the built-in conversion. + return AVIF_RESULT_NOT_IMPLEMENTED; +} + +avifResult avifImageRGBToYUVLibYUV8bpc(avifImage * image, const avifRGBImage * rgb) +{ + assert((image->depth == 8) && (rgb->depth == 8)); + // libavif uses byte-order when describing pixel formats, such that the R in RGBA is the lowest address, + // similar to PNG. libyuv orders in word-order, so libavif's RGBA would be referred to in libyuv as ABGR. + + // libyuv only handles BT.601 for RGB to YUV, and not all range/order/subsampling combinations. + // BT.470BG has the same coefficients as BT.601. + if ((image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_BT470BG) || (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_BT601)) { + if (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { + // Lookup table for RGB To Y (monochrome). + typedef int (*RGBtoY)(const uint8_t *, int, uint8_t *, int, int, int); + // First dimension is for avifRange. + RGBtoY lutRgbToY[2][AVIF_RGB_FORMAT_COUNT] = { // AVIF_RANGE_LIMITED + { + // // AVIF_RGB_FORMAT_ + NULL, // RGB + NULL, // RGBA + NULL, // ARGB + NULL, // BGR + ARGBToI400, // BGRA + NULL, // ABGR + NULL, // RGB_565 + }, + // AVIF_RANGE_FULL + { + // // AVIF_RGB_FORMAT_ + RAWToJ400, // RGB + ABGRToJ400, // RGBA + NULL, // ARGB + RGB24ToJ400, // BGR + ARGBToJ400, // BGRA + RGBAToJ400, // ABGR + NULL // RGB_565 + } + }; + RGBtoY rgbToY = lutRgbToY[image->yuvRange][rgb->format]; + if (rgbToY != NULL) { + if (rgbToY(rgb->pixels, + rgb->rowBytes, + image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y], + image->width, + image->height) != 0) { + return AVIF_RESULT_REFORMAT_FAILED; + } + return AVIF_RESULT_OK; + } + } else { + // Lookup table for RGB To YUV Matrix (average filter). + typedef int (*RGBtoYUV)(const uint8_t *, int, uint8_t *, int, uint8_t *, int, uint8_t *, int, int, int); + // First dimension is for avifRange. + RGBtoYUV lutRgbToYuv[2][AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { + // AVIF_RANGE_LIMITED + { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, RAWToI444, avifRAWToI422, RAWToI420, NULL }, // RGB + { NULL, avifABGRToI444, avifABGRToI422, ABGRToI420, NULL }, // RGBA + { NULL, avifBGRAToI444, avifBGRAToI422, BGRAToI420, NULL }, // ARGB + { NULL, avifRGB24ToI444, avifRGB24ToI422, RGB24ToI420, NULL }, // BGR + { NULL, ARGBToI444, ARGBToI422, ARGBToI420, NULL }, // BGRA + { NULL, avifRGBAToI444, avifRGBAToI422, RGBAToI420, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL } // RGB_565 + }, + // AVIF_RANGE_FULL + { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, RAWToJ444, avifRAWToJ422, RAWToJ420, NULL }, // RGB + { NULL, NULL, ABGRToJ422, ABGRToJ420, NULL }, // RGBA + { NULL, NULL, avifBGRAToJ422, avifBGRAToJ420, NULL }, // ARGB + { NULL, NULL, avifRGB24ToJ422, RGB24ToJ420, NULL }, // BGR + { NULL, NULL, ARGBToJ422, ARGBToJ420, NULL }, // BGRA + { NULL, NULL, avifRGBAToJ422, avifRGBAToJ420, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL } // RGB_565 + } + }; + RGBtoYUV rgbToYuv = lutRgbToYuv[image->yuvRange][rgb->format][image->yuvFormat]; + if (rgbToYuv != NULL) { + if (rgbToYuv(rgb->pixels, + rgb->rowBytes, + image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y], + image->yuvPlanes[AVIF_CHAN_U], + image->yuvRowBytes[AVIF_CHAN_U], + image->yuvPlanes[AVIF_CHAN_V], + image->yuvRowBytes[AVIF_CHAN_V], + image->width, + image->height) != 0) { + return AVIF_RESULT_REFORMAT_FAILED; + } + return AVIF_RESULT_OK; + } + } + } + // TODO: Use SplitRGBPlane() for AVIF_MATRIX_COEFFICIENTS_IDENTITY if faster than the built-in implementation + return AVIF_RESULT_NOT_IMPLEMENTED; +} + +//-------------------------------------------------------------------------------------------------- +// YUV to RGB + +// Note about the libyuv look up tables used for YUV-to-RGB conversion: +// libavif uses byte-order when describing pixel formats, such that the R in RGBA is the lowest address, similar to PNG. libyuv +// orders in word-order, so libavif's RGBA would be referred to in libyuv as ABGR. In addition, swapping U and V in any of the +// calls, along with using the Yvu matrix instead of Yuv matrix, swaps B and R in these orderings as well. +// +// libavif format libyuv Func UV matrix (and UV argument ordering) +// -------------------- ------------- ------------------------------------ +// For 8-bit YUV: +// AVIF_RGB_FORMAT_RGB *ToRGB24Matrix matrixYVU +// AVIF_RGB_FORMAT_RGBA *ToARGBMatrix matrixYVU +// AVIF_RGB_FORMAT_ARGB *ToRGBAMatrix matrixYVU +// AVIF_RGB_FORMAT_BGR *ToRGB24Matrix matrixYUV +// AVIF_RGB_FORMAT_BGRA *ToARGBMatrix matrixYUV +// AVIF_RGB_FORMAT_ABGR *ToRGBAMatrix matrixYUV +// AVIF_RGB_FORMAT_RGB_565 *ToRGB565Matrix matrixYUV +// +// For 10-bit and 12-bit YUV: +// AVIF_RGB_FORMAT_RGB n/a n/a +// AVIF_RGB_FORMAT_RGBA *ToARGBMatrix matrixYVU +// AVIF_RGB_FORMAT_ARGB n/a n/a +// AVIF_RGB_FORMAT_BGR n/a n/a +// AVIF_RGB_FORMAT_BGRA *ToARGBMatrix matrixYUV +// AVIF_RGB_FORMAT_ABGR n/a n/a +// AVIF_RGB_FORMAT_RGB_565 n/a n/a + +// Lookup table for isYVU. If the entry in this table is AVIF_TRUE, then it +// means that we are using a libyuv function with R and B channels swapped, +// which requires U and V planes also be swapped. +static const avifBool lutIsYVU[AVIF_RGB_FORMAT_COUNT] = { + // // AVIF_RGB_FORMAT_ + AVIF_TRUE, // RGB + AVIF_TRUE, // RGBA + AVIF_TRUE, // ARGB + AVIF_FALSE, // BGR + AVIF_FALSE, // BGRA + AVIF_FALSE, // ABGR + AVIF_FALSE, // RGB_565 +}; + +typedef int (*YUV400ToRGBMatrix)(const uint8_t *, int, uint8_t *, int, const struct YuvConstants *, int, int); +typedef int (*YUVToRGBMatrixFilter)(const uint8_t *, + int, + const uint8_t *, + int, + const uint8_t *, + int, + uint8_t *, + int, + const struct YuvConstants *, + int, + int, + enum FilterMode); +typedef int (*YUVAToRGBMatrixFilter)(const uint8_t *, + int, + const uint8_t *, + int, + const uint8_t *, + int, + const uint8_t *, + int, + uint8_t *, + int, + const struct YuvConstants *, + int, + int, + int, + enum FilterMode); +typedef int (*YUVToRGBMatrix)(const uint8_t *, int, const uint8_t *, int, const uint8_t *, int, uint8_t *, int, const struct YuvConstants *, int, int); +typedef int (*YUVAToRGBMatrix)(const uint8_t *, + int, + const uint8_t *, + int, + const uint8_t *, + int, + const uint8_t *, + int, + uint8_t *, + int, + const struct YuvConstants *, + int, + int, + int); +typedef int (*YUVToRGBMatrixFilterHighBitDepth)(const uint16_t *, + int, + const uint16_t *, + int, + const uint16_t *, + int, + uint8_t *, + int, + const struct YuvConstants *, + int, + int, + enum FilterMode); +typedef int (*YUVAToRGBMatrixFilterHighBitDepth)(const uint16_t *, + int, + const uint16_t *, + int, + const uint16_t *, + int, + const uint16_t *, + int, + uint8_t *, + int, + const struct YuvConstants *, + int, + int, + int, + enum FilterMode); +typedef int (*YUVToRGBMatrixHighBitDepth)(const uint16_t *, + int, + const uint16_t *, + int, + const uint16_t *, + int, + uint8_t *, + int, + const struct YuvConstants *, + int, + int); +typedef int (*YUVAToRGBMatrixHighBitDepth)(const uint16_t *, + int, + const uint16_t *, + int, + const uint16_t *, + int, + const uint16_t *, + int, + uint8_t *, + int, + const struct YuvConstants *, + int, + int, + int); + +// At most one pointer in this struct will be not-NULL. +typedef struct +{ + YUV400ToRGBMatrix yuv400ToRgbMatrix; + YUVToRGBMatrixFilter yuvToRgbMatrixFilter; + YUVAToRGBMatrixFilter yuvaToRgbMatrixFilter; + YUVToRGBMatrix yuvToRgbMatrix; + YUVAToRGBMatrix yuvaToRgbMatrix; + YUVToRGBMatrixFilterHighBitDepth yuvToRgbMatrixFilterHighBitDepth; + YUVAToRGBMatrixFilterHighBitDepth yuvaToRgbMatrixFilterHighBitDepth; + YUVToRGBMatrixHighBitDepth yuvToRgbMatrixHighBitDepth; + YUVAToRGBMatrixHighBitDepth yuvaToRgbMatrixHighBitDepth; +} LibyuvConversionFunction; + +// Only allow nearest-neighbor filter if explicitly specified or left as default. +static avifBool nearestNeighborFilterAllowed(int chromaUpsampling) +{ + return chromaUpsampling != AVIF_CHROMA_UPSAMPLING_BILINEAR && chromaUpsampling != AVIF_CHROMA_UPSAMPLING_BEST_QUALITY; +} + +// Returns AVIF_TRUE if the given yuvFormat and yuvDepth can be converted to 8-bit RGB using libyuv, AVIF_FALSE otherwise. When +// AVIF_TRUE is returned, exactly one function pointers will be populated with the appropriate conversion function. If +// alphaPreferred is set to AVIF_TRUE, then a function that can also copy the alpha channel will be preferred if available. +static avifBool getLibYUVConversionFunction(avifPixelFormat yuvFormat, + int yuvDepth, + avifRGBImage * rgb, + avifBool alphaPreferred, + LibyuvConversionFunction * lcf) +{ + // Lookup table for 8-bit YUV400 to 8-bit RGB Matrix. + static const YUV400ToRGBMatrix lutYuv400ToRgbMatrix[AVIF_RGB_FORMAT_COUNT] = { + // // AVIF_RGB_FORMAT_ + NULL, // RGB + I400ToARGBMatrix, // RGBA + NULL, // ARGB + NULL, // BGR + I400ToARGBMatrix, // BGRA + NULL, // ABGR + NULL, // RGB_565 + }; + + // Lookup table for 8-bit YUV To 8-bit RGB Matrix (with filter). + static const YUVToRGBMatrixFilter lutYuvToRgbMatrixFilter[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, I422ToRGB24MatrixFilter, I420ToRGB24MatrixFilter, NULL }, // RGB + { NULL, NULL, I422ToARGBMatrixFilter, I420ToARGBMatrixFilter, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, I422ToRGB24MatrixFilter, I420ToRGB24MatrixFilter, NULL }, // BGR + { NULL, NULL, I422ToARGBMatrixFilter, I420ToARGBMatrixFilter, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }; + + // Lookup table for 8-bit YUVA To 8-bit RGB Matrix (with filter). + static const YUVAToRGBMatrixFilter lutYuvaToRgbMatrixFilter[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, NULL, NULL, NULL }, // RGB + { NULL, NULL, I422AlphaToARGBMatrixFilter, I420AlphaToARGBMatrixFilter, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, NULL, NULL, NULL }, // BGR + { NULL, NULL, I422AlphaToARGBMatrixFilter, I420AlphaToARGBMatrixFilter, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }; + + // Lookup table for 8-bit YUV To 8-bit RGB Matrix (4:4:4 or nearest-neighbor filter). + static const YUVToRGBMatrix lutYuvToRgbMatrix[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, I444ToRGB24Matrix, NULL, I420ToRGB24Matrix, NULL }, // RGB + { NULL, I444ToARGBMatrix, I422ToARGBMatrix, I420ToARGBMatrix, NULL }, // RGBA + { NULL, NULL, I422ToRGBAMatrix, I420ToRGBAMatrix, NULL }, // ARGB + { NULL, I444ToRGB24Matrix, NULL, I420ToRGB24Matrix, NULL }, // BGR + { NULL, I444ToARGBMatrix, I422ToARGBMatrix, I420ToARGBMatrix, NULL }, // BGRA + { NULL, NULL, I422ToRGBAMatrix, I420ToRGBAMatrix, NULL }, // ABGR + { NULL, NULL, I422ToRGB565Matrix, I420ToRGB565Matrix, NULL }, // RGB_565 + }; + + // Lookup table for 8-bit YUVA To 8-bit RGB Matrix (4:4:4 or nearest-neighbor filter). + static const YUVAToRGBMatrix lutYuvaToRgbMatrix[AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, NULL, NULL, NULL }, // RGB + { NULL, I444AlphaToARGBMatrix, I422AlphaToARGBMatrix, I420AlphaToARGBMatrix, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, NULL, NULL, NULL }, // BGR + { NULL, I444AlphaToARGBMatrix, I422AlphaToARGBMatrix, I420AlphaToARGBMatrix, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }; + + // Lookup table for YUV To RGB Matrix (with filter). First dimension is for the YUV bit depth. + static const YUVToRGBMatrixFilterHighBitDepth lutYuvToRgbMatrixFilterHighBitDepth[2][AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { + // 10bpc + { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, NULL, NULL, NULL }, // RGB + { NULL, NULL, I210ToARGBMatrixFilter, I010ToARGBMatrixFilter, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, NULL, NULL, NULL }, // BGR + { NULL, NULL, I210ToARGBMatrixFilter, I010ToARGBMatrixFilter, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }, + // 12bpc + { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, NULL, NULL, NULL }, // RGB + { NULL, NULL, NULL, NULL, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, NULL, NULL, NULL }, // BGR + { NULL, NULL, NULL, NULL, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }, + }; + + // Lookup table for YUVA To RGB Matrix (with filter). First dimension is for the YUV bit depth. + static const YUVAToRGBMatrixFilterHighBitDepth lutYuvaToRgbMatrixFilterHighBitDepth[2][AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { + // 10bpc + { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, NULL, NULL, NULL }, // RGB + { NULL, NULL, I210AlphaToARGBMatrixFilter, I010AlphaToARGBMatrixFilter, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, NULL, NULL, NULL }, // BGR + { NULL, NULL, I210AlphaToARGBMatrixFilter, I010AlphaToARGBMatrixFilter, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }, + // 12bpc + { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, NULL, NULL, NULL }, // RGB + { NULL, NULL, NULL, NULL, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, NULL, NULL, NULL }, // BGR + { NULL, NULL, NULL, NULL, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }, + }; + + // Lookup table for YUV To RGB Matrix (4:4:4 or nearest-neighbor filter). First dimension is for the YUV bit depth. + static const YUVToRGBMatrixHighBitDepth lutYuvToRgbMatrixHighBitDepth[2][AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { + // 10bpc + { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, NULL, NULL, NULL }, // RGB + { NULL, I410ToARGBMatrix, I210ToARGBMatrix, I010ToARGBMatrix, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, NULL, NULL, NULL }, // BGR + { NULL, I410ToARGBMatrix, I210ToARGBMatrix, I010ToARGBMatrix, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }, + // 12bpc + { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, NULL, NULL, NULL }, // RGB + { NULL, NULL, NULL, I012ToARGBMatrix, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, NULL, NULL, NULL }, // BGR + { NULL, NULL, NULL, I012ToARGBMatrix, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }, + }; + + // Lookup table for YUVA To RGB Matrix (4:4:4 or nearest-neighbor filter). First dimension is for the YUV bit depth. + static const YUVAToRGBMatrixHighBitDepth lutYuvaToRgbMatrixHighBitDepth[2][AVIF_RGB_FORMAT_COUNT][AVIF_PIXEL_FORMAT_COUNT] = { + // 10bpc + { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, NULL, NULL, NULL }, // RGB + { NULL, I410AlphaToARGBMatrix, I210AlphaToARGBMatrix, I010AlphaToARGBMatrix, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, NULL, NULL, NULL }, // BGR + { NULL, I410AlphaToARGBMatrix, I210AlphaToARGBMatrix, I010AlphaToARGBMatrix, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }, + // 12bpc + { + // { NONE, YUV444, YUV422, YUV420, YUV400 } // AVIF_RGB_FORMAT_ + { NULL, NULL, NULL, NULL, NULL }, // RGB + { NULL, NULL, NULL, NULL, NULL }, // RGBA + { NULL, NULL, NULL, NULL, NULL }, // ARGB + { NULL, NULL, NULL, NULL, NULL }, // BGR + { NULL, NULL, NULL, NULL, NULL }, // BGRA + { NULL, NULL, NULL, NULL, NULL }, // ABGR + { NULL, NULL, NULL, NULL, NULL }, // RGB_565 + }, + }; + + memset(lcf, 0, sizeof(*lcf)); + assert(rgb->depth == 8); + if (yuvDepth > 8) { + assert(yuvDepth == 10 || yuvDepth == 12); + int depthIndex = (yuvDepth == 10) ? 0 : 1; + if (yuvFormat != AVIF_PIXEL_FORMAT_YUV444) { + if (alphaPreferred) { + lcf->yuvaToRgbMatrixFilterHighBitDepth = lutYuvaToRgbMatrixFilterHighBitDepth[depthIndex][rgb->format][yuvFormat]; + if (lcf->yuvaToRgbMatrixFilterHighBitDepth != NULL) { + return AVIF_TRUE; + } + } + lcf->yuvToRgbMatrixFilterHighBitDepth = lutYuvToRgbMatrixFilterHighBitDepth[depthIndex][rgb->format][yuvFormat]; + if (lcf->yuvToRgbMatrixFilterHighBitDepth != NULL) { + return AVIF_TRUE; + } + } + if (yuvFormat == AVIF_PIXEL_FORMAT_YUV444 || nearestNeighborFilterAllowed(rgb->chromaUpsampling)) { + if (alphaPreferred) { + lcf->yuvaToRgbMatrixHighBitDepth = lutYuvaToRgbMatrixHighBitDepth[depthIndex][rgb->format][yuvFormat]; + if (lcf->yuvaToRgbMatrixHighBitDepth != NULL) { + return AVIF_TRUE; + } + } + lcf->yuvToRgbMatrixHighBitDepth = lutYuvToRgbMatrixHighBitDepth[depthIndex][rgb->format][yuvFormat]; + if (lcf->yuvToRgbMatrixHighBitDepth != NULL) { + return AVIF_TRUE; + } + } + // Fallthrough is intentional. No high bitdepth libyuv function was found. Check if there is an 8-bit libyuv function which + // can used with a downshift. + } + if (yuvFormat == AVIF_PIXEL_FORMAT_YUV400) { + lcf->yuv400ToRgbMatrix = lutYuv400ToRgbMatrix[rgb->format]; + return lcf->yuv400ToRgbMatrix != NULL; + } + if (yuvFormat != AVIF_PIXEL_FORMAT_YUV444) { + if (alphaPreferred) { + lcf->yuvaToRgbMatrixFilter = lutYuvaToRgbMatrixFilter[rgb->format][yuvFormat]; + if (lcf->yuvaToRgbMatrixFilter != NULL) { + return AVIF_TRUE; + } + } + lcf->yuvToRgbMatrixFilter = lutYuvToRgbMatrixFilter[rgb->format][yuvFormat]; + if (lcf->yuvToRgbMatrixFilter != NULL) { + return AVIF_TRUE; + } + if (!nearestNeighborFilterAllowed(rgb->chromaUpsampling)) { + return AVIF_FALSE; + } + } + if (alphaPreferred) { + lcf->yuvaToRgbMatrix = lutYuvaToRgbMatrix[rgb->format][yuvFormat]; + if (lcf->yuvaToRgbMatrix != NULL) { + return AVIF_TRUE; + } + } + lcf->yuvToRgbMatrix = lutYuvToRgbMatrix[rgb->format][yuvFormat]; + return lcf->yuvToRgbMatrix != NULL; +} + +static void getLibYUVConstants(const avifImage * image, const struct YuvConstants ** matrixYUV, const struct YuvConstants ** matrixYVU) +{ + // Allow the identity matrix to be used with YUV 4:0:0. Replace the identity matrix with + // MatrixCoefficients 6 (BT.601). + const avifBool yuv400WithIdentityMatrix = (image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400) && + (image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY); + const avifMatrixCoefficients matrixCoefficients = yuv400WithIdentityMatrix ? AVIF_MATRIX_COEFFICIENTS_BT601 : image->matrixCoefficients; + if (image->yuvRange == AVIF_RANGE_FULL) { + switch (matrixCoefficients) { + // BT.709 full range YuvConstants were added in libyuv version 1772. + // See https://chromium-review.googlesource.com/c/libyuv/libyuv/+/2646472. + case AVIF_MATRIX_COEFFICIENTS_BT709: +#if LIBYUV_VERSION >= 1772 + *matrixYUV = &kYuvF709Constants; + *matrixYVU = &kYvuF709Constants; +#endif + break; + case AVIF_MATRIX_COEFFICIENTS_BT470BG: + case AVIF_MATRIX_COEFFICIENTS_BT601: + case AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED: + *matrixYUV = &kYuvJPEGConstants; + *matrixYVU = &kYvuJPEGConstants; + break; + // BT.2020 full range YuvConstants were added in libyuv version 1775. + // See https://chromium-review.googlesource.com/c/libyuv/libyuv/+/2678859. + case AVIF_MATRIX_COEFFICIENTS_BT2020_NCL: +#if LIBYUV_VERSION >= 1775 + *matrixYUV = &kYuvV2020Constants; + *matrixYVU = &kYvuV2020Constants; +#endif + break; + case AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL: + switch (image->colorPrimaries) { + case AVIF_COLOR_PRIMARIES_BT709: + case AVIF_COLOR_PRIMARIES_UNSPECIFIED: +#if LIBYUV_VERSION >= 1772 + *matrixYUV = &kYuvF709Constants; + *matrixYVU = &kYvuF709Constants; +#endif + break; + case AVIF_COLOR_PRIMARIES_BT470BG: + case AVIF_COLOR_PRIMARIES_BT601: + *matrixYUV = &kYuvJPEGConstants; + *matrixYVU = &kYvuJPEGConstants; + break; + case AVIF_COLOR_PRIMARIES_BT2020: +#if LIBYUV_VERSION >= 1775 + *matrixYUV = &kYuvV2020Constants; + *matrixYVU = &kYvuV2020Constants; +#endif + break; + + case AVIF_COLOR_PRIMARIES_UNKNOWN: + case AVIF_COLOR_PRIMARIES_BT470M: + case AVIF_COLOR_PRIMARIES_SMPTE240: + case AVIF_COLOR_PRIMARIES_GENERIC_FILM: + case AVIF_COLOR_PRIMARIES_XYZ: + case AVIF_COLOR_PRIMARIES_SMPTE431: + case AVIF_COLOR_PRIMARIES_SMPTE432: + case AVIF_COLOR_PRIMARIES_EBU3213: + break; + } + break; + + case AVIF_MATRIX_COEFFICIENTS_IDENTITY: + case AVIF_MATRIX_COEFFICIENTS_FCC: + case AVIF_MATRIX_COEFFICIENTS_SMPTE240: + case AVIF_MATRIX_COEFFICIENTS_YCGCO: + case AVIF_MATRIX_COEFFICIENTS_BT2020_CL: + case AVIF_MATRIX_COEFFICIENTS_SMPTE2085: + case AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL: + case AVIF_MATRIX_COEFFICIENTS_ICTCP: + break; + } + } else { // image->yuvRange == AVIF_RANGE_LIMITED + switch (matrixCoefficients) { + case AVIF_MATRIX_COEFFICIENTS_BT709: + *matrixYUV = &kYuvH709Constants; + *matrixYVU = &kYvuH709Constants; + break; + case AVIF_MATRIX_COEFFICIENTS_BT470BG: + case AVIF_MATRIX_COEFFICIENTS_BT601: + case AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED: + *matrixYUV = &kYuvI601Constants; + *matrixYVU = &kYvuI601Constants; + break; + case AVIF_MATRIX_COEFFICIENTS_BT2020_NCL: + *matrixYUV = &kYuv2020Constants; + *matrixYVU = &kYvu2020Constants; + break; + case AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_NCL: + switch (image->colorPrimaries) { + case AVIF_COLOR_PRIMARIES_BT709: + case AVIF_COLOR_PRIMARIES_UNSPECIFIED: + *matrixYUV = &kYuvH709Constants; + *matrixYVU = &kYvuH709Constants; + break; + case AVIF_COLOR_PRIMARIES_BT470BG: + case AVIF_COLOR_PRIMARIES_BT601: + *matrixYUV = &kYuvI601Constants; + *matrixYVU = &kYvuI601Constants; + break; + case AVIF_COLOR_PRIMARIES_BT2020: + *matrixYUV = &kYuv2020Constants; + *matrixYVU = &kYvu2020Constants; + break; + + case AVIF_COLOR_PRIMARIES_UNKNOWN: + case AVIF_COLOR_PRIMARIES_BT470M: + case AVIF_COLOR_PRIMARIES_SMPTE240: + case AVIF_COLOR_PRIMARIES_GENERIC_FILM: + case AVIF_COLOR_PRIMARIES_XYZ: + case AVIF_COLOR_PRIMARIES_SMPTE431: + case AVIF_COLOR_PRIMARIES_SMPTE432: + case AVIF_COLOR_PRIMARIES_EBU3213: + break; + } + break; + case AVIF_MATRIX_COEFFICIENTS_IDENTITY: + case AVIF_MATRIX_COEFFICIENTS_FCC: + case AVIF_MATRIX_COEFFICIENTS_SMPTE240: + case AVIF_MATRIX_COEFFICIENTS_YCGCO: + case AVIF_MATRIX_COEFFICIENTS_BT2020_CL: + case AVIF_MATRIX_COEFFICIENTS_SMPTE2085: + case AVIF_MATRIX_COEFFICIENTS_CHROMA_DERIVED_CL: + case AVIF_MATRIX_COEFFICIENTS_ICTCP: + break; + } + } +} + +static avifResult avifImageDownshiftTo8bpc(const avifImage * image, avifImage * image8, avifBool downshiftAlpha) +{ + avifImageSetDefaults(image8); + avifImageCopyNoAlloc(image8, image); + image8->depth = 8; + // downshiftAlpha will be true only if the image has an alpha plane. So it is safe to pass AVIF_PLANES_ALL here in that case. + assert(!downshiftAlpha || image->alphaPlane); + AVIF_CHECKRES(avifImageAllocatePlanes(image8, downshiftAlpha ? AVIF_PLANES_ALL : AVIF_PLANES_YUV)); + // 16384 for 10-bit and 4096 for 12-bit. + const int scale = 1 << (24 - image->depth); + for (int plane = AVIF_CHAN_Y; plane <= (downshiftAlpha ? AVIF_CHAN_A : AVIF_CHAN_V); ++plane) { + const uint32_t planeWidth = avifImagePlaneWidth(image, plane); + if (planeWidth == 0) { + continue; + } + Convert16To8Plane((const uint16_t *)avifImagePlane(image, plane), + avifImagePlaneRowBytes(image, plane) / 2, + avifImagePlane(image8, plane), + avifImagePlaneRowBytes(image8, plane), + scale, + planeWidth, + avifImagePlaneHeight(image, plane)); + } + return AVIF_RESULT_OK; +} + +IGNORE_CFI_ICALL avifResult avifImageYUVToRGBLibYUV(const avifImage * image, avifRGBImage * rgb, avifBool reformatAlpha, avifBool * alphaReformattedWithLibYUV) +{ + *alphaReformattedWithLibYUV = AVIF_FALSE; + // The width, height, and stride parameters of libyuv functions are all of the int type. + if (image->width > INT_MAX || image->height > INT_MAX || image->yuvRowBytes[AVIF_CHAN_Y] > INT_MAX || rgb->rowBytes > INT_MAX) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (rgb->depth != 8 || (image->depth != 8 && image->depth != 10 && image->depth != 12)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + // Find the correct libyuv YuvConstants, based on range and CP/MC + const struct YuvConstants * matrixYUV = NULL; + const struct YuvConstants * matrixYVU = NULL; + getLibYUVConstants(image, &matrixYUV, &matrixYVU); + if (!matrixYVU) { + // No YuvConstants exist for the current image; use the built-in YUV conversion + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + LibyuvConversionFunction lcf; + const avifBool alphaPreferred = reformatAlpha && image->alphaPlane && image->alphaRowBytes; + if (!getLibYUVConversionFunction(image->yuvFormat, image->depth, rgb, alphaPreferred, &lcf)) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (!image->alphaPlane || !image->alphaRowBytes) { + // If the image does not have an alpha plane, then libyuv always prefills the output RGB image with opaque alpha values. + *alphaReformattedWithLibYUV = AVIF_TRUE; + } + avifBool isYVU = lutIsYVU[rgb->format]; + const struct YuvConstants * matrix = isYVU ? matrixYVU : matrixYUV; + int libyuvResult = -1; + int uPlaneIndex = isYVU ? AVIF_CHAN_V : AVIF_CHAN_U; + int vPlaneIndex = isYVU ? AVIF_CHAN_U : AVIF_CHAN_V; + const enum FilterMode filter = + ((rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_FASTEST) || (rgb->chromaUpsampling == AVIF_CHROMA_UPSAMPLING_NEAREST)) + ? kFilterNone + : kFilterBilinear; + if (lcf.yuvToRgbMatrixFilterHighBitDepth != NULL) { + libyuvResult = lcf.yuvToRgbMatrixFilterHighBitDepth((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y] / 2, + (const uint16_t *)image->yuvPlanes[uPlaneIndex], + image->yuvRowBytes[uPlaneIndex] / 2, + (const uint16_t *)image->yuvPlanes[vPlaneIndex], + image->yuvRowBytes[vPlaneIndex] / 2, + rgb->pixels, + rgb->rowBytes, + matrix, + image->width, + image->height, + filter); + } else if (lcf.yuvaToRgbMatrixFilterHighBitDepth != NULL) { + libyuvResult = lcf.yuvaToRgbMatrixFilterHighBitDepth((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y] / 2, + (const uint16_t *)image->yuvPlanes[uPlaneIndex], + image->yuvRowBytes[uPlaneIndex] / 2, + (const uint16_t *)image->yuvPlanes[vPlaneIndex], + image->yuvRowBytes[vPlaneIndex] / 2, + (const uint16_t *)image->alphaPlane, + image->alphaRowBytes / 2, + rgb->pixels, + rgb->rowBytes, + matrix, + image->width, + image->height, + /*attenuate=*/0, + filter); + *alphaReformattedWithLibYUV = AVIF_TRUE; + } else if (lcf.yuvToRgbMatrixHighBitDepth != NULL) { + libyuvResult = lcf.yuvToRgbMatrixHighBitDepth((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y] / 2, + (const uint16_t *)image->yuvPlanes[uPlaneIndex], + image->yuvRowBytes[uPlaneIndex] / 2, + (const uint16_t *)image->yuvPlanes[vPlaneIndex], + image->yuvRowBytes[vPlaneIndex] / 2, + rgb->pixels, + rgb->rowBytes, + matrix, + image->width, + image->height); + } else if (lcf.yuvaToRgbMatrixHighBitDepth != NULL) { + libyuvResult = lcf.yuvaToRgbMatrixHighBitDepth((const uint16_t *)image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y] / 2, + (const uint16_t *)image->yuvPlanes[uPlaneIndex], + image->yuvRowBytes[uPlaneIndex] / 2, + (const uint16_t *)image->yuvPlanes[vPlaneIndex], + image->yuvRowBytes[vPlaneIndex] / 2, + (const uint16_t *)image->alphaPlane, + image->alphaRowBytes / 2, + rgb->pixels, + rgb->rowBytes, + matrix, + image->width, + image->height, + /*attentuate=*/0); + *alphaReformattedWithLibYUV = AVIF_TRUE; + } else { + avifImage image8; + avifBool inputIsHighBitDepth = image->depth > 8; + if (inputIsHighBitDepth) { + const avifBool downshiftAlpha = (lcf.yuvaToRgbMatrixFilter != NULL || lcf.yuvaToRgbMatrix != NULL); + AVIF_CHECKRES(avifImageDownshiftTo8bpc(image, &image8, downshiftAlpha)); + image = &image8; + } + if (lcf.yuv400ToRgbMatrix != NULL) { + libyuvResult = lcf.yuv400ToRgbMatrix(image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y], + rgb->pixels, + rgb->rowBytes, + matrix, + image->width, + image->height); + } else if (lcf.yuvToRgbMatrixFilter != NULL) { + libyuvResult = lcf.yuvToRgbMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y], + image->yuvPlanes[uPlaneIndex], + image->yuvRowBytes[uPlaneIndex], + image->yuvPlanes[vPlaneIndex], + image->yuvRowBytes[vPlaneIndex], + rgb->pixels, + rgb->rowBytes, + matrix, + image->width, + image->height, + filter); + } else if (lcf.yuvaToRgbMatrixFilter != NULL) { + libyuvResult = lcf.yuvaToRgbMatrixFilter(image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y], + image->yuvPlanes[uPlaneIndex], + image->yuvRowBytes[uPlaneIndex], + image->yuvPlanes[vPlaneIndex], + image->yuvRowBytes[vPlaneIndex], + image->alphaPlane, + image->alphaRowBytes, + rgb->pixels, + rgb->rowBytes, + matrix, + image->width, + image->height, + /*attenuate=*/0, + filter); + *alphaReformattedWithLibYUV = AVIF_TRUE; + } else if (lcf.yuvToRgbMatrix != NULL) { + libyuvResult = lcf.yuvToRgbMatrix(image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y], + image->yuvPlanes[uPlaneIndex], + image->yuvRowBytes[uPlaneIndex], + image->yuvPlanes[vPlaneIndex], + image->yuvRowBytes[vPlaneIndex], + rgb->pixels, + rgb->rowBytes, + matrix, + image->width, + image->height); + } else if (lcf.yuvaToRgbMatrix != NULL) { + libyuvResult = lcf.yuvaToRgbMatrix(image->yuvPlanes[AVIF_CHAN_Y], + image->yuvRowBytes[AVIF_CHAN_Y], + image->yuvPlanes[uPlaneIndex], + image->yuvRowBytes[uPlaneIndex], + image->yuvPlanes[vPlaneIndex], + image->yuvRowBytes[vPlaneIndex], + image->alphaPlane, + image->alphaRowBytes, + rgb->pixels, + rgb->rowBytes, + matrix, + image->width, + image->height, + /*attenuate=*/0); + *alphaReformattedWithLibYUV = AVIF_TRUE; + } + if (inputIsHighBitDepth) { + avifImageFreePlanes(&image8, AVIF_PLANES_ALL); + image = NULL; + } + } + return (libyuvResult != 0) ? AVIF_RESULT_REFORMAT_FAILED : AVIF_RESULT_OK; +} + +//-------------------------------------------------------------------------------------------------- + +avifResult avifRGBImagePremultiplyAlphaLibYUV(avifRGBImage * rgb) +{ + // See if the current settings can be accomplished with libyuv, and use it (if possible). + + // The width, height, and stride parameters of libyuv functions are all of the int type. + if (rgb->width > INT_MAX || rgb->height > INT_MAX || rgb->rowBytes > INT_MAX) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (rgb->depth != 8) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + // libavif uses byte-order when describing pixel formats, such that the R in RGBA is the lowest address, + // similar to PNG. libyuv orders in word-order, so libavif's RGBA would be referred to in libyuv as ABGR. + + // Order of RGB doesn't matter here. + if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { + if (ARGBAttenuate(rgb->pixels, rgb->rowBytes, rgb->pixels, rgb->rowBytes, rgb->width, rgb->height) != 0) { + return AVIF_RESULT_REFORMAT_FAILED; + } + return AVIF_RESULT_OK; + } + + return AVIF_RESULT_NOT_IMPLEMENTED; +} + +avifResult avifRGBImageUnpremultiplyAlphaLibYUV(avifRGBImage * rgb) +{ + // See if the current settings can be accomplished with libyuv, and use it (if possible). + + // The width, height, and stride parameters of libyuv functions are all of the int type. + if (rgb->width > INT_MAX || rgb->height > INT_MAX || rgb->rowBytes > INT_MAX) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + if (rgb->depth != 8) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + // libavif uses byte-order when describing pixel formats, such that the R in RGBA is the lowest address, + // similar to PNG. libyuv orders in word-order, so libavif's RGBA would be referred to in libyuv as ABGR. + + if (rgb->format == AVIF_RGB_FORMAT_RGBA || rgb->format == AVIF_RGB_FORMAT_BGRA) { + if (ARGBUnattenuate(rgb->pixels, rgb->rowBytes, rgb->pixels, rgb->rowBytes, rgb->width, rgb->height) != 0) { + return AVIF_RESULT_REFORMAT_FAILED; + } + return AVIF_RESULT_OK; + } + + return AVIF_RESULT_NOT_IMPLEMENTED; +} + +avifResult avifRGBImageToF16LibYUV(avifRGBImage * rgb) +{ + // The width, height, and stride parameters of libyuv functions are all of the int type. + if (rgb->width > INT_MAX || rgb->height > INT_MAX || rgb->rowBytes > INT_MAX) { + return AVIF_RESULT_NOT_IMPLEMENTED; + } + const float scale = 1.0f / ((1 << rgb->depth) - 1); + // Note: HalfFloatPlane requires the stride to be in bytes. + const int result = HalfFloatPlane((const uint16_t *)rgb->pixels, + rgb->rowBytes, + (uint16_t *)rgb->pixels, + rgb->rowBytes, + scale, + rgb->width * avifRGBFormatChannelCount(rgb->format), + rgb->height); + return (result == 0) ? AVIF_RESULT_OK : AVIF_RESULT_INVALID_ARGUMENT; +} + +unsigned int avifLibYUVVersion(void) +{ + return (unsigned int)LIBYUV_VERSION; +} + +#endif diff --git a/3rdparty/libavif/src/sampletransform.c b/3rdparty/libavif/src/sampletransform.c new file mode 100644 index 0000000..8c3188e --- /dev/null +++ b/3rdparty/libavif/src/sampletransform.c @@ -0,0 +1,408 @@ +// Copyright 2024 Google LLC +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include +#include + +//------------------------------------------------------------------------------ +// Convenience functions + +avifBool avifSampleTransformExpressionIsValid(const avifSampleTransformExpression * tokens, uint32_t numInputImageItems) +{ + uint32_t stackSize = 0; + for (uint32_t t = 0; t < tokens->count; ++t) { + const avifSampleTransformToken * token = &tokens->tokens[t]; + AVIF_CHECK(token->type < AVIF_SAMPLE_TRANSFORM_RESERVED); + if (token->type == AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX) { + // inputImageItemIndex is 1-based. + AVIF_CHECK(token->inputImageItemIndex != 0); + AVIF_CHECK(token->inputImageItemIndex <= numInputImageItems); + } + if (token->type < AVIF_SAMPLE_TRANSFORM_FIRST_UNARY_OPERATOR) { + // Likely an operand. + ++stackSize; + } else if (token->type < AVIF_SAMPLE_TRANSFORM_FIRST_BINARY_OPERATOR) { + // Likely a unary operator. + AVIF_CHECK(stackSize >= 1); + // Pop one and push one. + } else { + // Likely a binary operator. + AVIF_CHECK(stackSize >= 2); + --stackSize; // Pop two and push one. + } + } + AVIF_CHECK(stackSize == 1); + return AVIF_TRUE; +} + +avifBool avifSampleTransformExpressionIsEquivalentTo(const avifSampleTransformExpression * a, const avifSampleTransformExpression * b) +{ + if (a->count != b->count) { + return AVIF_FALSE; + } + + for (uint32_t t = 0; t < a->count; ++t) { + const avifSampleTransformToken * aToken = &a->tokens[t]; + const avifSampleTransformToken * bToken = &b->tokens[t]; + if (aToken->type != bToken->type || (aToken->type == AVIF_SAMPLE_TRANSFORM_CONSTANT && aToken->constant != bToken->constant)) { + return AVIF_FALSE; + } + // For AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX, no need to compare inputImageItemIndex + // because these are variables in the expression. + } + return AVIF_TRUE; +} + +//------------------------------------------------------------------------------ +// Recipe to expression + +static avifBool avifPushConstant(avifSampleTransformExpression * expression, int32_t constant) +{ + avifSampleTransformToken * token = (avifSampleTransformToken *)avifArrayPush(expression); + if (token == NULL) { + return AVIF_FALSE; + } + token->type = AVIF_SAMPLE_TRANSFORM_CONSTANT; + token->constant = constant; + return AVIF_TRUE; +} +static avifBool avifPushInputImageItem(avifSampleTransformExpression * expression, uint8_t inputImageItemIndex) +{ + avifSampleTransformToken * token = (avifSampleTransformToken *)avifArrayPush(expression); + if (token == NULL) { + return AVIF_FALSE; + } + token->type = AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX; + token->inputImageItemIndex = inputImageItemIndex; + return AVIF_TRUE; +} +static avifBool avifPushOperator(avifSampleTransformExpression * expression, avifSampleTransformTokenType operator) +{ + avifSampleTransformToken * token = (avifSampleTransformToken *)avifArrayPush(expression); + if (token == NULL) { + return AVIF_FALSE; + } + token->type = (uint8_t)operator; + return AVIF_TRUE; +} + +avifResult avifSampleTransformRecipeToExpression(avifSampleTransformRecipe recipe, avifSampleTransformExpression * expression) +{ + // Postfix (or Reverse Polish) notation. Brackets to highlight sub-expressions. + + if (recipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B) { + // reference_count is two: two 8-bit input images. + // (base_sample << 8) | hidden_sample + // Note: base_sample is encoded losslessly. hidden_sample is encoded lossily or losslessly. + AVIF_CHECKERR(avifArrayCreate(expression, sizeof(avifSampleTransformToken), 5), AVIF_RESULT_OUT_OF_MEMORY); + + { + // The base image represents the 8 most significant bits of the reconstructed, bit-depth-extended output image. + // Left shift the base image (which is also the primary item, or the auxiliary alpha item of the primary item) + // by 8 bits. This is equivalent to multiplying by 2^8. + AVIF_ASSERT_OR_RETURN(avifPushConstant(expression, 256)); + AVIF_ASSERT_OR_RETURN(avifPushInputImageItem(expression, 1)); + AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_PRODUCT)); + } + { + // The second image represents the 8 least significant bits of the reconstructed, bit-depth-extended output image. + AVIF_ASSERT_OR_RETURN(avifPushInputImageItem(expression, 2)); + } + AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_OR)); + return AVIF_RESULT_OK; + } + + if (recipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B) { + // reference_count is two: one 12-bit input image and one 8-bit input image (because AV1 does not support 4-bit samples). + // (base_sample << 4) | (hidden_sample >> 4) + // Note: base_sample is encoded losslessly. hidden_sample is encoded lossily or losslessly. + AVIF_CHECKERR(avifArrayCreate(expression, sizeof(avifSampleTransformToken), 7), AVIF_RESULT_OUT_OF_MEMORY); + + { + // The base image represents the 12 most significant bits of the reconstructed, bit-depth-extended output image. + // Left shift the base image (which is also the primary item, or the auxiliary alpha item of the primary item) + // by 4 bits. This is equivalent to multiplying by 2^4. + AVIF_ASSERT_OR_RETURN(avifPushConstant(expression, 16)); + AVIF_ASSERT_OR_RETURN(avifPushInputImageItem(expression, 1)); + AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_PRODUCT)); + } + { + // The second image represents the 4 least significant bits of the reconstructed, bit-depth-extended output image. + AVIF_ASSERT_OR_RETURN(avifPushInputImageItem(expression, 2)); + AVIF_ASSERT_OR_RETURN(avifPushConstant(expression, 16)); + AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_QUOTIENT)); + } + AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_SUM)); + return AVIF_RESULT_OK; + } + + if (recipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_8B_OVERLAP_4B) { + // reference_count is two: one 12-bit input image and one 8-bit input image. + // (base_sample << 4) + hidden_sample + // Note: Both base_sample and hidden_sample are encoded lossily or losslessly. hidden_sample overlaps + // with base_sample by 4 bits to alleviate the loss caused by the quantization of base_sample. + AVIF_CHECKERR(avifArrayCreate(expression, sizeof(avifSampleTransformToken), 7), AVIF_RESULT_OUT_OF_MEMORY); + + // The base image represents the 12 most significant bits of the reconstructed, bit-depth-extended output image. + // Left shift the base image (which is also the primary item, or the auxiliary alpha item of the primary item) + // by 4 bits. This is equivalent to multiplying by 2^4. + AVIF_ASSERT_OR_RETURN(avifPushConstant(expression, 16)); + AVIF_ASSERT_OR_RETURN(avifPushInputImageItem(expression, 1)); + AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_PRODUCT)); + + // The second image represents the offset to apply to the shifted base image to retrieve + // the original image, with some loss due to quantization. + AVIF_ASSERT_OR_RETURN(avifPushInputImageItem(expression, 2)); + AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_SUM)); + + // The second image is offset by 128 to have unsigned values to encode. + // Correct that last to always work with unsigned values in the operations above. + AVIF_ASSERT_OR_RETURN(avifPushConstant(expression, 128)); + AVIF_ASSERT_OR_RETURN(avifPushOperator(expression, AVIF_SAMPLE_TRANSFORM_DIFFERENCE)); + // Sample values are clamped to [0:1<= INT32_MAX ? INT32_MAX : (int32_t)value; +} + +static int32_t avifSampleTransformOperation32bOneOperand(int32_t operand, uint8_t operator) +{ + switch (operator) { + case AVIF_SAMPLE_TRANSFORM_NEGATION: + return avifSampleTransformClamp32b(-(int64_t)operand); + case AVIF_SAMPLE_TRANSFORM_ABSOLUTE: + return operand >= 0 ? operand : avifSampleTransformClamp32b(-(int64_t)operand); + case AVIF_SAMPLE_TRANSFORM_NOT: + return ~operand; + case AVIF_SAMPLE_TRANSFORM_BSR: { + if (operand <= 0) { + return 0; + } + int32_t log2 = 0; + operand >>= 1; + for (; operand != 0; ++log2) { + operand >>= 1; + } + return log2; + } + default: + assert(AVIF_FALSE); + } + return 0; +} + +static int32_t avifSampleTransformOperation32bTwoOperands(int32_t leftOperand, int32_t rightOperand, uint8_t operator) +{ + switch (operator) { + case AVIF_SAMPLE_TRANSFORM_SUM: + return avifSampleTransformClamp32b((int64_t)leftOperand + rightOperand); + case AVIF_SAMPLE_TRANSFORM_DIFFERENCE: + return avifSampleTransformClamp32b((int64_t)leftOperand - rightOperand); + case AVIF_SAMPLE_TRANSFORM_PRODUCT: + return avifSampleTransformClamp32b((int64_t)leftOperand * rightOperand); + case AVIF_SAMPLE_TRANSFORM_QUOTIENT: + return rightOperand == 0 ? leftOperand : avifSampleTransformClamp32b((int64_t)leftOperand / rightOperand); + case AVIF_SAMPLE_TRANSFORM_AND: + return leftOperand & rightOperand; + case AVIF_SAMPLE_TRANSFORM_OR: + return leftOperand | rightOperand; + case AVIF_SAMPLE_TRANSFORM_XOR: + return leftOperand ^ rightOperand; + case AVIF_SAMPLE_TRANSFORM_POW: { + if (leftOperand == 0 || leftOperand == 1) { + return leftOperand; + } + if (leftOperand == -1) { + return (rightOperand % 2 == 0) ? 1 : -1; + } + if (rightOperand == 0) { + return 1; + } + if (rightOperand == 1) { + return leftOperand; + } + if (rightOperand < 0) { + // L^R is in ]-1:1[ here, so truncating it always gives 0. + return 0; + } + int64_t result = leftOperand; + for (int32_t i = 1; i < rightOperand; ++i) { + result *= leftOperand; + if (result < INT32_MIN || result > INT32_MAX) { + return (leftOperand > 0 || rightOperand % 2 == 0) ? INT32_MAX : INT32_MIN; + } + } + return (int32_t)result; + } + case AVIF_SAMPLE_TRANSFORM_MIN: + return leftOperand <= rightOperand ? leftOperand : rightOperand; + case AVIF_SAMPLE_TRANSFORM_MAX: + return leftOperand <= rightOperand ? rightOperand : leftOperand; + default: + assert(AVIF_FALSE); + } + return 0; +} + +//------------------------------------------------------------------------------ +// Expression + +AVIF_ARRAY_DECLARE(avifSampleTransformStack32b, int32_t, elements); + +static avifResult avifImageApplyExpression32b(avifImage * dstImage, + const avifSampleTransformExpression * expression, + const avifImage * inputImageItems[], + avifPlanesFlags planes, + int32_t * stack, + uint32_t stackCapacity) +{ + // This slow path could be avoided by recognizing the recipe thanks to avifSampleTransformExpressionToRecipe() + // and having a dedicated optimized implementation for each recipe. + + const int32_t minValue = 0; + const int32_t maxValue = (1 << dstImage->depth) - 1; + + const avifBool skipColor = !(planes & AVIF_PLANES_YUV); + const avifBool skipAlpha = !(planes & AVIF_PLANES_A); + for (int c = AVIF_CHAN_Y; c <= AVIF_CHAN_A; ++c) { + const avifBool alpha = c == AVIF_CHAN_A; + if ((skipColor && !alpha) || (skipAlpha && alpha)) { + continue; + } + + const uint32_t planeWidth = avifImagePlaneWidth(dstImage, c); + const uint32_t planeHeight = avifImagePlaneHeight(dstImage, c); + for (uint32_t y = 0; y < planeHeight; ++y) { + for (uint32_t x = 0; x < planeWidth; ++x) { + uint32_t stackSize = 0; + for (uint32_t t = 0; t < expression->count; ++t) { + const avifSampleTransformToken * token = &expression->tokens[t]; + if (token->type == AVIF_SAMPLE_TRANSFORM_CONSTANT) { + AVIF_ASSERT_OR_RETURN(stackSize < stackCapacity); + stack[stackSize++] = token->constant; + } else if (token->type == AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX) { + const avifImage * image = inputImageItems[token->inputImageItemIndex - 1]; // 1-based + const uint8_t * row = avifImagePlane(image, c); + AVIF_ASSERT_OR_RETURN(row != NULL); + row += (size_t)avifImagePlaneRowBytes(image, c) * y; + AVIF_ASSERT_OR_RETURN(stackSize < stackCapacity); + stack[stackSize++] = avifImageUsesU16(image) ? ((const uint16_t *)row)[x] : row[x]; + } else if (token->type == AVIF_SAMPLE_TRANSFORM_NEGATION || token->type == AVIF_SAMPLE_TRANSFORM_ABSOLUTE || + token->type == AVIF_SAMPLE_TRANSFORM_NOT || token->type == AVIF_SAMPLE_TRANSFORM_BSR) { + AVIF_ASSERT_OR_RETURN(stackSize >= 1); + stack[stackSize - 1] = avifSampleTransformOperation32bOneOperand(stack[stackSize - 1], token->type); + // Pop one and push one. + } else { + AVIF_ASSERT_OR_RETURN(stackSize >= 2); + stack[stackSize - 2] = + avifSampleTransformOperation32bTwoOperands(stack[stackSize - 2], stack[stackSize - 1], token->type); + stackSize--; // Pop two and push one. + } + } + AVIF_ASSERT_OR_RETURN(stackSize == 1); + // Fit to the range defined by the PixelInformationProperty. + // The limited/full range is ignored, like in other libavif encoding and decoding paths. + stack[0] = AVIF_CLAMP(stack[0], minValue, maxValue); + + uint8_t * row = avifImagePlane(dstImage, c); + AVIF_ASSERT_OR_RETURN(row != NULL); + row += (size_t)avifImagePlaneRowBytes(dstImage, c) * y; + if (avifImageUsesU16(dstImage)) { + ((uint16_t *)row)[x] = (uint16_t)stack[0]; + } else { + row[x] = (uint8_t)stack[0]; + } + } + } + } + return AVIF_RESULT_OK; +} + +avifResult avifImageApplyExpression(avifImage * dstImage, + avifSampleTransformBitDepth bitDepth, + const avifSampleTransformExpression * expression, + uint8_t numInputImageItems, + const avifImage * inputImageItems[], + avifPlanesFlags planes) +{ + // Check that the expression is valid. + AVIF_ASSERT_OR_RETURN(avifSampleTransformExpressionIsValid(expression, numInputImageItems)); + const avifBool skipColor = !(planes & AVIF_PLANES_YUV); + const avifBool skipAlpha = !(planes & AVIF_PLANES_A); + for (int c = AVIF_CHAN_Y; c <= AVIF_CHAN_A; ++c) { + const avifBool alpha = c == AVIF_CHAN_A; + if ((skipColor && !alpha) || (skipAlpha && alpha)) { + continue; + } + + const uint32_t planeWidth = avifImagePlaneWidth(dstImage, c); + const uint32_t planeHeight = avifImagePlaneHeight(dstImage, c); + for (uint32_t i = 0; i < numInputImageItems; ++i) { + AVIF_CHECKERR(avifImagePlaneWidth(inputImageItems[i], c) == planeWidth, AVIF_RESULT_BMFF_PARSE_FAILED); + AVIF_CHECKERR(avifImagePlaneHeight(inputImageItems[i], c) == planeHeight, AVIF_RESULT_BMFF_PARSE_FAILED); + } + } + + // Then apply it. This part should not fail except for memory shortage reasons. + if (bitDepth == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32) { + uint32_t stackCapacity = expression->count / 2 + 1; + int32_t * stack = avifAlloc(stackCapacity * sizeof(int32_t)); + AVIF_CHECKERR(stack != NULL, AVIF_RESULT_OUT_OF_MEMORY); + const avifResult result = avifImageApplyExpression32b(dstImage, expression, inputImageItems, planes, stack, stackCapacity); + avifFree(stack); + return result; + } + return AVIF_RESULT_NOT_IMPLEMENTED; +} + +avifResult avifImageApplyOperations(avifImage * dstImage, + avifSampleTransformBitDepth bitDepth, + uint32_t numTokens, + const avifSampleTransformToken tokens[], + uint8_t numInputImageItems, + const avifImage * inputImageItems[], + avifPlanesFlags planes) +{ + avifSampleTransformExpression expression = { 0 }; + AVIF_CHECKERR(avifArrayCreate(&expression, sizeof(avifSampleTransformToken), numTokens), AVIF_RESULT_OUT_OF_MEMORY); + for (uint32_t t = 0; t < numTokens; ++t) { + avifSampleTransformToken * token = (avifSampleTransformToken *)avifArrayPush(&expression); + AVIF_ASSERT_OR_RETURN(token != NULL); + *token = tokens[t]; + } + const avifResult result = avifImageApplyExpression(dstImage, bitDepth, &expression, numInputImageItems, inputImageItems, planes); + avifArrayDestroy(&expression); + return result; +} diff --git a/3rdparty/libavif/src/scale.c b/3rdparty/libavif/src/scale.c new file mode 100644 index 0000000..777eca4 --- /dev/null +++ b/3rdparty/libavif/src/scale.c @@ -0,0 +1,201 @@ +// Copyright 2021 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" +#include + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wstrict-prototypes" // "this function declaration is not a prototype" +// The newline at the end of libyuv/version.h was accidentally deleted in version 1792 and restored +// in version 1813: +// https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3183182 +// https://chromium-review.googlesource.com/c/libyuv/libyuv/+/3527834 +#pragma clang diagnostic ignored "-Wnewline-eof" // "no newline at end of file" +#endif +#include +#if defined(__clang__) +#pragma clang diagnostic pop +#endif + +// This should be configurable and/or smarter. kFilterBox has the highest quality but is the slowest. +#define AVIF_LIBYUV_FILTER_MODE kFilterBox + +avifResult avifImageScaleWithLimit(avifImage * image, + uint32_t dstWidth, + uint32_t dstHeight, + uint32_t imageSizeLimit, + uint32_t imageDimensionLimit, + avifDiagnostics * diag) +{ + if ((image->width == dstWidth) && (image->height == dstHeight)) { + // Nothing to do + return AVIF_RESULT_OK; + } + + if ((dstWidth == 0) || (dstHeight == 0)) { + avifDiagnosticsPrintf(diag, "avifImageScaleWithLimit requested invalid dst dimensions [%ux%u]", dstWidth, dstHeight); + return AVIF_RESULT_INVALID_ARGUMENT; + } + if (avifDimensionsTooLarge(dstWidth, dstHeight, imageSizeLimit, imageDimensionLimit)) { + avifDiagnosticsPrintf(diag, "avifImageScaleWithLimit requested dst dimensions that are too large [%ux%u]", dstWidth, dstHeight); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + uint8_t * srcYUVPlanes[AVIF_PLANE_COUNT_YUV]; + uint32_t srcYUVRowBytes[AVIF_PLANE_COUNT_YUV]; + for (int i = 0; i < AVIF_PLANE_COUNT_YUV; ++i) { + srcYUVPlanes[i] = image->yuvPlanes[i]; + image->yuvPlanes[i] = NULL; + srcYUVRowBytes[i] = image->yuvRowBytes[i]; + image->yuvRowBytes[i] = 0; + } + const avifBool srcImageOwnsYUVPlanes = image->imageOwnsYUVPlanes; + image->imageOwnsYUVPlanes = AVIF_FALSE; + + uint8_t * srcAlphaPlane = image->alphaPlane; + image->alphaPlane = NULL; + uint32_t srcAlphaRowBytes = image->alphaRowBytes; + image->alphaRowBytes = 0; + const avifBool srcImageOwnsAlphaPlane = image->imageOwnsAlphaPlane; + image->imageOwnsAlphaPlane = AVIF_FALSE; + + const uint32_t srcWidth = image->width; + const uint32_t srcHeight = image->height; + const uint32_t srcUVWidth = avifImagePlaneWidth(image, AVIF_CHAN_U); + const uint32_t srcUVHeight = avifImagePlaneHeight(image, AVIF_CHAN_U); + image->width = dstWidth; + image->height = dstHeight; + + avifResult result = AVIF_RESULT_OK; + if (srcYUVPlanes[0] || srcAlphaPlane) { + // A simple conservative check to avoid integer overflows in libyuv's ScalePlane() and + // ScalePlane_12() functions. + if (srcWidth > 16384) { + avifDiagnosticsPrintf(diag, "avifImageScaleWithLimit requested invalid width scale for libyuv [%u -> %u]", srcWidth, dstWidth); + result = AVIF_RESULT_NOT_IMPLEMENTED; + goto cleanup; + } + if (srcHeight > 16384) { + avifDiagnosticsPrintf(diag, "avifImageScaleWithLimit requested invalid height scale for libyuv [%u -> %u]", srcHeight, dstHeight); + result = AVIF_RESULT_NOT_IMPLEMENTED; + goto cleanup; + } + } + + if (srcYUVPlanes[0]) { + const avifResult allocationResult = avifImageAllocatePlanes(image, AVIF_PLANES_YUV); + if (allocationResult != AVIF_RESULT_OK) { + avifDiagnosticsPrintf(diag, "Allocation of YUV planes failed: %s", avifResultToString(allocationResult)); + result = AVIF_RESULT_OUT_OF_MEMORY; + goto cleanup; + } + + for (int i = 0; i < AVIF_PLANE_COUNT_YUV; ++i) { + if (!srcYUVPlanes[i]) { + continue; + } + + const uint32_t srcW = (i == AVIF_CHAN_Y) ? srcWidth : srcUVWidth; + const uint32_t srcH = (i == AVIF_CHAN_Y) ? srcHeight : srcUVHeight; + const uint32_t dstW = avifImagePlaneWidth(image, i); + const uint32_t dstH = avifImagePlaneHeight(image, i); + if (image->depth > 8) { + uint16_t * const srcPlane = (uint16_t *)srcYUVPlanes[i]; + const uint32_t srcStride = srcYUVRowBytes[i] / 2; + uint16_t * const dstPlane = (uint16_t *)image->yuvPlanes[i]; + const uint32_t dstStride = image->yuvRowBytes[i] / 2; +#if LIBYUV_VERSION >= 1880 + const int failure = + ScalePlane_12(srcPlane, srcStride, srcW, srcH, dstPlane, dstStride, dstW, dstH, AVIF_LIBYUV_FILTER_MODE); + if (failure) { + avifDiagnosticsPrintf(diag, "ScalePlane_12() failed (%d)", failure); + result = (failure == 1) ? AVIF_RESULT_OUT_OF_MEMORY : AVIF_RESULT_UNKNOWN_ERROR; + goto cleanup; + } +#elif LIBYUV_VERSION >= 1774 + ScalePlane_12(srcPlane, srcStride, srcW, srcH, dstPlane, dstStride, dstW, dstH, AVIF_LIBYUV_FILTER_MODE); +#else + ScalePlane_16(srcPlane, srcStride, srcW, srcH, dstPlane, dstStride, dstW, dstH, AVIF_LIBYUV_FILTER_MODE); +#endif + } else { + uint8_t * const srcPlane = srcYUVPlanes[i]; + const uint32_t srcStride = srcYUVRowBytes[i]; + uint8_t * const dstPlane = image->yuvPlanes[i]; + const uint32_t dstStride = image->yuvRowBytes[i]; +#if LIBYUV_VERSION >= 1880 + const int failure = ScalePlane(srcPlane, srcStride, srcW, srcH, dstPlane, dstStride, dstW, dstH, AVIF_LIBYUV_FILTER_MODE); + if (failure) { + avifDiagnosticsPrintf(diag, "ScalePlane() failed (%d)", failure); + result = (failure == 1) ? AVIF_RESULT_OUT_OF_MEMORY : AVIF_RESULT_UNKNOWN_ERROR; + goto cleanup; + } +#else + ScalePlane(srcPlane, srcStride, srcW, srcH, dstPlane, dstStride, dstW, dstH, AVIF_LIBYUV_FILTER_MODE); +#endif + } + } + } + + if (srcAlphaPlane) { + const avifResult allocationResult = avifImageAllocatePlanes(image, AVIF_PLANES_A); + if (allocationResult != AVIF_RESULT_OK) { + avifDiagnosticsPrintf(diag, "Allocation of alpha plane failed: %s", avifResultToString(allocationResult)); + result = AVIF_RESULT_OUT_OF_MEMORY; + goto cleanup; + } + + if (image->depth > 8) { + uint16_t * const srcPlane = (uint16_t *)srcAlphaPlane; + const uint32_t srcStride = srcAlphaRowBytes / 2; + uint16_t * const dstPlane = (uint16_t *)image->alphaPlane; + const uint32_t dstStride = image->alphaRowBytes / 2; +#if LIBYUV_VERSION >= 1880 + const int failure = + ScalePlane_12(srcPlane, srcStride, srcWidth, srcHeight, dstPlane, dstStride, dstWidth, dstHeight, AVIF_LIBYUV_FILTER_MODE); + if (failure) { + avifDiagnosticsPrintf(diag, "ScalePlane_12() failed (%d)", failure); + result = (failure == 1) ? AVIF_RESULT_OUT_OF_MEMORY : AVIF_RESULT_UNKNOWN_ERROR; + goto cleanup; + } +#elif LIBYUV_VERSION >= 1774 + ScalePlane_12(srcPlane, srcStride, srcWidth, srcHeight, dstPlane, dstStride, dstWidth, dstHeight, AVIF_LIBYUV_FILTER_MODE); +#else + ScalePlane_16(srcPlane, srcStride, srcWidth, srcHeight, dstPlane, dstStride, dstWidth, dstHeight, AVIF_LIBYUV_FILTER_MODE); +#endif + } else { + uint8_t * const srcPlane = srcAlphaPlane; + const uint32_t srcStride = srcAlphaRowBytes; + uint8_t * const dstPlane = image->alphaPlane; + const uint32_t dstStride = image->alphaRowBytes; +#if LIBYUV_VERSION >= 1880 + const int failure = + ScalePlane(srcPlane, srcStride, srcWidth, srcHeight, dstPlane, dstStride, dstWidth, dstHeight, AVIF_LIBYUV_FILTER_MODE); + if (failure) { + avifDiagnosticsPrintf(diag, "ScalePlane() failed (%d)", failure); + result = (failure == 1) ? AVIF_RESULT_OUT_OF_MEMORY : AVIF_RESULT_UNKNOWN_ERROR; + goto cleanup; + } +#else + ScalePlane(srcPlane, srcStride, srcWidth, srcHeight, dstPlane, dstStride, dstWidth, dstHeight, AVIF_LIBYUV_FILTER_MODE); +#endif + } + } + +cleanup: + if (srcYUVPlanes[0] && srcImageOwnsYUVPlanes) { + for (int i = 0; i < AVIF_PLANE_COUNT_YUV; ++i) { + avifFree(srcYUVPlanes[i]); + } + } + if (srcAlphaPlane && srcImageOwnsAlphaPlane) { + avifFree(srcAlphaPlane); + } + return result; +} + +avifResult avifImageScale(avifImage * image, uint32_t dstWidth, uint32_t dstHeight, avifDiagnostics * diag) +{ + avifDiagnosticsClearError(diag); + return avifImageScaleWithLimit(image, dstWidth, dstHeight, AVIF_DEFAULT_IMAGE_SIZE_LIMIT, AVIF_DEFAULT_IMAGE_DIMENSION_LIMIT, diag); +} diff --git a/3rdparty/libavif/src/stream.c b/3rdparty/libavif/src/stream.c new file mode 100644 index 0000000..56965a8 --- /dev/null +++ b/3rdparty/libavif/src/stream.c @@ -0,0 +1,524 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include +#include +#include + +// --------------------------------------------------------------------------- +// avifROStream + +const uint8_t * avifROStreamCurrent(avifROStream * stream) +{ + return stream->raw->data + stream->offset; +} + +void avifROStreamStart(avifROStream * stream, avifROData * raw, avifDiagnostics * diag, const char * diagContext) +{ + stream->raw = raw; + stream->offset = 0; + stream->numUsedBitsInPartialByte = 0; + stream->diag = diag; + stream->diagContext = diagContext; + + // If diag is non-NULL, diagContext must also be non-NULL + assert(!stream->diag || stream->diagContext); +} + +avifBool avifROStreamHasBytesLeft(const avifROStream * stream, size_t byteCount) +{ + return byteCount <= (stream->raw->size - stream->offset); +} + +size_t avifROStreamRemainingBytes(const avifROStream * stream) +{ + return stream->raw->size - stream->offset; +} + +size_t avifROStreamOffset(const avifROStream * stream) +{ + return stream->offset; +} + +void avifROStreamSetOffset(avifROStream * stream, size_t offset) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + stream->offset = offset; + if (stream->offset > stream->raw->size) { + stream->offset = stream->raw->size; + } +} + +avifBool avifROStreamSkip(avifROStream * stream, size_t byteCount) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + if (!avifROStreamHasBytesLeft(stream, byteCount)) { + avifDiagnosticsPrintf(stream->diag, "%s: Failed to skip %zu bytes, truncated data?", stream->diagContext, byteCount); + return AVIF_FALSE; + } + stream->offset += byteCount; + return AVIF_TRUE; +} + +avifBool avifROStreamRead(avifROStream * stream, uint8_t * data, size_t size) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + if (!avifROStreamHasBytesLeft(stream, size)) { + avifDiagnosticsPrintf(stream->diag, "%s: Failed to read %zu bytes, truncated data?", stream->diagContext, size); + return AVIF_FALSE; + } + + memcpy(data, stream->raw->data + stream->offset, size); + stream->offset += size; + return AVIF_TRUE; +} + +avifBool avifROStreamReadUX8(avifROStream * stream, uint64_t * v, uint64_t factor) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + if (factor == 0) { + // Don't read anything, just set to 0 + *v = 0; + } else if (factor == 1) { + uint8_t tmp; + AVIF_CHECK(avifROStreamRead(stream, &tmp, 1)); + *v = tmp; + } else if (factor == 2) { + uint16_t tmp; + AVIF_CHECK(avifROStreamReadU16(stream, &tmp)); + *v = tmp; + } else if (factor == 4) { + uint32_t tmp; + AVIF_CHECK(avifROStreamReadU32(stream, &tmp)); + *v = tmp; + } else if (factor == 8) { + uint64_t tmp; + AVIF_CHECK(avifROStreamReadU64(stream, &tmp)); + *v = tmp; + } else { + // Unsupported factor + avifDiagnosticsPrintf(stream->diag, "%s: Failed to read UX8 value; Unsupported UX8 factor [%" PRIu64 "]", stream->diagContext, factor); + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +avifBool avifROStreamReadU16(avifROStream * stream, uint16_t * v) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint16_t))); + *v = avifNTOHS(*v); + return AVIF_TRUE; +} + +avifBool avifROStreamReadU16Endianness(avifROStream * stream, uint16_t * v, avifBool littleEndian) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint16_t))); + *v = littleEndian ? avifCTOHS(*v) : avifNTOHS(*v); + return AVIF_TRUE; +} + +avifBool avifROStreamReadU32(avifROStream * stream, uint32_t * v) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint32_t))); + *v = avifNTOHL(*v); + return AVIF_TRUE; +} + +avifBool avifROStreamReadU32Endianness(avifROStream * stream, uint32_t * v, avifBool littleEndian) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint32_t))); + *v = littleEndian ? avifCTOHL(*v) : avifNTOHL(*v); + return AVIF_TRUE; +} + +avifBool avifROStreamReadU64(avifROStream * stream, uint64_t * v) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + AVIF_CHECK(avifROStreamRead(stream, (uint8_t *)v, sizeof(uint64_t))); + *v = avifNTOH64(*v); + return AVIF_TRUE; +} + +avifBool avifROStreamSkipBits(avifROStream * stream, size_t bitCount) +{ + if (stream->numUsedBitsInPartialByte != 0) { + assert(stream->numUsedBitsInPartialByte < 8); + const size_t padding = AVIF_MIN(8 - stream->numUsedBitsInPartialByte, bitCount); + stream->numUsedBitsInPartialByte = (stream->numUsedBitsInPartialByte + padding) % 8; + bitCount -= padding; + if (bitCount == 0) { + return AVIF_TRUE; + } + } + const size_t num_bytes = (bitCount + 7) / 8; + AVIF_CHECK(avifROStreamSkip(stream, num_bytes)); + stream->numUsedBitsInPartialByte = bitCount % 8; + return AVIF_TRUE; +} + +avifBool avifROStreamReadBitsU8(avifROStream * stream, uint8_t * v, size_t bitCount) +{ + AVIF_CHECK(bitCount <= sizeof(*v) * 8); + uint32_t vU32; + AVIF_CHECK(avifROStreamReadBitsU32(stream, &vU32, bitCount)); + *v = (uint8_t)vU32; + return AVIF_TRUE; +} + +avifBool avifROStreamReadBitsU16(avifROStream * stream, uint16_t * v, size_t bitCount) +{ + AVIF_CHECK(bitCount <= sizeof(*v) * 8); + uint32_t vU32; + AVIF_CHECK(avifROStreamReadBitsU32(stream, &vU32, bitCount)); + *v = (uint16_t)vU32; + return AVIF_TRUE; +} + +avifBool avifROStreamReadBitsU32(avifROStream * stream, uint32_t * v, size_t bitCount) +{ + AVIF_CHECK(bitCount <= sizeof(*v) * 8); + *v = 0; + while (bitCount) { + if (stream->numUsedBitsInPartialByte == 0) { + AVIF_CHECK(avifROStreamSkip(stream, sizeof(uint8_t))); // Book a new partial byte in the stream. + } + assert(stream->offset > 0); + const uint8_t * packedBits = stream->raw->data + stream->offset - 1; + + const size_t numBits = AVIF_MIN(bitCount, 8 - stream->numUsedBitsInPartialByte); + stream->numUsedBitsInPartialByte += numBits; + bitCount -= numBits; + // The stream bits are packed starting with the most significant bit of the first input byte. + // This way, packed bits can be found in the same order in the bit stream. + const uint32_t bits = (*packedBits >> (8 - stream->numUsedBitsInPartialByte)) & ((1 << numBits) - 1); + // The value bits are ordered from the most significant bit to the least significant bit. + // In the case where avifROStreamReadBitsU32() is used to parse the unsigned integer value *v + // over multiple aligned bytes, this order corresponds to big endianness. + *v |= bits << bitCount; + + if (stream->numUsedBitsInPartialByte == 8) { + // Start a new partial byte the next time a bit is needed. + stream->numUsedBitsInPartialByte = 0; + } + } + return AVIF_TRUE; +} + +avifBool avifROStreamReadString(avifROStream * stream, char * output, size_t outputSize) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + + // Check for the presence of a null terminator in the stream. + size_t remainingBytes = avifROStreamRemainingBytes(stream); + const uint8_t * p = avifROStreamCurrent(stream); + avifBool foundNullTerminator = AVIF_FALSE; + for (size_t i = 0; i < remainingBytes; ++i) { + if (p[i] == 0) { + foundNullTerminator = AVIF_TRUE; + break; + } + } + if (!foundNullTerminator) { + avifDiagnosticsPrintf(stream->diag, "%s: Failed to find a NULL terminator when reading a string", stream->diagContext); + return AVIF_FALSE; + } + + const char * streamString = (const char *)p; + size_t stringLen = strlen(streamString); + stream->offset += stringLen + 1; // update the stream to have read the "whole string" in + + if (output && outputSize) { + // clamp to our output buffer + if (stringLen >= outputSize) { + stringLen = outputSize - 1; + } + memcpy(output, streamString, stringLen); + output[stringLen] = 0; + } + return AVIF_TRUE; +} + +avifBool avifROStreamReadBoxHeaderPartial(avifROStream * stream, avifBoxHeader * header, avifBool topLevel) +{ + // Section 4.2.2 of ISO/IEC 14496-12. + size_t startOffset = stream->offset; + + uint32_t smallSize; + AVIF_CHECK(avifROStreamReadU32(stream, &smallSize)); // unsigned int(32) size; + AVIF_CHECK(avifROStreamRead(stream, header->type, 4)); // unsigned int(32) type = boxtype; + + uint64_t size = smallSize; + if (size == 1) { + AVIF_CHECK(avifROStreamReadU64(stream, &size)); // unsigned int(64) largesize; + } + + if (!memcmp(header->type, "uuid", 4)) { + AVIF_CHECK(avifROStreamRead(stream, header->usertype, 16)); // unsigned int(8) usertype[16] = extended_type; + } else { + memset(header->usertype, 0, sizeof(header->usertype)); + } + + size_t bytesRead = stream->offset - startOffset; + if (size == 0) { + // Section 4.2.2 of ISO/IEC 14496-12. + // if size is 0, then this box shall be in a top-level box (i.e. not contained in another + // box), and be the last box in its 'file', and its payload extends to the end of that + // enclosing 'file'. This is normally only used for a MediaDataBox ('mdat'). + if (!topLevel) { + avifDiagnosticsPrintf(stream->diag, "%s: Non-top-level box with size 0", stream->diagContext); + return AVIF_FALSE; + } + + // The given stream may be incomplete and there is no guarantee that sizeHint is available and accurate. + // Otherwise size could be set to avifROStreamRemainingBytes(stream) + (stream->offset - startOffset) right now. + + // Wait for avifIOReadFunc() to return AVIF_RESULT_OK. + header->isSizeZeroBox = AVIF_TRUE; + header->size = 0; + return AVIF_TRUE; + } + + if ((size < bytesRead) || ((size - bytesRead) > SIZE_MAX)) { + avifDiagnosticsPrintf(stream->diag, "%s: Header size overflow check failure", stream->diagContext); + return AVIF_FALSE; + } + header->isSizeZeroBox = AVIF_FALSE; + header->size = (size_t)(size - bytesRead); + return AVIF_TRUE; +} + +avifBool avifROStreamReadBoxHeader(avifROStream * stream, avifBoxHeader * header) +{ + AVIF_CHECK(avifROStreamReadBoxHeaderPartial(stream, header, /*topLevel=*/AVIF_FALSE)); + if (header->size > avifROStreamRemainingBytes(stream)) { + avifDiagnosticsPrintf(stream->diag, "%s: Child box too large, possibly truncated data", stream->diagContext); + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +avifBool avifROStreamReadVersionAndFlags(avifROStream * stream, uint8_t * version, uint32_t * flags) +{ + uint8_t versionAndFlags[4]; + AVIF_CHECK(avifROStreamRead(stream, versionAndFlags, 4)); + if (version) { + *version = versionAndFlags[0]; + } + if (flags) { + *flags = (versionAndFlags[1] << 16) + (versionAndFlags[2] << 8) + (versionAndFlags[3] << 0); + } + return AVIF_TRUE; +} + +avifBool avifROStreamReadAndEnforceVersion(avifROStream * stream, uint8_t enforcedVersion, uint32_t * flags) +{ + uint8_t version; + AVIF_CHECK(avifROStreamReadVersionAndFlags(stream, &version, flags)); + if (version != enforcedVersion) { + avifDiagnosticsPrintf(stream->diag, "%s: Expecting box version %u, got version %u", stream->diagContext, enforcedVersion, version); + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +// --------------------------------------------------------------------------- +// avifRWStream + +#define AVIF_STREAM_BUFFER_INCREMENT (1024 * 1024) +static avifResult makeRoom(avifRWStream * stream, size_t size) +{ + AVIF_CHECKERR(size <= SIZE_MAX - stream->offset, AVIF_RESULT_OUT_OF_MEMORY); + size_t newSize = stream->offset + size; + if (newSize <= stream->raw->size) { + return AVIF_RESULT_OK; + } + // Make newSize a multiple of AVIF_STREAM_BUFFER_INCREMENT. + size_t rem = newSize % AVIF_STREAM_BUFFER_INCREMENT; + size_t padding = (rem == 0) ? 0 : AVIF_STREAM_BUFFER_INCREMENT - rem; + AVIF_CHECKERR(newSize <= SIZE_MAX - padding, AVIF_RESULT_OUT_OF_MEMORY); + newSize += padding; + return avifRWDataRealloc(stream->raw, newSize); +} + +void avifRWStreamStart(avifRWStream * stream, avifRWData * raw) +{ + stream->raw = raw; + stream->offset = 0; + stream->numUsedBitsInPartialByte = 0; +} + +size_t avifRWStreamOffset(const avifRWStream * stream) +{ + return stream->offset; +} + +void avifRWStreamSetOffset(avifRWStream * stream, size_t offset) +{ + stream->offset = offset; + if (stream->offset > stream->raw->size) { + stream->offset = stream->raw->size; + } +} + +void avifRWStreamFinishWrite(avifRWStream * stream) +{ + if (stream->raw->size != stream->offset) { + if (stream->offset) { + stream->raw->size = stream->offset; + } else { + avifRWDataFree(stream->raw); + } + } +} + +avifResult avifRWStreamWrite(avifRWStream * stream, const void * data, size_t size) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + if (size) { + AVIF_CHECKRES(makeRoom(stream, size)); + memcpy(stream->raw->data + stream->offset, data, size); + stream->offset += size; + } + return AVIF_RESULT_OK; +} + +avifResult avifRWStreamWriteChars(avifRWStream * stream, const char * chars, size_t size) +{ + return avifRWStreamWrite(stream, chars, size); +} + +avifResult avifRWStreamWriteFullBox(avifRWStream * stream, const char * type, size_t contentSize, int version, uint32_t flags, avifBoxMarker * marker) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + if (marker) { + *marker = stream->offset; + } + size_t headerSize = sizeof(uint32_t) + 4 /* size of type */; + if (version != -1) { + headerSize += 4; + } + + AVIF_CHECKRES(makeRoom(stream, headerSize)); + memset(stream->raw->data + stream->offset, 0, headerSize); + uint32_t noSize = avifHTONL((uint32_t)(headerSize + contentSize)); + memcpy(stream->raw->data + stream->offset, &noSize, sizeof(uint32_t)); + memcpy(stream->raw->data + stream->offset + 4, type, 4); + if (version != -1) { + stream->raw->data[stream->offset + 8] = (uint8_t)version; + stream->raw->data[stream->offset + 9] = (uint8_t)((flags >> 16) & 0xff); + stream->raw->data[stream->offset + 10] = (uint8_t)((flags >> 8) & 0xff); + stream->raw->data[stream->offset + 11] = (uint8_t)((flags >> 0) & 0xff); + } + stream->offset += headerSize; + + return AVIF_RESULT_OK; +} + +avifResult avifRWStreamWriteBox(avifRWStream * stream, const char * type, size_t contentSize, avifBoxMarker * marker) +{ + return avifRWStreamWriteFullBox(stream, type, contentSize, -1, 0, marker); +} + +avifResult avifRWStreamFinishBox(avifRWStream * stream, avifBoxMarker marker) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + size_t boxSize = stream->offset - marker; + // Since marker comes from a previous avifRWStreamWriteBox() or + // avifRWStreamWriteFullBox() call, boxSize must be >= the size of the size + // and type fields. This implies that boxSize cannot be equal to the two + // special values 0 and 1. + AVIF_ASSERT_OR_RETURN(boxSize >= sizeof(uint32_t) + 4); + AVIF_CHECKERR(boxSize <= UINT32_MAX, AVIF_RESULT_INVALID_ARGUMENT); + uint32_t noSize = avifHTONL((uint32_t)boxSize); + memcpy(stream->raw->data + marker, &noSize, sizeof(uint32_t)); + return AVIF_RESULT_OK; +} + +avifResult avifRWStreamWriteU8(avifRWStream * stream, uint8_t v) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + AVIF_CHECKRES(makeRoom(stream, 1)); + stream->raw->data[stream->offset] = v; + stream->offset += 1; + return AVIF_RESULT_OK; +} + +avifResult avifRWStreamWriteU16(avifRWStream * stream, uint16_t v) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + const size_t size = sizeof(uint16_t); + AVIF_CHECKRES(makeRoom(stream, size)); + v = avifHTONS(v); + memcpy(stream->raw->data + stream->offset, &v, size); + stream->offset += size; + return AVIF_RESULT_OK; +} + +avifResult avifRWStreamWriteU32(avifRWStream * stream, uint32_t v) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + const size_t size = sizeof(uint32_t); + AVIF_CHECKRES(makeRoom(stream, size)); + v = avifHTONL(v); + memcpy(stream->raw->data + stream->offset, &v, size); + stream->offset += size; + return AVIF_RESULT_OK; +} + +avifResult avifRWStreamWriteU64(avifRWStream * stream, uint64_t v) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + const size_t size = sizeof(uint64_t); + AVIF_CHECKRES(makeRoom(stream, size)); + v = avifHTON64(v); + memcpy(stream->raw->data + stream->offset, &v, size); + stream->offset += size; + return AVIF_RESULT_OK; +} + +avifResult avifRWStreamWriteZeros(avifRWStream * stream, size_t byteCount) +{ + assert(stream->numUsedBitsInPartialByte == 0); // Byte alignment is required. + AVIF_CHECKRES(makeRoom(stream, byteCount)); + memset(stream->raw->data + stream->offset, 0, byteCount); + stream->offset += byteCount; + return AVIF_RESULT_OK; +} + +avifResult avifRWStreamWriteBits(avifRWStream * stream, uint32_t v, size_t bitCount) +{ + AVIF_CHECKERR(bitCount >= 32 || (v >> bitCount) == 0, AVIF_RESULT_INVALID_ARGUMENT); + while (bitCount) { + if (stream->numUsedBitsInPartialByte == 0) { + AVIF_CHECKRES(makeRoom(stream, 1)); // Book a new partial byte in the stream. + stream->raw->data[stream->offset] = 0; + stream->offset += 1; + } + assert(stream->offset > 0); + uint8_t * packedBits = stream->raw->data + stream->offset - 1; + + const size_t numBits = AVIF_MIN(bitCount, 8 - stream->numUsedBitsInPartialByte); + stream->numUsedBitsInPartialByte += numBits; + bitCount -= numBits; + // Order the input bits from the most significant bit to the least significant bit. + // In the case where avifRWStreamWriteBits() is used to write the unsigned integer value v + // over multiple aligned bytes, this order corresponds to big endianness. + const uint32_t bits = (v >> bitCount) & ((1 << numBits) - 1); + // Pack bits starting with the most significant bit of the first output byte. + // This way, packed bits can be found in the same order in the bit stream. + *packedBits |= bits << (8 - stream->numUsedBitsInPartialByte); + + if (stream->numUsedBitsInPartialByte == 8) { + // Start a new partial byte the next time a bit is needed. + stream->numUsedBitsInPartialByte = 0; + } + } + return AVIF_RESULT_OK; +} diff --git a/3rdparty/libavif/src/utils.c b/3rdparty/libavif/src/utils.c new file mode 100644 index 0000000..11829e0 --- /dev/null +++ b/3rdparty/libavif/src/utils.c @@ -0,0 +1,294 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include +#include +#include + +float avifRoundf(float v) +{ + return floorf(v + 0.5f); +} + +// Thanks, Rob Pike! https://commandcenter.blogspot.nl/2012/04/byte-order-fallacy.html + +uint16_t avifHTONS(uint16_t s) +{ + uint16_t result = 0; + uint8_t * data = (uint8_t *)&result; + data[0] = (s >> 8) & 0xff; + data[1] = (s >> 0) & 0xff; + return result; +} + +uint16_t avifNTOHS(uint16_t s) +{ + const uint8_t * data = (const uint8_t *)&s; + return (uint16_t)((data[1] << 0) | (data[0] << 8)); +} + +uint16_t avifCTOHS(uint16_t s) +{ + const uint8_t * data = (const uint8_t *)&s; + return (uint16_t)((data[0] << 0) | (data[1] << 8)); +} + +uint32_t avifHTONL(uint32_t l) +{ + uint32_t result = 0; + uint8_t * data = (uint8_t *)&result; + data[0] = (l >> 24) & 0xff; + data[1] = (l >> 16) & 0xff; + data[2] = (l >> 8) & 0xff; + data[3] = (l >> 0) & 0xff; + return result; +} + +uint32_t avifNTOHL(uint32_t l) +{ + const uint8_t * data = (const uint8_t *)&l; + return ((uint32_t)data[3] << 0) | ((uint32_t)data[2] << 8) | ((uint32_t)data[1] << 16) | ((uint32_t)data[0] << 24); +} + +uint32_t avifCTOHL(uint32_t l) +{ + const uint8_t * data = (const uint8_t *)&l; + return ((uint32_t)data[0] << 0) | ((uint32_t)data[1] << 8) | ((uint32_t)data[2] << 16) | ((uint32_t)data[3] << 24); +} + +uint64_t avifHTON64(uint64_t l) +{ + uint64_t result = 0; + uint8_t * data = (uint8_t *)&result; + data[0] = (l >> 56) & 0xff; + data[1] = (l >> 48) & 0xff; + data[2] = (l >> 40) & 0xff; + data[3] = (l >> 32) & 0xff; + data[4] = (l >> 24) & 0xff; + data[5] = (l >> 16) & 0xff; + data[6] = (l >> 8) & 0xff; + data[7] = (l >> 0) & 0xff; + return result; +} + +uint64_t avifNTOH64(uint64_t l) +{ + const uint8_t * data = (const uint8_t *)&l; + return ((uint64_t)data[7] << 0) | ((uint64_t)data[6] << 8) | ((uint64_t)data[5] << 16) | ((uint64_t)data[4] << 24) | + ((uint64_t)data[3] << 32) | ((uint64_t)data[2] << 40) | ((uint64_t)data[1] << 48) | ((uint64_t)data[0] << 56); +} + +AVIF_ARRAY_DECLARE(avifArrayInternal, uint8_t, ptr); + +// On error, this function must set arr->ptr to NULL and both arr->count and arr->capacity to 0. +avifBool avifArrayCreate(void * arrayStruct, uint32_t elementSize, uint32_t initialCapacity) +{ + avifArrayInternal * arr = (avifArrayInternal *)arrayStruct; + arr->elementSize = elementSize ? elementSize : 1; + arr->count = 0; + arr->capacity = initialCapacity; + if (arr->capacity > SIZE_MAX / arr->elementSize) { + arr->ptr = NULL; + arr->capacity = 0; + return AVIF_FALSE; + } + size_t byteCount = (size_t)arr->elementSize * arr->capacity; + arr->ptr = (uint8_t *)avifAlloc(byteCount); + if (!arr->ptr) { + arr->capacity = 0; + return AVIF_FALSE; + } + memset(arr->ptr, 0, byteCount); + return AVIF_TRUE; +} + +void * avifArrayPush(void * arrayStruct) +{ + avifArrayInternal * arr = (avifArrayInternal *)arrayStruct; + if (arr->count == arr->capacity) { + uint8_t * oldPtr = arr->ptr; + size_t oldByteCount = (size_t)arr->elementSize * arr->capacity; + if (oldByteCount > SIZE_MAX / 2 || arr->capacity > UINT32_MAX / 2) { + return NULL; + } + size_t newByteCount = oldByteCount * 2; + uint8_t * newPtr = (uint8_t *)avifAlloc(newByteCount); + if (newPtr == NULL) { + return NULL; + } + arr->ptr = newPtr; + memset(arr->ptr + oldByteCount, 0, oldByteCount); + memcpy(arr->ptr, oldPtr, oldByteCount); + arr->capacity *= 2; + avifFree(oldPtr); + } + ++arr->count; + return &arr->ptr[(arr->count - 1) * (size_t)arr->elementSize]; +} + +void avifArrayPop(void * arrayStruct) +{ + avifArrayInternal * arr = (avifArrayInternal *)arrayStruct; + assert(arr->count > 0); + --arr->count; + memset(&arr->ptr[arr->count * (size_t)arr->elementSize], 0, arr->elementSize); +} + +void avifArrayDestroy(void * arrayStruct) +{ + avifArrayInternal * arr = (avifArrayInternal *)arrayStruct; + if (arr->ptr) { + avifFree(arr->ptr); + arr->ptr = NULL; + } + memset(arr, 0, sizeof(avifArrayInternal)); +} + +// |a| and |b| hold int32_t values. The int64_t type is used so that we can negate INT32_MIN without +// overflowing int32_t. +static int64_t calcGCD(int64_t a, int64_t b) +{ + if (a < 0) { + a *= -1; + } + if (b < 0) { + b *= -1; + } + while (b != 0) { + int64_t r = a % b; + a = b; + b = r; + } + return a; +} + +void avifFractionSimplify(avifFraction * f) +{ + int64_t gcd = calcGCD(f->n, f->d); + if (gcd > 1) { + f->n = (int32_t)(f->n / gcd); + f->d = (int32_t)(f->d / gcd); + } +} + +avifBool avifFractionCD(avifFraction * a, avifFraction * b) +{ + avifFractionSimplify(a); + avifFractionSimplify(b); + if (a->d != b->d) { + const int64_t ad = a->d; + const int64_t bd = b->d; + const int64_t anNew = a->n * bd; + const int64_t adNew = a->d * bd; + const int64_t bnNew = b->n * ad; + const int64_t bdNew = b->d * ad; + if (overflowsInt32(anNew) || overflowsInt32(adNew) || overflowsInt32(bnNew) || overflowsInt32(bdNew)) { + return AVIF_FALSE; + } + a->n = (int32_t)anNew; + a->d = (int32_t)adNew; + b->n = (int32_t)bnNew; + b->d = (int32_t)bdNew; + } + return AVIF_TRUE; +} + +avifBool avifFractionAdd(avifFraction a, avifFraction b, avifFraction * result) +{ + if (!avifFractionCD(&a, &b)) { + return AVIF_FALSE; + } + + const int64_t resultN = (int64_t)a.n + b.n; + if (overflowsInt32(resultN)) { + return AVIF_FALSE; + } + result->n = (int32_t)resultN; + result->d = a.d; + + avifFractionSimplify(result); + return AVIF_TRUE; +} + +avifBool avifFractionSub(avifFraction a, avifFraction b, avifFraction * result) +{ + if (!avifFractionCD(&a, &b)) { + return AVIF_FALSE; + } + + const int64_t resultN = (int64_t)a.n - b.n; + if (overflowsInt32(resultN)) { + return AVIF_FALSE; + } + result->n = (int32_t)resultN; + result->d = a.d; + + avifFractionSimplify(result); + return AVIF_TRUE; +} + +static avifBool avifDoubleToUnsignedFractionImpl(double v, uint32_t maxNumerator, uint32_t * numerator, uint32_t * denominator) +{ + if (isnan(v) || v < 0 || v > maxNumerator) { + return AVIF_FALSE; + } + + // Maximum denominator: makes sure that the numerator is <= maxNumerator and the denominator is <= UINT32_MAX. + const uint32_t maxD = (v <= 1) ? UINT32_MAX : (uint32_t)floor(maxNumerator / v); + + // Find the best approximation of v as a fraction using continued fractions, see + // https://en.wikipedia.org/wiki/Continued_fraction + *denominator = 1; + uint32_t previousD = 0; + double currentV = v - floor(v); + int iter = 0; + // Set a maximum number of iterations to be safe. Most numbers should + // converge in less than ~20 iterations. + // The golden ratio is the worst case and takes 39 iterations. + const int maxIter = 39; + while (iter < maxIter) { + const double numeratorDouble = (double)(*denominator) * v; + assert(numeratorDouble <= maxNumerator); + *numerator = (uint32_t)round(numeratorDouble); + if (fabs(numeratorDouble - (*numerator)) == 0.0) { + return AVIF_TRUE; + } + currentV = 1.0 / currentV; + const double newD = previousD + floor(currentV) * (*denominator); + if (newD > (double)maxD) { + // This is the best we can do with a denominator <= max_d. + return AVIF_TRUE; + } + previousD = *denominator; + assert(newD <= UINT32_MAX); + *denominator = (uint32_t)newD; + currentV -= floor(currentV); + ++iter; + } + // Maximum number of iterations reached, return what we've found. + // For max_iter >= 39 we shouldn't get here. max_iter can be set + // to a lower value to speed up the algorithm if needed. + *numerator = (uint32_t)round((double)(*denominator) * v); + return AVIF_TRUE; +} + +avifBool avifDoubleToSignedFraction(double v, avifSignedFraction * fraction) +{ + uint32_t positive_numerator; + if (!avifDoubleToUnsignedFractionImpl(fabs(v), INT32_MAX, &positive_numerator, &fraction->d)) { + return AVIF_FALSE; + } + fraction->n = (int32_t)positive_numerator; + if (v < 0) { + fraction->n *= -1; + } + return AVIF_TRUE; +} + +avifBool avifDoubleToUnsignedFraction(double v, avifUnsignedFraction * fraction) +{ + return avifDoubleToUnsignedFractionImpl(v, UINT32_MAX, &fraction->n, &fraction->d); +} diff --git a/3rdparty/libavif/src/write.c b/3rdparty/libavif/src/write.c new file mode 100644 index 0000000..fc68e95 --- /dev/null +++ b/3rdparty/libavif/src/write.c @@ -0,0 +1,3910 @@ +// Copyright 2019 Joe Drago. All rights reserved. +// SPDX-License-Identifier: BSD-2-Clause + +#include "avif/internal.h" + +#include +#include +#include +#include + +// Section 8.11.14.2 of ISO/IEC 14496-12 (ItemPropertyAssociationBox 'ipma' syntax): +// if (flags & 1) +// unsigned int(15) property_index; +// else +// unsigned int(7) property_index; +// +// libavif writes 'ipma' with flags set to 0. +#define MAX_PROPERTY_INDEX ((1 << 7) - 1) + +// The indices of the properties associated with an item. +typedef struct avifItemPropertyAssociation +{ + uint8_t property_index; // 1-indexed + avifBool essential; +} avifItemPropertyAssociation; +AVIF_ARRAY_DECLARE(avifItemPropertyAssociationArray, avifItemPropertyAssociation, association); + +// Used to store offsets in meta boxes which need to point at mdat offsets that +// aren't known yet. When an item's mdat payload is written, all registered fixups +// will have this now-known offset "fixed up". +typedef struct avifOffsetFixup +{ + size_t offset; +} avifOffsetFixup; +AVIF_ARRAY_DECLARE(avifOffsetFixupArray, avifOffsetFixup, fixup); + +static const char alphaURN[] = AVIF_URN_ALPHA0; +static const size_t alphaURNSize = sizeof(alphaURN); + +static const char xmpContentType[] = AVIF_CONTENT_TYPE_XMP; +static const size_t xmpContentTypeSize = sizeof(xmpContentType); + +static avifResult writeCodecConfig(avifRWStream * s, const avifCodecConfigurationBox * cfg); +static avifResult writeConfigBox(avifRWStream * s, const avifCodecConfigurationBox * cfg, const char * configPropName); + +// --------------------------------------------------------------------------- +// avifSetTileConfiguration + +static int floorLog2(uint32_t n) +{ + assert(n > 0); + int count = 0; + while (n != 0) { + ++count; + n >>= 1; + } + return count - 1; +} + +// Splits tilesLog2 into *tileDim1Log2 and *tileDim2Log2, considering the ratio of dim1 to dim2. +// +// Precondition: +// dim1 >= dim2 +// Postcondition: +// tilesLog2 == *tileDim1Log2 + *tileDim2Log2 +// *tileDim1Log2 >= *tileDim2Log2 +static void splitTilesLog2(uint32_t dim1, uint32_t dim2, int tilesLog2, int * tileDim1Log2, int * tileDim2Log2) +{ + assert(dim1 >= dim2); + uint32_t ratio = dim1 / dim2; + int diffLog2 = floorLog2(ratio); + int subtract = tilesLog2 - diffLog2; + if (subtract < 0) { + subtract = 0; + } + *tileDim2Log2 = subtract / 2; + *tileDim1Log2 = tilesLog2 - *tileDim2Log2; + assert(*tileDim1Log2 >= *tileDim2Log2); +} + +// Set the tile configuration: the number of tiles and the tile size. +// +// Tiles improve encoding and decoding speeds when multiple threads are available. However, for +// image coding, the total tile boundary length affects the compression efficiency because intra +// prediction can't go across tile boundaries. So the more tiles there are in an image, the worse +// the compression ratio is. For a given number of tiles, making the tile size close to a square +// tends to reduce the total tile boundary length inside the image. Use more tiles along the longer +// dimension of the image to make the tile size closer to a square. +void avifSetTileConfiguration(int threads, uint32_t width, uint32_t height, int * tileRowsLog2, int * tileColsLog2) +{ + *tileRowsLog2 = 0; + *tileColsLog2 = 0; + if (threads > 1) { + // Avoid small tiles because they are particularly bad for image coding. + // + // Use no more tiles than the number of threads. Aim for one tile per thread. Using more + // than one thread inside one tile could be less efficient. Using more tiles than the + // number of threads would result in a compression penalty without much benefit. + const uint32_t kMinTileArea = 512 * 512; + const uint32_t kMaxTiles = 32; + // AV1 requires width <= 65536 and height <= 65536, so their product fits + // in uint64_t and the resulting tile count fits in uint32_t. + const uint64_t imageArea = (uint64_t)width * height; + uint32_t tiles = (uint32_t)((imageArea + kMinTileArea - 1) / kMinTileArea); + if (tiles > kMaxTiles) { + tiles = kMaxTiles; + } + if (tiles > (uint32_t)threads) { + tiles = threads; + } + int tilesLog2 = floorLog2(tiles); + // If the image's width is greater than the height, use more tile columns than tile rows. + if (width >= height) { + splitTilesLog2(width, height, tilesLog2, tileColsLog2, tileRowsLog2); + } else { + splitTilesLog2(height, width, tilesLog2, tileRowsLog2, tileColsLog2); + } + } +} + +// --------------------------------------------------------------------------- +// avifCodecEncodeOutput + +avifCodecEncodeOutput * avifCodecEncodeOutputCreate(void) +{ + avifCodecEncodeOutput * encodeOutput = (avifCodecEncodeOutput *)avifAlloc(sizeof(avifCodecEncodeOutput)); + if (encodeOutput == NULL) { + return NULL; + } + memset(encodeOutput, 0, sizeof(avifCodecEncodeOutput)); + if (!avifArrayCreate(&encodeOutput->samples, sizeof(avifEncodeSample), 1)) { + avifCodecEncodeOutputDestroy(encodeOutput); + return NULL; + } + return encodeOutput; +} + +avifResult avifCodecEncodeOutputAddSample(avifCodecEncodeOutput * encodeOutput, const uint8_t * data, size_t len, avifBool sync) +{ + avifEncodeSample * sample = (avifEncodeSample *)avifArrayPush(&encodeOutput->samples); + AVIF_CHECKERR(sample, AVIF_RESULT_OUT_OF_MEMORY); + const avifResult result = avifRWDataSet(&sample->data, data, len); + if (result != AVIF_RESULT_OK) { + avifArrayPop(&encodeOutput->samples); + return result; + } + sample->sync = sync; + return AVIF_RESULT_OK; +} + +void avifCodecEncodeOutputDestroy(avifCodecEncodeOutput * encodeOutput) +{ + for (uint32_t sampleIndex = 0; sampleIndex < encodeOutput->samples.count; ++sampleIndex) { + avifRWDataFree(&encodeOutput->samples.sample[sampleIndex].data); + } + avifArrayDestroy(&encodeOutput->samples); + avifFree(encodeOutput); +} + +// --------------------------------------------------------------------------- +// avifEncoderItem + +// one "item" worth for encoder +typedef struct avifEncoderItem +{ + uint16_t id; + uint8_t type[4]; // 4-character 'item_type' field in the 'infe' (item info entry) box + avifCodec * codec; // only present on image items + avifCodecEncodeOutput * encodeOutput; // AV1 sample data + avifRWData metadataPayload; // Exif/XMP data + avifCodecConfigurationBox av1C; // Harvested in avifEncoderFinish(), if encodeOutput has samples + // TODO(yguyon): Rename or add av2C + uint32_t cellIndex; // Which row-major cell index corresponds to this item. only present on image items + avifItemCategory itemCategory; // Category of item being encoded + avifBool hiddenImage; // A hidden image item has (flags & 1) equal to 1 in its ItemInfoEntry. + + const char * infeName; + size_t infeNameSize; + const char * infeContentType; + size_t infeContentTypeSize; + avifOffsetFixupArray mdatFixups; + + uint16_t irefToID; // if non-zero, make an iref from this id -> irefToID + const char * irefType; + + uint32_t gridCols; // if non-zero (legal range [1-256]), this is a grid item + uint32_t gridRows; // if non-zero (legal range [1-256]), this is a grid item + + // the reconstructed image of a grid item will be trimmed to these dimensions (only present on grid items) + uint32_t gridWidth; + uint32_t gridHeight; + + uint32_t extraLayerCount; // if non-zero (legal range [1-(AVIF_MAX_AV1_LAYER_COUNT-1)]), this is a layered AV1 image + + uint16_t dimgFromID; // if non-zero, make an iref from dimgFromID -> this id + + avifItemPropertyAssociationArray associations; // 'ipma' +} avifEncoderItem; +AVIF_ARRAY_DECLARE(avifEncoderItemArray, avifEncoderItem, item); + +// --------------------------------------------------------------------------- +// avifEncoderItemReference + +// pointer to one "item" interested in +typedef avifEncoderItem * avifEncoderItemReference; +AVIF_ARRAY_DECLARE(avifEncoderItemReferenceArray, avifEncoderItemReference, ref); + +// --------------------------------------------------------------------------- +// avifEncoderFrame + +typedef struct avifEncoderFrame +{ + uint64_t durationInTimescales; +} avifEncoderFrame; +AVIF_ARRAY_DECLARE(avifEncoderFrameArray, avifEncoderFrame, frame); + +// --------------------------------------------------------------------------- +// avifEncoderData + +AVIF_ARRAY_DECLARE(avifEncoderItemIdArray, uint16_t, itemID); + +typedef struct avifEncoderData +{ + avifEncoderItemArray items; + avifEncoderFrameArray frames; + // quality values for the image, alpha, and gain map + // Note: these three fields are the actual quality values after the default values are resolved + int quality; + int qualityAlpha; + int qualityGainMap; + // tileRowsLog2 and tileColsLog2 are the actual tiling values after automatic tiling is handled + int tileRowsLog2; + int tileColsLog2; + avifEncoder lastEncoder; // Shallow state at last avifEncoderAddImageInternal() call. + // lastQuality and lastQualityAlpha are the quality and qualityAlpha values used last time + // Note: Gain maps are supported for still images only, so we don't need a "lastQualityGainMap" + int lastQuality; + int lastQualityAlpha; + // lastTileRowsLog2 and lastTileColsLog2 are the actual tiling values used last time + int lastTileRowsLog2; + int lastTileColsLog2; + // Holds metadata about the base image + avifImage * imageMetadata; + // Holds metadata derived from the avifGainMap struct (when present) about the alternate image + avifImage * altImageMetadata; + uint16_t lastItemID; + uint16_t primaryItemID; + avifEncoderItemIdArray alternativeItemIDs; // list of item ids for an 'altr' box (group of alternatives to each other) + avifBool singleImage; // if true, the AVIF_ADD_IMAGE_FLAG_SINGLE flag was set on the first call to avifEncoderAddImage() + avifBool alphaPresent; + size_t gainMapSizeBytes; + // Fields specific to AV1/AV2 + const char * imageItemType; // "av01" for AV1 ("av02" for AV2 if AVIF_CODEC_AVM) + const char * configPropName; // "av1C" for AV1 ("av2C" for AV2 if AVIF_CODEC_AVM) +} avifEncoderData; + +static void avifEncoderDataDestroy(avifEncoderData * data); + +// Returns NULL if a memory allocation failed. +static avifEncoderData * avifEncoderDataCreate(void) +{ + avifEncoderData * data = (avifEncoderData *)avifAlloc(sizeof(avifEncoderData)); + if (!data) { + return NULL; + } + memset(data, 0, sizeof(avifEncoderData)); + data->imageMetadata = avifImageCreateEmpty(); + if (!data->imageMetadata) { + goto error; + } + data->altImageMetadata = avifImageCreateEmpty(); + if (!data->altImageMetadata) { + goto error; + } + if (!avifArrayCreate(&data->items, sizeof(avifEncoderItem), 8)) { + goto error; + } + if (!avifArrayCreate(&data->frames, sizeof(avifEncoderFrame), 1)) { + goto error; + } + if (!avifArrayCreate(&data->alternativeItemIDs, sizeof(uint16_t), 1)) { + goto error; + } + return data; + +error: + avifEncoderDataDestroy(data); + return NULL; +} + +static avifEncoderItem * avifEncoderDataCreateItem(avifEncoderData * data, const char * type, const char * infeName, size_t infeNameSize, uint32_t cellIndex) +{ + avifEncoderItem * item = (avifEncoderItem *)avifArrayPush(&data->items); + if (item == NULL) { + return NULL; + } + ++data->lastItemID; + item->id = data->lastItemID; + memcpy(item->type, type, sizeof(item->type)); + item->infeName = infeName; + item->infeNameSize = infeNameSize; + item->encodeOutput = avifCodecEncodeOutputCreate(); + if (item->encodeOutput == NULL) { + goto error; + } + item->cellIndex = cellIndex; + if (!avifArrayCreate(&item->mdatFixups, sizeof(avifOffsetFixup), 4)) { + goto error; + } + if (!avifArrayCreate(&item->associations, sizeof(avifItemPropertyAssociation), 4)) { + goto error; + } + return item; + +error: + if (item->encodeOutput != NULL) { + avifCodecEncodeOutputDestroy(item->encodeOutput); + } + avifArrayDestroy(&item->mdatFixups); + --data->lastItemID; + avifArrayPop(&data->items); + return NULL; +} + +static avifEncoderItem * avifEncoderDataFindItemByID(avifEncoderData * data, uint16_t id) +{ + for (uint32_t itemIndex = 0; itemIndex < data->items.count; ++itemIndex) { + avifEncoderItem * item = &data->items.item[itemIndex]; + if (item->id == id) { + return item; + } + } + return NULL; +} + +static void avifEncoderDataDestroy(avifEncoderData * data) +{ + for (uint32_t i = 0; i < data->items.count; ++i) { + avifEncoderItem * item = &data->items.item[i]; + if (item->codec) { + avifCodecDestroy(item->codec); + } + avifCodecEncodeOutputDestroy(item->encodeOutput); + avifRWDataFree(&item->metadataPayload); + avifArrayDestroy(&item->mdatFixups); + avifArrayDestroy(&item->associations); + } + if (data->imageMetadata) { + avifImageDestroy(data->imageMetadata); + } + if (data->altImageMetadata) { + avifImageDestroy(data->altImageMetadata); + } + avifArrayDestroy(&data->items); + avifArrayDestroy(&data->frames); + avifArrayDestroy(&data->alternativeItemIDs); + avifFree(data); +} + +static avifResult avifEncoderItemAddMdatFixup(avifEncoderItem * item, const avifRWStream * s) +{ + avifOffsetFixup * fixup = (avifOffsetFixup *)avifArrayPush(&item->mdatFixups); + AVIF_CHECKERR(fixup != NULL, AVIF_RESULT_OUT_OF_MEMORY); + fixup->offset = avifRWStreamOffset(s); + return AVIF_RESULT_OK; +} + +// --------------------------------------------------------------------------- +// avifItemPropertyDedup - Provides ipco deduplication + +typedef struct avifItemProperty +{ + uint8_t index; + size_t offset; + size_t size; +} avifItemProperty; +AVIF_ARRAY_DECLARE(avifItemPropertyArray, avifItemProperty, property); + +typedef struct avifItemPropertyDedup +{ + avifItemPropertyArray properties; + avifRWStream s; // Temporary stream for each new property, checked against already-written boxes for deduplications + avifRWData buffer; // Temporary storage for 's' + uint8_t nextIndex; // 1-indexed, incremented every time another unique property is finished +} avifItemPropertyDedup; + +static avifItemPropertyDedup * avifItemPropertyDedupCreate(void) +{ + avifItemPropertyDedup * dedup = (avifItemPropertyDedup *)avifAlloc(sizeof(avifItemPropertyDedup)); + if (dedup == NULL) { + return NULL; + } + memset(dedup, 0, sizeof(avifItemPropertyDedup)); + if (!avifArrayCreate(&dedup->properties, sizeof(avifItemProperty), 8)) { + avifFree(dedup); + return NULL; + } + if (avifRWDataRealloc(&dedup->buffer, 2048) != AVIF_RESULT_OK) { + avifArrayDestroy(&dedup->properties); + avifFree(dedup); + return NULL; + } + return dedup; +} + +static void avifItemPropertyDedupDestroy(avifItemPropertyDedup * dedup) +{ + avifArrayDestroy(&dedup->properties); + avifRWDataFree(&dedup->buffer); + avifFree(dedup); +} + +// Resets the dedup's temporary write stream in preparation for a single item property's worth of writing +static void avifItemPropertyDedupStart(avifItemPropertyDedup * dedup) +{ + avifRWStreamStart(&dedup->s, &dedup->buffer); +} + +// This compares the newly written item property (in the dedup's temporary storage buffer) to +// already-written properties (whose offsets/sizes in outputStream are recorded in the dedup). If a +// match is found, the previous property's index is used. If this new property is unique, it is +// assigned the next available property index, written to the output stream, and its offset/size in +// the output stream is recorded in the dedup for future comparisons. +// +// On success, this function adds to the given ipma box a property association linking the reused +// or newly created property with the item. +static avifResult avifItemPropertyDedupFinish(avifItemPropertyDedup * dedup, + avifRWStream * outputStream, + avifItemPropertyAssociationArray * associations, + avifBool essential) +{ + uint8_t propertyIndex = 0; + const size_t newPropertySize = avifRWStreamOffset(&dedup->s); + + for (size_t i = 0; i < dedup->properties.count; ++i) { + avifItemProperty * property = &dedup->properties.property[i]; + if ((property->size == newPropertySize) && + !memcmp(&outputStream->raw->data[property->offset], dedup->buffer.data, newPropertySize)) { + // We've already written this exact property, reuse it + propertyIndex = property->index; + AVIF_ASSERT_OR_RETURN(propertyIndex != 0); + break; + } + } + + if (propertyIndex == 0) { + // Write a new property, and remember its location in the output stream for future deduplication + avifItemProperty * property = (avifItemProperty *)avifArrayPush(&dedup->properties); + AVIF_CHECKERR(property != NULL, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKERR(dedup->nextIndex < MAX_PROPERTY_INDEX, AVIF_RESULT_INVALID_ARGUMENT); + property->index = ++dedup->nextIndex; // preincrement so the first new index is 1 (as ipma is 1-indexed) + property->size = newPropertySize; + property->offset = avifRWStreamOffset(outputStream); + AVIF_CHECKRES(avifRWStreamWrite(outputStream, dedup->buffer.data, newPropertySize)); + propertyIndex = property->index; + } + + avifItemPropertyAssociation * association = (avifItemPropertyAssociation *)avifArrayPush(associations); + AVIF_CHECKERR(association != NULL, AVIF_RESULT_OUT_OF_MEMORY); + association->property_index = propertyIndex; + association->essential = essential; + return AVIF_RESULT_OK; +} + +// --------------------------------------------------------------------------- + +static const avifScalingMode noScaling = { { 1, 1 }, { 1, 1 } }; + +avifEncoder * avifEncoderCreate(void) +{ + avifEncoder * encoder = (avifEncoder *)avifAlloc(sizeof(avifEncoder)); + if (!encoder) { + return NULL; + } + memset(encoder, 0, sizeof(avifEncoder)); + encoder->codecChoice = AVIF_CODEC_CHOICE_AUTO; + encoder->maxThreads = 1; + encoder->speed = AVIF_SPEED_DEFAULT; + encoder->keyframeInterval = 0; + encoder->timescale = 1; + encoder->repetitionCount = AVIF_REPETITION_COUNT_INFINITE; + encoder->quality = AVIF_QUALITY_DEFAULT; + encoder->qualityAlpha = AVIF_QUALITY_DEFAULT; + encoder->qualityGainMap = AVIF_QUALITY_DEFAULT; + encoder->minQuantizer = AVIF_QUANTIZER_BEST_QUALITY; + encoder->maxQuantizer = AVIF_QUANTIZER_WORST_QUALITY; + encoder->minQuantizerAlpha = AVIF_QUANTIZER_BEST_QUALITY; + encoder->maxQuantizerAlpha = AVIF_QUANTIZER_WORST_QUALITY; + encoder->tileRowsLog2 = 0; + encoder->tileColsLog2 = 0; + encoder->autoTiling = AVIF_FALSE; + encoder->scalingMode = noScaling; + encoder->data = avifEncoderDataCreate(); + encoder->csOptions = avifCodecSpecificOptionsCreate(); + if (!encoder->data || !encoder->csOptions) { + avifEncoderDestroy(encoder); + return NULL; + } + encoder->headerFormat = AVIF_HEADER_DEFAULT; + encoder->creationTime = 0; + encoder->modificationTime = 0; + encoder->sampleTransformRecipe = AVIF_SAMPLE_TRANSFORM_NONE; + return encoder; +} + +void avifEncoderDestroy(avifEncoder * encoder) +{ + if (encoder->csOptions) { + avifCodecSpecificOptionsDestroy(encoder->csOptions); + } + if (encoder->data) { + avifEncoderDataDestroy(encoder->data); + } + avifFree(encoder); +} + +avifResult avifEncoderSetCodecSpecificOption(avifEncoder * encoder, const char * key, const char * value) +{ + return avifCodecSpecificOptionsSet(encoder->csOptions, key, value); +} + +static void avifEncoderBackupSettings(avifEncoder * encoder) +{ + avifEncoder * lastEncoder = &encoder->data->lastEncoder; + + // lastEncoder->data is only used to mark that lastEncoder is initialized. lastEncoder->data + // must not be dereferenced. + lastEncoder->data = encoder->data; + lastEncoder->codecChoice = encoder->codecChoice; + lastEncoder->maxThreads = encoder->maxThreads; + lastEncoder->speed = encoder->speed; + lastEncoder->keyframeInterval = encoder->keyframeInterval; + lastEncoder->timescale = encoder->timescale; + lastEncoder->repetitionCount = encoder->repetitionCount; + lastEncoder->extraLayerCount = encoder->extraLayerCount; + lastEncoder->minQuantizer = encoder->minQuantizer; + lastEncoder->maxQuantizer = encoder->maxQuantizer; + lastEncoder->minQuantizerAlpha = encoder->minQuantizerAlpha; + lastEncoder->maxQuantizerAlpha = encoder->maxQuantizerAlpha; + encoder->data->lastQuality = encoder->data->quality; + encoder->data->lastQualityAlpha = encoder->data->qualityAlpha; + encoder->data->lastTileRowsLog2 = encoder->data->tileRowsLog2; + encoder->data->lastTileColsLog2 = encoder->data->tileColsLog2; + lastEncoder->scalingMode = encoder->scalingMode; + lastEncoder->sampleTransformRecipe = encoder->sampleTransformRecipe; +} + +// This function detects changes made on avifEncoder. It returns true on success (i.e., if every +// change is valid), or false on failure (i.e., if any setting that can't change was changed). It +// reports a bitwise-OR of detected changes in encoderChanges. +static avifBool avifEncoderDetectChanges(const avifEncoder * encoder, avifEncoderChanges * encoderChanges) +{ + const avifEncoder * lastEncoder = &encoder->data->lastEncoder; + *encoderChanges = 0; + + if (!lastEncoder->data) { + // lastEncoder is not initialized. + return AVIF_TRUE; + } + + if ((lastEncoder->codecChoice != encoder->codecChoice) || (lastEncoder->maxThreads != encoder->maxThreads) || + (lastEncoder->speed != encoder->speed) || (lastEncoder->keyframeInterval != encoder->keyframeInterval) || + (lastEncoder->timescale != encoder->timescale) || (lastEncoder->repetitionCount != encoder->repetitionCount) || + (lastEncoder->extraLayerCount != encoder->extraLayerCount)) { + return AVIF_FALSE; + } + + if (encoder->data->lastQuality != encoder->data->quality) { + *encoderChanges |= AVIF_ENCODER_CHANGE_QUALITY; + } + if (encoder->data->lastQualityAlpha != encoder->data->qualityAlpha) { + *encoderChanges |= AVIF_ENCODER_CHANGE_QUALITY_ALPHA; + } + if (lastEncoder->minQuantizer != encoder->minQuantizer) { + *encoderChanges |= AVIF_ENCODER_CHANGE_MIN_QUANTIZER; + } + if (lastEncoder->maxQuantizer != encoder->maxQuantizer) { + *encoderChanges |= AVIF_ENCODER_CHANGE_MAX_QUANTIZER; + } + if (lastEncoder->minQuantizerAlpha != encoder->minQuantizerAlpha) { + *encoderChanges |= AVIF_ENCODER_CHANGE_MIN_QUANTIZER_ALPHA; + } + if (lastEncoder->maxQuantizerAlpha != encoder->maxQuantizerAlpha) { + *encoderChanges |= AVIF_ENCODER_CHANGE_MAX_QUANTIZER_ALPHA; + } + if (encoder->data->lastTileRowsLog2 != encoder->data->tileRowsLog2) { + *encoderChanges |= AVIF_ENCODER_CHANGE_TILE_ROWS_LOG2; + } + if (encoder->data->lastTileColsLog2 != encoder->data->tileColsLog2) { + *encoderChanges |= AVIF_ENCODER_CHANGE_TILE_COLS_LOG2; + } + if (memcmp(&lastEncoder->scalingMode, &encoder->scalingMode, sizeof(avifScalingMode)) != 0) { + *encoderChanges |= AVIF_ENCODER_CHANGE_SCALING_MODE; + } + if (encoder->csOptions->count > 0) { + *encoderChanges |= AVIF_ENCODER_CHANGE_CODEC_SPECIFIC; + } + + if (lastEncoder->sampleTransformRecipe != encoder->sampleTransformRecipe) { + return AVIF_FALSE; + } + + return AVIF_TRUE; +} + +// Same as 'avifEncoderWriteColorProperties' but for the colr nclx box only. +static avifResult avifEncoderWriteNclxProperty(avifRWStream * dedupStream, + avifRWStream * outputStream, + const avifImage * imageMetadata, + avifItemPropertyAssociationArray * associations, + avifItemPropertyDedup * dedup) +{ + if (dedup) { + avifItemPropertyDedupStart(dedup); + } + avifBoxMarker colr; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "colr", AVIF_BOX_SIZE_TBD, &colr)); + AVIF_CHECKRES(avifRWStreamWriteChars(dedupStream, "nclx", 4)); // unsigned int(32) colour_type; + AVIF_CHECKRES(avifRWStreamWriteU16(dedupStream, imageMetadata->colorPrimaries)); // unsigned int(16) colour_primaries; + AVIF_CHECKRES(avifRWStreamWriteU16(dedupStream, imageMetadata->transferCharacteristics)); // unsigned int(16) transfer_characteristics; + AVIF_CHECKRES(avifRWStreamWriteU16(dedupStream, imageMetadata->matrixCoefficients)); // unsigned int(16) matrix_coefficients; + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, (imageMetadata->yuvRange == AVIF_RANGE_FULL) ? 1 : 0, /*bitCount=*/1)); // unsigned int(1) full_range_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, 0, /*bitCount=*/7)); // unsigned int(7) reserved = 0; + AVIF_CHECKRES(avifRWStreamFinishBox(dedupStream, colr)); + if (dedup) { + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, associations, /*essential=*/AVIF_FALSE)); + } + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderWritePaspProperty(avifRWStream * dedupStream, + avifRWStream * outputStream, + const avifImage * imageMetadata, + avifItemPropertyAssociationArray * associations, + avifItemPropertyDedup * dedup); +static avifResult avifEncoderWriteTransformativeProperties(avifRWStream * dedupStream, + avifRWStream * outputStream, + const avifImage * imageMetadata, + avifItemPropertyAssociationArray * associations, + avifItemPropertyDedup * dedup); + +// This function is used in two codepaths: +// * writing color *item* properties +// * writing color *track* properties +// +// Item properties must have property associations with them and can be deduplicated (by reusing +// these associations), so this function leverages the ipma and dedup arguments to do this. +// +// Track properties, however, are implicitly associated by the track in which they are contained, so +// there is no need to build a property association box (ipma), and no way to deduplicate/reuse a +// property. In this case, the ipma and dedup properties should/will be set to NULL, and this +// function will avoid using them. +static avifResult avifEncoderWriteColorProperties(avifRWStream * outputStream, + const avifImage * imageMetadata, + avifItemPropertyAssociationArray * associations, + avifItemPropertyDedup * dedup) +{ + // outputStream is the final bitstream that will be output by the libavif encoder API. + // dedupStream is either equal to outputStream or to &dedup->s which is a temporary stream used + // to store parts of the final bitstream; these parts may be discarded if they are a duplicate + // of an already stored property. + avifRWStream * dedupStream = outputStream; + if (dedup) { + AVIF_ASSERT_OR_RETURN(associations); + + // Use the dedup's temporary stream for box writes. + dedupStream = &dedup->s; + } + + if (imageMetadata->icc.size > 0) { + if (dedup) { + avifItemPropertyDedupStart(dedup); + } + avifBoxMarker colr; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "colr", AVIF_BOX_SIZE_TBD, &colr)); + AVIF_CHECKRES(avifRWStreamWriteChars(dedupStream, "prof", 4)); // unsigned int(32) colour_type; + AVIF_CHECKRES(avifRWStreamWrite(dedupStream, imageMetadata->icc.data, imageMetadata->icc.size)); + AVIF_CHECKRES(avifRWStreamFinishBox(dedupStream, colr)); + if (dedup) { + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, associations, /*essential=*/AVIF_FALSE)); + } + } + + // HEIF 6.5.5.1, from Amendment 3 allows multiple colr boxes: "at most one for a given value of colour type" + // Therefore, *always* writing an nclx box, even if a prof box was already written above. + AVIF_CHECKRES(avifEncoderWriteNclxProperty(dedupStream, outputStream, imageMetadata, associations, dedup)); + + AVIF_CHECKRES(avifEncoderWritePaspProperty(dedupStream, outputStream, imageMetadata, associations, dedup)); + + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderWriteContentLightLevelInformation(avifRWStream * outputStream, + const avifContentLightLevelInformationBox * clli) +{ + AVIF_CHECKRES(avifRWStreamWriteBits(outputStream, clli->maxCLL, 16)); // unsigned int(16) max_content_light_level; + AVIF_CHECKRES(avifRWStreamWriteBits(outputStream, clli->maxPALL, 16)); // unsigned int(16) max_pic_average_light_level; + return AVIF_RESULT_OK; +} + +// Same as 'avifEncoderWriteColorProperties' but for properties related to High Dynamic Range only. +static avifResult avifEncoderWriteHDRProperties(avifRWStream * dedupStream, + avifRWStream * outputStream, + const avifImage * imageMetadata, + avifItemPropertyAssociationArray * associations, + avifItemPropertyDedup * dedup) +{ + // Write Content Light Level Information, if present + if (imageMetadata->clli.maxCLL || imageMetadata->clli.maxPALL) { + if (dedup) { + avifItemPropertyDedupStart(dedup); + } + avifBoxMarker clli; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "clli", AVIF_BOX_SIZE_TBD, &clli)); + AVIF_CHECKRES(avifEncoderWriteContentLightLevelInformation(dedupStream, &imageMetadata->clli)); + AVIF_CHECKRES(avifRWStreamFinishBox(dedupStream, clli)); + if (dedup) { + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, associations, /*essential=*/AVIF_FALSE)); + } + } + + // TODO(maryla): add other HDR boxes: mdcv, cclv, etc. (in avifEncoderWriteMiniHDRProperties() too) + + return AVIF_RESULT_OK; +} + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) +static avifResult avifEncoderWriteMiniHDRProperties(avifRWStream * outputStream, const avifImage * imageMetadata) +{ + const avifBool hasClli = imageMetadata->clli.maxCLL != 0 || imageMetadata->clli.maxPALL != 0; + const avifBool hasMdcv = AVIF_FALSE; + const avifBool hasCclv = AVIF_FALSE; + const avifBool hasAmve = AVIF_FALSE; + const avifBool hasReve = AVIF_FALSE; + const avifBool hasNdwt = AVIF_FALSE; + AVIF_CHECKRES(avifRWStreamWriteBits(outputStream, hasClli, 1)); // bit(1) clli_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(outputStream, hasMdcv, 1)); // bit(1) mdcv_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(outputStream, hasCclv, 1)); // bit(1) cclv_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(outputStream, hasAmve, 1)); // bit(1) amve_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(outputStream, hasReve, 1)); // bit(1) reve_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(outputStream, hasNdwt, 1)); // bit(1) ndwt_flag; + + if (hasClli) { + // ContentLightLevel clli; + AVIF_CHECKRES(avifEncoderWriteContentLightLevelInformation(outputStream, &imageMetadata->clli)); + } + if (hasMdcv) { + // MasteringDisplayColourVolume mdcv; + } + if (hasCclv) { + // ContentColourVolume cclv; + } + if (hasAmve) { + // AmbientViewingEnvironment amve; + } + if (hasReve) { + // ReferenceViewingEnvironment reve; + } + if (hasNdwt) { + // NominalDiffuseWhite ndwt; + } + return AVIF_RESULT_OK; +} +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + +static avifResult avifEncoderWritePaspProperty(avifRWStream * dedupStream, + avifRWStream * outputStream, + const avifImage * imageMetadata, + avifItemPropertyAssociationArray * associations, + avifItemPropertyDedup * dedup) +{ + if (imageMetadata->transformFlags & AVIF_TRANSFORM_PASP) { + if (dedup) { + avifItemPropertyDedupStart(dedup); + } + avifBoxMarker pasp; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "pasp", AVIF_BOX_SIZE_TBD, &pasp)); + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->pasp.hSpacing)); // unsigned int(32) hSpacing; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->pasp.vSpacing)); // unsigned int(32) vSpacing; + AVIF_CHECKRES(avifRWStreamFinishBox(dedupStream, pasp)); + if (dedup) { + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, associations, /*essential=*/AVIF_FALSE)); + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderWriteTransformativeProperties(avifRWStream * dedupStream, + avifRWStream * outputStream, + const avifImage * imageMetadata, + avifItemPropertyAssociationArray * associations, + avifItemPropertyDedup * dedup) +{ + if (imageMetadata->transformFlags & AVIF_TRANSFORM_CLAP) { + if (dedup) { + avifItemPropertyDedupStart(dedup); + } + avifBoxMarker clap; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "clap", AVIF_BOX_SIZE_TBD, &clap)); + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.widthN)); // unsigned int(32) cleanApertureWidthN; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.widthD)); // unsigned int(32) cleanApertureWidthD; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.heightN)); // unsigned int(32) cleanApertureHeightN; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.heightD)); // unsigned int(32) cleanApertureHeightD; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.horizOffN)); // unsigned int(32) horizOffN; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.horizOffD)); // unsigned int(32) horizOffD; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.vertOffN)); // unsigned int(32) vertOffN; + AVIF_CHECKRES(avifRWStreamWriteU32(dedupStream, imageMetadata->clap.vertOffD)); // unsigned int(32) vertOffD; + AVIF_CHECKRES(avifRWStreamFinishBox(dedupStream, clap)); + if (dedup) { + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, associations, /*essential=*/AVIF_TRUE)); + } + } + if (imageMetadata->transformFlags & AVIF_TRANSFORM_IROT) { + if (dedup) { + avifItemPropertyDedupStart(dedup); + } + avifBoxMarker irot; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "irot", AVIF_BOX_SIZE_TBD, &irot)); + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, 0, /*bitCount=*/6)); // unsigned int (6) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, imageMetadata->irot.angle & 0x3, /*bitCount=*/2)); // unsigned int (2) angle; + AVIF_CHECKRES(avifRWStreamFinishBox(dedupStream, irot)); + if (dedup) { + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, associations, /*essential=*/AVIF_TRUE)); + } + } + if (imageMetadata->transformFlags & AVIF_TRANSFORM_IMIR) { + if (dedup) { + avifItemPropertyDedupStart(dedup); + } + avifBoxMarker imir; + AVIF_CHECKRES(avifRWStreamWriteBox(dedupStream, "imir", AVIF_BOX_SIZE_TBD, &imir)); + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, 0, /*bitCount=*/7)); // unsigned int(7) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(dedupStream, imageMetadata->imir.axis ? 1 : 0, /*bitCount=*/1)); // unsigned int(1) axis; + AVIF_CHECKRES(avifRWStreamFinishBox(dedupStream, imir)); + if (dedup) { + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, outputStream, associations, /*essential=*/AVIF_TRUE)); + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifRWStreamWriteHandlerBox(avifRWStream * s, const char handlerType[4]) +{ + avifBoxMarker hdlr; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "hdlr", AVIF_BOX_SIZE_TBD, 0, 0, &hdlr)); + AVIF_CHECKRES(avifRWStreamWriteU32(s, 0)); // unsigned int(32) pre_defined = 0; + AVIF_CHECKRES(avifRWStreamWriteChars(s, handlerType, 4)); // unsigned int(32) handler_type; + AVIF_CHECKRES(avifRWStreamWriteZeros(s, 12)); // const unsigned int(32)[3] reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteChars(s, "", 1)); // string name; (writing null terminator) + AVIF_CHECKRES(avifRWStreamFinishBox(s, hdlr)); + return AVIF_RESULT_OK; +} + +// Write unassociated metadata items (EXIF, XMP) to a small meta box inside of a trak box. +// These items are implicitly associated with the track they are contained within. +static avifResult avifEncoderWriteTrackMetaBox(avifEncoder * encoder, avifRWStream * s) +{ + // Count how many non-image items (such as EXIF/XMP) are being written + uint32_t metadataItemCount = 0; + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if (memcmp(item->type, encoder->data->imageItemType, 4) != 0) { + ++metadataItemCount; + } + } + if (metadataItemCount == 0) { + // Don't even bother writing the trak meta box + return AVIF_RESULT_OK; + } + + avifBoxMarker meta; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "meta", AVIF_BOX_SIZE_TBD, 0, 0, &meta)); + + AVIF_CHECKRES(avifRWStreamWriteHandlerBox(s, "pict")); + + avifBoxMarker iloc; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0, &iloc)); + AVIF_CHECKRES(avifRWStreamWriteBits(s, 4, /*bitCount=*/4)); // unsigned int(4) offset_size; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 4, /*bitCount=*/4)); // unsigned int(4) length_size; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, /*bitCount=*/4)); // unsigned int(4) base_offset_size; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, /*bitCount=*/4)); // unsigned int(4) reserved; + AVIF_CHECKRES(avifRWStreamWriteU16(s, (uint16_t)metadataItemCount)); // unsigned int(16) item_count; + for (uint32_t trakItemIndex = 0; trakItemIndex < encoder->data->items.count; ++trakItemIndex) { + avifEncoderItem * item = &encoder->data->items.item[trakItemIndex]; + if (memcmp(item->type, encoder->data->imageItemType, 4) == 0) { + // Skip over all non-metadata items + continue; + } + + AVIF_CHECKRES(avifRWStreamWriteU16(s, item->id)); // unsigned int(16) item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(s, 0)); // unsigned int(16) data_reference_index; + AVIF_CHECKRES(avifRWStreamWriteU16(s, 1)); // unsigned int(16) extent_count; + AVIF_CHECKRES(avifEncoderItemAddMdatFixup(item, s)); // + AVIF_CHECKRES(avifRWStreamWriteU32(s, 0 /* set later */)); // unsigned int(offset_size*8) extent_offset; + AVIF_CHECKRES(avifRWStreamWriteU32(s, (uint32_t)item->metadataPayload.size)); // unsigned int(length_size*8) extent_length; + } + AVIF_CHECKRES(avifRWStreamFinishBox(s, iloc)); + + avifBoxMarker iinf; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0, &iinf)); + AVIF_CHECKRES(avifRWStreamWriteU16(s, (uint16_t)metadataItemCount)); // unsigned int(16) entry_count; + for (uint32_t trakItemIndex = 0; trakItemIndex < encoder->data->items.count; ++trakItemIndex) { + avifEncoderItem * item = &encoder->data->items.item[trakItemIndex]; + if (memcmp(item->type, encoder->data->imageItemType, 4) == 0) { + continue; + } + + AVIF_ASSERT_OR_RETURN(!item->hiddenImage); + avifBoxMarker infe; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "infe", AVIF_BOX_SIZE_TBD, 2, 0, &infe)); + AVIF_CHECKRES(avifRWStreamWriteU16(s, item->id)); // unsigned int(16) item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(s, 0)); // unsigned int(16) item_protection_index; + AVIF_CHECKRES(avifRWStreamWrite(s, item->type, 4)); // unsigned int(32) item_type; + AVIF_CHECKRES(avifRWStreamWriteChars(s, item->infeName, item->infeNameSize)); // string item_name; (writing null terminator) + if (item->infeContentType && item->infeContentTypeSize) { // string content_type; (writing null terminator) + AVIF_CHECKRES(avifRWStreamWriteChars(s, item->infeContentType, item->infeContentTypeSize)); + } + AVIF_CHECKRES(avifRWStreamFinishBox(s, infe)); + } + AVIF_CHECKRES(avifRWStreamFinishBox(s, iinf)); + + AVIF_CHECKRES(avifRWStreamFinishBox(s, meta)); + return AVIF_RESULT_OK; +} + +static avifResult avifWriteGridPayload(avifRWData * data, uint32_t gridCols, uint32_t gridRows, uint32_t gridWidth, uint32_t gridHeight) +{ + // ISO/IEC 23008-12 6.6.2.3.2 + // aligned(8) class ImageGrid { + // unsigned int(8) version = 0; + // unsigned int(8) flags; + // FieldLength = ((flags & 1) + 1) * 16; + // unsigned int(8) rows_minus_one; + // unsigned int(8) columns_minus_one; + // unsigned int(FieldLength) output_width; + // unsigned int(FieldLength) output_height; + // } + + uint8_t gridFlags = ((gridWidth > 65535) || (gridHeight > 65535)) ? 1 : 0; + + avifRWStream s; + avifRWStreamStart(&s, data); + AVIF_CHECKRES(avifRWStreamWriteU8(&s, 0)); // unsigned int(8) version = 0; + AVIF_CHECKRES(avifRWStreamWriteU8(&s, gridFlags)); // unsigned int(8) flags; + AVIF_CHECKRES(avifRWStreamWriteU8(&s, (uint8_t)(gridRows - 1))); // unsigned int(8) rows_minus_one; + AVIF_CHECKRES(avifRWStreamWriteU8(&s, (uint8_t)(gridCols - 1))); // unsigned int(8) columns_minus_one; + if (gridFlags & 1) { + AVIF_CHECKRES(avifRWStreamWriteU32(&s, gridWidth)); // unsigned int(FieldLength) output_width; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, gridHeight)); // unsigned int(FieldLength) output_height; + } else { + uint16_t tmpWidth = (uint16_t)gridWidth; + uint16_t tmpHeight = (uint16_t)gridHeight; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, tmpWidth)); // unsigned int(FieldLength) output_width; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, tmpHeight)); // unsigned int(FieldLength) output_height; + } + avifRWStreamFinishWrite(&s); + return AVIF_RESULT_OK; +} + +static avifBool avifGainMapIdenticalChannels(const avifGainMap * gainMap) +{ + return gainMap->gainMapMin[0].n == gainMap->gainMapMin[1].n && gainMap->gainMapMin[0].n == gainMap->gainMapMin[2].n && + gainMap->gainMapMin[0].d == gainMap->gainMapMin[1].d && gainMap->gainMapMin[0].d == gainMap->gainMapMin[2].d && + gainMap->gainMapMax[0].n == gainMap->gainMapMax[1].n && gainMap->gainMapMax[0].n == gainMap->gainMapMax[2].n && + gainMap->gainMapMax[0].d == gainMap->gainMapMax[1].d && gainMap->gainMapMax[0].d == gainMap->gainMapMax[2].d && + gainMap->gainMapGamma[0].n == gainMap->gainMapGamma[1].n && gainMap->gainMapGamma[0].n == gainMap->gainMapGamma[2].n && + gainMap->gainMapGamma[0].d == gainMap->gainMapGamma[1].d && gainMap->gainMapGamma[0].d == gainMap->gainMapGamma[2].d && + gainMap->baseOffset[0].n == gainMap->baseOffset[1].n && gainMap->baseOffset[0].n == gainMap->baseOffset[2].n && + gainMap->baseOffset[0].d == gainMap->baseOffset[1].d && gainMap->baseOffset[0].d == gainMap->baseOffset[2].d && + gainMap->alternateOffset[0].n == gainMap->alternateOffset[1].n && + gainMap->alternateOffset[0].n == gainMap->alternateOffset[2].n && + gainMap->alternateOffset[0].d == gainMap->alternateOffset[1].d && + gainMap->alternateOffset[0].d == gainMap->alternateOffset[2].d; +} + +// Returns the number of bytes written by avifWriteGainmapMetadata(). +static uint32_t avifGainMapMetadataSize(const avifGainMap * gainMap) +{ + const uint8_t channelCount = avifGainMapIdenticalChannels(gainMap) ? 1u : 3u; + return (uint32_t)(sizeof(uint16_t) * 2 + sizeof(uint8_t) + sizeof(uint32_t) * 4 + channelCount * sizeof(uint32_t) * 10); +} + +static avifResult avifWriteGainmapMetadata(avifRWStream * s, const avifGainMap * gainMap, avifDiagnostics * diag) +{ + AVIF_CHECKRES(avifGainMapValidateMetadata(gainMap, diag)); + const size_t offset = avifRWStreamOffset(s); + + // GainMapMetadata syntax as per clause C.2.2 of ISO 21496-1: + + // GainMapVersion syntax as per clause C.2.2 of ISO 21496-1: + const uint16_t minimumVersion = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(s, minimumVersion, 16)); // unsigned int(16) minimum_version; + const uint16_t writerVersion = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(s, writerVersion, 16)); // unsigned int(16) writer_version; + + if (minimumVersion == 0) { + const uint8_t channelCount = avifGainMapIdenticalChannels(gainMap) ? 1u : 3u; + AVIF_CHECKRES(avifRWStreamWriteBits(s, channelCount == 3, 1)); // unsigned int(1) is_multichannel; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->useBaseColorSpace, 1)); // unsigned int(1) use_base_colour_space; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, 6)); // unsigned int(6) reserved; + + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->baseHdrHeadroom.n, 32)); // unsigned int(32) base_hdr_headroom_numerator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->baseHdrHeadroom.d, 32)); // unsigned int(32) base_hdr_headroom_denominator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->alternateHdrHeadroom.n, 32)); // unsigned int(32) alternate_hdr_headroom_numerator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->alternateHdrHeadroom.d, 32)); // unsigned int(32) alternate_hdr_headroom_denominator; + + // GainMapChannel channels[channel_count]; + for (int c = 0; c < channelCount; ++c) { + // GainMapChannel syntax as per clause C.2.2 of ISO 21496-1: + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)gainMap->gainMapMin[c].n, 32)); // int(32) gain_map_min_numerator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->gainMapMin[c].d, 32)); // unsigned int(32) gain_map_min_denominator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)gainMap->gainMapMax[c].n, 32)); // int(32) gain_map_max_numerator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->gainMapMax[c].d, 32)); // unsigned int(32) gain_map_max_denominator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->gainMapGamma[c].n, 32)); // unsigned int(32) gamma_numerator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->gainMapGamma[c].d, 32)); // unsigned int(32) gamma_denominator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)gainMap->baseOffset[c].n, 32)); // int(32) base_offset_numerator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->baseOffset[c].d, 32)); // unsigned int(32) base_offset_denominator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)gainMap->alternateOffset[c].n, 32)); // int(32) alternate_offset_numerator; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainMap->alternateOffset[c].d, 32)); // unsigned int(32) alternate_offset_denominator; + } + } + + AVIF_ASSERT_OR_RETURN(avifRWStreamOffset(s) == offset + avifGainMapMetadataSize(gainMap)); + return AVIF_RESULT_OK; +} + +static avifResult avifWriteToneMappedImagePayload(avifRWData * data, const avifGainMap * gainMap, avifDiagnostics * diag) +{ + avifRWStream s; + avifRWStreamStart(&s, data); + // ToneMapImage syntax as per section 6.6.2.4.2 of ISO/IEC 23008-12:2024 + // amendment "Support for tone map derived image items and other improvements": + const uint8_t version = 0; + AVIF_CHECKRES(avifRWStreamWriteU8(&s, version)); // unsigned int(8) version = 0; + if (version == 0) { + AVIF_CHECKRES(avifWriteGainmapMetadata(&s, gainMap, diag)); // GainMapMetadata; + } + avifRWStreamFinishWrite(&s); + return AVIF_RESULT_OK; +} + +size_t avifEncoderGetGainMapSizeBytes(avifEncoder * encoder) +{ + return encoder->data->gainMapSizeBytes; +} + +// Sets altImageMetadata's metadata values to represent the "alternate" image as if applying the gain map to the base image. +// For grid images, imageWithGainMap is the metadata of the first cell. gridWidth and gridHeight are the dimensions of the +// full image. +static avifResult avifImageCopyAltImageMetadata(avifImage * altImageMetadata, const avifImage * imageWithGainMap, uint32_t gridWidth, uint32_t gridHeight) +{ + altImageMetadata->width = gridWidth; + altImageMetadata->height = gridHeight; + AVIF_CHECKRES(avifRWDataSet(&altImageMetadata->icc, imageWithGainMap->gainMap->altICC.data, imageWithGainMap->gainMap->altICC.size)); + altImageMetadata->colorPrimaries = imageWithGainMap->gainMap->altColorPrimaries; + altImageMetadata->transferCharacteristics = imageWithGainMap->gainMap->altTransferCharacteristics; + altImageMetadata->matrixCoefficients = imageWithGainMap->gainMap->altMatrixCoefficients; + altImageMetadata->yuvRange = imageWithGainMap->gainMap->altYUVRange; + altImageMetadata->depth = imageWithGainMap->gainMap->altDepth + ? imageWithGainMap->gainMap->altDepth + : AVIF_MAX(imageWithGainMap->depth, imageWithGainMap->gainMap->image->depth); + altImageMetadata->yuvFormat = (imageWithGainMap->gainMap->altPlaneCount == 1) ? AVIF_PIXEL_FORMAT_YUV400 : AVIF_PIXEL_FORMAT_YUV444; + altImageMetadata->clli = imageWithGainMap->gainMap->altCLLI; + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderWriteSampleTransformTokens(avifRWStream * s, const avifSampleTransformExpression * expression) +{ + AVIF_ASSERT_OR_RETURN(expression->count <= 255); + AVIF_CHECKRES(avifRWStreamWriteU8(s, (uint8_t)expression->count)); // unsigned int(8) token_count; + + for (uint32_t t = 0; t < expression->count; ++t) { + const avifSampleTransformToken * token = &expression->tokens[t]; + + if (token->type == AVIF_SAMPLE_TRANSFORM_CONSTANT) { + AVIF_CHECKRES(avifRWStreamWriteU8(s, token->type)); // unsigned int(8) token; + const uint32_t constant = (uint32_t)token->constant; + AVIF_CHECKRES(avifRWStreamWriteU32(s, constant)); // signed int(1<<(bit_depth+3)) constant; + } else if (token->type == AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX) { + AVIF_CHECKRES(avifRWStreamWriteU8(s, token->inputImageItemIndex)); // unsigned int(8) token; + } else { + // Operator. + AVIF_CHECKRES(avifRWStreamWriteU8(s, token->type)); // unsigned int(8) token; + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderWriteSampleTransformPayload(avifEncoder * encoder, avifRWData * data) +{ + avifRWStream s; + avifRWStreamStart(&s, data); + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/2)); // unsigned int(2) version = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/4)); // unsigned int(4) reserved; + // AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32 is necessary because the two input images + // once combined use 16-bit unsigned values, but intermediate results are stored in signed integers. + AVIF_CHECKRES(avifRWStreamWriteBits(&s, AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32, /*bitCount=*/2)); // unsigned int(2) bit_depth; + + avifSampleTransformExpression expression = { 0 }; + AVIF_CHECKRES(avifSampleTransformRecipeToExpression(encoder->sampleTransformRecipe, &expression)); + const avifResult result = avifEncoderWriteSampleTransformTokens(&s, &expression); + avifArrayDestroy(&expression); + if (result != AVIF_RESULT_OK) { + avifDiagnosticsPrintf(&encoder->diag, "Failed to write sample transform metadata for recipe %d", (int)encoder->sampleTransformRecipe); + return result; + } + + avifRWStreamFinishWrite(&s); + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderDataCreateExifItem(avifEncoderData * data, const avifRWData * exif) +{ + size_t exifTiffHeaderOffset; + const avifResult result = avifGetExifTiffHeaderOffset(exif->data, exif->size, &exifTiffHeaderOffset); + if (result != AVIF_RESULT_OK) { + // Couldn't find the TIFF header + return result; + } + + avifEncoderItem * exifItem = avifEncoderDataCreateItem(data, "Exif", "Exif", 5, 0); + if (!exifItem) { + return AVIF_RESULT_OUT_OF_MEMORY; + } + exifItem->irefToID = data->primaryItemID; + exifItem->irefType = "cdsc"; + + const uint32_t offset32bit = avifHTONL((uint32_t)exifTiffHeaderOffset); + AVIF_CHECKRES(avifRWDataRealloc(&exifItem->metadataPayload, sizeof(offset32bit) + exif->size)); + memcpy(exifItem->metadataPayload.data, &offset32bit, sizeof(offset32bit)); + memcpy(exifItem->metadataPayload.data + sizeof(offset32bit), exif->data, exif->size); + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderDataCreateXMPItem(avifEncoderData * data, const avifRWData * xmp) +{ + avifEncoderItem * xmpItem = avifEncoderDataCreateItem(data, "mime", "XMP", 4, 0); + if (!xmpItem) { + return AVIF_RESULT_OUT_OF_MEMORY; + } + xmpItem->irefToID = data->primaryItemID; + xmpItem->irefType = "cdsc"; + + xmpItem->infeContentType = xmpContentType; + xmpItem->infeContentTypeSize = xmpContentTypeSize; + AVIF_CHECKRES(avifRWDataSet(&xmpItem->metadataPayload, xmp->data, xmp->size)); + return AVIF_RESULT_OK; +} + +// Same as avifImageCopy() but pads the dstImage with border pixel values to reach dstWidth and dstHeight. +static avifResult avifImageCopyAndPad(avifImage * const dstImage, const avifImage * srcImage, uint32_t dstWidth, uint32_t dstHeight) +{ + AVIF_ASSERT_OR_RETURN(dstImage); + AVIF_ASSERT_OR_RETURN(!dstImage->width && !dstImage->height); // dstImage is not set yet. + AVIF_ASSERT_OR_RETURN(dstWidth >= srcImage->width); + AVIF_ASSERT_OR_RETURN(dstHeight >= srcImage->height); + + // Copy all fields but do not allocate the planes. + AVIF_CHECKRES(avifImageCopy(dstImage, srcImage, (avifPlanesFlag)0)); + dstImage->width = dstWidth; + dstImage->height = dstHeight; + + if (srcImage->yuvPlanes[AVIF_CHAN_Y]) { + AVIF_CHECKRES(avifImageAllocatePlanes(dstImage, AVIF_PLANES_YUV)); + } + if (srcImage->alphaPlane) { + AVIF_CHECKRES(avifImageAllocatePlanes(dstImage, AVIF_PLANES_A)); + } + const avifBool usesU16 = avifImageUsesU16(srcImage); + for (int plane = AVIF_CHAN_Y; plane <= AVIF_CHAN_A; ++plane) { + const uint8_t * srcRow = avifImagePlane(srcImage, plane); + const uint32_t srcRowBytes = avifImagePlaneRowBytes(srcImage, plane); + const uint32_t srcPlaneWidth = avifImagePlaneWidth(srcImage, plane); + const uint32_t srcPlaneHeight = avifImagePlaneHeight(srcImage, plane); // 0 for A if no alpha and 0 for UV if 4:0:0. + const size_t srcPlaneWidthBytes = (size_t)srcPlaneWidth << usesU16; + + uint8_t * dstRow = avifImagePlane(dstImage, plane); + const uint32_t dstRowBytes = avifImagePlaneRowBytes(dstImage, plane); + const uint32_t dstPlaneWidth = avifImagePlaneWidth(dstImage, plane); + const uint32_t dstPlaneHeight = avifImagePlaneHeight(dstImage, plane); // 0 for A if no alpha and 0 for UV if 4:0:0. + const size_t dstPlaneWidthBytes = (size_t)dstPlaneWidth << usesU16; + + for (uint32_t j = 0; j < srcPlaneHeight; ++j) { + memcpy(dstRow, srcRow, srcPlaneWidthBytes); + + // Pad columns. + if (dstPlaneWidth > srcPlaneWidth) { + if (usesU16) { + uint16_t * dstRow16 = (uint16_t *)dstRow; + for (uint32_t x = srcPlaneWidth; x < dstPlaneWidth; ++x) { + dstRow16[x] = dstRow16[srcPlaneWidth - 1]; + } + } else { + memset(&dstRow[srcPlaneWidth], dstRow[srcPlaneWidth - 1], dstPlaneWidth - srcPlaneWidth); + } + } + srcRow += srcRowBytes; + dstRow += dstRowBytes; + } + + // Pad rows. + for (uint32_t j = srcPlaneHeight; j < dstPlaneHeight; ++j) { + memcpy(dstRow, dstRow - dstRowBytes, dstPlaneWidthBytes); + dstRow += dstRowBytes; + } + } + return AVIF_RESULT_OK; +} + +static int avifGetQuality(int quality, int minQuantizer, int maxQuantizer) +{ + int quantizer; + + if (quality == AVIF_QUALITY_DEFAULT) { + // In older libavif releases, avifEncoder didn't have the quality and qualityAlpha fields. + // Supply a default value for quality. + quantizer = (minQuantizer + maxQuantizer) / 2; + quantizer = AVIF_CLAMP(quantizer, 0, 63); + quality = ((63 - quantizer) * 100 + 31) / 63; + } else { + quality = AVIF_CLAMP(quality, 0, 100); + } + + return quality; +} + +static const char infeNameColor[] = "Color"; +static const char infeNameAlpha[] = "Alpha"; +static const char infeNameGainMap[] = "GMap"; +static const char infeNameSampleTransform[] = "SampleTransform"; + +static const char * getInfeName(avifItemCategory itemCategory) +{ + if (avifIsAlpha(itemCategory)) { + return infeNameAlpha; + } + if (itemCategory == AVIF_ITEM_GAIN_MAP) { + return infeNameGainMap; + } + if (itemCategory >= AVIF_SAMPLE_TRANSFORM_MIN_CATEGORY && itemCategory <= AVIF_SAMPLE_TRANSFORM_MAX_CATEGORY) { + return infeNameSampleTransform; + } + return infeNameColor; +} + +// Adds the items for a single cell or a grid of cells. Outputs the topLevelItemID which is +// the only item if there is exactly one cell, or the grid item for multiple cells. +// Note: The topLevelItemID output argument has the type uint16_t* instead of avifEncoderItem** because +// the avifEncoderItem pointer may be invalidated by a call to avifEncoderDataCreateItem(). +static avifResult avifEncoderAddImageItems(avifEncoder * encoder, + uint32_t gridCols, + uint32_t gridRows, + uint32_t gridWidth, + uint32_t gridHeight, + avifItemCategory itemCategory, + uint16_t * topLevelItemID) +{ + const uint32_t cellCount = gridCols * gridRows; + const char * infeName = getInfeName(itemCategory); + const size_t infeNameSize = strlen(infeName) + 1; + + if (cellCount > 1) { + avifEncoderItem * gridItem = avifEncoderDataCreateItem(encoder->data, "grid", infeName, infeNameSize, 0); + AVIF_CHECKRES(avifWriteGridPayload(&gridItem->metadataPayload, gridCols, gridRows, gridWidth, gridHeight)); + gridItem->itemCategory = itemCategory; + gridItem->gridCols = gridCols; + gridItem->gridRows = gridRows; + gridItem->gridWidth = gridWidth; + gridItem->gridHeight = gridHeight; + *topLevelItemID = gridItem->id; + } + + for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) { + avifEncoderItem * item = + avifEncoderDataCreateItem(encoder->data, encoder->data->imageItemType, infeName, infeNameSize, cellIndex); + AVIF_CHECKERR(item, AVIF_RESULT_OUT_OF_MEMORY); + AVIF_CHECKRES(avifCodecCreate(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE, &item->codec)); + item->codec->csOptions = encoder->csOptions; + item->codec->diag = &encoder->diag; + item->itemCategory = itemCategory; + item->extraLayerCount = encoder->extraLayerCount; + + if (cellCount > 1) { + item->dimgFromID = *topLevelItemID; + item->hiddenImage = AVIF_TRUE; + } else { + *topLevelItemID = item->id; + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderCreateBitDepthExtensionItems(avifEncoder * encoder, + uint32_t gridCols, + uint32_t gridRows, + uint32_t gridWidth, + uint32_t gridHeight, + uint16_t colorItemID) +{ + AVIF_ASSERT_OR_RETURN(encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B || + encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B || + encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_8B_OVERLAP_4B); + + // There are multiple possible ISOBMFF box hierarchies for translucent images, + // using 'sato' (Sample Transform) derived image items: + // - a primary 'sato' item uses a main color coded item and a hidden color coded item; each color coded + // item has an auxiliary alpha coded item; the main color coded item and the 'sato' item are in + // an 'altr' group (backward-compatible, implemented) + // - a primary 'sato' item uses a main color coded item and a hidden color coded item; the primary + // 'sato' item has an auxiliary alpha 'sato' item using two alpha coded items (backward-incompatible) + // Likewise, there are multiple possible ISOBMFF box hierarchies for bit-depth-extended grids, + // using 'sato' (Sample Transform) derived image items: + // - a primary color 'grid', an auxiliary alpha 'grid', a hidden color 'grid', a hidden auxiliary alpha 'grid' + // and a 'sato' using the two color 'grid's as input items in this order; the primary color item + // and the 'sato' item being in an 'altr' group (backward-compatible, implemented) + // - a primary 'grid' of 'sato' cells and an auxiliary alpha 'grid' of 'sato' cells (backward-incompatible) + avifEncoderItem * sampleTransformItem = avifEncoderDataCreateItem(encoder->data, + "sato", + infeNameSampleTransform, + /*infeNameSize=*/strlen(infeNameSampleTransform) + 1, + /*cellIndex=*/0); + AVIF_CHECKRES(avifEncoderWriteSampleTransformPayload(encoder, &sampleTransformItem->metadataPayload)); + sampleTransformItem->itemCategory = AVIF_ITEM_SAMPLE_TRANSFORM; + uint16_t sampleTransformItemID = sampleTransformItem->id; + // 'altr' group + AVIF_ASSERT_OR_RETURN(encoder->data->alternativeItemIDs.count == 0); + uint16_t * alternativeItemID = (uint16_t *)avifArrayPush(&encoder->data->alternativeItemIDs); + AVIF_CHECKERR(alternativeItemID != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *alternativeItemID = sampleTransformItem->id; + alternativeItemID = (uint16_t *)avifArrayPush(&encoder->data->alternativeItemIDs); + AVIF_CHECKERR(alternativeItemID != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *alternativeItemID = colorItemID; + + uint16_t bitDepthExtensionColorItemId; + AVIF_CHECKRES( + avifEncoderAddImageItems(encoder, gridCols, gridRows, gridWidth, gridHeight, AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_COLOR, &bitDepthExtensionColorItemId)); + avifEncoderItem * bitDepthExtensionColorItem = avifEncoderDataFindItemByID(encoder->data, bitDepthExtensionColorItemId); + assert(bitDepthExtensionColorItem); + bitDepthExtensionColorItem->hiddenImage = AVIF_TRUE; + + // Set the color and bit depth extension items' dimgFromID value to point to the sample transform item. + // The color item shall be first, and the bit depth extension item second. avifEncoderFinish() writes the + // dimg item references in item id order, so as long as colorItemID < bitDepthExtensionColorItemId, the order + // will be correct. + AVIF_ASSERT_OR_RETURN(colorItemID < bitDepthExtensionColorItemId); + avifEncoderItem * colorItem = avifEncoderDataFindItemByID(encoder->data, colorItemID); + AVIF_ASSERT_OR_RETURN(colorItem != NULL); + AVIF_ASSERT_OR_RETURN(colorItem->dimgFromID == 0); // The internal API only allows one dimg value per item. + colorItem->dimgFromID = sampleTransformItemID; + bitDepthExtensionColorItem->dimgFromID = sampleTransformItemID; + + if (encoder->data->alphaPresent) { + uint16_t bitDepthExtensionAlphaItemId; + AVIF_CHECKRES( + avifEncoderAddImageItems(encoder, gridCols, gridRows, gridWidth, gridHeight, AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_ALPHA, &bitDepthExtensionAlphaItemId)); + avifEncoderItem * bitDepthExtensionAlphaItem = avifEncoderDataFindItemByID(encoder->data, bitDepthExtensionAlphaItemId); + assert(bitDepthExtensionAlphaItem); + bitDepthExtensionAlphaItem->irefType = "auxl"; + bitDepthExtensionAlphaItem->irefToID = bitDepthExtensionColorItemId; + if (encoder->data->imageMetadata->alphaPremultiplied) { + // The reference may have changed; fetch it again. + bitDepthExtensionColorItem = avifEncoderDataFindItemByID(encoder->data, bitDepthExtensionColorItemId); + assert(bitDepthExtensionColorItem); + bitDepthExtensionColorItem->irefType = "prem"; + bitDepthExtensionColorItem->irefToID = bitDepthExtensionAlphaItemId; + } + } + return AVIF_RESULT_OK; +} + +// Same as avifImageApplyExpression() but for the expression (inputImageItem [op] constant). +// Convenience function. +static avifResult avifImageApplyImgOpConst(avifImage * result, + const avifImage * inputImageItem, + avifSampleTransformTokenType op, + int32_t constant, + avifPlanesFlags planes) +{ + // Postfix notation. + const avifSampleTransformToken tokens[] = { { AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX, 0, /*inputImageItemIndex=*/1 }, + { AVIF_SAMPLE_TRANSFORM_CONSTANT, constant, 0 }, + { (uint8_t)op, 0, 0 } }; + return avifImageApplyOperations(result, AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32, /*numTokens=*/3, tokens, /*numInputImageItems=*/1, &inputImageItem, planes); +} + +static avifResult avifImageCreateAllocate(avifImage ** sampleTransformedImage, const avifImage * reference, uint32_t numBits, avifPlanesFlag planes) +{ + *sampleTransformedImage = avifImageCreate(reference->width, reference->height, numBits, reference->yuvFormat); + AVIF_CHECKERR(*sampleTransformedImage != NULL, AVIF_RESULT_OUT_OF_MEMORY); + return avifImageAllocatePlanes(*sampleTransformedImage, planes); +} + +// Finds the encoded base image and decodes it. Callers of this function must free +// *codec and *decodedBaseImage if not null, whether the function succeeds or not. +static avifResult avifEncoderDecodeSatoBaseImage(avifEncoder * encoder, + uint32_t cellIndex, + const avifImage * original, + uint32_t numBits, + avifPlanesFlag planes, + avifCodec ** codec, + avifImage ** decodedBaseImage) +{ + avifDecodeSample sample; + memset(&sample, 0, sizeof(sample)); + sample.spatialID = AVIF_SPATIAL_ID_UNSET; + + // Find the encoded bytes of the base image item. + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if (item->codec == NULL) { + continue; // Non-image item such as metadata. + } + if ((item->itemCategory != AVIF_ITEM_COLOR || planes != AVIF_PLANES_YUV) && + (item->itemCategory != AVIF_ITEM_ALPHA || planes != AVIF_PLANES_A)) { + continue; + } + if (item->cellIndex != cellIndex) { + continue; + } + + AVIF_ASSERT_OR_RETURN(item->encodeOutput != NULL); + AVIF_ASSERT_OR_RETURN(item->encodeOutput->samples.count == 1); + AVIF_ASSERT_OR_RETURN(item->encodeOutput->samples.sample[0].data.size != 0); + AVIF_ASSERT_OR_RETURN(sample.data.size == 0); // There should be only one base item. + sample.data.data = item->encodeOutput->samples.sample[0].data.data; + sample.data.size = item->encodeOutput->samples.sample[0].data.size; + } + AVIF_ASSERT_OR_RETURN(sample.data.size != 0); // There should be at least one base item. + + AVIF_CHECKRES(avifCodecCreate(AVIF_CODEC_CHOICE_AUTO, AVIF_CODEC_FLAG_CAN_DECODE, codec)); + (*codec)->diag = &encoder->diag; + (*codec)->maxThreads = encoder->maxThreads; + (*codec)->imageSizeLimit = AVIF_DEFAULT_IMAGE_SIZE_LIMIT; + (*codec)->imageDimensionLimit = AVIF_DEFAULT_IMAGE_DIMENSION_LIMIT; + AVIF_CHECKRES(avifImageCreateAllocate(decodedBaseImage, original, numBits, planes)); + avifBool isLimitedRangeAlpha = AVIF_FALSE; // Ignored. + AVIF_CHECKERR((*codec)->getNextImage(*codec, &sample, planes == AVIF_PLANES_A, &isLimitedRangeAlpha, *decodedBaseImage), + AVIF_RESULT_ENCODE_SAMPLE_TRANSFORM_FAILED); + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderCreateSatoImage(avifEncoder * encoder, + const avifEncoderItem * item, + avifBool itemWillBeEncodedLosslessly, + const avifImage * image, + avifImage ** sampleTransformedImage) +{ + const avifPlanesFlag planes = avifIsAlpha(item->itemCategory) ? AVIF_PLANES_A : AVIF_PLANES_YUV; + // The first image item used as input to the 'sato' Sample Transform derived image item. + avifBool isBase = item->itemCategory == AVIF_ITEM_COLOR || item->itemCategory == AVIF_ITEM_ALPHA; + if (!isBase) { + // The second image item used as input to the 'sato' Sample Transform derived image item. + AVIF_ASSERT_OR_RETURN(item->itemCategory >= AVIF_SAMPLE_TRANSFORM_MIN_CATEGORY && + item->itemCategory <= AVIF_SAMPLE_TRANSFORM_MAX_CATEGORY); + } + + if (encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B) { + if (isBase) { + AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 8, planes)); + AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_QUOTIENT, 256, planes)); + } else { + AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 8, planes)); + AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_AND, 255, planes)); + } + } else if (encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B) { + if (isBase) { + AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 12, planes)); + AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_QUOTIENT, 16, planes)); + } else { + AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 8, planes)); + AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_AND, 15, planes)); + // AVIF only supports 8, 10 or 12-bit image items. Scale the samples to fit the range. + // Note: The samples could be encoded as is without being shifted left before encoding, + // but they would not be shifted right after decoding either. Right shifting after + // decoding provides a guarantee on the range of values and on the lack of integer + // overflow, so it is safer to do these extra steps. + // It also makes more sense from a compression point-of-view to use the full range. + // Transform in-place. + AVIF_CHECKRES( + avifImageApplyImgOpConst(*sampleTransformedImage, *sampleTransformedImage, AVIF_SAMPLE_TRANSFORM_PRODUCT, 16, planes)); + if (!itemWillBeEncodedLosslessly) { + // Small loss at encoding could be amplified by the truncation caused by the right + // shift after decoding. Offset sample values now, before encoding, to round rather + // than floor the samples shifted after decoding. + // Note: Samples were just left shifted by numShiftedBits, so adding less than + // (1<sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_8B_OVERLAP_4B, + AVIF_RESULT_NOT_IMPLEMENTED); + if (isBase) { + AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 12, planes)); + AVIF_CHECKRES(avifImageApplyImgOpConst(*sampleTransformedImage, image, AVIF_SAMPLE_TRANSFORM_QUOTIENT, 16, planes)); + } else { + AVIF_CHECKRES(avifImageCreateAllocate(sampleTransformedImage, image, 8, planes)); + avifCodec * codec = NULL; + avifImage * decodedBaseImage = NULL; + avifResult result = avifEncoderDecodeSatoBaseImage(encoder, item->cellIndex, image, 12, planes, &codec, &decodedBaseImage); + if (result == AVIF_RESULT_OK) { + // decoded = main*16+hidden-128 so hidden = clamp_8b(original-main*16+128). Postfix notation. + const avifSampleTransformToken tokens[] = { { AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX, 0, /*inputImageItemIndex=*/1 }, + { AVIF_SAMPLE_TRANSFORM_INPUT_IMAGE_ITEM_INDEX, 0, /*inputImageItemIndex=*/2 }, + { AVIF_SAMPLE_TRANSFORM_CONSTANT, /*constant=*/16, 0 }, + { AVIF_SAMPLE_TRANSFORM_PRODUCT, 0, 0 }, + { AVIF_SAMPLE_TRANSFORM_DIFFERENCE, 0, 0 }, + { AVIF_SAMPLE_TRANSFORM_CONSTANT, /*constant=*/128, 0 }, + { AVIF_SAMPLE_TRANSFORM_SUM, 0, 0 } }; + // image is "original" (index 1) and decodedBaseImage is "main" (index 2) in the formula above. + const avifImage * inputImageItems[] = { image, decodedBaseImage }; + result = avifImageApplyOperations(*sampleTransformedImage, + AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_32, + /*numTokens=*/7, + tokens, + /*numInputImageItems=*/2, + inputImageItems, + planes); + } + if (decodedBaseImage) { + avifImageDestroy(decodedBaseImage); + } + if (codec) { + avifCodecDestroy(codec); + } + AVIF_CHECKRES(result); + } + } + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderCreateBitDepthExtensionImage(avifEncoder * encoder, + const avifEncoderItem * item, + avifBool itemWillBeEncodedLosslessly, + const avifImage * image, + avifImage ** sampleTransformedImage) +{ + AVIF_ASSERT_OR_RETURN(image->depth == 16); // Other bit depths could be supported but for now it is 16-bit only. + *sampleTransformedImage = NULL; + const avifResult result = avifEncoderCreateSatoImage(encoder, item, itemWillBeEncodedLosslessly, image, sampleTransformedImage); + if (result != AVIF_RESULT_OK && *sampleTransformedImage != NULL) { + avifImageDestroy(*sampleTransformedImage); + } + return result; +} + +static avifCodecType avifEncoderGetCodecType(const avifEncoder * encoder) +{ + // This asserts that images cannot be encoded with AVM unless AVIF_CODEC_CHOICE_AVM is explicitly selected. + assert((encoder->codecChoice != AVIF_CODEC_CHOICE_AUTO) || + (strcmp(avifCodecName(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE), "avm") != 0)); + return avifCodecTypeFromChoice(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE); +} + +// This function is called after every color frame is encoded. It returns AVIF_TRUE if a keyframe needs to be forced for the next +// alpha frame to be encoded, AVIF_FALSE otherwise. +static avifBool avifEncoderDataShouldForceKeyframeForAlpha(const avifEncoderData * data, + const avifEncoderItem * colorItem, + avifAddImageFlags addImageFlags) +{ + if (!data->alphaPresent) { + // There is no alpha plane. + return AVIF_FALSE; + } + if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { + // Not an animated image. + return AVIF_FALSE; + } + if (data->frames.count == 0) { + // data->frames.count is the number of frames that have been encoded so far by previous calls to avifEncoderAddImage. If + // this is the first frame, there is no need to force keyframe. + return AVIF_FALSE; + } + const uint32_t colorFramesOutputSoFar = colorItem->encodeOutput->samples.count; + const avifBool isLaggedOutput = (data->frames.count + 1) != colorFramesOutputSoFar; + if (isLaggedOutput) { + // If the encoder is operating with lag, then there is no way to determine if the last encoded frame was a keyframe until + // the encoder outputs it (after the lag). So do not force keyframe for alpha channel in this case. + return AVIF_FALSE; + } + return colorItem->encodeOutput->samples.sample[colorFramesOutputSoFar - 1].sync; +} + +static avifResult avifGetErrorForItemCategory(avifItemCategory itemCategory) +{ + if (itemCategory == AVIF_ITEM_GAIN_MAP) { + return AVIF_RESULT_ENCODE_GAIN_MAP_FAILED; + } + if (itemCategory == AVIF_ITEM_SAMPLE_TRANSFORM || + (itemCategory >= AVIF_SAMPLE_TRANSFORM_MIN_CATEGORY && itemCategory <= AVIF_SAMPLE_TRANSFORM_MAX_CATEGORY)) { + return AVIF_RESULT_ENCODE_SAMPLE_TRANSFORM_FAILED; + } + return avifIsAlpha(itemCategory) ? AVIF_RESULT_ENCODE_ALPHA_FAILED : AVIF_RESULT_ENCODE_COLOR_FAILED; +} + +static uint32_t avifGridWidth(uint32_t gridCols, const avifImage * firstCell, const avifImage * bottomRightCell) +{ + return (gridCols - 1) * firstCell->width + bottomRightCell->width; +} + +static uint32_t avifGridHeight(uint32_t gridRows, const avifImage * firstCell, const avifImage * bottomRightCell) +{ + return (gridRows - 1) * firstCell->height + bottomRightCell->height; +} + +static avifResult avifValidateGrid(uint32_t gridCols, + uint32_t gridRows, + const avifImage * const * cellImages, + avifBool validateGainMap, + avifDiagnostics * diag) +{ + const uint32_t cellCount = gridCols * gridRows; + const avifImage * firstCell = cellImages[0]; + const avifImage * bottomRightCell = cellImages[cellCount - 1]; + if (validateGainMap) { + AVIF_ASSERT_OR_RETURN(firstCell->gainMap && firstCell->gainMap->image); + firstCell = firstCell->gainMap->image; + AVIF_ASSERT_OR_RETURN(bottomRightCell->gainMap && bottomRightCell->gainMap->image); + bottomRightCell = bottomRightCell->gainMap->image; + } + const uint32_t tileWidth = firstCell->width; + const uint32_t tileHeight = firstCell->height; + if ((tileWidth > 65536) || (tileHeight > 65536)) { + avifDiagnosticsPrintf(diag, + "the first %s cell has invalid dimensions for AV1 and AV2: %ux%u", + validateGainMap ? "gain map" : "image", + tileWidth, + tileHeight); + return AVIF_RESULT_INVALID_ARGUMENT; + } + for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) { + const avifImage * cellImage = cellImages[cellIndex]; + if (validateGainMap) { + AVIF_ASSERT_OR_RETURN(cellImage->gainMap && cellImage->gainMap->image); + cellImage = cellImage->gainMap->image; + } + const uint32_t expectedCellWidth = ((cellIndex + 1) % gridCols) ? tileWidth : bottomRightCell->width; + const uint32_t expectedCellHeight = (cellIndex < (cellCount - gridCols)) ? tileHeight : bottomRightCell->height; + if ((cellImage->width != expectedCellWidth) || (cellImage->height != expectedCellHeight)) { + avifDiagnosticsPrintf(diag, + "%s cell %u has invalid dimensions: expected %ux%u found %ux%u", + validateGainMap ? "gain map" : "image", + cellIndex, + expectedCellWidth, + expectedCellHeight, + cellImage->width, + cellImage->height); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + + // MIAF (ISO 23000-22:2019), Section 7.3.11.4.1: + // All input images of a grid image item shall use the same coding format, chroma sampling format, and the + // same decoder configuration (see 7.3.6.2). + if ((cellImage->depth != firstCell->depth) || (cellImage->yuvFormat != firstCell->yuvFormat) || + (cellImage->yuvRange != firstCell->yuvRange) || (cellImage->colorPrimaries != firstCell->colorPrimaries) || + (cellImage->transferCharacteristics != firstCell->transferCharacteristics) || + (cellImage->matrixCoefficients != firstCell->matrixCoefficients) || (!!cellImage->alphaPlane != !!firstCell->alphaPlane) || + (cellImage->alphaPremultiplied != firstCell->alphaPremultiplied)) { + avifDiagnosticsPrintf(diag, + "all grid cells should have the same value for: depth, yuvFormat, yuvRange, colorPrimaries, " + "transferCharacteristics, matrixCoefficients, alphaPlane presence, alphaPremultiplied"); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + + // AV1 (Version 1.0.0 with Errata 1), Section 6.4.2. Color config semantics + // If matrix_coefficients is equal to MC_IDENTITY, it is a requirement of bitstream conformance that + // subsampling_x is equal to 0 and subsampling_y is equal to 0. + // Although matrix_coefficients in the Sequence Header OBU is set to Undefined (2), the requirement + // is still enforced here between what is written in the ColourInformationProperty of colour_type 'nclx' + // and the subsampling information in the Sequence Header OBU. + if (cellImage->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_IDENTITY && cellImage->yuvFormat != AVIF_PIXEL_FORMAT_YUV444) { + avifDiagnosticsPrintf(diag, "subsampling must be 0 (4:4:4) with identity matrix coefficients"); + return AVIF_RESULT_INVALID_ARGUMENT; + } + + if (!cellImage->yuvPlanes[AVIF_CHAN_Y]) { + return AVIF_RESULT_NO_CONTENT; + } + } + + if ((bottomRightCell->width > tileWidth) || (bottomRightCell->height > tileHeight)) { + avifDiagnosticsPrintf(diag, + "the last %s cell can be smaller but not larger than the other cells which are %ux%u, found %ux%u", + validateGainMap ? "gain map" : "image", + tileWidth, + tileHeight, + bottomRightCell->width, + bottomRightCell->height); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + const uint32_t gridWidth = avifGridWidth(gridCols, firstCell, bottomRightCell); + const uint32_t gridHeight = avifGridHeight(gridRows, firstCell, bottomRightCell); + if ((cellCount > 1) && !avifAreGridDimensionsValid(firstCell->yuvFormat, gridWidth, gridHeight, tileWidth, tileHeight, diag)) { + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderAddImageInternal(avifEncoder * encoder, + uint32_t gridCols, + uint32_t gridRows, + const avifImage * const * cellImages, + uint64_t durationInTimescales, + avifAddImageFlags addImageFlags) +{ + // ----------------------------------------------------------------------- + // Verify encoding is possible + + if (!avifCodecName(encoder->codecChoice, AVIF_CODEC_FLAG_CAN_ENCODE)) { + return AVIF_RESULT_NO_CODEC_AVAILABLE; + } + + if (encoder->extraLayerCount >= AVIF_MAX_AV1_LAYER_COUNT) { + avifDiagnosticsPrintf(&encoder->diag, "extraLayerCount [%u] must be less than %d", encoder->extraLayerCount, AVIF_MAX_AV1_LAYER_COUNT); + return AVIF_RESULT_INVALID_ARGUMENT; + } + + // ----------------------------------------------------------------------- + // Validate images + + const uint32_t cellCount = gridCols * gridRows; + if (cellCount == 0) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + + const avifImage * firstCell = cellImages[0]; + const avifImage * bottomRightCell = cellImages[cellCount - 1]; + AVIF_CHECKERR(firstCell->depth == 8 || firstCell->depth == 10 || firstCell->depth == 12 || + (firstCell->depth == 16 && encoder->sampleTransformRecipe != AVIF_SAMPLE_TRANSFORM_NONE), + AVIF_RESULT_UNSUPPORTED_DEPTH); + AVIF_CHECKERR(firstCell->yuvFormat != AVIF_PIXEL_FORMAT_NONE, AVIF_RESULT_NO_YUV_FORMAT_SELECTED); + if (!firstCell->width || !firstCell->height || !bottomRightCell->width || !bottomRightCell->height) { + return AVIF_RESULT_NO_CONTENT; + } + + AVIF_CHECKRES(avifValidateGrid(gridCols, gridRows, cellImages, /*validateGainMap=*/AVIF_FALSE, &encoder->diag)); + + const avifBool hasGainMap = (firstCell->gainMap && firstCell->gainMap->image != NULL); + + // Check that either all cells have a gain map, or none of them do. + // If a gain map is present, check that they all have the same gain map metadata. + for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) { + const avifImage * cellImage = cellImages[cellIndex]; + const avifBool cellHasGainMap = (cellImage->gainMap && cellImage->gainMap->image); + if (cellHasGainMap != hasGainMap) { + avifDiagnosticsPrintf(&encoder->diag, "cells should either all have a gain map image, or none of them should, found a mix"); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + if (hasGainMap) { + if (!avifSameGainMapAltMetadata(firstCell->gainMap, cellImage->gainMap)) { + avifDiagnosticsPrintf(&encoder->diag, "all cells should have the same alternate image metadata in the gain map"); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + if (!avifSameGainMapMetadata(firstCell->gainMap, cellImage->gainMap)) { + avifDiagnosticsPrintf(&encoder->diag, "all cells should have the same gain map metadata"); + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + } + } + + if (hasGainMap) { + // AVIF supports 16-bit images through sample transforms used as bit depth extensions, + // but this is not implemented for gain maps for now. Stick to at most 12 bits. + AVIF_CHECKERR(firstCell->gainMap->image->depth == 8 || firstCell->gainMap->image->depth == 10 || + firstCell->gainMap->image->depth == 12, + AVIF_RESULT_UNSUPPORTED_DEPTH); + AVIF_CHECKERR(firstCell->gainMap->image->yuvFormat != AVIF_PIXEL_FORMAT_NONE, AVIF_RESULT_NO_YUV_FORMAT_SELECTED); + AVIF_CHECKRES(avifValidateGrid(gridCols, gridRows, cellImages, /*validateGainMap=*/AVIF_TRUE, &encoder->diag)); + if (firstCell->gainMap->image->colorPrimaries != AVIF_COLOR_PRIMARIES_UNSPECIFIED || + firstCell->gainMap->image->transferCharacteristics != AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED) { + avifDiagnosticsPrintf(&encoder->diag, "the gain map image must have colorPrimaries = 2 and transferCharacteristics = 2"); + return AVIF_RESULT_INVALID_ARGUMENT; + } + } + + // ----------------------------------------------------------------------- + // Validate flags + + if (encoder->data->singleImage) { + // The previous call to avifEncoderAddImage() set AVIF_ADD_IMAGE_FLAG_SINGLE. + // avifEncoderAddImage() cannot be called again for this encode. + return AVIF_RESULT_ENCODE_COLOR_FAILED; + } + + if (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE) { + encoder->data->singleImage = AVIF_TRUE; + + if (encoder->extraLayerCount > 0) { + // AVIF_ADD_IMAGE_FLAG_SINGLE may not be set for layered image. + return AVIF_RESULT_INVALID_ARGUMENT; + } + + if (encoder->data->items.count > 0) { + // AVIF_ADD_IMAGE_FLAG_SINGLE may only be set on the first and only image. + return AVIF_RESULT_INVALID_ARGUMENT; + } + } + + // ----------------------------------------------------------------------- + // Choose AV1 or AV2 + + const avifCodecType codecType = avifEncoderGetCodecType(encoder); + switch (codecType) { + case AVIF_CODEC_TYPE_AV1: + encoder->data->imageItemType = "av01"; + encoder->data->configPropName = "av1C"; + break; +#if defined(AVIF_CODEC_AVM) + case AVIF_CODEC_TYPE_AV2: + encoder->data->imageItemType = "av02"; + encoder->data->configPropName = "av2C"; + break; +#endif + default: + return AVIF_RESULT_NO_CODEC_AVAILABLE; + } + + // ----------------------------------------------------------------------- + // Populate encoder data (color) quality, qualityAlpha and qualityGainMap. + encoder->data->quality = avifGetQuality(encoder->quality, encoder->minQuantizer, encoder->maxQuantizer); + + // If alpha quality, and min and max alpha quantizer have their default values, default to the same quality as color. + if (encoder->qualityAlpha == AVIF_QUALITY_DEFAULT && encoder->minQuantizerAlpha == AVIF_QUANTIZER_BEST_QUALITY && + encoder->maxQuantizerAlpha == AVIF_QUANTIZER_WORST_QUALITY) { + encoder->data->qualityAlpha = encoder->data->quality; + } else { + encoder->data->qualityAlpha = avifGetQuality(encoder->qualityAlpha, encoder->minQuantizerAlpha, encoder->maxQuantizerAlpha); + } + if (encoder->qualityGainMap == AVIF_QUALITY_DEFAULT) { + encoder->data->qualityGainMap = encoder->data->quality; // Default to the same quality as color. + } else { + encoder->data->qualityGainMap = + avifGetQuality(encoder->qualityGainMap, AVIF_QUANTIZER_BEST_QUALITY, AVIF_QUANTIZER_WORST_QUALITY); + } + + // ----------------------------------------------------------------------- + // Handle automatic tiling + + encoder->data->tileRowsLog2 = AVIF_CLAMP(encoder->tileRowsLog2, 0, 6); + encoder->data->tileColsLog2 = AVIF_CLAMP(encoder->tileColsLog2, 0, 6); + if (encoder->autoTiling) { + // Use as many tiles as allowed by the minimum tile area requirement and impose a maximum + // of 8 tiles. + const int threads = 8; + avifSetTileConfiguration(threads, firstCell->width, firstCell->height, &encoder->data->tileRowsLog2, &encoder->data->tileColsLog2); + } + + // ----------------------------------------------------------------------- + // All encoder settings are known now. Detect changes. + + avifEncoderChanges encoderChanges; + if (!avifEncoderDetectChanges(encoder, &encoderChanges)) { + return AVIF_RESULT_CANNOT_CHANGE_SETTING; + } + avifEncoderBackupSettings(encoder); + + // ----------------------------------------------------------------------- + + if (durationInTimescales == 0) { + durationInTimescales = 1; + } + + if (encoder->data->items.count == 0) { + // Make a copy of the first image's metadata (sans pixels) for future writing/validation + AVIF_CHECKRES(avifImageCopy(encoder->data->imageMetadata, firstCell, 0)); + + const uint32_t gridWidth = avifGridWidth(gridCols, firstCell, bottomRightCell); + const uint32_t gridHeight = avifGridHeight(gridRows, firstCell, bottomRightCell); + + if (hasGainMap) { + AVIF_CHECKRES( + avifImageCopyAltImageMetadata(encoder->data->altImageMetadata, encoder->data->imageMetadata, gridWidth, gridHeight)); + } + + // Prepare all AV1 items + uint16_t colorItemID; + AVIF_CHECKRES(avifEncoderAddImageItems(encoder, gridCols, gridRows, gridWidth, gridHeight, AVIF_ITEM_COLOR, &colorItemID)); + encoder->data->primaryItemID = colorItemID; + + encoder->data->alphaPresent = (firstCell->alphaPlane != NULL); + if (encoder->data->alphaPresent && (addImageFlags & AVIF_ADD_IMAGE_FLAG_SINGLE)) { + // If encoding a single image in which the alpha plane exists but is entirely opaque, + // simply skip writing an alpha AV1 payload entirely, as it'll be interpreted as opaque + // and is less bytes. + // + // However, if encoding an image sequence, the first frame's alpha plane being entirely + // opaque could be a false positive for removing the alpha AV1 payload, as it might simply + // be a fade out later in the sequence. This is why avifImageIsOpaque() is only called + // when encoding a single image. + + encoder->data->alphaPresent = AVIF_FALSE; + for (uint32_t cellIndex = 0; cellIndex < cellCount; ++cellIndex) { + const avifImage * cellImage = cellImages[cellIndex]; + if (!avifImageIsOpaque(cellImage)) { + encoder->data->alphaPresent = AVIF_TRUE; + break; + } + } + } + + if (encoder->data->alphaPresent) { + uint16_t alphaItemID; + AVIF_CHECKRES(avifEncoderAddImageItems(encoder, gridCols, gridRows, gridWidth, gridHeight, AVIF_ITEM_ALPHA, &alphaItemID)); + avifEncoderItem * alphaItem = avifEncoderDataFindItemByID(encoder->data, alphaItemID); + AVIF_ASSERT_OR_RETURN(alphaItem); + alphaItem->irefType = "auxl"; + alphaItem->irefToID = colorItemID; + if (encoder->data->imageMetadata->alphaPremultiplied) { + avifEncoderItem * colorItem = avifEncoderDataFindItemByID(encoder->data, colorItemID); + AVIF_ASSERT_OR_RETURN(colorItem); + colorItem->irefType = "prem"; + colorItem->irefToID = alphaItemID; + } + } + + if (firstCell->gainMap && firstCell->gainMap->image) { + avifEncoderItem * toneMappedItem = avifEncoderDataCreateItem(encoder->data, + "tmap", + infeNameGainMap, + /*infeNameSize=*/strlen(infeNameGainMap) + 1, + /*cellIndex=*/0); + AVIF_CHECKRES(avifWriteToneMappedImagePayload(&toneMappedItem->metadataPayload, firstCell->gainMap, &encoder->diag)); + // Even though the 'tmap' item is related to the gain map, it represents a color image and its metadata is more similar to the color item. + toneMappedItem->itemCategory = AVIF_ITEM_COLOR; + uint16_t toneMappedItemID = toneMappedItem->id; + + AVIF_ASSERT_OR_RETURN(encoder->data->alternativeItemIDs.count == 0); + uint16_t * alternativeItemID = (uint16_t *)avifArrayPush(&encoder->data->alternativeItemIDs); + AVIF_CHECKERR(alternativeItemID != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *alternativeItemID = toneMappedItemID; + + alternativeItemID = (uint16_t *)avifArrayPush(&encoder->data->alternativeItemIDs); + AVIF_CHECKERR(alternativeItemID != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *alternativeItemID = colorItemID; + + const uint32_t gainMapGridWidth = + avifGridWidth(gridCols, cellImages[0]->gainMap->image, cellImages[gridCols * gridRows - 1]->gainMap->image); + const uint32_t gainMapGridHeight = + avifGridHeight(gridRows, cellImages[0]->gainMap->image, cellImages[gridCols * gridRows - 1]->gainMap->image); + + uint16_t gainMapItemID; + AVIF_CHECKRES( + avifEncoderAddImageItems(encoder, gridCols, gridRows, gainMapGridWidth, gainMapGridHeight, AVIF_ITEM_GAIN_MAP, &gainMapItemID)); + avifEncoderItem * gainMapItem = avifEncoderDataFindItemByID(encoder->data, gainMapItemID); + AVIF_ASSERT_OR_RETURN(gainMapItem); + gainMapItem->hiddenImage = AVIF_TRUE; + + // Set the color item and gain map item's dimgFromID value to point to the tone mapped item. + // The color item shall be first, and the gain map second. avifEncoderFinish() writes the + // dimg item references in item id order, so as long as colorItemID < gainMapItemID, the order + // will be correct. + AVIF_ASSERT_OR_RETURN(colorItemID < gainMapItemID); + avifEncoderItem * colorItem = avifEncoderDataFindItemByID(encoder->data, colorItemID); + AVIF_ASSERT_OR_RETURN(colorItem); + AVIF_ASSERT_OR_RETURN(colorItem->dimgFromID == 0); // Our internal API only allows one dimg value per item. + colorItem->dimgFromID = toneMappedItemID; + gainMapItem->dimgFromID = toneMappedItemID; + } + + if (encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B || + encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B || + encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_8B_OVERLAP_4B) { + // For now, only 16-bit depth is supported. + AVIF_CHECKERR(firstCell->depth == 16, AVIF_RESULT_NOT_IMPLEMENTED); + // For now, gain maps are not supported in the same file as Sample Transforms ('altr' group conflict). + AVIF_CHECKERR(!firstCell->gainMap, AVIF_RESULT_NOT_IMPLEMENTED); + AVIF_CHECKRES(avifEncoderCreateBitDepthExtensionItems(encoder, gridCols, gridRows, gridWidth, gridHeight, colorItemID)); + } else { + AVIF_CHECKERR(encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_NONE, AVIF_RESULT_NOT_IMPLEMENTED); + } + + // ----------------------------------------------------------------------- + // Create metadata items (Exif, XMP) + + if (firstCell->exif.size > 0) { + const avifResult result = avifEncoderDataCreateExifItem(encoder->data, &firstCell->exif); + if (result != AVIF_RESULT_OK) { + return result; + } + } + + if (firstCell->xmp.size > 0) { + const avifResult result = avifEncoderDataCreateXMPItem(encoder->data, &firstCell->xmp); + if (result != AVIF_RESULT_OK) { + return result; + } + } + } else { + // Another frame in an image sequence, or layer in a layered image + + if (hasGainMap) { + avifDiagnosticsPrintf(&encoder->diag, "gain maps are not supported for image sequences or layered images"); + return AVIF_RESULT_NOT_IMPLEMENTED; + } + + const avifImage * imageMetadata = encoder->data->imageMetadata; + // Image metadata that are copied to the configuration property and nclx boxes are not allowed to change. + // If the first image in the sequence had an alpha plane (even if fully opaque), all + // subsequent images must have alpha as well. + if ((imageMetadata->depth != firstCell->depth) || (imageMetadata->yuvFormat != firstCell->yuvFormat) || + (imageMetadata->yuvRange != firstCell->yuvRange) || + (imageMetadata->yuvChromaSamplePosition != firstCell->yuvChromaSamplePosition) || + (imageMetadata->colorPrimaries != firstCell->colorPrimaries) || + (imageMetadata->transferCharacteristics != firstCell->transferCharacteristics) || + (imageMetadata->matrixCoefficients != firstCell->matrixCoefficients) || + (imageMetadata->alphaPremultiplied != firstCell->alphaPremultiplied) || + (encoder->data->alphaPresent && !firstCell->alphaPlane)) { + return AVIF_RESULT_INCOMPATIBLE_IMAGE; + } + } + + if (encoder->data->frames.count == 1) { + // We will be writing an image sequence. When writing the AV1SampleEntry (derived from + // VisualSampleEntry) in the stsd box, we need to cast imageMetadata->width and + // imageMetadata->height to uint16_t: + // class VisualSampleEntry(codingname) extends SampleEntry (codingname){ + // ... + // unsigned int(16) width; + // unsigned int(16) height; + // ... + // } + // Check whether it is safe to cast width and height to uint16_t. The maximum width and + // height of an AV1 frame are 65536, which just exceeds uint16_t. + AVIF_ASSERT_OR_RETURN(encoder->data->items.count > 0); + const avifImage * imageMetadata = encoder->data->imageMetadata; + AVIF_CHECKERR(imageMetadata->width <= 65535 && imageMetadata->height <= 65535, AVIF_RESULT_INVALID_ARGUMENT); + } + + // ----------------------------------------------------------------------- + // Encode AV1 OBUs + + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if (item->codec) { + const avifImage * cellImage = cellImages[item->cellIndex]; + avifImage * cellImagePlaceholder = NULL; // May be used as a temporary, modified cellImage. Left as NULL otherwise. + const avifImage * firstCellImage = firstCell; + + if (item->itemCategory == AVIF_ITEM_GAIN_MAP) { + AVIF_ASSERT_OR_RETURN(cellImage->gainMap && cellImage->gainMap->image); + cellImage = cellImage->gainMap->image; + AVIF_ASSERT_OR_RETURN(firstCell->gainMap && firstCell->gainMap->image); + firstCellImage = firstCell->gainMap->image; + } + + if ((cellImage->width != firstCellImage->width) || (cellImage->height != firstCellImage->height)) { + // Pad the right-most and/or bottom-most tiles so that all tiles share the same dimensions. + cellImagePlaceholder = avifImageCreateEmpty(); + AVIF_CHECKERR(cellImagePlaceholder, AVIF_RESULT_OUT_OF_MEMORY); + const avifResult result = + avifImageCopyAndPad(cellImagePlaceholder, cellImage, firstCellImage->width, firstCellImage->height); + if (result != AVIF_RESULT_OK) { + avifImageDestroy(cellImagePlaceholder); + return result; + } + cellImage = cellImagePlaceholder; + } + + const avifBool isAlpha = avifIsAlpha(item->itemCategory); + int quality = isAlpha ? encoder->data->qualityAlpha + : (item->itemCategory == AVIF_ITEM_GAIN_MAP) ? encoder->data->qualityGainMap + : encoder->data->quality; + + // Remember original quantizer values in case they change, to reset them afterwards. + int * encoderMinQuantizer = isAlpha ? &encoder->minQuantizerAlpha : &encoder->minQuantizer; + int * encoderMaxQuantizer = isAlpha ? &encoder->maxQuantizerAlpha : &encoder->maxQuantizer; + const int originalMinQuantizer = *encoderMinQuantizer; + const int originalMaxQuantizer = *encoderMaxQuantizer; + + if (encoder->sampleTransformRecipe != AVIF_SAMPLE_TRANSFORM_NONE) { + if ((encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B || + encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B) && + (item->itemCategory == AVIF_ITEM_COLOR || item->itemCategory == AVIF_ITEM_ALPHA)) { + // Encoding the least significant bits of a sample does not make any sense if the + // other bits are lossily compressed. Encode the most significant bits losslessly. + quality = AVIF_QUALITY_LOSSLESS; + *encoderMinQuantizer = AVIF_QUANTIZER_LOSSLESS; + *encoderMaxQuantizer = AVIF_QUANTIZER_LOSSLESS; + if (!avifEncoderDetectChanges(encoder, &encoderChanges)) { + assert(AVIF_FALSE); + } + } + + // Replace cellImage by the first or second input to the AVIF_ITEM_SAMPLE_TRANSFORM derived image item. + const avifBool itemWillBeEncodedLosslessly = (quality == AVIF_QUALITY_LOSSLESS); + avifImage * sampleTransformedImage = NULL; + if (cellImagePlaceholder) { + avifImageDestroy(cellImagePlaceholder); // Replaced by sampleTransformedImage. + cellImagePlaceholder = NULL; + } + AVIF_CHECKRES( + avifEncoderCreateBitDepthExtensionImage(encoder, item, itemWillBeEncodedLosslessly, cellImage, &sampleTransformedImage)); + assert(cellImagePlaceholder == NULL); + cellImagePlaceholder = sampleTransformedImage; // Transfer ownership. + cellImage = cellImagePlaceholder; + } + + // If alpha channel is present, set disableLaggedOutput to AVIF_TRUE. If the encoder supports it, this enables + // avifEncoderDataShouldForceKeyframeForAlpha to force a keyframe in the alpha channel whenever a keyframe has been + // encoded in the color channel for animated images. + avifResult encodeResult = item->codec->encodeImage(item->codec, + encoder, + cellImage, + isAlpha, + encoder->data->tileRowsLog2, + encoder->data->tileColsLog2, + quality, + encoderChanges, + /*disableLaggedOutput=*/encoder->data->alphaPresent, + addImageFlags, + item->encodeOutput); + // Revert quality settings if they changed. + if (*encoderMinQuantizer != originalMinQuantizer || *encoderMaxQuantizer != originalMaxQuantizer) { + avifEncoderBackupSettings(encoder); // Remember last encoding settings for next avifEncoderDetectChanges(). + *encoderMinQuantizer = originalMinQuantizer; + *encoderMaxQuantizer = originalMaxQuantizer; + } + if (cellImagePlaceholder) { + avifImageDestroy(cellImagePlaceholder); + } + if (encodeResult == AVIF_RESULT_UNKNOWN_ERROR) { + encodeResult = avifGetErrorForItemCategory(item->itemCategory); + } + AVIF_CHECKRES(encodeResult); + if (itemIndex == 0 && avifEncoderDataShouldForceKeyframeForAlpha(encoder->data, item, addImageFlags)) { + addImageFlags |= AVIF_ADD_IMAGE_FLAG_FORCE_KEYFRAME; + } + } + } + + avifEncoderFrame * frame = (avifEncoderFrame *)avifArrayPush(&encoder->data->frames); + AVIF_CHECKERR(frame != NULL, AVIF_RESULT_OUT_OF_MEMORY); + frame->durationInTimescales = durationInTimescales; + avifCodecSpecificOptionsClear(encoder->csOptions); + return AVIF_RESULT_OK; +} + +avifResult avifEncoderAddImage(avifEncoder * encoder, const avifImage * image, uint64_t durationInTimescales, avifAddImageFlags addImageFlags) +{ + avifDiagnosticsClearError(&encoder->diag); + return avifEncoderAddImageInternal(encoder, 1, 1, &image, durationInTimescales, addImageFlags); +} + +avifResult avifEncoderAddImageGrid(avifEncoder * encoder, + uint32_t gridCols, + uint32_t gridRows, + const avifImage * const * cellImages, + avifAddImageFlags addImageFlags) +{ + avifDiagnosticsClearError(&encoder->diag); + if ((gridCols == 0) || (gridCols > 256) || (gridRows == 0) || (gridRows > 256)) { + return AVIF_RESULT_INVALID_IMAGE_GRID; + } + if (encoder->extraLayerCount == 0) { + addImageFlags |= AVIF_ADD_IMAGE_FLAG_SINGLE; // image grids cannot be image sequences + } + return avifEncoderAddImageInternal(encoder, gridCols, gridRows, cellImages, 1, addImageFlags); +} + +static size_t avifEncoderFindExistingChunk(avifRWStream * s, size_t mdatStartOffset, const uint8_t * data, size_t size) +{ + const size_t mdatCurrentOffset = avifRWStreamOffset(s); + const size_t mdatSearchSize = mdatCurrentOffset - mdatStartOffset; + if (mdatSearchSize < size) { + return 0; + } + const size_t mdatEndSearchOffset = mdatCurrentOffset - size; + for (size_t searchOffset = mdatStartOffset; searchOffset <= mdatEndSearchOffset; ++searchOffset) { + if (!memcmp(data, &s->raw->data[searchOffset], size)) { + return searchOffset; + } + } + return 0; +} + +static avifResult avifEncoderWriteMediaDataBox(avifEncoder * encoder, + avifRWStream * s, + avifEncoderItemReferenceArray * layeredColorItems, + avifEncoderItemReferenceArray * layeredAlphaItems) +{ + encoder->ioStats.colorOBUSize = 0; + encoder->ioStats.alphaOBUSize = 0; + encoder->data->gainMapSizeBytes = 0; + + avifBoxMarker mdat; + AVIF_CHECKRES(avifRWStreamWriteBox(s, "mdat", AVIF_BOX_SIZE_TBD, &mdat)); + const size_t mdatStartOffset = avifRWStreamOffset(s); + for (uint32_t itemPasses = 0; itemPasses < 3; ++itemPasses) { + // Use multiple passes to pack in the following order: + // * Pass 0: metadata (Exif/XMP/gain map metadata) + // * Pass 1: alpha, gain map image (AV1) + // * Pass 2: all other item data (AV1 color) + // + // See here for the discussion on alpha coming before color: + // https://github.com/AOMediaCodec/libavif/issues/287 + // + // Exif and XMP are packed first as they're required to be fully available + // by avifDecoderParse() before it returns AVIF_RESULT_OK, unless ignoreXMP + // and ignoreExif are enabled. + // + const avifBool metadataPass = (itemPasses == 0); + const avifBool alphaAndGainMapPass = (itemPasses == 1); + + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if ((item->metadataPayload.size == 0) && (item->encodeOutput->samples.count == 0)) { + // this item has nothing for the mdat box + continue; + } + const avifBool isMetadata = !memcmp(item->type, "mime", 4) || !memcmp(item->type, "Exif", 4) || + !memcmp(item->type, "tmap", 4); + if (metadataPass != isMetadata) { + // only process metadata (XMP/Exif) payloads when metadataPass is true + continue; + } + const avifBool isAlpha = avifIsAlpha(item->itemCategory); + const avifBool isAlphaOrGainMap = isAlpha || item->itemCategory == AVIF_ITEM_GAIN_MAP; + if (alphaAndGainMapPass != isAlphaOrGainMap) { + // only process alpha payloads when alphaPass is true + continue; + } + + if ((encoder->extraLayerCount > 0) && (item->encodeOutput->samples.count > 0)) { + // Interleave - Pick out AV1 items and interleave them later. + // We always interleave all AV1 items for layered images. + AVIF_ASSERT_OR_RETURN(item->encodeOutput->samples.count == item->mdatFixups.count); + + avifEncoderItemReference * ref = + (avifEncoderItemReference *)avifArrayPush(isAlpha ? layeredAlphaItems : layeredColorItems); + AVIF_CHECKERR(ref != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *ref = item; + continue; + } + + size_t chunkOffset = 0; + + // Deduplication - See if an identical chunk to this has already been written. + // Doing it when item->encodeOutput->samples.count > 1 would require contiguous memory. + if (item->encodeOutput->samples.count == 1) { + avifEncodeSample * sample = &item->encodeOutput->samples.sample[0]; + chunkOffset = avifEncoderFindExistingChunk(s, mdatStartOffset, sample->data.data, sample->data.size); + } else if (item->encodeOutput->samples.count == 0) { + chunkOffset = avifEncoderFindExistingChunk(s, mdatStartOffset, item->metadataPayload.data, item->metadataPayload.size); + } + + if (!chunkOffset) { + // We've never seen this chunk before; write it out + chunkOffset = avifRWStreamOffset(s); + if (item->encodeOutput->samples.count > 0) { + for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { + avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; + AVIF_CHECKRES(avifRWStreamWrite(s, sample->data.data, sample->data.size)); + + if (isAlpha) { + encoder->ioStats.alphaOBUSize += sample->data.size; + } else if (item->itemCategory == AVIF_ITEM_COLOR) { + encoder->ioStats.colorOBUSize += sample->data.size; + } else if (item->itemCategory == AVIF_ITEM_GAIN_MAP) { + encoder->data->gainMapSizeBytes += sample->data.size; + } + } + } else { + AVIF_CHECKRES(avifRWStreamWrite(s, item->metadataPayload.data, item->metadataPayload.size)); + } + } + + for (uint32_t fixupIndex = 0; fixupIndex < item->mdatFixups.count; ++fixupIndex) { + avifOffsetFixup * fixup = &item->mdatFixups.fixup[fixupIndex]; + size_t prevOffset = avifRWStreamOffset(s); + avifRWStreamSetOffset(s, fixup->offset); + AVIF_CHECKRES(avifRWStreamWriteU32(s, (uint32_t)chunkOffset)); + avifRWStreamSetOffset(s, prevOffset); + } + } + } + + uint32_t layeredItemCount = AVIF_MAX(layeredColorItems->count, layeredAlphaItems->count); + if (layeredItemCount > 0) { + // Interleave samples of all AV1 items. + // We first write the first layer of all items, + // in which we write first layer of each cell, + // in which we write alpha first and then color. + avifBool hasMoreSample; + uint32_t layerIndex = 0; + do { + hasMoreSample = AVIF_FALSE; + for (uint32_t itemIndex = 0; itemIndex < layeredItemCount; ++itemIndex) { + for (int samplePass = 0; samplePass < 2; ++samplePass) { + // Alpha coming before color + avifEncoderItemReferenceArray * currentItems = (samplePass == 0) ? layeredAlphaItems : layeredColorItems; + if (itemIndex >= currentItems->count) { + continue; + } + + // There is no way to select which grid cell should be written first for now. + avifEncoderItem * item = currentItems->ref[itemIndex]; + if (item->encodeOutput->samples.count <= layerIndex) { + // We've already written all samples of this item + continue; + } else if (item->encodeOutput->samples.count > layerIndex + 1) { + hasMoreSample = AVIF_TRUE; + } + avifRWData * data = &item->encodeOutput->samples.sample[layerIndex].data; + size_t chunkOffset = avifEncoderFindExistingChunk(s, mdatStartOffset, data->data, data->size); + if (!chunkOffset) { + // We've never seen this chunk before; write it out + chunkOffset = avifRWStreamOffset(s); + AVIF_CHECKRES(avifRWStreamWrite(s, data->data, data->size)); + if (samplePass == 0) { + encoder->ioStats.alphaOBUSize += data->size; + } else { + encoder->ioStats.colorOBUSize += data->size; + } + } + + size_t prevOffset = avifRWStreamOffset(s); + avifRWStreamSetOffset(s, item->mdatFixups.fixup[layerIndex].offset); + AVIF_CHECKRES(avifRWStreamWriteU32(s, (uint32_t)chunkOffset)); + avifRWStreamSetOffset(s, prevOffset); + } + } + ++layerIndex; + } while (hasMoreSample); + + AVIF_ASSERT_OR_RETURN(layerIndex <= AVIF_MAX_AV1_LAYER_COUNT); + } + AVIF_CHECKRES(avifRWStreamFinishBox(s, mdat)); + return AVIF_RESULT_OK; +} + +static avifResult avifWriteAltrGroup(avifRWStream * s, uint32_t groupID, const avifEncoderItemIdArray * itemIDs) +{ + avifBoxMarker grpl; + AVIF_CHECKRES(avifRWStreamWriteBox(s, "grpl", AVIF_BOX_SIZE_TBD, &grpl)); + + avifBoxMarker altr; + AVIF_CHECKRES(avifRWStreamWriteFullBox(s, "altr", AVIF_BOX_SIZE_TBD, 0, 0, &altr)); + + AVIF_CHECKRES(avifRWStreamWriteU32(s, groupID)); // unsigned int(32) group_id; + AVIF_CHECKRES(avifRWStreamWriteU32(s, (uint32_t)itemIDs->count)); // unsigned int(32) num_entities_in_group; + for (uint32_t i = 0; i < itemIDs->count; ++i) { + AVIF_CHECKRES(avifRWStreamWriteU32(s, (uint32_t)itemIDs->itemID[i])); // unsigned int(32) entity_id; + } + + AVIF_CHECKRES(avifRWStreamFinishBox(s, altr)); + + AVIF_CHECKRES(avifRWStreamFinishBox(s, grpl)); + + return AVIF_RESULT_OK; +} + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) +// Returns true if the image can be encoded with a MinimizedImageBox instead of a full regular MetaBox. +static avifBool avifEncoderIsMiniCompatible(const avifEncoder * encoder) +{ + // The MinimizedImageBox ("mif3" brand) only supports non-layered, still images. + if (encoder->extraLayerCount || (encoder->data->frames.count != 1)) { + return AVIF_FALSE; + } + + if (encoder->sampleTransformRecipe != AVIF_SAMPLE_TRANSFORM_NONE) { + return AVIF_FALSE; + } + + // Check for maximum field values and maximum chunk sizes. + + // width_minus1 and height_minus1 + if (encoder->data->imageMetadata->width > (1 << 15) || encoder->data->imageMetadata->height > (1 << 15)) { + return AVIF_FALSE; + } + // icc_data_size_minus1, exif_data_size_minus1 and xmp_data_size_minus1 + if (encoder->data->imageMetadata->icc.size > (1 << 20) || encoder->data->imageMetadata->exif.size > (1 << 20) || + encoder->data->imageMetadata->xmp.size > (1 << 20)) { + return AVIF_FALSE; + } + // gainmap_width_minus1 and gainmap_height_minus1 + if (encoder->data->imageMetadata->gainMap != NULL && encoder->data->imageMetadata->gainMap->image != NULL && + (encoder->data->imageMetadata->gainMap->image->width > (1 << 15) || + encoder->data->imageMetadata->gainMap->image->height > (1 << 15))) { + return AVIF_FALSE; + } + // tmap_icc_data_size_minus1 + if (encoder->data->altImageMetadata->icc.size > (1 << 20)) { + return AVIF_FALSE; + } + // gainmap_metadata_size + if (encoder->data->imageMetadata->gainMap != NULL && avifGainMapMetadataSize(encoder->data->imageMetadata->gainMap) >= (1 << 20)) { + return AVIF_FALSE; + } + + // 4:4:4, 4:2:2, 4:2:0 and 4:0:0 are supported by a MinimizedImageBox. + // chroma_subsampling + if (encoder->data->imageMetadata->yuvFormat != AVIF_PIXEL_FORMAT_YUV444 && + encoder->data->imageMetadata->yuvFormat != AVIF_PIXEL_FORMAT_YUV422 && + encoder->data->imageMetadata->yuvFormat != AVIF_PIXEL_FORMAT_YUV420 && + encoder->data->imageMetadata->yuvFormat != AVIF_PIXEL_FORMAT_YUV400) { + return AVIF_FALSE; + } + // gainmap_chroma_subsampling + if (encoder->data->imageMetadata->gainMap != NULL && encoder->data->imageMetadata->gainMap->image != NULL && + (encoder->data->imageMetadata->gainMap->image->yuvFormat != AVIF_PIXEL_FORMAT_YUV444 && + encoder->data->imageMetadata->gainMap->image->yuvFormat != AVIF_PIXEL_FORMAT_YUV422 && + encoder->data->imageMetadata->gainMap->image->yuvFormat != AVIF_PIXEL_FORMAT_YUV420 && + encoder->data->imageMetadata->gainMap->image->yuvFormat != AVIF_PIXEL_FORMAT_YUV400)) { + return AVIF_FALSE; + } + + // colour_primaries, transfer_characteristics and matrix_coefficients + if (encoder->data->imageMetadata->colorPrimaries > 255 || encoder->data->imageMetadata->transferCharacteristics > 255 || + encoder->data->imageMetadata->matrixCoefficients > 255) { + return AVIF_FALSE; + } + // gainmap_colour_primaries, gainmap_transfer_characteristics and gainmap_matrix_coefficients + if (encoder->data->imageMetadata->gainMap != NULL && encoder->data->imageMetadata->gainMap->image != NULL && + (encoder->data->imageMetadata->gainMap->image->colorPrimaries > 255 || + encoder->data->imageMetadata->gainMap->image->transferCharacteristics > 255 || + encoder->data->imageMetadata->gainMap->image->matrixCoefficients > 255)) { + return AVIF_FALSE; + } + // tmap_colour_primaries, tmap_transfer_characteristics and tmap_matrix_coefficients + if (encoder->data->altImageMetadata->colorPrimaries > 255 || encoder->data->altImageMetadata->transferCharacteristics > 255 || + encoder->data->altImageMetadata->matrixCoefficients > 255) { + return AVIF_FALSE; + } + + const avifEncoderItem * colorItem = NULL; + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + + // Grids are not supported by a MinimizedImageBox. + if (item->gridCols || item->gridRows) { + return AVIF_FALSE; + } + + if (item->id == encoder->data->primaryItemID) { + assert(!colorItem); + colorItem = item; + // main_item_data_size_minus1 + if (item->encodeOutput->samples.count != 1 || item->encodeOutput->samples.sample[0].data.size > (1 << 28)) { + return AVIF_FALSE; + } + continue; // The primary item can be stored in the MinimizedImageBox. + } + if (item->itemCategory == AVIF_ITEM_ALPHA && item->irefToID == encoder->data->primaryItemID) { + // alpha_item_data_size + if (item->encodeOutput->samples.count != 1 || item->encodeOutput->samples.sample[0].data.size >= (1 << 28)) { + return AVIF_FALSE; + } + continue; // The alpha auxiliary item can be stored in the MinimizedImageBox. + } + if (item->itemCategory == AVIF_ITEM_GAIN_MAP) { + // gainmap_item_data_size + if (item->encodeOutput->samples.count != 1 || item->encodeOutput->samples.sample[0].data.size >= (1 << 28)) { + return AVIF_FALSE; + } + continue; // The gainmap input image item can be stored in the MinimizedImageBox. + } + if (!memcmp(item->type, "tmap", 4)) { + assert(item->itemCategory == AVIF_ITEM_COLOR); // Cannot be differentiated from the primary item by its itemCategory. + continue; // The tone mapping derived image item can be represented in the MinimizedImageBox. + } + if (!memcmp(item->type, "mime", 4) && !memcmp(item->infeName, "XMP", item->infeNameSize)) { + assert(item->metadataPayload.size == encoder->data->imageMetadata->xmp.size); + continue; // XMP metadata can be stored in the MinimizedImageBox. + } + if (!memcmp(item->type, "Exif", 4) && !memcmp(item->infeName, "Exif", item->infeNameSize)) { + assert(item->metadataPayload.size == encoder->data->imageMetadata->exif.size + 4); + const uint32_t exif_tiff_header_offset = *(uint32_t *)item->metadataPayload.data; + if (exif_tiff_header_offset != 0) { + return AVIF_FALSE; + } + continue; // Exif metadata can be stored in the MinimizedImageBox if exif_tiff_header_offset is 0. + } + + // Items besides the colorItem, the alphaItem, the gainmap item and Exif/XMP/ICC/HDR + // metadata are not directly supported by the MinimizedImageBox. + return AVIF_FALSE; + } + // A primary item is necessary. + if (!colorItem) { + return AVIF_FALSE; + } + return AVIF_TRUE; +} + +static avifResult avifEncoderWriteMiniBox(avifEncoder * encoder, avifRWStream * s); + +static avifResult avifEncoderWriteFileTypeBoxAndMiniBox(avifEncoder * encoder, avifRWData * output) +{ + avifRWStream s; + avifRWStreamStart(&s, output); + + avifBoxMarker ftyp; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "ftyp", AVIF_BOX_SIZE_TBD, &ftyp)); + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "mif3", 4)); // unsigned int(32) major_brand; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "avif", 4)); // unsigned int(32) minor_version; + // unsigned int(32) compatible_brands[]; + AVIF_CHECKRES(avifRWStreamFinishBox(&s, ftyp)); + + AVIF_CHECKRES(avifEncoderWriteMiniBox(encoder, &s)); + + avifRWStreamFinishWrite(&s); + return AVIF_RESULT_OK; +} + +static avifResult avifEncoderWriteMiniBox(avifEncoder * encoder, avifRWStream * s) +{ + const avifEncoderItem * colorItem = NULL; + const avifEncoderItem * alphaItem = NULL; + const avifEncoderItem * gainmapItem = NULL; + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if (item->id == encoder->data->primaryItemID) { + AVIF_ASSERT_OR_RETURN(!colorItem); + colorItem = item; + } else if (item->itemCategory == AVIF_ITEM_ALPHA && item->irefToID == encoder->data->primaryItemID) { + AVIF_ASSERT_OR_RETURN(!alphaItem); + alphaItem = item; + } + if (item->itemCategory == AVIF_ITEM_GAIN_MAP) { + AVIF_ASSERT_OR_RETURN(!gainmapItem); + gainmapItem = item; + } + } + + AVIF_ASSERT_OR_RETURN(colorItem); + const avifRWData * colorData = &colorItem->encodeOutput->samples.sample[0].data; + const avifRWData * alphaData = alphaItem ? &alphaItem->encodeOutput->samples.sample[0].data : NULL; + const avifRWData * gainmapData = gainmapItem ? &gainmapItem->encodeOutput->samples.sample[0].data : NULL; + + const avifImage * const image = encoder->data->imageMetadata; + + const avifBool hasAlpha = alphaItem != NULL; + const avifBool alphaIsPremultiplied = encoder->data->imageMetadata->alphaPremultiplied; + const avifBool hasGainmap = gainmapItem != NULL; + const avifBool hasHdr = hasGainmap; // libavif only supports gainmap-based HDR encoding for now. + const avifBool hasIcc = image->icc.size != 0; + const uint32_t chromaSubsampling = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV400 ? 0 + : image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 ? 1 + : image->yuvFormat == AVIF_PIXEL_FORMAT_YUV422 ? 2 + : 3; + + const avifColorPrimaries defaultColorPrimaries = hasIcc ? AVIF_COLOR_PRIMARIES_UNSPECIFIED : AVIF_COLOR_PRIMARIES_BT709; + const avifTransferCharacteristics defaultTransferCharacteristics = hasIcc ? AVIF_TRANSFER_CHARACTERISTICS_UNSPECIFIED + : AVIF_TRANSFER_CHARACTERISTICS_SRGB; + const avifMatrixCoefficients defaultMatrixCoefficients = chromaSubsampling == 0 ? AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED + : AVIF_MATRIX_COEFFICIENTS_BT601; + const avifBool hasExplicitCicp = image->colorPrimaries != defaultColorPrimaries || + image->transferCharacteristics != defaultTransferCharacteristics || + image->matrixCoefficients != defaultMatrixCoefficients; + + const avifBool floatFlag = AVIF_FALSE; + const avifBool fullRange = image->yuvRange == AVIF_RANGE_FULL; + + // In AV1, the chroma_sample_position syntax element is not present for the YUV 4:2:2 format. + // Assume that AV1 uses the same 4:2:2 chroma sample location as HEVC and VVC (colocated). + if (image->yuvFormat != AVIF_PIXEL_FORMAT_YUV420 && image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_UNKNOWN) { + avifDiagnosticsPrintf(&encoder->diag, + "YUV chroma sample position %d is only supported with 4:2:0 YUV format in AV1", + image->yuvChromaSamplePosition); + return AVIF_RESULT_INVALID_ARGUMENT; + } + // For the YUV 4:2:0 format, assume centered sample position unless specified otherwise. + // This is consistent with the behavior in read.c. + const avifBool chromaIsHorizontallyCentered = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 && + image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_VERTICAL && + image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + const avifBool chromaIsVerticallyCentered = image->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 && + image->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + + const uint32_t orientationMinus1 = avifImageIrotImirToExifOrientation(image) - 1; + + uint8_t infeType[4]; + uint8_t codecConfigType[4]; + avifBool hasExplicitCodecTypes; + if (encoder->codecChoice == AVIF_CODEC_CHOICE_AVM) { + memcpy(infeType, "av02", 4); + memcpy(codecConfigType, "av2C", 4); // Same syntax as 'av1C'. + hasExplicitCodecTypes = AVIF_TRUE; + } else { + memcpy(infeType, "av01", 4); + memcpy(codecConfigType, "av1C", 4); + // 'av01' and 'av1C' are implied by 'avif' minor_version field of FileTypeBox. No need to write them. + hasExplicitCodecTypes = AVIF_FALSE; + } + + // _minus1 is encoded for these fields. + AVIF_ASSERT_OR_RETURN(image->width != 0 && image->height != 0); + AVIF_ASSERT_OR_RETURN(colorData->size != 0); + + uint32_t largeDimensionsFlag = image->width - 1 >= (1 << 7) || image->height - 1 >= (1 << 7); + const uint32_t codecConfigSize = 4; // 'av1C' always uses 4 bytes. + uint32_t alphaCodecConfigSize = 0; // 0 if same codec config as main. Equal to codecConfigSize otherwise. + uint32_t gainmapCodecConfigSize = 0; // 0 if same codec config as main. Equal to codecConfigSize otherwise. + uint32_t gainmapMetadataSize = 0; + const uint32_t largeCodecConfigFlag = codecConfigSize >= (1 << 3); + uint32_t largeItemDataFlag = colorData->size - 1 >= (1 << 15) || (alphaData && alphaData->size >= (1 << 15)); + uint32_t largeMetadataFlag = (hasIcc && image->icc.size - 1 >= (1 << 10)) || + (image->exif.size != 0 && image->exif.size - 1 >= (1 << 10)) || + (image->xmp.size != 0 && image->xmp.size - 1 >= (1 << 10)); + + if (hasGainmap) { + AVIF_ASSERT_OR_RETURN(image->gainMap != NULL && image->gainMap->image != NULL); + gainmapMetadataSize = avifGainMapMetadataSize(image->gainMap); + AVIF_ASSERT_OR_RETURN(gainmapData != NULL); + + // _minus1 is encoded for these fields. + AVIF_ASSERT_OR_RETURN(image->gainMap->image->width != 0 && image->gainMap->image->height != 0); + + largeDimensionsFlag |= image->gainMap->image->width - 1 >= (1 << 7) || image->gainMap->image->height - 1 >= (1 << 7); + largeItemDataFlag |= gainmapData->size >= (1 << 15); + largeMetadataFlag |= + (encoder->data->altImageMetadata->icc.size != 0 && encoder->data->altImageMetadata->icc.size - 1 >= (1 << 10)) || + gainmapMetadataSize >= (1 << 10); + // image->gainMap->image->icc is ignored. + } + + avifBoxMarker mini; + AVIF_CHECKRES(avifRWStreamWriteBox(s, "mini", AVIF_BOX_SIZE_TBD, &mini)); + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, 2)); // bit(2) version = 0; + + // flags + AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCodecTypes, 1)); // bit(1) explicit_codec_types_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, floatFlag, 1)); // bit(1) float_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, fullRange, 1)); // bit(1) full_range_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, alphaItem != 0, 1)); // bit(1) alpha_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, hasExplicitCicp, 1)); // bit(1) explicit_cicp_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, hasHdr, 1)); // bit(1) hdr_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, hasIcc, 1)); // bit(1) icc_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->exif.size != 0, 1)); // bit(1) exif_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->xmp.size != 0, 1)); // bit(1) xmp_flag; + + AVIF_CHECKRES(avifRWStreamWriteBits(s, chromaSubsampling, 2)); // bit(2) chroma_subsampling; + AVIF_CHECKRES(avifRWStreamWriteBits(s, orientationMinus1, 3)); // bit(3) orientation_minus1; + + // Spatial extents + AVIF_CHECKRES(avifRWStreamWriteBits(s, largeDimensionsFlag, 1)); // bit(1) large_dimensions_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->width - 1, largeDimensionsFlag ? 15 : 7)); // unsigned int(large_dimensions_flag ? 15 : 7) width_minus1; + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->height - 1, largeDimensionsFlag ? 15 : 7)); // unsigned int(large_dimensions_flag ? 15 : 7) height_minus1; + + // Pixel information + if (chromaSubsampling == 1 || chromaSubsampling == 2) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, chromaIsHorizontallyCentered, 1)); // bit(1) chroma_is_horizontally_centered; + } + if (chromaSubsampling == 1) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, chromaIsVerticallyCentered, 1)); // bit(1) chroma_is_vertically_centered; + } + + if (floatFlag) { + // bit(2) bit_depth_log2_minus4; + AVIF_ASSERT_NOT_REACHED_OR_RETURN; + } else { + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->depth > 8, 1)); // bit(1) high_bit_depth_flag; + if (image->depth > 8) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->depth - 9, 3)); // bit(3) bit_depth_minus9; + } + } + + if (alphaItem) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, alphaIsPremultiplied, 1)); // bit(1) alpha_is_premultiplied; + } + + // Colour properties + if (hasExplicitCicp) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->colorPrimaries, 8)); // bit(8) colour_primaries; + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->transferCharacteristics, 8)); // bit(8) transfer_characteristics; + if (chromaSubsampling != 0) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, image->matrixCoefficients, 8)); // bit(8) matrix_coefficients; + } else { + AVIF_CHECKERR(image->matrixCoefficients == AVIF_MATRIX_COEFFICIENTS_UNSPECIFIED, AVIF_RESULT_ENCODE_COLOR_FAILED); + } + } + + if (hasExplicitCodecTypes) { + // bit(32) infe_type; + for (int i = 0; i < 4; ++i) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, infeType[i], 8)); + } + // bit(32) codec_config_type; + for (int i = 0; i < 4; ++i) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, codecConfigType[i], 8)); + } + } + + // High Dynamic Range properties + size_t tmapIccSize = 0; + if (hasHdr) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, hasGainmap, 1)); // bit(1) gainmap_flag; + if (hasGainmap) { + const avifImage * tmap = encoder->data->altImageMetadata; + const avifImage * gainmap = image->gainMap->image; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainmap->width - 1, largeDimensionsFlag ? 15 : 7)); // unsigned int(large_dimensions_flag ? 15 : 7) gainmap_width_minus1; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainmap->height - 1, largeDimensionsFlag ? 15 : 7)); // unsigned int(large_dimensions_flag ? 15 : 7) gainmap_height_minus1; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainmap->matrixCoefficients, 8)); // bit(8) gainmap_matrix_coefficients; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainmap->yuvRange == AVIF_RANGE_FULL, 1)); // bit(1) gainmap_full_range_flag; + const uint32_t gainmapChromaSubsampling = gainmap->yuvFormat == AVIF_PIXEL_FORMAT_YUV400 ? 0 + : gainmap->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 ? 1 + : gainmap->yuvFormat == AVIF_PIXEL_FORMAT_YUV422 ? 2 + : 3; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainmapChromaSubsampling, 2)); // bit(1) gainmap_chroma_subsampling; + if (gainmapChromaSubsampling == 1 || gainmapChromaSubsampling == 2) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, + gainmap->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 && + gainmap->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_VERTICAL && + gainmap->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_COLOCATED, + 1)); // bit(1) gainmap_chroma_is_horizontally_centered; + } + if (gainmapChromaSubsampling == 1) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, + gainmap->yuvFormat == AVIF_PIXEL_FORMAT_YUV420 && + gainmap->yuvChromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_COLOCATED, + 1)); // bit(1) gainmap_chroma_is_vertically_centered; + } + + const avifBool gainmapFloatFlag = AVIF_FALSE; + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainmapFloatFlag, 1)); // bit(1) gainmap_float_flag; + if (gainmapFloatFlag) { + // bit(2) gainmap_bit_depth_log2_minus4; + AVIF_ASSERT_NOT_REACHED_OR_RETURN; + } else { + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainmap->depth > 8, 1)); // bit(1) gainmap_high_bit_depth_flag; + if (gainmap->depth > 8) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainmap->depth - 9, 3)); // bit(3) gainmap_bit_depth_minus9; + } + } + + tmapIccSize = encoder->data->altImageMetadata->icc.size; + AVIF_CHECKRES(avifRWStreamWriteBits(s, tmapIccSize != 0, 1)); // bit(1) tmap_icc_flag; + const avifBool tmapHasExplicitCicp = tmap->colorPrimaries != AVIF_COLOR_PRIMARIES_BT709 || + tmap->transferCharacteristics != AVIF_TRANSFER_CHARACTERISTICS_SRGB || + tmap->matrixCoefficients != AVIF_MATRIX_COEFFICIENTS_BT601 || + tmap->yuvRange != AVIF_RANGE_FULL; + AVIF_CHECKRES(avifRWStreamWriteBits(s, tmapHasExplicitCicp, 1)); // bit(1) tmap_explicit_cicp_flag; + if (tmapHasExplicitCicp) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, tmap->colorPrimaries, 8)); // bit(8) tmap_colour_primaries; + AVIF_CHECKRES(avifRWStreamWriteBits(s, tmap->transferCharacteristics, 8)); // bit(8) tmap_transfer_characteristics; + AVIF_CHECKRES(avifRWStreamWriteBits(s, tmap->matrixCoefficients, 8)); // bit(8) tmap_matrix_coefficients; + AVIF_CHECKRES(avifRWStreamWriteBits(s, tmap->yuvRange == AVIF_RANGE_FULL, 1)); // bit(8) tmap_full_range_flag; + } + // gainmap->icc is ignored. + } + + AVIF_CHECKRES(avifEncoderWriteMiniHDRProperties(s, image)); + if (hasGainmap) { + AVIF_CHECKRES(avifEncoderWriteMiniHDRProperties(s, encoder->data->altImageMetadata)); + } + } + + // Chunk sizes + if (hasIcc || image->exif.size || image->xmp.size || (hasHdr && hasGainmap)) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, largeMetadataFlag, 1)); // bit(1) large_metadata_flag; + } + AVIF_CHECKRES(avifRWStreamWriteBits(s, largeCodecConfigFlag, 1)); // bit(1) large_codec_config_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(s, largeItemDataFlag, 1)); // bit(1) large_item_data_flag; + + if (hasIcc) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)image->icc.size - 1, largeMetadataFlag ? 20 : 10)); // unsigned int(large_metadata_flag ? 20 : 10) icc_data_size_minus1; + } + if (hasHdr && hasGainmap && tmapIccSize != 0) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)tmapIccSize - 1, largeMetadataFlag ? 20 : 10)); // unsigned int(large_metadata_flag ? 20 : 10) tmap_icc_data_size_minus1; + } + + if (hasHdr && hasGainmap) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainmapMetadataSize, largeMetadataFlag ? 20 : 10)); // unsigned int(large_metadata_flag ? 20 : 10) gainmap_metadata_size; + } + if (hasHdr && hasGainmap) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)gainmapData->size, largeItemDataFlag ? 28 : 15)); // unsigned int(large_item_data_flag ? 28 : 15) gainmap_item_data_size; + } + if (hasHdr && hasGainmap && gainmapData->size != 0) { + if (!memcmp(&colorItem->av1C, &gainmapItem->av1C, sizeof(colorItem->av1C))) { + // The gainmap codec config is copied from the main codec config. + // This is signaled by a size of 0. + gainmapCodecConfigSize = 0; + } else { + gainmapCodecConfigSize = codecConfigSize; + } + AVIF_CHECKRES(avifRWStreamWriteBits(s, gainmapCodecConfigSize, largeCodecConfigFlag ? 12 : 3)); // unsigned int(large_codec_config_flag ? 12 : 3) gainmap_item_codec_config_size; + } + + AVIF_CHECKRES(avifRWStreamWriteBits(s, codecConfigSize, largeCodecConfigFlag ? 12 : 3)); // unsigned int(large_codec_config_flag ? 12 : 3) main_item_codec_config_size; + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)colorData->size - 1, largeItemDataFlag ? 28 : 15)); // unsigned int(large_item_data_flag ? 28 : 15) main_item_data_size_minus1; + + if (hasAlpha) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)alphaData->size, largeItemDataFlag ? 28 : 15)); // unsigned int(large_item_data_flag ? 28 : 15) alpha_item_data_size; + } + if (hasAlpha && alphaData->size != 0) { + if (!memcmp(&colorItem->av1C, &alphaItem->av1C, sizeof(colorItem->av1C))) { + // The alpha codec config is copied from the main codec config. + // This is signaled by a size of 0. + alphaCodecConfigSize = 0; + } else { + alphaCodecConfigSize = codecConfigSize; + } + AVIF_CHECKRES(avifRWStreamWriteBits(s, alphaCodecConfigSize, largeCodecConfigFlag ? 12 : 3)); // unsigned int(large_codec_config_flag ? 12 : 3) alpha_item_codec_config_size; + } + + if (image->exif.size || image->xmp.size) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, 1)); // unsigned int(1) exif_xmp_compressed_flag + } + if (image->exif.size) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)image->exif.size - 1, largeMetadataFlag ? 20 : 10)); // unsigned int(large_metadata_flag ? 20 : 10) exif_data_size_minus_one; + } + if (image->xmp.size) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, (uint32_t)image->xmp.size - 1, largeMetadataFlag ? 20 : 10)); // unsigned int(large_metadata_flag ? 20 : 10) xmp_data_size_minus_one; + } + + // trailing_bits(); // bit padding till byte alignment + if (s->numUsedBitsInPartialByte != 0) { + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, 8 - s->numUsedBitsInPartialByte)); + } + const size_t headerBytes = avifRWStreamOffset(s); + + // Chunks + if (codecConfigSize > 0) { + AVIF_CHECKRES(writeCodecConfig(s, &colorItem->av1C)); // unsigned int(8) main_item_codec_config[main_item_codec_config_size]; + } + if (hasAlpha && alphaData->size != 0 && alphaCodecConfigSize != 0) { + AVIF_CHECKRES(writeCodecConfig(s, &alphaItem->av1C)); // unsigned int(8) alpha_item_codec_config[alpha_item_codec_config_size]; + } + if (hasHdr && hasGainmap && gainmapCodecConfigSize != 0) { + AVIF_CHECKRES(writeCodecConfig(s, &gainmapItem->av1C)); // unsigned int(8) gainmap_item_codec_config[gainmap_item_codec_config_size]; + } + + if (hasIcc) { + AVIF_CHECKRES(avifRWStreamWrite(s, image->icc.data, image->icc.size)); // unsigned int(8) icc_data[icc_data_size_minus1 + 1]; + } + if (hasHdr && hasGainmap && tmapIccSize != 0) { + AVIF_CHECKRES(avifRWStreamWrite(s, encoder->data->altImageMetadata->icc.data, tmapIccSize)); // unsigned int(8) tmap_icc_data[tmap_icc_data_size_minus1 + 1]; + } + if (hasHdr && hasGainmap && gainmapMetadataSize != 0) { + AVIF_CHECKRES(avifWriteGainmapMetadata(s, image->gainMap, &encoder->diag)); // unsigned int(8) gainmap_metadata[gainmap_metadata_size]; + } + + if (hasAlpha && alphaData->size != 0) { + AVIF_CHECKRES(avifRWStreamWrite(s, alphaData->data, alphaData->size)); // unsigned int(8) alpha_item_data[alpha_item_data_size]; + } + if (hasHdr && hasGainmap && gainmapData->size != 0) { + AVIF_CHECKRES(avifRWStreamWrite(s, gainmapData->data, gainmapData->size)); // unsigned int(8) gainmap_item_data[gainmap_item_data_size]; + } + + AVIF_CHECKRES(avifRWStreamWrite(s, colorData->data, colorData->size)); // unsigned int(8) main_item_data[main_item_data_size_minus1 + 1]; + + if (image->exif.size) { + AVIF_CHECKRES(avifRWStreamWrite(s, image->exif.data, image->exif.size)); // unsigned int(8) exif_data[exif_data_size_minus1 + 1]; + } + if (image->xmp.size) { + AVIF_CHECKRES(avifRWStreamWrite(s, image->xmp.data, image->xmp.size)); // unsigned int(8) xmp_data[xmp_data_size_minus1 + 1]; + } + + const size_t expectedChunkBytes = codecConfigSize + alphaCodecConfigSize + gainmapCodecConfigSize + image->icc.size + + tmapIccSize + gainmapMetadataSize + (hasAlpha ? alphaData->size : 0) + + (hasGainmap ? gainmapData->size : 0) + colorData->size + image->exif.size + image->xmp.size; + AVIF_ASSERT_OR_RETURN(avifRWStreamOffset(s) == headerBytes + expectedChunkBytes); + AVIF_CHECKRES(avifRWStreamFinishBox(s, mini)); + return AVIF_RESULT_OK; +} +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + +static avifResult avifRWStreamWriteProperties(avifItemPropertyDedup * const dedup, + avifRWStream * const s, + const avifEncoder * const encoder, + const avifImage * const imageMetadata, + const avifImage * const altImageMetadata) +{ + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + const avifBool isGrid = (item->gridCols > 0); + // Whether there is ipma to write for this item. + avifBool hasIpmaToWrite = item->codec || isGrid; + const avifBool isToneMappedImage = !memcmp(item->type, "tmap", 4); + if (isToneMappedImage) { + hasIpmaToWrite = AVIF_TRUE; + } + const avifBool isSampleTransformImage = !memcmp(item->type, "sato", 4); + if (isSampleTransformImage) { + hasIpmaToWrite = AVIF_TRUE; + } + item->associations.count = 0; + if (!hasIpmaToWrite) { + continue; + } + + if (item->dimgFromID && (item->extraLayerCount == 0)) { + avifEncoderItem * parentItem = avifEncoderDataFindItemByID(encoder->data, item->dimgFromID); + if (parentItem && !memcmp(parentItem->type, "grid", 4)) { + // All image cells from a grid should share the exact same properties unless they are + // layered image which have different al1x, so see if we've already written properties + // out for another cell in this grid, and if so, just steal their ipma and move on. + // This is a sneaky way to provide iprp deduplication. + + avifBool foundPreviousCell = AVIF_FALSE; + for (uint32_t dedupIndex = 0; dedupIndex < itemIndex; ++dedupIndex) { + avifEncoderItem * dedupItem = &encoder->data->items.item[dedupIndex]; + if ((item->dimgFromID == dedupItem->dimgFromID) && (dedupItem->extraLayerCount == 0)) { + // We've already written dedup's items out. Steal their ipma indices and move on! + item->associations.count = 0; + for (uint32_t associationIndex = 0; associationIndex < dedupItem->associations.count; ++associationIndex) { + avifItemPropertyAssociation * association = (avifItemPropertyAssociation *)avifArrayPush(&item->associations); + AVIF_CHECKERR(association != NULL, AVIF_RESULT_OUT_OF_MEMORY); + *association = dedupItem->associations.association[associationIndex]; + } + foundPreviousCell = AVIF_TRUE; + break; + } + } + if (foundPreviousCell) { + continue; + } + } + } + + const avifImage * itemMetadata = imageMetadata; + if (isToneMappedImage) { + itemMetadata = altImageMetadata; + } else if (item->itemCategory == AVIF_ITEM_GAIN_MAP) { + AVIF_ASSERT_OR_RETURN(itemMetadata->gainMap && itemMetadata->gainMap->image); + itemMetadata = itemMetadata->gainMap->image; + } + uint32_t imageWidth = itemMetadata->width; + uint32_t imageHeight = itemMetadata->height; + if (isGrid) { + imageWidth = item->gridWidth; + imageHeight = item->gridHeight; + } + + // ISO/IEC 23008-12:2024 section 6.5.1: + // Writers should arrange the descriptive properties specified in 6.5 prior to any other properties + // in the sequence associating properties with an item. + // For each item, write the descriptive properties first (and thus associate them before the + // transformative properties). + + // Properties all image items need (coded and derived) + // ispe = image spatial extent (width, height) + avifItemPropertyDedupStart(dedup); + avifBoxMarker ispe; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&dedup->s, "ispe", AVIF_BOX_SIZE_TBD, 0, 0, &ispe)); + AVIF_CHECKRES(avifRWStreamWriteU32(&dedup->s, imageWidth)); // unsigned int(32) image_width; + AVIF_CHECKRES(avifRWStreamWriteU32(&dedup->s, imageHeight)); // unsigned int(32) image_height; + AVIF_CHECKRES(avifRWStreamFinishBox(&dedup->s, ispe)); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, s, &item->associations, /*essential=*/AVIF_FALSE)); + + // pixi = pixel information (depth, channel count) + avifBool hasPixi = AVIF_TRUE; + // Pixi is optional for the 'tmap' item. + if (isToneMappedImage && imageMetadata->gainMap->altDepth == 0 && imageMetadata->gainMap->altPlaneCount == 0) { + hasPixi = AVIF_FALSE; + } + const avifBool isAlpha = avifIsAlpha(item->itemCategory); + uint8_t depth = (uint8_t)itemMetadata->depth; + if (encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B || + encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B || + encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_8B_OVERLAP_4B) { + if (item->itemCategory == AVIF_ITEM_SAMPLE_TRANSFORM) { + AVIF_ASSERT_OR_RETURN(depth == 16); // Only 16-bit depth is supported for now. + } else if (encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_8B_8B) { + depth = 8; + } else { + if (item->itemCategory == AVIF_ITEM_COLOR || item->itemCategory == AVIF_ITEM_ALPHA) { + depth = 12; + } else { + AVIF_ASSERT_OR_RETURN(item->itemCategory == AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_COLOR || + item->itemCategory == AVIF_ITEM_SAMPLE_TRANSFORM_INPUT_0_ALPHA); + // Will be shifted to 4-bit samples at decoding for AVIF_SAMPLE_TRANSFORM_BIT_DEPTH_EXTENSION_12B_4B. + depth = 8; + } + } + } else { + AVIF_CHECKERR(encoder->sampleTransformRecipe == AVIF_SAMPLE_TRANSFORM_NONE, AVIF_RESULT_NOT_IMPLEMENTED); + } + assert(isSampleTransformImage == (item->itemCategory == AVIF_ITEM_SAMPLE_TRANSFORM)); + + if (hasPixi) { + avifItemPropertyDedupStart(dedup); + uint8_t channelCount = (isAlpha || (itemMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV400)) ? 1 : 3; + // See ISO/IEC 23008-12:2024/CDAM 2:2025 section 6.5.6.3. + avifBoxMarker pixi; + uint32_t flags = 0; +#if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI) + if (encoder->headerFormat & AVIF_HEADER_EXTENDED_PIXI) { + flags |= 1; + } +#endif // AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI + AVIF_CHECKRES(avifRWStreamWriteFullBox(&dedup->s, "pixi", AVIF_BOX_SIZE_TBD, 0, flags, &pixi)); + AVIF_CHECKRES(avifRWStreamWriteU8(&dedup->s, channelCount)); // unsigned int (8) num_channels; + for (uint8_t chan = 0; chan < channelCount; ++chan) { + AVIF_CHECKRES(avifRWStreamWriteU8(&dedup->s, depth)); // unsigned int (8) bits_per_channel; + } +#if defined(AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI) + if (flags & 1) { + AVIF_ASSERT_OR_RETURN(item->av1C.chromaSamplePosition != AVIF_CHROMA_SAMPLE_POSITION_RESERVED); + // Do not signal any subsampling information if the sample position is unknown because the 'pixi' box + // does not have an enum entry for "unknown subsampling location". + const uint8_t subsampling_flag = item->av1C.chromaSamplePosition == AVIF_CHROMA_SAMPLE_POSITION_VERTICAL || + item->av1C.chromaSamplePosition == AVIF_CHROMA_SAMPLE_POSITION_COLOCATED; + for (uint8_t chan = 0; chan < channelCount; ++chan) { + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, 0, /*bitCount=*/3)); // unsigned int(3) channel_idc; + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, 0, /*bitCount=*/1)); // unsigned int(1) reserved; + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, 0, /*bitCount=*/2)); // unsigned int(2) component_format; + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, subsampling_flag, /*bitCount=*/1)); // unsigned int(1) subsampling_flag; + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, 0, /*bitCount=*/1)); // unsigned int(1) channel_label_flag; + if (subsampling_flag) { + const uint8_t subsamplingType = avifCodecConfigurationBoxGetSubsamplingType(&item->av1C, chan); + const uint8_t subsamplingLocation = subsamplingType == AVIF_PIXI_444 ? 0 + : item->av1C.chromaSamplePosition == AVIF_CHROMA_SAMPLE_POSITION_VERTICAL + ? 0 + : 2; + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, subsamplingType, /*bitCount=*/4)); // unsigned int(4) subsampling_type; + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, subsamplingLocation, /*bitCount=*/4)); // unsigned int(4) subsampling_location; + } + } + } +#endif // AVIF_ENABLE_EXPERIMENTAL_EXTENDED_PIXI + AVIF_CHECKRES(avifRWStreamFinishBox(&dedup->s, pixi)); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, s, &item->associations, /*essential=*/AVIF_FALSE)); + } + + // Codec configuration box ('av1C' or 'av2C') + if (item->codec) { + avifItemPropertyDedupStart(dedup); + AVIF_CHECKRES(writeConfigBox(&dedup->s, &item->av1C, encoder->data->configPropName)); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, s, &item->associations, /*essential=*/AVIF_TRUE)); + } + + if (isAlpha) { + // Alpha specific properties + + avifItemPropertyDedupStart(dedup); + avifBoxMarker auxC; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&dedup->s, "auxC", AVIF_BOX_SIZE_TBD, 0, 0, &auxC)); + AVIF_CHECKRES(avifRWStreamWriteChars(&dedup->s, alphaURN, alphaURNSize)); // string aux_type; + AVIF_CHECKRES(avifRWStreamFinishBox(&dedup->s, auxC)); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, s, &item->associations, /*essential=*/AVIF_FALSE)); + } else if (item->itemCategory == AVIF_ITEM_COLOR) { + // Color specific properties + // Note the 'tmap' (tone mapped image) item when a gain map is present also has itemCategory AVIF_ITEM_COLOR. + + AVIF_CHECKRES(avifEncoderWriteColorProperties(s, itemMetadata, &item->associations, dedup)); + AVIF_CHECKRES(avifEncoderWriteHDRProperties(&dedup->s, s, itemMetadata, &item->associations, dedup)); + } else if (item->itemCategory == AVIF_ITEM_GAIN_MAP) { + // Gain map specific properties + + // Write the colr nclx box. + AVIF_CHECKRES(avifEncoderWriteNclxProperty(&dedup->s, s, itemMetadata, &item->associations, dedup)); + + AVIF_CHECKRES(avifEncoderWritePaspProperty(&dedup->s, s, imageMetadata, &item->associations, dedup)); + } + + if (item->extraLayerCount > 0) { + // Layered Image Indexing Property + + avifItemPropertyDedupStart(dedup); + avifBoxMarker a1lx; + AVIF_CHECKRES(avifRWStreamWriteBox(&dedup->s, "a1lx", AVIF_BOX_SIZE_TBD, &a1lx)); + uint32_t layerSize[AVIF_MAX_AV1_LAYER_COUNT - 1] = { 0 }; + avifBool largeSize = AVIF_FALSE; + + for (uint32_t validLayer = 0; validLayer < item->extraLayerCount; ++validLayer) { + uint32_t size = (uint32_t)item->encodeOutput->samples.sample[validLayer].data.size; + layerSize[validLayer] = size; + if (size > 0xffff) { + largeSize = AVIF_TRUE; + } + } + + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, 0, /*bitCount=*/7)); // unsigned int(7) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(&dedup->s, largeSize ? 1 : 0, /*bitCount=*/1)); // unsigned int(1) large_size; + + // FieldLength = (large_size + 1) * 16; + // unsigned int(FieldLength) layer_size[3]; + for (uint32_t layer = 0; layer < AVIF_MAX_AV1_LAYER_COUNT - 1; ++layer) { + if (largeSize) { + AVIF_CHECKRES(avifRWStreamWriteU32(&dedup->s, layerSize[layer])); + } else { + AVIF_CHECKRES(avifRWStreamWriteU16(&dedup->s, (uint16_t)layerSize[layer])); + } + } + AVIF_CHECKRES(avifRWStreamFinishBox(&dedup->s, a1lx)); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, s, &item->associations, /*essential=*/AVIF_FALSE)); + + // We don't add an 'lsel' property since many decoders do not support it and will reject the image, + // see https://github.com/AOMediaCodec/libavif/pull/2429 + } + + // Write out any opaque properties from avifImageAddOpaqueProperty() or avifImageAddUUIDProperty(). + for (size_t i = 0; i < itemMetadata->numProperties; i++) { + avifItemPropertyDedupStart(dedup); + const avifImageItemProperty * prop = &itemMetadata->properties[i]; + avifBoxMarker propMarker; + AVIF_CHECKRES(avifRWStreamWriteBox(&dedup->s, (const char *)prop->boxtype, AVIF_BOX_SIZE_TBD, &propMarker)); + if (memcmp(prop->boxtype, "uuid", 4) == 0) { + AVIF_CHECKRES(avifRWStreamWrite(&dedup->s, prop->usertype, 16)); + } + AVIF_CHECKRES(avifRWStreamWrite(&dedup->s, prop->boxPayload.data, prop->boxPayload.size)); + AVIF_CHECKRES(avifRWStreamFinishBox(&dedup->s, propMarker)); + AVIF_CHECKRES(avifItemPropertyDedupFinish(dedup, s, &item->associations, /*essential=*/AVIF_FALSE)); + } + + // Also write the transformative properties. + + if (item->itemCategory == AVIF_ITEM_COLOR) { + // Color specific properties + // Note the 'tmap' (tone mapped image) item when a gain map is present also has itemCategory AVIF_ITEM_COLOR. + AVIF_CHECKRES(avifEncoderWriteTransformativeProperties(&dedup->s, s, itemMetadata, &item->associations, dedup)); + } else if (item->itemCategory == AVIF_ITEM_ALPHA) { + // Cropping, rotation and mirroring must also be applied to alpha auxiliary items. + AVIF_CHECKRES(avifEncoderWriteTransformativeProperties(&dedup->s, s, itemMetadata, &item->associations, dedup)); + } else if (item->itemCategory == AVIF_ITEM_GAIN_MAP) { + // Gain map specific properties + + // For the orientation, it could be done in multiple ways: + // - Bake the orientation in the base and gain map images. + // This does not allow for orientation changes without recompression. + // - Associate 'irot'/'imir' with the 'tmap' derived image item only. + // If so, decoding only the base image would give a different orientation than + // decoding the tone-mapped image. + // - Wrap the base image in an 'iden' derived image item and associate 'irot'/'imir' + // with the 'tmap' and 'iden' derived image items. 'iden' is not currently supported + // by libavif, reducing the backward compatibility of this solution. + // - Associate 'irot'/'imir' with the base and gain map image items. + // Do not associate 'irot'/'imir' with the 'tmap' derived image item. + // These transformative properties are supposed to be applied at decoding on + // image items before these are used as input to a derived image item. + // libavif uses this pattern at encoding and requires it at decoding. + // As of today, this is forbidden by the AVIF specification: + // https://aomediacodec.github.io/av1-avif/v1.1.0.html#file-constraints + // That rule was written before 'tmap' was proposed and may be relaxed for 'tmap'. + + // 'clap' is treated as 'irot'/'imir', although it could differ between the base and + // gain map image items if these have different dimensions. + if (imageMetadata->transformFlags & AVIF_TRANSFORM_CLAP) { + AVIF_CHECKERR(imageMetadata->width != itemMetadata->width || imageMetadata->height != itemMetadata->height, + AVIF_RESULT_NOT_IMPLEMENTED); + } + + // 'pasp' is not a transformative property (despite AVIF_TRANSFORM_PASP being part of + // avifTransformFlag) but it is assumed to apply to the gain map in the same way as + // the transformative properties above. + + // Based on the explanation above, 'clap', 'irot', 'imir' and 'pasp' have to match between the base and + // gain map image items in the container part of the encoded file. + // To enforce that, the transformative and 'pasp' properties of the gain map cannot be set explicitly in the API. + AVIF_CHECKERR(itemMetadata->transformFlags == AVIF_TRANSFORM_NONE, AVIF_RESULT_ENCODE_GAIN_MAP_FAILED); + AVIF_CHECKRES(avifEncoderWriteTransformativeProperties(&dedup->s, s, imageMetadata, &item->associations, dedup)); + } + } + return AVIF_RESULT_OK; +} + +avifResult avifEncoderFinish(avifEncoder * encoder, avifRWData * output) +{ + avifDiagnosticsClearError(&encoder->diag); + if (encoder->data->items.count == 0) { + return AVIF_RESULT_NO_CONTENT; + } + + const avifCodecType codecType = avifEncoderGetCodecType(encoder); + if (codecType == AVIF_CODEC_TYPE_UNKNOWN) { + return AVIF_RESULT_NO_CODEC_AVAILABLE; + } + + // ----------------------------------------------------------------------- + // Finish up encoding + + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if (item->codec) { + if (!item->codec->encodeFinish(item->codec, item->encodeOutput)) { + return avifGetErrorForItemCategory(item->itemCategory); + } + + if (item->encodeOutput->samples.count != encoder->data->frames.count) { + return avifGetErrorForItemCategory(item->itemCategory); + } + + if ((item->extraLayerCount > 0) && (item->encodeOutput->samples.count != item->extraLayerCount + 1)) { + // Check whether user has sent enough frames to encoder. + avifDiagnosticsPrintf(&encoder->diag, + "Expected %u frames given to avifEncoderAddImage() to encode this layered image according to extraLayerCount, but got %u frames.", + item->extraLayerCount + 1, + item->encodeOutput->samples.count); + return AVIF_RESULT_INVALID_ARGUMENT; + } + } + } + + // ----------------------------------------------------------------------- + // Harvest configuration properties from sequence headers + + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if (item->encodeOutput->samples.count > 0) { + const avifEncodeSample * firstSample = &item->encodeOutput->samples.sample[0]; + avifSequenceHeader sequenceHeader; + AVIF_CHECKERR(avifSequenceHeaderParse(&sequenceHeader, (const avifROData *)&firstSample->data, codecType), + avifGetErrorForItemCategory(item->itemCategory)); + item->av1C = sequenceHeader.av1C; + } + } + + // ----------------------------------------------------------------------- + // Begin write stream + +#if defined(AVIF_ENABLE_EXPERIMENTAL_MINI) + // Decide whether to go for a reduced MinimizedImageBox or a full regular MetaBox. + if ((encoder->headerFormat & AVIF_HEADER_MINI) && avifEncoderIsMiniCompatible(encoder)) { + AVIF_CHECKRES(avifEncoderWriteFileTypeBoxAndMiniBox(encoder, output)); + return AVIF_RESULT_OK; + } +#endif // AVIF_ENABLE_EXPERIMENTAL_MINI + + const avifImage * imageMetadata = encoder->data->imageMetadata; + uint64_t now = (uint64_t)time(NULL); + uint64_t modificationTime = (encoder->modificationTime != 0) ? encoder->modificationTime : now; + uint64_t creationTime = (encoder->creationTime != 0) ? encoder->creationTime : modificationTime; + // The epoch for creation_time and modification_time is midnight, Jan. 1, + // 1904, in UTC time. Add the number of seconds between that epoch and the + // Unix epoch. + creationTime += 2082844800; + modificationTime += 2082844800; + + avifRWStream s; + avifRWStreamStart(&s, output); + + // ----------------------------------------------------------------------- + // Write ftyp + + // Layered sequence is not supported for now. + const avifBool isSequence = (encoder->extraLayerCount == 0) && (encoder->data->frames.count > 1); + + const char * majorBrand = "avif"; + if (isSequence) { + majorBrand = "avis"; + } + + uint32_t minorVersion = 0; +#if defined(AVIF_CODEC_AVM) + if (codecType == AVIF_CODEC_TYPE_AV2) { + // TODO(yguyon): Experimental AV2-AVIF is AVIF version 2 for now (change once it is ratified). + minorVersion = 2; + } +#endif + + // According to section 5.2 of AV1 Image File Format specification v1.1.0: + // If the primary item or all the items referenced by the primary item are AV1 image items made only + // of Intra Frames, the brand "avio" should be used in the compatible_brands field of the FileTypeBox. + // See https://aomediacodec.github.io/av1-avif/v1.1.0.html#image-and-image-collection-brand. + // This rule corresponds to using the "avio" brand in all cases except for layered images, because: + // - Non-layered still images are always Intra Frames, even with grids; + // - Sequences cannot be combined with layers or grids, and the first frame of the sequence + // (referred to by the primary image item) is always an Intra Frame. + avifBool useAvioBrand; + if (isSequence) { + // According to section 5.3 of AV1 Image File Format specification v1.1.0: + // Additionally, if a file contains AV1 image sequences and the brand avio is used in the + // compatible_brands field of the FileTypeBox, the item constraints for this brand shall be met + // and at least one of the AV1 image sequences shall be made only of AV1 Samples marked as sync. + // See https://aomediacodec.github.io/av1-avif/v1.1.0.html#image-sequence-brand. + useAvioBrand = AVIF_FALSE; + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if (item->encodeOutput->samples.count == 0) { + continue; // Not a track. + } + avifBool onlySyncSamples = AVIF_TRUE; + for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { + if (!item->encodeOutput->samples.sample[sampleIndex].sync) { + onlySyncSamples = AVIF_FALSE; + break; + } + } + if (onlySyncSamples) { + useAvioBrand = AVIF_TRUE; // at least one of the AV1 image sequences is made only of sync samples + break; + } + } + } else { + // The gpac/ComplianceWarden tool only warns about the lack of the "avio" brand for sequences, + // and the specification says the brand "should" be used, not "shall". Leverage that opportunity + // to save four bytes for still images. + useAvioBrand = AVIF_FALSE; // Should be (encoder->extraLayerCount == 0) to be fully compliant. + } + + avifBoxMarker ftyp; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "ftyp", AVIF_BOX_SIZE_TBD, &ftyp)); + AVIF_CHECKRES(avifRWStreamWriteChars(&s, majorBrand, 4)); // unsigned int(32) major_brand; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, minorVersion)); // unsigned int(32) minor_version; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "avif", 4)); // unsigned int(32) compatible_brands[]; + if (useAvioBrand) { + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "avio", 4)); // ... compatible_brands[] + } + if (isSequence) { + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "avis", 4)); // ... compatible_brands[] + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "msf1", 4)); // ... compatible_brands[] + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "iso8", 4)); // ... compatible_brands[] + } + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "mif1", 4)); // ... compatible_brands[] + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "miaf", 4)); // ... compatible_brands[] + if ((imageMetadata->depth == 8) || (imageMetadata->depth == 10)) { // + if (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV420) { // + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "MA1B", 4)); // ... compatible_brands[] + } else if (imageMetadata->yuvFormat == AVIF_PIXEL_FORMAT_YUV444) { // + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "MA1A", 4)); // ... compatible_brands[] + } + } + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + if (!memcmp(encoder->data->items.item[itemIndex].type, "tmap", 4)) { + // ISO/IEC 23008-12:2024/AMD 1:2024(E) + // This brand enables file players to identify and decode HEIF files containing tone-map derived image + // items. When present, this brand shall be among the brands included in the compatible_brands + // array of the FileTypeBox. + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "tmap", 4)); // ... compatible_brands[] + break; + } + } + AVIF_CHECKRES(avifRWStreamFinishBox(&s, ftyp)); + + // ----------------------------------------------------------------------- + // Start meta + + avifBoxMarker meta; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "meta", AVIF_BOX_SIZE_TBD, 0, 0, &meta)); + + // ----------------------------------------------------------------------- + // Write hdlr + + AVIF_CHECKRES(avifRWStreamWriteHandlerBox(&s, "pict")); + + // ----------------------------------------------------------------------- + // Write pitm + + if (encoder->data->primaryItemID != 0) { + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "pitm", sizeof(uint16_t), 0, 0, /*marker=*/NULL)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, encoder->data->primaryItemID)); // unsigned int(16) item_ID; + } + + // ----------------------------------------------------------------------- + // Write iloc + + avifBoxMarker iloc; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "iloc", AVIF_BOX_SIZE_TBD, 0, 0, &iloc)); + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 4, /*bitCount=*/4)); // unsigned int(4) offset_size; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 4, /*bitCount=*/4)); // unsigned int(4) length_size; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/4)); // unsigned int(4) base_offset_size; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/4)); // unsigned int(4) reserved; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count)); // unsigned int(16) item_count; + + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->id)); // unsigned int(16) item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // unsigned int(16) data_reference_index; + + // Layered Image, write location for all samples + if (item->extraLayerCount > 0) { + uint32_t layerCount = item->extraLayerCount + 1; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)layerCount)); // unsigned int(16) extent_count; + for (uint32_t i = 0; i < layerCount; ++i) { + AVIF_CHECKRES(avifEncoderItemAddMdatFixup(item, &s)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0 /* set later */)); // unsigned int(offset_size*8) extent_offset; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)item->encodeOutput->samples.sample[i].data.size)); // unsigned int(length_size*8) extent_length; + } + continue; + } + + uint32_t contentSize = (uint32_t)item->metadataPayload.size; + if (item->encodeOutput->samples.count > 0) { + // This is choosing sample 0's size as there are two cases here: + // * This is a single image, in which case this is correct + // * This is an image sequence, but this file should still be a valid single-image avif, + // so there must still be a primary item pointing at a sync sample. Since the first + // frame of the image sequence is guaranteed to be a sync sample, it is chosen here. + contentSize = (uint32_t)item->encodeOutput->samples.sample[0].data.size; + } + + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 1)); // unsigned int(16) extent_count; + AVIF_CHECKRES(avifEncoderItemAddMdatFixup(item, &s)); // + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0 /* set later */)); // unsigned int(offset_size*8) extent_offset; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)contentSize)); // unsigned int(length_size*8) extent_length; + } + + AVIF_CHECKRES(avifRWStreamFinishBox(&s, iloc)); + + // ----------------------------------------------------------------------- + // Write iinf + + // Section 8.11.6.2 of ISO/IEC 14496-12. + avifBoxMarker iinf; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "iinf", AVIF_BOX_SIZE_TBD, 0, 0, &iinf)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)encoder->data->items.count)); // unsigned int(16) entry_count; + + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + + uint32_t flags = item->hiddenImage ? 1 : 0; + avifBoxMarker infe; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "infe", AVIF_BOX_SIZE_TBD, 2, flags, &infe)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->id)); // unsigned int(16) item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // unsigned int(16) item_protection_index; + AVIF_CHECKRES(avifRWStreamWrite(&s, item->type, 4)); // unsigned int(32) item_type; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, item->infeName, item->infeNameSize)); // utf8string item_name; (writing null terminator) + if (!memcmp(item->type, "mime", 4)) { + AVIF_CHECKRES(avifRWStreamWriteChars(&s, item->infeContentType, item->infeContentTypeSize)); // utf8string content_type; (writing null terminator) + // utf8string content_encoding; //optional + } else if (!memcmp(item->type, "uri ", 4)) { + // utf8string item_uri_type; + return AVIF_RESULT_NOT_IMPLEMENTED; + } + AVIF_CHECKRES(avifRWStreamFinishBox(&s, infe)); + } + + AVIF_CHECKRES(avifRWStreamFinishBox(&s, iinf)); + + // ----------------------------------------------------------------------- + // Write iref boxes + + avifBoxMarker iref = 0; + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + + // Count how many other items refer to this item with dimgFromID + uint16_t dimgCount = 0; + for (uint32_t dimgIndex = 0; dimgIndex < encoder->data->items.count; ++dimgIndex) { + avifEncoderItem * dimgItem = &encoder->data->items.item[dimgIndex]; + if (dimgItem->dimgFromID == item->id) { + ++dimgCount; + } + } + + if (dimgCount > 0) { + if (!iref) { + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0, &iref)); + } + avifBoxMarker refType; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "dimg", AVIF_BOX_SIZE_TBD, &refType)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->id)); // unsigned int(16) from_item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, dimgCount)); // unsigned int(16) reference_count; + for (uint32_t dimgIndex = 0; dimgIndex < encoder->data->items.count; ++dimgIndex) { + avifEncoderItem * dimgItem = &encoder->data->items.item[dimgIndex]; + if (dimgItem->dimgFromID == item->id) { + AVIF_CHECKRES(avifRWStreamWriteU16(&s, dimgItem->id)); // unsigned int(16) to_item_ID; + } + } + AVIF_CHECKRES(avifRWStreamFinishBox(&s, refType)); + } + + if (item->irefToID != 0) { + if (!iref) { + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "iref", AVIF_BOX_SIZE_TBD, 0, 0, &iref)); + } + avifBoxMarker refType; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD, &refType)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->id)); // unsigned int(16) from_item_ID; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 1)); // unsigned int(16) reference_count; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->irefToID)); // unsigned int(16) to_item_ID; + AVIF_CHECKRES(avifRWStreamFinishBox(&s, refType)); + } + } + if (iref) { + AVIF_CHECKRES(avifRWStreamFinishBox(&s, iref)); + } + + // ----------------------------------------------------------------------- + // Write iprp -> ipco/ipma + + avifBoxMarker iprp; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "iprp", AVIF_BOX_SIZE_TBD, &iprp)); + + avifItemPropertyDedup * dedup = avifItemPropertyDedupCreate(); + AVIF_CHECKERR(dedup != NULL, AVIF_RESULT_OUT_OF_MEMORY); + avifBoxMarker ipco; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "ipco", AVIF_BOX_SIZE_TBD, &ipco)); + avifImage * altImageMetadata = NULL; + altImageMetadata = encoder->data->altImageMetadata; + avifResult result = avifRWStreamWriteProperties(dedup, &s, encoder, imageMetadata, altImageMetadata); + avifItemPropertyDedupDestroy(dedup); + dedup = NULL; + AVIF_CHECKRES(result); + AVIF_CHECKRES(avifRWStreamFinishBox(&s, ipco)); + + avifBoxMarker ipma; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "ipma", AVIF_BOX_SIZE_TBD, 0, 0, &ipma)); + { + uint32_t ipmaCount = 0; + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if (item->associations.count > 0) { + AVIF_CHECKERR(ipmaCount < UINT32_MAX, AVIF_RESULT_INVALID_ARGUMENT); + ++ipmaCount; + } + } + AVIF_CHECKRES(avifRWStreamWriteU32(&s, ipmaCount)); // unsigned int(32) entry_count; + + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if (item->associations.count == 0) { + continue; + } + + AVIF_CHECKRES(avifRWStreamWriteU16(&s, item->id)); // unsigned int(16) item_ID; + AVIF_ASSERT_OR_RETURN(item->associations.count < (1 << 8)); + AVIF_CHECKRES(avifRWStreamWriteU8(&s, (uint8_t)item->associations.count)); // unsigned int(8) association_count; + for (uint32_t i = 0; i < item->associations.count; ++i) { + const avifItemPropertyAssociation * association = &item->associations.association[i]; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, association->essential ? 1 : 0, /*bitCount=*/1)); // bit(1) essential; + AVIF_ASSERT_OR_RETURN(association->property_index <= MAX_PROPERTY_INDEX); + AVIF_CHECKRES(avifRWStreamWriteBits(&s, association->property_index, /*bitCount=*/7)); // unsigned int(7) property_index; + } + } + } + AVIF_CHECKRES(avifRWStreamFinishBox(&s, ipma)); + + AVIF_CHECKRES(avifRWStreamFinishBox(&s, iprp)); + + // ----------------------------------------------------------------------- + // Write grpl/altr box + + if (encoder->data->alternativeItemIDs.count) { + // Section 8.18.3.3 of ISO 14496-12 (ISOBMFF) says: + // group_id is a non-negative integer assigned to the particular grouping that shall not be equal to any + // group_id value of any other EntityToGroupBox, any item_ID value of the hierarchy level + // (file, movie. or track) that contains the GroupsListBox, or any track_ID value (when the + // GroupsListBox is contained in the file level). + AVIF_ASSERT_OR_RETURN(encoder->data->lastItemID < UINT16_MAX); + ++encoder->data->lastItemID; + const uint32_t groupID = encoder->data->lastItemID; + AVIF_CHECKRES(avifWriteAltrGroup(&s, groupID, &encoder->data->alternativeItemIDs)); + } + + // ----------------------------------------------------------------------- + // Finish meta box + + AVIF_CHECKRES(avifRWStreamFinishBox(&s, meta)); + + // ----------------------------------------------------------------------- + // Write tracks (if an image sequence) + + if (isSequence) { + static const uint8_t unityMatrix[9][4] = { + /* clang-format off */ + { 0x00, 0x01, 0x00, 0x00 }, + { 0 }, + { 0 }, + { 0 }, + { 0x00, 0x01, 0x00, 0x00 }, + { 0 }, + { 0 }, + { 0 }, + { 0x40, 0x00, 0x00, 0x00 } /* clang-format on */ + }; + + if (encoder->repetitionCount < 0 && encoder->repetitionCount != AVIF_REPETITION_COUNT_INFINITE) { + return AVIF_RESULT_INVALID_ARGUMENT; + } + + uint64_t framesDurationInTimescales = 0; + for (uint32_t frameIndex = 0; frameIndex < encoder->data->frames.count; ++frameIndex) { + const avifEncoderFrame * frame = &encoder->data->frames.frame[frameIndex]; + framesDurationInTimescales += frame->durationInTimescales; + } + uint64_t durationInTimescales; + if (encoder->repetitionCount == AVIF_REPETITION_COUNT_INFINITE) { + durationInTimescales = AVIF_INDEFINITE_DURATION64; + } else { + uint64_t loopCount = encoder->repetitionCount + 1; + AVIF_ASSERT_OR_RETURN(framesDurationInTimescales != 0); + if (loopCount > UINT64_MAX / framesDurationInTimescales) { + // The multiplication will overflow uint64_t. + return AVIF_RESULT_INVALID_ARGUMENT; + } + durationInTimescales = framesDurationInTimescales * loopCount; + } + + // ------------------------------------------------------------------- + // Start moov + + avifBoxMarker moov; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "moov", AVIF_BOX_SIZE_TBD, &moov)); + + avifBoxMarker mvhd; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "mvhd", AVIF_BOX_SIZE_TBD, 1, 0, &mvhd)); + AVIF_CHECKRES(avifRWStreamWriteU64(&s, creationTime)); // unsigned int(64) creation_time; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, modificationTime)); // unsigned int(64) modification_time; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale)); // unsigned int(32) timescale; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, durationInTimescales)); // unsigned int(64) duration; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0x00010000)); // template int(32) rate = 0x00010000; // typically 1.0 + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0x0100)); // template int(16) volume = 0x0100; // typically, full volume + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // const bit(16) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 8)); // const unsigned int(32)[2] reserved = 0; + AVIF_CHECKRES(avifRWStreamWrite(&s, unityMatrix, sizeof(unityMatrix))); + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 24)); // bit(32)[6] pre_defined = 0; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, encoder->data->items.count)); // unsigned int(32) next_track_ID; + AVIF_CHECKRES(avifRWStreamFinishBox(&s, mvhd)); + + // ------------------------------------------------------------------- + // Write tracks + + for (uint32_t itemIndex = 0; itemIndex < encoder->data->items.count; ++itemIndex) { + avifEncoderItem * item = &encoder->data->items.item[itemIndex]; + if (item->encodeOutput->samples.count == 0) { + continue; + } + + uint32_t syncSamplesCount = 0; + for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { + avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; + if (sample->sync) { + ++syncSamplesCount; + } + } + + avifBoxMarker trak; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "trak", AVIF_BOX_SIZE_TBD, &trak)); + + avifBoxMarker tkhd; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "tkhd", AVIF_BOX_SIZE_TBD, 1, 1, &tkhd)); + AVIF_CHECKRES(avifRWStreamWriteU64(&s, creationTime)); // unsigned int(64) creation_time; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, modificationTime)); // unsigned int(64) modification_time; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, itemIndex + 1)); // unsigned int(32) track_ID; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0)); // const unsigned int(32) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, durationInTimescales)); // unsigned int(64) duration; + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 2)); // const unsigned int(32)[2] reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // template int(16) layer = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // template int(16) alternate_group = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // template int(16) volume = {if track_is_audio 0x0100 else 0}; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // const unsigned int(16) reserved = 0; + AVIF_CHECKRES(avifRWStreamWrite(&s, unityMatrix, sizeof(unityMatrix))); // template int(32)[9] matrix= // { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, imageMetadata->width << 16)); // unsigned int(32) width; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, imageMetadata->height << 16)); // unsigned int(32) height; + AVIF_CHECKRES(avifRWStreamFinishBox(&s, tkhd)); + + if (item->irefToID != 0) { + avifBoxMarker tref; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "tref", AVIF_BOX_SIZE_TBD, &tref)); + avifBoxMarker refType; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, item->irefType, AVIF_BOX_SIZE_TBD, &refType)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)item->irefToID)); + AVIF_CHECKRES(avifRWStreamFinishBox(&s, refType)); + AVIF_CHECKRES(avifRWStreamFinishBox(&s, tref)); + } + + avifBoxMarker edts; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "edts", AVIF_BOX_SIZE_TBD, &edts)); + uint32_t elstFlags = (encoder->repetitionCount != 0); + avifBoxMarker elst; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "elst", AVIF_BOX_SIZE_TBD, 1, elstFlags, &elst)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) entry_count; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, framesDurationInTimescales)); // unsigned int(64) segment_duration; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, 0)); // int(64) media_time; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 1)); // int(16) media_rate_integer; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // int(16) media_rate_fraction = 0; + AVIF_CHECKRES(avifRWStreamFinishBox(&s, elst)); + AVIF_CHECKRES(avifRWStreamFinishBox(&s, edts)); + + if (item->itemCategory != AVIF_ITEM_ALPHA) { + AVIF_CHECKRES(avifEncoderWriteTrackMetaBox(encoder, &s)); + } + + avifBoxMarker mdia; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "mdia", AVIF_BOX_SIZE_TBD, &mdia)); + + avifBoxMarker mdhd; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "mdhd", AVIF_BOX_SIZE_TBD, 1, 0, &mdhd)); + AVIF_CHECKRES(avifRWStreamWriteU64(&s, creationTime)); // unsigned int(64) creation_time; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, modificationTime)); // unsigned int(64) modification_time; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)encoder->timescale)); // unsigned int(32) timescale; + AVIF_CHECKRES(avifRWStreamWriteU64(&s, framesDurationInTimescales)); // unsigned int(64) duration; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 21956)); // bit(1) pad = 0; unsigned int(5)[3] language; ("und") + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // unsigned int(16) pre_defined = 0; + AVIF_CHECKRES(avifRWStreamFinishBox(&s, mdhd)); + + AVIF_CHECKRES(avifRWStreamWriteHandlerBox(&s, (item->itemCategory == AVIF_ITEM_ALPHA) ? "auxv" : "pict")); + + avifBoxMarker minf; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "minf", AVIF_BOX_SIZE_TBD, &minf)); + + avifBoxMarker vmhd; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "vmhd", AVIF_BOX_SIZE_TBD, 0, 1, &vmhd)); + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // template unsigned int(16) graphicsmode = 0; (copy over the existing image) + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 6)); // template unsigned int(16)[3] opcolor = {0, 0, 0}; + AVIF_CHECKRES(avifRWStreamFinishBox(&s, vmhd)); + + avifBoxMarker dinf; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "dinf", AVIF_BOX_SIZE_TBD, &dinf)); + avifBoxMarker dref; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "dref", AVIF_BOX_SIZE_TBD, 0, 0, &dref)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) entry_count; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "url ", /*contentSize=*/0, 0, 1, /*marker=*/NULL)); // flags:1 means data is in this file + AVIF_CHECKRES(avifRWStreamFinishBox(&s, dref)); + AVIF_CHECKRES(avifRWStreamFinishBox(&s, dinf)); + + // The boxes within the "stbl" box are ordered using the following recommendation in ISO/IEC 14496-12, Section 6.2.3: + // 4) It is recommended that the boxes within the Sample Table Box be in the following order: Sample Description + // (stsd), Time to Sample (stts), Sample to Chunk (stsc), Sample Size (stsz), Chunk Offset (stco). + // + // Any boxes not listed in the above line are placed in the end (after the "stco" box). + avifBoxMarker stbl; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, "stbl", AVIF_BOX_SIZE_TBD, &stbl)); + + avifBoxMarker stsd; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stsd", AVIF_BOX_SIZE_TBD, 0, 0, &stsd)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) entry_count; + avifBoxMarker imageItem; + AVIF_CHECKRES(avifRWStreamWriteBox(&s, encoder->data->imageItemType, AVIF_BOX_SIZE_TBD, &imageItem)); + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 6)); // const unsigned int(8)[6] reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 1)); // unsigned int(16) data_reference_index; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // unsigned int(16) pre_defined = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0)); // const unsigned int(16) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, sizeof(uint32_t) * 3)); // unsigned int(32)[3] pre_defined = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->width)); // unsigned int(16) width; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)imageMetadata->height)); // unsigned int(16) height; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0x00480000)); // template unsigned int(32) horizresolution + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0x00480000)); // template unsigned int(32) vertresolution + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0)); // const unsigned int(32) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 1)); // template unsigned int(16) frame_count = 1; + AVIF_CHECKRES(avifRWStreamWriteChars(&s, "\012AOM Coding", 11)); // string[32] compressorname; + AVIF_CHECKRES(avifRWStreamWriteZeros(&s, 32 - 11)); // + AVIF_CHECKRES(avifRWStreamWriteU16(&s, 0x0018)); // template unsigned int(16) depth = 0x0018; + AVIF_CHECKRES(avifRWStreamWriteU16(&s, (uint16_t)0xffff)); // int(16) pre_defined = -1; + AVIF_CHECKRES(writeConfigBox(&s, &item->av1C, encoder->data->configPropName)); + if (item->itemCategory == AVIF_ITEM_COLOR) { + AVIF_CHECKRES(avifEncoderWriteColorProperties(&s, imageMetadata, NULL, NULL)); + AVIF_CHECKRES(avifEncoderWriteHDRProperties(NULL, &s, imageMetadata, NULL, NULL)); + AVIF_CHECKRES(avifEncoderWriteTransformativeProperties(NULL, &s, imageMetadata, NULL, NULL)); + } + + avifBoxMarker ccst; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "ccst", AVIF_BOX_SIZE_TBD, 0, 0, &ccst)); + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/1)); // unsigned int(1) all_ref_pics_intra; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 1, /*bitCount=*/1)); // unsigned int(1) intra_pred_used; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 15, /*bitCount=*/4)); // unsigned int(4) max_ref_per_pic; + AVIF_CHECKRES(avifRWStreamWriteBits(&s, 0, /*bitCount=*/26)); // unsigned int(26) reserved; + AVIF_CHECKRES(avifRWStreamFinishBox(&s, ccst)); + + if (item->itemCategory == AVIF_ITEM_ALPHA) { + avifBoxMarker auxi; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "auxi", AVIF_BOX_SIZE_TBD, 0, 0, &auxi)); + AVIF_CHECKRES(avifRWStreamWriteChars(&s, alphaURN, alphaURNSize)); // string aux_track_type; + AVIF_CHECKRES(avifRWStreamFinishBox(&s, auxi)); + } + + AVIF_CHECKRES(avifRWStreamFinishBox(&s, imageItem)); + AVIF_CHECKRES(avifRWStreamFinishBox(&s, stsd)); + + avifBoxMarker stts; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stts", AVIF_BOX_SIZE_TBD, 0, 0, &stts)); + size_t sttsEntryCountOffset = avifRWStreamOffset(&s); + uint32_t sttsEntryCount = 0; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0)); // unsigned int(32) entry_count; + for (uint32_t sampleCount = 0, frameIndex = 0; frameIndex < encoder->data->frames.count; ++frameIndex) { + avifEncoderFrame * frame = &encoder->data->frames.frame[frameIndex]; + ++sampleCount; + if (frameIndex < (encoder->data->frames.count - 1)) { + avifEncoderFrame * nextFrame = &encoder->data->frames.frame[frameIndex + 1]; + if (frame->durationInTimescales == nextFrame->durationInTimescales) { + continue; + } + } + AVIF_CHECKRES(avifRWStreamWriteU32(&s, sampleCount)); // unsigned int(32) sample_count; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)frame->durationInTimescales)); // unsigned int(32) sample_delta; + sampleCount = 0; + ++sttsEntryCount; + } + size_t prevOffset = avifRWStreamOffset(&s); + avifRWStreamSetOffset(&s, sttsEntryCountOffset); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, sttsEntryCount)); + avifRWStreamSetOffset(&s, prevOffset); + AVIF_CHECKRES(avifRWStreamFinishBox(&s, stts)); + + avifBoxMarker stsc; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stsc", AVIF_BOX_SIZE_TBD, 0, 0, &stsc)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) entry_count; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) first_chunk; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, item->encodeOutput->samples.count)); // unsigned int(32) samples_per_chunk; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) sample_description_index; + AVIF_CHECKRES(avifRWStreamFinishBox(&s, stsc)); + + avifBoxMarker stsz; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stsz", AVIF_BOX_SIZE_TBD, 0, 0, &stsz)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 0)); // unsigned int(32) sample_size; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, item->encodeOutput->samples.count)); // unsigned int(32) sample_count; + for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { + avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; + AVIF_CHECKRES(avifRWStreamWriteU32(&s, (uint32_t)sample->data.size)); // unsigned int(32) entry_size; + } + AVIF_CHECKRES(avifRWStreamFinishBox(&s, stsz)); + + avifBoxMarker stco; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stco", AVIF_BOX_SIZE_TBD, 0, 0, &stco)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) entry_count; + AVIF_CHECKRES(avifEncoderItemAddMdatFixup(item, &s)); // + AVIF_CHECKRES(avifRWStreamWriteU32(&s, 1)); // unsigned int(32) chunk_offset; (set later) + AVIF_CHECKRES(avifRWStreamFinishBox(&s, stco)); + + avifBool hasNonSyncSample = AVIF_FALSE; + for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { + if (!item->encodeOutput->samples.sample[sampleIndex].sync) { + hasNonSyncSample = AVIF_TRUE; + break; + } + } + // ISO/IEC 14496-12, Section 8.6.2.1: + // If the SyncSampleBox is not present, every sample is a sync sample. + if (hasNonSyncSample) { + avifBoxMarker stss; + AVIF_CHECKRES(avifRWStreamWriteFullBox(&s, "stss", AVIF_BOX_SIZE_TBD, 0, 0, &stss)); + AVIF_CHECKRES(avifRWStreamWriteU32(&s, syncSamplesCount)); // unsigned int(32) entry_count; + for (uint32_t sampleIndex = 0; sampleIndex < item->encodeOutput->samples.count; ++sampleIndex) { + avifEncodeSample * sample = &item->encodeOutput->samples.sample[sampleIndex]; + if (sample->sync) { + AVIF_CHECKRES(avifRWStreamWriteU32(&s, sampleIndex + 1)); // unsigned int(32) sample_number; + } + } + AVIF_CHECKRES(avifRWStreamFinishBox(&s, stss)); + } + + AVIF_CHECKRES(avifRWStreamFinishBox(&s, stbl)); + + AVIF_CHECKRES(avifRWStreamFinishBox(&s, minf)); + AVIF_CHECKRES(avifRWStreamFinishBox(&s, mdia)); + AVIF_CHECKRES(avifRWStreamFinishBox(&s, trak)); + } + + // ------------------------------------------------------------------- + // Finish moov box + + AVIF_CHECKRES(avifRWStreamFinishBox(&s, moov)); + } + + // ----------------------------------------------------------------------- + // Write mdat + + avifEncoderItemReferenceArray layeredColorItems; + avifEncoderItemReferenceArray layeredAlphaItems; + if (!avifArrayCreate(&layeredColorItems, sizeof(avifEncoderItemReference), 1)) { + result = AVIF_RESULT_OUT_OF_MEMORY; + } + if (!avifArrayCreate(&layeredAlphaItems, sizeof(avifEncoderItemReference), 1)) { + result = AVIF_RESULT_OUT_OF_MEMORY; + } + if (result == AVIF_RESULT_OK) { + result = avifEncoderWriteMediaDataBox(encoder, &s, &layeredColorItems, &layeredAlphaItems); + } + avifArrayDestroy(&layeredColorItems); + avifArrayDestroy(&layeredAlphaItems); + AVIF_CHECKRES(result); + + // ----------------------------------------------------------------------- + // Finish up stream + + avifRWStreamFinishWrite(&s); + +#if defined(AVIF_ENABLE_COMPLIANCE_WARDEN) + AVIF_CHECKRES(avifIsCompliant(output->data, output->size)); +#endif + + return AVIF_RESULT_OK; +} + +avifResult avifEncoderWrite(avifEncoder * encoder, const avifImage * image, avifRWData * output) +{ + avifResult addImageResult = avifEncoderAddImage(encoder, image, 1, AVIF_ADD_IMAGE_FLAG_SINGLE); + if (addImageResult != AVIF_RESULT_OK) { + return addImageResult; + } + return avifEncoderFinish(encoder, output); +} + +// Implementation of section 2.3.3 of AV1 Codec ISO Media File Format Binding specification v1.2.0. +// See https://aomediacodec.github.io/av1-isobmff/v1.2.0.html#av1codecconfigurationbox-syntax. +static avifResult writeCodecConfig(avifRWStream * s, const avifCodecConfigurationBox * cfg) +{ + const size_t av1COffset = s->offset; + + AVIF_CHECKRES(avifRWStreamWriteBits(s, 1, /*bitCount=*/1)); // unsigned int (1) marker = 1; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 1, /*bitCount=*/7)); // unsigned int (7) version = 1; + + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->seqProfile, /*bitCount=*/3)); // unsigned int (3) seq_profile; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->seqLevelIdx0, /*bitCount=*/5)); // unsigned int (5) seq_level_idx_0; + + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->seqTier0, /*bitCount=*/1)); // unsigned int (1) seq_tier_0; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->highBitdepth, /*bitCount=*/1)); // unsigned int (1) high_bitdepth; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->twelveBit, /*bitCount=*/1)); // unsigned int (1) twelve_bit; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->monochrome, /*bitCount=*/1)); // unsigned int (1) monochrome; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->chromaSubsamplingX, /*bitCount=*/1)); // unsigned int (1) chroma_subsampling_x; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->chromaSubsamplingY, /*bitCount=*/1)); // unsigned int (1) chroma_subsampling_y; + AVIF_CHECKRES(avifRWStreamWriteBits(s, cfg->chromaSamplePosition, /*bitCount=*/2)); // unsigned int (2) chroma_sample_position; + + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, /*bitCount=*/3)); // unsigned int (3) reserved = 0; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, /*bitCount=*/1)); // unsigned int (1) initial_presentation_delay_present; + AVIF_CHECKRES(avifRWStreamWriteBits(s, 0, /*bitCount=*/4)); // unsigned int (4) reserved = 0; + + // According to section 2.2.1 of AV1 Image File Format specification v1.1.0, + // there is no need to write any OBU here. + // See https://aomediacodec.github.io/av1-avif/v1.1.0.html#av1-configuration-item-property. + // unsigned int (8) configOBUs[]; + + AVIF_ASSERT_OR_RETURN(s->offset - av1COffset == 4); // Make sure writeCodecConfig() writes exactly 4 bytes. + return AVIF_RESULT_OK; +} + +static avifResult writeConfigBox(avifRWStream * s, const avifCodecConfigurationBox * cfg, const char * configPropName) +{ + avifBoxMarker configBox; + AVIF_CHECKRES(avifRWStreamWriteBox(s, configPropName, AVIF_BOX_SIZE_TBD, &configBox)); + AVIF_CHECKRES(writeCodecConfig(s, cfg)); + AVIF_CHECKRES(avifRWStreamFinishBox(s, configBox)); + return AVIF_RESULT_OK; +} diff --git a/3rdparty/libavif/third_party/iccjpeg/iccjpeg.c b/3rdparty/libavif/third_party/iccjpeg/iccjpeg.c new file mode 100644 index 0000000..d08b4bd --- /dev/null +++ b/3rdparty/libavif/third_party/iccjpeg/iccjpeg.c @@ -0,0 +1,248 @@ +/* + * iccprofile.c + * + * This file provides code to read and write International Color Consortium + * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has + * defined a standard format for including such data in JPEG "APP2" markers. + * The code given here does not know anything about the internal structure + * of the ICC profile data; it just knows how to put the profile data into + * a JPEG file being written, or get it back out when reading. + * + * This code depends on new features added to the IJG JPEG library as of + * IJG release 6b; it will not compile or work with older IJG versions. + * + * NOTE: this code would need surgery to work on 16-bit-int machines + * with ICC profiles exceeding 64K bytes in size. If you need to do that, + * change all the "unsigned int" variables to "INT32". You'll also need + * to find a malloc() replacement that can allocate more than 64K. + */ + +#include "iccjpeg.h" +#include /* define malloc() */ + + +/* + * Since an ICC profile can be larger than the maximum size of a JPEG marker + * (64K), we need provisions to split it into multiple markers. The format + * defined by the ICC specifies one or more APP2 markers containing the + * following data: + * Identifying string ASCII "ICC_PROFILE\0" (12 bytes) + * Marker sequence number 1 for first APP2, 2 for next, etc (1 byte) + * Number of markers Total number of APP2's used (1 byte) + * Profile data (remainder of APP2 data) + * Decoders should use the marker sequence numbers to reassemble the profile, + * rather than assuming that the APP2 markers appear in the correct sequence. + */ + +#define ICC_MARKER (JPEG_APP0 + 2) /* JPEG marker code for ICC */ +#define ICC_OVERHEAD_LEN 14 /* size of non-profile data in APP2 */ +#define MAX_BYTES_IN_MARKER 65533 /* maximum data len of a JPEG marker */ +#define MAX_DATA_BYTES_IN_MARKER (MAX_BYTES_IN_MARKER - ICC_OVERHEAD_LEN) + + +/* + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +void +write_icc_profile (j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len) +{ + unsigned int num_markers; /* total number of markers we'll write */ + int cur_marker = 1; /* per spec, counting starts at 1 */ + unsigned int length; /* number of bytes to write in this marker */ + + /* Calculate the number of markers we'll need, rounding up of course */ + num_markers = icc_data_len / MAX_DATA_BYTES_IN_MARKER; + if (num_markers * MAX_DATA_BYTES_IN_MARKER != icc_data_len) + num_markers++; + + while (icc_data_len > 0) { + /* length of profile to put in this marker */ + length = icc_data_len; + if (length > MAX_DATA_BYTES_IN_MARKER) + length = MAX_DATA_BYTES_IN_MARKER; + icc_data_len -= length; + + /* Write the JPEG marker header (APP2 code and marker length) */ + jpeg_write_m_header(cinfo, ICC_MARKER, + (unsigned int) (length + ICC_OVERHEAD_LEN)); + + /* Write the marker identifying string "ICC_PROFILE" (null-terminated). + * We code it in this less-than-transparent way so that the code works + * even if the local character set is not ASCII. + */ + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x43); + jpeg_write_m_byte(cinfo, 0x5F); + jpeg_write_m_byte(cinfo, 0x50); + jpeg_write_m_byte(cinfo, 0x52); + jpeg_write_m_byte(cinfo, 0x4F); + jpeg_write_m_byte(cinfo, 0x46); + jpeg_write_m_byte(cinfo, 0x49); + jpeg_write_m_byte(cinfo, 0x4C); + jpeg_write_m_byte(cinfo, 0x45); + jpeg_write_m_byte(cinfo, 0x0); + + /* Add the sequencing info */ + jpeg_write_m_byte(cinfo, cur_marker); + jpeg_write_m_byte(cinfo, (int) num_markers); + + /* Add the profile data */ + while (length--) { + jpeg_write_m_byte(cinfo, *icc_data_ptr); + icc_data_ptr++; + } + cur_marker++; + } +} + + +/* + * Prepare for reading an ICC profile + */ + +void +setup_read_icc_profile (j_decompress_ptr cinfo) +{ + /* Tell the library to keep any APP2 data it may find */ + jpeg_save_markers(cinfo, ICC_MARKER, 0xFFFF); +} + + +/* + * Handy subroutine to test whether a saved marker is an ICC profile marker. + */ + +static boolean +marker_is_icc (jpeg_saved_marker_ptr marker) +{ + return + marker->marker == ICC_MARKER && + marker->data_length >= ICC_OVERHEAD_LEN && + /* verify the identifying string */ + GETJOCTET(marker->data[0]) == 0x49 && + GETJOCTET(marker->data[1]) == 0x43 && + GETJOCTET(marker->data[2]) == 0x43 && + GETJOCTET(marker->data[3]) == 0x5F && + GETJOCTET(marker->data[4]) == 0x50 && + GETJOCTET(marker->data[5]) == 0x52 && + GETJOCTET(marker->data[6]) == 0x4F && + GETJOCTET(marker->data[7]) == 0x46 && + GETJOCTET(marker->data[8]) == 0x49 && + GETJOCTET(marker->data[9]) == 0x4C && + GETJOCTET(marker->data[10]) == 0x45 && + GETJOCTET(marker->data[11]) == 0x0; +} + + +/* + * See if there was an ICC profile in the JPEG file being read; + * if so, reassemble and return the profile data. + * + * TRUE is returned if an ICC profile was found, FALSE if not. + * If TRUE is returned, *icc_data_ptr is set to point to the + * returned data, and *icc_data_len is set to its length. + * + * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() + * and must be freed by the caller with free() when the caller no longer + * needs it. (Alternatively, we could write this routine to use the + * IJG library's memory allocator, so that the data would be freed implicitly + * at jpeg_finish_decompress() time. But it seems likely that many apps + * will prefer to have the data stick around after decompression finishes.) + * + * NOTE: if the file contains invalid ICC APP2 markers, we just silently + * return FALSE. You might want to issue an error message instead. + */ + +boolean +read_icc_profile (j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len) +{ + jpeg_saved_marker_ptr marker; + int num_markers = 0; + int seq_no; + JOCTET *icc_data; + unsigned int total_length; +#define MAX_SEQ_NO 255 /* sufficient since marker numbers are bytes */ + char marker_present[MAX_SEQ_NO+1]; /* 1 if marker found */ + unsigned int data_length[MAX_SEQ_NO+1]; /* size of profile data in marker */ + unsigned int data_offset[MAX_SEQ_NO+1]; /* offset for data in marker */ + + *icc_data_ptr = NULL; /* avoid confusion if FALSE return */ + *icc_data_len = 0; + + /* This first pass over the saved markers discovers whether there are + * any ICC markers and verifies the consistency of the marker numbering. + */ + + for (seq_no = 1; seq_no <= MAX_SEQ_NO; seq_no++) + marker_present[seq_no] = 0; + + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker_is_icc(marker)) { + if (num_markers == 0) + num_markers = GETJOCTET(marker->data[13]); + else if (num_markers != GETJOCTET(marker->data[13])) + return FALSE; /* inconsistent num_markers fields */ + seq_no = GETJOCTET(marker->data[12]); + if (seq_no <= 0 || seq_no > num_markers) + return FALSE; /* bogus sequence number */ + if (marker_present[seq_no]) + return FALSE; /* duplicate sequence numbers */ + marker_present[seq_no] = 1; + data_length[seq_no] = marker->data_length - ICC_OVERHEAD_LEN; + } + } + + if (num_markers == 0) + return FALSE; + + /* Check for missing markers, count total space needed, + * compute offset of each marker's part of the data. + */ + + total_length = 0; + for (seq_no = 1; seq_no <= num_markers; seq_no++) { + if (marker_present[seq_no] == 0) + return FALSE; /* missing sequence number */ + data_offset[seq_no] = total_length; + total_length += data_length[seq_no]; + } + + if (total_length == 0) + return FALSE; /* found only empty markers? */ + + /* Allocate space for assembled data */ + icc_data = (JOCTET *) malloc(total_length * sizeof(JOCTET)); + if (icc_data == NULL) + return FALSE; /* oops, out of memory */ + + /* and fill it in */ + for (marker = cinfo->marker_list; marker != NULL; marker = marker->next) { + if (marker_is_icc(marker)) { + JOCTET FAR *src_ptr; + JOCTET *dst_ptr; + unsigned int length; + seq_no = GETJOCTET(marker->data[12]); + dst_ptr = icc_data + data_offset[seq_no]; + src_ptr = marker->data + ICC_OVERHEAD_LEN; + length = data_length[seq_no]; + while (length--) { + *dst_ptr++ = *src_ptr++; + } + } + } + + *icc_data_ptr = icc_data; + *icc_data_len = total_length; + + return TRUE; +} diff --git a/3rdparty/libavif/third_party/iccjpeg/iccjpeg.h b/3rdparty/libavif/third_party/iccjpeg/iccjpeg.h new file mode 100644 index 0000000..5e1888d --- /dev/null +++ b/3rdparty/libavif/third_party/iccjpeg/iccjpeg.h @@ -0,0 +1,73 @@ +/* + * iccprofile.h + * + * This file provides code to read and write International Color Consortium + * (ICC) device profiles embedded in JFIF JPEG image files. The ICC has + * defined a standard format for including such data in JPEG "APP2" markers. + * The code given here does not know anything about the internal structure + * of the ICC profile data; it just knows how to put the profile data into + * a JPEG file being written, or get it back out when reading. + * + * This code depends on new features added to the IJG JPEG library as of + * IJG release 6b; it will not compile or work with older IJG versions. + * + * NOTE: this code would need surgery to work on 16-bit-int machines + * with ICC profiles exceeding 64K bytes in size. See iccprofile.c + * for details. + */ + +#include /* needed to define "FILE", "NULL" */ +#include "jpeglib.h" + + +/* + * This routine writes the given ICC profile data into a JPEG file. + * It *must* be called AFTER calling jpeg_start_compress() and BEFORE + * the first call to jpeg_write_scanlines(). + * (This ordering ensures that the APP2 marker(s) will appear after the + * SOI and JFIF or Adobe markers, but before all else.) + */ + +extern void write_icc_profile JPP((j_compress_ptr cinfo, + const JOCTET *icc_data_ptr, + unsigned int icc_data_len)); + + +/* + * Reading a JPEG file that may contain an ICC profile requires two steps: + * + * 1. After jpeg_create_decompress() but before jpeg_read_header(), + * call setup_read_icc_profile(). This routine tells the IJG library + * to save in memory any APP2 markers it may find in the file. + * + * 2. After jpeg_read_header(), call read_icc_profile() to find out + * whether there was a profile and obtain it if so. + */ + + +/* + * Prepare for reading an ICC profile + */ + +extern void setup_read_icc_profile JPP((j_decompress_ptr cinfo)); + + +/* + * See if there was an ICC profile in the JPEG file being read; + * if so, reassemble and return the profile data. + * + * TRUE is returned if an ICC profile was found, FALSE if not. + * If TRUE is returned, *icc_data_ptr is set to point to the + * returned data, and *icc_data_len is set to its length. + * + * IMPORTANT: the data at **icc_data_ptr has been allocated with malloc() + * and must be freed by the caller with free() when the caller no longer + * needs it. (Alternatively, we could write this routine to use the + * IJG library's memory allocator, so that the data would be freed implicitly + * at jpeg_finish_decompress() time. But it seems likely that many apps + * will prefer to have the data stick around after decompression finishes.) + */ + +extern boolean read_icc_profile JPP((j_decompress_ptr cinfo, + JOCTET **icc_data_ptr, + unsigned int *icc_data_len)); diff --git a/3rdparty/libavif/third_party/libyuv/include/libyuv.h b/3rdparty/libavif/third_party/libyuv/include/libyuv.h new file mode 100644 index 0000000..fc0e430 --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/include/libyuv.h @@ -0,0 +1,21 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef INCLUDE_LIBYUV_H_ +#define INCLUDE_LIBYUV_H_ + +#include "libyuv/basic_types.h" +#include "libyuv/planar_functions.h" +#include "libyuv/row.h" +#include "libyuv/scale.h" +#include "libyuv/scale_row.h" +#include "libyuv/version.h" + +#endif // INCLUDE_LIBYUV_H_ diff --git a/3rdparty/libavif/third_party/libyuv/include/libyuv/basic_types.h b/3rdparty/libavif/third_party/libyuv/include/libyuv/basic_types.h new file mode 100644 index 0000000..1bea67f --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/include/libyuv/basic_types.h @@ -0,0 +1,68 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef INCLUDE_LIBYUV_BASIC_TYPES_H_ +#define INCLUDE_LIBYUV_BASIC_TYPES_H_ + +#include // For size_t and NULL + +#if !defined(INT_TYPES_DEFINED) && !defined(GG_LONGLONG) +#define INT_TYPES_DEFINED + +#if defined(_MSC_VER) && (_MSC_VER < 1600) +#include // for uintptr_t on x86 +typedef unsigned __int64 uint64_t; +typedef __int64 int64_t; +typedef unsigned int uint32_t; +typedef int int32_t; +typedef unsigned short uint16_t; +typedef short int16_t; +typedef unsigned char uint8_t; +typedef signed char int8_t; +#else +#include // for uintptr_t and C99 types +#endif // defined(_MSC_VER) && (_MSC_VER < 1600) +// Types are deprecated. Enable this macro for legacy types. +#ifdef LIBYUV_LEGACY_TYPES +typedef uint64_t uint64; +typedef int64_t int64; +typedef uint32_t uint32; +typedef int32_t int32; +typedef uint16_t uint16; +typedef int16_t int16; +typedef uint8_t uint8; +typedef int8_t int8; +#endif // LIBYUV_LEGACY_TYPES +#endif // INT_TYPES_DEFINED + +#if !defined(LIBYUV_API) +#if defined(_WIN32) || defined(__CYGWIN__) +#if defined(LIBYUV_BUILDING_SHARED_LIBRARY) +#define LIBYUV_API __declspec(dllexport) +#elif defined(LIBYUV_USING_SHARED_LIBRARY) +#define LIBYUV_API __declspec(dllimport) +#else +#define LIBYUV_API +#endif // LIBYUV_BUILDING_SHARED_LIBRARY +#elif defined(__GNUC__) && (__GNUC__ >= 4) && !defined(__APPLE__) && \ + (defined(LIBYUV_BUILDING_SHARED_LIBRARY) || \ + defined(LIBYUV_USING_SHARED_LIBRARY)) +#define LIBYUV_API __attribute__((visibility("default"))) +#else +#define LIBYUV_API +#endif // __GNUC__ +#endif // LIBYUV_API + +// TODO(fbarchard): Remove bool macros. +#define LIBYUV_BOOL int +#define LIBYUV_FALSE 0 +#define LIBYUV_TRUE 1 + +#endif // INCLUDE_LIBYUV_BASIC_TYPES_H_ diff --git a/3rdparty/libavif/third_party/libyuv/include/libyuv/planar_functions.h b/3rdparty/libavif/third_party/libyuv/include/libyuv/planar_functions.h new file mode 100644 index 0000000..3191a77 --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/include/libyuv/planar_functions.h @@ -0,0 +1,30 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef INCLUDE_LIBYUV_PLANAR_FUNCTIONS_H_ +#define INCLUDE_LIBYUV_PLANAR_FUNCTIONS_H_ + +#include "libyuv/basic_types.h" + +void CopyPlane(const uint8_t* src_y, + int src_stride_y, + uint8_t* dst_y, + int dst_stride_y, + int width, + int height); + +void CopyPlane_16(const uint16_t* src_y, + int src_stride_y, + uint16_t* dst_y, + int dst_stride_y, + int width, + int height); + +#endif // INCLUDE_LIBYUV_PLANAR_FUNCTIONS_H_ diff --git a/3rdparty/libavif/third_party/libyuv/include/libyuv/row.h b/3rdparty/libavif/third_party/libyuv/include/libyuv/row.h new file mode 100644 index 0000000..b521cde --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/include/libyuv/row.h @@ -0,0 +1,41 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef INCLUDE_LIBYUV_ROW_H_ +#define INCLUDE_LIBYUV_ROW_H_ + +#include // For NULL +#include // For malloc + +#include "libyuv/basic_types.h" + +#define align_buffer_64(var, size) \ + void* var##_mem = malloc((size) + 63); /* NOLINT */ \ + uint8_t* var = (uint8_t*)(((intptr_t)var##_mem + 63) & ~63) /* NOLINT */ + +#define free_aligned_buffer_64(var) \ + free(var##_mem); \ + var = NULL + +void CopyRow_C(const uint8_t* src, uint8_t* dst, int count); + +void InterpolateRow_C(uint8_t* dst_ptr, + const uint8_t* src_ptr, + ptrdiff_t src_stride, + int width, + int source_y_fraction); + +void InterpolateRow_16_C(uint16_t* dst_ptr, + const uint16_t* src_ptr, + ptrdiff_t src_stride, + int width, + int source_y_fraction); + +#endif // INCLUDE_LIBYUV_ROW_H_ diff --git a/3rdparty/libavif/third_party/libyuv/include/libyuv/scale.h b/3rdparty/libavif/third_party/libyuv/include/libyuv/scale.h new file mode 100644 index 0000000..cc62848 --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/include/libyuv/scale.h @@ -0,0 +1,54 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef INCLUDE_LIBYUV_SCALE_H_ +#define INCLUDE_LIBYUV_SCALE_H_ + +#include "libyuv/basic_types.h" + +// Supported filtering. +typedef enum FilterMode { + kFilterNone = 0, // Point sample; Fastest. + kFilterLinear = 1, // Filter horizontally only. + kFilterBilinear = 2, // Faster than box, but lower quality scaling down. + kFilterBox = 3 // Highest quality. +} FilterModeEnum; + +int ScalePlane(const uint8_t* src, + int src_stride, + int src_width, + int src_height, + uint8_t* dst, + int dst_stride, + int dst_width, + int dst_height, + enum FilterMode filtering); + +int ScalePlane_16(const uint16_t* src, + int src_stride, + int src_width, + int src_height, + uint16_t* dst, + int dst_stride, + int dst_width, + int dst_height, + enum FilterMode filtering); + +int ScalePlane_12(const uint16_t* src, + int src_stride, + int src_width, + int src_height, + uint16_t* dst, + int dst_stride, + int dst_width, + int dst_height, + enum FilterMode filtering); + +#endif // INCLUDE_LIBYUV_SCALE_H_ diff --git a/3rdparty/libavif/third_party/libyuv/include/libyuv/scale_row.h b/3rdparty/libavif/third_party/libyuv/include/libyuv/scale_row.h new file mode 100644 index 0000000..6fb93ff --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/include/libyuv/scale_row.h @@ -0,0 +1,147 @@ +/* + * Copyright 2013 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef INCLUDE_LIBYUV_SCALE_ROW_H_ +#define INCLUDE_LIBYUV_SCALE_ROW_H_ + +#include "libyuv/basic_types.h" +#include "libyuv/scale.h" + +// Scale ARGB vertically with bilinear interpolation. +void ScalePlaneVertical(int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint8_t* src_argb, + uint8_t* dst_argb, + int x, + int y, + int dy, + int bpp, + enum FilterMode filtering); + +void ScalePlaneVertical_16(int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint16_t* src_argb, + uint16_t* dst_argb, + int x, + int y, + int dy, + int wpp, + enum FilterMode filtering); + +// Simplify the filtering based on scale factors. +enum FilterMode ScaleFilterReduce(int src_width, + int src_height, + int dst_width, + int dst_height, + enum FilterMode filtering); + +// Divide num by div and return as 16.16 fixed point result. +int FixedDiv_C(int num, int div); +// Divide num - 1 by div - 1 and return as 16.16 fixed point result. +int FixedDiv1_C(int num, int div); +#define FixedDiv FixedDiv_C +#define FixedDiv1 FixedDiv1_C + +// Compute slope values for stepping. +void ScaleSlope(int src_width, + int src_height, + int dst_width, + int dst_height, + enum FilterMode filtering, + int* x, + int* y, + int* dx, + int* dy); + +void ScaleRowUp2_Linear_C(const uint8_t* src_ptr, + uint8_t* dst_ptr, + int dst_width); +void ScaleRowUp2_Bilinear_C(const uint8_t* src_ptr, + ptrdiff_t src_stride, + uint8_t* dst_ptr, + ptrdiff_t dst_stride, + int dst_width); +void ScaleRowUp2_Linear_16_C(const uint16_t* src_ptr, + uint16_t* dst_ptr, + int dst_width); +void ScaleRowUp2_Bilinear_16_C(const uint16_t* src_ptr, + ptrdiff_t src_stride, + uint16_t* dst_ptr, + ptrdiff_t dst_stride, + int dst_width); +void ScaleRowUp2_Linear_Any_C(const uint8_t* src_ptr, + uint8_t* dst_ptr, + int dst_width); +void ScaleRowUp2_Bilinear_Any_C(const uint8_t* src_ptr, + ptrdiff_t src_stride, + uint8_t* dst_ptr, + ptrdiff_t dst_stride, + int dst_width); +void ScaleRowUp2_Linear_16_Any_C(const uint16_t* src_ptr, + uint16_t* dst_ptr, + int dst_width); +void ScaleRowUp2_Bilinear_16_Any_C(const uint16_t* src_ptr, + ptrdiff_t src_stride, + uint16_t* dst_ptr, + ptrdiff_t dst_stride, + int dst_width); + +void ScaleCols_C(uint8_t* dst_ptr, + const uint8_t* src_ptr, + int dst_width, + int x, + int dx); +void ScaleCols_16_C(uint16_t* dst_ptr, + const uint16_t* src_ptr, + int dst_width, + int x, + int dx); +void ScaleColsUp2_C(uint8_t* dst_ptr, + const uint8_t* src_ptr, + int dst_width, + int, + int); +void ScaleColsUp2_16_C(uint16_t* dst_ptr, + const uint16_t* src_ptr, + int dst_width, + int, + int); +void ScaleFilterCols_C(uint8_t* dst_ptr, + const uint8_t* src_ptr, + int dst_width, + int x, + int dx); +void ScaleFilterCols_16_C(uint16_t* dst_ptr, + const uint16_t* src_ptr, + int dst_width, + int x, + int dx); +void ScaleFilterCols64_C(uint8_t* dst_ptr, + const uint8_t* src_ptr, + int dst_width, + int x32, + int dx); +void ScaleFilterCols64_16_C(uint16_t* dst_ptr, + const uint16_t* src_ptr, + int dst_width, + int x32, + int dx); +void ScaleAddRow_C(const uint8_t* src_ptr, uint16_t* dst_ptr, int src_width); +void ScaleAddRow_16_C(const uint16_t* src_ptr, + uint32_t* dst_ptr, + int src_width); + +#endif // INCLUDE_LIBYUV_SCALE_ROW_H_ diff --git a/3rdparty/libavif/third_party/libyuv/include/libyuv/version.h b/3rdparty/libavif/third_party/libyuv/include/libyuv/version.h new file mode 100644 index 0000000..bcea000 --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/include/libyuv/version.h @@ -0,0 +1,16 @@ +/* + * Copyright 2012 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef INCLUDE_LIBYUV_VERSION_H_ +#define INCLUDE_LIBYUV_VERSION_H_ + +#define LIBYUV_VERSION 1880 + +#endif // INCLUDE_LIBYUV_VERSION_H_ diff --git a/3rdparty/libavif/third_party/libyuv/source/planar_functions.c b/3rdparty/libavif/third_party/libyuv/source/planar_functions.c new file mode 100644 index 0000000..366752d --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/source/planar_functions.c @@ -0,0 +1,64 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "libyuv/planar_functions.h" + +#include +#include // for memset() + +#include "libyuv/row.h" +#include "libyuv/scale_row.h" // for ScaleRowDown2 + +// Copy a plane of data +void CopyPlane(const uint8_t* src_y, + int src_stride_y, + uint8_t* dst_y, + int dst_stride_y, + int width, + int height) { + int y; + void (*CopyRow)(const uint8_t* src, uint8_t* dst, int width) = CopyRow_C; + if (width <= 0 || height == 0) { + return; + } + // Negative height means invert the image. + if (height < 0) { + height = -height; + dst_y = dst_y + (height - 1) * dst_stride_y; + dst_stride_y = -dst_stride_y; + } + // Coalesce rows. + if (src_stride_y == width && dst_stride_y == width) { + width *= height; + height = 1; + src_stride_y = dst_stride_y = 0; + } + // Nothing to do. + if (src_y == dst_y && src_stride_y == dst_stride_y) { + return; + } + + // Copy plane + for (y = 0; y < height; ++y) { + CopyRow(src_y, dst_y, width); + src_y += src_stride_y; + dst_y += dst_stride_y; + } +} + +void CopyPlane_16(const uint16_t* src_y, + int src_stride_y, + uint16_t* dst_y, + int dst_stride_y, + int width, + int height) { + CopyPlane((const uint8_t*)src_y, src_stride_y * 2, (uint8_t*)dst_y, + dst_stride_y * 2, width * 2, height); +} diff --git a/3rdparty/libavif/third_party/libyuv/source/row_common.c b/3rdparty/libavif/third_party/libyuv/source/row_common.c new file mode 100644 index 0000000..a8c4f66 --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/source/row_common.c @@ -0,0 +1,105 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "libyuv/row.h" + +#include +#include // For memcpy and memset. + +#include "libyuv/basic_types.h" + +#define STATIC_CAST(type, expr) (type)(expr) + +void CopyRow_C(const uint8_t* src, uint8_t* dst, int count) { + memcpy(dst, src, count); +} + +// Blend 2 rows into 1. +static void HalfRow_C(const uint8_t* src_uv, + ptrdiff_t src_uv_stride, + uint8_t* dst_uv, + int width) { + int x; + for (x = 0; x < width; ++x) { + dst_uv[x] = (src_uv[x] + src_uv[src_uv_stride + x] + 1) >> 1; + } +} + +static void HalfRow_16_C(const uint16_t* src_uv, + ptrdiff_t src_uv_stride, + uint16_t* dst_uv, + int width) { + int x; + for (x = 0; x < width; ++x) { + dst_uv[x] = (src_uv[x] + src_uv[src_uv_stride + x] + 1) >> 1; + } +} + +// C version 2x2 -> 2x1. +void InterpolateRow_C(uint8_t* dst_ptr, + const uint8_t* src_ptr, + ptrdiff_t src_stride, + int width, + int source_y_fraction) { + int y1_fraction = source_y_fraction; + int y0_fraction = 256 - y1_fraction; + const uint8_t* src_ptr1 = src_ptr + src_stride; + int x; + assert(source_y_fraction >= 0); + assert(source_y_fraction < 256); + + if (y1_fraction == 0) { + memcpy(dst_ptr, src_ptr, width); + return; + } + if (y1_fraction == 128) { + HalfRow_C(src_ptr, src_stride, dst_ptr, width); + return; + } + for (x = 0; x < width; ++x) { + dst_ptr[0] = STATIC_CAST( + uint8_t, + (src_ptr[0] * y0_fraction + src_ptr1[0] * y1_fraction + 128) >> 8); + ++src_ptr; + ++src_ptr1; + ++dst_ptr; + } +} + +// C version 2x2 -> 2x1. +void InterpolateRow_16_C(uint16_t* dst_ptr, + const uint16_t* src_ptr, + ptrdiff_t src_stride, + int width, + int source_y_fraction) { + int y1_fraction = source_y_fraction; + int y0_fraction = 256 - y1_fraction; + const uint16_t* src_ptr1 = src_ptr + src_stride; + int x; + assert(source_y_fraction >= 0); + assert(source_y_fraction < 256); + + if (y1_fraction == 0) { + memcpy(dst_ptr, src_ptr, width * 2); + return; + } + if (y1_fraction == 128) { + HalfRow_16_C(src_ptr, src_stride, dst_ptr, width); + return; + } + for (x = 0; x < width; ++x) { + dst_ptr[0] = STATIC_CAST( + uint16_t, + (src_ptr[0] * y0_fraction + src_ptr1[0] * y1_fraction + 128) >> 8); + ++src_ptr; + ++src_ptr1; + ++dst_ptr; + } +} diff --git a/3rdparty/libavif/third_party/libyuv/source/scale.c b/3rdparty/libavif/third_party/libyuv/source/scale.c new file mode 100644 index 0000000..e4dd177 --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/source/scale.c @@ -0,0 +1,1010 @@ +/* + * Copyright 2011 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "libyuv/scale.h" + +#include +#include + +#include "libyuv/planar_functions.h" // For CopyPlane +#include "libyuv/row.h" +#include "libyuv/scale_row.h" + +#ifndef AVIF_LIBYUV_ABS_DEFINED +#define AVIF_LIBYUV_ABS_DEFINED +static __inline int Abs(int v) { + return v >= 0 ? v : -v; +} +#endif + +#define CENTERSTART(dx, s) (dx < 0) ? -((-dx >> 1) + s) : ((dx >> 1) + s) + +#define MIN1(x) ((x) < 1 ? 1 : (x)) + +static __inline uint32_t SumPixels(int iboxwidth, const uint16_t* src_ptr) { + uint32_t sum = 0u; + int x; + assert(iboxwidth > 0); + for (x = 0; x < iboxwidth; ++x) { + sum += src_ptr[x]; + } + return sum; +} + +static __inline uint32_t SumPixels_16(int iboxwidth, const uint32_t* src_ptr) { + uint32_t sum = 0u; + int x; + assert(iboxwidth > 0); + for (x = 0; x < iboxwidth; ++x) { + sum += src_ptr[x]; + } + return sum; +} + +static void ScaleAddCols2_C(int dst_width, + int boxheight, + int x, + int dx, + const uint16_t* src_ptr, + uint8_t* dst_ptr) { + int i; + int scaletbl[2]; + int minboxwidth = dx >> 16; + int boxwidth; + scaletbl[0] = 65536 / (MIN1(minboxwidth) * boxheight); + scaletbl[1] = 65536 / (MIN1(minboxwidth + 1) * boxheight); + for (i = 0; i < dst_width; ++i) { + int ix = x >> 16; + x += dx; + boxwidth = MIN1((x >> 16) - ix); + int scaletbl_index = boxwidth - minboxwidth; + assert((scaletbl_index == 0) || (scaletbl_index == 1)); + *dst_ptr++ = (uint8_t)(SumPixels(boxwidth, src_ptr + ix) * + scaletbl[scaletbl_index] >> + 16); + } +} + +static void ScaleAddCols2_16_C(int dst_width, + int boxheight, + int x, + int dx, + const uint32_t* src_ptr, + uint16_t* dst_ptr) { + int i; + int scaletbl[2]; + int minboxwidth = dx >> 16; + int boxwidth; + scaletbl[0] = 65536 / (MIN1(minboxwidth) * boxheight); + scaletbl[1] = 65536 / (MIN1(minboxwidth + 1) * boxheight); + for (i = 0; i < dst_width; ++i) { + int ix = x >> 16; + x += dx; + boxwidth = MIN1((x >> 16) - ix); + int scaletbl_index = boxwidth - minboxwidth; + assert((scaletbl_index == 0) || (scaletbl_index == 1)); + *dst_ptr++ = + SumPixels_16(boxwidth, src_ptr + ix) * scaletbl[scaletbl_index] >> 16; + } +} + +static void ScaleAddCols0_C(int dst_width, + int boxheight, + int x, + int dx, + const uint16_t* src_ptr, + uint8_t* dst_ptr) { + int scaleval = 65536 / boxheight; + int i; + (void)dx; + src_ptr += (x >> 16); + for (i = 0; i < dst_width; ++i) { + *dst_ptr++ = (uint8_t)(src_ptr[i] * scaleval >> 16); + } +} + +static void ScaleAddCols1_C(int dst_width, + int boxheight, + int x, + int dx, + const uint16_t* src_ptr, + uint8_t* dst_ptr) { + int boxwidth = MIN1(dx >> 16); + int scaleval = 65536 / (boxwidth * boxheight); + int i; + x >>= 16; + for (i = 0; i < dst_width; ++i) { + *dst_ptr++ = (uint8_t)(SumPixels(boxwidth, src_ptr + x) * scaleval >> 16); + x += boxwidth; + } +} + +static void ScaleAddCols1_16_C(int dst_width, + int boxheight, + int x, + int dx, + const uint32_t* src_ptr, + uint16_t* dst_ptr) { + int boxwidth = MIN1(dx >> 16); + int scaleval = 65536 / (boxwidth * boxheight); + int i; + for (i = 0; i < dst_width; ++i) { + *dst_ptr++ = SumPixels_16(boxwidth, src_ptr + x) * scaleval >> 16; + x += boxwidth; + } +} + +// Scale plane down to any dimensions, with interpolation. +// (boxfilter). +// +// Same method as SimpleScale, which is fixed point, outputting +// one pixel of destination using fixed point (16.16) to step +// through source, sampling a box of pixel with simple +// averaging. +static int ScalePlaneBox(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint8_t* src_ptr, + uint8_t* dst_ptr) { + int j, k; + // Initial source x/y coordinate and step values as 16.16 fixed point. + int x = 0; + int y = 0; + int dx = 0; + int dy = 0; + const int max_y = (src_height << 16); + ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterBox, &x, &y, + &dx, &dy); + src_width = Abs(src_width); + { + // Allocate a row buffer of uint16_t. + align_buffer_64(row16, src_width * 2); + if (!row16) + return 1; + void (*ScaleAddCols)(int dst_width, int boxheight, int x, int dx, + const uint16_t* src_ptr, uint8_t* dst_ptr) = + (dx & 0xffff) ? ScaleAddCols2_C + : ((dx != 0x10000) ? ScaleAddCols1_C : ScaleAddCols0_C); + void (*ScaleAddRow)(const uint8_t* src_ptr, uint16_t* dst_ptr, + int src_width) = ScaleAddRow_C; + + for (j = 0; j < dst_height; ++j) { + int boxheight; + int iy = y >> 16; + const uint8_t* src = src_ptr + iy * (int64_t)src_stride; + y += dy; + if (y > max_y) { + y = max_y; + } + boxheight = MIN1((y >> 16) - iy); + memset(row16, 0, src_width * 2); + for (k = 0; k < boxheight; ++k) { + ScaleAddRow(src, (uint16_t*)(row16), src_width); + src += src_stride; + } + ScaleAddCols(dst_width, boxheight, x, dx, (uint16_t*)(row16), dst_ptr); + dst_ptr += dst_stride; + } + free_aligned_buffer_64(row16); + } + return 0; +} + +static int ScalePlaneBox_16(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint16_t* src_ptr, + uint16_t* dst_ptr) { + int j, k; + // Initial source x/y coordinate and step values as 16.16 fixed point. + int x = 0; + int y = 0; + int dx = 0; + int dy = 0; + const int max_y = (src_height << 16); + ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterBox, &x, &y, + &dx, &dy); + src_width = Abs(src_width); + { + // Allocate a row buffer of uint32_t. + align_buffer_64(row32, src_width * 4); + if (!row32) + return 1; + void (*ScaleAddCols)(int dst_width, int boxheight, int x, int dx, + const uint32_t* src_ptr, uint16_t* dst_ptr) = + (dx & 0xffff) ? ScaleAddCols2_16_C : ScaleAddCols1_16_C; + void (*ScaleAddRow)(const uint16_t* src_ptr, uint32_t* dst_ptr, + int src_width) = ScaleAddRow_16_C; + +#if defined(HAS_SCALEADDROW_16_SSE2) + if (TestCpuFlag(kCpuHasSSE2) && IS_ALIGNED(src_width, 16)) { + ScaleAddRow = ScaleAddRow_16_SSE2; + } +#endif + + for (j = 0; j < dst_height; ++j) { + int boxheight; + int iy = y >> 16; + const uint16_t* src = src_ptr + iy * (int64_t)src_stride; + y += dy; + if (y > max_y) { + y = max_y; + } + boxheight = MIN1((y >> 16) - iy); + memset(row32, 0, src_width * 4); + for (k = 0; k < boxheight; ++k) { + ScaleAddRow(src, (uint32_t*)(row32), src_width); + src += src_stride; + } + ScaleAddCols(dst_width, boxheight, x, dx, (uint32_t*)(row32), dst_ptr); + dst_ptr += dst_stride; + } + free_aligned_buffer_64(row32); + } + return 0; +} + +// Scale plane down with bilinear interpolation. +static int ScalePlaneBilinearDown(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint8_t* src_ptr, + uint8_t* dst_ptr, + enum FilterMode filtering) { + // Initial source x/y coordinate and step values as 16.16 fixed point. + int x = 0; + int y = 0; + int dx = 0; + int dy = 0; + // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear. + // Allocate a row buffer. + align_buffer_64(row, src_width); + if (!row) + return 1; + + const int max_y = (src_height - 1) << 16; + int j; + void (*ScaleFilterCols)(uint8_t* dst_ptr, const uint8_t* src_ptr, + int dst_width, int x, int dx) = + (src_width >= 32768) ? ScaleFilterCols64_C : ScaleFilterCols_C; + void (*InterpolateRow)(uint8_t* dst_ptr, const uint8_t* src_ptr, + ptrdiff_t src_stride, int dst_width, + int source_y_fraction) = InterpolateRow_C; + ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y, + &dx, &dy); + src_width = Abs(src_width); + + if (y > max_y) { + y = max_y; + } + + for (j = 0; j < dst_height; ++j) { + int yi = y >> 16; + const uint8_t* src = src_ptr + yi * (int64_t)src_stride; + if (filtering == kFilterLinear) { + ScaleFilterCols(dst_ptr, src, dst_width, x, dx); + } else { + int yf = (y >> 8) & 255; + InterpolateRow(row, src, src_stride, src_width, yf); + ScaleFilterCols(dst_ptr, row, dst_width, x, dx); + } + dst_ptr += dst_stride; + y += dy; + if (y > max_y) { + y = max_y; + } + } + free_aligned_buffer_64(row); + return 0; +} + +static int ScalePlaneBilinearDown_16(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint16_t* src_ptr, + uint16_t* dst_ptr, + enum FilterMode filtering) { + // Initial source x/y coordinate and step values as 16.16 fixed point. + int x = 0; + int y = 0; + int dx = 0; + int dy = 0; + // TODO(fbarchard): Consider not allocating row buffer for kFilterLinear. + // Allocate a row buffer. + align_buffer_64(row, src_width * 2); + if (!row) + return 1; + + const int max_y = (src_height - 1) << 16; + int j; + void (*ScaleFilterCols)(uint16_t* dst_ptr, const uint16_t* src_ptr, + int dst_width, int x, int dx) = + (src_width >= 32768) ? ScaleFilterCols64_16_C : ScaleFilterCols_16_C; + void (*InterpolateRow)(uint16_t* dst_ptr, const uint16_t* src_ptr, + ptrdiff_t src_stride, int dst_width, + int source_y_fraction) = InterpolateRow_16_C; + ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y, + &dx, &dy); + src_width = Abs(src_width); + + if (y > max_y) { + y = max_y; + } + + for (j = 0; j < dst_height; ++j) { + int yi = y >> 16; + const uint16_t* src = src_ptr + yi * (int64_t)src_stride; + if (filtering == kFilterLinear) { + ScaleFilterCols(dst_ptr, src, dst_width, x, dx); + } else { + int yf = (y >> 8) & 255; + InterpolateRow((uint16_t*)row, src, src_stride, src_width, yf); + ScaleFilterCols(dst_ptr, (uint16_t*)row, dst_width, x, dx); + } + dst_ptr += dst_stride; + y += dy; + if (y > max_y) { + y = max_y; + } + } + free_aligned_buffer_64(row); + return 0; +} + +// Scale up down with bilinear interpolation. +static int ScalePlaneBilinearUp(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint8_t* src_ptr, + uint8_t* dst_ptr, + enum FilterMode filtering) { + int j; + // Initial source x/y coordinate and step values as 16.16 fixed point. + int x = 0; + int y = 0; + int dx = 0; + int dy = 0; + const int max_y = (src_height - 1) << 16; + void (*InterpolateRow)(uint8_t* dst_ptr, const uint8_t* src_ptr, + ptrdiff_t src_stride, int dst_width, + int source_y_fraction) = InterpolateRow_C; + void (*ScaleFilterCols)(uint8_t* dst_ptr, const uint8_t* src_ptr, + int dst_width, int x, int dx) = + filtering ? ScaleFilterCols_C : ScaleCols_C; + ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y, + &dx, &dy); + src_width = Abs(src_width); + + if (filtering && src_width >= 32768) { + ScaleFilterCols = ScaleFilterCols64_C; + } + if (!filtering && src_width * 2 == dst_width && x < 0x8000) { + ScaleFilterCols = ScaleColsUp2_C; + } + + if (y > max_y) { + y = max_y; + } + { + int yi = y >> 16; + const uint8_t* src = src_ptr + yi * (int64_t)src_stride; + + // Allocate 2 row buffers. + const int row_size = (dst_width + 31) & ~31; + align_buffer_64(row, row_size * 2); + if (!row) + return 1; + + uint8_t* rowptr = row; + int rowstride = row_size; + int lasty = yi; + + ScaleFilterCols(rowptr, src, dst_width, x, dx); + if (src_height > 1) { + src += src_stride; + } + ScaleFilterCols(rowptr + rowstride, src, dst_width, x, dx); + if (src_height > 2) { + src += src_stride; + } + + for (j = 0; j < dst_height; ++j) { + yi = y >> 16; + if (yi != lasty) { + if (y > max_y) { + y = max_y; + yi = y >> 16; + src = src_ptr + yi * (int64_t)src_stride; + } + if (yi != lasty) { + ScaleFilterCols(rowptr, src, dst_width, x, dx); + rowptr += rowstride; + rowstride = -rowstride; + lasty = yi; + if ((y + 65536) < max_y) { + src += src_stride; + } + } + } + if (filtering == kFilterLinear) { + InterpolateRow(dst_ptr, rowptr, 0, dst_width, 0); + } else { + int yf = (y >> 8) & 255; + InterpolateRow(dst_ptr, rowptr, rowstride, dst_width, yf); + } + dst_ptr += dst_stride; + y += dy; + } + free_aligned_buffer_64(row); + } + return 0; +} + +// Scale plane, horizontally up by 2 times. +// Uses linear filter horizontally, nearest vertically. +// This is an optimized version for scaling up a plane to 2 times of +// its original width, using linear interpolation. +// This is used to scale U and V planes of I422 to I444. +static void ScalePlaneUp2_Linear(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint8_t* src_ptr, + uint8_t* dst_ptr) { + void (*ScaleRowUp)(const uint8_t* src_ptr, uint8_t* dst_ptr, int dst_width) = + ScaleRowUp2_Linear_Any_C; + int i; + int y; + int dy; + + (void)src_width; + // This function can only scale up by 2 times horizontally. + assert(src_width == ((dst_width + 1) / 2)); + + if (dst_height == 1) { + ScaleRowUp(src_ptr + ((src_height - 1) / 2) * (int64_t)src_stride, dst_ptr, + dst_width); + } else { + dy = FixedDiv(src_height - 1, dst_height - 1); + y = (1 << 15) - 1; + for (i = 0; i < dst_height; ++i) { + ScaleRowUp(src_ptr + (y >> 16) * (int64_t)src_stride, dst_ptr, dst_width); + dst_ptr += dst_stride; + y += dy; + } + } +} + +// Scale plane, up by 2 times. +// This is an optimized version for scaling up a plane to 2 times of +// its original size, using bilinear interpolation. +// This is used to scale U and V planes of I420 to I444. +static void ScalePlaneUp2_Bilinear(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint8_t* src_ptr, + uint8_t* dst_ptr) { + void (*Scale2RowUp)(const uint8_t* src_ptr, ptrdiff_t src_stride, + uint8_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) = + ScaleRowUp2_Bilinear_Any_C; + int x; + + (void)src_width; + // This function can only scale up by 2 times. + assert(src_width == ((dst_width + 1) / 2)); + assert(src_height == ((dst_height + 1) / 2)); + + Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); + dst_ptr += dst_stride; + for (x = 0; x < src_height - 1; ++x) { + Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width); + src_ptr += src_stride; + // TODO(fbarchard): Test performance of writing one row of destination at a + // time. + dst_ptr += 2 * dst_stride; + } + if (!(dst_height & 1)) { + Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); + } +} + +// Scale at most 14 bit plane, horizontally up by 2 times. +// This is an optimized version for scaling up a plane to 2 times of +// its original width, using linear interpolation. +// stride is in count of uint16_t. +// This is used to scale U and V planes of I210 to I410 and I212 to I412. +static void ScalePlaneUp2_12_Linear(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint16_t* src_ptr, + uint16_t* dst_ptr) { + void (*ScaleRowUp)(const uint16_t* src_ptr, uint16_t* dst_ptr, + int dst_width) = ScaleRowUp2_Linear_16_Any_C; + int i; + int y; + int dy; + + (void)src_width; + // This function can only scale up by 2 times horizontally. + assert(src_width == ((dst_width + 1) / 2)); + + if (dst_height == 1) { + ScaleRowUp(src_ptr + ((src_height - 1) / 2) * (int64_t)src_stride, dst_ptr, + dst_width); + } else { + dy = FixedDiv(src_height - 1, dst_height - 1); + y = (1 << 15) - 1; + for (i = 0; i < dst_height; ++i) { + ScaleRowUp(src_ptr + (y >> 16) * (int64_t)src_stride, dst_ptr, dst_width); + dst_ptr += dst_stride; + y += dy; + } + } +} + +// Scale at most 12 bit plane, up by 2 times. +// This is an optimized version for scaling up a plane to 2 times of +// its original size, using bilinear interpolation. +// stride is in count of uint16_t. +// This is used to scale U and V planes of I010 to I410 and I012 to I412. +static void ScalePlaneUp2_12_Bilinear(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint16_t* src_ptr, + uint16_t* dst_ptr) { + void (*Scale2RowUp)(const uint16_t* src_ptr, ptrdiff_t src_stride, + uint16_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) = + ScaleRowUp2_Bilinear_16_Any_C; + int x; + + (void)src_width; + // This function can only scale up by 2 times. + assert(src_width == ((dst_width + 1) / 2)); + assert(src_height == ((dst_height + 1) / 2)); + + Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); + dst_ptr += dst_stride; + for (x = 0; x < src_height - 1; ++x) { + Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width); + src_ptr += src_stride; + dst_ptr += 2 * dst_stride; + } + if (!(dst_height & 1)) { + Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); + } +} + +static void ScalePlaneUp2_16_Linear(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint16_t* src_ptr, + uint16_t* dst_ptr) { + void (*ScaleRowUp)(const uint16_t* src_ptr, uint16_t* dst_ptr, + int dst_width) = ScaleRowUp2_Linear_16_Any_C; + int i; + int y; + int dy; + + (void)src_width; + // This function can only scale up by 2 times horizontally. + assert(src_width == ((dst_width + 1) / 2)); + + if (dst_height == 1) { + ScaleRowUp(src_ptr + ((src_height - 1) / 2) * (int64_t)src_stride, dst_ptr, + dst_width); + } else { + dy = FixedDiv(src_height - 1, dst_height - 1); + y = (1 << 15) - 1; + for (i = 0; i < dst_height; ++i) { + ScaleRowUp(src_ptr + (y >> 16) * (int64_t)src_stride, dst_ptr, dst_width); + dst_ptr += dst_stride; + y += dy; + } + } +} + +static void ScalePlaneUp2_16_Bilinear(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint16_t* src_ptr, + uint16_t* dst_ptr) { + void (*Scale2RowUp)(const uint16_t* src_ptr, ptrdiff_t src_stride, + uint16_t* dst_ptr, ptrdiff_t dst_stride, int dst_width) = + ScaleRowUp2_Bilinear_16_Any_C; + int x; + + (void)src_width; + // This function can only scale up by 2 times. + assert(src_width == ((dst_width + 1) / 2)); + assert(src_height == ((dst_height + 1) / 2)); + + Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); + dst_ptr += dst_stride; + for (x = 0; x < src_height - 1; ++x) { + Scale2RowUp(src_ptr, src_stride, dst_ptr, dst_stride, dst_width); + src_ptr += src_stride; + dst_ptr += 2 * dst_stride; + } + if (!(dst_height & 1)) { + Scale2RowUp(src_ptr, 0, dst_ptr, 0, dst_width); + } +} + +static int ScalePlaneBilinearUp_16(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint16_t* src_ptr, + uint16_t* dst_ptr, + enum FilterMode filtering) { + int j; + // Initial source x/y coordinate and step values as 16.16 fixed point. + int x = 0; + int y = 0; + int dx = 0; + int dy = 0; + const int max_y = (src_height - 1) << 16; + void (*InterpolateRow)(uint16_t* dst_ptr, const uint16_t* src_ptr, + ptrdiff_t src_stride, int dst_width, + int source_y_fraction) = InterpolateRow_16_C; + void (*ScaleFilterCols)(uint16_t* dst_ptr, const uint16_t* src_ptr, + int dst_width, int x, int dx) = + filtering ? ScaleFilterCols_16_C : ScaleCols_16_C; + ScaleSlope(src_width, src_height, dst_width, dst_height, filtering, &x, &y, + &dx, &dy); + src_width = Abs(src_width); + + if (filtering && src_width >= 32768) { + ScaleFilterCols = ScaleFilterCols64_16_C; + } + if (!filtering && src_width * 2 == dst_width && x < 0x8000) { + ScaleFilterCols = ScaleColsUp2_16_C; + } + if (y > max_y) { + y = max_y; + } + { + int yi = y >> 16; + const uint16_t* src = src_ptr + yi * (int64_t)src_stride; + + // Allocate 2 row buffers. + const int row_size = (dst_width + 31) & ~31; + align_buffer_64(row, row_size * 4); + int rowstride = row_size; + int lasty = yi; + uint16_t* rowptr = (uint16_t*)row; + if (!row) + return 1; + + ScaleFilterCols(rowptr, src, dst_width, x, dx); + if (src_height > 1) { + src += src_stride; + } + ScaleFilterCols(rowptr + rowstride, src, dst_width, x, dx); + if (src_height > 2) { + src += src_stride; + } + + for (j = 0; j < dst_height; ++j) { + yi = y >> 16; + if (yi != lasty) { + if (y > max_y) { + y = max_y; + yi = y >> 16; + src = src_ptr + yi * (int64_t)src_stride; + } + if (yi != lasty) { + ScaleFilterCols(rowptr, src, dst_width, x, dx); + rowptr += rowstride; + rowstride = -rowstride; + lasty = yi; + if ((y + 65536) < max_y) { + src += src_stride; + } + } + } + if (filtering == kFilterLinear) { + InterpolateRow(dst_ptr, rowptr, 0, dst_width, 0); + } else { + int yf = (y >> 8) & 255; + InterpolateRow(dst_ptr, rowptr, rowstride, dst_width, yf); + } + dst_ptr += dst_stride; + y += dy; + } + free_aligned_buffer_64(row); + } + return 0; +} + +// Scale Plane to/from any dimensions, without interpolation. +// Fixed point math is used for performance: The upper 16 bits +// of x and dx is the integer part of the source position and +// the lower 16 bits are the fixed decimal part. + +static void ScalePlaneSimple(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint8_t* src_ptr, + uint8_t* dst_ptr) { + int i; + void (*ScaleCols)(uint8_t* dst_ptr, const uint8_t* src_ptr, int dst_width, + int x, int dx) = ScaleCols_C; + // Initial source x/y coordinate and step values as 16.16 fixed point. + int x = 0; + int y = 0; + int dx = 0; + int dy = 0; + ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterNone, &x, &y, + &dx, &dy); + src_width = Abs(src_width); + + if (src_width * 2 == dst_width && x < 0x8000) { + ScaleCols = ScaleColsUp2_C; + } + + for (i = 0; i < dst_height; ++i) { + ScaleCols(dst_ptr, src_ptr + (y >> 16) * (int64_t)src_stride, dst_width, x, + dx); + dst_ptr += dst_stride; + y += dy; + } +} + +static void ScalePlaneSimple_16(int src_width, + int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint16_t* src_ptr, + uint16_t* dst_ptr) { + int i; + void (*ScaleCols)(uint16_t* dst_ptr, const uint16_t* src_ptr, int dst_width, + int x, int dx) = ScaleCols_16_C; + // Initial source x/y coordinate and step values as 16.16 fixed point. + int x = 0; + int y = 0; + int dx = 0; + int dy = 0; + ScaleSlope(src_width, src_height, dst_width, dst_height, kFilterNone, &x, &y, + &dx, &dy); + src_width = Abs(src_width); + + if (src_width * 2 == dst_width && x < 0x8000) { + ScaleCols = ScaleColsUp2_16_C; + } + + for (i = 0; i < dst_height; ++i) { + ScaleCols(dst_ptr, src_ptr + (y >> 16) * (int64_t)src_stride, dst_width, x, + dx); + dst_ptr += dst_stride; + y += dy; + } +} + +// Scale a plane. +// This function dispatches to a specialized scaler based on scale factor. +int ScalePlane(const uint8_t* src, + int src_stride, + int src_width, + int src_height, + uint8_t* dst, + int dst_stride, + int dst_width, + int dst_height, + enum FilterMode filtering) { + // Simplify filtering when possible. + filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height, + filtering); + + // Negative height means invert the image. + if (src_height < 0) { + src_height = -src_height; + src = src + (src_height - 1) * (int64_t)src_stride; + src_stride = -src_stride; + } + // Use specialized scales to improve performance for common resolutions. + // For example, all the 1/2 scalings will use ScalePlaneDown2() + if (dst_width == src_width && dst_height == src_height) { + // Straight copy. + CopyPlane(src, src_stride, dst, dst_stride, dst_width, dst_height); + return 0; + } + if (dst_width == src_width && filtering != kFilterBox) { + int dy = 0; + int y = 0; + // When scaling down, use the center 2 rows to filter. + // When scaling up, last row of destination uses the last 2 source rows. + if (dst_height <= src_height) { + dy = FixedDiv(src_height, dst_height); + y = CENTERSTART(dy, -32768); // Subtract 0.5 (32768) to center filter. + } else if (src_height > 1 && dst_height > 1) { + dy = FixedDiv1(src_height, dst_height); + } + // Arbitrary scale vertically, but unscaled horizontally. + ScalePlaneVertical(src_height, dst_width, dst_height, src_stride, + dst_stride, src, dst, 0, y, dy, /*bpp=*/1, filtering); + return 0; + } + if (filtering == kFilterBox && dst_height * 2 < src_height) { + return ScalePlaneBox(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst); + } + if ((dst_width + 1) / 2 == src_width && filtering == kFilterLinear) { + ScalePlaneUp2_Linear(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst); + return 0; + } + if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width && + (filtering == kFilterBilinear || filtering == kFilterBox)) { + ScalePlaneUp2_Bilinear(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst); + return 0; + } + if (filtering && dst_height > src_height) { + return ScalePlaneBilinearUp(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst, filtering); + } + if (filtering) { + return ScalePlaneBilinearDown(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst, filtering); + } + ScalePlaneSimple(src_width, src_height, dst_width, dst_height, src_stride, + dst_stride, src, dst); + return 0; +} + +int ScalePlane_16(const uint16_t* src, + int src_stride, + int src_width, + int src_height, + uint16_t* dst, + int dst_stride, + int dst_width, + int dst_height, + enum FilterMode filtering) { + // Simplify filtering when possible. + filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height, + filtering); + + // Negative height means invert the image. + if (src_height < 0) { + src_height = -src_height; + src = src + (src_height - 1) * (int64_t)src_stride; + src_stride = -src_stride; + } + // Use specialized scales to improve performance for common resolutions. + // For example, all the 1/2 scalings will use ScalePlaneDown2() + if (dst_width == src_width && dst_height == src_height) { + // Straight copy. + CopyPlane_16(src, src_stride, dst, dst_stride, dst_width, dst_height); + return 0; + } + if (dst_width == src_width && filtering != kFilterBox) { + int dy = 0; + int y = 0; + // When scaling down, use the center 2 rows to filter. + // When scaling up, last row of destination uses the last 2 source rows. + if (dst_height <= src_height) { + dy = FixedDiv(src_height, dst_height); + y = CENTERSTART(dy, -32768); // Subtract 0.5 (32768) to center filter. + // When scaling up, ensure the last row of destination uses the last + // source. Avoid divide by zero for dst_height but will do no scaling + // later. + } else if (src_height > 1 && dst_height > 1) { + dy = FixedDiv1(src_height, dst_height); + } + // Arbitrary scale vertically, but unscaled horizontally. + ScalePlaneVertical_16(src_height, dst_width, dst_height, src_stride, + dst_stride, src, dst, 0, y, dy, /*bpp=*/1, filtering); + return 0; + } + if (filtering == kFilterBox && dst_height * 2 < src_height) { + return ScalePlaneBox_16(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst); + } + if ((dst_width + 1) / 2 == src_width && filtering == kFilterLinear) { + ScalePlaneUp2_16_Linear(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst); + return 0; + } + if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width && + (filtering == kFilterBilinear || filtering == kFilterBox)) { + ScalePlaneUp2_16_Bilinear(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst); + return 0; + } + if (filtering && dst_height > src_height) { + return ScalePlaneBilinearUp_16(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst, filtering); + } + if (filtering) { + return ScalePlaneBilinearDown_16(src_width, src_height, dst_width, + dst_height, src_stride, dst_stride, src, + dst, filtering); + } + ScalePlaneSimple_16(src_width, src_height, dst_width, dst_height, src_stride, + dst_stride, src, dst); + return 0; +} + +int ScalePlane_12(const uint16_t* src, + int src_stride, + int src_width, + int src_height, + uint16_t* dst, + int dst_stride, + int dst_width, + int dst_height, + enum FilterMode filtering) { + // Simplify filtering when possible. + filtering = ScaleFilterReduce(src_width, src_height, dst_width, dst_height, + filtering); + + // Negative height means invert the image. + if (src_height < 0) { + src_height = -src_height; + src = src + (src_height - 1) * (int64_t)src_stride; + src_stride = -src_stride; + } + + if ((dst_width + 1) / 2 == src_width && filtering == kFilterLinear) { + ScalePlaneUp2_12_Linear(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst); + return 0; + } + if ((dst_height + 1) / 2 == src_height && (dst_width + 1) / 2 == src_width && + (filtering == kFilterBilinear || filtering == kFilterBox)) { + ScalePlaneUp2_12_Bilinear(src_width, src_height, dst_width, dst_height, + src_stride, dst_stride, src, dst); + return 0; + } + + return ScalePlane_16(src, src_stride, src_width, src_height, dst, dst_stride, + dst_width, dst_height, filtering); +} diff --git a/3rdparty/libavif/third_party/libyuv/source/scale_any.c b/3rdparty/libavif/third_party/libyuv/source/scale_any.c new file mode 100644 index 0000000..c3aa370 --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/source/scale_any.c @@ -0,0 +1,85 @@ +/* + * Copyright 2015 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include // For memset/memcpy + +#include "libyuv/scale.h" +#include "libyuv/scale_row.h" + +#include "libyuv/basic_types.h" + +// Scale up horizontally 2 times using linear filter. +#define SUH2LANY(NAME, SIMD, C, MASK, PTYPE) \ + void NAME(const PTYPE* src_ptr, PTYPE* dst_ptr, int dst_width) { \ + int work_width = (dst_width - 1) & ~1; \ + int r = work_width & MASK; \ + int n = work_width & ~MASK; \ + dst_ptr[0] = src_ptr[0]; \ + if (work_width > 0) { \ + if (n != 0) { \ + SIMD(src_ptr, dst_ptr + 1, n); \ + } \ + C(src_ptr + (n / 2), dst_ptr + n + 1, r); \ + } \ + dst_ptr[dst_width - 1] = src_ptr[(dst_width - 1) / 2]; \ + } + +// Even the C versions need to be wrapped, because boundary pixels have to +// be handled differently + +SUH2LANY(ScaleRowUp2_Linear_Any_C, + ScaleRowUp2_Linear_C, + ScaleRowUp2_Linear_C, + 0, + uint8_t) + +SUH2LANY(ScaleRowUp2_Linear_16_Any_C, + ScaleRowUp2_Linear_16_C, + ScaleRowUp2_Linear_16_C, + 0, + uint16_t) + +// Scale up 2 times using bilinear filter. +// This function produces 2 rows at a time. +#define SU2BLANY(NAME, SIMD, C, MASK, PTYPE) \ + void NAME(const PTYPE* src_ptr, ptrdiff_t src_stride, PTYPE* dst_ptr, \ + ptrdiff_t dst_stride, int dst_width) { \ + int work_width = (dst_width - 1) & ~1; \ + int r = work_width & MASK; \ + int n = work_width & ~MASK; \ + const PTYPE* sa = src_ptr; \ + const PTYPE* sb = src_ptr + src_stride; \ + PTYPE* da = dst_ptr; \ + PTYPE* db = dst_ptr + dst_stride; \ + da[0] = (3 * sa[0] + sb[0] + 2) >> 2; \ + db[0] = (sa[0] + 3 * sb[0] + 2) >> 2; \ + if (work_width > 0) { \ + if (n != 0) { \ + SIMD(sa, sb - sa, da + 1, db - da, n); \ + } \ + C(sa + (n / 2), sb - sa, da + n + 1, db - da, r); \ + } \ + da[dst_width - 1] = \ + (3 * sa[(dst_width - 1) / 2] + sb[(dst_width - 1) / 2] + 2) >> 2; \ + db[dst_width - 1] = \ + (sa[(dst_width - 1) / 2] + 3 * sb[(dst_width - 1) / 2] + 2) >> 2; \ + } + +SU2BLANY(ScaleRowUp2_Bilinear_Any_C, + ScaleRowUp2_Bilinear_C, + ScaleRowUp2_Bilinear_C, + 0, + uint8_t) + +SU2BLANY(ScaleRowUp2_Bilinear_16_Any_C, + ScaleRowUp2_Bilinear_16_C, + ScaleRowUp2_Bilinear_16_C, + 0, + uint16_t) diff --git a/3rdparty/libavif/third_party/libyuv/source/scale_common.c b/3rdparty/libavif/third_party/libyuv/source/scale_common.c new file mode 100644 index 0000000..9751ed7 --- /dev/null +++ b/3rdparty/libavif/third_party/libyuv/source/scale_common.c @@ -0,0 +1,558 @@ +/* + * Copyright 2013 The LibYuv Project Authors. All rights reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "libyuv/scale.h" + +#include +#include + +#include "libyuv/planar_functions.h" // For CopyARGB +#include "libyuv/row.h" +#include "libyuv/scale_row.h" + +#ifndef AVIF_LIBYUV_ABS_DEFINED +#define AVIF_LIBYUV_ABS_DEFINED +static __inline int Abs(int v) { + return v >= 0 ? v : -v; +} +#endif + +// Sample position: (O is src sample position, X is dst sample position) +// +// v dst_ptr at here v stop at here +// X O X X O X X O X X O X X O X +// ^ src_ptr at here +void ScaleRowUp2_Linear_C(const uint8_t* src_ptr, + uint8_t* dst_ptr, + int dst_width) { + int src_width = dst_width >> 1; + int x; + assert((dst_width % 2 == 0) && (dst_width >= 0)); + for (x = 0; x < src_width; ++x) { + dst_ptr[2 * x + 0] = (src_ptr[x + 0] * 3 + src_ptr[x + 1] * 1 + 2) >> 2; + dst_ptr[2 * x + 1] = (src_ptr[x + 0] * 1 + src_ptr[x + 1] * 3 + 2) >> 2; + } +} + +// Sample position: (O is src sample position, X is dst sample position) +// +// src_ptr at here +// X v X X X X X X X X X +// O O O O O +// X X X X X X X X X X +// ^ dst_ptr at here ^ stop at here +// X X X X X X X X X X +// O O O O O +// X X X X X X X X X X +void ScaleRowUp2_Bilinear_C(const uint8_t* src_ptr, + ptrdiff_t src_stride, + uint8_t* dst_ptr, + ptrdiff_t dst_stride, + int dst_width) { + const uint8_t* s = src_ptr; + const uint8_t* t = src_ptr + src_stride; + uint8_t* d = dst_ptr; + uint8_t* e = dst_ptr + dst_stride; + int src_width = dst_width >> 1; + int x; + assert((dst_width % 2 == 0) && (dst_width >= 0)); + for (x = 0; x < src_width; ++x) { + d[2 * x + 0] = + (s[x + 0] * 9 + s[x + 1] * 3 + t[x + 0] * 3 + t[x + 1] * 1 + 8) >> 4; + d[2 * x + 1] = + (s[x + 0] * 3 + s[x + 1] * 9 + t[x + 0] * 1 + t[x + 1] * 3 + 8) >> 4; + e[2 * x + 0] = + (s[x + 0] * 3 + s[x + 1] * 1 + t[x + 0] * 9 + t[x + 1] * 3 + 8) >> 4; + e[2 * x + 1] = + (s[x + 0] * 1 + s[x + 1] * 3 + t[x + 0] * 3 + t[x + 1] * 9 + 8) >> 4; + } +} + +// Only suitable for at most 14 bit range. +void ScaleRowUp2_Linear_16_C(const uint16_t* src_ptr, + uint16_t* dst_ptr, + int dst_width) { + int src_width = dst_width >> 1; + int x; + assert((dst_width % 2 == 0) && (dst_width >= 0)); + for (x = 0; x < src_width; ++x) { + dst_ptr[2 * x + 0] = (src_ptr[x + 0] * 3 + src_ptr[x + 1] * 1 + 2) >> 2; + dst_ptr[2 * x + 1] = (src_ptr[x + 0] * 1 + src_ptr[x + 1] * 3 + 2) >> 2; + } +} + +// Only suitable for at most 12bit range. +void ScaleRowUp2_Bilinear_16_C(const uint16_t* src_ptr, + ptrdiff_t src_stride, + uint16_t* dst_ptr, + ptrdiff_t dst_stride, + int dst_width) { + const uint16_t* s = src_ptr; + const uint16_t* t = src_ptr + src_stride; + uint16_t* d = dst_ptr; + uint16_t* e = dst_ptr + dst_stride; + int src_width = dst_width >> 1; + int x; + assert((dst_width % 2 == 0) && (dst_width >= 0)); + for (x = 0; x < src_width; ++x) { + d[2 * x + 0] = + (s[x + 0] * 9 + s[x + 1] * 3 + t[x + 0] * 3 + t[x + 1] * 1 + 8) >> 4; + d[2 * x + 1] = + (s[x + 0] * 3 + s[x + 1] * 9 + t[x + 0] * 1 + t[x + 1] * 3 + 8) >> 4; + e[2 * x + 0] = + (s[x + 0] * 3 + s[x + 1] * 1 + t[x + 0] * 9 + t[x + 1] * 3 + 8) >> 4; + e[2 * x + 1] = + (s[x + 0] * 1 + s[x + 1] * 3 + t[x + 0] * 3 + t[x + 1] * 9 + 8) >> 4; + } +} + +// Scales a single row of pixels using point sampling. +void ScaleCols_C(uint8_t* dst_ptr, + const uint8_t* src_ptr, + int dst_width, + int x, + int dx) { + int j; + for (j = 0; j < dst_width - 1; j += 2) { + dst_ptr[0] = src_ptr[x >> 16]; + x += dx; + dst_ptr[1] = src_ptr[x >> 16]; + x += dx; + dst_ptr += 2; + } + if (dst_width & 1) { + dst_ptr[0] = src_ptr[x >> 16]; + } +} + +void ScaleCols_16_C(uint16_t* dst_ptr, + const uint16_t* src_ptr, + int dst_width, + int x, + int dx) { + int j; + for (j = 0; j < dst_width - 1; j += 2) { + dst_ptr[0] = src_ptr[x >> 16]; + x += dx; + dst_ptr[1] = src_ptr[x >> 16]; + x += dx; + dst_ptr += 2; + } + if (dst_width & 1) { + dst_ptr[0] = src_ptr[x >> 16]; + } +} + +// Scales a single row of pixels up by 2x using point sampling. +void ScaleColsUp2_C(uint8_t* dst_ptr, + const uint8_t* src_ptr, + int dst_width, + int x, + int dx) { + int j; + (void)x; + (void)dx; + for (j = 0; j < dst_width - 1; j += 2) { + dst_ptr[1] = dst_ptr[0] = src_ptr[0]; + src_ptr += 1; + dst_ptr += 2; + } + if (dst_width & 1) { + dst_ptr[0] = src_ptr[0]; + } +} + +void ScaleColsUp2_16_C(uint16_t* dst_ptr, + const uint16_t* src_ptr, + int dst_width, + int x, + int dx) { + int j; + (void)x; + (void)dx; + for (j = 0; j < dst_width - 1; j += 2) { + dst_ptr[1] = dst_ptr[0] = src_ptr[0]; + src_ptr += 1; + dst_ptr += 2; + } + if (dst_width & 1) { + dst_ptr[0] = src_ptr[0]; + } +} + +// (1-f)a + fb can be replaced with a + f(b-a) +#if defined(__arm__) || defined(__aarch64__) +#define BLENDER(a, b, f) \ + (uint8_t)((int)(a) + ((((int)((f)) * ((int)(b) - (int)(a))) + 0x8000) >> 16)) +#else +// Intel uses 7 bit math with rounding. +#define BLENDER(a, b, f) \ + (uint8_t)((int)(a) + (((int)((f) >> 9) * ((int)(b) - (int)(a)) + 0x40) >> 7)) +#endif + +void ScaleFilterCols_C(uint8_t* dst_ptr, + const uint8_t* src_ptr, + int dst_width, + int x, + int dx) { + int j; + for (j = 0; j < dst_width - 1; j += 2) { + int xi = x >> 16; + int a = src_ptr[xi]; + int b = src_ptr[xi + 1]; + dst_ptr[0] = BLENDER(a, b, x & 0xffff); + x += dx; + xi = x >> 16; + a = src_ptr[xi]; + b = src_ptr[xi + 1]; + dst_ptr[1] = BLENDER(a, b, x & 0xffff); + x += dx; + dst_ptr += 2; + } + if (dst_width & 1) { + int xi = x >> 16; + int a = src_ptr[xi]; + int b = src_ptr[xi + 1]; + dst_ptr[0] = BLENDER(a, b, x & 0xffff); + } +} + +void ScaleFilterCols64_C(uint8_t* dst_ptr, + const uint8_t* src_ptr, + int dst_width, + int x32, + int dx) { + int64_t x = (int64_t)(x32); + int j; + for (j = 0; j < dst_width - 1; j += 2) { + int64_t xi = x >> 16; + int a = src_ptr[xi]; + int b = src_ptr[xi + 1]; + dst_ptr[0] = BLENDER(a, b, x & 0xffff); + x += dx; + xi = x >> 16; + a = src_ptr[xi]; + b = src_ptr[xi + 1]; + dst_ptr[1] = BLENDER(a, b, x & 0xffff); + x += dx; + dst_ptr += 2; + } + if (dst_width & 1) { + int64_t xi = x >> 16; + int a = src_ptr[xi]; + int b = src_ptr[xi + 1]; + dst_ptr[0] = BLENDER(a, b, x & 0xffff); + } +} +#undef BLENDER + +// Same as 8 bit arm blender but return is cast to uint16_t +#define BLENDER(a, b, f) \ + (uint16_t)( \ + (int)(a) + \ + (int)((((int64_t)((f)) * ((int64_t)(b) - (int)(a))) + 0x8000) >> 16)) + +void ScaleFilterCols_16_C(uint16_t* dst_ptr, + const uint16_t* src_ptr, + int dst_width, + int x, + int dx) { + int j; + for (j = 0; j < dst_width - 1; j += 2) { + int xi = x >> 16; + int a = src_ptr[xi]; + int b = src_ptr[xi + 1]; + dst_ptr[0] = BLENDER(a, b, x & 0xffff); + x += dx; + xi = x >> 16; + a = src_ptr[xi]; + b = src_ptr[xi + 1]; + dst_ptr[1] = BLENDER(a, b, x & 0xffff); + x += dx; + dst_ptr += 2; + } + if (dst_width & 1) { + int xi = x >> 16; + int a = src_ptr[xi]; + int b = src_ptr[xi + 1]; + dst_ptr[0] = BLENDER(a, b, x & 0xffff); + } +} + +void ScaleFilterCols64_16_C(uint16_t* dst_ptr, + const uint16_t* src_ptr, + int dst_width, + int x32, + int dx) { + int64_t x = (int64_t)(x32); + int j; + for (j = 0; j < dst_width - 1; j += 2) { + int64_t xi = x >> 16; + int a = src_ptr[xi]; + int b = src_ptr[xi + 1]; + dst_ptr[0] = BLENDER(a, b, x & 0xffff); + x += dx; + xi = x >> 16; + a = src_ptr[xi]; + b = src_ptr[xi + 1]; + dst_ptr[1] = BLENDER(a, b, x & 0xffff); + x += dx; + dst_ptr += 2; + } + if (dst_width & 1) { + int64_t xi = x >> 16; + int a = src_ptr[xi]; + int b = src_ptr[xi + 1]; + dst_ptr[0] = BLENDER(a, b, x & 0xffff); + } +} +#undef BLENDER + + +void ScaleAddRow_C(const uint8_t* src_ptr, uint16_t* dst_ptr, int src_width) { + int x; + assert(src_width > 0); + for (x = 0; x < src_width - 1; x += 2) { + dst_ptr[0] += src_ptr[0]; + dst_ptr[1] += src_ptr[1]; + src_ptr += 2; + dst_ptr += 2; + } + if (src_width & 1) { + dst_ptr[0] += src_ptr[0]; + } +} + +void ScaleAddRow_16_C(const uint16_t* src_ptr, + uint32_t* dst_ptr, + int src_width) { + int x; + assert(src_width > 0); + for (x = 0; x < src_width - 1; x += 2) { + dst_ptr[0] += src_ptr[0]; + dst_ptr[1] += src_ptr[1]; + src_ptr += 2; + dst_ptr += 2; + } + if (src_width & 1) { + dst_ptr[0] += src_ptr[0]; + } +} + + +// Scale plane vertically with bilinear interpolation. +void ScalePlaneVertical(int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint8_t* src_argb, + uint8_t* dst_argb, + int x, + int y, + int dy, + int bpp, // bytes per pixel. 4 for ARGB. + enum FilterMode filtering) { + // TODO(fbarchard): Allow higher bpp. + int dst_width_bytes = dst_width * bpp; + void (*InterpolateRow)(uint8_t* dst_argb, const uint8_t* src_argb, + ptrdiff_t src_stride, int dst_width, + int source_y_fraction) = InterpolateRow_C; + const int max_y = (src_height > 1) ? ((src_height - 1) << 16) - 1 : 0; + int j; + assert(bpp >= 1 && bpp <= 4); + assert(src_height != 0); + assert(dst_width > 0); + assert(dst_height > 0); + src_argb += (x >> 16) * bpp; + + for (j = 0; j < dst_height; ++j) { + int yi; + int yf; + if (y > max_y) { + y = max_y; + } + yi = y >> 16; + yf = filtering ? ((y >> 8) & 255) : 0; + InterpolateRow(dst_argb, src_argb + yi * src_stride, src_stride, + dst_width_bytes, yf); + dst_argb += dst_stride; + y += dy; + } +} + +void ScalePlaneVertical_16(int src_height, + int dst_width, + int dst_height, + int src_stride, + int dst_stride, + const uint16_t* src_argb, + uint16_t* dst_argb, + int x, + int y, + int dy, + int wpp, /* words per pixel. normally 1 */ + enum FilterMode filtering) { + // TODO(fbarchard): Allow higher wpp. + int dst_width_words = dst_width * wpp; + void (*InterpolateRow)(uint16_t* dst_argb, const uint16_t* src_argb, + ptrdiff_t src_stride, int dst_width, + int source_y_fraction) = InterpolateRow_16_C; + const int max_y = (src_height > 1) ? ((src_height - 1) << 16) - 1 : 0; + int j; + assert(wpp >= 1 && wpp <= 2); + assert(src_height != 0); + assert(dst_width > 0); + assert(dst_height > 0); + src_argb += (x >> 16) * wpp; + for (j = 0; j < dst_height; ++j) { + int yi; + int yf; + if (y > max_y) { + y = max_y; + } + yi = y >> 16; + yf = filtering ? ((y >> 8) & 255) : 0; + InterpolateRow(dst_argb, src_argb + yi * src_stride, src_stride, + dst_width_words, yf); + dst_argb += dst_stride; + y += dy; + } +} + +// Simplify the filtering based on scale factors. +enum FilterMode ScaleFilterReduce(int src_width, + int src_height, + int dst_width, + int dst_height, + enum FilterMode filtering) { + if (src_width < 0) { + src_width = -src_width; + } + if (src_height < 0) { + src_height = -src_height; + } + if (filtering == kFilterBox) { + // If scaling either axis to 0.5 or larger, switch from Box to Bilinear. + if (dst_width * 2 >= src_width || dst_height * 2 >= src_height) { + filtering = kFilterBilinear; + } + } + if (filtering == kFilterBilinear) { + if (src_height == 1) { + filtering = kFilterLinear; + } + // TODO(fbarchard): Detect any odd scale factor and reduce to Linear. + if (dst_height == src_height || dst_height * 3 == src_height) { + filtering = kFilterLinear; + } + // TODO(fbarchard): Remove 1 pixel wide filter restriction, which is to + // avoid reading 2 pixels horizontally that causes memory exception. + if (src_width == 1) { + filtering = kFilterNone; + } + } + if (filtering == kFilterLinear) { + if (src_width == 1) { + filtering = kFilterNone; + } + // TODO(fbarchard): Detect any odd scale factor and reduce to None. + if (dst_width == src_width || dst_width * 3 == src_width) { + filtering = kFilterNone; + } + } + return filtering; +} + +// Divide num by div and return as 16.16 fixed point result. +int FixedDiv_C(int num, int div) { + return (int)(((int64_t)(num) << 16) / div); +} + +// Divide num - 1 by div - 1 and return as 16.16 fixed point result. +int FixedDiv1_C(int num, int div) { + return (int)((((int64_t)(num) << 16) - 0x00010001) / (div - 1)); +} + +#define CENTERSTART(dx, s) (dx < 0) ? -((-dx >> 1) + s) : ((dx >> 1) + s) + +// Compute slope values for stepping. +void ScaleSlope(int src_width, + int src_height, + int dst_width, + int dst_height, + enum FilterMode filtering, + int* x, + int* y, + int* dx, + int* dy) { + assert(x != NULL); + assert(y != NULL); + assert(dx != NULL); + assert(dy != NULL); + assert(src_width != 0); + assert(src_height != 0); + assert(dst_width > 0); + assert(dst_height > 0); + // Check for 1 pixel and avoid FixedDiv overflow. + if (dst_width == 1 && src_width >= 32768) { + dst_width = src_width; + } + if (dst_height == 1 && src_height >= 32768) { + dst_height = src_height; + } + if (filtering == kFilterBox) { + // Scale step for point sampling duplicates all pixels equally. + *dx = FixedDiv(Abs(src_width), dst_width); + *dy = FixedDiv(src_height, dst_height); + *x = 0; + *y = 0; + } else if (filtering == kFilterBilinear) { + // Scale step for bilinear sampling renders last pixel once for upsample. + if (dst_width <= Abs(src_width)) { + *dx = FixedDiv(Abs(src_width), dst_width); + *x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter. + } else if (src_width > 1 && dst_width > 1) { + *dx = FixedDiv1(Abs(src_width), dst_width); + *x = 0; + } + if (dst_height <= src_height) { + *dy = FixedDiv(src_height, dst_height); + *y = CENTERSTART(*dy, -32768); // Subtract 0.5 (32768) to center filter. + } else if (src_height > 1 && dst_height > 1) { + *dy = FixedDiv1(src_height, dst_height); + *y = 0; + } + } else if (filtering == kFilterLinear) { + // Scale step for bilinear sampling renders last pixel once for upsample. + if (dst_width <= Abs(src_width)) { + *dx = FixedDiv(Abs(src_width), dst_width); + *x = CENTERSTART(*dx, -32768); // Subtract 0.5 (32768) to center filter. + } else if (src_width > 1 && dst_width > 1) { + *dx = FixedDiv1(Abs(src_width), dst_width); + *x = 0; + } + *dy = FixedDiv(src_height, dst_height); + *y = *dy >> 1; + } else { + // Scale step for point sampling duplicates all pixels equally. + *dx = FixedDiv(Abs(src_width), dst_width); + *dy = FixedDiv(src_height, dst_height); + *x = CENTERSTART(*dx, 0); + *y = CENTERSTART(*dy, 0); + } + // Negative src_width means horizontally mirror. + if (src_width < 0) { + *x += (dst_width - 1) * *dx; + *dx = -*dx; + // src_width = -src_width; // Caller must do this. + } +} +#undef CENTERSTART diff --git a/include/bimg/bimg.h b/include/bimg/bimg.h index def5b41..717588c 100644 --- a/include/bimg/bimg.h +++ b/include/bimg/bimg.h @@ -186,7 +186,6 @@ namespace bimg Dds, //!< DirectDraw Surface. Exr, //!< OpenEXR. Gif, //!< Graphics Interchange Format. - Gnf, //!< GNF (PlayStation). Hdr, //!< Radiance RGBE. Heif, //!< High Efficiency Image File Format. Jpeg, //!< JPEG. @@ -676,14 +675,6 @@ namespace bimg , bx::Error* _err ); - /// - ImageContainer* imageParseGnf( - bx::AllocatorI* _allocator - , const void* _src - , uint32_t _size - , bx::Error* _err - ); - /// void imageDecodeToR8( bx::AllocatorI* _allocator diff --git a/include/bimg/decode.h b/include/bimg/decode.h index a8952fb..c7d04a2 100644 --- a/include/bimg/decode.h +++ b/include/bimg/decode.h @@ -28,6 +28,10 @@ namespace bimg , bx::Error* _err = NULL ); + /// Returns a NULL-terminated list of the lower-case file name extensions for + /// the image formats that are compiled into this build. + const char* const* getSupportedExt(); + } // namespace bimg #endif // BIMG_DECODE_H_HEADER_GUARD diff --git a/scripts/bimg.lua b/scripts/bimg.lua index 790cdd7..1bd0625 100644 --- a/scripts/bimg.lua +++ b/scripts/bimg.lua @@ -14,7 +14,6 @@ project "bimg" files { path.join(BIMG_DIR, "include/**"), path.join(BIMG_DIR, "src/image.*"), - path.join(BIMG_DIR, "src/image_gnf.cpp"), path.join(BIMG_DIR, "3rdparty/astc-encoder/source/**.cpp"), path.join(BIMG_DIR, "3rdparty/astc-encoder/source/**.h"), diff --git a/scripts/bimg_decode.lua b/scripts/bimg_decode.lua index afd5a08..2315672 100644 --- a/scripts/bimg_decode.lua +++ b/scripts/bimg_decode.lua @@ -33,8 +33,37 @@ project "bimg_decode" } end + defines { + "AVIF_CODEC_DAV1D", + } + + includedirs { + path.join(BIMG_DIR, "3rdparty/libavif"), + path.join(BIMG_DIR, "3rdparty/libavif/include"), + path.join(BIMG_DIR, "3rdparty/libavif/third_party/libyuv/include"), + path.join(BIMG_DIR, "3rdparty/dav1d"), + path.join(BIMG_DIR, "3rdparty/dav1d/include"), + } + + files { + path.join(BIMG_DIR, "3rdparty/dav1d/dav1d-amalgamated.c"), + path.join(BIMG_DIR, "3rdparty/dav1d/dav1d-bitdepth-8.c"), + path.join(BIMG_DIR, "3rdparty/dav1d/dav1d-bitdepth-16.c"), + path.join(BIMG_DIR, "3rdparty/libavif/libavif-amalgamated.c"), + } + using_bx() + configuration { "vs*" } + includedirs { + path.join(BIMG_DIR, "3rdparty/dav1d/include/compat/msvc"), + } + + configuration { "gmake or xcode*" } + buildoptions_c { + "-std=c11", + } + configuration { "linux-*" } buildoptions { "-fPIC", diff --git a/src/bimg_p.h b/src/bimg_p.h index 658ba4c..0f0c94d 100644 --- a/src/bimg_p.h +++ b/src/bimg_p.h @@ -52,7 +52,6 @@ #include "config.h" #define BIMG_CHUNK_MAGIC_TEX BX_MAKEFOURCC('T', 'E', 'X', 0x0) -#define BIMG_CHUNK_MAGIC_GNF BX_MAKEFOURCC('G', 'N', 'F', ' ') BX_ERROR_RESULT(BIMG_ERROR, BX_MAKEFOURCC('b', 'i', 'm', 'g') ); @@ -116,13 +115,6 @@ namespace bimg , uint32_t _srcPitch ); - /// - bool imageParseGnf( - ImageContainer& _imageContainer - , bx::ReaderSeekerI* _reader - , bx::Error* _err - ); - /// bool imageParseKtx2( ImageContainer& _imageContainer diff --git a/src/config.h b/src/config.h index 2bf9932..6a835a8 100644 --- a/src/config.h +++ b/src/config.h @@ -66,6 +66,12 @@ # define BIMG_CONFIG_PARSE_ENABLE 1 #endif // BIMG_CONFIG_PARSE_ENABLE +/// AVIF (AV1 Image File Format) - image format based on AV1. +/// +#ifndef BIMG_CONFIG_PARSE_AVIF +# define BIMG_CONFIG_PARSE_AVIF BIMG_CONFIG_PARSE_ENABLE +#endif // BIMG_CONFIG_PARSE_AVIF + /// BMP (Windows Bitmap) - uncompressed raster image format. /// #ifndef BIMG_CONFIG_PARSE_BMP @@ -90,7 +96,7 @@ # define BIMG_CONFIG_PARSE_HDR BIMG_CONFIG_PARSE_ENABLE #endif // BIMG_CONFIG_PARSE_HDR -/// HEIF (High Efficiency Image File Format) - modern image format based on HEVC. +/// HEIF (High Efficiency Image File Format) - image format based on HEVC. /// #ifndef BIMG_CONFIG_PARSE_HEIF # define BIMG_CONFIG_PARSE_HEIF 0 diff --git a/src/image.cpp b/src/image.cpp index c286696..23a9fe5 100644 --- a/src/image.cpp +++ b/src/image.cpp @@ -316,7 +316,6 @@ namespace bimg "DDS", // Dds "EXR", // Exr "GIF", // Gif - "GNF", // Gnf "HDR", // Hdr "HEIF", // Heif "JPEG", // Jpeg @@ -5693,10 +5692,6 @@ namespace bimg { return imageParsePvr3(_imageContainer, _reader, _err); } - else if (BIMG_CHUNK_MAGIC_GNF == magic) - { - return imageParseGnf(_imageContainer, _reader, _err); - } else if (BIMG_CHUNK_MAGIC_TEX == magic) { TextureCreate tc; diff --git a/src/image_decode.cpp b/src/image_decode.cpp index d2233c3..767c294 100644 --- a/src/image_decode.cpp +++ b/src/image_decode.cpp @@ -62,6 +62,10 @@ void lodepng_free(void* _ptr) # include #endif // BIMG_CONFIG_PARSE_HEIF +#if BIMG_CONFIG_PARSE_AVIF +# include +#endif // BIMG_CONFIG_PARSE_AVIF + #if BIMG_CONFIG_PARSE_WEBP BX_PRAGMA_DIAGNOSTIC_PUSH(); BX_PRAGMA_DIAGNOSTIC_IGNORED_CLANG_GCC("-Wunused-function") @@ -1040,8 +1044,7 @@ namespace bimg #endif // BIMG_CONFIG_PARSE_JPEG } -#if BIMG_CONFIG_PARSE_HEIF - static ImageParser::Enum imageHeifFormat(const void* _data, uint32_t _size) + static bool imageIsAvifBrand(const void* _data, uint32_t _size) { const uint8_t* data = (const uint8_t*)_data; @@ -1059,13 +1062,12 @@ namespace bimg if (0 == bx::memCmp(data + offset, "avif", 4) || 0 == bx::memCmp(data + offset, "avis", 4) ) { - return ImageParser::Avif; + return true; } } - return ImageParser::Heif; + return false; } -#endif // BIMG_CONFIG_PARSE_HEIF static ImageContainer* imageParseLibHeif(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, bx::Error* _err) { @@ -1079,6 +1081,11 @@ namespace bimg return NULL; } + if (imageIsAvifBrand(_data, _size) ) + { + return NULL; + } + #if BIMG_CONFIG_PARSE_HEIF heif_context* ctx = heif_context_alloc(); @@ -1115,7 +1122,7 @@ namespace bimg if (NULL != output) { bx::memCopy(output->m_data, dstStride, data, srcStride, dstStride, height); - output->m_parser = imageHeifFormat(_data, _size); + output->m_parser = ImageParser::Heif; } } @@ -1133,6 +1140,99 @@ namespace bimg #endif // BIMG_CONFIG_PARSE_HEIF } + static ImageContainer* imageParseLibAvif(bx::AllocatorI* _allocator, const void* _data, uint32_t _size, bx::Error* _err) + { + BX_ERROR_SCOPE(_err); + + static uint8_t ftypMagic[] = { 0x66, 0x74, 0x79, 0x70 }; // "ftyp" at offset 4 + + if (12 > _size + || 0 != bx::memCmp( (const uint8_t*)_data + 4, ftypMagic, sizeof(ftypMagic) ) ) + { + return NULL; + } + + if (!imageIsAvifBrand(_data, _size) ) + { + return NULL; + } + +#if BIMG_CONFIG_PARSE_AVIF + avifDecoder* decoder = avifDecoderCreate(); + if (NULL == decoder) + { + BX_ERROR_SET(_err, BIMG_ERROR, "AVIF: Failed to create decoder."); + return NULL; + } + + avifResult result = avifDecoderSetIOMemory(decoder, (const uint8_t*)_data, _size); + if (AVIF_RESULT_OK == result) + { + result = avifDecoderParse(decoder); + } + if (AVIF_RESULT_OK == result) + { + result = avifDecoderNextImage(decoder); + } + + if (AVIF_RESULT_OK != result) + { + avifDecoderDestroy(decoder); + BX_ERROR_SET(_err, BIMG_ERROR, "AVIF: Failed to decode image."); + return NULL; + } + + const uint32_t width = decoder->image->width; + const uint32_t height = decoder->image->height; + const bool hasAlpha = 0 != decoder->alphaPresent; + + ImageContainer* output = imageAlloc(_allocator + , bimg::TextureFormat::RGBA8 + , width + , height + , 0 + , 1 + , false + , false + , NULL + ); + + if (NULL == output) + { + avifDecoderDestroy(decoder); + BX_ERROR_SET(_err, BIMG_ERROR, "AVIF: Unsupported dimensions."); + return NULL; + } + + avifRGBImage rgb; + avifRGBImageSetDefaults(&rgb, decoder->image); + rgb.format = AVIF_RGB_FORMAT_RGBA; + rgb.depth = 8; + rgb.pixels = (uint8_t*)output->m_data; + rgb.rowBytes = width*4; + + result = avifImageYUVToRGB(decoder->image, &rgb); + + avifDecoderDestroy(decoder); + + if (AVIF_RESULT_OK != result) + { + imageFree(output); + BX_ERROR_SET(_err, BIMG_ERROR, "AVIF: Failed to convert to RGBA."); + return NULL; + } + + output->m_hasAlpha = hasAlpha; + output->m_parser = ImageParser::Avif; + + return output; +#else + BX_UNUSED(_allocator); + BX_ERROR_SET(_err, BIMG_ERROR, "AVIF parsing is disabled (BIMG_CONFIG_PARSE_AVIF)."); + return NULL; +#endif // BIMG_CONFIG_PARSE_AVIF + } + #if BIMG_CONFIG_PARSE_WEBP static void* simpleWebpAlloc(void* _userdata, size_t _size) { @@ -1720,6 +1820,11 @@ namespace bimg return false; } + if (imageIsAvifBrand(_data, _size) ) + { + return false; + } + #if BIMG_CONFIG_PARSE_HEIF heif_context* ctx = heif_context_alloc(); @@ -1747,7 +1852,7 @@ namespace bimg return false; } - return imageInfoFinalize(_imageContainer, imageHeifFormat(_data, _size), bimg::TextureFormat::RGBA8, uint32_t(width), uint32_t(height), _err); + return imageInfoFinalize(_imageContainer, ImageParser::Heif, bimg::TextureFormat::RGBA8, uint32_t(width), uint32_t(height), _err); #else BX_UNUSED(_imageContainer, _data, _size); BX_ERROR_SET(_err, BIMG_ERROR, "HEIF parsing is disabled (BIMG_CONFIG_PARSE_HEIF)."); @@ -1755,6 +1860,63 @@ namespace bimg #endif // BIMG_CONFIG_PARSE_HEIF } + static bool imageParseInfoLibAvif(bx::AllocatorI* _allocator, ImageContainer& _imageContainer, const void* _data, uint32_t _size, bx::Error* _err) + { + BX_UNUSED(_allocator); + + static uint8_t ftypMagic[] = { 0x66, 0x74, 0x79, 0x70 }; // "ftyp" at offset 4 + + if (_size < 12 + || 0 != bx::memCmp( (const uint8_t*)_data + 4, ftypMagic, sizeof(ftypMagic) ) ) + { + return false; + } + + if (!imageIsAvifBrand(_data, _size) ) + { + return false; + } + +#if BIMG_CONFIG_PARSE_AVIF + avifDecoder* decoder = avifDecoderCreate(); + if (NULL == decoder) + { + BX_ERROR_SET(_err, BIMG_ERROR, "AVIF: Failed to create decoder."); + return false; + } + + avifResult result = avifDecoderSetIOMemory(decoder, (const uint8_t*)_data, _size); + if (AVIF_RESULT_OK == result) + { + result = avifDecoderParse(decoder); + } + + const uint32_t width = AVIF_RESULT_OK == result ? decoder->image->width : 0; + const uint32_t height = AVIF_RESULT_OK == result ? decoder->image->height : 0; + const bool hasAlpha = AVIF_RESULT_OK == result && 0 != decoder->alphaPresent; + + avifDecoderDestroy(decoder); + + if (AVIF_RESULT_OK != result) + { + BX_ERROR_SET(_err, BIMG_ERROR, "AVIF: Failed to read primary image."); + return false; + } + + if (!imageInfoFinalize(_imageContainer, ImageParser::Avif, bimg::TextureFormat::RGBA8, width, height, _err) ) + { + return false; + } + + _imageContainer.m_hasAlpha = hasAlpha; + return true; +#else + BX_UNUSED(_imageContainer, _data, _size); + BX_ERROR_SET(_err, BIMG_ERROR, "AVIF parsing is disabled (BIMG_CONFIG_PARSE_AVIF)."); + return false; +#endif // BIMG_CONFIG_PARSE_AVIF + } + bool imageParseInfo(bx::AllocatorI* _allocator, ImageContainer& _imageContainer, const void* _data, uint32_t _size, bx::Error* _err) { BX_ERROR_SCOPE(_err); @@ -1782,7 +1944,7 @@ namespace bimg return false; } - // Container formats (DDS/KTX/PVR3/GNF/TEX) are parsed header-only. + // Container formats (DDS/KTX/PVR3/TEX) are parsed header-only. { bx::Error containerErr; if (imageParse(_imageContainer, _data, _size, &containerErr) ) @@ -1803,6 +1965,7 @@ namespace bimg imageParseInfoJpeg, imageParseInfoSimpleWebp, imageParseInfoStbImage, + imageParseInfoLibAvif, imageParseInfoLibHeif, }; @@ -1835,12 +1998,12 @@ namespace bimg imageParseKtx2, imageParseKtx, imageParsePvr3, - imageParseGnf, imageParseLodePng, imageParseTinyExr, imageParseJpeg, imageParseSimpleWebp, imageParseStbImage, + imageParseLibAvif, imageParseLibHeif, }; @@ -1887,4 +2050,71 @@ namespace bimg return output; } + static const char* const s_supportedExt[] = + { +#if BIMG_CONFIG_PARSE_AVIF + "avif", +#endif // BIMG_CONFIG_PARSE_AVIF + +#if BIMG_CONFIG_PARSE_BMP + "bmp", +#endif // BIMG_CONFIG_PARSE_BMP + + "dds", +#if BIMG_CONFIG_PARSE_EXR + "exr", +#endif // BIMG_CONFIG_PARSE_EXR +#if BIMG_CONFIG_PARSE_GIF + "gif", +#endif // BIMG_CONFIG_PARSE_GIF +#if BIMG_CONFIG_PARSE_HDR + "hdr", +#endif // BIMG_CONFIG_PARSE_HDR + +#if BIMG_CONFIG_PARSE_HEIF + "heic", +#endif // BIMG_CONFIG_PARSE_HEIF + +#if BIMG_CONFIG_PARSE_JPEG + "jpeg", + "jpg", +#endif // BIMG_CONFIG_PARSE_JPEG + + "ktx", + "ktx2", + +#if BIMG_CONFIG_PARSE_PNM + "pgm", +#endif // BIMG_CONFIG_PARSE_PNM + +#if BIMG_CONFIG_PARSE_PNG + "png", +#endif // BIMG_CONFIG_PARSE_PNG + +#if BIMG_CONFIG_PARSE_PNM + "ppm", +#endif // BIMG_CONFIG_PARSE_PNM + +#if BIMG_CONFIG_PARSE_PSD + "psd", +#endif // BIMG_CONFIG_PARSE_PSD + + "pvr", + +#if BIMG_CONFIG_PARSE_TGA + "tga", +#endif // BIMG_CONFIG_PARSE_TGA + +#if BIMG_CONFIG_PARSE_WEBP + "webp", +#endif // BIMG_CONFIG_PARSE_WEBP + + NULL, + }; + + const char* const* getSupportedExt() + { + return s_supportedExt; + } + } // namespace bimg diff --git a/src/image_gnf.cpp b/src/image_gnf.cpp deleted file mode 100644 index cd6e4d3..0000000 --- a/src/image_gnf.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2011-2026 Branimir Karadzic. All rights reserved. - * License: https://github.com/bkaradzic/bimg/blob/master/LICENSE - */ - -#include "bimg_p.h" - -namespace bimg -{ - bool imageParseGnf(ImageContainer& _imageContainer, bx::ReaderSeekerI* _reader, bx::Error* _err) - { - BX_UNUSED(_imageContainer, _reader, _err); - BX_ERROR_SET(_err, BIMG_ERROR, "GNF: not supported."); - return false; - } - - ImageContainer* imageParseGnf(bx::AllocatorI* _allocator, const void* _src, uint32_t _size, bx::Error* _err) - { - BX_UNUSED(_allocator); - - bx::MemoryReader reader(_src, _size); - - uint32_t magic; - bx::read(&reader, magic, bx::ErrorIgnore{}); - - ImageContainer imageContainer; - if (BIMG_CHUNK_MAGIC_GNF != magic - || !imageParseGnf(imageContainer, &reader, _err) ) - { - return NULL; - } - - BX_ERROR_SET(_err, BIMG_ERROR, "GNF: not supported."); - return NULL; - } - -} // namespace bimg